Use quaternions through the DLL, in the Network protocol, and to interface the Refresh/Renderer.
Benefits:
Cleaner design, architecture open for future
improvements/added effects.
Problems:
Feasibility: Corinne Yu of ION Strom claimed to have worked quaternion support into their copy of the licensed Q2 codebase earlier this year.
There will not be a problem interfacing the renderer. >From the original exchange with John Carmack:
The renderer now takes matrix values instead of angles for more flexibility and less confusion. I am considering a quaternion representation for the game and network, but its not at the top of my priority list.
Implementation 1 - Minimal:
If quaternions were
only introduced in the network protocol, the DLL could
be changed later by anybody. Conversion of quaternions
to matrix and (if needed) angles could be done in
the Q2 client.
In: q_shared.h: tyepdef vec_t quats_t; // Maybe fixed? typedef quats_t quat_t[4];
short delta_angles[3] vec3_t viewangles vec3_t kick_angles vec3_t gunangles
byte angles[3];Implications of byte orient[4] in terms of angle resolution, and isotropy/distribution to be resolved. Might require normalization to unit quaternion when converted back to float. This is already a bulk of work. Implementation 2 - Maximal: This is a sketchy and incomplete list, that adds to the minimal requirements listed above. This list can't say anything about server or client internals. It deals with network, MAP/BSP and Game DLL in parts, but not completely.
#define QuatClear( q )\ #define QuatSet( q, s, vx, vy, vz )\ void QuatAdd( quat_t ql, quat_t qr, quat_t result ); void QuatSub( quat_t ql, quat_t qr, quat_t result ); void QuatCopy( quat_t src, quat_t dest ); void QuatInverse( quat_t q ); void QuatConjugate( quat_t q ); void QuatScale( quat_t q, quats_t scale, quat_t result ); vec_t QuatDotProduct( quat_t ql, quat_t qr ); quats_t QuatLength( quat_t q ); quats_t QuatNormalize( quat_t q ); void QuatMul_HOWELL ( quat_t ql, quat_t qr, quat_t result ); void QuatMul_HOWELL2( quat_t ql, quat_t qr, quat_t result ); void QuatMul_SALAMIN( quat_t ql, quat_t qr, quat_t result ); void QuatMul_LANDER ( quat_t ql, quat_t qr, quat_t result ); void QuatDiv( quat_t nom, quat_t denom, quat_t result ); void QuatSqr( quat_t q, quat_t result ); void QuatSqrt( quat_t q, quat_t result ); /** Quaternion rotated UP vector, 6 MUL. */ void Quat2VectorZ( quat_t q, vec3_t v ); /** Quaternion classification, to major axises. */ vector_type_t QuatClassify( quat_t q ); /** Converts a quaternion to angular displacement. */ void Quat2Axis ( quat_t q, axis_t* result ); /** Converts quaternion to OpenGL::glRotate parameters. */ void Quat2glRot( quat_t q, axis_t* glresult ); void Axis2Quat ( axis_t* ad, quat_t result ); void QuatScaleAngle( quat_t quat, float scale ); void QuatSetAngle( quat_t quat, float angle ); /** Conversion to matrix, 10 MUL */ void Quat2Matrix_SALAMIN( quat_t q, mat_t result ); void Quat2Matrix_WATT( quat_t q, mat_t result ); void Quat2Matrix_BOBIC( quat_t q, mat_t m ); void Matrix2Quat_WATT( mat_t m, quat_t result ); void Matrix2Quat_BOBIC( mat_t m, quat_t result ); void EulerPYR2Quat( vec3_t angles, quat_t quat); void EulerPYR2Quat_LANDER( vec3_t angles, quat_t q ); /** Convert two vectors to a quaternion. */ void Vectors2Quat( vec3_t v1, vec3_t v2, quat_t result ); /** Debug, dump a quaternion by components. */ void DumpQuat( quat_t q ); /** Quaternion lerp/slerp, q/-q, and normalizing. */ void QuatLerp_WATT ( quat_t p, quat_t q, float t, quat_t qt ); void QuatLerp_BOBIC ( quat_t p, quat_t q, float t, quat_t qt ); void QuatLerpNorm ( quat_t p, quat_t q, float t, quat_t qt ); void QuatSlerp_WATT ( quat_t p, quat_t q, float t, quat_t qt ); void QuatSlerp_BOBIC ( quat_t p, quat_t q, float t, quat_t qt ); /** Quaternion Lerping, preset, Iterator, fast linearly interpolated renormalization. */ typedef struct lerp_quat_t; qboolean QuatLerpInit( quat_t p, quat_t q, int steps, lerp_quat_t* desc ); qboolean QuatLerpIter( lerp_quat_t* desc, quat_t result ); /** Quaternion Slerping with preset and Iterator. */ typedef struct slerp_quat_t; qboolean QuatSlerpInit( quat_t p, quat_t q, int steps, slerp_quat_t* desc ); qboolean QuatSlerpIter( slerp_quat_t* desc, quat_t result );The utility functions above relate to angular displacement and matrix data types, modules which could also be provided. Q2 does not have a globally visible matrix type.
sv_rollangle sv_rollspeed run_pitch run_roll bob_pitch bob_roll v_dmg_roll v_dmg_pitchThere are also view pitching time parameters, viewpoint offsets, bob_up shift, and more involved. Per entity, ideal_yaw and yaw_speed is used to handle turns towards a target. There are also angular velocities, and rotational friction.
self->client->resp.cmd_angles self->client->pm.delta_angles self->client->pm.viewangles self->client->v_angle self->client->ps.viewangles self->client->ps.kick_angles self->client->ps.gunangles self->s.angles // server visible self->avelocity // these are not self->move_angles self->yaw_speed self->ideal_yaw self->angle // unused, Q1/QE3 leftover
{ "classname" "info_player_deathmatch" "origin" "56 -736 168" "angle" "0" "angles" "0" "42" "22" } In g_spawn.c: // change this to handle 3 angles if available case F_ANGLEHACK: v = atof(value); ((float *)(b+f->ofs))[0] = 0; ((float *)(b+f->ofs))[1] = v; ((float *)(b+f->ofs))[2] = 0; break;
In: g_local.h::spawn_temp_t quat_t skyaxis; In: g_spawn.c::SP_worldspawn gi.configstring ( CS_SKYAXIS, va("%f %f %f %f", st.skyaxis[0], st.skyaxis[1], st.skyaxis[2], st.skyaxis[3] ) );For now, this would simply encode the orientation as a quaternion. The Q2 Client could respond to changes (submitted as config strings) during a game by spherically interpolating between orientations, resuming the st.skyrotate constant rotation when the new orientation is reached. Updates of the CS_SKYROTATE configstring durign game should also be possible.
This is a minimal extension of the API offering for runtime sky rotation changes.
Related Proposals:
A reworked client side prediction of player POV movements,
and/or a reworked client side interpolation/extrapolation
of entity updates would be affected by introducing
quaternions to the network protocol.
The alternative of using an OpenGL::glRotate axis/angle
representation, which also uses four numbers for three
degrees of freedom, has not been proposed formally. It
shares most of the problems, while being less generic.
Some details depend on when the client handles updates
transfered by configstring changes.
Submitted 980321, revised 980328, revised 980420, comments to Bernd Kreimeier.