]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/lwo/pntspols.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / libs / picomodel / lwo / pntspols.c
1 /*\r
2 ======================================================================\r
3 pntspols.c\r
4 \r
5 Point and polygon functions for an LWO2 reader.\r
6 \r
7 Ernie Wright  17 Sep 00\r
8 ====================================================================== */\r
9 \r
10 #include "../picointernal.h"\r
11 #include "lwo2.h"\r
12 \r
13 \r
14 /*\r
15 ======================================================================\r
16 lwFreePoints()\r
17 \r
18 Free the memory used by an lwPointList.\r
19 ====================================================================== */\r
20 \r
21 void lwFreePoints( lwPointList *point )\r
22 {\r
23    int i;\r
24 \r
25    if ( point ) {\r
26       if ( point->pt ) {\r
27          for ( i = 0; i < point->count; i++ ) {\r
28             if ( point->pt[ i ].pol ) _pico_free( point->pt[ i ].pol );\r
29             if ( point->pt[ i ].vm ) _pico_free( point->pt[ i ].vm );\r
30          }\r
31          _pico_free( point->pt );\r
32       }\r
33       memset( point, 0, sizeof( lwPointList ));\r
34    }\r
35 }\r
36 \r
37 \r
38 /*\r
39 ======================================================================\r
40 lwFreePolygons()\r
41 \r
42 Free the memory used by an lwPolygonList.\r
43 ====================================================================== */\r
44 \r
45 void lwFreePolygons( lwPolygonList *plist )\r
46 {\r
47    int i, j;\r
48 \r
49    if ( plist ) {\r
50       if ( plist->pol ) {\r
51          for ( i = 0; i < plist->count; i++ ) {\r
52             if ( plist->pol[ i ].v ) {\r
53                for ( j = 0; j < plist->pol[ i ].nverts; j++ )\r
54                   if ( plist->pol[ i ].v[ j ].vm )\r
55                      _pico_free( plist->pol[ i ].v[ j ].vm );\r
56             }\r
57          }\r
58          if ( plist->pol[ 0 ].v )\r
59             _pico_free( plist->pol[ 0 ].v );\r
60          _pico_free( plist->pol );\r
61       }\r
62       memset( plist, 0, sizeof( lwPolygonList ));\r
63    }\r
64 }\r
65 \r
66 \r
67 /*\r
68 ======================================================================\r
69 lwGetPoints()\r
70 \r
71 Read point records from a PNTS chunk in an LWO2 file.  The points are\r
72 added to the array in the lwPointList.\r
73 ====================================================================== */\r
74 \r
75 int lwGetPoints( picoMemStream_t *fp, int cksize, lwPointList *point )\r
76 {\r
77    float *f;\r
78    int np, i, j;\r
79 \r
80    if ( cksize == 1 ) return 1;\r
81 \r
82    /* extend the point array to hold the new points */\r
83 \r
84    np = cksize / 12;\r
85    point->offset = point->count;\r
86    point->count += np;\r
87    if ( !_pico_realloc( (void *) &point->pt, (point->count - np) * sizeof( lwPoint ), point->count * sizeof( lwPoint )) )\r
88       return 0;\r
89    memset( &point->pt[ point->offset ], 0, np * sizeof( lwPoint ));\r
90 \r
91    /* read the whole chunk */\r
92 \r
93    f = ( float * ) getbytes( fp, cksize );\r
94    if ( !f ) return 0;\r
95    revbytes( f, 4, np * 3 );\r
96 \r
97    /* assign position values */\r
98 \r
99    for ( i = 0, j = 0; i < np; i++, j += 3 ) {\r
100       point->pt[ i ].pos[ 0 ] = f[ j ];\r
101       point->pt[ i ].pos[ 1 ] = f[ j + 1 ];\r
102       point->pt[ i ].pos[ 2 ] = f[ j + 2 ];\r
103    }\r
104 \r
105    _pico_free( f );\r
106    return 1;\r
107 }\r
108 \r
109 \r
110 /*\r
111 ======================================================================\r
112 lwGetBoundingBox()\r
113 \r
114 Calculate the bounding box for a point list, but only if the bounding\r
115 box hasn't already been initialized.\r
116 ====================================================================== */\r
117 \r
118 void lwGetBoundingBox( lwPointList *point, float bbox[] )\r
119 {\r
120    int i, j;\r
121 \r
122    if ( point->count == 0 ) return;\r
123 \r
124    for ( i = 0; i < 6; i++ )\r
125       if ( bbox[ i ] != 0.0f ) return;\r
126 \r
127    bbox[ 0 ] = bbox[ 1 ] = bbox[ 2 ] = 1e20f;\r
128    bbox[ 3 ] = bbox[ 4 ] = bbox[ 5 ] = -1e20f;\r
129    for ( i = 0; i < point->count; i++ ) {\r
130       for ( j = 0; j < 3; j++ ) {\r
131          if ( bbox[ j ] > point->pt[ i ].pos[ j ] )\r
132             bbox[ j ] = point->pt[ i ].pos[ j ];\r
133          if ( bbox[ j + 3 ] < point->pt[ i ].pos[ j ] )\r
134             bbox[ j + 3 ] = point->pt[ i ].pos[ j ];\r
135       }\r
136    }\r
137 }\r
138 \r
139 \r
140 /*\r
141 ======================================================================\r
142 lwAllocPolygons()\r
143 \r
144 Allocate or extend the polygon arrays to hold new records.\r
145 ====================================================================== */\r
146 \r
147 int lwAllocPolygons( lwPolygonList *plist, int npols, int nverts )\r
148 {\r
149    int i;\r
150 \r
151    plist->offset = plist->count;\r
152    plist->count += npols;\r
153    if ( !_pico_realloc( (void *) &plist->pol, (plist->count - npols) * sizeof( lwPolygon ), plist->count * sizeof( lwPolygon )) )\r
154       return 0;\r
155    memset( plist->pol + plist->offset, 0, npols * sizeof( lwPolygon ));\r
156 \r
157    plist->voffset = plist->vcount;\r
158    plist->vcount += nverts;\r
159    if ( !_pico_realloc( (void *) &plist->pol[ 0 ].v, (plist->vcount - nverts) * sizeof( lwPolVert ), plist->vcount * sizeof( lwPolVert )) )\r
160       return 0;\r
161    memset( plist->pol[ 0 ].v + plist->voffset, 0, nverts * sizeof( lwPolVert ));\r
162 \r
163    /* fix up the old vertex pointers */\r
164 \r
165    for ( i = 1; i < plist->offset; i++ )\r
166       plist->pol[ i ].v = plist->pol[ i - 1 ].v + plist->pol[ i - 1 ].nverts;\r
167 \r
168    return 1;\r
169 }\r
170 \r
171 \r
172 /*\r
173 ======================================================================\r
174 lwGetPolygons()\r
175 \r
176 Read polygon records from a POLS chunk in an LWO2 file.  The polygons\r
177 are added to the array in the lwPolygonList.\r
178 ====================================================================== */\r
179 \r
180 int lwGetPolygons( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset )\r
181 {\r
182    lwPolygon *pp;\r
183    lwPolVert *pv;\r
184    unsigned char *buf, *bp;\r
185    int i, j, flags, nv, nverts, npols;\r
186    unsigned int type;\r
187 \r
188 \r
189    if ( cksize == 0 ) return 1;\r
190 \r
191    /* read the whole chunk */\r
192 \r
193    set_flen( 0 );\r
194    type = getU4( fp );\r
195    buf = getbytes( fp, cksize - 4 );\r
196    if ( cksize != get_flen() ) goto Fail;\r
197 \r
198    /* count the polygons and vertices */\r
199 \r
200    nverts = 0;\r
201    npols = 0;\r
202    bp = buf;\r
203 \r
204    while ( bp < buf + cksize - 4 ) {\r
205       nv = sgetU2( &bp );\r
206       nv &= 0x03FF;\r
207       nverts += nv;\r
208       npols++;\r
209       for ( i = 0; i < nv; i++ )\r
210          j = sgetVX( &bp );\r
211    }\r
212 \r
213    if ( !lwAllocPolygons( plist, npols, nverts ))\r
214       goto Fail;\r
215 \r
216    /* fill in the new polygons */\r
217 \r
218    bp = buf;\r
219    pp = plist->pol + plist->offset;\r
220    pv = plist->pol[ 0 ].v + plist->voffset;\r
221 \r
222    for ( i = 0; i < npols; i++ ) {\r
223       nv = sgetU2( &bp );\r
224       flags = nv & 0xFC00;\r
225       nv &= 0x03FF;\r
226 \r
227       pp->nverts = nv;\r
228       pp->flags = flags;\r
229       pp->type = type;\r
230       if ( !pp->v ) pp->v = pv;\r
231       for ( j = 0; j < nv; j++ )\r
232          pp->v[ j ].index = sgetVX( &bp ) + ptoffset;\r
233 \r
234       pp++;\r
235       pv += nv;\r
236    }\r
237 \r
238    _pico_free( buf );\r
239    return 1;\r
240 \r
241 Fail:\r
242    if ( buf ) _pico_free( buf );\r
243    lwFreePolygons( plist );\r
244    return 0;\r
245 }\r
246 \r
247 \r
248 /*\r
249 ======================================================================\r
250 lwGetPolyNormals()\r
251 \r
252 Calculate the polygon normals.  By convention, LW's polygon normals\r
253 are found as the cross product of the first and last edges.  It's\r
254 undefined for one- and two-point polygons.\r
255 ====================================================================== */\r
256 \r
257 void lwGetPolyNormals( lwPointList *point, lwPolygonList *polygon )\r
258 {\r
259    int i, j;\r
260    float p1[ 3 ], p2[ 3 ], pn[ 3 ], v1[ 3 ], v2[ 3 ];\r
261 \r
262    for ( i = 0; i < polygon->count; i++ ) {\r
263       if ( polygon->pol[ i ].nverts < 3 ) continue;\r
264       for ( j = 0; j < 3; j++ ) {\r
265          p1[ j ] = point->pt[ polygon->pol[ i ].v[ 0 ].index ].pos[ j ];\r
266          p2[ j ] = point->pt[ polygon->pol[ i ].v[ 1 ].index ].pos[ j ];\r
267          pn[ j ] = point->pt[ polygon->pol[ i ].v[\r
268             polygon->pol[ i ].nverts - 1 ].index ].pos[ j ];\r
269       }\r
270 \r
271       for ( j = 0; j < 3; j++ ) {\r
272          v1[ j ] = p2[ j ] - p1[ j ];\r
273          v2[ j ] = pn[ j ] - p1[ j ];\r
274       }\r
275 \r
276       cross( v1, v2, polygon->pol[ i ].norm );\r
277       normalize( polygon->pol[ i ].norm );\r
278    }\r
279 }\r
280 \r
281 \r
282 /*\r
283 ======================================================================\r
284 lwGetPointPolygons()\r
285 \r
286 For each point, fill in the indexes of the polygons that share the\r
287 point.  Returns 0 if any of the memory allocations fail, otherwise\r
288 returns 1.\r
289 ====================================================================== */\r
290 \r
291 int lwGetPointPolygons( lwPointList *point, lwPolygonList *polygon )\r
292 {\r
293    int i, j, k;\r
294 \r
295    /* count the number of polygons per point */\r
296 \r
297    for ( i = 0; i < polygon->count; i++ )\r
298       for ( j = 0; j < polygon->pol[ i ].nverts; j++ )\r
299          ++point->pt[ polygon->pol[ i ].v[ j ].index ].npols;\r
300 \r
301    /* alloc per-point polygon arrays */\r
302 \r
303    for ( i = 0; i < point->count; i++ ) {\r
304       if ( point->pt[ i ].npols == 0 ) continue;\r
305       point->pt[ i ].pol = _pico_calloc( point->pt[ i ].npols, sizeof( int ));\r
306       if ( !point->pt[ i ].pol ) return 0;\r
307       point->pt[ i ].npols = 0;\r
308    }\r
309 \r
310    /* fill in polygon array for each point */\r
311 \r
312    for ( i = 0; i < polygon->count; i++ ) {\r
313       for ( j = 0; j < polygon->pol[ i ].nverts; j++ ) {\r
314          k = polygon->pol[ i ].v[ j ].index;\r
315          point->pt[ k ].pol[ point->pt[ k ].npols ] = i;\r
316          ++point->pt[ k ].npols;\r
317       }\r
318    }\r
319 \r
320    return 1;\r
321 }\r
322 \r
323 \r
324 /*\r
325 ======================================================================\r
326 lwResolvePolySurfaces()\r
327 \r
328 Convert tag indexes into actual lwSurface pointers.  If any polygons\r
329 point to tags for which no corresponding surface can be found, a\r
330 default surface is created.\r
331 ====================================================================== */\r
332 \r
333 int lwResolvePolySurfaces( lwPolygonList *polygon, lwTagList *tlist,\r
334    lwSurface **surf, int *nsurfs )\r
335 {\r
336    lwSurface **s, *st;\r
337    int i, index;\r
338 \r
339    if ( tlist->count == 0 ) return 1;\r
340 \r
341    s = _pico_calloc( tlist->count, sizeof( lwSurface * ));\r
342    if ( !s ) return 0;\r
343 \r
344    for ( i = 0; i < tlist->count; i++ ) {\r
345       st = *surf;\r
346       while ( st ) {\r
347          if ( !strcmp( st->name, tlist->tag[ i ] )) {\r
348             s[ i ] = st;\r
349             break;\r
350          }\r
351          st = st->next;\r
352       }\r
353    }\r
354 \r
355    for ( i = 0; i < polygon->count; i++ ) {\r
356       index = ( int ) polygon->pol[ i ].surf;\r
357       if ( index < 0 || index > tlist->count ) return 0;\r
358       if ( !s[ index ] ) {\r
359          s[ index ] = lwDefaultSurface();\r
360          if ( !s[ index ] ) return 0;\r
361          s[ index ]->name = _pico_alloc( strlen( tlist->tag[ index ] ) + 1 );\r
362          if ( !s[ index ]->name ) return 0;\r
363          strcpy( s[ index ]->name, tlist->tag[ index ] );\r
364          lwListAdd( surf, s[ index ] );\r
365          *nsurfs = *nsurfs + 1;\r
366       }\r
367       polygon->pol[ i ].surf = s[ index ];\r
368    }\r
369 \r
370    _pico_free( s );\r
371    return 1;\r
372 }\r
373 \r
374 \r
375 /*\r
376 ======================================================================\r
377 lwGetVertNormals()\r
378 \r
379 Calculate the vertex normals.  For each polygon vertex, sum the\r
380 normals of the polygons that share the point.  If the normals of the\r
381 current and adjacent polygons form an angle greater than the max\r
382 smoothing angle for the current polygon's surface, the normal of the\r
383 adjacent polygon is excluded from the sum.  It's also excluded if the\r
384 polygons aren't in the same smoothing group.\r
385 \r
386 Assumes that lwGetPointPolygons(), lwGetPolyNormals() and\r
387 lwResolvePolySurfaces() have already been called.\r
388 ====================================================================== */\r
389 \r
390 void lwGetVertNormals( lwPointList *point, lwPolygonList *polygon )\r
391 {\r
392    int j, k, n, g, h, p;\r
393    float a;\r
394 \r
395    for ( j = 0; j < polygon->count; j++ ) {\r
396       for ( n = 0; n < polygon->pol[ j ].nverts; n++ ) {\r
397          for ( k = 0; k < 3; k++ )\r
398             polygon->pol[ j ].v[ n ].norm[ k ] = polygon->pol[ j ].norm[ k ];\r
399 \r
400          if ( polygon->pol[ j ].surf->smooth <= 0 ) continue;\r
401 \r
402          p = polygon->pol[ j ].v[ n ].index;\r
403 \r
404          for ( g = 0; g < point->pt[ p ].npols; g++ ) {\r
405             h = point->pt[ p ].pol[ g ];\r
406             if ( h == j ) continue;\r
407 \r
408             if ( polygon->pol[ j ].smoothgrp != polygon->pol[ h ].smoothgrp )\r
409                continue;\r
410             a = vecangle( polygon->pol[ j ].norm, polygon->pol[ h ].norm );\r
411             if ( a > polygon->pol[ j ].surf->smooth ) continue;\r
412 \r
413             for ( k = 0; k < 3; k++ )\r
414                polygon->pol[ j ].v[ n ].norm[ k ] += polygon->pol[ h ].norm[ k ];\r
415          }\r
416 \r
417          normalize( polygon->pol[ j ].v[ n ].norm );\r
418       }\r
419    }\r
420 }\r
421 \r
422 \r
423 /*\r
424 ======================================================================\r
425 lwFreeTags()\r
426 \r
427 Free memory used by an lwTagList.\r
428 ====================================================================== */\r
429 \r
430 void lwFreeTags( lwTagList *tlist )\r
431 {\r
432    int i;\r
433 \r
434    if ( tlist ) {\r
435       if ( tlist->tag ) {\r
436          for ( i = 0; i < tlist->count; i++ )\r
437             if ( tlist->tag[ i ] ) _pico_free( tlist->tag[ i ] );\r
438          _pico_free( tlist->tag );\r
439       }\r
440       memset( tlist, 0, sizeof( lwTagList ));\r
441    }\r
442 }\r
443 \r
444 \r
445 /*\r
446 ======================================================================\r
447 lwGetTags()\r
448 \r
449 Read tag strings from a TAGS chunk in an LWO2 file.  The tags are\r
450 added to the lwTagList array.\r
451 ====================================================================== */\r
452 \r
453 int lwGetTags( picoMemStream_t *fp, int cksize, lwTagList *tlist )\r
454 {\r
455    char *buf, *bp;\r
456    int i, len, ntags;\r
457 \r
458    if ( cksize == 0 ) return 1;\r
459 \r
460    /* read the whole chunk */\r
461 \r
462    set_flen( 0 );\r
463    buf = getbytes( fp, cksize );\r
464    if ( !buf ) return 0;\r
465 \r
466    /* count the strings */\r
467 \r
468    ntags = 0;\r
469    bp = buf;\r
470    while ( bp < buf + cksize ) {\r
471       len = strlen( bp ) + 1;\r
472       len += len & 1;\r
473       bp += len;\r
474       ++ntags;\r
475    }\r
476 \r
477    /* expand the string array to hold the new tags */\r
478 \r
479    tlist->offset = tlist->count;\r
480    tlist->count += ntags;\r
481    if ( !_pico_realloc( (void *) &tlist->tag, (tlist->count - ntags) * sizeof( char * ), tlist->count * sizeof( char * )) )\r
482       goto Fail;\r
483    memset( &tlist->tag[ tlist->offset ], 0, ntags * sizeof( char * ));\r
484 \r
485    /* copy the new tags to the tag array */\r
486 \r
487    bp = buf;\r
488    for ( i = 0; i < ntags; i++ )\r
489       tlist->tag[ i + tlist->offset ] = sgetS0( &bp );\r
490 \r
491    _pico_free( buf );\r
492    return 1;\r
493 \r
494 Fail:\r
495    if ( buf ) _pico_free( buf );\r
496    return 0;\r
497 }\r
498 \r
499 \r
500 /*\r
501 ======================================================================\r
502 lwGetPolygonTags()\r
503 \r
504 Read polygon tags from a PTAG chunk in an LWO2 file.\r
505 ====================================================================== */\r
506 \r
507 int lwGetPolygonTags( picoMemStream_t *fp, int cksize, lwTagList *tlist,\r
508    lwPolygonList *plist )\r
509 {\r
510    unsigned int type;\r
511    int rlen = 0, i, j;\r
512 \r
513    set_flen( 0 );\r
514    type = getU4( fp );\r
515    rlen = get_flen();\r
516    if ( rlen < 0 ) return 0;\r
517 \r
518    if ( type != ID_SURF && type != ID_PART && type != ID_SMGP ) {\r
519       _pico_memstream_seek( fp, cksize - 4, PICO_SEEK_CUR );\r
520       return 1;\r
521    }\r
522 \r
523    while ( rlen < cksize ) {\r
524       i = getVX( fp ) + plist->offset;\r
525       j = getVX( fp ) + tlist->offset;\r
526       rlen = get_flen();\r
527       if ( rlen < 0 || rlen > cksize ) return 0;\r
528 \r
529       switch ( type ) {\r
530          case ID_SURF:  plist->pol[ i ].surf = ( lwSurface * ) j;  break;\r
531          case ID_PART:  plist->pol[ i ].part = j;  break;\r
532          case ID_SMGP:  plist->pol[ i ].smoothgrp = j;  break;\r
533       }\r
534    }\r
535 \r
536    return 1;\r
537 }\r