Bug in GeomTransform

Hi
this bug is actually a Java3D vecmath problem, but I post it here too (I’ve postet it in the Java3D forum too), because it affects the behavior of the GeomTransform.
The thing is this: GeomTransform uses a Matrix4f to store the transformation data into. The rotational part of the transformation data is always created out of Quat4f. When the Rotation data is requested the rotational part of the Matrix4f is transformed back into a Quat4f.
But: this transformation is not a identity transformation !!!
As an example see this code:


        Quat4f qStart = new Quat4f(0,0.707f,0.707f,0);
        Matrix4f mStart = new Matrix4f();
        mStart.set(qStart);
        Quat4f qEnd = new Quat4f();
        mStart.get(qEnd);
        Matrix4f mEnd = new Matrix4f();
        mEnd.set(qEnd);
        System.out.println("qStart: "+qStart);
        System.out.println("\nmStart: "+mStart);
        System.out.println("\nqEnd: "+qEnd);
        System.out.println("\nmEnd: "+mEnd);

When I execute it I get:

qStart: (0.0, 0.7071068, 0.7071068, 0.0)

mStart: -1.0000002, 0.0, 0.0, 0.0
0.0, -1.1920929E-7, 1.0000001, 0.0
0.0, 1.0000001, -1.1920929E-7, 0.0
0.0, 0.0, 0.0, 1.0

qEnd: (NaN, NaN, NaN, NaN)

mEnd: NaN, NaN, NaN, 0.0
NaN, NaN, NaN, 0.0
NaN, NaN, NaN, 0.0
0.0, 0.0, 0.0, 1.0

err… crap!

got a workaround?

Will.

I checked the Java3D forums, where I also posted it and they said, that they know this problem and that we should use Transform3D’s instead. But I don’t think this is a good idea for us, because we would have to get the Transform3D from Java3D or xith3d. :frowning:
It would be better if somebody knows how we could do the transformation without the matrix.

Well, we have this base covered a long time ago in jME-Physics. Because we didn’t rely on using vecmath, we used jme’s math stuff (TransformQuatMatrix), we were ok.

Why dont you just have a couple of Vector3f and a Quaternion in there instead of that matrix4f? This is how we solved it (more or less…)

DP

[quote]Well, we have this base covered a long time ago in jME-Physics. Because we didn’t rely on using vecmath, we used jme’s math stuff (TransformQuatMatrix), we were ok.

Why dont you just have a couple of Vector3f and a Quaternion in there instead of that matrix4f? This is how we solved it (more or less…)

DP
[/quote]
Thanks for the reply.
The GeomTransform multiplies two transforms (the one of the Body and the one of the EncapsulatedGeom). With Matrices this is simply matrix.mul(anotherMatrix).
How can we do this with a Vector and a Quat ?
How did you do this?

Arne

Right, well, i dont multiply em! lol, and it works:


 * Copyright (c) 2004, William Denniss. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials provided
 *   with the distribution.
 *
 * Neither the name of William Denniss nor the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A
 * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE
 *
 */
package org.odejava;

import org.odejava.ode.*;

import com.jme.math.Quaternion;
import com.jme.math.TransformMatrixQuat;
import com.jme.math.Vector3f;

/**
 * <p>The GeomTransform is a geom which encapsulates a Geom, allowing the encapsulated Geom to be transformed
 * relitive to it.  The GeomTransform can then be added to a Body.</p>
 *
 * <p>Please refer to the <a href="http://opende.sourceforge.net/ode-latest-userguide.html#sec_10_7_7">ODE Documentation</a>
 * for more information.  An example in ODE code exists <a href="http://q12.org/pipermail/ode/2002-July/005462.html"> here</a></p>
 *
 * <p>It is a non-placable geometry, therefor the transform setters
 * must not be called.  However the transform getters may be called.  As it is
 * non-placable, the getters don't simply delegate the call to ODE.  Rather they
 * return the world transform of the encapsulated Geom (that is, the transform
 * of the parent body multiplied by that of the encapsulated Geom).  Subsiquently,
 * you can bind GeomTransform objects to Display objects.</p>
 *
 * @author William Denniss
 */
public class GeomTransform extends PlaceableGeom {

    private PlaceableGeom encapsulatedGeom;

    private TransformMatrixQuat cachedBodyTransform = new TransformMatrixQuat();
    private TransformMatrixQuat cachedTmpTransform = new TransformMatrixQuat();

