]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_terrain.c
initial
[xonotic/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         /* keep the friggin compiler happy */
306         *fileName = *fileName;
307         
308         /* create pico parser */
309         p = _pico_new_parser( (picoByte_t*) buffer, bufSize );
310         if( p == NULL )
311                 return PICO_PMV_ERROR_MEMORY;
312         
313         /* get first token */
314         if( _pico_parse_first( p ) == NULL)
315                 return PICO_PMV_ERROR_IDENT;
316         
317         /* check first token */
318         if( _pico_stricmp( p->token, "picoterrain" ) )
319         {
320                 _pico_free_parser( p );
321                 return PICO_PMV_ERROR_IDENT;
322         }
323         
324         /* free the pico parser object */
325         _pico_free_parser( p );
326         
327         /* file seems to be a valid picoterrain file */
328         return PICO_PMV_OK;
329 }
330
331
332
333 /*
334 _terrain_load()
335 loads a picoterrain file
336 */
337
338 static picoModel_t *_terrain_load( PM_PARAMS_LOAD )
339 {
340         int                             i, j, v, pw[ 5 ], r;
341         picoParser_t    *p;
342         
343         char                    *shader, *heightmapFile, *colormapFile;
344         picoVec3_t              scale, origin;
345         
346         unsigned char   *imageBuffer;
347         int                             imageBufSize, w, h, cw, ch;
348         unsigned char   *heightmap, *colormap, *heightPixel, *colorPixel;
349         
350         picoModel_t             *picoModel;
351         picoSurface_t   *picoSurface;
352         picoShader_t    *picoShader;
353         picoVec3_t              xyz, normal;
354         picoVec2_t              st;
355         picoColor_t             color;
356         
357         
358         /* keep the friggin compiler happy */
359         *fileName = *fileName;
360         
361         /* create pico parser */
362         p = _pico_new_parser( (picoByte_t*) buffer, bufSize );
363         if( p == NULL )
364                 return NULL;
365         
366         /* get first token */
367         if( _pico_parse_first( p ) == NULL)
368                 return NULL;
369         
370         /* check first token */
371         if( _pico_stricmp( p->token, "picoterrain" ) )
372         {
373                 _pico_printf( PICO_ERROR, "Invalid PicoTerrain model" );
374                 _pico_free_parser( p );
375                 return NULL;
376         }
377         
378         /* setup */
379         shader = heightmapFile = colormapFile = NULL;
380         _pico_set_vec( scale, 512, 512, 32 );
381         
382         /* parse ase model file */
383         while( 1 )
384         {
385                 /* get first token on line */
386                 if( !_pico_parse_first( p ) )
387                         break;
388                 
389                 /* skip empty lines */
390                 if( !p->token || !p->token[ 0 ] )
391                         continue;
392                 
393                 /* shader */
394                 if( !_pico_stricmp( p->token, "shader" ) )
395                 {
396                         if( _pico_parse( p, 0 ) && p->token[ 0 ] )
397                         {
398                                 if( shader != NULL )
399                                         _pico_free( shader );
400                                 shader = _pico_clone_alloc( p->token );
401                         }
402                 }
403                 
404                 /* heightmap */
405                 else if( !_pico_stricmp( p->token, "heightmap" ) )
406                 {
407                         if( _pico_parse( p, 0 ) && p->token[ 0 ] )
408                         {
409                                 if( heightmapFile != NULL )
410                                         _pico_free( heightmapFile );
411                                 heightmapFile = _pico_clone_alloc( p->token );
412                         }
413                 }
414                 
415                 /* colormap */
416                 else if( !_pico_stricmp( p->token, "colormap" ) )
417                 {
418                         if( _pico_parse( p, 0 ) && p->token[ 0 ] )
419                         {
420                                 if( colormapFile != NULL )
421                                         _pico_free( colormapFile );
422                                 colormapFile = _pico_clone_alloc( p->token );
423                         }
424                 }
425                 
426                 /* scale */
427                 else if( !_pico_stricmp( p->token, "scale" ) )
428                 {
429                         _pico_parse_vec( p, scale );
430                 }
431                 
432                 /* skip unparsed rest of line and continue */
433                 _pico_parse_skip_rest( p );
434         }
435         
436         /* ----------------------------------------------------------------- */
437         
438         /* load heightmap */
439         heightmap = imageBuffer = NULL;
440         _pico_load_file( heightmapFile, &imageBuffer, &imageBufSize );
441         _terrain_load_tga_buffer( imageBuffer, &heightmap, &w, &h );
442         _pico_free( heightmapFile );
443         _pico_free_file( imageBuffer );
444         
445         if( heightmap == NULL || w < 2 || h < 2 )
446         {
447                 _pico_printf( PICO_ERROR, "PicoTerrain model with invalid heightmap" );
448                 if( shader != NULL )
449                         _pico_free( shader );
450                 if( colormapFile != NULL )
451                         _pico_free( colormapFile );
452                 _pico_free_parser( p );
453                 return NULL;
454         }
455         
456         /* set origin (bottom lowest corner of terrain mesh) */
457         _pico_set_vec( origin, (w / -2) * scale[ 0 ], (h / -2) * scale[ 1 ], -128 * scale[ 2 ] );
458         
459         /* load colormap */
460         colormap = imageBuffer = NULL;
461         _pico_load_file( colormapFile, &imageBuffer, &imageBufSize );
462         _terrain_load_tga_buffer( imageBuffer, &colormap, &cw, &ch );
463         _pico_free( colormapFile );
464         _pico_free_file( imageBuffer );
465         
466         if( cw != w || ch != h )
467         {
468                 _pico_printf( PICO_WARNING, "PicoTerrain colormap/heightmap size mismatch" );
469                 _pico_free( colormap );
470                 colormap = NULL;
471         }
472         
473         /* ----------------------------------------------------------------- */
474         
475         /* create new pico model */
476         picoModel = PicoNewModel();
477         if( picoModel == NULL )
478         {
479                 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
480                 return NULL;
481         }
482         
483         /* do model setup */
484         PicoSetModelFrameNum( picoModel, frameNum );
485         PicoSetModelNumFrames( picoModel, 1 ); /* sea */
486         PicoSetModelName( picoModel, fileName );
487         PicoSetModelFileName( picoModel, fileName );
488         
489         /* allocate new pico surface */
490         picoSurface = PicoNewSurface( picoModel );
491         if( picoSurface == NULL )
492         {
493                 _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
494                 PicoFreeModel( picoModel ); /* sea */
495                 return NULL;
496         }
497         
498         /* terrain surfaces are triangle meshes */
499         PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
500                 
501         /* set surface name */
502         PicoSetSurfaceName( picoSurface, "picoterrain" );
503                 
504         /* create new pico shader */
505         picoShader = PicoNewShader( picoModel );
506         if( picoShader == NULL )
507         {
508                 _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
509                 PicoFreeModel( picoModel );
510                 _pico_free( shader );
511                 return NULL;
512         }
513         
514         /* detox and set shader name */
515         _pico_setfext( shader, "" );
516         _pico_unixify( shader );
517         PicoSetShaderName( picoShader, shader );
518         _pico_free( shader );
519         
520         /* associate current surface with newly created shader */
521         PicoSetSurfaceShader( picoSurface, picoShader );
522         
523         /* make bogus normal */
524         _pico_set_vec( normal, 0.0f, 0.0f, 0.0f );
525         
526         /* create mesh */
527         for( j = 0; j < h; j++ )
528         {
529                 for( i = 0; i < w; i++ )
530                 {
531                         /* get pointers */
532                         v = i + (j * w);
533                         heightPixel = heightmap + v * 4;
534                         colorPixel = colormap
535                                 ? colormap + v * 4
536                                 : NULL;
537                         
538                         /* set xyz */
539                         _pico_set_vec( xyz, origin[ 0 ] + scale[ 0 ] * i,
540                                                                 origin[ 1 ] + scale[ 1 ] * j,
541                                                                 origin[ 2 ] + scale[ 2 ] * heightPixel[ 0 ] );
542                         PicoSetSurfaceXYZ( picoSurface, v, xyz );
543                         
544                         /* set normal */
545                         PicoSetSurfaceNormal( picoSurface, v, normal );
546                         
547                         /* set st */
548                         st[ 0 ] = (float) i;
549                         st[ 1 ] = (float) j;
550                         PicoSetSurfaceST( picoSurface, 0, v, st );
551                         
552                         /* set color */
553                         if( colorPixel != NULL )
554                                 _pico_set_color( color, colorPixel[ 0 ], colorPixel[ 1 ], colorPixel[ 2 ], colorPixel[ 3 ] );
555                         else
556                                 _pico_set_color( color, 255, 255, 255, 255 );
557                         PicoSetSurfaceColor( picoSurface, 0, v, color );
558                         
559                         /* set triangles (zero alpha in heightmap suppresses this quad) */
560                         if( i < (w - 1) && j < (h - 1) && heightPixel[ 3 ] >= 128 )
561                         {
562                                 /* set indexes */
563                                 pw[ 0 ] = i + (j * w);
564                                 pw[ 1 ] = i + ((j + 1) * w);
565                                 pw[ 2 ] = i + 1 + ((j + 1) * w);
566                                 pw[ 3 ] = i + 1 + (j * w);
567                                 pw[ 4 ] = i + (j * w);  /* same as pw[ 0 ] */
568                                 
569                                 /* set radix */
570                                 r = (i + j) & 1;
571                                 
572                                 /* make first triangle */
573                                 PicoSetSurfaceIndex( picoSurface, (v * 6 + 0), (picoIndex_t) pw[ r + 0 ] );
574                                 PicoSetSurfaceIndex( picoSurface, (v * 6 + 1), (picoIndex_t) pw[ r + 1 ] );
575                                 PicoSetSurfaceIndex( picoSurface, (v * 6 + 2), (picoIndex_t) pw[ r + 2 ] );
576                                 
577                                 /* make second triangle */
578                                 PicoSetSurfaceIndex( picoSurface, (v * 6 + 3), (picoIndex_t) pw[ r + 0 ] );
579                                 PicoSetSurfaceIndex( picoSurface, (v * 6 + 4), (picoIndex_t) pw[ r + 2 ] );
580                                 PicoSetSurfaceIndex( picoSurface, (v * 6 + 5), (picoIndex_t) pw[ r + 3 ] );
581                         }
582                 }
583         }
584         
585         /* free stuff */
586         _pico_free_parser( p );
587         _pico_free( heightmap );
588         _pico_free( colormap );
589         
590         /* return the new pico model */
591         return picoModel;
592 }
593
594
595
596 /* pico file format module definition */
597 const picoModule_t picoModuleTerrain =
598 {
599         "1.3",                                          /* module version string */
600         "PicoTerrain",                          /* module display name */
601         "Randy Reddig",                         /* author's name */
602         "2003 Randy Reddig",            /* module copyright */
603         {
604                 "picoterrain", NULL, NULL, NULL /* default extensions to use */
605         },
606         _terrain_canload,                       /* validation routine */
607         _terrain_load,                          /* load routine */
608          NULL,                                          /* save validation routine */
609          NULL                                           /* save routine */
610 };