Tachyon (current)  Current Main Branch
ac3dparse.c
Go to the documentation of this file.
1 /*
2  * ac3dparse.c - Code for parsing ac3d modeler files
3  *
4  * (C) Copyright 1994-2022 John E. Stone
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * $Id: ac3dparse.c,v 1.20 2022/02/18 18:18:36 johns Exp $
8  *
9  */
10 
11 /*
12  * See http://www.comp.lancs.ac.uk/computing/users/andy/ac3d/
13  * for more information on ac3d.
14  */
15 
16 #include <stdio.h>
17 #include <math.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <ctype.h> /* needed for toupper(), macro.. */
21 
22 #include "tachyon.h" /* ray tracer api */
23 
24 #define PARSE_INTERNAL
25 #include "ac3dparse.h" /* self protos */
26 #undef PARSE_INTERNAL
27 
28 static texentry textable[NUMTEXS]; /* texture lookup table */
29 static int numtextures; /* number of TEXDEF textures */
30 static int numobjectsparsed; /* total number of objects parsed so far */
31 static apicolor scenebackcol; /* scene background color */
32 
33 static int stringcmp(char * a, char * b) {
34  int i, s, l;
35 
36  s=strlen(a);
37  l=strlen(b);
38 
39  if (s != l)
40  return 1;
41 
42  for (i=0; i<s; i++) {
43  if (toupper(a[i]) != toupper(b[i])) {
44  return 1;
45  }
46  }
47  return 0;
48 }
49 
50 
51 static void GetAC3DString(FILE * dfile, char * str) {
52  char c;
53  int i=0;
54 
55  /* find opening quote */
56  while ((!feof(dfile)) && ((c = fgetc(dfile)) != '"'));
57 
58  /* scan in string until closing quote */
59  while ((!feof(dfile)) && ((c = fgetc(dfile)) != '"')) {
60  str[i] = c;
61  i++;
62  }
63  str[i]='\0'; /* nul terminate the string */
64 }
65 
66 static void reset_tex_table(void) {
67  numtextures=0;
68  memset(&textable, 0, sizeof(textable));
69 }
70 
71 static errcode add_texture(void * tex, char name[TEXNAMELEN]) {
72  textable[numtextures].tex=tex;
73  strcpy(textable[numtextures].name, name);
74 
75  numtextures++;
76  if (numtextures > NUMTEXS) {
77  printf("Parse: %d textures allocated, texture slots full!\n", numtextures);
78  numtextures--; /* keep writing over last texture if we've run out.. */
79  return PARSEALLOCERR;
80  }
81 
82  return PARSENOERR;
83 }
84 
85 static errcode GetString(FILE * dfile, char * string) {
86  char data[100];
87 
88  fscanf(dfile,"%s",data);
89  if (stringcmp(data, string) != 0) {
90  printf("parse: Expected %s, got %s \n",string, data);
91  printf("parse: Error while parsing object: %d \n",numobjectsparsed);
92  return PARSEBADSYNTAX;
93  }
94 
95  return PARSENOERR;
96 }
97 
98 unsigned int ParseAC3D(char * modelfile, SceneHandle scene) {
99  FILE * dfile;
100  char filehdr[255];
101  errcode rc;
102 
103  reset_tex_table();
104  dfile=NULL;
105 
106  dfile=fopen(modelfile,"r");
107  if (dfile==NULL) {
108  return PARSEBADFILE;
109  }
110 
111  fscanf(dfile, "%s", filehdr);
112  if (strcmp(filehdr, "AC3Db")) {
113  printf("Unknown version of AC3D Model File Format\n");
114  fclose(dfile);
115  return PARSEBADFILE;
116  }
117 
118  rc = GetScenedefs(dfile, scene);
119  if (rc != PARSENOERR)
120  return rc;
121 
122  scenebackcol.r = 0.0; /* default background is black */
123  scenebackcol.g = 0.0;
124  scenebackcol.b = 0.0;
125 
127  while ((rc = GetScene(dfile, scene)) == PARSENOERR) {
129  }
130  fclose(dfile);
131 
132  if (rc == PARSEEOF)
133  rc = PARSENOERR;
134 
135  rt_background(scene, scenebackcol);
136 
137  return rc;
138 }
139 
140 
141 static errcode GetScenedefs(FILE * dfile, SceneHandle scene) {
142  apivector Ccenter, Cview, Cup;
143  apiflt zoom, aspectratio;
144  int raydepth, antialiasing;
145 
146  rt_outputfile(scene, "outfile.tga");
147  rt_resolution(scene, 512, 512);
148  rt_verbose(scene, 0);
149 
150  zoom=1.0;
151  aspectratio=1.0;
152  antialiasing=0;
153  raydepth=6;
154 
155  Ccenter.x = 0.0;
156  Ccenter.y = 0.0;
157  Ccenter.z = -5.0;
158 
159  Cview.x = 0.0;
160  Cview.y = 0.0;
161  Cview.z = 1.0;
162 
163  Cup.x = 0.0;
164  Cup.y = 1.0;
165  Cup.z = 0.0;
166 
167  rt_camera_setup(scene, zoom, aspectratio, antialiasing, raydepth,
168  Ccenter, Cview, Cup);
169 
170  { /* lighting hack */
171  apivector ctr;
172  apitexture tex;
173 
174  memset(&tex, 0, sizeof(apitexture));
175 
176  tex.col.r = 1.0;
177  tex.col.g = 1.0;
178  tex.col.b = 1.0;
179  ctr.x = 0.0;
180  ctr.y = 0.0;
181  ctr.z = -100.0;
182 
183  rt_light(scene, rt_texture(scene, &tex), ctr, 1.0);
184  }
185 
186  return PARSENOERR;
187 }
188 
189 static errcode GetScene(FILE * dfile, SceneHandle scene) {
190  char objtype[80];
191 
192  if (fscanf(dfile, "%s", objtype) != 1) {
193  return PARSEEOF; /* end parsing */
194  }
195  if (!stringcmp(objtype, "MATERIAL")) {
196  return GetMaterial(dfile, scene);
197  }
198  if (!stringcmp(objtype, "OBJECT")) {
199  return GetObject(dfile, scene);
200  }
201 
202  printf("Found bad token: %s expected an object type\n", objtype);
203  return PARSEBADSYNTAX;
204 }
205 
206 static errcode GetMaterial(FILE * dfile, SceneHandle scene) {
207  apitexture tex;
208  float a,b,c,d;
209  int e;
210  errcode rc;
211  char texname[TEXNAMELEN];
212 
213  fscanf(dfile, "%s", texname);
214 
215  rc = GetString(dfile, "RGB");
216  fscanf(dfile, "%f %f %f", &a, &b, &c);
217  tex.col.r = a;
218  tex.col.g = b;
219  tex.col.b = c;
220  tex.diffuse = (a + b + c) / 3.0;
221 
222  rc = GetString(dfile, "AMB");
223  fscanf(dfile, "%f %f %f", &a, &b, &c);
224  tex.ambient= ((a + b + c) / 3.0);
225 
226  rc |= GetString(dfile, "EMIS");
227  fscanf(dfile, "%f %f %f", &a, &b, &c);
228 
229  rc |= GetString(dfile, "SPEC");
230  fscanf(dfile, "%f %f %f", &a, &b, &c);
231  tex.specular= (a + b + c) / 3.0;
232 
233  rc |= GetString(dfile, "SHI");
234  fscanf(dfile, "%d", &e);
235 
236  rc |= GetString(dfile, "TRANS");
237  fscanf(dfile, "%f", &d);
238  tex.opacity= (d > 0.99) ? 0.0 : (1.0 - d);
239  tex.texturefunc = 0;
240 
241  add_texture(rt_texture(scene, &tex), texname);
242 
243  return rc;
244 }
245 
246 static void RmatIdentity(RotMat rmat) {
247  int i, j;
248  for (j=0; j<3; j++) {
249  for (i=0; i<3; i++) {
250  if (i==j)
251  rmat[j][i] = 1.0;
252  else
253  rmat[j][i] = 0.0;
254  }
255  }
256 }
257 
258 static void RmatMult(RotMat A, RotMat B, RotMat C) {
259  int i, j, k;
260 
261  for (k=0; k<3; k++) {
262  for (j=0; j<3; j++) {
263  A[k][j] = 0.0;
264  for (i=0; i<3; i++) {
265  A[k][j] += B[k][i] * C[i][j];
266  }
267  }
268  }
269 }
270 
271 static void RmatTmatMult(TransMat NT, RotMat R, TransMat T) {
272  int i, j;
273  for (j=0; j<3; j++) {
274  NT[j] = 0.0;
275  for (i=0; i<3; i++) {
276  NT[j] += R[j][i] * T[i];
277  }
278  }
279 }
280 
281 static void tlist_add_tri(tri_list ** tlist, int v0, int v1, int v2,
282  int smooth, int texnum) {
283  tri_list * newlist;
284 
285  newlist = (tri_list *) malloc(sizeof(tri_list));
286  newlist->v0 = v0;
287  newlist->v1 = v1;
288  newlist->v2 = v2;
289  newlist->smooth = smooth;
290  newlist->texnum = texnum;
291  newlist->next = *tlist;
292 
293  *tlist = newlist;
294 }
295 
296 static void tlist_delete(tri_list ** tlist) {
297  tri_list * tmp;
298  tri_list * next;
299 
300  tmp = *tlist;
301 
302  while (tmp != NULL) {
303  next = tmp->next;
304  free(tmp);
305  tmp = next;
306  }
307 
308  *tlist = NULL;
309 }
310 
311 
312 static void clear_normals(apivector * normals, int numverts) {
313  apivector tmp;
314  int i;
315 
316  tmp.x = tmp.y = tmp.z = 0.0;
317 
318  for (i=0; i<numverts; i++) {
319  normals[i] = tmp;
320  }
321 }
322 
323 
324 static void normalize(apivector * vec) {
325  double length;
326  length = sqrt((vec->x * vec->x) + (vec->y * vec->y) + (vec->z * vec->z));
327  vec->x /= length;
328  vec->y /= length;
329  vec->z /= length;
330 }
331 
332 static void renormalize_normals(apivector * normals, int numverts) {
333  int i;
334 
335  for (i=0; i<numverts; i++) {
336  normalize(&normals[i]);
337  }
338 }
339 
341  apivector U, V;
342  apivector norm;
343 
344  U.x = v1->x - v0->x;
345  U.y = v1->y - v0->y;
346  U.z = v1->z - v0->z;
347 
348  V.x = v2->x - v0->x;
349  V.y = v2->y - v0->y;
350  V.z = v2->z - v0->z;
351 
352  norm.x = (U.y * V.z) - (U.z * V.y);
353  norm.y = (U.z * V.x) - (U.x * V.z);
354  norm.z = (U.x * V.y) - (U.y * V.x);
355 
356  normalize(&norm);
357 
358  return norm;
359 }
360 
361 static void gen_triangles(SceneHandle scene, tri_list * tlist,
362  apivector * vertex, apivector * normal) {
363  tri_list * cur;
364 
365  cur = tlist;
366  while (cur != NULL) {
367  if (cur->smooth) {
368  rt_stri(scene, textable[cur->texnum].tex,
369  vertex[cur->v0], vertex[cur->v1], vertex[cur->v2],
370  normal[cur->v0], normal[cur->v1], normal[cur->v2]);
371 
372  }
373  else {
374  rt_tri(scene, textable[cur->texnum].tex,
375  vertex[cur->v0], vertex[cur->v1], vertex[cur->v2]);
376  }
377 
378  cur = cur->next;
379  }
380 }
381 
382 
383 static errcode GetRecurseObject(SceneHandle scene, FILE *dfile,
384  RotMat rmat, TransMat tmat) {
385  char token[256], objname[128], name[128], texname[128], urlname[100];
386  RotMat localrmat, newrmat;
387  TransMat localtmat, newtmat;
388  float txrepa, txrepb, a, b;
389  int numvert, numsurf, surf, materialnum, numrefs, numkids, data;
390  int i, j;
391  apivector * vertexarray = NULL;
392  apivector * normalarray = NULL;
393  int * refarray = NULL;
394  tri_list * tlist = NULL;
395  errcode rc = PARSENOERR;
396 
397  /* zero out variables */
398  numvert = numsurf = surf = materialnum = numrefs = numkids = data = 0;
399 
400  RmatIdentity(localrmat);
401  localtmat[0] = 0.0;
402  localtmat[1] = 0.0;
403  localtmat[2] = 0.0;
404  fscanf(dfile, "%s", objname);
405  fscanf(dfile, "%s", token);
406  if (!stringcmp(token, "NAME")) {
407  GetAC3DString(dfile, name);
408  fscanf(dfile, "%s", token);
409  }
410  if (!stringcmp(token, "DATA")) {
411  fscanf(dfile, "%d", &data);
412  fscanf(dfile, "%s", token);
413  }
414  if (!stringcmp(token, "TEXTURE")) {
415  fscanf(dfile, "%s", texname);
416  fscanf(dfile, "%s", token);
417  }
418  if (!stringcmp(token, "TEXREP")) {
419  fscanf(dfile, "%f %f", &txrepa, &txrepb);
420  fscanf(dfile, "%s", token);
421  }
422  if (!stringcmp(token, "ROT")) {
423  for (j=0; j<3; j++) {
424  for(i=0; i<3; i++) {
425  fscanf(dfile, "%f", &localrmat[j][i]);
426  }
427  }
428  fscanf(dfile, "%s", token);
429  }
430  if (!stringcmp(token, "LOC")) {
431  for (j=0; j<3; j++) {
432  fscanf(dfile, "%f", &localtmat[j]);
433  }
434  fscanf(dfile, "%s", token);
435  }
436  if (!stringcmp(token, "ROT")) {
437  for (j=0; j<3; j++) {
438  for(i=0; i<3; i++) {
439  fscanf(dfile, "%f", &localrmat[j][i]);
440  }
441  }
442  fscanf(dfile, "%s", token);
443  }
444 
445  /* Perform Matrix Transforms for local coordinate system */
446  RmatTmatMult(newtmat, rmat, localtmat);
447  newtmat[0] += tmat[0];
448  newtmat[1] += tmat[1];
449  newtmat[2] += tmat[2];
450  RmatMult(newrmat, rmat, localrmat);
451 
452  if (!stringcmp(token, "URL")) {
453  fscanf(dfile, "%s", urlname);
454  fscanf(dfile, "%s", token);
455  }
456  if (!stringcmp(token, "TEXTURE")) {
457  GetAC3DString(dfile, token);
458  fscanf(dfile, "%s", token);
459  }
460  if (!stringcmp(token, "NUMVERT")) {
461  TransMat vtx, tvtx;
462 
463  fscanf(dfile, "%d", &numvert);
464 
465  vertexarray = (apivector *) malloc(numvert * sizeof(apivector));
466  normalarray = (apivector *) malloc(numvert * sizeof(apivector));
467 
468  /* initialize the normal array */
469  clear_normals(normalarray, numvert);
470 
471  /* load and transform vertices */
472  for (i=0; i<numvert; i++) {
473  fscanf(dfile, "%f %f %f", &vtx[0], &vtx[1], &vtx[2]);
474  /* transform vertices */
475  RmatTmatMult(tvtx, newrmat, vtx);
476  vertexarray[i].x = tvtx[0] + newtmat[0];
477  vertexarray[i].y = tvtx[1] + newtmat[1];
478  vertexarray[i].z = tvtx[2] + newtmat[2];
479  }
480 
481  fscanf(dfile, "%s", token);
482  }
483  if (!stringcmp(token, "NUMSURF")) {
484  fscanf(dfile, "%d", &numsurf);
485  fscanf(dfile, "%s", token);
486 
487  for (j=0; j<numsurf; j++) {
488  numrefs = surf = materialnum = 0;
489 
490  if (!stringcmp(token, "SURF")) {
491 
492 /* COMPILER BUG!!! */
493 /* This is messed up friend.. */
494 /* if done using a normal fscanf() the program nails its */
495 /* stack, and seg faults. This *must* be a compiler or */
496 /* libc bug. */
497 #if 1
498  fscanf(dfile, "%s", token);
499  sscanf(token, "%x", &surf);
500 #else
501  fscanf(dfile, "%x", &surf);
502 #endif
503  fscanf(dfile, "%s", token);
504  }
505  if (!stringcmp(token, "MAT")) {
506  fscanf(dfile, "%d", &materialnum);
507  fscanf(dfile, "%s", token);
508  }
509  if (!stringcmp(token, "REFS"))
510  fscanf(dfile, "%d", &numrefs);
511  else
512  return (rc |= PARSEBADSYNTAX);
513 
514  refarray = (int *) malloc(numrefs * sizeof(int));
515  for (i=0; i<numrefs; i++) {
516  fscanf(dfile, "%d %f %f", &refarray[i], &a, &b);
517  }
518 
519  /* generate triangles/polygons here */
520  /* ignore all lines, points and other non-surface primatives */
521  if ((surf & 0xF) == 0) {
522  int v0, vold, vnew;
523  apivector trinorm;
524 
525  /* add in surface normal to vertices */
526  trinorm = tri_normal(&vertexarray[refarray[1]],
527  &vertexarray[refarray[0]],
528  &vertexarray[refarray[2]]);
529 
530  for (i=0; i<numrefs; i++) {
531  normalarray[refarray[i]].x += trinorm.x;
532  normalarray[refarray[i]].y += trinorm.y;
533  normalarray[refarray[i]].z += trinorm.z;
534  }
535 
536  v0 = refarray[0];
537  vold = refarray[1];
538 
539  for (i=2; i<numrefs; i++) {
540  vnew = refarray[i];
541  tlist_add_tri(&tlist, vold, v0, vnew, surf & 0x10, materialnum);
542  vold = vnew;
543  }
544  }
545 
546  free(refarray);
547  refarray=NULL;
548  fscanf(dfile, "%s", token);
549  }
550  }
551 
552  if ((vertexarray != NULL) && (normalarray != NULL)) {
553  /* now that all vertex normals have been summed, we'll renormalize */
554  renormalize_normals(normalarray, numvert);
555 
556  gen_triangles(scene, tlist, vertexarray, normalarray);
557  tlist_delete(&tlist);
558  }
559 
560  /* free vertex and normal arrays */
561  if (vertexarray != NULL)
562  free(vertexarray);
563  vertexarray = NULL;
564 
565  if (normalarray != NULL)
566  free(normalarray);
567  normalarray = NULL;
568 
569 
570 
571  if (!stringcmp(token, "KIDS"))
572  fscanf(dfile, "%d", &numkids);
573  else
574  return (rc |= PARSEBADSYNTAX);
575 
576  /* Recurse to handle child geometry */
577  while (numkids > 0) {
578  rc |= GetString(dfile, "OBJECT");
579  rc |= GetRecurseObject(scene, dfile, newrmat, newtmat);
580  numkids--;
581  }
582 
583  return rc;
584 }
585 
586 static errcode GetObject(FILE *dfile, SceneHandle scene) {
587  RotMat defaultrmat;
588  TransMat defaulttmat;
589  errcode rc;
590 
591  RmatIdentity(defaultrmat);
592  defaulttmat[0] = 0.0;
593  defaulttmat[1] = 0.0;
594  defaulttmat[2] = 0.0;
595 
596  rc = GetRecurseObject(scene, dfile, defaultrmat, defaulttmat);
597 
598  return rc;
599 }
600 
601 
602 
603 
static void RmatTmatMult(TransMat NT, RotMat R, TransMat T)
Definition: ac3dparse.c:271
unsigned int ParseAC3D(char *modelfile, SceneHandle scene)
Definition: ac3dparse.c:98
static errcode GetMaterial(FILE *dfile, SceneHandle scene)
Definition: ac3dparse.c:206
#define PARSEALLOCERR
Definition: ac3dparse.h:16
static void RmatMult(RotMat A, RotMat B, RotMat C)
Definition: ac3dparse.c:258
float g
Green color component.
Definition: tachyon.h:61
void rt_camera_setup(SceneHandle voidscene, flt zoom, flt aspectratio, int antialiasing, int raydepth, apivector camcent, apivector viewvec, apivector upvec)
Define a camera for a perspective projection, given the specified zoom factor, aspect ratio...
Definition: api.c:229
float r
Red color component.
Definition: tachyon.h:60
void * rt_texture(SceneHandle sc, apitexture *apitex)
Translate a texture definition into the internal format used by Tachyon, and returns an opaque pointe...
Definition: api.c:933
apicolor col
base object color
Definition: tachyon.h:67
static errcode GetString(FILE *dfile, char *string)
Definition: ac3dparse.c:85
static void tlist_add_tri(tri_list **tlist, int v0, int v1, int v2, int smooth, int texnum)
Definition: ac3dparse.c:281
void rt_resolution(SceneHandle voidscene, int hres, int vres)
Set the horizontal and vertical resolution (in pixels) for the specified scene.
Definition: api.c:372
#define PARSENOERR
Definition: ac3dparse.h:11
static apivector tri_normal(apivector *v0, apivector *v1, apivector *v2)
Definition: ac3dparse.c:340
flt diffuse
diffuse reflection
Definition: tachyon.h:70
flt opacity
how opaque the object is
Definition: tachyon.h:72
static errcode GetScenedefs(FILE *dfile, SceneHandle scene)
Definition: ac3dparse.c:141
flt specular
specular reflection
Definition: tachyon.h:71
static int numobjectsparsed
Definition: ac3dparse.c:30
static errcode add_texture(void *tex, char name[TEXNAMELEN])
Definition: ac3dparse.c:71
static void renormalize_normals(apivector *normals, int numverts)
Definition: ac3dparse.c:332
static void GetAC3DString(FILE *dfile, char *str)
Definition: ac3dparse.c:51
static apicolor scenebackcol
Definition: ac3dparse.c:31
void rt_tri(SceneHandle voidscene, void *tex, apivector v0, apivector v1, apivector v2)
Define a flat-shaded triangle.
Definition: api.c:1224
static void reset_tex_table(void)
Definition: ac3dparse.c:66
static void tlist_delete(tri_list **tlist)
Definition: ac3dparse.c:296
flt x
X coordinate or direction component.
Definition: tachyon.h:54
flt apiflt
for backward compatibility
Definition: tachyon.h:49
flt y
Y coordinate or direction component.
Definition: tachyon.h:55
#define PARSEBADSYNTAX
Definition: ac3dparse.h:14
void rt_outputfile(SceneHandle voidscene, const char *outname)
Set the filename for the output image for the specified scene.
Definition: api.c:350
static errcode GetScene(FILE *dfile, SceneHandle scene)
Definition: ac3dparse.c:189
static void RmatIdentity(RotMat rmat)
Definition: ac3dparse.c:246
#define PARSEEOF
Definition: ac3dparse.h:15
static errcode GetRecurseObject(SceneHandle scene, FILE *dfile, RotMat rmat, TransMat tmat)
Definition: ac3dparse.c:383
static void normalize(apivector *vec)
Definition: ac3dparse.c:324
static int numtextures
Definition: ac3dparse.c:29
void * SceneHandle
Definition: tachyon.h:51
void rt_stri(SceneHandle voidscene, void *tex, apivector v0, apivector v1, apivector v2, apivector n0, apivector n1, apivector n2)
Define a smooth-shaded triangle using interpolated vertex normals.
Definition: api.c:1252
int texturefunc
which texture function to use
Definition: tachyon.h:66
flt ambient
ambient lighting
Definition: tachyon.h:69
void rt_background(SceneHandle voidscene, apicolor col)
Set the background color of the specified scene.
Definition: api.c:490
static void clear_normals(apivector *normals, int numverts)
Definition: ac3dparse.c:312
static int stringcmp(char *a, char *b)
Definition: ac3dparse.c:33
float b
Blue color component.
Definition: tachyon.h:62
__host__ __device__ float length(const float3 &v)
static texentry textable[NUMTEXS]
Definition: ac3dparse.c:28
static void gen_triangles(SceneHandle scene, tri_list *tlist, apivector *vertex, apivector *normal)
Definition: ac3dparse.c:361
Tachyon public API function prototypes and declarations used to drive the ray tracing engine...
void * rt_light(SceneHandle voidscene, void *tex, apivector ctr, flt rad)
Define a point light with associated texture, position, and radius.
Definition: api.c:1049
static errcode GetObject(FILE *dfile, SceneHandle scene)
Definition: ac3dparse.c:586
flt z
Z coordinate or direction component.
Definition: tachyon.h:56
void rt_verbose(SceneHandle voidscene, int v)
Enables or Disables verbose messages from the Tachyon library during rendering.
Definition: api.c:414
#define PARSEBADFILE
Definition: ac3dparse.h:12