    /**
     * Creates a GeomTransform with no name.
     *
     * @ode dCreateGeomTransform creates the object
     */
    public GeomTransform() {
        this("");
    }

    /**
     * Greats a GeomTransform with the given name
     *
     * @param name the name of this Geom
     *
     * @ode dCreateGeomTransform creates the object
     */
    public GeomTransform(String name) {
        super(name);

        spaceId = Ode.getPARENTSPACEID_ZERO();
        geomId = Ode.dCreateGeomTransform(spaceId);

        updateNativeAddr();

    }

    /**
     * Sets the encapsulated Geom.
     *
     * @param obj the geom this GeomTransform encapsulates.
     *          This Geom should not be added to any Space or associated
     *          with any Body.
     * @ode dGeomTransformSetGeom sets the encapsulated geom
     */
    public void setEncapsulatedGeom(PlaceableGeom obj) {
        if (encapsulatedGeom != null) {
            throw new IllegalOdejavaOperation("Attempt to assign a second Encapsulated geom.  GeomTransform can only have one encapsualted Geom.");
        }

        encapsulatedGeom = obj;
        Ode.dGeomTransformSetGeom(geomId, obj.getId());
        encapsulatedGeom.isEncapsulated = true;
    }

    /**
     * Removes the encapsulate Geom.
     *
     * @dGeomTransformSetGeom
     */
    public void removeEncapsulatedGeom() {
        Ode.dGeomTransformSetGeom(geomId, new SWIGTYPE_p_dGeomID(0, false));
        encapsulatedGeom.isEncapsulated = false;

        encapsulatedGeom = null;
    }

    /**
     * Returns the encapsulated geometry.
     *
     * @return the encapsulated geometry.
     */
    public PlaceableGeom getEncapsulatedGeom() {
        return encapsulatedGeom;

    }

    /**
     * Returns the world position of the encapsulated
     * geometry.  This is calcualted by multiplying the
     * transform matrix of the parent body with the offset
     * transform of the encapsulated geom.  To get the
     * offset of the encapsulated geom - call getPosition on
     * it.
     *
     * @return the world position of the encapsulated
     * geometry
     *
     * @throws IllegalOdejavaOperation if this GeomTransform doesn't
     *         have a parent body or doesn't have an encapsulated Geom
     *
     * @see updateCachedTransform
     */
    public Vector3f getPosition() {
        return getPosition(new Vector3f());
    }

    /**
     * Returns the world position of the encapsulated
     * geometry.  This is calcualted by multiplying the
     * transform matrix of the parent body with the offset
     * transform of the encapsulated geom.  To get the
     * offset of the encapsulated geom - call getPosition on
     * it.
     *
     * @param result Vector3f to write the result to
     *
     * @return the world position of the encapsulated
     * geometry
     *
     * @throws IllegalOdejavaOperation if this GeomTransform doesn't
     *         have a parent body or doesn't have an encapsulated Geom
     *
     * @see updateCachedTransform
     */
    public Vector3f getPosition(Vector3f result) {

        updateCachedTransform();

        cachedBodyTransform.getTranslation(result);

        return result;

    }

    /**
     * Returns the world quaternion of the encapsulated
     * geometry.  This is calcualted by multiplying the
     * transform matrix of the parent body with the offset
     * transform of the encapsulated geom.  To get the
     * offset of the encapsulated geom - call getQuaternion on
     * it.
     *
     * @param result Quat4f to write the result to
     *
     * @return the world quaternion of the encapsulated
     * geometry
     *
     * @throws IllegalOdejavaOperation if this GeomTransform doesn't
     *         have a parent body or doesn't have an encapsulated Geom
     *
     * @see updateCachedTransform
     */
    public Quaternion getQuaternion() {
        return getQuaternion(new Quaternion());
    }

    /**
     * Returns the world quaternion of the encapsulated
     * geometry.  This is calcualted by multiplying the
     * transform matrix of the parent body with the offset
     * transform of the encapsulated geom.  To get the
     * offset of the encapsulated geom - call getQuaternion on
     * it.
     *
     * @return the world quaternion of the encapsulated
     * geometry
     *
     * @throws IllegalOdejavaOperation if this GeomTransform doesn't
     *         have a parent body or doesn't have an encapsulated Geom
     *
     * @see updateCachedTransform
     */
    public Quaternion getQuaternion(Quaternion result) {
        updateCachedTransform();

        cachedBodyTransform.getRotation(result);
        return result;
    }
      
