goat3d

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