Question about Axle vector for Vehicles and Wheels

I’m a little confused about the Axle vector in the addWheel method. It looks like my tires rotate the correct way when I have the vector set to point to the left when looking in the direction of forward travel of the vehicle, but I thought I read something that indicated that it should be the vector that points to the right.



When I load a geometry from a j3o file for the wheel, do I set the Axle vector to be the direction pointing straight out to the side of the tire to the right or left?



Should I use the vector local to the geometry or the world (for instance if I use the same tire geometry in the file for both the right and left side, do I need to apply a local rotation to the geometry and then use a different Axle vector for each side)?



[EDIT] In the j3o file, we have the origin of the geometry located at the connection point, not the center of the tire. When I change the direction of the Axle vector, it internally shifts the location of the geometry by the width of the tire. I’m not completely sure if this is due to a rotation of the geometry that gets introduced when I have the wrong Axle direction or not, but it looks like it is. Does the addWheel rotate the tire internally to line up the geometry axis to the Axle direction by applying a local rotation to the tire?

Check the bullet documentation (I know, good joke) or just experiment. I was able to get out anything I wanted from bullets raycast vehicles. The wheel location is also bullets decision. The wheels are rotated by the vehicle, yes. Add them to nodes if they don’t move right.

Please keep in mind when you read this that I’m not an expect in rotation matrix, but I think I’ve narrowed my issue down. I’m not sure there is actually and issue with Bullet or JME at this point, but I could not figure out why my graphics were not moving correctly. I figured I was doing something wrong, but until I understood what bullet is doing, I didn’t know why.



When we created our vehicle chasis in blender and exported it, the Y direction is up and the Z direction was pointing to the back of the chasis. This means that the X direction is pointing to the right when facing the forward travel direction of the vehicle. We also created the wheels in the same blender file and set the orgin of the wheel to the inside center of each wheel with the X, Y, and Z pointing in the same directions as the chasis. We did this so that when we load the j3o file, I could simply set the wheel connection points to the vector resulting from subtracting the world origin of the wheel from the world origin of the chasis for each wheel so that I did not have to have special tuning for each vehicle type we created. This is necessary because the physics collision shape does not have the same origin as the chasis geometry due to shifting the center of gravity to the middle of the chasis geometry (our geometry origin is not the center of the chasis geometry). This is different from the examples and tests in jme where the origin of the wheel is in the middle of the wheel geometry and the placement of the wheel is hardcoded in java. We also have non-symetrical wheel textures meaning that the wheel texture is different on the outside of the wheel vs the inside of the wheel.



When jme creates the vehicle, it sets the coordinate system of the vehicle to the following:

[java]setCoordinateSystem(vehicleId, 0, 1, 2);[/java]

which calls the jni code:

[java]

JNIEXPORT void JNICALL Java_com_jme3_bullet_objects_PhysicsVehicle_setCoordinateSystem

(JNIEnv env, jobject object, jlong vehicleId, jint right, jint up, jint forward) {

btRaycastVehicle
vehicle = reinterpret_cast<btRaycastVehicle*>(vehicleId);

if (vehicle == NULL) {

jclass newExc = env->FindClass(“java/lang/NullPointerException”);

env->ThrowNew(newExc, “The native object does not exist.”);

return;

}

vehicle->setCoordinateSystem(right, up, forward);

}

[/java]



Now when we add a wheel, the axle direction vector is set to (1,0,0) because wheel +X is pointing to the right of the chasis (bullets definition of the axle direction) and the wheel direction is set to (0,-1,0) to point down to the ground.



When wheels are added to bullet, it does the following:


  • coverts the local wheel vectors (relative to chasis) to world vectors using the chasis matrix

  • take the opposite of the world wheel direction (up) and cross it with the new world axle direction (right) to create a new world forward direction for the wheel

  • sets the wheel world transform by creating a new matrix from the wheel world right, up, forward vectors and rotates it by the steering angle and current rotation angle



