Team Fortress 2 Source Code as on 22/4/2020
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.
 
 
 
 
 
 

928 lines
23 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "d3dapp.h"
#include "d3dx8math.h"
#include "mathlib/mathlib.h"
#include "ScratchPad3D.h"
#include "tier1/strtools.h"
#define SPColorExpand( c ) (c).m_vColor.x, (c).m_vColor.y, (c).m_vColor.z, (c).m_flAlpha
class VertPosDiffuse
{
public:
inline void Init(Vector const &vPos, float r, float g, float b, float a)
{
m_Pos = vPos;
SetDiffuse( r, g, b, a );
}
inline void SetDiffuse( float r, float g, float b, float a )
{
m_Diffuse[0] = (unsigned char)(b * 255.9f);
m_Diffuse[1] = (unsigned char)(g * 255.9f);
m_Diffuse[2] = (unsigned char)(r * 255.9f);
m_Diffuse[3] = (unsigned char)(a * 255.9f);
}
inline void SetDiffuse( Vector const &vColor )
{
SetDiffuse( vColor.x, vColor.y, vColor.z, 1 );
}
inline void SetTexCoords( const Vector2D &tCoords )
{
m_tCoords = tCoords;
}
static inline DWORD GetFVF() {return D3DFVF_DIFFUSE | D3DFVF_XYZ | D3DFVF_TEX1;}
Vector m_Pos;
unsigned char m_Diffuse[4];
Vector2D m_tCoords;
};
class PosController
{
public:
Vector m_vPos;
QAngle m_vAngles;
};
int g_nLines, g_nPolygons;
PosController g_ViewController;
Vector g_IdentityBasis[3] = {Vector(1,0,0), Vector(0,1,0), Vector(0,0,1)};
Vector g_ViewerPos;
Vector g_ViewerBasis[3];
VMatrix g_mModelView;
CScratchPad3D *g_pScratchPad = NULL;
FILETIME g_LastWriteTime;
char g_Filename[256];
// ------------------------------------------------------------------------------------------ //
// Helper functions.
// ------------------------------------------------------------------------------------------ //
inline float FClamp(float val, float min, float max)
{
return (val < min) ? min : (val > max ? max : val);
}
inline float FMin(float val1, float val2)
{
return (val1 < val2) ? val1 : val2;
}
inline float FMax(float val1, float val2)
{
return (val1 > val2) ? val1 : val2;
}
inline float CosDegrees(float angle)
{
return (float)cos(DEG2RAD(angle));
}
inline float SinDegrees(float angle)
{
return (float)sin(DEG2RAD(angle));
}
inline float FRand(float a, float b)
{
return a + (b - a) * ((float)rand() / VALVE_RAND_MAX);
}
void CheckResult( HRESULT hr )
{
if ( FAILED( hr ) )
{
Assert( 0 );
}
}
void DrawLine2(const Vector &vFrom, const Vector &vTo, float r1, float g1, float b1, float a1, float r2, float g2, float b2, float a2)
{
VertPosDiffuse verts[2];
verts[0].Init( vFrom, r1, g1, b1, 1 );
verts[1].Init( vTo, r2, g2, b2, 1 );
CheckResult ( g_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE ) );
CheckResult( g_pDevice->SetTexture( 0, NULL ) );
CheckResult( g_pDevice->DrawPrimitiveUP( D3DPT_LINELIST, 1, verts, sizeof(verts[0]) ) );
++g_nLines;
}
void DrawLine(const Vector &vFrom, const Vector &vTo, float r, float g, float b, float a)
{
DrawLine2(vFrom, vTo, r,g,b,a, r,g,b,a);
}
// zAngle's range is [-90,90].
// When zAngle is 0, the position is in the middle of the sphere (vertically).
// When zAngle is 90, the position is at the top of the sphere.
// When zAngle is -90, the position is at the bottom of the sphere.
Vector CalcSphereVecAngles(float xyAngle, float zAngle, float fRadius)
{
Vector vec;
vec.x = CosDegrees(xyAngle) * CosDegrees(zAngle);
vec.y = SinDegrees(xyAngle) * CosDegrees(zAngle);
vec.z = SinDegrees(zAngle);
return vec * fRadius;
}
// Figure out the rotation to look from vEye to vDest.
void SetupLookAt( const Vector &vEye, const Vector &vDest, Vector basis[3] )
{
basis[0] = (vDest - vEye); // Forward.
VectorNormalize( basis[0] );
basis[2].Init(0.0f, 0.0f, 1.0f); // Up.
basis[1] = basis[2].Cross(basis[0]); // Left.
VectorNormalize( basis[1] );
basis[2] = basis[0].Cross(basis[1]); // Regenerate up.
VectorNormalize( basis[2] );
}
D3DMATRIX* VEngineToTempD3DMatrix( VMatrix const &mat )
{
static VMatrix ret;
ret = mat.Transpose();
return (D3DMATRIX*)&ret;
}
void UpdateView(float mouseDeltaX, float mouseDeltaY)
{
VMatrix mRot;
PosController *pController;
pController = &g_ViewController;
// WorldCraft-like interface..
if( Sys_HasFocus() )
{
Vector vForward, vUp, vRight;
AngleVectors( pController->m_vAngles, &vForward, &vRight, &vUp );
static float fAngleScale = 0.4f;
static float fDistScale = 0.5f;
if( Sys_GetKeyState( APPKEY_LBUTTON ) )
{
if( Sys_GetKeyState( APPKEY_RBUTTON ) )
{
// Ok, move forward and backwards.
pController->m_vPos += vForward * -mouseDeltaY * fDistScale;
pController->m_vPos += vRight * mouseDeltaX * fDistScale;
}
else
{
pController->m_vAngles.y += -mouseDeltaX * fAngleScale;
pController->m_vAngles.x += mouseDeltaY * fAngleScale;
}
}
else if( Sys_GetKeyState( APPKEY_RBUTTON ) )
{
pController->m_vPos += vUp * -mouseDeltaY * fDistScale;
pController->m_vPos += vRight * mouseDeltaX * fDistScale;
}
}
// Set the projection matrix to 90 degrees.
D3DXMATRIX matProj;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/2, Sys_ScreenWidth() / (float)Sys_ScreenHeight(), 1.0f, 10000.0f );
g_pDevice->SetTransform( D3DTS_PROJECTION, &matProj );
// This matrix converts from D3D coordinates (X=right, Y=up, Z=forward)
// to VEngine coordinates (X=forward, Y=left, Z=up).
VMatrix mD3DToVEngine(
0.0f, 0.0f, 1.0f, 0.0f,
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
g_ViewerPos = pController->m_vPos;
mRot = SetupMatrixAngles( pController->m_vAngles );
g_mModelView = ~mD3DToVEngine * mRot.Transpose3x3() * SetupMatrixTranslation(-g_ViewerPos);
CheckResult( g_pDevice->SetTransform( D3DTS_VIEW, VEngineToTempD3DMatrix(g_mModelView) ) );
// World matrix is identity..
VMatrix mIdentity = SetupMatrixIdentity();
CheckResult( g_pDevice->SetTransform( D3DTS_WORLD, (D3DMATRIX*)&mIdentity ) );
}
// ------------------------------------------------------------------------------------------ //
// ScratchPad3D command implementation.
// ------------------------------------------------------------------------------------------ //
void CommandRender_Point( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice )
{
CScratchPad3D::CCommand_Point *pCmd = (CScratchPad3D::CCommand_Point*)pInCmd;
g_pDevice->SetRenderState( D3DRS_POINTSIZE, *((DWORD*)&pCmd->m_flPointSize) );
VertPosDiffuse vert;
vert.Init( pCmd->m_Vert.m_vPos, SPColorExpand(pCmd->m_Vert.m_vColor) );
g_pDevice->DrawPrimitiveUP( D3DPT_POINTLIST, 1, &vert, sizeof(vert) );
}
VertPosDiffuse g_LineBatchVerts[1024];
int g_nLineBatchVerts = 0;
void CommandRender_LinesStart( IDirect3DDevice8 *pDevice )
{
// Set states for line drawing.
CheckResult( g_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ) );
CheckResult( g_pDevice->SetTexture( 0, NULL ) );
}
void CommandRender_LinesStop( IDirect3DDevice8 *pDevice )
{
CheckResult( g_pDevice->DrawPrimitiveUP( D3DPT_LINELIST, g_nLineBatchVerts / 2, g_LineBatchVerts, sizeof(g_LineBatchVerts[0]) ) );
g_nLines += g_nLineBatchVerts / 2;
g_nLineBatchVerts = 0;
}
void CommandRender_Line( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice )
{
CScratchPad3D::CCommand_Line *pCmd = (CScratchPad3D::CCommand_Line*)pInCmd;
// Flush out the line cache?
if ( g_nLineBatchVerts == sizeof( g_LineBatchVerts ) / sizeof( g_LineBatchVerts[0] ) )
{
CommandRender_LinesStop( pDevice );
}
g_LineBatchVerts[g_nLineBatchVerts].m_Pos = pCmd->m_Verts[0].m_vPos;
g_LineBatchVerts[g_nLineBatchVerts].SetDiffuse( SPColorExpand( pCmd->m_Verts[0].m_vColor ) );
++g_nLineBatchVerts;
g_LineBatchVerts[g_nLineBatchVerts].m_Pos = pCmd->m_Verts[1].m_vPos;
g_LineBatchVerts[g_nLineBatchVerts].SetDiffuse( SPColorExpand( pCmd->m_Verts[1].m_vColor ) );
++g_nLineBatchVerts;
}
void CommandRender_Polygon( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice )
{
VertPosDiffuse verts[65];
CScratchPad3D::CCommand_Polygon *pCmd = (CScratchPad3D::CCommand_Polygon*)pInCmd;
int nVerts = min( 64, pCmd->m_Verts.Size() );
for( int i=0; i < nVerts; i++ )
{
verts[i].m_Pos[0] = pCmd->m_Verts[i].m_vPos.x;
verts[i].m_Pos[1] = pCmd->m_Verts[i].m_vPos.y;
verts[i].m_Pos[2] = pCmd->m_Verts[i].m_vPos.z;
verts[i].SetDiffuse( SPColorExpand( pCmd->m_Verts[i].m_vColor ) );
}
// Draw wireframe manually since D3D draws internal edges of the triangle fan.
DWORD dwFillMode;
g_pDevice->GetRenderState( D3DRS_FILLMODE, &dwFillMode );
if( dwFillMode == D3DFILL_WIREFRAME )
{
if( nVerts >= 2 )
{
g_pDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, nVerts-1, verts, sizeof(verts[0]) );
verts[nVerts] = verts[0];
g_pDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, 1, &verts[nVerts-1], sizeof(verts[0]) );
}
}
else
{
CheckResult( g_pDevice->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, nVerts - 2, verts, sizeof(verts[0]) ) );
}
++g_nPolygons;
}
void CommandRender_Matrix( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice )
{
CScratchPad3D::CCommand_Matrix *pCmd = (CScratchPad3D::CCommand_Matrix*)pInCmd;
VMatrix mTransposed = pCmd->m_mMatrix.Transpose();
CheckResult( g_pDevice->SetTransform( D3DTS_WORLD, (D3DMATRIX*)mTransposed.m ) );
}
void CommandRender_RenderState( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice )
{
CScratchPad3D::CCommand_RenderState *pCmd = (CScratchPad3D::CCommand_RenderState*)pInCmd;
switch( pCmd->m_State )
{
case IScratchPad3D::RS_FillMode:
{
if( pCmd->m_Val == IScratchPad3D::FillMode_Wireframe )
g_pDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
else
g_pDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
}
break;
case IScratchPad3D::RS_ZRead:
{
g_pDevice->SetRenderState( D3DRS_ZENABLE, pCmd->m_Val );
}
break;
case IScratchPad3D::RS_ZBias:
{
g_pDevice->SetRenderState( D3DRS_ZBIAS, pCmd->m_Val );
}
break;
}
}
class CCachedTextData : public CScratchPad3D::ICachedRenderData
{
public:
CCachedTextData()
{
m_pTexture = NULL;
}
~CCachedTextData()
{
if ( m_pTexture )
m_pTexture->Release();
}
virtual void Release()
{
delete this;
}
IDirect3DTexture8 *m_pTexture;
int m_BitmapWidth;
int m_BitmapHeight;
int m_nChars;
};
void GenerateTextGreyscaleBitmap(
const char *pText,
CUtlVector<unsigned char> &bitmap,
int *pWidth,
int *pHeight )
{
*pWidth = *pHeight = 0;
// Create a bitmap, font, and HDC.
HDC hDC = CreateCompatibleDC( NULL );
Assert( hDC );
HFONT hFont = ::CreateFontA(
18, // font height
0, 0, 0,
FW_MEDIUM,
false,
false,
false,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
ANTIALIASED_QUALITY,
DEFAULT_PITCH | FF_DONTCARE,
"Arial" );
Assert( hDC );
if ( !hFont )
{
DeleteDC( hDC );
return;
}
// Create a bitmap. Allow for width of 512. Hopefully, that can fit all the text we need.
int bigImageWidth = 512;
int bigImageHeight = 64;
BITMAPINFOHEADER bmi;
memset( &bmi, 0, sizeof( bmi ) );
bmi.biSize = sizeof(BITMAPINFOHEADER);
bmi.biWidth = bigImageWidth;
bmi.biHeight = -bigImageHeight;
bmi.biBitCount = 24;
bmi.biPlanes = 1;
bmi.biCompression = BI_RGB;
void *pBits = NULL;
HBITMAP hBitmap = CreateDIBSection( hDC,
(BITMAPINFO*)&bmi, DIB_RGB_COLORS, (void**)&pBits, NULL, 0 );
Assert( hBitmap && pBits );
if ( !hBitmap )
{
DeleteObject( hFont );
DeleteDC( hDC );
return;
}
// Select the font and bitmap into the DC.
HFONT hOldFont = (HFONT)SelectObject( hDC, hFont );
HBITMAP hOldBitmap = (HBITMAP)SelectObject( hDC, hBitmap );
// Draw the text into the DC.
SIZE size;
int textLen = strlen( pText );
GetTextExtentPoint32( hDC, pText, textLen, &size );
TextOut( hDC, 0, 0, pText, textLen );
// Now copy the bits out.
const unsigned char *pSrcBase = (const unsigned char*)pBits;
*pWidth = size.cx;
*pHeight = size.cy;
bitmap.SetSize( size.cy * size.cx );
for ( int y=0; y < size.cy; y++ )
{
for ( int x=0; x < size.cx; x++ )
{
const unsigned char *pSrc = &pSrcBase[ (y*bigImageWidth+x) * 3 ];
unsigned char *pDest = &bitmap[y * size.cx + x];
int avg = (pSrc[0] + pSrc[1] + pSrc[2]) / 3;
*pDest = 0xFF - (unsigned char)avg;
}
}
// Unselect the objects from the DC and cleanup everything.
SelectObject( hDC, hOldFont );
DeleteObject( hFont );
SelectObject( hDC, hOldBitmap );
DeleteObject( hBitmap );
DeleteDC( hDC );
}
IDirect3DTexture8* MakeD3DTextureFromBitmap(
CUtlVector<unsigned char> &bitmap,
int width,
int height,
bool bSolidBackground )
{
IDirect3DTexture8 *pRet = NULL;
HRESULT hr = g_pDevice->CreateTexture(
width,
height,
1,
0,
D3DFMT_A8R8G8B8,
D3DPOOL_MANAGED,
&pRet );
if ( !pRet || FAILED( hr ) )
return NULL;
// Lock the texture and fill it up.
D3DLOCKED_RECT lockedRect;
hr = pRet->LockRect( 0, &lockedRect, NULL, 0 );
if ( FAILED( hr ) )
{
Assert( false );
pRet->Release();
return NULL;
}
// Now fill it up.
unsigned char *pDestData = (unsigned char*)lockedRect.pBits;
for ( int y=0; y < height; y++ )
{
for ( int x=0; x < width; x++ )
{
unsigned char *pDestPixel = &pDestData[ (y*lockedRect.Pitch + x*4) ];
unsigned char cSrcColor = bitmap[y*width+x];
if ( bSolidBackground )
{
pDestPixel[3] = 0xFF;
pDestPixel[0] = pDestPixel[1] = pDestPixel[2] = cSrcColor;
}
else
{
pDestPixel[0] = pDestPixel[1] = pDestPixel[2] = pDestPixel[3] = 0xFF;
pDestPixel[3] = cSrcColor;
}
}
}
pRet->UnlockRect( 0 );
return pRet;
}
void CommandRender_Text( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice )
{
CScratchPad3D::CCommand_Text *pCmd = (CScratchPad3D::CCommand_Text*)pInCmd;
CTextParams *pParams = &pCmd->m_TextParams;
// First, see if we've already generated the texture for this string.
CCachedTextData *pCached = (CCachedTextData*)pCmd->m_pCachedRenderData;
if ( !pCached )
{
// Generate a bitmap for this text string.
CUtlVector<unsigned char> bitmap;
int width, height;
GenerateTextGreyscaleBitmap( pCmd->m_String.Base(), bitmap, &width, &height );
// Convert the bitmap into a D3D texture.
pCached = new CCachedTextData;
pCached->m_pTexture = MakeD3DTextureFromBitmap( bitmap, width, height, pParams->m_bSolidBackground );
pCached->m_BitmapWidth = width;
pCached->m_BitmapHeight = height;
pCached->m_nChars = strlen( pCmd->m_String.Base() );
// Cache it.
pCmd->m_pCachedRenderData = pCached;
}
// Figure out its orientation vectors.
Vector vForward, vRight, vUp;
AngleVectors( pParams->m_vAngles, &vForward, &vRight, &vUp );
// Backface removal?
bool bFlip = true;
if ( vForward.Dot( g_ViewerPos - pParams->m_vPos ) < 0 )
{
if ( pParams->m_bTwoSided )
bFlip = false;
else
return;
}
// This is really kludgy, but it's the best info we have here.
float flTotalWidth = pParams->m_flLetterWidth * pCached->m_nChars;
float flAvgCharWidth = (float)flTotalWidth / pCached->m_nChars;
float flTotalHeight = flAvgCharWidth * 3;
Vector vShift( 0, 0, 0 );
if ( pParams->m_bCentered )
vShift = vRight * ( -flTotalWidth/2 ) + vUp * ( flTotalHeight/2 );
// Now draw the quad with the texture in it.
VertPosDiffuse quad[5]; // Leave space for 1 more for the line strip for the border.
quad[0].m_Pos = pParams->m_vPos;
quad[1].m_Pos = pParams->m_vPos + vRight * flTotalWidth;
quad[2].m_Pos = quad[1].m_Pos - vUp * flTotalHeight;
quad[3].m_Pos = pParams->m_vPos - vUp * flTotalHeight;
// Set tex coords.
if ( bFlip )
{
quad[0].m_tCoords.Init( 1, 0 );
quad[1].m_tCoords.Init( 0, 0 );
quad[2].m_tCoords.Init( 0, 1 );
quad[3].m_tCoords.Init( 1, 1 );
}
else
{
quad[0].m_tCoords.Init( 0, 0 );
quad[1].m_tCoords.Init( 1, 0 );
quad[2].m_tCoords.Init( 1, 1 );
quad[3].m_tCoords.Init( 0, 1 );
}
for ( int i=0; i < 4; i++ )
{
quad[i].m_Pos += vShift;
quad[i].SetDiffuse( pParams->m_vColor.x, pParams->m_vColor.y, pParams->m_vColor.z, pParams->m_flAlpha );
}
// Draw.
// Backup render states.
DWORD tss[][3] = {
{ D3DTSS_COLOROP, D3DTOP_MODULATE, 0 },
{ D3DTSS_COLORARG1, D3DTA_DIFFUSE, 0 },
{ D3DTSS_COLORARG2, D3DTA_TEXTURE, 0 },
{ D3DTSS_ALPHAOP, D3DTOP_MODULATE, 0 },
{ D3DTSS_ALPHAARG1, D3DTA_DIFFUSE, 0 },
{ D3DTSS_ALPHAARG2, D3DTA_TEXTURE, 0 }
};
#define NUM_TSS ( sizeof( tss ) / sizeof( tss[0] ) )
DWORD rss[][3] = {
{ D3DRS_ALPHABLENDENABLE, TRUE, 0 },
{ D3DRS_FILLMODE, D3DFILL_SOLID, 0 },
{ D3DRS_SRCBLEND, D3DBLEND_SRCALPHA, 0 },
{ D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA, 0 },
{ D3DRS_FILLMODE, D3DFILL_SOLID, 0 }
};
#define NUM_RSS ( sizeof( rss ) / sizeof( rss[0] ) )
for ( int i=0; i < NUM_TSS; i++ )
{
g_pDevice->GetTextureStageState( 0, (D3DTEXTURESTAGESTATETYPE)tss[i][0], &tss[i][2] );
g_pDevice->SetTextureStageState( 0, (D3DTEXTURESTAGESTATETYPE)tss[i][0], tss[i][1] );
}
for ( int i=0; i < NUM_RSS; i++ )
{
g_pDevice->GetRenderState( (D3DRENDERSTATETYPE)rss[i][0], &rss[i][2] );
g_pDevice->SetRenderState( (D3DRENDERSTATETYPE)rss[i][0], rss[i][1] );
}
g_pDevice->SetTexture( 0, pCached->m_pTexture );
CheckResult( g_pDevice->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, 2, quad, sizeof(quad[0]) ) );
g_pDevice->SetTexture( 0, NULL );
++g_nPolygons;
// Restore render states.
for ( int i=0; i < NUM_TSS; i++ )
g_pDevice->SetTextureStageState( 0, (D3DTEXTURESTAGESTATETYPE)tss[i][0], tss[i][2] );
for ( int i=0; i < NUM_RSS; i++ )
g_pDevice->SetRenderState( (D3DRENDERSTATETYPE)rss[i][0], rss[i][2] );
// Draw wireframe outline..
if ( pParams->m_bOutline )
{
DWORD fillMode;
g_pDevice->GetRenderState( D3DRS_FILLMODE, &fillMode );
g_pDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
quad[4] = quad[0];
g_pDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, 4, quad, sizeof(quad[0]) );
g_pDevice->SetRenderState( D3DRS_FILLMODE, fillMode );
}
}
typedef void (*CommandRenderFunction_Start)( IDirect3DDevice8 *pDevice );
typedef void (*CommandRenderFunction_Stop)( IDirect3DDevice8 *pDevice );
typedef void (*CommandRenderFunction)( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice );
class CCommandRenderFunctions
{
public:
CommandRenderFunction_Start m_StartFn;
CommandRenderFunction_Start m_StopFn;
CommandRenderFunction m_RenderFn;
};
CCommandRenderFunctions g_CommandRenderFunctions[CScratchPad3D::COMMAND_NUMCOMMANDS] =
{
{ NULL, NULL, CommandRender_Point },
{ CommandRender_LinesStart, CommandRender_LinesStop, CommandRender_Line },
{ NULL, NULL, CommandRender_Polygon },
{ NULL, NULL, CommandRender_Matrix },
{ NULL, NULL, CommandRender_RenderState },
{ NULL, NULL, CommandRender_Text }
};
void RunCommands( )
{
// Set all the initial states.
g_pDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
g_pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );
VMatrix mIdentity = SetupMatrixIdentity();
g_pDevice->SetTransform( D3DTS_WORLD, (D3DMATRIX*)&mIdentity );
int iLastCmd = -1;
for( int i=0; i < g_pScratchPad->m_Commands.Size(); i++ )
{
CScratchPad3D::CBaseCommand *pCmd = g_pScratchPad->m_Commands[i];
if( pCmd->m_iCommand >= 0 && pCmd->m_iCommand < CScratchPad3D::COMMAND_NUMCOMMANDS )
{
// Call the start/stop handlers for this command type if they exist.
// These can be used to batch primitives.
if ( pCmd->m_iCommand != iLastCmd )
{
if ( iLastCmd != -1 )
{
if ( g_CommandRenderFunctions[iLastCmd].m_StopFn )
g_CommandRenderFunctions[iLastCmd].m_StopFn( g_pDevice );
}
iLastCmd = pCmd->m_iCommand;
if ( g_CommandRenderFunctions[pCmd->m_iCommand].m_StartFn )
g_CommandRenderFunctions[pCmd->m_iCommand].m_StartFn( g_pDevice );
}
g_CommandRenderFunctions[pCmd->m_iCommand].m_RenderFn( pCmd, g_pDevice );
}
}
// Call the final stop function.
if ( iLastCmd != -1 )
{
if ( g_CommandRenderFunctions[iLastCmd].m_StopFn )
g_CommandRenderFunctions[iLastCmd].m_StopFn( g_pDevice );
}
}
bool CheckForNewFile( bool bForce )
{
// See if the file has changed..
HANDLE hFile = CreateFile(
g_pScratchPad->m_pFilename,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL );
if( !hFile )
return false;
FILETIME createTime, accessTime, writeTime;
if( !GetFileTime( hFile, &createTime, &accessTime, &writeTime ) )
{
CloseHandle( hFile );
return false;
}
bool bChange = false;
if( memcmp(&writeTime, &g_LastWriteTime, sizeof(writeTime)) != 0 || bForce )
{
bChange = g_pScratchPad->LoadCommandsFromFile();
if( bChange )
{
memcpy( &g_LastWriteTime, &writeTime, sizeof(writeTime) );
}
}
CloseHandle( hFile );
return bChange;
}
// ------------------------------------------------------------------------------------------ //
// App callbacks.
// ------------------------------------------------------------------------------------------ //
void UpdateWindowText()
{
char str[512];
sprintf( str, "ScratchPad3DViewer: <%s> lines: %d, polygons: %d", g_Filename, g_nLines, g_nPolygons );
Sys_SetWindowText( str );
}
void AppInit()
{
// Viewer info.
g_ViewController.m_vPos.Init( -200, 0, 0 );
g_ViewController.m_vAngles.Init( 0, 0, 0 );
char const *pFilename = Sys_FindArg( "-file", "scratch.pad" );
Q_strncpy( g_Filename, pFilename, sizeof( g_Filename ) );
IFileSystem *pFileSystem = ScratchPad3D_SetupFileSystem();
if( !pFileSystem || pFileSystem->Init() != INIT_OK )
{
Sys_Quit();
}
// FIXME: I took this out of scratchpad 3d, not sure if this is even necessary any more
pFileSystem->AddSearchPath( ".", "PLATFORM" );
g_pScratchPad = new CScratchPad3D( pFilename, pFileSystem, false );
g_nLines = g_nPolygons = 0;
UpdateWindowText();
g_pDevice->SetRenderState( D3DRS_EDGEANTIALIAS, FALSE );
g_pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
g_pDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
g_pDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_DIFFUSE );
g_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
g_pDevice->SetTexture( 0, NULL );
// Setup point scaling parameters.
float flOne=1;
float flZero=0;
g_pDevice->SetRenderState( D3DRS_POINTSCALEENABLE, TRUE );
g_pDevice->SetRenderState( D3DRS_POINTSCALE_A, *((DWORD*)&flZero) );
g_pDevice->SetRenderState( D3DRS_POINTSCALE_B, *((DWORD*)&flZero) );
g_pDevice->SetRenderState( D3DRS_POINTSCALE_C, *((DWORD*)&flOne) );
memset( &g_LastWriteTime, 0, sizeof(g_LastWriteTime) );
}
void AppRender( float frametime, float mouseDeltaX, float mouseDeltaY, bool bInvalidRect )
{
g_nLines = 0;
g_nPolygons = 0;
g_pDevice->SetVertexShader( VertPosDiffuse::GetFVF() );
if( !bInvalidRect &&
!Sys_GetKeyState( APPKEY_LBUTTON ) &&
!Sys_GetKeyState( APPKEY_RBUTTON ) &&
!CheckForNewFile(false) )
{
Sys_Sleep( 100 );
return;
}
g_pDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1, 0 );
g_pDevice->BeginScene();
UpdateView( mouseDeltaX, mouseDeltaY );
RunCommands();
g_pDevice->EndScene();
g_pDevice->Present( NULL, NULL, NULL, NULL );
UpdateWindowText();
}
void AppPreResize()
{
for( int i=0; i < g_pScratchPad->m_Commands.Size(); i++ )
{
CScratchPad3D::CBaseCommand *pCmd = g_pScratchPad->m_Commands[i];
if ( pCmd->m_iCommand == CScratchPad3D::COMMAND_TEXT )
{
// Delete the cached data if there is any.
pCmd->ReleaseCachedRenderData();
}
}
}
void AppPostResize()
{
}
void AppExit( )
{
}
void AppKey( int key, int down )
{
if( key == 27 )
{
Sys_Quit();
}
else if( toupper(key) == 'U' )
{
CheckForNewFile( true );
AppRender( 0.1f, 0, 0, true );
}
}
void AppChar( int key )
{
}