[Solved] Curve/Spline scale fail? jme3 r7236

Hi!

I noticed by experience that a Curve (created via a Spline, at least) is not affected by the parent Node’s scale (or so it seems), however it is affected by the Node’s LocalTranslation and LocalRotation

More explicitly, I had

rootNode->subNode->geoBox

rootNode->subNode->geoCurve

and I am trying to rotate the geoBox (which is a Box) and make a Curve geoCurve show a trail of one of this box’s corners, specifically the Vector3f(getXExtent(), getYExtent(), getZExtent()) corner by using this as the in vector to geoBox.getLocalTransform().transformVector( inCorner, store ); in order to find it while the box is rotated/scaled/positioned who knows where, and I use this store as an input to Spline.addControlPoint(store); then I create a new Curve by passing it the Spline each time I update that… I know it’s a bad way, but I am learning so to speak, both jme3 and java, by the way, any hints would be appreciated!

This works “well” if I comment the scale line in simpleInitApp():

subNode.scale(1.3f, 0.7f, 0.4f );

otherwise if I allow it, the Courve is detached from that corner, although the box changed shape, not sure if it considers the box with scale 1… all right, I now see that if I use subNode.scale(anyNumf); it works well, this leads me to believe that only when I use scale(x,y,z) method it doesn’t work as expected. I’m not sure if it’s my fault or anything…

I would’ve posted the code except it’s quite a mess and a part of something more, let me know if I should extract only the needed parts and post it? I wouldn’t mind

Ok here’s the code and screenies(sorry looks like the indentation was lost):

pre type="java"
package org.jme3;





import com.jme3.app.SimpleApplication;


import com.jme3.material.Material;


import com.jme3.math.ColorRGBA;


import com.jme3.math.FastMath;


import com.jme3.math.Quaternion;


import com.jme3.math.Spline;


import com.jme3.math.Vector3f;


import com.jme3.scene.Geometry;


import com.jme3.scene.Node;


import com.jme3.scene.shape.Box;


import com.jme3.scene.shape.Curve;





/**

  • Sample 2 - How to use nodes as handles to manipulate objects in the scene

  • graph. You can rotate, translate, and scale objects by manipulating their

  • parent nodes. The Root Node is special: Only what is attached to the Root

  • Node appears in the scene.


    /


    public class HelloNode4 extends SimpleApplication {





    public static void main(String[] args) {


    HelloNode4 app = new HelloNode4();


    app.setShowSettings(false);


    app.start();


    }





    /


    • (non-Javadoc)



    • @see com.jme3.app.SimpleApplication#simpleUpdate(float)


      */


      @Override


      public void simpleUpdate(float tpf) {


      // cam.setDirection( box1.getCenter().normalizeLocal() );


      // setLookAt


      yaw = (yaw + FastMath.DEG_TO_RAD * 4 * tpf) % (FastMath.PI * 2);


      roll = (roll + FastMath.DEG_TO_RAD * 7 * tpf) % (FastMath.PI * 2);


      pitch = (pitch + FastMath.DEG_TO_RAD * 11 * tpf) % (FastMath.PI * 2);


      rot.fromAngles(yaw, roll, pitch);


      geoBox.setLocalRotation(rot);


      Vector3f store = new Vector3f(box.getXExtent(), box.getYExtent(),


      box.getZExtent());


      geoBox.getLocalTransform().transformVector(store, store);


      spline.addControlPoint(store);


      if (geoCurve != null) {


      subNode.detachChild(geoCurve);


      }


      geoCurve = new Geometry(“trails”, new Curve(spline, 1));


      geoCurve.setMaterial(curveMat);


      subNode.attachChild(geoCurve);


      }





      Geometry geoCurve = null;


      Material curveMat;





      Box box;


      Geometry geoBox;


      Quaternion rot = new Quaternion();


      float yaw = 0f;


      float roll = 0f;


      float pitch = 0f;


      Spline spline = new Spline();





      Node subNode;





      @Override


      public void simpleInitApp() {





      subNode = new Node();


      subNode.setLocalTranslation(new Vector3f(1, 3, 1));


      rot = new Quaternion();


      rot.fromAngles(1f, -2f, 4f);


      subNode.setLocalRotation(rot);


      box = new Box(0.5f, 0.9f, 1.3f);


      geoBox = new Geometry(“Box”, box);


      geoBox.setLocalTranslation(1f, 2f, 3f);


      geoBox.scale(1.3f, 1.2f, 1.1f);


      Material mat2 = new Material(assetManager,


      “Common/MatDefs/Misc/WireColor.j3md”);


      mat2.setColor(“Color”, ColorRGBA.Red);


      geoBox.setMaterial(mat2);





      curveMat = new Material(assetManager,


      “Common/MatDefs/Misc/WireColor.j3md”);


      curveMat.setColor(“Color”, ColorRGBA.Green);





      rootNode.setLocalTranslation(-1, -2, -4);


      rootNode.rotate(-1f, 2f, -4f);


      flyCam.setMoveSpeed(20f);





      subNode.attachChild(geoBox);


      rootNode.attachChild(subNode);


      subNode.scale(0.4f);


      subNode.scale(1.3f, 0.7f, 0.4f);// XXX: bug when uncommented


      }


      }



      /pre

      without the 3 param scale ie. commented - works well:



      ===========

      with the 3 param scale ie. uncommented - fail:



      I also wanted to use this opportunity to ask how should I do this “trail” thing, or how would you do it?

      Thanks in advance

