Rotation Tutorial

From Valve Developer Community
Revision as of 15:37, 27 July 2006 by Jay Stelly (talk | contribs)
Jump to navigation Jump to search

posted on hlcoders:

I'm simply trying to rotate the angles of an entity in relation to its position. Changing its angles.y alone for example is not what I need. If the entity is facing down, i need to change the angles based on its current angles. If it was facing down, i'd probably need to change x, but if it was in between, some of y and some of x would need to be changed, etc.. So I'm thinking a function similar (if not) AngleVectors must be used, but I'm unsure how..




This is just a simple transform (a rotation) concatenated with the current transform. Intuitively, when you do something like: pEntity->m_angRotation.y += 1;

it is effectively doing the same thing, but there is existing machinery and convention that makes incremental rotations simpler as long as you do them in the fixed space/order constraints that QAngles provide. For anything else you need to understand the underlying general principle.

The code in vphysics is not based on QAngles so that eliminates some of the confusion there. Here's a quick math lesson:

Here's some example code that may be useful (it does what you guys are asking two different ways). It's a modified version of NDebugOverlay::EntityBounds() that you can paste in to debugoverlay_shared.cpp and build/modify in your mod to help understand. The comments should help you understand the operators a bit - at least at the "black-box" level. Anyway, QAngles / matrix3x4_t / AxisAngle / Quaternion / VMatrix can all hold orientations and be converted into each other (so they are basically equivalent as long as we're keeping things simple). The matrix types also contain position/translation, but any of the other types can be paired with a position vector to represent that kind of transform as well. Since the original question was about orientation I just wrote up a quick example of how to apply a simple rotation to an object. If you actually wanted to take the rotated QAngles out at the end and set that as the entity's rotation, it would only work if the entity was not in a hieararchy. If there's a parent transform involved, you'd need to factor that in as well (this example does not cover that case).


 
//-----------------------------------------------------------------------------
// Purpose: Draws a box around an entity
//-----------------------------------------------------------------------------

void NDebugOverlay::EntityBounds( const CBaseEntity *pEntity, int r, int g, int b, int a, float flDuration )
{
	const CCollisionProperty *pCollide = pEntity->CollisionProp();
	// draw the base OBB for the object (default color is orange)
	BoxAngles( pCollide->GetCollisionOrigin(), pCollide->OBBMins(), pCollide->OBBMaxs(), pCollide->GetCollisionAngles(), r, g, b, a, flDuration );

	// this is the axis of rotation in world space
	Vector rotationAxisWs(0,0,1);
	const float rotationAngle = gpGlobals->curtime*10; // 10 degrees per second animated rotation
	//const float rotationAngle = 45; // degrees, Source's convention is that positive rotation is counter-clockwise
	

	// example 1, apply the rotation in the local space of the entity

	// compute rotation axis in entity local space
	// compute the transform as a matrix so we can concatenate it with the entity's current transform
	Vector rotationAxisLs;
	// The matrix maps vectors from entity space to world space, since we have a world space 
	// vector that we want in entity space we use the inverse operator VectorIRotate instead of VectorRotate
	// Note, you could also invert the matrix and use VectorRotate instead
	VectorIRotate( rotationAxisWs, pEntity->EntityToWorldTransform(), rotationAxisLs );

	// build a transform that rotates around that axis in local space by the angle
	// if there were an AxisAngleMatrix() routine we could use that directly, but there isn't 
	// so convert to a quaternion first, then a matrix
	Quaternion q;

	// NOTE: assumes axis is a unit vector, non-unit vectors will bias the resulting rotation angle (but not the axis)
	AxisAngleQuaternion( rotationAxisLs, rotationAngle, q );
	
	// convert to a matrix
	matrix3x4_t xform;
	QuaternionMatrix( q, vec3_origin, xform );
	
	// apply the rotation to the entity input space (local)
	matrix3x4_t localToWorldMatrix;
	ConcatTransforms( pEntity->EntityToWorldTransform(), xform, localToWorldMatrix );

	// extract the compound rotation as a QAngle
	QAngle localAngles;
	MatrixAngles( localToWorldMatrix, localAngles );

	// draw the rotated box in blue
	BoxAngles( pCollide->GetCollisionOrigin(), pCollide->OBBMins(), pCollide->OBBMaxs(), localAngles, 0, 0, 255, a, flDuration );


	{

		// example 2, apply the rotation in world space directly

		// build a transform that rotates around that axis in world space by the angle
		// note: add ten degrees so the boxes are separately visible
		// compute the transform as a matrix so we can concatenate it with the entity's current transform
		Quaternion q;
		AxisAngleQuaternion( rotationAxisWs, rotationAngle+10, q );

		// convert to a matrix
		matrix3x4_t xform;
		QuaternionMatrix( q, vec3_origin, xform );

		// apply the rotation to the entity output space (world)
		matrix3x4_t localToWorldMatrix;
		ConcatTransforms( xform, pEntity->EntityToWorldTransform(), localToWorldMatrix );

		// extract the compound rotation as a QAngle
		QAngle localAngles;
		MatrixAngles( localToWorldMatrix, localAngles );

		// draw the rotated + 10 box in yellow
		BoxAngles( pCollide->GetCollisionOrigin(), pCollide->OBBMins(), pCollide->OBBMaxs(), localAngles, 255, 255, 0, a, flDuration );
	}
}