Attaching a Spatial to a Node without changing the Spatial's World Transform

Hi all,



Is there a way for me to calculate an inverse transformation of a Transformation object? Let’s say if I have a transformation T, I would like to find the reverse transformation T’ so that if I apply the transformation T, then apply the transformation T’, my object would not be moved at all.



Here is why I think I need it:


  • Let’s say I have a Node N1 with the world transformation T1, a Node N2 with the world transformation T2, and a Spatial S3.
  • At some point I attach the spatial S3 into Node N1, then set the local transformation of S3 to some transformation T3.
  • At some other point I would like to detach S3 from N1 and attach it to N2, but I do not want to change the current position of S3 at all (meaning that I would like to keep the world transformation of S3 unchanged).



    If I just attach simple attach S3 to N2 (which implicitly detach it from N1), jmonkeyengine will just simply keep the local transformation T3 unchanged, meaning that when it is combine with T2 from N2, my spatial S3 will now be moved/scaled/rotated to some unpredicted location.



    I figure out that if I can calculate the reverse transformation T2’ from the Node N2, I will first get the world transformation T3 of S3, detach it from N1, attach S3 to N1, then combine T3 with T2’ and use it as the new local transformation of S3. That way the world translation, scale, and rotation of S3 will not change even though I not attach it to a different Node.



    All of the above seems complicated, but I tried my best to make it as clear as I can on my intention. I hope I can get some help :).



    Thanks for being patient to even read through all this :).
2 Likes

I’ve gotta ask…



Why are you performing transforms against the spatial and the node it is going into? Can’t you just keep one of them (i.e. the node… or the spatial–I assume you mean Geometry when referring to spatial… though a spatial could be either a geometry or a node) static?



Explaining why you are doing it this way will help clarify your question. I’m sure it’s just what you need to do, however, without knowing why it is hard to understand the question.

I guess you just have to multiply with the inverse transformation matrix.

See here: https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:math#matrix

1 Like

there is a transformInverseVector() method in the Transform class, there is no javadoc, but what it does is exactly what you want to do.

Thank you all for replying.



@t0neg0d: I just used Spatial as a general case. Some times it could be a Geometry. But it could also be a Node that has a Geometry in it. In a general case, I feel that I cannot keep any of them static. This is just an example that I made up, but let’s say i implement a card game. There are two hands (e.g., belongs to two different players) that I represent using two Nodes (N1 and N2). There is a card (representing by S3) that belongs to N1 at first. At some point though, I want to make S3 move from it’s hand to a position on the other hand. Now since S3 now belongs to the other hand, I would like to attach it to N2 before I calculate a local movement that would move it in N2 coordinate into its final destination. The thing is at the moment I attach the card to N2, I do not want the card to “jump” to some new position yet. I want it to stay where it is then i will move it to its position in N2 gradually.



@nehon & survivor: Yes, technically, it looks like all I need to do is doing what you guys advice. But exactly how and what do I do? After I attach S3 into N2, I need to set S3.setLocalTransformation(…) to some value, otherwise it will “jump” into a new location (because it’s current local transformation is still not changed as it was attached into N1, but now it is attached into N2, which has its own world transformation value). So I dont know how transformInverseVector would help me yet:

  • Mathematically speaking, I think I can use that to calculate the new local translation of S3.
  • But how about local scale and local rotation of S3? How would transformInverseVector help?

You are right, there’s no “transform.invert()” at the moment. You could create a Matrix4f from a Transform and invert that matrix, but you have to do it “by hand” because Transform and Matrix4f don’t fit together well.

Looks like this is what I am looking for (I just use the ’ to indicate inverse, e.g., S1’ is inverse of S):


  • Suppose the original transformation has the scale S1, rotation R1, and Translation T1.
  • The inverse transformation will have:
  • Scale S2 = S1’ (inverse of S1)
  • Rotation R2 = R1’ (inverse of R1)
  • Translation T2 = T1’.R1’.S1’ (inverse T1 multiple by inverse R1 multiple by inverse S1)



    Initial test shows that it works. The newly added spatial will keep its own world transformation. I need to work a bit more on the efficiency of the operation (like using multLocal instead of mult and stuff like that, and some more testing). But after getting the inverse transformation, this is what I did:



    [java]

    Transformation inverseTransformation = getInverseTransformation( newParent.getWorldTransform() );

    Transformation newLocalTransformation = newChild.getWorldTransform().clone().combineWithParent(inverseTransformation);

    newParent.attachChild(newChild);

    newChild.setLocalTransformation(newLocalTransformation);

    [/java]



    What I am not sure of is the math (did my best but I am not that good with matrix math). Could anybody verify the above math stuff for me?

That’s it. Looks correct to me.

Oops, looks like the “inverse transform” that I calculate is correct, meaning that



[java]

transform.combineWithParent( getInverseTransform(transform) )

[/java]



will always give us the “identity” transformation.



However, I was wrong when I thought that the new local transform for the child should be



[java]

newChild.getWorldTransform().clone().combineWithParent(inverseTransformation);

[/java]



Looks like I should just write a method getNewLocalTransform( newParent.getWorldTransform(), newChild.getWorldTransform() ). So in that case, the new local transform should simply be this:



[java]

Vector3f newScale = childTransform.getScale().divide(parentTransform.getScale());

Quaternion newRotation = parentTransform.getRotation().inverse().mult( childTransform.getRotation() );

Vector3f newTranslation = parentTransform.getRotation().inverse().mult(

childTransform.getTranslation().subtract( parentTransform.getTranslation()) ).divide(parentTransform.getScale());

[/java]



Tested that much thoroughly this time. Also reviewed the math more thoroughly - my math improves a lot today :). Now, all I have to do is to make the above code efficient, again :).

Thanks nehon for checking the math for me. That helps me confirm my solution. In case anybody stumbles into the same problem, this is my final solution. Again, the goal is to attach a Spatial into a new Node, without changing the world transform of the Spatial.



[java]

/**

  • Attaching the <code>child</code> to this new <code>parent</code> Node, without
  • changing the child world transform, regardless of the parent world transform.
  • If the child was attached to another Node before, it will be detached from that Node.
  • @param parent
  • @param child

    */

    public static void safeAttach(Node parent, Spatial child) {

    Transform childWorldTransform = child.getWorldTransform();

    Transform parentWorldTransform = parent.getWorldTransform();

    Transform newLocalTransform = getLocalTransformToPreserveWorldTransform(parentWorldTransform, childWorldTransform);

    parent.attachChild(child);

    child.setLocalTransform(newLocalTransform);

    }



    private static Transform getLocalTransformToPreserveWorldTransform(Transform parentTransform, Transform childTransform) {

    Vector3f scale = childTransform.getScale().divide(parentTransform.getScale());

    Quaternion rotation = parentTransform.getRotation().inverse().multLocal( childTransform.getRotation() );

    Vector3f translation = parentTransform.getRotation().inverse()

    .multLocal( childTransform.getTranslation().subtract( parentTransform.getTranslation()) )

    .divideLocal(parentTransform.getScale());

    return new Transform(translation, rotation, scale);

    }

    [/java]
3 Likes