allow more light contributions to the grid; remove a weird fudge
[divverent/netradiant.git] / tools / quake3 / q3data / 3dslib.c
1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
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.
11
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.
16
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
20 */
21
22 #include <assert.h>
23 #include "q3data.h"
24
25 static void Load3DS( const char *filename, _3DS_t *p3DS, qboolean verbose );
26
27 static qboolean s_verbose;
28
29 #define MAX_MATERIALS 100
30 #define MAX_NAMED_OBJECTS 100
31 #define MAX_MESH_MATERIAL_GROUPS 100
32 #define MAX_TRI_OBJECTS 512
33
34 static char s_buffer[1000000];
35
36 static int ReadString( FILE *fp, char *buffer )
37 {
38         int i = 0;
39         int bytesRead = 0;
40
41         do
42         {
43                 fread( &buffer[i], 1, sizeof( char ), fp );
44                 bytesRead++;
45         } while ( buffer[i++] != 0 );
46         buffer[i] = 0;
47
48         return bytesRead;
49 }
50
51 static int ReadChunkAndLength( FILE *fp, short *chunk, long *len )
52 {
53         if ( fread( chunk, sizeof( short ), 1, fp ) != 1 )
54                 return 0;
55         if ( fread( len, sizeof( long ), 1, fp ) != 1 )
56                 Error( "Unexpected EOF found" );
57         return 1;
58 }
59
60 static void LoadMapName( FILE *fp, char *buffer, int thisChunkLen )
61 {
62         unsigned short chunkID;
63         long chunkLen;
64         long bytesRead = 0;
65
66         while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
67         {
68                 switch ( chunkID )
69                 {
70                 case _3DS_CHUNK_MAT_MAPNAME:
71                         fread( buffer, chunkLen - 6, 1, fp );
72                         break;
73                 default:
74                         fread( s_buffer, chunkLen - 6, 1, fp );
75                         break;
76                 }
77                 bytesRead += chunkLen;
78                 if ( bytesRead >= thisChunkLen )
79                         return;
80         }
81 }
82
83 static void LoadMaterialList( FILE *fp, long thisChunkLen, _3DSMaterial_t *pMat )
84 {
85         long chunkLen;
86         unsigned short chunkID;
87         long bytesRead = 0;
88         _3DSMaterial_t mat;
89         char curdir[1024];
90         char buffer[2048];
91
92         memset( &mat, 0, sizeof( mat ) );
93
94         if ( s_verbose )
95                 printf( "    >>> MATERIAL LIST\n" );
96
97         while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
98         {
99                 switch ( chunkID )
100                 {
101                         case _3DS_CHUNK_MAT_NAME:
102                                 fread( mat.name, chunkLen - 6, 1, fp );
103                                 if ( s_verbose )
104                                         printf( "        found mat name '%s'\n", mat.name );
105                                 break;
106                         case _3DS_CHUNK_TEXMAP:
107                                 LoadMapName( fp, mat.texture, chunkLen - 6 );
108                                 if ( s_verbose )
109                                         printf( "        found texture '%s'\n", mat.texture );
110                                 break;
111                         case _3DS_CHUNK_SPECMAP:
112                                 LoadMapName( fp, mat.specular, chunkLen - 6 );
113                                 if ( s_verbose )
114                                         printf( "        found specular map '%s'\n", mat.specular );
115                                 break;
116                         case _3DS_CHUNK_OPACMAP:
117                                 LoadMapName( fp, mat.opacity, chunkLen - 6 );
118                                 if ( s_verbose )
119                                         printf( "        found opacity map '%s'\n", mat.opacity );
120                                 break;
121                         case _3DS_CHUNK_REFLMAP:
122                                 LoadMapName( fp, mat.reflection, chunkLen - 6 );
123                                 if ( s_verbose )
124                                         printf( "        found reflection map '%s'\n", mat.reflection );
125                                 break;
126                         case _3DS_CHUNK_BUMPMAP:
127                                 LoadMapName( fp, mat.bump, chunkLen - 6 );
128                                 if ( s_verbose )
129                                         printf( "        found bump map '%s'\n", mat.bump );
130                                 break;
131                         default:
132                                 fread( s_buffer, chunkLen - 6, 1, fp );
133                                 break;
134                 }
135
136                 bytesRead += chunkLen;
137
138                 if ( bytesRead >= thisChunkLen )
139                         break;
140         }
141
142         Q_getwd( curdir );
143
144         if ( mat.texture[0] )
145         {
146                 sprintf( buffer, "%s%s", curdir, mat.texture );
147                 if ( strstr( buffer, gamedir + 1 ) )
148                         strcpy( mat.texture, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
149                 else
150                         strcpy( mat.texture, buffer );
151         }
152
153         if ( mat.specular[0] )
154         {
155                 sprintf( buffer, "%s%s", curdir, mat.specular );
156                 if ( strstr( buffer, gamedir + 1 ) )
157                         strcpy( mat.specular, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
158                 else
159                         strcpy( mat.specular, buffer );
160         }
161
162         if ( mat.bump[0] )
163         {
164                 sprintf( buffer, "%s%s", curdir, mat.bump );
165                 if ( strstr( buffer, gamedir + 1 ) )
166                         strcpy( mat.bump, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
167                 else
168                         strcpy( mat.bump, buffer );
169         }
170
171         if ( mat.reflection[0] )
172         {
173                 sprintf( buffer, "%s%s", curdir, mat.reflection );
174                 if ( strstr( buffer, gamedir + 1 ) )
175                         strcpy( mat.reflection, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
176                 else
177                         strcpy( mat.reflection, buffer );
178         }
179
180         if ( mat.opacity[0] )
181         {
182                 sprintf( buffer, "%s%s", curdir, mat.opacity );
183                 if ( strstr( buffer, gamedir + 1 ) )
184                         strcpy( mat.opacity, strstr( buffer, gamedir + 1 ) + strlen( gamedir ) - 1 );
185                 else
186                         strcpy( mat.opacity, buffer );
187         }
188
189         *pMat = mat;
190 }
191
192 static void LoadMeshMaterialGroup( FILE *fp, long thisChunkLen, _3DSMeshMaterialGroup_t *pMMG )
193 {
194         _3DSMeshMaterialGroup_t mmg;
195
196         memset( &mmg, 0, sizeof( mmg ) );
197
198         ReadString( fp, mmg.name );
199
200         fread( &mmg.numFaces, sizeof( mmg.numFaces ), 1, fp );
201         mmg.pFaces = malloc( sizeof( mmg.pFaces[0] ) * mmg.numFaces );
202         fread( mmg.pFaces, sizeof( mmg.pFaces[0] ), mmg.numFaces, fp );
203
204         if ( s_verbose )
205         {
206                 printf( "    >>> MESH MATERIAL GROUP '%s' (%d faces)\n", mmg.name, mmg.numFaces );
207
208                 {
209                         int i;
210
211                         for ( i = 0; i < mmg.numFaces; i++ )
212                         {
213                                 printf( "        %d\n", mmg.pFaces[i] );
214                         }
215                 }
216         }
217
218         *pMMG = mmg;
219 }
220
221 static void LoadNamedTriObject( FILE *fp, long thisChunkLen, _3DSTriObject_t *pTO )
222 {
223         long chunkLen;
224         unsigned short chunkID;
225         int i = 0;
226         long bytesRead = 0;
227         _3DSTriObject_t triObj;
228         _3DSMeshMaterialGroup_t meshMaterialGroups[MAX_MESH_MATERIAL_GROUPS];
229         int numMeshMaterialGroups = 0;
230
231         memset( &triObj, 0, sizeof( triObj ) );
232
233         if ( s_verbose )
234                 printf( "        >>> NAMED TRI OBJECT\n" );
235
236         while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
237         {
238                 switch ( chunkID )
239                 {
240                 case _3DS_CHUNK_MSH_MAT_GROUP:
241                         LoadMeshMaterialGroup( fp, chunkLen - 6, &meshMaterialGroups[numMeshMaterialGroups] );
242                         bytesRead += chunkLen;
243                         numMeshMaterialGroups++;
244                         break;
245                 case _3DS_CHUNK_FACE_ARRAY:
246                         fread( &triObj.numFaces, sizeof( triObj.numFaces ), 1, fp );
247                         assert( triObj.pFaces == 0 );
248
249                         triObj.pFaces = malloc( sizeof( triObj.pFaces[0] ) * triObj.numFaces );
250                         fread( triObj.pFaces, sizeof( triObj.pFaces[0] ), triObj.numFaces, fp );
251                         bytesRead += sizeof( triObj.numFaces ) + triObj.numFaces * sizeof( triObj.pFaces[0] ) + 6;
252
253                         if ( s_verbose )
254                         {
255                                 printf( "            found face array with %d faces\n", triObj.numFaces );
256                                 for ( i = 0; i < triObj.numFaces; i++ )
257                                 {
258                                         printf( "                %d: %d,%d,%d\n", i, triObj.pFaces[i].a, triObj.pFaces[i].b, triObj.pFaces[i].c );
259                                 }
260                         }
261
262                         break;
263                 case _3DS_CHUNK_POINT_ARRAY:
264                         fread( &triObj.numPoints, sizeof( triObj.numPoints ), 1, fp );
265                         triObj.pPoints = malloc( sizeof( triObj.pPoints[0] ) * triObj.numPoints );
266                         fread( triObj.pPoints, sizeof( triObj.pPoints[0] ), triObj.numPoints, fp );
267                         bytesRead += sizeof( triObj.numPoints ) + triObj.numPoints * sizeof( triObj.pPoints[0] ) + 6;
268
269                         // flip points around into our coordinate system
270                         for ( i = 0; i < triObj.numPoints; i++ )
271                         {
272                                 float x, y, z;
273
274                                 x = triObj.pPoints[i].x;
275                                 y = triObj.pPoints[i].y;
276                                 z = triObj.pPoints[i].z;
277
278                                 triObj.pPoints[i].x = -y;
279                                 triObj.pPoints[i].y = x;
280                                 triObj.pPoints[i].z = z;
281                         }
282
283                         if ( s_verbose )
284                         {
285                                 printf( "            found point array with %d points\n", triObj.numPoints );
286                                 for ( i = 0; i < triObj.numPoints; i++ )
287                                 {
288                                         printf( "                %d: %f,%f,%f\n", i, triObj.pPoints[i].x, triObj.pPoints[i].y, triObj.pPoints[i].z );
289                                 }
290                         }
291                         break;
292                 case _3DS_CHUNK_TEX_VERTS:
293                         fread( &triObj.numTexVerts, sizeof( triObj.numTexVerts ), 1, fp );
294                         triObj.pTexVerts = malloc( sizeof( triObj.pTexVerts[0] ) * triObj.numTexVerts );
295                         fread( triObj.pTexVerts, sizeof( triObj.pTexVerts[0] ), triObj.numTexVerts, fp );
296                         bytesRead += sizeof( triObj.numTexVerts ) + sizeof( triObj.pTexVerts[0] ) * triObj.numTexVerts + 6;
297
298                         if ( s_verbose )
299                         {
300                                 printf( "            found tex vert array with %d tex verts\n", triObj.numTexVerts );
301                                 for ( i = 0; i < triObj.numTexVerts; i++ )
302                                 {
303                                         printf( "                %d: %f,%f\n", i, triObj.pTexVerts[i].s, triObj.pTexVerts[i].t );
304                                 }
305                         }
306                         break;
307                 default:
308                         fread( s_buffer, chunkLen - 6, 1, fp );
309                         bytesRead += chunkLen;
310                         break;
311                 }
312
313                 if ( bytesRead >= thisChunkLen )
314                         break;
315         }
316         *pTO = triObj;
317
318         if ( numMeshMaterialGroups == 0 )
319         {
320                 numMeshMaterialGroups = 1;
321                 strcpy( meshMaterialGroups[0].name, "(null)" );
322                 if ( pTO->numTexVerts ) {
323                         printf( "Warning: assigning (null) skin to tri object\n" );
324                 }
325         }
326         else
327         {
328                 assert( pTO->numFaces == meshMaterialGroups[0].numFaces );
329         }
330
331         pTO->pMeshMaterialGroups = malloc( sizeof( _3DSMeshMaterialGroup_t ) * numMeshMaterialGroups );
332         memcpy( pTO->pMeshMaterialGroups, meshMaterialGroups, numMeshMaterialGroups * sizeof( meshMaterialGroups[0] ) );
333         pTO->numMeshMaterialGroups = numMeshMaterialGroups;
334
335         //
336         // sanity checks
337         //
338         assert( numMeshMaterialGroups <= 1 );
339 }
340
341 static void LoadNamedObject( FILE *fp, long thisChunkLen, _3DSNamedObject_t *pNO )
342 {
343         long chunkLen;
344         unsigned short chunkID;
345         int i = 0;
346         long bytesRead = 0;
347         char name[100];
348         _3DSTriObject_t triObj[MAX_TRI_OBJECTS];
349         int numTriObjects = 0;
350
351         memset( triObj, 0, sizeof( triObj ) );
352
353         bytesRead += ReadString( fp, name );
354
355         if ( s_verbose )
356                 printf( "    >>> NAMED OBJECT '%s'\n", name );
357
358         while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
359         {
360                 switch ( chunkID )
361                 {
362                 case _3DS_CHUNK_NAMED_TRI_OBJECT:
363                         LoadNamedTriObject( fp, chunkLen - 6, &triObj[numTriObjects] );
364                         numTriObjects++;
365                         break;
366                 default:
367                         fread( s_buffer, chunkLen - 6, 1, fp );
368                         break;
369                 }
370
371                 bytesRead += chunkLen;
372
373                 if ( bytesRead >= thisChunkLen )
374                         break;
375         }
376
377         strcpy( pNO->name, name );
378         pNO->pTriObjects = malloc( sizeof( _3DSTriObject_t ) * numTriObjects );
379         memcpy( pNO->pTriObjects, triObj, sizeof( triObj[0] ) * numTriObjects );
380         pNO->numTriObjects = numTriObjects;
381
382         assert( numTriObjects <= 1 );
383 }
384
385 static void LoadEditChunk( FILE *fp, long thisChunkLen, _3DSEditChunk_t *pEC )
386 {
387         unsigned short chunkID;
388         long chunkLen;
389         long bytesRead = 0;
390         _3DSEditChunk_t editChunk;
391
392         _3DSMaterial_t mat[MAX_MATERIALS];
393         _3DSNamedObject_t namedObjects[MAX_NAMED_OBJECTS];
394
395         int numMaterials = 0, numNamedObjects = 0;
396
397         memset( &editChunk, 0, sizeof( editChunk ) );
398
399         if ( s_verbose )
400                 printf( ">>> EDIT CHUNK\n" );
401
402         while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
403         {
404                 switch ( chunkID )
405                 {
406                 case _3DS_CHUNK_MAT_LIST:
407                         LoadMaterialList( fp, chunkLen - 6, &mat[numMaterials] );
408                         numMaterials++;
409                         break;
410                 case _3DS_CHUNK_NAMED_OBJECT:
411                         LoadNamedObject( fp, chunkLen - 6, &namedObjects[numNamedObjects] );
412                         if ( namedObjects[numNamedObjects].numTriObjects != 0 )
413                                 ++numNamedObjects;
414                         break;
415                 case _3DS_CHUNK_MESH_VERSION:
416                 default:
417                         fread( s_buffer, chunkLen - 6, 1, fp );
418                         break;
419                 }
420
421                 bytesRead += chunkLen;
422
423                 if ( bytesRead >= thisChunkLen )
424                         break;
425         }
426
427         if ( numMaterials == 0 )
428         {
429                 numMaterials = 1;
430                 strcpy( mat[0].name, "(null)" );
431                 printf( "Warning: no material definitions found\n" );
432         }
433
434         pEC->numNamedObjects = numNamedObjects;
435
436         pEC->pMaterials = malloc( sizeof( _3DSMaterial_t ) * numMaterials );
437         pEC->pNamedObjects = malloc( sizeof( _3DSNamedObject_t ) * numNamedObjects );
438
439         memcpy( pEC->pMaterials, mat, numMaterials * sizeof( mat[0] ) );
440         memcpy( pEC->pNamedObjects, namedObjects, numNamedObjects * sizeof( namedObjects[0] ) );
441 }
442
443 static void Load3DS( const char *filename, _3DS_t *p3DS, qboolean verbose )
444 {
445         FILE *fp;
446         unsigned short chunkID;
447         long  chunkLen;
448         _3DSEditChunk_t editChunk;
449
450         s_verbose = verbose;
451
452         if ( ( fp = fopen( filename, "rb" ) ) == 0 )
453                 Error( "Unable to open '%s'", filename );
454
455         // read magic number
456         if ( ( fread( &chunkID, sizeof( short ), 1, fp ) != 1 ) ||
457                  ( LittleShort( chunkID ) != _3DS_CHUNK_MAGIC ) )
458         {
459                 Error( "Missing or incorrect magic number in '%s'", filename );
460         }
461         if ( fread( &chunkLen, sizeof( chunkLen ), 1, fp ) != 1 )
462                 Error( "Unexpected EOF encountered in '%s'", filename );
463         // version number
464         if ( !ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
465                 Error( "Missing version number in '%s'", filename );
466         if ( fread( s_buffer, chunkLen - 6, 1, fp ) != 1 )
467                 Error( "Unexpected EOF encountered in '%s'", filename );
468
469         while ( ReadChunkAndLength( fp, &chunkID, &chunkLen ) )
470         {
471                 switch ( chunkID )
472                 {
473                         case _3DS_CHUNK_EDIT:
474                                 LoadEditChunk( fp, chunkLen - 6, &editChunk );
475                                 break;
476                         case _3DS_CHUNK_KEYFRAME_DATA:
477                                 fread( s_buffer, chunkLen - 6, 1, fp );
478                                 break;
479                         default:
480                                 fread( s_buffer, chunkLen - 6, 1, fp );
481                                 break;
482                 }
483         }
484
485         fclose( fp );
486
487         p3DS->editChunk = editChunk;
488 }
489
490 static void ComputeNormals( _3DSTriObject_t *pTO, triangle_t *pTris )
491 {
492         vec3_t faceNormals[POLYSET_MAXTRIANGLES];
493         vec3_t vertexNormals[POLYSET_MAXTRIANGLES*3];
494         vec3_t side0, side1, facenormal;
495         int f, v;
496
497         memset( faceNormals, 0, sizeof( faceNormals ) );
498         memset( vertexNormals, 0, sizeof( vertexNormals ) );
499
500         //
501         // compute face normals
502         //
503         for ( f = 0; f < pTO->numFaces; f++ )
504         {
505                 VectorSubtract( pTris[f].verts[0], pTris[f].verts[1], side0 );
506                 VectorSubtract( pTris[f].verts[2], pTris[f].verts[1], side1 );
507
508                 CrossProduct( side0, side1, facenormal );
509                 VectorNormalize( facenormal, faceNormals[f] );
510         }
511
512         //
513         // sum vertex normals
514         //
515         for ( v = 0; v < pTO->numPoints; v++ )
516         {
517                 for ( f = 0; f < pTO->numFaces; f++ )
518                 {
519                         if ( ( pTO->pFaces[f].a == v ) ||
520                                  ( pTO->pFaces[f].b == v ) ||
521                                  ( pTO->pFaces[f].c == v ) )
522                         {
523                                 vertexNormals[v][0] += faceNormals[f][0];
524                                 vertexNormals[v][1] += faceNormals[f][1];
525                                 vertexNormals[v][2] += faceNormals[f][2];
526                         }
527                 }
528
529                 VectorNormalize( vertexNormals[v], vertexNormals[v] );
530         }
531
532         //
533         // copy vertex normals into triangles
534         //
535         for ( f = 0; f < pTO->numFaces; f++ )
536         {
537                 int i0 = pTO->pFaces[f].c;
538                 int i1 = pTO->pFaces[f].b;
539                 int i2 = pTO->pFaces[f].a;
540
541                 VectorCopy( vertexNormals[i0], pTris[f].normals[0] );
542                 VectorCopy( vertexNormals[i1], pTris[f].normals[1] );
543                 VectorCopy( vertexNormals[i2], pTris[f].normals[2] );
544         }
545 }
546
547 /*
548 ** void _3DS_LoadPolysets
549 */
550 void _3DS_LoadPolysets( const char *filename, polyset_t **ppPSET, int *numpsets, qboolean verbose )
551 {
552         _3DS_t _3ds;
553         int numPolysets;
554         polyset_t *pPSET;
555         triangle_t *ptri, *triangles;
556         int i;
557
558         // load the 3DS
559         memset( &_3ds, 0, sizeof( _3ds ) );
560         Load3DS( filename, &_3ds, verbose );
561
562         // compute information
563         numPolysets = _3ds.editChunk.numNamedObjects;
564
565         // allocate memory
566         pPSET = calloc( 1, numPolysets * sizeof( polyset_t ) );
567         triangles = ptri = calloc( 1, POLYSET_MAXTRIANGLES * sizeof( triangle_t ) );
568
569         // copy the data over
570         for ( i = 0; i < numPolysets; i++ )
571         {
572                 char matnamebuf[1024];
573                 int j;
574                 triangle_t *tri;
575                 _3DSTriObject_t *pTO = &_3ds.editChunk.pNamedObjects[i].pTriObjects[0];
576
577                 pPSET[i].triangles = ptri;
578                 pPSET[i].numtriangles = pTO->numFaces;
579                 strcpy( pPSET[i].name, _3ds.editChunk.pNamedObjects[i].name );
580
581                 strcpy( matnamebuf, filename );
582                 if ( strrchr( matnamebuf, '/' ) )
583                         *( strrchr( matnamebuf, '/' ) + 1 )= 0;
584                 strcat( matnamebuf, pTO->pMeshMaterialGroups[0].name );
585
586                 if ( strstr( matnamebuf, gamedir ) )
587                         strcpy( pPSET[i].materialname, strstr( matnamebuf, gamedir ) + strlen( gamedir ) );
588                 else
589                         strcpy( pPSET[i].materialname, pTO->pMeshMaterialGroups[0].name );
590
591                 assert( pPSET[i].numtriangles < POLYSET_MAXTRIANGLES );
592
593                 for ( tri = ptri, j = 0; j < pPSET[i].numtriangles; j++ )
594                 {
595                         int i0 = pTO->pFaces[j].c;
596                         int i1 = pTO->pFaces[j].b;
597                         int i2 = pTO->pFaces[j].a;
598
599                         tri->verts[0][0] = pTO->pPoints[i0].x;
600                         tri->verts[0][1] = pTO->pPoints[i0].y;
601                         tri->verts[0][2] = pTO->pPoints[i0].z;
602
603                         tri->verts[1][0] = pTO->pPoints[i1].x;
604                         tri->verts[1][1] = pTO->pPoints[i1].y;
605                         tri->verts[1][2] = pTO->pPoints[i1].z;
606
607                         tri->verts[2][0] = pTO->pPoints[i2].x;
608                         tri->verts[2][1] = pTO->pPoints[i2].y;
609                         tri->verts[2][2] = pTO->pPoints[i2].z;
610 /*
611                         for ( k = 0; k < 3; k++ )
612                         {
613                                 tri->colors[0][k] = 1;
614                                 tri->colors[1][k] = 1;
615                                 tri->colors[2][k] = 1;
616                         }
617 */
618
619                         if ( pTO->pTexVerts )
620                         {
621                                 tri->texcoords[0][0] = pTO->pTexVerts[i0].s;
622                                 tri->texcoords[0][1] = 1.0f - pTO->pTexVerts[i0].t;
623                                 tri->texcoords[1][0] = pTO->pTexVerts[i1].s;
624                                 tri->texcoords[1][1] = 1.0f - pTO->pTexVerts[i1].t;
625                                 tri->texcoords[2][0] = pTO->pTexVerts[i2].s;
626                                 tri->texcoords[2][1] = 1.0f - pTO->pTexVerts[i2].t;
627                         }
628
629                         tri++;
630                 }
631
632                 ptri += pPSET[i].numtriangles;
633                 assert( ptri - triangles < POLYSET_MAXTRIANGLES );
634         }
635
636         // compute normal data
637 #if 0
638         for ( i = 0; i < numPolysets; i++ )
639         {
640                 // unique vertices based solely on vertex position
641                 ComputeNormals( &_3ds.editChunk.pNamedObjects[i].pTriObjects[0],
642                                                  pPSET[i].triangles );
643         }
644 #endif
645
646         free( _3ds.editChunk.pMaterials );
647         free( _3ds.editChunk.pNamedObjects );
648
649         *ppPSET = pPSET;
650         *numpsets = numPolysets;
651 }