1 /* -----------------------------------------------------------------------------
5 Copyright (c) 2003, Randy Reddig & seaw0lf
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
11 Redistributions of source code must retain the above copyright notice, this list
12 of conditions and the following disclaimer.
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.
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.
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.
33 ----------------------------------------------------------------------------- */
43 #include "picointernal.h"
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;
60 _terrain_load_tga_buffer()
61 loads a tga image into a newly allocated image buffer
62 fixme: replace/clean this function
65 void _terrain_load_tga_buffer( unsigned char *buffer, unsigned char **pic, int *width, int *height )
68 int columns, rows, numPixels;
69 unsigned char *pixbuf;
72 unsigned char *targa_rgba;
82 targa_header.id_length = *buf_p++;
83 targa_header.colormap_type = *buf_p++;
84 targa_header.image_type = *buf_p++;
86 targa_header.colormap_index = _pico_little_short ( *(short*)buf_p );
88 targa_header.colormap_length = _pico_little_short ( *(short*) buf_p );
90 targa_header.colormap_size = *buf_p++;
91 targa_header.x_origin = _pico_little_short ( *(short*) buf_p );
93 targa_header.y_origin = _pico_little_short ( *(short*) buf_p );
95 targa_header.width = _pico_little_short ( *(short*) buf_p );
97 targa_header.height = _pico_little_short ( *(short*) buf_p );
99 targa_header.pixel_size = *buf_p++;
100 targa_header.attributes = *buf_p++;
102 if( targa_header.image_type != 2 && targa_header.image_type != 10 && targa_header.image_type != 3 )
104 _pico_printf( PICO_ERROR, "Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n");
109 if( targa_header.colormap_type != 0 )
111 _pico_printf( PICO_ERROR, "Indexed color TGA images not supported\n" );
115 if( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 && targa_header.image_type != 3 )
117 _pico_printf( PICO_ERROR, "Only 32 or 24 bit TGA images supported (not indexed color)\n");
122 columns = targa_header.width;
123 rows = targa_header.height;
124 numPixels = columns * rows;
131 targa_rgba = _pico_alloc( numPixels * 4 );
134 if (targa_header.id_length != 0)
135 buf_p += targa_header.id_length; // skip TARGA image comment
137 if ( targa_header.image_type==2 || targa_header.image_type == 3 )
139 // Uncompressed RGB or gray scale image
140 for(row=rows-1; row>=0; row--)
142 pixbuf = targa_rgba + row*columns*4;
143 for(column=0; column<columns; column++)
145 unsigned char red,green,blue,alphabyte;
146 switch (targa_header.pixel_size)
172 alphabyte = *buf_p++;
176 *pixbuf++ = alphabyte;
185 /* rle encoded pixels */
186 else if( targa_header.image_type == 10 )
188 unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
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) {
212 alphabyte = *buf_p++;
215 //Error("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
219 for(j=0;j<packetSize;j++) {
225 if (column==columns) { // run spans across rows
231 pixbuf = targa_rgba + row*columns*4;
235 else { // non run-length packet
236 for(j=0;j<packetSize;j++) {
237 switch (targa_header.pixel_size) {
251 alphabyte = *buf_p++;
255 *pixbuf++ = alphabyte;
258 //Sysprintf("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
262 if (column==columns) { // pixel packet run spans across rows
268 pixbuf = targa_rgba + row*columns*4;
277 /* fix vertically flipped image */
278 if( (targa_header.attributes & (1<<5)) )
281 for (row = 0; row < .5f * rows; row++)
283 for (column = 0; column < columns; column++)
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;
297 validates a picoterrain file
300 static int _terrain_canload( PM_PARAMS_CANLOAD )
305 /* create pico parser */
306 p = _pico_new_parser( (const picoByte_t*) buffer, bufSize );
308 return PICO_PMV_ERROR_MEMORY;
310 /* get first token */
311 if( _pico_parse_first( p ) == NULL)
312 return PICO_PMV_ERROR_IDENT;
314 /* check first token */
315 if( _pico_stricmp( p->token, "picoterrain" ) )
317 _pico_free_parser( p );
318 return PICO_PMV_ERROR_IDENT;
321 /* free the pico parser object */
322 _pico_free_parser( p );
324 /* file seems to be a valid picoterrain file */
332 loads a picoterrain file
335 static picoModel_t *_terrain_load( PM_PARAMS_LOAD )
337 int i, j, v, pw[ 5 ], r;
340 char *shader, *heightmapFile, *colormapFile;
341 picoVec3_t scale, origin;
343 unsigned char *imageBuffer;
344 int imageBufSize, w, h, cw, ch;
345 unsigned char *heightmap, *colormap, *heightPixel, *colorPixel;
347 picoModel_t *picoModel;
348 picoSurface_t *picoSurface;
349 picoShader_t *picoShader;
350 picoVec3_t xyz, normal;
355 /* create pico parser */
356 p = _pico_new_parser( (const picoByte_t*) buffer, bufSize );
360 /* get first token */
361 if( _pico_parse_first( p ) == NULL)
364 /* check first token */
365 if( _pico_stricmp( p->token, "picoterrain" ) )
367 _pico_printf( PICO_ERROR, "Invalid PicoTerrain model" );
368 _pico_free_parser( p );
373 shader = heightmapFile = colormapFile = NULL;
374 _pico_set_vec( scale, 512, 512, 32 );
376 /* parse ase model file */
379 /* get first token on line */
380 if( !_pico_parse_first( p ) )
383 /* skip empty lines */
384 if( !p->token || !p->token[ 0 ] )
388 if( !_pico_stricmp( p->token, "shader" ) )
390 if( _pico_parse( p, 0 ) && p->token[ 0 ] )
393 _pico_free( shader );
394 shader = _pico_clone_alloc( p->token );
399 else if( !_pico_stricmp( p->token, "heightmap" ) )
401 if( _pico_parse( p, 0 ) && p->token[ 0 ] )
403 if( heightmapFile != NULL )
404 _pico_free( heightmapFile );
405 heightmapFile = _pico_clone_alloc( p->token );
410 else if( !_pico_stricmp( p->token, "colormap" ) )
412 if( _pico_parse( p, 0 ) && p->token[ 0 ] )
414 if( colormapFile != NULL )
415 _pico_free( colormapFile );
416 colormapFile = _pico_clone_alloc( p->token );
421 else if( !_pico_stricmp( p->token, "scale" ) )
423 _pico_parse_vec( p, scale );
426 /* skip unparsed rest of line and continue */
427 _pico_parse_skip_rest( p );
430 /* ----------------------------------------------------------------- */
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 );
439 if( heightmap == NULL || w < 2 || h < 2 )
441 _pico_printf( PICO_ERROR, "PicoTerrain model with invalid heightmap" );
443 _pico_free( shader );
444 if( colormapFile != NULL )
445 _pico_free( colormapFile );
446 _pico_free_parser( p );
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 ] );
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 );
460 if( cw != w || ch != h )
462 _pico_printf( PICO_WARNING, "PicoTerrain colormap/heightmap size mismatch" );
463 _pico_free( colormap );
467 /* ----------------------------------------------------------------- */
469 /* create new pico model */
470 picoModel = PicoNewModel();
471 if( picoModel == NULL )
473 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
478 PicoSetModelFrameNum( picoModel, frameNum );
479 PicoSetModelNumFrames( picoModel, 1 ); /* sea */
480 PicoSetModelName( picoModel, fileName );
481 PicoSetModelFileName( picoModel, fileName );
483 /* allocate new pico surface */
484 picoSurface = PicoNewSurface( picoModel );
485 if( picoSurface == NULL )
487 _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
488 PicoFreeModel( picoModel ); /* sea */
492 /* terrain surfaces are triangle meshes */
493 PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
495 /* set surface name */
496 PicoSetSurfaceName( picoSurface, "picoterrain" );
498 /* create new pico shader */
499 picoShader = PicoNewShader( picoModel );
500 if( picoShader == NULL )
502 _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
503 PicoFreeModel( picoModel );
504 _pico_free( shader );
508 /* detox and set shader name */
509 _pico_setfext( shader, "" );
510 _pico_unixify( shader );
511 PicoSetShaderName( picoShader, shader );
512 _pico_free( shader );
514 /* associate current surface with newly created shader */
515 PicoSetSurfaceShader( picoSurface, picoShader );
517 /* make bogus normal */
518 _pico_set_vec( normal, 0.0f, 0.0f, 0.0f );
521 for( j = 0; j < h; j++ )
523 for( i = 0; i < w; i++ )
527 heightPixel = heightmap + v * 4;
528 colorPixel = colormap
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 );
539 PicoSetSurfaceNormal( picoSurface, v, normal );
544 PicoSetSurfaceST( picoSurface, 0, v, st );
547 if( colorPixel != NULL )
548 _pico_set_color( color, colorPixel[ 0 ], colorPixel[ 1 ], colorPixel[ 2 ], colorPixel[ 3 ] );
550 _pico_set_color( color, 255, 255, 255, 255 );
551 PicoSetSurfaceColor( picoSurface, 0, v, color );
553 /* set triangles (zero alpha in heightmap suppresses this quad) */
554 if( i < (w - 1) && j < (h - 1) && heightPixel[ 3 ] >= 128 )
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 ] */
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 ] );
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 ] );
580 _pico_free_parser( p );
581 _pico_free( heightmap );
582 _pico_free( colormap );
584 /* return the new pico model */
590 /* pico file format module definition */
591 const picoModule_t picoModuleTerrain =
593 "1.3", /* module version string */
594 "PicoTerrain", /* module display name */
595 "Randy Reddig", /* author's name */
596 "2003 Randy Reddig", /* module copyright */
598 "picoterrain", NULL, NULL, NULL /* default extensions to use */
600 _terrain_canload, /* validation routine */
601 _terrain_load, /* load routine */
602 NULL, /* save validation routine */
603 NULL /* save routine */