class QuatPlayer expands TMale1 config(Rotations); //////////////////////////////////////////////////////////////////////// // local Quaternion structure struct Quaternion { var float X, Y, Z, W; }; // these offsets are read from the config file. var config int roll_offset, pitch_offset, yaw_offset; /////////////////////////////////// Other changes //////////////// // convenience function to set a vector's values function final Vector SetVect(float x, float y, float z) { local Vector dest; dest.X = x; dest.Y = y; dest.Z = z; return dest; } // convenience function to print a Quaternion function final PrintQuat(Quaternion q) { ClientMessage("Quat is: " @q.X @q.Y @q.Z @q.W); } /////////////////////////////// Quaternion Math Routines ////////////////////// // convert an angle and an axis to the Quaternion corresponding to that // rotation. the axis vector must be of unit length, but we assume as much. final function Quaternion axis_to_quat(Vector axis, float phi) { local float scale; local Quaternion q; scale = sin(phi/2.0); q.X = axis.X * scale; q.Y = axis.Y * scale; q.Z = axis.Z * scale; q.W = cos(phi/2.0); return q; } // Concatenates two quaternions final function Quaternion add_quats(Quaternion q1, Quaternion q2) { local Quaternion dest; local Vector t1, t2, t3; local float t1DOTt2; // copy first three elements of quats into temp vectors, // scaled by the W value from the opposite quat t1 = SetVect(q1.X, q1.Y, q1.Z); t2 = SetVect(q2.X, q2.Y, q2.Z); // dot & cross product, then scale t1 and t2 t1DOTt2 = t1 Dot t2; t3 = t2 Cross t1; t1 *= q2.W; t2 *= q1.W; // the first three elements of the final quaternion are the sums of // the corresponding fields of t1, t2 and t3. dest.X = t1.X + t2.X + t3.X; dest.Y = t1.Y + t2.Y + t3.Y; dest.Z = t1.Z + t2.Z + t3.Z; // the last element of the final quaternion... dest.W = (q1.W * q2.W) - t1DOTt2; return dest; } // normalizes a quaternion. This shouldn't need to be called unless a quaternion // is sticking around for hundreds of concatenations. final function normalize_quat(out Quaternion q) { local float mag; mag = Sqrt(q.X*q.X + q.Y*q.Y + q.Z*q.Z + q.W*q.W); q.X /= mag; q.Y /= mag; q.Z /= mag; q.W /= mag; } // build a rotation matrix out of a quaternion. "matrices" are // simple one-dimensional arrays of 16 floats, laid out as follows: // // 1 2 3 4 // +----------- // 1| 0 1 2 3 // 2| 4 5 6 7 // 3| 8 9 10 11 // 4|12 13 14 15 // // We don't actually bother setting the elements that we don't // don't refer to elsewhere in this code, but they're left commented // out for your eddification. final function build_rotmatrix(out float M[16], Quaternion q) { M[0] = 1.0 - 2.0 * (q.Y * q.Y + q.Z * q.Z); M[1] = 2.0 * (q.X * q.Y - q.Z * q.W); M[2] = 2.0 * (q.Z * q.X + q.Y * q.W); //M[3] = 0.0; M[4] = 2.0 * (q.X * q.Y + q.Z * q.W); M[5]= 1.0 - 2.0 * (q.Z * q.Z + q.X * q.X); M[6] = 2.0 * (q.Y * q.Z - q.X * q.W); //M[7] = 0.0; M[8] = 2.0 * (q.Z * q.X - q.Y * q.W); M[9] = 2.0 * (q.Y * q.Z + q.X * q.W); M[10] = 1.0 - 2.0 * (q.Y * q.Y + q.X * q.X); //M[11] = 0.0; //M[12] = 0.0; //M[13] = 0.0; //M[14] = 0.0; //M[15] = 1.0; } // this is a general matrix-vector multiply. but we don't USE it. /* final function Vector transform_vect(float M[16], Vector v) { local Vector dest; dest = SetVect(M[0]*v.X + M[1]*v.Y + M[2]*v.Z, M[4]*v.X + M[5]*v.Y + M[6]*v.Z, M[8]*v.X + M[9]*v.Y + M[10]*v.Z); return dest; } */ // this function takes a rotation matrix and returns the corresponding UnrealScript // Rotator. Essentially what it's doing is using the matrix to transform the unit // vectors along the three coordinate axes, and then passing these vectors to // OrthoRotation(). But what's actually going on is much faster. final function Rotator get_rotator_from_matrix(float M[16]) { local Vector xa, ya, za; local Rotator R; // we can extract the rotated axes directly from the matrix, // without any multiplies at all! cool! xa = SetVect(M[0], M[4], M[8]); ya = SetVect(M[1], M[5], M[9]); za = SetVect(M[2], M[6], M[10]); // seems to be incorrect! we can fix that... // convert the axes to a rotator. R = OrthoRotation(xa, ya, za); return R; } //////////////////////////////// PlayerCalcView ///////////////////////// // this is the function we overload from PlayerPawn which sets the // player's view rotation for every frame. function PlayerCalcView(out actor ViewActor, out vector CameraLocation, out rotator CameraRotation ) { local vector View,HitLocation,HitNormal, FirstHit, spot; local float DesiredDist, ViewDist, WallOutDist; local actor HitActor; local Pawn PTarget; // additional local variables from Cort local Quaternion rquat, pquat, yquat, drquat, dpquat, dyquat, oquat; local Vector yaw_axis, pitch_axis, roll_axis; // the coordinate x/y/z axes local float M[16]; // rotation matrix if ( ViewTarget != None ) { ViewActor = ViewTarget; CameraLocation = ViewTarget.Location; CameraRotation = ViewTarget.Rotation; PTarget = Pawn(ViewTarget); if ( PTarget != None ) { if ( Level.NetMode == NM_Client ) { if ( PTarget.bIsPlayer ) PTarget.ViewRotation = TargetViewRotation; PTarget.EyeHeight = TargetEyeHeight; if ( PTarget.Weapon != None ) PTarget.Weapon.PlayerViewOffset = TargetWeaponViewOffset; } if ( PTarget.bIsPlayer ) CameraRotation = PTarget.ViewRotation; CameraLocation.Z += PTarget.EyeHeight; } if ( Carcass(ViewTarget) != None ) { if ( bBehindView || (ViewTarget.Physics == PHYS_None) ) CameraRotation = ViewRotation; else ViewRotation = CameraRotation; if ( bBehindView ) CalcBehindView(CameraLocation, CameraRotation, 190); } else if ( bBehindView ) CalcBehindView(CameraLocation, CameraRotation, 180); return; } ///////////////// Cort's changes here! // initialize axes roll_axis = SetVect(1.0, 0.0, 0.0); pitch_axis = SetVect(0.0, 1.0, 0.0); yaw_axis = SetVect(0.0, 0.0, 1.0); // convert angle/axis pairs to quaternions rquat = axis_to_quat(roll_axis, ViewRotation.Roll*3.141592/32768.0); pquat = axis_to_quat(pitch_axis, ViewRotation.Pitch*3.141592/32768.0); yquat = axis_to_quat(yaw_axis, ViewRotation.Yaw*3.141592/32768.0); // these don't really need to be re-calculated every frame, but... drquat = axis_to_quat(roll_axis, roll_offset*3.141592/32768.0); dpquat = axis_to_quat(pitch_axis, pitch_offset*3.141592/32768.0); dyquat = axis_to_quat(yaw_axis, yaw_offset*3.141592/32768.0); // add the quats in the proper order rquat = add_quats(rquat, drquat); pquat = add_quats(pquat, dpquat); oquat = add_quats(yquat, pquat); oquat = add_quats(oquat, rquat); oquat = add_quats(oquat, dyquat); // build rotation matrix from oquat build_rotmatrix(M, oquat); // get Rotator from matrix ViewRotation = get_rotator_from_matrix(M); /////////////////// End Cort's changes // View rotation. CameraRotation = ViewRotation; DesiredFOV = DefaultFOV; ViewActor = self; if( bBehindView ) //up and behind (for death scene) CalcBehindView(CameraLocation, CameraRotation, 180); else { // First-person view. CameraLocation = Location; CameraLocation.Z += Default.BaseEyeHeight; } }