One thing I've noticed lately in every FPS game I've played is that gibs don't roll and and tumble properly. Can it really be that hard to do? Well, yes it is, if you use an Euler angle orientation system (like Q3 does). After fooling around a bit I quickly discovered that it was nigh on impossible to rotate an object about an arbitrary axis using Q3's angles system. Just try it: give an object angular velocities on more than one axis and you'll end up with a weird spin/wiggle effect that looks worse than not rotating the object at all. I'd heard of quaternions before and knew that they were said to be good for describing arbitrary rotations, but that's all I knew. After a bit of googling I was ready to try my hand at applying quaternions to Q3. Here is the result.

Start off by adding the following quaternion manipulation functions to `q_math.c` (these functions were adapted from Nick Bobic's quaternion article on Gamasutra).
I know that modifying the `q_*` files is generally frowned upon but I think that in this case `q_math.c` is the logical place for these functions.

void AnglesToQuat (const vec3_t angles, vec4_t quat) { vec3_t a; float cr, cp, cy, sr, sp, sy, cpcy, spsy; a[PITCH] = (M_PI/360.0) * angles[PITCH]; a[YAW] = (M_PI/360.0) * angles[YAW]; a[ROLL] = (M_PI/360.0) * angles[ROLL]; cr = cos(a[ROLL]); cp = cos(a[PITCH]); cy = cos(a[YAW]); sr = sin(a[ROLL]); sp = sin(a[PITCH]); sy = sin(a[YAW]); cpcy = cp * cy; spsy = sp * sy; quat[0] = cr * cpcy + sr * spsy; // w quat[1] = sr * cpcy - cr * spsy; // x quat[2] = cr * sp * cy + sr * cp * sy; // y quat[3] = cr * cp * sy - sr * sp * cy; // z } void QuatToAxis(vec4_t q, vec3_t axis[3]) { float wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; x2 = q[1] + q[1]; y2 = q[2] + q[2]; z2 = q[3] + q[3]; xx = q[1] * x2; xy = q[1] * y2; xz = q[1] * z2; yy = q[2] * y2; yz = q[2] * z2; zz = q[3] * z2; wx = q[0] * x2; wy = q[0] * y2; wz = q[0] * z2; axis[0][0] = 1.0 - (yy + zz); axis[1][0] = xy - wz; axis[2][0] = xz + wy; axis[0][1] = xy + wz; axis[1][1] = 1.0 - (xx + zz); axis[2][1] = yz - wx; axis[0][2] = xz - wy; axis[1][2] = yz + wx; axis[2][2] = 1.0 - (xx + yy); } void QuatMul(const vec4_t q1, const vec4_t q2, vec4_t res) { float A, B, C, D, E, F, G, H; A = (q1[0] + q1[1])*(q2[0] + q2[1]); B = (q1[3] - q1[2])*(q2[2] - q2[3]); C = (q1[0] - q1[1])*(q2[2] + q2[3]); D = (q1[2] + q1[3])*(q2[0] - q2[1]); E = (q1[1] + q1[3])*(q2[1] + q2[2]); F = (q1[1] - q1[3])*(q2[1] - q2[2]); G = (q1[0] + q1[2])*(q2[0] - q2[3]); H = (q1[0] - q1[2])*(q2[0] + q2[3]); res[0] = B + (H - E - F + G)*0.5; res[1] = A - (E + F + G + H)*0.5; res[2] = C + (E - F + G - H)*0.5; res[3] = D + (E - F - G + H)*0.5; }

Don't forget to put their prototypes in `q_shared.h`:

void AnglesToQuat (const vec3_t angles, vec4_t quat); void QuatToAxis(vec4_t q, vec3_t m[3]); void QuatMul(const vec4_t q1, const vec4_t q2, vec4_t res);

Also put the following macro definition in `q_shared.h`:

#define QuatInit(w,x,y,z,q) ((q)[0]=(w),(q)[1]=(x),(q)[2]=(y),(q)[3]=(z));

We need a place to store the new quaternions so open `cg_local.h` and add the following to the definition of the `localEntity_t` struct:

vec4_t quatOrient; vec4_t quatRot; vec3_t rotAxis; float angVel;

Now open `cg_localents.c` and find the function `CG_ReflectVelocity()`.
Inside this function add the following code at the end:

if (le->leFlags & LEF_TUMBLE) { // collided with a surface so calculate the new rotation axis CrossProduct (trace->plane.normal, velocity, le->rotAxis); le->angVel = VectorNormalize (le->rotAxis) / le->radius; // save current orientation as a rotation from model's base orientation QuatMul (le->quatOrient, le->quatRot, le->quatRot); // reset the orientation QuatInit(1,0,0,0,le->quatOrient); }

Next, scroll down to the function `CG_AddFragment()` and find the code:

if ( le->leFlags & LEF_TUMBLE ) { vec3_t angles; BG_EvaluateTrajectory( &le->angles, cg.time, angles ); AnglesToAxis( angles, le->refEntity.axis ); }

And replace it with this:

if (le->leFlags & LEF_TUMBLE) { vec4_t qrot; // angular rotation for this frame float angle = le->angVel * (cg.time - le->angles.trTime) * 0.001/2; // create the rotation quaternion qrot[0] = cos (angle); // real part VectorScale (le->rotAxis, sin(angle), &qrot[1]);// imaginary part // create the new orientation QuatMul (le->quatOrient, qrot, le->quatOrient); // apply the combined previous rotations around other axes QuatMul (le->quatOrient, le->quatRot, qrot); // convert the orientation into the form the renderer wants QuatToAxis (qrot, le->refEntity.axis); le->angles.trTime = cg.time; }

Open `cg_effects.c` and find the function `CG_LaunchGib()`.
At the end of this function add the following code:

le->angles.trType = TR_LINEAR; le->angles.trTime = cg.time; le->angVel = 20 * crandom(); // random angular velocity le->rotAxis[0] = crandom(); // random axis of rotation le->rotAxis[1] = crandom(); le->rotAxis[2] = crandom(); VectorNormalize (le->rotAxis); // normalize the rotation axis QuatInit (1,0,0,0,le->quatRot); QuatInit (1,0,0,0,le->quatOrient); le->radius = 12; le->leFlags = LEF_TUMBLE;

Open `cg_weapons.c` and find the function `CG_MachineGunEjectBrass()`.
Find the lines:

le->angles.trDelta[0] = 2; le->angles.trDelta[1] = 1; le->angles.trDelta[2] = 0;

And replace them with:

AnglesToQuat (le->angles.trBase, le->quatOrient); le->angVel = 10 * random(); le->rotAxis[0] = crandom(); le->rotAxis[1] = crandom(); le->rotAxis[2] = crandom(); VectorNormalize (le->rotAxis); le->radius = 4; QuatInit (1,0,0,0,le->quatRot);

Scroll down to the function `CG_ShotgunEjectBrass()` and find the lines:

le->angles.trDelta[0] = 1; le->angles.trDelta[1] = 0.5; le->angles.trDelta[2] = 0;

And replace them with:

AnglesToQuat (le->angles.trBase, le->quatOrient); le->angVel = 10 * random(); le->rotAxis[0] = crandom(); le->rotAxis[1] = crandom(); le->rotAxis[2] = crandom(); VectorNormalize (le->rotAxis); le->radius = 6; QuatInit (1,0,0,0,le->quatRot);That's it. You're done. Hopefully I haven't left anything out. Compile the cgame module and try it out. You may want to play around with the

timescalecvar. Values between 0.5 and 0.7 worked good for me. You also might want to temporarily increase the damage in

void QuatToAngles (const vec4_t q, vec3_t a) { vec4_t q2; q2[0] = q[0]*q[0]; q2[1] = q[1]*q[1]; q2[2] = q[2]*q[2]; q2[3] = q[3]*q[3]; a[ROLL] = (180.0/M_PI)*atan2 (2*(q[2]*q[3] + q[1]*q[0]) , (-q2[1] - q2[2] + q2[3] + q2[0])); a[PITCH] = (180.0/M_PI)*asin (-2*(q[1]*q[3] - q[2]*q[0])); a[YAW] = (180.0/M_PI)*atan2 (2*(q[1]*q[2] + q[3]*q[0]) , (q2[1] - q2[2] - q2[3] + q2[0])); }

Original post: http://www.quake3world.com/ubb/Forum4/HTML/006766.html?

Converted into HTML by glossy glossyshit@hotmail.com

Mangled with CSS by PhaethonH PhaethonH@gmail.com

Permission has been granted by JackOfAllTrades to make verbatim copies of this document and make modifications as needed (which allowed this marked-up version).