mhhhh…maybe there is a problem with the bounds of the curve

Thanks for the test case i’m gonna look at it

1 Like

cool thanks for taking a look!

I modified the code to add movement “forward” also, but mostly just cleaning it up in the hope it would be “easier”

This is when the “XXX:” parts are both uncommented (when it works):

http://i.imgur.com/XoEao.png

pre type="java"
package org.jme3.tests;





import com.jme3.app.SimpleApplication;


import com.jme3.material.Material;


import com.jme3.math.ColorRGBA;


import com.jme3.math.FastMath;


import com.jme3.math.Spline;


import com.jme3.math.Vector3f;


import com.jme3.scene.Geometry;


import com.jme3.scene.Node;


import com.jme3.scene.shape.Box;


import com.jme3.scene.shape.Curve;





/**

  • Sample 2 - How to use nodes as handles to manipulate objects in the scene

  • graph. You can rotate, translate, and scale objects by manipulating their

  • parent nodes. The Root Node is special: Only what is attached to the Root

  • Node appears in the scene.


    /


    public class HelloNode4 extends SimpleApplication {





    private Vector3f cornerPos;





    private Geometry geoCurve;





    private Geometry geoBox;





    private final Spline spline = new Spline();





    public static void main(String[] args) {


    HelloNode4 app = new HelloNode4();


    app.setShowSettings(false);


    app.start();


    }





    /


    • (non-Javadoc)



    • @see com.jme3.app.SimpleApplication#simpleUpdate(float)


      */


      @Override


      public void simpleUpdate(float tpf) {


      // calculating by how much to rotate now(relative to previous position),


      // not the exact rotation position(as it was in


      // prev. revision)


      float yaw = (FastMath.DEG_TO_RAD * 4 * tpf) % (FastMath.PI * 2);


      float roll = (FastMath.DEG_TO_RAD * 7 * tpf) % (FastMath.PI * 2);


      float pitch = (FastMath.DEG_TO_RAD * 11 * tpf) % (FastMath.PI * 2);


      // rotating the box by this much


      geoBox.rotate(yaw, roll, pitch);


      // move box “forward” too, that is, forward relative to itself ie.


      // spaceship moving forward


      geoBox.move(geoBox.getLocalRotation().getRotationColumn(2).mult(tpf));





      Vector3f clonedCornerPos = cornerPos.clone();


      // now applying the same transform (pos/rot/scale) to the corner as the


      // box has


      geoBox.getLocalTransform().transformVector(clonedCornerPos,// in,


      clonedCornerPos// store


      );


      // we have the corner’s exact pos now, relative to the Node the geoBox &


      // geoCurve are both in


      // we add that pos to the curve


      spline.addControlPoint(clonedCornerPos);// must be cloned!


      // we must create new Curve object because we can’t add/update the


      // spline in existing one (?!)


      geoCurve.setMesh(new Curve(spline, 1));


      }





      @Override


      public void simpleInitApp() {


      flyCam.setMoveSpeed(20f);





      Node subNode = new Node();


      subNode.setLocalTranslation(new Vector3f(2, 0.5f, 1));


      subNode.rotate(1f, -2f, 4f);


      // a box with non 0,0,0 center which means a position of x,y,z relative


      // to it’s parent geoBox(below)


      Box box = new Box(new Vector3f(2, 3, 4), 0.5f, 0.9f, 1.3f);


      geoBox = new Geometry(“Box”, box);


      geoBox.setLocalTranslation(1f, 2f, 3f);


      geoBox.scale(1.3f, 1.2f, 1.1f);// always ok


      geoBox.rotate(2f, -4f, 0.4f);


      Material mat2 = new Material(assetManager,


      “Common/MatDefs/Misc/WireColor.j3md”);


      mat2.setColor(“Color”, ColorRGBA.Red);


      geoBox.setMaterial(mat2);





      Material curveMat = new Material(assetManager,


      “Common/MatDefs/Misc/WireColor.j3md”);


      curveMat.setColor(“Color”, ColorRGBA.Green);


      geoCurve = new Geometry(“trails”);


      geoCurve.setMaterial(curveMat);


      subNode.attachChild(geoCurve);


      subNode.attachChild(geoBox);





      rootNode.setLocalTranslation(-1, -2, -4);


      rootNode.rotate(-1f, 2f, -4f);





      rootNode.attachChild(subNode);


      rootNode.scale(1.5f);// this is always ok


      subNode.scale(0.4f);// this is always ok too


      // subNode.scale(1.3f, 0.7f, 0.4f);// XXX: bug when uncommented


      // rootNode.scale(0.4f, 1.4f, 1.1f);// XXX: or/and this





      // doing this here only once


      cornerPos = new Vector3f();


      // calculating position of corner, first getting corner as it were if


      // box were at 0,0,0 and no rotation/scale


      cornerPos.set(





      box.getXExtent(), box.getYExtent(), box.getZExtent());


      // now considering box may have a different than 0,0,0 center


      cornerPos.addLocal(box.getCenter());


      }


      }



      /pre



      oh btw, I messed up the rotation, that is, it’s different from previous code (in first post) xD

