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
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 ) |
|
{ |
|
} |
|
|
|
|
|
|
|
|
|
|