1 // obj.cxx -- routines to handle "sorta" WaveFront .obj format files.
3 // Written by Curtis Olson, started October 1997.
5 // Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #ifdef FG_MATH_EXCEPTION_CLASH
41 // #if defined ( __sun__ )
42 // extern "C" void *memmove(void *, const void *, size_t);
43 // extern "C" void *memset(void *, int, size_t);
46 #include <Include/compiler.h>
50 #include <ctype.h> // isdigit()
52 #include <Debug/logstream.hxx>
53 #include <Misc/fgstream.hxx>
54 #include <Include/fg_constants.h>
55 #include <Main/options.hxx>
56 #include <Math/mat3.h>
57 #include <Math/fg_random.h>
58 #include <Math/point3d.hxx>
59 #include <Math/polar3d.hxx>
60 #include <Misc/stopwatch.hxx>
61 #include <Scenery/tileentry.hxx>
63 #include "materialmgr.hxx"
69 static double normals[FG_MAX_NODES][3];
70 static double tex_coords[FG_MAX_NODES*3][3];
73 // given three points defining a triangle, calculate the normal
74 static void calc_normal(Point3D p1, Point3D p2,
75 Point3D p3, double normal[3])
80 v1[0] = p2[0] - p1[0]; v1[1] = p2[1] - p1[1]; v1[2] = p2[2] - p1[2];
81 v2[0] = p3[0] - p1[0]; v2[1] = p3[1] - p1[1]; v2[2] = p3[2] - p1[2];
83 MAT3cross_product(normal, v1, v2);
84 MAT3_NORMALIZE_VEC(normal,temp);
86 // fgPrintf( FG_TERRAIN, FG_DEBUG, " Normal = %.2f %.2f %.2f\n",
87 // normal[0], normal[1], normal[2]);
91 #define FG_TEX_CONSTANT 69.0
94 // Calculate texture coordinates for a given point.
95 static Point3D calc_tex_coords(const Point3D& node, const Point3D& ref) {
98 // double tmplon, tmplat;
100 // cout << "-> " << node[0] << " " << node[1] << " " << node[2] << endl;
101 // cout << "-> " << ref.x() << " " << ref.y() << " " << ref.z() << endl;
103 cp = Point3D( node[0] + ref.x(),
107 pp = fgCartToPolar3d(cp);
109 // tmplon = pp.lon() * RAD_TO_DEG;
110 // tmplat = pp.lat() * RAD_TO_DEG;
111 // cout << tmplon << " " << tmplat << endl;
113 pp.setx( fmod(RAD_TO_DEG * FG_TEX_CONSTANT * pp.x(), 11.0) );
114 pp.sety( fmod(RAD_TO_DEG * FG_TEX_CONSTANT * pp.y(), 11.0) );
116 if ( pp.x() < 0.0 ) {
117 pp.setx( pp.x() + 11.0 );
120 if ( pp.y() < 0.0 ) {
121 pp.sety( pp.y() + 11.0 );
124 // cout << pp << endl;
130 // Load a .obj file and build the GL fragment list
131 int fgObjLoad( const string& path, FGTileEntry *t) {
134 double approx_normal[3], normal[3] /*, scale = 0.0 */;
135 // double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin;
136 // GLfloat sgenparams[] = { 1.0, 0.0, 0.0, 0.0 };
137 GLint display_list = 0;
139 int in_fragment = 0, in_faces = 0, vncount, vtcount;
140 int n1 = 0, n2 = 0, n3 = 0, n4 = 0;
142 int last1 = 0, last2 = 0, odd = 0;
146 double tex_width = 1000.0, tex_height = 1000.0;
148 // printf("loading %s\n", path.c_str() );
150 // Attempt to open "path.gz" or "path"
151 fg_gzifstream in( path );
152 if ( ! in.is_open() ) {
153 FG_LOG( FG_TERRAIN, FG_ALERT, "Cannot open file: " << path );
157 shading = current_options.get_shading();
163 t->bounding_radius = 0.0;
169 // ignore initial comments and blank lines. (priming the pump)
170 // in >> skipcomment;
173 while ( ! in.eof() ) {
183 if ( in.get( c ) && c == '#' ) {
184 // process a comment line
186 // getline( in, line );
187 // cout << "comment = " << line << endl;
191 if ( token == "gbs" ) {
192 // reference point (center offset)
193 in >> t->center >> t->bounding_radius;
195 // cout << "center = " << center
196 // << " radius = " << t->bounding_radius << endl;
197 } else if ( token == "bs" ) {
198 // reference point (center offset)
199 in >> fragment.center;
200 in >> fragment.bounding_radius;
202 // cout << "center = " << fragment.center
203 // << " radius = " << fragment.bounding_radius << endl;
204 } else if ( token == "usemtl" ) {
205 // material property specification
207 // series of individual triangles
212 // this also signals the start of a new fragment
214 // close out the previous structure and start the next
216 // printf("xglEnd(); xglEndList();\n");
219 fragment.display_list = display_list;
221 // push this fragment onto the tile's object list
222 t->fragment_list.push_back(fragment);
227 // printf("start of fragment (usemtl)\n");
229 display_list = xglGenLists(1);
230 xglNewList(display_list, GL_COMPILE);
231 // printf("xglGenLists(); xglNewList();\n");
234 // reset the existing face list
235 // printf("cleaning a fragment with %d faces\n",
236 // fragment.faces.size());
239 // scan the material line
242 fragment.tile_ptr = t;
244 // find this material in the properties list
245 if ( ! material_mgr.find( material, fragment.material_ptr )) {
246 FG_LOG( FG_TERRAIN, FG_ALERT,
247 "Ack! unknown usemtl name = " << material
251 // set the texture width and height values for this
253 FGMaterial m = fragment.material_ptr->get_m();
254 tex_width = m.get_xsize();
255 tex_height = m.get_ysize();
256 // cout << "(w) = " << tex_width << " (h) = "
257 // << tex_width << endl;
259 // initialize the fragment transformation matrix
261 for ( i = 0; i < 16; i++ ) {
262 fragment.matrix[i] = 0.0;
264 fragment.matrix[0] = fragment.matrix[5] =
265 fragment.matrix[10] = fragment.matrix[15] = 1.0;
268 // unknown comment, just gobble the input untill the
278 // cout << "token = " << token << endl;
280 if ( token == "vn" ) {
282 if ( vncount < FG_MAX_NODES ) {
283 in >> normals[vncount][0]
284 >> normals[vncount][1]
285 >> normals[vncount][2];
288 FG_LOG( FG_TERRAIN, FG_ALERT,
289 "Read too many vertex normals in " << path
290 << " ... dying :-(" );
293 } else if ( token == "vt" ) {
294 // vertex texture coordinate
295 if ( vtcount < FG_MAX_NODES*3 ) {
296 in >> tex_coords[vtcount][0]
297 >> tex_coords[vtcount][1];
300 FG_LOG( FG_TERRAIN, FG_ALERT,
301 "Read too many vertex texture coords in " << path
306 } else if ( token == "v" ) {
308 if ( t->ncount < FG_MAX_NODES ) {
309 /* in >> nodes[t->ncount][0]
310 >> nodes[t->ncount][1]
311 >> nodes[t->ncount][2]; */
313 nodes.push_back(node);
316 FG_LOG( FG_TERRAIN, FG_ALERT,
317 "Read too many nodes in " << path
318 << " ... dying :-(");
321 } else if ( token == "t" ) {
322 // start a new triangle strip
324 n1 = n2 = n3 = n4 = 0;
326 // fgPrintf( FG_TERRAIN, FG_DEBUG,
327 // " new tri strip = %s", line);
328 in >> n1 >> n2 >> n3;
329 fragment.add_face(n1, n2, n3);
331 // fgPrintf( FG_TERRAIN, FG_DEBUG, "(t) = ");
333 xglBegin(GL_TRIANGLE_STRIP);
334 // printf("xglBegin(tristrip) %d %d %d\n", n1, n2, n3);
340 // Shading model is "GL_SMOOTH" so use precalculated
341 // (averaged) normals
342 // MAT3_SCALE_VEC(normal, normals[n1], scale);
343 xglNormal3dv(normal);
344 pp = calc_tex_coords(nodes[n1], center);
345 xglTexCoord2f(pp.lon(), pp.lat());
346 xglVertex3dv(nodes[n1].get_n());
348 // MAT3_SCALE_VEC(normal, normals[n2], scale);
349 xglNormal3dv(normal);
350 pp = calc_tex_coords(nodes[n2], center);
351 xglTexCoord2f(pp.lon(), pp.lat());
352 xglVertex3dv(nodes[n2].get_n());
354 // MAT3_SCALE_VEC(normal, normals[n3], scale);
355 xglNormal3dv(normal);
356 pp = calc_tex_coords(nodes[n3], center);
357 xglTexCoord2f(pp.lon(), pp.lat());
358 xglVertex3dv(nodes[n3].get_n());
360 // Shading model is "GL_FLAT" so calculate per face
361 // normals on the fly.
363 calc_normal(nodes[n1], nodes[n2],
364 nodes[n3], approx_normal);
366 calc_normal(nodes[n2], nodes[n1],
367 nodes[n3], approx_normal);
369 // MAT3_SCALE_VEC(normal, approx_normal, scale);
370 xglNormal3dv(normal);
372 pp = calc_tex_coords(nodes[n1], center);
373 xglTexCoord2f(pp.lon(), pp.lat());
374 xglVertex3dv(nodes[n1].get_n());
376 pp = calc_tex_coords(nodes[n2], center);
377 xglTexCoord2f(pp.lon(), pp.lat());
378 xglVertex3dv(nodes[n2].get_n());
380 pp = calc_tex_coords(nodes[n3], center);
381 xglTexCoord2f(pp.lon(), pp.lat());
382 xglVertex3dv(nodes[n3].get_n());
384 // printf("some normals, texcoords, and vertices\n");
390 // There can be three or four values
392 while ( in.get(c) ) {
394 break; // only the one
404 fragment.add_face(n3, n2, n4);
407 // Shading model is "GL_SMOOTH"
408 // MAT3_SCALE_VEC(normal, normals[n4], scale);
410 // Shading model is "GL_FLAT"
411 calc_normal(nodes[n3], nodes[n2], nodes[n4],
413 // MAT3_SCALE_VEC(normal, approx_normal, scale);
415 xglNormal3dv(normal);
416 pp = calc_tex_coords(nodes[n4], center);
417 xglTexCoord2f(pp.lon(), pp.lat());
418 xglVertex3dv(nodes[n4].get_n());
423 // printf("a normal, texcoord, and vertex (4th)\n");
425 } else if ( token == "tf" ) {
427 // fgPrintf( FG_TERRAIN, FG_DEBUG, "new fan");
429 xglBegin(GL_TRIANGLE_FAN);
432 xglNormal3dv(normals[n1]);
433 if ( in.get( c ) && c == '/' ) {
435 pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
436 pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
439 pp = calc_tex_coords(nodes[n1], center);
441 xglTexCoord2f(pp.x(), pp.y());
442 xglVertex3dv(nodes[n1].get_n());
445 xglNormal3dv(normals[n2]);
446 if ( in.get( c ) && c == '/' ) {
448 pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
449 pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
452 pp = calc_tex_coords(nodes[n2], center);
454 xglTexCoord2f(pp.x(), pp.y());
455 xglVertex3dv(nodes[n2].get_n());
457 // read all subsequent numbers until next thing isn't a number
468 if ( ! isdigit(c) || in.eof() ) {
473 // cout << " triangle = "
474 // << n1 << "," << n2 << "," << n3
476 xglNormal3dv(normals[n3]);
477 if ( in.get( c ) && c == '/' ) {
479 pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
480 pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
483 pp = calc_tex_coords(nodes[n3], center);
485 xglTexCoord2f(pp.x(), pp.y());
486 xglVertex3dv(nodes[n3].get_n());
488 fragment.add_face(n1, n2, n3);
493 } else if ( token == "f" ) {
497 xglBegin(GL_TRIANGLES);
498 // printf("xglBegin(triangles)\n");
502 // fgPrintf( FG_TERRAIN, FG_DEBUG, "new triangle = %s", line);*/
503 in >> n1 >> n2 >> n3;
504 fragment.add_face(n1, n2, n3);
506 // xglNormal3d(normals[n1][0], normals[n1][1], normals[n1][2]);
507 xglNormal3dv(normals[n1]);
508 pp = calc_tex_coords(nodes[n1], center);
509 xglTexCoord2f(pp.lon(), pp.lat());
510 xglVertex3dv(nodes[n1].get_n());
512 xglNormal3dv(normals[n2]);
513 pp = calc_tex_coords(nodes[n2], center);
514 xglTexCoord2f(pp.lon(), pp.lat());
515 xglVertex3dv(nodes[n2].get_n());
517 xglNormal3dv(normals[n3]);
518 pp = calc_tex_coords(nodes[n3], center);
519 xglTexCoord2f(pp.lon(), pp.lat());
520 xglVertex3dv(nodes[n3].get_n());
521 // printf("some normals, texcoords, and vertices (tris)\n");
522 } else if ( token == "q" ) {
523 // continue a triangle strip
526 // fgPrintf( FG_TERRAIN, FG_DEBUG, "continued tri strip = %s ",
530 // There can be one or two values
532 while ( in.get(c) ) {
534 break; // only the one
543 // fgPrintf( FG_TERRAIN, FG_DEBUG, "read %d %d\n", n1, n2);
546 fragment.add_face(last1, last2, n1);
548 fragment.add_face(last2, last1, n1);
552 // Shading model is "GL_SMOOTH"
553 // MAT3_SCALE_VEC(normal, normals[n1], scale);
555 // Shading model is "GL_FLAT"
557 calc_normal(nodes[last1], nodes[last2],
558 nodes[n1], approx_normal);
560 calc_normal(nodes[last2], nodes[last1],
561 nodes[n1], approx_normal);
563 // MAT3_SCALE_VEC(normal, approx_normal, scale);
565 xglNormal3dv(normal);
567 pp = calc_tex_coords(nodes[n1], center);
568 xglTexCoord2f(pp.lon(), pp.lat());
569 xglVertex3dv(nodes[n1].get_n());
570 // printf("a normal, texcoord, and vertex (4th)\n");
577 // fgPrintf( FG_TERRAIN, FG_DEBUG, " (cont)\n");
580 fragment.add_face(last1, last2, n2);
582 fragment.add_face(last2, last1, n2);
586 // Shading model is "GL_SMOOTH"
587 // MAT3_SCALE_VEC(normal, normals[n2], scale);
589 // Shading model is "GL_FLAT"
591 calc_normal(nodes[last1], nodes[last2],
592 nodes[n2], approx_normal);
594 calc_normal(nodes[last2], nodes[last1],
595 nodes[n2], approx_normal);
597 // MAT3_SCALE_VEC(normal, approx_normal, scale);
599 xglNormal3dv(normal);
601 pp = calc_tex_coords(nodes[n2], center);
602 xglTexCoord2f(pp.lon(), pp.lat());
603 xglVertex3dv(nodes[n2].get_n());
604 // printf("a normal, texcoord, and vertex (4th)\n");
611 FG_LOG( FG_TERRAIN, FG_WARN, "Unknown token in "
612 << path << " = " << token );
615 // eat white space before start of while loop so if we are
616 // done with useful input it is noticed before hand.
626 // close out the previous structure and start the next
629 // printf("xglEnd(); xglEndList();\n");
632 fragment.display_list = display_list;
634 // push this fragment onto the tile's object list
635 t->fragment_list.push_back(fragment);
639 // Draw normal vectors (for visually verifying normals)
641 xglColor3f(0.0, 0.0, 0.0);
642 for ( i = 0; i < t->ncount; i++ ) {
643 xglVertex3d(nodes[i][0],
646 xglVertex3d(nodes[i][0] + 500*normals[i][0],
647 nodes[i][1] + 500*normals[i][1],
648 nodes[i][2] + 500*normals[i][2]);
656 FG_LOG( FG_TERRAIN, FG_INFO,
657 "Loaded " << path << " in "
658 << stopwatch.elapsedSeconds() << " seconds" );