]> icculus.org git repositories - divverent/netradiant.git/blob - libs/picomodel/lwo/lwob.c
more warnings done. Now q3map2 is warning free apart from libpng deprecation problems.
[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 = NULL;
192    lwPlugin *shdr = NULL;
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             if(!tex) goto Fail;
356             flags = getU2( fp );
357
358             i = -1;
359             if ( flags & 1 ) i = 0;
360             if ( flags & 2 ) i = 1;
361             if ( flags & 4 ) i = 2;
362             if(i < 0) goto Fail;
363             tex->axis = i;
364             if ( tex->type == ID_IMAP )
365                tex->param.imap.axis = i;
366             else
367                tex->param.proc.axis = i;
368
369             if ( flags &  8 ) tex->tmap.coord_sys = 1;
370             if ( flags & 16 ) tex->negative = 1;
371             if ( flags & 32 ) tex->param.imap.pblend = 1;
372             if ( flags & 64 ) {
373                tex->param.imap.aa_strength = 1.0f;
374                tex->param.imap.aas_flags = 1;
375             }
376             break;
377
378          case ID_TSIZ:
379             if(!tex) goto Fail;
380             for ( i = 0; i < 3; i++ )
381                tex->tmap.size.val[ i ] = getF4( fp );
382             break;
383
384          case ID_TCTR:
385             if(!tex) goto Fail;
386             for ( i = 0; i < 3; i++ )
387                tex->tmap.center.val[ i ] = getF4( fp );
388             break;
389
390          case ID_TFAL:
391             if(!tex) goto Fail;
392             for ( i = 0; i < 3; i++ )
393                tex->tmap.falloff.val[ i ] = getF4( fp );
394             break;
395
396          case ID_TVEL:
397             if(!tex) goto Fail;
398             for ( i = 0; i < 3; i++ )
399                v[ i ] = getF4( fp );
400             tex->tmap.center.eindex = add_tvel( tex->tmap.center.val, v,
401                &obj->env, &obj->nenvs );
402             break;
403
404          case ID_TCLR:
405             if(!tex) goto Fail;
406             if ( tex->type == ID_PROC )
407                for ( i = 0; i < 3; i++ )
408                   tex->param.proc.value[ i ] = getU1( fp ) / 255.0f;
409             break;
410
411          case ID_TVAL:
412             if(!tex) goto Fail;
413             tex->param.proc.value[ 0 ] = getI2( fp ) / 256.0f;
414             break;
415
416          case ID_TAMP:
417             if(!tex) goto Fail;
418             if ( tex->type == ID_IMAP )
419                tex->param.imap.amplitude.val = getF4( fp );
420             break;
421
422          case ID_TIMG:
423             if(!tex) goto Fail;
424             s = getS0( fp );
425             tex->param.imap.cindex = add_clip( s, &obj->clip, &obj->nclips );
426             break;
427
428          case ID_TAAS:
429             if(!tex) goto Fail;
430             tex->param.imap.aa_strength = getF4( fp );
431             tex->param.imap.aas_flags = 1;
432             break;
433
434          case ID_TREF:
435             if(!tex) goto Fail;
436             tex->tmap.ref_object = getbytes( fp, sz );
437             break;
438
439          case ID_TOPC:
440             if(!tex) goto Fail;
441             tex->opacity.val = getF4( fp );
442             break;
443
444          case ID_TFP0:
445             if(!tex) goto Fail;
446             if ( tex->type == ID_IMAP )
447                tex->param.imap.wrapw.val = getF4( fp );
448             break;
449
450          case ID_TFP1:
451             if(!tex) goto Fail;
452             if ( tex->type == ID_IMAP )
453                tex->param.imap.wraph.val = getF4( fp );
454             break;
455
456          case ID_SHDR:
457             shdr = _pico_calloc( 1, sizeof( lwPlugin ));
458             if ( !shdr ) goto Fail;
459             shdr->name = getbytes( fp, sz );
460             lwListAdd( (void *) &surf->shader, shdr );
461             surf->nshaders++;
462             break;
463
464          case ID_SDAT:
465             if(!shdr) goto Fail;
466             shdr->data = getbytes( fp, sz );
467             break;
468
469          default:
470             break;
471       }
472
473       /* error while reading current subchunk? */
474
475       rlen = get_flen();
476       if ( rlen < 0 || rlen > sz ) goto Fail;
477
478       /* skip unread parts of the current subchunk */
479
480       if ( rlen < sz )
481          _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );
482
483       /* end of the SURF chunk? */
484
485       if ( cksize <= _pico_memstream_tell( fp ) - pos )
486          break;
487
488       /* get the next subchunk header */
489
490       set_flen( 0 );
491       id = getU4( fp );
492       sz = getU2( fp );
493       if ( 6 != get_flen() ) goto Fail;
494    }
495
496    return surf;
497
498 Fail:
499    if ( surf ) lwFreeSurface( surf );
500    return NULL;
501 }
502
503
504 /*
505 ======================================================================
506 lwGetPolygons5()
507
508 Read polygon records from a POLS chunk in an LWOB file.  The polygons
509 are added to the array in the lwPolygonList.
510 ====================================================================== */
511
512 int lwGetPolygons5( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset )
513 {
514    lwPolygon *pp;
515    lwPolVert *pv;
516    unsigned char *buf, *bp;
517    int i, j, nv, nverts, npols;
518
519
520    if ( cksize == 0 ) return 1;
521
522    /* read the whole chunk */
523
524    set_flen( 0 );
525    buf = getbytes( fp, cksize );
526    if ( !buf ) goto Fail;
527
528    /* count the polygons and vertices */
529
530    nverts = 0;
531    npols = 0;
532    bp = buf;
533
534    while ( bp < buf + cksize ) {
535       nv = sgetU2( &bp );
536       nverts += nv;
537       npols++;
538       bp += 2 * nv;
539       i = sgetI2( &bp );
540       if ( i < 0 ) bp += 2;      /* detail polygons */
541    }
542
543    if ( !lwAllocPolygons( plist, npols, nverts ))
544       goto Fail;
545
546    /* fill in the new polygons */
547
548    bp = buf;
549    pp = plist->pol + plist->offset;
550    pv = plist->pol[ 0 ].v + plist->voffset;
551
552    for ( i = 0; i < npols; i++ ) {
553       nv = sgetU2( &bp );
554
555       pp->nverts = nv;
556       pp->type = ID_FACE;
557       if ( !pp->v ) pp->v = pv;
558       for ( j = 0; j < nv; j++ )
559          pv[ j ].index = sgetU2( &bp ) + ptoffset;
560       j = sgetI2( &bp );
561       if ( j < 0 ) {
562          j = -j;
563          bp += 2;
564       }
565       j -= 1;
566       pp->surf = ( lwSurface * ) (size_t) j;
567
568       pp++;
569       pv += nv;
570    }
571
572    _pico_free( buf );
573    return 1;
574
575 Fail:
576    if ( buf ) _pico_free( buf );
577    lwFreePolygons( plist );
578    return 0;
579 }
580
581
582 /*
583 ======================================================================
584 getLWObject5()
585
586 Returns the contents of an LWOB, given its filename, or NULL if the
587 file couldn't be loaded.  On failure, failID and failpos can be used
588 to diagnose the cause.
589
590 1.  If the file isn't an LWOB, failpos will contain 12 and failID will
591     be unchanged.
592
593 2.  If an error occurs while reading an LWOB, failID will contain the
594     most recently read IFF chunk ID, and failpos will contain the
595     value returned by _pico_memstream_tell() at the time of the failure.
596
597 3.  If the file couldn't be opened, or an error occurs while reading
598     the first 12 bytes, both failID and failpos will be unchanged.
599
600 If you don't need this information, failID and failpos can be NULL.
601 ====================================================================== */
602
603 lwObject *lwGetObject5( const char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos )
604 {
605    lwObject *object;
606    lwLayer *layer;
607    lwNode *node;
608    unsigned int id, formsize, type, cksize;
609
610
611    /* open the file */
612
613    if ( !fp ) return NULL;
614
615    /* read the first 12 bytes */
616
617    set_flen( 0 );
618    id       = getU4( fp );
619    formsize = getU4( fp );
620    type     = getU4( fp );
621    if ( 12 != get_flen() ) {
622       return NULL;
623    }
624
625    /* LWOB? */
626
627    if ( id != ID_FORM || type != ID_LWOB ) {
628       if ( failpos ) *failpos = 12;
629       return NULL;
630    }
631
632    /* allocate an object and a default layer */
633
634    object = _pico_calloc( 1, sizeof( lwObject ));
635    if ( !object ) goto Fail;
636
637    layer = _pico_calloc( 1, sizeof( lwLayer ));
638    if ( !layer ) goto Fail;
639    object->layer = layer;
640    object->nlayers = 1;
641
642    /* get the first chunk header */
643
644    id = getU4( fp );
645    cksize = getU4( fp );
646    if ( 0 > get_flen() ) goto Fail;
647
648    /* process chunks as they're encountered */
649
650    while ( 1 ) {
651       cksize += cksize & 1;
652
653       switch ( id )
654       {
655          case ID_PNTS:
656             if ( !lwGetPoints( fp, cksize, &layer->point ))
657                goto Fail;
658             break;
659
660          case ID_POLS:
661             if ( !lwGetPolygons5( fp, cksize, &layer->polygon,
662                layer->point.offset ))
663                goto Fail;
664             break;
665
666          case ID_SRFS:
667             if ( !lwGetTags( fp, cksize, &object->taglist ))
668                goto Fail;
669             break;
670
671          case ID_SURF:
672             node = ( lwNode * ) lwGetSurface5( fp, cksize, object );
673             if ( !node ) goto Fail;
674             lwListAdd( (void *) &object->surf, node );
675             object->nsurfs++;
676             break;
677
678          default:
679             _pico_memstream_seek( fp, cksize, PICO_SEEK_CUR );
680             break;
681       }
682
683       /* end of the file? */
684
685       if ( formsize <= (unsigned int) (_pico_memstream_tell( fp ) - 8) ) break;
686
687       /* get the next chunk header */
688
689       set_flen( 0 );
690       id = getU4( fp );
691       cksize = getU4( fp );
692       if ( 8 != get_flen() ) goto Fail;
693    }
694
695    lwGetBoundingBox( &layer->point, layer->bbox );
696    lwGetPolyNormals( &layer->point, &layer->polygon );
697    if ( !lwGetPointPolygons( &layer->point, &layer->polygon )) goto Fail;
698    if ( !lwResolvePolySurfaces( &layer->polygon, &object->taglist,
699       &object->surf, &object->nsurfs )) goto Fail;
700    lwGetVertNormals( &layer->point, &layer->polygon );
701
702    return object;
703
704 Fail:
705    if ( failID ) *failID = id;
706    if ( fp ) {
707       if ( failpos ) *failpos = _pico_memstream_tell( fp );
708    }
709    lwFreeObject( object );
710    return NULL;
711 }
712
713 int lwValidateObject5( const char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos )
714 {
715    unsigned int id, type;
716
717
718    /* open the file */
719
720    if ( !fp ) return PICO_PMV_ERROR_MEMORY;
721
722    /* read the first 12 bytes */
723
724    set_flen( 0 );
725    id       = getU4( fp );
726    /* formsize = */ getU4( fp );
727    type     = getU4( fp );
728    if ( 12 != get_flen() ) {
729       return PICO_PMV_ERROR_SIZE;
730    }
731
732    /* LWOB? */
733
734    if ( id != ID_FORM || type != ID_LWOB ) {
735       if ( failpos ) *failpos = 12;
736       return PICO_PMV_ERROR_IDENT;
737    }
738
739    return PICO_PMV_OK;
740 }