]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_terrain.c
my own uncrustify run
[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         int row, column;
67         int columns, rows, numPixels;
68         unsigned char   *pixbuf;
69         unsigned char   *buf_p;
70         tga_t targa_header;
71         unsigned char   *targa_rgba;
72
73
74         *pic = NULL;
75
76         if ( buffer == NULL ) {
77                 return;
78         }
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                 _pico_printf( PICO_ERROR, "Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n" );
104                 pic = NULL;
105                 return;
106         }
107
108         if ( targa_header.colormap_type != 0 ) {
109                 _pico_printf( PICO_ERROR, "Indexed color TGA images not supported\n" );
110                 return;
111         }
112
113         if ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 && targa_header.image_type != 3 ) {
114                 _pico_printf( PICO_ERROR, "Only 32 or 24 bit TGA images supported (not indexed color)\n" );
115                 pic = NULL;
116                 return;
117         }
118
119         columns = targa_header.width;
120         rows = targa_header.height;
121         numPixels = columns * rows;
122
123         if ( width ) {
124                 *width = columns;
125         }
126         if ( height ) {
127                 *height = rows;
128         }
129
130         targa_rgba = _pico_alloc( numPixels * 4 );
131         *pic = targa_rgba;
132
133         if ( targa_header.id_length != 0 ) {
134                 buf_p += targa_header.id_length;  // skip TARGA image comment
135
136         }
137         if ( targa_header.image_type == 2 || targa_header.image_type == 3 ) {
138                 // Uncompressed RGB or gray scale image
139                 for ( row = rows - 1; row >= 0; row-- )
140                 {
141                         pixbuf = targa_rgba + row * columns * 4;
142                         for ( column = 0; column < columns; column++ )
143                         {
144                                 unsigned char red,green,blue,alphabyte;
145                                 switch ( targa_header.pixel_size )
146                                 {
147
148                                 case 8:
149                                         blue = *buf_p++;
150                                         green = blue;
151                                         red = blue;
152                                         *pixbuf++ = red;
153                                         *pixbuf++ = green;
154                                         *pixbuf++ = blue;
155                                         *pixbuf++ = 255;
156                                         break;
157
158                                 case 24:
159                                         blue = *buf_p++;
160                                         green = *buf_p++;
161                                         red = *buf_p++;
162                                         *pixbuf++ = red;
163                                         *pixbuf++ = green;
164                                         *pixbuf++ = blue;
165                                         *pixbuf++ = 255;
166                                         break;
167                                 case 32:
168                                         blue = *buf_p++;
169                                         green = *buf_p++;
170                                         red = *buf_p++;
171                                         alphabyte = *buf_p++;
172                                         *pixbuf++ = red;
173                                         *pixbuf++ = green;
174                                         *pixbuf++ = blue;
175                                         *pixbuf++ = alphabyte;
176                                         break;
177                                 default:
178                                         break;
179                                 }
180                         }
181                 }
182         }
183
184         /* rle encoded pixels */
185         else if ( targa_header.image_type == 10 ) {
186                 unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
187
188                 red = 0;
189                 green = 0;
190                 blue = 0;
191                 alphabyte = 0xff;
192
193                 for ( row = rows - 1; row >= 0; row-- ) {
194                         pixbuf = targa_rgba + row * columns * 4;
195                         for ( column = 0; column < columns; ) {
196                                 packetHeader = *buf_p++;
197                                 packetSize = 1 + ( packetHeader & 0x7f );
198                                 if ( packetHeader & 0x80 ) {        // run-length packet
199                                         switch ( targa_header.pixel_size ) {
200                                         case 24:
201                                                 blue = *buf_p++;
202                                                 green = *buf_p++;
203                                                 red = *buf_p++;
204                                                 alphabyte = 255;
205                                                 break;
206                                         case 32:
207                                                 blue = *buf_p++;
208                                                 green = *buf_p++;
209                                                 red = *buf_p++;
210                                                 alphabyte = *buf_p++;
211                                                 break;
212                                         default:
213                                                 //Error("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
214                                                 break;
215                                         }
216
217                                         for ( j = 0; j < packetSize; j++ ) {
218                                                 *pixbuf++ = red;
219                                                 *pixbuf++ = green;
220                                                 *pixbuf++ = blue;
221                                                 *pixbuf++ = alphabyte;
222                                                 column++;
223                                                 if ( column == columns ) { // run spans across rows
224                                                         column = 0;
225                                                         if ( row > 0 ) {
226                                                                 row--;
227                                                         }
228                                                         else{
229                                                                 goto breakOut;
230                                                         }
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                                                         }
267                                                         else{
268                                                                 goto breakOut;
269                                                         }
270                                                         pixbuf = targa_rgba + row * columns * 4;
271                                                 }
272                                         }
273                                 }
274                         }
275 breakOut:;
276                 }
277         }
278
279         /* fix vertically flipped image */
280         if ( ( targa_header.attributes & ( 1 << 5 ) ) ) {
281                 int flip;
282                 for ( row = 0; row < .5f * rows; row++ )
283                 {
284                         for ( column = 0; column < columns; column++ )
285                         {
286                                 flip = *( (int*)targa_rgba + row * columns + column );
287                                 *( (int*)targa_rgba + row * columns + column ) = *( (int*)targa_rgba + ( ( rows - 1 ) - row ) * columns + column );
288                                 *( (int*)targa_rgba + ( ( rows - 1 ) - row ) * columns + column ) = flip;
289                         }
290                 }
291         }
292 }
293
294
295
296 /*
297    _terrain_canload()
298    validates a picoterrain file
299  */
300
301 static int _terrain_canload( PM_PARAMS_CANLOAD ){
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
311         /* get first token */
312         if ( _pico_parse_first( p ) == NULL ) {
313                 return PICO_PMV_ERROR_IDENT;
314         }
315
316         /* check first token */
317         if ( _pico_stricmp( p->token, "picoterrain" ) ) {
318                 _pico_free_parser( p );
319                 return PICO_PMV_ERROR_IDENT;
320         }
321
322         /* free the pico parser object */
323         _pico_free_parser( p );
324
325         /* file seems to be a valid picoterrain file */
326         return PICO_PMV_OK;
327 }
328
329
330
331 /*
332    _terrain_load()
333    loads a picoterrain file
334  */
335
336 static picoModel_t *_terrain_load( PM_PARAMS_LOAD ){
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
361         /* get first token */
362         if ( _pico_parse_first( p ) == NULL ) {
363                 return NULL;
364         }
365
366         /* check first token */
367         if ( _pico_stricmp( p->token, "picoterrain" ) ) {
368                 _pico_printf( PICO_ERROR, "Invalid PicoTerrain model" );
369                 _pico_free_parser( p );
370                 return NULL;
371         }
372
373         /* setup */
374         shader = heightmapFile = colormapFile = NULL;
375         _pico_set_vec( scale, 512, 512, 32 );
376
377         /* parse ase model file */
378         while ( 1 )
379         {
380                 /* get first token on line */
381                 if ( !_pico_parse_first( p ) ) {
382                         break;
383                 }
384
385                 /* skip empty lines */
386                 if ( !p->token || !p->token[ 0 ] ) {
387                         continue;
388                 }
389
390                 /* shader */
391                 if ( !_pico_stricmp( p->token, "shader" ) ) {
392                         if ( _pico_parse( p, 0 ) && p->token[ 0 ] ) {
393                                 if ( shader != NULL ) {
394                                         _pico_free( shader );
395                                 }
396                                 shader = _pico_clone_alloc( p->token );
397                         }
398                 }
399
400                 /* heightmap */
401                 else if ( !_pico_stricmp( p->token, "heightmap" ) ) {
402                         if ( _pico_parse( p, 0 ) && p->token[ 0 ] ) {
403                                 if ( heightmapFile != NULL ) {
404                                         _pico_free( heightmapFile );
405                                 }
406                                 heightmapFile = _pico_clone_alloc( p->token );
407                         }
408                 }
409
410                 /* colormap */
411                 else if ( !_pico_stricmp( p->token, "colormap" ) ) {
412                         if ( _pico_parse( p, 0 ) && p->token[ 0 ] ) {
413                                 if ( colormapFile != NULL ) {
414                                         _pico_free( colormapFile );
415                                 }
416                                 colormapFile = _pico_clone_alloc( p->token );
417                         }
418                 }
419
420                 /* scale */
421                 else if ( !_pico_stricmp( p->token, "scale" ) ) {
422                         _pico_parse_vec( p, scale );
423                 }
424
425                 /* skip unparsed rest of line and continue */
426                 _pico_parse_skip_rest( p );
427         }
428
429         /* ----------------------------------------------------------------- */
430
431         /* load heightmap */
432         heightmap = imageBuffer = NULL;
433         _pico_load_file( heightmapFile, &imageBuffer, &imageBufSize );
434         _terrain_load_tga_buffer( imageBuffer, &heightmap, &w, &h );
435         _pico_free( heightmapFile );
436         _pico_free_file( imageBuffer );
437
438         if ( heightmap == NULL || w < 2 || h < 2 ) {
439                 _pico_printf( PICO_ERROR, "PicoTerrain model with invalid heightmap" );
440                 if ( shader != NULL ) {
441                         _pico_free( shader );
442                 }
443                 if ( colormapFile != NULL ) {
444                         _pico_free( colormapFile );
445                 }
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                 _pico_printf( PICO_WARNING, "PicoTerrain colormap/heightmap size mismatch" );
462                 _pico_free( colormap );
463                 colormap = NULL;
464         }
465
466         /* ----------------------------------------------------------------- */
467
468         /* create new pico model */
469         picoModel = PicoNewModel();
470         if ( picoModel == NULL ) {
471                 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
472                 return NULL;
473         }
474
475         /* do model setup */
476         PicoSetModelFrameNum( picoModel, frameNum );
477         PicoSetModelNumFrames( picoModel, 1 ); /* sea */
478         PicoSetModelName( picoModel, fileName );
479         PicoSetModelFileName( picoModel, fileName );
480
481         /* allocate new pico surface */
482         picoSurface = PicoNewSurface( picoModel );
483         if ( picoSurface == NULL ) {
484                 _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
485                 PicoFreeModel( picoModel ); /* sea */
486                 return NULL;
487         }
488
489         /* terrain surfaces are triangle meshes */
490         PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
491
492         /* set surface name */
493         PicoSetSurfaceName( picoSurface, "picoterrain" );
494
495         /* create new pico shader */
496         picoShader = PicoNewShader( picoModel );
497         if ( picoShader == NULL ) {
498                 _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
499                 PicoFreeModel( picoModel );
500                 _pico_free( shader );
501                 return NULL;
502         }
503
504         /* detox and set shader name */
505         _pico_setfext( shader, "" );
506         _pico_unixify( shader );
507         PicoSetShaderName( picoShader, shader );
508         _pico_free( shader );
509
510         /* associate current surface with newly created shader */
511         PicoSetSurfaceShader( picoSurface, picoShader );
512
513         /* make bogus normal */
514         _pico_set_vec( normal, 0.0f, 0.0f, 0.0f );
515
516         /* create mesh */
517         for ( j = 0; j < h; j++ )
518         {
519                 for ( i = 0; i < w; i++ )
520                 {
521                         /* get pointers */
522                         v = i + ( j * w );
523                         heightPixel = heightmap + v * 4;
524                         colorPixel = colormap
525                                                  ? colormap + v * 4
526                                                  : NULL;
527
528                         /* set xyz */
529                         _pico_set_vec( xyz, origin[ 0 ] + scale[ 0 ] * i,
530                                                    origin[ 1 ] + scale[ 1 ] * j,
531                                                    origin[ 2 ] + scale[ 2 ] * heightPixel[ 0 ] );
532                         PicoSetSurfaceXYZ( picoSurface, v, xyz );
533
534                         /* set normal */
535                         PicoSetSurfaceNormal( picoSurface, v, normal );
536
537                         /* set st */
538                         st[ 0 ] = (float) i;
539                         st[ 1 ] = (float) j;
540                         PicoSetSurfaceST( picoSurface, 0, v, st );
541
542                         /* set color */
543                         if ( colorPixel != NULL ) {
544                                 _pico_set_color( color, colorPixel[ 0 ], colorPixel[ 1 ], colorPixel[ 2 ], colorPixel[ 3 ] );
545                         }
546                         else{
547                                 _pico_set_color( color, 255, 255, 255, 255 );
548                         }
549                         PicoSetSurfaceColor( picoSurface, 0, v, color );
550
551                         /* set triangles (zero alpha in heightmap suppresses this quad) */
552                         if ( i < ( w - 1 ) && j < ( h - 1 ) && heightPixel[ 3 ] >= 128 ) {
553                                 /* set indexes */
554                                 pw[ 0 ] = i + ( j * w );
555                                 pw[ 1 ] = i + ( ( j + 1 ) * w );
556                                 pw[ 2 ] = i + 1 + ( ( j + 1 ) * w );
557                                 pw[ 3 ] = i + 1 + ( j * w );
558                                 pw[ 4 ] = i + ( j * w );  /* same as pw[ 0 ] */
559
560                                 /* set radix */
561                                 r = ( i + j ) & 1;
562
563                                 /* make first triangle */
564                                 PicoSetSurfaceIndex( picoSurface, ( v * 6 + 0 ), (picoIndex_t) pw[ r + 0 ] );
565                                 PicoSetSurfaceIndex( picoSurface, ( v * 6 + 1 ), (picoIndex_t) pw[ r + 1 ] );
566                                 PicoSetSurfaceIndex( picoSurface, ( v * 6 + 2 ), (picoIndex_t) pw[ r + 2 ] );
567
568                                 /* make second triangle */
569                                 PicoSetSurfaceIndex( picoSurface, ( v * 6 + 3 ), (picoIndex_t) pw[ r + 0 ] );
570                                 PicoSetSurfaceIndex( picoSurface, ( v * 6 + 4 ), (picoIndex_t) pw[ r + 2 ] );
571                                 PicoSetSurfaceIndex( picoSurface, ( v * 6 + 5 ), (picoIndex_t) pw[ r + 3 ] );
572                         }
573                 }
574         }
575
576         /* free stuff */
577         _pico_free_parser( p );
578         _pico_free( heightmap );
579         _pico_free( colormap );
580
581         /* return the new pico model */
582         return picoModel;
583 }
584
585
586
587 /* pico file format module definition */
588 const picoModule_t picoModuleTerrain =
589 {
590         "1.3",                      /* module version string */
591         "PicoTerrain",              /* module display name */
592         "Randy Reddig",             /* author's name */
593         "2003 Randy Reddig",        /* module copyright */
594         {
595                 "picoterrain", NULL, NULL, NULL /* default extensions to use */
596         },
597         _terrain_canload,           /* validation routine */
598         _terrain_load,              /* load routine */
599         NULL,                       /* save validation routine */
600         NULL                        /* save routine */
601 };