1 /**
2 * @file
3 * @brief ASE model loading
4 */
5
6 /*
7 Copyright (C) 1999-2007 id Software, Inc. and contributors.
8 For a list of contributors, see the accompanying CONTRIBUTORS file.
9
10 This file is part of GtkRadiant.
11
12 GtkRadiant is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 GtkRadiant is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with GtkRadiant; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 */
26
27 #include "aselib.h"
28 #include "../bsp.h"
29 #include "shared.h"
30
31 #define MAX_ASE_MATERIALS 32
32 #define MAX_ASE_OBJECTS 64
33 #define MAX_ASE_ANIMATIONS 32
34 #define MAX_ASE_ANIMATION_FRAMES 512
35
36 #define VERBOSE(x) { if (ase.verbose) { Com_Printf x; } }
37
38 typedef struct {
39 float x, y, z;
40 float nx, ny, nz;
41 float s, t;
42 } aseVertex_t;
43
44 typedef struct {
45 float s, t;
46 } aseTVertex_t;
47
48 typedef int aseFace_t[3];
49
50 typedef struct {
51 int numFaces;
52 int numVertexes;
53 int numTVertexes;
54
55 int timeValue;
56
57 aseVertex_t* vertexes;
58 aseTVertex_t* tvertexes;
59 aseFace_t* faces, *tfaces;
60
61 int currentFace, currentVertex;
62 } aseMesh_t;
63
64 typedef struct {
65 int numFrames;
66 aseMesh_t frames[MAX_ASE_ANIMATION_FRAMES];
67
68 int currentFrame;
69 } aseMeshAnimation_t;
70
71 typedef struct {
72 char name[MAX_QPATH];
73 } aseMaterial_t;
74
75 /**
76 * @brief contains the animate sequence of a single surface using a single material
77 */
78 typedef struct {
79 char name[MAX_QPATH];
80
81 int materialRef;
82 int numAnimations;
83
84 aseMeshAnimation_t anim;
85 } aseGeomObject_t;
86
87 typedef struct {
88 int numMaterials;
89 aseMaterial_t materials[MAX_ASE_MATERIALS];
90 aseGeomObject_t objects[MAX_ASE_OBJECTS];
91
92 char* buffer;
93 char* curpos;
94 int len;
95
96 int currentObject;
97 bool verbose;
98 } ase_t;
99
100 static char s_token[1024];
101 static ase_t ase;
102
103 static void ASE_Process(void);
104 static void ASE_FreeGeomObject(int ndx);
105
ASE_Load(const char * filename,bool verbose)106 void ASE_Load (const char* filename, bool verbose)
107 {
108 ScopedFile file;
109 FS_OpenFile(filename, &file, FILE_READ);
110 if (!file)
111 Sys_Error("File not found '%s'", filename);
112
113 OBJZERO(ase);
114
115 ase.verbose = verbose;
116 ase.len = FS_FileLength(&file);
117
118 ase.curpos = ase.buffer = Mem_AllocTypeN(char, ase.len);
119 if (!ase.curpos)
120 Sys_Error("Could not allocate memory for ase loading");
121
122 Verb_Printf(VERB_EXTRA, "Processing '%s'\n", filename);
123
124 if (FS_Read(ase.buffer, ase.len, &file) != 1) {
125 Sys_Error("fread() != -1 for '%s'", filename);
126 }
127
128 ASE_Process();
129 }
130
ASE_Free(void)131 void ASE_Free (void)
132 {
133 int i;
134
135 for (i = 0; i < ase.currentObject; i++)
136 ASE_FreeGeomObject(i);
137 }
138
ASE_GetNumSurfaces(void)139 int ASE_GetNumSurfaces (void)
140 {
141 return ase.currentObject;
142 }
143
ASE_GetSurfaceName(int which)144 const char* ASE_GetSurfaceName (int which)
145 {
146 aseGeomObject_t* pObject = &ase.objects[which];
147
148 if (!pObject->anim.numFrames)
149 return 0;
150
151 return pObject->name;
152 }
153
154 /**
155 * @brief Returns an animation (sequence of polysets)
156 */
ASE_GetSurfaceAnimation(int whichSurface)157 polyset_t* ASE_GetSurfaceAnimation (int whichSurface)
158 {
159 aseGeomObject_t* pObject = &ase.objects[whichSurface];
160 polyset_t* psets;
161 int numFramesInAnimation;
162 int i, f;
163
164 if (!pObject->anim.numFrames)
165 return 0;
166
167 numFramesInAnimation = pObject->anim.numFrames;
168
169 psets = Mem_AllocTypeN(polyset_t, numFramesInAnimation);
170
171 for (f = 0, i = 0; i < numFramesInAnimation; i++) {
172 int t;
173 aseMesh_t* pMesh = &pObject->anim.frames[i];
174 polyset_t* ps = &psets[f];
175
176 strcpy(ps->name, pObject->name);
177 strcpy(ps->materialname, ase.materials[pObject->materialRef].name);
178
179 ps->triangles = Mem_AllocTypeN(triangle_t, pObject->anim.frames[i].numFaces);
180 ps->numtriangles = pObject->anim.frames[i].numFaces;
181
182 for (t = 0; t < pObject->anim.frames[i].numFaces; t++) {
183 int k;
184
185 for (k = 0; k < 3; k++) {
186 triangle_t* tri = &ps->triangles[t];
187 const int vIdx = pMesh->faces[t][k];
188 aseVertex_t* v = &pMesh->vertexes[vIdx];
189 VectorSet(tri->verts[k], v->x, v->y, v->z);
190
191 if (pMesh->tvertexes && pMesh->tfaces) {
192 aseTVertex_t* tv = &pMesh->tvertexes[pMesh->tfaces[t][k]];
193 Vector2Set(tri->texcoords[k], tv->s, tv->t);
194 }
195 }
196 }
197
198 f++;
199 }
200
201 return psets;
202 }
203
ASE_FreeGeomObject(int ndx)204 static void ASE_FreeGeomObject (int ndx)
205 {
206 aseGeomObject_t* pObject;
207 int i;
208
209 pObject = &ase.objects[ndx];
210
211 for (i = 0; i < pObject->anim.numFrames; i++) {
212 Mem_Free(pObject->anim.frames[i].vertexes);
213 Mem_Free(pObject->anim.frames[i].tvertexes);
214 Mem_Free(pObject->anim.frames[i].faces);
215 Mem_Free(pObject->anim.frames[i].tfaces);
216 }
217
218 OBJZERO(*pObject);
219 }
220
ASE_GetCurrentMesh(void)221 static aseMesh_t* ASE_GetCurrentMesh (void)
222 {
223 aseGeomObject_t* pObject;
224
225 if (ase.currentObject >= MAX_ASE_OBJECTS)
226 Sys_Error("Too many GEOMOBJECTs");
227
228 pObject = &ase.objects[ase.currentObject];
229
230 if (pObject->anim.currentFrame >= MAX_ASE_ANIMATION_FRAMES)
231 Sys_Error("Too many MESHes");
232
233 return &pObject->anim.frames[pObject->anim.currentFrame];
234 }
235
CharIsTokenDelimiter(int ch)236 static inline int CharIsTokenDelimiter (int ch)
237 {
238 if (ch <= ' ')
239 return 1;
240 return 0;
241 }
242
ASE_GetToken(bool restOfLine)243 static int ASE_GetToken (bool restOfLine)
244 {
245 int i = 0;
246
247 if (ase.buffer == 0)
248 return 0;
249
250 if ((ase.curpos - ase.buffer) == ase.len)
251 return 0;
252
253 /* skip over crap */
254 while (ase.curpos - ase.buffer < ase.len && *ase.curpos <= ' ') {
255 ase.curpos++;
256 }
257
258 while ((ase.curpos - ase.buffer) < ase.len) {
259 s_token[i] = *ase.curpos;
260
261 ase.curpos++;
262 i++;
263
264 if ((CharIsTokenDelimiter(s_token[i - 1]) && !restOfLine) ||
265 (s_token[i - 1] == '\n' || s_token[i - 1] == '\r')) {
266 s_token[i - 1] = 0;
267 break;
268 }
269 }
270
271 s_token[i] = 0;
272
273 return 1;
274 }
275
ASE_ParseBracedBlock(void (* parser)(const char * token))276 static void ASE_ParseBracedBlock (void (*parser)(const char* token))
277 {
278 int indent = 0;
279
280 while (ASE_GetToken(false)) {
281 if (Q_streq(s_token, "{")) {
282 indent++;
283 } else if (Q_streq(s_token, "}")) {
284 --indent;
285 if (indent == 0)
286 break;
287 else if (indent < 0)
288 Sys_Error("Unexpected '}'");
289 } else {
290 if (parser)
291 parser(s_token);
292 }
293 }
294 }
295
ASE_SkipEnclosingBraces(void)296 static void ASE_SkipEnclosingBraces (void)
297 {
298 int indent = 0;
299
300 while (ASE_GetToken(false)) {
301 if (Q_streq(s_token, "{")) {
302 indent++;
303 } else if (Q_streq(s_token, "}")) {
304 indent--;
305 if (indent == 0)
306 break;
307 else if (indent < 0)
308 Sys_Error("Unexpected '}'");
309 }
310 }
311 }
312
ASE_SkipRestOfLine(void)313 static void ASE_SkipRestOfLine (void)
314 {
315 ASE_GetToken(true);
316 }
317
ASE_KeyMAP_DIFFUSE(const char * token)318 static void ASE_KeyMAP_DIFFUSE (const char* token)
319 {
320 if (Q_streq(token, "*BITMAP")) {
321 const char* bitmap;
322 size_t len;
323
324 ASE_GetToken(false);
325
326 /* skip the " */
327 bitmap = &s_token[1];
328 len = strlen(bitmap) - 1;
329 s_token[len] = '\0';
330
331 Com_StripExtension(bitmap, ase.materials[ase.numMaterials].name, MAX_QPATH);
332 Verb_Printf(VERB_EXTRA, "ase material name: \'%s\'\n", ase.materials[ase.numMaterials].name);
333 }
334 }
335
ASE_KeyMATERIAL(const char * token)336 static void ASE_KeyMATERIAL (const char* token)
337 {
338 if (Q_streq(token, "*MAP_DIFFUSE"))
339 ASE_ParseBracedBlock(ASE_KeyMAP_DIFFUSE);
340 }
341
ASE_KeyMATERIAL_LIST(const char * token)342 static void ASE_KeyMATERIAL_LIST (const char* token)
343 {
344 if (Q_streq(token, "*MATERIAL_COUNT")) {
345 ASE_GetToken(false);
346 VERBOSE(("..num materials: %s\n", s_token));
347 if (atoi(s_token) > MAX_ASE_MATERIALS) {
348 Sys_Error("Too many materials!");
349 }
350 ase.numMaterials = 0;
351 } else if (Q_streq(token, "*MATERIAL")) {
352 VERBOSE(("..material %d ", ase.numMaterials));
353 ASE_ParseBracedBlock(ASE_KeyMATERIAL);
354 ase.numMaterials++;
355 }
356 }
357
ASE_KeyMESH_VERTEX_LIST(const char * token)358 static void ASE_KeyMESH_VERTEX_LIST (const char* token)
359 {
360 aseMesh_t* pMesh = ASE_GetCurrentMesh();
361
362 if (Q_streq(token, "*MESH_VERTEX")) {
363 ASE_GetToken(false); /* skip number */
364
365 ASE_GetToken(false);
366 pMesh->vertexes[pMesh->currentVertex].y = atof(s_token);
367
368 ASE_GetToken(false);
369 pMesh->vertexes[pMesh->currentVertex].x = -atof(s_token);
370
371 ASE_GetToken(false);
372 pMesh->vertexes[pMesh->currentVertex].z = atof(s_token);
373
374 pMesh->currentVertex++;
375
376 if (pMesh->currentVertex > pMesh->numVertexes)
377 Sys_Error("pMesh->currentVertex >= pMesh->numVertexes");
378 } else
379 Sys_Error("Unknown token '%s' while parsing MESH_VERTEX_LIST", token);
380 }
381
ASE_KeyMESH_FACE_LIST(const char * token)382 static void ASE_KeyMESH_FACE_LIST (const char* token)
383 {
384 aseMesh_t* pMesh = ASE_GetCurrentMesh();
385
386 if (Q_streq(token, "*MESH_FACE")) {
387 ASE_GetToken(false); /* skip face number */
388
389 ASE_GetToken(false); /* skip label */
390 ASE_GetToken(false); /* first vertex */
391 pMesh->faces[pMesh->currentFace][0] = atoi(s_token);
392
393 ASE_GetToken(false); /* skip label */
394 ASE_GetToken(false); /* second vertex */
395 pMesh->faces[pMesh->currentFace][2] = atoi(s_token);
396
397 ASE_GetToken(false); /* skip label */
398 ASE_GetToken(false); /* third vertex */
399 pMesh->faces[pMesh->currentFace][1] = atoi(s_token);
400
401 ASE_GetToken(true);
402
403 #if 0
404 if ((p = strstr(s_token, "*MESH_MTLID")) != 0) {
405 p += strlen("*MESH_MTLID") + 1;
406 mtlID = atoi(p);
407 } else {
408 Sys_Error("No *MESH_MTLID found for face!");
409 }
410 #endif
411
412 pMesh->currentFace++;
413 } else
414 Sys_Error("Unknown token '%s' while parsing MESH_FACE_LIST", token);
415 }
416
ASE_KeyTFACE_LIST(const char * token)417 static void ASE_KeyTFACE_LIST (const char* token)
418 {
419 aseMesh_t* pMesh = ASE_GetCurrentMesh();
420
421 if (Q_streq(token, "*MESH_TFACE")) {
422 int a, b, c;
423 aseFace_t* f;
424
425 ASE_GetToken(false);
426
427 ASE_GetToken(false);
428 a = atoi(s_token);
429 ASE_GetToken(false);
430 c = atoi(s_token);
431 ASE_GetToken(false);
432 b = atoi(s_token);
433
434 f = &pMesh->tfaces[pMesh->currentFace];
435 *f[0] = a;
436 *f[1] = b;
437 *f[2] = c;
438
439 pMesh->currentFace++;
440 } else
441 Sys_Error("Unknown token '%s' in MESH_TFACE", token);
442 }
443
ASE_KeyMESH_TVERTLIST(const char * token)444 static void ASE_KeyMESH_TVERTLIST (const char* token)
445 {
446 aseMesh_t* pMesh = ASE_GetCurrentMesh();
447
448 if (Q_streq(token, "*MESH_TVERT")) {
449 char u[80], v[80], w[80];
450
451 ASE_GetToken(false);
452
453 ASE_GetToken(false);
454 strcpy(u, s_token);
455
456 ASE_GetToken(false);
457 strcpy(v, s_token);
458
459 ASE_GetToken(false);
460 strcpy(w, s_token);
461
462 pMesh->tvertexes[pMesh->currentVertex].s = atof(u);
463 pMesh->tvertexes[pMesh->currentVertex].t = 1.0f - atof(v);
464
465 pMesh->currentVertex++;
466
467 if (pMesh->currentVertex > pMesh->numTVertexes) {
468 Sys_Error("pMesh->currentVertex > pMesh->numTVertexes");
469 }
470 } else
471 Sys_Error("Unknown token '%s' while parsing MESH_TVERTLIST", token);
472 }
473
ASE_KeyMESH(const char * token)474 static void ASE_KeyMESH (const char* token)
475 {
476 aseMesh_t* pMesh = ASE_GetCurrentMesh();
477
478 if (Q_streq(token, "*TIMEVALUE")) {
479 ASE_GetToken(false);
480
481 pMesh->timeValue = atoi(s_token);
482 VERBOSE((".....timevalue: %d\n", pMesh->timeValue));
483 } else if (Q_streq(token, "*MESH_NUMVERTEX")) {
484 ASE_GetToken(false);
485
486 pMesh->numVertexes = atoi(s_token);
487 VERBOSE((".....TIMEVALUE: %d\n", pMesh->timeValue));
488 VERBOSE((".....num vertexes: %d\n", pMesh->numVertexes));
489 } else if (Q_streq(token, "*MESH_NUMFACES")) {
490 ASE_GetToken(false);
491
492 pMesh->numFaces = atoi(s_token);
493 VERBOSE((".....num faces: %d\n", pMesh->numFaces));
494 } else if (Q_streq(token, "*MESH_NUMTVFACES")) {
495 ASE_GetToken(false);
496
497 if (atoi(s_token) != pMesh->numFaces)
498 Sys_Error("MESH_NUMTVFACES != MESH_NUMFACES");
499 } else if (Q_streq(token, "*MESH_NUMTVERTEX")) {
500 ASE_GetToken(false);
501
502 pMesh->numTVertexes = atoi(s_token);
503 VERBOSE((".....num tvertexes: %d\n", pMesh->numTVertexes));
504 } else if (Q_streq(token, "*MESH_VERTEX_LIST")) {
505 pMesh->vertexes = Mem_AllocTypeN(aseVertex_t, pMesh->numVertexes);
506 pMesh->currentVertex = 0;
507 VERBOSE((".....parsing MESH_VERTEX_LIST\n"));
508 ASE_ParseBracedBlock(ASE_KeyMESH_VERTEX_LIST);
509 } else if (Q_streq(token, "*MESH_TVERTLIST")) {
510 pMesh->currentVertex = 0;
511 pMesh->tvertexes = Mem_AllocTypeN(aseTVertex_t, pMesh->numTVertexes);
512 VERBOSE((".....parsing MESH_TVERTLIST\n"));
513 ASE_ParseBracedBlock(ASE_KeyMESH_TVERTLIST);
514 } else if (Q_streq(token, "*MESH_FACE_LIST")) {
515 pMesh->faces = Mem_AllocTypeN(aseFace_t, pMesh->numFaces);
516 pMesh->currentFace = 0;
517 VERBOSE((".....parsing MESH_FACE_LIST\n"));
518 ASE_ParseBracedBlock(ASE_KeyMESH_FACE_LIST);
519 } else if (Q_streq(token, "*MESH_TFACELIST")) {
520 pMesh->tfaces = Mem_AllocTypeN(aseFace_t, pMesh->numFaces);
521 pMesh->currentFace = 0;
522 VERBOSE((".....parsing MESH_TFACE_LIST\n"));
523 ASE_ParseBracedBlock(ASE_KeyTFACE_LIST);
524 } else if (Q_streq(token, "*MESH_NORMALS")) {
525 ASE_ParseBracedBlock(0);
526 }
527 }
528
ASE_KeyGEOMOBJECT(const char * token)529 static void ASE_KeyGEOMOBJECT (const char* token)
530 {
531 if (Q_streq(token, "*NODE_NAME")) {
532 char* name = ase.objects[ase.currentObject].name;
533
534 ASE_GetToken(true);
535 VERBOSE((" %s\n", s_token));
536 strcpy(ase.objects[ase.currentObject].name, s_token + 1);
537 if (strchr(ase.objects[ase.currentObject].name, '"'))
538 *strchr(ase.objects[ase.currentObject].name, '"') = 0;
539
540 if (strstr(name, "tag") == name) {
541 while (strchr(name, '_') != strrchr(name, '_')) {
542 *strrchr(name, '_') = 0;
543 }
544 while (strrchr(name, ' ')) {
545 *strrchr(name, ' ') = 0;
546 }
547 }
548 } else if (Q_streq(token, "*NODE_PARENT")) {
549 ASE_SkipRestOfLine();
550 }
551 /* ignore unused data blocks */
552 else if (Q_streq(token, "*NODE_TM") || Q_streq(token, "*TM_ANIMATION")) {
553 ASE_ParseBracedBlock(0);
554 }
555 /* ignore regular meshes that aren't part of animation */
556 else if (Q_streq(token, "*MESH")) {
557 #if 0
558 if (strstr(ase.objects[ase.currentObject].name, "tag_") == ase.objects[ase.currentObject].name) {
559 s_forceStaticMesh = true;
560 ASE_ParseBracedBlock(ASE_KeyMESH);
561 s_forceStaticMesh = false;
562 }
563 #endif
564 ASE_ParseBracedBlock(ASE_KeyMESH);
565 if (++ase.objects[ase.currentObject].anim.currentFrame == MAX_ASE_ANIMATION_FRAMES) {
566 Sys_Error("Too many animation frames");
567 }
568 ase.objects[ase.currentObject].anim.numFrames = ase.objects[ase.currentObject].anim.currentFrame;
569 ase.objects[ase.currentObject].numAnimations++;
570 #if 0
571 /* ignore meshes that aren't part of animations if this object isn't a a tag */
572 else {
573 ASE_ParseBracedBlock(0);
574 }
575 #endif
576 }
577 /* according to spec these are obsolete */
578 else if (Q_streq(token, "*MATERIAL_REF")) {
579 ASE_GetToken(false);
580
581 ase.objects[ase.currentObject].materialRef = atoi(s_token);
582 }
583 /* ignore sequences of animation frames */
584 else if (Q_streq(token, "*MESH_ANIMATION")) {
585 ASE_SkipEnclosingBraces();
586 }
587 /* skip unused info */
588 else if (Q_streq(token, "*PROP_MOTIONBLUR") || Q_streq(token, "*PROP_CASTSHADOW") || Q_streq(token, "*PROP_RECVSHADOW")) {
589 ASE_SkipRestOfLine();
590 }
591 }
592
ConcatenateObjects(aseGeomObject_t * pObjA,aseGeomObject_t * pObjB)593 static void ConcatenateObjects (aseGeomObject_t* pObjA, aseGeomObject_t* pObjB)
594 {
595 }
596
CollapseObjects(void)597 static void CollapseObjects (void)
598 {
599 int i;
600 int numObjects = ase.currentObject;
601
602 for (i = 0; i < numObjects; i++) {
603 int j;
604
605 /* skip tags */
606 if (strstr(ase.objects[i].name, "tag") == ase.objects[i].name)
607 continue;
608
609 if (!ase.objects[i].numAnimations)
610 continue;
611
612 for (j = i + 1; j < numObjects; j++) {
613 if (strstr(ase.objects[j].name, "tag") == ase.objects[j].name)
614 continue;
615
616 if (ase.objects[i].materialRef == ase.objects[j].materialRef)
617 if (ase.objects[j].numAnimations)
618 ConcatenateObjects(&ase.objects[i], &ase.objects[j]);
619 }
620 }
621 }
622
ASE_Process(void)623 static void ASE_Process (void)
624 {
625 while (ASE_GetToken(false)) {
626 if (Q_streq(s_token, "*3DSMAX_ASCIIEXPORT") || Q_streq(s_token, "*COMMENT")) {
627 ASE_SkipRestOfLine();
628 } else if (Q_streq(s_token, "*SCENE"))
629 ASE_SkipEnclosingBraces();
630 else if (Q_streq(s_token, "*MATERIAL_LIST")) {
631 VERBOSE(("MATERIAL_LIST\n"));
632
633 ASE_ParseBracedBlock(ASE_KeyMATERIAL_LIST);
634 } else if (Q_streq(s_token, "*GEOMOBJECT")) {
635 VERBOSE(("GEOMOBJECT"));
636
637 ASE_ParseBracedBlock(ASE_KeyGEOMOBJECT);
638
639 if (strstr(ase.objects[ase.currentObject].name, "Bip") ||
640 strstr(ase.objects[ase.currentObject].name, "ignore_")) {
641 ASE_FreeGeomObject(ase.currentObject);
642 VERBOSE(("(discarding BIP/ignore object)\n"));
643 } else {
644 if (++ase.currentObject == MAX_ASE_OBJECTS) {
645 Sys_Error("Too many GEOMOBJECTs");
646 }
647 }
648 } else if (s_token[0]) {
649 Com_Printf("Unknown token '%s'\n", s_token);
650 }
651 }
652
653 if (!ase.currentObject)
654 Sys_Error("No animation data!");
655
656 CollapseObjects();
657 }
658