/*
Copyright (C) 2010 Matthew Baranowski, Sander van Rossen & Raven software.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#ifdef WIN32
#include "system.h"
#include "ndictionary.h"
#include "md3gl.h"
#include "md3view.h"
#include "Resource.h"
#include "AFXRES.H"
#include "text.h"
#include "oddbits.h"
#include "animation.h"
#include "bmp.h"
#include "clipboard.h"
extern HINSTANCE WinhInstance;
extern NodeSequenceInfo tagMenuList;
HDC hDC_;
bool sys_rbuttondown = false;
bool sys_lbuttondown = false;
bool sys_mbuttondown = false;
OPENFILENAME *FileOpenDialog(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl, int type);
OPENFILENAME *FileSaveDialog(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl, int type);
/*
selects a 16 bit color file format, could be higher though it wouldn't work with a voodoo
*/
void SetPixelFormat( HDC hdc)
{
PIXELFORMATDESCRIPTOR pfd, *ppfd;
int pixelformat;
ppfd = &pfd;
memset(ppfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
ppfd->nSize = sizeof(PIXELFORMATDESCRIPTOR);
ppfd->nVersion = 1;
ppfd->dwFlags = PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
ppfd->dwLayerMask = PFD_MAIN_PLANE;
ppfd->iPixelType = PFD_TYPE_RGBA;
ppfd->cColorBits = 16;
ppfd->cDepthBits = 16;
ppfd->cAccumBits = 0;
ppfd->cStencilBits = 0;
pixelformat = ChoosePixelFormat( hdc, ppfd);
if ( pixelformat == 0) {
Error("ChoosePixelFormat failed");
}
if (ppfd->dwFlags & PFD_NEED_PALETTE) {
Error ("ChoosePixelFormat needs palette" );
}
if (SetPixelFormat( hdc, pixelformat, ppfd) == FALSE) {
Error("SetPixelFormat failed");
}
}
/*
** ChoosePFD
**
** Helper function that replaces ChoosePixelFormat.
*/
#define MAX_PFDS 256
static int GLW_ChoosePFD( HDC hDC, PIXELFORMATDESCRIPTOR *pPFD )
{
PIXELFORMATDESCRIPTOR pfds[MAX_PFDS+1];
int maxPFD = 0;
int i;
int bestMatch = 0;
OutputDebugString( va("...GLW_ChoosePFD( %d, %d, %d )\n", ( int ) pPFD->cColorBits, ( int ) pPFD->cDepthBits, ( int ) pPFD->cStencilBits) );
// count number of PFDs
//
maxPFD = DescribePixelFormat( hDC, 1, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[0] );
if ( maxPFD > MAX_PFDS )
{
OutputDebugString( va( "...numPFDs > MAX_PFDS (%d > %d)\n", maxPFD, MAX_PFDS) );
maxPFD = MAX_PFDS;
}
OutputDebugString( va("...%d PFDs found\n", maxPFD - 1) );
FILE *handle = fopen("MD3View_GL_report.txt","wt");
fprintf(handle,"Total PFDs: %d\n\n",maxPFD);
// grab information
for ( i = 1; i <= maxPFD; i++ )
{
DescribePixelFormat( hDC, i, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[i] );
fprintf(handle,"PFD %d/%d\n",i,maxPFD);
fprintf(handle,"=========\n");
#define FLAGDUMP(flag) if ( (pfds[i].dwFlags & flag ) != 0 ) fprintf(handle,"(flag: %s)\n",#flag);
FLAGDUMP( PFD_DOUBLEBUFFER );
FLAGDUMP( PFD_STEREO );
FLAGDUMP( PFD_DRAW_TO_WINDOW );
FLAGDUMP( PFD_DRAW_TO_BITMAP );
FLAGDUMP( PFD_SUPPORT_GDI );
FLAGDUMP( PFD_SUPPORT_OPENGL );
FLAGDUMP( PFD_GENERIC_FORMAT );
FLAGDUMP( PFD_NEED_PALETTE );
FLAGDUMP( PFD_NEED_SYSTEM_PALETTE );
FLAGDUMP( PFD_SWAP_EXCHANGE );
FLAGDUMP( PFD_SWAP_COPY );
FLAGDUMP( PFD_SWAP_LAYER_BUFFERS );
FLAGDUMP( PFD_GENERIC_ACCELERATED );
FLAGDUMP( PFD_SUPPORT_DIRECTDRAW );
if ( pfds[i].iPixelType == PFD_TYPE_RGBA )
{
// fprintf(handle,"RGBA mode\n");
}
else
{
fprintf(handle,"NOT RGBA mode!!!!!!!!!!!!\n");
}
fprintf(handle, "Colour bits: %d\n",pfds[i].cColorBits);
fprintf(handle, "Depth bits: %d\n",pfds[i].cDepthBits);
fprintf(handle,"\n");
}
// look for a best match
for ( i = 1; i <= maxPFD; i++ )
{
fprintf(handle,"(bestMatch: %d)\n",bestMatch );
//
// make sure this has hardware acceleration
//
if ( ( pfds[i].dwFlags & PFD_GENERIC_FORMAT ) != 0 )
{
// if ( !r_allowSoftwareGL->integer )
{
// if ( r_verbose->integer )
{
fprintf(handle,//OutputDebugString(
va ("...PFD %d rejected, software acceleration\n", i ));
}
continue;
}
}
// verify pixel type
if ( pfds[i].iPixelType != PFD_TYPE_RGBA )
{
// if ( r_verbose->integer )
{
fprintf(handle,//OutputDebugString(
va("...PFD %d rejected, not RGBA\n", i) );
}
continue;
}
// verify proper flags
if ( ( ( pfds[i].dwFlags & pPFD->dwFlags ) & pPFD->dwFlags ) != pPFD->dwFlags )
{
// if ( r_verbose->integer )
{
fprintf(handle,//OutputDebugString(
va("...PFD %d rejected, improper flags (0x%x instead of 0x%x)\n", i, pfds[i].dwFlags, pPFD->dwFlags) );
}
continue;
}
// verify enough bits
if ( pfds[i].cDepthBits < 15 )
{
fprintf(handle,va("...PFD %d rejected, depth bits only %d (<15)\n", i, pfds[i].cDepthBits) );
continue;
}
/* if ( ( pfds[i].cStencilBits < 4 ) && ( pPFD->cStencilBits > 0 ) )
{
continue;
}
*/
//
// selection criteria (in order of priority):
//
// PFD_STEREO
// colorBits
// depthBits
// stencilBits
//
if ( bestMatch )
{
/*
// check stereo
if ( ( pfds[i].dwFlags & PFD_STEREO ) && ( !( pfds[bestMatch].dwFlags & PFD_STEREO ) ) && ( pPFD->dwFlags & PFD_STEREO ) )
{
bestMatch = i;
continue;
}
if ( !( pfds[i].dwFlags & PFD_STEREO ) && ( pfds[bestMatch].dwFlags & PFD_STEREO ) && ( pPFD->dwFlags & PFD_STEREO ) )
{
bestMatch = i;
continue;
}
*/
// check color
if ( pfds[bestMatch].cColorBits != pPFD->cColorBits )
{
// prefer perfect match
if ( pfds[i].cColorBits == pPFD->cColorBits )
{
bestMatch = i;
continue;
}
// otherwise if this PFD has more bits than our best, use it
else if ( pfds[i].cColorBits > pfds[bestMatch].cColorBits )
{
bestMatch = i;
continue;
}
}
// check depth
if ( pfds[bestMatch].cDepthBits != pPFD->cDepthBits )
{
// prefer perfect match
if ( pfds[i].cDepthBits == pPFD->cDepthBits )
{
bestMatch = i;
continue;
}
// otherwise if this PFD has more bits than our best, use it
else if ( pfds[i].cDepthBits > pfds[bestMatch].cDepthBits )
{
bestMatch = i;
continue;
}
}
/*
// check stencil
if ( pfds[bestMatch].cStencilBits != pPFD->cStencilBits )
{
// prefer perfect match
if ( pfds[i].cStencilBits == pPFD->cStencilBits )
{
bestMatch = i;
continue;
}
// otherwise if this PFD has more bits than our best, use it
else if ( ( pfds[i].cStencilBits > pfds[bestMatch].cStencilBits ) &&
( pPFD->cStencilBits > 0 ) )
{
bestMatch = i;
continue;
}
}
*/
}
else
{
bestMatch = i;
}
}
fprintf(handle,"Bestmode: %d\n",bestMatch);
if ( !bestMatch )
{
fprintf(handle,"No decent mode found!\n");
fclose(handle);
return 0;
}
if ( ( pfds[bestMatch].dwFlags & PFD_GENERIC_FORMAT ) != 0 )
{
// if ( !r_allowSoftwareGL->integer )
// {
// ri.Printf( PRINT_ALL, "...no hardware acceleration found\n" );
// return 0;
// }
// else
{
fprintf(handle,//OutputDebugString(
"...using software emulation\n" );
}
}
else if ( pfds[bestMatch].dwFlags & PFD_GENERIC_ACCELERATED )
{
fprintf(handle,//OutputDebugString(
"...MCD acceleration found\n" );
}
else
{
fprintf(handle,//OutputDebugString(
"...hardware acceleration found\n" );
}
*pPFD = pfds[bestMatch];
fclose(handle);
return bestMatch;
}
/*
creates an OpenGL rendering context and makes it current
*/
void InitOpenGL( HWND hwnd )
{
HDC hdc = GetDC( hwnd );
mdview.hdc = hdc;
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR), // WORD nSize;
1, // WORD nVersion;
PFD_DRAW_TO_WINDOW | // DWORD dwFlags;
PFD_DOUBLEBUFFER | //
PFD_SUPPORT_OPENGL, //
PFD_TYPE_RGBA, // BYTE iPixelType;
24, // BYTE cColorBits;
// not used to select mode...
//
0, // BYTE cRedBits;
0, // BYTE cRedShift;
0, // BYTE cGreenBits;
0, // BYTE cGreenShift;
0, // BYTE cBlueBits;
0, // BYTE cBlueShift;
0, // BYTE cAlphaBits;
0, // BYTE cAlphaShift;
0, // BYTE cAccumBits;
0, // BYTE cAccumRedBits;
0, // BYTE cAccumGreenBits;
0, // BYTE cAccumBlueBits;
0, // BYTE cAccumAlphaBits;
32,//24, // BYTE cDepthBits;
// not used to select mode...
0, // BYTE cStencilBits;
0, // BYTE cAuxBuffers;
PFD_MAIN_PLANE, // BYTE iLayerType;
// not used to select mode...
0, // BYTE bReserved;
0, // DWORD dwLayerMask;
0, // DWORD dwVisibleMask;
0 // DWORD dwDamageMask;
};
/*
// choose a pixel format that best matches the one we want...
//
int iPixelFormat = ChoosePixelFormat(hdc,&pfd);
//
// set the pixel format for this device context...
//
*/
int iPixelFormat = GLW_ChoosePFD( hdc, &pfd );
VERIFY(SetPixelFormat(hdc, iPixelFormat, &pfd));
// SetPixelFormat( hdc );
HGLRC glrc = wglCreateContext( hdc );
mdview.glrc = glrc;
if ( glrc == NULL) {
Error("Failed on wglCreateContext( HDC hdc )");
}
if (!wglMakeCurrent( hdc, glrc)) {
Error("Failed on wglMakeCurrent(..)" );
}
}
bool gbMinimised = false;
/*
called every pass of the event loop
*/
void SysOnIdle()
{
// OutputDebugString("Idle\n");
if (!gbMinimised)
{
animation_loop();
}
else
{
Sleep(0);
}
}
/*
renders the model with simple viewing parameters
*/
void SysOnPaint( HWND hwnd, bool bFlip /* = true */ )
{
if (!gbMinimised)
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
wglMakeCurrent( mdview.hdc, mdview.glrc );
render_mdview();
if (bFlip)
{
HDC hDC = GetDC(hwnd);
SwapBuffers( hDC );
ReleaseDC(hwnd,hDC);
}
EndPaint(hwnd, &ps);
}
}
/*
notifies md3view of resize event
*/
void SysOnSize(HWND hwnd, UINT state, int cx, int cy)
{
RECT rect;
GetClientRect( hwnd, &rect );
set_windowSize( rect.right, rect.bottom );
g_iScreenWidth = rect.right - rect.left;
g_iScreenHeight= rect.bottom- rect.top;
gbMinimised = (state == SIZE_MINIMIZED);
InvalidateRect( hwnd, NULL, FALSE );
}
/*
sets quit parameter to break out of the message loop
*/
int SysOnDestroy(HWND hWnd)
{
Text_Destroy(); // while GL context still valid
if (mdview.glrc)
{
wglDeleteContext( mdview.glrc );
mdview.glrc = NULL;
}
if (mdview.hdc)
{
ReleaseDC(mdview.hwnd,mdview.hdc);
mdview.hdc = NULL;
}
extern void FakeCvars_Shutdown(void);
FakeCvars_Shutdown();
mdview.done = true;
return 0;
}
/*
passes control to drag.cpp drag function
*/
POINT DragStartPoint;
void SysOnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
{
POINT point;
GetCursorPos(&point);
if (!(sys_rbuttondown || sys_lbuttondown || sys_mbuttondown))
{
//ScreenToClient(&point);
return;
}
if (drag( (mkey_enum)keyFlags, point.x, point.y ))
{
SetCursorPos(DragStartPoint.x,DragStartPoint.y);
}
}
/*
releases and shows the cursor again
*/
void SysOnRButtonUp(HWND hwnd, int x, int y, UINT flags)
{
if (!sys_rbuttondown) return;
ReleaseCapture();
ShowCursor( true );
end_drag( (mkey_enum)flags, x, y );
sys_rbuttondown = false;
}
/*
same as above
*/
void SysOnLButtonUp(HWND hwnd, int x, int y, UINT flags)
{
if (!sys_lbuttondown) return;
ReleaseCapture();
ShowCursor( true );
end_drag( (mkey_enum)flags, x, y );
sys_lbuttondown = false;
}
/*
on mouse down, hide the cursor and capture it to window
*/
void SysOnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
{
GetCursorPos(&DragStartPoint);
start_drag( (mkey_enum)keyFlags, DragStartPoint.x, DragStartPoint.y );
SetCapture( hwnd );
ShowCursor( false );
sys_rbuttondown = true;
}
/*
*/
void SysOnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
{
GetCursorPos(&DragStartPoint);
start_drag( (mkey_enum)keyFlags, DragStartPoint.x, DragStartPoint.y );
SetCapture( hwnd );
ShowCursor( false );
sys_lbuttondown = true;
}
/*
processes command issued from the tag menu
*/
// I've added an extra param to this call, it won't affect anyone else if you pass NULL as that param,
// this involved a slight change below, but it's safe -slc
//
void SysOnTagCommand( HWND hwnd, int id, HWND hwndCtl, UINT codeNotify, char *psFullFilenameToUseInstead )
{
int i = 0, tagID = id - ID_TAG_START;
NodePosition pos;
GLMODEL_DBLPTR dblptr;
for (pos=tagMenuList.first() ; pos!=NULL ; pos=tagMenuList.after(pos)) {
if (i == tagID) break;
i++;
}
#ifdef DEBUG_PARANOID
if (pos==NULL) Debug("tagMenuList entry not found");
#endif
dblptr = (GLMODEL_DBLPTR)pos->element();
char fullName[256]={0};
OPENFILENAME *ofn=NULL;
if (psFullFilenameToUseInstead)
{
strcpy(fullName,psFullFilenameToUseInstead);
}
else
{
ofn = FileOpenDialog(hwnd, id, codeNotify, hwndCtl, IDS_FILESTRING );
if (ofn)
{
strcpy( fullName, ofn->lpstrFile );
strcat( fullName, ofn->lpstrFileTitle );
}
}
if (strlen(fullName))
{
if (dblptr != 0) freemdl_fromtag(dblptr);
loadmdl_totag( fullName, dblptr );
InvalidateRect( hwnd, NULL, FALSE );
//leave this here!! without the screen doesn't
//refresh properly sometimes!!
InvalidateRect( mdview.hwnd, NULL, FALSE );
if (ofn)
free( ofn );
}
else
{
if (dblptr != 0)
{
freemdl_fromtag( dblptr );
}
}
}
// we get here when the user selects one of the anim sequences on the new pulldown menus...
//
void SysOnAnimsCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
// OutputDebugString(va("Menu item code %d\n",id));
if (id < ID_MENUITEMS_LOWERANIMS)
{
// upper anim clicked on...
//
int iSelection = id-ID_MENUITEMS_UPPERANIMS;
// 0 = choose seq
// 1 = choose multi-seq
// 2 = none
// 3 = 1st seq
// 4 = 2nd seq etc...
switch (iSelection)
{
case 0: // choose seq
case 1: // choose multi-seq
ErrorBox("Ignore this for now");
iAnimLockNumber_Upper = 0; // xxxxxxxxxxxxxxxxxxxxx for now...
break;
default:
iAnimLockNumber_Upper = iSelection-2;
break;
}
}
else
{
// lower anim clicked on...
//
int iSelection = id-ID_MENUITEMS_LOWERANIMS;
// 0 = choose seq
// 1 = choose multi-seq
// 2 = none
// 3 = 1st seq
// 4 = 2nd seq etc...
switch (iSelection)
{
case 0: // choose seq
case 1: // choose multi-seq
ErrorBox("Ignore this for now");
iAnimLockNumber_Lower = 0; // xxxxxxxxxxxxxxxxxxxxx for now...
break;
default:
iAnimLockNumber_Lower = iSelection-2;
break;
}
}
}
#if 0
// takes (eg) "q:\quake\baseq3\textures\borg\name.tga"
//
// and produces "q:\quake\baseq3\" // note the trailing backslash, for this app only!!!!!!!!!!!!!!!!!!!!
//
// (copied from ShaderEd, but this app doesn't have MFC, so no CStrings...)
//
LPCSTR Filename_QUAKEBASEONLY(LPCSTR psFullPathedName /* CString &string */)
{
static char sLine[1024];
char *p;
strcpy(sLine,psFullPathedName);
while ((p=strchr(sLine,'/'))!=0) *p='\\'; // string.Replace("/","\\");
strlwr(sLine); // string.MakeLower();
/* int loc = string.Find("\\quake");
if (loc>=0)
{
loc = string.Find("\\",loc+1); // pointing at "\\baseq3" (or "demoq3" etc)
if (loc>=0)
{
loc = string.Find("\\",loc+1); // pointing at first part of string past the quake dir stuff
string = string.Left(loc);
}
}
*/
p = strstr(sLine,"\\quake");
if (p)
{
p = strstr(p+1,"\\"); // pointing at "\\baseq3" (or "demoq3" etc)
if (p)
{
p = p = strstr(p+1,"\\"); // pointing at first part of string past the quake dir stuff
*(p+1)=0; // +1 ensures trailing slash is left on as well
}
}
return sLine;
}
#endif
#define BASEDIRNAME "base"
char qdir[1024];
char gamedir[1024]; // q:\quake\baseef
// totally hacky and awful code pasted from other bits of crud
LPCSTR Filename_QUAKEBASEONLY(LPCSTR psFullPathedName )
{
static char sLine[1024];
char temp[1024];
char *path = temp;
const char *c;
const char *sep;
int len, count;
sLine[0]=0;
strcpy(path,psFullPathedName);
_strlwr(path);
// search for "base" in path from the RIGHT hand side (and must have a [back]slash just before it)
len = strlen(BASEDIRNAME);
for (c=path+strlen(path)-1 ; c != path ; c--)
{
// int i;
if (!strnicmp (c, BASEDIRNAME, len)
&&
(*(c-1) == '/' || *(c-1) == '\\') // would be more efficient to do this first, but only checking after a strncasecmp ok ensures no invalid pointer-1 access
)
{
sep = c + len;
count = 1;
while (*sep && *sep != '/' && *sep != '\\')
{
sep++;
count++;
}
strncpy (gamedir, path, c+len+count-path);
gamedir[c+len+count-path]=0;
strncpy (qdir, path, c-path);
qdir[c-path] = 0;
}
}
strcpy(sLine,gamedir);
while ((path=strchr(sLine,'/'))!=NULL) *path='\\';
return sLine;
}
bool ExportThisModel( LPCSTR psFilename, gl_model* pModel, bool bExportAsMD3);
bool R_ExportModel( gl_model* pModel, bool bMD3)
{
bool bReturn = true;
if (pModel)
{
OutputDebugString(va("Exporting model \"%s\"\n",pModel->sMDXFullPathname));
// export this model...
//
ExportThisModel( va("%s%s",Filename_WithoutExt(pModel->sMDXFullPathname), bMD3?".md3":".glm"), pModel, bMD3 );
// export its children...
//
for (UINT j=0; jiNumTags ; j++)
{
gl_model *pChild = pModel->linkedModels[j];
if (pChild)
{
if (!R_ExportModel(pChild, bMD3))
{
bReturn = false;
}
}
}
}
return bReturn;
}
void ExportMD3(void)
{
if (GetYesNo("Export as MD3, are you sure?"))
{
if (!R_ExportModel( mdview.baseModel, true ))
{
ErrorBox("Errors occured, unable to export as MD3!");
}
else
{
ExportThisModel( NULL, NULL, true); // bool bExportAsMD3
}
}
}
bool g_bPerfect = false;
void ExportG2(bool bPerfect)
{
g_bPerfect = bPerfect;
if ( mdview.baseModel )
{
if (GetYesNo(va("Export as Ghoul2 .GLM/.GLA files%s, are you sure?",bPerfect?" ( *without* 90-degree skew )":"")))
{
if (!R_ExportModel( mdview.baseModel, false ))
{
ErrorBox("Errors occured, unable to export as Ghoul2!");
}
else
{
ExportThisModel( NULL, NULL, false); // bool bExportAsMD3
}
}
}
else
{
ErrorBox( "No model loaded!");
}
}
/*
processes events from the menu
*/
void SysOnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
// process tag menu requests
if ((id >= ID_TAG_START) && (id < ID_TAG_START+tagMenuList.size())) {
SysOnTagCommand( hwnd, id, hwndCtl, codeNotify, NULL );
InvalidateRect( mdview.hwnd, NULL, FALSE );
}
if (id >= ID_MENUITEMS_UPPERANIMS && idlpstrFile );
strcat( sFullName, ofn->lpstrFileTitle );
free( ofn );
}
}
if (strlen(sFullName))
{
strcpy( mdview.basepath, Filename_QUAKEBASEONLY(sFullName));
free_mdviewdata();
bool bOk = loadmdl( sFullName );
// if model loaded ok, and it was called lower.md3, and had a tag called "tag_torso", then
// automatically (try) load "upper.md3" from the same place
bool bLowerMD3 = !stricmp(Filename_WithoutPath(sFullName),"lower.md3");
bool bLowerMDR = !stricmp(Filename_WithoutPath(sFullName),"lower.mdr");
if (bOk)
{
if ((bLowerMD3||bLowerMDR) && giTagMenuSubtractValue_Torso!=-1)
{
char sFullPathedName_Upper[MAX_PATH];
strcpy(sFullPathedName_Upper,va("%s\\upper.%s",Filename_PathOnly(sFullName),bLowerMD3?"md3":"mdr"));
if (file_exists(sFullPathedName_Upper))
{
pModel_Lower = pLastLoadedModel;
pLastLoadedModel = NULL;
int newID = tagMenuList.size(); // get to end of tag list
newID-= giTagMenuSubtractValue_Torso; // back up to "tag_torso"
newID+= ID_TAG_START; // account for resource.h value
SysOnTagCommand( hwnd, newID, hwndCtl, codeNotify, sFullPathedName_Upper);
pModel_Upper = pLastLoadedModel; // NULL or ptr
// switch on flat texturing and filtering (looks better for what we need)...
//
SysOnCommand(hwnd, ID_VIEW_TEXTURED, hwndCtl, codeNotify);
SysOnCommand(hwnd, ID_VIEW_FILTEREDTEXTURE, hwndCtl, codeNotify);
// now try auto-loading the head...
//
if (giTagMenuSubtractValue_Head!=-1)
{
char sFullPathedName_Head[MAX_PATH];
strcpy(sFullPathedName_Head,va("%s\\head.md3",Filename_PathOnly(sFullName)));
if (file_exists(sFullPathedName_Head))
{
pLastLoadedModel = NULL;
int newID = tagMenuList.size(); // get to end of tag list
newID-= giTagMenuSubtractValue_Head; // back up to "tag_head"
newID+= ID_TAG_START; // account for resource.h value
SysOnTagCommand( hwnd, newID, hwndCtl, codeNotify, sFullPathedName_Head);
}
}
}
// now attempt the animation stuff... (this is now done whether or not an UPPER model exists, to
// cope with weird stuff like the vermin model)
//
HDC hDC = GetDC(hwnd);
LoadAnimationCFG(va("%s\\animation.cfg",Filename_PathOnly(sFullName)),hDC);
ReleaseDC(hwnd,hDC);
InvalidateRect( hwnd, NULL, FALSE );
}
else
{
// if you load something with "weapon" in it's path somewhere, and that ends in "_hand.md3", then
// try and auto-load the main components of a weapon onto the appropriate tags...
//
if (strstr(String_ToLower(sFullName),"weapon") && strstr(String_ToLower(sFullName),"_hand.md3"))
{
char sWeaponBasename[MAX_PATH];
strcpy(sWeaponBasename,Filename_WithoutExt(Filename_WithoutPath(sFullName)));
*strrchr(sWeaponBasename,'_')=0; // '_' will be present at this point
int iWeaponID = tagMenuList.size(); // get to end of tag list
iWeaponID-= giTagMenuSubtractValue_Weapon; // back up to correct tag
iWeaponID+= ID_TAG_START; // account for resource.h value
int iBarrelID = tagMenuList.size(); // get to end of tag list
iBarrelID-= giTagMenuSubtractValue_Barrel; // back up to correct tag
iBarrelID+= ID_TAG_START; // account for resource.h value
int iBarrel2ID = tagMenuList.size(); // get to end of tag list
iBarrel2ID-= giTagMenuSubtractValue_Barrel2; // back up to correct tag
iBarrel2ID+= ID_TAG_START; // account for resource.h value
int iBarrel3ID = tagMenuList.size(); // get to end of tag list
iBarrel3ID-= giTagMenuSubtractValue_Barrel3; // back up to correct tag
iBarrel3ID+= ID_TAG_START; // account for resource.h value
int iBarrel4ID = tagMenuList.size(); // get to end of tag list
iBarrel4ID-= giTagMenuSubtractValue_Barrel4; // back up to correct tag
iBarrel4ID+= ID_TAG_START; // account for resource.h value
int iTagMenuSubtractValue_Weapon = giTagMenuSubtractValue_Weapon;
int iTagMenuSubtractValue_Barrel = giTagMenuSubtractValue_Barrel;
int iTagMenuSubtractValue_Barrel2 = giTagMenuSubtractValue_Barrel2;
int iTagMenuSubtractValue_Barrel3 = giTagMenuSubtractValue_Barrel3;
int iTagMenuSubtractValue_Barrel4 = giTagMenuSubtractValue_Barrel4;
if (iTagMenuSubtractValue_Weapon != -1)
SysOnTagCommand( hwnd, iWeaponID, hwndCtl, codeNotify, va("%s\\%s.md3", Filename_PathOnly(sFullName),sWeaponBasename));
if (iTagMenuSubtractValue_Barrel != -1)
SysOnTagCommand( hwnd, iBarrelID, hwndCtl, codeNotify, va("%s\\%s_barrel.md3", Filename_PathOnly(sFullName),sWeaponBasename));
if (iTagMenuSubtractValue_Barrel2 != -1)
SysOnTagCommand( hwnd, iBarrel2ID,hwndCtl, codeNotify, va("%s\\%s_barrel2.md3",Filename_PathOnly(sFullName),sWeaponBasename));
if (iTagMenuSubtractValue_Barrel3 != -1)
SysOnTagCommand( hwnd, iBarrel3ID,hwndCtl, codeNotify, va("%s\\%s_barrel3.md3",Filename_PathOnly(sFullName),sWeaponBasename));
if (iTagMenuSubtractValue_Barrel4 != -1)
SysOnTagCommand( hwnd, iBarrel4ID,hwndCtl, codeNotify, va("%s\\%s_barrel4.md3",Filename_PathOnly(sFullName),sWeaponBasename));
}
}
}
InvalidateRect( hwnd, NULL, FALSE );
//leave this here!! without the screen doesn't
//refresh properly sometimes!!
InvalidateRect( mdview.hwnd, NULL, FALSE );
}
break;
}
case ID_FILE_EXPORTTORAW:
{
OPENFILENAME *ofn;
ofn = FileSaveDialog(hwnd, id, codeNotify, hwndCtl, IDS_RAWFILEFILTER );
if (ofn)
{
char fullName[256];
strcpy( fullName, ofn->lpstrFileTitle );
write_baseModelToRaw( fullName );
free( ofn );
}
break;
}
case ID_FILE_SAVE_MD3:
{
ExportMD3();
break;
}
case ID_FILE_SAVE_G2:
{
ExportG2(false);
break;
}
case ID_FILE_SAVE_G2_PERFECT:
{
ExportG2(true);
break;
}
case ID_FILE_IMPORTSKIN:
{
OPENFILENAME *ofn;
ofn = FileOpenDialog(hwnd, id, codeNotify, hwndCtl, IDS_SKINFILEFILTER );
if (ofn)
{
char fullName[256];
strcpy( fullName, ofn->lpstrFile );
strcat( fullName, ofn->lpstrFileTitle );
importNewSkin( fullName );
InvalidateRect( hwnd, NULL, FALSE );
//leave this here!! without the screen doesn't
//refresh properly sometimes!!
InvalidateRect( mdview.hwnd, NULL, FALSE );
free( ofn );
}
break;
}
case ID_FILE_IMPORTMULTISEQ:
{
OPENFILENAME *ofn;
ofn = FileOpenDialog(hwnd, id, codeNotify, hwndCtl, IDS_SEQFILTER );
if (ofn)
{
char fullName[256];
strcpy( fullName, ofn->lpstrFile );
strcat( fullName, ofn->lpstrFileTitle );
ParseSequenceLockFile( fullName );
InvalidateRect( hwnd, NULL, FALSE );
//leave this here!! without the screen doesn't
//refresh properly sometimes!!
InvalidateRect( mdview.hwnd, NULL, FALSE );
free( ofn );
}
break;
}
case ID_FILE_EXIT:
{
mdview.done = true;
break;
}
case ID_ABOUT:
{
MessageBox(hwnd, ABOUT_TEXT, "About",MB_OK | MB_ICONINFORMATION);
break;
}
case ID_SCREENSHOT_CLIPBOARD:
{
StartWait();
gbTextInhibit = true;
SysOnPaint( hwnd, false ); // false = no buffer flip
ScreenShot(NULL,"(C) Raven Software 2000");
gbTextInhibit = false;
void *pvDIB;
int iBytes;
if (BMP_GetMemDIB(pvDIB, iBytes))
{
ClipBoard_SendDIB(pvDIB, iBytes);
}
EndWait();
InvalidateRect( hwnd, NULL, FALSE );
break;
}
case ID_SCREENSHOT_FILE:
{
gbTextInhibit = true;
SysOnPaint( hwnd, false ); // false = no buffer flip
gl_model *model = NULL;
// get the last model loaded to use as snapshot name base...
//
if (!mdview.modelList->isEmpty())
{
for (NodePosition pos=mdview.modelList->first(); pos!=NULL ; pos=mdview.modelList->after(pos))
{
model = (gl_model *)pos->element();
}
assert(model);
if (model)
{
char sBaseName[MAX_PATH];
sprintf(sBaseName, Filename_WithoutPath(Filename_PathOnly(model->sMDXFullPathname)));
int iName;
for (iName=0; iName<1000; iName++)
{
char sFilename[MAX_PATH];
sprintf(sFilename, "c:\\%s_%03d.bmp",sBaseName,iName);
if (!FileExists(sFilename))
{
StartWait();
ScreenShot(sFilename,"(C) Raven Software 2000");
EndWait();
break;
}
}
if (iName==1000)
{
ErrorBox("Couldn't find a free save slot!");
}
}
}
else
{
ErrorBox("No model load to work out path from!\n\n( So why take a snapshot, dopey? :-)");
}
gbTextInhibit = false;
InvalidateRect( hwnd, NULL, FALSE );
break;
}
case ID_VIEW_COLOURPICKER:
{
CHOOSECOLOR cc;
static COLORREF crefs[16];
memset(&cc,0,sizeof(cc));
cc.lStructSize = sizeof(cc);
cc.hwndOwner = mdview.hwnd;
// cc.hInstance = NULL;
cc.lpCustColors = crefs;
cc.rgbResult = mdview._B<<16 | mdview._G<<8 | mdview._R; // COLORREF rgbResult;
cc.Flags = CC_RGBINIT | CC_ANYCOLOR | CC_SOLIDCOLOR | /*CC_FULLOPEN | */ 0;
if (ChooseColor(&cc))
{
DWORD d = cc.rgbResult;
mdview._B = (cc.rgbResult>>16) & 0xFF;
mdview._G = (cc.rgbResult>>8 ) & 0xFF;
mdview._R = (cc.rgbResult>>0 ) & 0xFF;
}
}
break;
case ID_VIEW_REFRESHTEXTURE:
refreshTextureRes();
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEW_WIREFRAME:
oglStateWireframe();
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEW_FLATSHADED:
oglStateShadedFlat();
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEW_TEXTURED:
oglStateFlatTextured();
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEWPOS_RESET:
extern void reset_viewpos(void);
reset_viewpos();
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEW_TEXTUREDWIREFRAME:
oglStateShadedTexturedAndWireFrame();
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEW_TEXTUREDSHADED:
oglStateShadedTextured();
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEW_NEAREST:
mdview.texMode = TEX_FAST;
setTextureFilter();
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEW_UNFILTEREDTEXTURE:
mdview.texMode = TEX_UNFILTERED;
setTextureFilter();
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEW_FILTEREDTEXTURE:
mdview.texMode = TEX_FILTERED;
setTextureFilter();
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEW_LOADEDSTUFF:
char *GetLoadedModelInfo(void);
InfoBox(GetLoadedModelInfo());
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_ANIMATION_SLOWER:
mdview.animSpeed *= ANIM_SLOWER;
break;
case ID_ANIMATION_FASTER:
mdview.animSpeed *= ANIM_FASTER;
break;
case ID_ANIMATION_STOP:
mdview.animate = false;
break;
case ID_ANIMATION_START:
mdview.animate = true;
break;
case ID_ANIMATION_REWIND:
rewindAnim();
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_FRAME_DW:
FrameAdvanceAnim(-1);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_FRAME_UP:
FrameAdvanceAnim(1) ;
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_LOD0:
SetLODLevel(0);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_LOD1:
SetLODLevel(1);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_LOD2:
SetLODLevel(2);
InvalidateRect( hwnd, NULL, FALSE );
break;
extern void TextureList_ReMip(int iMIPLevel);
case ID_PICMIP0:
TextureList_ReMip(0);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_PICMIP1:
TextureList_ReMip(1);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_PICMIP2:
TextureList_ReMip(2);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_PICMIP3:
TextureList_ReMip(3);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_PICMIP4:
TextureList_ReMip(4);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_PICMIP5:
TextureList_ReMip(5);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_PICMIP6:
TextureList_ReMip(6);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_PICMIP7:
TextureList_ReMip(7);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_FACE_INCANIM:
SetFaceSkin(1);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_FACE_DECANIM:
SetFaceSkin(-1);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_FACE_RESANIM:
SetFaceSkin(0);
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEWAXIS:
mdview.bAxisView = !mdview.bAxisView;
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEWALPHA:
mdview.bUseAlpha = !mdview.bUseAlpha;
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEW_FOVTOGGLE:
mdview.dFOV = (mdview.dFOV == 10.0f)?80.0f:(mdview.dFOV == 80.0f)?90:10.0f;
RECT rect;
GetClientRect( hwnd, &rect );
set_windowSize( rect.right, rect.bottom );
InvalidateRect( hwnd, NULL, FALSE);
break;
case ID_VIEW_BOUNDSTOGGLE:
mdview.bBBox = !mdview.bBBox;
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_VIEWLOWERANIM_INC:
if (RunningNT() == 4)
{
if (++iAnimDisplayNumber_Lower > Animation_GetNumLowerSequences()) // num can be size+1 @ max (0=no seq lock)
iAnimDisplayNumber_Lower = 0;
InvalidateRect( hwnd, NULL, FALSE );
}
break;
case ID_VIEWLOWERANIM_DEC:
if (RunningNT() == 4)
{
if (--iAnimDisplayNumber_Lower < 0)
iAnimDisplayNumber_Lower = Animation_GetNumLowerSequences();
InvalidateRect( hwnd, NULL, FALSE );
}
break;
case ID_VIEWLOWERANIM_LOCK:
if (RunningNT() == 4)
{
iAnimLockNumber_Lower = iAnimDisplayNumber_Lower;
FrameAdvanceAnim(0); // cheat, force it to re-evaluate legaliser in case new locks are on
InvalidateRect( hwnd, NULL, FALSE );
}
break;
case ID_VIEWUPPERANIM_INC:
if (RunningNT() == 4)
{
if (++iAnimDisplayNumber_Upper > Animation_GetNumUpperSequences()) // num can be size+1 @ max (0=no seq lock)
iAnimDisplayNumber_Upper = 0;
InvalidateRect( hwnd, NULL, FALSE );
}
break;
case ID_VIEWUPPERANIM_DEC:
if (RunningNT() == 4)
{
if (--iAnimDisplayNumber_Upper < 0)
iAnimDisplayNumber_Upper = Animation_GetNumUpperSequences();
InvalidateRect( hwnd, NULL, FALSE );
}
break;
case ID_VIEWUPPERANIM_LOCK:
if (RunningNT() == 4)
{
iAnimLockNumber_Upper = iAnimDisplayNumber_Upper;
FrameAdvanceAnim(0); // cheat, force it to re-evaluate legaliser in case new locks are on
InvalidateRect( hwnd, NULL, FALSE );
}
break;
case ID_ANIMATION_INTERPOLATE:
if (mdview.interpolate) mdview.interpolate = false;
else mdview.interpolate = true;
break;
}
// I see no reason not to do this, it also helps with things like toggling interp OFF when doing a slow anim, which
// wouldn't otherwise instantly cause a screen redraw to remove the work "(interpolated)" from the screen...
//
InvalidateRect( hwnd, NULL, FALSE );
}
void SysOnKeyDown(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags) {
}
void SysOnKeyUp(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags) { }
bool SysOnCreate(HWND hwnd, CREATESTRUCT FAR* lpCreateStruct)
{
mdview.hwnd = hwnd;
InitOpenGL( hwnd );
initialize_glstate();
extern void OnceOnly_GLVarsInit(void);
OnceOnly_GLVarsInit();
extern void FakeCvars_OnceOnlyInit(void);
FakeCvars_OnceOnlyInit();
RECT rect;
GetClientRect( hwnd, &rect );
set_windowSize( rect.right, rect.bottom );
return true;
}
/* ---------------------------------------------- dialog code -------------------------------------------- */
/*
saves the directory name after every dialog use
*/
char szDirName[256]={0};
void SetDirectory(LPOPENFILENAME lpofn)
{
lpofn->lpstrFile[lpofn->nFileOffset] = '\0';
lstrcpy(szDirName, lpofn->lpstrFile);
//lstrcpy(mdview.basepath, lpofn->lpstrFile);
}
/*
handles the file open dialog
*/
OPENFILENAME *FileOpenDialog(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl, int type)
{
OPENFILENAME *ofn = (OPENFILENAME *)malloc(sizeof( OPENFILENAME ));
memset( ofn, 0, sizeof( OPENFILENAME ) );
static char szFile[256]; // filename string
static char szFileTitle[256]; // file-title string
static char szFilter[256]; // filter string
char chReplace; // strparator for szFilter
int i, cbString; // integer count variables
// Retrieve the current directory name and store it in szDirName.
if (szDirName[0] == '\0')
{
GetCurrentDirectory(256,szDirName);
}
// Place the terminating null character in the szFile.
szFile[0] = '\0';
// Load the filter string from the resource file.
cbString = LoadString(WinhInstance, type, szFilter, sizeof(szFilter));
// Add a terminating null character to the filter string.
chReplace = szFilter[cbString - 1];
for (i = 0; szFilter[i] != '\0'; i++)
{
if (szFilter[i] == chReplace)
szFilter[i] = '\0';
}
// Set the members of the OPENFILENAME structure.
ofn->lStructSize = sizeof(OPENFILENAME);
ofn->hwndOwner = hwnd;
ofn->lpstrFilter = szFilter;
ofn->nFilterIndex = 1;
ofn->lpstrFile = szFile;
ofn->nMaxFile = sizeof(szFile);
ofn->lpstrFileTitle = szFileTitle;
ofn->nMaxFileTitle = sizeof(szFileTitle);
ofn->lpstrInitialDir = szDirName;
ofn->Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
// Display the Open dialog box.
if (GetOpenFileName(ofn))
{
SetDirectory(ofn);
return ofn;
}
return NULL;
}
/*
handles the file save dialog
*/
OPENFILENAME *FileSaveDialog(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl, int type)
{
OPENFILENAME *ofn = (OPENFILENAME *)malloc(sizeof( OPENFILENAME ));
memset( ofn, 0, sizeof( OPENFILENAME ) );
static char szFile[256]; // filename string
static char szFileTitle[256]; // file-title string
static char szFilter[256]; // filter string
char chReplace; // strparator for szFilter
int i, cbString; // integer count variables
// Retrieve the current directory name and store it in szDirName.
if (szDirName[0] == '\0')
{
GetCurrentDirectory(256,szDirName);
}
// Place the terminating null character in the szFile.
szFile[0] = '\0';
// Load the filter string from the resource file.
cbString = LoadString(WinhInstance, type, szFilter, sizeof(szFilter));
// Add a terminating null character to the filter string.
chReplace = szFilter[cbString - 1];
for (i = 0; szFilter[i] != '\0'; i++)
{
if (szFilter[i] == chReplace)
szFilter[i] = '\0';
}
// Set the members of the OPENFILENAME structure.
ofn->lStructSize = sizeof(OPENFILENAME);
ofn->hwndOwner = hwnd;
ofn->lpstrFilter = szFilter;
ofn->nFilterIndex = 1;
ofn->lpstrFile = szFile;
ofn->nMaxFile = sizeof(szFile);
ofn->lpstrFileTitle = szFileTitle;
ofn->nMaxFileTitle = sizeof(szFileTitle);
ofn->lpstrInitialDir = szDirName;
ofn->Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
// Display the Open dialog box.
if (GetSaveFileName(ofn))
{
//SetDirectory(ofn);
return ofn;
}
return NULL;
}
#endif