Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2120 lines
70 KiB

//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#include "mathlib/mathlib.h"
#if defined(__SPU__)
#include "ps3/spu_job_shared.h"
#endif
#include "bone_setup_PS3.h"
#include <string.h>
#if !defined(__SPU__)
#include "tier0/vprof.h"
#endif
#include "mathlib/ssequaternion.h"
#include "bone_utils_PS3.h"
// -----------------------------------------------------------------
// -----------------------------------------------------------------
// from mathlib_base.cpp
// -----------------------------------------------------------------
#if 0
void ConcatTransforms_Aligned_PS3( const matrix3x4a_t &m0, const matrix3x4a_t &m1, matrix3x4a_t &out )
{
// AssertAligned( &m0 );
// AssertAligned( &m1 );
// AssertAligned( &out );
fltx4 lastMask = *(fltx4 *)(&g_SIMD_ComponentMask[3]);
fltx4 rowA0 = LoadAlignedSIMD( m0.m_flMatVal[0] );
fltx4 rowA1 = LoadAlignedSIMD( m0.m_flMatVal[1] );
fltx4 rowA2 = LoadAlignedSIMD( m0.m_flMatVal[2] );
fltx4 rowB0 = LoadAlignedSIMD( m1.m_flMatVal[0] );
fltx4 rowB1 = LoadAlignedSIMD( m1.m_flMatVal[1] );
fltx4 rowB2 = LoadAlignedSIMD( m1.m_flMatVal[2] );
// now we have the rows of m0 and the columns of m1
// first output row
fltx4 A0 = SplatXSIMD(rowA0);
fltx4 A1 = SplatYSIMD(rowA0);
fltx4 A2 = SplatZSIMD(rowA0);
fltx4 mul00 = MulSIMD( A0, rowB0 );
fltx4 mul01 = MulSIMD( A1, rowB1 );
fltx4 mul02 = MulSIMD( A2, rowB2 );
fltx4 out0 = AddSIMD( mul00, AddSIMD(mul01,mul02) );
// second output row
A0 = SplatXSIMD(rowA1);
A1 = SplatYSIMD(rowA1);
A2 = SplatZSIMD(rowA1);
fltx4 mul10 = MulSIMD( A0, rowB0 );
fltx4 mul11 = MulSIMD( A1, rowB1 );
fltx4 mul12 = MulSIMD( A2, rowB2 );
fltx4 out1 = AddSIMD( mul10, AddSIMD(mul11,mul12) );
// third output row
A0 = SplatXSIMD(rowA2);
A1 = SplatYSIMD(rowA2);
A2 = SplatZSIMD(rowA2);
fltx4 mul20 = MulSIMD( A0, rowB0 );
fltx4 mul21 = MulSIMD( A1, rowB1 );
fltx4 mul22 = MulSIMD( A2, rowB2 );
fltx4 out2 = AddSIMD( mul20, AddSIMD(mul21,mul22) );
// add in translation vector
A0 = AndSIMD(rowA0,lastMask);
A1 = AndSIMD(rowA1,lastMask);
A2 = AndSIMD(rowA2,lastMask);
out0 = AddSIMD(out0, A0);
out1 = AddSIMD(out1, A1);
out2 = AddSIMD(out2, A2);
StoreAlignedSIMD( out.m_flMatVal[0], out0 );
StoreAlignedSIMD( out.m_flMatVal[1], out1 );
StoreAlignedSIMD( out.m_flMatVal[2], out2 );
}
void ConcatTransforms_PS3( const matrix3x4_t& in1, const matrix3x4_t& in2, matrix3x4_t& out )
{
#if 0
// test for ones that'll be 2x faster
if ( (((size_t)&in1) % 16) == 0 && (((size_t)&in2) % 16) == 0 && (((size_t)&out) % 16) == 0 )
{
ConcatTransforms_Aligned( in1, in2, out );
return;
}
#endif
fltx4 lastMask = *(fltx4 *)(&g_SIMD_ComponentMask[3]);
fltx4 rowA0 = LoadUnalignedSIMD( in1.m_flMatVal[0] );
fltx4 rowA1 = LoadUnalignedSIMD( in1.m_flMatVal[1] );
fltx4 rowA2 = LoadUnalignedSIMD( in1.m_flMatVal[2] );
fltx4 rowB0 = LoadUnalignedSIMD( in2.m_flMatVal[0] );
fltx4 rowB1 = LoadUnalignedSIMD( in2.m_flMatVal[1] );
fltx4 rowB2 = LoadUnalignedSIMD( in2.m_flMatVal[2] );
// now we have the rows of m0 and the columns of m1
// first output row
fltx4 A0 = SplatXSIMD(rowA0);
fltx4 A1 = SplatYSIMD(rowA0);
fltx4 A2 = SplatZSIMD(rowA0);
fltx4 mul00 = MulSIMD( A0, rowB0 );
fltx4 mul01 = MulSIMD( A1, rowB1 );
fltx4 mul02 = MulSIMD( A2, rowB2 );
fltx4 out0 = AddSIMD( mul00, AddSIMD(mul01,mul02) );
// second output row
A0 = SplatXSIMD(rowA1);
A1 = SplatYSIMD(rowA1);
A2 = SplatZSIMD(rowA1);
fltx4 mul10 = MulSIMD( A0, rowB0 );
fltx4 mul11 = MulSIMD( A1, rowB1 );
fltx4 mul12 = MulSIMD( A2, rowB2 );
fltx4 out1 = AddSIMD( mul10, AddSIMD(mul11,mul12) );
// third output row
A0 = SplatXSIMD(rowA2);
A1 = SplatYSIMD(rowA2);
A2 = SplatZSIMD(rowA2);
fltx4 mul20 = MulSIMD( A0, rowB0 );
fltx4 mul21 = MulSIMD( A1, rowB1 );
fltx4 mul22 = MulSIMD( A2, rowB2 );
fltx4 out2 = AddSIMD( mul20, AddSIMD(mul21,mul22) );
// add in translation vector
A0 = AndSIMD(rowA0,lastMask);
A1 = AndSIMD(rowA1,lastMask);
A2 = AndSIMD(rowA2,lastMask);
out0 = AddSIMD(out0, A0);
out1 = AddSIMD(out1, A1);
out2 = AddSIMD(out2, A2);
// write to output
StoreUnalignedSIMD( out.m_flMatVal[0], out0 );
StoreUnalignedSIMD( out.m_flMatVal[1], out1 );
StoreUnalignedSIMD( out.m_flMatVal[2], out2 );
}
void MatrixAngles_PS3( const matrix3x4_t& matrix, float *angles )
{
float forward[3];
float left[3];
float up[3];
//
// Extract the basis vectors from the matrix. Since we only need the Z
// component of the up vector, we don't get X and Y.
//
forward[0] = matrix[0][0];
forward[1] = matrix[1][0];
forward[2] = matrix[2][0];
left[0] = matrix[0][1];
left[1] = matrix[1][1];
left[2] = matrix[2][1];
up[2] = matrix[2][2];
float xyDist = sqrtf( forward[0] * forward[0] + forward[1] * forward[1] );
// enough here to get angles?
if ( xyDist > 0.001f )
{
// (yaw) y = ATAN( forward.y, forward.x ); -- in our space, forward is the X axis
angles[1] = RAD2DEG( atan2f( forward[1], forward[0] ) );
// (pitch) x = ATAN( -forward.z, sqrt(forward.x*forward.x+forward.y*forward.y) );
angles[0] = RAD2DEG( atan2f( -forward[2], xyDist ) );
// (roll) z = ATAN( left.z, up.z );
angles[2] = RAD2DEG( atan2f( left[2], up[2] ) );
}
else // forward is mostly Z, gimbal lock-
{
// (yaw) y = ATAN( -left.x, left.y ); -- forward is mostly z, so use right for yaw
angles[1] = RAD2DEG( atan2f( -left[0], left[1] ) );
// (pitch) x = ATAN( -forward.z, sqrt(forward.x*forward.x+forward.y*forward.y) );
angles[0] = RAD2DEG( atan2f( -forward[2], xyDist ) );
// Assume no roll in this case as one degree of freedom has been lost (i.e. yaw == roll)
angles[2] = 0.0f;
}
}
void MatrixAngles_PS3( const matrix3x4_t& matrix, RadianEuler &angles, Vector &position )
{
MatrixGetColumn_PS3( matrix, 3, position );
MatrixAngles_PS3( matrix, angles );
}
void MatrixAngles_PS3( const matrix3x4_t &matrix, Quaternion &q, Vector &pos )
{
float trace;
trace = matrix[0][0] + matrix[1][1] + matrix[2][2] + 1.0f;
if( trace > 1.0f + FLT_EPSILON )
{
// VPROF_INCREMENT_COUNTER("MatrixQuaternion A",1);
q.x = ( matrix[2][1] - matrix[1][2] );
q.y = ( matrix[0][2] - matrix[2][0] );
q.z = ( matrix[1][0] - matrix[0][1] );
q.w = trace;
}
else if ( matrix[0][0] > matrix[1][1] && matrix[0][0] > matrix[2][2] )
{
// VPROF_INCREMENT_COUNTER("MatrixQuaternion B",1);
trace = 1.0f + matrix[0][0] - matrix[1][1] - matrix[2][2];
q.x = trace;
q.y = (matrix[1][0] + matrix[0][1] );
q.z = (matrix[0][2] + matrix[2][0] );
q.w = (matrix[2][1] - matrix[1][2] );
}
else if (matrix[1][1] > matrix[2][2])
{
// VPROF_INCREMENT_COUNTER("MatrixQuaternion C",1);
trace = 1.0f + matrix[1][1] - matrix[0][0] - matrix[2][2];
q.x = (matrix[0][1] + matrix[1][0] );
q.y = trace;
q.z = (matrix[2][1] + matrix[1][2] );
q.w = (matrix[0][2] - matrix[2][0] );
}
else
{
// VPROF_INCREMENT_COUNTER("MatrixQuaternion D",1);
trace = 1.0f + matrix[2][2] - matrix[0][0] - matrix[1][1];
q.x = (matrix[0][2] + matrix[2][0] );
q.y = (matrix[2][1] + matrix[1][2] );
q.z = trace;
q.w = (matrix[1][0] - matrix[0][1] );
}
QuaternionNormalize_PS3( q );
#if 0
// check against the angle version
RadianEuler ang;
MatrixAngles( matrix, ang );
Quaternion test;
AngleQuaternion( ang, test );
float d = QuaternionDotProduct( q, test );
Assert( fabs(d) > 0.99 && fabs(d) < 1.01 );
#endif
MatrixGetColumn_PS3( matrix, 3, pos );
}
void MatrixGetColumn_PS3( const matrix3x4_t& in, int column, Vector &out )
{
out.x = in[0][column];
out.y = in[1][column];
out.z = in[2][column];
}
void MatrixSetColumn_PS3( const Vector &in, int column, matrix3x4_t& out )
{
out[0][column] = in.x;
out[1][column] = in.y;
out[2][column] = in.z;
}
void MatrixInvert_PS3( const matrix3x4_t& in, matrix3x4_t& out )
{
// if ( &in == &out )
// {
// V_swap(out[0][1],out[1][0]);
// V_swap(out[0][2],out[2][0]);
// V_swap(out[1][2],out[2][1]);
// }
// else
{
// transpose the matrix
out[0][0] = in[0][0];
out[0][1] = in[1][0];
out[0][2] = in[2][0];
out[1][0] = in[0][1];
out[1][1] = in[1][1];
out[1][2] = in[2][1];
out[2][0] = in[0][2];
out[2][1] = in[1][2];
out[2][2] = in[2][2];
}
// now fix up the translation to be in the other space
float tmp[3];
tmp[0] = in[0][3];
tmp[1] = in[1][3];
tmp[2] = in[2][3];
out[0][3] = -DotProduct_PS3( tmp, out[0] );
out[1][3] = -DotProduct_PS3( tmp, out[1] );
out[2][3] = -DotProduct_PS3( tmp, out[2] );
}
void SetIdentityMatrix_PS3( matrix3x4_t& matrix )
{
memset( matrix.Base(), 0, sizeof(float)*3*4 );
matrix[0][0] = 1.0f;
matrix[1][1] = 1.0f;
matrix[2][2] = 1.0f;
}
void VectorRotate_PS3( const float * RESTRICT in1, const matrix3x4_t& in2, float * RESTRICT out )
{
// Assert( in1 != out );
out[0] = DotProduct_PS3( in1, in2[0] );
out[1] = DotProduct_PS3( in1, in2[1] );
out[2] = DotProduct_PS3( in1, in2[2] );
}
void AngleMatrix_PS3( RadianEuler const &angles, const Vector &position, matrix3x4_t& matrix )
{
AngleMatrix_PS3( angles, matrix );
MatrixSetColumn_PS3( position, 3, matrix );
}
void AngleMatrix_PS3( const RadianEuler& angles, matrix3x4_t& matrix )
{
QAngle quakeEuler( RAD2DEG( angles.y ), RAD2DEG( angles.z ), RAD2DEG( angles.x ) );
AngleMatrix_PS3( quakeEuler, matrix );
}
void AngleMatrix_PS3( const QAngle &angles, const Vector &position, matrix3x4_t& matrix )
{
AngleMatrix_PS3( angles, matrix );
MatrixSetColumn_PS3( position, 3, matrix );
}
void AngleMatrix_PS3( const QAngle &angles, matrix3x4_t& matrix )
{
float sr, sp, sy, cr, cp, cy;
#ifdef _X360
fltx4 radians, scale, sine, cosine;
radians = LoadUnaligned3SIMD( angles.Base() );
scale = ReplicateX4( M_PI_F / 180.f );
radians = MulSIMD( radians, scale );
SinCos3SIMD( sine, cosine, radians );
sp = SubFloat( sine, 0 ); sy = SubFloat( sine, 1 ); sr = SubFloat( sine, 2 );
cp = SubFloat( cosine, 0 ); cy = SubFloat( cosine, 1 ); cr = SubFloat( cosine, 2 );
#else
SinCos( DEG2RAD( angles[YAW] ), &sy, &cy );
SinCos( DEG2RAD( angles[PITCH] ), &sp, &cp );
SinCos( DEG2RAD( angles[ROLL] ), &sr, &cr );
#endif
// matrix = (YAW * PITCH) * ROLL
matrix[0][0] = cp*cy;
matrix[1][0] = cp*sy;
matrix[2][0] = -sp;
// NOTE: Do not optimize this to reduce multiplies! optimizer bug will screw this up.
matrix[0][1] = sr*sp*cy+cr*-sy;
matrix[1][1] = sr*sp*sy+cr*cy;
matrix[2][1] = sr*cp;
matrix[0][2] = (cr*sp*cy+-sr*-sy);
matrix[1][2] = (cr*sp*sy+-sr*cy);
matrix[2][2] = cr*cp;
matrix[0][3] = 0.0f;
matrix[1][3] = 0.0f;
matrix[2][3] = 0.0f;
}
void AngleQuaternion_PS3( const RadianEuler &angles, Quaternion &outQuat )
{
float sr, sp, sy, cr, cp, cy;
#ifdef _X360
fltx4 radians, scale, sine, cosine;
radians = LoadUnaligned3SIMD( &angles.x );
scale = ReplicateX4( 0.5f );
radians = MulSIMD( radians, scale );
SinCos3SIMD( sine, cosine, radians );
// NOTE: The ordering here is *different* from the AngleQuaternion below
// because p, y, r are not in the same locations in QAngle + RadianEuler. Yay!
sr = SubFloat( sine, 0 ); sp = SubFloat( sine, 1 ); sy = SubFloat( sine, 2 );
cr = SubFloat( cosine, 0 ); cp = SubFloat( cosine, 1 ); cy = SubFloat( cosine, 2 );
#else
SinCos( angles.z * 0.5f, &sy, &cy );
SinCos( angles.y * 0.5f, &sp, &cp );
SinCos( angles.x * 0.5f, &sr, &cr );
#endif
// NJS: for some reason VC6 wasn't recognizing the common subexpressions:
float srXcp = sr * cp, crXsp = cr * sp;
outQuat.x = srXcp*cy-crXsp*sy; // X
outQuat.y = crXsp*cy+srXcp*sy; // Y
float crXcp = cr * cp, srXsp = sr * sp;
outQuat.z = crXcp*sy-srXsp*cy; // Z
outQuat.w = crXcp*cy+srXsp*sy; // W (real component)
}
void Hermite_Spline_PS3( const Vector &p1, const Vector &p2, const Vector &d1, const Vector &d2, float t, Vector& output )
{
float tSqr = t*t;
float tCube = t*tSqr;
Assert( &output != &p1 );
Assert( &output != &p2 );
Assert( &output != &d1 );
Assert( &output != &d2 );
float b1 = 2.0f*tCube-3.0f*tSqr+1.0f;
float b2 = 1.0f - b1; // -2*tCube+3*tSqr;
float b3 = tCube-2*tSqr+t;
float b4 = tCube-tSqr;
VectorScale_PS3( p1, b1, output );
VectorMA_PS3( output, b2, p2, output );
VectorMA_PS3( output, b3, d1, output );
VectorMA_PS3( output, b4, d2, output );
}
float Hermite_Spline_PS3( float p1, float p2, float d1, float d2, float t )
{
float output;
float tSqr = t*t;
float tCube = t*tSqr;
float b1 = 2.0f*tCube-3.0f*tSqr+1.0f;
float b2 = 1.0f - b1; // -2*tCube+3*tSqr;
float b3 = tCube-2*tSqr+t;
float b4 = tCube-tSqr;
output = p1 * b1;
output += p2 * b2;
output += d1 * b3;
output += d2 * b4;
return output;
}
void Hermite_SplineBasis_PS3( float t, float basis[4] )
{
float tSqr = t*t;
float tCube = t*tSqr;
basis[0] = 2.0f*tCube-3.0f*tSqr+1.0f;
basis[1] = 1.0f - basis[0]; // -2*tCube+3*tSqr;
basis[2] = tCube-2*tSqr+t;
basis[3] = tCube-tSqr;
}
//#pragma optimize( "g", off )
void Hermite_Spline_PS3( const Vector &p0, const Vector &p1, const Vector &p2, float t, Vector& output )
{
Vector e10, e21;
VectorSubtract_PS3( p1, p0, e10 );
VectorSubtract_PS3( p2, p1, e21 );
Hermite_Spline_PS3( p1, p2, e10, e21, t, output );
}
//#pragma optimize( "", on )
float Hermite_Spline_PS3( float p0, float p1, float p2, float t )
{
return Hermite_Spline_PS3( p1, p2, p1 - p0, p2 - p1, t );
}
void Hermite_Spline_PS3( const Quaternion &q0, const Quaternion &q1, const Quaternion &q2, float t, Quaternion &output )
{
// cheap, hacked version of quaternions
Quaternion q0a;
Quaternion q1a;
QuaternionAlign_PS3( q2, q0, q0a );
QuaternionAlign_PS3( q2, q1, q1a );
output.x = Hermite_Spline_PS3( q0a.x, q1a.x, q2.x, t );
output.y = Hermite_Spline_PS3( q0a.y, q1a.y, q2.y, t );
output.z = Hermite_Spline_PS3( q0a.z, q1a.z, q2.z, t );
output.w = Hermite_Spline_PS3( q0a.w, q1a.w, q2.w, t );
QuaternionNormalize_PS3( output );
}
//-----------------------------------------------------------------------------
// Purpose: Converts a quaternion into engine angles
// Input : *quaternion - q3 + q0.i + q1.j + q2.k
// *outAngles - PITCH, YAW, ROLL
//-----------------------------------------------------------------------------
void QuaternionAngles_PS3( const Quaternion &q, RadianEuler &angles )
{
Assert( s_bMathlibInitialized );
Assert( q.IsValid() );
// FIXME: doing it this way calculates too much data, needs to do an optimized version...
matrix3x4_t matrix;
QuaternionMatrix_PS3( q, matrix );
MatrixAngles_PS3( matrix, angles );
Assert( angles.IsValid() );
}
#endif
// some assumptions made about alignment here
#define _VEC_SWIZZLE_Y0X0X0Y0 (__vector unsigned char) { 0x04,0x05,0x06,0x07, 0x00,0x01,0x02,0x03, 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07 }
#define _VEC_SWIZZLE_Y0Y0Z0Z0 (__vector unsigned char) { 0x04,0x05,0x06,0x07, 0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B, 0x08,0x09,0x0A,0x0B }
#define _VEC_SWIZZLE_Z0W0W0W0 (__vector unsigned char) { 0x08,0x09,0x0A,0x0B, 0x0C,0x0D,0x0E,0x0F, 0x0C,0x0D,0x0E,0x0F, 0x0C,0x0D,0x0E,0x0F }
#define _VEC_SWIZZLE_Z0Z0Y0X0 (__vector unsigned char) { 0x08,0x09,0x0A,0x0B, 0x08,0x09,0x0A,0x0B, 0x04,0x05,0x06,0x07, 0x00,0x01,0x02,0x03 }
#define _VEC_SWIZZLE_X0Y1Z1W1 (__vector unsigned char) { 0x00,0x01,0x02,0x03, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B, 0x1C,0x1D,0x1E,0x1F }
#define _VEC_SWIZZLE_X0Z1X0Z1 (__vector unsigned char) { 0x00,0x01,0x02,0x03, 0x18,0x19,0x1A,0x1B, 0x00,0x01,0x02,0x03, 0x18,0x19,0x1A,0x1B }
#define _VEC_SWIZZLE_X0Y0X1Y1 (__vector unsigned char) { 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07, 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17 }
#define _VEC_SWIZZLE_X0Y0Z0X1 (__vector unsigned char) { 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B, 0x10,0x11,0x12,0x13 }
#define _VEC_SWIZZLE_Y0X0W0Y1 (__vector unsigned char) { 0x04,0x05,0x06,0x07, 0x00,0x01,0x02,0x03, 0x0C,0x0D,0x0E,0x0F, 0x14,0x15,0x16,0x17 }
#define _VEC_SWIZZLE_X0Y0Z0Z1 (__vector unsigned char) { 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B, 0x18,0x19,0x1A,0x1B }
#define _VEC_SWIZZLE_Z1W0Z1W0 (__vector unsigned char) { 0x18,0x19,0x1A,0x1B, 0x0C,0x0D,0x0E,0x0F, 0x18,0x19,0x1A,0x1B, 0x0C,0x0D,0x0E,0x0F }
#define _VEC_SWIZZLE_Z0W1Z0W1 (__vector unsigned char) { 0x08,0x09,0x0A,0x0B, 0x1C,0x1D,0x1E,0x1F, 0x08,0x09,0x0A,0x0B, 0x1C,0x1D,0x1E,0x1F }
#define _VEC_ONEZEROZEROZERO (__vector float) { 1.0f, 0.0f, 0.0f, 0.0f }
#define _VEC_ZEROSIGNSIGNZERO (__vector unsigned int) { 0x0, 0x80000000, 0x80000000, 0x0 }
#define _VEC_ZEROSIGNSIGNSIGN (__vector unsigned int) { 0x0, 0x80000000, 0x80000000, 0x80000000 }
#define _VEC_SIGNZEROSIGNSIGN (__vector unsigned int) { 0x80000000, 0x0, 0x80000000, 0x80000000 }
#define _VEC_SIGNSIGNZEROSIGN (__vector unsigned int) { 0x80000000, 0x80000000, 0x0, 0x80000000 }
#define _VEC_SIGNZEROZEROZERO (__vector unsigned int) { 0x80000000, 0x0, 0x0, 0x0 }
#define _VEC_SIGNSIGNZEROZERO (__vector unsigned int) { 0x80000000, 0x80000000, 0x0, 0x0 }
#define _VEC_ZEROZEROZEROSIGN (__vector unsigned int) { 0x0, 0x0, 0x0, 0x80000000 }
#if defined(__SPU__)
// cyclic dependancy workaround (redefinition here) - TODO: remove
const fltx4 Four_Epsilons={FLT_EPSILON,FLT_EPSILON,FLT_EPSILON,FLT_EPSILON};
#endif
void QuaternionMatrix_PS3( const Quaternion &q, const Vector &pos, matrix3x4a_t& matrix )
{
fltx4 v0, v1, v2, v3, v4, v5, v6, v7;
v0 = LoadUnalignedSIMD( &q ); // x, y, z, w (q)
v6 = LoadUnalignedSIMD( &pos ); // px, py, pz, pw
v1 = AddSIMD( v0, v0 ); // 2x, 2y, 2z, 2w
v2 = vec_perm( v0, v0, _VEC_SWIZZLE_Y0X0X0Y0 ); // y, x, x, y
v3 = vec_perm( v1, v1, _VEC_SWIZZLE_Y0Y0Z0Z0 ); // 2y, 2y, 2z, 2z
v2 = MulSIMD( v2, v3 ); // 2yy, 2xy, 2xz, 2yz
v4 = vec_perm( v0, v0, _VEC_SWIZZLE_Z0W0W0W0 ); // z, w, w, w
v5 = vec_perm( v1, v1, _VEC_SWIZZLE_Z0Z0Y0X0 ); // 2z, 2z, 2y, 2x
v4 = MulSIMD( v4, v5 ); // 2zz, 2zw, 2yw, 2xw
v0 = MulSIMD( v0, v1 ); // 2xx, 2yy, 2zz, 2ww
v0 = vec_perm( v0, v1, _VEC_SWIZZLE_X0Y1Z1W1 ); // 2xx, 2y, 2z, 2w
// last two elements of third row
v7 = SubSIMD( _VEC_ONEZEROZEROZERO, v0 ); // 1-2xx, --, --, --
v7 = SubSIMD( v7, v2 ); // 1-2xx-2yy, --, --, --
v7 = vec_perm( v7, v6, _VEC_SWIZZLE_X0Z1X0Z1 ); // 1-2xx-2yy, pz, --, --
// first row
// 1-2yy-2zz, 2xy-2zw, 2xz+2yw, px
v2 = vec_xor( v2, (fltx4)_VEC_SIGNZEROZEROZERO );// -2yy, 2xy, 2xz, 2yz
v4 = vec_xor( v4, (fltx4)_VEC_SIGNSIGNZEROSIGN );// -2zz, -2zw, 2yw, -2xw
v4 = AddSIMD( v4, _VEC_ONEZEROZEROZERO ); // 1-2zz, -2zw, 2yw, -2xw
v3 = AddSIMD( v4, v2 ); // 1-2zz-2yy, 2xy-2zw, 2xz+2yw, 2yz-2xw
StoreAlignedSIMD( matrix[0], vec_perm( v3, v6, _VEC_SWIZZLE_X0Y0Z0X1 ) ); // 1-2zz-2yy, 2xy-2zw, 2xz+2yw, px
// second row
// 2xy+2wz, 1-2xx-2zz, 2yz-2xw, py
v2 = vec_perm( v0, v2, _VEC_SWIZZLE_X0Y1Z1W1 ); // 2xx, 2xy, 2xz, 2yz
v2 = vec_xor( v2, (fltx4)_VEC_SIGNZEROZEROZERO );// -2xx, 2xy, 2xz, 2yz
v4 = vec_xor( v4, (fltx4)_VEC_ZEROSIGNSIGNZERO );// 1-2zz, 2zw, -2yw, -2xw
v3 = AddSIMD( v4, v2 ); // 1-2xx-2zz, 2xy+2zw, 2xz-2yw, 2yz-2xw
StoreAlignedSIMD( matrix[1], vec_perm( v3, v6, _VEC_SWIZZLE_Y0X0W0Y1 ) ); // 2xy+2zw, 1-2xx-2zz, 2yz-2xw, py
// third row
// 2xz-2yw, 2yz+2xw, 1-2xx-2yy, pz
v2 = SubSIMD( v2, v4 ); // 2zz-1-2xx, 2xy-2zw, 2xz+2yw, 2yz+2xw
v3 = vec_perm( v3, v2, _VEC_SWIZZLE_Z0W1Z0W1 ); // 2xz-2yw, 2yz+2xw, --, --
v3 = vec_perm( v3, v7, _VEC_SWIZZLE_X0Y0X1Y1 ); // 2xz-2yw, 2yz+2xw, 1-2xx-2yy, pz
StoreAlignedSIMD( matrix[2], v3 ); // 2xz+2yw, 2xw+2yz, 1-2xx-2yy, pz
}
void QuaternionAlign_PS3( const Quaternion &p, const Quaternion &q, QuaternionAligned &qt )
{
fltx4 p1, q1, qt1;
p1 = LoadUnalignedSIMD( &p );
q1 = LoadUnalignedSIMD( &q );
qt1 = QuaternionAlignSIMD( p1, q1 );
StoreAlignedSIMD( (QuaternionAligned *)&qt, qt1 );
AssertFatal( qt.IsValid() );
}
void QuaternionSlerp_PS3( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt )
{
fltx4 p1, q1, qt1;
p1 = LoadUnalignedSIMD( &p );
q1 = LoadUnalignedSIMD( &q );
qt1 = QuaternionSlerpSIMD( p1, q1, t );
StoreUnalignedSIMD( qt.Base(), qt1 );
AssertFatal( qt.IsValid() );
}
void QuaternionSlerpNoAlign_PS3( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt )
{
fltx4 p1, q1, qt1;
p1 = LoadUnalignedSIMD( &p );
q1 = LoadUnalignedSIMD( &q );
qt1 = QuaternionSlerpNoAlignSIMD( p1, q1, t );
StoreUnalignedSIMD( qt.Base(), qt1 );
AssertFatal( qt.IsValid() );
}
float QuaternionNormalize_PS3( Quaternion &q )
{
fltx4 q1, radius, result;
bi32x4 mask;
q1 = LoadUnalignedSIMD( &q );
radius = Dot4SIMD( q1, q1 );
mask = CmpEqSIMD( radius, Four_Zeros ); // all ones iff radius = 0
result = ReciprocalSqrtSIMD( radius );
result = MulSIMD( result, q1 );
result = MaskedAssign( mask, q1, result ); // if radius was 0, just return q
StoreUnalignedSIMD( q.Base(), result );
AssertFatal( q.IsValid() );
return GetComponentSIMD( radius, 0 );
}
void QuaternionBlend_PS3( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt )
{
fltx4 psimd, qsimd, qtsimd;
psimd = LoadUnalignedSIMD( p.Base() );
qsimd = LoadUnalignedSIMD( q.Base() );
qtsimd = QuaternionBlendSIMD( psimd, qsimd, t );
StoreUnalignedSIMD( qt.Base(), qtsimd );
AssertFatal( qt.IsValid() );
}
void QuaternionBlendNoAlign_PS3( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt )
{
fltx4 p1, q1, qt1;
p1 = LoadUnalignedSIMD( &p );
q1 = LoadUnalignedSIMD( &q );
qt1 = QuaternionBlendNoAlignSIMD( p1, q1, t );
StoreUnalignedSIMD( qt.Base(), qt1 );
AssertFatal( qt.IsValid() );
}
void QuaternionIdentityBlend_PS3( const Quaternion &p, float t, Quaternion &qt )
{
fltx4 p1, t1, tw, sclp, qt1;
p1 = LoadUnalignedSIMD( &p );
t1 = ReplicateX4( t );
sclp = SubSIMD( _VEC_ONEF, t1 );
qt1 = MulSIMD( p1, sclp );
tw = _VEC_ZEROF;
tw = SetWSIMD( tw, t1 );
tw = XorSIMD( tw, AndSIMD( qt1, (fltx4)_VEC_ZEROZEROZEROSIGN ) );
qt1 = AddSIMD( qt1, tw );
QuaternionNormalizeSIMD( qt1 );
StoreUnalignedSIMD( qt.Base(), qt1 );
AssertFatal( qt.IsValid() );
}
void QuaternionScale_PS3( const Quaternion &p, float t, Quaternion &q )
{
fltx4 p1, q1;
p1 = LoadUnalignedSIMD( &p );
q1 = QuaternionScaleSIMD( p1, t );
StoreUnalignedSIMD( q.Base(), q1 );
AssertFatal( q.IsValid() );
}
void QuaternionAdd_PS3( const Quaternion &p, const Quaternion &q, Quaternion &qt )
{
fltx4 p1, q1, qt1;
fltx4 q2;
p1 = LoadUnalignedSIMD( &p );
q1 = LoadUnalignedSIMD( &q );
q2 = QuaternionAlignSIMD( p1, q1 );
qt1 = AddSIMD( p1, q2 );
StoreUnalignedSIMD( qt.Base(), qt1 );
AssertFatal( qt.IsValid() );
}
float QuaternionDotProduct_PS3( const Quaternion &p, const Quaternion &q )
{
fltx4 p1, q1, qt1;
p1 = LoadUnalignedSIMD( &p );
q1 = LoadUnalignedSIMD( &q );
qt1 = Dot4SIMD( p1, q1 );
#if !defined(__SPU__)
QuaternionAligned qt;
StoreAlignedSIMD( qt.Base(), qt1 );
AssertFatal( qt.IsValid() );
#endif
return GetComponentSIMD( qt1, 0 );
}
void QuaternionMult_PS3( const Quaternion &p, const Quaternion &q, Quaternion &qt )
{
fltx4 p1, q1, qt1;
p1 = LoadUnalignedSIMD( &p );
q1 = LoadUnalignedSIMD( &q );
qt1 = QuaternionMultSIMD( p1, q1 );
StoreUnalignedSIMD( qt.Base(), qt1 );
AssertFatal( qt.IsValid() );
}
//-----------------------------------------------------------------------------
// Purpose: build boneToWorld transforms for a specific bone
//-----------------------------------------------------------------------------
void BuildBoneChain_PS3(
const int *pBoneParent,
const matrix3x4a_t &rootxform,
const BoneVector pos[],
const BoneQuaternion q[],
int iBone,
matrix3x4a_t *pBoneToWorld )
{
CBoneBitList_PS3 boneComputed;
BuildBoneChainPartial_PS3( pBoneParent, rootxform, pos, q, iBone, pBoneToWorld, boneComputed, -1 );
return;
}
void BuildBoneChain_PS3(
const int *pBoneParent,
const matrix3x4a_t &rootxform,
const BoneVector pos[],
const BoneQuaternion q[],
int iBone,
matrix3x4a_t *pBoneToWorld,
CBoneBitList_PS3 &boneComputed )
{
BuildBoneChainPartial_PS3( pBoneParent, rootxform, pos, q, iBone, pBoneToWorld, boneComputed, -1 );
}
void BuildBoneChainPartial_PS3(
const int *pBoneParent,
const matrix3x4a_t &rootxform,
const BoneVector pos[],
const BoneQuaternion q[],
int iBone,
matrix3x4a_t *pBoneToWorld,
CBoneBitList_PS3 &boneComputed,
int iRoot )
{
if ( boneComputed.IsBoneMarked( iBone ) )
return;
matrix3x4a_t bonematrix;
QuaternionMatrix_PS3( q[ iBone ], pos[ iBone ], bonematrix );
int parent = pBoneParent[ iBone ];
if( parent == -1 || iBone == iRoot )
{
ConcatTransforms_Aligned_PS3( rootxform, bonematrix, pBoneToWorld[ iBone ] );
}
else
{
// evil recursive!!!
BuildBoneChainPartial_PS3( pBoneParent, rootxform, pos, q, parent, pBoneToWorld, boneComputed, iRoot );
ConcatTransforms_Aligned_PS3( pBoneToWorld[ parent ], bonematrix, pBoneToWorld[ iBone ]);
}
boneComputed.MarkBone( iBone );
}
//-----------------------------------------------------------------------------
// Purpose: qt = ( s * p ) * q
//-----------------------------------------------------------------------------
void QuaternionSM_PS3( float s, const Quaternion &p, const Quaternion &q, Quaternion &qt )
{
// Quaternion p1, q1;
//
// QuaternionScale_PS3( p, s, p1 );
// QuaternionMult_PS3( p1, q, q1 );
// QuaternionNormalize_PS3( q1 );
// qt[0] = q1[0];
// qt[1] = q1[1];
// qt[2] = q1[2];
// qt[3] = q1[3];
fltx4 p1, q1, qt1;
p1 = LoadUnalignedSIMD( &p );
q1 = LoadUnalignedSIMD( &q );
p1 = QuaternionScaleSIMD( p1, s );
q1 = QuaternionMultSIMD( p1, q1 );
qt1 = QuaternionNormalizeSIMD( q1 );
StoreUnalignedSIMD( qt.Base(), qt1 );
}
//-----------------------------------------------------------------------------
// Purpose: qt = p * ( s * q )
//-----------------------------------------------------------------------------
void QuaternionMA_PS3( const Quaternion &p, float s, const Quaternion &q, Quaternion &qt )
{
// Quaternion p1, q1;
//
// QuaternionScale_PS3( q, s, q1 );
// QuaternionMult_PS3( p, q1, p1 );
// QuaternionNormalize_PS3( p1 );
// qt[0] = p1[0];
// qt[1] = p1[1];
// qt[2] = p1[2];
// qt[3] = p1[3];
fltx4 p1, q1, qt1;
q1 = LoadUnalignedSIMD( &q );
p1 = LoadUnalignedSIMD( &p );
q1 = QuaternionScaleSIMD( q1, s );
p1 = QuaternionMultSIMD( p1, q1 );
qt1 = QuaternionNormalizeSIMD( p1 );
StoreUnalignedSIMD( qt.Base(), qt1 );
}
//-----------------------------------------------------------------------------
// Purpose: qt = p + s * q
//-----------------------------------------------------------------------------
void QuaternionAccumulate_PS3( const Quaternion &p, float s, const Quaternion &q, Quaternion &qt )
{
// Quaternion q2;
// QuaternionAlign_PS3( p, q, q2 );
//
// qt[0] = p[0] + s * q2[0];
// qt[1] = p[1] + s * q2[1];
// qt[2] = p[2] + s * q2[2];
// qt[3] = p[3] + s * q2[3];
fltx4 p1, s1, q1, qt1;
p1 = LoadUnalignedSIMD( &p );
q1 = LoadUnalignedSIMD( &q );
s1 = ReplicateX4( s );
qt1 = QuaternionAlignSIMD( p1, q1 );
qt1 = MaddSIMD( qt1, s1, p1 );
StoreUnalignedSIMD( qt.Base(), qt1 );
}
void GetBoneMapBoneWeight_SPU( bonejob_SPU *pBonejob, accumposeentry_SPU *pPoseEntry, int *&pLS_boneMap, float *&pLS_boneWeight )
{
int maxAnimBones = pBonejob->numBones;
for( int lp = 0; lp < MAX_BLENDANIMS; lp++ )
{
if( pPoseEntry->animIndices[lp] != -1 )
{
maxAnimBones = MAX( maxAnimBones, pPoseEntry->anims[ pPoseEntry->animIndices[lp] ].animstudiohdr_numbones );
}
}
if( pPoseEntry->pEA_seqgroup_boneMap )
{
pLS_boneMap = (int *)SPUmemcpy_UnalignedGet_MustSync( pLS_boneMap, (uint32)pPoseEntry->pEA_seqgroup_boneMap, sizeof(int) * maxAnimBones, DMATAG_ANIM_SYNC_BONEMAPWEIGHT );
}
Assert( pPoseEntry->pEA_seqdesc_boneWeight );
pLS_boneWeight = (float *)SPUmemcpy_UnalignedGet_MustSync( pLS_boneWeight, (uint32)pPoseEntry->pEA_seqdesc_boneWeight, sizeof(float) * maxAnimBones, DMATAG_ANIM_SYNC_BONEMAPWEIGHT );
}
//-----------------------------------------------------------------------------
// Purpose: blend together in world space q1,pos1 with q2,pos2. Return result in q1,pos1.
// 0 returns q1, pos1. 1 returns q2, pos2
//-----------------------------------------------------------------------------
void WorldSpaceSlerp_SPU(
bonejob_SPU* pSPUJob,
accumposeentry_SPU *pPoseEntry,
BoneQuaternion *q1,
BoneVector *pos1,
const BoneQuaternion *q2,
const BoneVector *pos2,
const int *boneMap,
const float *boneWeight,
float s,
int boneMask )
{
SNPROF_ANIM("WorldSpaceSlerp_SPU");
int i, j;
float s1; // weight of parent for q2, pos2
float s2; // weight for q2, pos2
// make fake root transform
matrix3x4a_t rootXform;
SetIdentityMatrix_PS3( rootXform );
// matrices for q2, pos2
// matrix3x4a_t *srcBoneToWorld = g_matStack[0];
matrix3x4a_t *srcBoneToWorld = (matrix3x4a_t *)PushLSStack( sizeof(matrix3x4a_t) * pSPUJob->maxBones );
CBoneBitList_PS3 srcBoneComputed;
// matrix3x4a_t *destBoneToWorld = g_matStack[1];
matrix3x4a_t *destBoneToWorld = (matrix3x4a_t *)PushLSStack( sizeof(matrix3x4a_t) * pSPUJob->maxBones );
CBoneBitList_PS3 destBoneComputed;
// matrix3x4a_t *targetBoneToWorld = g_matStack[2];
matrix3x4a_t *targetBoneToWorld = (matrix3x4a_t *)PushLSStack( sizeof(matrix3x4a_t) * pSPUJob->maxBones );
// CBoneBitList_PS3 targetBoneComputed;
// get bonemap and boneweights
for( i = 0; i < pSPUJob->numBones; i++ )
{
// skip unused bones
// if (!(pStudioHdr->boneFlags(i) & boneMask))
if ( !( pSPUJob->boneFlags[ i ] & boneMask ) )
{
continue;
}
// int n = pbone[i].parent;
int n = pSPUJob->boneParent[ i ];
s1 = 0.0f;
if( pPoseEntry->pEA_seqgroup_boneMap )
{
// j = pSeqGroup->boneMap[i];
j = boneMap[ i ];
if( j >= 0 )
{
// s2 = s * seqdesc.weight( j ); // blend in based on this bones weight
s2 = s * boneWeight[ j ]; // blend in based on this bones weight
if( n != -1 )
{
// s1 = s * seqdesc.weight( pSeqGroup->boneMap[n] );
s1 = s * boneWeight[ boneMap[ n ] ];
}
}
else
{
s2 = 0.0f;
}
}
else
{
// s2 = s * seqdesc.weight( i ); // blend in based on this bones weight
s2 = s * boneWeight[ i ]; // blend in based on this bones weight
if (n != -1)
{
// s1 = s * seqdesc.weight( n );
s1 = s * boneWeight[ n ];
}
}
if( s1 == 1.0f && s2 == 1.0f )
{
pos1[i] = pos2[i];
q1[i] = q2[i];
}
else if( s2 > 0.0f )
{
BoneQuaternion srcQ, destQ;
BoneVector srcPos, destPos;
BoneQuaternion targetQ;
BoneVector targetPos;
BoneVector tmp;
BuildBoneChain_PS3( pSPUJob->boneParent, rootXform, pos1, q1, i, destBoneToWorld, destBoneComputed );
BuildBoneChain_PS3( pSPUJob->boneParent, rootXform, pos2, q2, i, srcBoneToWorld, srcBoneComputed );
MatrixAngles_PS3( destBoneToWorld[i], destQ, destPos );
MatrixAngles_PS3( srcBoneToWorld[i], srcQ, srcPos );
QuaternionSlerp_PS3( destQ, srcQ, s2, targetQ );
AngleMatrix_PS3( targetQ, destPos, targetBoneToWorld[i] );
// back solve
if( n == -1 )
{
MatrixAngles_PS3( targetBoneToWorld[i], q1[i], tmp );
}
else
{
matrix3x4a_t worldToBone;
MatrixInvert_PS3( targetBoneToWorld[n], worldToBone );
matrix3x4a_t local;
ConcatTransforms_Aligned_PS3( worldToBone, targetBoneToWorld[i], local );
MatrixAngles_PS3( local, q1[i], tmp );
// blend bone lengths (local space)
pos1[i] = Lerp_PS3( s2, pos1[i], pos2[i] );
}
}
}
PopLSStack( sizeof(matrix3x4a_t) * pSPUJob->maxBones );
PopLSStack( sizeof(matrix3x4a_t) * pSPUJob->maxBones );
PopLSStack( sizeof(matrix3x4a_t) * pSPUJob->maxBones );
}
//-----------------------------------------------------------------------------
// Purpose: blend together q1,pos1 with q2,pos2. Return result in q1,pos1.
// 0 returns q1, pos1. 1 returns q2, pos2
//
// assumes p, q arrays aligned
//-----------------------------------------------------------------------------
void SlerpBones_SPU(
bonejob_SPU* pSPUJob,
accumposeentry_SPU *pPoseEntry,
BoneQuaternion *q1,
BoneVector *pos1,
const BoneQuaternion *q2,
const BoneVector *pos2,
const int *boneMap,
const float *boneWeight,
float s,
int boneMask )
{
SNPROF_ANIM( "SlerpBones_SPU" );
// Assert 16-byte alignment of in and out arrays.
// AssertMsg(
// ((reinterpret_cast<unsigned int>(q1) & 0x0F)==0) &&
// ((reinterpret_cast<unsigned int>(q2) & 0x0F)==0) ,
// "Input arrays to SlerpBones are not aligned! Catastrophe is inevitable.\n");
if( s <= 0.0f )
return;
if( s > 1.0f )
{
s = 1.0f;
}
if( pPoseEntry->seqdesc_flags & STUDIO_WORLD )
{
WorldSpaceSlerp_SPU( pSPUJob, pPoseEntry, q1, pos1, q2, pos2, boneMap, boneWeight, s, boneMask );
return;
}
int i;
// get bonemap and boneweights
// virtualmodel_t *pVModel = pStudioHdr->GetVirtualModel();
//
// const virtualgroup_t * RESTRICT pSeqGroup = NULL;
// if( pVModel )
// {
// pSeqGroup = pVModel->pSeqGroup( sequence );
// }
// Build weightlist for all bones
int nBoneCount = pSPUJob->numBones;
// int nBoneCount = pStudioHdr->numbones();
// float * RESTRICT pS2 = (float*)stackalloc( nBoneCount * sizeof(float) ); // 16-byte aligned
// float *pS2 = g_floatStack[ 0 ];
float *pS2 = (float *)PushLSStack( sizeof(float) * nBoneCount );
// if( pSeqGroup ) // hoist this branch outside of the inner loop for speed (even correctly predicted branches are an eight cycle latency)
if( pPoseEntry->pEA_seqgroup_boneMap )
{
for( i = 0; i < nBoneCount; i++ )
{
// skip unused bones
// if( !(pStudioHdr->boneFlags(i) & boneMask) ||
// pSeqGroup->boneMap[i] < 0 )
if( !( pSPUJob->boneFlags[ i ] & boneMask ) ||
boneMap[ i ] < 0 )
{
pS2[i] = 0.0f;
}
else
{
// boneMap[i] is not a float, don't be lured by the siren call of fcmp
// pS2[i] = s * seqdesc.weight( pSeqGroup->boneMap[i] );
pS2[i] = s * boneWeight[ boneMap[ i ] ];
}
}
}
else // !pSeqGroup
{
for( i = 0; i < nBoneCount; i++ )
{
// skip unused bones
// if( !(pStudioHdr->boneFlags(i) & boneMask) )
if( !( pSPUJob->boneFlags[ i ] & boneMask ) )
{
pS2[i] = 0.0f;
}
else
{
// pS2[i] = s * seqdesc.weight( i ); // blend in based on this bones weight
pS2[i] = s * boneWeight[ i ]; // blend in based on this bones weight
}
}
}
float weight;
int nBoneCountRoundedFour = ( nBoneCount ) & (~(3));
if( pPoseEntry->seqdesc_flags & STUDIO_DELTA )
{
// do as many as we can four at a time, then take care of stragglers.
for( i = 0; i < nBoneCountRoundedFour; i+=4 )
{
fltx4 weightfour = LoadAlignedSIMD(pS2+i); // four weights
FourQuaternions q1four, q2four;
FourQuaternions result;
q1four.LoadAndSwizzleAligned(q1+i); // four quaternions
q2four.LoadAndSwizzleAligned(q2+i); // four quaternions
if( pPoseEntry->seqdesc_flags & STUDIO_POST )
{
// result = q1 * ( weight * q2 )
result = q1four.MulAc(weightfour, q2four);
}
else
{
// result = ( s * q1 ) * q2
result = q2four.ScaleMul(weightfour, q1four);
}
// mask out unused channels, replacing them with original data
{
bi32x4 tinyScales = CmpLeSIMD( weightfour, Four_Zeros );
result.x = MaskedAssign(tinyScales, q1four.x, result.x);
result.y = MaskedAssign(tinyScales, q1four.y, result.y);
result.z = MaskedAssign(tinyScales, q1four.z, result.z);
result.w = MaskedAssign(tinyScales, q1four.w, result.w);
}
result.SwizzleAndStoreAlignedMasked(q1+i, CmpGtSIMD(weightfour,Four_Zeros) );
fltx4 originalpos1simd[4], pos1simd[4], pos2simd[4];
originalpos1simd[0] = pos1simd[0] = LoadAlignedSIMD(pos1[i+0].Base());
originalpos1simd[1] = pos1simd[1] = LoadAlignedSIMD(pos1[i+1].Base());
originalpos1simd[2] = pos1simd[2] = LoadAlignedSIMD(pos1[i+2].Base());
originalpos1simd[3] = pos1simd[3] = LoadAlignedSIMD(pos1[i+3].Base());
pos2simd[0] = LoadAlignedSIMD(pos2[i+0].Base());
pos2simd[1] = LoadAlignedSIMD(pos2[i+1].Base());
pos2simd[2] = LoadAlignedSIMD(pos2[i+2].Base());
pos2simd[3] = LoadAlignedSIMD(pos2[i+3].Base());
// should be able to use aligned loads??
// originalpos1simd[0] = pos1simd[0] = LoadUnalignedSIMD(pos1[i+0].Base());
// originalpos1simd[1] = pos1simd[1] = LoadUnalignedSIMD(pos1[i+1].Base());
// originalpos1simd[2] = pos1simd[2] = LoadUnalignedSIMD(pos1[i+2].Base());
// originalpos1simd[3] = pos1simd[3] = LoadUnalignedSIMD(pos1[i+3].Base());
// pos2simd[0] = LoadUnalignedSIMD(pos2[i+0].Base());
// pos2simd[1] = LoadUnalignedSIMD(pos2[i+1].Base());
// pos2simd[2] = LoadUnalignedSIMD(pos2[i+2].Base());
// pos2simd[3] = LoadUnalignedSIMD(pos2[i+3].Base());
fltx4 splatweights[4] = { SplatXSIMD(weightfour),
SplatYSIMD(weightfour),
SplatZSIMD(weightfour),
SplatWSIMD(weightfour) };
fltx4 Zero = Four_Zeros;
pos1simd[0] = MaddSIMD(pos2simd[0], splatweights[0], pos1simd[0] );
splatweights[0] = ( fltx4 ) CmpGtSIMD(splatweights[0], Zero);
pos1simd[1] = MaddSIMD(pos2simd[1], splatweights[1], pos1simd[1] );
splatweights[1] = ( fltx4 ) CmpGtSIMD(splatweights[1], Zero);
pos1simd[2] = MaddSIMD(pos2simd[2], splatweights[2], pos1simd[2] );
splatweights[2] = ( fltx4 ) CmpGtSIMD(splatweights[2], Zero);
pos1simd[3] = MaddSIMD(pos2simd[3], splatweights[3], pos1simd[3] );
splatweights[3] = ( fltx4 ) CmpGtSIMD(splatweights[3], Zero);
// mask out unweighted bones
/*
if (pS2[i+0] > 0)
StoreUnaligned3SIMD( pos1[i + 0].Base(), pos1simd[0] );
if (pS2[i+1] > 0)
StoreUnaligned3SIMD( pos1[i + 1].Base(), pos1simd[1] );
if (pS2[i+2] > 0)
StoreUnaligned3SIMD( pos1[i + 2].Base(), pos1simd[2] );
if (pS2[i+3] > 0)
StoreUnaligned3SIMD( pos1[i + 3].Base(), pos1simd[3] );
*/
StoreAlignedSIMD( pos1[i + 0].Base(), MaskedAssign( ( bi32x4 ) splatweights[0], pos1simd[0], originalpos1simd[0] ) );
StoreAlignedSIMD( pos1[i + 1].Base(), MaskedAssign( ( bi32x4 ) splatweights[1], pos1simd[1], originalpos1simd[1] ) );
StoreAlignedSIMD( pos1[i + 2].Base(), MaskedAssign( ( bi32x4 ) splatweights[2], pos1simd[2], originalpos1simd[2] ) );
StoreAlignedSIMD( pos1[i + 3].Base(), MaskedAssign( ( bi32x4 ) splatweights[3], pos1simd[3], originalpos1simd[3] ) );
// StoreAligned3SIMD( pos1[i + 0].Base(), MaskedAssign( ( bi32x4 ) splatweights[0], pos1simd[0], originalpos1simd[0] ) );
// StoreAligned3SIMD( pos1[i + 1].Base(), MaskedAssign( ( bi32x4 ) splatweights[1], pos1simd[1], originalpos1simd[1] ) );
// StoreAligned3SIMD( pos1[i + 2].Base(), MaskedAssign( ( bi32x4 ) splatweights[2], pos1simd[2], originalpos1simd[2] ) );
// StoreAligned3SIMD( pos1[i + 3].Base(), MaskedAssign( ( bi32x4 ) splatweights[3], pos1simd[3], originalpos1simd[3] ) );
// StoreUnaligned3SIMD( pos1[i + 0].Base(), MaskedAssign( ( bi32x4 ) splatweights[0], pos1simd[0], originalpos1simd[0] ) );
// StoreUnaligned3SIMD( pos1[i + 1].Base(), MaskedAssign( ( bi32x4 ) splatweights[1], pos1simd[1], originalpos1simd[1] ) );
// StoreUnaligned3SIMD( pos1[i + 2].Base(), MaskedAssign( ( bi32x4 ) splatweights[2], pos1simd[2], originalpos1simd[2] ) );
// StoreUnaligned3SIMD( pos1[i + 3].Base(), MaskedAssign( ( bi32x4 ) splatweights[3], pos1simd[3], originalpos1simd[3] ) );
}
// take care of stragglers
// odd that this is like this? for( false ; i < nBoneCount; i++ )
for( ; i < nBoneCount; i++ )
{
weight = pS2[i];
if ( weight <= 0.0f )
continue;
if ( pPoseEntry->seqdesc_flags & STUDIO_POST )
{
QuaternionMA_PS3( q1[i], weight, q2[i], q1[i] );
// QuaternionMASIMD( q1[i], weight, q2[i], q1[i] );
// FIXME: are these correct?
pos1[i][0] = pos1[i][0] + pos2[i][0] * weight;
pos1[i][1] = pos1[i][1] + pos2[i][1] * weight;
pos1[i][2] = pos1[i][2] + pos2[i][2] * weight;
}
else
{
QuaternionSM_PS3( weight, q2[i], q1[i], q1[i] );
// QuaternionSMSIMD( weight, q2[i], q1[i], q1[i] );
// FIXME: are these correct?
pos1[i][0] = pos1[i][0] + pos2[i][0] * weight;
pos1[i][1] = pos1[i][1] + pos2[i][1] * weight;
pos1[i][2] = pos1[i][2] + pos2[i][2] * weight;
}
}
PopLSStack( sizeof(float) * nBoneCount );
// #if defined(__SPU__)
// if( pSPUJob->numBones == 70 )
// {
// Quaternion *pQ = &q1[1];
// VjobSpuLog("q1[1]: %f, %f, %f, %f\n", pQ->x, pQ->y, pQ->z, pQ->w );
// }
// #endif
return;
}
//// SLERP PHASE
// Some bones need to be slerped with alignment.
// Others do not.
// Some need to be ignored altogether.
// Build arrays indicating which are which.
// This is the corral approach. Another approach
// would be to compute both the aligned and unaligned
// slerps of each bone in the first pass through the
// array, and then do a masked selection of each
// based on the masks. However there really isn't
// a convenient way to turn the int flags that
// specify which approach to take, into fltx4 masks.
// int * RESTRICT aBonesSlerpAlign = (int *)stackalloc(nBoneCount * sizeof(int));
// float * RESTRICT aBonesSlerpAlignWeights = (float *)stackalloc(nBoneCount * sizeof(float));
// int * RESTRICT aBonesSlerpNoAlign = (int *)stackalloc(nBoneCount * sizeof(int));
// float * RESTRICT aBonesSlerpNoAlignWeights = (float *)stackalloc(nBoneCount * sizeof(float));
// int *aBonesSlerpAlign = g_intStack[ 0 ];
// float *aBonesSlerpAlignWeights = g_floatStack[ 1 ];
// int *aBonesSlerpNoAlign = g_intStack[ 1 ];
// float *aBonesSlerpNoAlignWeights = g_floatStack[ 2 ];
int *aBonesSlerpAlign = (int *)PushLSStack( sizeof(int) * nBoneCount );
float *aBonesSlerpAlignWeights = (float *)PushLSStack( sizeof(float) * nBoneCount );
int *aBonesSlerpNoAlign = (int *)PushLSStack( sizeof(int) * nBoneCount );
float *aBonesSlerpNoAlignWeights = (float *)PushLSStack( sizeof(float) * nBoneCount );
int numBonesSlerpAlign = 0;
int numBonesSlerpNoAlign = 0;
// BoneQuaternion * RESTRICT testOutput = (BoneQuaternion *)stackalloc(nBoneCount * sizeof(BoneQuaternion));
// sweep forward through the array and determine where to corral each bone.
for( i = 0 ; i < nBoneCount ; ++i )
{
float weight = pS2[i];
if( weight == 1.0f )
{
q1[i] = q2[i];
pos1[i] = pos2[i];
}
else if( weight > 0.0f ) // ignore small bones
{
// if( pStudioHdr->boneFlags(i) & BONE_FIXED_ALIGNMENT )
if( pSPUJob->boneFlags[ i ] & BONE_FIXED_ALIGNMENT )
{
aBonesSlerpNoAlign[ numBonesSlerpNoAlign ] = i;
aBonesSlerpNoAlignWeights[ numBonesSlerpNoAlign ] = weight;
++numBonesSlerpNoAlign;
}
else
{
aBonesSlerpAlign[ numBonesSlerpAlign ] = i;
aBonesSlerpAlignWeights[ numBonesSlerpAlign ] = weight;
++numBonesSlerpAlign;
}
}
}
// okay, compute all the aligned, and all the unaligned bones, four at
// a time if possible.
const fltx4 One = Four_Ones;
/////////////////
// // // Aligned!
nBoneCountRoundedFour = ( numBonesSlerpAlign ) & ~3;
for( i = 0 ; i < nBoneCountRoundedFour ; i+=4 )
{
fltx4 weights = LoadAlignedSIMD( aBonesSlerpAlignWeights+i );
fltx4 oneMinusWeight = SubSIMD(One, weights);
// position component:
// pos1[i][0] = pos1[i][0] * s1 + pos2[i][0] * weight;
fltx4 pos1simd[4];
fltx4 pos2simd[4];
pos1simd[0] = LoadAlignedSIMD(pos1[aBonesSlerpAlign[i+0]].Base());
pos1simd[1] = LoadAlignedSIMD(pos1[aBonesSlerpAlign[i+1]].Base());
pos1simd[2] = LoadAlignedSIMD(pos1[aBonesSlerpAlign[i+2]].Base());
pos1simd[3] = LoadAlignedSIMD(pos1[aBonesSlerpAlign[i+3]].Base());
pos2simd[0] = LoadAlignedSIMD(pos2[aBonesSlerpAlign[i+0]].Base());
pos2simd[1] = LoadAlignedSIMD(pos2[aBonesSlerpAlign[i+1]].Base());
pos2simd[2] = LoadAlignedSIMD(pos2[aBonesSlerpAlign[i+2]].Base());
pos2simd[3] = LoadAlignedSIMD(pos2[aBonesSlerpAlign[i+3]].Base());
// pos1simd[0] = LoadUnaligned3SIMD(pos1[aBonesSlerpAlign[i+0]].Base());
// pos1simd[1] = LoadUnaligned3SIMD(pos1[aBonesSlerpAlign[i+1]].Base());
// pos1simd[2] = LoadUnaligned3SIMD(pos1[aBonesSlerpAlign[i+2]].Base());
// pos1simd[3] = LoadUnaligned3SIMD(pos1[aBonesSlerpAlign[i+3]].Base());
// pos2simd[0] = LoadUnaligned3SIMD(pos2[aBonesSlerpAlign[i+0]].Base());
// pos2simd[1] = LoadUnaligned3SIMD(pos2[aBonesSlerpAlign[i+1]].Base());
// pos2simd[2] = LoadUnaligned3SIMD(pos2[aBonesSlerpAlign[i+2]].Base());
// pos2simd[3] = LoadUnaligned3SIMD(pos2[aBonesSlerpAlign[i+3]].Base());
pos1simd[0] = MulSIMD( SplatXSIMD(oneMinusWeight) , pos1simd[0] );
pos1simd[1] = MulSIMD( SplatYSIMD(oneMinusWeight) , pos1simd[1] );
pos1simd[2] = MulSIMD( SplatZSIMD(oneMinusWeight) , pos1simd[2] );
pos1simd[3] = MulSIMD( SplatWSIMD(oneMinusWeight) , pos1simd[3] );
fltx4 posWriteMasks[4]; // don't overwrite where there was zero weight
{
fltx4 splatweights[4];
fltx4 Zero = Four_Zeros;
splatweights[0] = SplatXSIMD(weights);
splatweights[1] = SplatYSIMD(weights);
splatweights[2] = SplatZSIMD(weights);
splatweights[3] = SplatWSIMD(weights);
pos1simd[0] = MaddSIMD( splatweights[0] , pos2simd[0], pos1simd[0] );
posWriteMasks[0] = ( fltx4 ) CmpGtSIMD(splatweights[0], Zero);
pos1simd[1] = MaddSIMD( splatweights[1] , pos2simd[1], pos1simd[1] );
posWriteMasks[1] = ( fltx4 ) CmpGtSIMD(splatweights[1], Zero);
pos1simd[2] = MaddSIMD( splatweights[2] , pos2simd[2], pos1simd[2] );
posWriteMasks[2] = ( fltx4 ) CmpGtSIMD(splatweights[2], Zero);
pos1simd[3] = MaddSIMD( splatweights[3] , pos2simd[3], pos1simd[3] );
posWriteMasks[3] = ( fltx4 ) CmpGtSIMD(splatweights[3], Zero);
}
FourQuaternions q1four, q2four, result;
q1four.LoadAndSwizzleAligned( q1 + aBonesSlerpAlign[i+0],
q1 + aBonesSlerpAlign[i+1],
q1 + aBonesSlerpAlign[i+2],
q1 + aBonesSlerpAlign[i+3] );
#if 0
// FIXME: the SIMD slerp doesn't handle quaternions that have opposite signs
q2four.LoadAndSwizzleAligned( q2 + aBonesSlerpAlign[i+0],
q2 + aBonesSlerpAlign[i+1],
q2 + aBonesSlerpAlign[i+2],
q2 + aBonesSlerpAlign[i+3] );
result = q2four.Slerp(q1four, oneMinusWeight);
#else
// force the quaternions to be the same sign (< 180 degree separation)
BoneQuaternion q20, q21, q22, q23;
QuaternionAlign_PS3( q1[aBonesSlerpAlign[i+0]], q2[aBonesSlerpAlign[i+0]], q20 );
QuaternionAlign_PS3( q1[aBonesSlerpAlign[i+1]], q2[aBonesSlerpAlign[i+1]], q21 );
QuaternionAlign_PS3( q1[aBonesSlerpAlign[i+2]], q2[aBonesSlerpAlign[i+2]], q22 );
QuaternionAlign_PS3( q1[aBonesSlerpAlign[i+3]], q2[aBonesSlerpAlign[i+3]], q23 );
q2four.LoadAndSwizzleAligned( &q20, &q21, &q22, &q23 );
result = q2four.SlerpNoAlign(q1four, oneMinusWeight);
#endif
result.SwizzleAndStoreAligned( q1 + aBonesSlerpAlign[i+0],
q1 + aBonesSlerpAlign[i+1],
q1 + aBonesSlerpAlign[i+2],
q1 + aBonesSlerpAlign[i+3] );
StoreAlignedSIMD( pos1[aBonesSlerpAlign[i+0]].Base(), pos1simd[0] );
StoreAlignedSIMD( pos1[aBonesSlerpAlign[i+1]].Base(), pos1simd[1] );
StoreAlignedSIMD( pos1[aBonesSlerpAlign[i+2]].Base(), pos1simd[2] );
StoreAlignedSIMD( pos1[aBonesSlerpAlign[i+3]].Base(), pos1simd[3] );
// StoreUnaligned3SIMD( pos1[aBonesSlerpAlign[i+0]].Base(), pos1simd[0] );
// StoreUnaligned3SIMD( pos1[aBonesSlerpAlign[i+1]].Base(), pos1simd[1] );
// StoreUnaligned3SIMD( pos1[aBonesSlerpAlign[i+2]].Base(), pos1simd[2] );
// StoreUnaligned3SIMD( pos1[aBonesSlerpAlign[i+3]].Base(), pos1simd[3] );
}
// handle stragglers
// for( i ; i < numBonesSlerpAlign ; ++i )
for( ; i < numBonesSlerpAlign ; ++i )
{
BoneQuaternion q3;
weight = aBonesSlerpAlignWeights[i];
int k = aBonesSlerpAlign[i];
float s1 = 1.0 - weight;
QuaternionSlerp_PS3( q2[k], q1[k], s1, q3 );
q1[k][0] = q3[0];
q1[k][1] = q3[1];
q1[k][2] = q3[2];
q1[k][3] = q3[3];
pos1[k][0] = pos1[k][0] * s1 + pos2[k][0] * weight;
pos1[k][1] = pos1[k][1] * s1 + pos2[k][1] * weight;
pos1[k][2] = pos1[k][2] * s1 + pos2[k][2] * weight;
}
///////////////////
// // // Unaligned!
nBoneCountRoundedFour = (numBonesSlerpNoAlign) & ~3;
for( i = 0 ; i < nBoneCountRoundedFour ; i+=4 )
{
fltx4 weights = LoadAlignedSIMD( aBonesSlerpNoAlignWeights+i );
fltx4 oneMinusWeight = SubSIMD(One, weights);
// position component:
// pos1[i][0] = pos1[i][0] * s1 + pos2[i][0] * weight;
fltx4 pos1simd[4];
fltx4 pos2simd[4];
pos1simd[0] = LoadAlignedSIMD(pos1[aBonesSlerpNoAlign[i+0]].Base());
pos1simd[1] = LoadAlignedSIMD(pos1[aBonesSlerpNoAlign[i+1]].Base());
pos1simd[2] = LoadAlignedSIMD(pos1[aBonesSlerpNoAlign[i+2]].Base());
pos1simd[3] = LoadAlignedSIMD(pos1[aBonesSlerpNoAlign[i+3]].Base());
pos2simd[0] = LoadAlignedSIMD(pos2[aBonesSlerpNoAlign[i+0]].Base());
pos2simd[1] = LoadAlignedSIMD(pos2[aBonesSlerpNoAlign[i+1]].Base());
pos2simd[2] = LoadAlignedSIMD(pos2[aBonesSlerpNoAlign[i+2]].Base());
pos2simd[3] = LoadAlignedSIMD(pos2[aBonesSlerpNoAlign[i+3]].Base());
// pos1simd[0] = LoadUnaligned3SIMD(pos1[aBonesSlerpNoAlign[i+0]].Base());
// pos1simd[1] = LoadUnaligned3SIMD(pos1[aBonesSlerpNoAlign[i+1]].Base());
// pos1simd[2] = LoadUnaligned3SIMD(pos1[aBonesSlerpNoAlign[i+2]].Base());
// pos1simd[3] = LoadUnaligned3SIMD(pos1[aBonesSlerpNoAlign[i+3]].Base());
// pos2simd[0] = LoadUnaligned3SIMD(pos2[aBonesSlerpNoAlign[i+0]].Base());
// pos2simd[1] = LoadUnaligned3SIMD(pos2[aBonesSlerpNoAlign[i+1]].Base());
// pos2simd[2] = LoadUnaligned3SIMD(pos2[aBonesSlerpNoAlign[i+2]].Base());
// pos2simd[3] = LoadUnaligned3SIMD(pos2[aBonesSlerpNoAlign[i+3]].Base());
pos1simd[0] = MulSIMD( SplatXSIMD(oneMinusWeight) , pos1simd[0] );
pos1simd[1] = MulSIMD( SplatYSIMD(oneMinusWeight) , pos1simd[1] );
pos1simd[2] = MulSIMD( SplatZSIMD(oneMinusWeight) , pos1simd[2] );
pos1simd[3] = MulSIMD( SplatWSIMD(oneMinusWeight) , pos1simd[3] );
pos1simd[0] = MaddSIMD( SplatXSIMD(weights) , pos2simd[0], pos1simd[0] );
pos1simd[1] = MaddSIMD( SplatYSIMD(weights) , pos2simd[1], pos1simd[1] );
pos1simd[2] = MaddSIMD( SplatZSIMD(weights) , pos2simd[2], pos1simd[2] );
pos1simd[3] = MaddSIMD( SplatWSIMD(weights) , pos2simd[3], pos1simd[3] );
FourQuaternions q1four, q2four, result;
q1four.LoadAndSwizzleAligned( q1 + aBonesSlerpNoAlign[i+0],
q1 + aBonesSlerpNoAlign[i+1],
q1 + aBonesSlerpNoAlign[i+2],
q1 + aBonesSlerpNoAlign[i+3] );
q2four.LoadAndSwizzleAligned( q2 + aBonesSlerpNoAlign[i+0],
q2 + aBonesSlerpNoAlign[i+1],
q2 + aBonesSlerpNoAlign[i+2],
q2 + aBonesSlerpNoAlign[i+3] );
result = q2four.SlerpNoAlign(q1four, oneMinusWeight);
result.SwizzleAndStoreAligned( q1 + aBonesSlerpNoAlign[i+0],
q1 + aBonesSlerpNoAlign[i+1],
q1 + aBonesSlerpNoAlign[i+2],
q1 + aBonesSlerpNoAlign[i+3] );
StoreAlignedSIMD(pos1[aBonesSlerpNoAlign[i+0]].Base(), pos1simd[0]);
StoreAlignedSIMD(pos1[aBonesSlerpNoAlign[i+1]].Base(), pos1simd[1]);
StoreAlignedSIMD(pos1[aBonesSlerpNoAlign[i+2]].Base(), pos1simd[2]);
StoreAlignedSIMD(pos1[aBonesSlerpNoAlign[i+3]].Base(), pos1simd[3]);
// StoreUnaligned3SIMD(pos1[aBonesSlerpNoAlign[i+0]].Base(), pos1simd[0]);
// StoreUnaligned3SIMD(pos1[aBonesSlerpNoAlign[i+1]].Base(), pos1simd[1]);
// StoreUnaligned3SIMD(pos1[aBonesSlerpNoAlign[i+2]].Base(), pos1simd[2]);
// StoreUnaligned3SIMD(pos1[aBonesSlerpNoAlign[i+3]].Base(), pos1simd[3]);
}
// handle stragglers
// for( i ; i < numBonesSlerpNoAlign ; ++i )
for( ; i < numBonesSlerpNoAlign ; ++i )
{
weight = aBonesSlerpNoAlignWeights[ i ];
int k = aBonesSlerpNoAlign[ i ];
float s1 = 1.0 - weight;
BoneQuaternion q3;
QuaternionSlerpNoAlign_PS3( q2[ k ], q1[ k ], s1, q3 );
q1[k][0] = q3[0];
q1[k][1] = q3[1];
q1[k][2] = q3[2];
q1[k][3] = q3[3];
pos1[k][0] = pos1[k][0] * s1 + pos2[k][0] * weight;
pos1[k][1] = pos1[k][1] * s1 + pos2[k][1] * weight;
pos1[k][2] = pos1[k][2] * s1 + pos2[k][2] * weight;
}
// aBonesSlerpNoAlignWeights
PopLSStack( sizeof(float) * nBoneCount );
// aBonesSlerpNoAlign
PopLSStack( sizeof(int) * nBoneCount );
// aBonesSlerpAlignWeights
PopLSStack( sizeof(float) * nBoneCount );
// aBonesSlerpAlign
PopLSStack( sizeof(int) * nBoneCount );
// pS2
PopLSStack( sizeof(float) * nBoneCount );
}
template <int N>
struct GetLog2_t
{};
template<>
struct GetLog2_t<0x00100000>
{
enum {kLog2 = 20};
};
//---------------------------------------------------------------------
// Make sure quaternions are within 180 degrees of one another, if not, reverse q
//---------------------------------------------------------------------
FORCEINLINE fltx4 BoneQuaternionAlignSIMD( const fltx4 &p, const fltx4 &q )
{
// decide if one of the quaternions is backwards
bi32x4 cmp = CmpLtSIMD( Dot4SIMD(p,q), Four_Zeros );
fltx4 result = MaskedAssign( cmp, NegSIMD(q), q );
return result;
}
// SSE + X360 implementation
FORCEINLINE fltx4 BoneQuaternionNormalizeSIMD( const fltx4 &q )
{
fltx4 radius, result;
bi32x4 mask;
radius = Dot4SIMD( q, q );
mask = CmpEqSIMD( radius, Four_Zeros ); // all ones iff radius = 0
result = ReciprocalSqrtSIMD( radius );
result = MulSIMD( result, q );
return MaskedAssign( mask, q, result ); // if radius was 0, just return q
}
//-----------------------------------------------------------------------------
// Purpose: Inter-animation blend. Assumes both types are identical.
// blend together q1,pos1 with q2,pos2. Return result in q1,pos1.
// 0 returns q1, pos1. 1 returns q2, pos2
//-----------------------------------------------------------------------------
void BlendBones_PS3(
const bonejob_SPU *pBonejob,
const accumposeentry_SPU *pPoseEntry,
BoneQuaternion *q1,
BoneVector *pos1,
const int *boneMap,
const float *boneWeight,
const BoneQuaternion *q2,
const BoneVector *pos2,
float s,
int boneMask )
{
SNPROF_ANIM("BlendBones_PS3");
int i, j;
Quaternion q3;
if( s <= 0.0f )
{
Assert(0); // shouldn't have been called
return;
}
else if( s >= 1.0f )
{
//CMiniProfilerGuard mpguard(&g_lmp_BlendBones1, pStudioHdr->numbones());
Assert(0); // shouldn't have been called
for (i = 0; i < pBonejob->numBones; i++)
{
// skip unused bones
if( !( pBonejob->boneFlags[i] & boneMask) )
{
continue;
}
if( pPoseEntry->pEA_seqgroup_boneMap )
{
j = boneMap[i];
}
else
{
j = i;
}
if( j >= 0 && boneWeight[j] > 0.0f )
{
q1[i] = q2[i];
pos1[i] = pos2[i];
}
}
return;
}
float s2 = s;
float s1 = 1.0f - s2;
//CMiniProfilerGuard mpguard(&g_lmp_BlendBones2,pStudioHdr->numbones()); // 130-180 ticks without profilers; 167-190 ticks with all profilers on
int nMode = 2;//g_cv_BlendBonesMode.GetInt();
#ifndef DEDICATED
if(nMode)
{
const int numBones = pBonejob->numBones;
const int *RESTRICT pBonePseudoWeight = (int*)boneWeight;//(int*)seqdesc.pBoneweight(0); // we'll treat floats as ints to check for > 0.0
// int *RESTRICT pActiveBones = (int*)stackalloc(numBones * sizeof(int) * 2), *RESTRICT pActiveBonesEnd = pActiveBones;
int *pActiveBones = (int *)PushLSStack( numBones * sizeof(int) * 2 );
int *pActiveBonesEnd = pActiveBones;
{
// BONE_PROFILE_LOOP(BlendBoneLoop2a,numBones); // 20 ticks straight; 12-14 ticks 4 at a time; 14-19 ticks 8 at a time (compiler generated code)
i = 0;
#ifdef _X360 // on PC, this is slower
for(; i+3 < numBones; i+=4)
{
int isBoneActiveA = pStudioHdr->boneFlags(i ) & boneMask;
int isBoneActiveB = pStudioHdr->boneFlags(i+1) & boneMask;
int isBoneActiveC = pStudioHdr->boneFlags(i+2) & boneMask;
int isBoneActiveD = pStudioHdr->boneFlags(i+3) & boneMask;
isBoneActiveA = isBoneActiveA | -isBoneActiveA; // the high bit is now 1 iff the flags check
isBoneActiveB = isBoneActiveB | -isBoneActiveB; // the high bit is now 1 iff the flags check
isBoneActiveC = isBoneActiveC | -isBoneActiveC; // the high bit is now 1 iff the flags check
isBoneActiveD = isBoneActiveD | -isBoneActiveD; // the high bit is now 1 iff the flags check
isBoneActiveA = _rotl(isBoneActiveA,1) & 1; // now it's either 0 or 1
isBoneActiveB = _rotl(isBoneActiveB,1) & 1; // now it's either 0 or 1
isBoneActiveC = _rotl(isBoneActiveC,1) & 1; // now it's either 0 or 1
isBoneActiveD = _rotl(isBoneActiveD,1) & 1; // now it's either 0 or 1
*pActiveBonesEnd = i+0;
pActiveBonesEnd += isBoneActiveA;
*pActiveBonesEnd = i+1;
pActiveBonesEnd += isBoneActiveB;
*pActiveBonesEnd = i+2;
pActiveBonesEnd += isBoneActiveC;
*pActiveBonesEnd = i+3;
pActiveBonesEnd += isBoneActiveD;
}
#endif
for(; i < numBones; ++i)
{
*pActiveBonesEnd = i;
int isBoneActive = pBonejob->boneFlags[i] & boneMask;
isBoneActive = isBoneActive | -isBoneActive; // the high bit is now 1 iff the flags check
isBoneActive = _rotl(isBoneActive,1) & 1; // now it's either 0 or 1
pActiveBonesEnd += isBoneActive;
}
}
// now we have a list of bones whose flags & mask != 0
// we need to create bone pay
if( pPoseEntry->pEA_seqgroup_boneMap )// if( pSeqGroup )
{
int *pEnd = pActiveBones;
{
// BONE_PROFILE_LOOP(BlendBoneLoop2b,pActiveBonesEnd - pActiveBones);//21-25 straight; 16-18 4 at a time;
int *RESTRICT pActiveBone = pActiveBones;
#ifdef _X360 // on PC, this is slower
for(; pActiveBone + 3 < pActiveBonesEnd; pActiveBone += 4)
{
int nActiveBoneA = pActiveBone[0];
int nActiveBoneB = pActiveBone[1];
int nActiveBoneC = pActiveBone[2];
int nActiveBoneD = pActiveBone[3];
int nMappedBoneA = pSeqGroup->boneMap[nActiveBoneA];
int nMappedBoneB = pSeqGroup->boneMap[nActiveBoneB];
int nMappedBoneC = pSeqGroup->boneMap[nActiveBoneC];
int nMappedBoneD = pSeqGroup->boneMap[nActiveBoneD];
pEnd[numBones] = nMappedBoneA;
*pEnd = nActiveBoneA;
pEnd += _rotl(~nMappedBoneA,1) & 1; // if nMappedBone < 0, don't advance the end
pEnd[numBones] = nMappedBoneB;
*pEnd = nActiveBoneB;
pEnd += _rotl(~nMappedBoneB,1) & 1; // if nMappedBone < 0, don't advance the end
pEnd[numBones] = nMappedBoneC;
*pEnd = nActiveBoneC;
pEnd += _rotl(~nMappedBoneC,1) & 1; // if nMappedBone < 0, don't advance the end
pEnd[numBones] = nMappedBoneD;
*pEnd = nActiveBoneD;
pEnd += _rotl(~nMappedBoneD,1) & 1; // if nMappedBone < 0, don't advance the end
}
#endif
for(; pActiveBone < pActiveBonesEnd; ++pActiveBone)
{
int nActiveBone = *pActiveBone;
int nMappedBone = boneMap[ nActiveBone ];
pEnd[ numBones ] = nMappedBone;
*pEnd = nActiveBone;
pEnd += _rotl(~nMappedBone,1) & 1; // if nMappedBone < 0, don't advance the end
}
}
pActiveBonesEnd = pEnd; // the new end of the array of active bones, with negatively-mapped bones taken out
// now get rid of non-positively-weighted bones
pEnd = pActiveBones;
{
// BONE_PROFILE_LOOP(BlendBoneLoop2c,pActiveBonesEnd - pActiveBones);//18-23 straight; 14-17 ticks 4 at a time
int *RESTRICT pActiveBone = pActiveBones;
#ifdef _X360 // on PC, this is slower
int *RESTRICT pMappedBone = pActiveBones+numBones;
for(; pActiveBone+3 < pActiveBonesEnd; pActiveBone += 4, pMappedBone += 4)
{
int nActiveBoneA = pActiveBone[0];
int nActiveBoneB = pActiveBone[1];
int nActiveBoneC = pActiveBone[2];
int nActiveBoneD = pActiveBone[3];
int nMappedBoneA = pMappedBone[0];
int nMappedBoneB = pMappedBone[1];
int nMappedBoneC = pMappedBone[2];
int nMappedBoneD = pMappedBone[3];
int pseudoWeightA = pBonePseudoWeight[nMappedBoneA];
int pseudoWeightB = pBonePseudoWeight[nMappedBoneB];
int pseudoWeightC = pBonePseudoWeight[nMappedBoneC];
int pseudoWeightD = pBonePseudoWeight[nMappedBoneD];
*pEnd = nActiveBoneA;
pEnd += _rotl(-pseudoWeightA, 1) & 1; // pseudoWeight must be strictly positive to advance and let this bone stay
*pEnd = nActiveBoneB;
pEnd += _rotl(-pseudoWeightB, 1) & 1; // pseudoWeight must be strictly positive to advance and let this bone stay
*pEnd = nActiveBoneC;
pEnd += _rotl(-pseudoWeightC, 1) & 1; // pseudoWeight must be strictly positive to advance and let this bone stay
*pEnd = nActiveBoneD;
pEnd += _rotl(-pseudoWeightD, 1) & 1; // pseudoWeight must be strictly positive to advance and let this bone stay
}
#endif
for(; pActiveBone < pActiveBonesEnd; ++pActiveBone)
{
int nActiveBone = *pActiveBone;
int nMappedBone = pActiveBone[numBones];
int pseudoWeight = pBonePseudoWeight[nMappedBone];
*pEnd = nActiveBone;
pEnd += _rotl(-pseudoWeight, 1) & 1; // pseudoWeight must be strictly positive to advance and let this bone stay
}
}
pActiveBonesEnd = pEnd;
}
else
{
// one mapping stage off
// now get rid of non-positively-weighted bones
int *pEnd = pActiveBones;
// {BONE_PROFILE_LOOP(BlendBoneLoop2d,pActiveBonesEnd-pActiveBones);//20-50
for(int *RESTRICT pActiveBone = pActiveBones; pActiveBone < pActiveBonesEnd; ++pActiveBone)
{
int nActiveBone = *pActiveBone;
int pseudoWeight = pBonePseudoWeight[nActiveBone];
*pEnd = nActiveBone;
pEnd += _rotl(-pseudoWeight, 1) & 1; // pseudoWeight must be strictly positive to advance and let this bone stay
}//}
pActiveBonesEnd = pEnd;
}
enum
{
nBoneFixedAlignmentShift = GetLog2_t<BONE_FIXED_ALIGNMENT>::kLog2
};
// NOTE: When merging back to main, enable this code because Fixed-Alignment is not used in L4D, but may be used in main
fltx4 scale1 = ReplicateX4( s1 );
fltx4 scale2 = SubSIMD( Four_Ones, scale1 );
//fltx4 maskW = LoadAlignedSIMD( (const float *)(g_SIMD_ComponentMask[3]) );
// pass through all active bones to blend them; those that need it are already aligned
{
// 120-155 ticks 4 horizontal at a time; 130 ticks with 1 dot quaternion alignment
//
// BONE_PROFILE_LOOP(BlendBoneLoop2g,pActiveBonesEnd-pActiveBones);
const int *RESTRICT p = pActiveBones, *RESTRICT pNext;
#if 0//ndef _X360
// swizzled (vertical) 4 at a time processing
for(; (pNext = p+4) < pActiveBonesEnd; p = pNext)
{
int nBoneA = p[0], nBoneB = p[1], nBoneC = p[2], nBoneD = p[3];
BoneQuaternion *RESTRICT pq1A = &q1[nBoneA];
BoneQuaternion *RESTRICT pq1B = &q1[nBoneB];
BoneQuaternion *RESTRICT pq1C = &q1[nBoneC];
BoneQuaternion *RESTRICT pq1D = &q1[nBoneD];
const BoneQuaternion *RESTRICT pq2A = &q2[nBoneA];
const BoneQuaternion *RESTRICT pq2B = &q2[nBoneB];
const BoneQuaternion *RESTRICT pq2C = &q2[nBoneC];
const BoneQuaternion *RESTRICT pq2D = &q2[nBoneD];
float *pp1A = pos1[nBoneA].Base();
float *pp1B = pos1[nBoneB].Base();
float *pp1C = pos1[nBoneC].Base();
float *pp1D = pos1[nBoneD].Base();
const float *pp2A = pos2[nBoneA].Base();
const float *pp2B = pos2[nBoneB].Base();
const float *pp2C = pos2[nBoneC].Base();
const float *pp2D = pos2[nBoneD].Base();
FourQuaternions four4q1, four4q2;
four4q1.LoadAndSwizzleAligned(pq1A,pq1B,pq1C,pq1D);
four4q2.LoadAndSwizzleAligned(pq2A,pq2B,pq2C,pq2D);
FourVectors four4Pos1, four4Pos2;
four4Pos1.LoadAndSwizzleUnaligned(pp1A,pp1B,pp1C,pp1D);
four4Pos2.LoadAndSwizzleUnaligned(pp2A,pp2B,pp2C,pp2D);
four4q1 = QuaternionAlign(four4q2, four4q1);
FourQuaternions four4Blended = QuaternionNormalize(Madd( four4q1, scale1, Mul( four4q2 , scale2 )));
// now blend the linear parts
FourVectors f4PosBlended = Madd(four4Pos1, scale1, Mul(four4Pos2, scale2));
f4PosBlended.TransposeOntoUnaligned3(*(fltx4*)pp1A, *(fltx4*)pp1B, *(fltx4*)pp1C, *(fltx4*)pp1D);
four4Blended.SwizzleAndStoreAligned(pq1A,pq1B,pq1C,pq1D);
}
#else
// horizontal 4 at a time processing
for(; (pNext = p+4) < pActiveBonesEnd; p = pNext)
{
int nBoneA = p[0], nBoneB = p[1], nBoneC = p[2], nBoneD = p[3];
float *RESTRICT pq1A = q1[nBoneA].Base(), *pp1A = pos1[nBoneA].Base();
float *RESTRICT pq1B = q1[nBoneB].Base(), *pp1B = pos1[nBoneB].Base();
float *RESTRICT pq1C = q1[nBoneC].Base(), *pp1C = pos1[nBoneC].Base();
float *RESTRICT pq1D = q1[nBoneD].Base(), *pp1D = pos1[nBoneD].Base();
const float *RESTRICT pq2A = q2[nBoneA].Base(), *pp2A = pos2[nBoneA].Base();
const float *RESTRICT pq2B = q2[nBoneB].Base(), *pp2B = pos2[nBoneB].Base();
const float *RESTRICT pq2C = q2[nBoneC].Base(), *pp2C = pos2[nBoneC].Base();
const float *RESTRICT pq2D = q2[nBoneD].Base(), *pp2D = pos2[nBoneD].Base();
fltx4 f4q1A = LoadAlignedSIMD(pq1A), f4q2A = LoadAlignedSIMD(pq2A);
fltx4 f4q1B = LoadAlignedSIMD(pq1B), f4q2B = LoadAlignedSIMD(pq2B);
fltx4 f4q1C = LoadAlignedSIMD(pq1C), f4q2C = LoadAlignedSIMD(pq2C);
fltx4 f4q1D = LoadAlignedSIMD(pq1D), f4q2D = LoadAlignedSIMD(pq2D);
//ALIGN fltx4 f4Pos1A = LoadUnaligned3SIMD(pp1A), f4Pos2A = LoadUnaligned3SIMD(pp2A);
fltx4 f4Pos1A = LoadAlignedSIMD(pp1A), f4Pos2A = LoadAlignedSIMD(pp2A);
//ALIGN fltx4 f4Pos1B = LoadUnaligned3SIMD(pp1B), f4Pos2B = LoadUnaligned3SIMD(pp2B);
fltx4 f4Pos1B = LoadAlignedSIMD(pp1B), f4Pos2B = LoadAlignedSIMD(pp2B);
//ALIGN fltx4 f4Pos1C = LoadUnaligned3SIMD(pp1C), f4Pos2C = LoadUnaligned3SIMD(pp2C);
fltx4 f4Pos1C = LoadAlignedSIMD(pp1C), f4Pos2C = LoadAlignedSIMD(pp2C);
//ALIGN fltx4 f4Pos1D = LoadUnaligned3SIMD(pp1D), f4Pos2D = LoadUnaligned3SIMD(pp2D);
fltx4 f4Pos1D = LoadAlignedSIMD(pp1D), f4Pos2D = LoadAlignedSIMD(pp2D);
f4q1A = BoneQuaternionAlignSIMD(f4q2A, f4q1A);
f4q1B = BoneQuaternionAlignSIMD(f4q2B, f4q1B);
f4q1C = BoneQuaternionAlignSIMD(f4q2C, f4q1C);
f4q1D = BoneQuaternionAlignSIMD(f4q2D, f4q1D);
fltx4 f4BlendedA = MulSIMD( scale2, f4q2A );
fltx4 f4BlendedB = MulSIMD( scale2, f4q2B );
fltx4 f4BlendedC = MulSIMD( scale2, f4q2C );
fltx4 f4BlendedD = MulSIMD( scale2, f4q2D );
f4BlendedA = MaddSIMD( scale1, f4q1A, f4BlendedA );
f4BlendedB = MaddSIMD( scale1, f4q1B, f4BlendedB );
f4BlendedC = MaddSIMD( scale1, f4q1C, f4BlendedC );
f4BlendedD = MaddSIMD( scale1, f4q1D, f4BlendedD );
f4BlendedA = BoneQuaternionNormalizeSIMD(f4BlendedA);
f4BlendedB = BoneQuaternionNormalizeSIMD(f4BlendedB);
f4BlendedC = BoneQuaternionNormalizeSIMD(f4BlendedC);
f4BlendedD = BoneQuaternionNormalizeSIMD(f4BlendedD);
// now blend the linear parts
fltx4 f4PosBlendedA = MaddSIMD(scale1, f4Pos1A, MulSIMD(scale2,f4Pos2A));
fltx4 f4PosBlendedB = MaddSIMD(scale1, f4Pos1B, MulSIMD(scale2,f4Pos2B));
fltx4 f4PosBlendedC = MaddSIMD(scale1, f4Pos1C, MulSIMD(scale2,f4Pos2C));
fltx4 f4PosBlendedD = MaddSIMD(scale1, f4Pos1D, MulSIMD(scale2,f4Pos2D));
//f4PosBlended = MaskedAssign(maskW, f4Pos1, f4PosBlended);
StoreAlignedSIMD(pq1A,f4BlendedA);
//ALIGN StoreUnaligned3SIMD(pp1A, f4PosBlendedA);
StoreAlignedSIMD(pp1A, f4PosBlendedA);
StoreAlignedSIMD(pq1B,f4BlendedB);
//ALIGN StoreUnaligned3SIMD(pp1B, f4PosBlendedB);
StoreAlignedSIMD(pp1B, f4PosBlendedB);
StoreAlignedSIMD(pq1C,f4BlendedC);
//ALIGN StoreUnaligned3SIMD(pp1C, f4PosBlendedC);
StoreAlignedSIMD(pp1C, f4PosBlendedC);
StoreAlignedSIMD(pq1D,f4BlendedD);
//ALIGN StoreUnaligned3SIMD(pp1D, f4PosBlendedD);
StoreAlignedSIMD(pp1D, f4PosBlendedD);
}
#endif
for(; p < pActiveBonesEnd; ++p)
{
int nBone = *p;
float *RESTRICT pq1 = q1[nBone].Base(), *RESTRICT pp1 = pos1[nBone].Base();
const float *RESTRICT pq2 = q2[nBone].Base(), *RESTRICT pp2 = pos2[nBone].Base();
fltx4 f4q1 = LoadAlignedSIMD(pq1), f4q2 = LoadAlignedSIMD(pq2);
//ALIGN fltx4 f4Pos1 = LoadUnaligned3SIMD(pp1), f4Pos2 = LoadUnaligned3SIMD(pp2);
fltx4 f4Pos1 = LoadAlignedSIMD(pp1), f4Pos2 = LoadAlignedSIMD(pp2);
f4q1 = BoneQuaternionAlignSIMD(f4q2, f4q1);
fltx4 f4Blended = MulSIMD( scale2, f4q2 );
f4Blended = MaddSIMD( scale1, f4q1, f4Blended );
f4Blended = BoneQuaternionNormalizeSIMD(f4Blended);
// now blend the linear parts
fltx4 f4PosBlended = MaddSIMD(scale1, f4Pos1, MulSIMD(scale2,f4Pos2));
//f4PosBlended = MaskedAssign(maskW, f4Pos1, f4PosBlended);
StoreAlignedSIMD(pq1,f4Blended);
//ALIGN StoreUnaligned3SIMD(pp1, f4PosBlended);
StoreAlignedSIMD(pp1, f4PosBlended);
}
}
PopLSStack( numBones * sizeof(int) * 2 );
}
else
#endif // POSIX
{
// 360-400 ticks per loop pass
// there are usually 40-100 bones on average in a frame
for( i = 0; i < pBonejob->numBones; i++ )
{
// skip unused bones
if( !( pBonejob->boneFlags[i] & boneMask) )
{
continue;
}
if( pPoseEntry->pEA_seqgroup_boneMap )
{
j = boneMap[i];
}
else
{
j = i;
}
if( j >= 0 && boneWeight[j] > 0.0f )
{
if( pBonejob->boneFlags[i] & BONE_FIXED_ALIGNMENT)
{
QuaternionBlendNoAlign_PS3( q2[i], q1[i], s1, q3 );
}
else
{
QuaternionBlend_PS3( q2[i], q1[i], s1, q3 );
}
q1[i][0] = q3[0];
q1[i][1] = q3[1];
q1[i][2] = q3[2];
q1[i][3] = q3[3];
pos1[i][0] = pos1[i][0] * s1 + pos2[i][0] * s2;
pos1[i][1] = pos1[i][1] * s1 + pos2[i][1] * s2;
pos1[i][2] = pos1[i][2] * s1 + pos2[i][2] * s2;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Scale a set of bones. Must be of type delta
//-----------------------------------------------------------------------------
void ScaleBones_PS3(
const bonejob_SPU *pBonejob,
const accumposeentry_SPU *pPoseEntry,
BoneQuaternion *q1,
BoneVector *pos1,
const int *boneMap,
const float *boneWeight,
float s,
int boneMask )
{
SNPROF_ANIM("ScaleBones_PS3");
int i, j;
Quaternion q3;
float s2 = s;
float s1 = 1.0f - s2;
for (i = 0; i < pBonejob->numBones; i++)
{
// skip unused bones
if( !( pBonejob->boneFlags[i] & boneMask) )
{
continue;
}
if( pPoseEntry->pEA_seqgroup_boneMap )
{
j = boneMap[i];
}
else
{
j = i;
}
if( j >= 0 && boneWeight[j] > 0.0f )
{
QuaternionIdentityBlend_PS3( q1[i], s1, q1[i] );
VectorScale_PS3( pos1[i], s2, pos1[i] );
}
}
}
// temp - debugging DMA's
// NOINLINE void *SPUmemcpy_UnalignedGet( void *ls, uint32 ea, uint32_t size )
// {
// void *aligned_ls;
//
// aligned_ls = (void *)((uint32)ls | (ea & 0xf)); // + 0xf in case ls not 16B aligned
//
// #if defined(__SPU__)
// //Msg("GET ls:0x%x, ea:0x%x, size:%d\n", (uint32_t)aligned_ls, ea, size);
// // SPU
// cellDmaUnalignedGet( aligned_ls, ea, size, DMATAG_ANIM, 0, 0 );
// cellDmaWaitTagStatusAny( 1 << DMATAG_ANIM );
// #else
// // PPU
// memcpy( aligned_ls, (void *)ea, size );
// #endif
//
//
// return aligned_ls;
// }
//
//
// NOINLINE void SPUmemcpy_UnalignedPut( void *ls, uint32 ea, uint32_t size )
// {
// #if defined(__SPU__)
// //Msg("PUT ls:0x%x, ea:0x%x, size:%d\n", (uint32_t)ls, ea, size);
// // SPU
// cellDmaUnalignedPut( ls, ea, size, DMATAG_ANIM, 0, 0 );
// cellDmaWaitTagStatusAny( 1 << DMATAG_ANIM );
// #else
// Assert(((uint32)ls&0xf) == ea&0xf);
//
// // PPU
// memcpy( (void *)ea, ls, size );
// #endif
// }