2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 static void Load3DS( const char *filename, _3DS_t *p3DS, qboolean verbose );
27 static qboolean s_verbose;
29 #define MAX_MATERIALS 100
30 #define MAX_NAMED_OBJECTS 100
31 #define MAX_MESH_MATERIAL_GROUPS 100
32 #define MAX_TRI_OBJECTS 512
34 static char s_buffer[1000000];
36 static int ReadString( FILE *fp, char *buffer ){
42 fread( &buffer[i], 1, sizeof( char ), fp );
44 } while ( buffer[i++] != 0 );
50 static int ReadChunkAndLength( FILE *fp, unsigned short *chunk, long *len ){
51 if ( fread( chunk, sizeof( short ), 1, fp ) != 1 ) {
54 if ( fread( len, sizeof( long ), 1, fp ) != 1 ) {
55 Error( "Unexpected EOF found" );
60 static void LoadMapName( FILE *fp, char *buffer, int thisChunkLen ){
61 unsigned short chunkID;
65 while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
69 case _3DS_CHUNK_MAT_MAPNAME:
70 fread( buffer, chunkLen - 6, 1, fp );
73 fread( s_buffer, chunkLen - 6, 1, fp );
76 bytesRead += chunkLen;
77 if ( bytesRead >= thisChunkLen ) {
83 static void LoadMaterialList( FILE *fp, long thisChunkLen, _3DSMaterial_t *pMat ){
85 unsigned short chunkID;
91 memset( &mat, 0, sizeof( mat ) );
94 printf( " >>> MATERIAL LIST\n" );
97 while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
101 case _3DS_CHUNK_MAT_NAME:
102 fread( mat.name, chunkLen - 6, 1, fp );
104 printf( " found mat name '%s'\n", mat.name );
107 case _3DS_CHUNK_TEXMAP:
108 LoadMapName( fp, mat.texture, chunkLen - 6 );
110 printf( " found texture '%s'\n", mat.texture );
113 case _3DS_CHUNK_SPECMAP:
114 LoadMapName( fp, mat.specular, chunkLen - 6 );
116 printf( " found specular map '%s'\n", mat.specular );
119 case _3DS_CHUNK_OPACMAP:
120 LoadMapName( fp, mat.opacity, chunkLen - 6 );
122 printf( " found opacity map '%s'\n", mat.opacity );
125 case _3DS_CHUNK_REFLMAP:
126 LoadMapName( fp, mat.reflection, chunkLen - 6 );
128 printf( " found reflection map '%s'\n", mat.reflection );
131 case _3DS_CHUNK_BUMPMAP:
132 LoadMapName( fp, mat.bump, chunkLen - 6 );
134 printf( " found bump map '%s'\n", mat.bump );
138 fread( s_buffer, chunkLen - 6, 1, fp );
142 bytesRead += chunkLen;
144 if ( bytesRead >= thisChunkLen ) {
151 if ( mat.texture[0] ) {
152 sprintf( buffer, "%s%s", curdir, mat.texture );
153 if ( strstr( buffer, gamedir + 1 ) ) {
154 strcpy( mat.texture, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
157 strcpy( mat.texture, buffer );
161 if ( mat.specular[0] ) {
162 sprintf( buffer, "%s%s", curdir, mat.specular );
163 if ( strstr( buffer, gamedir + 1 ) ) {
164 strcpy( mat.specular, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
167 strcpy( mat.specular, buffer );
172 sprintf( buffer, "%s%s", curdir, mat.bump );
173 if ( strstr( buffer, gamedir + 1 ) ) {
174 strcpy( mat.bump, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
177 strcpy( mat.bump, buffer );
181 if ( mat.reflection[0] ) {
182 sprintf( buffer, "%s%s", curdir, mat.reflection );
183 if ( strstr( buffer, gamedir + 1 ) ) {
184 strcpy( mat.reflection, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
187 strcpy( mat.reflection, buffer );
191 if ( mat.opacity[0] ) {
192 sprintf( buffer, "%s%s", curdir, mat.opacity );
193 if ( strstr( buffer, gamedir + 1 ) ) {
194 strcpy( mat.opacity, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
197 strcpy( mat.opacity, buffer );
204 static void LoadMeshMaterialGroup( FILE *fp, long thisChunkLen, _3DSMeshMaterialGroup_t *pMMG ){
205 _3DSMeshMaterialGroup_t mmg;
207 memset( &mmg, 0, sizeof( mmg ) );
209 ReadString( fp, mmg.name );
211 fread( &mmg.numFaces, sizeof( mmg.numFaces ), 1, fp );
212 mmg.pFaces = malloc( sizeof( mmg.pFaces[0] ) * mmg.numFaces );
213 fread( mmg.pFaces, sizeof( mmg.pFaces[0] ), mmg.numFaces, fp );
216 printf( " >>> MESH MATERIAL GROUP '%s' (%d faces)\n", mmg.name, mmg.numFaces );
221 for ( i = 0; i < mmg.numFaces; i++ )
223 printf( " %d\n", mmg.pFaces[i] );
231 static void LoadNamedTriObject( FILE *fp, long thisChunkLen, _3DSTriObject_t *pTO ){
233 unsigned short chunkID;
236 _3DSTriObject_t triObj;
237 _3DSMeshMaterialGroup_t meshMaterialGroups[MAX_MESH_MATERIAL_GROUPS];
238 int numMeshMaterialGroups = 0;
240 memset( &triObj, 0, sizeof( triObj ) );
243 printf( " >>> NAMED TRI OBJECT\n" );
246 while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
250 case _3DS_CHUNK_MSH_MAT_GROUP:
251 LoadMeshMaterialGroup( fp, chunkLen - 6, &meshMaterialGroups[numMeshMaterialGroups] );
252 bytesRead += chunkLen;
253 numMeshMaterialGroups++;
255 case _3DS_CHUNK_FACE_ARRAY:
256 fread( &triObj.numFaces, sizeof( triObj.numFaces ), 1, fp );
257 assert( triObj.pFaces == 0 );
259 triObj.pFaces = malloc( sizeof( triObj.pFaces[0] ) * triObj.numFaces );
260 fread( triObj.pFaces, sizeof( triObj.pFaces[0] ), triObj.numFaces, fp );
261 bytesRead += sizeof( triObj.numFaces ) + triObj.numFaces * sizeof( triObj.pFaces[0] ) + 6;
264 printf( " found face array with %d faces\n", triObj.numFaces );
265 for ( i = 0; i < triObj.numFaces; i++ )
267 printf( " %d: %d,%d,%d\n", i, triObj.pFaces[i].a, triObj.pFaces[i].b, triObj.pFaces[i].c );
272 case _3DS_CHUNK_POINT_ARRAY:
273 fread( &triObj.numPoints, sizeof( triObj.numPoints ), 1, fp );
274 triObj.pPoints = malloc( sizeof( triObj.pPoints[0] ) * triObj.numPoints );
275 fread( triObj.pPoints, sizeof( triObj.pPoints[0] ), triObj.numPoints, fp );
276 bytesRead += sizeof( triObj.numPoints ) + triObj.numPoints * sizeof( triObj.pPoints[0] ) + 6;
278 // flip points around into our coordinate system
279 for ( i = 0; i < triObj.numPoints; i++ )
283 x = triObj.pPoints[i].x;
284 y = triObj.pPoints[i].y;
285 z = triObj.pPoints[i].z;
287 triObj.pPoints[i].x = -y;
288 triObj.pPoints[i].y = x;
289 triObj.pPoints[i].z = z;
293 printf( " found point array with %d points\n", triObj.numPoints );
294 for ( i = 0; i < triObj.numPoints; i++ )
296 printf( " %d: %f,%f,%f\n", i, triObj.pPoints[i].x, triObj.pPoints[i].y, triObj.pPoints[i].z );
300 case _3DS_CHUNK_TEX_VERTS:
301 fread( &triObj.numTexVerts, sizeof( triObj.numTexVerts ), 1, fp );
302 triObj.pTexVerts = malloc( sizeof( triObj.pTexVerts[0] ) * triObj.numTexVerts );
303 fread( triObj.pTexVerts, sizeof( triObj.pTexVerts[0] ), triObj.numTexVerts, fp );
304 bytesRead += sizeof( triObj.numTexVerts ) + sizeof( triObj.pTexVerts[0] ) * triObj.numTexVerts + 6;
307 printf( " found tex vert array with %d tex verts\n", triObj.numTexVerts );
308 for ( i = 0; i < triObj.numTexVerts; i++ )
310 printf( " %d: %f,%f\n", i, triObj.pTexVerts[i].s, triObj.pTexVerts[i].t );
315 fread( s_buffer, chunkLen - 6, 1, fp );
316 bytesRead += chunkLen;
320 if ( bytesRead >= thisChunkLen ) {
326 if ( numMeshMaterialGroups == 0 ) {
327 numMeshMaterialGroups = 1;
328 strcpy( meshMaterialGroups[0].name, "(null)" );
329 if ( pTO->numTexVerts ) {
330 printf( "Warning: assigning (null) skin to tri object\n" );
335 assert( pTO->numFaces == meshMaterialGroups[0].numFaces );
338 pTO->pMeshMaterialGroups = malloc( sizeof( _3DSMeshMaterialGroup_t ) * numMeshMaterialGroups );
339 memcpy( pTO->pMeshMaterialGroups, meshMaterialGroups, numMeshMaterialGroups * sizeof( meshMaterialGroups[0] ) );
340 pTO->numMeshMaterialGroups = numMeshMaterialGroups;
345 assert( numMeshMaterialGroups <= 1 );
348 static void LoadNamedObject( FILE *fp, long thisChunkLen, _3DSNamedObject_t *pNO ){
350 unsigned short chunkID;
354 _3DSTriObject_t triObj[MAX_TRI_OBJECTS];
355 int numTriObjects = 0;
357 memset( triObj, 0, sizeof( triObj ) );
359 bytesRead += ReadString( fp, name );
362 printf( " >>> NAMED OBJECT '%s'\n", name );
365 while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
369 case _3DS_CHUNK_NAMED_TRI_OBJECT:
370 LoadNamedTriObject( fp, chunkLen - 6, &triObj[numTriObjects] );
374 fread( s_buffer, chunkLen - 6, 1, fp );
378 bytesRead += chunkLen;
380 if ( bytesRead >= thisChunkLen ) {
385 strcpy( pNO->name, name );
386 pNO->pTriObjects = malloc( sizeof( _3DSTriObject_t ) * numTriObjects );
387 memcpy( pNO->pTriObjects, triObj, sizeof( triObj[0] ) * numTriObjects );
388 pNO->numTriObjects = numTriObjects;
390 assert( numTriObjects <= 1 );
393 static void LoadEditChunk( FILE *fp, long thisChunkLen, _3DSEditChunk_t *pEC ){
394 unsigned short chunkID;
397 _3DSEditChunk_t editChunk;
399 _3DSMaterial_t mat[MAX_MATERIALS];
400 _3DSNamedObject_t namedObjects[MAX_NAMED_OBJECTS];
402 int numMaterials = 0, numNamedObjects = 0;
404 memset( &editChunk, 0, sizeof( editChunk ) );
407 printf( ">>> EDIT CHUNK\n" );
410 while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
414 case _3DS_CHUNK_MAT_LIST:
415 LoadMaterialList( fp, chunkLen - 6, &mat[numMaterials] );
418 case _3DS_CHUNK_NAMED_OBJECT:
419 LoadNamedObject( fp, chunkLen - 6, &namedObjects[numNamedObjects] );
420 if ( namedObjects[numNamedObjects].numTriObjects != 0 ) {
424 case _3DS_CHUNK_MESH_VERSION:
426 fread( s_buffer, chunkLen - 6, 1, fp );
430 bytesRead += chunkLen;
432 if ( bytesRead >= thisChunkLen ) {
437 if ( numMaterials == 0 ) {
439 strcpy( mat[0].name, "(null)" );
440 printf( "Warning: no material definitions found\n" );
443 pEC->numNamedObjects = numNamedObjects;
445 pEC->pMaterials = malloc( sizeof( _3DSMaterial_t ) * numMaterials );
446 pEC->pNamedObjects = malloc( sizeof( _3DSNamedObject_t ) * numNamedObjects );
448 memcpy( pEC->pMaterials, mat, numMaterials * sizeof( mat[0] ) );
449 memcpy( pEC->pNamedObjects, namedObjects, numNamedObjects * sizeof( namedObjects[0] ) );
452 static void Load3DS( const char *filename, _3DS_t *p3DS, qboolean verbose ){
454 unsigned short chunkID;
456 _3DSEditChunk_t editChunk;
460 if ( ( fp = fopen( filename, "rb" ) ) == 0 ) {
461 Error( "Unable to open '%s'", filename );
465 if ( ( fread( &chunkID, sizeof( short ), 1, fp ) != 1 ) ||
466 ( LittleShort( chunkID ) != _3DS_CHUNK_MAGIC ) ) {
467 Error( "Missing or incorrect magic number in '%s'", filename );
469 if ( fread( &chunkLen, sizeof( chunkLen ), 1, fp ) != 1 ) {
470 Error( "Unexpected EOF encountered in '%s'", filename );
473 if ( !ReadChunkAndLength( fp, &chunkID, &chunkLen ) ) {
474 Error( "Missing version number in '%s'", filename );
476 if ( fread( s_buffer, chunkLen - 6, 1, fp ) != 1 ) {
477 Error( "Unexpected EOF encountered in '%s'", filename );
480 while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
484 case _3DS_CHUNK_EDIT:
485 LoadEditChunk( fp, chunkLen - 6, &editChunk );
487 case _3DS_CHUNK_KEYFRAME_DATA:
488 fread( s_buffer, chunkLen - 6, 1, fp );
491 fread( s_buffer, chunkLen - 6, 1, fp );
498 p3DS->editChunk = editChunk;
501 static void ComputeNormals( _3DSTriObject_t *pTO, triangle_t *pTris ){
502 vec3_t faceNormals[POLYSET_MAXTRIANGLES];
503 vec3_t vertexNormals[POLYSET_MAXTRIANGLES * 3];
504 vec3_t side0, side1, facenormal;
507 memset( faceNormals, 0, sizeof( faceNormals ) );
508 memset( vertexNormals, 0, sizeof( vertexNormals ) );
511 // compute face normals
513 for ( f = 0; f < pTO->numFaces; f++ )
515 VectorSubtract( pTris[f].verts[0], pTris[f].verts[1], side0 );
516 VectorSubtract( pTris[f].verts[2], pTris[f].verts[1], side1 );
518 CrossProduct( side0, side1, facenormal );
519 VectorNormalize( facenormal, faceNormals[f] );
523 // sum vertex normals
525 for ( v = 0; v < pTO->numPoints; v++ )
527 for ( f = 0; f < pTO->numFaces; f++ )
529 if ( ( pTO->pFaces[f].a == v ) ||
530 ( pTO->pFaces[f].b == v ) ||
531 ( pTO->pFaces[f].c == v ) ) {
532 vertexNormals[v][0] += faceNormals[f][0];
533 vertexNormals[v][1] += faceNormals[f][1];
534 vertexNormals[v][2] += faceNormals[f][2];
538 VectorNormalize( vertexNormals[v], vertexNormals[v] );
542 // copy vertex normals into triangles
544 for ( f = 0; f < pTO->numFaces; f++ )
546 int i0 = pTO->pFaces[f].c;
547 int i1 = pTO->pFaces[f].b;
548 int i2 = pTO->pFaces[f].a;
550 VectorCopy( vertexNormals[i0], pTris[f].normals[0] );
551 VectorCopy( vertexNormals[i1], pTris[f].normals[1] );
552 VectorCopy( vertexNormals[i2], pTris[f].normals[2] );
557 ** void _3DS_LoadPolysets
559 void _3DS_LoadPolysets( const char *filename, polyset_t **ppPSET, int *numpsets, qboolean verbose ){
563 triangle_t *ptri, *triangles;
567 memset( &_3ds, 0, sizeof( _3ds ) );
568 Load3DS( filename, &_3ds, verbose );
570 // compute information
571 numPolysets = _3ds.editChunk.numNamedObjects;
574 pPSET = calloc( 1, numPolysets * sizeof( polyset_t ) );
575 triangles = ptri = calloc( 1, POLYSET_MAXTRIANGLES * sizeof( triangle_t ) );
577 // copy the data over
578 for ( i = 0; i < numPolysets; i++ )
580 char matnamebuf[1024];
583 _3DSTriObject_t *pTO = &_3ds.editChunk.pNamedObjects[i].pTriObjects[0];
585 pPSET[i].triangles = ptri;
586 pPSET[i].numtriangles = pTO->numFaces;
587 strcpy( pPSET[i].name, _3ds.editChunk.pNamedObjects[i].name );
589 strcpy( matnamebuf, filename );
590 if ( strrchr( matnamebuf, '/' ) ) {
591 *( strrchr( matnamebuf, '/' ) + 1 ) = 0;
593 strcat( matnamebuf, pTO->pMeshMaterialGroups[0].name );
595 if ( strstr( matnamebuf, gamedir ) ) {
596 strcpy( pPSET[i].materialname, strstr( matnamebuf, gamedir ) + strlen( gamedir ) );
599 strcpy( pPSET[i].materialname, pTO->pMeshMaterialGroups[0].name );
602 assert( pPSET[i].numtriangles < POLYSET_MAXTRIANGLES );
604 for ( tri = ptri, j = 0; j < pPSET[i].numtriangles; j++ )
606 int i0 = pTO->pFaces[j].c;
607 int i1 = pTO->pFaces[j].b;
608 int i2 = pTO->pFaces[j].a;
610 tri->verts[0][0] = pTO->pPoints[i0].x;
611 tri->verts[0][1] = pTO->pPoints[i0].y;
612 tri->verts[0][2] = pTO->pPoints[i0].z;
614 tri->verts[1][0] = pTO->pPoints[i1].x;
615 tri->verts[1][1] = pTO->pPoints[i1].y;
616 tri->verts[1][2] = pTO->pPoints[i1].z;
618 tri->verts[2][0] = pTO->pPoints[i2].x;
619 tri->verts[2][1] = pTO->pPoints[i2].y;
620 tri->verts[2][2] = pTO->pPoints[i2].z;
622 for ( k = 0; k < 3; k++ )
624 tri->colors[0][k] = 1;
625 tri->colors[1][k] = 1;
626 tri->colors[2][k] = 1;
630 if ( pTO->pTexVerts ) {
631 tri->texcoords[0][0] = pTO->pTexVerts[i0].s;
632 tri->texcoords[0][1] = 1.0f - pTO->pTexVerts[i0].t;
633 tri->texcoords[1][0] = pTO->pTexVerts[i1].s;
634 tri->texcoords[1][1] = 1.0f - pTO->pTexVerts[i1].t;
635 tri->texcoords[2][0] = pTO->pTexVerts[i2].s;
636 tri->texcoords[2][1] = 1.0f - pTO->pTexVerts[i2].t;
642 ptri += pPSET[i].numtriangles;
643 assert( ptri - triangles < POLYSET_MAXTRIANGLES );
646 // compute normal data
648 for ( i = 0; i < numPolysets; i++ )
650 // unique vertices based solely on vertex position
651 ComputeNormals( &_3ds.editChunk.pNamedObjects[i].pTriObjects[0],
652 pPSET[i].triangles );
656 free( _3ds.editChunk.pMaterials );
657 free( _3ds.editChunk.pNamedObjects );
660 *numpsets = numPolysets;