ValveIntersenseTransforms
From DigitalBlacksmith
[edit] Intersense in Valve
OK this isn't a complete explanation..but holds most the key points..
[edit] Console variable
These specify the transform from the tracker to the head.
//These variables capture the transform from the tracker to the feet ConVar armar_tracker_head_x( "armar_tracker_head_x", "0", 0); ConVar armar_tracker_head_y( "armar_tracker_head_y", "0", 0); ConVar armar_tracker_head_z( "armar_tracker_head_z", "0", 0); ConVar armar_tracker_head_pitch( "armar_tracker_head_pitch", "0", 0); ConVar armar_tracker_head_yaw( "armar_tracker_head_yaw", "0", 0); ConVar armar_tracker_head_roll( "armar_tracker_head_roll", "0", 0);
These specify the transform from the lab coordinate space to a local space center on the fwd right upright..
//These transalte the IS1200/900 coord frame to map coord frame ConVar armar_coord_frame_x( "armar_coord_frame_x", "-284.64", 0); ConVar armar_coord_frame_y( "armar_coord_frame_y", "-139.3", 0); ConVar armar_coord_frame_z( "armar_coord_frame_z", "-23.5", 0);
This is the special sauce method. Note the technique of grabbing the local client player entity and using it as a transformation workbench. Allows you to use all the handy matrix and transformation methods common the CBaseEntity:
/**
* Given tracker position and orientation, return the feet
*/
matrix3x4_t getHeadTransfrom(float tx, float ty, float tz, float tp, float tyaw, float tr) {
//Get a copy of the player to use as a temporary tracker
C_BasePlayer* tracker = C_BasePlayer::GetLocalPlayer();
//Set the virtual tracker to the real tracker
tracker->SetAbsOrigin(Vector(tx,ty,tz));
//TODO: Are these in the right order?
tracker->SetAbsAngles(QAngle(tp, tyaw, tr));
Vector p_offset(armar_tracker_head_x.GetFloat(), armar_tracker_head_y.GetFloat(), armar_tracker_head_z.GetFloat());
//TODO: Implement this -- armar_tracker_head_pitch.GetFloat(), armar_tracker_head_yaw.GetFloat(), armar_tracker_head_roll.GetFloat();
Vector o_offset(0,0,0);
//Create to the player's head
matrix3x4_t xform;
xform[0][0] =1;
xform[0][1] =0;
xform[0][2] =0;
xform[0][3] =p_offset.x;
xform[1][0] =0;
xform[1][1] =1;
xform[1][2] =0;
xform[1][3] =p_offset.y;
xform[2][0] =0;
xform[2][1] =0;
xform[2][2] =1;
xform[2][3] =p_offset.z;
xform[3][0] =0;
xform[3][1] =0;
xform[3][2] =0;
xform[3][3] =1;
//Execute the first matrix operation (translation)
matrix3x4_t translationTemp;
ConcatTransforms( tracker->EntityToWorldTransform(), xform, translationTemp);
//Set up the x axis rotation matrix
Quaternion qx;
Vector rotationAxisWs_x =Vector( 1,0,0 );
AxisAngleQuaternion( rotationAxisWs_x, o_offset.x, qx );
QuaternionMatrix( qx, vec3_origin, xform );
//Execute the x axis rotation matrix
matrix3x4_t x_temp;
ConcatTransforms( translationTemp, xform, x_temp );
//Set up the y axis rotation matrix
Quaternion qy;
Vector rotationAxisWs_y =Vector( 0,1,0 );
AxisAngleQuaternion( rotationAxisWs_y, o_offset.y, qy );
QuaternionMatrix( qy, vec3_origin, xform );
//Execute the y axis rotation matrix
matrix3x4_t y_temp;
ConcatTransforms( x_temp, xform, y_temp );
//Set up the z axis rotation matrix
Quaternion qz;
Vector rotationAxisWs_z =Vector( 0,0,1 );
AxisAngleQuaternion( rotationAxisWs_z, o_offset.z, qz );
QuaternionMatrix( qz, vec3_origin, xform );
//Execute the z axis rotation matrix -- put result in our final answer
matrix3x4_t localToWorldMatrix ;
ConcatTransforms( y_temp, xform, localToWorldMatrix );
//Now load the resultant transform back into our entity's transform
tracker->SetLocalTransform(localToWorldMatrix);
//Msg("post trans\n");
//debugMatrix(localToWorldMatrix);
//Msg("\n\n");
return localToWorldMatrix;
}
It gets called by these methods...note how the intersense units and angles are tweaked before calling getHeadTransform.
This one sets the orientation:
/*
================
AdjustAngles
Moves the local angle positions
================
*/
void CInput::AdjustAngles ( float frametime )
{
float speed;
QAngle viewangles;
// Determine control scaling factor ( multiplies time )
speed = DetermineKeySpeed( frametime );
// Retrieve latest view direction from engine
engine->GetViewAngles( viewangles );
//OHAN ADD BEGIN - Get view angle from the tracker
if(arctl.IsTrackerInitialized() && (useIntersense == true)){
//pitch, yaw, roll
float pitch, yaw, roll;
arctl.GetOrientation(pitch, yaw, roll, HEAD);
viewangles.x = pitch;
viewangles.y = -1*(yaw+90);
viewangles.z = -1*(roll);
float ox, oy, oz;
arctl.GetPosition(ox, oy, oz, HEAD);
matrix3x4_t fTransform = getHeadTransfrom(ox, oy, oz, viewangles.x, viewangles.y, viewangles.z);
//the angles of the feet -- they are same as eyes
QAngle testangles;
MatrixToAngles(fTransform, testangles);
Msg("computed viewangles = %f, %f, %f\n", testangles.x, testangles.y, testangles.z);
Msg("good viewangles = %f, %f, %f\n", viewangles.x, viewangles.y, viewangles.z);
//viewangles.x = -viewangles.x;
//viewangles.y = -viewangles.y;
}
else{
// Adjust YAW
AdjustYaw( speed, viewangles );
// Adjust PITCH if keyboard looking
AdjustPitch( speed, viewangles );
// Make sure values are legitimate
ClampAngles( viewangles );
}
//OHAN ADD END
// Store new view angles into engine view direction
engine->SetViewAngles( viewangles );
}
This one sets position:
void CInput::CreateMove ( int sequence_number, float input_sample_frametime, bool active )
{
CUserCmd *cmd = &m_pCommands[ sequence_number % MULTIPLAYER_BACKUP];
cmd->Reset();
cmd->command_number = sequence_number;
cmd->tick_count = gpGlobals->tickcount;
QAngle viewangles;
if ( active || sv_noclipduringpause.GetInt() )
{
// Determine view angles
AdjustAngles ( input_sample_frametime );
// OHAN ADD: Use tracker for position if position tracking is available //STEVE and enabled
if(arctl.HasPositionTracking() && !enable_calibration.GetBool() && (useIntersense == true)){
// Send console command to the server from client side every "send_ticker" interval
if(send_ticker >= TICK_INTERVAL){
//First get the angles of the tracker
float pitch, yaw, roll;
arctl.GetOrientation(pitch, yaw, roll, HEAD);
viewangles.x = pitch;
viewangles.y = -1*(yaw+90);
viewangles.z = -1*(roll);
char command[64];
float ox, oy, oz;
float x,y,z;
// get the position of the head and send it to player position server
arctl.GetPosition(ox, oy, oz, HEAD);
//Rotate the coord system about the x axis
z = oz *-1;
x = oy;
y = ox;
//Convert meters to inches (Valve uses inches)
//float factor = move_factor.GetFloat();
float factor = 39.3700787;
//Msg("multy x= %f, factor=%f\n", x, factor);
x *= factor; y *= factor; z *= factor;
//Msg("Raw position data = %f, %f, %f\n", x, y, z);
//Add any coordinate frame translation
x = x + armar_coord_frame_x.GetFloat();
y = y + armar_coord_frame_y.GetFloat();
z = z + armar_coord_frame_z.GetFloat();
//Msg("Offset position data = %f, %f, %f\n", x, y, z);
//Msg("Raw orientation data = %f, %f, %f\n", pitch, yaw, roll);
matrix3x4_t fTransform = getHeadTransfrom(x, y, z, viewangles.x, viewangles.y, viewangles.z);
x = fTransform[0][3];
y = fTransform[1][3];
z = fTransform[2][3]-64; //Here we always subtract out the height of the player
//Msg("set_user_position %f %f %f\n", x, y, z);
sprintf(command, "set_user_position %f %f %f", x, y, z);
engine->ClientCmd(command);
send_ticker = 0;
}
else {
send_ticker++;
}
}
if(!arctl.HasPositionTracking() || enable_calibration.GetBool()){
// Determine sideways movement
ComputeSideMove( cmd );
// Determine vertical movement
ComputeUpwardMove( cmd );
// Determine forward movement
ComputeForwardMove( cmd );
// Scale based on holding speed key or having too fast of a velocity based on client maximum
// speed.
ScaleMovements( cmd );
}
// OHAN ADD END
// Allow mice and other controllers to add their inputs
ControllerMove( input_sample_frametime, cmd );
}
else
{
// need to run and reset mouse input so that there is no view pop when unpausing
if ( !m_fCameraInterceptingMouse && m_fMouseActive )
{
float mx, my;
GetAccumulatedMouseDeltasAndResetAccumulators( &mx, &my );
ResetMouse();
}
}
// Retreive view angles from engine ( could have been set in IN_AdjustAngles above )
engine->GetViewAngles( viewangles );
// Latch and clear impulse
cmd->impulse = in_impulse;
in_impulse = 0;
// Latch and clear weapon selection
if ( m_hSelectedWeapon != NULL )
{
C_BaseCombatWeapon *weapon = m_hSelectedWeapon;
cmd->weaponselect = weapon->entindex();
cmd->weaponsubtype = weapon->GetSubType();
// Always clear weapon selection
m_hSelectedWeapon = NULL;
}
// Set button and flag bits
cmd->buttons = GetButtonBits( 1 );
//MSW COMMENT OUT JOYSTICK
/*
// Using joystick?
if ( in_joystick.GetInt() )
{
if ( cmd->forwardmove > 0 )
{
cmd->buttons |= IN_FORWARD;
}
else if ( cmd->forwardmove < 0 )
{
cmd->buttons |= IN_BACK;
}
}
*/
// Use new view angles if alive, otherwise user last angles we stored off.
if ( g_iAlive )
{
VectorCopy( viewangles, cmd->viewangles );
VectorCopy( viewangles, m_angPreviousViewAngles );
}
else
{
VectorCopy( m_angPreviousViewAngles, cmd->viewangles );
}
// Let the move manager override anything it wants to.
g_pClientMode->CreateMove( input_sample_frametime, cmd );
// Get current view angles after the client mode tweaks with it
engine->SetViewAngles( cmd->viewangles );
m_flLastForwardMove = cmd->forwardmove;
cmd->random_seed = MD5_PseudoRandom( sequence_number ) & 0x7fffffff;
HLTVCamera()->CreateMove( cmd );
// Temporary Test: Ohan
/*Vector v;
prediction->GetViewOrigin(v);
Msg("(%f, %f, %f)\n", v.x, v.y, v.z);*/
#if defined( HL2_CLIENT_DLL )
// copy backchannel data
int i;
for (i = 0; i < m_EntityGroundContact.Count(); i++)
{
cmd->entitygroundcontact.AddToTail( m_EntityGroundContact[i] );
}
m_EntityGroundContact.RemoveAll();
#endif
}
--Steve 04:15, 12 March 2008 (EDT)