    /**
     * Called by the transform getters.  Gets the
     * transform of the parent Body and
     * multiplies it by the transform of the encapsulated
     * Geom.  The result is stored in the field
     * cachedBodyTransform.
     *
     * @throws IllegalOdejavaOperation if this GeomTransform doesn't
     *         have a parent body or doesn't have an encapsulated Geom

     */
    protected void updateCachedTransform() {

        if (this.getBody() == null) {
            throw new IllegalOdejavaOperation("This GeomTransform must be added to a body before its transform getters can be called.");
        }
        if (encapsulatedGeom == null) {
            throw new IllegalOdejavaOperation("This GeomTransform must encapsulate a Geom before its transform getters can be called.");
        }

        cachedBodyTransform.loadIdentity();
        cachedTmpTransform.loadIdentity();

        cachedBodyTransform.setRotationQuaternion(this.getBody().getQuaternion());
        cachedBodyTransform.setTranslation(this.getBody().getPosition());


        cachedTmpTransform.setRotationQuaternion(encapsulatedGeom.getQuaternion());
        cachedTmpTransform.setTranslation(encapsulatedGeom.getPosition());

        //cachedBodyTransform.mul(cachedTmpTransform);

    }
}


package com.jme.math;

import com.jme.scene.Spatial;

/**
 * Started Date: Jul 16, 2004


 * Same as TransformMatrix, but stores rotations as quats, not Matrix3f.  This is faster for interpolation, but slower
 * than a matrix using Matrix3f for rotation when doing point translation.
 * @author Jack Lindamood
 */
public class TransformMatrixQuat {

    private Quaternion rot=new Quaternion();
    private Vector3f translation=new Vector3f();
    private Vector3f scale=new Vector3f(1,1,1);

    /**
     * Sets this rotation to the given Quaternion value.
     * @param rot The new rotation for this matrix.
     */
    public void setRotationQuaternion(Quaternion rot) {
        this.rot.set(rot);
    }

    /**
     * Sets this translation to the given value.
     * @param trans The new translation for this matrix.
     */
    public void setTranslation(Vector3f trans) {
        this.translation.set(trans);
    }

    /**
     * Sets this scale to the given value.
     * @param scale The new scale for this matrix.
     */
    public void setScale(Vector3f scale) {
        this.scale.set(scale);
    }

    /**
     * Stores this translation value into the given vector3f.  If trans is null, a new vector3f is created to
     * hold the value.  The value, once stored, is returned.
     * @param trans The store location for this matrix's translation.
     * @return The value of this matrix's translation.
     */
    public Vector3f getTranslation(Vector3f trans) {
        if (trans==null) trans=new Vector3f();
        trans.set(this.translation);
        return trans;
    }

    /**
     * Stores this rotation value into the given Quaternion.  If quat is null, a new Quaternion is created to
     * hold the value.  The value, once stored, is returned.
     * @param quat The store location for this matrix's rotation.
     * @return The value of this matrix's rotation.
     */
    public Quaternion getRotation(Quaternion quat) {
        if (quat==null) quat=new Quaternion();
        quat.set(rot);
        return quat;
    }

    /**
     * Stores this scale value into the given vector3f.  If scale is null, a new vector3f is created to
     * hold the value.  The value, once stored, is returned.
     * @param scale The store location for this matrix's scale.
     * @return The value of this matrix's scale.
     */
    public Vector3f getScale(Vector3f scale) {
        if (scale==null) scale=new Vector3f();
        scale.set(this.scale);
        return scale;
    }

    /**
     * Sets this matrix to the interpolation between the first matrix and the second by delta amount.
     * @param t1 The begining transform.
     * @param t2 The ending transform.
     * @param delta An amount between 0 and 1 representing how far to interpolate from t1 to t2.
     */
    public void interpolateTransforms(TransformMatrixQuat t1, TransformMatrixQuat t2, float delta) {
        this.rot.slerp(t1.rot,t2.rot,delta);
        this.translation.interpolate(t1.translation,t2.translation,delta);
        this.scale.interpolate(t1.scale,t2.scale,delta);
    }

    /**
     * Changes the values of this matrix acording to it's parent.  Very similar to the concept of Node/Spatial transforms.
     * @param parent The parent matrix.
     * @return This matrix, after combining.
     */
    public TransformMatrixQuat combineWithParent(TransformMatrixQuat parent) {
        scale.multLocal(parent.scale);
        rot.multLocal(parent.rot);
        parent
            .rot
            .multLocal(translation)
            .multLocal(parent.scale)
            .addLocal(parent.translation);
        return this;
    }

