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