]> icculus.org git repositories - divverent/netradiant.git/blob - libs/picomodel/lwo/lwob.c
initial
[divverent/netradiant.git] / libs / picomodel / lwo / lwob.c
1 /*
2 ======================================================================
3 lwob.c
4
5 Functions for an LWOB reader.  LWOB is the LightWave object format
6 for versions of LW prior to 6.0.
7
8 Ernie Wright  17 Sep 00
9 ====================================================================== */
10
11 #include "../picointernal.h"
12 #include "lwo2.h"
13
14 /* disable warnings */
15 #ifdef WIN32
16 #pragma warning( disable:4018 )         /* signed/unsigned mismatch */
17 #endif
18
19
20 /* IDs specific to LWOB */
21
22 #define ID_SRFS  LWID_('S','R','F','S')
23 #define ID_FLAG  LWID_('F','L','A','G')
24 #define ID_VLUM  LWID_('V','L','U','M')
25 #define ID_VDIF  LWID_('V','D','I','F')
26 #define ID_VSPC  LWID_('V','S','P','C')
27 #define ID_RFLT  LWID_('R','F','L','T')
28 #define ID_BTEX  LWID_('B','T','E','X')
29 #define ID_CTEX  LWID_('C','T','E','X')
30 #define ID_DTEX  LWID_('D','T','E','X')
31 #define ID_LTEX  LWID_('L','T','E','X')
32 #define ID_RTEX  LWID_('R','T','E','X')
33 #define ID_STEX  LWID_('S','T','E','X')
34 #define ID_TTEX  LWID_('T','T','E','X')
35 #define ID_TFLG  LWID_('T','F','L','G')
36 #define ID_TSIZ  LWID_('T','S','I','Z')
37 #define ID_TCTR  LWID_('T','C','T','R')
38 #define ID_TFAL  LWID_('T','F','A','L')
39 #define ID_TVEL  LWID_('T','V','E','L')
40 #define ID_TCLR  LWID_('T','C','L','R')
41 #define ID_TVAL  LWID_('T','V','A','L')
42 #define ID_TAMP  LWID_('T','A','M','P')
43 #define ID_TIMG  LWID_('T','I','M','G')
44 #define ID_TAAS  LWID_('T','A','A','S')
45 #define ID_TREF  LWID_('T','R','E','F')
46 #define ID_TOPC  LWID_('T','O','P','C')
47 #define ID_SDAT  LWID_('S','D','A','T')
48 #define ID_TFP0  LWID_('T','F','P','0')
49 #define ID_TFP1  LWID_('T','F','P','1')
50
51
52 /*
53 ======================================================================
54 add_clip()
55
56 Add a clip to the clip list.  Used to store the contents of an RIMG or
57 TIMG surface subchunk.
58 ====================================================================== */
59
60 static int add_clip( char *s, lwClip **clist, int *nclips )
61 {
62    lwClip *clip;
63    char *p;
64
65    clip = _pico_calloc( 1, sizeof( lwClip ));
66    if ( !clip ) return 0;
67
68    clip->contrast.val = 1.0f;
69    clip->brightness.val = 1.0f;
70    clip->saturation.val = 1.0f;
71    clip->gamma.val = 1.0f;
72
73    if ( p = strstr( s, "(sequence)" )) {
74       p[ -1 ] = 0;
75       clip->type = ID_ISEQ;
76       clip->source.seq.prefix = s;
77       clip->source.seq.digits = 3;
78    }
79    else {
80       clip->type = ID_STIL;
81       clip->source.still.name = s;
82    }
83
84    *nclips++;
85    clip->index = *nclips;
86
87    lwListAdd( (void *) clist, clip );
88
89    return clip->index;
90 }
91
92
93 /*
94 ======================================================================
95 add_tvel()
96
97 Add a triple of envelopes to simulate the old texture velocity
98 parameters.
99 ====================================================================== */
100
101 static int add_tvel( float pos[], float vel[], lwEnvelope **elist, int *nenvs )
102 {
103    lwEnvelope *env;
104    lwKey *key0, *key1;
105    int i;
106
107    for ( i = 0; i < 3; i++ ) {
108       env = _pico_calloc( 1, sizeof( lwEnvelope ));
109       key0 = _pico_calloc( 1, sizeof( lwKey ));
110       key1 = _pico_calloc( 1, sizeof( lwKey ));
111       if ( !env || !key0 || !key1 ) return 0;
112
113       key0->next = key1;
114       key0->value = pos[ i ];
115       key0->time = 0.0f;
116       key1->prev = key0;
117       key1->value = pos[ i ] + vel[ i ] * 30.0f;
118       key1->time = 1.0f;
119       key0->shape = key1->shape = ID_LINE;
120
121       env->index = *nenvs + i + 1;
122       env->type = 0x0301 + i;
123       env->name = _pico_alloc( 11 );
124       if ( env->name ) {
125          strcpy( env->name, "Position.X" );
126          env->name[ 9 ] += i;
127       }
128       env->key = key0;
129       env->nkeys = 2;
130       env->behavior[ 0 ] = BEH_LINEAR;
131       env->behavior[ 1 ] = BEH_LINEAR;
132
133       lwListAdd( (void *) elist, env );
134    }
135
136    *nenvs += 3;
137    return env->index - 2;
138 }
139
140
141 /*
142 ======================================================================
143 get_texture()
144
145 Create a new texture for BTEX, CTEX, etc. subchunks.
146 ====================================================================== */
147
148 static lwTexture *get_texture( char *s )
149 {
150    lwTexture *tex;
151
152    tex = _pico_calloc( 1, sizeof( lwTexture ));
153    if ( !tex ) return NULL;
154
155    tex->tmap.size.val[ 0 ] =
156    tex->tmap.size.val[ 1 ] =
157    tex->tmap.size.val[ 2 ] = 1.0f;
158    tex->opacity.val = 1.0f;
159    tex->enabled = 1;
160
161    if ( strstr( s, "Image Map" )) {
162       tex->type = ID_IMAP;
163       if ( strstr( s, "Planar" ))           tex->param.imap.projection = 0;
164       else if ( strstr( s, "Cylindrical" )) tex->param.imap.projection = 1;
165       else if ( strstr( s, "Spherical" ))   tex->param.imap.projection = 2;
166       else if ( strstr( s, "Cubic" ))       tex->param.imap.projection = 3;
167       else if ( strstr( s, "Front" ))       tex->param.imap.projection = 4;
168       tex->param.imap.aa_strength = 1.0f;
169       tex->param.imap.amplitude.val = 1.0f;
170       _pico_free( s );
171    }
172    else {
173       tex->type = ID_PROC;
174       tex->param.proc.name = s;
175    }
176
177    return tex;
178 }
179
180
181 /*
182 ======================================================================
183 lwGetSurface5()
184
185 Read an lwSurface from an LWOB file.
186 ====================================================================== */
187
188 lwSurface *lwGetSurface5( picoMemStream_t *fp, int cksize, lwObject *obj )
189 {
190    lwSurface *surf;
191    lwTexture *tex;
192    lwPlugin *shdr;
193    char *s;
194    float v[ 3 ];
195    unsigned int id, flags;
196    unsigned short sz;
197    int pos, rlen, i;
198
199
200    /* allocate the Surface structure */
201
202    surf = _pico_calloc( 1, sizeof( lwSurface ));
203    if ( !surf ) goto Fail;
204
205    /* non-zero defaults */
206
207    surf->color.rgb[ 0 ] = 0.78431f;
208    surf->color.rgb[ 1 ] = 0.78431f;
209    surf->color.rgb[ 2 ] = 0.78431f;
210    surf->diffuse.val    = 1.0f;
211    surf->glossiness.val = 0.4f;
212    surf->bump.val       = 1.0f;
213    surf->eta.val        = 1.0f;
214    surf->sideflags      = 1;
215
216    /* remember where we started */
217
218    set_flen( 0 );
219    pos = _pico_memstream_tell( fp );
220
221    /* name */
222
223    surf->name = getS0( fp );
224
225    /* first subchunk header */
226
227    id = getU4( fp );
228    sz = getU2( fp );
229    if ( 0 > get_flen() ) goto Fail;
230
231    /* process subchunks as they're encountered */
232
233    while ( 1 ) {
234       sz += sz & 1;
235       set_flen( 0 );
236
237       switch ( id ) {
238          case ID_COLR:
239             surf->color.rgb[ 0 ] = getU1( fp ) / 255.0f;
240             surf->color.rgb[ 1 ] = getU1( fp ) / 255.0f;
241             surf->color.rgb[ 2 ] = getU1( fp ) / 255.0f;
242             break;
243
244          case ID_FLAG:
245             flags = getU2( fp );
246             if ( flags &   4 ) surf->smooth = 1.56207f;
247             if ( flags &   8 ) surf->color_hilite.val = 1.0f;
248             if ( flags &  16 ) surf->color_filter.val = 1.0f;
249             if ( flags & 128 ) surf->dif_sharp.val = 0.5f;
250             if ( flags & 256 ) surf->sideflags = 3;
251             if ( flags & 512 ) surf->add_trans.val = 1.0f;
252             break;
253
254          case ID_LUMI:
255             surf->luminosity.val = getI2( fp ) / 256.0f;
256             break;
257
258          case ID_VLUM:
259             surf->luminosity.val = getF4( fp );
260             break;
261
262          case ID_DIFF:
263             surf->diffuse.val = getI2( fp ) / 256.0f;
264             break;
265
266          case ID_VDIF:
267             surf->diffuse.val = getF4( fp );
268             break;
269
270          case ID_SPEC:
271             surf->specularity.val = getI2( fp ) / 256.0f;
272             break;
273
274          case ID_VSPC:
275             surf->specularity.val = getF4( fp );
276             break;
277
278          case ID_GLOS:
279             surf->glossiness.val = ( float ) log( getU2( fp )) / 20.7944f;
280             break;
281
282          case ID_SMAN:
283             surf->smooth = getF4( fp );
284             break;
285
286          case ID_REFL:
287             surf->reflection.val.val = getI2( fp ) / 256.0f;
288             break;
289
290          case ID_RFLT:
291             surf->reflection.options = getU2( fp );
292             break;
293
294          case ID_RIMG:
295             s = getS0( fp );
296             surf->reflection.cindex = add_clip( s, &obj->clip, &obj->nclips );
297             surf->reflection.options = 3;
298             break;
299
300          case ID_RSAN:
301             surf->reflection.seam_angle = getF4( fp );
302             break;
303
304          case ID_TRAN:
305             surf->transparency.val.val = getI2( fp ) / 256.0f;
306             break;
307
308          case ID_RIND:
309             surf->eta.val = getF4( fp );
310             break;
311
312          case ID_BTEX:
313             s = getbytes( fp, sz );
314             tex = get_texture( s );
315             lwListAdd( (void *) &surf->bump.tex, tex );
316             break;
317
318          case ID_CTEX:
319             s = getbytes( fp, sz );
320             tex = get_texture( s );
321             lwListAdd( (void *) &surf->color.tex, tex );
322             break;
323
324          case ID_DTEX:
325             s = getbytes( fp, sz );
326             tex = get_texture( s );
327             lwListAdd( (void *) &surf->diffuse.tex, tex );
328             break;
329
330          case ID_LTEX:
331             s = getbytes( fp, sz );
332             tex = get_texture( s );
333             lwListAdd( (void *) &surf->luminosity.tex, tex );
334             break;
335
336          case ID_RTEX:
337             s = getbytes( fp, sz );
338             tex = get_texture( s );
339             lwListAdd( (void *) &surf->reflection.val.tex, tex );
340             break;
341
342          case ID_STEX:
343             s = getbytes( fp, sz );
344             tex = get_texture( s );
345             lwListAdd( (void *) &surf->specularity.tex, tex );
346             break;
347
348          case ID_TTEX:
349             s = getbytes( fp, sz );
350             tex = get_texture( s );
351             lwListAdd( (void *) &surf->transparency.val.tex, tex );
352             break;
353
354          case ID_TFLG:
355             flags = getU2( fp );
356
357             if ( flags & 1 ) i = 0;
358             if ( flags & 2 ) i = 1;
359             if ( flags & 4 ) i = 2;
360             tex->axis = i;
361             if ( tex->type == ID_IMAP )
362                tex->param.imap.axis = i;
363             else
364                tex->param.proc.axis = i;
365
366             if ( flags &  8 ) tex->tmap.coord_sys = 1;
367             if ( flags & 16 ) tex->negative = 1;
368             if ( flags & 32 ) tex->param.imap.pblend = 1;
369             if ( flags & 64 ) {
370                tex->param.imap.aa_strength = 1.0f;
371                tex->param.imap.aas_flags = 1;
372             }
373             break;
374
375          case ID_TSIZ:
376             for ( i = 0; i < 3; i++ )
377                tex->tmap.size.val[ i ] = getF4( fp );
378             break;
379
380          case ID_TCTR:
381             for ( i = 0; i < 3; i++ )
382                tex->tmap.center.val[ i ] = getF4( fp );
383             break;
384
385          case ID_TFAL:
386             for ( i = 0; i < 3; i++ )
387                tex->tmap.falloff.val[ i ] = getF4( fp );
388             break;
389
390          case ID_TVEL:
391             for ( i = 0; i < 3; i++ )
392                v[ i ] = getF4( fp );
393             tex->tmap.center.eindex = add_tvel( tex->tmap.center.val, v,
394                &obj->env, &obj->nenvs );
395             break;
396
397          case ID_TCLR:
398             if ( tex->type == ID_PROC )
399                for ( i = 0; i < 3; i++ )
400                   tex->param.proc.value[ i ] = getU1( fp ) / 255.0f;
401             break;
402
403          case ID_TVAL:
404             tex->param.proc.value[ 0 ] = getI2( fp ) / 256.0f;
405             break;
406
407          case ID_TAMP:
408             if ( tex->type == ID_IMAP )
409                tex->param.imap.amplitude.val = getF4( fp );
410             break;
411
412          case ID_TIMG:
413             s = getS0( fp );
414             tex->param.imap.cindex = add_clip( s, &obj->clip, &obj->nclips );
415             break;
416
417          case ID_TAAS:
418             tex->param.imap.aa_strength = getF4( fp );
419             tex->param.imap.aas_flags = 1;
420             break;
421
422          case ID_TREF:
423             tex->tmap.ref_object = getbytes( fp, sz );
424             break;
425
426          case ID_TOPC:
427             tex->opacity.val = getF4( fp );
428             break;
429
430          case ID_TFP0:
431             if ( tex->type == ID_IMAP )
432                tex->param.imap.wrapw.val = getF4( fp );
433             break;
434
435          case ID_TFP1:
436             if ( tex->type == ID_IMAP )
437                tex->param.imap.wraph.val = getF4( fp );
438             break;
439
440          case ID_SHDR:
441             shdr = _pico_calloc( 1, sizeof( lwPlugin ));
442             if ( !shdr ) goto Fail;
443             shdr->name = getbytes( fp, sz );
444             lwListAdd( (void *) &surf->shader, shdr );
445             surf->nshaders++;
446             break;
447
448          case ID_SDAT:
449             shdr->data = getbytes( fp, sz );
450             break;
451
452          default:
453             break;
454       }
455
456       /* error while reading current subchunk? */
457
458       rlen = get_flen();
459       if ( rlen < 0 || rlen > sz ) goto Fail;
460
461       /* skip unread parts of the current subchunk */
462
463       if ( rlen < sz )
464          _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );
465
466       /* end of the SURF chunk? */
467
468       if ( cksize <= _pico_memstream_tell( fp ) - pos )
469          break;
470
471       /* get the next subchunk header */
472
473       set_flen( 0 );
474       id = getU4( fp );
475       sz = getU2( fp );
476       if ( 6 != get_flen() ) goto Fail;
477    }
478
479    return surf;
480
481 Fail:
482    if ( surf ) lwFreeSurface( surf );
483    return NULL;
484 }
485
486
487 /*
488 ======================================================================
489 lwGetPolygons5()
490
491 Read polygon records from a POLS chunk in an LWOB file.  The polygons
492 are added to the array in the lwPolygonList.
493 ====================================================================== */
494
495 int lwGetPolygons5( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset )
496 {
497    lwPolygon *pp;
498    lwPolVert *pv;
499    unsigned char *buf, *bp;
500    int i, j, nv, nverts, npols;
501
502
503    if ( cksize == 0 ) return 1;
504
505    /* read the whole chunk */
506
507    set_flen( 0 );
508    buf = getbytes( fp, cksize );
509    if ( !buf ) goto Fail;
510
511    /* count the polygons and vertices */
512
513    nverts = 0;
514    npols = 0;
515    bp = buf;
516
517    while ( bp < buf + cksize ) {
518       nv = sgetU2( &bp );
519       nverts += nv;
520       npols++;
521       bp += 2 * nv;
522       i = sgetI2( &bp );
523       if ( i < 0 ) bp += 2;      /* detail polygons */
524    }
525
526    if ( !lwAllocPolygons( plist, npols, nverts ))
527       goto Fail;
528
529    /* fill in the new polygons */
530
531    bp = buf;
532    pp = plist->pol + plist->offset;
533    pv = plist->pol[ 0 ].v + plist->voffset;
534
535    for ( i = 0; i < npols; i++ ) {
536       nv = sgetU2( &bp );
537
538       pp->nverts = nv;
539       pp->type = ID_FACE;
540       if ( !pp->v ) pp->v = pv;
541       for ( j = 0; j < nv; j++ )
542          pv[ j ].index = sgetU2( &bp ) + ptoffset;
543       j = sgetI2( &bp );
544       if ( j < 0 ) {
545          j = -j;
546          bp += 2;
547       }
548       j -= 1;
549       pp->surf = ( lwSurface * ) j;
550
551       pp++;
552       pv += nv;
553    }
554
555    _pico_free( buf );
556    return 1;
557
558 Fail:
559    if ( buf ) _pico_free( buf );
560    lwFreePolygons( plist );
561    return 0;
562 }
563
564
565 /*
566 ======================================================================
567 getLWObject5()
568
569 Returns the contents of an LWOB, given its filename, or NULL if the
570 file couldn't be loaded.  On failure, failID and failpos can be used
571 to diagnose the cause.
572
573 1.  If the file isn't an LWOB, failpos will contain 12 and failID will
574     be unchanged.
575
576 2.  If an error occurs while reading an LWOB, failID will contain the
577     most recently read IFF chunk ID, and failpos will contain the
578     value returned by _pico_memstream_tell() at the time of the failure.
579
580 3.  If the file couldn't be opened, or an error occurs while reading
581     the first 12 bytes, both failID and failpos will be unchanged.
582
583 If you don't need this information, failID and failpos can be NULL.
584 ====================================================================== */
585
586 lwObject *lwGetObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos )
587 {
588    lwObject *object;
589    lwLayer *layer;
590    lwNode *node;
591    unsigned int id, formsize, type, cksize;
592
593
594    /* open the file */
595
596    if ( !fp ) return NULL;
597
598    /* read the first 12 bytes */
599
600    set_flen( 0 );
601    id       = getU4( fp );
602    formsize = getU4( fp );
603    type     = getU4( fp );
604    if ( 12 != get_flen() ) {
605       return NULL;
606    }
607
608    /* LWOB? */
609
610    if ( id != ID_FORM || type != ID_LWOB ) {
611       if ( failpos ) *failpos = 12;
612       return NULL;
613    }
614
615    /* allocate an object and a default layer */
616
617    object = _pico_calloc( 1, sizeof( lwObject ));
618    if ( !object ) goto Fail;
619
620    layer = _pico_calloc( 1, sizeof( lwLayer ));
621    if ( !layer ) goto Fail;
622    object->layer = layer;
623    object->nlayers = 1;
624
625    /* get the first chunk header */
626
627    id = getU4( fp );
628    cksize = getU4( fp );
629    if ( 0 > get_flen() ) goto Fail;
630
631    /* process chunks as they're encountered */
632
633    while ( 1 ) {
634       cksize += cksize & 1;
635
636       switch ( id )
637       {
638          case ID_PNTS:
639             if ( !lwGetPoints( fp, cksize, &layer->point ))
640                goto Fail;
641             break;
642
643          case ID_POLS:
644             if ( !lwGetPolygons5( fp, cksize, &layer->polygon,
645                layer->point.offset ))
646                goto Fail;
647             break;
648
649          case ID_SRFS:
650             if ( !lwGetTags( fp, cksize, &object->taglist ))
651                goto Fail;
652             break;
653
654          case ID_SURF:
655             node = ( lwNode * ) lwGetSurface5( fp, cksize, object );
656             if ( !node ) goto Fail;
657             lwListAdd( (void *) &object->surf, node );
658             object->nsurfs++;
659             break;
660
661          default:
662             _pico_memstream_seek( fp, cksize, PICO_SEEK_CUR );
663             break;
664       }
665
666       /* end of the file? */
667
668       if ( formsize <= _pico_memstream_tell( fp ) - 8 ) break;
669
670       /* get the next chunk header */
671
672       set_flen( 0 );
673       id = getU4( fp );
674       cksize = getU4( fp );
675       if ( 8 != get_flen() ) goto Fail;
676    }
677
678    lwGetBoundingBox( &layer->point, layer->bbox );
679    lwGetPolyNormals( &layer->point, &layer->polygon );
680    if ( !lwGetPointPolygons( &layer->point, &layer->polygon )) goto Fail;
681    if ( !lwResolvePolySurfaces( &layer->polygon, &object->taglist,
682       &object->surf, &object->nsurfs )) goto Fail;
683    lwGetVertNormals( &layer->point, &layer->polygon );
684
685    return object;
686
687 Fail:
688    if ( failID ) *failID = id;
689    if ( fp ) {
690       if ( failpos ) *failpos = _pico_memstream_tell( fp );
691    }
692    lwFreeObject( object );
693    return NULL;
694 }
695
696 int lwValidateObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos )
697 {
698    unsigned int id, formsize, type;
699
700
701    /* open the file */
702
703    if ( !fp ) return PICO_PMV_ERROR_MEMORY;
704
705    /* read the first 12 bytes */
706
707    set_flen( 0 );
708    id       = getU4( fp );
709    formsize = getU4( fp );
710    type     = getU4( fp );
711    if ( 12 != get_flen() ) {
712       return PICO_PMV_ERROR_SIZE;
713    }
714
715    /* LWOB? */
716
717    if ( id != ID_FORM || type != ID_LWOB ) {
718       if ( failpos ) *failpos = 12;
719       return PICO_PMV_ERROR_IDENT;
720    }
721
722    return PICO_PMV_OK;
723 }