goat3d

annotate exporters/maxgoat/src/maxgoat.cc @ 73:9862541fdcf5

- build qt goatview on linux - fixed line endings in a bunch of files
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 06 May 2014 03:57:11 +0300
parents 36e39632db75
children 8b156bc5205b
rev   line source
nuclear@73 1 #include <stdio.h>
nuclear@73 2 #include <string.h>
nuclear@73 3 #include <stdlib.h>
nuclear@73 4 #include <ctype.h>
nuclear@73 5 #include <errno.h>
nuclear@73 6 #include <map>
nuclear@73 7 #include <vector>
nuclear@73 8 #include <windows.h>
nuclear@73 9 #include <shlobj.h>
nuclear@73 10 #include "max.h"
nuclear@73 11 #include "impexp.h" // SceneExport
nuclear@73 12 #include "iparamb2.h" // ClassDesc2
nuclear@73 13 #include "plugapi.h"
nuclear@73 14 #include "IGame.h"
nuclear@73 15 #include "IGameExport.h"
nuclear@73 16 #include "IGameControl.h"
nuclear@73 17 #include "IConversionmanager.h"
nuclear@73 18 #include "goat3d.h"
nuclear@73 19 #include "config.h"
nuclear@73 20 #include "logger.h"
nuclear@73 21 #include "resource.h"
nuclear@73 22
nuclear@73 23
nuclear@73 24 #pragma comment (lib, "core.lib")
nuclear@73 25 #pragma comment (lib, "geom.lib")
nuclear@73 26 #pragma comment (lib, "gfx.lib")
nuclear@73 27 #pragma comment (lib, "mesh.lib")
nuclear@73 28 #pragma comment (lib, "maxutil.lib")
nuclear@73 29 #pragma comment (lib, "maxscrpt.lib")
nuclear@73 30 #pragma comment (lib, "paramblk2.lib")
nuclear@73 31 #pragma comment (lib, "msxml2.lib")
nuclear@73 32 #pragma comment (lib, "igame.lib")
nuclear@73 33 #pragma comment (lib, "comctl32.lib")
nuclear@73 34
nuclear@73 35
nuclear@73 36 #define COPYRIGHT \
nuclear@73 37 L"Copyright 2014 (C) John Tsiombikas - GNU General Public License v3, see COPYING for details."
nuclear@73 38 #define VERSION(major, minor) \
nuclear@73 39 ((major) * 100 + ((minor) < 10 ? (minor) * 10 : (minor)))
nuclear@73 40
nuclear@73 41 static INT_PTR CALLBACK scene_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
nuclear@73 42 static INT_PTR CALLBACK anim_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam);
nuclear@73 43 static void get_position_keys(IGameControl *ctrl, goat3d_node *node);
nuclear@73 44 static void get_xyz_position_keys(IGameControl *ctrl, goat3d_node *node);
nuclear@73 45 static void get_rotation_keys(IGameControl *ctrl, goat3d_node *node);
nuclear@73 46 static void get_euler_keys(IGameControl *ctrl, goat3d_node *node);
nuclear@73 47 static void get_scaling_keys(IGameControl *ctrl, goat3d_node *node);
nuclear@73 48 static const char *max_string(const MCHAR *wstr);
nuclear@73 49
nuclear@73 50 HINSTANCE hinst;
nuclear@73 51
nuclear@73 52 class GoatExporter : public SceneExport {
nuclear@73 53 private:
nuclear@73 54 std::map<IGameMaterial*, goat3d_material*> mtlmap;
nuclear@73 55 std::map<IGameNode*, goat3d_node*> nodemap;
nuclear@73 56
nuclear@73 57 public:
nuclear@73 58 IGameScene *igame;
nuclear@73 59
nuclear@73 60 int DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent = FALSE, DWORD opt = 0);
nuclear@73 61
nuclear@73 62 void process_materials(goat3d *goat);
nuclear@73 63
nuclear@73 64 void process_node(goat3d *goat, goat3d_node *parent, IGameNode *maxnode);
nuclear@73 65
nuclear@73 66 void process_mesh(goat3d *goat, goat3d_mesh *mesh, IGameObject *maxobj);
nuclear@73 67 void process_light(goat3d *goat, goat3d_light *light, IGameObject *maxobj);
nuclear@73 68 void process_camera(goat3d *goat, goat3d_camera *cam, IGameObject *maxobj);
nuclear@73 69
nuclear@73 70
nuclear@73 71 int ExtCount() { return 1; }
nuclear@73 72 const TCHAR *Ext(int n) { return L"goatsce"; }
nuclear@73 73 const TCHAR *LongDesc() { return L"Goat3D scene file"; }
nuclear@73 74 const TCHAR *ShortDesc() { return L"Goat3D"; }
nuclear@73 75 const TCHAR *AuthorName() { return L"John Tsiombikas"; }
nuclear@73 76 const TCHAR *CopyrightMessage() { return COPYRIGHT; }
nuclear@73 77 const TCHAR *OtherMessage1() { return L"other1"; }
nuclear@73 78 const TCHAR *OtherMessage2() { return L"other2"; }
nuclear@73 79 unsigned int Version() { return VERSION(VER_MAJOR, VER_MINOR); }
nuclear@73 80 void ShowAbout(HWND win) { MessageBoxA(win, "Goat3D exporter plugin", "About this plugin", 0); }
nuclear@73 81 };
nuclear@73 82
nuclear@73 83 class GoatAnimExporter : public GoatExporter {
nuclear@73 84 private:
nuclear@73 85 public:
nuclear@73 86 int DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent = FALSE, DWORD opt = 0);
nuclear@73 87
nuclear@73 88 const TCHAR *Ext(int n) { return L"goatanm"; }
nuclear@73 89 const TCHAR *LongDesc() { return L"Goat3D animation file"; }
nuclear@73 90 };
nuclear@73 91
nuclear@73 92
nuclear@73 93 // ---- GoatExporter implementation ----
nuclear@73 94
nuclear@73 95 int GoatExporter::DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface,
nuclear@73 96 BOOL non_interactive, DWORD opt)
nuclear@73 97 {
nuclear@73 98 if(!DialogBox(hinst, MAKEINTRESOURCE(IDD_GOAT_SCE), 0, scene_gui_handler)) {
nuclear@73 99 return IMPEXP_CANCEL;
nuclear@73 100 }
nuclear@73 101
nuclear@73 102 mtlmap.clear();
nuclear@73 103 nodemap.clear();
nuclear@73 104
nuclear@73 105 char fname[512];
nuclear@73 106 wcstombs(fname, name, sizeof fname - 1);
nuclear@73 107 for(int i=0; fname[i]; i++) {
nuclear@73 108 fname[i] = tolower(fname[i]);
nuclear@73 109 }
nuclear@73 110 char *basename = (char*)alloca(strlen(fname) + 1);
nuclear@73 111 strcpy(basename, fname);
nuclear@73 112 char *suffix = strrchr(basename, '.');
nuclear@73 113 if(suffix) *suffix = 0;
nuclear@73 114
nuclear@73 115 maxlog("Exporting Goat3D Scene (text) file: %s\n", fname);
nuclear@73 116 if(!(igame = GetIGameInterface())) {
nuclear@73 117 maxlog("failed to get the igame interface\n");
nuclear@73 118 return IMPEXP_FAIL;
nuclear@73 119 }
nuclear@73 120 IGameConversionManager *cm = GetConversionManager();
nuclear@73 121 cm->SetCoordSystem(IGameConversionManager::IGAME_OGL);
nuclear@73 122 igame->InitialiseIGame();
nuclear@73 123
nuclear@73 124 goat3d *goat = goat3d_create();
nuclear@73 125 goat3d_set_name(goat, basename);
nuclear@73 126
nuclear@73 127 process_materials(goat);
nuclear@73 128
nuclear@73 129 // process all nodes
nuclear@73 130 for(int i=0; i<igame->GetTopLevelNodeCount(); i++) {
nuclear@73 131 IGameNode *node = igame->GetTopLevelNode(i);
nuclear@73 132 process_node(goat, 0, node);
nuclear@73 133 }
nuclear@73 134
nuclear@73 135 if(goat3d_save(goat, fname) == -1) {
nuclear@73 136 goat3d_free(goat);
nuclear@73 137 return IMPEXP_FAIL;
nuclear@73 138 }
nuclear@73 139
nuclear@73 140 goat3d_free(goat);
nuclear@73 141 return IMPEXP_SUCCESS;
nuclear@73 142 }
nuclear@73 143
nuclear@73 144 void GoatExporter::process_materials(goat3d *goat)
nuclear@73 145 {
nuclear@73 146 IGameProperty *prop;
nuclear@73 147
nuclear@73 148 int num_mtl = igame->GetRootMaterialCount();
nuclear@73 149 for(int i=0; i<num_mtl; i++) {
nuclear@73 150 IGameMaterial *maxmtl = igame->GetRootMaterial(i);
nuclear@73 151 if(maxmtl) {
nuclear@73 152 goat3d_material *mtl = goat3d_create_mtl();
nuclear@73 153
nuclear@73 154 const char *name = max_string(maxmtl->GetMaterialName());
nuclear@73 155 if(name) {
nuclear@73 156 goat3d_set_mtl_name(mtl, name);
nuclear@73 157 }
nuclear@73 158
nuclear@73 159 // diffuse
nuclear@73 160 if((prop = maxmtl->GetDiffuseData())) {
nuclear@73 161 Point3 diffuse(1, 1, 1);
nuclear@73 162 prop->GetPropertyValue(diffuse);
nuclear@73 163 goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_DIFFUSE, diffuse[0],
nuclear@73 164 diffuse[1], diffuse[2]);
nuclear@73 165 }
nuclear@73 166 // specular
nuclear@73 167 if((prop = maxmtl->GetSpecularData())) {
nuclear@73 168 Point3 specular(0, 0, 0);
nuclear@73 169 prop->GetPropertyValue(specular);
nuclear@73 170
nuclear@73 171 float sstr = 1.0;
nuclear@73 172 if((prop = maxmtl->GetSpecularLevelData())) {
nuclear@73 173 prop->GetPropertyValue(sstr);
nuclear@73 174 }
nuclear@73 175 goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_SPECULAR, specular[0] * sstr,
nuclear@73 176 specular[1] * sstr, specular[2] * sstr);
nuclear@73 177 }
nuclear@73 178 // shininess
nuclear@73 179 if((prop = maxmtl->GetGlossinessData())) {
nuclear@73 180 float shin;
nuclear@73 181 prop->GetPropertyValue(shin);
nuclear@73 182 goat3d_set_mtl_attrib1f(mtl, GOAT3D_MAT_ATTR_SHININESS, shin * 100.0);
nuclear@73 183 }
nuclear@73 184
nuclear@73 185 // textures
nuclear@73 186 for(int j=0; j<maxmtl->GetNumberOfTextureMaps(); j++) {
nuclear@73 187 IGameTextureMap *tex = maxmtl->GetIGameTextureMap(j);
nuclear@73 188
nuclear@73 189 const char *fname = max_string(tex->GetBitmapFileName());
nuclear@73 190 if(!fname) {
nuclear@73 191 continue;
nuclear@73 192 }
nuclear@73 193
nuclear@73 194 int slot = tex->GetStdMapSlot();
nuclear@73 195 switch(slot) {
nuclear@73 196 case ID_DI: // diffuse
nuclear@73 197 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_DIFFUSE, fname);
nuclear@73 198 break;
nuclear@73 199
nuclear@73 200 case ID_SP:
nuclear@73 201 case ID_SS:
nuclear@73 202 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SPECULAR, fname);
nuclear@73 203 break;
nuclear@73 204
nuclear@73 205 case ID_SH:
nuclear@73 206 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SHININESS, fname);
nuclear@73 207 break;
nuclear@73 208
nuclear@73 209 case ID_BU:
nuclear@73 210 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_NORMAL, fname);
nuclear@73 211 break;
nuclear@73 212
nuclear@73 213 case ID_RL:
nuclear@73 214 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_REFLECTION, fname);
nuclear@73 215 break;
nuclear@73 216
nuclear@73 217 case ID_RR:
nuclear@73 218 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_TRANSMISSION, fname);
nuclear@73 219 break;
nuclear@73 220
nuclear@73 221 default:
nuclear@73 222 break;
nuclear@73 223 }
nuclear@73 224 }
nuclear@73 225
nuclear@73 226 goat3d_add_mtl(goat, mtl);
nuclear@73 227 mtlmap[maxmtl] = mtl;
nuclear@73 228 }
nuclear@73 229 }
nuclear@73 230 }
nuclear@73 231
nuclear@73 232 void GoatExporter::process_node(goat3d *goat, goat3d_node *parent, IGameNode *maxnode)
nuclear@73 233 {
nuclear@73 234 goat3d_node *node = goat3d_create_node();
nuclear@73 235 goat3d_add_node(goat, node);
nuclear@73 236
nuclear@73 237 if(parent) {
nuclear@73 238 goat3d_add_node_child(parent, node);
nuclear@73 239 }
nuclear@73 240
nuclear@73 241 const char *name = max_string(maxnode->GetName());
nuclear@73 242 if(name) {
nuclear@73 243 goat3d_set_node_name(node, name);
nuclear@73 244 }
nuclear@73 245
nuclear@73 246 IGameObject *maxobj = maxnode->GetIGameObject();
nuclear@73 247 IGameObject::ObjectTypes type = maxobj->GetIGameType();
nuclear@73 248
nuclear@73 249 switch(type) {
nuclear@73 250 case IGameObject::IGAME_MESH:
nuclear@73 251 {
nuclear@73 252 goat3d_mesh *mesh = goat3d_create_mesh();
nuclear@73 253 if(name) goat3d_set_mesh_name(mesh, name);
nuclear@73 254 goat3d_set_node_object(node, GOAT3D_NODE_MESH, mesh);
nuclear@73 255
nuclear@73 256 // get the node material and assign it to the mesh
nuclear@73 257 IGameMaterial *maxmtl = maxnode->GetNodeMaterial();
nuclear@73 258 goat3d_material *mtl = mtlmap[maxmtl];
nuclear@73 259 if(mtl) {
nuclear@73 260 goat3d_set_mesh_mtl(mesh, mtl);
nuclear@73 261 }
nuclear@73 262
nuclear@73 263 process_mesh(goat, mesh, maxobj);
nuclear@73 264 goat3d_add_mesh(goat, mesh);
nuclear@73 265 }
nuclear@73 266 break;
nuclear@73 267
nuclear@73 268 case IGameObject::IGAME_LIGHT:
nuclear@73 269 {
nuclear@73 270 goat3d_light *light = goat3d_create_light();
nuclear@73 271 //if(name) goat3d_set_light_name(light, name);
nuclear@73 272 goat3d_set_node_object(node, GOAT3D_NODE_LIGHT, light);
nuclear@73 273
nuclear@73 274 process_light(goat, light, maxobj);
nuclear@73 275 goat3d_add_light(goat, light);
nuclear@73 276 }
nuclear@73 277 break;
nuclear@73 278
nuclear@73 279 case IGameObject::IGAME_CAMERA:
nuclear@73 280 {
nuclear@73 281 goat3d_camera *cam = goat3d_create_camera();
nuclear@73 282 //if(name) goat3d_set_camera_name(camera, name);
nuclear@73 283 goat3d_set_node_object(node, GOAT3D_NODE_CAMERA, cam);
nuclear@73 284
nuclear@73 285 process_camera(goat, cam, maxobj);
nuclear@73 286 goat3d_add_camera(goat, cam);
nuclear@73 287 }
nuclear@73 288 break;
nuclear@73 289
nuclear@73 290 default:
nuclear@73 291 // otherwise don't assign an object, essentially treating it as a null node
nuclear@73 292 break;
nuclear@73 293 }
nuclear@73 294
nuclear@73 295 // grab the animation data
nuclear@73 296 if(!dynamic_cast<GoatAnimExporter*>(this)) {
nuclear@73 297 // no animation, just get the static PRS
nuclear@73 298 GMatrix maxmatrix = maxnode->GetObjectTM();
nuclear@73 299 Point3 trans = maxmatrix.Translation();
nuclear@73 300 Quat rot = maxmatrix.Rotation();
nuclear@73 301 Point3 scale = maxmatrix.Scaling();
nuclear@73 302
nuclear@73 303 goat3d_set_node_position(node, trans.x, trans.y, trans.z, 0);
nuclear@73 304 goat3d_set_node_rotation(node, rot.x, rot.y, rot.z, rot.w, 0);
nuclear@73 305 goat3d_set_node_scaling(node, scale.x, scale.y, scale.z, 0);
nuclear@73 306
nuclear@73 307 } else {
nuclear@73 308 // exporting animations (if available)
nuclear@73 309 // TODO sample keys if requested
nuclear@73 310 IGameControl *ctrl = maxnode->GetIGameControl();
nuclear@73 311 if(ctrl) {
nuclear@73 312 if(ctrl->IsAnimated(IGAME_POS) || ctrl->IsAnimated(IGAME_POS_X) ||
nuclear@73 313 ctrl->IsAnimated(IGAME_POS_Y) || ctrl->IsAnimated(IGAME_POS_Z)) {
nuclear@73 314 get_position_keys(ctrl, node);
nuclear@73 315 }
nuclear@73 316 if(ctrl->IsAnimated(IGAME_ROT) || ctrl->IsAnimated(IGAME_EULER_X) ||
nuclear@73 317 ctrl->IsAnimated(IGAME_EULER_Y) || ctrl->IsAnimated(IGAME_EULER_Z)) {
nuclear@73 318 get_rotation_keys(ctrl, node);
nuclear@73 319 }
nuclear@73 320 if(ctrl->IsAnimated(IGAME_SCALE)) {
nuclear@73 321 get_scaling_keys(ctrl, node);
nuclear@73 322 }
nuclear@73 323 } else {
nuclear@73 324 maxlog("%s: failed to get IGameControl for node: %s\n", __FUNCTION__, name);
nuclear@73 325 }
nuclear@73 326 }
nuclear@73 327
nuclear@73 328 for(int i=0; i<maxnode->GetChildCount(); i++) {
nuclear@73 329 process_node(goat, node, maxnode->GetNodeChild(i));
nuclear@73 330 }
nuclear@73 331 }
nuclear@73 332
nuclear@73 333 #define KEY_TIME(key) ((long)(TicksToSec(key.t) * 1000.0))
nuclear@73 334
nuclear@73 335 static void get_position_keys(IGameControl *ctrl, goat3d_node *node)
nuclear@73 336 {
nuclear@73 337 const char *nodename = goat3d_get_node_name(node);
nuclear@73 338 IGameKeyTab keys;
nuclear@73 339
nuclear@73 340 if(ctrl->GetLinearKeys(keys, IGAME_POS)) {
nuclear@73 341 maxlog("node %s: getting %d linear position keys\n", nodename, keys.Count());
nuclear@73 342 for(int i=0; i<keys.Count(); i++) {
nuclear@73 343 Point3 p = keys[i].linearKey.pval;
nuclear@73 344 goat3d_set_node_position(node, p.x, p.y, p.z, KEY_TIME(keys[i]));
nuclear@73 345 }
nuclear@73 346 } else if(ctrl->GetBezierKeys(keys, IGAME_POS)) {
nuclear@73 347 maxlog("node %s: getting %d bezier position keys\n", nodename, keys.Count());
nuclear@73 348 for(int i=0; i<keys.Count(); i++) {
nuclear@73 349 Point3 p = keys[i].bezierKey.pval;
nuclear@73 350 goat3d_set_node_position(node, p.x, p.y, p.z, KEY_TIME(keys[i]));
nuclear@73 351 }
nuclear@73 352 } else if(ctrl->GetTCBKeys(keys, IGAME_POS)) {
nuclear@73 353 maxlog("node %s: getting %d tcb position keys\n", nodename, keys.Count());
nuclear@73 354 for(int i=0; i<keys.Count(); i++) {
nuclear@73 355 Point3 p = keys[i].tcbKey.pval;
nuclear@73 356 goat3d_set_node_position(node, p.x, p.y, p.z, KEY_TIME(keys[i]));
nuclear@73 357 }
nuclear@73 358 } else {
nuclear@73 359 get_xyz_position_keys(ctrl, node);
nuclear@73 360 }
nuclear@73 361 }
nuclear@73 362
nuclear@73 363 static void get_xyz_position_keys(IGameControl *ctrl, goat3d_node *node)
nuclear@73 364 {
nuclear@73 365 const char *nodename = goat3d_get_node_name(node);
nuclear@73 366 IGameKeyTab keys;
nuclear@73 367 IGameControlType postype[] = {IGAME_POS_X, IGAME_POS_Y, IGAME_POS_Z};
nuclear@73 368 std::map<long, Point3> pos;
nuclear@73 369
nuclear@73 370 for(int i=0; i<3; i++) {
nuclear@73 371 if(ctrl->GetLinearKeys(keys, postype[i])) {
nuclear@73 372 maxlog("node %s: getting %d linear position %c keys\n", nodename, keys.Count(), "xyz"[i]);
nuclear@73 373 for(int j=0; j<keys.Count(); j++) {
nuclear@73 374 long tm = KEY_TIME(keys[j]);
nuclear@73 375 Point3 v = pos[tm];
nuclear@73 376 v[i] = keys[j].linearKey.fval;
nuclear@73 377 pos[tm] = v;
nuclear@73 378 }
nuclear@73 379 } else if(ctrl->GetBezierKeys(keys, postype[i])) {
nuclear@73 380 maxlog("node %s: getting %d bezier position %c keys\n", nodename, keys.Count(), "xyz"[i]);
nuclear@73 381 for(int j=0; j<keys.Count(); j++) {
nuclear@73 382 long tm = KEY_TIME(keys[j]);
nuclear@73 383 Point3 v = pos[tm];
nuclear@73 384 v[i] = keys[j].bezierKey.fval;
nuclear@73 385 pos[tm] = v;
nuclear@73 386 }
nuclear@73 387 } else if(ctrl->GetTCBKeys(keys, postype[i])) {
nuclear@73 388 maxlog("node %s: getting %d tcb position %c keys\n", nodename, keys.Count(), "xyz"[i]);
nuclear@73 389 for(int j=0; j<keys.Count(); j++) {
nuclear@73 390 long tm = KEY_TIME(keys[j]);
nuclear@73 391 Point3 v = pos[tm];
nuclear@73 392 v[i] = keys[j].tcbKey.fval;
nuclear@73 393 pos[tm] = v;
nuclear@73 394 }
nuclear@73 395 }
nuclear@73 396 }
nuclear@73 397
nuclear@73 398 std::map<long, Point3>::iterator it = pos.begin();
nuclear@73 399 while(it != pos.end()) {
nuclear@73 400 Point3 p = it->second;
nuclear@73 401 goat3d_set_node_position(node, p.x, p.y, p.z, it->first);
nuclear@73 402 ++it;
nuclear@73 403 }
nuclear@73 404 }
nuclear@73 405
nuclear@73 406 static void get_rotation_keys(IGameControl *ctrl, goat3d_node *node)
nuclear@73 407 {
nuclear@73 408 const char *nodename = goat3d_get_node_name(node);
nuclear@73 409 IGameKeyTab rkeys;
nuclear@73 410
nuclear@73 411 if(ctrl->GetLinearKeys(rkeys, IGAME_ROT)) {
nuclear@73 412 maxlog("node %s: getting %d linear rotation keys\n", nodename, rkeys.Count());
nuclear@73 413 for(int i=0; i<rkeys.Count(); i++) {
nuclear@73 414 Quat q = rkeys[i].linearKey.qval;
nuclear@73 415 goat3d_set_node_rotation(node, q.x, q.y, q.z, q.w, KEY_TIME(rkeys[i]));
nuclear@73 416 }
nuclear@73 417 } else if(ctrl->GetBezierKeys(rkeys, IGAME_ROT)) {
nuclear@73 418 maxlog("node %s: getting %d bezier rotation keys\n", nodename, rkeys.Count());
nuclear@73 419 for(int i=0; i<rkeys.Count(); i++) {
nuclear@73 420 Quat q = rkeys[i].bezierKey.qval;
nuclear@73 421 goat3d_set_node_rotation(node, q.x, q.y, q.z, q.w, KEY_TIME(rkeys[i]));
nuclear@73 422 }
nuclear@73 423 } else if(ctrl->GetTCBKeys(rkeys, IGAME_ROT)) {
nuclear@73 424 maxlog("node %s: getting %d TCB rotation keys\n", nodename, rkeys.Count());
nuclear@73 425 for(int i=0; i<rkeys.Count(); i++) {
nuclear@73 426 Quat q(rkeys[i].tcbKey.aval);
nuclear@73 427 goat3d_set_node_rotation(node, q.x, q.y, q.z, q.w, KEY_TIME(rkeys[i]));
nuclear@73 428 }
nuclear@73 429 } else {
nuclear@73 430 get_euler_keys(ctrl, node);
nuclear@73 431 }
nuclear@73 432 }
nuclear@73 433
nuclear@73 434 static void get_euler_keys(IGameControl *ctrl, goat3d_node *node)
nuclear@73 435 {
nuclear@73 436 const char *nodename = goat3d_get_node_name(node);
nuclear@73 437 IGameKeyTab keys;
nuclear@73 438 IGameControlType eulertype[] = {IGAME_EULER_X, IGAME_EULER_Y, IGAME_EULER_Z};
nuclear@73 439 std::map<long, Point3> euler;
nuclear@73 440
nuclear@73 441 for(int i=0; i<3; i++) {
nuclear@73 442 if(ctrl->GetLinearKeys(keys, eulertype[i])) {
nuclear@73 443 maxlog("node %s: getting %d linear euler %c keys\n", nodename, keys.Count(), "xyz"[i]);
nuclear@73 444 for(int j=0; j<keys.Count(); j++) {
nuclear@73 445 long tm = KEY_TIME(keys[j]);
nuclear@73 446 Point3 v = euler[tm];
nuclear@73 447 v[i] = keys[j].linearKey.fval;
nuclear@73 448 euler[tm] = v;
nuclear@73 449 }
nuclear@73 450 } else if(ctrl->GetBezierKeys(keys, eulertype[i])) {
nuclear@73 451 maxlog("node %s: getting %d bezier euler %c keys\n", nodename, keys.Count(), "xyz"[i]);
nuclear@73 452 for(int j=0; j<keys.Count(); j++) {
nuclear@73 453 long tm = KEY_TIME(keys[j]);
nuclear@73 454 Point3 v = euler[tm];
nuclear@73 455 v[i] = keys[j].bezierKey.fval;
nuclear@73 456 euler[tm] = v;
nuclear@73 457 }
nuclear@73 458 } else if(ctrl->GetTCBKeys(keys, eulertype[i])) {
nuclear@73 459 maxlog("node %s: getting %d tcb euler %c keys\n", nodename, keys.Count(), "xyz"[i]);
nuclear@73 460 for(int j=0; j<keys.Count(); j++) {
nuclear@73 461 long tm = KEY_TIME(keys[j]);
nuclear@73 462 Point3 v = euler[tm];
nuclear@73 463 v[i] = keys[j].tcbKey.fval;
nuclear@73 464 euler[tm] = v;
nuclear@73 465 }
nuclear@73 466 }
nuclear@73 467 }
nuclear@73 468
nuclear@73 469 int order = ctrl->GetEulerOrder();
nuclear@73 470 std::map<long, Point3>::iterator it = euler.begin();
nuclear@73 471 while(it != euler.end()) {
nuclear@73 472 Quat q;
nuclear@73 473 EulerToQuat(it->second, q, order);
nuclear@73 474 goat3d_set_node_rotation(node, q.x, q.y, q.z, q.w, it->first);
nuclear@73 475 ++it;
nuclear@73 476 }
nuclear@73 477 }
nuclear@73 478
nuclear@73 479 static void get_scaling_keys(IGameControl *ctrl, goat3d_node *node)
nuclear@73 480 {
nuclear@73 481 const char *nodename = goat3d_get_node_name(node);
nuclear@73 482 IGameKeyTab keys;
nuclear@73 483
nuclear@73 484 // XXX the way I'm using the ScaleValue is wrong, but fuck it...
nuclear@73 485
nuclear@73 486 if(ctrl->GetLinearKeys(keys, IGAME_SCALE)) {
nuclear@73 487 maxlog("node %s: getting %d linear scaling keys\n", nodename, keys.Count());
nuclear@73 488 for(int i=0; i<keys.Count(); i++) {
nuclear@73 489 ScaleValue s = keys[i].linearKey.sval;
nuclear@73 490 goat3d_set_node_scaling(node, s.s.x, s.s.y, s.s.z, KEY_TIME(keys[i]));
nuclear@73 491 }
nuclear@73 492 } else if(ctrl->GetBezierKeys(keys, IGAME_SCALE)) {
nuclear@73 493 maxlog("node %s: getting %d bezier scaling keys\n", nodename, keys.Count());
nuclear@73 494 for(int i=0; i<keys.Count(); i++) {
nuclear@73 495 ScaleValue s = keys[i].bezierKey.sval;
nuclear@73 496 goat3d_set_node_scaling(node, s.s.x, s.s.y, s.s.z, KEY_TIME(keys[i]));
nuclear@73 497 }
nuclear@73 498 } else if(ctrl->GetTCBKeys(keys, IGAME_SCALE)) {
nuclear@73 499 maxlog("node %s: getting %d tcb scaling keys\n", nodename, keys.Count());
nuclear@73 500 for(int i=0; i<keys.Count(); i++) {
nuclear@73 501 ScaleValue s = keys[i].tcbKey.sval;
nuclear@73 502 goat3d_set_node_scaling(node, s.s.x, s.s.y, s.s.z, KEY_TIME(keys[i]));
nuclear@73 503 }
nuclear@73 504 }
nuclear@73 505 }
nuclear@73 506
nuclear@73 507 static bool get_anim_bounds(IGameNode *node, long *tstart, long *tend);
nuclear@73 508 static bool get_node_anim_bounds(IGameNode *node, long *tstart, long *tend);
nuclear@73 509
nuclear@73 510 static bool get_anim_bounds(IGameScene *igame, long *tstart, long *tend)
nuclear@73 511 {
nuclear@73 512 long tmin = LONG_MAX;
nuclear@73 513 long tmax = LONG_MIN;
nuclear@73 514
nuclear@73 515 int num_nodes = igame->GetTopLevelNodeCount();
nuclear@73 516 for(int i=0; i<num_nodes; i++) {
nuclear@73 517 long t0, t1;
nuclear@73 518 if(get_anim_bounds(igame->GetTopLevelNode(i), &t0, &t1)) {
nuclear@73 519 if(t0 < tmin) tmin = t0;
nuclear@73 520 if(t1 > tmax) tmax = t1;
nuclear@73 521 }
nuclear@73 522 }
nuclear@73 523
nuclear@73 524 if(tmin != LONG_MAX) {
nuclear@73 525 *tstart = tmin;
nuclear@73 526 *tend = tmax;
nuclear@73 527 return true;
nuclear@73 528 }
nuclear@73 529 return false;
nuclear@73 530 }
nuclear@73 531
nuclear@73 532 static bool get_anim_bounds(IGameNode *node, long *tstart, long *tend)
nuclear@73 533 {
nuclear@73 534 long tmin = LONG_MAX;
nuclear@73 535 long tmax = LONG_MIN;
nuclear@73 536
nuclear@73 537 get_node_anim_bounds(node, &tmin, &tmax);
nuclear@73 538
nuclear@73 539 int num_children = node->GetChildCount();
nuclear@73 540 for(int i=0; i<num_children; i++) {
nuclear@73 541 long t0, t1;
nuclear@73 542 if(get_anim_bounds(node->GetNodeChild(i), &t0, &t1)) {
nuclear@73 543 if(t0 < tmin) tmin = t0;
nuclear@73 544 if(t1 > tmax) tmax = t1;
nuclear@73 545 }
nuclear@73 546 }
nuclear@73 547
nuclear@73 548 if(tmin != LONG_MAX) {
nuclear@73 549 *tstart = tmin;
nuclear@73 550 *tend = tmax;
nuclear@73 551 return true;
nuclear@73 552 }
nuclear@73 553 return false;
nuclear@73 554 }
nuclear@73 555
nuclear@73 556 static bool get_node_anim_bounds(IGameNode *node, long *tstart, long *tend)
nuclear@73 557 {
nuclear@73 558 static const IGameControlType ctypes[] = {
nuclear@73 559 IGAME_POS, IGAME_POS_X, IGAME_POS_Y, IGAME_POS_Z,
nuclear@73 560 IGAME_ROT, IGAME_EULER_X, IGAME_EULER_Y, IGAME_EULER_Z,
nuclear@73 561 IGAME_SCALE
nuclear@73 562 };
nuclear@73 563
nuclear@73 564 // NOTE: apparently if I don't call GetIGameObject, then GetIGameControl always returns null...
nuclear@73 565 node->GetIGameObject();
nuclear@73 566 IGameControl *ctrl = node->GetIGameControl();
nuclear@73 567 if(!ctrl) {
nuclear@73 568 maxlog("%s: failed to get IGameControl for node: %s\n", __FUNCTION__, max_string(node->GetName()));
nuclear@73 569 return false;
nuclear@73 570 }
nuclear@73 571
nuclear@73 572 IGameKeyTab keys;
nuclear@73 573 long t0, t1;
nuclear@73 574 long tmin = LONG_MAX;
nuclear@73 575 long tmax = LONG_MIN;
nuclear@73 576
nuclear@73 577 for(int i=0; i<sizeof ctypes / sizeof *ctypes; i++) {
nuclear@73 578 if(ctrl->GetBezierKeys(keys, ctypes[i]) && keys.Count()) {
nuclear@73 579 t0 = KEY_TIME(keys[0]);
nuclear@73 580 t1 = KEY_TIME(keys[keys.Count() - 1]);
nuclear@73 581 if(t0 < tmin) tmin = t0;
nuclear@73 582 if(t1 > tmax) tmax = t1;
nuclear@73 583 }
nuclear@73 584 if(ctrl->GetLinearKeys(keys, ctypes[i]) && keys.Count()) {
nuclear@73 585 t0 = KEY_TIME(keys[0]);
nuclear@73 586 t1 = KEY_TIME(keys[keys.Count() - 1]);
nuclear@73 587 if(t0 < tmin) tmin = t0;
nuclear@73 588 if(t1 > tmax) tmax = t1;
nuclear@73 589 }
nuclear@73 590 if(ctrl->GetTCBKeys(keys, ctypes[i]) && keys.Count()) {
nuclear@73 591 t0 = KEY_TIME(keys[0]);
nuclear@73 592 t1 = KEY_TIME(keys[keys.Count() - 1]);
nuclear@73 593 if(t0 < tmin) tmin = t0;
nuclear@73 594 if(t1 > tmax) tmax = t1;
nuclear@73 595 }
nuclear@73 596 }
nuclear@73 597
nuclear@73 598 if(tmin != LONG_MAX) {
nuclear@73 599 *tstart = tmin;
nuclear@73 600 *tend = tmax;
nuclear@73 601 return true;
nuclear@73 602 }
nuclear@73 603 return false;
nuclear@73 604 }
nuclear@73 605
nuclear@73 606 void GoatExporter::process_mesh(goat3d *goat, goat3d_mesh *mesh, IGameObject *maxobj)
nuclear@73 607 {
nuclear@73 608 IGameMesh *maxmesh = (IGameMesh*)maxobj;
nuclear@73 609
nuclear@73 610 maxmesh->SetCreateOptimizedNormalList(); // not needed any more according to docs
nuclear@73 611 maxobj->InitializeData();
nuclear@73 612
nuclear@73 613 int num_verts = maxmesh->GetNumberOfVerts();
nuclear@73 614 int num_faces = maxmesh->GetNumberOfFaces();
nuclear@73 615 //assert(maxmesh->GetNumberOfTexVerts() == num_verts);
nuclear@73 616
nuclear@73 617 float *vertices = new float[num_verts * 3];
nuclear@73 618 float *normals = new float[num_verts * 3];
nuclear@73 619 //float *texcoords = new float[num_verts * 2];
nuclear@73 620 int *indices = new int[num_faces * 3];
nuclear@73 621
nuclear@73 622 for(int i=0; i<num_verts; i++) {
nuclear@73 623 Point3 v = maxmesh->GetVertex(i, true);
nuclear@73 624 vertices[i * 3] = v.x;
nuclear@73 625 vertices[i * 3 + 1] = v.y;
nuclear@73 626 vertices[i * 3 + 2] = v.z;
nuclear@73 627 }
nuclear@73 628
nuclear@73 629 for(int i=0; i<maxmesh->GetNumberOfNormals(); i++) {
nuclear@73 630 Point3 norm = maxmesh->GetNormal(i);
nuclear@73 631
nuclear@73 632 int vidx = maxmesh->GetNormalVertexIndex(i);
nuclear@73 633 normals[vidx * 3] = norm.x;
nuclear@73 634 normals[vidx * 3 + 1] = norm.y;
nuclear@73 635 normals[vidx * 3 + 2] = norm.z;
nuclear@73 636 }
nuclear@73 637
nuclear@73 638 /*for(int i=0; i<maxmesh->GetNumberOfTexVerts(); i++) {
nuclear@73 639 Point3 tex = maxmesh->GetTexVertex(i);
nuclear@73 640
nuclear@73 641 texcoords[i * 2] = tex.x;
nuclear@73 642 texcoords[i * 2 + 1] = tex.y;
nuclear@73 643 }*/
nuclear@73 644
nuclear@73 645 // get the faces
nuclear@73 646 for(int i=0; i<num_faces; i++) {
nuclear@73 647 FaceEx *face = maxmesh->GetFace(i);
nuclear@73 648 indices[i * 3] = face->vert[0];
nuclear@73 649 indices[i * 3 + 1] = face->vert[1];
nuclear@73 650 indices[i * 3 + 2] = face->vert[2];
nuclear@73 651 // TODO at some point I'll have to split based on normal/texcoord indices
nuclear@73 652 }
nuclear@73 653
nuclear@73 654 goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_VERTEX, vertices, num_verts);
nuclear@73 655 goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_NORMAL, normals, num_verts);
nuclear@73 656 //goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_TEXCOORD, texcoords, num_verts);
nuclear@73 657 goat3d_set_mesh_faces(mesh, indices, num_faces);
nuclear@73 658
nuclear@73 659 delete [] vertices;
nuclear@73 660 delete [] normals;
nuclear@73 661 //delete [] texcoords;
nuclear@73 662 delete [] indices;
nuclear@73 663 }
nuclear@73 664
nuclear@73 665 void GoatExporter::process_light(goat3d *goat, goat3d_light *light, IGameObject *maxobj)
nuclear@73 666 {
nuclear@73 667 // TODO
nuclear@73 668 }
nuclear@73 669
nuclear@73 670 void GoatExporter::process_camera(goat3d *goat, goat3d_camera *cam, IGameObject *maxobj)
nuclear@73 671 {
nuclear@73 672 // TODO
nuclear@73 673 }
nuclear@73 674
nuclear@73 675 static INT_PTR CALLBACK scene_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
nuclear@73 676 {
nuclear@73 677 switch(msg) {
nuclear@73 678 case WM_INITDIALOG:
nuclear@73 679 CheckDlgButton(win, IDC_GOAT_NODES, 1);
nuclear@73 680 CheckDlgButton(win, IDC_GOAT_MESHES, 1);
nuclear@73 681 CheckDlgButton(win, IDC_GOAT_LIGHTS, 1);
nuclear@73 682 CheckDlgButton(win, IDC_GOAT_CAMERAS, 1);
nuclear@73 683 break;
nuclear@73 684
nuclear@73 685 case WM_COMMAND:
nuclear@73 686 switch(LOWORD(wparam)) {
nuclear@73 687 case IDOK:
nuclear@73 688 EndDialog(win, 1);
nuclear@73 689 break;
nuclear@73 690
nuclear@73 691 case IDCANCEL:
nuclear@73 692 EndDialog(win, 0);
nuclear@73 693 break;
nuclear@73 694
nuclear@73 695 default:
nuclear@73 696 return 0;
nuclear@73 697 }
nuclear@73 698 break;
nuclear@73 699
nuclear@73 700 default:
nuclear@73 701 return 0;
nuclear@73 702 }
nuclear@73 703
nuclear@73 704 return 1;
nuclear@73 705 }
nuclear@73 706
nuclear@73 707
nuclear@73 708
nuclear@73 709 // ---- GoatAnimExporter implementation ----
nuclear@73 710 static long tstart, tend;
nuclear@73 711
nuclear@73 712 int GoatAnimExporter::DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent, DWORD opt)
nuclear@73 713 {
nuclear@73 714 if(!(igame = GetIGameInterface())) {
nuclear@73 715 maxlog("failed to get the igame interface\n");
nuclear@73 716 return IMPEXP_FAIL;
nuclear@73 717 }
nuclear@73 718 IGameConversionManager *cm = GetConversionManager();
nuclear@73 719 cm->SetCoordSystem(IGameConversionManager::IGAME_OGL);
nuclear@73 720 igame->InitialiseIGame();
nuclear@73 721 igame->SetStaticFrame(0);
nuclear@73 722
nuclear@73 723 tstart = tend = 0;
nuclear@73 724 get_anim_bounds(igame, &tstart, &tend);
nuclear@73 725
nuclear@73 726 if(!DialogBox(hinst, MAKEINTRESOURCE(IDD_GOAT_ANM), 0, anim_gui_handler)) {
nuclear@73 727 igame->ReleaseIGame();
nuclear@73 728 return IMPEXP_CANCEL;
nuclear@73 729 }
nuclear@73 730
nuclear@73 731 char fname[512];
nuclear@73 732 wcstombs(fname, name, sizeof fname - 1);
nuclear@73 733 for(int i=0; fname[i]; i++) {
nuclear@73 734 fname[i] = tolower(fname[i]);
nuclear@73 735 }
nuclear@73 736
nuclear@73 737 maxlog("Exporting Goat3D Animation (text) file: %s\n", fname);
nuclear@73 738
nuclear@73 739 goat3d *goat = goat3d_create();
nuclear@73 740
nuclear@73 741 // process all nodes
nuclear@73 742 for(int i=0; i<igame->GetTopLevelNodeCount(); i++) {
nuclear@73 743 IGameNode *node = igame->GetTopLevelNode(i);
nuclear@73 744 process_node(goat, 0, node);
nuclear@73 745 }
nuclear@73 746
nuclear@73 747 if(goat3d_save_anim(goat, fname) == -1) {
nuclear@73 748 goat3d_free(goat);
nuclear@73 749 igame->ReleaseIGame();
nuclear@73 750 return IMPEXP_FAIL;
nuclear@73 751 }
nuclear@73 752
nuclear@73 753 goat3d_free(goat);
nuclear@73 754 igame->ReleaseIGame();
nuclear@73 755 return IMPEXP_SUCCESS;
nuclear@73 756 }
nuclear@73 757
nuclear@73 758 static INT_PTR CALLBACK anim_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
nuclear@73 759 {
nuclear@73 760 switch(msg) {
nuclear@73 761 case WM_INITDIALOG:
nuclear@73 762 {
nuclear@73 763 wchar_t buf[128];
nuclear@73 764 CheckDlgButton(win, IDC_GOAT_ANM_FULL, BST_CHECKED);
nuclear@73 765 CheckDlgButton(win, IDC_RAD_KEYS_ORIG, BST_CHECKED);
nuclear@73 766 wsprintf(buf, L"%ld", tstart);
nuclear@73 767 SetDlgItemText(win, IDC_EDIT_TSTART, buf);
nuclear@73 768 wsprintf(buf, L"%ld", tend);
nuclear@73 769 SetDlgItemText(win, IDC_EDIT_TEND, buf);
nuclear@73 770 }
nuclear@73 771 break;
nuclear@73 772
nuclear@73 773 case WM_COMMAND:
nuclear@73 774 switch(LOWORD(wparam)) {
nuclear@73 775 case IDOK:
nuclear@73 776 EndDialog(win, 1);
nuclear@73 777 break;
nuclear@73 778
nuclear@73 779 case IDCANCEL:
nuclear@73 780 EndDialog(win, 0);
nuclear@73 781 break;
nuclear@73 782
nuclear@73 783 default:
nuclear@73 784 return 0;
nuclear@73 785 }
nuclear@73 786 break;
nuclear@73 787
nuclear@73 788 default:
nuclear@73 789 return 0;
nuclear@73 790 }
nuclear@73 791
nuclear@73 792 return 1;
nuclear@73 793 }
nuclear@73 794
nuclear@73 795 // ------------------------------------------
nuclear@73 796
nuclear@73 797 class GoatClassDesc : public ClassDesc2 {
nuclear@73 798 public:
nuclear@73 799 int IsPublic() { return TRUE; }
nuclear@73 800 void *Create(BOOL loading = FALSE) { return new GoatExporter; }
nuclear@73 801 const TCHAR *ClassName() { return L"GoatExporter"; }
nuclear@73 802 SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
nuclear@73 803 Class_ID ClassID() { return Class_ID(0x77050f0d, 0x7d4c5ab5); }
nuclear@73 804 const TCHAR *Category() { return L"Mutant Stargoat"; }
nuclear@73 805
nuclear@73 806 const TCHAR *InternalName() { return L"GoatExporter"; }
nuclear@73 807 HINSTANCE HInstance() { return hinst; }
nuclear@73 808 };
nuclear@73 809
nuclear@73 810 class GoatAnimClassDesc : public ClassDesc2 {
nuclear@73 811 public:
nuclear@73 812 int IsPublic() { return TRUE; }
nuclear@73 813 void *Create(BOOL loading = FALSE) { return new GoatAnimExporter; }
nuclear@73 814 const TCHAR *ClassName() { return L"GoatAnimExporter"; }
nuclear@73 815 SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
nuclear@73 816 Class_ID ClassID() { return Class_ID(0x51b94924, 0x2e0332f3); }
nuclear@73 817 const TCHAR *Category() { return L"Mutant Stargoat"; }
nuclear@73 818
nuclear@73 819 const TCHAR *InternalName() { return L"GoatAnimExporter"; }
nuclear@73 820 HINSTANCE HInstance() { return hinst; }
nuclear@73 821 };
nuclear@73 822
nuclear@73 823 // TODO: make 2 class descriptors, one for goat3d, one for goat3danim
nuclear@73 824 static GoatClassDesc class_desc;
nuclear@73 825 static GoatAnimClassDesc anim_class_desc;
nuclear@73 826
nuclear@73 827 BOOL WINAPI DllMain(HINSTANCE inst_handle, ULONG reason, void *reserved)
nuclear@73 828 {
nuclear@73 829 if(reason == DLL_PROCESS_ATTACH) {
nuclear@73 830 hinst = inst_handle;
nuclear@73 831 DisableThreadLibraryCalls(hinst);
nuclear@73 832 }
nuclear@73 833 return TRUE;
nuclear@73 834 }
nuclear@73 835
nuclear@73 836 extern "C" {
nuclear@73 837
nuclear@73 838 __declspec(dllexport) const TCHAR *LibDescription()
nuclear@73 839 {
nuclear@73 840 return L"test exporter";
nuclear@73 841 }
nuclear@73 842
nuclear@73 843 __declspec(dllexport) int LibNumberClasses()
nuclear@73 844 {
nuclear@73 845 return 1;
nuclear@73 846 }
nuclear@73 847
nuclear@73 848 __declspec(dllexport) ClassDesc *LibClassDesc(int i)
nuclear@73 849 {
nuclear@73 850 switch(i) {
nuclear@73 851 case 0:
nuclear@73 852 return &class_desc;
nuclear@73 853 case 1:
nuclear@73 854 return &anim_class_desc;
nuclear@73 855 default:
nuclear@73 856 break;
nuclear@73 857 }
nuclear@73 858 return 0;
nuclear@73 859 }
nuclear@73 860
nuclear@73 861 __declspec(dllexport) ULONG LibVersion()
nuclear@73 862 {
nuclear@73 863 return Get3DSMAXVersion();
nuclear@73 864 }
nuclear@73 865
nuclear@73 866 __declspec(dllexport) int LibInitialize()
nuclear@73 867 {
nuclear@73 868 static char path[1024];
nuclear@73 869
nuclear@73 870 SHGetFolderPathA(0, CSIDL_PERSONAL, 0, 0, path);
nuclear@73 871 strcat(path, "/testexp.log");
nuclear@73 872
nuclear@73 873 maxlog_open(path);
nuclear@73 874 return TRUE;
nuclear@73 875 }
nuclear@73 876
nuclear@73 877 __declspec(dllexport) int LibShutdown()
nuclear@73 878 {
nuclear@73 879 maxlog_close();
nuclear@73 880 return TRUE;
nuclear@73 881 }
nuclear@73 882
nuclear@73 883 } // extern "C"
nuclear@73 884
nuclear@73 885
nuclear@73 886 static const char *max_string(const MCHAR *wstr)
nuclear@73 887 {
nuclear@73 888 if(!wstr) return 0;
nuclear@73 889 static char str[512];
nuclear@73 890 wcstombs(str, wstr, sizeof str - 1);
nuclear@73 891 return str;
nuclear@73 892 }