]> git.mxchange.org Git - flightgear.git/blob - src/Objects/obj.cxx
Split out some code from obj.cxx to userdata.[ch]xx
[flightgear.git] / src / Objects / 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 <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
43 #include "userdata.hxx"
44
45 #include "obj.hxx"
46
47 SG_USING_STD(string);
48
49
50 // Generate an ocean tile
51 bool fgGenTile( const string& path, SGBucket b,
52                 Point3D *center, double *bounding_radius,
53                 SGMaterialLib *matlib, ssgBranch* geometry )
54 {
55     ssgSimpleState *state = NULL;
56
57     geometry->setName( (char *)path.c_str() );
58
59     double tex_width = 1000.0;
60     // double tex_height;
61
62     // find Ocean material in the properties list
63     SGMaterial *mat = matlib->find( "Ocean" );
64     if ( mat != NULL ) {
65         // set the texture width and height values for this
66         // material
67         tex_width = mat->get_xsize();
68         // tex_height = newmat->get_ysize();
69         
70         // set ssgState
71         state = mat->get_state();
72     } else {
73         SG_LOG( SG_TERRAIN, SG_ALERT, 
74                 "Ack! unknown usemtl name = " << "Ocean" 
75                 << " in " << path );
76     }
77
78     // Calculate center point
79     double clon = b.get_center_lon();
80     double clat = b.get_center_lat();
81     double height = b.get_height();
82     double width = b.get_width();
83
84     *center = sgGeodToCart( Point3D(clon*SGD_DEGREES_TO_RADIANS,
85                                     clat*SGD_DEGREES_TO_RADIANS,
86                                     0.0) );
87     // cout << "center = " << center << endl;;
88     
89     // Caculate corner vertices
90     Point3D geod[4];
91     geod[0] = Point3D( clon - width/2.0, clat - height/2.0, 0.0 );
92     geod[1] = Point3D( clon + width/2.0, clat - height/2.0, 0.0 );
93     geod[2] = Point3D( clon + width/2.0, clat + height/2.0, 0.0 );
94     geod[3] = Point3D( clon - width/2.0, clat + height/2.0, 0.0 );
95
96     Point3D rad[4];
97     int i;
98     for ( i = 0; i < 4; ++i ) {
99         rad[i] = Point3D( geod[i].x() * SGD_DEGREES_TO_RADIANS,
100                           geod[i].y() * SGD_DEGREES_TO_RADIANS,
101                           geod[i].z() );
102     }
103
104     Point3D cart[4], rel[4];
105     for ( i = 0; i < 4; ++i ) {
106         cart[i] = sgGeodToCart(rad[i]);
107         rel[i] = cart[i] - *center;
108         // cout << "corner " << i << " = " << cart[i] << endl;
109     }
110
111     // Calculate bounding radius
112     *bounding_radius = center->distance3D( cart[0] );
113     // cout << "bounding radius = " << t->bounding_radius << endl;
114
115     // Calculate normals
116     Point3D normals[4];
117     for ( i = 0; i < 4; ++i ) {
118         double length = cart[i].distance3D( Point3D(0.0) );
119         normals[i] = cart[i] / length;
120         // cout << "normal = " << normals[i] << endl;
121     }
122
123     // Calculate texture coordinates
124     point_list geod_nodes;
125     geod_nodes.clear();
126     geod_nodes.reserve(4);
127     int_list rectangle;
128     rectangle.clear();
129     rectangle.reserve(4);
130     for ( i = 0; i < 4; ++i ) {
131         geod_nodes.push_back( geod[i] );
132         rectangle.push_back( i );
133     }
134     point_list texs = calc_tex_coords( b, geod_nodes, rectangle, 
135                                        1000.0 / tex_width );
136
137     // Allocate ssg structure
138     ssgVertexArray   *vl = new ssgVertexArray( 4 );
139     ssgNormalArray   *nl = new ssgNormalArray( 4 );
140     ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
141     ssgColourArray   *cl = new ssgColourArray( 1 );
142
143     sgVec4 color;
144     sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
145     cl->add( color );
146
147     // sgVec3 *vtlist = new sgVec3 [ 4 ];
148     // t->vec3_ptrs.push_back( vtlist );
149     // sgVec3 *vnlist = new sgVec3 [ 4 ];
150     // t->vec3_ptrs.push_back( vnlist );
151     // sgVec2 *tclist = new sgVec2 [ 4 ];
152     // t->vec2_ptrs.push_back( tclist );
153
154     sgVec2 tmp2;
155     sgVec3 tmp3;
156     for ( i = 0; i < 4; ++i ) {
157         sgSetVec3( tmp3, 
158                    rel[i].x(), rel[i].y(), rel[i].z() );
159         vl->add( tmp3 );
160
161         sgSetVec3( tmp3, 
162                    normals[i].x(), normals[i].y(), normals[i].z() );
163         nl->add( tmp3 );
164
165         sgSetVec2( tmp2, texs[i].x(), texs[i].y());
166         tl->add( tmp2 );
167     }
168     
169     ssgLeaf *leaf = 
170         new ssgVtxTable ( GL_TRIANGLE_FAN, vl, nl, tl, cl );
171
172     leaf->setState( state );
173
174     geometry->addKid( leaf );
175
176     return true;
177 }
178
179
180 /**
181  * SSG callback for an in-range leaf of randomly-placed objects.
182  *
183  * This pretraversal callback is attached to a branch that is
184  * traversed only when a leaf is in range.  If the leaf is not
185  * currently prepared to be populated with randomly-placed objects,
186  * this callback will prepare it (actual population is handled by
187  * the tri_in_range_callback for individual triangles).
188  *
189  * @param entity The entity to which the callback is attached (not used).
190  * @param mask The entity's traversal mask (not used).
191  * @return Always 1, to allow traversal and culling to continue.
192  */
193 static int
194 leaf_in_range_callback (ssgEntity * entity, int mask)
195 {
196   LeafUserData * data = (LeafUserData *)entity->getUserData();
197
198   if (!data->is_filled_in) {
199                                 // Iterate through all the triangles
200                                 // and populate them.
201     int num_tris = data->leaf->getNumTriangles();
202     for ( int i = 0; i < num_tris; ++i ) {
203             data->setup_triangle(i);
204     }
205     data->is_filled_in = true;
206   }
207   return 1;
208 }
209
210
211 /**
212  * SSG callback for an out-of-range leaf of randomly-placed objects.
213  *
214  * This pretraversal callback is attached to a branch that is
215  * traversed only when a leaf is out of range.  If the leaf is
216  * currently prepared to be populated with randomly-placed objects (or
217  * is actually populated), the objects will be removed.
218  *
219  * @param entity The entity to which the callback is attached (not used).
220  * @param mask The entity's traversal mask (not used).
221  * @return Always 0, to prevent any further traversal or culling.
222  */
223 static int
224 leaf_out_of_range_callback (ssgEntity * entity, int mask)
225 {
226   LeafUserData * data = (LeafUserData *)entity->getUserData();
227   if (data->is_filled_in) {
228     data->branch->removeAllKids();
229     data->is_filled_in = false;
230   }
231   return 0;
232 }
233
234
235 /**
236  * Randomly place objects on a surface.
237  *
238  * The leaf node provides the geometry of the surface, while the
239  * material provides the objects and placement density.  Latitude
240  * and longitude are required so that the objects can be rotated
241  * to the world-up vector.  This function does not actually add
242  * any objects; instead, it attaches an ssgRangeSelector to the
243  * branch with callbacks to generate the objects when needed.
244  *
245  * @param leaf The surface where the objects should be placed.
246  * @param branch The branch that will hold the randomly-placed objects.
247  * @param center The center of the leaf in FlightGear coordinates.
248  * @param material_name The name of the surface's material.
249  */
250 static void
251 gen_random_surface_objects (ssgLeaf *leaf,
252                             ssgBranch *branch,
253                             Point3D *center,
254                             SGMaterial *mat )
255 {
256                                 // If the surface has no triangles, return
257                                 // now.
258     int num_tris = leaf->getNumTriangles();
259     if (num_tris < 1)
260         return;
261
262                                 // If the material has no randomly-placed
263                                 // objects, return now.
264     if (mat->get_object_group_count() < 1)
265         return;
266
267                                 // Calculate the geodetic centre of
268                                 // the tile, for aligning automatic
269                                 // objects.
270     double lon_deg, lat_rad, lat_deg, alt_m, sl_radius_m;
271     Point3D geoc = sgCartToPolar3d(*center);
272     lon_deg = geoc.lon() * SGD_RADIANS_TO_DEGREES;
273     sgGeocToGeod(geoc.lat(), geoc.radius(),
274                  &lat_rad, &alt_m, &sl_radius_m);
275     lat_deg = lat_rad * SGD_RADIANS_TO_DEGREES;
276
277                                 // LOD for the leaf
278                                 // max random object range: 20000m
279     float ranges[] = { 0, 20000, 1000000 };
280     ssgRangeSelector * lod = new ssgRangeSelector;
281     lod->setRanges(ranges, 3);
282     branch->addKid(lod);
283
284                                 // Create the in-range and out-of-range
285                                 // branches.
286     ssgBranch * in_range = new ssgBranch;
287     ssgBranch * out_of_range = new ssgBranch;
288     lod->addKid(in_range);
289     lod->addKid(out_of_range);
290
291     LeafUserData * data = new LeafUserData;
292     data->is_filled_in = false;
293     data->leaf = leaf;
294     data->mat = mat;
295     data->branch = in_range;
296     data->sin_lat = sin(lat_deg * SGD_DEGREES_TO_RADIANS);
297     data->cos_lat = cos(lat_deg * SGD_DEGREES_TO_RADIANS);
298     data->sin_lon = sin(lon_deg * SGD_DEGREES_TO_RADIANS);
299     data->cos_lon = cos(lon_deg * SGD_DEGREES_TO_RADIANS);
300
301     in_range->setUserData(data);
302     in_range->setTravCallback(SSG_CALLBACK_PRETRAV, leaf_in_range_callback);
303     out_of_range->setUserData(data);
304     out_of_range->setTravCallback(SSG_CALLBACK_PRETRAV,
305                                    leaf_out_of_range_callback);
306     out_of_range
307       ->addKid(new DummyBSphereEntity(leaf->getBSphere()->getRadius()));
308 }
309
310
311 \f
312 ////////////////////////////////////////////////////////////////////////
313 // Scenery loaders.
314 ////////////////////////////////////////////////////////////////////////
315
316 // Load an Binary obj file
317 bool fgBinObjLoad( const string& path, const bool is_base,
318                    Point3D *center,
319                    double *bounding_radius,
320                    SGMaterialLib *matlib,
321                    bool use_random_objects,
322                    ssgBranch* geometry,
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             sgVec3 up;
357             sgSetVec3( up, center->x(), center->y(), center->z() );
358             // returns a transform -> lod -> leaf structure
359             ssgBranch *branch = sgMakeDirectionalLights( nodes, normals,
360                                                          pts_v[i], pts_n[i],
361                                                          matlib,
362                                                          pt_materials[i], up );
363             if ( pt_materials[i].substr(0, 16) == "RWY_BLUE_TAXIWAY" ) {
364                 taxi_lights->addKid( branch );
365             } else {
366                 rwy_lights->addKid( branch );
367             }
368         } else {
369             material = pt_materials[i];
370             tex_index.clear();
371             ssgLeaf *leaf = sgMakeLeaf( path, GL_POINTS, matlib, material,
372                                         nodes, normals, texcoords,
373                                         pts_v[i], pts_n[i], tex_index,
374                                         false, ground_lights );
375             geometry->addKid( leaf );
376         }
377     }
378
379     // Put all randomly-placed objects under a separate branch
380     // (actually an ssgRangeSelector) named "random-models".
381     ssgBranch * random_object_branch = 0;
382     if (use_random_objects) {
383         float ranges[] = { 0, 20000 }; // Maximum 20km range for random objects
384         ssgRangeSelector * object_lod = new ssgRangeSelector;
385         object_lod->setRanges(ranges, 2);
386         object_lod->setName("random-models");
387         geometry->addKid(object_lod);
388         random_object_branch = new ssgBranch;
389         object_lod->addKid(random_object_branch);
390     }
391
392     // generate triangles
393     string_list const& tri_materials = obj.get_tri_materials();
394     group_list const& tris_v = obj.get_tris_v();
395     group_list const& tris_n = obj.get_tris_n();
396     group_list const& tris_tc = obj.get_tris_tc();
397     for ( i = 0; i < tris_v.size(); ++i ) {
398         ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLES, matlib,
399                                     tri_materials[i],
400                                     nodes, normals, texcoords,
401                                     tris_v[i], tris_n[i], tris_tc[i],
402                                     is_base, ground_lights );
403
404         if ( use_random_objects ) {
405             SGMaterial *mat = matlib->find( tri_materials[i] );
406             if ( mat == NULL ) {
407                 SG_LOG( SG_INPUT, SG_ALERT,
408                         "Unknown material for random surface objects = "
409                         << tri_materials[i] );
410             }
411             gen_random_surface_objects( leaf, random_object_branch,
412                                         center, mat );
413         }
414         geometry->addKid( leaf );
415     }
416
417     // generate strips
418     string_list const& strip_materials = obj.get_strip_materials();
419     group_list const& strips_v = obj.get_strips_v();
420     group_list const& strips_n = obj.get_strips_n();
421     group_list const& strips_tc = obj.get_strips_tc();
422     for ( i = 0; i < strips_v.size(); ++i ) {
423         ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLE_STRIP,
424                                     matlib, strip_materials[i],
425                                     nodes, normals, texcoords,
426                                     strips_v[i], strips_n[i], strips_tc[i],
427                                     is_base, ground_lights );
428
429         if ( use_random_objects ) {
430             SGMaterial *mat = matlib->find( strip_materials[i] );
431             if ( mat == NULL ) {
432                 SG_LOG( SG_INPUT, SG_ALERT,
433                         "Unknown material for random surface objects = "
434                         << strip_materials[i] );
435             }
436             gen_random_surface_objects( leaf, random_object_branch,
437                                         center, mat );
438         }
439         geometry->addKid( leaf );
440     }
441
442     // generate fans
443     string_list const& fan_materials = obj.get_fan_materials();
444     group_list const& fans_v = obj.get_fans_v();
445     group_list const& fans_n = obj.get_fans_n();
446     group_list const& fans_tc = obj.get_fans_tc();
447     for ( i = 0; i < fans_v.size(); ++i ) {
448         ssgLeaf *leaf = sgMakeLeaf( path, GL_TRIANGLE_FAN,
449                                     matlib, fan_materials[i],
450                                     nodes, normals, texcoords,
451                                     fans_v[i], fans_n[i], fans_tc[i],
452                                     is_base, ground_lights );
453         if ( use_random_objects ) {
454             SGMaterial *mat = matlib->find( fan_materials[i] );
455             if ( mat == NULL ) {
456                 SG_LOG( SG_INPUT, SG_ALERT,
457                         "Unknown material for random surface objects = "
458                         << fan_materials[i] );
459             }
460             gen_random_surface_objects( leaf, random_object_branch,
461                                         center, mat );
462         }
463         geometry->addKid( leaf );
464     }
465
466     return true;
467 }