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.
302 lines
7.9 KiB
302 lines
7.9 KiB
//========= Copyright © Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//==========================================================================//
|
|
|
|
#include "cmatrixstack.h"
|
|
|
|
|
|
// Define RUN_TEST_CODE to validate that CMatrixStack behaves the same as ID3DXMatrixStack
|
|
//#define RUN_TEST_CODE
|
|
#if defined( RUN_TEST_CODE )
|
|
|
|
#ifndef WIN32
|
|
#error sorry man
|
|
#endif
|
|
#ifdef _X360
|
|
#include "d3d9.h"
|
|
#include "d3dx9.h"
|
|
#else
|
|
#include <windows.h>
|
|
#include "../../dx9sdk/include/d3d9.h"
|
|
#include "../../dx9sdk/include/d3dx9.h"
|
|
#endif
|
|
|
|
#else // RUN_TEST_CODE
|
|
|
|
#if !defined( _X360 )
|
|
struct D3DXMATRIX : public VMatrix{};
|
|
struct D3DXVECTOR3 : public Vector{};
|
|
#endif // _X360
|
|
|
|
#endif // RUN_TEST_CODE
|
|
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
|
|
CMatrixStack::CMatrixStack( void )
|
|
{
|
|
m_Stack.AddToTail();
|
|
m_Stack.Tail().Identity();
|
|
}
|
|
|
|
CMatrixStack::~CMatrixStack( void )
|
|
{
|
|
Assert( m_Stack.Count() == 1 ); // Catch push/pop mismatch
|
|
Assert( m_Stack.Tail().IsIdentity() ); // Modifying the root matrix is probably unintentional
|
|
}
|
|
|
|
D3DXMATRIX *CMatrixStack::GetTop( void )
|
|
{
|
|
return (D3DXMATRIX *)&m_Stack.Tail();
|
|
}
|
|
|
|
void CMatrixStack::Push()
|
|
{
|
|
AssertMsg( m_Stack.Count() < 100, "CMatrixStack - Push/Pop mismatch!" ); // Catch push/pop mismatch
|
|
// Duplicate the current 'top' matrix (NOTE: AddToTail can realloc!)
|
|
m_Stack.AddToTail();
|
|
VMatrix *top = &m_Stack.Tail();
|
|
top[0] = top[-1];
|
|
}
|
|
|
|
void CMatrixStack::Pop()
|
|
{
|
|
AssertMsg( m_Stack.Count() > 1, "CMatrixStack - Push/Pop mismatch!" ); // Catch push/pop mismatch
|
|
m_Stack.RemoveMultipleFromTail( 1 );
|
|
}
|
|
|
|
void CMatrixStack::LoadIdentity()
|
|
{
|
|
m_Stack.Tail().Identity();
|
|
}
|
|
|
|
void CMatrixStack::LoadMatrix( const D3DXMATRIX *pMat )
|
|
{
|
|
COMPILE_TIME_ASSERT( sizeof( VMatrix ) == sizeof( D3DXMATRIX ) );
|
|
memcpy( &m_Stack.Tail(), pMat, sizeof( VMatrix ) );
|
|
}
|
|
|
|
void CMatrixStack::MultMatrix( const D3DXMATRIX *pMat )
|
|
{
|
|
// Right-multiply
|
|
VMatrix &top = m_Stack.Tail();
|
|
VMatrix result;
|
|
MatrixMultiply( top, *(const VMatrix *)pMat, result );
|
|
top = result;
|
|
}
|
|
|
|
void CMatrixStack::MultMatrixLocal( const D3DXMATRIX *pMat )
|
|
{
|
|
// Left-multiply
|
|
VMatrix &top = m_Stack.Tail();
|
|
VMatrix result;
|
|
MatrixMultiply( *(const VMatrix *)pMat, top, result );
|
|
top = result;
|
|
}
|
|
|
|
bool CMatrixStack::ScaleLocal( float x, float y, float z )
|
|
{
|
|
VMatrix scale;
|
|
MatrixBuildScale( scale, x, y, z );
|
|
// scale = scale.Transpose(); // A no-op, in this case :)
|
|
MultMatrixLocal( (D3DXMATRIX *)&scale );
|
|
return true;
|
|
}
|
|
|
|
bool CMatrixStack::RotateAxisLocal( const D3DXVECTOR3 *pV, float angleInRadians )
|
|
{
|
|
COMPILE_TIME_ASSERT( sizeof( Vector ) == sizeof( D3DXVECTOR3 ) );
|
|
const Vector &axis = *(const Vector *)pV;
|
|
VMatrix rotate;
|
|
MatrixBuildRotationAboutAxis( rotate, axis, angleInRadians * 180 / M_PI );
|
|
rotate = rotate.Transpose();
|
|
MultMatrixLocal( (D3DXMATRIX *)&rotate );
|
|
return true;
|
|
}
|
|
|
|
bool CMatrixStack::TranslateLocal( float x, float y, float z )
|
|
{
|
|
VMatrix translate;
|
|
MatrixBuildTranslation( translate, x, y, z );
|
|
translate = translate.Transpose();
|
|
MultMatrixLocal( (D3DXMATRIX *)&translate );
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================//
|
|
//
|
|
// Test code to ensure this produces the same results as ID3DMATRIXStack
|
|
//
|
|
//==========================================================================//
|
|
|
|
#if defined( RUN_TEST_CODE )
|
|
|
|
class CMatrixStack_Test
|
|
{
|
|
enum TestOps
|
|
{
|
|
LOAD_IDENT = 0,
|
|
LOAD_MAT = 1,
|
|
PUSH = 2,
|
|
POP = 3,
|
|
MULT = 4,
|
|
MULT_LOCAL = 5,
|
|
// Only these local/left-multiply variants are used by ShaderAPI (not the right-multiply ROTATE/TRANSLATE/SCALE versions)
|
|
SCALE_LOCAL = 6,
|
|
ROTATE_LOCAL = 7,
|
|
TRANSLATE_LOCAL = 8,
|
|
NUM_TEST_OPS
|
|
};
|
|
|
|
public:
|
|
CMatrixStack *m_pStack;
|
|
ID3DXMatrixStack *m_pD3DStack;
|
|
CMatrixStack_Test()
|
|
{
|
|
m_pStack = new CMatrixStack();
|
|
HRESULT result = D3DXCreateMatrixStack( 0, &m_pD3DStack );
|
|
if ( result == S_OK )
|
|
{
|
|
VMatrix testMatrix;
|
|
testMatrix.Identity();
|
|
MatrixTranslate( testMatrix, Vector( 1, 2, 3 ) );
|
|
MatrixRotate( testMatrix, Vector( 1, 0, 0 ), 90 );
|
|
MatrixTranslate( testMatrix, Vector( 4, 5, 6 ) );
|
|
// CMatrixStack mimics D3DX's transposed matrix style
|
|
testMatrix = testMatrix.Transpose();
|
|
|
|
// Leave the top matrix unmodified
|
|
m_pStack->Push();
|
|
m_pD3DStack->Push();
|
|
int depth = 2;
|
|
|
|
Msg( "CMatrixStack test...\n" );
|
|
srand(1352469);
|
|
for ( int i = 0; i < 1000; i++ )
|
|
{
|
|
TestOps op = (TestOps)( rand() % NUM_TEST_OPS );
|
|
switch( op )
|
|
{
|
|
case LOAD_IDENT:
|
|
Msg( "LOAD_IDENT\n" );
|
|
m_pStack->LoadIdentity();
|
|
m_pD3DStack->LoadIdentity();
|
|
break;
|
|
case LOAD_MAT:
|
|
Msg( "LOAD_MAT\n" );
|
|
m_pStack->LoadMatrix( (D3DXMATRIX *) &testMatrix );
|
|
m_pD3DStack->LoadMatrix( (D3DXMATRIX *) &testMatrix );
|
|
break;
|
|
case PUSH:
|
|
Msg( "PUSH\n" );
|
|
m_pStack->Push();
|
|
m_pD3DStack->Push();
|
|
depth++;
|
|
break;
|
|
case POP:
|
|
if ( depth > 2 ) // Leave the top matrix unmodified
|
|
{
|
|
Msg( "POP\n" );
|
|
m_pStack->Pop();
|
|
m_pD3DStack->Pop();
|
|
depth--;
|
|
}
|
|
break;
|
|
case MULT:
|
|
Msg( "MULT\n" );
|
|
m_pStack->MultMatrix( (D3DXMATRIX *) &testMatrix );
|
|
m_pD3DStack->MultMatrix( (D3DXMATRIX *) &testMatrix );
|
|
break;
|
|
case MULT_LOCAL:
|
|
Msg( "MULT_LOCAL\n" );
|
|
m_pStack->MultMatrixLocal( (D3DXMATRIX *) &testMatrix );
|
|
m_pD3DStack->MultMatrixLocal( (D3DXMATRIX *) &testMatrix );
|
|
break;
|
|
case SCALE_LOCAL:
|
|
Msg( "SCALE_LOCAL\n" );
|
|
if ( i & 1 )
|
|
{
|
|
m_pStack->ScaleLocal( 2.0f, 2.0f, 2.0f );
|
|
m_pD3DStack->ScaleLocal( 2.0f, 2.0f, 2.0f );
|
|
}
|
|
else
|
|
{
|
|
m_pStack->ScaleLocal( 0.5f, 0.5f, 0.5f );
|
|
m_pD3DStack->ScaleLocal( 0.5f, 0.5f, 0.5f );
|
|
}
|
|
break;
|
|
case ROTATE_LOCAL:
|
|
{
|
|
Msg( "ROTATE_LOCAL\n" );
|
|
float angleInRadians = ( (i&1)?+1:-1 )*0.5f*M_PI;
|
|
D3DXVECTOR3 axis(1,0,0);
|
|
if ( (i%3) == 1 ) axis = D3DXVECTOR3(0,1,0);
|
|
if ( (i%3) == 2 ) axis = D3DXVECTOR3(0,0,1);
|
|
m_pStack->RotateAxisLocal( &axis, angleInRadians );
|
|
m_pD3DStack->RotateAxisLocal( &axis, angleInRadians );
|
|
break;
|
|
}
|
|
case TRANSLATE_LOCAL:
|
|
{
|
|
Msg( "TRANSLATE_LOCAL\n" );
|
|
Vector delta = RandomVector( -10, +10 );
|
|
m_pStack->TranslateLocal( delta.x, delta.y, delta.z );
|
|
m_pD3DStack->TranslateLocal( delta.x, delta.y, delta.z );
|
|
break;
|
|
}
|
|
}
|
|
CompareTopMatrices( op );
|
|
}
|
|
while( depth > 1 )
|
|
{
|
|
m_pStack->Pop();
|
|
m_pD3DStack->Pop();
|
|
depth--;
|
|
CompareTopMatrices( POP );
|
|
}
|
|
|
|
m_pD3DStack->Release();
|
|
}
|
|
delete m_pStack;
|
|
}
|
|
|
|
void CompareTopMatrices( TestOps op )
|
|
{
|
|
// Compare the top matrices
|
|
float mat[4][4], d3dMat[4][4];
|
|
COMPILE_TIME_ASSERT( sizeof( D3DXMATRIX ) == 16*sizeof( float ) );
|
|
memcpy( mat, m_pStack->GetTop(), sizeof( D3DXMATRIX ) );
|
|
memcpy( d3dMat, m_pD3DStack->GetTop(), sizeof( D3DXMATRIX ) );
|
|
for ( int y = 0; y < 4; y++ )
|
|
{
|
|
for ( int x = 0; x < 4; x++ )
|
|
{
|
|
static float absEpsilon = 1.0e-5f, relEpsilon = 1.0e-4f;
|
|
float absolute = fabsf( mat[y][x] - d3dMat[y][x] );
|
|
float relative = 0;
|
|
if ( fabsf( d3dMat[y][x] ) > 0.00001f )
|
|
{
|
|
relative = fabsf( ( mat[y][x] / d3dMat[y][x] ) - 1.0f );
|
|
}
|
|
AssertMsg9( ( absolute <= absEpsilon ) && ( relative <= relEpsilon ),
|
|
"DIFFERENCE! CMatrixStack[%d][%d] = %10f, ID3DXMatrixStack[%d][%d] = %10f (OP: %d, Absolute diff: %10e, Relative diff: %10e)\n",
|
|
y, x, mat[y][x],
|
|
y, x, d3dMat[y][x],
|
|
op, absolute, relative );
|
|
}
|
|
}
|
|
|
|
// Copy the D3D version back onto ours, to negate accumulated numerical differences
|
|
m_pStack->LoadMatrix( m_pD3DStack->GetTop() );
|
|
}
|
|
};
|
|
static CMatrixStack_Test g_MatrixStackTest;
|
|
|
|
#endif // defined( RUN_TEST_CODE )
|