]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/picomodel.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / libs / picomodel / picomodel.c
1 /* -----------------------------------------------------------------------------\r
2 \r
3 PicoModel Library\r
4 \r
5 Copyright (c) 2002, Randy Reddig & seaw0lf\r
6 All rights reserved.\r
7 \r
8 Redistribution and use in source and binary forms, with or without modification,\r
9 are permitted provided that the following conditions are met:\r
10 \r
11 Redistributions of source code must retain the above copyright notice, this list\r
12 of conditions and the following disclaimer.\r
13 \r
14 Redistributions in binary form must reproduce the above copyright notice, this\r
15 list of conditions and the following disclaimer in the documentation and/or\r
16 other materials provided with the distribution.\r
17 \r
18 Neither the names of the copyright holders nor the names of its contributors may\r
19 be used to endorse or promote products derived from this software without\r
20 specific prior written permission.\r
21 \r
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND\r
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\r
26 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\r
29 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
32 \r
33 ----------------------------------------------------------------------------- */\r
34 \r
35 \r
36 \r
37 /* marker */\r
38 #define PICOMODEL_C\r
39 \r
40 \r
41 \r
42 /* dependencies */\r
43 #include "picointernal.h"\r
44 \r
45 \r
46 \r
47 /*\r
48 PicoInit()\r
49 initializes the picomodel library\r
50 */\r
51 \r
52 int PicoInit( void )\r
53 {\r
54         /* successfully initialized -sea */\r
55         return 1;\r
56 }\r
57 \r
58 \r
59 \r
60 /*\r
61 PicoShutdown()\r
62 shuts the pico model library down\r
63 */\r
64 \r
65 void PicoShutdown( void )\r
66 {\r
67         /* do something interesting here in the future */\r
68         return;\r
69 }\r
70 \r
71 \r
72 \r
73 /*\r
74 PicoError()\r
75 returns last picomodel error code (see PME_* defines)\r
76 */\r
77 \r
78 int PicoError( void )\r
79 {\r
80         /* todo: do something here */\r
81         return 0;\r
82 }\r
83 \r
84 \r
85 \r
86 /*\r
87 PicoSetMallocFunc()\r
88 sets the ptr to the malloc function\r
89 */\r
90 \r
91 void PicoSetMallocFunc( void *(*func)( size_t ) )\r
92 {\r
93         if( func != NULL )\r
94                 _pico_ptr_malloc = func;\r
95 }\r
96 \r
97 \r
98 \r
99 /*\r
100 PicoSetFreeFunc()\r
101 sets the ptr to the free function\r
102 */\r
103 \r
104 void PicoSetFreeFunc( void (*func)( void* ) )\r
105 {\r
106         if( func != NULL )\r
107                 _pico_ptr_free = func;\r
108 }\r
109 \r
110 \r
111 \r
112 /*\r
113 PicoSetLoadFileFunc()\r
114 sets the ptr to the file load function\r
115 */\r
116 \r
117 void PicoSetLoadFileFunc( void (*func)( char*, unsigned char**, int* ) )\r
118 {\r
119         if( func != NULL )\r
120                 _pico_ptr_load_file = func;\r
121 }\r
122 \r
123 \r
124 \r
125 /*\r
126 PicoSetFreeFileFunc()\r
127 sets the ptr to the free function\r
128 */\r
129 \r
130 void PicoSetFreeFileFunc( void (*func)( void* ) )\r
131 {\r
132         if( func != NULL )\r
133                 _pico_ptr_free_file = func;\r
134 }\r
135 \r
136 \r
137 \r
138 /*\r
139 PicoSetPrintFunc()\r
140 sets the ptr to the print function\r
141 */\r
142 \r
143 void PicoSetPrintFunc( void (*func)( int, const char* ) )\r
144 {\r
145         if( func != NULL )\r
146                 _pico_ptr_print = func;\r
147 }\r
148 \r
149 \r
150 \r
151 /*\r
152 PicoLoadModel()\r
153 the meat and potatoes function\r
154 */\r
155 \r
156 picoModel_t     *PicoLoadModel( char *fileName, int frameNum )\r
157 {\r
158         const picoModule_t      **modules, *pm;\r
159         picoModel_t                     *model;\r
160         picoByte_t                      *buffer;\r
161         int                                     bufSize;\r
162         char                            *modelFileName, *remapFileName;\r
163 \r
164         \r
165         /* init */\r
166         model = NULL;\r
167         \r
168         /* make sure we've got a file name */\r
169         if( fileName == NULL )\r
170         {\r
171                 _pico_printf( PICO_ERROR, "PicoLoadModel: No filename given (fileName == NULL)" );\r
172                 return NULL;\r
173         }\r
174         \r
175         /* load file data (buffer is allocated by host app) */\r
176         _pico_load_file( fileName, &buffer, &bufSize );\r
177         if( bufSize < 0 )\r
178         {\r
179                 _pico_printf( PICO_ERROR, "PicoLoadModel: Failed loading model %s", fileName );\r
180                 return NULL;\r
181         }\r
182 \r
183         /* get ptr to list of supported modules */\r
184         modules = PicoModuleList( NULL );\r
185         \r
186         /* run it through the various loader functions and try */\r
187         /* to find a loader that fits the given file data */\r
188         for( ; *modules != NULL; modules++ )\r
189         {\r
190                 /* get module */\r
191                 pm = *modules;\r
192                 \r
193                 /* sanity check */\r
194                 if( pm == NULL)\r
195                         break;\r
196 \r
197                 /* module must be able to load */\r
198                 if( pm->canload == NULL || pm->load == NULL )\r
199                         continue;\r
200                 \r
201                 /* see whether this module can load the model file or not */\r
202                 if( pm->canload( fileName, buffer, bufSize ) == PICO_PMV_OK )\r
203                 {\r
204                         /* use loader provided by module to read the model data */\r
205                         model = pm->load( fileName, frameNum, buffer, bufSize );\r
206                         if( model == NULL )\r
207                         {\r
208                                 _pico_free_file( buffer );\r
209                                 return NULL;\r
210                         }\r
211                         \r
212                         /* assign pointer to file format module */\r
213                         model->module = pm;\r
214                         \r
215                         /* get model file name */\r
216                         modelFileName = PicoGetModelFileName( model );\r
217                         \r
218                         /* apply model remappings from <model>.remap */\r
219                         if( strlen( modelFileName ) )\r
220                         {\r
221                                 /* alloc copy of model file name */\r
222                                 remapFileName = _pico_alloc( strlen( modelFileName ) + 20 );\r
223                                 if( remapFileName != NULL )\r
224                                 {\r
225                                         /* copy model file name and change extension */\r
226                                         strcpy( remapFileName, modelFileName );\r
227                                         _pico_setfext( remapFileName, "remap" );\r
228 \r
229                                         /* try to remap model; we don't handle the result */\r
230                                         PicoRemapModel( model, remapFileName );\r
231 \r
232                                         /* free the remap file name string */\r
233                                         _pico_free( remapFileName );\r
234                                 }\r
235                         }\r
236                         \r
237                         /* model was loaded, so break out of loop */\r
238                         break;\r
239                 }\r
240         }\r
241         \r
242         /* free memory used by file buffer */\r
243         if( buffer)\r
244                 _pico_free_file( buffer );\r
245 \r
246         /* return */\r
247         return model;\r
248 }\r
249 \r
250 \r
251 \r
252 /* ----------------------------------------------------------------------------\r
253 models\r
254 ---------------------------------------------------------------------------- */\r
255 \r
256 /*\r
257 PicoNewModel()\r
258 creates a new pico model\r
259 */\r
260 \r
261 picoModel_t *PicoNewModel( void )\r
262 {\r
263         picoModel_t     *model;\r
264         \r
265         /* allocate */\r
266         model = _pico_alloc( sizeof(picoModel_t) );\r
267         if( model == NULL )\r
268                 return NULL;\r
269 \r
270         /* clear */\r
271         memset( model,0,sizeof(picoModel_t) );\r
272         \r
273         /* model set up */\r
274         _pico_zero_bounds( model->mins,model->maxs );\r
275 \r
276         /* set initial frame count to 1 -sea */\r
277         model->numFrames = 1;\r
278 \r
279         /* return ptr to new model */\r
280         return model;\r
281 }\r
282 \r
283 \r
284 \r
285 /*\r
286 PicoFreeModel()\r
287 frees a model and all associated data\r
288 */\r
289 \r
290 void PicoFreeModel( picoModel_t *model )\r
291 {\r
292         int                             i;\r
293         \r
294 \r
295         /* sanity check */\r
296         if( model == NULL )\r
297                 return;\r
298         \r
299         /* free bits */\r
300         if( model->name )\r
301                 _pico_free( model->name );\r
302         \r
303         /* free shaders */\r
304         for( i = 0; i < model->numShaders; i++ )\r
305                 PicoFreeShader( model->shader[ i ] );\r
306         free( model->shader );\r
307         \r
308         /* free surfaces */\r
309         for( i = 0; i < model->numSurfaces; i++ )\r
310                 PicoFreeSurface( model->surface[ i ] );\r
311         free( model->surface );\r
312         \r
313         /* free the model */\r
314         _pico_free( model );\r
315 }\r
316 \r
317 \r
318 \r
319 /*\r
320 PicoAdjustModel()\r
321 adjusts a models's memory allocations to handle the requested sizes.\r
322 will always grow, never shrink\r
323 */\r
324 \r
325 int PicoAdjustModel( picoModel_t *model, int numShaders, int numSurfaces )\r
326 {\r
327         /* dummy check */\r
328         if( model == NULL )\r
329                 return 0;\r
330         \r
331         /* bare minimums */\r
332         /* sea: null surface/shader fix (1s=>0s) */\r
333         if( numShaders < 0 )\r
334                 numShaders = 0;\r
335         if( numSurfaces < 0 )\r
336                 numSurfaces = 0;\r
337 \r
338         /* additional shaders? */\r
339         while( numShaders > model->maxShaders )\r
340         {\r
341                 model->maxShaders += PICO_GROW_SHADERS;\r
342                 if( !_pico_realloc( (void *) &model->shader, model->numShaders * sizeof( *model->shader ), model->maxShaders * sizeof( *model->shader ) ) )\r
343                         return 0;\r
344         }\r
345         \r
346         /* set shader count to higher */\r
347         if( numShaders > model->numShaders )\r
348                 model->numShaders = numShaders;\r
349         \r
350         /* additional surfaces? */\r
351         while( numSurfaces > model->maxSurfaces )\r
352         {\r
353                 model->maxSurfaces += PICO_GROW_SURFACES;\r
354                 if( !_pico_realloc( (void *) &model->surface, model->numSurfaces * sizeof( *model->surface ), model->maxSurfaces * sizeof( *model->surface ) ) )\r
355                         return 0;\r
356         }\r
357         \r
358         /* set shader count to higher */\r
359         if( numSurfaces > model->numSurfaces )\r
360                 model->numSurfaces = numSurfaces;\r
361         \r
362         /* return ok */\r
363         return 1;\r
364 }\r
365 \r
366 \r
367 \r
368 /* ----------------------------------------------------------------------------\r
369 shaders\r
370 ---------------------------------------------------------------------------- */\r
371 \r
372 /*\r
373 PicoNewShader()\r
374 creates a new pico shader and returns its index. -sea\r
375 */\r
376 \r
377 picoShader_t *PicoNewShader( picoModel_t *model )\r
378 {\r
379         picoShader_t    *shader;\r
380         \r
381 \r
382         /* allocate and clear */\r
383         shader = _pico_alloc( sizeof(picoShader_t) );\r
384         if( shader == NULL )\r
385                 return NULL;\r
386         memset( shader, 0, sizeof(picoShader_t) );\r
387         \r
388         /* attach it to the model */\r
389         if( model != NULL )\r
390         {\r
391                 /* adjust model */\r
392                 if( !PicoAdjustModel( model, model->numShaders + 1, 0 ) )\r
393                 {\r
394                         _pico_free( shader );\r
395                         return NULL;\r
396                 }\r
397                 /* attach */\r
398                 model->shader[ model->numShaders - 1 ] = shader;\r
399                 shader->model = model;\r
400         }\r
401         /* setup default shader colors */\r
402         _pico_set_color( shader->ambientColor,0,0,0,0 );\r
403         _pico_set_color( shader->diffuseColor,255,255,255,1 );\r
404         _pico_set_color( shader->specularColor,0,0,0,0 );\r
405 \r
406         /* no need to do this, but i do it anyway */\r
407         shader->transparency = 0;\r
408         shader->shininess = 0;\r
409 \r
410         /* return the newly created shader */\r
411         return shader;\r
412 }\r
413 \r
414 \r
415 \r
416 /*\r
417 PicoFreeShader()\r
418 frees a shader and all associated data -sea\r
419 */\r
420 \r
421 void PicoFreeShader( picoShader_t *shader )\r
422 {\r
423         /* dummy check */\r
424         if( shader == NULL )\r
425                 return;\r
426         \r
427         /* free bits */\r
428         if( shader->name )\r
429                 _pico_free( shader->name );\r
430         if( shader->mapName )\r
431                 _pico_free( shader->mapName );\r
432         \r
433         /* free the shader */\r
434         _pico_free( shader );\r
435 }\r
436 \r
437 \r
438 \r
439 /*\r
440 PicoFindShader()\r
441 finds a named shader in a model\r
442 */\r
443 \r
444 picoShader_t *PicoFindShader( picoModel_t *model, char *name, int caseSensitive )\r
445 {\r
446         int             i;\r
447         \r
448         \r
449         /* sanity checks */\r
450         if( model == NULL || name == NULL )     /* sea: null name fix */\r
451                 return NULL;\r
452         \r
453         /* walk list */\r
454         for( i = 0; i < model->numShaders; i++ )\r
455         {\r
456                 /* skip null shaders or shaders with null names */\r
457                 if( model->shader[ i ] == NULL ||\r
458                         model->shader[ i ]->name == NULL )\r
459                         continue;\r
460 \r
461                 /* compare the shader name with name we're looking for */\r
462                 if( caseSensitive )\r
463                 {\r
464                         if( !strcmp( name, model->shader[ i ]->name ) )\r
465                                 return model->shader[ i ];\r
466                 }\r
467                 else if( !_pico_stricmp( name, model->shader[ i ]->name ) )\r
468                                 return model->shader[ i ];\r
469         }\r
470         \r
471         /* named shader not found */\r
472         return NULL;\r
473 }\r
474 \r
475 \r
476 \r
477 /* ----------------------------------------------------------------------------\r
478 surfaces\r
479 ---------------------------------------------------------------------------- */\r
480 \r
481 /*\r
482 PicoNewSurface()\r
483 creates a new pico surface\r
484 */\r
485 \r
486 picoSurface_t *PicoNewSurface( picoModel_t *model )\r
487 {\r
488         picoSurface_t   *surface;\r
489         char surfaceName[64];\r
490         \r
491         /* allocate and clear */\r
492         surface = _pico_alloc( sizeof( *surface ) );\r
493         if( surface == NULL )\r
494                 return NULL;\r
495         memset( surface, 0, sizeof( *surface ) );\r
496         \r
497         /* attach it to the model */\r
498         if( model != NULL )\r
499         {\r
500                 /* adjust model */\r
501                 if( !PicoAdjustModel( model, 0, model->numSurfaces + 1 ) )\r
502                 {\r
503                         _pico_free( surface );\r
504                         return NULL;\r
505                 }\r
506                 \r
507                 /* attach */\r
508                 model->surface[ model->numSurfaces - 1 ] = surface;\r
509                 surface->model = model;\r
510                 \r
511                 /* set default name */\r
512                 sprintf( surfaceName, "Unnamed_%d", model->numSurfaces );\r
513                 PicoSetSurfaceName( surface, surfaceName );\r
514         }\r
515         \r
516         /* return */\r
517         return surface;\r
518 }\r
519 \r
520 \r
521 \r
522 /*\r
523 PicoFreeSurface()\r
524 frees a surface and all associated data\r
525 */\r
526 void PicoFreeSurface( picoSurface_t *surface )\r
527 {\r
528         int             i;\r
529         \r
530         \r
531         /* dummy check */\r
532         if( surface == NULL )\r
533                 return;\r
534         \r
535         /* free bits */\r
536         _pico_free( surface->xyz );\r
537         _pico_free( surface->normal );\r
538         _pico_free( surface->index );\r
539         _pico_free( surface->faceNormal );\r
540         \r
541         /* free arrays */\r
542         for( i = 0; i < surface->numSTArrays; i++ )\r
543                 _pico_free( surface->st[ i ] );\r
544         free( surface->st );\r
545         for( i = 0; i < surface->numColorArrays; i++ )\r
546                 _pico_free( surface->color[ i ] );\r
547         free( surface->color );\r
548         \r
549         /* free the surface */\r
550         _pico_free( surface );\r
551 }\r
552 \r
553 \r
554 \r
555 /*\r
556 PicoAdjustSurface()\r
557 adjusts a surface's memory allocations to handle the requested sizes.\r
558 will always grow, never shrink\r
559 */\r
560 \r
561 int PicoAdjustSurface( picoSurface_t *surface, int numVertexes, int numSTArrays, int numColorArrays, int numIndexes, int numFaceNormals )\r
562 {\r
563         int             i;\r
564         \r
565         \r
566         /* dummy check */\r
567         if( surface == NULL )\r
568                 return 0;\r
569         \r
570         /* bare minimums */\r
571         if( numVertexes < 1 )\r
572                 numVertexes = 1;\r
573         if( numSTArrays < 1 )\r
574                 numSTArrays = 1;\r
575         if( numColorArrays < 1 )\r
576                 numColorArrays = 1;\r
577         if( numIndexes < 1 )\r
578                 numIndexes = 1;\r
579         \r
580         /* additional vertexes? */\r
581         while( numVertexes > surface->maxVertexes ) /* fix */\r
582         {\r
583                 surface->maxVertexes += PICO_GROW_VERTEXES;\r
584                 if( !_pico_realloc( (void *) &surface->xyz, surface->numVertexes * sizeof( *surface->xyz ), surface->maxVertexes * sizeof( *surface->xyz ) ) )\r
585                         return 0;\r
586                 if( !_pico_realloc( (void *) &surface->normal, surface->numVertexes * sizeof( *surface->normal ), surface->maxVertexes * sizeof( *surface->normal ) ) )\r
587                         return 0;\r
588                 for( i = 0; i < surface->numSTArrays; i++ )\r
589                         if( !_pico_realloc( (void*) &surface->st[ i ], surface->numVertexes * sizeof( *surface->st[ i ] ), surface->maxVertexes * sizeof( *surface->st[ i ] ) ) )\r
590                         return 0;\r
591                 for( i = 0; i < surface->numColorArrays; i++ )\r
592                         if( !_pico_realloc( (void*) &surface->color[ i ], surface->numVertexes * sizeof( *surface->color[ i ] ), surface->maxVertexes * sizeof( *surface->color[ i ] ) ) )\r
593                         return 0;\r
594         }\r
595         \r
596         /* set vertex count to higher */\r
597         if( numVertexes > surface->numVertexes )\r
598                 surface->numVertexes = numVertexes;\r
599         \r
600         /* additional st arrays? */\r
601         while( numSTArrays > surface->maxSTArrays ) /* fix */\r
602         {\r
603                 surface->maxSTArrays += PICO_GROW_ARRAYS;\r
604                 if( !_pico_realloc( (void*) &surface->st, surface->numSTArrays * sizeof( *surface->st ), surface->maxSTArrays * sizeof( *surface->st ) ) )\r
605                         return 0;\r
606                 while( surface->numSTArrays < numSTArrays )\r
607                 {\r
608                         surface->st[ surface->numSTArrays ] = _pico_alloc( surface->maxVertexes * sizeof( *surface->st[ 0 ] ) );\r
609                         memset( surface->st[ surface->numSTArrays ], 0, surface->maxVertexes * sizeof( *surface->st[ 0 ] ) );\r
610                         surface->numSTArrays++;\r
611                 }\r
612         }\r
613         \r
614         /* additional color arrays? */\r
615         while( numColorArrays > surface->maxColorArrays ) /* fix */\r
616         {\r
617                 surface->maxColorArrays += PICO_GROW_ARRAYS;\r
618                 if( !_pico_realloc( (void*) &surface->color, surface->numColorArrays * sizeof( *surface->color ), surface->maxColorArrays * sizeof( *surface->color ) ) )\r
619                         return 0;\r
620                 while( surface->numColorArrays < numColorArrays )\r
621                 {\r
622                         surface->color[ surface->numColorArrays ] = _pico_alloc( surface->maxVertexes * sizeof( *surface->color[ 0 ] ) );\r
623                         memset( surface->color[ surface->numColorArrays ], 0, surface->maxVertexes * sizeof( *surface->color[ 0 ] ) );\r
624                         surface->numColorArrays++;\r
625                 }\r
626         }\r
627         \r
628         /* additional indexes? */\r
629         while( numIndexes > surface->maxIndexes ) /* fix */\r
630         {\r
631                 surface->maxIndexes += PICO_GROW_INDEXES;\r
632                 if( !_pico_realloc( (void*) &surface->index, surface->numIndexes * sizeof( *surface->index ), surface->maxIndexes * sizeof( *surface->index ) ) )\r
633                         return 0;\r
634         }\r
635         \r
636         /* set index count to higher */\r
637         if( numIndexes > surface->numIndexes )\r
638                 surface->numIndexes = numIndexes;\r
639 \r
640         /* additional face normals? */\r
641         while( numFaceNormals > surface->maxFaceNormals ) /* fix */\r
642         {\r
643                 surface->maxFaceNormals += PICO_GROW_FACES;\r
644                 if( !_pico_realloc( (void *) &surface->faceNormal, surface->numFaceNormals * sizeof( *surface->faceNormal ), surface->maxFaceNormals * sizeof( *surface->faceNormal ) ) )\r
645                         return 0;\r
646         }\r
647 \r
648         /* set face normal count to higher */\r
649         if( numFaceNormals > surface->numFaceNormals )\r
650                 surface->numFaceNormals = numFaceNormals;\r
651 \r
652         /* return ok */\r
653         return 1;\r
654 }\r
655 \r
656 \r
657 /* PicoFindSurface:\r
658  *   Finds first matching named surface in a model.\r
659  */\r
660 picoSurface_t *PicoFindSurface(\r
661         picoModel_t *model, char *name, int caseSensitive )\r
662 {\r
663         int             i;\r
664 \r
665         /* sanity check */\r
666         if( model == NULL || name == NULL )\r
667                 return NULL;\r
668         \r
669         /* walk list */\r
670         for( i = 0; i < model->numSurfaces; i++ )\r
671         {\r
672                 /* skip null surfaces or surfaces with null names */\r
673                 if( model->surface[ i ] == NULL ||\r
674                         model->surface[ i ]->name == NULL )\r
675                         continue;\r
676 \r
677                 /* compare the surface name with name we're looking for */\r
678                 if (caseSensitive) {\r
679                         if( !strcmp(name,model->surface[ i ]->name) )\r
680                                 return model->surface[ i ];\r
681                 } else {\r
682                         if( !_pico_stricmp(name,model->surface[ i ]->name) )\r
683                                 return model->surface[ i ];\r
684                 }\r
685         }\r
686         /* named surface not found */\r
687         return NULL;\r
688 }\r
689 \r
690 \r
691 \r
692 /*----------------------------------------------------------------------------\r
693   PicoSet*() Setter Functions\r
694 ----------------------------------------------------------------------------*/\r
695 \r
696 void PicoSetModelName( picoModel_t *model, char *name )\r
697 {\r
698         if( model == NULL || name == NULL )\r
699                 return;\r
700         if( model->name != NULL )\r
701                 _pico_free( model->name );\r
702 \r
703         model->name = _pico_clone_alloc( name,-1 );\r
704 }\r
705 \r
706 \r
707 \r
708 void PicoSetModelFileName( picoModel_t *model, char *fileName )\r
709 {\r
710         if( model == NULL || fileName == NULL )\r
711                 return;\r
712         if( model->fileName != NULL )\r
713                 _pico_free( model->fileName );\r
714 \r
715         model->fileName = _pico_clone_alloc( fileName,-1 );\r
716 }\r
717 \r
718 \r
719 \r
720 void PicoSetModelFrameNum( picoModel_t *model, int frameNum )\r
721 {\r
722         if( model == NULL )\r
723                 return;\r
724         model->frameNum = frameNum;\r
725 }\r
726 \r
727 \r
728 \r
729 void PicoSetModelNumFrames( picoModel_t *model, int numFrames )\r
730 {\r
731         if( model == NULL )\r
732                 return;\r
733         model->numFrames = numFrames;\r
734 }\r
735 \r
736 \r
737 \r
738 void PicoSetModelData( picoModel_t *model, void *data )\r
739 {\r
740         if( model == NULL )\r
741                 return;\r
742         model->data = data;\r
743 }\r
744 \r
745 \r
746 \r
747 void PicoSetShaderName( picoShader_t *shader, char *name )\r
748 {\r
749         if( shader == NULL || name == NULL )\r
750                 return;\r
751         if( shader->name != NULL )\r
752                 _pico_free( shader->name );\r
753 \r
754         shader->name = _pico_clone_alloc( name,-1 );\r
755 }\r
756 \r
757 \r
758 \r
759 void PicoSetShaderMapName( picoShader_t *shader, char *mapName )\r
760 {\r
761         if( shader == NULL || mapName == NULL )\r
762                 return;\r
763         if( shader->mapName != NULL )\r
764                 _pico_free( shader->mapName );\r
765 \r
766         shader->mapName = _pico_clone_alloc( mapName,-1 );\r
767 }\r
768 \r
769 \r
770 \r
771 void PicoSetShaderAmbientColor( picoShader_t *shader, picoColor_t color )\r
772 {\r
773         if( shader == NULL || color == NULL )\r
774                 return;\r
775         shader->ambientColor[ 0 ] = color[ 0 ];\r
776         shader->ambientColor[ 1 ] = color[ 1 ];\r
777         shader->ambientColor[ 2 ] = color[ 2 ];\r
778         shader->ambientColor[ 3 ] = color[ 3 ];\r
779 }\r
780 \r
781 \r
782 \r
783 void PicoSetShaderDiffuseColor( picoShader_t *shader, picoColor_t color )\r
784 {\r
785         if( shader == NULL || color == NULL )\r
786                 return;\r
787         shader->diffuseColor[ 0 ] = color[ 0 ];\r
788         shader->diffuseColor[ 1 ] = color[ 1 ];\r
789         shader->diffuseColor[ 2 ] = color[ 2 ];\r
790         shader->diffuseColor[ 3 ] = color[ 3 ];\r
791 }\r
792 \r
793 \r
794 \r
795 void PicoSetShaderSpecularColor( picoShader_t *shader, picoColor_t color )\r
796 {\r
797         if( shader == NULL || color == NULL )\r
798                 return;\r
799         shader->specularColor[ 0 ] = color[ 0 ];\r
800         shader->specularColor[ 1 ] = color[ 1 ];\r
801         shader->specularColor[ 2 ] = color[ 2 ];\r
802         shader->specularColor[ 3 ] = color[ 3 ];\r
803 }\r
804 \r
805 \r
806 \r
807 void PicoSetShaderTransparency( picoShader_t *shader, float value )\r
808 {\r
809         if( shader == NULL )\r
810                 return;\r
811         shader->transparency = value;\r
812 \r
813         /* cap to 0..1 range */\r
814         if (shader->transparency < 0.0)\r
815                 shader->transparency = 0.0;\r
816         if (shader->transparency > 1.0)\r
817                 shader->transparency = 1.0;\r
818 }\r
819 \r
820 \r
821 \r
822 void PicoSetShaderShininess( picoShader_t *shader, float value )\r
823 {\r
824         if( shader == NULL )\r
825                 return;\r
826         shader->shininess = value;\r
827 \r
828         /* cap to 0..127 range */\r
829         if (shader->shininess < 0.0)\r
830                 shader->shininess = 0.0;\r
831         if (shader->shininess > 127.0)\r
832                 shader->shininess = 127.0;\r
833 }\r
834 \r
835 \r
836 \r
837 void PicoSetSurfaceData( picoSurface_t *surface, void *data )\r
838 {\r
839         if( surface == NULL )\r
840                 return;\r
841         surface->data = data;\r
842 }\r
843 \r
844 \r
845 \r
846 void PicoSetSurfaceType( picoSurface_t *surface, picoSurfaceType_t type )\r
847 {\r
848         if( surface == NULL )\r
849                 return;\r
850         surface->type = type;\r
851 }\r
852 \r
853 \r
854 \r
855 void PicoSetSurfaceName( picoSurface_t *surface, char *name )\r
856 {\r
857         if( surface == NULL || name == NULL )\r
858                 return;\r
859         if( surface->name != NULL )\r
860                 _pico_free( surface->name );\r
861 \r
862         surface->name = _pico_clone_alloc( name,-1 );\r
863 }\r
864 \r
865 \r
866 \r
867 void PicoSetSurfaceShader( picoSurface_t *surface, picoShader_t *shader )\r
868 {\r
869         if( surface == NULL )\r
870                 return;\r
871         surface->shader = shader;\r
872 }\r
873 \r
874 \r
875 \r
876 void PicoSetSurfaceXYZ( picoSurface_t *surface, int num, picoVec3_t xyz )\r
877 {\r
878         if( surface == NULL || num < 0 || xyz == NULL )\r
879                 return;\r
880         if( !PicoAdjustSurface( surface, num + 1, 0, 0, 0, 0 ) )\r
881                 return;\r
882         _pico_copy_vec( xyz, surface->xyz[ num ] );\r
883         if( surface->model != NULL )\r
884                 _pico_expand_bounds( xyz, surface->model->mins, surface->model->maxs );\r
885 }\r
886 \r
887 \r
888 \r
889 void PicoSetSurfaceNormal( picoSurface_t *surface, int num, picoVec3_t normal )\r
890 {\r
891         if( surface == NULL || num < 0 || normal == NULL )\r
892                 return;\r
893         if( !PicoAdjustSurface( surface, num + 1, 0, 0, 0, 0 ) )\r
894                 return;\r
895         _pico_copy_vec( normal, surface->normal[ num ] );\r
896 }\r
897 \r
898 \r
899 \r
900 void PicoSetSurfaceST( picoSurface_t *surface, int array, int num, picoVec2_t st )\r
901 {\r
902         if( surface == NULL || num < 0 || st == NULL )\r
903                 return;\r
904         if( !PicoAdjustSurface( surface, num + 1, array + 1, 0, 0, 0 ) )\r
905                 return;\r
906         surface->st[ array ][ num ][ 0 ] = st[ 0 ];\r
907         surface->st[ array ][ num ][ 1 ] = st[ 1 ];\r
908 }\r
909 \r
910 \r
911 \r
912 void PicoSetSurfaceColor( picoSurface_t *surface, int array, int num, picoColor_t color )\r
913 {\r
914         if( surface == NULL || num < 0 || color == NULL )\r
915                 return;\r
916         if( !PicoAdjustSurface( surface, num + 1, 0, array + 1, 0, 0 ) )\r
917                 return;\r
918         surface->color[ array ][ num ][ 0 ] = color[ 0 ];\r
919         surface->color[ array ][ num ][ 1 ] = color[ 1 ];\r
920         surface->color[ array ][ num ][ 2 ] = color[ 2 ];\r
921         surface->color[ array ][ num ][ 3 ] = color[ 3 ];\r
922 }\r
923 \r
924 \r
925 \r
926 void PicoSetSurfaceIndex( picoSurface_t *surface, int num, picoIndex_t index )\r
927 {\r
928         if( surface == NULL || num < 0 )\r
929                 return;\r
930         if( !PicoAdjustSurface( surface, 0, 0, 0, num + 1, 0 ) )\r
931                 return;\r
932         surface->index[ num ] = index;\r
933 }\r
934 \r
935 \r
936 \r
937 void PicoSetSurfaceIndexes( picoSurface_t *surface, int num, picoIndex_t *index, int count )\r
938 {\r
939         if( num < 0 || index == NULL || count < 1 )\r
940                 return;\r
941         if( !PicoAdjustSurface( surface, 0, 0, 0, num + count, 0 ) )\r
942                 return;\r
943         memcpy( &surface->index[ num ], index, count * sizeof( surface->index[ num ] ) );\r
944 }\r
945 \r
946 \r
947 \r
948 void PicoSetFaceNormal( picoSurface_t *surface, int num, picoVec3_t normal )\r
949 {\r
950         if( surface == NULL || num < 0 || normal == NULL )\r
951                 return;\r
952         if( !PicoAdjustSurface( surface, 0, 0, 0, 0, num + 1 ) )\r
953                 return;\r
954         _pico_copy_vec( normal, surface->faceNormal[ num ] );\r
955 }\r
956 \r
957 \r
958 void PicoSetSurfaceSpecial( picoSurface_t *surface, int num, int special )\r
959 {\r
960         if( surface == NULL || num < 0 || num >= PICO_MAX_SPECIAL )\r
961                 return;\r
962         surface->special[ num ] = special;\r
963 }\r
964 \r
965 \r
966 \r
967 /*----------------------------------------------------------------------------\r
968   PicoGet*() Getter Functions\r
969 ----------------------------------------------------------------------------*/\r
970 \r
971 char *PicoGetModelName( picoModel_t *model )\r
972 {\r
973         if( model == NULL )\r
974                 return NULL;\r
975         if( model->name == NULL)\r
976                 return (char*) "";\r
977         return model->name;\r
978 }\r
979 \r
980 \r
981 \r
982 char *PicoGetModelFileName( picoModel_t *model )\r
983 {\r
984         if( model == NULL )\r
985                 return NULL;\r
986         if( model->fileName == NULL)\r
987                 return (char*) "";\r
988         return model->fileName;\r
989 }\r
990 \r
991 \r
992 \r
993 int PicoGetModelFrameNum( picoModel_t *model )\r
994 {\r
995         if( model == NULL )\r
996                 return 0;\r
997         return model->frameNum;\r
998 }\r
999 \r
1000 \r
1001 \r
1002 int PicoGetModelNumFrames( picoModel_t *model )\r
1003 {\r
1004         if( model == NULL )\r
1005                 return 0;\r
1006         return model->numFrames;\r
1007 }\r
1008 \r
1009 \r
1010 \r
1011 void *PicoGetModelData( picoModel_t *model )\r
1012 {\r
1013         if( model == NULL )\r
1014                 return NULL;\r
1015         return model->data;\r
1016 }\r
1017 \r
1018 \r
1019 \r
1020 int PicoGetModelNumShaders( picoModel_t *model )\r
1021 {\r
1022         if( model == NULL )\r
1023                 return 0;\r
1024         return model->numShaders;\r
1025 }\r
1026 \r
1027 \r
1028 \r
1029 picoShader_t *PicoGetModelShader( picoModel_t *model, int num )\r
1030 {\r
1031         /* a few sanity checks */\r
1032         if( model == NULL )\r
1033                 return NULL;\r
1034         if( model->shader == NULL)\r
1035                 return NULL;\r
1036         if( num < 0 || num >= model->numShaders )\r
1037                 return NULL;\r
1038         \r
1039         /* return the shader */\r
1040         return model->shader[ num ];\r
1041 }\r
1042 \r
1043 \r
1044 \r
1045 int PicoGetModelNumSurfaces( picoModel_t *model )\r
1046 {\r
1047         if( model == NULL )\r
1048                 return 0;\r
1049         return model->numSurfaces;\r
1050 }\r
1051 \r
1052 \r
1053 \r
1054 picoSurface_t *PicoGetModelSurface( picoModel_t *model, int num )\r
1055 {\r
1056         /* a few sanity checks */\r
1057         if( model == NULL )\r
1058                 return NULL;\r
1059         if( model->surface == NULL)\r
1060                 return NULL;\r
1061         if( num < 0 || num >= model->numSurfaces )\r
1062                 return NULL;\r
1063         \r
1064         /* return the surface */\r
1065         return model->surface[ num ];\r
1066 }\r
1067 \r
1068 \r
1069 \r
1070 int PicoGetModelTotalVertexes( picoModel_t *model )\r
1071 {\r
1072         int             i, count;\r
1073         \r
1074         \r
1075         if( model == NULL )\r
1076                 return 0;\r
1077         if( model->surface == NULL )\r
1078                 return 0;\r
1079         \r
1080         count = 0;\r
1081         for( i = 0; i < model->numSurfaces; i++ )\r
1082                  count += PicoGetSurfaceNumVertexes( model->surface[ i ] );\r
1083         \r
1084         return count;\r
1085 }\r
1086 \r
1087 \r
1088 \r
1089 int PicoGetModelTotalIndexes( picoModel_t *model )\r
1090 {\r
1091         int             i, count;\r
1092         \r
1093         \r
1094         if( model == NULL )\r
1095                 return 0;\r
1096         if( model->surface == NULL )\r
1097                 return 0;\r
1098         \r
1099         count = 0;\r
1100         for( i = 0; i < model->numSurfaces; i++ )\r
1101                  count += PicoGetSurfaceNumIndexes( model->surface[ i ] );\r
1102         \r
1103         return count;\r
1104 }\r
1105 \r
1106 \r
1107 \r
1108 char *PicoGetShaderName( picoShader_t *shader )\r
1109 {\r
1110         if( shader == NULL )\r
1111                 return NULL;\r
1112         if( shader->name == NULL)\r
1113                 return (char*) "";\r
1114         return shader->name;\r
1115 }\r
1116 \r
1117 \r
1118 \r
1119 char *PicoGetShaderMapName( picoShader_t *shader )\r
1120 {\r
1121         if( shader == NULL )\r
1122                 return NULL;\r
1123         if( shader->mapName == NULL)\r
1124                 return (char*) "";\r
1125         return shader->mapName;\r
1126 }\r
1127 \r
1128 \r
1129 \r
1130 picoByte_t *PicoGetShaderAmbientColor( picoShader_t *shader )\r
1131 {\r
1132         if( shader == NULL )\r
1133                 return NULL;\r
1134         return shader->ambientColor;\r
1135 }\r
1136 \r
1137 \r
1138 \r
1139 picoByte_t *PicoGetShaderDiffuseColor( picoShader_t *shader )\r
1140 {\r
1141         if( shader == NULL )\r
1142                 return NULL;\r
1143         return shader->diffuseColor;\r
1144 }\r
1145 \r
1146 \r
1147 \r
1148 picoByte_t *PicoGetShaderSpecularColor( picoShader_t *shader )\r
1149 {\r
1150         if( shader == NULL )\r
1151                 return NULL;\r
1152         return shader->specularColor;\r
1153 }\r
1154 \r
1155 \r
1156 \r
1157 float PicoGetShaderTransparency( picoShader_t *shader )\r
1158 {\r
1159         if( shader == NULL )\r
1160                 return 0.0f;\r
1161         return shader->transparency;\r
1162 }\r
1163 \r
1164 \r
1165 \r
1166 float PicoGetShaderShininess( picoShader_t *shader )\r
1167 {\r
1168         if( shader == NULL )\r
1169                 return 0.0f;\r
1170         return shader->shininess;\r
1171 }\r
1172 \r
1173 \r
1174 \r
1175 void *PicoGetSurfaceData( picoSurface_t *surface )\r
1176 {\r
1177         if( surface == NULL )\r
1178                 return NULL;\r
1179         return surface->data;\r
1180 }\r
1181 \r
1182 \r
1183 \r
1184 picoSurfaceType_t PicoGetSurfaceType( picoSurface_t *surface )\r
1185 {\r
1186         if( surface == NULL )\r
1187                 return PICO_BAD;\r
1188         return surface->type;\r
1189 }\r
1190 \r
1191 \r
1192 \r
1193 char *PicoGetSurfaceName( picoSurface_t *surface )\r
1194 {\r
1195         if( surface == NULL )\r
1196                 return NULL;\r
1197         if( surface->name == NULL )\r
1198                 return (char*) "";\r
1199         return surface->name;\r
1200 }\r
1201 \r
1202 \r
1203 \r
1204 picoShader_t *PicoGetSurfaceShader( picoSurface_t *surface )\r
1205 {\r
1206         if( surface == NULL )\r
1207                 return NULL;\r
1208         return surface->shader;\r
1209 }\r
1210 \r
1211 \r
1212 \r
1213 int PicoGetSurfaceNumVertexes( picoSurface_t *surface )\r
1214 {\r
1215         if( surface == NULL )\r
1216                 return 0;\r
1217         return surface->numVertexes;\r
1218 }\r
1219 \r
1220 \r
1221 \r
1222 picoVec_t *PicoGetSurfaceXYZ( picoSurface_t *surface, int num )\r
1223 {\r
1224         if( surface == NULL || num < 0 || num > surface->numVertexes )\r
1225                 return NULL;\r
1226         return surface->xyz[ num ];\r
1227 }\r
1228 \r
1229 \r
1230 \r
1231 picoVec_t *PicoGetSurfaceNormal( picoSurface_t *surface, int num )\r
1232 {\r
1233         if( surface == NULL || num < 0 || num > surface->numVertexes )\r
1234                 return NULL;\r
1235         return surface->normal[ num ];\r
1236 }\r
1237 \r
1238 \r
1239 \r
1240 picoVec_t *PicoGetSurfaceST( picoSurface_t *surface, int array, int num  )\r
1241 {\r
1242         if( surface == NULL || array < 0 || array > surface->numSTArrays || num < 0 || num > surface->numVertexes )\r
1243                 return NULL;\r
1244         return surface->st[ array ][ num ];\r
1245 }\r
1246 \r
1247 \r
1248 \r
1249 picoByte_t *PicoGetSurfaceColor( picoSurface_t *surface, int array, int num )\r
1250 {\r
1251         if( surface == NULL || array < 0 || array > surface->numColorArrays || num < 0 || num > surface->numVertexes )\r
1252                 return NULL;\r
1253         return surface->color[ array ][ num ];\r
1254 }\r
1255 \r
1256 \r
1257 \r
1258 int PicoGetSurfaceNumIndexes( picoSurface_t *surface )\r
1259 {\r
1260         if( surface == NULL )\r
1261                 return 0;\r
1262         return surface->numIndexes;\r
1263 }\r
1264 \r
1265 \r
1266 \r
1267 picoIndex_t PicoGetSurfaceIndex( picoSurface_t *surface, int num )\r
1268 {\r
1269         if( surface == NULL || num < 0 || num > surface->numIndexes )\r
1270                 return 0;\r
1271         return surface->index[ num ];\r
1272 }\r
1273 \r
1274 \r
1275 \r
1276 picoIndex_t *PicoGetSurfaceIndexes( picoSurface_t *surface, int num )\r
1277 {\r
1278         if( surface == NULL || num < 0 || num > surface->numIndexes )\r
1279                 return NULL;\r
1280         return &surface->index[ num ];\r
1281 }\r
1282 \r
1283 \r
1284 picoVec_t *PicoGetFaceNormal( picoSurface_t *surface, int num )\r
1285 {\r
1286         if( surface == NULL || num < 0 || num > surface->numFaceNormals )\r
1287                 return NULL;\r
1288         return surface->faceNormal[ num ];\r
1289 }\r
1290 \r
1291 \r
1292 int PicoGetSurfaceSpecial( picoSurface_t *surface, int num )\r
1293 {\r
1294         if( surface == NULL || num < 0 || num >= PICO_MAX_SPECIAL )\r
1295                 return 0;\r
1296         return surface->special[ num ];\r
1297 }\r
1298 \r
1299 \r
1300 \r
1301 /* ----------------------------------------------------------------------------\r
1302 hashtable related functions\r
1303 ---------------------------------------------------------------------------- */\r
1304 \r
1305 /* hashtable code for faster vertex lookups */\r
1306 //#define HASHTABLE_SIZE 32768 // 2048                  /* power of 2, use & */\r
1307 #define HASHTABLE_SIZE 7919 // 32749 // 2039    /* prime, use % */\r
1308 \r
1309 int PicoGetHashTableSize( void )\r
1310 {\r
1311         return HASHTABLE_SIZE;\r
1312 }\r
1313 \r
1314 #define HASH_USE_EPSILON\r
1315 \r
1316 #ifdef HASH_USE_EPSILON\r
1317 #define HASH_XYZ_EPSILON                                        0.01f\r
1318 #define HASH_XYZ_EPSILONSPACE_MULTIPLIER        1.f / HASH_XYZ_EPSILON\r
1319 #define HASH_ST_EPSILON                                         0.0001f\r
1320 #define HASH_NORMAL_EPSILON                                     0.02f\r
1321 #endif\r
1322 \r
1323 unsigned int PicoVertexCoordGenerateHash( picoVec3_t xyz )\r
1324 {\r
1325         unsigned int hash = 0;\r
1326 \r
1327 #ifndef HASH_USE_EPSILON\r
1328         hash += ~(*((unsigned int*) &xyz[ 0 ]) << 15);\r
1329         hash ^= (*((unsigned int*) &xyz[ 0 ]) >> 10);\r
1330         hash += (*((unsigned int*) &xyz[ 1 ]) << 3);\r
1331         hash ^= (*((unsigned int*) &xyz[ 1 ]) >> 6);\r
1332         hash += ~(*((unsigned int*) &xyz[ 2 ]) << 11);\r
1333         hash ^= (*((unsigned int*) &xyz[ 2 ]) >> 16);\r
1334 #else\r
1335         picoVec3_t xyz_epsilonspace;\r
1336 \r
1337         _pico_scale_vec( xyz, HASH_XYZ_EPSILONSPACE_MULTIPLIER, xyz_epsilonspace );\r
1338         xyz_epsilonspace[ 0 ] = (float)floor(xyz_epsilonspace[ 0 ]);\r
1339         xyz_epsilonspace[ 1 ] = (float)floor(xyz_epsilonspace[ 1 ]);\r
1340         xyz_epsilonspace[ 2 ] = (float)floor(xyz_epsilonspace[ 2 ]);\r
1341 \r
1342         hash += ~(*((unsigned int*) &xyz_epsilonspace[ 0 ]) << 15);\r
1343         hash ^= (*((unsigned int*) &xyz_epsilonspace[ 0 ]) >> 10);\r
1344         hash += (*((unsigned int*) &xyz_epsilonspace[ 1 ]) << 3);\r
1345         hash ^= (*((unsigned int*) &xyz_epsilonspace[ 1 ]) >> 6);\r
1346         hash += ~(*((unsigned int*) &xyz_epsilonspace[ 2 ]) << 11);\r
1347         hash ^= (*((unsigned int*) &xyz_epsilonspace[ 2 ]) >> 16);\r
1348 #endif\r
1349 \r
1350         //hash = hash & (HASHTABLE_SIZE-1);\r
1351         hash = hash % (HASHTABLE_SIZE);\r
1352         return hash;\r
1353 }\r
1354 \r
1355 picoVertexCombinationHash_t **PicoNewVertexCombinationHashTable( void )\r
1356 {\r
1357         picoVertexCombinationHash_t     **hashTable = _pico_alloc( HASHTABLE_SIZE * sizeof(picoVertexCombinationHash_t*) );\r
1358 \r
1359         memset( hashTable, 0, HASHTABLE_SIZE * sizeof(picoVertexCombinationHash_t*) );\r
1360 \r
1361         return hashTable;\r
1362 }\r
1363 \r
1364 void PicoFreeVertexCombinationHashTable( picoVertexCombinationHash_t **hashTable )\r
1365 {\r
1366         int                                                     i;\r
1367         picoVertexCombinationHash_t     *vertexCombinationHash;\r
1368         picoVertexCombinationHash_t *nextVertexCombinationHash;\r
1369 \r
1370         /* dummy check */\r
1371         if (hashTable == NULL)\r
1372                 return;\r
1373 \r
1374         for( i = 0; i < HASHTABLE_SIZE; i++ )\r
1375         {\r
1376                 if (hashTable[ i ])\r
1377                 {\r
1378                         nextVertexCombinationHash = NULL;\r
1379 \r
1380                         for( vertexCombinationHash = hashTable[ i ]; vertexCombinationHash; vertexCombinationHash = nextVertexCombinationHash )\r
1381                         {\r
1382                                 nextVertexCombinationHash = vertexCombinationHash->next;\r
1383                                 if (vertexCombinationHash->data != NULL)\r
1384                                 {\r
1385                                         _pico_free( vertexCombinationHash->data );\r
1386                                 }\r
1387                                 _pico_free( vertexCombinationHash );\r
1388                         }\r
1389                 }\r
1390         }\r
1391 \r
1392         _pico_free( hashTable );\r
1393 }\r
1394 \r
1395 picoVertexCombinationHash_t *PicoFindVertexCombinationInHashTable( picoVertexCombinationHash_t **hashTable, picoVec3_t xyz, picoVec3_t normal, picoVec3_t st, picoColor_t color )\r
1396 {\r
1397         unsigned int                            hash;\r
1398         picoVertexCombinationHash_t     *vertexCombinationHash;\r
1399 \r
1400         /* dumy check */\r
1401         if (hashTable == NULL || xyz == NULL || normal == NULL || st == NULL || color == NULL )\r
1402                 return NULL;\r
1403 \r
1404         hash = PicoVertexCoordGenerateHash( xyz );\r
1405 \r
1406         for( vertexCombinationHash = hashTable[ hash ]; vertexCombinationHash; vertexCombinationHash = vertexCombinationHash->next )\r
1407         {\r
1408 #ifndef HASH_USE_EPSILON\r
1409                 /* check xyz */\r
1410                 if( (vertexCombinationHash->vcd.xyz[ 0 ] != xyz[ 0 ] || vertexCombinationHash->vcd.xyz[ 1 ] != xyz[ 1 ] || vertexCombinationHash->vcd.xyz[ 2 ] != xyz[ 2 ]) )\r
1411                         continue;\r
1412 \r
1413                 /* check normal */\r
1414                 if( (vertexCombinationHash->vcd.normal[ 0 ] != normal[ 0 ] || vertexCombinationHash->vcd.normal[ 1 ] != normal[ 1 ] || vertexCombinationHash->vcd.normal[ 2 ] != normal[ 2 ]) )\r
1415                         continue;\r
1416                 \r
1417                 /* check st */\r
1418                 if( vertexCombinationHash->vcd.st[ 0 ] != st[ 0 ] || vertexCombinationHash->vcd.st[ 1 ] != st[ 1 ] )\r
1419                         continue;\r
1420 #else\r
1421                 /* check xyz */\r
1422                 if( ( fabs(xyz[ 0 ] - vertexCombinationHash->vcd.xyz[ 0 ]) ) > HASH_XYZ_EPSILON ||\r
1423                         ( fabs(xyz[ 1 ] - vertexCombinationHash->vcd.xyz[ 1 ]) ) > HASH_XYZ_EPSILON ||\r
1424                         ( fabs(xyz[ 2 ] - vertexCombinationHash->vcd.xyz[ 2 ]) ) > HASH_XYZ_EPSILON )\r
1425                         continue;\r
1426 \r
1427                 /* check normal */\r
1428                 if( ( fabs(normal[ 0 ] - vertexCombinationHash->vcd.normal[ 0 ]) ) > HASH_NORMAL_EPSILON ||\r
1429                         ( fabs(normal[ 1 ] - vertexCombinationHash->vcd.normal[ 1 ]) ) > HASH_NORMAL_EPSILON ||\r
1430                         ( fabs(normal[ 2 ] - vertexCombinationHash->vcd.normal[ 2 ]) ) > HASH_NORMAL_EPSILON )\r
1431                         continue;\r
1432                 \r
1433                 /* check st */\r
1434                 if( ( fabs(st[ 0 ] - vertexCombinationHash->vcd.st[ 0 ]) ) > HASH_ST_EPSILON ||\r
1435                         ( fabs(st[ 1 ] - vertexCombinationHash->vcd.st[ 1 ]) ) > HASH_ST_EPSILON )\r
1436                         continue;\r
1437 #endif\r
1438 \r
1439                 /* check color */\r
1440                 if( *((int*) vertexCombinationHash->vcd.color) != *((int*) color) )\r
1441                         continue;\r
1442 \r
1443                 /* gotcha */\r
1444                 return vertexCombinationHash;\r
1445         }\r
1446 \r
1447         return NULL;\r
1448 }\r
1449 \r
1450 picoVertexCombinationHash_t *PicoAddVertexCombinationToHashTable( picoVertexCombinationHash_t **hashTable, picoVec3_t xyz, picoVec3_t normal, picoVec3_t st, picoColor_t color, picoIndex_t index )\r
1451 {\r
1452         unsigned int                            hash;\r
1453         picoVertexCombinationHash_t     *vertexCombinationHash;\r
1454 \r
1455         /* dumy check */\r
1456         if (hashTable == NULL || xyz == NULL || normal == NULL || st == NULL || color == NULL )\r
1457                 return NULL;\r
1458 \r
1459         vertexCombinationHash = _pico_alloc( sizeof(picoVertexCombinationHash_t) );\r
1460 \r
1461         if (!vertexCombinationHash)\r
1462                 return NULL;\r
1463 \r
1464         hash = PicoVertexCoordGenerateHash( xyz );\r
1465 \r
1466         _pico_copy_vec( xyz, vertexCombinationHash->vcd.xyz );\r
1467         _pico_copy_vec( normal, vertexCombinationHash->vcd.normal );\r
1468         _pico_copy_vec2( st, vertexCombinationHash->vcd.st );\r
1469         _pico_copy_color( color, vertexCombinationHash->vcd.color );\r
1470         vertexCombinationHash->index = index;\r
1471         vertexCombinationHash->data = NULL;\r
1472         vertexCombinationHash->next = hashTable[ hash ];\r
1473         hashTable[ hash ] = vertexCombinationHash;\r
1474 \r
1475         return vertexCombinationHash;\r
1476 }\r
1477 \r
1478 /* ----------------------------------------------------------------------------\r
1479 specialized routines\r
1480 ---------------------------------------------------------------------------- */\r
1481 \r
1482 /*\r
1483 PicoFindSurfaceVertex()\r
1484 finds a vertex matching the set parameters\r
1485 fixme: needs non-naive algorithm\r
1486 */\r
1487 \r
1488 int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t normal, int numSTs, picoVec2_t *st, int numColors, picoColor_t *color )\r
1489 {\r
1490         int             i, j;\r
1491         \r
1492         \r
1493         /* dummy check */\r
1494         if( surface == NULL || surface->numVertexes <= 0 )\r
1495                 return -1;\r
1496         \r
1497         /* walk vertex list */\r
1498         for( i = 0; i < surface->numVertexes; i++ )\r
1499         {\r
1500                 /* check xyz */\r
1501                 if( xyz != NULL && (surface->xyz[ i ][ 0 ] != xyz[ 0 ] || surface->xyz[ i ][ 1 ] != xyz[ 1 ] || surface->xyz[ i ][ 2 ] != xyz[ 2 ]) )\r
1502                         continue;\r
1503                 \r
1504                 /* check normal */\r
1505                 if( normal != NULL && (surface->normal[ i ][ 0 ] != normal[ 0 ] || surface->normal[ i ][ 1 ] != normal[ 1 ] || surface->normal[ i ][ 2 ] != normal[ 2 ]) )\r
1506                         continue;\r
1507                 \r
1508                 /* check st */\r
1509                 if( numSTs > 0 && st != NULL )\r
1510                 {\r
1511                         for( j = 0; j < numSTs; j++ )\r
1512                         {\r
1513                                 if( surface->st[ j ][ i ][ 0 ] != st[ j ][ 0 ] || surface->st[ j ][ i ][ 1 ] != st[ j ][ 1 ] )\r
1514                                         break;\r
1515                         }\r
1516                         if( j != numSTs )\r
1517                                 continue;\r
1518                 }\r
1519                 \r
1520                 /* check color */\r
1521                 if( numColors > 0 && color != NULL )\r
1522                 {\r
1523                         for( j = 0; j < numSTs; j++ )\r
1524                         {\r
1525                                 if( *((int*) surface->color[ j ]) != *((int*) color[ j ]) )\r
1526                                         break;\r
1527                         }\r
1528                         if( j != numColors )\r
1529                                 continue;\r
1530                 }\r
1531                 \r
1532                 /* vertex matches */\r
1533                 return i;\r
1534         }\r
1535         \r
1536         /* nada */\r
1537         return -1;\r
1538 }\r
1539 \r
1540 \r
1541 \r
1542 /*\r
1543 PicoFixSurfaceNormals()\r
1544 fixes broken normals (certain formats bork normals)\r
1545 */\r
1546 \r
1547 #define MAX_NORMAL_VOTES                128\r
1548 #define EQUAL_NORMAL_EPSILON    0.01\r
1549 #define BAD_NORMAL_EPSILON              0.5\r
1550 \r
1551 void PicoFixSurfaceNormals( picoSurface_t *surface )\r
1552 {\r
1553         int                             i, j, k, a, b, c, numVotes, faceIndex;\r
1554         picoVec3_t              votes[ MAX_NORMAL_VOTES ];\r
1555         picoVec3_t              *normals, diff;\r
1556         picoVec4_t              plane;\r
1557         \r
1558         \r
1559         /* dummy check */\r
1560         if( surface == NULL || surface->numVertexes == 0 )\r
1561                 return;\r
1562         \r
1563         /* fixme: handle other surface types */\r
1564         if( surface->type != PICO_TRIANGLES )\r
1565                 return;\r
1566         \r
1567         /* allocate normal storage */\r
1568         normals = _pico_alloc( surface->numVertexes * sizeof( *normals ) );\r
1569         if( normals == NULL )\r
1570         {\r
1571                 _pico_printf( PICO_ERROR, "PicoFixSurfaceNormals: Unable to allocate memory for temporary normal storage" );\r
1572                 return;\r
1573         }\r
1574         \r
1575         /* zero it out */\r
1576         memset( normals, 0, surface->numVertexes * sizeof( *normals ) );\r
1577         \r
1578         /* walk vertex list */\r
1579         for( i = 0; i < surface->numVertexes; i++ )\r
1580         {\r
1581                 /* zero out votes */\r
1582                 numVotes = 0;\r
1583                 \r
1584                 /* find all the triangles that reference this vertex */\r
1585                 for( j = 0, faceIndex = 0; j < surface->numIndexes; j += 3, faceIndex++ )\r
1586                 {\r
1587                         /* get triangle */\r
1588                         a = surface->index[ j ];\r
1589                         b = surface->index[ j + 1 ];\r
1590                         c = surface->index[ j + 2 ];\r
1591                         \r
1592                         /* ignore degenerate triangles */\r
1593                         if( a == b || b == c || c == a )\r
1594                                 continue;\r
1595                         \r
1596                         /* ignore indexes out of range */\r
1597                         if( a < 0 || a >= surface->numVertexes ||\r
1598                                 b < 0 || b >= surface->numVertexes ||\r
1599                                 c < 0 || c >= surface->numVertexes )\r
1600                                 continue;\r
1601                         \r
1602                         /* test triangle */\r
1603                         if( a == i || b == i || c == i )\r
1604                         {\r
1605                                 /* if this surface has face normals */\r
1606                                 if( surface->numFaceNormals && faceIndex < surface->numFaceNormals )\r
1607                                 {\r
1608                                         _pico_copy_vec( surface->faceNormal[ faceIndex ], plane );\r
1609                                         if( plane[ 0 ] == 0.f && plane[ 1 ] == 0.f && plane[ 2 ] == 0.f )\r
1610                                         {\r
1611                                                 /* if null normal, make plane from the 3 points */\r
1612                                                 if( _pico_calc_plane( plane, surface->xyz[ a ], surface->xyz[ b ], surface->xyz[ c ] ) == 0 )\r
1613                                                 {\r
1614                                                         continue;\r
1615                                                 }\r
1616                                         }\r
1617                                 }\r
1618                                 /* make a plane from the 3 points */\r
1619                                 else if( _pico_calc_plane( plane, surface->xyz[ a ], surface->xyz[ b ], surface->xyz[ c ] ) == 0 )\r
1620                                 {\r
1621                                         continue;\r
1622                                 }\r
1623 \r
1624                                 /* see if this normal has already been voted */\r
1625                                 for( k = 0; k < numVotes; k++ )\r
1626                                 {\r
1627                                         _pico_subtract_vec( plane, votes[ k ], diff );\r
1628                                         if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&\r
1629                                                 fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&\r
1630                                                 fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )\r
1631                                                 break;\r
1632                                 }\r
1633                                 \r
1634                                 /* add a new vote? */\r
1635                                 if( k == numVotes && numVotes < MAX_NORMAL_VOTES )\r
1636                                 {\r
1637                                         _pico_copy_vec( plane, votes[ numVotes ] );\r
1638                                         numVotes++;\r
1639                                 }\r
1640                         }\r
1641                 }\r
1642                 \r
1643                 /* tally votes */\r
1644                 if( numVotes > 0 )\r
1645                 {\r
1646                         /* create average normal */\r
1647                         _pico_zero_vec( normals[ i ] );\r
1648                         for( k = 0; k < numVotes; k++ )\r
1649                                 _pico_add_vec( normals[ i ], votes[ k ], normals[ i ] );\r
1650                         \r
1651                         /* normalize it */\r
1652                         if( _pico_normalize_vec( normals[ i ] ) )\r
1653                         {\r
1654                                 /* test against actual normal */\r
1655                                 if( fabs( _pico_dot_vec( normals[ i ], surface->normal[ i ] ) - 1 ) > BAD_NORMAL_EPSILON )\r
1656                                 {\r
1657                                         //%     printf( "Normal %8d: (%f %f %f) -> (%f %f %f)\n", i,\r
1658                                         //%             surface->normal[ i ][ 0 ], surface->normal[ i ][ 1 ], surface->normal[ i ][ 2 ],\r
1659                                         //%             normals[ i ][ 0 ], normals[ i ][ 1 ], normals[ i ][ 2 ] );\r
1660                                         _pico_copy_vec( normals[ i ], surface->normal[ i ] );\r
1661                                 }\r
1662                         }\r
1663                 }\r
1664         }\r
1665         \r
1666         /* free normal storage */\r
1667         _pico_free( normals );\r
1668 }\r
1669 \r
1670 \r
1671 \r
1672 \r
1673 /*\r
1674 PicoRemapModel() - sea\r
1675 remaps model material/etc. information using the remappings\r
1676 contained in the given 'remapFile' (full path to the ascii file to open)\r
1677 returns 1 on success or 0 on error\r
1678 */\r
1679 \r
1680 #define _prm_error_return \\r
1681 { \\r
1682         _pico_free_parser( p ); \\r
1683         _pico_free_file( remapBuffer ); \\r
1684         return 0; \\r
1685 }\r
1686 \r
1687 int PicoRemapModel( picoModel_t *model, char *remapFile )\r
1688 {\r
1689         picoParser_t    *p;\r
1690         picoByte_t              *remapBuffer;\r
1691         int                             remapBufSize;\r
1692         \r
1693         \r
1694         /* sanity checks */\r
1695         if( model == NULL || remapFile == NULL )\r
1696                 return 0;\r
1697         \r
1698         /* load remap file contents */\r
1699         _pico_load_file( remapFile,&remapBuffer,&remapBufSize );\r
1700         \r
1701         /* check result */\r
1702         if( remapBufSize == 0 )\r
1703                 return 1;       /* file is empty: no error */\r
1704         if( remapBufSize < 0 )\r
1705                 return 0;       /* load failed: error */\r
1706         \r
1707         /* create a new pico parser */\r
1708         p = _pico_new_parser( remapBuffer, remapBufSize );\r
1709         if (p == NULL)\r
1710         {\r
1711                 /* ram is really cheap nowadays... */\r
1712                 _prm_error_return;\r
1713         }\r
1714         \r
1715         /* doo teh parse */\r
1716         while( 1 )\r
1717         {\r
1718                 /* get next token in remap file */\r
1719                 if (!_pico_parse( p,1 ))\r
1720                         break;\r
1721 \r
1722                 /* skip over c++ style comment lines */\r
1723                 if (!_pico_stricmp(p->token,"//"))\r
1724                 {\r
1725                         _pico_parse_skip_rest( p );\r
1726                         continue;\r
1727                 }\r
1728                 \r
1729                 /* block for quick material shader name remapping */\r
1730                 /* materials { "m" (=>|->|=) "s" } */\r
1731                 if( !_pico_stricmp(p->token, "materials" ) )\r
1732                 {\r
1733                         int level = 1;\r
1734 \r
1735                         /* check bracket */\r
1736                         if (!_pico_parse_check( p,1,"{" ))\r
1737                                 _prm_error_return;\r
1738 \r
1739                         /* process assignments */\r
1740                         while( 1 )\r
1741                         {\r
1742                                 picoShader_t    *shader;\r
1743                                 char                    *materialName;\r
1744                                 \r
1745                                 \r
1746                                 /* get material name */\r
1747                                 if (_pico_parse( p,1 ) == NULL) break;\r
1748                                 if (!strlen(p->token)) continue;\r
1749                                 materialName = _pico_clone_alloc( p->token,-1 );\r
1750                                 if (materialName == NULL)\r
1751                                         _prm_error_return;\r
1752 \r
1753                                 /* handle levels */\r
1754                                 if (p->token[0] == '{') level++;\r
1755                                 if (p->token[0] == '}') level--;\r
1756                                 if (!level) break;\r
1757 \r
1758                                 /* get next token (assignment token or shader name) */\r
1759                                 if (!_pico_parse( p,0 ))\r
1760                                 {\r
1761                                         _pico_free( materialName );\r
1762                                         _prm_error_return;\r
1763                                 }\r
1764                                 /* skip assignment token (if present) */\r
1765                                 if (!strcmp(p->token,"=>") ||\r
1766                                         !strcmp(p->token,"->") ||\r
1767                                         !strcmp(p->token,"="))\r
1768                                 {\r
1769                                         /* simply grab the next token */\r
1770                                         if (!_pico_parse( p,0 ))\r
1771                                         {\r
1772                                                 _pico_free( materialName );\r
1773                                                 _prm_error_return;\r
1774                                         }\r
1775                                 }\r
1776                                 /* try to find material by name */\r
1777                                 shader = PicoFindShader( model,materialName,0 );\r
1778 \r
1779                                 /* we've found a material matching the name */\r
1780                                 if (shader != NULL)\r
1781                                 {\r
1782                                         PicoSetShaderName( shader,p->token );\r
1783                                 }\r
1784                                 /* free memory used by material name */\r
1785                                 _pico_free( materialName );\r
1786 \r
1787                                 /* skip rest */\r
1788                                 _pico_parse_skip_rest( p );\r
1789                         }\r
1790                 }\r
1791                 /* block for detailed single material remappings */\r
1792                 /* materials[ "m" ] { key data... } */\r
1793                 else if (!_pico_stricmp(p->token,"materials["))\r
1794                 {\r
1795                         picoShader_t *shader;\r
1796                         char *tempMaterialName;\r
1797                         int level = 1;\r
1798 \r
1799                         /* get material name */\r
1800                         if (!_pico_parse( p,0 ))\r
1801                                 _prm_error_return;\r
1802 \r
1803                         /* temporary copy of material name */\r
1804                         tempMaterialName = _pico_clone_alloc( p->token,-1 );\r
1805                         if (tempMaterialName == NULL)\r
1806                                 _prm_error_return;\r
1807 \r
1808                         /* check square closing bracket */\r
1809                         if (!_pico_parse_check( p,0,"]" ))\r
1810                                 _prm_error_return;                      \r
1811 \r
1812                         /* try to find material by name */\r
1813                         shader = PicoFindShader( model,tempMaterialName,0 );\r
1814 \r
1815                         /* free memory used by temporary material name */\r
1816                         _pico_free( tempMaterialName );\r
1817 \r
1818                         /* we haven't found a material matching the name */\r
1819                         /* so we simply skip the braced section now and */\r
1820                         /* continue parsing with the next main token */\r
1821                         if (shader == NULL)\r
1822                         {\r
1823                                 _pico_parse_skip_braced( p );\r
1824                                 continue;\r
1825                         }\r
1826                         /* check opening bracket */\r
1827                         if (!_pico_parse_check( p,1,"{" ))\r
1828                                 _prm_error_return;\r
1829 \r
1830                         /* process material info keys */\r
1831                         while( 1 )\r
1832                         {\r
1833                                 /* get key name */\r
1834                                 if (_pico_parse( p,1 ) == NULL) break;\r
1835                                 if (!strlen(p->token)) continue;\r
1836 \r
1837                                 /* handle levels */\r
1838                                 if (p->token[0] == '{') level++;\r
1839                                 if (p->token[0] == '}') level--;\r
1840                                 if (!level) break;\r
1841 \r
1842                                 /* remap shader name */\r
1843                                 if (!_pico_stricmp(p->token,"shader"))\r
1844                                 {\r
1845                                         if (!_pico_parse( p,0 )) _prm_error_return;\r
1846                                         PicoSetShaderName( shader,p->token );\r
1847                                 }\r
1848                                 /* remap shader map name */\r
1849                                 else if (!_pico_stricmp(p->token,"mapname"))\r
1850                                 {\r
1851                                         if (!_pico_parse( p,0 )) _prm_error_return;\r
1852                                         PicoSetShaderMapName( shader,p->token );\r
1853                                 }\r
1854                                 /* remap shader's ambient color */\r
1855                                 else if (!_pico_stricmp(p->token,"ambient"))\r
1856                                 {\r
1857                                         picoColor_t color;\r
1858                                         picoVec3_t  v;\r
1859 \r
1860                                         /* get vector from parser */\r
1861                                         if (!_pico_parse_vec( p,v )) _prm_error_return;\r
1862 \r
1863                                         /* store as color */\r
1864                                         color[ 0 ] = (picoByte_t)v[ 0 ];\r
1865                                         color[ 1 ] = (picoByte_t)v[ 1 ];\r
1866                                         color[ 2 ] = (picoByte_t)v[ 2 ];\r
1867 \r
1868                                         /* set new ambient color */\r
1869                                         PicoSetShaderAmbientColor( shader,color );\r
1870                                 }\r
1871                                 /* remap shader's diffuse color */\r
1872                                 else if (!_pico_stricmp(p->token,"diffuse"))\r
1873                                 {\r
1874                                         picoColor_t color;\r
1875                                         picoVec3_t  v;\r
1876 \r
1877                                         /* get vector from parser */\r
1878                                         if (!_pico_parse_vec( p,v )) _prm_error_return;\r
1879 \r
1880                                         /* store as color */\r
1881                                         color[ 0 ] = (picoByte_t)v[ 0 ];\r
1882                                         color[ 1 ] = (picoByte_t)v[ 1 ];\r
1883                                         color[ 2 ] = (picoByte_t)v[ 2 ];\r
1884 \r
1885                                         /* set new ambient color */\r
1886                                         PicoSetShaderDiffuseColor( shader,color );\r
1887                                 }\r
1888                                 /* remap shader's specular color */\r
1889                                 else if (!_pico_stricmp(p->token,"specular"))\r
1890                                 {\r
1891                                         picoColor_t color;\r
1892                                         picoVec3_t  v;\r
1893 \r
1894                                         /* get vector from parser */\r
1895                                         if (!_pico_parse_vec( p,v )) _prm_error_return;\r
1896 \r
1897                                         /* store as color */\r
1898                                         color[ 0 ] = (picoByte_t)v[ 0 ];\r
1899                                         color[ 1 ] = (picoByte_t)v[ 1 ];\r
1900                                         color[ 2 ] = (picoByte_t)v[ 2 ];\r
1901 \r
1902                                         /* set new ambient color */\r
1903                                         PicoSetShaderSpecularColor( shader,color );\r
1904                                 }\r
1905                                 /* skip rest */\r
1906                                 _pico_parse_skip_rest( p );\r
1907                         }\r
1908                 }\r
1909                 /* end 'materials[' */\r
1910         }\r
1911         \r
1912         /* free both parser and file buffer */\r
1913         _pico_free_parser( p );\r
1914         _pico_free_file( remapBuffer );\r
1915 \r
1916         /* return with success */\r
1917         return 1;\r
1918 }\r
1919 \r
1920 \r
1921 /*\r
1922 PicoAddTriangleToModel() - jhefty\r
1923 A nice way to add individual triangles to the model.\r
1924 Chooses an appropriate surface based on the shader, or adds a new surface if necessary\r
1925 */\r
1926 \r
1927 void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t** normals, \r
1928                                                         int numSTs, picoVec2_t **st, int numColors, picoColor_t **colors,\r
1929                                                         picoShader_t* shader )\r
1930 {\r
1931         int i,j;\r
1932         int vertDataIndex;\r
1933         picoSurface_t* workSurface = NULL;\r
1934 \r
1935         /* see if a surface already has the shader */\r
1936         for ( i = 0 ; i < model->numSurfaces ; i++ )\r
1937         {\r
1938                 workSurface = model->surface[i];\r
1939                 if ( workSurface->shader == shader )\r
1940                 {                       \r
1941                         break;\r
1942                 }\r
1943         }\r
1944 \r
1945         /* no surface uses this shader yet, so create a new surface */\r
1946         if ( !workSurface || i >=model->numSurfaces )\r
1947         {\r
1948                 /* create a new surface in the model for the unique shader */\r
1949                 workSurface = PicoNewSurface(model);\r
1950                 if ( !workSurface )\r
1951                 {\r
1952                         _pico_printf ( PICO_ERROR , "Could not allocate a new surface!\n" );\r
1953                         return;\r
1954                 }\r
1955 \r
1956                 /* do surface setup */\r
1957                 PicoSetSurfaceType( workSurface, PICO_TRIANGLES );\r
1958                 PicoSetSurfaceName( workSurface, shader->name );\r
1959                 PicoSetSurfaceShader( workSurface, shader );\r
1960         }\r
1961 \r
1962         /* add the triangle data to the surface */\r
1963         for ( i = 0 ; i < 3 ; i++ )     \r
1964         {\r
1965                 /* get the next free spot in the index array */\r
1966                 int newVertIndex = PicoGetSurfaceNumIndexes ( workSurface );\r
1967 \r
1968                 /* get the index of the vertex that we're going to store at newVertIndex */\r
1969                 vertDataIndex = PicoFindSurfaceVertexNum ( workSurface , *xyz[i] , *normals[i] , numSTs , st[i] , numColors , colors[i]);\r
1970 \r
1971                 /* the vertex wasn't found, so create a new vertex in the pool from the data we have */\r
1972                 if ( vertDataIndex == -1 )\r
1973                 {                       \r
1974                         /* find the next spot for a new vertex */\r
1975                         vertDataIndex = PicoGetSurfaceNumVertexes ( workSurface );                      \r
1976 \r
1977                         /* assign the data to it */\r
1978                         PicoSetSurfaceXYZ ( workSurface ,vertDataIndex , *xyz[i] );\r
1979                         PicoSetSurfaceNormal ( workSurface , vertDataIndex , *normals[i] );                     \r
1980 \r
1981                         /* make sure to copy over all available ST's and colors for the vertex */\r
1982                         for ( j = 0 ; j < numColors ; j++ )\r
1983                         {\r
1984                                 PicoSetSurfaceColor( workSurface , j , vertDataIndex , colors[i][j] );\r
1985                         }\r
1986                         for ( j = 0 ; j < numSTs ; j++ )\r
1987                         {\r
1988                                 PicoSetSurfaceST ( workSurface , j , vertDataIndex , st[i][j] );\r
1989                         }\r
1990                 }\r
1991 \r
1992                 /* add this vertex to the triangle */           \r
1993                 PicoSetSurfaceIndex ( workSurface , newVertIndex , vertDataIndex );\r
1994         }       \r
1995 }\r