]> icculus.org git repositories - divverent/netradiant.git/blob - libs/picomodel/pm_terrain.c
q3map2 is now waring free
[divverent/netradiant.git] / libs / picomodel / pm_terrain.c
1 /* -----------------------------------------------------------------------------
2
3 PicoModel Library 
4
5 Copyright (c) 2003, Randy Reddig & seaw0lf
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
10
11 Redistributions of source code must retain the above copyright notice, this list
12 of conditions and the following disclaimer.
13
14 Redistributions in binary form must reproduce the above copyright notice, this
15 list of conditions and the following disclaimer in the documentation and/or
16 other materials provided with the distribution.
17
18 Neither the names of the copyright holders nor the names of its contributors may
19 be used to endorse or promote products derived from this software without
20 specific prior written permission. 
21
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33 ----------------------------------------------------------------------------- */
34
35
36
37 /* marker */
38 #define PM_TERRAIN_C
39
40
41
42 /* dependencies */
43 #include "picointernal.h"
44
45
46
47 typedef struct tga_s
48 {
49         unsigned char   id_length, colormap_type, image_type;
50         unsigned short  colormap_index, colormap_length;
51         unsigned char   colormap_size;
52         unsigned short  x_origin, y_origin, width, height;
53         unsigned char   pixel_size, attributes;
54 }
55 tga_t;
56
57
58
59 /*
60 _terrain_load_tga_buffer()
61 loads a tga image into a newly allocated image buffer
62 fixme: replace/clean this function
63 */
64
65 void _terrain_load_tga_buffer( unsigned char *buffer, unsigned char **pic, int *width, int *height )
66 {
67         int                             row, column;
68         int                             columns, rows, numPixels;
69         unsigned char   *pixbuf;
70         unsigned char   *buf_p;
71         tga_t                   targa_header;
72         unsigned char   *targa_rgba;
73         
74         
75         *pic = NULL;
76         
77         if( buffer == NULL )
78                 return;
79         
80         buf_p = buffer;
81         
82         targa_header.id_length = *buf_p++;
83         targa_header.colormap_type = *buf_p++;
84         targa_header.image_type = *buf_p++;
85         
86         targa_header.colormap_index = _pico_little_short ( *(short*)buf_p );
87         buf_p += 2;
88         targa_header.colormap_length = _pico_little_short ( *(short*) buf_p );
89         buf_p += 2;
90         targa_header.colormap_size = *buf_p++;
91         targa_header.x_origin = _pico_little_short ( *(short*) buf_p );
92         buf_p += 2;
93         targa_header.y_origin = _pico_little_short ( *(short*) buf_p );
94         buf_p += 2;
95         targa_header.width = _pico_little_short ( *(short*) buf_p );
96         buf_p += 2;
97         targa_header.height = _pico_little_short ( *(short*) buf_p );
98         buf_p += 2;
99         targa_header.pixel_size = *buf_p++;
100         targa_header.attributes = *buf_p++;
101
102         if( targa_header.image_type != 2 && targa_header.image_type != 10 && targa_header.image_type != 3 ) 
103         {
104                 _pico_printf( PICO_ERROR, "Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n");
105                 pic = NULL;
106                 return;
107         }
108
109         if( targa_header.colormap_type != 0 )
110         {
111                 _pico_printf( PICO_ERROR, "Indexed color TGA images not supported\n" );
112                 return;
113         }
114
115         if( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 && targa_header.image_type != 3 )
116         {
117                 _pico_printf( PICO_ERROR, "Only 32 or 24 bit TGA images supported (not indexed color)\n");
118                 pic = NULL;
119                 return;
120         }
121
122         columns = targa_header.width;
123         rows = targa_header.height;
124         numPixels = columns * rows;
125
126         if (width)
127                 *width = columns;
128         if (height)
129                 *height = rows;
130
131         targa_rgba = _pico_alloc( numPixels * 4 );
132         *pic = targa_rgba;
133
134         if (targa_header.id_length != 0)
135                 buf_p += targa_header.id_length;  // skip TARGA image comment
136         
137         if ( targa_header.image_type==2 || targa_header.image_type == 3 )
138         { 
139                 // Uncompressed RGB or gray scale image
140                 for(row=rows-1; row>=0; row--) 
141                 {
142                         pixbuf = targa_rgba + row*columns*4;
143                         for(column=0; column<columns; column++) 
144                         {
145                                 unsigned char red,green,blue,alphabyte;
146                                 switch (targa_header.pixel_size) 
147                                 {
148                                         
149                                 case 8:
150                                         blue = *buf_p++;
151                                         green = blue;
152                                         red = blue;
153                                         *pixbuf++ = red;
154                                         *pixbuf++ = green;
155                                         *pixbuf++ = blue;
156                                         *pixbuf++ = 255;
157                                         break;
158
159                                 case 24:
160                                         blue = *buf_p++;
161                                         green = *buf_p++;
162                                         red = *buf_p++;
163                                         *pixbuf++ = red;
164                                         *pixbuf++ = green;
165                                         *pixbuf++ = blue;
166                                         *pixbuf++ = 255;
167                                         break;
168                                 case 32:
169                                         blue = *buf_p++;
170                                         green = *buf_p++;
171                                         red = *buf_p++;
172                                         alphabyte = *buf_p++;
173                                         *pixbuf++ = red;
174                                         *pixbuf++ = green;
175                                         *pixbuf++ = blue;
176                                         *pixbuf++ = alphabyte;
177                                         break;
178                                 default:
179                                         break;
180                                 }
181                         }
182                 }
183         }
184         
185         /* rle encoded pixels */
186         else if( targa_header.image_type == 10 )
187         {   
188                 unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
189
190                 red = 0;
191                 green = 0;
192                 blue = 0;
193                 alphabyte = 0xff;
194
195                 for(row=rows-1; row>=0; row--) {
196                         pixbuf = targa_rgba + row*columns*4;
197                         for(column=0; column<columns; ) {
198                                 packetHeader= *buf_p++;
199                                 packetSize = 1 + (packetHeader & 0x7f);
200                                 if (packetHeader & 0x80) {        // run-length packet
201                                         switch (targa_header.pixel_size) {
202                                                 case 24:
203                                                                 blue = *buf_p++;
204                                                                 green = *buf_p++;
205                                                                 red = *buf_p++;
206                                                                 alphabyte = 255;
207                                                                 break;
208                                                 case 32:
209                                                                 blue = *buf_p++;
210                                                                 green = *buf_p++;
211                                                                 red = *buf_p++;
212                                                                 alphabyte = *buf_p++;
213                                                                 break;
214                                                 default:
215                                                         //Error("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
216                                                         break;
217                                         }
218         
219                                         for(j=0;j<packetSize;j++) {
220                                                 *pixbuf++=red;
221                                                 *pixbuf++=green;
222                                                 *pixbuf++=blue;
223                                                 *pixbuf++=alphabyte;
224                                                 column++;
225                                                 if (column==columns) { // run spans across rows
226                                                         column=0;
227                                                         if (row>0)
228                                                                 row--;
229                                                         else
230                                                                 goto breakOut;
231                                                         pixbuf = targa_rgba + row*columns*4;
232                                                 }
233                                         }
234                                 }
235                                 else {                            // non run-length packet
236                                         for(j=0;j<packetSize;j++) {
237                                                 switch (targa_header.pixel_size) {
238                                                         case 24:
239                                                                         blue = *buf_p++;
240                                                                         green = *buf_p++;
241                                                                         red = *buf_p++;
242                                                                         *pixbuf++ = red;
243                                                                         *pixbuf++ = green;
244                                                                         *pixbuf++ = blue;
245                                                                         *pixbuf++ = 255;
246                                                                         break;
247                                                         case 32:
248                                                                         blue = *buf_p++;
249                                                                         green = *buf_p++;
250                                                                         red = *buf_p++;
251                                                                         alphabyte = *buf_p++;
252                                                                         *pixbuf++ = red;
253                                                                         *pixbuf++ = green;
254                                                                         *pixbuf++ = blue;
255                                                                         *pixbuf++ = alphabyte;
256                                                                         break;
257                                                         default:
258                                                                 //Sysprintf("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
259                                                                 break;
260                                                 }
261                                                 column++;
262                                                 if (column==columns) { // pixel packet run spans across rows
263                                                         column=0;
264                                                         if (row>0)
265                                                                 row--;
266                                                         else
267                                                                 goto breakOut;
268                                                         pixbuf = targa_rgba + row*columns*4;
269                                                 }                                               
270                                         }
271                                 }
272                         }
273                         breakOut:;
274                 }
275         }
276
277         /* fix vertically flipped image */
278         if( (targa_header.attributes & (1<<5)) )
279         {
280                 int flip;
281                 for (row = 0; row < .5f * rows; row++)
282                 {
283                         for (column = 0; column < columns; column++)
284                         {
285                                 flip = *( (int*)targa_rgba + row * columns + column);
286                                 *( (int*)targa_rgba + row * columns + column) = *( (int*)targa_rgba + ( ( rows - 1 ) - row ) * columns + column );
287                                 *( (int*)targa_rgba + ( ( rows - 1 ) - row ) * columns + column ) = flip;
288                         }
289                 }
290         }
291 }
292
293
294
295 /*
296 _terrain_canload()
297 validates a picoterrain file
298 */
299
300 static int _terrain_canload( PM_PARAMS_CANLOAD )
301 {
302         picoParser_t    *p;
303         
304         
305         /* create pico parser */
306         p = _pico_new_parser( (const picoByte_t*) buffer, bufSize );
307         if( p == NULL )
308                 return PICO_PMV_ERROR_MEMORY;
309         
310         /* get first token */
311         if( _pico_parse_first( p ) == NULL)
312                 return PICO_PMV_ERROR_IDENT;
313         
314         /* check first token */
315         if( _pico_stricmp( p->token, "picoterrain" ) )
316         {
317                 _pico_free_parser( p );
318                 return PICO_PMV_ERROR_IDENT;
319         }
320         
321         /* free the pico parser object */
322         _pico_free_parser( p );
323         
324         /* file seems to be a valid picoterrain file */
325         return PICO_PMV_OK;
326 }
327
328
329
330 /*
331 _terrain_load()
332 loads a picoterrain file
333 */
334
335 static picoModel_t *_terrain_load( PM_PARAMS_LOAD )
336 {
337         int                             i, j, v, pw[ 5 ], r;
338         picoParser_t    *p;
339         
340         char                    *shader, *heightmapFile, *colormapFile;
341         picoVec3_t              scale, origin;
342         
343         unsigned char   *imageBuffer;
344         int                             imageBufSize, w, h, cw, ch;
345         unsigned char   *heightmap, *colormap, *heightPixel, *colorPixel;
346         
347         picoModel_t             *picoModel;
348         picoSurface_t   *picoSurface;
349         picoShader_t    *picoShader;
350         picoVec3_t              xyz, normal;
351         picoVec2_t              st;
352         picoColor_t             color;
353         
354         
355         /* create pico parser */
356         p = _pico_new_parser( (const picoByte_t*) buffer, bufSize );
357         if( p == NULL )
358                 return NULL;
359         
360         /* get first token */
361         if( _pico_parse_first( p ) == NULL)
362                 return NULL;
363         
364         /* check first token */
365         if( _pico_stricmp( p->token, "picoterrain" ) )
366         {
367                 _pico_printf( PICO_ERROR, "Invalid PicoTerrain model" );
368                 _pico_free_parser( p );
369                 return NULL;
370         }
371         
372         /* setup */
373         shader = heightmapFile = colormapFile = NULL;
374         _pico_set_vec( scale, 512, 512, 32 );
375         
376         /* parse ase model file */
377         while( 1 )
378         {
379                 /* get first token on line */
380                 if( !_pico_parse_first( p ) )
381                         break;
382                 
383                 /* skip empty lines */
384                 if( !p->token || !p->token[ 0 ] )
385                         continue;
386                 
387                 /* shader */
388                 if( !_pico_stricmp( p->token, "shader" ) )
389                 {
390                         if( _pico_parse( p, 0 ) && p->token[ 0 ] )
391                         {
392                                 if( shader != NULL )
393                                         _pico_free( shader );
394                                 shader = _pico_clone_alloc( p->token );
395                         }
396                 }
397                 
398                 /* heightmap */
399                 else if( !_pico_stricmp( p->token, "heightmap" ) )
400                 {
401                         if( _pico_parse( p, 0 ) && p->token[ 0 ] )
402                         {
403                                 if( heightmapFile != NULL )
404                                         _pico_free( heightmapFile );
405                                 heightmapFile = _pico_clone_alloc( p->token );
406                         }
407                 }
408                 
409                 /* colormap */
410                 else if( !_pico_stricmp( p->token, "colormap" ) )
411                 {
412                         if( _pico_parse( p, 0 ) && p->token[ 0 ] )
413                         {
414                                 if( colormapFile != NULL )
415                                         _pico_free( colormapFile );
416                                 colormapFile = _pico_clone_alloc( p->token );
417                         }
418                 }
419                 
420                 /* scale */
421                 else if( !_pico_stricmp( p->token, "scale" ) )
422                 {
423                         _pico_parse_vec( p, scale );
424                 }
425                 
426                 /* skip unparsed rest of line and continue */
427                 _pico_parse_skip_rest( p );
428         }
429         
430         /* ----------------------------------------------------------------- */
431         
432         /* load heightmap */
433         heightmap = imageBuffer = NULL;
434         _pico_load_file( heightmapFile, &imageBuffer, &imageBufSize );
435         _terrain_load_tga_buffer( imageBuffer, &heightmap, &w, &h );
436         _pico_free( heightmapFile );
437         _pico_free_file( imageBuffer );
438         
439         if( heightmap == NULL || w < 2 || h < 2 )
440         {
441                 _pico_printf( PICO_ERROR, "PicoTerrain model with invalid heightmap" );
442                 if( shader != NULL )
443                         _pico_free( shader );
444                 if( colormapFile != NULL )
445                         _pico_free( colormapFile );
446                 _pico_free_parser( p );
447                 return NULL;
448         }
449         
450         /* set origin (bottom lowest corner of terrain mesh) */
451         _pico_set_vec( origin, (w / -2) * scale[ 0 ], (h / -2) * scale[ 1 ], -128 * scale[ 2 ] );
452         
453         /* load colormap */
454         colormap = imageBuffer = NULL;
455         _pico_load_file( colormapFile, &imageBuffer, &imageBufSize );
456         _terrain_load_tga_buffer( imageBuffer, &colormap, &cw, &ch );
457         _pico_free( colormapFile );
458         _pico_free_file( imageBuffer );
459         
460         if( cw != w || ch != h )
461         {
462                 _pico_printf( PICO_WARNING, "PicoTerrain colormap/heightmap size mismatch" );
463                 _pico_free( colormap );
464                 colormap = NULL;
465         }
466         
467         /* ----------------------------------------------------------------- */
468         
469         /* create new pico model */
470         picoModel = PicoNewModel();
471         if( picoModel == NULL )
472         {
473                 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
474                 return NULL;
475         }
476         
477         /* do model setup */
478         PicoSetModelFrameNum( picoModel, frameNum );
479         PicoSetModelNumFrames( picoModel, 1 ); /* sea */
480         PicoSetModelName( picoModel, fileName );
481         PicoSetModelFileName( picoModel, fileName );
482         
483         /* allocate new pico surface */
484         picoSurface = PicoNewSurface( picoModel );
485         if( picoSurface == NULL )
486         {
487                 _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
488                 PicoFreeModel( picoModel ); /* sea */
489                 return NULL;
490         }
491         
492         /* terrain surfaces are triangle meshes */
493         PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
494                 
495         /* set surface name */
496         PicoSetSurfaceName( picoSurface, "picoterrain" );
497                 
498         /* create new pico shader */
499         picoShader = PicoNewShader( picoModel );
500         if( picoShader == NULL )
501         {
502                 _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
503                 PicoFreeModel( picoModel );
504                 _pico_free( shader );
505                 return NULL;
506         }
507         
508         /* detox and set shader name */
509         _pico_setfext( shader, "" );
510         _pico_unixify( shader );
511         PicoSetShaderName( picoShader, shader );
512         _pico_free( shader );
513         
514         /* associate current surface with newly created shader */
515         PicoSetSurfaceShader( picoSurface, picoShader );
516         
517         /* make bogus normal */
518         _pico_set_vec( normal, 0.0f, 0.0f, 0.0f );
519         
520         /* create mesh */
521         for( j = 0; j < h; j++ )
522         {
523                 for( i = 0; i < w; i++ )
524                 {
525                         /* get pointers */
526                         v = i + (j * w);
527                         heightPixel = heightmap + v * 4;
528                         colorPixel = colormap
529                                 ? colormap + v * 4
530                                 : NULL;
531                         
532                         /* set xyz */
533                         _pico_set_vec( xyz, origin[ 0 ] + scale[ 0 ] * i,
534                                                                 origin[ 1 ] + scale[ 1 ] * j,
535                                                                 origin[ 2 ] + scale[ 2 ] * heightPixel[ 0 ] );
536                         PicoSetSurfaceXYZ( picoSurface, v, xyz );
537                         
538                         /* set normal */
539                         PicoSetSurfaceNormal( picoSurface, v, normal );
540                         
541                         /* set st */
542                         st[ 0 ] = (float) i;
543                         st[ 1 ] = (float) j;
544                         PicoSetSurfaceST( picoSurface, 0, v, st );
545                         
546                         /* set color */
547                         if( colorPixel != NULL )
548                                 _pico_set_color( color, colorPixel[ 0 ], colorPixel[ 1 ], colorPixel[ 2 ], colorPixel[ 3 ] );
549                         else
550                                 _pico_set_color( color, 255, 255, 255, 255 );
551                         PicoSetSurfaceColor( picoSurface, 0, v, color );
552                         
553                         /* set triangles (zero alpha in heightmap suppresses this quad) */
554                         if( i < (w - 1) && j < (h - 1) && heightPixel[ 3 ] >= 128 )
555                         {
556                                 /* set indexes */
557                                 pw[ 0 ] = i + (j * w);
558                                 pw[ 1 ] = i + ((j + 1) * w);
559                                 pw[ 2 ] = i + 1 + ((j + 1) * w);
560                                 pw[ 3 ] = i + 1 + (j * w);
561                                 pw[ 4 ] = i + (j * w);  /* same as pw[ 0 ] */
562                                 
563                                 /* set radix */
564                                 r = (i + j) & 1;
565                                 
566                                 /* make first triangle */
567                                 PicoSetSurfaceIndex( picoSurface, (v * 6 + 0), (picoIndex_t) pw[ r + 0 ] );
568                                 PicoSetSurfaceIndex( picoSurface, (v * 6 + 1), (picoIndex_t) pw[ r + 1 ] );
569                                 PicoSetSurfaceIndex( picoSurface, (v * 6 + 2), (picoIndex_t) pw[ r + 2 ] );
570                                 
571                                 /* make second triangle */
572                                 PicoSetSurfaceIndex( picoSurface, (v * 6 + 3), (picoIndex_t) pw[ r + 0 ] );
573                                 PicoSetSurfaceIndex( picoSurface, (v * 6 + 4), (picoIndex_t) pw[ r + 2 ] );
574                                 PicoSetSurfaceIndex( picoSurface, (v * 6 + 5), (picoIndex_t) pw[ r + 3 ] );
575                         }
576                 }
577         }
578         
579         /* free stuff */
580         _pico_free_parser( p );
581         _pico_free( heightmap );
582         _pico_free( colormap );
583         
584         /* return the new pico model */
585         return picoModel;
586 }
587
588
589
590 /* pico file format module definition */
591 const picoModule_t picoModuleTerrain =
592 {
593         "1.3",                                          /* module version string */
594         "PicoTerrain",                          /* module display name */
595         "Randy Reddig",                         /* author's name */
596         "2003 Randy Reddig",            /* module copyright */
597         {
598                 "picoterrain", NULL, NULL, NULL /* default extensions to use */
599         },
600         _terrain_canload,                       /* validation routine */
601         _terrain_load,                          /* load routine */
602          NULL,                                          /* save validation routine */
603          NULL                                           /* save routine */
604 };