[java]
btWheelInfo& btRaycastVehicle::addWheel( const btVector3& connectionPointCS, const btVector3& wheelDirectionCS0,const btVector3& wheelAxleCS, btScalar suspensionRestLength, btScalar wheelRadius,const btVehicleTuning& tuning, bool isFrontWheel)
{

btWheelInfoConstructionInfo ci;

ci.m_chassisConnectionCS = connectionPointCS;
ci.m_wheelDirectionCS = wheelDirectionCS0;
ci.m_wheelAxleCS = wheelAxleCS;
ci.m_suspensionRestLength = suspensionRestLength;
ci.m_wheelRadius = wheelRadius;
ci.m_suspensionStiffness = tuning.m_suspensionStiffness;
ci.m_wheelsDampingCompression = tuning.m_suspensionCompression;
ci.m_wheelsDampingRelaxation = tuning.m_suspensionDamping;
ci.m_frictionSlip = tuning.m_frictionSlip;
ci.m_bIsFrontWheel = isFrontWheel;
ci.m_maxSuspensionTravelCm = tuning.m_maxSuspensionTravelCm;
ci.m_maxSuspensionForce = tuning.m_maxSuspensionForce;

m_wheelInfo.push_back( btWheelInfo(ci));

btWheelInfo& wheel = m_wheelInfo[getNumWheels()-1];

<strong>
updateWheelTransformsWS( wheel , false );
updateWheelTransform(getNumWheels()-1,false);
</strong>
return wheel;
}
[/java]

[java]
void btRaycastVehicle::updateWheelTransformsWS(btWheelInfo& wheel , bool interpolatedTransform)
{
wheel.m_raycastInfo.m_isInContact = false;

btTransform chassisTrans = getChassisWorldTransform();
if (interpolatedTransform && (getRigidBody()->getMotionState()))
{
getRigidBody()->getMotionState()->getWorldTransform(chassisTrans);
}

wheel.m_raycastInfo.m_hardPointWS = chassisTrans( wheel.m_chassisConnectionPointCS );
wheel.m_raycastInfo.m_wheelDirectionWS = chassisTrans.getBasis() * wheel.m_wheelDirectionCS ;
wheel.m_raycastInfo.m_wheelAxleWS = chassisTrans.getBasis() * wheel.m_wheelAxleCS;
}
[/java]

[java]
void btRaycastVehicle::updateWheelTransform( int wheelIndex , bool interpolatedTransform)
{

btWheelInfo& wheel = m_wheelInfo[ wheelIndex ];
updateWheelTransformsWS(wheel,interpolatedTransform);
btVector3 up = -wheel.m_raycastInfo.m_wheelDirectionWS;
const btVector3& right = wheel.m_raycastInfo.m_wheelAxleWS;
btVector3 fwd = up.cross(right);
fwd = fwd.normalize();
// up = right.cross(fwd);
// up.normalize();

//rotate around steering over de wheelAxleWS
btScalar steering = wheel.m_steering;

btQuaternion steeringOrn(up,steering);//wheel.m_steering);
btMatrix3x3 steeringMat(steeringOrn);

btQuaternion rotatingOrn(right,-wheel.m_rotation);
btMatrix3x3 rotatingMat(rotatingOrn);

btMatrix3x3 basis2(
right[0],fwd[0],up[0],
right[1],fwd[1],up[1],
right[2],fwd[2],up[2]
);

wheel.m_worldTransform.setBasis(steeringMat * rotatingMat * basis2);
wheel.m_worldTransform.setOrigin(
wheel.m_raycastInfo.m_hardPointWS + wheel.m_raycastInfo.m_wheelDirectionWS * wheel.m_raycastInfo.m_suspensionLength
);
}
[/java]

In my case, since the vehicle has not be rotated at all yet, this creates a forward direction of the world -Z.

I believe this causes a different forward vector between the chasis and the wheel since bullet is not told the direction of the forward vector of the chasis, only the axis to use. I think this means that it assumes that the forward direction of the chasis is the + of the forward axis supplied by the setCoordinateSystem initially which would be +Z.

The results are that when a positive acceleration is applied, the car moves forwards, but the wheels rotate the wrong direction (ie wheel tread graphically rolls backward but the vehicle is going forward).

If the original axle direction (relative to chasis) is set to (-1,0,0) as in the examples, 3 things happen.
First, the wheels are internally rotated 180 deg by bullet since the wheel axle direction is the opposite of the chasis X direction and since the origin of the wheels are not in the center of the geometry, the wheels are shifted inward by the wheel width and the inside wheel texture is showing on the outside.
Second, a positive acceleration value moves the car backward instead of forward.
Third, the direction of the wheel tread now rotates the same direction as the chasis.

Since the jme examples always place the wheels in code, have the origin of the wheel in the center of the geometry, and have symetrical textures (in the ferari case), I don't think these issues are noticed.

So, we are going to create our vehicle chasis with +Z pointing in the forward direction of the vehicle, add the wheels with a axle direction of (-1,0,0 since the chasis +X is now pointing to the left). Since the axle direction is (-1,0,0) I think we will still have the issue of the wheel geometries being internally rotated by bullet. If we do, we'll have to deal with that by having the same texture on both sides of the wheel and shift the wheel by the width of the wheel in code.