    /**
     * Applies the values of this matrix to the given Spatial.
     * @param spatial The spatial to be affected by this matrix.
     */
    public void applyToSpatial(Spatial spatial) {
        spatial.getLocalScale().set(scale);
        spatial.getLocalRotation().set(rot);
        spatial.getLocalTranslation().set(translation);
    }

    /**
     * Sets this matrix's translation to the given x,y,z values.
     * @param x This matrix's new x translation.
     * @param y This matrix's new y translation.
     * @param z This matrix's new z translation.
     */
    public void setTranslation(float x,float y, float z) {
        translation.set(x,y,z);
    }

    /**
     * Sets this matrix's scale to the given x,y,z values.
     * @param x This matrix's new x scale.
     * @param y This matrix's new y scale.
     * @param z This matrix's new z scale.
     */     public void setScale(float x, float y, float z) {
        scale.set(x,y,z);
    }

    /**
     * Loads the identity.  Equal to translation=1,1,1 scale=0,0,0 rot=0,0,0,1.
     */
    public void loadIdentity() {
        translation.set(0,0,0);
        scale.set(1,1,1);
        rot.set(0,0,0,1);
    }

    /**
     * Sets this matrix to be equal to the given matrix.
     * @param matrixQuat The matrix to be equal to.
     */
    public void set(TransformMatrixQuat matrixQuat) {
        this.translation.set(matrixQuat.translation);
        this.rot.set(matrixQuat.rot);
        this.scale.set(matrixQuat.scale);
    }
}


And thats it. Please note that the above TransformMatrixQuat is under the BSD license copyrighted jME.

Heh, this works fine for jme-physics, im not sure about you kerazze Xith dudes.

[quote]Right, well, i dont multiply em! lol, and it works:


        cachedBodyTransform.loadIdentity();
        cachedTmpTransform.loadIdentity();

        cachedBodyTransform.setRotationQuaternion(this.getBody().getQuaternion());
        cachedBodyTransform.setTranslation(this.getBody().getPosition());


        cachedTmpTransform.setRotationQuaternion(encapsulatedGeom.getQuaternion());
        cachedTmpTransform.setTranslation(encapsulatedGeom.getPosition());

        //cachedBodyTransform.mul(cachedTmpTransform);

    }
}

[/quote]
Yeah but you don’t apply cachedTmpTransform to cachedBodyTransform either. Or have I missed something ???

i know i dont. But it works fine somehow…

If you dont believe me, pull up the jme-physics (http://jme-physics.sourceforge.net) and run CompoundObjectTest…

DP

Hi I looked at the code from CompoundedObjectTest, but it doesn’t use GeomTransforms in anyway. Does jMe use this physics stuff indirecly - I mean a user doesn’t call it directly ?
Can it then be that the GeomTransform (at least the one you printed) is not anymore used, so errors don’t show up there?

Ps: You know I’m very sceptical, because when looking at the code there is no way it could work:
You set a TransformMatrixQuat, you don’t use - what’s this for - it can’t have any use. You don’t even use it in any other function there. I checked it by searching after “cachedTmpTransform”

Yes, it does use it, but as you said, indirectly.

Look at com.jmex.physics.PhysicsEntityFactory.createNode()

We definetly use GeomTransform. Oh yer, did you run the test? Does it work?

PS. I agree about the cacheTmp thing, it can be removed from our source.

PS2. I am really really crap at maths, so i really can’t tell you why this works, all i can say that it does.

DP

I looked at it, but PhysicsEntityFactory isn’t used by the test. Is there maybe a Class extending PhysicsEntityFactory that’s used ?

No I havent yet, because I don’t really want to install jMe - I’m happy with xith :).
Could you perhaps give me a JavaWebStart-Link, that shows this demo ?
Uhh and anothing thing: I’ve got an idea how we can test if it’s used. You just simply have to put a System.out.println(“blahblah”) in the code. If it says “blahblah”, we know it uses it.

Arne

Arne, our approach to physics is very different from Odejava’s. The test only creates a PhysicsWorld, and DynamicPhysicsObjects (and a floor which is a staticPhysicsObject). Tee rest happens behind the scenes without you knowing anything. So PhysicsEntityFactory is actually instantiated and created by PhysicsWorld.

Anyway, it is definetly created. And as for your webstart thing, i’l get on it.

DP

http://www.myjavaserver.com/~digiwired/physics.jnlp

