//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //===========================================================================// #include #include "scratchpad3d.h" #include "tier2/tier2.h" #include "tier0/dbg.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #ifdef POSIX #define __stdcall #endif #ifndef POSIX // NOTE - linux doesn't need any of this code! extern "C" { extern void __stdcall Sleep( unsigned long ms ); }; class CFileRead { public: CFileRead( IFileSystem* pFileSystem, FileHandle_t fp ) { m_pFileSystem = pFileSystem; m_fp = fp; m_Pos = 0; } bool Read( void *pDest, int len ) { int count = m_pFileSystem->Read( pDest, len, m_fp ); m_Pos += count; return count == len; } IFileSystem* m_pFileSystem; FileHandle_t m_fp; int m_Pos; }; // ------------------------------------------------------------------------ // // CCommand_Point. // ------------------------------------------------------------------------ // void CScratchPad3D::CCommand_Point::Read( CFileRead *pFile ) { pFile->Read( &m_flPointSize, sizeof(m_flPointSize) ); pFile->Read( &m_Vert, sizeof(m_Vert) ); } void CScratchPad3D::CCommand_Point::Write( IFileSystem* pFileSystem, FileHandle_t fp ) { pFileSystem->Write( &m_flPointSize, sizeof(m_flPointSize), fp ); pFileSystem->Write( &m_Vert, sizeof(m_Vert), fp ); } // ------------------------------------------------------------------------ // // CCommand_Line. // ------------------------------------------------------------------------ // void CScratchPad3D::CCommand_Line::Read( CFileRead *pFile ) { pFile->Read( m_Verts, sizeof(m_Verts) ); } void CScratchPad3D::CCommand_Line::Write( IFileSystem* pFileSystem, FileHandle_t fp ) { pFileSystem->Write( m_Verts, sizeof(m_Verts), fp ); } // ------------------------------------------------------------------------ // // CCommand_Polygon. // ------------------------------------------------------------------------ // void CScratchPad3D::CCommand_Polygon::Read( CFileRead *pFile ) { int count; pFile->Read( &count, sizeof(count) ); m_Verts.RemoveAll(); m_Verts.AddMultipleToTail( count ); if( count ) pFile->Read( &m_Verts[0], sizeof(CSPVert)*count ); } void CScratchPad3D::CCommand_Polygon::Write( IFileSystem* pFileSystem, FileHandle_t fp ) { int count = m_Verts.Count(); pFileSystem->Write( &count, sizeof(count), fp ); if( count ) pFileSystem->Write( &m_Verts[0], sizeof(CSPVert)*count, fp ); } // ------------------------------------------------------------------------ // // CCommand_Matrix. // ------------------------------------------------------------------------ // void CScratchPad3D::CCommand_Matrix::Read( CFileRead *pFile ) { pFile->Read( &m_mMatrix, sizeof(m_mMatrix) ); } void CScratchPad3D::CCommand_Matrix::Write( IFileSystem* pFileSystem, FileHandle_t fp ) { pFileSystem->Write( &m_mMatrix, sizeof(m_mMatrix), fp ); } // ------------------------------------------------------------------------ // // CCommand_RenderState. // ------------------------------------------------------------------------ // void CScratchPad3D::CCommand_RenderState::Read( CFileRead *pFile ) { pFile->Read( &m_State, sizeof(m_State) ); pFile->Read( &m_Val, sizeof(m_Val) ); } void CScratchPad3D::CCommand_RenderState::Write( IFileSystem* pFileSystem, FileHandle_t fp ) { pFileSystem->Write( &m_State, sizeof(m_State), fp ); pFileSystem->Write( &m_Val, sizeof(m_Val), fp ); } // ------------------------------------------------------------------------ // // CCommand_Text. // ------------------------------------------------------------------------ // void CScratchPad3D::CCommand_Text::Read( CFileRead *pFile ) { int strLen; pFile->Read( &strLen, sizeof( strLen ) ); m_String.SetSize( strLen ); pFile->Read( m_String.Base(), strLen ); pFile->Read( &m_TextParams, sizeof( m_TextParams ) ); } void CScratchPad3D::CCommand_Text::Write( IFileSystem* pFileSystem, FileHandle_t fp ) { int strLen = m_String.Count(); pFileSystem->Write( &strLen, sizeof( strLen ), fp ); pFileSystem->Write( m_String.Base(), strLen, fp ); pFileSystem->Write( &m_TextParams, sizeof( m_TextParams ), fp ); } // ------------------------------------------------------------------------ // // CScratchPad3D internals. // ------------------------------------------------------------------------ // CScratchPad3D::CScratchPad3D( char const *pFilename, IFileSystem* pFileSystem, bool bAutoClear ) { m_pFileSystem = pFileSystem; m_pFilename = pFilename; m_bAutoFlush = true; if( bAutoClear ) Clear(); // Clear whatever is in the file.. } void CScratchPad3D::AutoFlush() { if( m_bAutoFlush ) Flush(); } void CScratchPad3D::DrawRectGeneric( int iPlane, int otherDim1, int otherDim2, float planeDist, const Vector2D &vMin, const Vector2D &vMax, const CSPColor &vColor ) { Vector verts[4]; verts[0][iPlane] = verts[1][iPlane] = verts[2][iPlane] = verts[3][iPlane] = planeDist; verts[0][otherDim1] = vMin.x; verts[0][otherDim2] = vMin.y; verts[1][otherDim1] = vMin.x; verts[1][otherDim2] = vMax.y; verts[2][otherDim1] = vMax.x; verts[2][otherDim2] = vMax.y; verts[3][otherDim1] = vMax.x; verts[3][otherDim2] = vMin.y; DrawPolygon( CSPVertList(verts, 4, vColor) ); } void CScratchPad3D::DeleteCommands() { for( int i=0; i < m_Commands.Count(); i++ ) delete m_Commands[i]; m_Commands.RemoveAll(); } bool CScratchPad3D::LoadCommandsFromFile( ) { DeleteCommands(); FileHandle_t fp = m_pFileSystem->Open( m_pFilename, "rb" ); if( !fp ) return false; long fileEndPos = m_pFileSystem->Size( fp ); CFileRead fileRead( m_pFileSystem, fp ); while( fileRead.m_Pos != fileEndPos ) { unsigned char iCommand; fileRead.Read( &iCommand, sizeof(iCommand) ); CBaseCommand *pCmd = NULL; if( iCommand == COMMAND_POINT ) pCmd = new CCommand_Point; else if( iCommand == COMMAND_LINE ) pCmd = new CCommand_Line; else if( iCommand == COMMAND_POLYGON ) pCmd = new CCommand_Polygon; else if( iCommand == COMMAND_MATRIX ) pCmd = new CCommand_Matrix; else if( iCommand == COMMAND_RENDERSTATE ) pCmd = new CCommand_RenderState; else if ( iCommand == COMMAND_TEXT ) pCmd = new CCommand_Text; if( !pCmd ) { Assert( !"LoadCommandsFromFile: invalid file" ); m_pFileSystem->Close( fp ); return false; } pCmd->Read( &fileRead ); m_Commands.AddToTail( pCmd ); } m_pFileSystem->Close( fp ); return true; } // ------------------------------------------------------------------------ // // CScratchPad3D's IScratchPad3D implementation. // ------------------------------------------------------------------------ // void CScratchPad3D::Release() { Flush(); delete this; } void CScratchPad3D::SetMapping( const Vector &vInputMin, const Vector &vInputMax, const Vector &vOutputMin, const Vector &vOutputMax ) { CCommand_Matrix *cmd = new CCommand_Matrix; m_Commands.AddToTail( cmd ); Vector vDivisor(1,1,1); for( int i=0; i < 3; i++ ) vDivisor[i] = fabs(vInputMax[i] - vInputMin[i]) < 0.0001f ? 0.001f : (vInputMax[i] - vInputMin[i]); Vector vScale = (vOutputMax - vOutputMin) / vDivisor; Vector vShift = -vInputMin * vScale + vOutputMin; cmd->m_mMatrix.Init( vScale.x, 0, 0, vShift.x, 0, vScale.y, 0, vShift.y, 0, 0, vScale.z, vShift.z, 0, 0, 0, 1 ); AutoFlush(); } bool CScratchPad3D::GetAutoFlush() { return m_bAutoFlush; } void CScratchPad3D::SetAutoFlush( bool bAutoFlush ) { m_bAutoFlush = bAutoFlush; if( m_bAutoFlush ) Flush(); } void CScratchPad3D::DrawPoint( CSPVert const &v, float flPointSize ) { CCommand_Point *cmd = new CCommand_Point; m_Commands.AddToTail( cmd ); cmd->m_Vert = v; cmd->m_flPointSize = flPointSize; AutoFlush(); } void CScratchPad3D::DrawLine( CSPVert const &v1, CSPVert const &v2 ) { CCommand_Line *cmd = new CCommand_Line; m_Commands.AddToTail( cmd ); cmd->m_Verts[0] = v1; cmd->m_Verts[1] = v2; AutoFlush(); } void CScratchPad3D::DrawPolygon( CSPVertList const &verts ) { CCommand_Polygon *cmd = new CCommand_Polygon; m_Commands.AddToTail( cmd ); cmd->m_Verts.AddVectorToTail( verts.m_Verts ); AutoFlush(); } void CScratchPad3D::DrawRectYZ( float xPos, const Vector2D &vMin, const Vector2D &vMax, const CSPColor &vColor ) { DrawRectGeneric( 0, 1, 2, xPos, vMin, vMax, vColor ); } void CScratchPad3D::DrawRectXZ( float yPos, const Vector2D &vMin, const Vector2D &vMax, const CSPColor &vColor ) { DrawRectGeneric( 1, 0, 2, yPos, vMin, vMax, vColor ); } void CScratchPad3D::DrawRectXY( float zPos, const Vector2D &vMin, const Vector2D &vMax, const CSPColor &vColor ) { DrawRectGeneric( 2, 0, 1, zPos, vMin, vMax, vColor ); } void CScratchPad3D::SetRenderState( RenderState state, unsigned long val ) { CCommand_RenderState *cmd = new CCommand_RenderState; m_Commands.AddToTail( cmd ); cmd->m_State = (unsigned long)state; cmd->m_Val = val; } void CScratchPad3D::DrawWireframeBox( const Vector &vMin, const Vector &vMax, const Vector &vColor ) { // Bottom 4. DrawLine( CSPVert(Vector(vMin.x, vMin.y, vMin.z), vColor), CSPVert(Vector(vMax.x, vMin.y, vMin.z), vColor) ); DrawLine( CSPVert(Vector(vMin.x, vMin.y, vMin.z), vColor), CSPVert(Vector(vMin.x, vMax.y, vMin.z), vColor) ); DrawLine( CSPVert(Vector(vMax.x, vMin.y, vMin.z), vColor), CSPVert(Vector(vMax.x, vMax.y, vMin.z), vColor) ); DrawLine( CSPVert(Vector(vMax.x, vMax.y, vMin.z), vColor), CSPVert(Vector(vMin.x, vMax.y, vMin.z), vColor) ); // Top 4. DrawLine( CSPVert(Vector(vMin.x, vMin.y, vMax.z), vColor), CSPVert(Vector(vMax.x, vMin.y, vMax.z), vColor) ); DrawLine( CSPVert(Vector(vMin.x, vMin.y, vMax.z), vColor), CSPVert(Vector(vMin.x, vMax.y, vMax.z), vColor) ); DrawLine( CSPVert(Vector(vMax.x, vMin.y, vMax.z), vColor), CSPVert(Vector(vMax.x, vMax.y, vMax.z), vColor) ); DrawLine( CSPVert(Vector(vMax.x, vMax.y, vMax.z), vColor), CSPVert(Vector(vMin.x, vMax.y, vMax.z), vColor) ); // Connecting 4. DrawLine( CSPVert(Vector(vMin.x, vMin.y, vMin.z), vColor), CSPVert(Vector(vMin.x, vMin.y, vMax.z), vColor) ); DrawLine( CSPVert(Vector(vMin.x, vMax.y, vMin.z), vColor), CSPVert(Vector(vMin.x, vMax.y, vMax.z), vColor) ); DrawLine( CSPVert(Vector(vMax.x, vMax.y, vMin.z), vColor), CSPVert(Vector(vMax.x, vMax.y, vMax.z), vColor) ); DrawLine( CSPVert(Vector(vMax.x, vMin.y, vMin.z), vColor), CSPVert(Vector(vMax.x, vMin.y, vMax.z), vColor) ); } void CScratchPad3D::DrawText( const char *pStr, const CTextParams ¶ms ) { CCommand_Text *cmd = new CCommand_Text; m_Commands.AddToTail( cmd ); cmd->m_String.CopyArray( pStr, strlen( pStr ) + 1 ); cmd->m_TextParams = params; AutoFlush(); } void CScratchPad3D::Clear() { FileHandle_t fp; while( ( fp = m_pFileSystem->Open(m_pFilename, "wb") ) == NULL ) { #ifdef _WIN32 Sleep( 5 ); #elif POSIX usleep( 5 ); #endif } m_pFileSystem->Close( fp ); DeleteCommands(); } void CScratchPad3D::Flush() { FileHandle_t fp; while( ( fp = m_pFileSystem->Open(m_pFilename, "ab+") ) == NULL ) { #ifdef _WIN32 Sleep( 5 ); #elif POSIX usleep( 5 ); #endif } // Append the new commands to the file. for( int i=0; i < m_Commands.Count(); i++ ) { m_pFileSystem->Write( &m_Commands[i]->m_iCommand, sizeof(m_Commands[i]->m_iCommand), fp ); m_Commands[i]->Write( m_pFileSystem, fp ); } m_pFileSystem->Close( fp ); DeleteCommands(); } void CScratchPad3D::DrawImageBW( unsigned char const *pData, int width, int height, int pitchInBytes, bool bOutlinePixels, bool bOutlineImage, Vector *vCorners ) { SPRGBA *pRGBA = new SPRGBA[width*height]; for( int y=0; y < height; y++ ) { SPRGBA *pDest = &pRGBA[ y * width ]; unsigned char const *pSrc = &pData[ y * pitchInBytes ]; for( int x=0; x < width; x++ ) { pDest->r = pDest->g = pDest->b = *pSrc; ++pSrc; ++pDest; } } DrawImageRGBA( pRGBA, width, height, width*sizeof(SPRGBA), bOutlinePixels, bOutlineImage, vCorners ); delete [] pRGBA; } void CScratchPad3D::DrawPolygonsForPixels( SPRGBA *pData, int width, int height, int pitchInBytes, Vector *vCorners ) { // Scan top-down. Vector vCurLeft = vCorners[1]; Vector vCurRight = vCorners[2]; Vector vLeftInc = (vCorners[0] - vCorners[1]) / height; Vector vRightInc = (vCorners[3] - vCorners[2]) / height; Vector vNextLeft = vCurLeft + vLeftInc; Vector vNextRight = vCurRight + vRightInc; Vector vPolyBox[4]; Vector &vTopLeft = vPolyBox[0]; Vector &vTopRight = vPolyBox[1]; Vector &vBottomRight = vPolyBox[2]; Vector &vBottomLeft = vPolyBox[3]; for( int y=0; y < height; y++ ) { vTopLeft = vCurLeft; vBottomLeft = vNextLeft; Vector vTopXInc = (vCurRight - vCurLeft) / width; Vector vBottomXInc = (vNextRight - vNextLeft) / width; vTopRight = vTopLeft + vTopXInc; vBottomRight = vBottomLeft + vBottomXInc; SPRGBA *pSrc = &pData[ y * (pitchInBytes/sizeof(SPRGBA)) ]; for( int x=0; x < width; x++ ) { if ( pData ) DrawPolygon( CSPVertList( vPolyBox, 4, Vector(pSrc->r/255.1f, pSrc->g/255.1f, pSrc->b/255.1f) ) ); else DrawPolygon( CSPVertList( vPolyBox, 4, Vector(1,1,1) ) ); ++pSrc; vTopLeft += vTopXInc; vTopRight += vTopXInc; vBottomLeft += vBottomXInc; vBottomRight += vBottomXInc; } vCurLeft += vLeftInc; vNextLeft += vLeftInc; vCurRight += vRightInc; vNextRight += vRightInc; } } void CScratchPad3D::DrawImageRGBA( SPRGBA *pData, int width, int height, int pitchInBytes, bool bOutlinePixels, bool bOutlineImage, Vector *vCorners ) { Assert( pitchInBytes % sizeof(SPRGBA) == 0 ); Vector vDefaultCorners[4]; if ( !vCorners ) { vCorners = vDefaultCorners; vDefaultCorners[0].Init( -100, -100 ); vDefaultCorners[1].Init( -100, 100 ); vDefaultCorners[2].Init( 100, 100 ); vDefaultCorners[3].Init( 100, -100 ); } // Don't auto-flush while drawing all these primitives. bool bOldAutoFlush = m_bAutoFlush; m_bAutoFlush = false; // Draw solids. SetRenderState( IScratchPad3D::RS_FillMode, IScratchPad3D::FillMode_Solid ); DrawPolygonsForPixels( pData, width, height, pitchInBytes, vCorners ); // Draw wireframe. if ( bOutlinePixels ) { SetRenderState( IScratchPad3D::RS_FillMode, IScratchPad3D::FillMode_Wireframe ); DrawPolygonsForPixels( NULL, width, height, pitchInBytes, vCorners ); } // Draw an outline around the whole image. if ( bOutlineImage ) { SetRenderState( IScratchPad3D::RS_FillMode, IScratchPad3D::FillMode_Wireframe ); DrawPolygon( CSPVertList( vCorners, 4 ) ); } // Restore the old auto-flush state. m_bAutoFlush = bOldAutoFlush; AutoFlush(); } // ------------------------------------------------------------------------ // // Global functions. // ------------------------------------------------------------------------ // IFileSystem* ScratchPad3D_SetupFileSystem() { // Get a filesystem interface. CSysModule *pModule = Sys_LoadModule( "filesystem_stdio" ); if( !pModule ) return NULL; CreateInterfaceFn fn = Sys_GetFactory( pModule ); IFileSystem *pFileSystem; if( !fn || (pFileSystem = (IFileSystem *)fn( FILESYSTEM_INTERFACE_VERSION, NULL )) == NULL ) { Sys_UnloadModule( pModule ); return NULL; } return pFileSystem; } IScratchPad3D* ScratchPad3D_Create( char const *pFilename ) { IFileSystem *pFileSystem = ScratchPad3D_SetupFileSystem(); if( !pFileSystem ) return NULL; CScratchPad3D *pRet = new CScratchPad3D( pFilename, pFileSystem, true ); return pRet; } #endif // POSIX