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