Hithis 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:

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. 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

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?

/** * <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 */publicclassGeomTransformextendsPlaceableGeom {

/** * 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 */publicvoidsetEncapsulatedGeom(PlaceableGeomobj) {if (encapsulatedGeom != null) {thrownewIllegalOdejavaOperation("Attempt to assign a second Encapsulated geom. GeomTransform can only have one encapsualted Geom."); }

/** * 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 */publicVector3fgetPosition() {returngetPosition(newVector3f()); }

/** * 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 */publicVector3fgetPosition(Vector3fresult) {

updateCachedTransform();

cachedBodyTransform.getTranslation(result);

returnresult;

}

/** * 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 */publicQuaterniongetQuaternion() {returngetQuaternion(newQuaternion()); }

/** * 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 */publicQuaterniongetQuaternion(Quaternionresult) {updateCachedTransform();

cachedBodyTransform.getRotation(result);returnresult; }

/** * 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

*/protectedvoidupdateCachedTransform() {

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

/** * Started Date: Jul 16, 2004<br><br> * 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 */publicclassTransformMatrixQuat {

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

/** * Sets this translation to the given value. * @param trans The new translation for this matrix. */publicvoidsetTranslation(Vector3ftrans) {this.translation.set(trans); }

/** * Sets this scale to the given value. * @param scale The new scale for this matrix. */publicvoidsetScale(Vector3fscale) {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. */publicVector3fgetTranslation(Vector3ftrans) {if (trans==null) trans=newVector3f();trans.set(this.translation);returntrans; }

/** * 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. */publicQuaterniongetRotation(Quaternionquat) {if (quat==null) quat=newQuaternion();quat.set(rot);returnquat; }

/** * 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. */publicVector3fgetScale(Vector3fscale) {if (scale==null) scale=newVector3f();scale.set(this.scale);returnscale; }

/** * 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. */publicvoidinterpolateTransforms(TransformMatrixQuatt1, TransformMatrixQuatt2, floatdelta) {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. */publicTransformMatrixQuatcombineWithParent(TransformMatrixQuatparent) {scale.multLocal(parent.scale);rot.multLocal(parent.rot);parent .rot .multLocal(translation) .multLocal(parent.scale) .addLocal(parent.translation);returnthis; }

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

/** * Sets this matrix to be equal to the given matrix. * @param matrixQuat The matrix to be equal to. */publicvoidset(TransformMatrixQuatmatrixQuat) {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.

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"

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

Quote

Oh yer, did you run the test? Does it work?

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, 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.

/** * <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 */publicclassGeomTransformextendsPlaceableGeom {

/** * 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 */publicvoidsetEncapsulatedGeom(PlaceableGeomobj) {if (encapsulatedGeom != null) {thrownewIllegalOdejavaOperation("Attempt to assign a second Encapsulated geom. GeomTransform can only have one encapsualted Geom."); }

/** * 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 */publicVector3fgetPosition() {returngetPosition(newVector3f()); }

/** * 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 */publicVector3fgetPosition(Vector3fresult) {

updateCachedTransform();

cachedBodyTransform.getTranslation(result);

returnresult;

}

/** * 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 */publicQuaterniongetQuaternion() {returngetQuaternion(newQuaternion()); }

/** * 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 */publicQuaterniongetQuaternion(Quaternionresult) {updateCachedTransform();

cachedBodyTransform.getRotation(result);returnresult; }

/** * 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

*/protectedvoidupdateCachedTransform() {

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

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:

I implemented your formular, but it doesn't work. I've got a body transform of 0,0,0,1and a encapsulated transform of 0,0.707f,0.707f,0and 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 When the body-rotation is the Identity rotation it works fine - not like the other code

Hmm... sounds like the Matrix4f is being populated with NaNS. The Matrix4f bug may be in the set(Quat4f q) method (instead or in addition to the bug in the get(Quat4f q) method). You could try implementing set(Quat4f q). The upper 3x3 is:

Ohh - I forgot to tell you:The matrix created out of the rotation and translation from my function is the same as the one created by multiplying the two matrices.

I don't know if RunDemo or OdejavaToXith is working correctly, because OdejavaToXith is doing something special with Geomtransforms I don't understand. It adds another TransformGroup there. But shouldn't GeomTransform automatically return the correct Position transformed by the body and the Geom?

And something else that I want to get sure of:(0, 0.707, 0.707, 0) is a valid Quat4f, isn't it? It get's normalized to (0, 0.70710677, 0.70710677, 0) I'm asking, because (0, 0.707107, 0.707107, 0) gets also normalized to the same value, but it doesn't create the NaNs. It creates a Quat4f of (0,0,0, ...E-4)

I think this is totally strange espacially because the rotation-output does not show any signs, that there might be differences, but RunDemo shows different rotations.The positions are different, but RunDemo shows the same position.

My only conclusion is, that I've misunderstood something, or rundemo is not working correctly.

I think this is a good workaround for the bug , but I haven't been able to test it completely . I'd like to know if it also works for you.

Arne

PS: I have created a subclass of GeomTransform, to do this, because I wasn't able to compile the odejava src - something with the xode stuff sucked - It wasn't able to find some Classes there. It would be good if someone did the changes also to the GeomTransform directly.

Holy crap - it's no bug at all - at least it shows correctly, when Joints are used and the simulation proceeds - the code I wrote doesn't work there. So I'm going back to using the old GeomTransform - And with having one big mystery more ...

java-gaming.org is not responsible for the content posted by its members, including references to external websites,
and other references that may or may not have a relation with our primarily
gaming and game production oriented community.
inquiries and complaints can be sent via email to the info‑account of the
company managing the website of java‑gaming.org