ok, I looked into this a bit more. I’m not completely sure what’s the issue…but it’s math related. The scale is correctly applied in the engine.



the thing is, you attach th e curve geom and the box geom to a node, you scale the node in the init of the application.

Then you recreate the mesh of the curve on each frame.

The problem is that doing than you change the center of mass of the curve geom, and thus it’s position relative to it’s parent (the scaled node). It works fine with uniform scale because all the dimensions are scaled, but with non uniform it creates some kind of offset. I don’t know for sure the math behind this, but I feel it’s the issue.

Btw it works if just scale the box alone.



I don’t know what you are trying to do here, if it’s for learning purpose it’s fine, but if it’s an effect you want to reuse later, you should not recreate the mesh on each frame. You will trash the memory in no time making the GC crazy, and this will yield poor performance.



If you really want a trailing effect i suggest you use a particle emitter instead of the curve. Use the very same “corner” method for positioning the emitter on each frame, and you’ll have a nice effect.

2 Likes

It was definitely for learning purposes though if I wanted to do it for real I wouldn’t know how, maybe if I rewrite a new Curve class and somehow keep adding to the existing mesh when a new spline point is added, no idea how to do this yet, hopefully after more learning I’d get a clue lol



I’ll try that emitter as you said, though I did want something permanent(like exactly what the Curve does but I did want to avoid ‘new’-ing all the time - did not know how lol), not sure if emitter does that (it was my impression so far that it fades out)



By the way, I did try this after I updated my drivers and restored settings to defaults (had 2 other issues that were existent because of them) though (obviously since you tested it) this issue kept persisting. Hope someone can fix this if it’s an issue in jme3 - hmm, this reminded me of some old code in jme2 which had some issue with the same scale when non-uniform like it wouldn’t detect it hit the sphere… maybe I can find it and test it again (if I remember right, it was one of the main reasons I gave up at that time)