There you go. Enjoy the physics show!

DP

Thanks
It works fine :slight_smile:

Makes the mystery only bigger :frowning:


package org.odejava;

import org.odejava.ode.*;

import com.jme.math.Quaternion;
import com.jme.math.TransformMatrixQuat;
import com.jme.math.Vector3f;

/**
 * <p>The GeomTransform is a geom which encapsulates a Geom, allowing the encapsulated Geom to be transformed
 * relitive to it.  The GeomTransform can then be added to a Body.</p>
 *
 * <p>Please refer to the <a href="http://opende.sourceforge.net/ode-latest-userguide.html#sec_10_7_7">ODE Documentation</a>
 * for more information.  An example in ODE code exists <a href="http://q12.org/pipermail/ode/2002-July/005462.html"> here</a></p>
 *
 * <p>It is a non-placable geometry, therefor the transform setters
 * must not be called.  However the transform getters may be called.  As it is
 * non-placable, the getters don't simply delegate the call to ODE.  Rather they
 * return the world transform of the encapsulated Geom (that is, the transform
 * of the parent body multiplied by that of the encapsulated Geom).  Subsiquently,
 * you can bind GeomTransform objects to Display objects.</p>
 *
 * @author William Denniss
 */
public class GeomTransform extends PlaceableGeom {

    private PlaceableGeom encapsulatedGeom;

    private TransformMatrixQuat cachedBodyTransform = new TransformMatrixQuat();

    /**
     * Creates a GeomTransform with no name.
     *
     * @ode dCreateGeomTransform creates the object
     */
    public GeomTransform() {
        this("");
    }

    /**
     * Greats a GeomTransform with the given name
     *
     * @param name the name of this Geom
     *
     * @ode dCreateGeomTransform creates the object
     */
    public GeomTransform(String name) {
        super(name);

        spaceId = Ode.getPARENTSPACEID_ZERO();
        geomId = Ode.dCreateGeomTransform(spaceId);

        updateNativeAddr();

    }

    /**
     * Sets the encapsulated Geom.
     *
     * @param obj the geom this GeomTransform encapsulates.
     *          This Geom should not be added to any Space or associated
     *          with any Body.
     * @ode dGeomTransformSetGeom sets the encapsulated geom
     */
    public void setEncapsulatedGeom(PlaceableGeom obj) {
        if (encapsulatedGeom != null) {
            throw new IllegalOdejavaOperation("Attempt to assign a second Encapsulated geom.  GeomTransform can only have one encapsualted Geom.");
        }

        encapsulatedGeom = obj;
        Ode.dGeomTransformSetGeom(geomId, obj.getId());
        encapsulatedGeom.isEncapsulated = true;
    }

    /**
     * Removes the encapsulate Geom.
     *
     * @dGeomTransformSetGeom
     */
    public void removeEncapsulatedGeom() {
        Ode.dGeomTransformSetGeom(geomId, new SWIGTYPE_p_dGeomID(0, false));
        encapsulatedGeom.isEncapsulated = false;

        encapsulatedGeom = null;
    }

    /**
     * Returns the encapsulated geometry.
     *
     * @return the encapsulated geometry.
     */
    public PlaceableGeom getEncapsulatedGeom() {
        return encapsulatedGeom;

    }

    /**
     * Returns the world position of the encapsulated
     * geometry.  This is calcualted by multiplying the
     * transform matrix of the parent body with the offset
     * transform of the encapsulated geom.  To get the
     * offset of the encapsulated geom - call getPosition on
     * it.
     *
     * @return the world position of the encapsulated
     * geometry
     *
     * @throws IllegalOdejavaOperation if this GeomTransform doesn't
     *         have a parent body or doesn't have an encapsulated Geom
     *
     * @see updateCachedTransform
     */
    public Vector3f getPosition() {
        return getPosition(new Vector3f());
    }

    /**
     * Returns the world position of the encapsulated
     * geometry.  This is calcualted by multiplying the
     * transform matrix of the parent body with the offset
     * transform of the encapsulated geom.  To get the
     * offset of the encapsulated geom - call getPosition on
     * it.
     *
     * @param result Vector3f to write the result to
     *
     * @return the world position of the encapsulated
     * geometry
     *
     * @throws IllegalOdejavaOperation if this GeomTransform doesn't
     *         have a parent body or doesn't have an encapsulated Geom
     *
     * @see updateCachedTransform
     */
    public Vector3f getPosition(Vector3f result) {

        updateCachedTransform();

        cachedBodyTransform.getTranslation(result);

        return result;

    }

