Transform

Started by
10 comments, last by NEXUSKill 11 years, 9 months ago
Hi guys,could you give me a hand ,now I trapped in the XNA transform problem,I write a simply demo about a helicopter fly with a propeller and rotor,now the problem is I'm going to change the Y value of the helicopter also change the propeller and rotor,after I done it,the strage thing appear rotor and propeller didn't fowllow the direction
of helicoper,even the rotor is far away from helicopter!How could I do?
Advertisement
Try this (pseudo code, trick works for OpenGL and DX8 as i remember it, probably does for XNA too)


DrawHelicopter()
{
DrawHull
DrawPropeller
Transform(Rotate)
PushMatrix
DrawRotator
PopMatrix
}

Render()
{
Transform(Translate)
PushMatrix
DrawHelicopter()
PopMatrix
}
I can't understand popMatrix and pushMatrix,but now I can show my code,could you check the error for me

private void DrawRotor()
{
Vector3 RotorPosition = new Vector3(airplanePosition.X +0.7f - 0.2f + 0.25f, airplanePosition.Y - 0.3f , airplanePosition.Z + 0.4f); ;
Matrix RotorTranslation, world, scale, rotationZ, OrbitTranslation, OrbitRotateY;
scale = Matrix.CreateScale(0.3f, 0.7f, 0.0f);
OrbitRotateY = Matrix.CreateRotationY(RotationAngle() - 0.04f);
rotationZ = Matrix.CreateRotationZ(propellerSpin);
OrbitTranslation = Matrix.CreateTranslation(1.04f , 0.0f, 0.2f);
RotorTranslation = Matrix.CreateTranslation(RotorPosition);
world = scale * rotationZ * OrbitTranslation * OrbitRotateY * RotorTranslation;
// world = scale * rotationZ * RotorTranslation;
// 4: set shader parameters
positionColorEffectWVP.SetValue(world * cam.viewMatrix
* cam.projectionMatrix);

// 5: draw object - primitive type, vertices, # of primitives
PositionColorShader(PrimitiveType.TriangleStrip, propellerVertices, 2);


RotorPosition = new Vector3(airplanePosition.X + 0.7f - 0.2f + 0.22f, airplanePosition.Y + 0.3f, airplanePosition.Z);
RotorTranslation = Matrix.CreateTranslation(RotorPosition);
OrbitTranslation = Matrix.CreateTranslation(1.17f, 0.0f, 0.0f);
OrbitRotateY = Matrix.CreateRotationY(RotationAngle() + 0.04f);
world = scale * rotationZ * OrbitTranslation * OrbitRotateY * RotorTranslation;
// world = scale * rotationZ * OrbitRotateY * RotorTranslation;
// 4: set shader parameters
positionColorEffectWVP.SetValue(world * cam.viewMatrix
* cam.projectionMatrix);

// 5: draw object - primitive type, vertices, # of primitives
PositionColorShader(PrimitiveType.TriangleStrip, propellerVertices, 2);
}
rotor sometime sideways from airplane.

PushMatrix pushes the transformation stack, telling that all the following transformations will happen together, and not affect the previously transformed vertices.
PopMatrix pops the stack. Back to the level above. Or below, depending on how you see it. Towards the root level.
still doesn't make sence,I just think the peoblem is within that function show to you,you should check that code for the result.

Hi guys,I think you shoud consider the code I was telling you,because the rotor and propeller always sideway control by this section of code.
I'm not experienced with XNA. Can you point out the places in the code where you actually perform draw calls?

positionColorEffectWVP.SetValue(world * cam.viewMatrix
* cam.projectionMatrix);

// 5: draw object - primitive type, vertices, # of primitives
PositionColorShader(PrimitiveType.TriangleStrip, propellerVertices, 2);
two calls darw the primitive




So one of your problems is that rotor position is a direct offset from the plane.

