1 /***********************************************************************
2 * z:/wata/src/a/csmash/loadparts.cpp
3 * $Id: loadparts.cpp,v 1.14 2003/11/19 16:49:31 nan Exp $
4 *
5 * Copyright by ESESoft.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer
16 * in the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * The name of the author may not be used to endorse or promote
20 * products derived from this software without specific prior written
21 * permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
24 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
27 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 ***********************************************************************/
36 #include "ttinc.h"
37
38 #include <iostream>
39 #include <string>
40 #include <map>
41 #include <list>
42 #include <algorithm>
43
44 #include "float"
45 #include "matrix"
46 #include "affine"
47
48 #include "LoadImage.h"
49 #include "parts.h"
50 #include "loadparts.h"
51
52 // for(;;) namescoping hack.
53 // VC++ 6 is not compliant with latest ANSI C++ (but VC++7 does).
54 #if defined(_MSC_VER) && (_MSC_VER <= 1200)
55 # define for if(0);else for
56 #endif
57 #define elif else if
58
59 #define BEGIN_ANONYMOUS namespace {
60 #define END_ANONYMOUS }
61
62 /***********************************************************************
63 * Local data and functions
64 ***********************************************************************/
65 BEGIN_ANONYMOUS
66
67 template <typename T>
between(const T & a,const T & x,const T & b)68 inline bool between(const T& a, const T& x, const T& b) {
69 return a <= x && x <= b;
70 }
71
clamp(long a,long x,long b)72 inline const long clamp(long a, long x, long b) {
73 if (a > x) return a;
74 elif (b < x) return b;
75 else return x;
76 }
77
streq(const char * a,const char * b)78 inline bool streq(const char *a, const char *b) {
79 return 0 == strcmp(a, b);
80 }
81
strieq(const char * a,const char * b)82 inline bool strieq(const char *a, const char *b) {
83 #ifdef _WIN32
84 return 0 == _stricmp(a, b);
85 #else
86 return 0 == strcasecmp(a, b);
87 #endif
88 }
89
90 struct auto_fp
91 {
92 FILE *fp;
93 bool needclose;
auto_fpauto_fp94 inline auto_fp(FILE *fp, bool b = true) : fp(fp), needclose(b) {}
~auto_fpauto_fp95 inline ~auto_fp() { if (needclose && fp) fclose(fp); }
operator FILE*auto_fp96 inline operator FILE*() { return fp; }
operator !auto_fp97 inline bool operator!() const { return !fp; }
98 };
99
loadAffine4F(const char * str,affine4F * pm)100 bool loadAffine4F(const char *str, affine4F *pm)
101 {
102 FILE *fp = fopen(str, "r");
103 if (!fp) return false;
104
105 affine4F &m = *pm;
106 m = affine4F(1);
107 const char *delim = " \t(,);\r\n";
108 char line[256];
109 int i = 0;
110 while (NULL != fgets(line, sizeof(line), fp)) {
111 const char *token = strtok(line, delim);
112 if (!token) continue;
113 do {
114 if (strieq("affine3", token)) continue;
115 Float f = (Float)strtod(token, NULL);
116 int x = i / 3;
117 int y = i % 3;
118 m[x][y] = f;
119 if (12 == ++i) break;
120 }
121 while ((token = strtok(NULL, delim)));
122 }
123 fclose(fp);
124 if (12 != i) return false;
125
126 return true;
127 }
128
129 END_ANONYMOUS
130 /***********************************************************************
131 * Class parts
132 ***********************************************************************/
133 // symbol table
134 #define SYM(A) { parts::sym_##A, #A }
135 static struct symtab_t {
136 parts::symbol_t sym;
137 const char *str;
138 } symtab[] = {
139 SYM(null), SYM(load), SYM(create), SYM(polyhedron),
140 SYM(anim), SYM(texture), SYM(body),
141 { parts::sym_unknown, NULL },
142 { parts::sym_unknown, "NanTheBLACK, our guru:-p" }
143 };
144 #undef SYM
145
146 // parts object store
147 static parts_map partsmap;
148
getsym(const char * str)149 parts::symbol_t parts::getsym(const char *str)
150 {
151 if (!str) return sym_unknown;
152 for (symtab_t *p = symtab; p->str; ++p) {
153 if (streq(p->str, str)) return p->sym;
154 }
155 return sym_unknown;
156 }
157
sym2str(parts::symbol_t sym)158 const char* parts::sym2str(parts::symbol_t sym)
159 {
160 for (symtab_t *p = symtab; p->str; ++p) {
161 if (p->sym == sym) return p->str;
162 }
163 return "sym_unknown";
164 }
165
getobject(const char * name)166 parts* parts::getobject(const char* name)
167 {
168 const parts_map::iterator i = partsmap.find(name);
169 if (partsmap.end() == i) return NULL;
170 else return i->second;
171 }
172
addobject(const char * name,parts * p)173 bool parts::addobject(const char *name, parts* p)
174 {
175 const parts_map::iterator i = partsmap.find(name);
176 if (partsmap.end() != i) return false;
177
178 p->name = name;
179 partsmap[name] = p;
180 return true;
181 }
182
delobject(const char * name)183 bool parts::delobject(const char *name)
184 {
185 parts_map::iterator i = partsmap.find(name);
186 if (partsmap.end() == i) return false;
187 delete i->second;
188 partsmap.erase(i);
189 return true;
190 }
191
clearobjects()192 void parts::clearobjects()
193 {
194 partsmap.clear();
195 }
196
realizeobjects()197 bool parts::realizeobjects()
198 {
199 bool r = true;
200 for (parts_map::iterator i = partsmap.begin(); i != partsmap.end(); ++i) {
201 r &= (i->second)->realize();
202 }
203 return r;
204 }
205
unrealizeobjects()206 void parts::unrealizeobjects()
207 {
208 for (parts_map::iterator i = partsmap.begin(); i != partsmap.end(); ++i) {
209 (i->second)->unrealize();
210 }
211 }
212
loadobjects(const char * str)213 bool parts::loadobjects(const char *str)
214 {
215 try {
216 loadfile(str);
217 }
218 catch (const error &e) {
219 printf("loadfile failed\n");
220 printf(e.what());
221 return false;
222 }
223 return true;
224 }
225
loadfile(const char * str)226 bool parts::loadfile(const char *str)
227 {
228 auto_fp fp(fopen(str, "r"));
229 if (!fp) return false;
230
231 int lineno = 0;
232 do {
233 const char *delim = " \t\r\n;";
234 char line[4096];
235 fgets(line, sizeof(line), fp);
236 if (feof((FILE*)fp)) break;
237 ++lineno;
238 int l = strlen(line);
239 int addline = 0;
240
241 while (l > 0 && (line[l-1] == '\r' || line[l-1] == '\n')) {
242 line[--l] = '\0';
243 }
244
245 while ('\\' == line[l-1]) {
246 // concat next line(s)
247 int bufsize = clamp(0U, sizeof(line)-l, sizeof(line)-1);
248 fgets(&line[l-2], bufsize, fp);
249 if (feof((FILE*)fp)) break;
250 l = strlen(line);
251 while (l > 0 && (line[l-1] == '\r' || line[l-1] == '\n')) {
252 line[--l] = '\0';
253 }
254 ++addline;
255 }
256
257 int argc = 0;
258 const char *argv[256];
259 const char *token = strtok(line, delim);
260 const int argcmax = sizeof(argv) / sizeof(const char*);
261 if (!token || '#' == *token) continue;
262 do {
263 argv[argc++] = token;
264 if (argcmax == argc) {
265 throw verror(lineno, "This line has %d or more arguments\n", argcmax);
266 }
267 } while ((token = strtok(NULL, delim)));
268 argv[argc] = NULL;
269 int optind = 0;
270
271 token = argv[optind++];
272 symbol_t sym = getsym(token);
273 switch (sym) {
274 case sym_load:
275 load_load(lineno, argc, argv, &optind); break;
276 case sym_create:
277 load_create(lineno, argc, argv, &optind); break;
278 default:
279 throw verror(lineno, "error unknown command %s\n", token);
280 }
281 lineno += addline;
282 } while (!ferror((FILE*)fp));
283
284 return true;
285 }
286
load_load(int lineno,int argc,const char * argv[],int * poptind)287 bool parts::load_load(int lineno, int argc, const char *argv[], int* poptind)
288 {
289 int& optind = *poptind;
290
291 const char *token = argv[optind++];
292 if (!token) {
293 throw verror(lineno, "error type not specified\n");
294 }
295 symbol_t sym = getsym(token);
296
297 const char *objectname = argv[optind++];
298 if (!objectname) {
299 throw verror(lineno, "object name is not specified\n");
300 }
301
302 const char *filename = argv[optind++];
303 switch (sym) {
304 case sym_texture: {
305 texture_parts* object = new texture_parts(objectname);
306 if (!addobject(objectname, object)) {
307 delete object;
308 throw verror(lineno, "%s is already loaded\n", objectname);
309 }
310 object->load(filename);
311 break;
312 } /* texture */
313
314 case sym_polyhedron: {
315 polyhedron_parts *object = new polyhedron_parts(objectname);
316 if (!addobject(objectname, object)) {
317 delete object;
318 throw verror(lineno, "%s is already loaded\n", objectname);
319 }
320 object->load(filename);
321 load_polyhedron(lineno, object, argc, argv, &optind);
322
323 break;
324 } /* polyhedron */
325
326 case sym_anim: {
327 anim_parts *object = new anim_parts(objectname);
328 if (!addobject(objectname, object)) {
329 delete object;
330 throw verror(lineno, "%s is already loaded\n", objectname);
331 }
332 object->load(filename);
333 load_anim(lineno, object, argc, argv, &optind);
334
335 break;
336 } /* anim */
337
338 default:
339 throw verror(lineno, "error unknown type(%s) specified\n", token);
340 }
341
342 return true;
343 }
344
load_polyhedron(int lineno,polyhedron_parts * object,int argc,const char * argv[],int * poptind)345 bool parts::load_polyhedron(int lineno, polyhedron_parts *object,
346 int argc, const char* argv[], int *poptind)
347 {
348 int &optind = *poptind;
349 const char *option;
350 while ((option = argv[optind++])) {
351 if ('-' != *option) {
352 throw verror(lineno, "unknown option %s\n", option);
353 }
354 const char *operand = argv[optind++];
355 if (!operand) {
356 throw verror(lineno, "no operadnd for %s\n", option);
357 }
358 switch (option[1]) {
359 case 'c': { /* colormap */
360 colormap cmap;
361 if (!cmap.load(operand)) {
362 throw verror(lineno, "could not load colormap %s\n", operand);
363 }
364 object->object->cmap = cmap;
365 break;
366 }
367 case 't': { /* texture */
368 parts *tex = getobject(operand);
369 if (!tex) {
370 throw verror(lineno, "texture %s not loaded\n", operand);
371 }
372 if (!object->assign(tex)) {
373 throw verror(lineno, "%s is not assignable\n", operand);
374 }
375 break;
376 }
377 case 'm': { /* matrix */
378 affine4F m;
379 if (!loadAffine4F(operand, &m)) {
380 throw verror(lineno, "matrix %s cannot be loaded\n", operand);
381 }
382 *object->object *= m;
383
384 break;
385 }
386 default:
387 throw verror(lineno, "unknown option %s\n", option);
388 }
389 }
390 // create normal vectors of polyhedron
391 object->object->getNormal();
392 return true;
393 }
394
load_anim(int lineno,anim_parts * object,int argc,const char * argv[],int * poptind)395 bool parts::load_anim(int lineno, anim_parts* object,
396 int argc, const char *argv[], int *poptind)
397 {
398 int& optind = *poptind;
399 int i = 0;
400 while (const char *name = argv[optind++]) {
401 if ('-' == *name) {
402 if (streq("-pre", name) || streq("-post", name)) {
403 affine4F m;
404 const char *fname = argv[optind++];
405 if (!fname || !loadAffine4F(fname, &m)) {
406 throw verror(lineno, "mat %s cannot be loaded\n", fname);
407 }
408 affineanim &anim = *object->object;
409 for (int i = 0; anim.numFrames > i; ++i) {
410 if (streq("-pre", name)) {
411 anim.matrices[i] = m * anim.matrices[i];
412 } else {
413 anim.matrices[i] *= m;
414 }
415 }
416 }
417 else {
418 throw verror(lineno, "unknown option %s\n", name);
419 }
420 } else {
421 parts *poly = getobject(name);
422 if (!poly) {
423 throw verror(lineno, "%s not loaded\n", name);
424 }
425 if (!object->assign(poly)) {
426 throw verror(lineno, "%s is not assignable\n", name);
427 }
428 ++i;
429 }
430 }
431 if (!i) {
432 printf("%d: %s is empty object\n", lineno, object->name.c_str());
433 }
434 return true;
435 }
436
load_create(int lineno,int argc,const char * argv[],int * poptind)437 bool parts::load_create(int lineno, int argc, const char *argv[], int *poptind)
438 {
439 int &optind = *poptind;
440 const char *token = argv[optind++];
441 if (!token) {
442 throw verror(lineno, "object type is not specified\n");
443 }
444 switch (getsym(token)) {
445 case sym_body: {
446 const char *objectname = argv[optind++];
447 if (!objectname) {
448 throw verror(lineno, "object name is not specified\n");
449 }
450 body_parts *object = new body_parts(objectname);
451 if (!addobject(objectname, object)) {
452 throw verror(lineno, "%s is already loaded\n", objectname);
453 }
454 int i = 0;
455 while ((token = argv[optind++])) {
456 parts* p = getobject(token);
457 if (!p) {
458 throw verror(lineno, "%s is not loaded\n", token);
459 }
460 object->assign(p);
461 ++i;
462 }
463 if (!i) {
464 printf("%d: %s is empty\n", lineno, objectname);
465 }
466 break;
467 }
468 default:
469 throw verror("type %s cannot be created\n", token);
470 }
471 return true;
472 }
473
474 /***********************************************************************
475 * Class texture_parts
476 ***********************************************************************/
load(const char * str)477 bool texture_parts::load(const char *str)
478 {
479 filename = str;
480 return true;
481 }
482
unrealize()483 void texture_parts::unrealize()
484 {
485 if (object) {
486 glDeleteTextures(1, &object);
487 object = 0;
488 }
489 }
490
realize()491 bool texture_parts::realize()
492 {
493 if (object) return true;
494
495 static int allowedsize[] = {
496 64, 128, 130, 256, 512, 0
497 };
498
499 ImageData img;
500 bool loaded;
501
502 loaded = img.LoadFile(filename.c_str());
503
504 if (!loaded) {
505 throw verror("could not load texture %s\n", filename.c_str());
506 }
507 int width = img.GetWidth();
508 int height = img.GetHeight();
509 int i, j;
510 for (i = 0; 0 != allowedsize[i]; ++i) {
511 if (width == allowedsize[i]) break;
512 }
513 for (j = 0; 0 != allowedsize[i]; ++j) {
514 if (height == allowedsize[i]) break;
515 }
516 if (0 == allowedsize[i] || 0 == allowedsize[j]) {
517 throw verror("texture %s has illegal size(%d,%d)\n",
518 filename.c_str(), width, height);
519 }
520
521 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
522 glGenTextures(1, &object);
523 glBindTexture(GL_TEXTURE_2D, object);
524 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
525 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
526 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
527 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
528 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
529 glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0,
530 GL_RGBA, GL_UNSIGNED_BYTE, img.GetImage());
531
532 if (0 == object) {
533 char buf[256];
534 snprintf(buf, sizeof(buf), "texture %s cannot be realized\n",
535 filename.c_str());
536 printf(buf);
537 throw error(buf);
538 return false;
539 } else {
540 return true;
541 }
542 }
543
544 /***********************************************************************
545 * Class polyhedron_parts
546 ***********************************************************************/
load(const char * str)547 bool polyhedron_parts::load(const char *str)
548 {
549 object = new polyhedron(str);
550 if (!object->points) {
551 delete object;
552 object = NULL;
553 throw verror("polyhedron %s cannot be loaded\n", str);
554 }
555 return true;
556 }
557
assign(parts * a)558 bool polyhedron_parts::assign(parts* a)
559 {
560 switch (a->type()) {
561 case sym_texture:
562 tex = reinterpret_cast<texture_parts*>(a);
563 break;
564 default:
565 throw verror("%s(%s) cannot be assigned to polyhedron(%s)\n",
566 a->name.c_str(), a->typestr(), name.c_str());
567 }
568 return true;
569 }
570
render() const571 void polyhedron_parts::render() const
572 {
573 float NanTheBLACK[4] = { 0, 0, 0, 1 };
574 float ManOfVirtue[4] = { 1, 1, 1, 1 };
575
576 polyhedron &poly = *object;
577 if (tex && poly.texcoord && tex->object) {
578 glEnable(GL_TEXTURE_2D);
579 glBindTexture(GL_TEXTURE_2D, tex->object);
580 // glColor4fv(ManOfVirtue);
581 for (int i = 0; poly.numPolygons > i; ++i) {
582 const polygon &face = poly.getPolygon(i);
583 glBegin(face.glBeginSize());
584 for (int j = 0; face.size > j; ++j) {
585 poly.cmap[face.c()].glBind();
586 glNormal3fv((float*)&face.rn(j));
587 glTexCoord2fv((float*)&face.rst(j));
588 glVertex3fv((float*)&face.rv(j));
589 }
590 glEnd();
591 }
592 } else {
593 glDisable(GL_TEXTURE_2D);
594 for (int i = 0; poly.numPolygons > i; ++i) {
595 const polygon &face = poly.getPolygon(i);
596 glBegin(face.glBeginSize());
597 for (int j = 0; face.size > j; ++j) {
598 poly.cmap[face.c()].glBind();
599 glNormal3fv((float*)&face.rn(j));
600 glVertex3fv((float*)&face.rv(j));
601 }
602 glEnd();
603 }
604 }
605 }
606
renderWire(const vector3F & origin) const607 void polyhedron_parts::renderWire(const vector3F& origin) const
608 {
609 polyhedron &poly = *object;
610
611 glBegin(GL_LINES);
612 for (int i = 0; poly.numEdges > i; ++i) {
613 int p0 = poly.edges[i].p0;
614 int p1 = poly.edges[i].p1;
615 bool draw = false;
616 if (p1 >= 0) {
617 // Render if one polygon on the side of edge is visible
618 // while other side is not visible.
619 vector3F v = poly.points[poly.edges[i].v0] - origin;
620 Float i0 = v * poly.planeNormal[p0];
621 Float i1 = v * poly.planeNormal[p1];
622 if (i0 * i1 <= 0) draw = true;
623 } else {
624 // This edge has a polygon only on one side.
625 draw = true;
626 }
627 if (draw) {
628 vector3F &v0 = poly.points[poly.edges[i].v0];
629 vector3F &v1 = poly.points[poly.edges[i].v1];
630 glVertex3fv((float*)&v0);
631 glVertex3fv((float*)&v1);
632 }
633 }
634 glEnd();
635 }
636 /***********************************************************************
637 * Class anim_parts
638 ***********************************************************************/
load(const char * str)639 bool anim_parts::load(const char *str)
640 {
641 object = new affineanim(str);
642 if (!object->matrices) {
643 throw verror("could not load anim %s\n", str);
644 }
645 return true;
646 }
647
assign(parts * a)648 bool anim_parts::assign(parts* a)
649 {
650 switch (a->type()) {
651 case sym_polyhedron:
652 poly.push_back(reinterpret_cast<polyhedron_parts*>(a));
653 break;
654 default:
655 throw verror("%s(%s) cannot be assigned to anim(%s)\n",
656 a->name.c_str(), a->typestr(), name.c_str());
657 }
658 return true;
659 }
660
render(int frame) const661 void anim_parts::render(int frame) const
662 {
663 affineanim &anim = *object;
664 glPushMatrix();
665 #ifdef CHIYO
666 glTranslatef(0,0,0.1F); // Her shoes go underground without this:-)
667 #endif
668 glMultMatrixf((float*)&anim[frame]);
669 for (std::list<polyhedron_parts*>::const_iterator i = poly.begin();
670 poly.end() != i; ++i) {
671 (*i)->render();
672 }
673 glPopMatrix();
674 }
675
renderWire(int frame) const676 void anim_parts::renderWire(int frame) const
677 {
678 affineanim &anim = *object;
679 glPushMatrix();
680 #ifdef CHIYO
681 glTranslatef(0,0,0.1F); // Her shoes go underground without this:-)
682 #endif
683 glMultMatrixf((float*)&anim[frame]);
684
685 affine4F t;
686 glGetFloatv(GL_MODELVIEW_MATRIX, (float*)&t);
687 vector3F origin = vector3F(0) * ~t;
688
689 for (std::list<polyhedron_parts*>::const_iterator i = poly.begin();
690 poly.end() != i; ++i) {
691 (*i)->renderWire(origin);
692 }
693 glPopMatrix();
694 }
695
696 /***********************************************************************
697 * Class body_parts
698 ***********************************************************************/
assign(parts * a)699 bool body_parts::assign(parts* a)
700 {
701 switch (a->type()) {
702 case sym_anim:
703 object.push_back(reinterpret_cast<anim_parts*>(a));
704 break;
705 default:
706 throw verror("%s(%s) cannot be assigned to body (%s)\n",
707 a->name.c_str(), a->typestr(), name.c_str());
708 }
709 return true;
710 }
711
render(int frame) const712 void body_parts::render(int frame) const
713 {
714 for (std::list<anim_parts*>::const_iterator i = object.begin();
715 object.end() != i; ++i) {
716 (*i)->render(frame);
717 }
718 }
719
renderWire(int frame) const720 void body_parts::renderWire(int frame) const
721 {
722 for (std::list<anim_parts*>::const_iterator i = object.begin();
723 object.end() != i; ++i) {
724 (*i)->renderWire(frame);
725 }
726 }
727
728 /***********************************************************************
729 * END OF loadparts.cpp
730 ***********************************************************************/
731