whatever is the issue here, it could be the same issue in this topic

http://hub.jmonkeyengine.org/groups/general-2/forum/topic/help-me-fix-rotation-in-testmousepick-java-after-rootnode-locally-translated-jme3-7247/

where I basically fail to properly rotate a normal but only when translation of the rootNode is different than 0,0,0 , seems to be unaffected by rootNode’s scale & rotation though

I’ll be posting there my latest modifs which include both this topic’s “bug” and that topic’s “bug” or whatever they are



EDIT: I added an eclipse project to this link: http://www.mediafire.com/file/rk4o7ri3ty2j0x5/JME3NodeTranslations_eclipse.rar (also included lwjgl dll though only for 64bit windows)

what that does is showing how the white tip boxes on top of the arrows coords are being affected somehow when parent nodes are transformed, look at rootNode is perfect, yet I’m using the same method of setLocalTranslation

surely I must be missing something, can’t be a jme3 r7281 bug

http://i.imgur.com/qFwK2.jpg

I’ve hit this issue again, apparently, in this post

http://hub.jmonkeyengine.org/groups/general-2/forum/topic/remove-boxs-middle-lines-to-look-like-a-wirebox-possible/#post-125688

where only when using non-uniform scale things gets messed up,

is it really me failing to do something or is it a jme3 bug? (I somehow doubt that’s the latter - like I refuse to believe, although I initially did)



EDIT: in that post the issue was fixed with non-uniform scale (fixed in jme3 r7291)

but when running the code in the first post it still didn’t fix THIS issue (of this topic) so it must be a different issue(?) here

Finally, this is what’s been done (after 2 jme3 bugfixes by momoko_fan) and after I re-edited my code, this is what works:

a red rotating wireframe Box that leaves a green trail from one of its corners, no matter what the parents of the box and curve are (that is, regardless of their parents’ transforms, the curve is correctly shown)

much has been learned → me happy monkey :smiley:

tested in jme3 r7296

http://i.imgur.com/hZvTA.jpg

[java]package org.jme3.forum;



import java.util.prefs.BackingStoreException;



import com.jme3.app.SimpleApplication;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.FastMath;

import com.jme3.math.Quaternion;

import com.jme3.math.Spline;

import com.jme3.math.Vector3f;

import com.jme3.scene.Geometry;

import com.jme3.scene.Node;

import com.jme3.scene.shape.Box;

import com.jme3.scene.shape.Curve;

import com.jme3.system.AppSettings;



