[Math] 3D Bezier Path orientation interpolation
by Phillip O'Shea · in Torque Game Engine Advanced · 03/30/2009 (3:35 am) · 8 replies
Ouch, nasty title!
I am working on a 3D Quadratic Bezier Path (think TGB's pathing method, only 3D) and all is working well, except the object's orientation.
I basically have no idea how to ensure that the object attached to the path is orientated correctly. Here is a snippet of the math:
Each node has its own transform, so at t = 0, the object should have the same transform of the source node, and at t = 1, it should be the same transform of the destination node. In between is the hard part!
I'd appreciate a bit of direction.
I am working on a 3D Quadratic Bezier Path (think TGB's pathing method, only 3D) and all is working well, except the object's orientation.
I basically have no idea how to ensure that the object attached to the path is orientated correctly. Here is a snippet of the math:
static Point3F gBezierAxis( 1.f, 0.f, 0.f );
...
void VPath::advanceBezierPath( const VPathObject &pPathObject )
{
// Nodes.
const VPathNode &srcNode = mNodeList[pPathObject.SourceNode];
const VPathNode &dstNode = mNodeList[pPathObject.DestinationNode];
// Positions.
const Point3F &pt0 = srcNode.Position;
const Point3F &pt3 = dstNode.Position;
// Fetch Node Rotation Matrices.
MatrixF mat0, mat1;
srcNode.Rotation.setMatrix( &mat0 );
dstNode.Rotation.setMatrix( &mat1 );
// Determine Tangent Axis.
Point3F pt1( gBezierAxis * srcNode.Weight );
Point3F pt2( -gBezierAxis * dstNode.Weight );
// Rotate Axis.
mat0.mulP( pt1 );
mat1.mulP( pt2 );
// Offset Points.
pt1 += pt0;
pt2 += pt3;
// Interp Times.
const F32 t = getMin( pPathObject.Interp, 1.f );
const F32 it = ( 1.f - t );
// Calculate New Position.
const Point3F newPosition = ( pt0 * it * it * it ) + ( 3 * pt1 * it * it * t ) + ( 3 * pt2 * it * t * t ) + ( pt3 * t * t * t );
// Apply Position.
pPathObject.Object->setPosition( newPosition );
}Each node has its own transform, so at t = 0, the object should have the same transform of the source node, and at t = 1, it should be the same transform of the destination node. In between is the hard part!
I'd appreciate a bit of direction.
About the author
Head of Violent Tulip, a small independent software development company working in Wollongong, Australia. Go to http://www.violent-tulip.com/ to see our latest offerings.
#2
03/30/2009 (4:07 am)
Thats what I was thinking, but the actual way of orientating it is where I get in trouble. The easiest thing is to just get the current position of the object before you apply it. Then the position delta is just new - old:void VPath::advanceBezierPath( const VPathObject &pPathObject )
{
...
// Grab Old Position.
const Point3F oldPosition = pPathObject.Object->getPosition();
// Calculate New Position.
const Point3F newPosition = ( pt0 * it * it * it ) + ( 3 * pt1 * it * it * t ) + ( 3 * pt2 * it * t * t ) + ( pt3 * t * t * t );
// Determine Change.
Point3F deltaPosition = ( newPosition - oldPosition );
// Apply Position.
pPathObject.Object->setPosition( newPosition );
}
#3
Now you should have your axis-angle orientation. I'm pretty sure Torque has something for setting orientations with quaternions, probably as part of the MatrixF class, but I haven't a clue what the method would be at the moment.
Hope it helps.
Cheers
03/30/2009 (4:45 am)
I'm not able to get into my code at the moment and I'm not sure if I'm remembering the exact functions/methods you'll need, but you can do something along these lines://get the normalized direction vector Point3F dirVector = newPosition - oldPosition; dirVector.normalize(); //starting direction vector of your node. I think this is default in Torque Point3F baseDir( 0, 1, 0 ); //axis is the cross product of the two vectors Point3F axis = mCross( baseDir, dirVector ); //angle is the dot product of the two vectors F32 angle = mDot( baseDir, dirVector );
Now you should have your axis-angle orientation. I'm pretty sure Torque has something for setting orientations with quaternions, probably as part of the MatrixF class, but I haven't a clue what the method would be at the moment.
Hope it helps.
Cheers
#4
03/30/2009 (7:45 am)
Are srcNode.Rotation and dstNode.Rotation quaternions? If so, you can interpolate them exactly as you do with the position, then normalize the quaternion.
#5
Gerald, thanks for that, I think it is close but maybe I am doing something wrong.
I ended up adding a QuatF storing the rotation of the path object and every time he moves over a node it will reset to the source node's rotation.
I then applied your code:
When I look at the angle that is being produced, it is very small (0x-05) and doesn't do anything to the rotation of the shape.
03/30/2009 (1:59 pm)
Manoel, I can't do a linear interpolation between the two nodes because Beziers don't always have linear paths between nodes.Gerald, thanks for that, I think it is close but maybe I am doing something wrong.
I ended up adding a QuatF storing the rotation of the path object and every time he moves over a node it will reset to the source node's rotation.
... // Reset Rotation. pathObject.Rotation = mNodeList[pathObject.SourceNode].Rotation; // Reset Transform. MatrixF mat; pathObject.Rotation.setMatrix( &mat ); mat.setPosition( pathObject.Object->getPosition() ); pathObject.Object->setTransform( mat ); ...
I then applied your code:
void VPath::advanceBezierPath( VPathObject &pPathObject )
{
...
// get the normalized direction vector
Point3F dirVector = newPosition - oldPosition;
dirVector.normalize();
// Z-Axis is UP
Point3F baseDir( 0.f, 0.f, 1.f );
// axis is the cross product of the two vectors
Point3F axis = mCross( baseDir, dirVector );
// angle is the dot product of the two vectors
F32 angle = mDot( baseDir, dirVector );
QuatF rot( axis, angle );
// Update Rotation.
pPathObject.Rotation *= rot;
// Set Transform.
MatrixF mat;
pPathObject.Rotation.setMatrix( &mat );
mat.setPosition( newPosition );
// Apply.
pPathObject.Object->setTransform( mat );
}When I look at the angle that is being produced, it is very small (0x-05) and doesn't do anything to the rotation of the shape.
#6
I'm using this code for aiming my ai turrets.
03/30/2009 (3:49 pm)
you can calc with atan2 tooI'm using this code for aiming my ai turrets.
dir = targerpos-currentpos; d = sqrt( dir.x*dir.x + dir.y*dir.y ); x_angle = atan2( dir.z, dir.x ); z_angle = atan2( dir.z, d ); rot_mtx = MatrixCreateFromEuler( x_angle, 0, -z_angle );
#7
03/31/2009 (7:14 am)
Phillip, I meant doing the bezier interpolation on the rotations (since they are actually Point4F vectors), but thinking again that might not give you the results you want (since it'll depends on the rotation of the path nodes, and they aren't guaranteed to orientate along the path).
#8
04/06/2009 (6:17 pm)
I ended up using a "lookAt" method, its pretty simple, yet effective:// Z-Axis. VectorF zVec = ( newPosition - oldPosition ); zVec.normalize(); // X-Axis. VectorF xVec = mCross( Point3F( 0.f, 0.f, 1.f ), zVec ); xVec.normalize(); // Y-Axis. VectorF yVec = mCross( zVec, xVec ); yVec.normalize(); // Setup Object Transform. MatrixF mat( true ); mat.setColumn( 0, xVec ); mat.setColumn( 1, yVec ); mat.setColumn( 2, zVec ); mat.setColumn( 3, newPosition );
Torque Owner Gerald Fishel
Development Ninja