goat3d

annotate exporters/maxgoat/src/maxgoat.cc @ 72:36e39632db75

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