Sorry for the long post, but this was bothering me and I thought it might be helpful to someone eventually. :) If anything looks completely wrong, please let me know.

Yes, make the vehicles point in the z direction or else make them move backwards, this is what I found out as well. Look at the vehicle test and maybe the vehicle editor in the SDK, both consider this and then go by the following logic and it seems to work out well… For the wheels the SDK editor uses nodes. The issue is that the wheels meshes would have to have their centers at 0/0/0 to work properly but when you export a car model the geometry location is 0/0/0 and the mesh location is off where the wheel is. Thats why they do the “big circle” then.

Thanks for the confirmation. I reviewed the wiki info on vehicles, the test class, and the native bullet code to understand what was going on. I just wanted to find out why. I think I do now.

Okay, seems I understood you, I got a bit lost in the long text and was all like “xyzzxyxz” :wink: Just having a separate wheel model at 0/0/0 should work if I got your right, shouldn’t it?

I believe that if the axle direction is in the opposite direction as the chasis, the wheel geometry/spatial will get rotated. With the spatial origin in the middle, the position of the wheel will be fine and if the texture on both sides of the wheel are the same, then I think it’s fine. If you have different textures on each side of the wheel, I think the wrong texture will end up showing from the outside of the vehicle. My designer is changing our vehicle to have +Z pointing in the travel direction now. After I get the new j3o, I’ll try it to make sure.



Also, I haven’t tried it yet, but if you have a node instead of a geometry for the wheel and then rotate the geometry 180deg to counteract the bullet rotation, I think you’ll be back to having the wheel rotating the wrong way, wouldn’t you?

@iwgeric said:
Also, I haven't tried it yet, but if you have a node instead of a geometry for the wheel and then rotate the geometry 180deg to counteract the bullet rotation, I think you'll be back to having the wheel rotating the wrong way, wouldn't you?

Uh no I don't think so, should work.

I should know tonight. I’ll let you know how it goes.

The final version is working. We ended up creating the vehicle chasis with the +Z pointing out the front of the vehicle. This forces the wheel axle to be set to (-1,0,0) to make it point to the right. As expected, setting the axle to (-1,0,0) causes the wheel to be rotated 180deg which shifts the wheel location by the wheel width because the origin of the wheel is on the side and the wrong texture is displayed.



Creating a node for the wheel and rotating the geometry by 180deg around Y corrects the bullet rotation and places the wheel in the right location. Also, this shows the tire tread rotating the correct direction when moving forward and backward.



So, the moral of the story is to:

  1. create the chasis with Y up, Z pointing forward, X pointing to the left
  2. set the wheel axle to (-1,0,0)
  3. create a wheel node and rotate the wheel geometry 180deg around Y to counteract the internal bullet rotation
  4. add the wheel node to the chasis instead of the geometry



    Of course, if your wheel geometry has it’s origin in the middle of the wheel and you have symetrical textures, none of this is necessary, but this process should work for all cases.



    @normen, thanks for the help understanding this.



    I can’t believe this is all necessary, but it’s the way bullet works, I guess. Hope this helps someone else.

Hi,



Hmm…I still don’t get it…

Are you talking about world or local coordinates in blender?



If world:

Z is always pointing upwards; if I export the body that way, the car will be standing on it’s tail in the vehicle editor.



If local:

I need to rotate the vertices in edit mode to modify the local axis but, unless I do Object/apply/rotation (which makes the local coordinates pointing upwards) I get the following result.

http://imgur.com/LBRFr



Assuming that I want to use the SDK without rotating the wheels individually from code, could somebody please point me the right direction ? :slight_smile:



Thanks a lot!

In JME Y is upwards, not Z.

When you export from blender, you want the vertices defined so that Y is up and Z is forward. When I did this last time, the object in Blender was drawn such that the Blender Z was UP and the Blender Y was FORWARD. Then, when we exported from Blender to OGRE, we set the XZ*Y option so that the Y and Z components of the vertices were switched.

Hmm, I think the blender importer must fix this for you when you convert from blender to j3o using the SDK tool as I don’t remember having to do any correction rotations… although it’s possible that I did and forgot about it as it’s a pretty trivial transform.

Maybe, I haven’t used the SDK to convert the blend file. We’ve always exported to OGRE and then converted that to j3o with the SDK.

Thanks guys! That works now!



I thougth that you are talking about blender’s coordinates not the JME’s coordinates.



I’ve just tested; importing directly from blender works as well.