Rotation Tutorial
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 ); } }