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