Files
firestorm/Gameleap/code/mw4/Tools/Max43DSPlugins/AnimationSuite/SkeletonVerify.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

1410 lines
38 KiB
C++

//===========================================================================//
// File: SkeletonVerify.cpp
// Project: MechWarrior 4
// Contents: Plugin to verify mech skeletons
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 06/10/99 jkyle Initial coding,
//---------------------------------------------------------------------------//
// Copyright (C) 1999, Fasa Interactive, Inc.
// All Rights reserved worldwide
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL
//===========================================================================//
#include "stdafx.h"
#ifdef STRICT // Already defined by stdafx,
#undef STRICT // so we avoid warning linking msg
#endif
#ifdef _MBCS // The same as above
#undef _MBCS
#endif
#include "SkeletonVerify.hpp"
#include "MaxProxies\MaxProxyHeaders.hpp"
#include "errordlg.h"
#include <Stuff\StuffHeaders.hpp>
#include <adept\collisionvolume.hpp>
#include "cvexport.h"
#include "animationsuite.h"
#include "exportUtil.h"
#include <GameOS\ToolOS.hpp>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////////////////////////////////////////////////////////
// ProtoTypes
#define FLOAT_EPSILON 0.00001f // (FLOAT)0.0 is close to this
enum ERR_ENUM {
ERR_INVALIDNODE = 1,
ERR_INVALIDCHILD,
ERR_NOZEROROOTPOSITION,
ERR_NOZEROROTATION,
ERR_NOJOINTWORLD,
ERR_HASSCALE,
ERR_NOPLUGINS,
ERR_DATAERRORS
};
int VerifyCallbackFn(int,INode *,LPVOID);
int g_maxTextSize;
extern void ConstructCVs(Interface* ip,
MaxCollisionVolume **pSolidCv,
MaxCollisionVolume **pHierarchicalCv,
bool bSuppressPrompts,
bool bRemapMaterial);
//////////////////////////////////////////////////////////////////////
// Global Variables
SkeletonVerifyPanel g_skeletonVerifyPanel;
CMapStringToOb g_mapJoints; // Maps a string to a CStringList - used for verifying models
CMapStringToOb g_mapJoints_Compare; // Maps a string to CStringList - used for comparing models
////////////////////////////////////////////////////////////////////////////////
// SkeletonVerifyPanel Dialog Procedure to handle User and System Interactions
static BOOL CALLBACK SkeletonVerifyPanelProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
switch (msg)
{
case WM_QUICKFILE_BEGIN:
{
//g_skeletonVerifyPanel.Init(hWnd);
SendDlgItemMessage(hWnd,IDC_SKELETONVERIFY_LISTBOX,LB_RESETCONTENT,0,0);
SendDlgItemMessage(hWnd,IDC_SKELETONVERIFY_LISTBOX,LB_SETHORIZONTALEXTENT,300,0);
break;
}
case WM_QUICKFILE_END:
{
g_skeletonVerifyPanel.m_ip->ForceCompleteRedraw();
break;
}
case WM_QUICKFILE_APPLYFILE:
{
char *name = (char *)wParam;
char *path = (char *)lParam;
g_skeletonVerifyPanel.m_ip->LoadFromFile(path);
// g_skeletonVerifyPanel.m_ip->ForceCompleteRedraw();
int iCheck = SendDlgItemMessage(hWnd,IDC_CHECK_COMPARE,BM_GETCHECK,0,0);
if (BST_CHECKED == iCheck)
{
int status = g_skeletonVerifyPanel.SkeletonCompare(VerifyCallbackFn,(LPVOID)GetDlgItem(hWnd,IDC_SKELETONVERIFY_LISTBOX));
}
else
{
int status = g_skeletonVerifyPanel.SkeletonVerify(VerifyCallbackFn,(LPVOID)GetDlgItem(hWnd,IDC_SKELETONVERIFY_LISTBOX));
}
break;
}
case WM_INITDIALOG: //Panel's extra initialization
g_skeletonVerifyPanel.Init(hWnd);
break;
case WM_DESTROY: //Panel's extra destruction
g_skeletonVerifyPanel.Destroy(hWnd);
break;
case WM_COMMAND :
{ //Handle Panel's controls
HWND hwndCtrl = (HWND)lParam;
switch (LOWORD(wParam))
{
case IDC_CHECKEXPORT:
{
g_skeletonVerifyPanel.OnErrorCheck(VerifyCallbackFn);
break;
}
case IDC_CHECK_COMPARE:
{
switch (HIWORD(wParam))
{
case BN_CLICKED:
{
int iCheck = SendMessage(hwndCtrl,BM_GETCHECK,0,0);
if (BST_CHECKED == iCheck)
{
SetWindowText(GetDlgItem(hWnd,IDC_SKELETONVERIFY_EXECUTE),"Compare");
}
else
{
SetWindowText(GetDlgItem(hWnd,IDC_SKELETONVERIFY_EXECUTE),"Verify");
}
break;
}
}
break;
}
case IDC_SKELETONVERIFY_COMPARE:
{
CFileDialog fd(TRUE,NULL,NULL,0,"Max Files (*.max)|*.max||",CWnd::FromHandle(hWnd));
char *filebuf = new char[MAX_PATH*200];
filebuf[0] = 0;
fd.m_ofn.lpstrFile = filebuf;
fd.m_ofn.nMaxFile = MAX_PATH*200;
if (fd.DoModal()==IDOK)
{
CString path = fd.GetPathName();
char fname[20];
_splitpath((LPCTSTR)path,NULL,NULL,fname,NULL);
g_skeletonVerifyPanel.m_ip->LoadFromFile(path);
g_skeletonVerifyPanel.m_ip->ForceCompleteRedraw();
EnableWindow(GetDlgItem(hWnd,IDC_CHECK_COMPARE),true);
CString str;
str.Format("Compare to: %s",fname);
SetDlgItemText(hWnd,IDC_CHECK_COMPARE,str);
SendDlgItemMessage(hWnd,IDC_CHECK_COMPARE,BM_SETCHECK,BST_CHECKED,0);
SetWindowText(GetDlgItem(hWnd,IDC_SKELETONVERIFY_EXECUTE),"Compare");
g_skeletonVerifyPanel.InitCompareMap();
}
break;
}
case IDC_QUICKFILE_LISTBOX:
{
switch (HIWORD(wParam))
{
case LBN_SELCHANGE:
{
HWND hListBox = (HWND)lParam;
int index = SendMessage(hListBox,LB_GETCURSEL,0,0);
char *path = (char *)SendMessage(hListBox,LB_GETITEMDATA,index,0);
SendDlgItemMessage(hWnd,IDC_SKELETONVERIFY_LISTBOX,LB_RESETCONTENT,0,0);
SendDlgItemMessage(hWnd,IDC_SKELETONVERIFY_LISTBOX,LB_SETHORIZONTALEXTENT,300,0);
g_skeletonVerifyPanel.m_ip->LoadFromFile(path);
g_skeletonVerifyPanel.m_ip->ForceCompleteRedraw();
int iCheck = SendDlgItemMessage(hWnd,IDC_CHECK_COMPARE,BM_GETCHECK,0,0);
if (BST_CHECKED == iCheck)
{
int status = g_skeletonVerifyPanel.SkeletonCompare(VerifyCallbackFn,(LPVOID)GetDlgItem(hWnd,IDC_SKELETONVERIFY_LISTBOX));
}
else
{
//g_skeletonVerifyPanel.Init(hWnd);
int status = g_skeletonVerifyPanel.SkeletonVerify(VerifyCallbackFn,(LPVOID)GetDlgItem(hWnd,IDC_SKELETONVERIFY_LISTBOX));
}
break;
}
}
break;
}
case IDC_ABOUT: {
CAbout dlg;
dlg.DoModal();
break;}
case IDC_SKELETONVERIFY_EXECUTE:
{
SendDlgItemMessage(hWnd,IDC_SKELETONVERIFY_LISTBOX,LB_RESETCONTENT,0,0);
SendDlgItemMessage(hWnd,IDC_SKELETONVERIFY_LISTBOX,LB_SETHORIZONTALEXTENT,300,0);
g_maxTextSize = 0;
int iCheck = SendDlgItemMessage(hWnd,IDC_CHECK_COMPARE,BM_GETCHECK,0,0);
if (BST_CHECKED == iCheck)
{
int status = g_skeletonVerifyPanel.SkeletonCompare(VerifyCallbackFn,(LPVOID)GetDlgItem(hWnd,IDC_SKELETONVERIFY_LISTBOX));
}
else
{
//g_skeletonVerifyPanel.Init(hWnd);
int status = g_skeletonVerifyPanel.SkeletonVerify(VerifyCallbackFn,(LPVOID)GetDlgItem(hWnd,IDC_SKELETONVERIFY_LISTBOX));
}
//SendDlgItemMessage(hWnd,IDC_SKELETONVERIFY_LISTBOX,LB_SETHORIZONTALEXTENT,g_maxTextSize,0);
//MessageBox(hWnd,"Skeleton Verification Complete","Skeleton Verify",MB_OK);
break;
}
case IDC_SKELETONVERIFY_LISTBOX:
{
switch (HIWORD(wParam))
{
case LBN_SELCHANGE:
{
g_skeletonVerifyPanel.HandleSelChange(hwndCtrl);
break;
}
}
break;
}
}
break;
}
////////////////////////////////////////////////////////////////////////
//The following lines are not necessary for MAX2 Plugins
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MOUSEMOVE:
g_skeletonVerifyPanel.m_ip->RollupMouseMessage(hWnd, msg, wParam, lParam);
break;
////////////////////////////////////////////////////////////////////////
default:
return FALSE;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////
// Display the Plugin's Panel
void SkeletonVerifyPanel::BeginEditParams(Interface* ip, IUtil* iu)
{
m_ip = ip; //Get the Plugin's Interface
m_iu = iu; //Get the Utility Plugin's Interface
//Display the plugin's Rollup Dialog
AFX_MANAGE_STATE(AfxGetStaticModuleState());
HINSTANCE loc_hInstance = AfxGetResourceHandle ();
m_hPanel = m_ip->AddRollupPage(loc_hInstance, MAKEINTRESOURCE(IDD_SKELETONVERIFY_PANEL),
SkeletonVerifyPanelProc, GetString(IDS_SKELETONVERIFY_NAME),0);
m_hQuickFile = EnableQuickFile(m_ip,m_hPanel);
}
////////////////////////////////////////////////////////////////////////////////
// Free the Plugins' Panel
void SkeletonVerifyPanel::EndEditParams(Interface *ip, IUtil *iu)
{
DisableQuickFile(ip,m_hQuickFile);
m_iu = NULL;
m_ip = NULL;
//Remove the Rollup Dialog
ip->DeleteRollupPage(m_hPanel);
m_hPanel = NULL;
}
// first word is the parent, the rest is the list of children
char g_szJointMap[][100] =
{
"joint_WORLD joint_VEL handle_rfoot handle_lfoot",
"joint_VEL joint_ROOT",
"joint_ROOT joint_luleg joint_ruleg joint_hipbelow",
"joint_luleg joint_lmleg joint_ldleg luleg",
"joint_lmleg joint_ldleg",
"joint_ldleg joint_lankle site_ldjetport site_lfoot",
"joint_lankle joint_ltoe joint_lftoe joint_lbtoe joint_litoe joint_lotoe lfoot site_lfoot",
"joint_ruleg joint_rmleg joint_rdleg ruleg",
"joint_rmleg joint_rdleg",
"joint_rdleg joint_rankle site_rdjetport site_rfoot",
"joint_rankle joint_rtoe joint_rftoe joint_rbtoe joint_ritoe joint_rotoe rfoot site_rfoot",
"joint_hipbelow joint_hip",
"joint_hip joint_hipabove joint_torso",
"joint_hipabove joint_torsobelow hip",
"joint_torsobelow joint_torso",
"joint_torso joint_luarm joint_ruarm joint_torsoabove",
"joint_torsoabove joint_head joint_centertorsofront joint_centertorsorear",
"joint_torsoabove joint_righttorsofront joint_lefttorsofront",
"joint_torsoabove joint_luarmabove joint_ruarmabove joint_missile torso site_eyepoint",
"joint_torsoabove site_eject site_light site_radar site_coolantflush site_lmissileport",
"joint_torsoabove site_rmissileport site_missileport site_lexhaust site_rexhaust site_exhaust",
"joint_torsoabove site_rujetport site_lujetport site_torsoport site_ltorsoport site_lutorsoport",
"joint_torsoabove site_ldtorsoport site_rtorsoport site_rutorsoport site_rdtorsoport",
"joint_torsoabove joint_cage joint_lmissile joint_rmissile",
"joint_torsoabove site_jcattach site_jcjetport01 site_jcjetport02 site_jcjetport03 site_jcjetport04",
"joint_cage site_cageleft site_cageright site_light",
"joint_luarmabove joint_luarm",
"joint_ruarmabove joint_ruarm",
"joint_luarm joint_lmarm joint_lgunabove joint_lgun luarm",
"joint_lmarm joint_ldarm",
"joint_ldarm joint_lgunabove",
"joint_lgunabove joint_lgun",
"joint_ruarm joint_rmarm joint_rgunabove joint_rgun ruarm",
"joint_rmarm joint_rdarm",
"joint_rdarm joint_rgunabove",
"joint_rgunabove joint_rgun",
"joint_lgun site_ldgunport site_lugunport site_lgunport site_lshellport site_logunport",
"joint_lgun site_ligunport site_lgunexhaust lgun",
"joint_rgun site_rdgunport site_rugunport site_rgunport site_rshellport site_rogunport",
"joint_rgun site_rdgunport site_rgunexhaust rgun",
"joint_missile missile site_exhaust site_missileport",
"joint_righttorsofront site_rmissileport site_rtorsoport site_rdtorsoport site_rujetport",
"joint_lefttorsofront site_lmissileport site_ltorsoport site_ldtorsoport site_lujetport",
"joint_head site_lutorsoport",
"joint_centertorsofront site_ldtorsoport",
"joint_specialone",
"joint_specialtwo"};
// These are the valid geometry names
char g_szGeomSuffix[] =
"lgun luarm rgun ruarm torso hip rftoe ritoe rotoe rfoot rdleg ruleg \
lftoe litoe lotoe lfoot ldleg luleg rmleg lmleg lbtoe rbtoe ltoe rtoe \
runninglights searchlight specialone specialtwo \
Team_Left Team_Right Pilot_Left Pilot_Right";
// These are the joints that should have 0 world state rotation
char g_szJointNoRotate[] = "WORLD hipbelow hipabove torsobelow torsoabove ruarmabove luarmabove rgunabove lgunabove";
CStringList g_listJointNoRotate;
CStringList g_listGeomSuffix;
////////////////////////////////////////////////////////////////////////////////
// Panel Initialization callback
void SkeletonVerifyPanel::Init(HWND hWnd)
{
HWND hList = GetDlgItem(hWnd,IDC_SKELETONVERIFY_LISTBOX);
LONG lStyle = GetWindowLong(hList,GWL_STYLE);
lStyle |= WS_HSCROLL;
SetWindowLong(hList,GWL_STYLE,lStyle);
// Initialize the map
{
for (int x=0;x<(sizeof(g_szJointMap)/100);x++)
{
char szData[100];
strcpy(szData,g_szJointMap[x]);
char *pname = strtok(szData," ");
if (pname)
{
CString strName = CString(pname);
strName.MakeLower();
CStringList *pList;
if (g_mapJoints[strName])
pList = (CStringList *)g_mapJoints[strName];
else
g_mapJoints[strName] = pList = new CStringList;
while (char *p = strtok(NULL," "))
{
CString str1 = CString(p);
str1.MakeLower();
pList->AddTail(str1);
if (!g_mapJoints[str1])
g_mapJoints[str1] = new CStringList;
}
}
}
}
//Initialize the zero rotation list
{
g_listJointNoRotate.RemoveAll();
char *dup = strdup(g_szJointNoRotate);
char *str = strtok(dup," ");
while (str)
{
g_listJointNoRotate.AddTail(CString("joint_")+CString(str));
str = strtok(NULL," ");
}
delete dup;
}
//Initialize the geometry names
{
g_listGeomSuffix.RemoveAll();
char *dup = strdup(g_szGeomSuffix);
char *str = strtok(dup," ");
while (str)
{
CString name(str);
name.MakeLower();
g_listGeomSuffix.AddTail(name);
str = strtok(NULL," ");
}
delete dup;
}
// Initialize the compare map if a model is currently loaded
{
}
}
////////////////////////////////////////////////////////////////////////////////
// Panel Destruction callback
void SkeletonVerifyPanel::Destroy(HWND hWnd)
{
//destroy the map list pointer data
{
POSITION pos = g_mapJoints.GetStartPosition();
while (pos)
{
CString str;
CObject *pObj;
g_mapJoints.GetNextAssoc(pos,str,pObj);
str.MakeLower();
CStringList *pList = (CStringList *)pObj;
pList->RemoveAll();
delete pList;
}
g_mapJoints.RemoveAll();
pos = g_mapJoints_Compare.GetStartPosition();
while (pos)
{
CString str;
CObject *pObj;
g_mapJoints_Compare.GetNextAssoc(pos,str,pObj);
str.MakeLower();
CStringList *pList = (CStringList *)pObj;
pList->RemoveAll();
delete pList;
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Functions
int SkeletonVerifyPanel::SkeletonVerify(LPSKELETONVERIFY_CALLBACKFN pFn,LPVOID lpVoid)
{
// INode *pRoot = m_ip->GetRootNode();
INode *pRoot = m_ip->GetINodeByName("joint_WORLD");
if (NULL == pRoot)
{
if (pFn)
pFn(ERR_NOJOINTWORLD,pRoot,lpVoid);
else
MessageBox(NULL,"joint_WORLD not found","World Node error",MB_OK);
return 0;
}
// Check for world position at 0,0,0
{
// Point3 offset = pRoot->GetObjOffsetPos();
Matrix3 mat = pRoot->GetObjTMAfterWSM(0);
Point3 offset = mat.GetTrans();
if ((offset.x != 0.0) && (offset.y != 0.0) && (offset.z != 0.0))
{
if (pFn)
pFn(ERR_NOZEROROOTPOSITION,pRoot,lpVoid);
else
MessageBox(NULL,"Root not positioned at 0","Root Node error",MB_OK);
}
}
// Verify the joint hiearchy
{
int status = VerifyNode(pRoot,pFn,lpVoid);
}
return 0;
}
int SkeletonVerifyPanel::VerifyNode(INode *pNode,LPSKELETONVERIFY_CALLBACKFN pFn,LPVOID lpVoid)
{
int status = 0;
CString strName(pNode->GetName());
strName.MakeLower();
CStringList *pList = (CStringList *)g_mapJoints[strName];
if (NULL == pList)
{
if (pFn)
pFn(ERR_INVALIDNODE,pNode,lpVoid);
else
{
CString str = "Invalid node naming: "+strName;
MessageBox(NULL,str,"Node error",MB_OK);
}
// Invalid node strName
return 1;
}
//Verify that the node has no rotation if it is not supposed to
{
if (g_listJointNoRotate.Find(strName))
{
Matrix3 mat = pNode->GetObjTMAfterWSM(0);
bool isrot = FALSE;
float val;
for (int x = 0;x<3;x++)
for (int y = 0;y<3;y++)
if (x==y)
{
if (FLOAT_EPSILON <= ((val = mat.GetRow(x)[y])-1.0f))
isrot = TRUE;
}
else
if (FLOAT_EPSILON <= fabs(val = mat.GetRow(x)[y]))
isrot =TRUE;
if (isrot)
{
if (pFn)
pFn(ERR_NOZEROROTATION,pNode,lpVoid);
else
MessageBox(NULL,"Node requires zero rotation","Node error",MB_OK);
}
}
}
//Verify that the node has no scale if it is not supposed to
{
Matrix3 parentTM, nodeTM, localTM;
nodeTM = pNode->GetNodeTM(0);
parentTM = pNode->GetParentTM(0);
localTM = nodeTM*Inverse(parentTM);
AffineParts ap;
decomp_affine(localTM, &ap);
Point3 s = ap.k;
BOOL bError = false;
for (int x=0;x<3;x++)
if (fabs(s[x]-1.0)>0.00001)
bError = true;
if (bError)
{
if (pFn)
pFn(ERR_HASSCALE,pNode,lpVoid);
else
MessageBox(NULL,"Node requires zero scale","Node error",MB_OK);
}
}
for (int x=0;x<pNode->NumberOfChildren();x++)
{
INode *pChild = pNode->GetChildNode(x);
CString strChildName(pChild->GetName());
strChildName.MakeLower();
strChildName.Replace(" ", "");
if (pList->Find(strChildName,NULL))
{
status |= VerifyNode(pChild,pFn,lpVoid);
}
else
{
CString geoName = strChildName;
int n = strChildName.Find("_", 1);
if (n>0)
geoName = strChildName.Right(strChildName.GetLength()-n-1);
if (g_listGeomSuffix.Find(geoName) ||
strChildName.Find("cv_")>=0)
{
// This is a piece of geometry, or CV
// ignore the name_ (prefix) and don't check chidlren
}
else
{
if (pFn)
pFn(ERR_INVALIDCHILD,pChild,lpVoid);
else
{
CString str = "Invalid child of "+strName+" : "+strChildName;
MessageBox(NULL,str,"Node error",MB_OK);
}
// pChild is not a valid child of pNode
status |= 2;
}
}
}
return status;
}
static int VerifyCallbackFn(int status,INode *pNode,LPVOID lpVoid)
{
HWND hWnd = (HWND)lpVoid;
char str[100];
switch (status)
{
case ERR_INVALIDNODE:
{
strcpy(str,"Invalid Node Naming");
break;
}
case ERR_INVALIDCHILD:
{
INode *pParent = pNode->GetParentNode();
if (pParent)
sprintf(str,"(%s) ",pParent->GetName());
strcat(str,"Invalid Child Node");
break;
}
case ERR_NOZEROROOTPOSITION:
{
strcpy(str,"Root Position not (0,0,0)");
break;
}
case ERR_NOZEROROTATION:
{
strcpy(str,"Requires Zero Rotation");
break;
}
case ERR_NOJOINTWORLD:
{
strcpy(str,"joint_WORLD node not found");
break;
}
case ERR_HASSCALE:
{
strcpy(str,"Node has scale");
break;
}
case ERR_NOPLUGINS:
{
strcpy(str,"Plugins.ini not found");
}
case ERR_DATAERRORS:
{
strcpy(str,"Found geometry data errors");
}
default:
{
}
}
char errstr[100];
sprintf(errstr,"%-20s - %s",pNode?pNode->GetName():"NULL",str);
int index = SendMessage(hWnd,LB_ADDSTRING,NULL,(LPARAM)errstr);
SIZE size;
HDC hDC = GetDC(hWnd);
GetTextExtentPoint32(hDC,errstr,strlen(errstr),&size);
ReleaseDC(hWnd,hDC);
//int width = SendDlgItemMessage(hWnd,IDC_SKELETONVERIFY_LISTBOX,LB_GETHORIZONTALEXTENT,0,0);
if (g_maxTextSize < size.cx)
g_maxTextSize = size.cx;
//SendDlgItemMessage(hWnd,IDC_SKELETONVERIFY_LISTBOX,LB_SETHORIZONTALEXTENT,size.cx,0);
SendMessage(hWnd,LB_SETITEMDATA,index,(LPARAM)pNode);
return 0;
}
// When the ListBox control recieves a selchange notification message, handle
int SkeletonVerifyPanel::HandleSelChange(HWND hWnd)
{
int selcount = SendMessage(hWnd,LB_GETSELCOUNT,0,0);
if (selcount>0)
{
int *selarray = new int[selcount];
int result = SendMessage(hWnd,LB_GETSELITEMS,selcount,(LPARAM)selarray);
// result should be equal to selcount
m_ip->ClearNodeSelection(FALSE);
for (int x=0;x<selcount;x++)
{
INode *pNode = (INode *)SendMessage(hWnd,LB_GETITEMDATA,selarray[x],0);
if (pNode)
{
m_ip->SelectNode(pNode,FALSE);
}
}
m_ip->ForceCompleteRedraw();
}
return 0;
}
int SkeletonVerifyPanel::AddNode(INode *pNode)
{
if (pNode == NULL)
return 1;
CStringList *pList;
CString strName = pNode->GetName();
strName.MakeLower();
if (g_mapJoints_Compare[strName])
pList = (CStringList *)g_mapJoints_Compare[strName];
else
g_mapJoints_Compare[strName] = pList = new CStringList;
for (int x=0;x<pNode->NumberOfChildren();x++)
{
INode *pChild = pNode->GetChildNode(x);
CString strChild = pChild->GetName();
strChild.MakeLower();
pList->AddTail(strChild);
if (!g_mapJoints_Compare[strChild])
g_mapJoints_Compare[strChild] = new CStringList;
AddNode(pChild);
}
return 0;
}
int SkeletonVerifyPanel::InitCompareMap()
{
// Duplicate code from the :Destroy
POSITION pos = g_mapJoints_Compare.GetStartPosition();
while (pos)
{
CString str;
CObject *pObj;
g_mapJoints_Compare.GetNextAssoc(pos,str,pObj);
str.MakeLower();
CStringList *pList = (CStringList *)pObj;
pList->RemoveAll();
delete pList;
}
g_mapJoints_Compare.RemoveAll();
// add the joints
INode *pRoot = m_ip->GetINodeByName("joint_WORLD");
AddNode(pRoot);
return 0;
}
int SkeletonVerifyPanel::CompareNode(INode *pNode,LPSKELETONVERIFY_CALLBACKFN pFn,LPVOID lpVoid)
{
int status = 0;
CString strName(pNode->GetName());
CStringList *pList = (CStringList *)g_mapJoints_Compare[strName];
if (NULL == pList)
{
if (pFn)
pFn(ERR_INVALIDNODE,pNode,lpVoid);
else
{
CString str = "Invalid node naming: "+strName;
MessageBox(NULL,str,"Node naming error",MB_OK);
}
// Invalid node strName
return 1;
}
//Verify that the node has no rotation if it is not supposed to
/* {
if (g_listJointNoRotate.Find(strName))
{
Matrix3 mat = pNode->GetObjTMAfterWSM(0);
bool isrot = FALSE;
float val;
for (int x = 0;x<3;x++)
for (int y = 0;y<3;y++)
if (x==y)
{
if (FLOAT_EPSILON <= ((val = mat.GetRow(x)[y])-1.0f))
isrot = TRUE;
}
else
if (FLOAT_EPSILON <= fabs(val = mat.GetRow(x)[y]))
isrot =TRUE;
if (isrot)
{
if (pFn)
pFn(ERR_NOZEROROTATION,pNode,lpVoid);
else
MessageBox(NULL,"Node requires zero rotation","Node error",MB_OK);
}
}
}
*/
for (int x=0;x<pNode->NumberOfChildren();x++)
{
INode *pChild = pNode->GetChildNode(x);
CString strChildName(pChild->GetName());
if (pList->Find(strChildName,NULL))
{
status |= CompareNode(pChild,pFn,lpVoid);
}
else
{
if (pFn)
pFn(ERR_INVALIDCHILD,pChild,lpVoid);
else
{
CString str = strChildName+"is not a valid child of "+strName;
MessageBox(NULL,str,"Node error",MB_OK);
}
// pChild is not a valid child of pNode
status |= 2;
}
}
return status;
}
int SkeletonVerifyPanel::CheckCv(INode *pNode)
{
int err = 0;
CString name = pNode->GetName();
if (strnicmp(name,"cv_",3)==0)
{
// Check that it is a cv node class
Object *pObj = pNode->GetObjectRef();
if (pObj && (pObj->ClassID() != CVOBJECT_CLASS_ID))
{
CString str;
str.Format("%s is not a collision volume",name);
ReportError(str);
err++;
}
}
int count = pNode->NumberOfChildren();
for (int x=0;x>count;x++)
err+= CheckCv(pNode->GetChildNode(x));
return err;
}
int SkeletonVerifyPanel::SkeletonCompare(LPSKELETONVERIFY_CALLBACKFN pFn,LPVOID lpVoid)
{
// INode *pRoot = m_ip->GetRootNode();
INode *pRoot = m_ip->GetINodeByName("joint_WORLD");
if (NULL == pRoot)
{
if (pFn)
pFn(ERR_NOJOINTWORLD,pRoot,lpVoid);
else
MessageBox(NULL,"joint_WORLD not found","World Node error",MB_OK);
}
// Check for world position at 0,0,0
/* {
Matrix3 mat = pRoot->GetObjTMAfterWSM(0);
Point3 offset = mat.GetTrans();
if ((offset.x != 0.0) && (offset.y != 0.0) && (offset.z != 0.0))
{
if (pFn)
pFn(ERR_NOZEROROOTPOSITION,pRoot,lpVoid);
else
MessageBox(NULL,"Root not positioned at 0","Root Node error",MB_OK);
}
}
*/
// Verify the joint hiearchy
{
int status = CompareNode(pRoot,pFn,lpVoid);
}
return 0;
}
void SkeletonVerifyPanel::OnErrorCheck(LPSKELETONVERIFY_CALLBACKFN pFn)
{
CErrorDlg dlg;
if (IDOK == dlg.DoModal())
{
TSTR maxDataPath = m_ip->GetDir(APP_PLUGCFG_DIR);
CString ini_path = Stuff::MString(maxDataPath)+"\\PlugIns.ini";
bool ini_file_exists = gos_DoesFileExist(ini_path);
if (!ini_file_exists)
{
if (pFn)
pFn(ERR_NOPLUGINS,NULL,(LPVOID)m_ip->GetMAXHWnd());
else
ReportError("Plugins.Ini file does not exist");
}
if (dlg.m_bAnim)
{
// Recursively - check the nodes
/*
JointPreference *prefs = JointEditDialog::GetJointPrefrence(node);
if(prefs != NULL)
{
if (prefs->excludeJoint == JointPreference::IncludeJoint)
{
include_joint = true;
}
else if (prefs->excludeJoint == JointPreference::AutoJoint)
{
if(dialog->NodeAnimated(node))
{
include_joint = true;
}
}
}
Control *tm_controler = node->GetTMController();
Control *translation_controler = tm_controler->GetPositionController();
Control *rotation_controler = tm_controler->GetRotationController();
if (c->ClassID() == Class_ID(LININTERP_POSITION_CLASS_ID,0))
{
return MW4Animation::JointAnimData::Point3DLinearKeyType;
}
else if (c->ClassID() == Class_ID(LININTERP_ROTATION_CLASS_ID,0))
{
return MW4Animation::JointAnimData::QuaternionLinearKeyType;
}
STOP(("INVALID KEY TYPE!!!!!"));
*/
}
if (dlg.m_bBatch)
{
CFileDialog fd(TRUE,NULL,NULL,0,"Batch Files (*.batch)|*.batch||",NULL);
if (IDOK == fd.DoModal())
{
// Check the Batch notation file
int err = CheckBatch(pFn,m_ip,(char *)(LPCTSTR)fd.GetPathName());
}
// Load the skeleton and save the names of the geometry nodes (mesh nodes)
// Verify that all Geometry node names in LOD are in skeleton
}
// Check that the OBB file has collision volumes - run skeleton verify too?
if (dlg.m_bObb)
{
int err = CheckCv(m_ip->GetRootNode());
// Check that the OBB file has collision volumes - run skeleton verify too?
// This is the CV exporter code:
MaxCollisionVolume *pSolidCv = NULL;
MaxCollisionVolume *pHierarchicalCv = NULL;
ConstructCVs(m_ip, &pSolidCv, &pHierarchicalCv);
if (!pSolidCv && !pHierarchicalCv)
{
CString str = CString(m_ip->GetCurFileName())+" does not contain any collision volumes.";
ReportError(str);
}
if (pHierarchicalCv)
delete pHierarchicalCv;
if (pSolidCv)
delete pSolidCv;
}
if (dlg.m_bLights)
{
// Check that the lights are a valid type
}
// Do this last cuz it loads files
if (dlg.m_bErf)
{
MAXProxies::MAXScene *scene = MAXProxies::MAXScene::MakeProxy(m_ip,"");
Check_Object(scene);
Stuff::NotationFile config_file(ini_path);
MAXProxies::FindMAXErrors process_errors(&config_file,m_ip,0,false,(LPVOID)&ReportError);
int err;
if (err = scene->FindErrors(&process_errors)> 0)
{
if (pFn)
pFn(ERR_DATAERRORS,NULL,(LPVOID)m_ip->GetMAXHWnd());
else
ReportError("Found Data Errors: Verification FAILED!");
}
}
MessageBox(NULL,"Error Check Done","Check For Errors",MB_OK);
}
}
static bool SetBit(WORD &flag,int bit)
{
WORD val = 1 << bit;
if (flag & val)
return true;
flag &= val;
return false;
}
/*
int FindGeometry(CStringList *list,INode *pNode)
{
int count =0;
if(IsNodeGeometry(pNode))
{
count ++;
CString name = pNode->GetName();
name.MakeLower();
if (NULL == list->Find(name))
list->AddHead(name);
}
for (int c = 0; c < pNode->NumberOfChildren(); c++)
{
count += FindGeometry(list,pNode->GetChildNode(c)
}
return count;
}
*/
int SkeletonVerifyPanel::CheckBatch(LPSKELETONVERIFY_CALLBACKFN pFn,Interface *m_ip,char *batch_filename)
{
Stuff::NotationFile notefile(batch_filename);
Stuff::NotationFile::PageIterator *pages = notefile.MakePageIterator();
Check_Object(pages);
Stuff::Page *page;
// Enumerate through the pages
// Each page - look for LODSkel, LODCHAR, LODDAM,OBB, ANIM - that files exist
// See that there are the same number of LODCHAR as LODDAM
while((page = pages->ReadAndNext()) != NULL)
{
Check_Object(page);
const char *pagename = page->GetName();
CStringList listChar,listDam,listDist,listShape;
CString path,obb,anim,skel;
WORD charFlag=0,damFlag=0,distFlag=0,shapeFlag=0;
// Gather Data
Stuff::Page::NoteIterator *notes = page->MakeNoteIterator();
Check_Object(notes);
Stuff::Note *note;
// Enumerate the entries in the section
while ((note = notes->ReadAndNext()) != NULL)
{
Check_Object(note);
const char *notename = note->GetName();
const char *entryname;
note->GetEntry(&entryname);
/************************/
// Compile the data
if (0==stricmp(notename,"path"))
{
if (path.GetLength())
{
ReportError(CString(pagename)+":"+CString(notename)+" set multiple times.");
// Error: This was already set
}
path = entryname;
}
else if (0==stricmp(notename,"anim"))
{
if (anim.GetLength())
{
// Error: This was already set
ReportError(CString(pagename)+":"+CString(notename)+" set multiple times.");
}
anim = entryname;
}
else if (0==stricmp(notename,"obb"))
{
if (obb.GetLength())
{
// Error: This was already set
ReportError(CString(pagename)+":"+CString(notename)+" set multiple times.");
}
obb = entryname;
}
else if (0==stricmp(notename,"LODskel"))
{
if (skel.GetLength())
{
// Error: This was already set
ReportError(CString(pagename)+":"+CString(notename)+" set multiple times.");
}
skel = entryname;
}
else if (0==stricmp(notename,"LODShape"))
{
listShape.AddTail(entryname);
int i = atoi(entryname+8);
if (SetBit(shapeFlag,i))
{
// Error: This was already set
ReportError(CString(pagename)+":"+CString(notename)+" set multiple times.");
}
}
else if (0==stricmp(notename,"LODDist"))
{
listDist.AddTail(entryname);
int i = atoi(entryname+7);
if (SetBit(distFlag,i))
{
// Error: This was already set
ReportError(CString(pagename)+":"+CString(notename)+" set multiple times.");
}
}
else if (0==stricmp(notename,"LODChar"))
{
listChar.AddTail(entryname);
int i = atoi(entryname+7);
if (SetBit(charFlag,i))
{
// Error: This was already set
ReportError(CString(pagename)+":"+CString(notename)+" set multiple times.");
}
}
else if (0==stricmp(notename,"LODDam"))
{
listDam.AddTail(entryname);
int i = atoi(entryname+6);
if (SetBit(damFlag,i))
{
// Error: This was already set
ReportError(CString(pagename)+":"+CString(notename)+" set multiple times.");
}
}
/************************/
}
Check_Object(notes);
delete notes;
/************************/
// Cross reference data
// Check that the LOD distances are sequential
{
WORD w = distFlag;
while (w & 1)
{
w = w>>1;
}
if (w)
{
// Error: LODDist is not sequential
ReportError(CString(pagename)+": LODDist is not sequential.");
}
}
// Check that we are doing either a shape or a character
bool bCharacter = (1 == listChar.GetCount());
if (listShape.GetCount())
{
if (bCharacter)
{
// Error: Specified both character and shape
ReportError(CString(pagename)+": Specified both character and shape.");
}
}
else
{
if (!bCharacter)
{
// Error: Specified neither character or shape
ReportError(CString(pagename)+": Needs to specify LOD Character or Shape");
}
}
// Check that if any LOD damage is specified that they match the distances
if (listDam.GetCount())
{
if (damFlag != distFlag)
{
// Error: LOD Damage does not match LOD Distances
ReportError(CString(pagename)+": LODDam does not match LODDist");
}
// Check that these files exist
POSITION pos = listDam.GetHeadPosition();
while (pos)
{
CString name = listDam.GetNext(pos);
if (!gos_DoesFileExist(name))
{
// Error: File Does not exist
ReportError(CString(pagename)+": "+name+" Does not exist.");
}
}
}
// Check that if any LOD char is specified that they match the distances
if (listChar.GetCount())
{
if (charFlag != distFlag)
{
// Error: LODChar does not match LODDist
ReportError(CString(pagename)+": LODChar does not match LODDist");
}
// LODSkel is required
if (0 == skel.GetLength())
{
// Error: LODSkel is required for Character export
ReportError(CString(pagename)+": LODSkel is required for character export.");
}
// Note: Anim and OBB are optional for Character export
// Check that these files exist
POSITION pos = listChar.GetHeadPosition();
while (pos)
{
CString name = listChar.GetNext(pos);
if (!gos_DoesFileExist(name))
{
// Error: File Does not exist
ReportError(CString(pagename)+": "+name+" Does not exist.");
}
}
}
// Check that if any LOD shape is specified that they match the distances
if (listShape.GetCount())
{
if (shapeFlag != distFlag)
{
// Error: LODShape does not match LODDist
ReportError(CString(pagename)+": LODShape does not match LODDist.");
}
if (skel.GetLength())
{
// Error: LODSkel not needed for shape export
ReportError(CString(pagename)+": LODSkel not needed for shape export.");
}
if (anim.GetLength())
{
// Error: anim not needed for shape export
ReportError(CString(pagename)+": "+anim+" not needed for shape export.");
}
// Note: OBB is optional for Shape export
// Check that these files exist
POSITION pos = listShape.GetHeadPosition();
while (pos)
{
CString name = listShape.GetNext(pos);
if (!gos_DoesFileExist(name))
{
// Error: File Does not exist
ReportError(CString(pagename)+": "+name+" Does not exist.");
}
}
}
if (path.GetLength())
{
if (!gos_DoesFileExist(path))
{
// Error: File Does not exist
ReportError(CString(pagename)+": "+path+" Does not exist.");
}
}
else
{
// Error: Path is required
ReportError(CString(pagename)+": Path is required.");
}
if (skel.GetLength())
{
if (!gos_DoesFileExist(skel))
{
// Error: File Does not exist
ReportError(CString(pagename)+": "+skel+" Does not exist.");
}
}
if (anim.GetLength())
{
if (!gos_DoesFileExist(anim))
{
// Error: File Does not exist
ReportError(CString(pagename)+": "+anim+" Does not exist.");
}
}
if (obb.GetLength())
{
if (!gos_DoesFileExist(obb))
{
// Error: File Does not exist
ReportError(CString(pagename)+": "+obb+" Does not exist.");
}
}
int err;
if (skel.GetLength())
err = m_ip->LoadFromFile(skel);
// Load the skeleton and save the names of the geometry nodes (mesh nodes)
/*CStringList meshList;
int count = FindGeometry(&meshList,m_ip->GetRootNode());
*/
// Verify that all Geometry node names in LOD are in skeleton
// Check that the OBB file has collision volumes - run skeleton verify too?
// This is the CV exporter code:
static int runcv = 0;
if (runcv)
{
MaxCollisionVolume *pCv = NULL;
CString name_world("joint_WORLD");
INode *pRoot = FindNodeByName(m_ip->GetRootNode(),name_world);
if (pRoot != NULL)
{
pCv = ConstructCVHeirarchy(pRoot);
Check_Object(pCv);
}
else
{
// this is not a heiracrhied object so recurse the first level children looking for
// cv's.
int cv_children = 0;
for (int x=0;x<m_ip->GetRootNode()->NumberOfChildren();x++)
{
INode *pChild = m_ip->GetRootNode()->GetChildNode(x);
if (!strnicmp(pChild->GetName(),"cv_",3))
{
MaxCollisionVolume *child_cv = ConstructCVHeirarchy(pChild);
if (child_cv != NULL)
{
// make sure we haven't found more than one base child that is a cv.
Check_Object(child_cv);
++cv_children;
if (cv_children > 1)
{
// Error: Objects are only allowed to have one base cv...
CString str = CString(pChild->GetName())+"Objects are only allowed to have one base cv . All other CV's must be childed off of that base parent";
ReportError(str);
//STOP(("Objects are only allowed to have one base cv . All other CV's must be childed off of that base parent"));
}
pCv = child_cv;
}
}
}
}
if (pCv)
{
if (pRoot != NULL)
{
// pVel is now the root of the heiarchy so far, let's export the SOLID obb
{
// find the velocity colision volume
MaxCollisionVolume* cv_velocity = pCv->FindCVNode("joint_WORLD");
if (NULL == cv_velocity)
{
// Error: ...
// NEED to report the error
//STOP(("Exporting OBB of a mech with no collision volumes. TODO: report this error and continue for batch export."));
//CString str = "Error exporting OBB: "+CString(name)+" has no collision volume under joint_WORLD";
//AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
//AfxMessageBox(str, MB_OK);
delete pCv;
return false;
}
Check_Object(cv_velocity);
//deleteing velocity automaticly removes it from the heirarchy.
// this is a behavior of chain.
delete cv_velocity;
Unregister_Pointer(cv_velocity);
}
}
delete pCv;
}
else
{
CString str = CString(skel)+" does not contain any collision volumes.";
ReportError(str);
}
}
/************************/
}
delete pages;
return 1;
}