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 /* keep the friggin compiler happy */
306 *fileName = *fileName;
308 /* create pico parser */
309 p = _pico_new_parser( (picoByte_t*) buffer, bufSize );
311 return PICO_PMV_ERROR_MEMORY;
313 /* get first token */
314 if( _pico_parse_first( p ) == NULL)
315 return PICO_PMV_ERROR_IDENT;
317 /* check first token */
318 if( _pico_stricmp( p->token, "picoterrain" ) )
320 _pico_free_parser( p );
321 return PICO_PMV_ERROR_IDENT;
324 /* free the pico parser object */
325 _pico_free_parser( p );
327 /* file seems to be a valid picoterrain file */
335 loads a picoterrain file
338 static picoModel_t *_terrain_load( PM_PARAMS_LOAD )
340 int i, j, v, pw[ 5 ], r;
343 char *shader, *heightmapFile, *colormapFile;
344 picoVec3_t scale, origin;
346 unsigned char *imageBuffer;
347 int imageBufSize, w, h, cw, ch;
348 unsigned char *heightmap, *colormap, *heightPixel, *colorPixel;
350 picoModel_t *picoModel;
351 picoSurface_t *picoSurface;
352 picoShader_t *picoShader;
353 picoVec3_t xyz, normal;
358 /* keep the friggin compiler happy */
359 *fileName = *fileName;
361 /* create pico parser */
362 p = _pico_new_parser( (picoByte_t*) buffer, bufSize );
366 /* get first token */
367 if( _pico_parse_first( p ) == NULL)
370 /* check first token */
371 if( _pico_stricmp( p->token, "picoterrain" ) )
373 _pico_printf( PICO_ERROR, "Invalid PicoTerrain model" );
374 _pico_free_parser( p );
379 shader = heightmapFile = colormapFile = NULL;
380 _pico_set_vec( scale, 512, 512, 32 );
382 /* parse ase model file */
385 /* get first token on line */
386 if( !_pico_parse_first( p ) )
389 /* skip empty lines */
390 if( !p->token || !p->token[ 0 ] )
394 if( !_pico_stricmp( p->token, "shader" ) )
396 if( _pico_parse( p, 0 ) && p->token[ 0 ] )
399 _pico_free( shader );
400 shader = _pico_clone_alloc( p->token );
405 else if( !_pico_stricmp( p->token, "heightmap" ) )
407 if( _pico_parse( p, 0 ) && p->token[ 0 ] )
409 if( heightmapFile != NULL )
410 _pico_free( heightmapFile );
411 heightmapFile = _pico_clone_alloc( p->token );
416 else if( !_pico_stricmp( p->token, "colormap" ) )
418 if( _pico_parse( p, 0 ) && p->token[ 0 ] )
420 if( colormapFile != NULL )
421 _pico_free( colormapFile );
422 colormapFile = _pico_clone_alloc( p->token );
427 else if( !_pico_stricmp( p->token, "scale" ) )
429 _pico_parse_vec( p, scale );
432 /* skip unparsed rest of line and continue */
433 _pico_parse_skip_rest( p );
436 /* ----------------------------------------------------------------- */
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 );
445 if( heightmap == NULL || w < 2 || h < 2 )
447 _pico_printf( PICO_ERROR, "PicoTerrain model with invalid heightmap" );
449 _pico_free( shader );
450 if( colormapFile != NULL )
451 _pico_free( colormapFile );
452 _pico_free_parser( p );
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 ] );
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 );
466 if( cw != w || ch != h )
468 _pico_printf( PICO_WARNING, "PicoTerrain colormap/heightmap size mismatch" );
469 _pico_free( colormap );
473 /* ----------------------------------------------------------------- */
475 /* create new pico model */
476 picoModel = PicoNewModel();
477 if( picoModel == NULL )
479 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
484 PicoSetModelFrameNum( picoModel, frameNum );
485 PicoSetModelNumFrames( picoModel, 1 ); /* sea */
486 PicoSetModelName( picoModel, fileName );
487 PicoSetModelFileName( picoModel, fileName );
489 /* allocate new pico surface */
490 picoSurface = PicoNewSurface( picoModel );
491 if( picoSurface == NULL )
493 _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
494 PicoFreeModel( picoModel ); /* sea */
498 /* terrain surfaces are triangle meshes */
499 PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
501 /* set surface name */
502 PicoSetSurfaceName( picoSurface, "picoterrain" );
504 /* create new pico shader */
505 picoShader = PicoNewShader( picoModel );
506 if( picoShader == NULL )
508 _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
509 PicoFreeModel( picoModel );
510 _pico_free( shader );
514 /* detox and set shader name */
515 _pico_setfext( shader, "" );
516 _pico_unixify( shader );
517 PicoSetShaderName( picoShader, shader );
518 _pico_free( shader );
520 /* associate current surface with newly created shader */
521 PicoSetSurfaceShader( picoSurface, picoShader );
523 /* make bogus normal */
524 _pico_set_vec( normal, 0.0f, 0.0f, 0.0f );
527 for( j = 0; j < h; j++ )
529 for( i = 0; i < w; i++ )
533 heightPixel = heightmap + v * 4;
534 colorPixel = colormap
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 );
545 PicoSetSurfaceNormal( picoSurface, v, normal );
550 PicoSetSurfaceST( picoSurface, 0, v, st );
553 if( colorPixel != NULL )
554 _pico_set_color( color, colorPixel[ 0 ], colorPixel[ 1 ], colorPixel[ 2 ], colorPixel[ 3 ] );
556 _pico_set_color( color, 255, 255, 255, 255 );
557 PicoSetSurfaceColor( picoSurface, 0, v, color );
559 /* set triangles (zero alpha in heightmap suppresses this quad) */
560 if( i < (w - 1) && j < (h - 1) && heightPixel[ 3 ] >= 128 )
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 ] */
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 ] );
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 ] );
586 _pico_free_parser( p );
587 _pico_free( heightmap );
588 _pico_free( colormap );
590 /* return the new pico model */
596 /* pico file format module definition */
597 const picoModule_t picoModuleTerrain =
599 "1.3", /* module version string */
600 "PicoTerrain", /* module display name */
601 "Randy Reddig", /* author's name */
602 "2003 Randy Reddig", /* module copyright */
604 "picoterrain", NULL, NULL, NULL /* default extensions to use */
606 _terrain_canload, /* validation routine */
607 _terrain_load, /* load routine */
608 NULL, /* save validation routine */
609 NULL /* save routine */