tge/tools/ms2dtsExporter/DTSPlugin.cpp
2025-02-17 23:17:30 -06:00

576 lines
19 KiB
C++
Executable File
Raw Permalink Blame History

#include <windows.h>
#include <commctrl.h>
#include <fstream>
#include <cstdio>
#include <cassert>
#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<char*>(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<6F> Luis Cebri<72>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();
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() ;
}