Imagine your helicopter has three transform matrices: Scale, Rotate (for what direction it's facing), Translate (from the origin)
Imagine your rotor has two transform matrices: Scale, Rotate (for the degrees it's spun), Translate (to translate it from the helicopter body)

So your final transform matrix for the helicopter is: Helicopter.Transform = Helicopter.Scale * Helicopter.Rotate * Helicopter.Translate
But for the rotor, you have to apply the helicopter's matrices as well so that its offset from the helicopter properly: Rotor.Transform = Rotor.Scale * Rotor.Rotate * Rotor.Translate * Helicopter.Rotate + Matrix.CreateTranslation(Vector3.Transform(Vector3.Zero, Helicopter.Transform))
You multiply the rotor's transform matrix by Helicopter.Rotate so that the rotor will be angled properly against the fin rather than, say, be perpendicular to the fin since the user rotated the helicopter 90 degrees to the right. You then basically add the helicopter's position to the rotor's position so that the rotor isn't floating at (0,0) when the helicopter is at (30, 5) and rotated 10 degrees in the yaw.

Maybe this will fix your code? It's just off the top of my head.
In addition to GGulati's explanation, you can represent (logically, or in your code) the helicopter cockpit and its rotor(s) as a transform hierarchy of the transforms he describes. Your rotors will be spinning in a certain direction, and at a certain speed that should not affect the helicopter cockpit at all. However, the rotor positions in world space have to be rigid to the cockpit, e.g. the helicopter goes up, the rotors spin but are translated up accordingly. Each piece, therefore, maintains its own "local" transform - the cockpit probably is centered at the origin, but the main rotor is translated some units up the y-axis and so on. Then when you're updating the scene, you combine these local transforms into a final world transform for each piece of geometry.

For example, from my own code organization:

1. Each geometry has a local/world Transform, where a Transform is comprised of a Vector3 for Scale, a Quaternion for Rotation, and a Vector3 for Translation. You can of course use a rotation matrix rather than a quaternion, or keep three matrices for each component - but I tend to shy away from matrices.

2. The scene is organized with parent-child relationships. So the cockpit would be a parent, and the two rotors would be children (they have a reference to their parent and vice versa).

3. Every Update() call on the scene, each child combines their local transform with their parent's world transform:

Cockpit - No parent, simply uses its local Transform as its world transform.

Rotors - Parent is the cockpit, so their locals are combined with the cockpit's world transform.

The actual combination code looks something like this:



private Vector3 _scale;
private Quaterion _rotation;
private Vector3 _translation;
private bool _cacheRefresh;
private Matrix _cachedMatrix;

...

//Combines the components into a matrix
public Matrix Matrix {
get {
if(_cacheRefresh) {
Matrix scaleM;
Matrix rotationM;
Matrix translationM;

Matrix.FromScale(ref _scale, out scaleM);
Matrix.FromQuaternion(ref _rotation, out rotationM);
Matrix.FromTranslation(ref _translation, out translationM);

_cachedMatrix = scaleM * rotationM * translationM;
_cacheRefresh = false;
}

return _cachedMatrix;
}
}

public void CombineWithParent(Transform parent) {
//Multiply scaling
Vector3.Multiply(ref parent._scale, ref _scale, out _scale);

//Multiply rotation
Quaternion.Multiply(ref parent._rotation, ref _rotation, out _rotation);

//Combine translation
Vector3.Multiply(ref _translation, ref parent._scale, out _translation);
Vector3.Transform(ref _translation, ref parent._rotation, out _translation);
Vector3.Add(ref _translation, ref parent._translation, out _translation);

_cacheRefresh = true;
}


The (world) transform class I use has a matrix property which would then be used in with your view/projection matrices when setting the world-view-projection to the effect you're using. I cache that matrix too, so it's only computed once unless if any of the individual components change. Likewise, my scene management only updates world transforms if a scene object's transform has changed to reduce redundant computations. For example, the cockpit's transform may not change, but the rotor's rotation will, so only the world matrix is recomputed for the rotor and not for the cockpit. If the cockpit's transform changed, both would need to be updated. (Basically update any scene object, and any other objects it would influence - so children).

I mention this because your DrawRotor() could be made a lot simpler where each geometry has its own transforms, that get compiled to a world matrix in some general function like I posted above, making it easy to add new objects to Render, since you may only need to change a few transform properties without having to re-write the same sort of code. Also, you can reduce computations for parts of your models that don't actually move in a frame.

This topic is closed to new replies.

Advertisement