/**

  • Sample 2 - How to use nodes as handles to manipulate objects in the scene
  • graph. You can rotate, translate, and scale objects by manipulating their
  • parent nodes. The Root Node is special: Only what is attached to the Root
  • Node appears in the scene.

    /

    public class HelloNode4Fixed extends SimpleApplication {

    public static void main(String[] args) throws BackingStoreException {

    HelloNode4Fixed app = new HelloNode4Fixed();

    AppSettings cfg = new AppSettings(true);

    cfg.load(cfg.getTitle());

    cfg.setVSync(true);

    app.setSettings(cfg);

    app.setShowSettings(false);

    app.start();

    }



    float tpfTimer = 0;

    // how many times a sec to create a spline point

    final static float timesASec = 2;

    final static float every = 1 / timesASec;



    /

  • (non-Javadoc)

    *
  • @see com.jme3.app.SimpleApplication#simpleUpdate(float)

    */

    @Override

    public void simpleUpdate(float tpf) {



    yaw = (yaw + FastMath.DEG_TO_RAD * 4 * tpf) % (FastMath.PI * 2);

    roll = (roll + FastMath.DEG_TO_RAD * 7 * tpf) % (FastMath.PI * 2);

    pitch = (pitch + FastMath.DEG_TO_RAD * 11 * tpf) % (FastMath.PI * 2);

    persistentRotation.fromAngles(yaw, roll, pitch);

    // the box keeps rotating

    boxGeom.setLocalRotation(persistentRotation);



    // we keep getting world coords of the box’s (same)corner

    Vector3f worldPosOfBoxsCorner = boxGeom.localToWorld(boxsCorner, null);



    tpfTimer -= tpf;

    if (tpfTimer <= 0) {

    tpfTimer = every;



    if (curveGeom != null) {

    // first time don’t detach, it’s not attached yet

    curvesParent.detachChild(curveGeom);

    }



    curveSpline.addControlPoint(worldPosOfBoxsCorner);



    // just for learning/testing purposes (ie. don’t do this):

    curveGeom = new Geometry(“trails”, new Curve(curveSpline, 1));

    curveGeom.setMaterial(curveMat);

    curvesParent.attachChild(curveGeom);



    // make the curveGeom have opposing transforms, so it seems that it

    // has no parents ie. like it’s in world space

    Vector3f nullifyParentTranslation = curvesParent.worldToLocal(

    Vector3f.ZERO, null);

    curveGeom.setLocalTranslation(nullifyParentTranslation);



    Vector3f parentScale = curvesParent.getWorldScale().clone();

    curveGeom.setLocalScale(Vector3f.UNIT_XYZ.divide(parentScale));



    curveGeom.setLocalRotation(curvesParent.getWorldRotation()

    .inverse());

    // curveGeom.getWorldTransform();// safety

    }

    }



    private Geometry curveGeom = null;

    private Material curveMat;

    private Box boxMesh;

    private Geometry boxGeom;

    private Quaternion persistentRotation = new Quaternion();

    private float yaw = 0f;

    private float roll = 0f;

    private float pitch = 0f;

    private final Spline curveSpline = new Spline();

    private Node subRoot;

    private Node curvesParent;// reference to parent

    private Vector3f boxsCorner;



    @Override

    public void simpleInitApp() {

    cam.setFrustumFar(111111f);

    // XXX:uncomment only one of the following 2 blocks:



    // ===start1: for when rootNode is non-transformed:

    // cam.setLocation(new Vector3f(101.11522f, -5.4640026f, -114.97108f));

    // cam.setRotation(new Quaternion(-0.095678836f, 0.35348037f,

    // 0.036373425f, 0.9298248f));

    // ===end1.



    // ===start2: for when rootNode is transformed:

    rootNode.setLocalTranslation(-91, -22, +54);

    rootNode.rotate(-1f, 2f, -4f);

    rootNode.scale(0.3f, 3.7f, 0.4f);

    cam.setLocation(new Vector3f(-112.212425f, 341.09134f, 536.55414f));

    cam.setRotation(new Quaternion(-0.21162057f, -0.13502307f,

    -0.029532198f, 0.9675295f));

    // ===end2.



    flyCam.setMoveSpeed(1000f);

    subRoot = new Node(“transformed subRoot”);

    subRoot.setLocalTranslation(new Vector3f(-71, 13, -31));

    persistentRotation = new Quaternion();

    persistentRotation.fromAngles(1f, -2f, 4f);

    subRoot.setLocalRotation(persistentRotation);

    boxMesh = new Box(0.5f, 0.9f, 1.3f);

    boxsCorner = new Vector3f(boxMesh.getXExtent(), boxMesh.getYExtent(),

    boxMesh.getZExtent());

    boxGeom = new Geometry(“rotating Box”, boxMesh);

    boxGeom.setLocalTranslation(31f, -22f, 13f);

    boxGeom.scale(3.3f, 0.2f, 8.1f);

    Material boxMat = new Material(assetManager,

    “Common/MatDefs/Misc/WireColor.j3md”);

    boxMat.setColor(“Color”, ColorRGBA.Red);

    boxGeom.setMaterial(boxMat);

    curveMat = new Material(assetManager,

    “Common/MatDefs/Misc/WireColor.j3md”);

    curveMat.setColor(“Color”, ColorRGBA.Green);

    flyCam.setMoveSpeed(20f);

    curvesParent = subRoot;// refer to parent

    subRoot.attachChild(boxGeom);

    rootNode.attachChild(subRoot);

    // subNode.scale(0.4f);

    subRoot.scale(3.3f, 7.7f, 0.4f);

    }

    }[/java]
1 Like