#include #include #include #include #include #include "resource.h" #pragma warning ( disable: 4786 ) #include "DTSPlugin.h" #include "DTSInterpolation.h" using namespace DTS ; HINSTANCE cDTSPlugin::hInstance ; cDTSPlugin::cDTSPlugin() { model = 0 ; } const char * cDTSPlugin::GetTitle() { #ifdef DEBUG return "Torque Game Engine DTS (Debug)..." ; #else return "Torque Game Engine DTS..." ; #endif } int cDTSPlugin::GetType() { return cMsPlugIn::eTypeExport ; } // ----------------------------------------------------------------------- bool extractSequence(char * name,MilkshapeShape::ImportConfig::Sequence* seq) { // Seq: ambient=2-3, cyclic, fps=2 if (strncmp(name,"seq:",4)) return false; bool named = false; char *ptr = name+4; for (char *tok = strtok(ptr,","); tok != 0; tok = strtok(0,",")) { // Strip lead/trailing white space for (; isspace(*tok); tok++); for (char* itr = tok + strlen(tok)-1; itr > tok && isspace(*itr); itr--) *itr = 0; // unsigned int fps; char buff[256],start[50],end[50]; if (sscanf(tok,"%[a-zA-Z0-9]=%[0-9]-%[0-9]",buff,start,end) == 3) { seq->name = buff; // Let the user specify start and end frames "base" 1, the // structure requires "base" 0, and end to be one past the last // frame. seq->start = atoi(start) - 1; seq->end = atoi(end); named = true; } else if (sscanf(tok,"fps=%d",&fps)) seq->fps = fps; else if (!strcmp(tok,"cyclic")) seq->cyclic = true; } if (!named) { char buff[512]; sprintf(buff,"Malformed sequence material: %s",name); MessageBox (NULL,buff,"Torque Game Engine (DTS) Exporter", MB_ICONSTOP | MB_OK) ; } return named; } bool extractOptions(char * name,MilkshapeShape::ImportConfig* config) { // opt: size=%d, size=%d, scale=%f, cyclic, fps=%d if (strncmp(name,"opt:",4)) return false; bool named = false; char *ptr = name+4; for (char *tok = strtok(ptr,","); tok != 0; tok = strtok(0,",")) { // Strip lead/trailing white space for (; isspace(*tok); tok++); for (char* itr = tok + strlen(tok)-1; itr > tok && isspace(*itr); itr--) *itr = 0; // int iv; float fv; if (sscanf(tok,"scale=%f",&fv)) config->scaleFactor = fv; else if (sscanf(tok,"size=%d",&iv)) config->minimumSize = iv; else if (sscanf(tok,"fps=%d",&iv)) config->animationFPS = iv; else if (!strcmp(tok,"cyclic")) config->animationCyclic = true; } return named; } // -------------------------------------------------------------------------- // Settings dialog box // -------------------------------------------------------------------------- static cDTSPlugin * currentPlugin = 0 ; static BOOL CALLBACK dialogProc (HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) { assert (currentPlugin != 0) ; return currentPlugin->settingsDialog(wnd, msg, wparam, lparam) ; } BOOL cDTSPlugin::settingsDialog (HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) { char buffer[512] ; int i ; switch (msg) { case WM_INITDIALOG: { savedConfig = config ; // Initialize edit controls sprintf (buffer, "%g", config.scaleFactor) ; SendDlgItemMessage (wnd, IDC_FACTOR, WM_SETTEXT, 0, (LPARAM)&buffer) ; EnableWindow (GetDlgItem(wnd, IDC_FACTOR), false) ; sprintf (buffer, "%d", config.minimumSize) ; SendDlgItemMessage (wnd, IDC_MINSIZE, WM_SETTEXT, 0, (LPARAM)&buffer) ; EnableWindow (GetDlgItem(wnd, IDC_MINSIZE), false) ; // Initialize check boxes SendDlgItemMessage (wnd, IDC_CHECK_MATERIALS, BM_SETCHECK, config.withMaterials, 0L) ; SendDlgItemMessage (wnd, IDC_CHECK_COLLVISIBLE, BM_SETCHECK, config.collisionVisible, 0L) ; SendDlgItemMessage (wnd, IDC_CHECK_ANIMATION, BM_SETCHECK, config.animation, 0L) ; // Only enable the mesh option if there are meshes called "Collision" bool collisionMesh = false; for (i = 0 ; i < msModel_GetMeshCount(model) ; i++) { msMesh * mesh = msModel_GetMeshAt (model, i) ; msMesh_GetName (mesh, buffer, 512) ; if (!_strnicmp (buffer, "Collision", 9)) collisionMesh = true; } EnableWindow (GetDlgItem(wnd, IDC_COL_MESH),collisionMesh) ; if (!collisionMesh && config.collisionType == Shape::C_Mesh) config.collisionType = Shape::C_None; // Initialize radio buttons switch (config.collisionType) { case Shape::C_BBox: SendDlgItemMessage (wnd, IDC_COL_BBOX, BM_SETCHECK, 1, 0L) ; break ; case Shape::C_Cylinder: SendDlgItemMessage (wnd, IDC_COL_CYLINDER, BM_SETCHECK, 1, 0L) ; break ; case Shape::C_Mesh: SendDlgItemMessage (wnd, IDC_COL_MESH, BM_SETCHECK, 1, 0L) ; break ; default: SendDlgItemMessage (wnd, IDC_COL_NONE, BM_SETCHECK, 1, 0L) ; break ; } // Disable/enable controls switch (config.collisionType) { case Shape::C_Mesh: EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITY), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYLABEL), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYMIN), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYMAX), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_MESHES), TRUE) ; break ; case 0: case Shape::C_BBox: EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITY), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYLABEL), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYMIN), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYMAX), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_MESHES), FALSE) ; break ; default: EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITY), TRUE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYLABEL), TRUE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYMIN), TRUE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYMAX), TRUE) ; EnableWindow (GetDlgItem(wnd, IDC_MESHES), FALSE) ; break ; } // Animation Settings sprintf (buffer, "%d", config.animationFPS) ; SendDlgItemMessage (wnd, IDC_FPS, WM_SETTEXT, 0, (LPARAM)&buffer) ; SendDlgItemMessage (wnd, IDC_CHECK_CYCLIC, BM_SETCHECK, config.animationCyclic, 0L) ; EnableWindow (GetDlgItem(wnd, IDC_FPS), false) ; EnableWindow (GetDlgItem(wnd, IDC_ANIMATION), false) ; EnableWindow (GetDlgItem(wnd, IDC_CHECK_CYCLIC), false) ; // Initialize trackbar SendDlgItemMessage (wnd, IDC_COMPLEXITY, TBM_SETRANGE, FALSE, MAKELONG(0,10)) ; SendDlgItemMessage (wnd, IDC_COMPLEXITY, TBM_SETPOS, TRUE, (int)(10.0f * config.collisionComplexity)) ; // Initialize Animation Sequence list struct { char* name; int width; } columns[5] = { { "Name", 100 }, { "Start", 40 }, { "End", 40 }, { "FPS", 40 }, { "Cylic", 40 } }; LVCOLUMN lvc; lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; for (int ic = 0; ic < 5; ic++) { // Columns lvc.iSubItem = ic; lvc.pszText = columns[ic].name; lvc.cx = columns[ic].width; lvc.fmt = (!ic)? LVCFMT_LEFT: LVCFMT_CENTER; ListView_InsertColumn(GetDlgItem(wnd, IDC_SEQUENCE_LIST), ic, &lvc); } LVITEM lvi; lvi.mask = LVIF_TEXT; for (int sc = 0; sc < config.sequence.size(); sc++) { // List items MilkshapeShape::ImportConfig::Sequence& si = config.sequence[sc]; lvi.iItem = sc; lvi.iSubItem = 0; lvi.pszText = const_cast(si.name.data()); ListView_InsertItem(GetDlgItem(wnd, IDC_SEQUENCE_LIST), &lvi); char buffer[256]; lvi.pszText = buffer; lvi.iSubItem = 1; sprintf(buffer,"%d",si.start + 1); ListView_SetItem(GetDlgItem(wnd, IDC_SEQUENCE_LIST), &lvi); lvi.iSubItem = 2; sprintf(buffer,"%d",si.end); ListView_SetItem(GetDlgItem(wnd, IDC_SEQUENCE_LIST), &lvi); lvi.iSubItem = 3; sprintf(buffer,"%d",si.fps); ListView_SetItem(GetDlgItem(wnd, IDC_SEQUENCE_LIST), &lvi); lvi.iSubItem = 4; sprintf(buffer,"%s",si.cyclic? "true": "false"); ListView_SetItem(GetDlgItem(wnd, IDC_SEQUENCE_LIST), &lvi); } break ; } case WM_COMMAND: // Handle check boxes if (HIWORD(wparam) == BN_CLICKED) { if (LOWORD(wparam) == IDC_CHECK_MATERIALS) { config.withMaterials = (SendDlgItemMessage (wnd, IDC_CHECK_MATERIALS, BM_GETCHECK, 0, 0) == BST_CHECKED) ; } if (LOWORD(wparam) == IDC_CHECK_ANIMATION) { config.animation = (SendDlgItemMessage (wnd, IDC_CHECK_ANIMATION, BM_GETCHECK, 0, 0) == BST_CHECKED) ; } if (LOWORD(wparam) == IDC_CHECK_CYCLIC) { config.animationCyclic = (SendDlgItemMessage (wnd, IDC_CHECK_CYCLIC, BM_GETCHECK, 0, 0) == BST_CHECKED) ; } if (LOWORD(wparam) == IDC_CHECK_COLLVISIBLE) { config.collisionVisible = (SendDlgItemMessage (wnd, IDC_CHECK_COLLVISIBLE, BM_GETCHECK, 0, 0) == BST_CHECKED) ; } } // Handle radio buttons if (HIWORD(wparam) == BN_CLICKED) { if (LOWORD(wparam) == IDC_COL_BBOX) config.collisionType = Shape::C_BBox ; if (LOWORD(wparam) == IDC_COL_CYLINDER) config.collisionType = Shape::C_Cylinder ; if (LOWORD(wparam) == IDC_COL_MESH) config.collisionType = Shape::C_Mesh ; if (LOWORD(wparam) == IDC_COL_NONE) config.collisionType = Shape::C_None ; // Disable/enable controls switch (config.collisionType) { case Shape::C_Mesh: EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITY), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYLABEL), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYMIN), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYMAX), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_MESHES), TRUE) ; break ; case 0: case Shape::C_BBox: EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITY), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYLABEL), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYMIN), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYMAX), FALSE) ; EnableWindow (GetDlgItem(wnd, IDC_MESHES), FALSE) ; break ; default: EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITY), TRUE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYLABEL), TRUE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYMIN), TRUE) ; EnableWindow (GetDlgItem(wnd, IDC_COMPLEXITYMAX), TRUE) ; EnableWindow (GetDlgItem(wnd, IDC_MESHES), FALSE) ; break ; } } // Handle buttons at the bottom of the dialog box if (wparam == IDCANCEL) { config = savedConfig ; EndDialog (wnd, IDCANCEL) ; return TRUE ; } if (wparam == IDOK) { EndDialog (wnd, IDOK) ; return TRUE ; } if (wparam == IDABOUT) { MessageBox (wnd, "Torque Game Engine (DTS) Exporter\n\r" "Version 1.3 \n\r" "(C) GarageGames & José Luis Cebrián 2001\n\r" "____________________________________________________________\n\r\n\r" "This is a work in progress, many DTS features are currently not yet supported.\n\r" "Please check the www.garagegames.com web site for the latest information on this\n\r" "exporter or the Torque engine in general.\n\r" "\n\r" "If you make the collision meshes visible, they are assigned a texture called\n\r" "\"collision_mesh\". If this JPG, or PNG texture does not exists in the .DTS\n\r" "directory, the collision mesh will appear grey in the engine.", "Torque Game Engine (DTS) Exporter", MB_OK | MB_ICONINFORMATION) ; return TRUE ; } break ; case WM_NOTIFY: // Handle spin buttons if (lparam) { LPNMHDR hdr = (LPNMHDR)lparam ; if (hdr->code == UDN_DELTAPOS) { if (hdr->idFrom == IDC_FACTORSPIN) { LPNMUPDOWN delta = (LPNMUPDOWN)lparam ; if (delta->iDelta > 0) config.scaleFactor *= 2.0f ; if (delta->iDelta < 0) config.scaleFactor *= 0.5f ; sprintf (buffer, "%g", config.scaleFactor) ; SendDlgItemMessage (wnd, IDC_FACTOR, WM_SETTEXT, 0, (LPARAM)&buffer) ; } } } break ; case WM_HSCROLL: // Handle trackbar if ((HWND)lparam == GetDlgItem(wnd, IDC_COMPLEXITY)) { float pos = (float)SendDlgItemMessage(wnd, IDC_COMPLEXITY, TBM_GETPOS, 0, 0); config.collisionComplexity = pos / 10.0f ; } break ; } return FALSE ; } // -------------------------------------------------------------------------- // Plugin entry point // -------------------------------------------------------------------------- int cDTSPlugin::Execute (msModel * model) { if (!model) return -1 ; this->model = model ; // Don't export an empty model if (msModel_GetMeshCount(model) == 0) { MessageBox (NULL, "The model is empty. Nothing to export.", "Torque Game Engine (DTS) Exporter", MB_ICONSTOP | MB_OK) ; return 0 ; } // If there are any meshes called "Collision", use those for collision for (int i = 0 ; i < msModel_GetMeshCount(model) ; i++) { char buffer[256] ; msMesh * mesh = msModel_GetMeshAt (model, i) ; msMesh_GetName (mesh, buffer, 256) ; if (_strnicmp (buffer, "Collision", 9) == 0) { config.collisionType = Shape::C_Mesh ; break; } } // Register the common controls before use // (it is safe to do this more than once) INITCOMMONCONTROLSEX ic ; ic.dwSize = sizeof(ic) ; ic.dwICC = ICC_UPDOWN_CLASS | ICC_BAR_CLASSES ; InitCommonControlsEx (&ic) ; // Extract options and sequence information from the materials. // The options are done first since they can effect the animation // sequences and we don't want to be affected by material ordering. int numMaterials = msModel_GetMaterialCount(model) ; config.reset(); int i; for (i = 0 ; i < numMaterials ; i++) { char name[MS_MAX_PATH+1]; msMaterial * msMat = msModel_GetMaterialAt(model, i); msMaterial_GetName (msMat, name, MS_MAX_PATH); extractOptions(name,&config); } for (i = 0 ; i < numMaterials ; i++) { char name[MS_MAX_PATH+1]; msMaterial * msMat = msModel_GetMaterialAt(model, i); msMaterial_GetName (msMat, name, MS_MAX_PATH); MilkshapeShape::ImportConfig::Sequence si; si.fps = config.animationFPS; si.cyclic = config.animationCyclic; if (extractSequence(name,&si)) config.sequence.push_back(si); } // If there were no sequences the default is a looping ambient // for the total timeline if (!config.sequence.size()) { MilkshapeShape::ImportConfig::Sequence si; si.name = "Ambient"; si.start = 0; si.end = msModel_GetTotalFrames (model); si.cyclic = true; si.fps = config.animationFPS; config.sequence.push_back(si); } // Show a preferences dialog currentPlugin = this ; if (DialogBox (hInstance, MAKEINTRESOURCE(IDD_DIALOG), NULL, dialogProc) != IDOK) { currentPlugin = 0 ; return 0 ; } currentPlugin = 0 ; // Gets the file name OPENFILENAME ofn ; char szFile[512] = "" ; ZeroMemory (&ofn, sizeof(ofn)) ; ofn.lStructSize = sizeof(ofn) ; ofn.hInstance = GetModuleHandle(NULL) ; ofn.lpstrFilter = "DTS Files\0*.dts\0All\0*.*\0" ; ofn.lpstrFile = szFile ; ofn.Flags = OFN_PATHMUSTEXIST | OFN_ENABLESIZING | OFN_OVERWRITEPROMPT ; ofn.nMaxFile = 500 ; ofn.lpstrDefExt = "dts" ; if (GetSaveFileName (&ofn) != 0) { std::ofstream file (szFile, std::ios::binary | std::ios::trunc | std::ios::out) ; if (!file) { MessageBox (NULL, "Error creating the file.", "Torque Game Engine (DTS) Exporter", MB_ICONSTOP | MB_OK) ; return 0 ; } else { LoadCursor (hInstance, IDC_WAIT) ; MilkshapeShape shape(model, config) ; shape.save(file) ; LoadCursor (hInstance, IDC_ARROW) ; return 1 ; } } return 0 ; } // -------------------------------------------------------------------------- // DLL entry point // -------------------------------------------------------------------------- BOOL APIENTRY DllMain (HINSTANCE h, DWORD, LPVOID) { cDTSPlugin::setInstance(h) ; return TRUE ; } cMsPlugIn * CreatePlugIn() { return new cDTSPlugin() ; }