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 ){
67 int columns, rows, numPixels;
68 unsigned char *pixbuf;
71 unsigned char *targa_rgba;
76 if ( buffer == NULL ) {
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 ) {
103 _pico_printf( PICO_ERROR, "Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n" );
108 if ( targa_header.colormap_type != 0 ) {
109 _pico_printf( PICO_ERROR, "Indexed color TGA images not supported\n" );
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" );
119 columns = targa_header.width;
120 rows = targa_header.height;
121 numPixels = columns * rows;
130 targa_rgba = _pico_alloc( numPixels * 4 );
133 if ( targa_header.id_length != 0 ) {
134 buf_p += targa_header.id_length; // skip TARGA image comment
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-- )
141 pixbuf = targa_rgba + row * columns * 4;
142 for ( column = 0; column < columns; column++ )
144 unsigned char red,green,blue,alphabyte;
145 switch ( targa_header.pixel_size )
171 alphabyte = *buf_p++;
175 *pixbuf++ = alphabyte;
184 /* rle encoded pixels */
185 else if ( targa_header.image_type == 10 ) {
186 unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
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 ) {
210 alphabyte = *buf_p++;
213 //Error("LoadTGA: illegal pixel_size '%d' in file '%s'\n", targa_header.pixel_size, name );
217 for ( j = 0; j < packetSize; j++ ) {
221 *pixbuf++ = alphabyte;
223 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
270 pixbuf = targa_rgba + row * columns * 4;
279 /* fix vertically flipped image */
280 if ( ( targa_header.attributes & ( 1 << 5 ) ) ) {
282 for ( row = 0; row < .5f * rows; row++ )
284 for ( column = 0; column < columns; column++ )
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;
298 validates a picoterrain file
301 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;
311 /* get first token */
312 if ( _pico_parse_first( p ) == NULL ) {
313 return PICO_PMV_ERROR_IDENT;
316 /* check first token */
317 if ( _pico_stricmp( p->token, "picoterrain" ) ) {
318 _pico_free_parser( p );
319 return PICO_PMV_ERROR_IDENT;
322 /* free the pico parser object */
323 _pico_free_parser( p );
325 /* file seems to be a valid picoterrain file */
333 loads a picoterrain file
336 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 );
361 /* get first token */
362 if ( _pico_parse_first( p ) == NULL ) {
366 /* check first token */
367 if ( _pico_stricmp( p->token, "picoterrain" ) ) {
368 _pico_printf( PICO_ERROR, "Invalid PicoTerrain model" );
369 _pico_free_parser( p );
374 shader = heightmapFile = colormapFile = NULL;
375 _pico_set_vec( scale, 512, 512, 32 );
377 /* parse ase model file */
380 /* get first token on line */
381 if ( !_pico_parse_first( p ) ) {
385 /* skip empty lines */
386 if ( !p->token || !p->token[ 0 ] ) {
391 if ( !_pico_stricmp( p->token, "shader" ) ) {
392 if ( _pico_parse( p, 0 ) && p->token[ 0 ] ) {
393 if ( shader != NULL ) {
394 _pico_free( shader );
396 shader = _pico_clone_alloc( p->token );
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 );
406 heightmapFile = _pico_clone_alloc( p->token );
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 );
416 colormapFile = _pico_clone_alloc( p->token );
421 else if ( !_pico_stricmp( p->token, "scale" ) ) {
422 _pico_parse_vec( p, scale );
425 /* skip unparsed rest of line and continue */
426 _pico_parse_skip_rest( p );
429 /* ----------------------------------------------------------------- */
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 );
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 );
443 if ( colormapFile != NULL ) {
444 _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 ) {
461 _pico_printf( PICO_WARNING, "PicoTerrain colormap/heightmap size mismatch" );
462 _pico_free( colormap );
466 /* ----------------------------------------------------------------- */
468 /* create new pico model */
469 picoModel = PicoNewModel();
470 if ( picoModel == NULL ) {
471 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
476 PicoSetModelFrameNum( picoModel, frameNum );
477 PicoSetModelNumFrames( picoModel, 1 ); /* sea */
478 PicoSetModelName( picoModel, fileName );
479 PicoSetModelFileName( picoModel, fileName );
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 */
489 /* terrain surfaces are triangle meshes */
490 PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
492 /* set surface name */
493 PicoSetSurfaceName( picoSurface, "picoterrain" );
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 );
504 /* detox and set shader name */
505 _pico_setfext( shader, "" );
506 _pico_unixify( shader );
507 PicoSetShaderName( picoShader, shader );
508 _pico_free( shader );
510 /* associate current surface with newly created shader */
511 PicoSetSurfaceShader( picoSurface, picoShader );
513 /* make bogus normal */
514 _pico_set_vec( normal, 0.0f, 0.0f, 0.0f );
517 for ( j = 0; j < h; j++ )
519 for ( i = 0; i < w; i++ )
523 heightPixel = heightmap + v * 4;
524 colorPixel = colormap
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 );
535 PicoSetSurfaceNormal( picoSurface, v, normal );
540 PicoSetSurfaceST( picoSurface, 0, v, st );
543 if ( colorPixel != NULL ) {
544 _pico_set_color( color, colorPixel[ 0 ], colorPixel[ 1 ], colorPixel[ 2 ], colorPixel[ 3 ] );
547 _pico_set_color( color, 255, 255, 255, 255 );
549 PicoSetSurfaceColor( picoSurface, 0, v, color );
551 /* set triangles (zero alpha in heightmap suppresses this quad) */
552 if ( i < ( w - 1 ) && j < ( h - 1 ) && heightPixel[ 3 ] >= 128 ) {
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 ] */
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 ] );
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 ] );
577 _pico_free_parser( p );
578 _pico_free( heightmap );
579 _pico_free( colormap );
581 /* return the new pico model */
587 /* pico file format module definition */
588 const picoModule_t picoModuleTerrain =
590 "1.3", /* module version string */
591 "PicoTerrain", /* module display name */
592 "Randy Reddig", /* author's name */
593 "2003 Randy Reddig", /* module copyright */
595 "picoterrain", NULL, NULL, NULL /* default extensions to use */
597 _terrain_canload, /* validation routine */
598 _terrain_load, /* load routine */
599 NULL, /* save validation routine */
600 NULL /* save routine */