]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - libs/picomodel/lwo/lwob.c
set eol-style
[xonotic/netradiant.git] / libs / picomodel / lwo / lwob.c
index 0e386a3017371e076a1790541141b9e3879fa56b..edf42cff06088aad197f7e82a123f41ac9441a32 100644 (file)
-/*\r
-======================================================================\r
-lwob.c\r
-\r
-Functions for an LWOB reader.  LWOB is the LightWave object format\r
-for versions of LW prior to 6.0.\r
-\r
-Ernie Wright  17 Sep 00\r
-====================================================================== */\r
-\r
-#include "../picointernal.h"\r
-#include "lwo2.h"\r
-\r
-/* disable warnings */\r
-#ifdef _WIN32\r
-#pragma warning( disable:4018 )                /* signed/unsigned mismatch */\r
-#endif\r
-\r
-\r
-/* IDs specific to LWOB */\r
-\r
-#define ID_SRFS  LWID_('S','R','F','S')\r
-#define ID_FLAG  LWID_('F','L','A','G')\r
-#define ID_VLUM  LWID_('V','L','U','M')\r
-#define ID_VDIF  LWID_('V','D','I','F')\r
-#define ID_VSPC  LWID_('V','S','P','C')\r
-#define ID_RFLT  LWID_('R','F','L','T')\r
-#define ID_BTEX  LWID_('B','T','E','X')\r
-#define ID_CTEX  LWID_('C','T','E','X')\r
-#define ID_DTEX  LWID_('D','T','E','X')\r
-#define ID_LTEX  LWID_('L','T','E','X')\r
-#define ID_RTEX  LWID_('R','T','E','X')\r
-#define ID_STEX  LWID_('S','T','E','X')\r
-#define ID_TTEX  LWID_('T','T','E','X')\r
-#define ID_TFLG  LWID_('T','F','L','G')\r
-#define ID_TSIZ  LWID_('T','S','I','Z')\r
-#define ID_TCTR  LWID_('T','C','T','R')\r
-#define ID_TFAL  LWID_('T','F','A','L')\r
-#define ID_TVEL  LWID_('T','V','E','L')\r
-#define ID_TCLR  LWID_('T','C','L','R')\r
-#define ID_TVAL  LWID_('T','V','A','L')\r
-#define ID_TAMP  LWID_('T','A','M','P')\r
-#define ID_TIMG  LWID_('T','I','M','G')\r
-#define ID_TAAS  LWID_('T','A','A','S')\r
-#define ID_TREF  LWID_('T','R','E','F')\r
-#define ID_TOPC  LWID_('T','O','P','C')\r
-#define ID_SDAT  LWID_('S','D','A','T')\r
-#define ID_TFP0  LWID_('T','F','P','0')\r
-#define ID_TFP1  LWID_('T','F','P','1')\r
-\r
-\r
-/*\r
-======================================================================\r
-add_clip()\r
-\r
-Add a clip to the clip list.  Used to store the contents of an RIMG or\r
-TIMG surface subchunk.\r
-====================================================================== */\r
-\r
-static int add_clip( char *s, lwClip **clist, int *nclips )\r
-{\r
-   lwClip *clip;\r
-   char *p;\r
-\r
-   clip = _pico_calloc( 1, sizeof( lwClip ));\r
-   if ( !clip ) return 0;\r
-\r
-   clip->contrast.val = 1.0f;\r
-   clip->brightness.val = 1.0f;\r
-   clip->saturation.val = 1.0f;\r
-   clip->gamma.val = 1.0f;\r
-\r
-   if ( p = strstr( s, "(sequence)" )) {\r
-      p[ -1 ] = 0;\r
-      clip->type = ID_ISEQ;\r
-      clip->source.seq.prefix = s;\r
-      clip->source.seq.digits = 3;\r
-   }\r
-   else {\r
-      clip->type = ID_STIL;\r
-      clip->source.still.name = s;\r
-   }\r
-\r
-   *nclips++;\r
-   clip->index = *nclips;\r
-\r
-   lwListAdd( clist, clip );\r
-\r
-   return clip->index;\r
-}\r
-\r
-\r
-/*\r
-======================================================================\r
-add_tvel()\r
-\r
-Add a triple of envelopes to simulate the old texture velocity\r
-parameters.\r
-====================================================================== */\r
-\r
-static int add_tvel( float pos[], float vel[], lwEnvelope **elist, int *nenvs )\r
-{\r
-   lwEnvelope *env;\r
-   lwKey *key0, *key1;\r
-   int i;\r
-\r
-   for ( i = 0; i < 3; i++ ) {\r
-      env = _pico_calloc( 1, sizeof( lwEnvelope ));\r
-      key0 = _pico_calloc( 1, sizeof( lwKey ));\r
-      key1 = _pico_calloc( 1, sizeof( lwKey ));\r
-      if ( !env || !key0 || !key1 ) return 0;\r
-\r
-      key0->next = key1;\r
-      key0->value = pos[ i ];\r
-      key0->time = 0.0f;\r
-      key1->prev = key0;\r
-      key1->value = pos[ i ] + vel[ i ] * 30.0f;\r
-      key1->time = 1.0f;\r
-      key0->shape = key1->shape = ID_LINE;\r
-\r
-      env->index = *nenvs + i + 1;\r
-      env->type = 0x0301 + i;\r
-      env->name = _pico_alloc( 11 );\r
-      if ( env->name ) {\r
-         strcpy( env->name, "Position.X" );\r
-         env->name[ 9 ] += i;\r
-      }\r
-      env->key = key0;\r
-      env->nkeys = 2;\r
-      env->behavior[ 0 ] = BEH_LINEAR;\r
-      env->behavior[ 1 ] = BEH_LINEAR;\r
-\r
-      lwListAdd( elist, env );\r
-   }\r
-\r
-   *nenvs += 3;\r
-   return env->index - 2;\r
-}\r
-\r
-\r
-/*\r
-======================================================================\r
-get_texture()\r
-\r
-Create a new texture for BTEX, CTEX, etc. subchunks.\r
-====================================================================== */\r
-\r
-static lwTexture *get_texture( char *s )\r
-{\r
-   lwTexture *tex;\r
-\r
-   tex = _pico_calloc( 1, sizeof( lwTexture ));\r
-   if ( !tex ) return NULL;\r
-\r
-   tex->tmap.size.val[ 0 ] =\r
-   tex->tmap.size.val[ 1 ] =\r
-   tex->tmap.size.val[ 2 ] = 1.0f;\r
-   tex->opacity.val = 1.0f;\r
-   tex->enabled = 1;\r
-\r
-   if ( strstr( s, "Image Map" )) {\r
-      tex->type = ID_IMAP;\r
-      if ( strstr( s, "Planar" ))           tex->param.imap.projection = 0;\r
-      else if ( strstr( s, "Cylindrical" )) tex->param.imap.projection = 1;\r
-      else if ( strstr( s, "Spherical" ))   tex->param.imap.projection = 2;\r
-      else if ( strstr( s, "Cubic" ))       tex->param.imap.projection = 3;\r
-      else if ( strstr( s, "Front" ))       tex->param.imap.projection = 4;\r
-      tex->param.imap.aa_strength = 1.0f;\r
-      tex->param.imap.amplitude.val = 1.0f;\r
-      _pico_free( s );\r
-   }\r
-   else {\r
-      tex->type = ID_PROC;\r
-      tex->param.proc.name = s;\r
-   }\r
-\r
-   return tex;\r
-}\r
-\r
-\r
-/*\r
-======================================================================\r
-lwGetSurface5()\r
-\r
-Read an lwSurface from an LWOB file.\r
-====================================================================== */\r
-\r
-lwSurface *lwGetSurface5( picoMemStream_t *fp, int cksize, lwObject *obj )\r
-{\r
-   lwSurface *surf;\r
-   lwTexture *tex;\r
-   lwPlugin *shdr;\r
-   char *s;\r
-   float v[ 3 ];\r
-   unsigned int id, flags;\r
-   unsigned short sz;\r
-   int pos, rlen, i;\r
-\r
-\r
-   /* allocate the Surface structure */\r
-\r
-   surf = _pico_calloc( 1, sizeof( lwSurface ));\r
-   if ( !surf ) goto Fail;\r
-\r
-   /* non-zero defaults */\r
-\r
-   surf->color.rgb[ 0 ] = 0.78431f;\r
-   surf->color.rgb[ 1 ] = 0.78431f;\r
-   surf->color.rgb[ 2 ] = 0.78431f;\r
-   surf->diffuse.val    = 1.0f;\r
-   surf->glossiness.val = 0.4f;\r
-   surf->bump.val       = 1.0f;\r
-   surf->eta.val        = 1.0f;\r
-   surf->sideflags      = 1;\r
-\r
-   /* remember where we started */\r
-\r
-   set_flen( 0 );\r
-   pos = _pico_memstream_tell( fp );\r
-\r
-   /* name */\r
-\r
-   surf->name = getS0( fp );\r
-\r
-   /* first subchunk header */\r
-\r
-   id = getU4( fp );\r
-   sz = getU2( fp );\r
-   if ( 0 > get_flen() ) goto Fail;\r
-\r
-   /* process subchunks as they're encountered */\r
-\r
-   while ( 1 ) {\r
-      sz += sz & 1;\r
-      set_flen( 0 );\r
-\r
-      switch ( id ) {\r
-         case ID_COLR:\r
-            surf->color.rgb[ 0 ] = getU1( fp ) / 255.0f;\r
-            surf->color.rgb[ 1 ] = getU1( fp ) / 255.0f;\r
-            surf->color.rgb[ 2 ] = getU1( fp ) / 255.0f;\r
-            break;\r
-\r
-         case ID_FLAG:\r
-            flags = getU2( fp );\r
-            if ( flags &   4 ) surf->smooth = 1.56207f;\r
-            if ( flags &   8 ) surf->color_hilite.val = 1.0f;\r
-            if ( flags &  16 ) surf->color_filter.val = 1.0f;\r
-            if ( flags & 128 ) surf->dif_sharp.val = 0.5f;\r
-            if ( flags & 256 ) surf->sideflags = 3;\r
-            if ( flags & 512 ) surf->add_trans.val = 1.0f;\r
-            break;\r
-\r
-         case ID_LUMI:\r
-            surf->luminosity.val = getI2( fp ) / 256.0f;\r
-            break;\r
-\r
-         case ID_VLUM:\r
-            surf->luminosity.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_DIFF:\r
-            surf->diffuse.val = getI2( fp ) / 256.0f;\r
-            break;\r
-\r
-         case ID_VDIF:\r
-            surf->diffuse.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_SPEC:\r
-            surf->specularity.val = getI2( fp ) / 256.0f;\r
-            break;\r
-\r
-         case ID_VSPC:\r
-            surf->specularity.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_GLOS:\r
-            surf->glossiness.val = ( float ) log( getU2( fp )) / 20.7944f;\r
-            break;\r
-\r
-         case ID_SMAN:\r
-            surf->smooth = getF4( fp );\r
-            break;\r
-\r
-         case ID_REFL:\r
-            surf->reflection.val.val = getI2( fp ) / 256.0f;\r
-            break;\r
-\r
-         case ID_RFLT:\r
-            surf->reflection.options = getU2( fp );\r
-            break;\r
-\r
-         case ID_RIMG:\r
-            s = getS0( fp );\r
-            surf->reflection.cindex = add_clip( s, &obj->clip, &obj->nclips );\r
-            surf->reflection.options = 3;\r
-            break;\r
-\r
-         case ID_RSAN:\r
-            surf->reflection.seam_angle = getF4( fp );\r
-            break;\r
-\r
-         case ID_TRAN:\r
-            surf->transparency.val.val = getI2( fp ) / 256.0f;\r
-            break;\r
-\r
-         case ID_RIND:\r
-            surf->eta.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_BTEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->bump.tex, tex );\r
-            break;\r
-\r
-         case ID_CTEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->color.tex, tex );\r
-            break;\r
-\r
-         case ID_DTEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->diffuse.tex, tex );\r
-            break;\r
-\r
-         case ID_LTEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->luminosity.tex, tex );\r
-            break;\r
-\r
-         case ID_RTEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->reflection.val.tex, tex );\r
-            break;\r
-\r
-         case ID_STEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->specularity.tex, tex );\r
-            break;\r
-\r
-         case ID_TTEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->transparency.val.tex, tex );\r
-            break;\r
-\r
-         case ID_TFLG:\r
-            flags = getU2( fp );\r
-\r
-            if ( flags & 1 ) i = 0;\r
-            if ( flags & 2 ) i = 1;\r
-            if ( flags & 4 ) i = 2;\r
-            tex->axis = i;\r
-            if ( tex->type == ID_IMAP )\r
-               tex->param.imap.axis = i;\r
-            else\r
-               tex->param.proc.axis = i;\r
-\r
-            if ( flags &  8 ) tex->tmap.coord_sys = 1;\r
-            if ( flags & 16 ) tex->negative = 1;\r
-            if ( flags & 32 ) tex->param.imap.pblend = 1;\r
-            if ( flags & 64 ) {\r
-               tex->param.imap.aa_strength = 1.0f;\r
-               tex->param.imap.aas_flags = 1;\r
-            }\r
-            break;\r
-\r
-         case ID_TSIZ:\r
-            for ( i = 0; i < 3; i++ )\r
-               tex->tmap.size.val[ i ] = getF4( fp );\r
-            break;\r
-\r
-         case ID_TCTR:\r
-            for ( i = 0; i < 3; i++ )\r
-               tex->tmap.center.val[ i ] = getF4( fp );\r
-            break;\r
-\r
-         case ID_TFAL:\r
-            for ( i = 0; i < 3; i++ )\r
-               tex->tmap.falloff.val[ i ] = getF4( fp );\r
-            break;\r
-\r
-         case ID_TVEL:\r
-            for ( i = 0; i < 3; i++ )\r
-               v[ i ] = getF4( fp );\r
-            tex->tmap.center.eindex = add_tvel( tex->tmap.center.val, v,\r
-               &obj->env, &obj->nenvs );\r
-            break;\r
-\r
-         case ID_TCLR:\r
-            if ( tex->type == ID_PROC )\r
-               for ( i = 0; i < 3; i++ )\r
-                  tex->param.proc.value[ i ] = getU1( fp ) / 255.0f;\r
-            break;\r
-\r
-         case ID_TVAL:\r
-            tex->param.proc.value[ 0 ] = getI2( fp ) / 256.0f;\r
-            break;\r
-\r
-         case ID_TAMP:\r
-            if ( tex->type == ID_IMAP )\r
-               tex->param.imap.amplitude.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_TIMG:\r
-            s = getS0( fp );\r
-            tex->param.imap.cindex = add_clip( s, &obj->clip, &obj->nclips );\r
-            break;\r
-\r
-         case ID_TAAS:\r
-            tex->param.imap.aa_strength = getF4( fp );\r
-            tex->param.imap.aas_flags = 1;\r
-            break;\r
-\r
-         case ID_TREF:\r
-            tex->tmap.ref_object = getbytes( fp, sz );\r
-            break;\r
-\r
-         case ID_TOPC:\r
-            tex->opacity.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_TFP0:\r
-            if ( tex->type == ID_IMAP )\r
-               tex->param.imap.wrapw.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_TFP1:\r
-            if ( tex->type == ID_IMAP )\r
-               tex->param.imap.wraph.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_SHDR:\r
-            shdr = _pico_calloc( 1, sizeof( lwPlugin ));\r
-            if ( !shdr ) goto Fail;\r
-            shdr->name = getbytes( fp, sz );\r
-            lwListAdd( &surf->shader, shdr );\r
-            surf->nshaders++;\r
-            break;\r
-\r
-         case ID_SDAT:\r
-            shdr->data = getbytes( fp, sz );\r
-            break;\r
-\r
-         default:\r
-            break;\r
-      }\r
-\r
-      /* error while reading current subchunk? */\r
-\r
-      rlen = get_flen();\r
-      if ( rlen < 0 || rlen > sz ) goto Fail;\r
-\r
-      /* skip unread parts of the current subchunk */\r
-\r
-      if ( rlen < sz )\r
-         _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );\r
-\r
-      /* end of the SURF chunk? */\r
-\r
-      if ( cksize <= _pico_memstream_tell( fp ) - pos )\r
-         break;\r
-\r
-      /* get the next subchunk header */\r
-\r
-      set_flen( 0 );\r
-      id = getU4( fp );\r
-      sz = getU2( fp );\r
-      if ( 6 != get_flen() ) goto Fail;\r
-   }\r
-\r
-   return surf;\r
-\r
-Fail:\r
-   if ( surf ) lwFreeSurface( surf );\r
-   return NULL;\r
-}\r
-\r
-\r
-/*\r
-======================================================================\r
-lwGetPolygons5()\r
-\r
-Read polygon records from a POLS chunk in an LWOB file.  The polygons\r
-are added to the array in the lwPolygonList.\r
-====================================================================== */\r
-\r
-int lwGetPolygons5( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset )\r
-{\r
-   lwPolygon *pp;\r
-   lwPolVert *pv;\r
-   unsigned char *buf, *bp;\r
-   int i, j, nv, nverts, npols;\r
-\r
-\r
-   if ( cksize == 0 ) return 1;\r
-\r
-   /* read the whole chunk */\r
-\r
-   set_flen( 0 );\r
-   buf = getbytes( fp, cksize );\r
-   if ( !buf ) goto Fail;\r
-\r
-   /* count the polygons and vertices */\r
-\r
-   nverts = 0;\r
-   npols = 0;\r
-   bp = buf;\r
-\r
-   while ( bp < buf + cksize ) {\r
-      nv = sgetU2( &bp );\r
-      nverts += nv;\r
-      npols++;\r
-      bp += 2 * nv;\r
-      i = sgetI2( &bp );\r
-      if ( i < 0 ) bp += 2;      /* detail polygons */\r
-   }\r
-\r
-   if ( !lwAllocPolygons( plist, npols, nverts ))\r
-      goto Fail;\r
-\r
-   /* fill in the new polygons */\r
-\r
-   bp = buf;\r
-   pp = plist->pol + plist->offset;\r
-   pv = plist->pol[ 0 ].v + plist->voffset;\r
-\r
-   for ( i = 0; i < npols; i++ ) {\r
-      nv = sgetU2( &bp );\r
-\r
-      pp->nverts = nv;\r
-      pp->type = ID_FACE;\r
-      if ( !pp->v ) pp->v = pv;\r
-      for ( j = 0; j < nv; j++ )\r
-         pv[ j ].index = sgetU2( &bp ) + ptoffset;\r
-      j = sgetI2( &bp );\r
-      if ( j < 0 ) {\r
-         j = -j;\r
-         bp += 2;\r
-      }\r
-      j -= 1;\r
-      pp->surf = ( lwSurface * ) j;\r
-\r
-      pp++;\r
-      pv += nv;\r
-   }\r
-\r
-   _pico_free( buf );\r
-   return 1;\r
-\r
-Fail:\r
-   if ( buf ) _pico_free( buf );\r
-   lwFreePolygons( plist );\r
-   return 0;\r
-}\r
-\r
-\r
-/*\r
-======================================================================\r
-getLWObject5()\r
-\r
-Returns the contents of an LWOB, given its filename, or NULL if the\r
-file couldn't be loaded.  On failure, failID and failpos can be used\r
-to diagnose the cause.\r
-\r
-1.  If the file isn't an LWOB, failpos will contain 12 and failID will\r
-    be unchanged.\r
-\r
-2.  If an error occurs while reading an LWOB, failID will contain the\r
-    most recently read IFF chunk ID, and failpos will contain the\r
-    value returned by _pico_memstream_tell() at the time of the failure.\r
-\r
-3.  If the file couldn't be opened, or an error occurs while reading\r
-    the first 12 bytes, both failID and failpos will be unchanged.\r
-\r
-If you don't need this information, failID and failpos can be NULL.\r
-====================================================================== */\r
-\r
-lwObject *lwGetObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos )\r
-{\r
-   lwObject *object;\r
-   lwLayer *layer;\r
-   lwNode *node;\r
-   unsigned int id, formsize, type, cksize;\r
-\r
-\r
-   /* open the file */\r
-\r
-   if ( !fp ) return NULL;\r
-\r
-   /* read the first 12 bytes */\r
-\r
-   set_flen( 0 );\r
-   id       = getU4( fp );\r
-   formsize = getU4( fp );\r
-   type     = getU4( fp );\r
-   if ( 12 != get_flen() ) {\r
-      return NULL;\r
-   }\r
-\r
-   /* LWOB? */\r
-\r
-   if ( id != ID_FORM || type != ID_LWOB ) {\r
-      if ( failpos ) *failpos = 12;\r
-      return NULL;\r
-   }\r
-\r
-   /* allocate an object and a default layer */\r
-\r
-   object = _pico_calloc( 1, sizeof( lwObject ));\r
-   if ( !object ) goto Fail;\r
-\r
-   layer = _pico_calloc( 1, sizeof( lwLayer ));\r
-   if ( !layer ) goto Fail;\r
-   object->layer = layer;\r
-   object->nlayers = 1;\r
-\r
-   /* get the first chunk header */\r
-\r
-   id = getU4( fp );\r
-   cksize = getU4( fp );\r
-   if ( 0 > get_flen() ) goto Fail;\r
-\r
-   /* process chunks as they're encountered */\r
-\r
-   while ( 1 ) {\r
-      cksize += cksize & 1;\r
-\r
-      switch ( id )\r
-      {\r
-         case ID_PNTS:\r
-            if ( !lwGetPoints( fp, cksize, &layer->point ))\r
-               goto Fail;\r
-            break;\r
-\r
-         case ID_POLS:\r
-            if ( !lwGetPolygons5( fp, cksize, &layer->polygon,\r
-               layer->point.offset ))\r
-               goto Fail;\r
-            break;\r
-\r
-         case ID_SRFS:\r
-            if ( !lwGetTags( fp, cksize, &object->taglist ))\r
-               goto Fail;\r
-            break;\r
-\r
-         case ID_SURF:\r
-            node = ( lwNode * ) lwGetSurface5( fp, cksize, object );\r
-            if ( !node ) goto Fail;\r
-            lwListAdd( &object->surf, node );\r
-            object->nsurfs++;\r
-            break;\r
-\r
-         default:\r
-            _pico_memstream_seek( fp, cksize, PICO_SEEK_CUR );\r
-            break;\r
-      }\r
-\r
-      /* end of the file? */\r
-\r
-      if ( formsize <= _pico_memstream_tell( fp ) - 8 ) break;\r
-\r
-      /* get the next chunk header */\r
-\r
-      set_flen( 0 );\r
-      id = getU4( fp );\r
-      cksize = getU4( fp );\r
-      if ( 8 != get_flen() ) goto Fail;\r
-   }\r
-\r
-   lwGetBoundingBox( &layer->point, layer->bbox );\r
-   lwGetPolyNormals( &layer->point, &layer->polygon );\r
-   if ( !lwGetPointPolygons( &layer->point, &layer->polygon )) goto Fail;\r
-   if ( !lwResolvePolySurfaces( &layer->polygon, &object->taglist,\r
-      &object->surf, &object->nsurfs )) goto Fail;\r
-   lwGetVertNormals( &layer->point, &layer->polygon );\r
-\r
-   return object;\r
-\r
-Fail:\r
-   if ( failID ) *failID = id;\r
-   if ( fp ) {\r
-      if ( failpos ) *failpos = _pico_memstream_tell( fp );\r
-   }\r
-   lwFreeObject( object );\r
-   return NULL;\r
-}\r
-\r
-int lwValidateObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos )\r
-{\r
-   unsigned int id, formsize, type;\r
-\r
-\r
-   /* open the file */\r
-\r
-   if ( !fp ) return PICO_PMV_ERROR_MEMORY;\r
-\r
-   /* read the first 12 bytes */\r
-\r
-   set_flen( 0 );\r
-   id       = getU4( fp );\r
-   formsize = getU4( fp );\r
-   type     = getU4( fp );\r
-   if ( 12 != get_flen() ) {\r
-      return PICO_PMV_ERROR_SIZE;\r
-   }\r
-\r
-   /* LWOB? */\r
-\r
-   if ( id != ID_FORM || type != ID_LWOB ) {\r
-      if ( failpos ) *failpos = 12;\r
-      return PICO_PMV_ERROR_IDENT;\r
-   }\r
-\r
-   return PICO_PMV_OK;\r
-}\r
+/*
+======================================================================
+lwob.c
+
+Functions for an LWOB reader.  LWOB is the LightWave object format
+for versions of LW prior to 6.0.
+
+Ernie Wright  17 Sep 00
+====================================================================== */
+
+#include "../picointernal.h"
+#include "lwo2.h"
+
+/* disable warnings */
+#ifdef _WIN32
+#pragma warning( disable:4018 )                /* signed/unsigned mismatch */
+#endif
+
+
+/* IDs specific to LWOB */
+
+#define ID_SRFS  LWID_('S','R','F','S')
+#define ID_FLAG  LWID_('F','L','A','G')
+#define ID_VLUM  LWID_('V','L','U','M')
+#define ID_VDIF  LWID_('V','D','I','F')
+#define ID_VSPC  LWID_('V','S','P','C')
+#define ID_RFLT  LWID_('R','F','L','T')
+#define ID_BTEX  LWID_('B','T','E','X')
+#define ID_CTEX  LWID_('C','T','E','X')
+#define ID_DTEX  LWID_('D','T','E','X')
+#define ID_LTEX  LWID_('L','T','E','X')
+#define ID_RTEX  LWID_('R','T','E','X')
+#define ID_STEX  LWID_('S','T','E','X')
+#define ID_TTEX  LWID_('T','T','E','X')
+#define ID_TFLG  LWID_('T','F','L','G')
+#define ID_TSIZ  LWID_('T','S','I','Z')
+#define ID_TCTR  LWID_('T','C','T','R')
+#define ID_TFAL  LWID_('T','F','A','L')
+#define ID_TVEL  LWID_('T','V','E','L')
+#define ID_TCLR  LWID_('T','C','L','R')
+#define ID_TVAL  LWID_('T','V','A','L')
+#define ID_TAMP  LWID_('T','A','M','P')
+#define ID_TIMG  LWID_('T','I','M','G')
+#define ID_TAAS  LWID_('T','A','A','S')
+#define ID_TREF  LWID_('T','R','E','F')
+#define ID_TOPC  LWID_('T','O','P','C')
+#define ID_SDAT  LWID_('S','D','A','T')
+#define ID_TFP0  LWID_('T','F','P','0')
+#define ID_TFP1  LWID_('T','F','P','1')
+
+
+/*
+======================================================================
+add_clip()
+
+Add a clip to the clip list.  Used to store the contents of an RIMG or
+TIMG surface subchunk.
+====================================================================== */
+
+static int add_clip( char *s, lwClip **clist, int *nclips )
+{
+   lwClip *clip;
+   char *p;
+
+   clip = _pico_calloc( 1, sizeof( lwClip ));
+   if ( !clip ) return 0;
+
+   clip->contrast.val = 1.0f;
+   clip->brightness.val = 1.0f;
+   clip->saturation.val = 1.0f;
+   clip->gamma.val = 1.0f;
+
+   if ( p = strstr( s, "(sequence)" )) {
+      p[ -1 ] = 0;
+      clip->type = ID_ISEQ;
+      clip->source.seq.prefix = s;
+      clip->source.seq.digits = 3;
+   }
+   else {
+      clip->type = ID_STIL;
+      clip->source.still.name = s;
+   }
+
+   *nclips++;
+   clip->index = *nclips;
+
+   lwListAdd( clist, clip );
+
+   return clip->index;
+}
+
+
+/*
+======================================================================
+add_tvel()
+
+Add a triple of envelopes to simulate the old texture velocity
+parameters.
+====================================================================== */
+
+static int add_tvel( float pos[], float vel[], lwEnvelope **elist, int *nenvs )
+{
+   lwEnvelope *env;
+   lwKey *key0, *key1;
+   int i;
+
+   for ( i = 0; i < 3; i++ ) {
+      env = _pico_calloc( 1, sizeof( lwEnvelope ));
+      key0 = _pico_calloc( 1, sizeof( lwKey ));
+      key1 = _pico_calloc( 1, sizeof( lwKey ));
+      if ( !env || !key0 || !key1 ) return 0;
+
+      key0->next = key1;
+      key0->value = pos[ i ];
+      key0->time = 0.0f;
+      key1->prev = key0;
+      key1->value = pos[ i ] + vel[ i ] * 30.0f;
+      key1->time = 1.0f;
+      key0->shape = key1->shape = ID_LINE;
+
+      env->index = *nenvs + i + 1;
+      env->type = 0x0301 + i;
+      env->name = _pico_alloc( 11 );
+      if ( env->name ) {
+         strcpy( env->name, "Position.X" );
+         env->name[ 9 ] += i;
+      }
+      env->key = key0;
+      env->nkeys = 2;
+      env->behavior[ 0 ] = BEH_LINEAR;
+      env->behavior[ 1 ] = BEH_LINEAR;
+
+      lwListAdd( elist, env );
+   }
+
+   *nenvs += 3;
+   return env->index - 2;
+}
+
+
+/*
+======================================================================
+get_texture()
+
+Create a new texture for BTEX, CTEX, etc. subchunks.
+====================================================================== */
+
+static lwTexture *get_texture( char *s )
+{
+   lwTexture *tex;
+
+   tex = _pico_calloc( 1, sizeof( lwTexture ));
+   if ( !tex ) return NULL;
+
+   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;
+
+   if ( strstr( s, "Image Map" )) {
+      tex->type = ID_IMAP;
+      if ( strstr( s, "Planar" ))           tex->param.imap.projection = 0;
+      else if ( strstr( s, "Cylindrical" )) tex->param.imap.projection = 1;
+      else if ( strstr( s, "Spherical" ))   tex->param.imap.projection = 2;
+      else if ( strstr( s, "Cubic" ))       tex->param.imap.projection = 3;
+      else if ( strstr( s, "Front" ))       tex->param.imap.projection = 4;
+      tex->param.imap.aa_strength = 1.0f;
+      tex->param.imap.amplitude.val = 1.0f;
+      _pico_free( s );
+   }
+   else {
+      tex->type = ID_PROC;
+      tex->param.proc.name = s;
+   }
+
+   return tex;
+}
+
+
+/*
+======================================================================
+lwGetSurface5()
+
+Read an lwSurface from an LWOB file.
+====================================================================== */
+
+lwSurface *lwGetSurface5( picoMemStream_t *fp, int cksize, lwObject *obj )
+{
+   lwSurface *surf;
+   lwTexture *tex;
+   lwPlugin *shdr;
+   char *s;
+   float v[ 3 ];
+   unsigned int id, flags;
+   unsigned short sz;
+   int pos, rlen, i;
+
+
+   /* 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 );
+
+   /* name */
+
+   surf->name = 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 ] = getU1( fp ) / 255.0f;
+            surf->color.rgb[ 1 ] = getU1( fp ) / 255.0f;
+            surf->color.rgb[ 2 ] = getU1( fp ) / 255.0f;
+            break;
+
+         case ID_FLAG:
+            flags = getU2( fp );
+            if ( flags &   4 ) surf->smooth = 1.56207f;
+            if ( flags &   8 ) surf->color_hilite.val = 1.0f;
+            if ( flags &  16 ) surf->color_filter.val = 1.0f;
+            if ( flags & 128 ) surf->dif_sharp.val = 0.5f;
+            if ( flags & 256 ) surf->sideflags = 3;
+            if ( flags & 512 ) surf->add_trans.val = 1.0f;
+            break;
+
+         case ID_LUMI:
+            surf->luminosity.val = getI2( fp ) / 256.0f;
+            break;
+
+         case ID_VLUM:
+            surf->luminosity.val = getF4( fp );
+            break;
+
+         case ID_DIFF:
+            surf->diffuse.val = getI2( fp ) / 256.0f;
+            break;
+
+         case ID_VDIF:
+            surf->diffuse.val = getF4( fp );
+            break;
+
+         case ID_SPEC:
+            surf->specularity.val = getI2( fp ) / 256.0f;
+            break;
+
+         case ID_VSPC:
+            surf->specularity.val = getF4( fp );
+            break;
+
+         case ID_GLOS:
+            surf->glossiness.val = ( float ) log( getU2( fp )) / 20.7944f;
+            break;
+
+         case ID_SMAN:
+            surf->smooth = getF4( fp );
+            break;
+
+         case ID_REFL:
+            surf->reflection.val.val = getI2( fp ) / 256.0f;
+            break;
+
+         case ID_RFLT:
+            surf->reflection.options = getU2( fp );
+            break;
+
+         case ID_RIMG:
+            s = getS0( fp );
+            surf->reflection.cindex = add_clip( s, &obj->clip, &obj->nclips );
+            surf->reflection.options = 3;
+            break;
+
+         case ID_RSAN:
+            surf->reflection.seam_angle = getF4( fp );
+            break;
+
+         case ID_TRAN:
+            surf->transparency.val.val = getI2( fp ) / 256.0f;
+            break;
+
+         case ID_RIND:
+            surf->eta.val = getF4( fp );
+            break;
+
+         case ID_BTEX:
+            s = getbytes( fp, sz );
+            tex = get_texture( s );
+            lwListAdd( &surf->bump.tex, tex );
+            break;
+
+         case ID_CTEX:
+            s = getbytes( fp, sz );
+            tex = get_texture( s );
+            lwListAdd( &surf->color.tex, tex );
+            break;
+
+         case ID_DTEX:
+            s = getbytes( fp, sz );
+            tex = get_texture( s );
+            lwListAdd( &surf->diffuse.tex, tex );
+            break;
+
+         case ID_LTEX:
+            s = getbytes( fp, sz );
+            tex = get_texture( s );
+            lwListAdd( &surf->luminosity.tex, tex );
+            break;
+
+         case ID_RTEX:
+            s = getbytes( fp, sz );
+            tex = get_texture( s );
+            lwListAdd( &surf->reflection.val.tex, tex );
+            break;
+
+         case ID_STEX:
+            s = getbytes( fp, sz );
+            tex = get_texture( s );
+            lwListAdd( &surf->specularity.tex, tex );
+            break;
+
+         case ID_TTEX:
+            s = getbytes( fp, sz );
+            tex = get_texture( s );
+            lwListAdd( &surf->transparency.val.tex, tex );
+            break;
+
+         case ID_TFLG:
+            flags = getU2( fp );
+
+            if ( flags & 1 ) i = 0;
+            if ( flags & 2 ) i = 1;
+            if ( flags & 4 ) i = 2;
+            tex->axis = i;
+            if ( tex->type == ID_IMAP )
+               tex->param.imap.axis = i;
+            else
+               tex->param.proc.axis = i;
+
+            if ( flags &  8 ) tex->tmap.coord_sys = 1;
+            if ( flags & 16 ) tex->negative = 1;
+            if ( flags & 32 ) tex->param.imap.pblend = 1;
+            if ( flags & 64 ) {
+               tex->param.imap.aa_strength = 1.0f;
+               tex->param.imap.aas_flags = 1;
+            }
+            break;
+
+         case ID_TSIZ:
+            for ( i = 0; i < 3; i++ )
+               tex->tmap.size.val[ i ] = getF4( fp );
+            break;
+
+         case ID_TCTR:
+            for ( i = 0; i < 3; i++ )
+               tex->tmap.center.val[ i ] = getF4( fp );
+            break;
+
+         case ID_TFAL:
+            for ( i = 0; i < 3; i++ )
+               tex->tmap.falloff.val[ i ] = getF4( fp );
+            break;
+
+         case ID_TVEL:
+            for ( i = 0; i < 3; i++ )
+               v[ i ] = getF4( fp );
+            tex->tmap.center.eindex = add_tvel( tex->tmap.center.val, v,
+               &obj->env, &obj->nenvs );
+            break;
+
+         case ID_TCLR:
+            if ( tex->type == ID_PROC )
+               for ( i = 0; i < 3; i++ )
+                  tex->param.proc.value[ i ] = getU1( fp ) / 255.0f;
+            break;
+
+         case ID_TVAL:
+            tex->param.proc.value[ 0 ] = getI2( fp ) / 256.0f;
+            break;
+
+         case ID_TAMP:
+            if ( tex->type == ID_IMAP )
+               tex->param.imap.amplitude.val = getF4( fp );
+            break;
+
+         case ID_TIMG:
+            s = getS0( fp );
+            tex->param.imap.cindex = add_clip( s, &obj->clip, &obj->nclips );
+            break;
+
+         case ID_TAAS:
+            tex->param.imap.aa_strength = getF4( fp );
+            tex->param.imap.aas_flags = 1;
+            break;
+
+         case ID_TREF:
+            tex->tmap.ref_object = getbytes( fp, sz );
+            break;
+
+         case ID_TOPC:
+            tex->opacity.val = getF4( fp );
+            break;
+
+         case ID_TFP0:
+            if ( tex->type == ID_IMAP )
+               tex->param.imap.wrapw.val = getF4( fp );
+            break;
+
+         case ID_TFP1:
+            if ( tex->type == ID_IMAP )
+               tex->param.imap.wraph.val = getF4( fp );
+            break;
+
+         case ID_SHDR:
+            shdr = _pico_calloc( 1, sizeof( lwPlugin ));
+            if ( !shdr ) goto Fail;
+            shdr->name = getbytes( fp, sz );
+            lwListAdd( &surf->shader, shdr );
+            surf->nshaders++;
+            break;
+
+         case ID_SDAT:
+            shdr->data = getbytes( fp, sz );
+            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;
+}
+
+
+/*
+======================================================================
+lwGetPolygons5()
+
+Read polygon records from a POLS chunk in an LWOB file.  The polygons
+are added to the array in the lwPolygonList.
+====================================================================== */
+
+int lwGetPolygons5( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset )
+{
+   lwPolygon *pp;
+   lwPolVert *pv;
+   unsigned char *buf, *bp;
+   int i, j, nv, nverts, npols;
+
+
+   if ( cksize == 0 ) return 1;
+
+   /* read the whole chunk */
+
+   set_flen( 0 );
+   buf = getbytes( fp, cksize );
+   if ( !buf ) goto Fail;
+
+   /* count the polygons and vertices */
+
+   nverts = 0;
+   npols = 0;
+   bp = buf;
+
+   while ( bp < buf + cksize ) {
+      nv = sgetU2( &bp );
+      nverts += nv;
+      npols++;
+      bp += 2 * nv;
+      i = sgetI2( &bp );
+      if ( i < 0 ) bp += 2;      /* detail polygons */
+   }
+
+   if ( !lwAllocPolygons( plist, npols, nverts ))
+      goto Fail;
+
+   /* fill in the new polygons */
+
+   bp = buf;
+   pp = plist->pol + plist->offset;
+   pv = plist->pol[ 0 ].v + plist->voffset;
+
+   for ( i = 0; i < npols; i++ ) {
+      nv = sgetU2( &bp );
+
+      pp->nverts = nv;
+      pp->type = ID_FACE;
+      if ( !pp->v ) pp->v = pv;
+      for ( j = 0; j < nv; j++ )
+         pv[ j ].index = sgetU2( &bp ) + ptoffset;
+      j = sgetI2( &bp );
+      if ( j < 0 ) {
+         j = -j;
+         bp += 2;
+      }
+      j -= 1;
+      pp->surf = ( lwSurface * ) j;
+
+      pp++;
+      pv += nv;
+   }
+
+   _pico_free( buf );
+   return 1;
+
+Fail:
+   if ( buf ) _pico_free( buf );
+   lwFreePolygons( plist );
+   return 0;
+}
+
+
+/*
+======================================================================
+getLWObject5()
+
+Returns the contents of an LWOB, given its filename, or NULL if the
+file couldn't be loaded.  On failure, failID and failpos can be used
+to diagnose the cause.
+
+1.  If the file isn't an LWOB, failpos will contain 12 and failID will
+    be unchanged.
+
+2.  If an error occurs while reading an LWOB, failID will contain the
+    most recently read IFF chunk ID, and failpos will contain the
+    value returned by _pico_memstream_tell() at the time of the failure.
+
+3.  If the file couldn't be opened, or an error occurs while reading
+    the first 12 bytes, both failID and failpos will be unchanged.
+
+If you don't need this information, failID and failpos can be NULL.
+====================================================================== */
+
+lwObject *lwGetObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos )
+{
+   lwObject *object;
+   lwLayer *layer;
+   lwNode *node;
+   unsigned int id, formsize, type, cksize;
+
+
+   /* open the file */
+
+   if ( !fp ) return NULL;
+
+   /* read the first 12 bytes */
+
+   set_flen( 0 );
+   id       = getU4( fp );
+   formsize = getU4( fp );
+   type     = getU4( fp );
+   if ( 12 != get_flen() ) {
+      return NULL;
+   }
+
+   /* LWOB? */
+
+   if ( id != ID_FORM || type != ID_LWOB ) {
+      if ( failpos ) *failpos = 12;
+      return NULL;
+   }
+
+   /* allocate an object and a default layer */
+
+   object = _pico_calloc( 1, sizeof( lwObject ));
+   if ( !object ) goto Fail;
+
+   layer = _pico_calloc( 1, sizeof( lwLayer ));
+   if ( !layer ) goto Fail;
+   object->layer = layer;
+   object->nlayers = 1;
+
+   /* get the first chunk header */
+
+   id = getU4( fp );
+   cksize = getU4( fp );
+   if ( 0 > get_flen() ) goto Fail;
+
+   /* process chunks as they're encountered */
+
+   while ( 1 ) {
+      cksize += cksize & 1;
+
+      switch ( id )
+      {
+         case ID_PNTS:
+            if ( !lwGetPoints( fp, cksize, &layer->point ))
+               goto Fail;
+            break;
+
+         case ID_POLS:
+            if ( !lwGetPolygons5( fp, cksize, &layer->polygon,
+               layer->point.offset ))
+               goto Fail;
+            break;
+
+         case ID_SRFS:
+            if ( !lwGetTags( fp, cksize, &object->taglist ))
+               goto Fail;
+            break;
+
+         case ID_SURF:
+            node = ( lwNode * ) lwGetSurface5( fp, cksize, object );
+            if ( !node ) goto Fail;
+            lwListAdd( &object->surf, node );
+            object->nsurfs++;
+            break;
+
+         default:
+            _pico_memstream_seek( fp, cksize, PICO_SEEK_CUR );
+            break;
+      }
+
+      /* end of the file? */
+
+      if ( formsize <= _pico_memstream_tell( fp ) - 8 ) break;
+
+      /* get the next chunk header */
+
+      set_flen( 0 );
+      id = getU4( fp );
+      cksize = getU4( fp );
+      if ( 8 != get_flen() ) goto Fail;
+   }
+
+   lwGetBoundingBox( &layer->point, layer->bbox );
+   lwGetPolyNormals( &layer->point, &layer->polygon );
+   if ( !lwGetPointPolygons( &layer->point, &layer->polygon )) goto Fail;
+   if ( !lwResolvePolySurfaces( &layer->polygon, &object->taglist,
+      &object->surf, &object->nsurfs )) goto Fail;
+   lwGetVertNormals( &layer->point, &layer->polygon );
+
+   return object;
+
+Fail:
+   if ( failID ) *failID = id;
+   if ( fp ) {
+      if ( failpos ) *failpos = _pico_memstream_tell( fp );
+   }
+   lwFreeObject( object );
+   return NULL;
+}
+
+int lwValidateObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos )
+{
+   unsigned int id, formsize, type;
+
+
+   /* open the file */
+
+   if ( !fp ) return PICO_PMV_ERROR_MEMORY;
+
+   /* read the first 12 bytes */
+
+   set_flen( 0 );
+   id       = getU4( fp );
+   formsize = getU4( fp );
+   type     = getU4( fp );
+   if ( 12 != get_flen() ) {
+      return PICO_PMV_ERROR_SIZE;
+   }
+
+   /* LWOB? */
+
+   if ( id != ID_FORM || type != ID_LWOB ) {
+      if ( failpos ) *failpos = 12;
+      return PICO_PMV_ERROR_IDENT;
+   }
+
+   return PICO_PMV_OK;
+}