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  - http://www.flightgear.org/~curt
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 // $Id$
23 
24 
25 #ifdef HAVE_CONFIG_H
26 #  include <simgear_config.h>
27 #endif
28 
29 #include "obj.hxx"
30 
31 #include <simgear/debug/logstream.hxx>
32 #include <simgear/io/sg_binobj.hxx>
33 
34 #include "SGTileGeometryBin.hxx"        // for original tile loading
35 #include "SGTileDetailsCallback.hxx"    // for tile details ( random objects, and lighting )
36 
37 
38 using namespace simgear;
39 
40 osg::Node*
SGLoadBTG(const std::string & path,const simgear::SGReaderWriterOptions * options)41 SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options)
42 {
43     SGBinObject tile;
44     if (!tile.read_bin(path))
45       return NULL;
46 
47     SGMaterialLibPtr matlib;
48     osg::ref_ptr<SGMaterialCache> matcache;
49     bool useVBOs = false;
50     bool simplifyDistant = false;
51     bool simplifyNear    = false;
52     double ratio       = SG_SIMPLIFIER_RATIO;
53     double maxLength   = SG_SIMPLIFIER_MAX_LENGTH;
54     double maxError    = SG_SIMPLIFIER_MAX_ERROR;
55     double object_range = SG_OBJECT_RANGE_ROUGH;
56     double tile_min_expiry = SG_TILE_MIN_EXPIRY;
57 
58     if (options) {
59       matlib = options->getMaterialLib();
60       useVBOs = (options->getPluginStringData("SimGear::USE_VBOS") == "ON");
61       SGPropertyNode* propertyNode = options->getPropertyNode().get();
62 
63       // We control whether we simplify the nearby terrain and distant terrain separatey.
64       // However, we don't allow only simplifying the near terrain!
65       simplifyNear = propertyNode->getBoolValue("/sim/rendering/terrain/simplifier/enabled-near", simplifyNear);
66       simplifyDistant = simplifyNear || propertyNode->getBoolValue("/sim/rendering/terrain/simplifier/enabled-far", simplifyDistant);
67       ratio = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/ratio", ratio);
68       maxLength = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/max-length", maxLength);
69       maxError = propertyNode->getDoubleValue("/sim/rendering/terrain/simplifier/max-error", maxError);
70       object_range = propertyNode->getDoubleValue("/sim/rendering/static-lod/rough", object_range);
71       tile_min_expiry= propertyNode->getDoubleValue("/sim/rendering/plod-minimum-expiry-time-secs", tile_min_expiry);
72     }
73 
74     SGVec3d center = tile.get_gbs_center();
75     SGGeod geodPos = SGGeod::fromCart(center);
76     SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
77     if (matlib)
78     	matcache = matlib->generateMatCache(geodPos);
79 
80     // rotate the tiles so that the bounding boxes get nearly axis aligned.
81     // this will help the collision tree's bounding boxes a bit ...
82     std::vector<SGVec3d> nodes = tile.get_wgs84_nodes();
83     for (unsigned i = 0; i < nodes.size(); ++i)
84       nodes[i] = hlOr.transform(nodes[i]);
85     tile.set_wgs84_nodes(nodes);
86 
87     SGQuatf hlOrf(hlOr[0], hlOr[1], hlOr[2], hlOr[3]);
88     std::vector<SGVec3f> normals = tile.get_normals();
89     for (unsigned i = 0; i < normals.size(); ++i)
90       normals[i] = hlOrf.transform(normals[i]);
91     tile.set_normals(normals);
92 
93     // tile surface
94     osg::ref_ptr<SGTileGeometryBin> tileGeometryBin = new SGTileGeometryBin();
95 
96     if (!tileGeometryBin->insertSurfaceGeometry(tile, matcache))
97       return NULL;
98 
99     osg::Node* node = tileGeometryBin->getSurfaceGeometry(matcache, useVBOs);
100     if (node && simplifyDistant) {
101       osgUtil::Simplifier simplifier(ratio, maxError, maxLength);
102       node->accept(simplifier);
103     }
104 
105     // The toplevel transform for that tile.
106     osg::MatrixTransform* transform = new osg::MatrixTransform;
107     transform->setName(path);
108     transform->setMatrix(osg::Matrix::rotate(toOsg(hlOr))*
109                          osg::Matrix::translate(toOsg(center)));
110 
111     if (node) {
112       // tile points
113       SGTileDetailsCallback* tileDetailsCallback = new SGTileDetailsCallback;
114       tileDetailsCallback->insertPtGeometry( tile, matcache );
115 
116       // PagedLOD for the random objects so we don't need to generate
117       // them all on tile loading.
118       osg::PagedLOD* pagedLOD = new osg::PagedLOD;
119       pagedLOD->setCenterMode(osg::PagedLOD::USE_BOUNDING_SPHERE_CENTER);
120       pagedLOD->setName("pagedObjectLOD");
121 
122       if (simplifyNear == simplifyDistant) {
123         // Same terrain type is used for both near and far distances,
124         // so add it to the main group.
125         osg::Group* terrainGroup = new osg::Group;
126         terrainGroup->setName("BTGTerrainGroup");
127         terrainGroup->addChild(node);
128         transform->addChild(terrainGroup);
129       } else if (simplifyDistant) {
130         // Simplified terrain is only used in the distance, the
131         // call-back below will re-generate the closer version
132         pagedLOD->addChild(node, 2*object_range + SG_TILE_RADIUS, FLT_MAX);
133       }
134 
135       osg::ref_ptr<SGReaderWriterOptions> opt;
136       opt = SGReaderWriterOptions::copyOrCreate(options);
137 
138       // we just need to know about the read file callback that itself holds the data
139       tileDetailsCallback->_options = opt;
140       tileDetailsCallback->_path = std::string(path);
141       tileDetailsCallback->_loadterrain = ! (simplifyNear == simplifyDistant);
142       tileDetailsCallback->_gbs_center = center;
143       tileDetailsCallback->_rootNode = node;
144       tileDetailsCallback->_randomSurfaceLightsComputed = false;
145       tileDetailsCallback->_tileRandomObjectsComputed = false;
146 
147       osg::ref_ptr<osgDB::Options> callbackOptions = new osgDB::Options;
148       callbackOptions->setObjectCacheHint(osgDB::Options::CACHE_ALL);
149       callbackOptions->setReadFileCallback(tileDetailsCallback);
150       pagedLOD->setDatabaseOptions(callbackOptions.get());
151 
152       // Ensure that the random objects aren't expired too quickly
153       pagedLOD->setMinimumExpiryTime(pagedLOD->getNumChildren(), tile_min_expiry);
154       pagedLOD->setFileName(pagedLOD->getNumChildren(), "Dummy filename for random objects callback");
155 
156       // LOD Range is 2x the object range plus the tile radius because we display some objects up to 2x the
157       // range to reduce popping.
158       pagedLOD->setRange(pagedLOD->getNumChildren(), 0,  2 *object_range + SG_TILE_RADIUS);
159       transform->addChild(pagedLOD);
160     }
161 
162     transform->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
163     return transform;
164 }
165