]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/obj.cxx
6009eac55d61eb0bc6c030230c4dd25f16399021
[simgear.git] / simgear / scene / tgdb / obj.cxx
1 // obj.cxx -- routines to handle loading scenery and building the plib
2 //            scene graph.
3 //
4 // Written by Curtis Olson, started October 1997.
5 //
6 // Copyright (C) 1997  Curtis L. Olson  - curt@infoplane.com
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 //
22 // $Id$
23
24
25 #ifdef HAVE_CONFIG_H
26 #  include <simgear_config.h>
27 #endif
28
29 #include <simgear/compiler.h>
30
31 #include STL_STRING
32
33 #include <simgear/bucket/newbucket.hxx>
34 #include <simgear/io/sg_binobj.hxx>
35 #include <simgear/math/sg_geodesy.hxx>
36 #include <simgear/math/sg_types.hxx>
37 #include <simgear/misc/texcoord.hxx>
38 #include <simgear/scene/material/mat.hxx>
39 #include <simgear/scene/material/matlib.hxx>
40 #include <simgear/scene/tgdb/leaf.hxx>
41 #include <simgear/scene/tgdb/pt_lights.hxx>
42 #include <simgear/scene/tgdb/userdata.hxx>
43
44 #include "obj.hxx"
45
46 SG_USING_STD(string);
47
48
49 // Generate an ocean tile
50 bool sgGenTile( const string& path, SGBucket b,
51                 Point3D *center, double *bounding_radius,
52                 SGMaterialLib *matlib, ssgBranch* geometry )
53 {
54     ssgSimpleState *state = NULL;
55
56     geometry->setName( (char *)path.c_str() );
57
58     double tex_width = 1000.0;
59     // double tex_height;
60
61     // find Ocean material in the properties list
62     SGMaterial *mat = matlib->find( "Ocean" );
63     if ( mat != NULL ) {
64         // set the texture width and height values for this
65         // material
66         tex_width = mat->get_xsize();
67         // tex_height = newmat->get_ysize();
68         
69         // set ssgState
70         state = mat->get_state();
71     } else {
72         SG_LOG( SG_TERRAIN, SG_ALERT, 
73                 "Ack! unknown usemtl name = " << "Ocean" 
74                 << " in " << path );
75     }
76
77     // Calculate center point
78     double clon = b.get_center_lon();
79     double clat = b.get_center_lat();
80     double height = b.get_height();
81     double width = b.get_width();
82
83     *center = sgGeodToCart( Point3D(clon*SGD_DEGREES_TO_RADIANS,
84                                     clat*SGD_DEGREES_TO_RADIANS,
85                                     0.0) );
86     // cout << "center = " << center << endl;;
87     
88     // Caculate corner vertices
89     Point3D geod[4];
90     geod[0] = Point3D( clon - width/2.0, clat - height/2.0, 0.0 );
91     geod[1] = Point3D( clon + width/2.0, clat - height/2.0, 0.0 );
92     geod[2] = Point3D( clon + width/2.0, clat + height/2.0, 0.0 );
93     geod[3] = Point3D( clon - width/2.0, clat + height/2.0, 0.0 );
94
95     Point3D rad[4];
96     int i;
97     for ( i = 0; i < 4; ++i ) {
98         rad[i] = Point3D( geod[i].x() * SGD_DEGREES_TO_RADIANS,
99                           geod[i].y() * SGD_DEGREES_TO_RADIANS,
100                           geod[i].z() );
101     }
102
103     Point3D cart[4], rel[4];
104     for ( i = 0; i < 4; ++i ) {
105         cart[i] = sgGeodToCart(rad[i]);
106         rel[i] = cart[i] - *center;
107         // cout << "corner " << i << " = " << cart[i] << endl;
108     }
109
110     // Calculate bounding radius
111     *bounding_radius = center->distance3D( cart[0] );
112     // cout << "bounding radius = " << t->bounding_radius << endl;
113
114     // Calculate normals
115     Point3D normals[4];
116     for ( i = 0; i < 4; ++i ) {
117         double length = cart[i].distance3D( Point3D(0.0) );
118         normals[i] = cart[i] / length;
119         // cout << "normal = " << normals[i] << endl;
120     }
121
122     // Calculate texture coordinates
123     point_list geod_nodes;
124     geod_nodes.clear();
125     geod_nodes.reserve(4);
126     int_list rectangle;
127     rectangle.clear();
128     rectangle.reserve(4);
129     for ( i = 0; i < 4; ++i ) {
130         geod_nodes.push_back( geod[i] );
131         rectangle.push_back( i );
132     }
133     point_list texs = sgCalcTexCoords( b, geod_nodes, rectangle, 
134                                        1000.0 / tex_width );
135
136     // Allocate ssg structure
137     ssgVertexArray   *vl = new ssgVertexArray( 4 );
138     ssgNormalArray   *nl = new ssgNormalArray( 4 );
139     ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
140     ssgColourArray   *cl = new ssgColourArray( 1 );
141
142     sgVec4 color;
143     sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
144     cl->add( color );
145
146     // sgVec3 *vtlist = new sgVec3 [ 4 ];
147     // t->vec3_ptrs.push_back( vtlist );
148     // sgVec3 *vnlist = new sgVec3 [ 4 ];
149     // t->vec3_ptrs.push_back( vnlist );
150     // sgVec2 *tclist = new sgVec2 [ 4 ];
151     // t->vec2_ptrs.push_back( tclist );
152
153     sgVec2 tmp2;
154     sgVec3 tmp3;
155     for ( i = 0; i < 4; ++i ) {
156         sgSetVec3( tmp3, 
157                    rel[i].x(), rel[i].y(), rel[i].z() );
158         vl->add( tmp3 );
159
160         sgSetVec3( tmp3, 
161                    normals[i].x(), normals[i].y(), normals[i].z() );
162         nl->add( tmp3 );
163
164         sgSetVec2( tmp2, texs[i].x(), texs[i].y());
165         tl->add( tmp2 );
166     }
167     
168     ssgLeaf *leaf = 
169         new ssgVtxTable ( GL_TRIANGLE_FAN, vl, nl, tl, cl );
170
171     leaf->setState( state );
172
173     geometry->addKid( leaf );
174
175     return true;
176 }
177
178
179 /**
180  * SSG callback for an in-range leaf of randomly-placed objects.
181  *
182  * This pretraversal callback is attached to a branch that is
183  * traversed only when a leaf is in range.  If the leaf is not
184  * currently prepared to be populated with randomly-placed objects,
185  * this callback will prepare it (actual population is handled by
186  * the tri_in_range_callback for individual triangles).
187  *
188  * @param entity The entity to which the callback is attached (not used).
189  * @param mask The entity's traversal mask (not used).
190  * @return Always 1, to allow traversal and culling to continue.
191  */
192 static int
193 leaf_in_range_callback (ssgEntity * entity, int mask)
194 {
195   SGLeafUserData * data = (SGLeafUserData *)entity->getUserData();
196
197   if (!data->is_filled_in) {
198                                 // Iterate through all the triangles
199                                 // and populate them.
200     int num_tris = data->leaf->getNumTriangles();
201     for ( int i = 0; i < num_tris; ++i ) {
202             data->setup_triangle(i);
203     }
204     data->is_filled_in = true;
205   }
206   return 1;
207 }
208
209
210 /**
211  * SSG callback for an out-of-range leaf of randomly-placed objects.
212  *
213  * This pretraversal callback is attached to a branch that is
214  * traversed only when a leaf is out of range.  If the leaf is
215  * currently prepared to be populated with randomly-placed objects (or
216  * is actually populated), the objects will be removed.
217  *
218  * @param entity The entity to which the callback is attached (not used).
219  * @param mask The entity's traversal mask (not used).
220  * @return Always 0, to prevent any further traversal or culling.
221  */
222 static int
223 leaf_out_of_range_callback (ssgEntity * entity, int mask)
224 {
225   SGLeafUserData * data = (SGLeafUserData *)entity->getUserData();
226   if (data->is_filled_in) {
227     data->branch->removeAllKids();
228     data->is_filled_in = false;
229   }
230   return 0;
231 }
232
233
234 /**
235  * Randomly place objects on a surface.
236  *
237  * The leaf node provides the geometry of the surface, while the
238  * material provides the objects and placement density.  Latitude
239  * and longitude are required so that the objects can be rotated
240  * to the world-up vector.  This function does not actually add
241  * any objects; instead, it attaches an ssgRangeSelector to the
242  * branch with callbacks to generate the objects when needed.
243  *
244  * @param leaf The surface where the objects should be placed.
245  * @param branch The branch that will hold the randomly-placed objects.
246  * @param center The center of the leaf in FlightGear coordinates.
247  * @param material_name The name of the surface's material.
248  */
249 static void
250 gen_random_surface_objects (ssgLeaf *leaf,
251                             ssgBranch *branch,
252                             Point3D *center,
253                             SGMaterial *mat )
254 {
255                                 // If the surface has no triangles, return
256                                 // now.
257     int num_tris = leaf->getNumTriangles();
258     if (num_tris < 1)
259         return;
260
261                                 // If the material has no randomly-placed
262                                 // objects, return now.
263     if (mat->get_object_group_count() < 1)
264         return;
265
266                                 // Calculate the geodetic centre of
267                                 // the tile, for aligning automatic
268                                 // objects.
269     double lon_deg, lat_rad, lat_deg, alt_m, sl_radius_m;
270     Point3D geoc = sgCartToPolar3d(*center);
271     lon_deg = geoc.lon() * SGD_RADIANS_TO_DEGREES;
272     sgGeocToGeod(geoc.lat(), geoc.radius(),
273                  &lat_rad, &alt_m, &sl_radius_m);
274     lat_deg = lat_rad * SGD_RADIANS_TO_DEGREES;
275
276                                 // LOD for the leaf
277                                 // max random object range: 20000m
278     float ranges[] = { 0, 20000, 1000000 };
279     ssgRangeSelector * lod = new ssgRangeSelector;
280     lod->setRanges(ranges, 3);
281     branch->addKid(lod);
282
283                                 // Create the in-range and out-of-range
284                                 // branches.
285     ssgBranch * in_range = new ssgBranch;
286     ssgBranch * out_of_range = new ssgBranch;
287     lod->addKid(in_range);
288     lod->addKid(out_of_range);
289
290     SGLeafUserData * data = new SGLeafUserData;
291     data->is_filled_in = false;
292     data->leaf = leaf;
293     data->mat = mat;
294     data->branch = in_range;
295     data->sin_lat = sin(lat_deg * SGD_DEGREES_TO_RADIANS);
296     data->cos_lat = cos(lat_deg * SGD_DEGREES_TO_RADIANS);
297     data->sin_lon = sin(lon_deg * SGD_DEGREES_TO_RADIANS);
298     data->cos_lon = cos(lon_deg * SGD_DEGREES_TO_RADIANS);
299
300     in_range->setUserData(data);
301     in_range->setTravCallback(SSG_CALLBACK_PRETRAV, leaf_in_range_callback);
302     out_of_range->setUserData(data);
303     out_of_range->setTravCallback(SSG_CALLBACK_PRETRAV,
304                                    leaf_out_of_range_callback);
305     out_of_range
306       ->addKid(new SGDummyBSphereEntity(leaf->getBSphere()->getRadius()));
307 }
308
309
310 \f
311 ////////////////////////////////////////////////////////////////////////
312 // Scenery loaders.
313 ////////////////////////////////////////////////////////////////////////
314
315 // Load an Binary obj file
316 bool sgBinObjLoad( const string& path, const bool is_base,
317                    Point3D *center,
318                    double *bounding_radius,
319                    SGMaterialLib *matlib,
320                    bool use_random_objects,
321                    ssgBranch *geometry,
322                    ssgBranch *vasi_lights,
323                    ssgBranch *rwy_lights,
324                    ssgBranch *taxi_lights,
325                    ssgVertexArray *ground_lights )
326 {
327     SGBinObject obj;
328
329     if ( ! obj.read_bin( path ) ) {
330         return false;
331     }
332
333     geometry->setName( (char *)path.c_str() );
334
335     // reference point (center offset/bounding sphere)
336     *center = obj.get_gbs_center();
337     *bounding_radius = obj.get_gbs_radius();
338
339     point_list const& nodes = obj.get_wgs84_nodes();
340     // point_list const& colors = obj.get_colors();
341     point_list const& normals = obj.get_normals();
342     point_list const& texcoords = obj.get_texcoords();
343
344     string material;
345     int_list tex_index;
346
347     group_list::size_type i;
348
349     // generate points
350     string_list const& pt_materials = obj.get_pt_materials();
351     group_list const& pts_v = obj.get_pts_v();
352     group_list const& pts_n = obj.get_pts_n();
353     for ( i = 0; i < pts_v.size(); ++i ) {
354         // cout << "pts_v.size() = " << pts_v.size() << endl;
355         if ( pt_materials[i].substr(0, 3) == "RWY" ) {
356             // airport environment lighting
357             sgVec3 up;
358             sgSetVec3( up, center->x(), center->y(), center->z() );
359             // returns a transform -> lod -> leaf structure
360             ssgBranch *branch = sgMakeDirectionalLights( nodes, normals,
361                                                          pts_v[i], pts_n[i],
362                                                          matlib,
363                                                          pt_materials[i], up );
364             if ( pt_materials[i] == "RWY_VASI_LIGHTS" ) {
365                 vasi_lights->addKid( branch );
366             } else if ( pt_materials[i] == "RWY_BLUE_TAXIWAY_LIGHTS"
367                 || pt_materials[i] == "RWY_GREEN_TAXIWAY_LIGHTS" )
368             {
369                 taxi_lights->addKid( branch );
370             } else {
371                 rwy_lights->addKid( branch );
372             }
373         } else {
374             // other geometry
375             material = pt_materials[i];
376             tex_index.clear();
377             ssgLeaf *leaf = sgMakeLeaf( path, GL_POINTS, matlib, material,
378                                         nodes, normals, texcoords,
379                                         pts_v[i], pts_n[i], tex_index,
380                                         false, ground_lights );
381             geometry->addKid( leaf );
382         }
383     }
384
385     // Put all randomly-placed objects under a separate branch
386     // (actually an ssgRangeSelector) named "random-models".
387     ssgBranch * random_object_branch = 0;
388     if (use_random_objects) {
389         float ranges[] = { 0, 20000 }; // Maximum 20km range for random objects
390         ssgRangeSelector * object_lod = new ssgRangeSelector;
391         object_lod->setRanges(ranges, 2);
392         object_lod->setName("random-models");
393         geometry->addKid(object_lod);
394         random_object_branch = new ssgBranch;
395         object_lod->addKid(random_object_branch);
396     }
397
398     // generate triangles
399     string_list const& tri_materials = obj.get_tri_materials();
400     group_list const& tris_v = obj.get_tris_v();
401     group_list const& tris_n = obj.get_tris_n();
402     group_list const& tris_tc = obj.get_tris_tc();
403     for ( i = 0; i < tris_v.size(); ++i ) {
404         ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLES, matlib,
405                                     tri_materials[i],
406                                     nodes, normals, texcoords,
407                                     tris_v[i], tris_n[i], tris_tc[i],
408                                     is_base, ground_lights );
409
410         if ( use_random_objects ) {
411             SGMaterial *mat = matlib->find( tri_materials[i] );
412             if ( mat == NULL ) {
413                 SG_LOG( SG_INPUT, SG_ALERT,
414                         "Unknown material for random surface objects = "
415                         << tri_materials[i] );
416             } else {
417                 gen_random_surface_objects( leaf, random_object_branch,
418                                             center, mat );
419             }
420         }
421         geometry->addKid( leaf );
422     }
423
424     // generate strips
425     string_list const& strip_materials = obj.get_strip_materials();
426     group_list const& strips_v = obj.get_strips_v();
427     group_list const& strips_n = obj.get_strips_n();
428     group_list const& strips_tc = obj.get_strips_tc();
429     for ( i = 0; i < strips_v.size(); ++i ) {
430         ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLE_STRIP,
431                                     matlib, strip_materials[i],
432                                     nodes, normals, texcoords,
433                                     strips_v[i], strips_n[i], strips_tc[i],
434                                     is_base, ground_lights );
435
436         if ( use_random_objects ) {
437             SGMaterial *mat = matlib->find( strip_materials[i] );
438             if ( mat == NULL ) {
439                 SG_LOG( SG_INPUT, SG_ALERT,
440                         "Unknown material for random surface objects = "
441                         << strip_materials[i] );
442             } else {
443                 gen_random_surface_objects( leaf, random_object_branch,
444                                             center, mat );
445             }
446         }
447         geometry->addKid( leaf );
448     }
449
450     // generate fans
451     string_list const& fan_materials = obj.get_fan_materials();
452     group_list const& fans_v = obj.get_fans_v();
453     group_list const& fans_n = obj.get_fans_n();
454     group_list const& fans_tc = obj.get_fans_tc();
455     for ( i = 0; i < fans_v.size(); ++i ) {
456         ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLE_FAN,
457                                     matlib, fan_materials[i],
458                                     nodes, normals, texcoords,
459                                     fans_v[i], fans_n[i], fans_tc[i],
460                                     is_base, ground_lights );
461         if ( use_random_objects ) {
462             SGMaterial *mat = matlib->find( fan_materials[i] );
463             if ( mat == NULL ) {
464                 SG_LOG( SG_INPUT, SG_ALERT,
465                         "Unknown material for random surface objects = "
466                         << fan_materials[i] );
467             } else {
468                 gen_random_surface_objects( leaf, random_object_branch,
469                                             center, mat );
470             }
471         }
472
473         geometry->addKid( leaf );
474     }
475
476     return true;
477 }