    /**
     * Returns the world quaternion of the encapsulated
     * geometry.  This is calcualted by multiplying the
     * transform matrix of the parent body with the offset
     * transform of the encapsulated geom.  To get the
     * offset of the encapsulated geom - call getQuaternion on
     * it.
     *
     * @param result Quat4f to write the result to
     *
     * @return the world quaternion of the encapsulated
     * geometry
     *
     * @throws IllegalOdejavaOperation if this GeomTransform doesn't
     *         have a parent body or doesn't have an encapsulated Geom
     *
     * @see updateCachedTransform
     */
    public Quaternion getQuaternion() {
        return getQuaternion(new Quaternion());
    }

    /**
     * Returns the world quaternion of the encapsulated
     * geometry.  This is calcualted by multiplying the
     * transform matrix of the parent body with the offset
     * transform of the encapsulated geom.  To get the
     * offset of the encapsulated geom - call getQuaternion on
     * it.
     *
     * @return the world quaternion of the encapsulated
     * geometry
     *
     * @throws IllegalOdejavaOperation if this GeomTransform doesn't
     *         have a parent body or doesn't have an encapsulated Geom
     *
     * @see updateCachedTransform
     */
    public Quaternion getQuaternion(Quaternion result) {
        updateCachedTransform();

        cachedBodyTransform.getRotation(result);
        return result;
    }
      
    /**
     * Called by the transform getters.  Gets the
     * transform of the parent Body and
     * multiplies it by the transform of the encapsulated
     * Geom.  The result is stored in the field
     * cachedBodyTransform.
     *
     * @throws IllegalOdejavaOperation if this GeomTransform doesn't
     *         have a parent body or doesn't have an encapsulated Geom

     */
    protected void updateCachedTransform() {

        if (this.getBody() == null) {
            throw new IllegalOdejavaOperation("This GeomTransform must be added to a body before its transform getters can be called.");
        }
        if (encapsulatedGeom == null) {
            throw new IllegalOdejavaOperation("This GeomTransform must encapsulate a Geom before its transform getters can be called.");
        }

        cachedBodyTransform.loadIdentity();

        cachedBodyTransform.setRotationQuaternion(this.getBody().getQuaternion());

    }
}

This also works.

PS. This is probably a workaround and not a solution. Tho i dont see why it isn’t a solution…

Anyways, it works, and thats what matters…for now

DP

Have you put a System.out.println(“blabla”) into the updateCachedTransform-Function? I really would like to know if it gets executed.

The Matrices in GeomTransform are used only for the convenience getter functions to get position and rotation. These getter functions are not called by Ode (I don’t use them in my applications either), so this likely explains why the jME demo works.

As for a workaround, I suggest someone implementing a matrixToQuat method in GeomTransform until Matrix4f is fixed. The matrix to quat formula is:

qx = (m21 - m12)/(4.0fqw)
qy = (m02 - m20)/(4.0f
qw)
qz = (m10 - m01)/(4.0f*qw)
qw = sqrt(1.0f + m00 + m11 + m22)/2.0f

just done your sysout thing. And no, it doesn’t get executed.

DP

I implemented your formular, but it doesn’t work. I’ve got a body transform of 0,0,0,1
and a encapsulated transform of 0,0.707f,0.707f,0
and the result, the transform of the GeomTransform, is NaN,NaN,NaN,NaN like the vecmath result.
but (0,0,0,1).mul(0,0.707f,0.707f,0) = (0,0.707f,0.707f,0) , because (0,0,0,1) is the Identity rotation.

I wrote myself some code without the Matrix, it brings good results as the output, but in RunDemo - I’m testing my stuff with RunDemo, because I hope there are no errors there. But there still seems to be a problem with body rotations :frowning:
When the body-rotation is the Identity rotation it works fine - not like the other code :slight_smile:


        this.getBody().getQuaternion(tmpQuat);
        this.getEncapsulatedGeom().getPosition(position);
        rotation.set(position.x,position.y,position.z,0);
        rotation.mul(tmpQuat);
        position.set(rotation.x,rotation.y,rotation.z);
        this.getBody().getPosition(tmpPos);
        position.add(tmpPos);
        
        this.getEncapsulatedGeom().getQuaternion(rotation);
        rotation.mul(tmpQuat);

Edit: position and rotation are the output values - the ones you get with getQuaternion and getPosition