//===========================================================================// // 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 #include #include "cvexport.h" #include "animationsuite.h" #include "exportUtil.h" #include #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;xNumberOfChildren();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;xSelectNode(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;xNumberOfChildren();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;xNumberOfChildren();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;xGetRootNode()->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; }