// // Half-Life Model Viewer (c) 1999 by Mete Ciragan // // file: mdlviewer.cpp // last modified: Jun 03 1999, Mete Ciragan // copyright: The programs and associated files contained in this // distribution were developed by Mete Ciragan. The programs // are not in the public domain, but they are freely // distributable without licensing fees. These programs are // provided without guarantee or warrantee expressed or // implied. // // version: 1.2 // // email: mete@swissquake.ch // web: http://www.swissquake.ch/chumbalum-soft/ // #include #include #include #include #include #include #include "mdlviewer.h" #include "GlWindow.h" #include "ControlPanel.h" #include "StudioModel.h" #include "pakviewer.h" #include "FileAssociation.h" #include "stringlib.h" MDLViewer *g_MDLViewer = 0; char g_appTitle[] = "Paranoia 2 Model Viewer v1.31 stable"; static char recentFiles[8][256] = { "", "", "", "", "", "", "", "" }; extern bool bUseWeaponOrigin; extern bool bUseWeaponLeftHand; void MDLViewer::initRecentFiles( void ) { for (int i = 0; i < 8; i++) { if (strlen (recentFiles[i])) { mb->modify (IDC_FILE_RECENTMODELS1 + i, IDC_FILE_RECENTMODELS1 + i, recentFiles[i]); } else { mb->modify (IDC_FILE_RECENTMODELS1 + i, IDC_FILE_RECENTMODELS1 + i, "(empty)"); mb->setEnabled (IDC_FILE_RECENTMODELS1 + i, false); } } } void MDLViewer::loadRecentFiles( void ) { char str[256]; for( int i = 0; i < 8; i++ ) { mx_snprintf( str, sizeof( str ), "RecentFile%i", i ); if( !LoadString( str, recentFiles[i] )) break; } } void MDLViewer::saveRecentFiles( void ) { char str[256]; if( !InitRegistry( )) return; for( int i = 0; i < 8; i++ ) { mx_snprintf( str, sizeof( str ), "RecentFile%i", i ); if( !SaveString( str, recentFiles[i] )) break; } } MDLViewer :: MDLViewer() : mxWindow( 0, 0, 0, 0, 0, g_appTitle, mxWindow::Normal ) { // create menu stuff mb = new mxMenuBar (this); mxMenu *menuFile = new mxMenu (); mxMenu *menuOptions = new mxMenu (); mxMenu *menuView = new mxMenu (); mxMenu *menuHelp = new mxMenu (); mb->addMenu ("File", menuFile); mb->addMenu ("Options", menuOptions); mb->addMenu ("Tools", menuView); mb->addMenu ("Help", menuHelp); mxMenu *menuRecentModels = new mxMenu (); menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS1); menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS2); menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS3); menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS4); mxMenu *menuRecentPakFiles = new mxMenu (); menuRecentPakFiles->add ("(empty)", IDC_FILE_RECENTPAKFILES1); menuRecentPakFiles->add ("(empty)", IDC_FILE_RECENTPAKFILES2); menuRecentPakFiles->add ("(empty)", IDC_FILE_RECENTPAKFILES3); menuRecentPakFiles->add ("(empty)", IDC_FILE_RECENTPAKFILES4); menuFile->add ("Load Model...", IDC_FILE_LOADMODEL); menuFile->add ("Save Model...", IDC_FILE_SAVEMODEL); menuFile->addSeparator (); menuFile->add ("Load Background Texture...", IDC_FILE_LOADBACKGROUNDTEX); menuFile->add ("Load Ground Texture...", IDC_FILE_LOADGROUNDTEX); menuFile->addSeparator (); menuFile->add ("Unload Ground Texture", IDC_FILE_UNLOADGROUNDTEX); menuFile->addSeparator (); menuFile->add ("Open PAK file...", IDC_FILE_OPENPAKFILE); menuFile->add ("Close PAK file", IDC_FILE_CLOSEPAKFILE); menuFile->addSeparator (); menuFile->addMenu ("Recent Models", menuRecentModels); menuFile->addMenu ("Recent PAK files", menuRecentPakFiles); menuFile->addSeparator (); menuFile->add ("Exit", IDC_FILE_EXIT); menuOptions->add ("Background Color...", IDC_OPTIONS_COLORBACKGROUND); menuOptions->add ("Ground Color...", IDC_OPTIONS_COLORGROUND); menuOptions->add ("Light Color...", IDC_OPTIONS_COLORLIGHT); menuOptions->addSeparator (); menuOptions->add( "Sequence AutoPlay", IDC_OPTIONS_AUTOPLAY ); menuOptions->add( "Using BoneWeights", IDC_OPTIONS_BLENDWEIGHTS ); menuOptions->addSeparator (); menuOptions->add( "Use weapon origin", IDC_OPTIONS_WEAPONORIGIN ); menuOptions->add( "Weapon left-handed", IDC_OPTIONS_LEFTHAND ); menuOptions->addSeparator (); menuOptions->add ("Center View", IDC_OPTIONS_CENTERVIEW); menuOptions->add ("Reset View", IDC_OPTIONS_RESETVIEW); #ifdef WIN32 menuOptions->addSeparator (); menuOptions->add ("Make Screenshot...", IDC_OPTIONS_MAKESCREENSHOT); //menuOptions->add ("Dump Model Info", IDC_OPTIONS_DUMP); #endif menuView->add ("File Associations...", IDC_VIEW_FILEASSOCIATIONS); #ifdef WIN32 menuHelp->add ("Goto Homepage...", IDC_HELP_GOTOHOMEPAGE); menuHelp->addSeparator (); #endif menuHelp->add ("About...", IDC_HELP_ABOUT); mb->setChecked( IDC_OPTIONS_WEAPONORIGIN, bUseWeaponOrigin ); mb->setChecked( IDC_OPTIONS_LEFTHAND, bUseWeaponLeftHand ); mb->setChecked( IDC_OPTIONS_AUTOPLAY, g_viewerSettings.sequence_autoplay ? true : false ); mb->setChecked( IDC_OPTIONS_BLENDWEIGHTS, g_viewerSettings.studio_blendweights ? true : false ); // create the OpenGL window d_GlWindow = new GlWindow (this, 0, 0, 0, 0, "", mxWindow::Normal); #ifdef WIN32 SetWindowLong ((HWND) d_GlWindow->getHandle (), GWL_EXSTYLE, WS_EX_CLIENTEDGE); #endif d_cpl = new ControlPanel (this); d_cpl->setGlWindow (d_GlWindow); d_GlWindow->setControlPanel(d_cpl); g_GlWindow = d_GlWindow; // finally create the pakviewer window d_PAKViewer = new PAKViewer (this); g_FileAssociation = new FileAssociation (); loadRecentFiles (); initRecentFiles (); setBounds (20, 20, 640, 540); setVisible (true); if( g_viewerSettings.showGround ) d_cpl->setShowGround (true); if( g_viewerSettings.groundTexFile[0] ) d_GlWindow->loadTexture( g_viewerSettings.groundTexFile, TEXTURE_GROUND ); else d_GlWindow->loadTexture( NULL, TEXTURE_GROUND ); } MDLViewer::~MDLViewer () { // grab some params in case that hasn't updates if( d_cpl ) g_viewerSettings.editStep = d_cpl->getEditStep(); g_viewerSettings.showMaximized = isMaximized(); saveRecentFiles (); SaveViewerSettings (); g_studioModel.FreeModel (); #ifdef WIN32 DeleteFile ("midump.txt"); #endif } void MDLViewer :: checkboxSet( int id, bool bState ) { mb->setChecked( id, bState ); } int MDLViewer::handleEvent (mxEvent *event) { switch (event->event) { case mxEvent::Action: { switch (event->action) { case IDC_FILE_LOADMODEL: { const char *ptr = mxGetOpenFileName (this, 0, "*.mdl"); if (ptr) { int i; d_cpl->loadModel (ptr); for (i = 0; i < 4; i++) { if (!mx_strcasecmp (recentFiles[i], ptr)) break; } // swap existing recent file if (i < 4) { char tmp[256]; strcpy (tmp, recentFiles[0]); strcpy (recentFiles[0], recentFiles[i]); strcpy (recentFiles[i], tmp); } // insert recent file else { for (i = 3; i > 0; i--) strcpy (recentFiles[i], recentFiles[i - 1]); strcpy (recentFiles[0], ptr); } initRecentFiles (); } } break; case IDC_FILE_SAVEMODEL: { char *ptr = (char *) mxGetSaveFileName (this, g_viewerSettings.modelPath, "*.mdl", g_viewerSettings.modelPath); if (!ptr) break; char filename[256]; char ext[16]; strcpy( filename, ptr ); strcpy( ext, mx_getextension( filename )); if( mx_strcasecmp( ext, ".mdl" )) strcat( filename, ".mdl" ); if( !g_studioModel.SaveModel( filename )) { mxMessageBox( this, "Error saving model.", g_appTitle, MX_MB_OK | MX_MB_ERROR ); } else { strcpy( g_viewerSettings.modelFile, filename ); g_viewerSettings.numModelChanges = 0; // all the settings are handled } } break; case IDC_FILE_LOADBACKGROUNDTEX: case IDC_FILE_LOADGROUNDTEX: { const char *ptr = mxGetOpenFileName (this, 0, "*.bmp;*.tga;*.pcx"); if (ptr) { int name = TEXTURE_UNUSED; if( event->action == IDC_FILE_LOADBACKGROUNDTEX ) name = TEXTURE_BACKGROUND; else if( event->action == IDC_FILE_LOADGROUNDTEX ) name = TEXTURE_GROUND; if (d_GlWindow->loadTexture( ptr, name )) { if (event->action == IDC_FILE_LOADBACKGROUNDTEX) d_cpl->setShowBackground (true); else d_cpl->setShowGround (true); } else mxMessageBox (this, "Error loading texture.", g_appTitle, MX_MB_OK | MX_MB_ERROR); } } break; case IDC_FILE_UNLOADGROUNDTEX: { d_GlWindow->loadTexture( NULL, TEXTURE_GROUND ); d_cpl->setShowGround (false); } break; case IDC_FILE_OPENPAKFILE: { const char *ptr = mxGetOpenFileName (this, "\\sierra\\half-life\\valve", "*.pak"); if (ptr) { int i; d_PAKViewer->openPAKFile (ptr); for (i = 4; i < 8; i++) { if (!mx_strcasecmp (recentFiles[i], ptr)) break; } // swap existing recent file if (i < 8) { char tmp[256]; strcpy (tmp, recentFiles[4]); strcpy (recentFiles[4], recentFiles[i]); strcpy (recentFiles[i], tmp); } // insert recent file else { for (i = 7; i > 4; i--) strcpy (recentFiles[i], recentFiles[i - 1]); strcpy (recentFiles[4], ptr); } initRecentFiles (); redraw (); } } break; case IDC_FILE_CLOSEPAKFILE: { d_PAKViewer->closePAKFile (); redraw (); } break; case IDC_FILE_RECENTMODELS1: case IDC_FILE_RECENTMODELS2: case IDC_FILE_RECENTMODELS3: case IDC_FILE_RECENTMODELS4: { int i = event->action - IDC_FILE_RECENTMODELS1; d_cpl->loadModel (recentFiles[i]); char tmp[256]; strcpy (tmp, recentFiles[0]); strcpy (recentFiles[0], recentFiles[i]); strcpy (recentFiles[i], tmp); initRecentFiles (); redraw (); } break; case IDC_FILE_RECENTPAKFILES1: case IDC_FILE_RECENTPAKFILES2: case IDC_FILE_RECENTPAKFILES3: case IDC_FILE_RECENTPAKFILES4: { int i = event->action - IDC_FILE_RECENTPAKFILES1 + 4; d_PAKViewer->openPAKFile (recentFiles[i]); char tmp[256]; strcpy (tmp, recentFiles[4]); strcpy (recentFiles[4], recentFiles[i]); strcpy (recentFiles[i], tmp); initRecentFiles (); redraw (); } break; case IDC_FILE_EXIT: { d_PAKViewer->closePAKFile (); redraw (); mx::quit (); } break; case IDC_OPTIONS_COLORBACKGROUND: case IDC_OPTIONS_COLORGROUND: case IDC_OPTIONS_COLORLIGHT: { float *cols[3] = { g_viewerSettings.bgColor, g_viewerSettings.gColor, g_viewerSettings.lColor }; float *col = cols[event->action - IDC_OPTIONS_COLORBACKGROUND]; int r = (int) (col[0] * 255.0f); int g = (int) (col[1] * 255.0f); int b = (int) (col[2] * 255.0f); if (mxChooseColor (this, &r, &g, &b)) { col[0] = (float) r / 255.0f; col[1] = (float) g / 255.0f; col[2] = (float) b / 255.0f; } } break; case IDC_OPTIONS_CENTERVIEW: d_cpl->centerView (false); break; case IDC_OPTIONS_RESETVIEW: d_cpl->centerView (true); break; case IDC_OPTIONS_MAKESCREENSHOT: { char *ptr = (char *)mxGetSaveFileName( this, "", "*.bmp" ); if( ptr ) { if( !strstr( ptr, ".bmp" )) strcat( ptr, ".bmp" ); d_GlWindow->dumpViewport( ptr ); } } break; case IDC_OPTIONS_WEAPONORIGIN: bUseWeaponOrigin = !mb->isChecked( IDC_OPTIONS_WEAPONORIGIN ); mb->setChecked( IDC_OPTIONS_WEAPONORIGIN, bUseWeaponOrigin ); break; case IDC_OPTIONS_LEFTHAND: bUseWeaponLeftHand = !mb->isChecked( IDC_OPTIONS_LEFTHAND ); mb->setChecked( IDC_OPTIONS_LEFTHAND, bUseWeaponLeftHand ); break; case IDC_OPTIONS_AUTOPLAY: g_viewerSettings.sequence_autoplay = !mb->isChecked( IDC_OPTIONS_AUTOPLAY ); mb->setChecked( IDC_OPTIONS_AUTOPLAY, g_viewerSettings.sequence_autoplay ? true : false ); break; case IDC_OPTIONS_BLENDWEIGHTS: g_viewerSettings.studio_blendweights = !mb->isChecked( IDC_OPTIONS_BLENDWEIGHTS ); mb->setChecked( IDC_OPTIONS_BLENDWEIGHTS, g_viewerSettings.studio_blendweights ? true : false ); break; case IDC_OPTIONS_DUMP: d_cpl->dumpModelInfo (); break; case IDC_VIEW_FILEASSOCIATIONS: g_FileAssociation->setAssociation (0); g_FileAssociation->setVisible (true); break; #ifdef WIN32 case IDC_HELP_GOTOHOMEPAGE: ShellExecute (0, "open", "http://cs-mapping.com.ua/forum/forumdisplay.php?f=189", 0, 0, SW_SHOW); break; #endif case IDC_HELP_ABOUT: mxMessageBox (this, "Paranoia 2 Model Viewer v1.31 (c) 2019 by Unkle Mike\n" "Based on original HLMV code by Mete Ciragan\n\n" "Left-drag to rotate.\n" "Right-drag to zoom.\n" "Shift-left-drag to x-y-pan.\n" "Ctrl-drag to move lights.\n\n" "Build:\t" __DATE__ ".\n" "Email:\tg-cont@rambler.ru\n" "Web:\thttp://www.hlfx.ru/forum", "About Paranoia 2 Model Viewer", MX_MB_OK | MX_MB_INFORMATION ); break; } //switch (event->action) } // mxEvent::Action break; case mxEvent::Size: { int w = event->width; int h = event->height; int y = mb->getHeight (); #ifdef WIN32 #define HEIGHT 120 #else #define HEIGHT 140 h -= 40; #endif if (d_PAKViewer->isVisible ()) { w -= 170; d_PAKViewer->setBounds (w, y, 170, h); } d_GlWindow->setBounds (0, y, w, h - HEIGHT); d_cpl->setBounds (0, y + h - HEIGHT, w, HEIGHT); } break; case mxEvent::KeyDown: { switch( (char)event->key ) { case 32: { static float lasttime = 0.0f; if( lasttime > g_studioModel.getCurrentTime( )) break; lasttime = g_studioModel.getCurrentTime() + 0.1f; int iSeq = g_studioModel.GetSequence(); if( iSeq == g_studioModel.SetSequence( iSeq + 1 )) g_studioModel.SetSequence( 0 ); } break; case 27: if( !getParent( )) // fullscreen mode ? mx::quit(); break; case 37: if( g_viewerSettings.numModelPathes > 0 ) d_cpl->loadModel( LoadPrevModel( )); break; case 39: if( g_viewerSettings.numModelPathes > 0 ) d_cpl->loadModel( LoadNextModel( )); break; case VK_F5: { bool oldUseWeaponOrigin = bUseWeaponOrigin; d_cpl->loadModel( g_viewerSettings.modelFile, false ); bUseWeaponOrigin = oldUseWeaponOrigin; break; } case 'g': case 'п': g_viewerSettings.showGround = !g_viewerSettings.showGround; if( !g_viewerSettings.showGround ) g_viewerSettings.mirror = false; break; case 'h': case 'р': g_viewerSettings.showHitBoxes = !g_viewerSettings.showHitBoxes; break; case 'o': case 'щ': g_viewerSettings.showBones = !g_viewerSettings.showBones; break; case '5': g_viewerSettings.transparency -= 0.05f; if( g_viewerSettings.transparency < 0.0f ) g_viewerSettings.transparency = 0.0f; break; case '6': g_viewerSettings.transparency += 0.05f; if( g_viewerSettings.transparency > 1.0f ) g_viewerSettings.transparency = 1.0f; break; case 'b': case 'и': g_viewerSettings.showBackground = !g_viewerSettings.showBackground; break; case 's': case 'ы': g_viewerSettings.useStencil = !g_viewerSettings.useStencil; break; case 'm': case 'ь': g_viewerSettings.mirror = !g_viewerSettings.mirror; if( g_viewerSettings.mirror ) g_viewerSettings.showGround = true; break; case 'v': case 'м': bUseWeaponOrigin = !mb->isChecked( IDC_OPTIONS_WEAPONORIGIN ); mb->setChecked( IDC_OPTIONS_WEAPONORIGIN, bUseWeaponOrigin ); break; case 'l': case 'д': bUseWeaponLeftHand = !mb->isChecked( IDC_OPTIONS_LEFTHAND ); mb->setChecked( IDC_OPTIONS_LEFTHAND, bUseWeaponLeftHand ); break; case 'w': case 'ц': g_viewerSettings.studio_blendweights = !mb->isChecked( IDC_OPTIONS_BLENDWEIGHTS ); mb->setChecked( IDC_OPTIONS_BLENDWEIGHTS, g_viewerSettings.studio_blendweights ? true : false ); 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; } // event->event return 1; } void MDLViewer::redraw () { mxEvent event; event.event = mxEvent::Size; event.width = w2 (); event.height = h2 (); handleEvent (&event); } int main( int argc, char *argv[] ) { // // make sure, we start in the right directory // mx_setcwd (mx::getApplicationPath ()); atexit( Sys_CloseLog ); char cmdline[1024] = ""; if (argc > 1) { strcpy (cmdline, argv[1]); for (int i = 2; i < argc; i++) { strcat (cmdline, " "); strcat (cmdline, argv[i]); } } if( IsAliasModel( cmdline ) && COM_FileExists( "q1mv.exe" )) { strcpy (cmdline, "q1mv.exe" ); for (int i = 1; i < argc; i++) { strcat (cmdline, " "); strcat (cmdline, argv[i]); } WinExec (cmdline, SW_SHOW); return 0; } LoadViewerSettings(); //mx::setDisplayMode (0, 0, 0); mx::init (argc, argv); g_MDLViewer = new MDLViewer (); g_MDLViewer->setMenuBar (g_MDLViewer->getMenuBar ()); g_MDLViewer->setBounds (20, 20, 640, 540); g_MDLViewer->setVisible (true); if( g_viewerSettings.showMaximized ) g_MDLViewer->Maximize(); if (Q_stristr (cmdline, ".mdl")) { g_ControlPanel->loadModel (cmdline); } int ret = mx::run (); mx::cleanup (); return ret; }