openal and fontft memleak fixes from keltar
[fegdk.git] / tools / fexport / fexport.cpp
blob88e8563b62db321dff967cf215e593ad0c7e2ff5
1 #include "pch.h"
2 #include "fexport.h"
3 #include "resource.h"
4 #include <algorithm>
6 // $Log$
7 // Revision 1.1 2005/01/18 19:11:00 waker
8 // Initial revision
9 //
10 // Revision 1.1.1.1 2005/01/03 15:21:07 waker
11 // import
13 // Revision 1.15 2004/11/10 01:22:10 waker
14 // added lightmap support
16 // Revision 1.14 2003/12/19 20:09:18 waker
17 // added progress bar (really useless on hi-poly models not split up into separate objects)
19 // Revision 1.13 2003/06/09 13:10:43 waker
20 // exports to right fmt (additional parenthesis w/ mtl name)
22 // Revision 1.12 2002/12/02 11:05:26 waker
23 // added parent check in animation controller export
25 // Revision 1.11 2002/12/02 10:57:47 waker
26 // INode::GetObjTMAfterWSM is used instead of INode::GetNodeTM
28 // Revision 1.10 2002/11/23 16:07:54 waker
29 // going home, saving changes
32 #define FEXPORT_CLASS_ID Class_ID(0x770130b9, 0x5a8d06cc)
34 class FExport : public SceneExport {
35 public:
36 static HWND hParams;
37 static BOOL CALLBACK ParmsDlgProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
39 //Constructor/Destructor
40 FExport( void ) {}
41 ~FExport( void ) {}
42 BOOL SupportsOptions( int ext, DWORD options ) { return ( options & SCENE_EXPORT_SELECTED ) ? TRUE : FALSE; }
43 int ExtCount( void ) { return 1; }
44 const TCHAR *Ext( int i ) { return _T( "mdl" ); }
45 const TCHAR *LongDesc( void ) { return _T( "fe model and animation exporter" ); }
46 const TCHAR *ShortDesc( void ) { return _T( "fe model" ); }
47 const TCHAR *AuthorName( void ) { return _T( "[_WaKeR_]" ); }
48 const TCHAR *CopyrightMessage( void ) { return _T( "" ); }
49 const TCHAR *OtherMessage1( void ) { return _T( "" ); }
50 const TCHAR *OtherMessage2( void ) { return _T( "" ); }
51 unsigned int Version( void ) { return 1; }
52 void ShowAbout( HWND hWnd ) {}
54 // exported stuff
55 struct ExpMtl
57 TSTR name;
58 TSTR textureName;
59 int index;
61 typedef std::map< Mtl *, int > MtlMap;
62 MtlMap mtlIndex;
63 std::vector< ExpMtl > mtlList;
65 typedef std::vector< INode * > boneList;
67 int numberOfNodes;
69 enum nodeType { none, tri, light, dummy };
71 struct ExpNode
73 ExpNode()
75 node = NULL;
76 export = true;
77 type = none;
79 ~ExpNode()
81 node = NULL;
82 for ( size_t i = 0; i < children.size(); i++ )
83 delete children[i];
86 struct entity_t
88 std::map< std::string, std::string > props;
91 nodeType type;
92 INode* node;
93 bool export;
94 entity_t lightentity;
96 struct Face
98 int v[3];
101 std::vector< Point3 > verts; // indexed by faces
102 std::vector< Point3 > colors; // indexed by colorFaces
103 std::vector< boneList > boneAssignments; // indexed by faces if present
104 std::vector< Point3 > normals; // indexed by normalFaces
105 std::vector< Point2 > uvVerts; // indexed by uvFaces
106 std::vector< Point2 > uvLightmap; // indexed by uvLightmapFaces if present
108 std::vector< Face > faces;
109 std::vector< Face > colorFaces;
110 std::vector< Face > normalFaces;
111 std::vector< Face > uvFaces;
112 std::vector< Face > uvLightmapFaces;
114 std::vector< int > mtlIndex;
116 std::vector< ExpNode * > children;
118 float animSpeed; // frames per second (or matrices per second, or whatever..)
119 std::vector< Matrix3 > animFrames;
122 ExpNode* rootExpNode;
123 Interface* pInterface;
125 int DoExport( const TCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPrompts, DWORD options )
127 TSTR fname = name;
128 pInterface = i;
129 // tolower
130 fname.toLower();
131 // get params
132 // BOOL res = DialogBox( hInstance, MAKEINTRESOURCE( IDD_PARMS ), i->GetMAXHWnd(), ParmsDlgProc );
133 // if ( res )
134 ExportScene( i, fname, options );
135 return 1;
138 // dummy progress func
139 static DWORD WINAPI fn(LPVOID arg) { return 0; }
140 void ExportScene( Interface *ip, const TSTR &fname, DWORD options )
142 TCHAR dp[_MAX_PATH];
143 TCHAR df[_MAX_FNAME];
144 TCHAR de[_MAX_EXT];
145 BMMSplitFilename( fname.data(), dp, df, de );
147 numberOfNodes = 0;
148 ExportMaterials( ip->GetRootNode(), df, options, numberOfNodes );
149 rootExpNode = NULL;
151 // Startup the progress bar.
152 ip->ProgressStart( _T( "Exporting FE model file..." ), TRUE, fn, NULL );
154 if ( ExportGeometry( rootExpNode, ip->GetRootNode(), df, options, 0 ) )
156 Save( fname );
158 if ( rootExpNode )
160 delete rootExpNode;
161 rootExpNode = NULL;
164 ip->ProgressEnd();
167 void Save( const TSTR &fname )
169 FILE *fp;
170 fp = _tfopen( fname.data(), _T( "w+t" ) );
171 assert( fp );
172 if ( !fp )
173 return;
175 SaveMaterials( fp );
176 SaveNode( fp, rootExpNode );
178 fclose( fp );
181 void SaveMaterials( FILE *fp )
183 if ( mtlList.size() )
185 for ( int i = 0; i < (int)mtlList.size(); i++ )
187 FILE *fp = _tfopen( ( mtlList[i].name + _T( ".fmtl" ) ).data(), _T( "w+t" ) );
188 assert( fp );
189 if ( !fp )
190 return;
192 _ftprintf( fp, _T( "\"%s\" {\n" ), mtlList[i].name );
193 _ftprintf( fp, _T( "\teffectfile effects/simple.fx\n" ) );
194 if ( mtlList[i].textureName.Length() )
195 _ftprintf( fp, _T( "\ttexinput basetexture \"%s\"\n" ), mtlList[i].textureName.data() );
196 _ftprintf( fp, _T( "}\n" ) );
198 fclose( fp );
201 // write materials to textfile
202 _ftprintf( fp, _T( "materials {\n" ) );
203 for ( int i = 0; i < (int)mtlList.size(); i++ )
205 _ftprintf( fp, _T( "\t\"materials/%s\"\n" ), mtlList[i].name );
207 _ftprintf( fp, _T( "}\n\n" ) );
211 void SaveNode( FILE *fp, ExpNode *node, const TSTR &indent = _T( "" ) )
213 if ( !node->export )
215 assert( node->children.empty() );
216 return; // shouldn't have children
219 TSTR ind = indent + _T( "\t" );
220 int i;
222 if ( node->type == tri )
223 _ftprintf( fp, _T( "%striobject \"%s\" {\n" ), indent.data(), node->node->GetName() );
224 else if ( node->type == light )
225 _ftprintf( fp, _T( "%sentity \"%s\" {\n" ), indent.data(), node->node->GetName() );
226 else if ( node->type == dummy )
227 _ftprintf( fp, _T( "%sdummy \"%s\" {\n" ), indent.data(), node->node->GetName() );
228 else
230 TCHAR str[256];
231 _stprintf( str, _T( "node '%s' has no correct type assignment. will be written as dummy." ), node->node->GetName() );
232 warning( str );
233 _ftprintf( fp, _T( "%sdummy \"%s\" {\n" ), indent.data(), node->node->GetName() );
236 // matrix
237 float m[4][4];
238 Matrix3 nodeTM = node->node->GetObjTMAfterWSM( 0 );
239 if ( node->node->GetParentNode() )
240 nodeTM = nodeTM * Inverse( node->node->GetParentNode()->GetObjTMAfterWSM( 0 ) );
241 Point3 x = nodeTM.GetRow( 0 );
242 Point3 y = nodeTM.GetRow( 1 );
243 Point3 z = nodeTM.GetRow( 2 );
244 m[0][0] = x.x; m[0][1] = x.z; m[0][2] = x.y; m[0][3] = 0;
245 m[1][0] = z.x; m[1][1] = z.z; m[1][2] = z.y; m[1][3] = 0;
246 m[2][0] = y.x; m[2][1] = y.z; m[2][2] = y.y; m[2][3] = 0;
247 m[3][0] = nodeTM.GetTrans().x; m[3][1] = nodeTM.GetTrans().z; m[3][2] = nodeTM.GetTrans().y; m[3][3] = 1;
249 _ftprintf( fp, _T( "%snodeTransform {\n" ), ind.data() );
250 _ftprintf( fp, _T( "%s\t%f %f %f\n" ), ind.data(), m[0][0], m[0][1], m[0][2] );
251 _ftprintf( fp, _T( "%s\t%f %f %f\n" ), ind.data(), m[1][0], m[1][1], m[1][2] );
252 _ftprintf( fp, _T( "%s\t%f %f %f\n" ), ind.data(), m[2][0], m[2][1], m[2][2] );
253 _ftprintf( fp, _T( "%s\t%f %f %f\n" ), ind.data(), m[3][0], m[3][1], m[3][2] );
254 _ftprintf( fp, _T( "%s}\n" ), ind.data() );
256 if ( node->type == tri && node->verts.size() )
258 // verts
259 _ftprintf( fp, _T( "%sverts {\n" ), ind.data() );
260 _ftprintf( fp, _T( "%s\tcount %d\n" ), ind.data(), (int)node->verts.size() );
261 for ( i = 0; i < (int)node->verts.size(); i++ )
263 _ftprintf( fp, _T( "%s\tv %f %f %f\n" ), ind.data(), node->verts[i].x, node->verts[i].y, node->verts[i].z );
265 _ftprintf( fp, _T( "%s}\n" ), ind.data() );
267 // faces
268 _ftprintf( fp, _T( "%sfaces {\n" ), ind.data() );
269 _ftprintf( fp, _T( "%s\tcount %d\n" ), ind.data(), (int)node->faces.size() );
270 for ( i = 0; i < (int)node->faces.size(); i++ )
272 _ftprintf( fp, _T( "%s\tf %d %d %d\n" ), ind.data(), node->faces[i].v[0], node->faces[i].v[1], node->faces[i].v[2] );
274 _ftprintf( fp, _T( "%s}\n" ), ind.data() );
276 // colors
277 _ftprintf( fp, _T( "%scolors {\n" ), ind.data() );
278 _ftprintf( fp, _T( "%s\tcount %d\n" ), ind.data(), (int)node->colors.size() );
279 for ( i = 0; i < (int)node->colors.size(); i++ )
281 _ftprintf( fp, _T( "%s\tv %f %f %f\n" ), ind.data(), node->colors[i].x, node->colors[i].y, node->colors[i].z );
283 _ftprintf( fp, _T( "%s}\n" ), ind.data() );
285 // colorfaces
286 _ftprintf( fp, _T( "%scolorFaces {\n" ), ind.data() );
287 _ftprintf( fp, _T( "%s\tcount %d\n" ), ind.data(), (int)node->colorFaces.size() );
288 for ( i = 0; i < (int)node->colorFaces.size(); i++ )
290 _ftprintf( fp, _T( "%s\tf %d %d %d\n" ), ind.data(), node->colorFaces[i].v[0], node->colorFaces[i].v[1], node->colorFaces[i].v[2] );
292 _ftprintf( fp, _T( "%s}\n" ), ind.data() );
294 // normals
295 _ftprintf( fp, _T( "%snormals {\n" ), ind.data() );
296 _ftprintf( fp, _T( "%s\tcount %d\n" ), ind.data(), (int)node->normals.size() );
297 for ( i = 0; i < (int)node->normals.size(); i++ )
299 _ftprintf( fp, _T( "%s\tv %f %f %f\n" ), ind.data(), node->normals[i].x, node->normals[i].y, node->normals[i].z );
301 _ftprintf( fp, _T( "%s}\n" ), ind.data() );
303 // normalFaces
304 _ftprintf( fp, _T( "%snormalFaces {\n" ), ind.data() );
305 _ftprintf( fp, _T( "%s\tcount %d\n" ), ind.data(), (int)node->normalFaces.size() );
306 for ( i = 0; i < (int)node->normalFaces.size(); i++ )
308 _ftprintf( fp, _T( "%s\tf %d %d %d\n" ), ind.data(), node->normalFaces[i].v[0], node->normalFaces[i].v[1], node->normalFaces[i].v[2] );
310 _ftprintf( fp, _T( "%s}\n" ), ind.data() );
312 if ( node->uvVerts.size() )
314 // uvVerts
315 _ftprintf( fp, _T( "%suvVerts {\n" ), ind.data() );
316 _ftprintf( fp, _T( "%s\tcount %d\n" ), ind.data(), (int)node->uvVerts.size() );
317 for ( i = 0; i < (int)node->uvVerts.size(); i++ )
319 _ftprintf( fp, _T( "%s\tv %f %f\n" ), ind.data(), node->uvVerts[i].x, node->uvVerts[i].y );
321 _ftprintf( fp, _T( "%s}\n" ), ind.data() );
323 // uvFaces
324 _ftprintf( fp, _T( "%suvFaces {\n" ), ind.data() );
325 _ftprintf( fp, _T( "%s\tcount %d\n" ), ind.data(), (int)node->uvFaces.size() );
326 for ( i = 0; i < (int)node->uvFaces.size(); i++ )
328 _ftprintf( fp, _T( "%s\tf %d %d %d\n" ), ind.data(), node->uvFaces[i].v[0], node->uvFaces[i].v[1], node->uvFaces[i].v[2] );
330 _ftprintf( fp, _T( "%s}\n" ), ind.data() );
333 if ( node->uvLightmap.size() )
335 // uvLightmap
336 _ftprintf( fp, _T( "%suvLightmap {\n" ), ind.data() );
337 _ftprintf( fp, _T( "%s\tcount %d\n" ), ind.data(), (int)node->uvLightmap.size() );
338 for ( i = 0; i < (int)node->uvLightmap.size(); i++ )
340 _ftprintf( fp, _T( "%s\tv %f %f\n" ), ind.data(), node->uvLightmap[i].x, node->uvLightmap[i].y );
342 _ftprintf( fp, _T( "%s}\n" ), ind.data() );
344 // uvLightmapFaces
345 _ftprintf( fp, _T( "%suvLightmapFaces {\n" ), ind.data() );
346 _ftprintf( fp, _T( "%s\tcount %d\n" ), ind.data(), (int)node->uvLightmapFaces.size() );
347 for ( i = 0; i < (int)node->uvLightmapFaces.size(); i++ )
349 _ftprintf( fp, _T( "%s\tf %d %d %d\n" ), ind.data(), node->uvLightmapFaces[i].v[0], node->uvLightmapFaces[i].v[1], node->uvLightmapFaces[i].v[2] );
351 _ftprintf( fp, _T( "%s}\n" ), ind.data() );
354 if ( node->mtlIndex.size() )
356 // mtl assignments
357 _ftprintf( fp, _T( "%smtlIndex {\n" ), ind.data() );
358 _ftprintf( fp, _T( "%s\tcount %d\n" ), ind.data(), (int)node->mtlIndex.size() );
359 for ( i = 0; i < (int)node->mtlIndex.size(); i++ )
361 _ftprintf( fp, _T( "%s\tf %d\n" ), ind.data(), node->mtlIndex[i] );
363 _ftprintf( fp, _T( "%s}\n" ), ind.data() );
366 if ( false == node->animFrames.empty() )
368 // write animation
369 _ftprintf( fp, _T( "%sanimation {\n" ), ind.data() );
370 _ftprintf( fp, _T( "%s\tframeRate %f\n" ), ind.data(), node->animSpeed );
371 _ftprintf( fp, _T( "%s\tnumFrames %d\n" ), ind.data(), (int)node->animFrames.size() );
372 for ( i = 0; i < (int)node->animFrames.size(); i++ )
374 // frame time
375 _ftprintf( fp, _T( "%s\ttime %f transform " ), ind.data(), i * TicksToSec( GetTicksPerFrame() ) );
376 float m[4][4];
377 Matrix3 &nodeTM = node->animFrames[i];
378 Point3 x = nodeTM.GetRow( 0 );
379 Point3 y = nodeTM.GetRow( 1 );
380 Point3 z = nodeTM.GetRow( 2 );
381 m[0][0] = x.x; m[0][1] = x.z; m[0][2] = x.y; m[0][3] = 0;
382 m[1][0] = z.x; m[1][1] = z.z; m[1][2] = z.y; m[1][3] = 0;
383 m[2][0] = y.x; m[2][1] = y.z; m[2][2] = y.y; m[2][3] = 0;
384 m[3][0] = nodeTM.GetTrans().x; m[3][1] = nodeTM.GetTrans().z; m[3][2] = nodeTM.GetTrans().y; m[3][3] = 1;
385 _ftprintf( fp, _T( "%f %f %f " ), m[0][0], m[0][1], m[0][2] );
386 _ftprintf( fp, _T( "%f %f %f " ), m[1][0], m[1][1], m[1][2] );
387 _ftprintf( fp, _T( "%f %f %f " ), m[2][0], m[2][1], m[2][2] );
388 _ftprintf( fp, _T( "%f %f %f\n" ), m[3][0], m[3][1], m[3][2] );
390 _ftprintf( fp, _T( "%s}\n" ), ind.data() );
393 else if ( node->type == light )
395 std::map< std::string, std::string >::iterator it;
396 for ( it = node->lightentity.props.begin(); it != node->lightentity.props.end(); it++ )
398 _ftprintf( fp, _T( "%sprop \"%s\" \"%s\"\n" ), ind.data(), (*it).first.c_str(), (*it).second.c_str() );
402 // children
403 for ( i = 0; i < (int)node->children.size(); i++ )
405 SaveNode( fp, node->children[i], ind );
408 _ftprintf( fp, _T( "%s}\n" ), indent.data() );
411 void ExportMaterials( INode *node, const TSTR &parentName, DWORD options, int &numNodes )
413 numNodes++;
414 if ( !( options & SCENE_EXPORT_SELECTED ) || ( node->Selected() ) )
416 if ( node->GetMtl() )
417 ExportMaterial( node->GetMtl(), parentName );
419 for ( int i = 0; i < node->NumberOfChildren(); i++ )
420 ExportMaterials( node->GetChildNode( i ), parentName, options, numNodes );
423 void ExportMaterial( Mtl *mtl, const TSTR &parentName )
425 if ( mtl->ClassID() == Class_ID( MULTI_CLASS_ID, 0 ) )
427 TSTR name = parentName + _T( "_" ) + mtl->GetName();
428 for ( int i = 0; i < mtl->NumSubMtls(); i++ )
429 ExportMaterial( mtl->GetSubMtl( i ), name );
431 else if ( mtl->ClassID() == Class_ID( DMTL_CLASS_ID, 0 ) )
433 ExportStdMat( mtl, parentName );
437 void ExportStdMat( Mtl *mtl, const TSTR &parentName )
439 MtlMap::iterator it = mtlIndex.find( mtl );
440 if ( it != mtlIndex.end() )
441 return;
443 StdMat *stdMtl = ( StdMat * )mtl;
444 ExpMtl m;
446 m.name = parentName + _T( "_" ) + mtl->GetName();
448 Texmap *tmap = stdMtl->GetSubTexmap( ID_DI );
450 // if ( !tmap )
451 // return; // do not export texture-less materials
453 if ( !tmap || tmap->ClassID() != Class_ID( BMTEX_CLASS_ID, 0 ) )
454 tmap = NULL;
455 // return; // unsupported texmap class
457 TSTR texname;
458 if ( tmap )
460 BitmapTex *bmt = ( BitmapTex * ) tmap;
461 TextureOutput *tout = bmt->GetTexout();
462 texname = bmt->GetMapName();
463 TCHAR dp[_MAX_PATH];
464 TCHAR df[_MAX_FNAME];
465 TCHAR de[_MAX_EXT];
466 BMMSplitFilename( texname, dp, df, de );
467 m.textureName = TSTR( df ) + TSTR( de );
470 mtlIndex[mtl] = (int)mtlList.size();
471 mtlList.push_back( m );
474 bool ExportGeometry( ExpNode *parentNode, INode *node, const TSTR &parentName, DWORD options, int numNodes, bool parentDiscard = false )
476 if ( GetCOREInterface()->GetCancel() )
477 return false;
479 numNodes++;
481 bool del;
482 ExpNode *n = new ExpNode;
483 if ( !parentNode )
484 rootExpNode = n;
485 else
487 if ( parentDiscard )
488 rootExpNode->children.push_back( n );
489 else
490 parentNode->children.push_back( n );
492 n->node = node;
494 TriObject *o = GetTriObjectFromNode( node, 0, del );
495 if ( o )
497 n->type = tri;
498 if ( ( options & SCENE_EXPORT_SELECTED ) && ( !node->Selected() ) )
500 n->export = false;
501 parentDiscard = true; // all selected children will be attached to the root node
504 if ( n->export )
506 Matrix3 nodeTM = node->GetObjTMAfterWSM(0);
507 int order[3];
508 if (!nodeTM.Parity())
509 { order[0]=0; order[1]=1; order[2]=2; }
510 else
511 { order[0]=2; order[1]=1; order[2]=0; }
513 // export geometry info (verts, normals, etc)
514 Mesh &mesh = o->GetMesh();
516 size_t i;
518 // verts
519 n->verts.resize( mesh.getNumVerts() );
520 for ( i = 0; i < ( int )n->verts.size(); i++ )
522 n->verts[i].x = mesh.verts[i].x;
523 n->verts[i].y = mesh.verts[i].z;
524 n->verts[i].z = mesh.verts[i].y;
526 n->faces.resize( mesh.getNumFaces() );
527 // memcpy( &n->faces.front(), mesh.faces, n->faces.size() * sizeof( Face ) );
528 for ( i = 0; i < n->faces.size(); i++ )
530 n->faces[i].v[0] = mesh.faces[i].v[order[2]];
531 n->faces[i].v[1] = mesh.faces[i].v[order[1]];
532 n->faces[i].v[2] = mesh.faces[i].v[order[0]];
535 // colors
536 if ( mesh.vertCol )
538 n->colors.resize( mesh.getNumVertCol() );
539 for ( i = 0; i < (size_t)mesh.getNumVertCol(); i++ )
541 n->colors[i] = mesh.vertCol[i];
543 n->colorFaces.resize( mesh.getNumFaces() );
544 for ( i = 0; i < (size_t)mesh.getNumFaces(); i++ )
546 n->colorFaces[i].v[0] = mesh.vcFace[i].t[order[2]];
547 n->colorFaces[i].v[1] = mesh.vcFace[i].t[order[1]];
548 n->colorFaces[i].v[2] = mesh.vcFace[i].t[order[0]];
552 // uv
553 if ( 0 != mesh.getNumTVerts() )
555 n->uvVerts.resize( mesh.getNumTVerts() );
556 for ( i = 0; i < ( int )n->uvVerts.size(); i++ )
558 n->uvVerts[i].x = mesh.tVerts[i].x;
559 n->uvVerts[i].y = 1 - mesh.tVerts[i].y;
561 n->uvFaces.resize( mesh.getNumFaces() );
562 // memcpy( &n->uvFaces.front(), mesh.tvFace, n->faces.size() * sizeof( TVFace ) );
563 for ( i = 0; i < n->uvFaces.size(); i++ )
565 n->uvFaces[i].v[0] = mesh.tvFace[i].t[order[2]];
566 n->uvFaces[i].v[1] = mesh.tvFace[i].t[order[1]];
567 n->uvFaces[i].v[2] = mesh.tvFace[i].t[order[0]];
571 // lightmap uv
572 for (i = 2; i < MAX_MESHMAPS; i++)
574 if (mesh.mapSupport (i))
576 // fill uvLightmap
577 size_t nverts = mesh.getNumMapVerts (i);
578 n->uvLightmap.resize (nverts);
579 for (size_t vx = 0; vx < nverts; vx++)
581 n->uvLightmap[vx].x = mesh.mapVerts (i)[vx].x;
582 n->uvLightmap[vx].y = 1 - mesh.mapVerts (i)[vx].y;
585 n->uvLightmapFaces.resize( mesh.getNumFaces() );
586 // memcpy( &n->uvFaces.front(), mesh.tvFace, n->faces.size() * sizeof( TVFace ) );
587 for (size_t f = 0; f < n->uvLightmapFaces.size(); f++)
589 n->uvLightmapFaces[f].v[0] = mesh.mapFaces (i)[f].t[order[2]];
590 n->uvLightmapFaces[f].v[1] = mesh.mapFaces (i)[f].t[order[1]];
591 n->uvLightmapFaces[f].v[2] = mesh.mapFaces (i)[f].t[order[0]];
593 break;
597 // face normals
598 std::vector< Point3 > faceNormals;
599 faceNormals.resize( mesh.getNumFaces() );
600 ExpNode::Face *face = &n->faces.front();
601 for ( i = 0; i < ( int )faceNormals.size(); i++, face++ )
603 Point3 v0 = n->verts[face->v[order[0]]];
604 Point3 v1 = n->verts[face->v[order[1]]];
605 Point3 v2 = n->verts[face->v[order[2]]];
606 Point3 norm = ( v1 - v0 ) ^ ( v2 - v1 );
607 faceNormals[i] = norm;
610 // indexing
611 std::vector< std::vector<int> > indexing;
612 indexing.resize( n->verts.size() );
613 face = &n->faces.front();
614 for ( i = 0; i < n->faces.size(); i++, face++ )
616 indexing[face->v[order[0]]].push_back( (int)i );
617 indexing[face->v[order[1]]].push_back( (int)i );
618 indexing[face->v[order[2]]].push_back( (int)i );
621 // vertex normals
622 face = &n->faces.front();
623 n->normalFaces.resize( n->faces.size() );
624 for ( i = 0; i < n->faces.size(); i++, face++ )
626 for ( int k = 0; k < 3; k++ )
628 // calc normal for face's vertex
629 Point3 norm = faceNormals[i];
630 std::vector<int> &ind = indexing[face->v[order[k]]];
631 for ( int j = 0; j < (int)ind.size(); j++ )
633 if ( ind[j] != i && ( mesh.faces[i].getSmGroup() & mesh.faces[ind[j]].getSmGroup() ) != 0 )
634 norm += faceNormals[ind[j]];
636 norm = norm.Normalize();
638 // check for duplicate
639 std::vector< Point3 >::iterator it;
640 it = std::find( n->normals.begin(), n->normals.end(), norm );
641 int index = (int)n->normals.size();
642 if ( it != n->normals.end() )
643 index = it - n->normals.begin();
644 else
645 n->normals.push_back( norm );
646 n->normalFaces[i].v[order[k]] = index;
650 // material assignments
651 Mtl *mtl = node->GetMtl();
652 if ( mtl )
654 n->mtlIndex.resize( mesh.getNumFaces() );
655 for ( i = 0; i < (size_t)mesh.getNumFaces(); i++ )
657 MtlMap::iterator it;
658 if ( mtl->IsMultiMtl() )
659 it = mtlIndex.find( mtl->GetSubMtl( mesh.faces[i].getMatID() % mtl->NumSubMtls() ) );
660 else
661 it = mtlIndex.find( mtl );
663 if ( it == mtlIndex.end() )
664 n->mtlIndex[i] = -1;
665 else
666 n->mtlIndex[i] = ( *it ).second;
671 if ( del )
672 o->DeleteThis();
674 else
676 // can be light or dummy
677 ObjectState state = node->EvalWorldState(0);
678 if ( state.obj && state.obj->SuperClassID() == LIGHT_CLASS_ID )
680 n->lightentity.props[_T( "classname" )] = _T( "light" );
681 // export light
682 TCHAR str[256]; // required to do some formatting. unsafe!
683 n->type = light;
684 GenLight *lightObj=(GenLight *)state.obj;
685 if ( lightObj->GetUseLight() == FALSE )
686 n->lightentity.props[_T( "disabled" )] = _T( "1" );
687 ObjLightDesc *ld = lightObj->CreateLightDesc( node );
689 // export diffuse color
690 // TODO: export color animation controller
691 Color rgb=lightObj->GetRGBColor(0);
692 _stprintf( str, _T( "%.2f %.2f %.2f" ), rgb.r, rgb.g, rgb.b );
693 n->lightentity.props[ _T( "diffusecolor" ) ] = str;
695 // export multiplier
696 // multiplier = lightObj->GetIntensity( 0 );
698 // export light type
699 int type=lightObj->Type();
700 switch ( type )
702 case OMNI_LIGHT:
703 n->lightentity.props[ _T( "type" ) ] = _T( "omni" );
704 break;
705 case TSPOT_LIGHT:
706 case FSPOT_LIGHT:
707 n->lightentity.props[ _T( "type" ) ] = _T( "spot" );
708 break;
709 case DIR_LIGHT:
710 case TDIR_LIGHT:
711 n->lightentity.props[ _T( "type" ) ] = _T( "dir" );
712 break;
713 default:
714 _stprintf( str, _T( "light '%s' has unknown type and will be treated as dummy object." ), node->GetName() );
715 warning( str );
716 n->type = dummy;
719 // export atten and range
720 float atten[3] = { 1, 0, 0 };
721 float range = 1.0e+5f;
722 if ( lightObj->GetUseAtten() )
724 range = lightObj->GetAtten(0, ATTEN_END);
725 atten[0] = 1.f;
726 atten[1] = 1.f / ( lightObj->GetAtten( 0, ATTEN_END ) - lightObj->GetAtten( 0, ATTEN_START ) );
727 atten[2] = atten[1] * atten[1];
729 _stprintf( str, _T( "%.2f" ), range );
730 n->lightentity.props[ _T( "range" ) ] = str;
731 _stprintf( str, _T( "%.2f %.2f %.2f" ), atten[0], atten[1], atten[2] );
732 n->lightentity.props[ _T( "atten" ) ] = str;
734 // get shadowcasting parms
735 n->lightentity.props[ _T( "castshadows" ) ] = lightObj->GetShadow() ? _T( "1" ) : _T( "0" );
737 #if 0 // FIXME: uncomment this if matrix stuff doesn't apply
738 // get direction (from matrix or directly from light)
739 if ( type != OMNI_LIGHT )
741 Point3 pos=node->GetObjTMAfterWSM(0).GetTrans();
742 position = Point3( pos.x, pos.z, pos.y );
743 INode *target=node->GetTarget();
744 Point3 direction;
745 if ( target )
747 Point3 t=target->GetObjTMAfterWSM(0).GetTrans();
748 direction = Point3( t.x, t.z, t.y );
749 direction -= position;
750 direction = Normalize( direction );
752 else
754 Point4 at = node->GetObjTMAfterWSM(0).GetColumn( 1 );
755 direction = Point3( at.x, at.z, at.y );
756 direction = Normalize( direction );
758 _stprintf( str, _T( "%.2f %.2f %.2f" ), direction[0], direction[1], direction[2] );
759 n->lightentity.props[ _T( "direction" ) ] = str;
761 #endif
762 if ( type == TSPOT_LIGHT || type == FSPOT_LIGHT )
764 float theta = lightObj->GetHotspot(0) * 3.14159f / 360.f;
765 float phi = lightObj->GetFallsize(0) * 3.14159f / 360.f;
766 float falloff = 1.f; // FIXME: does 3dsmax has an analogue?
767 _stprintf( str, _T( "%.2f" ), theta );
768 n->lightentity.props[ _T( "theta" ) ] = str;
769 _stprintf( str, _T( "%.2f" ), theta );
770 n->lightentity.props[ _T( "phi" ) ] = str;
771 _stprintf( str, _T( "%.2f" ), falloff );
772 n->lightentity.props[ _T( "falloff" ) ] = str;
775 else
777 n->type = dummy;
778 // dummy has no additional attributes
782 // export animation
783 if ( !ExportAnim( node, n->animFrames ) )
784 n->animFrames.clear();
785 else
787 n->animSpeed = GetFrameRate();
790 GetCOREInterface()->ProgressUpdate((int)((float)numNodes/numberOfNodes*100.0f));
792 for ( int i = 0; i < node->NumberOfChildren(); i++ )
794 if ( !ExportGeometry( n, node->GetChildNode( i ), parentName, options, numNodes, parentDiscard ) )
795 return false;
798 return true;
801 #define ALMOST_ZERO 1.0e-3f
802 // Not truly the correct way to compare floats of arbitary magnitude...
803 bool EqualPoint3( const Point3 &p1, const Point3 &p2)
805 if (fabs(p1.x - p2.x) > ALMOST_ZERO)
806 return false;
807 if (fabs(p1.y - p2.y) > ALMOST_ZERO)
808 return false;
809 if (fabs(p1.z - p2.z) > ALMOST_ZERO)
810 return false;
812 return true;
815 bool ExportAnim( INode *node, std::vector<Matrix3> &frames )
817 TimeValue start = pInterface->GetAnimRange().Start();
818 TimeValue end = pInterface->GetAnimRange().End();
819 TimeValue t;
820 int delta = GetTicksPerFrame();
821 Matrix3 startTM;
822 Matrix3 tm;
823 bool animated = false;
825 for ( t = start; t <= end; t += delta )
827 tm = node->GetParentNode() ? node->GetObjTMAfterWSM( t ) * Inverse( node->GetParentNode()->GetObjTMAfterWSM( t ) ) : node->GetObjTMAfterWSM( t );
828 if ( t != start )
830 if ( !EqualPoint3( tm.GetRow( 0 ), startTM.GetRow( 0 ) ) )
831 animated = true;
832 if ( !EqualPoint3( tm.GetRow( 1 ), startTM.GetRow( 1 ) ) )
833 animated = true;
834 if ( !EqualPoint3( tm.GetRow( 2 ), startTM.GetRow( 2 ) ) )
835 animated = true;
836 if ( !EqualPoint3( tm.GetTrans(), startTM.GetTrans() ) )
837 animated = true;
839 else
840 startTM = tm;
841 frames.push_back( tm );
843 return animated;
846 TriObject* GetTriObjectFromNode( INode *pNode, TimeValue t, bool &bDeleteIt )
848 bDeleteIt = false;
849 Object *obj = pNode->EvalWorldState( t ).obj;
850 if ( obj && obj->CanConvertToType( Class_ID( TRIOBJ_CLASS_ID, 0 ) ) ) {
851 TriObject *tri = ( TriObject * )obj->ConvertToType( t, Class_ID( TRIOBJ_CLASS_ID, 0 ) );
852 if ( obj != tri ) bDeleteIt = true;
853 return tri;
855 return NULL;
858 void warning( TCHAR *str )
860 ::MessageBox( pInterface->GetMAXHWnd(), str, "fexport warning", MB_OK | MB_ICONINFORMATION );
864 BOOL CALLBACK FExport::ParmsDlgProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
866 switch ( msg )
868 case WM_INITDIALOG:
869 break;
870 case WM_COMMAND:
871 switch ( LOWORD( wParam ) )
873 case IDOK:
874 EndDialog( hwnd, TRUE );
875 break;
876 case IDCANCEL:
877 EndDialog( hwnd, FALSE );
878 break;
880 break;
881 default:
882 return FALSE;
884 return TRUE;
887 class FExportClassDesc:public ClassDesc2 {
888 public:
889 int IsPublic() {return 1;}
890 void * Create(BOOL loading = FALSE) {return new FExport();}
891 const TCHAR * ClassName() {return GetString(IDS_CLASS_NAME);}
892 SClass_ID SuperClassID() {return SCENE_EXPORT_CLASS_ID;}
893 Class_ID ClassID() {return FEXPORT_CLASS_ID;}
894 const TCHAR* Category() {return GetString(IDS_CATEGORY);}
895 const TCHAR* InternalName() { return _T("FExport"); } // returns fixed parsable name (scripter-visible name)
896 HINSTANCE HInstance() { return hInstance; } // returns owning module handle
899 static FExportClassDesc FExportDesc;
900 ClassDesc2* GetFExportDesc() {return &FExportDesc;}