//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include #include #include #include #include #include #include #include #include #include #include "MatSysWin.h" #include "MDLViewer.h" #include "StudioModel.h" #include "ControlPanel.h" #include "ViewerSettings.h" #include "materialsystem/imaterialsystem.h" #include "materialsystem/imaterialproxyfactory.h" #include "filesystem.h" #include #include "materialsystem/imesh.h" #include "expressions.h" #include "hlfaceposer.h" #include "ifaceposersound.h" #include "materialsystem/IMaterialSystemHardwareConfig.h" #include "materialsystem/itexture.h" #include "materialsystem/MaterialSystem_Config.h" #include "istudiorender.h" #include "choreowidgetdrawhelper.h" #include "faceposer_models.h" #include "tier0/icommandline.h" #include "mathlib/vmatrix.h" #include "vstdlib/cvar.h" IFileSystem *filesystem = NULL; extern char g_appTitle[]; // FIXME: move all this to mxMatSysWin class DummyMaterialProxyFactory : public IMaterialProxyFactory { public: virtual IMaterialProxy *CreateProxy( const char *proxyName ) {return NULL;} virtual void DeleteProxy( IMaterialProxy *pProxy ) {} }; DummyMaterialProxyFactory g_DummyMaterialProxyFactory; static void ReleaseMaterialSystemObjects() { StudioModel::ReleaseStudioModel(); models->ReleaseModels(); } static void RestoreMaterialSystemObjects( int nChangeFlags ) { StudioModel::RestoreStudioModel(); models->RestoreModels(); } void InitMaterialSystemConfig(MaterialSystem_Config_t *pConfig) { pConfig->SetFlag( MATSYS_VIDCFG_FLAGS_USING_MULTIPLE_WINDOWS, true ); } Vector g_vright( 50, 50, 0 ); // needs to be set to viewer's right in order for chrome to work IMaterial *g_materialBackground = NULL; IMaterial *g_materialWireframe = NULL; IMaterial *g_materialWireframeVertexColor = NULL; IMaterial *g_materialWireframeVertexColorNoCull = NULL; IMaterial *g_materialDebugCopyBaseTexture = NULL; IMaterial *g_materialFlatshaded = NULL; IMaterial *g_materialSmoothshaded = NULL; IMaterial *g_materialBones = NULL; IMaterial *g_materialLines = NULL; IMaterial *g_materialFloor = NULL; IMaterial *g_materialVertexColor = NULL; IMaterial *g_materialShadow = NULL; MatSysWindow *g_pMatSysWindow = 0; #define MATSYSWIN_NAME "3D View" MatSysWindow::MatSysWindow (mxWindow *parent, int x, int y, int w, int h, const char *label, int style) : IFacePoserToolWindow( "3D View", "3D View" ), mxMatSysWindow ( parent, x, y, w, h, label, style ) { g_pMaterialSystem->SetMaterialProxyFactory( &g_DummyMaterialProxyFactory ); SetAutoProcess( true ); setLabel( MATSYSWIN_NAME ); m_bSuppressSwap = false; m_hWnd = (HWND)getHandle(); Con_Printf( "Setting material system video mode\n" ); MaterialSystem_Config_t config; config = g_pMaterialSystem->GetCurrentConfigForVideoCard(); InitMaterialSystemConfig(&config); g_pMaterialSystem->OverrideConfig( config, false ); // config.m_VideoMode.m_Width = config.m_VideoMode.m_Height = 0; config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, true ); config.SetFlag( MATSYS_VIDCFG_FLAGS_RESIZING, true ); if (!g_pMaterialSystem->SetMode( ( void * )m_hWnd, config ) ) { return; } g_pMaterialSystem->AddReleaseFunc( ReleaseMaterialSystemObjects ); g_pMaterialSystem->AddRestoreFunc( RestoreMaterialSystemObjects ); Con_Printf( "Loading debug materials\n" ); ITexture *pCubemapTexture = g_pMaterialSystem->FindTexture( "hlmv/cubemap", NULL, true ); pCubemapTexture->IncrementReferenceCount(); CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); pRenderContext->BindLocalCubemap( pCubemapTexture ); g_materialBackground = g_pMaterialSystem->FindMaterial("particle/particleapp_background", TEXTURE_GROUP_OTHER, true); g_materialWireframe = g_pMaterialSystem->FindMaterial("debug/debugmrmwireframe", TEXTURE_GROUP_OTHER, true); g_materialWireframeVertexColor = g_pMaterialSystem->FindMaterial("debug/debugwireframevertexcolor", TEXTURE_GROUP_OTHER, true); // test: create this from code - you need a vmt to make $nocull 1 happen, can't do it from the render context { KeyValues *pVMTKeyValues = new KeyValues( "Wireframe" ); pVMTKeyValues->SetInt("$ignorez", 1); pVMTKeyValues->SetInt("$nocull", 1); pVMTKeyValues->SetInt("$vertexcolor", 1); pVMTKeyValues->SetInt("$decal", 1); g_materialWireframeVertexColorNoCull = g_pMaterialSystem->CreateMaterial( "debug/wireframenocull", pVMTKeyValues ); } { KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" ); pVMTKeyValues->SetString("$basetexture", "vgui/white" ); g_materialDebugCopyBaseTexture = g_pMaterialSystem->CreateMaterial( "debug/copybasetexture", pVMTKeyValues ); } g_materialFlatshaded = g_pMaterialSystem->FindMaterial("debug/debugdrawflatpolygons", TEXTURE_GROUP_OTHER, true); g_materialSmoothshaded = g_pMaterialSystem->FindMaterial("debug/debugmrmfullbright2", TEXTURE_GROUP_OTHER, true); g_materialBones = g_pMaterialSystem->FindMaterial("debug/debugmrmwireframe", TEXTURE_GROUP_OTHER, true); g_materialLines = g_pMaterialSystem->FindMaterial("debug/debugwireframevertexcolor", TEXTURE_GROUP_OTHER, true); g_materialFloor = g_pMaterialSystem->FindMaterial("hlmv/floor", TEXTURE_GROUP_OTHER, true); g_materialVertexColor = g_pMaterialSystem->FindMaterial("debug/debugvertexcolor", TEXTURE_GROUP_OTHER, true); g_materialShadow = g_pMaterialSystem->FindMaterial("hlmv/shadow", TEXTURE_GROUP_OTHER, true); if (!parent) setVisible (true); else mx::setIdleWindow (this); m_bSuppressResize = false; m_stickyDepth = 0; m_bIsSticky = false; m_snapshotDepth = 0; } MatSysWindow::~MatSysWindow () { mx::setIdleWindow (0); } void MatSysWindow::redraw() { BaseClass::redraw(); return; if ( IsLocked() ) { RECT bounds; GetClientRect( (HWND)getHandle(), &bounds ); bounds.bottom = bounds.top + GetCaptionHeight(); CChoreoWidgetDrawHelper helper( this, bounds ); HandleToolRedraw( helper ); } } #define MAX_FPS 250.0f #define MIN_TIMESTEP ( 1.0f / MAX_FPS ) double realtime = 0.0f; void MatSysWindow::Frame( void ) { static bool recursion_guard = false; static double prev = 0.0; double curr = (double) mx::getTickCount () / 1000.0; double dt = ( curr - prev ); if ( recursion_guard ) return; recursion_guard = true; // clamp to MAX_FPS if ( dt >= 0.0 && dt < MIN_TIMESTEP ) { Sleep( max( 0, (int)( ( MIN_TIMESTEP - dt ) * 1000.0f ) ) ); recursion_guard = false; return; } if ( prev != 0.0 ) { dt = min( 0.1, dt ); g_MDLViewer->Think( dt ); realtime += dt; } prev = curr; DrawFrame(); recursion_guard = false; } void MatSysWindow::DrawFrame( void ) { if (!g_viewerSettings.pause) { redraw (); } } int MatSysWindow::handleEvent (mxEvent *event) { MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); int iret = 0; if ( HandleToolEvent( event ) ) { return iret; } static float oldrx = 0, oldry = 0, oldtz = 50, oldtx = 0, oldty = 0; static float oldlrx = 0, oldlry = 0; static int oldx, oldy; switch (event->event) { case mxEvent::Idle: { iret = 1; Frame(); } break; case mxEvent::MouseDown: { StudioModel *pModel = models->GetActiveStudioModel(); if (!pModel) break; oldrx = pModel->m_angles[0]; oldry = pModel->m_angles[1]; oldtx = pModel->m_origin[0]; oldty = pModel->m_origin[1]; oldtz = pModel->m_origin[2]; oldx = (short)event->x; oldy = (short)event->y; oldlrx = g_viewerSettings.lightrot[0]; oldlry = g_viewerSettings.lightrot[1]; g_viewerSettings.pause = false; float r = 1.0/3.0 * min( w(), h() ); float d = sqrt( ( float )( (event->x - w()/2) * (event->x - w()/2) + (event->y - h()/2) * (event->y - h()/2) ) ); if (d < r) g_viewerSettings.rotating = false; else g_viewerSettings.rotating = true; iret = 1; } break; case mxEvent::MouseDrag: { StudioModel *pModel = models->GetActiveStudioModel(); if (!pModel) break; if (event->buttons & mxEvent::MouseLeftButton) { if (event->modifiers & mxEvent::KeyShift) { pModel->m_origin[1] = oldty - (float) ((short)event->x - oldx) * 0.1; pModel->m_origin[2] = oldtz + (float) ((short)event->y - oldy) * 0.1; } else if (event->modifiers & mxEvent::KeyCtrl) { float ry = (float) (event->y - oldy); float rx = (float) (event->x - oldx); oldx = event->x; oldy = event->y; QAngle movement = QAngle( ry, rx, 0 ); matrix3x4_t tmp1, tmp2, tmp3; AngleMatrix( g_viewerSettings.lightrot, tmp1 ); AngleMatrix( movement, tmp2 ); ConcatTransforms( tmp2, tmp1, tmp3 ); MatrixAngles( tmp3, g_viewerSettings.lightrot ); } else { if (!g_viewerSettings.rotating) { float ry = (float) (event->y - oldy); float rx = (float) (event->x - oldx); oldx = event->x; oldy = event->y; QAngle movement; matrix3x4_t tmp1, tmp2, tmp3; movement = QAngle( 0, rx, 0 ); AngleMatrix( pModel->m_angles, tmp1 ); AngleMatrix( movement, tmp2 ); ConcatTransforms( tmp1, tmp2, tmp3 ); MatrixAngles( tmp3, pModel->m_angles ); movement = QAngle( ry, 0, 0 ); AngleMatrix( pModel->m_angles, tmp1 ); AngleMatrix( movement, tmp2 ); ConcatTransforms( tmp2, tmp1, tmp3 ); MatrixAngles( tmp3, pModel->m_angles ); } else { float ang1 = (180 / 3.1415) * atan2( oldx - w()/2.0, oldy - h()/2.0 ); float ang2 = (180 / 3.1415) * atan2( event->x - w()/2.0, event->y - h()/2.0 ); oldx = event->x; oldy = event->y; QAngle movement = QAngle( 0, 0, ang2 - ang1 ); matrix3x4_t tmp1, tmp2, tmp3; AngleMatrix( pModel->m_angles, tmp1 ); AngleMatrix( movement, tmp2 ); ConcatTransforms( tmp2, tmp1, tmp3 ); MatrixAngles( tmp3, pModel->m_angles ); } } } else if (event->buttons & mxEvent::MouseRightButton) { pModel->m_origin[0] = oldtx + (float) ((short)event->y - oldy) * 0.1; pModel->m_origin[0] = clamp( pModel->m_origin[0], 8.0f, 1024.0f ); } redraw (); iret = 1; } break; case mxEvent::KeyDown: { iret = 1; switch (event->key) { default: iret = 0; break; case 116: // F5 { g_MDLViewer->Refresh(); } break; case 32: { int iSeq = models->GetActiveStudioModel()->GetSequence(); if (iSeq == models->GetActiveStudioModel()->SetSequence (iSeq + 1)) { g_pControlPanel->setSequence( 0 ); } else { g_pControlPanel->setSequence( iSeq + 1 ); } } break; case 27: if (!getParent ()) // fullscreen mode ? mx::quit (); break; case 'g': g_viewerSettings.showGround = !g_viewerSettings.showGround; break; case 'h': g_viewerSettings.showHitBoxes = !g_viewerSettings.showHitBoxes; break; case 'o': g_viewerSettings.showBones = !g_viewerSettings.showBones; break; case 'b': g_viewerSettings.showBackground = !g_viewerSettings.showBackground; break; case 'm': g_viewerSettings.showMovement = !g_viewerSettings.showMovement; break; case '1': case '2': case '3': case '4': g_viewerSettings.renderMode = event->key - '1'; break; case '-': g_viewerSettings.speedScale -= 0.1f; if (g_viewerSettings.speedScale < 0.0f) g_viewerSettings.speedScale = 0.0f; break; case '+': g_viewerSettings.speedScale += 0.1f; if (g_viewerSettings.speedScale > 5.0f) g_viewerSettings.speedScale = 5.0f; break; } } break; } // switch (event->event) return iret; } void drawFloor () { CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); pRenderContext->Bind(g_materialFloor); pRenderContext->MatrixMode(MATERIAL_MODEL); pRenderContext->PushMatrix(); pRenderContext->LoadIdentity(); pRenderContext->MatrixMode(MATERIAL_VIEW); pRenderContext->PushMatrix(); pRenderContext->LoadIdentity(); { IMesh* pMesh = pRenderContext->GetDynamicMesh(); CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); float dist=-15000.0f; float tMin=0, tMax=1; meshBuilder.Position3f(-dist, dist, dist); meshBuilder.TexCoord2f( 0, tMin,tMax ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( dist, dist, dist); meshBuilder.TexCoord2f( 0, tMax,tMax ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( dist,-dist, dist); meshBuilder.TexCoord2f( 0, tMax,tMin ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f(-dist,-dist, dist); meshBuilder.TexCoord2f( 0, tMin,tMin ); meshBuilder.Color4ub( 255, 255, 255, 255 ); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); } pRenderContext->MatrixMode(MATERIAL_MODEL); pRenderContext->PopMatrix(); pRenderContext->MatrixMode(MATERIAL_VIEW); pRenderContext->PopMatrix(); } void setupRenderMode () { } void MatSysWindow::SuppressBufferSwap( bool bSuppress ) { m_bSuppressSwap = bSuppress; } void MatSysWindow::draw () { int i; g_pMaterialSystem->BeginFrame( 0 ); CUtlVector< StudioModel * > modellist; modellist.AddToTail( models->GetActiveStudioModel() ); if ( models->CountVisibleModels() > 0 ) { modellist.RemoveAll(); for ( i = 0; i < models->Count(); i++ ) { if ( models->IsModelShownIn3DView( i ) ) { modellist.AddToTail( models->GetStudioModel( i ) ); } } } CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); pRenderContext->ClearBuffers(true, true); int captiony = GetCaptionHeight(); int viewh = h2() - captiony; g_pMaterialSystem->SetView( (HWND)getHandle() ); pRenderContext->Viewport( 0, captiony, w2(), viewh ); pRenderContext->MatrixMode( MATERIAL_PROJECTION ); pRenderContext->LoadIdentity( ); pRenderContext->PerspectiveX(20.0f, (float)w2() / (float)viewh, 1.0f, 20000.0f); pRenderContext->MatrixMode( MATERIAL_VIEW ); pRenderContext->LoadIdentity( ); // FIXME: why is this needed? Doesn't SetView() override this? pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up pRenderContext->Rotate( -90, 0, 0, 1 ); int modelcount = modellist.Count(); int countover2 = modelcount / 2; int ydelta = g_pControlPanel->GetModelGap(); int yoffset = -countover2 * ydelta; for ( i = 0 ; i < modelcount; i++ ) { modellist[ i ]->IncrementFramecounter( ); Vector oldtrans = modellist[ i ]->m_origin; modellist[ i ]->m_origin[ 1 ] = oldtrans[ 1 ] + yoffset; yoffset += ydelta; modellist[ i ]->GetStudioRender()->BeginFrame(); modellist[ i ]->DrawModel(); modellist[ i ]->GetStudioRender()->EndFrame(); modellist[ i ]->m_origin = oldtrans; } // // draw ground // if (g_viewerSettings.showGround) { drawFloor (); } if (!m_bSuppressSwap) { g_pMaterialSystem->SwapBuffers(); } g_pMaterialSystem->EndFrame(); } void MatSysWindow::EnableStickySnapshotMode( ) { m_stickyDepth++; } void MatSysWindow::DisableStickySnapshotMode( ) { if (--m_stickyDepth == 0) { if (m_bIsSticky) { m_bIsSticky = false; HWND wnd = (HWND)getHandle(); // Move back to original position SetWindowPlacement( wnd, &m_wp ); SuppressResize( false ); SetCursor( m_hPrevCursor ); } } } void MatSysWindow::PushSnapshotMode( int nSnapShotSize ) { if (m_snapshotDepth++ == 0) { if (m_stickyDepth) { if (m_bIsSticky) return; m_bIsSticky = true; m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_WAIT ) ); } SuppressResize( true ); RECT rcClient; HWND wnd = (HWND)getHandle(); GetWindowPlacement( wnd, &m_wp ); GetClientRect( wnd, &rcClient ); MoveWindow( wnd, 0, 0, nSnapShotSize + 16, nSnapShotSize + 16, TRUE ); } } void MatSysWindow::PopSnapshotMode( ) { if (--m_snapshotDepth == 0) { if (m_stickyDepth == 0) { HWND wnd = (HWND)getHandle(); // Move back to original position SetWindowPlacement( wnd, &m_wp ); SuppressResize( false ); } } } void MatSysWindow::TakeSnapshotRect( const char *pFilename, int x, int y, int w, int h ) { int i; HANDLE hf; BITMAPFILEHEADER hdr; BITMAPINFOHEADER bi; DWORD dwTmp, imageSize; byte *hp, b, *pBlue, *pRed; w = ( w + 3 ) & ~3; imageSize = w * h * 3; // Create the file hf = CreateFile( pFilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if( hf == INVALID_HANDLE_VALUE ) { return; } // file header hdr.bfType = 0x4d42; // 'BM' hdr.bfSize = (DWORD) ( sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + imageSize ); hdr.bfReserved1 = 0; hdr.bfReserved2 = 0; hdr.bfOffBits = (DWORD) ( sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) ); if( !WriteFile( hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), (LPDWORD) &dwTmp, NULL ) ) Error( "Couldn't write file header to snapshot.\n" ); // bitmap header bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = w; bi.biHeight = h; bi.biPlanes = 1; bi.biBitCount = 24; bi.biCompression = BI_RGB; bi.biSizeImage = 0; //vid.rowbytes * vid.height; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrUsed = 0; bi.biClrImportant = 0; if( !WriteFile( hf, (LPVOID) &bi, sizeof(BITMAPINFOHEADER), (LPDWORD) &dwTmp, NULL ) ) Error( "Couldn't write bitmap header to snapshot.\n" ); // bitmap bits hp = (byte *) malloc(imageSize); if (hp == NULL) Error( "Couldn't allocate bitmap header to snapshot.\n" ); // Get Bits from the renderer CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); pRenderContext->ReadPixels( x, y, w, h, hp, IMAGE_FORMAT_RGB888 ); // Invert vertically for BMP format for (i = 0; i < h / 2; i++) { byte *top = hp + i * w * 3; byte *bottom = hp + (h - i - 1) * w * 3; for (int j = 0; j < w * 3; j++) { b = *top; *top = *bottom; *bottom = b; top++; bottom++; } } // Reverse Red and Blue pRed = hp; pBlue = pRed + 2; for(i = 0; i < w * h;i++) { b = *pRed; *pRed = *pBlue; *pBlue = b; pBlue += 3; pRed += 3; } if( !WriteFile( hf, (LPVOID)hp, imageSize, (LPDWORD) &dwTmp, NULL ) ) Error( "Couldn't write bitmap data snapshot.\n" ); free(hp); // clean up if( !CloseHandle( hf ) ) Error( "Couldn't close file for snapshot.\n" ); } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool MatSysWindow::IsSuppressingResize( void ) { return m_bSuppressResize; } //----------------------------------------------------------------------------- // Purpose: // Input : suppress - //----------------------------------------------------------------------------- void MatSysWindow::SuppressResize( bool suppress ) { m_bSuppressResize = suppress; } void MatSysWindow::TakeScreenShot (const char *filename) { redraw (); int w = w2 (); int h = h2 (); mxImage *image = new mxImage (); if (image->create (w, h, 24)) { #if 0 glReadBuffer (GL_FRONT); glReadPixels (0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, image->data); #else HDC hdc = GetDC ((HWND) getHandle ()); byte *data = (byte *) image->data; int i = 0; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { COLORREF cref = GetPixel (hdc, x, y); data[i++] = (byte) ((cref >> 0)& 0xff); data[i++] = (byte) ((cref >> 8) & 0xff); data[i++] = (byte) ((cref >> 16) & 0xff); } } ReleaseDC ((HWND) getHandle (), hdc); #endif if (!mxTgaWrite (filename, image)) mxMessageBox (this, "Error writing screenshot.", g_appTitle, MX_MB_OK | MX_MB_ERROR); delete image; } }