]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/renderer/Model_lwo.cpp
Use the same OpenAL headers on all platforms.
[icculus/iodoom3.git] / neo / renderer / Model_lwo.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "Model_lwo.h"
33
34 /*
35 ======================================================================
36
37         Converted from lwobject sample prog from LW 6.5 SDK.
38
39 ======================================================================
40 */
41
42 /*
43 ======================================================================
44 lwFreeClip()
45
46 Free memory used by an lwClip.
47 ====================================================================== */
48
49 void lwFreeClip( lwClip *clip )
50 {
51    if ( clip ) {
52       lwListFree( clip->ifilter, (void (__cdecl *)(void *))lwFreePlugin );
53       lwListFree( clip->pfilter, (void (__cdecl *)(void *))lwFreePlugin );
54           switch( clip->type ) {
55                 case ID_STIL: {
56               if ( clip->source.still.name ) Mem_Free( clip->source.still.name );
57                   break;
58                 }
59                 case ID_ISEQ: {
60           if ( clip->source.seq.suffix ) Mem_Free( clip->source.seq.suffix );
61           if ( clip->source.seq.prefix ) Mem_Free( clip->source.seq.prefix );
62                   break;
63                 }
64                 case ID_ANIM: {
65                   if ( clip->source.anim.server ) Mem_Free( clip->source.anim.server );
66           if ( clip->source.anim.name ) Mem_Free( clip->source.anim.name );
67                   break;
68             }
69                 case ID_XREF: {
70                   if ( clip->source.xref.string ) Mem_Free( clip->source.xref.string );
71                   break;
72                 }
73                 case ID_STCC: {
74           if ( clip->source.cycle.name ) Mem_Free( clip->source.cycle.name );
75                   break;
76             }
77           }
78       Mem_Free( clip );
79    }
80 }
81
82
83 /*
84 ======================================================================
85 lwGetClip()
86
87 Read image references from a CLIP chunk in an LWO2 file.
88 ====================================================================== */
89
90 lwClip *lwGetClip( idFile *fp, int cksize )
91 {
92    lwClip *clip;
93    lwPlugin *filt;
94    unsigned int id;
95    unsigned short sz;
96    int pos, rlen;
97
98
99    /* allocate the Clip structure */
100
101    clip = (lwClip*)Mem_ClearedAlloc( sizeof( lwClip ) );
102    if ( !clip ) goto Fail;
103
104    clip->contrast.val = 1.0f;
105    clip->brightness.val = 1.0f;
106    clip->saturation.val = 1.0f;
107    clip->gamma.val = 1.0f;
108
109    /* remember where we started */
110
111    set_flen( 0 );
112    pos = fp->Tell();
113
114    /* index */
115
116    clip->index = getI4( fp );
117
118    /* first subchunk header */
119
120    clip->type = getU4( fp );
121    sz = getU2( fp );
122    if ( 0 > get_flen() ) goto Fail;
123
124    sz += sz & 1;
125    set_flen( 0 );
126
127    switch ( clip->type ) {
128       case ID_STIL:
129          clip->source.still.name = getS0( fp );
130          break;
131
132       case ID_ISEQ:
133          clip->source.seq.digits  = getU1( fp );
134          clip->source.seq.flags   = getU1( fp );
135          clip->source.seq.offset  = getI2( fp );
136          clip->source.seq.start   = getI2( fp );
137          clip->source.seq.end     = getI2( fp );
138          clip->source.seq.prefix  = getS0( fp );
139          clip->source.seq.suffix  = getS0( fp );
140          break;
141
142       case ID_ANIM:
143          clip->source.anim.name   = getS0( fp );
144          clip->source.anim.server = getS0( fp );
145          rlen = get_flen();
146          clip->source.anim.data   = getbytes( fp, sz - rlen );
147          break;
148
149       case ID_XREF:
150          clip->source.xref.index  = getI4( fp );
151          clip->source.xref.string = getS0( fp );
152          break;
153
154       case ID_STCC:
155          clip->source.cycle.lo   = getI2( fp );
156          clip->source.cycle.hi   = getI2( fp );
157          clip->source.cycle.name = getS0( fp );
158          break;
159
160       default:
161          break;
162    }
163
164    /* error while reading current subchunk? */
165
166    rlen = get_flen();
167    if ( rlen < 0 || rlen > sz ) goto Fail;
168
169    /* skip unread parts of the current subchunk */
170
171    if ( rlen < sz )
172       fp->Seek( sz - rlen, FS_SEEK_CUR );
173
174    /* end of the CLIP chunk? */
175
176    rlen = fp->Tell() - pos;
177    if ( cksize < rlen ) goto Fail;
178    if ( cksize == rlen )
179       return clip;
180
181    /* process subchunks as they're encountered */
182
183    id = getU4( fp );
184    sz = getU2( fp );
185    if ( 0 > get_flen() ) goto Fail;
186
187    while ( 1 ) {
188       sz += sz & 1;
189       set_flen( 0 );
190
191       switch ( id ) {
192          case ID_TIME:
193             clip->start_time = getF4( fp );
194             clip->duration = getF4( fp );
195             clip->frame_rate = getF4( fp );
196             break;
197
198          case ID_CONT:
199             clip->contrast.val = getF4( fp );
200             clip->contrast.eindex = getVX( fp );
201             break;
202
203          case ID_BRIT:
204             clip->brightness.val = getF4( fp );
205             clip->brightness.eindex = getVX( fp );
206             break;
207
208          case ID_SATR:
209             clip->saturation.val = getF4( fp );
210             clip->saturation.eindex = getVX( fp );
211             break;
212
213          case ID_HUE:
214             clip->hue.val = getF4( fp );
215             clip->hue.eindex = getVX( fp );
216             break;
217
218          case ID_GAMM:
219             clip->gamma.val = getF4( fp );
220             clip->gamma.eindex = getVX( fp );
221             break;
222
223          case ID_NEGA:
224             clip->negative = getU2( fp );
225             break;
226
227          case ID_IFLT:
228          case ID_PFLT:
229             filt = (lwPlugin*)Mem_ClearedAlloc( sizeof( lwPlugin ) );
230             if ( !filt ) goto Fail;
231
232             filt->name = getS0( fp );
233             filt->flags = getU2( fp );
234             rlen = get_flen();
235             filt->data = getbytes( fp, sz - rlen );
236
237             if ( id == ID_IFLT ) {
238                lwListAdd( (void**)&clip->ifilter, filt );
239                clip->nifilters++;
240             }
241             else {
242                lwListAdd( (void**)&clip->pfilter, filt );
243                clip->npfilters++;
244             }
245             break;
246
247          default:
248             break;
249       }
250
251       /* error while reading current subchunk? */
252
253       rlen = get_flen();
254       if ( rlen < 0 || rlen > sz ) goto Fail;
255
256       /* skip unread parts of the current subchunk */
257
258       if ( rlen < sz )
259          fp->Seek( sz - rlen, FS_SEEK_CUR );
260
261       /* end of the CLIP chunk? */
262
263       rlen = fp->Tell() - pos;
264       if ( cksize < rlen ) goto Fail;
265       if ( cksize == rlen ) break;
266
267       /* get the next chunk header */
268
269       set_flen( 0 );
270       id = getU4( fp );
271       sz = getU2( fp );
272       if ( 6 != get_flen() ) goto Fail;
273    }
274
275    return clip;
276
277 Fail:
278    lwFreeClip( clip );
279    return NULL;
280 }
281
282
283 /*
284 ======================================================================
285 lwFindClip()
286
287 Returns an lwClip pointer, given a clip index.
288 ====================================================================== */
289
290 lwClip *lwFindClip( lwClip *list, int index )
291 {
292    lwClip *clip;
293
294    clip = list;
295    while ( clip ) {
296       if ( clip->index == index ) break;
297       clip = clip->next;
298    }
299    return clip;
300 }
301
302
303 /*
304 ======================================================================
305 lwFreeEnvelope()
306
307 Free the memory used by an lwEnvelope.
308 ====================================================================== */
309
310 void lwFree( void *ptr ) {
311         Mem_Free( ptr );
312 }
313
314 void lwFreeEnvelope( lwEnvelope *env )
315 {
316    if ( env ) {
317       if ( env->name ) Mem_Free( env->name );
318       lwListFree( env->key, lwFree );
319       lwListFree( env->cfilter, (void (__cdecl *)(void *))lwFreePlugin );
320       Mem_Free( env );
321    }
322 }
323
324
325 static int compare_keys( lwKey *k1, lwKey *k2 )
326 {
327    return k1->time > k2->time ? 1 : k1->time < k2->time ? -1 : 0;
328 }
329
330
331 /*
332 ======================================================================
333 lwGetEnvelope()
334
335 Read an ENVL chunk from an LWO2 file.
336 ====================================================================== */
337
338 lwEnvelope *lwGetEnvelope( idFile *fp, int cksize )
339 {
340    lwEnvelope *env;
341    lwKey *key;
342    lwPlugin *plug;
343    unsigned int id;
344    unsigned short sz;
345    float f[ 4 ];
346    int i, nparams, pos, rlen;
347
348
349    /* allocate the Envelope structure */
350
351    env = (lwEnvelope*)Mem_ClearedAlloc( sizeof( lwEnvelope ) );
352    if ( !env ) goto Fail;
353
354    /* remember where we started */
355
356    set_flen( 0 );
357    pos = fp->Tell();
358
359    /* index */
360
361    env->index = getVX( fp );
362
363    /* first subchunk header */
364
365    id = getU4( fp );
366    sz = getU2( fp );
367    if ( 0 > get_flen() ) goto Fail;
368
369    /* process subchunks as they're encountered */
370
371    while ( 1 ) {
372       sz += sz & 1;
373       set_flen( 0 );
374
375       switch ( id ) {
376          case ID_TYPE:
377             env->type = getU2( fp );
378             break;
379
380          case ID_NAME:
381             env->name = getS0( fp );
382             break;
383
384          case ID_PRE:
385             env->behavior[ 0 ] = getU2( fp );
386             break;
387
388          case ID_POST:
389             env->behavior[ 1 ] = getU2( fp );
390             break;
391
392          case ID_KEY:
393             key = (lwKey*)Mem_ClearedAlloc( sizeof( lwKey ) );
394             if ( !key ) goto Fail;
395             key->time = getF4( fp );
396             key->value = getF4( fp );
397             lwListInsert( (void**)&env->key, key, (int (__cdecl *)(void *,void *))compare_keys );
398             env->nkeys++;
399             break;
400
401          case ID_SPAN:
402             if ( !key ) goto Fail;
403             key->shape = getU4( fp );
404
405             nparams = ( sz - 4 ) / 4;
406             if ( nparams > 4 ) nparams = 4;
407             for ( i = 0; i < nparams; i++ )
408                f[ i ] = getF4( fp );
409
410             switch ( key->shape ) {
411                case ID_TCB:
412                   key->tension = f[ 0 ];
413                   key->continuity = f[ 1 ];
414                   key->bias = f[ 2 ];
415                   break;
416
417                case ID_BEZI:
418                case ID_HERM:
419                case ID_BEZ2:
420                   for ( i = 0; i < nparams; i++ )
421                      key->param[ i ] = f[ i ];
422                   break;
423             }
424             break;
425
426          case ID_CHAN:
427             plug = (lwPlugin*)Mem_ClearedAlloc( sizeof( lwPlugin ) );
428             if ( !plug ) goto Fail;
429
430             plug->name = getS0( fp );
431             plug->flags = getU2( fp );
432             plug->data = getbytes( fp, sz - get_flen() );
433
434             lwListAdd( (void**)&env->cfilter, plug );
435             env->ncfilters++;
436             break;
437
438          default:
439             break;
440       }
441
442       /* error while reading current subchunk? */
443
444       rlen = get_flen();
445       if ( rlen < 0 || rlen > sz ) goto Fail;
446
447       /* skip unread parts of the current subchunk */
448
449       if ( rlen < sz )
450          fp->Seek( sz - rlen, FS_SEEK_CUR );
451
452       /* end of the ENVL chunk? */
453
454       rlen = fp->Tell() - pos;
455       if ( cksize < rlen ) goto Fail;
456       if ( cksize == rlen ) break;
457
458       /* get the next subchunk header */
459
460       set_flen( 0 );
461       id = getU4( fp );
462       sz = getU2( fp );
463       if ( 6 != get_flen() ) goto Fail;
464    }
465
466    return env;
467
468 Fail:
469    lwFreeEnvelope( env );
470    return NULL;
471 }
472
473
474 /*
475 ======================================================================
476 lwFindEnvelope()
477
478 Returns an lwEnvelope pointer, given an envelope index.
479 ====================================================================== */
480
481 lwEnvelope *lwFindEnvelope( lwEnvelope *list, int index )
482 {
483    lwEnvelope *env;
484
485    env = list;
486    while ( env ) {
487       if ( env->index == index ) break;
488       env = env->next;
489    }
490    return env;
491 }
492
493
494 /*
495 ======================================================================
496 range()
497
498 Given the value v of a periodic function, returns the equivalent value
499 v2 in the principal interval [lo, hi].  If i isn't NULL, it receives
500 the number of wavelengths between v and v2.
501
502    v2 = v - i * (hi - lo)
503
504 For example, range( 3 pi, 0, 2 pi, i ) returns pi, with i = 1.
505 ====================================================================== */
506
507 static float range( float v, float lo, float hi, int *i )
508 {
509    float v2, r = hi - lo;
510
511    if ( r == 0.0 ) {
512       if ( i ) *i = 0;
513       return lo;
514    }
515
516    v2 = lo + v - r * ( float ) floor(( double ) v / r );
517    if ( i ) *i = -( int )(( v2 - v ) / r + ( v2 > v ? 0.5 : -0.5 ));
518
519    return v2;
520 }
521
522
523 /*
524 ======================================================================
525 hermite()
526
527 Calculate the Hermite coefficients.
528 ====================================================================== */
529
530 static void hermite( float t, float *h1, float *h2, float *h3, float *h4 )
531 {
532    float t2, t3;
533
534    t2 = t * t;
535    t3 = t * t2;
536
537    *h2 = 3.0f * t2 - t3 - t3;
538    *h1 = 1.0f - *h2;
539    *h4 = t3 - t2;
540    *h3 = *h4 - t2 + t;
541 }
542
543
544 /*
545 ======================================================================
546 bezier()
547
548 Interpolate the value of a 1D Bezier curve.
549 ====================================================================== */
550
551 static float bezier( float x0, float x1, float x2, float x3, float t )
552 {
553    float a, b, c, t2, t3;
554
555    t2 = t * t;
556    t3 = t2 * t;
557
558    c = 3.0f * ( x1 - x0 );
559    b = 3.0f * ( x2 - x1 ) - c;
560    a = x3 - x0 - c - b;
561
562    return a * t3 + b * t2 + c * t + x0;
563 }
564
565
566 /*
567 ======================================================================
568 bez2_time()
569
570 Find the t for which bezier() returns the input time.  The handle
571 endpoints of a BEZ2 curve represent the control points, and these have
572 (time, value) coordinates, so time is used as both a coordinate and a
573 parameter for this curve type.
574 ====================================================================== */
575
576 static float bez2_time( float x0, float x1, float x2, float x3, float time,
577    float *t0, float *t1 )
578 {
579    float v, t;
580
581    t = *t0 + ( *t1 - *t0 ) * 0.5f;
582    v = bezier( x0, x1, x2, x3, t );
583    if ( idMath::Fabs( time - v ) > .0001f ) {
584       if ( v > time )
585          *t1 = t;
586       else
587          *t0 = t;
588       return bez2_time( x0, x1, x2, x3, time, t0, t1 );
589    }
590    else
591       return t;
592 }
593
594
595 /*
596 ======================================================================
597 bez2()
598
599 Interpolate the value of a BEZ2 curve.
600 ====================================================================== */
601
602 static float bez2( lwKey *key0, lwKey *key1, float time )
603 {
604    float x, y, t, t0 = 0.0f, t1 = 1.0f;
605
606    if ( key0->shape == ID_BEZ2 )
607       x = key0->time + key0->param[ 2 ];
608    else
609       x = key0->time + ( key1->time - key0->time ) / 3.0f;
610
611    t = bez2_time( key0->time, x, key1->time + key1->param[ 0 ], key1->time,
612       time, &t0, &t1 );
613
614    if ( key0->shape == ID_BEZ2 )
615       y = key0->value + key0->param[ 3 ];
616    else
617       y = key0->value + key0->param[ 1 ] / 3.0f;
618
619    return bezier( key0->value, y, key1->param[ 1 ] + key1->value, key1->value, t );
620 }
621
622
623 /*
624 ======================================================================
625 outgoing()
626
627 Return the outgoing tangent to the curve at key0.  The value returned
628 for the BEZ2 case is used when extrapolating a linear pre behavior and
629 when interpolating a non-BEZ2 span.
630 ====================================================================== */
631
632 static float outgoing( lwKey *key0, lwKey *key1 )
633 {
634    float a, b, d, t, out;
635
636    switch ( key0->shape )
637    {
638       case ID_TCB:
639          a = ( 1.0f - key0->tension )
640            * ( 1.0f + key0->continuity )
641            * ( 1.0f + key0->bias );
642          b = ( 1.0f - key0->tension )
643            * ( 1.0f - key0->continuity )
644            * ( 1.0f - key0->bias );
645          d = key1->value - key0->value;
646
647          if ( key0->prev ) {
648             t = ( key1->time - key0->time ) / ( key1->time - key0->prev->time );
649             out = t * ( a * ( key0->value - key0->prev->value ) + b * d );
650          }
651          else
652             out = b * d;
653          break;
654
655       case ID_LINE:
656          d = key1->value - key0->value;
657          if ( key0->prev ) {
658             t = ( key1->time - key0->time ) / ( key1->time - key0->prev->time );
659             out = t * ( key0->value - key0->prev->value + d );
660          }
661          else
662             out = d;
663          break;
664
665       case ID_BEZI:
666       case ID_HERM:
667          out = key0->param[ 1 ];
668          if ( key0->prev )
669             out *= ( key1->time - key0->time ) / ( key1->time - key0->prev->time );
670          break;
671
672       case ID_BEZ2:
673          out = key0->param[ 3 ] * ( key1->time - key0->time );
674          if ( idMath::Fabs( key0->param[ 2 ] ) > 1e-5f )
675             out /= key0->param[ 2 ];
676          else
677             out *= 1e5f;
678          break;
679
680       case ID_STEP:
681       default:
682          out = 0.0f;
683          break;
684    }
685
686    return out;
687 }
688
689
690 /*
691 ======================================================================
692 incoming()
693
694 Return the incoming tangent to the curve at key1.  The value returned
695 for the BEZ2 case is used when extrapolating a linear post behavior.
696 ====================================================================== */
697
698 static float incoming( lwKey *key0, lwKey *key1 )
699 {
700    float a, b, d, t, in;
701
702    switch ( key1->shape )
703    {
704       case ID_LINE:
705          d = key1->value - key0->value;
706          if ( key1->next ) {
707             t = ( key1->time - key0->time ) / ( key1->next->time - key0->time );
708             in = t * ( key1->next->value - key1->value + d );
709          }
710          else
711             in = d;
712          break;
713
714       case ID_TCB:
715          a = ( 1.0f - key1->tension )
716            * ( 1.0f - key1->continuity )
717            * ( 1.0f + key1->bias );
718          b = ( 1.0f - key1->tension )
719            * ( 1.0f + key1->continuity )
720            * ( 1.0f - key1->bias );
721          d = key1->value - key0->value;
722
723          if ( key1->next ) {
724             t = ( key1->time - key0->time ) / ( key1->next->time - key0->time );
725             in = t * ( b * ( key1->next->value - key1->value ) + a * d );
726          }
727          else
728             in = a * d;
729          break;
730
731       case ID_BEZI:
732       case ID_HERM:
733          in = key1->param[ 0 ];
734          if ( key1->next )
735             in *= ( key1->time - key0->time ) / ( key1->next->time - key0->time );
736          break;
737          return in;
738
739       case ID_BEZ2:
740          in = key1->param[ 1 ] * ( key1->time - key0->time );
741          if ( idMath::Fabs( key1->param[ 0 ] ) > 1e-5f )
742             in /= key1->param[ 0 ];
743          else
744             in *= 1e5f;
745          break;
746
747       case ID_STEP:
748       default:
749          in = 0.0f;
750          break;
751    }
752
753    return in;
754 }
755
756
757 /*
758 ======================================================================
759 evalEnvelope()
760
761 Given a list of keys and a time, returns the interpolated value of the
762 envelope at that time.
763 ====================================================================== */
764
765 float evalEnvelope( lwEnvelope *env, float time )
766 {
767    lwKey *key0, *key1, *skey, *ekey;
768    float t, h1, h2, h3, h4, in, out, offset = 0.0f;
769    int noff;
770
771
772    /* if there's no key, the value is 0 */
773
774    if ( env->nkeys == 0 ) return 0.0f;
775
776    /* if there's only one key, the value is constant */
777
778    if ( env->nkeys == 1 )
779       return env->key->value;
780
781    /* find the first and last keys */
782
783    skey = ekey = env->key;
784    while ( ekey->next ) ekey = ekey->next;
785
786    /* use pre-behavior if time is before first key time */
787
788    if ( time < skey->time ) {
789       switch ( env->behavior[ 0 ] )
790       {
791          case BEH_RESET:
792             return 0.0f;
793
794          case BEH_CONSTANT:
795             return skey->value;
796
797          case BEH_REPEAT:
798             time = range( time, skey->time, ekey->time, NULL );
799             break;
800
801          case BEH_OSCILLATE:
802             time = range( time, skey->time, ekey->time, &noff );
803             if ( noff % 2 )
804                time = ekey->time - skey->time - time;
805             break;
806
807          case BEH_OFFSET:
808             time = range( time, skey->time, ekey->time, &noff );
809             offset = noff * ( ekey->value - skey->value );
810             break;
811
812          case BEH_LINEAR:
813             out = outgoing( skey, skey->next )
814                 / ( skey->next->time - skey->time );
815             return out * ( time - skey->time ) + skey->value;
816       }
817    }
818
819    /* use post-behavior if time is after last key time */
820
821    else if ( time > ekey->time ) {
822       switch ( env->behavior[ 1 ] )
823       {
824          case BEH_RESET:
825             return 0.0f;
826
827          case BEH_CONSTANT:
828             return ekey->value;
829
830          case BEH_REPEAT:
831             time = range( time, skey->time, ekey->time, NULL );
832             break;
833
834          case BEH_OSCILLATE:
835             time = range( time, skey->time, ekey->time, &noff );
836             if ( noff % 2 )
837                time = ekey->time - skey->time - time;
838             break;
839
840          case BEH_OFFSET:
841             time = range( time, skey->time, ekey->time, &noff );
842             offset = noff * ( ekey->value - skey->value );
843             break;
844
845          case BEH_LINEAR:
846             in = incoming( ekey->prev, ekey )
847                / ( ekey->time - ekey->prev->time );
848             return in * ( time - ekey->time ) + ekey->value;
849       }
850    }
851
852    /* get the endpoints of the interval being evaluated */
853
854    key0 = env->key;
855    while ( time > key0->next->time )
856       key0 = key0->next;
857    key1 = key0->next;
858
859    /* check for singularities first */
860
861    if ( time == key0->time )
862       return key0->value + offset;
863    else if ( time == key1->time )
864       return key1->value + offset;
865
866    /* get interval length, time in [0, 1] */
867
868    t = ( time - key0->time ) / ( key1->time - key0->time );
869
870    /* interpolate */
871
872    switch ( key1->shape )
873    {
874       case ID_TCB:
875       case ID_BEZI:
876       case ID_HERM:
877          out = outgoing( key0, key1 );
878          in = incoming( key0, key1 );
879          hermite( t, &h1, &h2, &h3, &h4 );
880          return h1 * key0->value + h2 * key1->value + h3 * out + h4 * in + offset;
881
882       case ID_BEZ2:
883          return bez2( key0, key1, time ) + offset;
884
885       case ID_LINE:
886          return key0->value + t * ( key1->value - key0->value ) + offset;
887
888       case ID_STEP:
889          return key0->value + offset;
890
891       default:
892          return offset;
893    }
894 }
895
896
897
898 /*
899 ======================================================================
900 lwListFree()
901
902 Free the items in a list.
903 ====================================================================== */
904
905 void lwListFree( void *list, void ( *freeNode )( void * ))
906 {
907    lwNode *node, *next;
908
909    node = ( lwNode * ) list;
910    while ( node ) {
911       next = node->next;
912       freeNode( node );
913       node = next;
914    }
915 }
916
917
918 /*
919 ======================================================================
920 lwListAdd()
921
922 Append a node to a list.
923 ====================================================================== */
924
925 void lwListAdd( void **list, void *node )
926 {
927    lwNode *head, *tail;
928
929    head = *(( lwNode ** ) list );
930    if ( !head ) {
931       *list = node;
932       return;
933    }
934    while ( head ) {
935       tail = head;
936       head = head->next;
937    }
938    tail->next = ( lwNode * ) node;
939    (( lwNode * ) node )->prev = tail;
940 }
941
942
943 /*
944 ======================================================================
945 lwListInsert()
946
947 Insert a node into a list in sorted order.
948 ====================================================================== */
949
950 void lwListInsert( void **vlist, void *vitem, int ( *compare )( void *, void * ))
951 {
952    lwNode **list, *item, *node, *prev;
953
954    if ( !*vlist ) {
955       *vlist = vitem;
956       return;
957    }
958
959    list = ( lwNode ** ) vlist;
960    item = ( lwNode * ) vitem;
961    node = *list;
962    prev = NULL;
963
964    while ( node ) {
965       if ( 0 < compare( node, item )) break;
966       prev = node;
967       node = node->next;
968    }
969
970    if ( !prev ) {
971       *list = item;
972       node->prev = item;
973       item->next = node;
974    }
975    else if ( !node ) {
976       prev->next = item;
977       item->prev = prev;
978    }
979    else {
980       item->next = node;
981       item->prev = prev;
982       prev->next = item;
983       node->prev = item;
984    }
985 }
986
987 /*
988 ======================================================================
989 flen
990
991 This accumulates a count of the number of bytes read.  Callers can set
992 it at the beginning of a sequence of reads and then retrieve it to get
993 the number of bytes actually read.  If one of the I/O functions fails,
994 flen is set to an error code, after which the I/O functions ignore
995 read requests until flen is reset.
996 ====================================================================== */
997
998 #define FLEN_ERROR -9999
999
1000 static int flen;
1001
1002 void set_flen( int i ) { flen = i; }
1003
1004 int get_flen( void ) { return flen; }
1005
1006 void *getbytes( idFile *fp, int size )
1007 {
1008    void *data;
1009
1010    if ( flen == FLEN_ERROR ) return NULL;
1011    if ( size < 0 ) {
1012       flen = FLEN_ERROR;
1013       return NULL;
1014    }
1015    data = Mem_ClearedAlloc( size );
1016    if ( !data ) {
1017       flen = FLEN_ERROR;
1018       return NULL;
1019    }
1020    if ( size != fp->Read( data, size ) ) {
1021       flen = FLEN_ERROR;
1022       Mem_Free( data );
1023       return NULL;
1024    }
1025
1026    flen += size;
1027    return data;
1028 }
1029
1030
1031 void skipbytes( idFile *fp, int n )
1032 {
1033    if ( flen == FLEN_ERROR ) return;
1034    if ( fp->Seek( n, FS_SEEK_CUR ))
1035       flen = FLEN_ERROR;
1036    else
1037       flen += n;
1038 }
1039
1040
1041 int getI1( idFile *fp )
1042 {
1043    int i, c;
1044
1045    if ( flen == FLEN_ERROR ) return 0;
1046         c = 0;
1047    i = fp->Read(&c, 1);
1048    if ( i < 0 ) {
1049       flen = FLEN_ERROR;
1050       return 0;
1051    }
1052    if ( c > 127 ) c -= 256;
1053    flen += 1;
1054    return c;
1055 }
1056
1057
1058 short getI2( idFile *fp )
1059 {
1060    short i;
1061
1062    if ( flen == FLEN_ERROR ) return 0;
1063    if ( 2 != fp->Read( &i, 2 )) {
1064       flen = FLEN_ERROR;
1065       return 0;
1066    }
1067    BigRevBytes( &i, 2, 1 );
1068    flen += 2;
1069    return i;
1070 }
1071
1072
1073 int getI4( idFile *fp )
1074 {
1075    int i;
1076
1077    if ( flen == FLEN_ERROR ) return 0;
1078    if ( 4 != fp->Read( &i, 4 )) {
1079       flen = FLEN_ERROR;
1080       return 0;
1081    }
1082    BigRevBytes( &i, 4, 1 );
1083    flen += 4;
1084    return i;
1085 }
1086
1087
1088 unsigned char getU1( idFile *fp )
1089 {
1090    int i, c;
1091
1092    if ( flen == FLEN_ERROR ) return 0;
1093           c = 0;
1094    i = fp->Read(&c, 1);
1095    if ( i < 0 ) {
1096       flen = FLEN_ERROR;
1097       return 0;
1098    }
1099    flen += 1;
1100    return c;
1101 }
1102
1103
1104 unsigned short getU2( idFile *fp )
1105 {
1106    unsigned short i;
1107
1108    if ( flen == FLEN_ERROR ) return 0;
1109    if ( 2 != fp->Read( &i, 2 )) {
1110       flen = FLEN_ERROR;
1111       return 0;
1112    }
1113    BigRevBytes( &i, 2, 1 );
1114    flen += 2;
1115    return i;
1116 }
1117
1118
1119 unsigned int getU4( idFile *fp )
1120 {
1121    unsigned int i;
1122
1123    if ( flen == FLEN_ERROR ) return 0;
1124    if ( 4 != fp->Read( &i, 4 )) {
1125       flen = FLEN_ERROR;
1126       return 0;
1127    }
1128    BigRevBytes( &i, 4, 1 );
1129    flen += 4;
1130    return i;
1131 }
1132
1133
1134 int getVX( idFile *fp )
1135 {
1136     byte c;
1137    int i;
1138
1139    if ( flen == FLEN_ERROR ) return 0;
1140
1141         c = 0;
1142    if (fp->Read(&c, 1) == -1) {
1143            return 0;
1144    }
1145
1146    if ( c != 0xFF ) {
1147       i = c << 8;
1148           c = 0;
1149           if (fp->Read(&c, 1) == -1) {
1150                   return 0;
1151           }
1152       i |= c;
1153       flen += 2;
1154    }
1155    else {
1156           c = 0;
1157           if (fp->Read(&c, 1) == -1) {
1158                   return 0;
1159           }
1160       i = c << 16;
1161           c = 0;
1162           if (fp->Read(&c, 1) == -1) {
1163                   return 0;
1164           }
1165       i |= c << 8;
1166           c = 0;
1167           if (fp->Read(&c, 1) == -1) {
1168                   return 0;
1169           }
1170       i |= c;
1171       flen += 4;
1172    }
1173
1174    return i;
1175 }
1176
1177
1178 float getF4( idFile *fp )
1179 {
1180    float f;
1181
1182    if ( flen == FLEN_ERROR ) return 0.0f;
1183    if ( 4 != fp->Read( &f, 4 ) ) {
1184       flen = FLEN_ERROR;
1185       return 0.0f;
1186    }
1187    BigRevBytes( &f, 4, 1 );
1188    flen += 4;
1189
1190    if ( FLOAT_IS_DENORMAL( f ) ) {
1191            f = 0.0f;
1192    }
1193    return f;
1194 }
1195
1196
1197 char *getS0( idFile *fp )
1198 {
1199    char *s;
1200    int i, c, len, pos;
1201
1202    if ( flen == FLEN_ERROR ) return NULL;
1203
1204    pos = fp->Tell();
1205    for ( i = 1; ; i++ ) {
1206            c = 0;
1207            if (fp->Read(&c, 1) == -1) {
1208                    flen = FLEN_ERROR;
1209                    return NULL;
1210            }
1211            if ( c == 0 ) break;
1212    }
1213
1214    if ( i == 1 ) {
1215       if ( fp->Seek( pos + 2, FS_SEEK_SET ))
1216          flen = FLEN_ERROR;
1217       else
1218          flen += 2;
1219       return NULL;
1220    }
1221
1222    len = i + ( i & 1 );
1223    s = (char*)Mem_ClearedAlloc( len );
1224    if ( !s ) {
1225       flen = FLEN_ERROR;
1226       return NULL;
1227    }
1228
1229    if ( fp->Seek( pos, FS_SEEK_SET )) {
1230       flen = FLEN_ERROR;
1231       return NULL;
1232    }
1233    if ( len != fp->Read( s, len )) {
1234       flen = FLEN_ERROR;
1235       return NULL;
1236    }
1237
1238    flen += len;
1239    return s;
1240 }
1241
1242
1243 int sgetI1( unsigned char **bp )
1244 {
1245    int i;
1246
1247    if ( flen == FLEN_ERROR ) return 0;
1248    i = **bp;
1249    if ( i > 127 ) i -= 256;
1250    flen += 1;
1251    *bp++;
1252    return i;
1253 }
1254
1255
1256 short sgetI2( unsigned char **bp )
1257 {
1258    short i;
1259
1260    if ( flen == FLEN_ERROR ) return 0;
1261    memcpy( &i, *bp, 2 );
1262    BigRevBytes( &i, 2, 1 );
1263    flen += 2;
1264    *bp += 2;
1265    return i;
1266 }
1267
1268
1269 int sgetI4( unsigned char **bp )
1270 {
1271    int i;
1272
1273    if ( flen == FLEN_ERROR ) return 0;
1274    memcpy( &i, *bp, 4 );
1275    BigRevBytes( &i, 4, 1 );
1276    flen += 4;
1277    *bp += 4;
1278    return i;
1279 }
1280
1281
1282 unsigned char sgetU1( unsigned char **bp )
1283 {
1284    unsigned char c;
1285
1286    if ( flen == FLEN_ERROR ) return 0;
1287    c = **bp;
1288    flen += 1;
1289    *bp++;
1290    return c;
1291 }
1292
1293
1294 unsigned short sgetU2( unsigned char **bp )
1295 {
1296    unsigned char *buf = *bp;
1297    unsigned short i;
1298
1299    if ( flen == FLEN_ERROR ) return 0;
1300    i = ( buf[ 0 ] << 8 ) | buf[ 1 ];
1301    flen += 2;
1302    *bp += 2;
1303    return i;
1304 }
1305
1306
1307 unsigned int sgetU4( unsigned char **bp )
1308 {
1309    unsigned int i;
1310
1311    if ( flen == FLEN_ERROR ) return 0;
1312    memcpy( &i, *bp, 4 );
1313    BigRevBytes( &i, 4, 1 );
1314    flen += 4;
1315    *bp += 4;
1316    return i;
1317 }
1318
1319
1320 int sgetVX( unsigned char **bp )
1321 {
1322    unsigned char *buf = *bp;
1323    int i;
1324
1325    if ( flen == FLEN_ERROR ) return 0;
1326
1327    if ( buf[ 0 ] != 0xFF ) {
1328       i = buf[ 0 ] << 8 | buf[ 1 ];
1329       flen += 2;
1330       *bp += 2;
1331    }
1332    else {
1333       i = ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ];
1334       flen += 4;
1335       *bp += 4;
1336    }
1337    return i;
1338 }
1339
1340
1341 float sgetF4( unsigned char **bp )
1342 {
1343    float f;
1344
1345    if ( flen == FLEN_ERROR ) return 0.0f;
1346    memcpy( &f, *bp, 4 );
1347    BigRevBytes( &f, 4, 1 );
1348    flen += 4;
1349    *bp += 4;
1350
1351    if ( FLOAT_IS_DENORMAL( f ) ) {
1352            f = 0.0f;
1353    }
1354    return f;
1355 }
1356
1357
1358 char *sgetS0( unsigned char **bp )
1359 {
1360    char *s;
1361    unsigned char *buf = *bp;
1362    int len;
1363
1364    if ( flen == FLEN_ERROR ) return NULL;
1365
1366    len = strlen( (const char*)buf ) + 1;
1367    if ( len == 1 ) {
1368       flen += 2;
1369       *bp += 2;
1370       return NULL;
1371    }
1372    len += len & 1;
1373    s = (char*)Mem_ClearedAlloc( len );
1374    if ( !s ) {
1375       flen = FLEN_ERROR;
1376       return NULL;
1377    }
1378
1379    memcpy( s, buf, len );
1380    flen += len;
1381    *bp += len;
1382    return s;
1383 }
1384
1385 /*
1386 ======================================================================
1387 lwFreeLayer()
1388
1389 Free memory used by an lwLayer.
1390 ====================================================================== */
1391
1392 void lwFreeLayer( lwLayer *layer )
1393 {
1394    if ( layer ) {
1395       if ( layer->name ) Mem_Free( layer->name );
1396       lwFreePoints( &layer->point );
1397       lwFreePolygons( &layer->polygon );
1398       lwListFree( layer->vmap, (void (__cdecl *)(void *))lwFreeVMap );
1399       Mem_Free( layer );
1400    }
1401 }
1402
1403
1404 /*
1405 ======================================================================
1406 lwFreeObject()
1407
1408 Free memory used by an lwObject.
1409 ====================================================================== */
1410
1411 void lwFreeObject( lwObject *object )
1412 {
1413    if ( object ) {
1414       lwListFree( object->layer, (void (__cdecl *)(void *))lwFreeLayer );
1415       lwListFree( object->env, (void (__cdecl *)(void *))lwFreeEnvelope );
1416       lwListFree( object->clip, (void (__cdecl *)(void *))lwFreeClip );
1417       lwListFree( object->surf, (void (__cdecl *)(void *))lwFreeSurface );
1418       lwFreeTags( &object->taglist );
1419       Mem_Free( object );
1420    }
1421 }
1422
1423
1424 /*
1425 ======================================================================
1426 lwGetObject()
1427
1428 Returns the contents of a LightWave object, given its filename, or
1429 NULL if the file couldn't be loaded.  On failure, failID and failpos
1430 can be used to diagnose the cause.
1431
1432 1.  If the file isn't an LWO2 or an LWOB, failpos will contain 12 and
1433     failID will be unchanged.
1434
1435 2.  If an error occurs while reading, failID will contain the most
1436     recently read IFF chunk ID, and failpos will contain the value
1437     returned by fp->Tell() at the time of the failure.
1438
1439 3.  If the file couldn't be opened, or an error occurs while reading
1440     the first 12 bytes, both failID and failpos will be unchanged.
1441
1442 If you don't need this information, failID and failpos can be NULL.
1443 ====================================================================== */
1444
1445 lwObject *lwGetObject( const char *filename, unsigned int *failID, int *failpos )
1446 {
1447    idFile *fp = NULL;
1448    lwObject *object;
1449    lwLayer *layer;
1450    lwNode *node;
1451    int id, formsize, type, cksize;
1452    int i, rlen;
1453
1454    fp = fileSystem->OpenFileRead( filename );
1455    if ( !fp ) {
1456            return NULL;
1457    }
1458
1459    /* read the first 12 bytes */
1460
1461    set_flen( 0 );
1462    id       = getU4( fp );
1463    formsize = getU4( fp );
1464    type     = getU4( fp );
1465    if ( 12 != get_flen() ) {
1466       fileSystem->CloseFile( fp );
1467       return NULL;
1468    }
1469
1470    /* is this a LW object? */
1471
1472    if ( id != ID_FORM ) {
1473       fileSystem->CloseFile( fp );
1474       if ( failpos ) *failpos = 12;
1475       return NULL;
1476    }
1477
1478    if ( type != ID_LWO2 ) {
1479           fileSystem->CloseFile( fp );
1480       if ( type == ID_LWOB )
1481          return lwGetObject5( filename, failID, failpos );
1482       else {
1483          if ( failpos ) *failpos = 12;
1484          return NULL;
1485       }
1486    }
1487
1488    /* allocate an object and a default layer */
1489
1490    object = (lwObject*)Mem_ClearedAlloc( sizeof( lwObject ) );
1491    if ( !object ) goto Fail;
1492
1493    layer = (lwLayer*)Mem_ClearedAlloc( sizeof( lwLayer ) );
1494    if ( !layer ) goto Fail;
1495    object->layer = layer;
1496
1497    object->timeStamp = fp->Timestamp();
1498
1499    /* get the first chunk header */
1500
1501    id = getU4( fp );
1502    cksize = getU4( fp );
1503    if ( 0 > get_flen() ) goto Fail;
1504
1505    /* process chunks as they're encountered */
1506
1507    while ( 1 ) {
1508       cksize += cksize & 1;
1509
1510       switch ( id )
1511       {
1512          case ID_LAYR:
1513             if ( object->nlayers > 0 ) {
1514                layer = (lwLayer*)Mem_ClearedAlloc( sizeof( lwLayer ) );
1515                if ( !layer ) goto Fail;
1516                lwListAdd( (void**)&object->layer, layer );
1517             }
1518             object->nlayers++;
1519
1520             set_flen( 0 );
1521             layer->index = getU2( fp );
1522             layer->flags = getU2( fp );
1523             layer->pivot[ 0 ] = getF4( fp );
1524             layer->pivot[ 1 ] = getF4( fp );
1525             layer->pivot[ 2 ] = getF4( fp );
1526             layer->name = getS0( fp );
1527
1528             rlen = get_flen();
1529             if ( rlen < 0 || rlen > cksize ) goto Fail;
1530             if ( rlen <= cksize - 2 )
1531                layer->parent = getU2( fp );
1532             rlen = get_flen();
1533             if ( rlen < cksize )
1534                fp->Seek( cksize - rlen, FS_SEEK_CUR );
1535             break;
1536
1537          case ID_PNTS:
1538             if ( !lwGetPoints( fp, cksize, &layer->point ))
1539                goto Fail;
1540             break;
1541
1542          case ID_POLS:
1543             if ( !lwGetPolygons( fp, cksize, &layer->polygon,
1544                layer->point.offset ))
1545                goto Fail;
1546             break;
1547
1548          case ID_VMAP:
1549          case ID_VMAD:
1550             node = ( lwNode * ) lwGetVMap( fp, cksize, layer->point.offset,
1551                layer->polygon.offset, id == ID_VMAD );
1552             if ( !node ) goto Fail;
1553             lwListAdd( (void**)&layer->vmap, node );
1554             layer->nvmaps++;
1555             break;
1556
1557          case ID_PTAG:
1558             if ( !lwGetPolygonTags( fp, cksize, &object->taglist,
1559                &layer->polygon ))
1560                goto Fail;
1561             break;
1562
1563          case ID_BBOX:
1564             set_flen( 0 );
1565             for ( i = 0; i < 6; i++ )
1566                layer->bbox[ i ] = getF4( fp );
1567             rlen = get_flen();
1568             if ( rlen < 0 || rlen > cksize ) goto Fail;
1569             if ( rlen < cksize )
1570                fp->Seek( cksize - rlen, FS_SEEK_CUR );
1571             break;
1572
1573          case ID_TAGS:
1574             if ( !lwGetTags( fp, cksize, &object->taglist ))
1575                goto Fail;
1576             break;
1577
1578          case ID_ENVL:
1579             node = ( lwNode * ) lwGetEnvelope( fp, cksize );
1580             if ( !node ) goto Fail;
1581             lwListAdd( (void**)&object->env, node );
1582             object->nenvs++;
1583             break;
1584
1585          case ID_CLIP:
1586             node = ( lwNode * ) lwGetClip( fp, cksize );
1587             if ( !node ) goto Fail;
1588             lwListAdd( (void**)&object->clip, node );
1589             object->nclips++;
1590             break;
1591
1592          case ID_SURF:
1593             node = ( lwNode * ) lwGetSurface( fp, cksize );
1594             if ( !node ) goto Fail;
1595             lwListAdd( (void**)&object->surf, node );
1596             object->nsurfs++;
1597             break;
1598
1599          case ID_DESC:
1600          case ID_TEXT:
1601          case ID_ICON:
1602          default:
1603             fp->Seek( cksize, FS_SEEK_CUR );
1604             break;
1605       }
1606
1607       /* end of the file? */
1608
1609       if ( formsize <= fp->Tell() - 8 ) break;
1610
1611       /* get the next chunk header */
1612
1613       set_flen( 0 );
1614       id = getU4( fp );
1615       cksize = getU4( fp );
1616       if ( 8 != get_flen() ) goto Fail;
1617    }
1618
1619    fileSystem->CloseFile( fp );
1620    fp = NULL;
1621
1622    if ( object->nlayers == 0 )
1623       object->nlayers = 1;
1624
1625    layer = object->layer;
1626    while ( layer ) {
1627       lwGetBoundingBox( &layer->point, layer->bbox );
1628       lwGetPolyNormals( &layer->point, &layer->polygon );
1629       if ( !lwGetPointPolygons( &layer->point, &layer->polygon )) goto Fail;
1630       if ( !lwResolvePolySurfaces( &layer->polygon, &object->taglist,
1631          &object->surf, &object->nsurfs )) goto Fail;
1632       lwGetVertNormals( &layer->point, &layer->polygon );
1633       if ( !lwGetPointVMaps( &layer->point, layer->vmap )) goto Fail;
1634       if ( !lwGetPolyVMaps( &layer->polygon, layer->vmap )) goto Fail;
1635       layer = layer->next;
1636    }
1637
1638    return object;
1639
1640 Fail:
1641    if ( failID ) *failID = id;
1642    if ( fp ) {
1643       if ( failpos ) *failpos = fp->Tell();
1644       fileSystem->CloseFile( fp );
1645    }
1646    lwFreeObject( object );
1647    return NULL;
1648 }
1649
1650
1651
1652
1653 /* IDs specific to LWOB */
1654
1655 #define ID_SRFS  LWID_('S','R','F','S')
1656 #define ID_FLAG  LWID_('F','L','A','G')
1657 #define ID_VLUM  LWID_('V','L','U','M')
1658 #define ID_VDIF  LWID_('V','D','I','F')
1659 #define ID_VSPC  LWID_('V','S','P','C')
1660 #define ID_RFLT  LWID_('R','F','L','T')
1661 #define ID_BTEX  LWID_('B','T','E','X')
1662 #define ID_CTEX  LWID_('C','T','E','X')
1663 #define ID_DTEX  LWID_('D','T','E','X')
1664 #define ID_LTEX  LWID_('L','T','E','X')
1665 #define ID_RTEX  LWID_('R','T','E','X')
1666 #define ID_STEX  LWID_('S','T','E','X')
1667 #define ID_TTEX  LWID_('T','T','E','X')
1668 #define ID_TFLG  LWID_('T','F','L','G')
1669 #define ID_TSIZ  LWID_('T','S','I','Z')
1670 #define ID_TCTR  LWID_('T','C','T','R')
1671 #define ID_TFAL  LWID_('T','F','A','L')
1672 #define ID_TVEL  LWID_('T','V','E','L')
1673 #define ID_TCLR  LWID_('T','C','L','R')
1674 #define ID_TVAL  LWID_('T','V','A','L')
1675 #define ID_TAMP  LWID_('T','A','M','P')
1676 #define ID_TIMG  LWID_('T','I','M','G')
1677 #define ID_TAAS  LWID_('T','A','A','S')
1678 #define ID_TREF  LWID_('T','R','E','F')
1679 #define ID_TOPC  LWID_('T','O','P','C')
1680 #define ID_SDAT  LWID_('S','D','A','T')
1681 #define ID_TFP0  LWID_('T','F','P','0')
1682 #define ID_TFP1  LWID_('T','F','P','1')
1683
1684
1685 /*
1686 ======================================================================
1687 add_clip()
1688
1689 Add a clip to the clip list.  Used to store the contents of an RIMG or
1690 TIMG surface subchunk.
1691 ====================================================================== */
1692
1693 static int add_clip( char *s, lwClip **clist, int *nclips )
1694 {
1695    lwClip *clip;
1696    char *p;
1697
1698    clip = (lwClip*)Mem_ClearedAlloc( sizeof( lwClip ) );
1699    if ( !clip ) return 0;
1700
1701    clip->contrast.val = 1.0f;
1702    clip->brightness.val = 1.0f;
1703    clip->saturation.val = 1.0f;
1704    clip->gamma.val = 1.0f;
1705
1706    if ( p = strstr( s, "(sequence)" )) {
1707       p[ -1 ] = 0;
1708       clip->type = ID_ISEQ;
1709       clip->source.seq.prefix = s;
1710       clip->source.seq.digits = 3;
1711    }
1712    else {
1713       clip->type = ID_STIL;
1714       clip->source.still.name = s;
1715    }
1716
1717    *nclips++;
1718    clip->index = *nclips;
1719
1720    lwListAdd( (void**)clist, clip );
1721
1722    return clip->index;
1723 }
1724
1725
1726 /*
1727 ======================================================================
1728 add_tvel()
1729
1730 Add a triple of envelopes to simulate the old texture velocity
1731 parameters.
1732 ====================================================================== */
1733
1734 static int add_tvel( float pos[], float vel[], lwEnvelope **elist, int *nenvs )
1735 {
1736    lwEnvelope *env;
1737    lwKey *key0, *key1;
1738    int i;
1739
1740    for ( i = 0; i < 3; i++ ) {
1741       env = (lwEnvelope*)Mem_ClearedAlloc( sizeof( lwEnvelope ) );
1742       key0 = (lwKey*)Mem_ClearedAlloc( sizeof( lwKey ) );
1743       key1 = (lwKey*)Mem_ClearedAlloc( sizeof( lwKey ) );
1744       if ( !env || !key0 || !key1 ) return 0;
1745
1746       key0->next = key1;
1747       key0->value = pos[ i ];
1748       key0->time = 0.0f;
1749       key1->prev = key0;
1750       key1->value = pos[ i ] + vel[ i ] * 30.0f;
1751       key1->time = 1.0f;
1752       key0->shape = key1->shape = ID_LINE;
1753
1754       env->index = *nenvs + i + 1;
1755       env->type = 0x0301 + i;
1756       env->name = (char*)Mem_ClearedAlloc( 11 );
1757       if ( env->name ) {
1758          strcpy( env->name, "Position.X" );
1759          env->name[ 9 ] += i;
1760       }
1761       env->key = key0;
1762       env->nkeys = 2;
1763       env->behavior[ 0 ] = BEH_LINEAR;
1764       env->behavior[ 1 ] = BEH_LINEAR;
1765
1766       lwListAdd( (void**)elist, env );
1767    }
1768
1769    *nenvs += 3;
1770    return env->index - 2;
1771 }
1772
1773
1774 /*
1775 ======================================================================
1776 get_texture()
1777
1778 Create a new texture for BTEX, CTEX, etc. subchunks.
1779 ====================================================================== */
1780
1781 static lwTexture *get_texture( char *s )
1782 {
1783    lwTexture *tex;
1784
1785    tex = (lwTexture*)Mem_ClearedAlloc( sizeof( lwTexture ) );
1786    if ( !tex ) return NULL;
1787
1788    tex->tmap.size.val[ 0 ] =
1789    tex->tmap.size.val[ 1 ] =
1790    tex->tmap.size.val[ 2 ] = 1.0f;
1791    tex->opacity.val = 1.0f;
1792    tex->enabled = 1;
1793
1794    if ( strstr( s, "Image Map" )) {
1795       tex->type = ID_IMAP;
1796       if ( strstr( s, "Planar" ))           tex->param.imap.projection = 0;
1797       else if ( strstr( s, "Cylindrical" )) tex->param.imap.projection = 1;
1798       else if ( strstr( s, "Spherical" ))   tex->param.imap.projection = 2;
1799       else if ( strstr( s, "Cubic" ))       tex->param.imap.projection = 3;
1800       else if ( strstr( s, "Front" ))       tex->param.imap.projection = 4;
1801       tex->param.imap.aa_strength = 1.0f;
1802       tex->param.imap.amplitude.val = 1.0f;
1803       Mem_Free( s );
1804    }
1805    else {
1806       tex->type = ID_PROC;
1807       tex->param.proc.name = s;
1808    }
1809
1810    return tex;
1811 }
1812
1813
1814 /*
1815 ======================================================================
1816 lwGetSurface5()
1817
1818 Read an lwSurface from an LWOB file.
1819 ====================================================================== */
1820
1821 lwSurface *lwGetSurface5( idFile *fp, int cksize, lwObject *obj )
1822 {
1823    lwSurface *surf;
1824    lwTexture *tex;
1825    lwPlugin *shdr;
1826    char *s;
1827    float v[ 3 ];
1828    unsigned int id, flags;
1829    unsigned short sz;
1830    int pos, rlen, i;
1831
1832
1833    /* allocate the Surface structure */
1834
1835    surf = (lwSurface*)Mem_ClearedAlloc( sizeof( lwSurface ) );
1836    if ( !surf ) goto Fail;
1837
1838    /* non-zero defaults */
1839
1840    surf->color.rgb[ 0 ] = 0.78431f;
1841    surf->color.rgb[ 1 ] = 0.78431f;
1842    surf->color.rgb[ 2 ] = 0.78431f;
1843    surf->diffuse.val    = 1.0f;
1844    surf->glossiness.val = 0.4f;
1845    surf->bump.val       = 1.0f;
1846    surf->eta.val        = 1.0f;
1847    surf->sideflags      = 1;
1848
1849    /* remember where we started */
1850
1851    set_flen( 0 );
1852    pos = fp->Tell();
1853
1854    /* name */
1855
1856    surf->name = getS0( fp );
1857
1858    /* first subchunk header */
1859
1860    id = getU4( fp );
1861    sz = getU2( fp );
1862    if ( 0 > get_flen() ) goto Fail;
1863
1864    /* process subchunks as they're encountered */
1865
1866    while ( 1 ) {
1867       sz += sz & 1;
1868       set_flen( 0 );
1869
1870       switch ( id ) {
1871          case ID_COLR:
1872             surf->color.rgb[ 0 ] = getU1( fp ) / 255.0f;
1873             surf->color.rgb[ 1 ] = getU1( fp ) / 255.0f;
1874             surf->color.rgb[ 2 ] = getU1( fp ) / 255.0f;
1875             break;
1876
1877          case ID_FLAG:
1878             flags = getU2( fp );
1879             if ( flags &   4 ) surf->smooth = 1.56207f;
1880             if ( flags &   8 ) surf->color_hilite.val = 1.0f;
1881             if ( flags &  16 ) surf->color_filter.val = 1.0f;
1882             if ( flags & 128 ) surf->dif_sharp.val = 0.5f;
1883             if ( flags & 256 ) surf->sideflags = 3;
1884             if ( flags & 512 ) surf->add_trans.val = 1.0f;
1885             break;
1886
1887          case ID_LUMI:
1888             surf->luminosity.val = getI2( fp ) / 256.0f;
1889             break;
1890
1891          case ID_VLUM:
1892             surf->luminosity.val = getF4( fp );
1893             break;
1894
1895          case ID_DIFF:
1896             surf->diffuse.val = getI2( fp ) / 256.0f;
1897             break;
1898
1899          case ID_VDIF:
1900             surf->diffuse.val = getF4( fp );
1901             break;
1902
1903          case ID_SPEC:
1904             surf->specularity.val = getI2( fp ) / 256.0f;
1905             break;
1906
1907          case ID_VSPC:
1908             surf->specularity.val = getF4( fp );
1909             break;
1910
1911          case ID_GLOS:
1912             surf->glossiness.val = ( float ) logf( ( float) getU2( fp )) / 20.7944f;
1913             break;
1914
1915          case ID_SMAN:
1916             surf->smooth = getF4( fp );
1917             break;
1918
1919          case ID_REFL:
1920             surf->reflection.val.val = getI2( fp ) / 256.0f;
1921             break;
1922
1923          case ID_RFLT:
1924             surf->reflection.options = getU2( fp );
1925             break;
1926
1927          case ID_RIMG:
1928             s = getS0( fp );
1929             surf->reflection.cindex = add_clip( s, &obj->clip, &obj->nclips );
1930             surf->reflection.options = 3;
1931             break;
1932
1933          case ID_RSAN:
1934             surf->reflection.seam_angle = getF4( fp );
1935             break;
1936
1937          case ID_TRAN:
1938             surf->transparency.val.val = getI2( fp ) / 256.0f;
1939             break;
1940
1941          case ID_RIND:
1942             surf->eta.val = getF4( fp );
1943             break;
1944
1945          case ID_BTEX:
1946             s = (char*)getbytes( fp, sz );
1947             tex = get_texture( s );
1948             lwListAdd( (void**)&surf->bump.tex, tex );
1949             break;
1950
1951          case ID_CTEX:
1952             s = (char*)getbytes( fp, sz );
1953             tex = get_texture( s );
1954             lwListAdd( (void**)&surf->color.tex, tex );
1955             break;
1956
1957          case ID_DTEX:
1958             s = (char*)getbytes( fp, sz );
1959             tex = get_texture( s );
1960             lwListAdd( (void**)&surf->diffuse.tex, tex );
1961             break;
1962
1963          case ID_LTEX:
1964             s = (char*)getbytes( fp, sz );
1965             tex = get_texture( s );
1966             lwListAdd( (void**)&surf->luminosity.tex, tex );
1967             break;
1968
1969          case ID_RTEX:
1970             s = (char*)getbytes( fp, sz );
1971             tex = get_texture( s );
1972             lwListAdd( (void**)&surf->reflection.val.tex, tex );
1973             break;
1974
1975          case ID_STEX:
1976             s = (char*)getbytes( fp, sz );
1977             tex = get_texture( s );
1978             lwListAdd( (void**)&surf->specularity.tex, tex );
1979             break;
1980
1981          case ID_TTEX:
1982             s = (char*)getbytes( fp, sz );
1983             tex = get_texture( s );
1984             lwListAdd( (void**)&surf->transparency.val.tex, tex );
1985             break;
1986
1987          case ID_TFLG:
1988             flags = getU2( fp );
1989
1990             if ( flags & 1 ) i = 0;
1991             if ( flags & 2 ) i = 1;
1992             if ( flags & 4 ) i = 2;
1993             tex->axis = i;
1994             if ( tex->type == ID_IMAP )
1995                tex->param.imap.axis = i;
1996             else
1997                tex->param.proc.axis = i;
1998
1999             if ( flags &  8 ) tex->tmap.coord_sys = 1;
2000             if ( flags & 16 ) tex->negative = 1;
2001             if ( flags & 32 ) tex->param.imap.pblend = 1;
2002             if ( flags & 64 ) {
2003                tex->param.imap.aa_strength = 1.0f;
2004                tex->param.imap.aas_flags = 1;
2005             }
2006             break;
2007
2008          case ID_TSIZ:
2009             for ( i = 0; i < 3; i++ )
2010                tex->tmap.size.val[ i ] = getF4( fp );
2011             break;
2012
2013          case ID_TCTR:
2014             for ( i = 0; i < 3; i++ )
2015                tex->tmap.center.val[ i ] = getF4( fp );
2016             break;
2017
2018          case ID_TFAL:
2019             for ( i = 0; i < 3; i++ )
2020                tex->tmap.falloff.val[ i ] = getF4( fp );
2021             break;
2022
2023          case ID_TVEL:
2024             for ( i = 0; i < 3; i++ )
2025                v[ i ] = getF4( fp );
2026             tex->tmap.center.eindex = add_tvel( tex->tmap.center.val, v,
2027                &obj->env, &obj->nenvs );
2028             break;
2029
2030          case ID_TCLR:
2031             if ( tex->type == ID_PROC )
2032                for ( i = 0; i < 3; i++ )
2033                   tex->param.proc.value[ i ] = getU1( fp ) / 255.0f;
2034             break;
2035
2036          case ID_TVAL:
2037             tex->param.proc.value[ 0 ] = getI2( fp ) / 256.0f;
2038             break;
2039
2040          case ID_TAMP:
2041             if ( tex->type == ID_IMAP )
2042                tex->param.imap.amplitude.val = getF4( fp );
2043             break;
2044
2045          case ID_TIMG:
2046             s = getS0( fp );
2047             tex->param.imap.cindex = add_clip( s, &obj->clip, &obj->nclips );
2048             break;
2049
2050          case ID_TAAS:
2051             tex->param.imap.aa_strength = getF4( fp );
2052             tex->param.imap.aas_flags = 1;
2053             break;
2054
2055          case ID_TREF:
2056             tex->tmap.ref_object = (char*)getbytes( fp, sz );
2057             break;
2058
2059          case ID_TOPC:
2060             tex->opacity.val = getF4( fp );
2061             break;
2062
2063          case ID_TFP0:
2064             if ( tex->type == ID_IMAP )
2065                tex->param.imap.wrapw.val = getF4( fp );
2066             break;
2067
2068          case ID_TFP1:
2069             if ( tex->type == ID_IMAP )
2070                tex->param.imap.wraph.val = getF4( fp );
2071             break;
2072
2073          case ID_SHDR:
2074             shdr = (lwPlugin*)Mem_ClearedAlloc( sizeof( lwPlugin ) );
2075             if ( !shdr ) goto Fail;
2076             shdr->name = (char*)getbytes( fp, sz );
2077             lwListAdd( (void**)&surf->shader, shdr );
2078             surf->nshaders++;
2079             break;
2080
2081          case ID_SDAT:
2082             shdr->data = getbytes( fp, sz );
2083             break;
2084
2085          default:
2086             break;
2087       }
2088
2089       /* error while reading current subchunk? */
2090
2091       rlen = get_flen();
2092       if ( rlen < 0 || rlen > sz ) goto Fail;
2093
2094       /* skip unread parts of the current subchunk */
2095
2096       if ( rlen < sz )
2097          fp->Seek( sz - rlen, FS_SEEK_CUR );
2098
2099       /* end of the SURF chunk? */
2100
2101       if ( cksize <= fp->Tell() - pos )
2102          break;
2103
2104       /* get the next subchunk header */
2105
2106       set_flen( 0 );
2107       id = getU4( fp );
2108       sz = getU2( fp );
2109       if ( 6 != get_flen() ) goto Fail;
2110    }
2111
2112    return surf;
2113
2114 Fail:
2115    if ( surf ) lwFreeSurface( surf );
2116    return NULL;
2117 }
2118
2119
2120 /*
2121 ======================================================================
2122 lwGetPolygons5()
2123
2124 Read polygon records from a POLS chunk in an LWOB file.  The polygons
2125 are added to the array in the lwPolygonList.
2126 ====================================================================== */
2127
2128 int lwGetPolygons5( idFile *fp, int cksize, lwPolygonList *plist, int ptoffset )
2129 {
2130    lwPolygon *pp;
2131    lwPolVert *pv;
2132    unsigned char *buf, *bp;
2133    int i, j, nv, nverts, npols;
2134
2135
2136    if ( cksize == 0 ) return 1;
2137
2138    /* read the whole chunk */
2139
2140    set_flen( 0 );
2141    buf = (unsigned char*)getbytes( fp, cksize );
2142    if ( !buf ) goto Fail;
2143
2144    /* count the polygons and vertices */
2145
2146    nverts = 0;
2147    npols = 0;
2148    bp = buf;
2149
2150    while ( bp < buf + cksize ) {
2151       nv = sgetU2( &bp );
2152       nverts += nv;
2153       npols++;
2154       bp += 2 * nv;
2155       i = sgetI2( &bp );
2156       if ( i < 0 ) bp += 2;      /* detail polygons */
2157    }
2158
2159    if ( !lwAllocPolygons( plist, npols, nverts ))
2160       goto Fail;
2161
2162    /* fill in the new polygons */
2163
2164    bp = buf;
2165    pp = plist->pol + plist->offset;
2166    pv = plist->pol[ 0 ].v + plist->voffset;
2167
2168    for ( i = 0; i < npols; i++ ) {
2169       nv = sgetU2( &bp );
2170
2171       pp->nverts = nv;
2172       pp->type = ID_FACE;
2173       if ( !pp->v ) pp->v = pv;
2174       for ( j = 0; j < nv; j++ )
2175          pv[ j ].index = sgetU2( &bp ) + ptoffset;
2176       j = sgetI2( &bp );
2177       if ( j < 0 ) {
2178          j = -j;
2179          bp += 2;
2180       }
2181       j -= 1;
2182       pp->surf = ( lwSurface * ) j;
2183
2184       pp++;
2185       pv += nv;
2186    }
2187
2188    Mem_Free( buf );
2189    return 1;
2190
2191 Fail:
2192    if ( buf ) Mem_Free( buf );
2193    lwFreePolygons( plist );
2194    return 0;
2195 }
2196
2197
2198 /*
2199 ======================================================================
2200 getLWObject5()
2201
2202 Returns the contents of an LWOB, given its filename, or NULL if the
2203 file couldn't be loaded.  On failure, failID and failpos can be used
2204 to diagnose the cause.
2205
2206 1.  If the file isn't an LWOB, failpos will contain 12 and failID will
2207     be unchanged.
2208
2209 2.  If an error occurs while reading an LWOB, failID will contain the
2210     most recently read IFF chunk ID, and failpos will contain the
2211     value returned by fp->Tell() at the time of the failure.
2212
2213 3.  If the file couldn't be opened, or an error occurs while reading
2214     the first 12 bytes, both failID and failpos will be unchanged.
2215
2216 If you don't need this information, failID and failpos can be NULL.
2217 ====================================================================== */
2218
2219 lwObject *lwGetObject5( const char *filename, unsigned int *failID, int *failpos )
2220 {
2221    idFile *fp = NULL;
2222    lwObject *object;
2223    lwLayer *layer;
2224    lwNode *node;
2225    int id, formsize, type, cksize;
2226
2227
2228    /* open the file */
2229
2230    //fp = fopen( filename, "rb" );
2231    //if ( !fp ) return NULL;
2232
2233    /* read the first 12 bytes */
2234    fp = fileSystem->OpenFileRead( filename );
2235    if ( !fp ) {
2236            return NULL;
2237    }
2238
2239    set_flen( 0 );
2240    id       = getU4( fp );
2241    formsize = getU4( fp );
2242    type     = getU4( fp );
2243    if ( 12 != get_flen() ) {
2244       fileSystem->CloseFile( fp );
2245       return NULL;
2246    }
2247
2248    /* LWOB? */
2249
2250    if ( id != ID_FORM || type != ID_LWOB ) {
2251       fileSystem->CloseFile( fp );
2252       if ( failpos ) *failpos = 12;
2253       return NULL;
2254    }
2255
2256    /* allocate an object and a default layer */
2257
2258    object = (lwObject*)Mem_ClearedAlloc( sizeof( lwObject ) );
2259    if ( !object ) goto Fail2;
2260
2261    layer = (lwLayer*)Mem_ClearedAlloc( sizeof( lwLayer ) );
2262    if ( !layer ) goto Fail2;
2263    object->layer = layer;
2264    object->nlayers = 1;
2265
2266    /* get the first chunk header */
2267
2268    id = getU4( fp );
2269    cksize = getU4( fp );
2270    if ( 0 > get_flen() ) goto Fail2;
2271
2272    /* process chunks as they're encountered */
2273
2274    while ( 1 ) {
2275       cksize += cksize & 1;
2276
2277       switch ( id )
2278       {
2279          case ID_PNTS:
2280             if ( !lwGetPoints( fp, cksize, &layer->point ))
2281                goto Fail2;
2282             break;
2283
2284          case ID_POLS:
2285             if ( !lwGetPolygons5( fp, cksize, &layer->polygon,
2286                layer->point.offset ))
2287                goto Fail2;
2288             break;
2289
2290          case ID_SRFS:
2291             if ( !lwGetTags( fp, cksize, &object->taglist ))
2292                goto Fail2;
2293             break;
2294
2295          case ID_SURF:
2296             node = ( lwNode * ) lwGetSurface5( fp, cksize, object );
2297             if ( !node ) goto Fail2;
2298             lwListAdd( (void**)&object->surf, node );
2299             object->nsurfs++;
2300             break;
2301
2302          default:
2303             fp->Seek( cksize, FS_SEEK_CUR );
2304             break;
2305       }
2306
2307       /* end of the file? */
2308
2309       if ( formsize <= fp->Tell() - 8 ) break;
2310
2311       /* get the next chunk header */
2312
2313       set_flen( 0 );
2314       id = getU4( fp );
2315       cksize = getU4( fp );
2316       if ( 8 != get_flen() ) goto Fail2;
2317    }
2318
2319    fileSystem->CloseFile( fp );
2320    fp = NULL;
2321
2322    lwGetBoundingBox( &layer->point, layer->bbox );
2323    lwGetPolyNormals( &layer->point, &layer->polygon );
2324    if ( !lwGetPointPolygons( &layer->point, &layer->polygon )) goto Fail2;
2325    if ( !lwResolvePolySurfaces( &layer->polygon, &object->taglist,
2326       &object->surf, &object->nsurfs )) goto Fail2;
2327    lwGetVertNormals( &layer->point, &layer->polygon );
2328
2329    return object;
2330
2331 Fail2:
2332    if ( failID ) *failID = id;
2333    if ( fp ) {
2334       if ( failpos ) *failpos = fp->Tell();
2335       fileSystem->CloseFile( fp );
2336    }
2337    lwFreeObject( object );
2338    return NULL;
2339 }
2340
2341 /*
2342 ======================================================================
2343 lwFreePoints()
2344
2345 Free the memory used by an lwPointList.
2346 ====================================================================== */
2347
2348 void lwFreePoints( lwPointList *point )
2349 {
2350    int i;
2351
2352    if ( point ) {
2353       if ( point->pt ) {
2354          for ( i = 0; i < point->count; i++ ) {
2355             if ( point->pt[ i ].pol ) Mem_Free( point->pt[ i ].pol );
2356             if ( point->pt[ i ].vm ) Mem_Free( point->pt[ i ].vm );
2357          }
2358          Mem_Free( point->pt );
2359       }
2360       memset( point, 0, sizeof( lwPointList ));
2361    }
2362 }
2363
2364
2365 /*
2366 ======================================================================
2367 lwFreePolygons()
2368
2369 Free the memory used by an lwPolygonList.
2370 ====================================================================== */
2371
2372 void lwFreePolygons( lwPolygonList *plist )
2373 {
2374    int i, j;
2375
2376    if ( plist ) {
2377       if ( plist->pol ) {
2378          for ( i = 0; i < plist->count; i++ ) {
2379             if ( plist->pol[ i ].v ) {
2380                for ( j = 0; j < plist->pol[ i ].nverts; j++ )
2381                   if ( plist->pol[ i ].v[ j ].vm )
2382                      Mem_Free( plist->pol[ i ].v[ j ].vm );
2383             }
2384          }
2385          if ( plist->pol[ 0 ].v )
2386             Mem_Free( plist->pol[ 0 ].v );
2387          Mem_Free( plist->pol );
2388       }
2389       memset( plist, 0, sizeof( lwPolygonList ));
2390    }
2391 }
2392
2393
2394 /*
2395 ======================================================================
2396 lwGetPoints()
2397
2398 Read point records from a PNTS chunk in an LWO2 file.  The points are
2399 added to the array in the lwPointList.
2400 ====================================================================== */
2401
2402 int lwGetPoints( idFile *fp, int cksize, lwPointList *point )
2403 {
2404         float *f;
2405         int np, i, j;
2406
2407         if ( cksize == 1 ) return 1;
2408
2409         /* extend the point array to hold the new points */
2410
2411         np = cksize / 12;
2412         point->offset = point->count;
2413         point->count += np;
2414         lwPoint *oldpt = point->pt;
2415         point->pt = (lwPoint*)Mem_Alloc( point->count * sizeof( lwPoint ) );
2416         if ( !point->pt ) return 0;
2417         if ( oldpt ) {
2418                 memcpy( point->pt, oldpt, point->offset * sizeof( lwPoint ) );
2419                 Mem_Free( oldpt );
2420         }
2421         memset( &point->pt[ point->offset ], 0, np * sizeof( lwPoint ) );
2422
2423         /* read the whole chunk */
2424
2425         f = ( float * ) getbytes( fp, cksize );
2426         if ( !f ) return 0;
2427         BigRevBytes( f, 4, np * 3 );
2428
2429         /* assign position values */
2430
2431         for ( i = 0, j = 0; i < np; i++, j += 3 ) {
2432                 point->pt[ i ].pos[ 0 ] = f[ j ];
2433                 point->pt[ i ].pos[ 1 ] = f[ j + 1 ];
2434                 point->pt[ i ].pos[ 2 ] = f[ j + 2 ];
2435         }
2436
2437         Mem_Free( f );
2438         return 1;
2439 }
2440
2441
2442 /*
2443 ======================================================================
2444 lwGetBoundingBox()
2445
2446 Calculate the bounding box for a point list, but only if the bounding
2447 box hasn't already been initialized.
2448 ====================================================================== */
2449
2450 void lwGetBoundingBox( lwPointList *point, float bbox[] )
2451 {
2452         int i, j;
2453
2454         if ( point->count == 0 ) return;
2455
2456         for ( i = 0; i < 6; i++ )
2457                 if ( bbox[ i ] != 0.0f ) return;
2458
2459         bbox[ 0 ] = bbox[ 1 ] = bbox[ 2 ] = 1e20f;
2460         bbox[ 3 ] = bbox[ 4 ] = bbox[ 5 ] = -1e20f;
2461         for ( i = 0; i < point->count; i++ ) {
2462                 for ( j = 0; j < 3; j++ ) {
2463                         if ( bbox[ j ] > point->pt[ i ].pos[ j ] )
2464                         bbox[ j ] = point->pt[ i ].pos[ j ];
2465                         if ( bbox[ j + 3 ] < point->pt[ i ].pos[ j ] )
2466                         bbox[ j + 3 ] = point->pt[ i ].pos[ j ];
2467                 }
2468         }
2469 }
2470
2471
2472 /*
2473 ======================================================================
2474 lwAllocPolygons()
2475
2476 Allocate or extend the polygon arrays to hold new records.
2477 ====================================================================== */
2478
2479 int lwAllocPolygons( lwPolygonList *plist, int npols, int nverts )
2480 {
2481         int i;
2482
2483         plist->offset = plist->count;
2484         plist->count += npols;
2485         lwPolygon *oldpol = plist->pol;
2486         plist->pol = (lwPolygon*)Mem_Alloc( plist->count * sizeof( lwPolygon ) );
2487         if ( !plist->pol ) return 0;
2488         if ( oldpol ) {
2489                 memcpy( plist->pol, oldpol, plist->offset * sizeof( lwPolygon ) );
2490                 Mem_Free( oldpol );
2491         }
2492         memset( plist->pol + plist->offset, 0, npols * sizeof( lwPolygon ) );
2493
2494         plist->voffset = plist->vcount;
2495         plist->vcount += nverts;
2496         lwPolVert *oldpolv = plist->pol[0].v;
2497         plist->pol[0].v = (lwPolVert*)Mem_Alloc( plist->vcount * sizeof( lwPolVert ) );
2498         if ( !plist->pol[ 0 ].v ) return 0;
2499         if ( oldpolv ) {
2500                 memcpy( plist->pol[0].v, oldpolv, plist->voffset * sizeof( lwPolVert ) );
2501                 Mem_Free( oldpolv );
2502         }
2503         memset( plist->pol[ 0 ].v + plist->voffset, 0, nverts * sizeof( lwPolVert ) );
2504
2505         /* fix up the old vertex pointers */
2506
2507         for ( i = 1; i < plist->offset; i++ )
2508                 plist->pol[ i ].v = plist->pol[ i - 1 ].v + plist->pol[ i - 1 ].nverts;
2509
2510         return 1;
2511 }
2512
2513
2514 /*
2515 ======================================================================
2516 lwGetPolygons()
2517
2518 Read polygon records from a POLS chunk in an LWO2 file.  The polygons
2519 are added to the array in the lwPolygonList.
2520 ====================================================================== */
2521
2522 int lwGetPolygons( idFile *fp, int cksize, lwPolygonList *plist, int ptoffset )
2523 {
2524    lwPolygon *pp;
2525    lwPolVert *pv;
2526    unsigned char *buf, *bp;
2527    int i, j, flags, nv, nverts, npols;
2528    unsigned int type;
2529
2530
2531    if ( cksize == 0 ) return 1;
2532
2533    /* read the whole chunk */
2534
2535    set_flen( 0 );
2536    type = getU4( fp );
2537    buf = (unsigned char*)getbytes( fp, cksize - 4 );
2538    if ( cksize != get_flen() ) goto Fail;
2539
2540    /* count the polygons and vertices */
2541
2542    nverts = 0;
2543    npols = 0;
2544    bp = buf;
2545
2546    while ( bp < buf + cksize - 4 ) {
2547       nv = sgetU2( &bp );
2548       nv &= 0x03FF;
2549       nverts += nv;
2550       npols++;
2551       for ( i = 0; i < nv; i++ )
2552          j = sgetVX( &bp );
2553    }
2554
2555    if ( !lwAllocPolygons( plist, npols, nverts ))
2556       goto Fail;
2557
2558    /* fill in the new polygons */
2559
2560    bp = buf;
2561    pp = plist->pol + plist->offset;
2562    pv = plist->pol[ 0 ].v + plist->voffset;
2563
2564    for ( i = 0; i < npols; i++ ) {
2565       nv = sgetU2( &bp );
2566       flags = nv & 0xFC00;
2567       nv &= 0x03FF;
2568
2569       pp->nverts = nv;
2570       pp->flags = flags;
2571       pp->type = type;
2572       if ( !pp->v ) pp->v = pv;
2573       for ( j = 0; j < nv; j++ )
2574          pp->v[ j ].index = sgetVX( &bp ) + ptoffset;
2575
2576       pp++;
2577       pv += nv;
2578    }
2579
2580    Mem_Free( buf );
2581    return 1;
2582
2583 Fail:
2584    if ( buf ) Mem_Free( buf );
2585    lwFreePolygons( plist );
2586    return 0;
2587 }
2588
2589
2590 /*
2591 ======================================================================
2592 lwGetPolyNormals()
2593
2594 Calculate the polygon normals.  By convention, LW's polygon normals
2595 are found as the cross product of the first and last edges.  It's
2596 undefined for one- and two-point polygons.
2597 ====================================================================== */
2598
2599 void lwGetPolyNormals( lwPointList *point, lwPolygonList *polygon )
2600 {
2601    int i, j;
2602    float p1[ 3 ], p2[ 3 ], pn[ 3 ], v1[ 3 ], v2[ 3 ];
2603
2604    for ( i = 0; i < polygon->count; i++ ) {
2605       if ( polygon->pol[ i ].nverts < 3 ) continue;
2606       for ( j = 0; j < 3; j++ ) {
2607
2608                   // FIXME: track down why indexes are way out of range
2609          p1[ j ] = point->pt[ polygon->pol[ i ].v[ 0 ].index ].pos[ j ];
2610          p2[ j ] = point->pt[ polygon->pol[ i ].v[ 1 ].index ].pos[ j ];
2611          pn[ j ] = point->pt[ polygon->pol[ i ].v[ polygon->pol[ i ].nverts - 1 ].index ].pos[ j ];
2612       }
2613
2614       for ( j = 0; j < 3; j++ ) {
2615          v1[ j ] = p2[ j ] - p1[ j ];
2616          v2[ j ] = pn[ j ] - p1[ j ];
2617       }
2618
2619       cross( v1, v2, polygon->pol[ i ].norm );
2620       normalize( polygon->pol[ i ].norm );
2621    }
2622 }
2623
2624
2625 /*
2626 ======================================================================
2627 lwGetPointPolygons()
2628
2629 For each point, fill in the indexes of the polygons that share the
2630 point.  Returns 0 if any of the memory allocations fail, otherwise
2631 returns 1.
2632 ====================================================================== */
2633
2634 int lwGetPointPolygons( lwPointList *point, lwPolygonList *polygon )
2635 {
2636    int i, j, k;
2637
2638    /* count the number of polygons per point */
2639
2640    for ( i = 0; i < polygon->count; i++ )
2641       for ( j = 0; j < polygon->pol[ i ].nverts; j++ )
2642          ++point->pt[ polygon->pol[ i ].v[ j ].index ].npols;
2643
2644    /* alloc per-point polygon arrays */
2645
2646    for ( i = 0; i < point->count; i++ ) {
2647       if ( point->pt[ i ].npols == 0 ) continue;
2648       point->pt[ i ].pol = (int*)Mem_ClearedAlloc( point->pt[ i ].npols * sizeof( int ) );
2649       if ( !point->pt[ i ].pol ) return 0;
2650       point->pt[ i ].npols = 0;
2651    }
2652
2653    /* fill in polygon array for each point */
2654
2655    for ( i = 0; i < polygon->count; i++ ) {
2656       for ( j = 0; j < polygon->pol[ i ].nverts; j++ ) {
2657          k = polygon->pol[ i ].v[ j ].index;
2658          point->pt[ k ].pol[ point->pt[ k ].npols ] = i;
2659          ++point->pt[ k ].npols;
2660       }
2661    }
2662
2663    return 1;
2664 }
2665
2666
2667 /*
2668 ======================================================================
2669 lwResolvePolySurfaces()
2670
2671 Convert tag indexes into actual lwSurface pointers.  If any polygons
2672 point to tags for which no corresponding surface can be found, a
2673 default surface is created.
2674 ====================================================================== */
2675
2676 int lwResolvePolySurfaces( lwPolygonList *polygon, lwTagList *tlist,
2677    lwSurface **surf, int *nsurfs )
2678 {
2679    lwSurface **s, *st;
2680    int i, index;
2681
2682    if ( tlist->count == 0 ) return 1;
2683
2684    s = (lwSurface**)Mem_ClearedAlloc( tlist->count * sizeof( lwSurface * ) );
2685    if ( !s ) return 0;
2686
2687    for ( i = 0; i < tlist->count; i++ ) {
2688       st = *surf;
2689       while ( st ) {
2690          if ( !strcmp( st->name, tlist->tag[ i ] )) {
2691             s[ i ] = st;
2692             break;
2693          }
2694          st = st->next;
2695       }
2696    }
2697
2698    for ( i = 0; i < polygon->count; i++ ) {
2699       index = ( int ) polygon->pol[ i ].surf;
2700       if ( index < 0 || index > tlist->count ) return 0;
2701       if ( !s[ index ] ) {
2702          s[ index ] = lwDefaultSurface();
2703          if ( !s[ index ] ) return 0;
2704          s[ index ]->name = (char*)Mem_ClearedAlloc( strlen( tlist->tag[ index ] ) + 1 );
2705          if ( !s[ index ]->name ) return 0;
2706          strcpy( s[ index ]->name, tlist->tag[ index ] );
2707          lwListAdd( (void**)surf, s[ index ] );
2708          *nsurfs = *nsurfs + 1;
2709       }
2710       polygon->pol[ i ].surf = s[ index ];
2711    }
2712
2713    Mem_Free( s );
2714    return 1;
2715 }
2716
2717
2718 /*
2719 ======================================================================
2720 lwGetVertNormals()
2721
2722 Calculate the vertex normals.  For each polygon vertex, sum the
2723 normals of the polygons that share the point.  If the normals of the
2724 current and adjacent polygons form an angle greater than the max
2725 smoothing angle for the current polygon's surface, the normal of the
2726 adjacent polygon is excluded from the sum.  It's also excluded if the
2727 polygons aren't in the same smoothing group.
2728
2729 Assumes that lwGetPointPolygons(), lwGetPolyNormals() and
2730 lwResolvePolySurfaces() have already been called.
2731 ====================================================================== */
2732
2733 void lwGetVertNormals( lwPointList *point, lwPolygonList *polygon )
2734 {
2735    int j, k, n, g, h, p;
2736    float a;
2737
2738    for ( j = 0; j < polygon->count; j++ ) {
2739       for ( n = 0; n < polygon->pol[ j ].nverts; n++ ) {
2740          for ( k = 0; k < 3; k++ )
2741             polygon->pol[ j ].v[ n ].norm[ k ] = polygon->pol[ j ].norm[ k ];
2742
2743          if ( polygon->pol[ j ].surf->smooth <= 0 ) continue;
2744
2745          p = polygon->pol[ j ].v[ n ].index;
2746
2747          for ( g = 0; g < point->pt[ p ].npols; g++ ) {
2748             h = point->pt[ p ].pol[ g ];
2749             if ( h == j ) continue;
2750
2751             if ( polygon->pol[ j ].smoothgrp != polygon->pol[ h ].smoothgrp )
2752                continue;
2753             a = vecangle( polygon->pol[ j ].norm, polygon->pol[ h ].norm );
2754             if ( a > polygon->pol[ j ].surf->smooth ) continue;
2755
2756             for ( k = 0; k < 3; k++ )
2757                polygon->pol[ j ].v[ n ].norm[ k ] += polygon->pol[ h ].norm[ k ];
2758          }
2759
2760          normalize( polygon->pol[ j ].v[ n ].norm );
2761       }
2762    }
2763 }
2764
2765
2766 /*
2767 ======================================================================
2768 lwFreeTags()
2769
2770 Free memory used by an lwTagList.
2771 ====================================================================== */
2772
2773 void lwFreeTags( lwTagList *tlist )
2774 {
2775         int i;
2776
2777         if ( tlist ) {
2778                 if ( tlist->tag ) {
2779                         for ( i = 0; i < tlist->count; i++ )
2780                                 if ( tlist->tag[ i ] ) {
2781                                         Mem_Free( tlist->tag[ i ] );
2782                                 }
2783                         Mem_Free( tlist->tag );
2784                 }
2785                 memset( tlist, 0, sizeof( lwTagList ));
2786         }
2787 }
2788
2789
2790 /*
2791 ======================================================================
2792 lwGetTags()
2793
2794 Read tag strings from a TAGS chunk in an LWO2 file.  The tags are
2795 added to the lwTagList array.
2796 ====================================================================== */
2797
2798 int lwGetTags( idFile *fp, int cksize, lwTagList *tlist )
2799 {
2800         char *buf, *bp;
2801         int i, len, ntags;
2802
2803         if ( cksize == 0 ) return 1;
2804
2805         /* read the whole chunk */
2806
2807         set_flen( 0 );
2808         buf = (char*)getbytes( fp, cksize );
2809         if ( !buf ) return 0;
2810
2811         /* count the strings */
2812
2813         ntags = 0;
2814         bp = buf;
2815         while ( bp < buf + cksize ) {
2816                 len = strlen( bp ) + 1;
2817                 len += len & 1;
2818                 bp += len;
2819                 ++ntags;
2820         }
2821
2822         /* expand the string array to hold the new tags */
2823
2824         tlist->offset = tlist->count;
2825         tlist->count += ntags;
2826         char **oldtag = tlist->tag;
2827         tlist->tag = (char**)Mem_Alloc( tlist->count * sizeof( char * ) );
2828         if ( !tlist->tag ) goto Fail;
2829         if ( oldtag ) {
2830                 memcpy( tlist->tag, oldtag, tlist->offset * sizeof( char * ) );
2831                 Mem_Free( oldtag );
2832         }
2833         memset( &tlist->tag[ tlist->offset ], 0, ntags * sizeof( char * ) );
2834
2835         /* copy the new tags to the tag array */
2836
2837         bp = buf;
2838         for ( i = 0; i < ntags; i++ )
2839                 tlist->tag[ i + tlist->offset ] = sgetS0( (unsigned char**)&bp );
2840
2841         Mem_Free( buf );
2842         return 1;
2843
2844 Fail:
2845         if ( buf ) Mem_Free( buf );
2846         return 0;
2847 }
2848
2849
2850 /*
2851 ======================================================================
2852 lwGetPolygonTags()
2853
2854 Read polygon tags from a PTAG chunk in an LWO2 file.
2855 ====================================================================== */
2856
2857 int lwGetPolygonTags( idFile *fp, int cksize, lwTagList *tlist, lwPolygonList *plist )
2858 {
2859         unsigned int type;
2860         int rlen = 0, i, j;
2861
2862         set_flen( 0 );
2863         type = getU4( fp );
2864         rlen = get_flen();
2865         if ( rlen < 0 ) return 0;
2866
2867         if ( type != ID_SURF && type != ID_PART && type != ID_SMGP ) {
2868                 fp->Seek( cksize - 4, FS_SEEK_CUR );
2869                 return 1;
2870         }
2871
2872         while ( rlen < cksize ) {
2873                 i = getVX( fp ) + plist->offset;
2874                 j = getVX( fp ) + tlist->offset;
2875                 rlen = get_flen();
2876                 if ( rlen < 0 || rlen > cksize ) return 0;
2877
2878                 switch ( type ) {
2879                         case ID_SURF:  plist->pol[ i ].surf = ( lwSurface * ) j;  break;
2880                         case ID_PART:  plist->pol[ i ].part = j;  break;
2881                         case ID_SMGP:  plist->pol[ i ].smoothgrp = j;  break;
2882                 }
2883         }
2884
2885         return 1;
2886 }
2887
2888
2889 /*
2890 ======================================================================
2891 lwFreePlugin()
2892
2893 Free the memory used by an lwPlugin.
2894 ====================================================================== */
2895
2896 void lwFreePlugin( lwPlugin *p )
2897 {
2898         if ( p ) {
2899                 if ( p->ord ) Mem_Free( p->ord );
2900                 if ( p->name ) Mem_Free( p->name );
2901                 if ( p->data ) Mem_Free( p->data );
2902                 Mem_Free( p );
2903         }
2904 }
2905
2906
2907 /*
2908 ======================================================================
2909 lwFreeTexture()
2910
2911 Free the memory used by an lwTexture.
2912 ====================================================================== */
2913
2914 void lwFreeTexture( lwTexture *t )
2915 {
2916         if ( t ) {
2917                 if ( t->ord ) Mem_Free( t->ord );
2918                 switch ( t->type ) {
2919                         case ID_IMAP:
2920                         if ( t->param.imap.vmap_name ) Mem_Free( t->param.imap.vmap_name );
2921                         break;
2922                         case ID_PROC:
2923                         if ( t->param.proc.name ) Mem_Free( t->param.proc.name );
2924                         if ( t->param.proc.data ) Mem_Free( t->param.proc.data );
2925                         break;
2926                         case ID_GRAD:
2927                         if ( t->param.grad.key ) Mem_Free( t->param.grad.key );
2928                         if ( t->param.grad.ikey ) Mem_Free( t->param.grad.ikey );
2929                         break;
2930                 }
2931                 if ( t->tmap.ref_object ) Mem_Free( t->tmap.ref_object );
2932                 Mem_Free( t );
2933         }
2934 }
2935
2936
2937 /*
2938 ======================================================================
2939 lwFreeSurface()
2940
2941 Free the memory used by an lwSurface.
2942 ====================================================================== */
2943
2944 void lwFreeSurface( lwSurface *surf )
2945 {
2946         if ( surf ) {
2947                 if ( surf->name ) Mem_Free( surf->name );
2948                 if ( surf->srcname ) Mem_Free( surf->srcname );
2949
2950                 lwListFree( surf->shader, (void (__cdecl *)(void *))lwFreePlugin );
2951
2952                 lwListFree( surf->color.tex, (void (__cdecl *)(void *))lwFreeTexture );
2953                 lwListFree( surf->luminosity.tex, (void (__cdecl *)(void *))lwFreeTexture );
2954                 lwListFree( surf->diffuse.tex, (void (__cdecl *)(void *))lwFreeTexture );
2955                 lwListFree( surf->specularity.tex, (void (__cdecl *)(void *))lwFreeTexture );
2956                 lwListFree( surf->glossiness.tex, (void (__cdecl *)(void *))lwFreeTexture );
2957                 lwListFree( surf->reflection.val.tex, (void (__cdecl *)(void *))lwFreeTexture );
2958                 lwListFree( surf->transparency.val.tex, (void (__cdecl *)(void *))lwFreeTexture );
2959                 lwListFree( surf->eta.tex, (void (__cdecl *)(void *))lwFreeTexture );
2960                 lwListFree( surf->translucency.tex, (void (__cdecl *)(void *))lwFreeTexture );
2961                 lwListFree( surf->bump.tex, (void (__cdecl *)(void *))lwFreeTexture );
2962
2963                 Mem_Free( surf );
2964         }
2965 }
2966
2967
2968 /*
2969 ======================================================================
2970 lwGetTHeader()
2971
2972 Read a texture map header from a SURF.BLOK in an LWO2 file.  This is
2973 the first subchunk in a BLOK, and its contents are common to all three
2974 texture types.
2975 ====================================================================== */
2976
2977 int lwGetTHeader( idFile *fp, int hsz, lwTexture *tex )
2978 {
2979         unsigned int id;
2980         unsigned short sz;
2981         int pos, rlen;
2982
2983
2984         /* remember where we started */
2985
2986         set_flen( 0 );
2987         pos = fp->Tell();
2988
2989         /* ordinal string */
2990
2991         tex->ord = getS0( fp );
2992
2993         /* first subchunk header */
2994
2995         id = getU4( fp );
2996         sz = getU2( fp );
2997         if ( 0 > get_flen() ) return 0;
2998
2999         /* process subchunks as they're encountered */
3000
3001         while ( 1 ) {
3002                 sz += sz & 1;
3003                 set_flen( 0 );
3004
3005                 switch ( id ) {
3006                         case ID_CHAN:
3007                                 tex->chan = getU4( fp );
3008                                 break;
3009
3010                         case ID_OPAC:
3011                                 tex->opac_type = getU2( fp );
3012                                 tex->opacity.val = getF4( fp );
3013                                 tex->opacity.eindex = getVX( fp );
3014                                 break;
3015
3016                         case ID_ENAB:
3017                                 tex->enabled = getU2( fp );
3018                                 break;
3019
3020                         case ID_NEGA:
3021                                 tex->negative = getU2( fp );
3022                                 break;
3023
3024                         case ID_AXIS:
3025                                 tex->axis = getU2( fp );
3026                                 break;
3027
3028                         default:
3029                                 break;
3030                 }
3031
3032                 /* error while reading current subchunk? */
3033
3034                 rlen = get_flen();
3035                 if ( rlen < 0 || rlen > sz ) return 0;
3036
3037                 /* skip unread parts of the current subchunk */
3038
3039                 if ( rlen < sz )
3040                         fp->Seek( sz - rlen, FS_SEEK_CUR );
3041
3042                 /* end of the texture header subchunk? */
3043
3044                 if ( hsz <= fp->Tell() - pos )
3045                         break;
3046
3047                 /* get the next subchunk header */
3048
3049                 set_flen( 0 );
3050                 id = getU4( fp );
3051                 sz = getU2( fp );
3052                 if ( 6 != get_flen() ) return 0;
3053         }
3054
3055         set_flen( fp->Tell() - pos );
3056         return 1;
3057 }
3058
3059
3060 /*
3061 ======================================================================
3062 lwGetTMap()
3063
3064 Read a texture map from a SURF.BLOK in an LWO2 file.  The TMAP
3065 defines the mapping from texture to world or object coordinates.
3066 ====================================================================== */
3067
3068 int lwGetTMap( idFile *fp, int tmapsz, lwTMap *tmap )
3069 {
3070         unsigned int id;
3071         unsigned short sz;
3072         int rlen, pos, i;
3073
3074         pos = fp->Tell();
3075         id = getU4( fp );
3076         sz = getU2( fp );
3077         if ( 0 > get_flen() ) return 0;
3078
3079         while ( 1 ) {
3080                 sz += sz & 1;
3081                 set_flen( 0 );
3082
3083                 switch ( id ) {
3084                         case ID_SIZE:
3085                                 for ( i = 0; i < 3; i++ )
3086                                 tmap->size.val[ i ] = getF4( fp );
3087                                 tmap->size.eindex = getVX( fp );
3088                                 break;
3089
3090                         case ID_CNTR:
3091                                 for ( i = 0; i < 3; i++ )
3092                                 tmap->center.val[ i ] = getF4( fp );
3093                                 tmap->center.eindex = getVX( fp );
3094                                 break;
3095
3096                         case ID_ROTA:
3097                                 for ( i = 0; i < 3; i++ )
3098                                 tmap->rotate.val[ i ] = getF4( fp );
3099                                 tmap->rotate.eindex = getVX( fp );
3100                                 break;
3101
3102                         case ID_FALL:
3103                                 tmap->fall_type = getU2( fp );
3104                                 for ( i = 0; i < 3; i++ )
3105                                 tmap->falloff.val[ i ] = getF4( fp );
3106                                 tmap->falloff.eindex = getVX( fp );
3107                                 break;
3108
3109                         case ID_OREF:
3110                                 tmap->ref_object = getS0( fp );
3111                                 break;
3112
3113                         case ID_CSYS:
3114                                 tmap->coord_sys = getU2( fp );
3115                                 break;
3116
3117                         default:
3118                                 break;
3119                 }
3120
3121                 /* error while reading the current subchunk? */
3122
3123                 rlen = get_flen();
3124                 if ( rlen < 0 || rlen > sz ) return 0;
3125
3126                 /* skip unread parts of the current subchunk */
3127
3128                 if ( rlen < sz )
3129                         fp->Seek( sz - rlen, FS_SEEK_CUR );
3130
3131                 /* end of the TMAP subchunk? */
3132
3133                 if ( tmapsz <= fp->Tell() - pos )
3134                         break;
3135
3136                 /* get the next subchunk header */
3137
3138                 set_flen( 0 );
3139                 id = getU4( fp );
3140                 sz = getU2( fp );
3141                 if ( 6 != get_flen() ) return 0;
3142         }
3143
3144         set_flen( fp->Tell() - pos );
3145         return 1;
3146 }
3147
3148
3149 /*
3150 ======================================================================
3151 lwGetImageMap()
3152
3153 Read an lwImageMap from a SURF.BLOK in an LWO2 file.
3154 ====================================================================== */
3155
3156 int lwGetImageMap( idFile *fp, int rsz, lwTexture *tex )
3157 {
3158         unsigned int id;
3159         unsigned short sz;
3160         int rlen, pos;
3161
3162         pos = fp->Tell();
3163         id = getU4( fp );
3164         sz = getU2( fp );
3165         if ( 0 > get_flen() ) return 0;
3166
3167         while ( 1 ) {
3168                 sz += sz & 1;
3169                 set_flen( 0 );
3170
3171                 switch ( id ) {
3172                         case ID_TMAP:
3173                                 if ( !lwGetTMap( fp, sz, &tex->tmap )) return 0;
3174                                 break;
3175
3176                         case ID_PROJ:
3177                                 tex->param.imap.projection = getU2( fp );
3178                                 break;
3179
3180                         case ID_VMAP:
3181                                 tex->param.imap.vmap_name = getS0( fp );
3182                                 break;
3183
3184                         case ID_AXIS:
3185                                 tex->param.imap.axis = getU2( fp );
3186                                 break;
3187
3188                         case ID_IMAG:
3189                                 tex->param.imap.cindex = getVX( fp );
3190                                 break;
3191
3192                         case ID_WRAP:
3193                                 tex->param.imap.wrapw_type = getU2( fp );
3194                                 tex->param.imap.wraph_type = getU2( fp );
3195                                 break;
3196
3197                         case ID_WRPW:
3198                                 tex->param.imap.wrapw.val = getF4( fp );
3199                                 tex->param.imap.wrapw.eindex = getVX( fp );
3200                                 break;
3201
3202                         case ID_WRPH:
3203                                 tex->param.imap.wraph.val = getF4( fp );
3204                                 tex->param.imap.wraph.eindex = getVX( fp );
3205                                 break;
3206
3207                         case ID_AAST:
3208                                 tex->param.imap.aas_flags = getU2( fp );
3209                                 tex->param.imap.aa_strength = getF4( fp );
3210                                 break;
3211
3212                         case ID_PIXB:
3213                                 tex->param.imap.pblend = getU2( fp );
3214                                 break;
3215
3216                         case ID_STCK:
3217                                 tex->param.imap.stck.val = getF4( fp );
3218                                 tex->param.imap.stck.eindex = getVX( fp );
3219                                 break;
3220
3221                         case ID_TAMP:
3222                                 tex->param.imap.amplitude.val = getF4( fp );
3223                                 tex->param.imap.amplitude.eindex = getVX( fp );
3224                                 break;
3225
3226                         default:
3227                                 break;
3228                 }
3229
3230                 /* error while reading the current subchunk? */
3231
3232                 rlen = get_flen();
3233                 if ( rlen < 0 || rlen > sz ) return 0;
3234
3235                 /* skip unread parts of the current subchunk */
3236
3237                 if ( rlen < sz )
3238                         fp->Seek( sz - rlen, FS_SEEK_CUR );
3239
3240                 /* end of the image map? */
3241
3242                 if ( rsz <= fp->Tell() - pos )
3243                         break;
3244
3245                 /* get the next subchunk header */
3246
3247                 set_flen( 0 );
3248                 id = getU4( fp );
3249                 sz = getU2( fp );
3250                 if ( 6 != get_flen() ) return 0;
3251         }
3252
3253         set_flen( fp->Tell() - pos );
3254         return 1;
3255 }
3256
3257
3258 /*
3259 ======================================================================
3260 lwGetProcedural()
3261
3262 Read an lwProcedural from a SURF.BLOK in an LWO2 file.
3263 ====================================================================== */
3264
3265 int lwGetProcedural( idFile *fp, int rsz, lwTexture *tex )
3266 {
3267    unsigned int id;
3268    unsigned short sz;
3269    int rlen, pos;
3270
3271    pos = fp->Tell();
3272    id = getU4( fp );
3273    sz = getU2( fp );
3274    if ( 0 > get_flen() ) return 0;
3275
3276    while ( 1 ) {
3277       sz += sz & 1;
3278       set_flen( 0 );
3279
3280       switch ( id ) {
3281          case ID_TMAP:
3282             if ( !lwGetTMap( fp, sz, &tex->tmap )) return 0;
3283             break;
3284
3285          case ID_AXIS:
3286             tex->param.proc.axis = getU2( fp );
3287             break;
3288
3289          case ID_VALU:
3290             tex->param.proc.value[ 0 ] = getF4( fp );
3291             if ( sz >= 8 ) tex->param.proc.value[ 1 ] = getF4( fp );
3292             if ( sz >= 12 ) tex->param.proc.value[ 2 ] = getF4( fp );
3293             break;
3294
3295          case ID_FUNC:
3296             tex->param.proc.name = getS0( fp );
3297             rlen = get_flen();
3298             tex->param.proc.data = getbytes( fp, sz - rlen );
3299             break;
3300
3301          default:
3302             break;
3303       }
3304
3305       /* error while reading the current subchunk? */
3306
3307       rlen = get_flen();
3308       if ( rlen < 0 || rlen > sz ) return 0;
3309
3310       /* skip unread parts of the current subchunk */
3311
3312       if ( rlen < sz )
3313          fp->Seek( sz - rlen, FS_SEEK_CUR );
3314
3315       /* end of the procedural block? */
3316
3317       if ( rsz <= fp->Tell() - pos )
3318          break;
3319
3320       /* get the next subchunk header */
3321
3322       set_flen( 0 );
3323       id = getU4( fp );
3324       sz = getU2( fp );
3325       if ( 6 != get_flen() ) return 0;
3326    }
3327
3328    set_flen( fp->Tell() - pos );
3329    return 1;
3330 }
3331
3332
3333 /*
3334 ======================================================================
3335 lwGetGradient()
3336
3337 Read an lwGradient from a SURF.BLOK in an LWO2 file.
3338 ====================================================================== */
3339
3340 int lwGetGradient( idFile *fp, int rsz, lwTexture *tex )
3341 {
3342    unsigned int id;
3343    unsigned short sz;
3344    int rlen, pos, i, j, nkeys;
3345
3346    pos = fp->Tell();
3347    id = getU4( fp );
3348    sz = getU2( fp );
3349    if ( 0 > get_flen() ) return 0;
3350
3351    while ( 1 ) {
3352       sz += sz & 1;
3353       set_flen( 0 );
3354
3355       switch ( id ) {
3356          case ID_TMAP:
3357             if ( !lwGetTMap( fp, sz, &tex->tmap )) return 0;
3358             break;
3359
3360          case ID_PNAM:
3361             tex->param.grad.paramname = getS0( fp );
3362             break;
3363
3364          case ID_INAM:
3365             tex->param.grad.itemname = getS0( fp );
3366             break;
3367
3368          case ID_GRST:
3369             tex->param.grad.start = getF4( fp );
3370             break;
3371
3372          case ID_GREN:
3373             tex->param.grad.end = getF4( fp );
3374             break;
3375
3376          case ID_GRPT:
3377             tex->param.grad.repeat = getU2( fp );
3378             break;
3379
3380          case ID_FKEY:
3381             nkeys = sz / sizeof( lwGradKey );
3382             tex->param.grad.key = (lwGradKey*)Mem_ClearedAlloc( nkeys * sizeof( lwGradKey ) );
3383             if ( !tex->param.grad.key ) return 0;
3384             for ( i = 0; i < nkeys; i++ ) {
3385                tex->param.grad.key[ i ].value = getF4( fp );
3386                for ( j = 0; j < 4; j++ )
3387                   tex->param.grad.key[ i ].rgba[ j ] = getF4( fp );
3388             }
3389             break;
3390
3391          case ID_IKEY:
3392             nkeys = sz / 2;
3393             tex->param.grad.ikey = (short*)Mem_ClearedAlloc( nkeys * sizeof( short ) );
3394             if ( !tex->param.grad.ikey ) return 0;
3395             for ( i = 0; i < nkeys; i++ )
3396                tex->param.grad.ikey[ i ] = getU2( fp );
3397             break;
3398
3399          default:
3400             break;
3401       }
3402
3403       /* error while reading the current subchunk? */
3404
3405       rlen = get_flen();
3406       if ( rlen < 0 || rlen > sz ) return 0;
3407
3408       /* skip unread parts of the current subchunk */
3409
3410       if ( rlen < sz )
3411          fp->Seek( sz - rlen, FS_SEEK_CUR );
3412
3413       /* end of the gradient? */
3414
3415       if ( rsz <= fp->Tell() - pos )
3416          break;
3417
3418       /* get the next subchunk header */
3419
3420       set_flen( 0 );
3421       id = getU4( fp );
3422       sz = getU2( fp );
3423       if ( 6 != get_flen() ) return 0;
3424    }
3425
3426    set_flen( fp->Tell() - pos );
3427    return 1;
3428 }
3429
3430
3431 /*
3432 ======================================================================
3433 lwGetTexture()
3434
3435 Read an lwTexture from a SURF.BLOK in an LWO2 file.
3436 ====================================================================== */
3437
3438 lwTexture *lwGetTexture( idFile *fp, int bloksz, unsigned int type )
3439 {
3440    lwTexture *tex;
3441    unsigned short sz;
3442    int ok;
3443
3444    tex = (lwTexture*)Mem_ClearedAlloc( sizeof( lwTexture ) );
3445    if ( !tex ) return NULL;
3446
3447    tex->type = type;
3448    tex->tmap.size.val[ 0 ] =
3449    tex->tmap.size.val[ 1 ] =
3450    tex->tmap.size.val[ 2 ] = 1.0f;
3451    tex->opacity.val = 1.0f;
3452    tex->enabled = 1;
3453
3454    sz = getU2( fp );
3455    if ( !lwGetTHeader( fp, sz, tex )) {
3456       Mem_Free( tex );
3457       return NULL;
3458    }
3459
3460    sz = bloksz - sz - 6;
3461    switch ( type ) {
3462       case ID_IMAP:  ok = lwGetImageMap( fp, sz, tex );  break;
3463       case ID_PROC:  ok = lwGetProcedural( fp, sz, tex );  break;
3464       case ID_GRAD:  ok = lwGetGradient( fp, sz, tex );  break;
3465       default:
3466          ok = !fp->Seek( sz, FS_SEEK_CUR );
3467    }
3468
3469    if ( !ok ) {
3470       lwFreeTexture( tex );
3471       return NULL;
3472    }
3473
3474    set_flen( bloksz );
3475    return tex;
3476 }
3477
3478
3479 /*
3480 ======================================================================
3481 lwGetShader()
3482
3483 Read a shader record from a SURF.BLOK in an LWO2 file.
3484 ====================================================================== */
3485
3486 lwPlugin *lwGetShader( idFile *fp, int bloksz )
3487 {
3488    lwPlugin *shdr;
3489    unsigned int id;
3490    unsigned short sz;
3491    int hsz, rlen, pos;
3492
3493    shdr = (lwPlugin*)Mem_ClearedAlloc( sizeof( lwPlugin ) );
3494    if ( !shdr ) return NULL;
3495
3496    pos = fp->Tell();
3497    set_flen( 0 );
3498    hsz = getU2( fp );
3499    shdr->ord = getS0( fp );
3500    id = getU4( fp );
3501    sz = getU2( fp );
3502    if ( 0 > get_flen() ) goto Fail;
3503
3504    while ( hsz > 0 ) {
3505       sz += sz & 1;
3506       hsz -= sz;
3507       if ( id == ID_ENAB ) {
3508          shdr->flags = getU2( fp );
3509          break;
3510       }
3511       else {
3512          fp->Seek( sz, FS_SEEK_CUR );
3513          id = getU4( fp );
3514          sz = getU2( fp );
3515       }
3516    }
3517
3518    id = getU4( fp );
3519    sz = getU2( fp );
3520    if ( 0 > get_flen() ) goto Fail;
3521
3522    while ( 1 ) {
3523       sz += sz & 1;
3524       set_flen( 0 );
3525
3526       switch ( id ) {
3527          case ID_FUNC:
3528             shdr->name = getS0( fp );
3529             rlen = get_flen();
3530             shdr->data = getbytes( fp, sz - rlen );
3531             break;
3532
3533          default:
3534             break;
3535       }
3536
3537       /* error while reading the current subchunk? */
3538
3539       rlen = get_flen();
3540       if ( rlen < 0 || rlen > sz ) goto Fail;
3541
3542       /* skip unread parts of the current subchunk */
3543
3544       if ( rlen < sz )
3545          fp->Seek( sz - rlen, FS_SEEK_CUR );
3546
3547       /* end of the shader block? */
3548
3549       if ( bloksz <= fp->Tell() - pos )
3550          break;
3551
3552       /* get the next subchunk header */
3553
3554       set_flen( 0 );
3555       id = getU4( fp );
3556       sz = getU2( fp );
3557       if ( 6 != get_flen() ) goto Fail;
3558    }
3559
3560    set_flen( fp->Tell() - pos );
3561    return shdr;
3562
3563 Fail:
3564    lwFreePlugin( shdr );
3565    return NULL;
3566 }
3567
3568
3569 /*
3570 ======================================================================
3571 compare_textures()
3572 compare_shaders()
3573
3574 Callbacks for the lwListInsert() function, which is called to add
3575 textures to surface channels and shaders to surfaces.
3576 ====================================================================== */
3577
3578 static int compare_textures( lwTexture *a, lwTexture *b )
3579 {
3580    return strcmp( a->ord, b->ord );
3581 }
3582
3583
3584 static int compare_shaders( lwPlugin *a, lwPlugin *b )
3585 {
3586    return strcmp( a->ord, b->ord );
3587 }
3588
3589
3590 /*
3591 ======================================================================
3592 add_texture()
3593
3594 Finds the surface channel (lwTParam or lwCParam) to which a texture is
3595 applied, then calls lwListInsert().
3596 ====================================================================== */
3597
3598 static int add_texture( lwSurface *surf, lwTexture *tex )
3599 {
3600    lwTexture **list;
3601
3602    switch ( tex->chan ) {
3603       case ID_COLR:  list = &surf->color.tex;             break;
3604       case ID_LUMI:  list = &surf->luminosity.tex;        break;
3605       case ID_DIFF:  list = &surf->diffuse.tex;           break;
3606       case ID_SPEC:  list = &surf->specularity.tex;       break;
3607       case ID_GLOS:  list = &surf->glossiness.tex;        break;
3608       case ID_REFL:  list = &surf->reflection.val.tex;    break;
3609       case ID_TRAN:  list = &surf->transparency.val.tex;  break;
3610       case ID_RIND:  list = &surf->eta.tex;               break;
3611       case ID_TRNL:  list = &surf->translucency.tex;      break;
3612       case ID_BUMP:  list = &surf->bump.tex;              break;
3613       default:  return 0;
3614    }
3615
3616    lwListInsert( (void**)list, tex, (int (__cdecl *)(void *,void *))compare_textures );
3617    return 1;
3618 }
3619
3620
3621 /*
3622 ======================================================================
3623 lwDefaultSurface()
3624
3625 Allocate and initialize a surface.
3626 ====================================================================== */
3627
3628 lwSurface *lwDefaultSurface( void )
3629 {
3630    lwSurface *surf;
3631
3632    surf = (lwSurface*)Mem_ClearedAlloc( sizeof( lwSurface ) );
3633    if ( !surf ) return NULL;
3634
3635    surf->color.rgb[ 0 ] = 0.78431f;
3636    surf->color.rgb[ 1 ] = 0.78431f;
3637    surf->color.rgb[ 2 ] = 0.78431f;
3638    surf->diffuse.val    = 1.0f;
3639    surf->glossiness.val = 0.4f;
3640    surf->bump.val       = 1.0f;
3641    surf->eta.val        = 1.0f;
3642    surf->sideflags      = 1;
3643
3644    return surf;
3645 }
3646
3647
3648 /*
3649 ======================================================================
3650 lwGetSurface()
3651
3652 Read an lwSurface from an LWO2 file.
3653 ====================================================================== */
3654
3655 lwSurface *lwGetSurface( idFile *fp, int cksize )
3656 {
3657    lwSurface *surf;
3658    lwTexture *tex;
3659    lwPlugin *shdr;
3660    unsigned int id, type;
3661    unsigned short sz;
3662    int pos, rlen;
3663
3664
3665    /* allocate the Surface structure */
3666
3667    surf = (lwSurface*)Mem_ClearedAlloc( sizeof( lwSurface ) );
3668    if ( !surf ) goto Fail;
3669
3670    /* non-zero defaults */
3671
3672    surf->color.rgb[ 0 ] = 0.78431f;
3673    surf->color.rgb[ 1 ] = 0.78431f;
3674    surf->color.rgb[ 2 ] = 0.78431f;
3675    surf->diffuse.val    = 1.0f;
3676    surf->glossiness.val = 0.4f;
3677    surf->bump.val       = 1.0f;
3678    surf->eta.val        = 1.0f;
3679    surf->sideflags      = 1;
3680
3681    /* remember where we started */
3682
3683    set_flen( 0 );
3684    pos = fp->Tell();
3685
3686    /* names */
3687
3688    surf->name = getS0( fp );
3689    surf->srcname = getS0( fp );
3690
3691    /* first subchunk header */
3692
3693    id = getU4( fp );
3694    sz = getU2( fp );
3695    if ( 0 > get_flen() ) goto Fail;
3696
3697    /* process subchunks as they're encountered */
3698
3699    while ( 1 ) {
3700       sz += sz & 1;
3701       set_flen( 0 );
3702
3703       switch ( id ) {
3704          case ID_COLR:
3705             surf->color.rgb[ 0 ] = getF4( fp );
3706             surf->color.rgb[ 1 ] = getF4( fp );
3707             surf->color.rgb[ 2 ] = getF4( fp );
3708             surf->color.eindex = getVX( fp );
3709             break;
3710
3711          case ID_LUMI:
3712             surf->luminosity.val = getF4( fp );
3713             surf->luminosity.eindex = getVX( fp );
3714             break;
3715
3716          case ID_DIFF:
3717             surf->diffuse.val = getF4( fp );
3718             surf->diffuse.eindex = getVX( fp );
3719             break;
3720
3721          case ID_SPEC:
3722             surf->specularity.val = getF4( fp );
3723             surf->specularity.eindex = getVX( fp );
3724             break;
3725
3726          case ID_GLOS:
3727             surf->glossiness.val = getF4( fp );
3728             surf->glossiness.eindex = getVX( fp );
3729             break;
3730
3731          case ID_REFL:
3732             surf->reflection.val.val = getF4( fp );
3733             surf->reflection.val.eindex = getVX( fp );
3734             break;
3735
3736          case ID_RFOP:
3737             surf->reflection.options = getU2( fp );
3738             break;
3739
3740          case ID_RIMG:
3741             surf->reflection.cindex = getVX( fp );
3742             break;
3743
3744          case ID_RSAN:
3745             surf->reflection.seam_angle = getF4( fp );
3746             break;
3747
3748          case ID_TRAN:
3749             surf->transparency.val.val = getF4( fp );
3750             surf->transparency.val.eindex = getVX( fp );
3751             break;
3752
3753          case ID_TROP:
3754             surf->transparency.options = getU2( fp );
3755             break;
3756
3757          case ID_TIMG:
3758             surf->transparency.cindex = getVX( fp );
3759             break;
3760
3761          case ID_RIND:
3762             surf->eta.val = getF4( fp );
3763             surf->eta.eindex = getVX( fp );
3764             break;
3765
3766          case ID_TRNL:
3767             surf->translucency.val = getF4( fp );
3768             surf->translucency.eindex = getVX( fp );
3769             break;
3770
3771          case ID_BUMP:
3772             surf->bump.val = getF4( fp );
3773             surf->bump.eindex = getVX( fp );
3774             break;
3775
3776          case ID_SMAN:
3777             surf->smooth = getF4( fp );
3778             break;
3779
3780          case ID_SIDE:
3781             surf->sideflags = getU2( fp );
3782             break;
3783
3784          case ID_CLRH:
3785             surf->color_hilite.val = getF4( fp );
3786             surf->color_hilite.eindex = getVX( fp );
3787             break;
3788
3789          case ID_CLRF:
3790             surf->color_filter.val = getF4( fp );
3791             surf->color_filter.eindex = getVX( fp );
3792             break;
3793
3794          case ID_ADTR:
3795             surf->add_trans.val = getF4( fp );
3796             surf->add_trans.eindex = getVX( fp );
3797             break;
3798
3799          case ID_SHRP:
3800             surf->dif_sharp.val = getF4( fp );
3801             surf->dif_sharp.eindex = getVX( fp );
3802             break;
3803
3804          case ID_GVAL:
3805             surf->glow.val = getF4( fp );
3806             surf->glow.eindex = getVX( fp );
3807             break;
3808
3809          case ID_LINE:
3810             surf->line.enabled = 1;
3811             if ( sz >= 2 ) surf->line.flags = getU2( fp );
3812             if ( sz >= 6 ) surf->line.size.val = getF4( fp );
3813             if ( sz >= 8 ) surf->line.size.eindex = getVX( fp );
3814             break;
3815
3816          case ID_ALPH:
3817             surf->alpha_mode = getU2( fp );
3818             surf->alpha = getF4( fp );
3819             break;
3820
3821          case ID_AVAL:
3822             surf->alpha = getF4( fp );
3823             break;
3824
3825          case ID_BLOK:
3826             type = getU4( fp );
3827
3828             switch ( type ) {
3829                case ID_IMAP:
3830                case ID_PROC:
3831                case ID_GRAD:
3832                   tex = lwGetTexture( fp, sz - 4, type );
3833                   if ( !tex ) goto Fail;
3834                   if ( !add_texture( surf, tex ))
3835                      lwFreeTexture( tex );
3836                   set_flen( 4 + get_flen() );
3837                   break;
3838                case ID_SHDR:
3839                   shdr = lwGetShader( fp, sz - 4 );
3840                   if ( !shdr ) goto Fail;
3841                   lwListInsert( (void**)&surf->shader, shdr, (int (__cdecl *)(void *,void *))compare_shaders );
3842                   ++surf->nshaders;
3843                   set_flen( 4 + get_flen() );
3844                   break;
3845             }
3846             break;
3847
3848          default:
3849             break;
3850       }
3851
3852       /* error while reading current subchunk? */
3853
3854       rlen = get_flen();
3855       if ( rlen < 0 || rlen > sz ) goto Fail;
3856
3857       /* skip unread parts of the current subchunk */
3858
3859       if ( rlen < sz )
3860          fp->Seek( sz - rlen, FS_SEEK_CUR );
3861
3862       /* end of the SURF chunk? */
3863
3864       if ( cksize <= fp->Tell() - pos )
3865          break;
3866
3867       /* get the next subchunk header */
3868
3869       set_flen( 0 );
3870       id = getU4( fp );
3871       sz = getU2( fp );
3872       if ( 6 != get_flen() ) goto Fail;
3873    }
3874
3875    return surf;
3876
3877 Fail:
3878    if ( surf ) lwFreeSurface( surf );
3879    return NULL;
3880 }
3881
3882
3883 float dot( float a[], float b[] )
3884 {
3885    return a[ 0 ] * b[ 0 ] + a[ 1 ] * b[ 1 ] + a[ 2 ] * b[ 2 ];
3886 }
3887
3888
3889 void cross( float a[], float b[], float c[] )
3890 {
3891    c[ 0 ] = a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ];
3892    c[ 1 ] = a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ];
3893    c[ 2 ] = a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ];
3894 }
3895
3896
3897 void normalize( float v[] )
3898 {
3899    float r;
3900
3901    r = ( float ) idMath::Sqrt( dot( v, v ));
3902    if ( r > 0 ) {
3903       v[ 0 ] /= r;
3904       v[ 1 ] /= r;
3905       v[ 2 ] /= r;
3906    }
3907 }
3908
3909 /*
3910 ======================================================================
3911 lwFreeVMap()
3912
3913 Free memory used by an lwVMap.
3914 ====================================================================== */
3915
3916 void lwFreeVMap( lwVMap *vmap )
3917 {
3918    if ( vmap ) {
3919       if ( vmap->name ) Mem_Free( vmap->name );
3920       if ( vmap->vindex ) Mem_Free( vmap->vindex );
3921       if ( vmap->pindex ) Mem_Free( vmap->pindex );
3922       if ( vmap->val ) {
3923          if ( vmap->val[ 0 ] ) Mem_Free( vmap->val[ 0 ] );
3924          Mem_Free( vmap->val );
3925       }
3926       Mem_Free( vmap );
3927    }
3928 }
3929
3930
3931 /*
3932 ======================================================================
3933 lwGetVMap()
3934
3935 Read an lwVMap from a VMAP or VMAD chunk in an LWO2.
3936 ====================================================================== */
3937
3938 lwVMap *lwGetVMap( idFile *fp, int cksize, int ptoffset, int poloffset,
3939    int perpoly )
3940 {
3941    unsigned char *buf, *bp;
3942    lwVMap *vmap;
3943    float *f;
3944    int i, j, npts, rlen;
3945
3946
3947    /* read the whole chunk */
3948
3949    set_flen( 0 );
3950    buf = (unsigned char*)getbytes( fp, cksize );
3951    if ( !buf ) return NULL;
3952
3953    vmap = (lwVMap*)Mem_ClearedAlloc( sizeof( lwVMap ) );
3954    if ( !vmap ) {
3955       Mem_Free( buf );
3956       return NULL;
3957    }
3958
3959    /* initialize the vmap */
3960
3961    vmap->perpoly = perpoly;
3962
3963    bp = buf;
3964    set_flen( 0 );
3965    vmap->type = sgetU4( &bp );
3966    vmap->dim  = sgetU2( &bp );
3967    vmap->name = sgetS0( &bp );
3968    rlen = get_flen();
3969
3970    /* count the vmap records */
3971
3972    npts = 0;
3973    while ( bp < buf + cksize ) {
3974       i = sgetVX( &bp );
3975       if ( perpoly )
3976          i = sgetVX( &bp );
3977       bp += vmap->dim * sizeof( float );
3978       ++npts;
3979    }
3980
3981    /* allocate the vmap */
3982
3983    vmap->nverts = npts;
3984    vmap->vindex = (int*)Mem_ClearedAlloc( npts * sizeof( int ) );
3985    if ( !vmap->vindex ) goto Fail;
3986    if ( perpoly ) {
3987       vmap->pindex = (int*)Mem_ClearedAlloc( npts * sizeof( int ) );
3988       if ( !vmap->pindex ) goto Fail;
3989    }
3990
3991    if ( vmap->dim > 0 ) {
3992       vmap->val = (float**)Mem_ClearedAlloc( npts * sizeof( float * ) );
3993       if ( !vmap->val ) goto Fail;
3994       f = (float*)Mem_ClearedAlloc( npts * vmap->dim * sizeof( float ) );
3995       if ( !f ) goto Fail;
3996       for ( i = 0; i < npts; i++ )
3997          vmap->val[ i ] = f + i * vmap->dim;
3998    }
3999
4000    /* fill in the vmap values */
4001
4002    bp = buf + rlen;
4003    for ( i = 0; i < npts; i++ ) {
4004       vmap->vindex[ i ] = sgetVX( &bp );
4005       if ( perpoly )
4006          vmap->pindex[ i ] = sgetVX( &bp );
4007       for ( j = 0; j < vmap->dim; j++ )
4008          vmap->val[ i ][ j ] = sgetF4( &bp );
4009    }
4010
4011    Mem_Free( buf );
4012    return vmap;
4013
4014 Fail:
4015    if ( buf ) Mem_Free( buf );
4016    lwFreeVMap( vmap );
4017    return NULL;
4018 }
4019
4020
4021 /*
4022 ======================================================================
4023 lwGetPointVMaps()
4024
4025 Fill in the lwVMapPt structure for each point.
4026 ====================================================================== */
4027
4028 int lwGetPointVMaps( lwPointList *point, lwVMap *vmap )
4029 {
4030    lwVMap *vm;
4031    int i, j, n;
4032
4033    /* count the number of vmap values for each point */
4034
4035    vm = vmap;
4036    while ( vm ) {
4037       if ( !vm->perpoly )
4038          for ( i = 0; i < vm->nverts; i++ )
4039             ++point->pt[ vm->vindex[ i ]].nvmaps;
4040       vm = vm->next;
4041    }
4042
4043    /* allocate vmap references for each mapped point */
4044
4045    for ( i = 0; i < point->count; i++ ) {
4046       if ( point->pt[ i ].nvmaps ) {
4047          point->pt[ i ].vm = (lwVMapPt*)Mem_ClearedAlloc( point->pt[ i ].nvmaps * sizeof( lwVMapPt ) );
4048          if ( !point->pt[ i ].vm ) return 0;
4049          point->pt[ i ].nvmaps = 0;
4050       }
4051    }
4052
4053    /* fill in vmap references for each mapped point */
4054
4055    vm = vmap;
4056    while ( vm ) {
4057       if ( !vm->perpoly ) {
4058          for ( i = 0; i < vm->nverts; i++ ) {
4059             j = vm->vindex[ i ];
4060             n = point->pt[ j ].nvmaps;
4061             point->pt[ j ].vm[ n ].vmap = vm;
4062             point->pt[ j ].vm[ n ].index = i;
4063             ++point->pt[ j ].nvmaps;
4064          }
4065       }
4066       vm = vm->next;
4067    }
4068
4069    return 1;
4070 }
4071
4072
4073 /*
4074 ======================================================================
4075 lwGetPolyVMaps()
4076
4077 Fill in the lwVMapPt structure for each polygon vertex.
4078 ====================================================================== */
4079
4080 int lwGetPolyVMaps( lwPolygonList *polygon, lwVMap *vmap )
4081 {
4082    lwVMap *vm;
4083    lwPolVert *pv;
4084    int i, j;
4085
4086    /* count the number of vmap values for each polygon vertex */
4087
4088    vm = vmap;
4089    while ( vm ) {
4090       if ( vm->perpoly ) {
4091          for ( i = 0; i < vm->nverts; i++ ) {
4092             for ( j = 0; j < polygon->pol[ vm->pindex[ i ]].nverts; j++ ) {
4093                pv = &polygon->pol[ vm->pindex[ i ]].v[ j ];
4094                if ( vm->vindex[ i ] == pv->index ) {
4095                   ++pv->nvmaps;
4096                   break;
4097                }
4098             }
4099          }
4100       }
4101       vm = vm->next;
4102    }
4103
4104    /* allocate vmap references for each mapped vertex */
4105
4106    for ( i = 0; i < polygon->count; i++ ) {
4107       for ( j = 0; j < polygon->pol[ i ].nverts; j++ ) {
4108          pv = &polygon->pol[ i ].v[ j ];
4109          if ( pv->nvmaps ) {
4110             pv->vm = (lwVMapPt*)Mem_ClearedAlloc( pv->nvmaps * sizeof( lwVMapPt ) );
4111             if ( !pv->vm ) return 0;
4112             pv->nvmaps = 0;
4113          }
4114       }
4115    }
4116
4117    /* fill in vmap references for each mapped point */
4118
4119    vm = vmap;
4120    while ( vm ) {
4121       if ( vm->perpoly ) {
4122          for ( i = 0; i < vm->nverts; i++ ) {
4123             for ( j = 0; j < polygon->pol[ vm->pindex[ i ]].nverts; j++ ) {
4124                pv = &polygon->pol[ vm->pindex[ i ]].v[ j ];
4125                if ( vm->vindex[ i ] == pv->index ) {
4126                   pv->vm[ pv->nvmaps ].vmap = vm;
4127                   pv->vm[ pv->nvmaps ].index = i;
4128                   ++pv->nvmaps;
4129                   break;
4130                }
4131             }
4132          }
4133       }
4134       vm = vm->next;
4135    }
4136
4137    return 1;
4138 }