1108 lines
22 KiB
C
1108 lines
22 KiB
C
/*
|
|
======================================================================
|
|
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;
|
|
}
|