/* ====================================================================== surface.c Surface functions for an LWO2 reader. Ernie Wright 17 Sep 00 ====================================================================== */ #include "picointernal.h" #include "lwo2.h" /* ====================================================================== lwFreePlugin() Free the memory used by an lwPlugin. ====================================================================== */ void lwFreePlugin( lwPlugin *p ){ if ( p ) { if ( p->ord ) { _pico_free( p->ord ); } if ( p->name ) { _pico_free( p->name ); } if ( p->data ) { _pico_free( p->data ); } _pico_free( p ); } } /* ====================================================================== lwFreeTexture() Free the memory used by an lwTexture. ====================================================================== */ void lwFreeTexture( lwTexture *t ){ if ( t ) { if ( t->ord ) { _pico_free( t->ord ); } switch ( t->type ) { case ID_IMAP: if ( t->param.imap.vmap_name ) { _pico_free( t->param.imap.vmap_name ); } if ( t->tmap.ref_object ) { _pico_free( t->tmap.ref_object ); } break; case ID_PROC: if ( t->param.proc.name ) { _pico_free( t->param.proc.name ); } if ( t->param.proc.data ) { _pico_free( t->param.proc.data ); } break; case ID_GRAD: if ( t->param.grad.key ) { _pico_free( t->param.grad.key ); } if ( t->param.grad.ikey ) { _pico_free( t->param.grad.ikey ); } break; } _pico_free( t ); } } /* ====================================================================== lwFreeSurface() Free the memory used by an lwSurface. ====================================================================== */ void lwFreeSurface( lwSurface *surf ){ if ( surf ) { if ( surf->name ) { _pico_free( surf->name ); } if ( surf->srcname ) { _pico_free( surf->srcname ); } lwListFree(surf->shader, (void (*)(void *)) lwFreePlugin); void (*freeTexture)(void *) = (void (*)(void *)) lwFreeTexture; lwListFree(surf->color.tex, freeTexture); lwListFree(surf->luminosity.tex, freeTexture); lwListFree(surf->diffuse.tex, freeTexture); lwListFree(surf->specularity.tex, freeTexture); lwListFree(surf->glossiness.tex, freeTexture); lwListFree(surf->reflection.val.tex, freeTexture); lwListFree(surf->transparency.val.tex, freeTexture); lwListFree(surf->eta.tex, freeTexture); lwListFree(surf->translucency.tex, freeTexture); lwListFree(surf->bump.tex, freeTexture); _pico_free( surf ); } } /* ====================================================================== lwGetTHeader() Read a texture map header from a SURF.BLOK in an LWO2 file. This is the first subchunk in a BLOK, and its contents are common to all three texture types. ====================================================================== */ int lwGetTHeader( picoMemStream_t *fp, int hsz, lwTexture *tex ){ unsigned int id; unsigned short sz; int pos, rlen; /* remember where we started */ set_flen( 0 ); pos = _pico_memstream_tell( fp ); /* ordinal string */ tex->ord = getS0( fp ); /* first subchunk header */ id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) { return 0; } /* process subchunks as they're encountered */ while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_CHAN: tex->chan = getU4( fp ); break; case ID_OPAC: tex->opac_type = getU2( fp ); tex->opacity.val = getF4( fp ); tex->opacity.eindex = getVX( fp ); break; case ID_ENAB: tex->enabled = getU2( fp ); break; case ID_NEGA: tex->negative = getU2( fp ); break; case ID_AXIS: tex->axis = getU2( fp ); break; default: break; } /* error while reading current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) { return 0; } /* skip unread parts of the current subchunk */ if ( rlen < sz ) { _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); } /* end of the texture header subchunk? */ if ( hsz <= _pico_memstream_tell( fp ) - pos ) { break; } /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) { return 0; } } set_flen( _pico_memstream_tell( fp ) - pos ); return 1; } /* ====================================================================== lwGetTMap() Read a texture map from a SURF.BLOK in an LWO2 file. The TMAP defines the mapping from texture to world or object coordinates. ====================================================================== */ int lwGetTMap( picoMemStream_t *fp, int tmapsz, lwTMap *tmap ){ unsigned int id; unsigned short sz; int rlen, pos, i; pos = _pico_memstream_tell( fp ); id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) { return 0; } while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_SIZE: for ( i = 0; i < 3; i++ ) tmap->size.val[ i ] = getF4( fp ); tmap->size.eindex = getVX( fp ); break; case ID_CNTR: for ( i = 0; i < 3; i++ ) tmap->center.val[ i ] = getF4( fp ); tmap->center.eindex = getVX( fp ); break; case ID_ROTA: for ( i = 0; i < 3; i++ ) tmap->rotate.val[ i ] = getF4( fp ); tmap->rotate.eindex = getVX( fp ); break; case ID_FALL: tmap->fall_type = getU2( fp ); for ( i = 0; i < 3; i++ ) tmap->falloff.val[ i ] = getF4( fp ); tmap->falloff.eindex = getVX( fp ); break; case ID_OREF: tmap->ref_object = getS0( fp ); break; case ID_CSYS: tmap->coord_sys = getU2( fp ); break; default: break; } /* error while reading the current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) { return 0; } /* skip unread parts of the current subchunk */ if ( rlen < sz ) { _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); } /* end of the TMAP subchunk? */ if ( tmapsz <= _pico_memstream_tell( fp ) - pos ) { break; } /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) { return 0; } } set_flen( _pico_memstream_tell( fp ) - pos ); return 1; } /* ====================================================================== lwGetImageMap() Read an lwImageMap from a SURF.BLOK in an LWO2 file. ====================================================================== */ int lwGetImageMap( picoMemStream_t *fp, int rsz, lwTexture *tex ){ unsigned int id; unsigned short sz; int rlen, pos; pos = _pico_memstream_tell( fp ); id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) { return 0; } while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_TMAP: if ( !lwGetTMap( fp, sz, &tex->tmap ) ) { return 0; } break; case ID_PROJ: tex->param.imap.projection = getU2( fp ); break; case ID_VMAP: tex->param.imap.vmap_name = getS0( fp ); break; case ID_AXIS: tex->param.imap.axis = getU2( fp ); break; case ID_IMAG: tex->param.imap.cindex = getVX( fp ); break; case ID_WRAP: tex->param.imap.wrapw_type = getU2( fp ); tex->param.imap.wraph_type = getU2( fp ); break; case ID_WRPW: tex->param.imap.wrapw.val = getF4( fp ); tex->param.imap.wrapw.eindex = getVX( fp ); break; case ID_WRPH: tex->param.imap.wraph.val = getF4( fp ); tex->param.imap.wraph.eindex = getVX( fp ); break; case ID_AAST: tex->param.imap.aas_flags = getU2( fp ); tex->param.imap.aa_strength = getF4( fp ); break; case ID_PIXB: tex->param.imap.pblend = getU2( fp ); break; case ID_STCK: tex->param.imap.stck.val = getF4( fp ); tex->param.imap.stck.eindex = getVX( fp ); break; case ID_TAMP: tex->param.imap.amplitude.val = getF4( fp ); tex->param.imap.amplitude.eindex = getVX( fp ); break; default: break; } /* error while reading the current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) { return 0; } /* skip unread parts of the current subchunk */ if ( rlen < sz ) { _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); } /* end of the image map? */ if ( rsz <= _pico_memstream_tell( fp ) - pos ) { break; } /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) { return 0; } } set_flen( _pico_memstream_tell( fp ) - pos ); return 1; } /* ====================================================================== lwGetProcedural() Read an lwProcedural from a SURF.BLOK in an LWO2 file. ====================================================================== */ int lwGetProcedural( picoMemStream_t *fp, int rsz, lwTexture *tex ){ unsigned int id; unsigned short sz; int rlen, pos; pos = _pico_memstream_tell( fp ); id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) { return 0; } while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_TMAP: if ( !lwGetTMap( fp, sz, &tex->tmap ) ) { return 0; } break; case ID_AXIS: tex->param.proc.axis = getU2( fp ); break; case ID_VALU: tex->param.proc.value[ 0 ] = getF4( fp ); if ( sz >= 8 ) { tex->param.proc.value[ 1 ] = getF4( fp ); } if ( sz >= 12 ) { tex->param.proc.value[ 2 ] = getF4( fp ); } break; case ID_FUNC: tex->param.proc.name = getS0( fp ); rlen = get_flen(); tex->param.proc.data = getbytes( fp, sz - rlen ); break; default: break; } /* error while reading the current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) { return 0; } /* skip unread parts of the current subchunk */ if ( rlen < sz ) { _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); } /* end of the procedural block? */ if ( rsz <= _pico_memstream_tell( fp ) - pos ) { break; } /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) { return 0; } } set_flen( _pico_memstream_tell( fp ) - pos ); return 1; } /* ====================================================================== lwGetGradient() Read an lwGradient from a SURF.BLOK in an LWO2 file. ====================================================================== */ int lwGetGradient( picoMemStream_t *fp, int rsz, lwTexture *tex ){ unsigned int id; unsigned short sz; int rlen, pos, i, j, nkeys; pos = _pico_memstream_tell( fp ); id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) { return 0; } while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_TMAP: if ( !lwGetTMap( fp, sz, &tex->tmap ) ) { return 0; } break; case ID_PNAM: tex->param.grad.paramname = getS0( fp ); break; case ID_INAM: tex->param.grad.itemname = getS0( fp ); break; case ID_GRST: tex->param.grad.start = getF4( fp ); break; case ID_GREN: tex->param.grad.end = getF4( fp ); break; case ID_GRPT: tex->param.grad.repeat = getU2( fp ); break; case ID_FKEY: nkeys = sz / sizeof( lwGradKey ); tex->param.grad.key = _pico_calloc( nkeys, sizeof( lwGradKey ) ); if ( !tex->param.grad.key ) { return 0; } for ( i = 0; i < nkeys; i++ ) { tex->param.grad.key[ i ].value = getF4( fp ); for ( j = 0; j < 4; j++ ) tex->param.grad.key[ i ].rgba[ j ] = getF4( fp ); } break; case ID_IKEY: nkeys = sz / 2; tex->param.grad.ikey = _pico_calloc( nkeys, sizeof( short ) ); if ( !tex->param.grad.ikey ) { return 0; } for ( i = 0; i < nkeys; i++ ) tex->param.grad.ikey[ i ] = getU2( fp ); break; default: break; } /* error while reading the current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) { return 0; } /* skip unread parts of the current subchunk */ if ( rlen < sz ) { _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); } /* end of the gradient? */ if ( rsz <= _pico_memstream_tell( fp ) - pos ) { break; } /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) { return 0; } } set_flen( _pico_memstream_tell( fp ) - pos ); return 1; } /* ====================================================================== lwGetTexture() Read an lwTexture from a SURF.BLOK in an LWO2 file. ====================================================================== */ lwTexture *lwGetTexture( picoMemStream_t *fp, int bloksz, unsigned int type ){ lwTexture *tex; unsigned short sz; int ok; tex = _pico_calloc( 1, sizeof( lwTexture ) ); if ( !tex ) { return NULL; } tex->type = type; tex->tmap.size.val[ 0 ] = tex->tmap.size.val[ 1 ] = tex->tmap.size.val[ 2 ] = 1.0f; tex->opacity.val = 1.0f; tex->enabled = 1; sz = getU2( fp ); if ( !lwGetTHeader( fp, sz, tex ) ) { _pico_free( tex ); return NULL; } sz = bloksz - sz - 6; switch ( type ) { case ID_IMAP: ok = lwGetImageMap( fp, sz, tex ); break; case ID_PROC: ok = lwGetProcedural( fp, sz, tex ); break; case ID_GRAD: ok = lwGetGradient( fp, sz, tex ); break; default: ok = !_pico_memstream_seek( fp, sz, PICO_SEEK_CUR ); } if ( !ok ) { lwFreeTexture( tex ); return NULL; } set_flen( bloksz ); return tex; } /* ====================================================================== lwGetShader() Read a shader record from a SURF.BLOK in an LWO2 file. ====================================================================== */ lwPlugin *lwGetShader( picoMemStream_t *fp, int bloksz ){ lwPlugin *shdr; unsigned int id; unsigned short sz; int hsz, rlen, pos; shdr = _pico_calloc( 1, sizeof( lwPlugin ) ); if ( !shdr ) { return NULL; } pos = _pico_memstream_tell( fp ); set_flen( 0 ); hsz = getU2( fp ); shdr->ord = getS0( fp ); id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) { goto Fail; } while ( hsz > 0 ) { sz += sz & 1; hsz -= sz; if ( id == ID_ENAB ) { shdr->flags = getU2( fp ); break; } else { _pico_memstream_seek( fp, sz, PICO_SEEK_CUR ); id = getU4( fp ); sz = getU2( fp ); } } id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) { goto Fail; } while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_FUNC: shdr->name = getS0( fp ); rlen = get_flen(); shdr->data = getbytes( fp, sz - rlen ); break; default: break; } /* error while reading the current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) { goto Fail; } /* skip unread parts of the current subchunk */ if ( rlen < sz ) { _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); } /* end of the shader block? */ if ( bloksz <= _pico_memstream_tell( fp ) - pos ) { break; } /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) { goto Fail; } } set_flen( _pico_memstream_tell( fp ) - pos ); return shdr; Fail: lwFreePlugin( shdr ); return NULL; } /* ====================================================================== compare_textures() compare_shaders() Callbacks for the lwListInsert() function, which is called to add textures to surface channels and shaders to surfaces. ====================================================================== */ static int compare_textures( lwTexture *a, lwTexture *b ){ return strcmp( a->ord, b->ord ); } static int compare_shaders( lwPlugin *a, lwPlugin *b ){ return strcmp( a->ord, b->ord ); } /* ====================================================================== add_texture() Finds the surface channel (lwTParam or lwCParam) to which a texture is applied, then calls lwListInsert(). ====================================================================== */ static int add_texture( lwSurface *surf, lwTexture *tex ){ lwTexture **list; switch ( tex->chan ) { case ID_COLR: list = &surf->color.tex; break; case ID_LUMI: list = &surf->luminosity.tex; break; case ID_DIFF: list = &surf->diffuse.tex; break; case ID_SPEC: list = &surf->specularity.tex; break; case ID_GLOS: list = &surf->glossiness.tex; break; case ID_REFL: list = &surf->reflection.val.tex; break; case ID_TRAN: list = &surf->transparency.val.tex; break; case ID_RIND: list = &surf->eta.tex; break; case ID_TRNL: list = &surf->translucency.tex; break; case ID_BUMP: list = &surf->bump.tex; break; default: return 0; } lwListInsert((void **) list, tex, (int (*)(void *, void *)) compare_textures); return 1; } /* ====================================================================== lwDefaultSurface() Allocate and initialize a surface. ====================================================================== */ lwSurface *lwDefaultSurface( void ){ lwSurface *surf; surf = _pico_calloc( 1, sizeof( lwSurface ) ); if ( !surf ) { return NULL; } surf->color.rgb[ 0 ] = 0.78431f; surf->color.rgb[ 1 ] = 0.78431f; surf->color.rgb[ 2 ] = 0.78431f; surf->diffuse.val = 1.0f; surf->glossiness.val = 0.4f; surf->bump.val = 1.0f; surf->eta.val = 1.0f; surf->sideflags = 1; return surf; } /* ====================================================================== lwGetSurface() Read an lwSurface from an LWO2 file. ====================================================================== */ lwSurface *lwGetSurface( picoMemStream_t *fp, int cksize ){ lwSurface *surf; lwTexture *tex; lwPlugin *shdr; unsigned int id, type; unsigned short sz; int pos, rlen; /* allocate the Surface structure */ surf = _pico_calloc( 1, sizeof( lwSurface ) ); if ( !surf ) { goto Fail; } /* non-zero defaults */ surf->color.rgb[ 0 ] = 0.78431f; surf->color.rgb[ 1 ] = 0.78431f; surf->color.rgb[ 2 ] = 0.78431f; surf->diffuse.val = 1.0f; surf->glossiness.val = 0.4f; surf->bump.val = 1.0f; surf->eta.val = 1.0f; surf->sideflags = 1; /* remember where we started */ set_flen( 0 ); pos = _pico_memstream_tell( fp ); /* names */ surf->name = getS0( fp ); surf->srcname = getS0( fp ); /* first subchunk header */ id = getU4( fp ); sz = getU2( fp ); if ( 0 > get_flen() ) { goto Fail; } /* process subchunks as they're encountered */ while ( 1 ) { sz += sz & 1; set_flen( 0 ); switch ( id ) { case ID_COLR: surf->color.rgb[ 0 ] = getF4( fp ); surf->color.rgb[ 1 ] = getF4( fp ); surf->color.rgb[ 2 ] = getF4( fp ); surf->color.eindex = getVX( fp ); break; case ID_LUMI: surf->luminosity.val = getF4( fp ); surf->luminosity.eindex = getVX( fp ); break; case ID_DIFF: surf->diffuse.val = getF4( fp ); surf->diffuse.eindex = getVX( fp ); break; case ID_SPEC: surf->specularity.val = getF4( fp ); surf->specularity.eindex = getVX( fp ); break; case ID_GLOS: surf->glossiness.val = getF4( fp ); surf->glossiness.eindex = getVX( fp ); break; case ID_REFL: surf->reflection.val.val = getF4( fp ); surf->reflection.val.eindex = getVX( fp ); break; case ID_RFOP: surf->reflection.options = getU2( fp ); break; case ID_RIMG: surf->reflection.cindex = getVX( fp ); break; case ID_RSAN: surf->reflection.seam_angle = getF4( fp ); break; case ID_TRAN: surf->transparency.val.val = getF4( fp ); surf->transparency.val.eindex = getVX( fp ); break; case ID_TROP: surf->transparency.options = getU2( fp ); break; case ID_TIMG: surf->transparency.cindex = getVX( fp ); break; case ID_RIND: surf->eta.val = getF4( fp ); surf->eta.eindex = getVX( fp ); break; case ID_TRNL: surf->translucency.val = getF4( fp ); surf->translucency.eindex = getVX( fp ); break; case ID_BUMP: surf->bump.val = getF4( fp ); surf->bump.eindex = getVX( fp ); break; case ID_SMAN: surf->smooth = getF4( fp ); break; case ID_SIDE: surf->sideflags = getU2( fp ); break; case ID_CLRH: surf->color_hilite.val = getF4( fp ); surf->color_hilite.eindex = getVX( fp ); break; case ID_CLRF: surf->color_filter.val = getF4( fp ); surf->color_filter.eindex = getVX( fp ); break; case ID_ADTR: surf->add_trans.val = getF4( fp ); surf->add_trans.eindex = getVX( fp ); break; case ID_SHRP: surf->dif_sharp.val = getF4( fp ); surf->dif_sharp.eindex = getVX( fp ); break; case ID_GVAL: surf->glow.val = getF4( fp ); surf->glow.eindex = getVX( fp ); break; case ID_LINE: surf->line.enabled = 1; if ( sz >= 2 ) { surf->line.flags = getU2( fp ); } if ( sz >= 6 ) { surf->line.size.val = getF4( fp ); } if ( sz >= 8 ) { surf->line.size.eindex = getVX( fp ); } break; case ID_ALPH: surf->alpha_mode = getU2( fp ); surf->alpha = getF4( fp ); break; case ID_AVAL: surf->alpha = getF4( fp ); break; case ID_BLOK: type = getU4( fp ); switch ( type ) { case ID_IMAP: case ID_PROC: case ID_GRAD: tex = lwGetTexture( fp, sz - 4, type ); if ( !tex ) { goto Fail; } if ( !add_texture( surf, tex ) ) { lwFreeTexture( tex ); } set_flen( 4 + get_flen() ); break; case ID_SHDR: shdr = lwGetShader( fp, sz - 4 ); if ( !shdr ) { goto Fail; } lwListInsert((void **) &surf->shader, shdr, (int (*)(void *, void *)) compare_shaders); ++surf->nshaders; set_flen( 4 + get_flen() ); break; } break; default: break; } /* error while reading current subchunk? */ rlen = get_flen(); if ( rlen < 0 || rlen > sz ) { goto Fail; } /* skip unread parts of the current subchunk */ if ( rlen < sz ) { _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); } /* end of the SURF chunk? */ if ( cksize <= _pico_memstream_tell( fp ) - pos ) { break; } /* get the next subchunk header */ set_flen( 0 ); id = getU4( fp ); sz = getU2( fp ); if ( 6 != get_flen() ) { goto Fail; } } return surf; Fail: if ( surf ) { lwFreeSurface( surf ); } return NULL; }