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