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.
37 // #if defined ( __sun__ )
38 // extern "C" void *memmove(void *, const void *, size_t);
39 // extern "C" void *memset(void *, int, size_t);
42 #include <Include/compiler.h>
46 #include <ctype.h> // isdigit()
48 #include <Debug/logstream.hxx>
49 #include <Misc/fgstream.hxx>
50 #include <Include/fg_constants.h>
51 #include <Main/options.hxx>
52 #include <Math/mat3.h>
53 #include <Math/fg_random.h>
54 #include <Math/point3d.hxx>
55 #include <Math/polar3d.hxx>
56 #include <Misc/stopwatch.hxx>
57 #include <Scenery/tile.hxx>
59 #include "material.hxx"
65 static double normals[MAX_NODES][3];
68 // given three points defining a triangle, calculate the normal
69 static void calc_normal(double p1[3], double p2[3],
70 double p3[3], double normal[3])
75 v1[0] = p2[0] - p1[0]; v1[1] = p2[1] - p1[1]; v1[2] = p2[2] - p1[2];
76 v2[0] = p3[0] - p1[0]; v2[1] = p3[1] - p1[1]; v2[2] = p3[2] - p1[2];
78 MAT3cross_product(normal, v1, v2);
79 MAT3_NORMALIZE_VEC(normal,temp);
81 // fgPrintf( FG_TERRAIN, FG_DEBUG, " Normal = %.2f %.2f %.2f\n",
82 // normal[0], normal[1], normal[2]);
86 #define FG_TEX_CONSTANT 69.0
89 // Calculate texture coordinates for a given point.
90 static Point3D calc_tex_coords(double *node, const Point3D& ref) {
93 // double tmplon, tmplat;
95 // cout << "-> " << node[0] << " " << node[1] << " " << node[2] << endl;
96 // cout << "-> " << ref.x() << " " << ref.y() << " " << ref.z() << endl;
98 cp = Point3D( node[0] + ref.x(),
102 pp = fgCartToPolar3d(cp);
104 // tmplon = pp.lon() * RAD_TO_DEG;
105 // tmplat = pp.lat() * RAD_TO_DEG;
106 // cout << tmplon << " " << tmplat << endl;
108 pp.setx( fmod(RAD_TO_DEG * FG_TEX_CONSTANT * pp.x(), 11.0) );
109 pp.sety( fmod(RAD_TO_DEG * FG_TEX_CONSTANT * pp.y(), 11.0) );
111 if ( pp.x() < 0.0 ) {
112 pp.setx( pp.x() + 11.0 );
115 if ( pp.y() < 0.0 ) {
116 pp.sety( pp.y() + 11.0 );
119 // cout << pp << endl;
125 // Load a .obj file and build the GL fragment list
126 int fgObjLoad( const string& path, fgTILE *t) {
129 double approx_normal[3], normal[3] /*, scale = 0.0 */;
130 // double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin;
131 // GLfloat sgenparams[] = { 1.0, 0.0, 0.0, 0.0 };
132 GLint display_list = 0;
134 int in_fragment = 0, in_faces = 0, vncount;
135 int n1 = 0, n2 = 0, n3 = 0, n4 = 0;
136 int last1 = 0, last2 = 0, odd = 0;
140 // printf("loading %s\n", path.c_str() );
142 // Attempt to open "path.gz" or "path"
143 fg_gzifstream in( path );
145 FG_LOG( FG_TERRAIN, FG_ALERT, "Cannot open file: " << path );
149 shading = current_options.get_shading();
154 t->bounding_radius = 0.0;
161 // ignore initial comments and blank lines. (priming the pump)
162 // in >> skipcomment;
165 while ( ! in.eof() ) {
171 if ( in.get( c ) && c == '#' ) {
172 // process a comment line
174 // getline( in, line );
175 // cout << "comment = " << line << endl;
179 if ( token == "gbs" ) {
180 // reference point (center offset)
181 in >> t->center >> t->bounding_radius;
183 // cout << "center = " << center
184 // << " radius = " << t->bounding_radius << endl;
185 } else if ( token == "bs" ) {
186 // reference point (center offset)
187 in >> fragment.center;
188 in >> fragment.bounding_radius;
190 // cout << "center = " << fragment.center
191 // << " radius = " << fragment.bounding_radius << endl;
192 } else if ( token == "usemtl" ) {
193 // material property specification
195 // series of individual triangles
200 // this also signals the start of a new fragment
202 // close out the previous structure and start the next
204 // printf("xglEnd(); xglEndList();\n");
207 fragment.display_list = display_list;
209 // push this fragment onto the tile's object list
210 t->fragment_list.push_back(fragment);
215 // printf("start of fragment (usemtl)\n");
217 display_list = xglGenLists(1);
218 xglNewList(display_list, GL_COMPILE);
219 // printf("xglGenLists(); xglNewList();\n");
222 // reset the existing face list
223 // printf("cleaning a fragment with %d faces\n",
224 // fragment.faces.size());
227 // scan the material line
230 fragment.tile_ptr = t;
232 // find this material in the properties list
233 if ( ! material_mgr.find( material, fragment.material_ptr )) {
234 FG_LOG( FG_TERRAIN, FG_ALERT,
235 "Ack! unknown usemtl name = " << material
239 // initialize the fragment transformation matrix
241 for ( i = 0; i < 16; i++ ) {
242 fragment.matrix[i] = 0.0;
244 fragment.matrix[0] = fragment.matrix[5] =
245 fragment.matrix[10] = fragment.matrix[15] = 1.0;
248 // unknown comment, just gobble the input untill the
258 // cout << "token = " << token << endl;
260 if ( token == "vn" ) {
262 if ( vncount < MAX_NODES ) {
263 in >> normals[vncount][0]
264 >> normals[vncount][1]
265 >> normals[vncount][2];
268 FG_LOG( FG_TERRAIN, FG_ALERT,
269 "Read too many vertex normals ... dying :-(" );
272 } else if ( token == "v" ) {
274 if ( t->ncount < MAX_NODES ) {
275 in >> t->nodes[t->ncount][0]
276 >> t->nodes[t->ncount][1]
277 >> t->nodes[t->ncount][2];
280 FG_LOG( FG_TERRAIN, FG_ALERT,
281 "Read too many nodes ... dying :-(");
284 } else if ( token == "t" ) {
285 // start a new triangle strip
287 n1 = n2 = n3 = n4 = 0;
289 // fgPrintf( FG_TERRAIN, FG_DEBUG,
290 // " new tri strip = %s", line);
291 in >> n1 >> n2 >> n3;
292 fragment.add_face(n1, n2, n3);
294 // fgPrintf( FG_TERRAIN, FG_DEBUG, "(t) = ");
296 xglBegin(GL_TRIANGLE_STRIP);
297 // printf("xglBegin(tristrip) %d %d %d\n", n1, n2, n3);
303 // Shading model is "GL_SMOOTH" so use precalculated
304 // (averaged) normals
305 // MAT3_SCALE_VEC(normal, normals[n1], scale);
306 xglNormal3dv(normal);
307 pp = calc_tex_coords(nodes[n1], center);
308 xglTexCoord2f(pp.lon(), pp.lat());
309 xglVertex3dv(nodes[n1]);
311 // MAT3_SCALE_VEC(normal, normals[n2], scale);
312 xglNormal3dv(normal);
313 pp = calc_tex_coords(nodes[n2], center);
314 xglTexCoord2f(pp.lon(), pp.lat());
315 xglVertex3dv(nodes[n2]);
317 // MAT3_SCALE_VEC(normal, normals[n3], scale);
318 xglNormal3dv(normal);
319 pp = calc_tex_coords(nodes[n3], center);
320 xglTexCoord2f(pp.lon(), pp.lat());
321 xglVertex3dv(nodes[n3]);
323 // Shading model is "GL_FLAT" so calculate per face
324 // normals on the fly.
326 calc_normal(nodes[n1], nodes[n2],
327 nodes[n3], approx_normal);
329 calc_normal(nodes[n2], nodes[n1],
330 nodes[n3], approx_normal);
332 // MAT3_SCALE_VEC(normal, approx_normal, scale);
333 xglNormal3dv(normal);
335 pp = calc_tex_coords(nodes[n1], center);
336 xglTexCoord2f(pp.lon(), pp.lat());
337 xglVertex3dv(nodes[n1]);
339 pp = calc_tex_coords(nodes[n2], center);
340 xglTexCoord2f(pp.lon(), pp.lat());
341 xglVertex3dv(nodes[n2]);
343 pp = calc_tex_coords(nodes[n3], center);
344 xglTexCoord2f(pp.lon(), pp.lat());
345 xglVertex3dv(nodes[n3]);
347 // printf("some normals, texcoords, and vertices\n");
353 // There can be three or four values
355 while ( in.get(c) ) {
357 break; // only the one
367 fragment.add_face(n3, n2, n4);
370 // Shading model is "GL_SMOOTH"
371 // MAT3_SCALE_VEC(normal, normals[n4], scale);
373 // Shading model is "GL_FLAT"
374 calc_normal(nodes[n3], nodes[n2], nodes[n4],
376 // MAT3_SCALE_VEC(normal, approx_normal, scale);
378 xglNormal3dv(normal);
379 pp = calc_tex_coords(nodes[n4], center);
380 xglTexCoord2f(pp.lon(), pp.lat());
381 xglVertex3dv(nodes[n4]);
386 // printf("a normal, texcoord, and vertex (4th)\n");
388 } else if ( token == "tf" ) {
390 // fgPrintf( FG_TERRAIN, FG_DEBUG, "new fan");
392 xglBegin(GL_TRIANGLE_FAN);
395 xglNormal3dv(normals[n1]);
396 pp = calc_tex_coords(nodes[n1], center);
397 xglTexCoord2f(pp.lon(), pp.lat());
398 xglVertex3dv(nodes[n1]);
401 xglNormal3dv(normals[n2]);
402 pp = calc_tex_coords(nodes[n2], center);
403 xglTexCoord2f(pp.lon(), pp.lat());
404 xglVertex3dv(nodes[n2]);
406 // read all subsequent numbers until next thing isn't a number
413 if ( ! isdigit(c) || in.eof() ) {
418 // cout << " triangle = "
419 // << n1 << "," << n2 << "," << n3
421 xglNormal3dv(normals[n3]);
422 pp = calc_tex_coords(nodes[n3], center);
423 xglTexCoord2f(pp.lon(), pp.lat());
424 xglVertex3dv(nodes[n3]);
426 fragment.add_face(n1, n2, n3);
431 } else if ( token == "f" ) {
435 xglBegin(GL_TRIANGLES);
436 // printf("xglBegin(triangles)\n");
440 // fgPrintf( FG_TERRAIN, FG_DEBUG, "new triangle = %s", line);*/
441 in >> n1 >> n2 >> n3;
442 fragment.add_face(n1, n2, n3);
444 // xglNormal3d(normals[n1][0], normals[n1][1], normals[n1][2]);
445 xglNormal3dv(normals[n1]);
446 pp = calc_tex_coords(nodes[n1], center);
447 xglTexCoord2f(pp.lon(), pp.lat());
448 xglVertex3dv(nodes[n1]);
450 xglNormal3dv(normals[n2]);
451 pp = calc_tex_coords(nodes[n2], center);
452 xglTexCoord2f(pp.lon(), pp.lat());
453 xglVertex3dv(nodes[n2]);
455 xglNormal3dv(normals[n3]);
456 pp = calc_tex_coords(nodes[n3], center);
457 xglTexCoord2f(pp.lon(), pp.lat());
458 xglVertex3dv(nodes[n3]);
459 // printf("some normals, texcoords, and vertices (tris)\n");
460 } else if ( token == "q" ) {
461 // continue a triangle strip
464 // fgPrintf( FG_TERRAIN, FG_DEBUG, "continued tri strip = %s ",
468 // There can be one or two values
470 while ( in.get(c) ) {
472 break; // only the one
481 // fgPrintf( FG_TERRAIN, FG_DEBUG, "read %d %d\n", n1, n2);
484 fragment.add_face(last1, last2, n1);
486 fragment.add_face(last2, last1, n1);
490 // Shading model is "GL_SMOOTH"
491 // MAT3_SCALE_VEC(normal, normals[n1], scale);
493 // Shading model is "GL_FLAT"
495 calc_normal(nodes[last1], nodes[last2], nodes[n1],
498 calc_normal(nodes[last2], nodes[last1], nodes[n1],
501 // MAT3_SCALE_VEC(normal, approx_normal, scale);
503 xglNormal3dv(normal);
505 pp = calc_tex_coords(nodes[n1], center);
506 xglTexCoord2f(pp.lon(), pp.lat());
507 xglVertex3dv(nodes[n1]);
508 // printf("a normal, texcoord, and vertex (4th)\n");
515 // fgPrintf( FG_TERRAIN, FG_DEBUG, " (cont)\n");
518 fragment.add_face(last1, last2, n2);
520 fragment.add_face(last2, last1, n2);
524 // Shading model is "GL_SMOOTH"
525 // MAT3_SCALE_VEC(normal, normals[n2], scale);
527 // Shading model is "GL_FLAT"
529 calc_normal(nodes[last1], nodes[last2],
530 nodes[n2], approx_normal);
532 calc_normal(nodes[last2], nodes[last1],
533 nodes[n2], approx_normal);
535 // MAT3_SCALE_VEC(normal, approx_normal, scale);
537 xglNormal3dv(normal);
539 pp = calc_tex_coords(nodes[n2], center);
540 xglTexCoord2f(pp.lon(), pp.lat());
541 xglVertex3dv(nodes[n2]);
542 // printf("a normal, texcoord, and vertex (4th)\n");
549 FG_LOG( FG_TERRAIN, FG_WARN, "Unknown token in "
550 << path << " = " << token );
553 // eat white space before start of while loop so if we are
554 // done with useful input it is noticed before hand.
560 // close out the previous structure and start the next
563 // printf("xglEnd(); xglEndList();\n");
566 fragment.display_list = display_list;
568 // push this fragment onto the tile's object list
569 t->fragment_list.push_back(fragment);
573 // Draw normal vectors (for visually verifying normals)
575 xglColor3f(0.0, 0.0, 0.0);
576 for ( i = 0; i < t->ncount; i++ ) {
577 xglVertex3d(t->nodes[i][0],
580 xglVertex3d(t->nodes[i][0] + 500*normals[i][0],
581 t->nodes[i][1] + 500*normals[i][1],
582 t->nodes[i][2] + 500*normals[i][2]);
588 FG_LOG( FG_TERRAIN, FG_INFO,
589 "Loaded " << path << " in "
590 << stopwatch.elapsedSeconds() << " seconds" );