1 /*
2  * Copyright (C) 1998 Janne L�f <jlof@mail.student.oulu.fi>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 
19 
20 #include "lw.h"
21 #include <stdio.h>
22 #include <string.h>
23 #include <math.h>
24 
25 #define MK_ID(a,b,c,d) ((((guint32)(a))<<24)| \
26 			(((guint32)(b))<<16)| \
27 			(((guint32)(c))<< 8)| \
28 			(((guint32)(d))    ))
29 
30 #define ID_FORM MK_ID('F','O','R','M')
31 #define ID_LWOB MK_ID('L','W','O','B')
32 #define ID_PNTS MK_ID('P','N','T','S')
33 #define ID_SRFS MK_ID('S','R','F','S')
34 #define ID_SURF MK_ID('S','U','R','F')
35 #define ID_POLS MK_ID('P','O','L','S')
36 #define ID_COLR MK_ID('C','O','L','R')
37 
read_char(FILE * f)38 static gint32 read_char(FILE *f)
39 {
40   int c = fgetc(f);
41   g_return_val_if_fail(c != EOF, 0);
42   return c;
43 }
44 
read_short(FILE * f)45 static gint32 read_short(FILE *f)
46 {
47   return (read_char(f)<<8) | read_char(f);
48 }
49 
read_long(FILE * f)50 static gint32 read_long(FILE *f)
51 {
52   return (read_char(f)<<24) | (read_char(f)<<16) | (read_char(f)<<8) | read_char(f);
53 }
54 
read_float(FILE * f)55 static GLfloat read_float(FILE *f)
56 {
57   union { GLfloat g; gint32 x; } u;
58   u.x = read_long(f);
59   return u.g;
60 }
61 
read_string(FILE * f,char * s)62 static gint read_string(FILE *f, char *s)
63 {
64   gint c;
65   gint cnt = 0;
66   do {
67     c = read_char(f);
68     if (cnt < LW_MAX_NAME_LEN)
69       s[cnt] = c;
70     else
71       s[LW_MAX_NAME_LEN-1] = 0;
72     cnt++;
73   } while (c != 0);
74   /* if length of string (including \0) is odd skip another byte */
75   if (cnt%2) {
76     read_char(f);
77     cnt++;
78   }
79   return cnt;
80 }
81 
read_srfs(FILE * f,gint nbytes,lwObject * lwo)82 static void read_srfs(FILE *f, gint nbytes, lwObject *lwo)
83 {
84   int guess_cnt = lwo->material_cnt;
85 
86   while (nbytes > 0) {
87     lwMaterial *material;
88 
89     /* allocate more memory for materials if needed */
90     if (guess_cnt <= lwo->material_cnt) {
91       guess_cnt += guess_cnt/2 + 4;
92       lwo->material = g_realloc(lwo->material, sizeof(lwMaterial)*guess_cnt);
93     }
94     material = lwo->material + lwo->material_cnt++;
95 
96     /* read name */
97     nbytes -= read_string(f,material->name);
98 
99     /* defaults */
100     material->r = 0.7;
101     material->g = 0.7;
102     material->b = 0.7;
103   }
104   lwo->material = g_realloc(lwo->material, sizeof(lwMaterial)*lwo->material_cnt);
105 }
106 
107 
read_surf(FILE * f,gint nbytes,lwObject * lwo)108 static void read_surf(FILE *f, gint nbytes, lwObject *lwo)
109 {
110   int i;
111   char name[LW_MAX_NAME_LEN];
112   lwMaterial *material = NULL;
113 
114   /* read surface name */
115   nbytes -= read_string(f,name);
116 
117   /* find material */
118   for (i=0; i< lwo->material_cnt; i++) {
119     if (strcmp(lwo->material[i].name,name) == 0) {
120       material = &lwo->material[i];
121       break;
122     }
123   }
124   g_return_if_fail(material != NULL);
125 
126   /* read values */
127   while (nbytes > 0) {
128     gint id = read_long(f);
129     gint len = read_short(f);
130     nbytes -= 6 + len + (len%2);
131 
132     switch (id) {
133     case ID_COLR:
134       material->r = read_char(f) / 255.0;
135       material->g = read_char(f) / 255.0;
136       material->b = read_char(f) / 255.0;
137       read_char(f); /* dummy */
138       break;
139     default:
140       fseek(f, len+(len%2), SEEK_CUR);
141     }
142   }
143 }
144 
145 
read_pols(FILE * f,int nbytes,lwObject * lwo)146 static void read_pols(FILE *f, int nbytes, lwObject *lwo)
147 {
148   int guess_cnt = lwo->face_cnt;
149 
150   while (nbytes > 0) {
151     lwFace *face;
152     int i;
153 
154     /* allocate more memory for polygons if necessary */
155     if (guess_cnt <= lwo->face_cnt) {
156       guess_cnt += guess_cnt + 4;
157       lwo->face = g_realloc(lwo->face, sizeof(lwFace)*guess_cnt);
158     }
159     face = lwo->face + lwo->face_cnt++;
160 
161     /* number of points in this face */
162     face->index_cnt = read_short(f);
163     nbytes -= 2;
164 
165     /* allocate space for points */
166     face->index = g_malloc0(sizeof(int)*face->index_cnt);
167 
168     /* read points in */
169     for (i=0; i<face->index_cnt; i++) {
170       face->index[i] = read_short(f);
171       nbytes -= 2;
172     }
173 
174     /* read surface material */
175     face->material = read_short(f);
176     nbytes -= 2;
177 
178     /* skip over detail  polygons */
179     if (face->material < 0) {
180       int det_cnt;
181       face->material = -face->material;
182       det_cnt = read_short(f);
183       nbytes -= 2;
184       while (det_cnt-- > 0) {
185 	int cnt = read_short(f);
186 	fseek(f, cnt*2+2, SEEK_CUR);
187 	nbytes -= cnt*2+2;
188       }
189     }
190     face->material -= 1;
191   }
192   /* readjust to true size */
193   lwo->face = g_realloc(lwo->face, sizeof(lwFace)*lwo->face_cnt);
194 }
195 
196 
197 
read_pnts(FILE * f,gint nbytes,lwObject * lwo)198 static void read_pnts(FILE *f, gint nbytes, lwObject *lwo)
199 {
200   int i;
201   lwo->vertex_cnt = nbytes / 12;
202   lwo->vertex = g_malloc0(sizeof(GLfloat)*lwo->vertex_cnt*3);
203   for (i=0; i<lwo->vertex_cnt; i++) {
204     lwo->vertex[i*3+0] = read_float(f);
205     lwo->vertex[i*3+1] = read_float(f);
206     lwo->vertex[i*3+2] = read_float(f);
207   }
208 }
209 
210 
211 
212 
213 
214 
lw_is_lwobject(const char * lw_file)215 gint lw_is_lwobject(const char *lw_file)
216 {
217   FILE *f = fopen(lw_file, "rb");
218   if (f) {
219     gint32 form = read_long(f);
220     gint32 nlen = read_long(f);
221     gint32 lwob = read_long(f);
222     fclose(f);
223     if (form == ID_FORM && nlen != 0 && lwob == ID_LWOB)
224       return TRUE;
225   }
226   return FALSE;
227 }
228 
229 
lw_object_read(const char * lw_file)230 lwObject *lw_object_read(const char *lw_file)
231 {
232   FILE *f = NULL;
233   lwObject *lw_object = NULL;
234 
235   gint32 form_bytes = 0;
236   gint32 read_bytes = 0;
237 
238   /* open file */
239   f = fopen(lw_file, "rb");
240   if (f == NULL) {
241     g_warning("can't open file %s", lw_file);
242     return NULL;
243   }
244 
245   /* check for headers */
246   if (read_long(f) != ID_FORM) {
247     g_warning("file %s is not an IFF file", lw_file);
248     fclose(f);
249     return NULL;
250   }
251   form_bytes = read_long(f);
252   read_bytes += 4;
253 
254   if (read_long(f) != ID_LWOB) {
255     g_warning("file %s is not a LWOB file", lw_file);
256     fclose(f);
257     return NULL;
258   }
259 
260   /* create new lwObject */
261   lw_object = g_malloc0(sizeof(lwObject));
262 
263   /* read chunks */
264   while (read_bytes < form_bytes) {
265     gint32  id     = read_long(f);
266     gint32  nbytes = read_long(f);
267     read_bytes += 8 + nbytes + (nbytes%2);
268 
269     switch (id) {
270     case ID_PNTS:
271       read_pnts(f, nbytes, lw_object);
272       break;
273     case ID_POLS:
274       read_pols(f, nbytes, lw_object);
275       break;
276     case ID_SRFS:
277       read_srfs(f, nbytes, lw_object);
278       break;
279     case ID_SURF:
280       read_surf(f, nbytes, lw_object);
281       break;
282     default:
283       fseek(f, nbytes + (nbytes%2), SEEK_CUR);
284     }
285   }
286 
287   fclose(f);
288   return lw_object;
289 }
290 
291 
292 
293 
294 
295 
296 
lw_object_free(lwObject * lw_object)297 void lw_object_free(lwObject *lw_object)
298 {
299   g_return_if_fail(lw_object != NULL);
300 
301   if (lw_object->face) {
302     int i;
303     for (i=0; i<lw_object->face_cnt; i++)
304       g_free(lw_object->face[i].index);
305     g_free(lw_object->face);
306   }
307   g_free(lw_object->material);
308   g_free(lw_object->vertex);
309   g_free(lw_object);
310 }
311 
312 
313 
314 
315 
316 #define PX(i) (lw_object->vertex[face->index[i]*3+0])
317 #define PY(i) (lw_object->vertex[face->index[i]*3+1])
318 #define PZ(i) (lw_object->vertex[face->index[i]*3+2])
lw_object_show(const lwObject * lw_object)319 void lw_object_show(const lwObject *lw_object)
320 {
321   int i,j;
322   int prev_index_cnt = -1;
323   int prev_material  = -1;
324   GLfloat prev_nx = 0;
325   GLfloat prev_ny = 0;
326   GLfloat prev_nz = 0;
327 
328   g_return_if_fail(lw_object != NULL);
329 
330   for (i=0; i<lw_object->face_cnt; i++) {
331     GLfloat ax,ay,az,bx,by,bz,nx,ny,nz,r;
332     const lwFace *face = lw_object->face+i;
333 
334     /* ignore faces with less than 3 points */
335     if (face->index_cnt < 3)
336       continue;
337 
338     /* calculate normal */
339     ax = PX(1) - PX(0);
340     ay = PY(1) - PY(0);
341     az = PZ(1) - PZ(0);
342 
343     bx = PX(face->index_cnt-1) - PX(0);
344     by = PY(face->index_cnt-1) - PY(0);
345     bz = PZ(face->index_cnt-1) - PZ(0);
346 
347     nx = ay * bz - az * by;
348     ny = az * bx - ax * bz;
349     nz = ax * by - ay * bx;
350 
351     r = sqrt(nx*nx + ny*ny + nz*nz);
352     if (r < 0.000001) /* avoid division by zero */
353       continue;
354     nx /= r;
355     ny /= r;
356     nz /= r;
357 
358     /* glBegin/glEnd */
359     if (prev_index_cnt != face->index_cnt || prev_index_cnt > 4) {
360       if (prev_index_cnt > 0) glEnd();
361       prev_index_cnt = face->index_cnt;
362       switch (face->index_cnt) {
363       case 3:
364 	glBegin(GL_TRIANGLES);
365 	break;
366       case 4:
367 	glBegin(GL_QUADS);
368 	break;
369       default:
370 	glBegin(GL_POLYGON);
371       }
372     }
373 
374     /* update material if necessary */
375     if (prev_material != face->material) {
376       prev_material = face->material;
377       glColor3f(lw_object->material[face->material].r,
378 		lw_object->material[face->material].g,
379 		lw_object->material[face->material].b);
380     }
381 
382     /* update normal if necessary */
383     if (nx != prev_nx || ny != prev_ny || nz != prev_nz) {
384       prev_nx = nx;
385       prev_ny = ny;
386       prev_nz = nz;
387       glNormal3f(nx,ny,nz);
388     }
389 
390     /* draw polygon/triangle/quad */
391     for (j=0; j<face->index_cnt; j++)
392       glVertex3f(PX(j),PY(j),PZ(j));
393 
394   }
395 
396   /* if glBegin was called call glEnd */
397   if (prev_index_cnt > 0)
398     glEnd();
399 }
400 
401 
lw_object_radius(const lwObject * lwo)402 GLfloat lw_object_radius(const lwObject *lwo)
403 {
404   int i;
405   double max_radius = 0.0;
406 
407   g_return_val_if_fail(lwo != NULL, 0.0);
408 
409   for (i=0; i<lwo->vertex_cnt; i++) {
410     GLfloat *v = &lwo->vertex[i*3];
411     double r = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
412     if (r > max_radius)
413       max_radius = r;
414   }
415   return sqrt(max_radius);
416 }
417 
lw_object_scale(lwObject * lwo,GLfloat scale)418 void lw_object_scale(lwObject *lwo, GLfloat scale)
419 {
420   int i;
421 
422   g_return_if_fail(lwo != NULL);
423 
424   for (i=0; i<lwo->vertex_cnt; i++) {
425     lwo->vertex[i*3+0] *= scale;
426     lwo->vertex[i*3+1] *= scale;
427     lwo->vertex[i*3+2] *= scale;
428   }
429 }
430 
431 
432