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