goat3d

view exporters/maxgoat/src/maxgoat.cc @ 69:66cd8266f078

fixed animation export
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 21 Apr 2014 03:44:10 +0300
parents 8ecaf9cd3ce7
children 36e39632db75
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 }
111 maxlog("Exporting Goat3D Scene (text) file: %s\n", fname);
112 if(!(igame = GetIGameInterface())) {
113 maxlog("failed to get the igame interface\n");
114 return IMPEXP_FAIL;
115 }
116 IGameConversionManager *cm = GetConversionManager();
117 cm->SetCoordSystem(IGameConversionManager::IGAME_OGL);
118 igame->InitialiseIGame();
120 goat3d *goat = goat3d_create();
122 process_materials(goat);
124 // process all nodes
125 for(int i=0; i<igame->GetTopLevelNodeCount(); i++) {
126 IGameNode *node = igame->GetTopLevelNode(i);
127 process_node(goat, 0, node);
128 }
130 if(goat3d_save(goat, fname) == -1) {
131 goat3d_free(goat);
132 return IMPEXP_FAIL;
133 }
135 goat3d_free(goat);
136 return IMPEXP_SUCCESS;
137 }
139 void GoatExporter::process_materials(goat3d *goat)
140 {
141 IGameProperty *prop;
143 int num_mtl = igame->GetRootMaterialCount();
144 for(int i=0; i<num_mtl; i++) {
145 IGameMaterial *maxmtl = igame->GetRootMaterial(i);
146 if(maxmtl) {
147 goat3d_material *mtl = goat3d_create_mtl();
149 const char *name = max_string(maxmtl->GetMaterialName());
150 if(name) {
151 goat3d_set_mtl_name(mtl, name);
152 }
154 // diffuse
155 if((prop = maxmtl->GetDiffuseData())) {
156 Point3 diffuse(1, 1, 1);
157 prop->GetPropertyValue(diffuse);
158 goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_DIFFUSE, diffuse[0],
159 diffuse[1], diffuse[2]);
160 }
161 // specular
162 if((prop = maxmtl->GetSpecularData())) {
163 Point3 specular(0, 0, 0);
164 prop->GetPropertyValue(specular);
166 float sstr = 1.0;
167 if((prop = maxmtl->GetSpecularLevelData())) {
168 prop->GetPropertyValue(sstr);
169 }
170 goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_SPECULAR, specular[0] * sstr,
171 specular[1] * sstr, specular[2] * sstr);
172 }
173 // shininess
174 if((prop = maxmtl->GetGlossinessData())) {
175 float shin;
176 prop->GetPropertyValue(shin);
177 goat3d_set_mtl_attrib1f(mtl, GOAT3D_MAT_ATTR_SHININESS, shin * 100.0);
178 }
180 // textures
181 for(int j=0; j<maxmtl->GetNumberOfTextureMaps(); j++) {
182 IGameTextureMap *tex = maxmtl->GetIGameTextureMap(j);
184 const char *fname = max_string(tex->GetBitmapFileName());
185 if(!fname) {
186 continue;
187 }
189 int slot = tex->GetStdMapSlot();
190 switch(slot) {
191 case ID_DI: // diffuse
192 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_DIFFUSE, fname);
193 break;
195 case ID_SP:
196 case ID_SS:
197 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SPECULAR, fname);
198 break;
200 case ID_SH:
201 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SHININESS, fname);
202 break;
204 case ID_BU:
205 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_NORMAL, fname);
206 break;
208 case ID_RL:
209 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_REFLECTION, fname);
210 break;
212 case ID_RR:
213 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_TRANSMISSION, fname);
214 break;
216 default:
217 break;
218 }
219 }
221 goat3d_add_mtl(goat, mtl);
222 mtlmap[maxmtl] = mtl;
223 }
224 }
225 }
227 void GoatExporter::process_node(goat3d *goat, goat3d_node *parent, IGameNode *maxnode)
228 {
229 goat3d_node *node = goat3d_create_node();
230 goat3d_add_node(goat, node);
232 if(parent) {
233 goat3d_add_node_child(parent, node);
234 }
236 const char *name = max_string(maxnode->GetName());
237 if(name) {
238 goat3d_set_node_name(node, name);
239 }
241 IGameObject *maxobj = maxnode->GetIGameObject();
242 IGameObject::ObjectTypes type = maxobj->GetIGameType();
244 switch(type) {
245 case IGameObject::IGAME_MESH:
246 {
247 goat3d_mesh *mesh = goat3d_create_mesh();
248 if(name) goat3d_set_mesh_name(mesh, name);
249 goat3d_set_node_object(node, GOAT3D_NODE_MESH, mesh);
251 // get the node material and assign it to the mesh
252 IGameMaterial *maxmtl = maxnode->GetNodeMaterial();
253 goat3d_material *mtl = mtlmap[maxmtl];
254 if(mtl) {
255 goat3d_set_mesh_mtl(mesh, mtl);
256 }
258 process_mesh(goat, mesh, maxobj);
259 goat3d_add_mesh(goat, mesh);
260 }
261 break;
263 case IGameObject::IGAME_LIGHT:
264 {
265 goat3d_light *light = goat3d_create_light();
266 //if(name) goat3d_set_light_name(light, name);
267 goat3d_set_node_object(node, GOAT3D_NODE_LIGHT, light);
269 process_light(goat, light, maxobj);
270 goat3d_add_light(goat, light);
271 }
272 break;
274 case IGameObject::IGAME_CAMERA:
275 {
276 goat3d_camera *cam = goat3d_create_camera();
277 //if(name) goat3d_set_camera_name(camera, name);
278 goat3d_set_node_object(node, GOAT3D_NODE_CAMERA, cam);
280 process_camera(goat, cam, maxobj);
281 goat3d_add_camera(goat, cam);
282 }
283 break;
285 default:
286 // otherwise don't assign an object, essentially treating it as a null node
287 break;
288 }
290 // grab the animation data
291 IGameControl *ctrl = maxnode->GetIGameControl();
293 if(!dynamic_cast<GoatAnimExporter*>(this)) {
294 // no animation, just get the static PRS
295 GMatrix maxmatrix = maxnode->GetObjectTM();
296 Point3 trans = maxmatrix.Translation();
297 Quat rot = maxmatrix.Rotation();
298 Point3 scale = maxmatrix.Scaling();
300 goat3d_set_node_position(node, trans.x, trans.y, trans.z, 0);
301 goat3d_set_node_rotation(node, rot.x, rot.y, rot.z, rot.w, 0);
302 goat3d_set_node_scaling(node, scale.x, scale.y, scale.z, 0);
304 } else {
305 // exporting animations (if available)
306 // TODO sample keys if requested
307 if(ctrl->IsAnimated(IGAME_POS) || ctrl->IsAnimated(IGAME_POS_X) ||
308 ctrl->IsAnimated(IGAME_POS_Y) || ctrl->IsAnimated(IGAME_POS_Z)) {
309 get_position_keys(ctrl, node);
310 }
311 if(ctrl->IsAnimated(IGAME_ROT) || ctrl->IsAnimated(IGAME_EULER_X) ||
312 ctrl->IsAnimated(IGAME_EULER_Y) || ctrl->IsAnimated(IGAME_EULER_Z)) {
313 get_rotation_keys(ctrl, node);
314 }
315 if(ctrl->IsAnimated(IGAME_SCALE)) {
316 get_scaling_keys(ctrl, node);
317 }
318 }
320 for(int i=0; i<maxnode->GetChildCount(); i++) {
321 process_node(goat, node, maxnode->GetNodeChild(i));
322 }
323 }
325 #define KEY_TIME(key) ((long)(TicksToSec(key.t) * 1000.0))
327 static void get_position_keys(IGameControl *ctrl, goat3d_node *node)
328 {
329 const char *nodename = goat3d_get_node_name(node);
330 IGameKeyTab keys;
332 if(ctrl->GetLinearKeys(keys, IGAME_POS)) {
333 maxlog("node %s: getting %d linear position keys\n", nodename, keys.Count());
334 for(int i=0; i<keys.Count(); i++) {
335 Point3 p = keys[i].linearKey.pval;
336 goat3d_set_node_position(node, p.x, p.y, p.z, KEY_TIME(keys[i]));
337 }
338 } else if(ctrl->GetBezierKeys(keys, IGAME_POS)) {
339 maxlog("node %s: getting %d bezier position keys\n", nodename, keys.Count());
340 for(int i=0; i<keys.Count(); i++) {
341 Point3 p = keys[i].bezierKey.pval;
342 goat3d_set_node_position(node, p.x, p.y, p.z, KEY_TIME(keys[i]));
343 }
344 } else if(ctrl->GetTCBKeys(keys, IGAME_POS)) {
345 maxlog("node %s: getting %d tcb position keys\n", nodename, keys.Count());
346 for(int i=0; i<keys.Count(); i++) {
347 Point3 p = keys[i].tcbKey.pval;
348 goat3d_set_node_position(node, p.x, p.y, p.z, KEY_TIME(keys[i]));
349 }
350 } else {
351 get_xyz_position_keys(ctrl, node);
352 }
353 }
355 static void get_xyz_position_keys(IGameControl *ctrl, goat3d_node *node)
356 {
357 const char *nodename = goat3d_get_node_name(node);
358 IGameKeyTab keys;
359 IGameControlType postype[] = {IGAME_POS_X, IGAME_POS_Y, IGAME_POS_Z};
360 std::map<long, Point3> pos;
362 for(int i=0; i<3; i++) {
363 if(ctrl->GetLinearKeys(keys, postype[i])) {
364 maxlog("node %s: getting %d linear position %c keys\n", nodename, keys.Count(), "xyz"[i]);
365 for(int j=0; j<keys.Count(); j++) {
366 long tm = KEY_TIME(keys[j]);
367 Point3 v = pos[tm];
368 v[i] = keys[j].linearKey.fval;
369 pos[tm] = v;
370 }
371 } else if(ctrl->GetBezierKeys(keys, postype[i])) {
372 maxlog("node %s: getting %d bezier 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].bezierKey.fval;
377 pos[tm] = v;
378 }
379 } else if(ctrl->GetTCBKeys(keys, postype[i])) {
380 maxlog("node %s: getting %d tcb 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].tcbKey.fval;
385 pos[tm] = v;
386 }
387 }
388 }
390 std::map<long, Point3>::iterator it = pos.begin();
391 while(it != pos.end()) {
392 Point3 p = it->second;
393 goat3d_set_node_position(node, p.x, p.y, p.z, it->first);
394 ++it;
395 }
396 }
398 static void get_rotation_keys(IGameControl *ctrl, goat3d_node *node)
399 {
400 const char *nodename = goat3d_get_node_name(node);
401 IGameKeyTab rkeys;
403 if(ctrl->GetLinearKeys(rkeys, IGAME_ROT)) {
404 maxlog("node %s: getting %d linear rotation keys\n", nodename, rkeys.Count());
405 for(int i=0; i<rkeys.Count(); i++) {
406 Quat q = rkeys[i].linearKey.qval;
407 goat3d_set_node_rotation(node, q.x, q.y, q.z, q.w, KEY_TIME(rkeys[i]));
408 }
409 } else if(ctrl->GetBezierKeys(rkeys, IGAME_ROT)) {
410 maxlog("node %s: getting %d bezier rotation keys\n", nodename, rkeys.Count());
411 for(int i=0; i<rkeys.Count(); i++) {
412 Quat q = rkeys[i].bezierKey.qval;
413 goat3d_set_node_rotation(node, q.x, q.y, q.z, q.w, KEY_TIME(rkeys[i]));
414 }
415 } else if(ctrl->GetTCBKeys(rkeys, IGAME_ROT)) {
416 maxlog("node %s: getting %d TCB rotation keys\n", nodename, rkeys.Count());
417 for(int i=0; i<rkeys.Count(); i++) {
418 Quat q(rkeys[i].tcbKey.aval);
419 goat3d_set_node_rotation(node, q.x, q.y, q.z, q.w, KEY_TIME(rkeys[i]));
420 }
421 } else {
422 get_euler_keys(ctrl, node);
423 }
424 }
426 static void get_euler_keys(IGameControl *ctrl, goat3d_node *node)
427 {
428 const char *nodename = goat3d_get_node_name(node);
429 IGameKeyTab keys;
430 IGameControlType eulertype[] = {IGAME_EULER_X, IGAME_EULER_Y, IGAME_EULER_Z};
431 std::map<long, Point3> euler;
433 for(int i=0; i<3; i++) {
434 if(ctrl->GetLinearKeys(keys, eulertype[i])) {
435 maxlog("node %s: getting %d linear euler %c keys\n", nodename, keys.Count(), "xyz"[i]);
436 for(int j=0; j<keys.Count(); j++) {
437 long tm = KEY_TIME(keys[j]);
438 Point3 v = euler[tm];
439 v[i] = keys[j].linearKey.fval;
440 euler[tm] = v;
441 }
442 } else if(ctrl->GetBezierKeys(keys, eulertype[i])) {
443 maxlog("node %s: getting %d bezier 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].bezierKey.fval;
448 euler[tm] = v;
449 }
450 } else if(ctrl->GetTCBKeys(keys, eulertype[i])) {
451 maxlog("node %s: getting %d tcb 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].tcbKey.fval;
456 euler[tm] = v;
457 }
458 }
459 }
461 int order = ctrl->GetEulerOrder();
462 std::map<long, Point3>::iterator it = euler.begin();
463 while(it != euler.end()) {
464 Quat q;
465 EulerToQuat(it->second, q, order);
466 goat3d_set_node_rotation(node, q.x, q.y, q.z, q.w, it->first);
467 ++it;
468 }
469 }
471 static void get_scaling_keys(IGameControl *ctrl, goat3d_node *node)
472 {
473 const char *nodename = goat3d_get_node_name(node);
474 IGameKeyTab keys;
476 // XXX the way I'm using the ScaleValue is wrong, but fuck it...
478 if(ctrl->GetLinearKeys(keys, IGAME_SCALE)) {
479 maxlog("node %s: getting %d linear scaling keys\n", nodename, keys.Count());
480 for(int i=0; i<keys.Count(); i++) {
481 ScaleValue s = keys[i].linearKey.sval;
482 goat3d_set_node_scaling(node, s.s.x, s.s.y, s.s.z, KEY_TIME(keys[i]));
483 }
484 } else if(ctrl->GetBezierKeys(keys, IGAME_SCALE)) {
485 maxlog("node %s: getting %d bezier scaling keys\n", nodename, keys.Count());
486 for(int i=0; i<keys.Count(); i++) {
487 ScaleValue s = keys[i].bezierKey.sval;
488 goat3d_set_node_scaling(node, s.s.x, s.s.y, s.s.z, KEY_TIME(keys[i]));
489 }
490 } else if(ctrl->GetTCBKeys(keys, IGAME_SCALE)) {
491 maxlog("node %s: getting %d tcb scaling keys\n", nodename, keys.Count());
492 for(int i=0; i<keys.Count(); i++) {
493 ScaleValue s = keys[i].tcbKey.sval;
494 goat3d_set_node_scaling(node, s.s.x, s.s.y, s.s.z, KEY_TIME(keys[i]));
495 }
496 }
497 }
499 #if 0
500 static bool get_anim_bounds(IGameNode *node, long *tstart, long *tend);
502 static bool get_anim_bounds(IGameScene *igame, long *tstart, long *tend)
503 {
504 int tmin = LONG_MAX;
505 int tmax = LONG_MIN;
507 int num_nodes = igame->GetTopLevelNodeCount();
508 for(int i=0; i<num_nodes; i++) {
509 long t0, t1;
510 if(get_anim_bounds(igame->GetTopLevelNode(i), &t0, &t1)) {
511 if(t0 < tmin) tmin = t0;
512 if(t1 > tmax) tmax = t1;
513 }
514 }
516 if(tmin != LONG_MAX) {
517 *tstart = tmin;
518 *tend = tmax;
519 return true;
520 }
521 return false;
522 }
524 static bool get_anim_bounds(IGameNode *node, long *tstart, long *tend)
525 {
526 int tmin = LONG_MAX;
527 int tmax = LONG_MIN;
529 int num_children = node->GetChildCount();
530 for(int i=0; i<num_children; i++) {
531 long t0, t1;
532 if(get_anim_bounds(node->GetNodeChild(i), &t0, &t1)) {
533 if(t0 < tmin) tmin = t0;
534 if(t1 > tmax) tmax = t1;
535 }
536 }
538 if(tmin != LONG_MAX) {
539 *tstart = tmin;
540 *tend = tmax;
541 return true;
542 }
543 return false;
544 }
545 #endif
547 void GoatExporter::process_mesh(goat3d *goat, goat3d_mesh *mesh, IGameObject *maxobj)
548 {
549 IGameMesh *maxmesh = (IGameMesh*)maxobj;
551 maxmesh->SetCreateOptimizedNormalList(); // not needed any more according to docs
552 maxobj->InitializeData();
554 int num_verts = maxmesh->GetNumberOfVerts();
555 int num_faces = maxmesh->GetNumberOfFaces();
556 //assert(maxmesh->GetNumberOfTexVerts() == num_verts);
558 float *vertices = new float[num_verts * 3];
559 float *normals = new float[num_verts * 3];
560 //float *texcoords = new float[num_verts * 2];
561 int *indices = new int[num_faces * 3];
563 for(int i=0; i<num_verts; i++) {
564 Point3 v = maxmesh->GetVertex(i, true);
565 vertices[i * 3] = v.x;
566 vertices[i * 3 + 1] = v.y;
567 vertices[i * 3 + 2] = v.z;
568 }
570 for(int i=0; i<maxmesh->GetNumberOfNormals(); i++) {
571 Point3 norm = maxmesh->GetNormal(i);
573 int vidx = maxmesh->GetNormalVertexIndex(i);
574 normals[vidx * 3] = norm.x;
575 normals[vidx * 3 + 1] = norm.y;
576 normals[vidx * 3 + 2] = norm.z;
577 }
579 /*for(int i=0; i<maxmesh->GetNumberOfTexVerts(); i++) {
580 Point3 tex = maxmesh->GetTexVertex(i);
582 texcoords[i * 2] = tex.x;
583 texcoords[i * 2 + 1] = tex.y;
584 }*/
586 // get the faces
587 for(int i=0; i<num_faces; i++) {
588 FaceEx *face = maxmesh->GetFace(i);
589 indices[i * 3] = face->vert[0];
590 indices[i * 3 + 1] = face->vert[1];
591 indices[i * 3 + 2] = face->vert[2];
592 // TODO at some point I'll have to split based on normal/texcoord indices
593 }
595 goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_VERTEX, vertices, num_verts);
596 goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_NORMAL, normals, num_verts);
597 //goat3d_set_mesh_attribs(mesh, GOAT3D_MESH_ATTR_TEXCOORD, texcoords, num_verts);
598 goat3d_set_mesh_faces(mesh, indices, num_faces);
600 delete [] vertices;
601 delete [] normals;
602 //delete [] texcoords;
603 delete [] indices;
604 }
606 void GoatExporter::process_light(goat3d *goat, goat3d_light *light, IGameObject *maxobj)
607 {
608 // TODO
609 }
611 void GoatExporter::process_camera(goat3d *goat, goat3d_camera *cam, IGameObject *maxobj)
612 {
613 // TODO
614 }
616 static INT_PTR CALLBACK scene_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
617 {
618 switch(msg) {
619 case WM_INITDIALOG:
620 CheckDlgButton(win, IDC_GOAT_NODES, 1);
621 CheckDlgButton(win, IDC_GOAT_MESHES, 1);
622 CheckDlgButton(win, IDC_GOAT_LIGHTS, 1);
623 CheckDlgButton(win, IDC_GOAT_CAMERAS, 1);
624 break;
626 case WM_COMMAND:
627 switch(LOWORD(wparam)) {
628 case IDOK:
629 EndDialog(win, 1);
630 break;
632 case IDCANCEL:
633 EndDialog(win, 0);
634 break;
636 default:
637 return 0;
638 }
639 break;
641 default:
642 return 0;
643 }
645 return 1;
646 }
650 // ---- GoatAnimExporter implementation ----
652 int GoatAnimExporter::DoExport(const MCHAR *name, ExpInterface *eiface, Interface *iface, BOOL silent, DWORD opt)
653 {
654 if(!(igame = GetIGameInterface())) {
655 maxlog("failed to get the igame interface\n");
656 return IMPEXP_FAIL;
657 }
658 IGameConversionManager *cm = GetConversionManager();
659 cm->SetCoordSystem(IGameConversionManager::IGAME_OGL);
660 igame->InitialiseIGame();
662 //long tstart = 0, tend = 0;
663 //get_anim_bounds(igame, &tstart, &tend);
665 if(!DialogBox(hinst, MAKEINTRESOURCE(IDD_GOAT_ANM), 0, anim_gui_handler)) {
666 igame->ReleaseIGame();
667 return IMPEXP_CANCEL;
668 }
670 char fname[512];
671 wcstombs(fname, name, sizeof fname - 1);
672 for(int i=0; fname[i]; i++) {
673 fname[i] = tolower(fname[i]);
674 }
676 maxlog("Exporting Goat3D Animation (text) file: %s\n", fname);
677 igame->SetStaticFrame(0);
679 goat3d *goat = goat3d_create();
681 // process all nodes
682 for(int i=0; i<igame->GetTopLevelNodeCount(); i++) {
683 IGameNode *node = igame->GetTopLevelNode(i);
684 process_node(goat, 0, node);
685 }
687 if(goat3d_save_anim(goat, fname) == -1) {
688 goat3d_free(goat);
689 igame->ReleaseIGame();
690 return IMPEXP_FAIL;
691 }
693 goat3d_free(goat);
694 igame->ReleaseIGame();
695 return IMPEXP_SUCCESS;
696 }
698 static INT_PTR CALLBACK anim_gui_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam)
699 {
700 switch(msg) {
701 case WM_INITDIALOG:
702 CheckDlgButton(win, IDC_GOAT_ANM_FULL, 1);
703 CheckDlgButton(win, IDC_RAD_KEYS_ORIG, 1);
704 break;
706 case WM_COMMAND:
707 switch(LOWORD(wparam)) {
708 case IDOK:
709 EndDialog(win, 1);
710 break;
712 case IDCANCEL:
713 EndDialog(win, 0);
714 break;
716 default:
717 return 0;
718 }
719 break;
721 default:
722 return 0;
723 }
725 return 1;
726 }
728 // ------------------------------------------
730 class GoatClassDesc : public ClassDesc2 {
731 public:
732 int IsPublic() { return TRUE; }
733 void *Create(BOOL loading = FALSE) { return new GoatExporter; }
734 const TCHAR *ClassName() { return L"GoatExporter"; }
735 SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
736 Class_ID ClassID() { return Class_ID(0x77050f0d, 0x7d4c5ab5); }
737 const TCHAR *Category() { return L"Mutant Stargoat"; }
739 const TCHAR *InternalName() { return L"GoatExporter"; }
740 HINSTANCE HInstance() { return hinst; }
741 };
743 class GoatAnimClassDesc : public ClassDesc2 {
744 public:
745 int IsPublic() { return TRUE; }
746 void *Create(BOOL loading = FALSE) { return new GoatAnimExporter; }
747 const TCHAR *ClassName() { return L"GoatAnimExporter"; }
748 SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
749 Class_ID ClassID() { return Class_ID(0x51b94924, 0x2e0332f3); }
750 const TCHAR *Category() { return L"Mutant Stargoat"; }
752 const TCHAR *InternalName() { return L"GoatAnimExporter"; }
753 HINSTANCE HInstance() { return hinst; }
754 };
756 // TODO: make 2 class descriptors, one for goat3d, one for goat3danim
757 static GoatClassDesc class_desc;
758 static GoatAnimClassDesc anim_class_desc;
760 BOOL WINAPI DllMain(HINSTANCE inst_handle, ULONG reason, void *reserved)
761 {
762 if(reason == DLL_PROCESS_ATTACH) {
763 hinst = inst_handle;
764 DisableThreadLibraryCalls(hinst);
765 }
766 return TRUE;
767 }
769 extern "C" {
771 __declspec(dllexport) const TCHAR *LibDescription()
772 {
773 return L"test exporter";
774 }
776 __declspec(dllexport) int LibNumberClasses()
777 {
778 return 1;
779 }
781 __declspec(dllexport) ClassDesc *LibClassDesc(int i)
782 {
783 switch(i) {
784 case 0:
785 return &class_desc;
786 case 1:
787 return &anim_class_desc;
788 default:
789 break;
790 }
791 return 0;
792 }
794 __declspec(dllexport) ULONG LibVersion()
795 {
796 return Get3DSMAXVersion();
797 }
799 __declspec(dllexport) int LibInitialize()
800 {
801 static char path[1024];
803 SHGetFolderPathA(0, CSIDL_PERSONAL, 0, 0, path);
804 strcat(path, "/testexp.log");
806 maxlog_open(path);
807 return TRUE;
808 }
810 __declspec(dllexport) int LibShutdown()
811 {
812 maxlog_close();
813 return TRUE;
814 }
816 } // extern "C"
819 static const char *max_string(const MCHAR *wstr)
820 {
821 if(!wstr) return 0;
822 static char str[512];
823 wcstombs(str, wstr, sizeof str - 1);
824 return str;