]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/matmodel.cxx
Memory leak fixes from Till Busch
[simgear.git] / simgear / scene / material / matmodel.cxx
1 // matmodel.cxx -- class to handle models tied to a material property
2 //
3 // Written by David Megginson, started May 1998.
4 //
5 // Copyright (C) 1998 - 2003  Curtis L. Olson  - http://www.flightgear.org/~curt
6 //
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.
11 //
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.
16 //
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <simgear_config.h>
26 #endif
27
28 #include <simgear/compiler.h>
29
30 #include <map>
31 SG_USING_STD(map);
32
33 #include <osg/AlphaFunc>
34 #include <osg/Group>
35 #include <osg/LOD>
36 #include <osg/StateSet>
37 #include <osg/Transform>
38
39 #ifdef SG_MATH_EXCEPTION_CLASH
40 #  include <math.h>
41 #endif
42
43 #include <simgear/debug/logstream.hxx>
44 #include <simgear/math/sg_random.h>
45 #include <simgear/misc/sg_path.hxx>
46 #include <simgear/misc/sgstream.hxx>
47 #include <simgear/scene/model/modellib.hxx>
48
49 #include "matmodel.hxx"
50
51 \f
52 ////////////////////////////////////////////////////////////////////////
53 // Local static functions.
54 ////////////////////////////////////////////////////////////////////////
55
56 /**
57  * Internal method to test whether a file exists.
58  *
59  * TODO: this should be moved to a SimGear library of local file
60  * functions.
61  */
62 static inline bool
63 local_file_exists( const string& path ) {
64     sg_gzifstream in( path );
65     if ( ! in.is_open() ) {
66         return false;
67     } else {
68         return true;
69     }
70 }
71
72
73 \f
74 ////////////////////////////////////////////////////////////////////////
75 // Implementation of SGMatModel.
76 ////////////////////////////////////////////////////////////////////////
77
78 SGMatModel::SGMatModel (const SGPropertyNode * node, double range_m)
79   : _models_loaded(false),
80     _coverage_m2(node->getDoubleValue("coverage-m2", 1000000)),
81     _range_m(range_m)
82 {
83                                 // Sanity check
84   if (_coverage_m2 < 1000) {
85     SG_LOG(SG_INPUT, SG_ALERT, "Random object coverage " << _coverage_m2
86            << " is too small, forcing, to 1000");
87     _coverage_m2 = 1000;
88   }
89
90                                 // Note all the model paths
91   vector <SGPropertyNode_ptr> path_nodes = node->getChildren("path");
92   for (unsigned int i = 0; i < path_nodes.size(); i++)
93     _paths.push_back(path_nodes[i]->getStringValue());
94
95                                 // Note the heading type
96   string hdg = node->getStringValue("heading-type", "fixed");
97   if (hdg == "fixed") {
98     _heading_type = HEADING_FIXED;
99   } else if (hdg == "billboard") {
100     _heading_type = HEADING_BILLBOARD;
101   } else if (hdg == "random") {
102     _heading_type = HEADING_RANDOM;
103   } else {
104     _heading_type = HEADING_FIXED;
105     SG_LOG(SG_INPUT, SG_ALERT, "Unknown heading type: " << hdg
106            << "; using 'fixed' instead.");
107   }
108
109   // uncomment to preload models
110   // load_models();
111 }
112
113 SGMatModel::~SGMatModel ()
114 {
115 }
116
117 int
118 SGMatModel::get_model_count( SGModelLib *modellib,
119                              const string &fg_root,
120                              SGPropertyNode *prop_root,
121                              double sim_time_sec )
122 {
123   load_models( modellib, fg_root, prop_root, sim_time_sec );
124   return _models.size();
125 }
126
127 inline void
128 SGMatModel::load_models ( SGModelLib *modellib,
129                           const string &fg_root,
130                           SGPropertyNode *prop_root,
131                           double sim_time_sec )
132 {
133                                 // Load model only on demand
134   if (!_models_loaded) {
135     for (unsigned int i = 0; i < _paths.size(); i++) {
136       osg::Node *entity = modellib->load_model( fg_root, _paths[i],
137                                                 prop_root, sim_time_sec,
138                                                 /*cache_object*/ true );
139       if (entity != 0) {
140         // FIXME: this stuff can be handled
141         // in the XML wrapper as well (at least,
142         // the billboarding should be handled
143         // there).
144         
145         // Create multiple LoD nodes so instead of all objects
146         // of the same type appearing at once, some appear further
147         // away.
148         //
149         // Very basic hardcoded distribution:
150         // 4 at normal range
151         // 2 at 1.5 times normal range
152         // 1 at 2 time normal range.
153         //
154         // We achieve this by creating the three different LoD
155         // nodes and adding them to the _models list multiple times.
156         
157         osg::LOD * lod1 = new osg::LOD;
158         lod1->setName("Model LOD");
159         lod1->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
160         lod1->setRange(0, 0, _range_m);
161         
162         osg::LOD * lod15 = new osg::LOD;
163         lod15->setName("Model LOD - 1.5");
164         lod15->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
165         lod15->setRange(0, 0, 1.5 * _range_m);
166
167         osg::LOD * lod2 = new osg::LOD;
168         lod2->setName("Model LOD - 2.0");
169         lod2->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
170         lod2->setRange(0, 0, 2.0 * _range_m);
171         
172         if (_heading_type == HEADING_BILLBOARD) {
173           // if the model is a billboard, it is likely :
174           // 1. a branch with only leaves,
175           // 2. a tree or a non rectangular shape faked by transparency
176           // We add alpha clamp then
177           osg::StateSet* stateSet = entity->getOrCreateStateSet();
178           osg::AlphaFunc* alphaFunc =
179             new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01f);
180           stateSet->setAttributeAndModes(alphaFunc,
181                                          osg::StateAttribute::OVERRIDE);
182           stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
183           
184               lod1->addChild(entity);
185               lod15->addChild(entity);
186               lod2->addChild(entity);
187         } else {
188           lod1->addChild(entity);
189           lod15->addChild(entity);
190           lod2->addChild(entity);
191         }
192       
193         // Vary the distribution of LoDs by adding multiple times.
194         _models.push_back(lod1);
195         _models.push_back(lod1);
196         _models.push_back(lod1);
197         _models.push_back(lod1);
198
199         _models.push_back(lod15);
200         _models.push_back(lod15);
201
202         _models.push_back(lod2);
203
204       } else {
205         SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]);
206       }
207     }
208   }
209   _models_loaded = true;
210 }
211
212 osg::Node*
213 SGMatModel::get_model( int index,
214                        SGModelLib *modellib,
215                        const string &fg_root,
216                        SGPropertyNode *prop_root,
217                        double sim_time_sec )
218 {
219   load_models( modellib, fg_root, prop_root, sim_time_sec ); // comment this out if preloading models
220   return _models[index].get();
221 }
222
223 osg::Node*
224 SGMatModel::get_random_model( SGModelLib *modellib,
225                               const string &fg_root,
226                               SGPropertyNode *prop_root,
227                               double sim_time_sec )
228 {
229   load_models( modellib, fg_root, prop_root, sim_time_sec ); // comment this out if preloading models
230   int nModels = _models.size();
231   int index = int(sg_random() * nModels);
232   if (index >= nModels)
233     index = 0;
234   return _models[index].get();
235 }
236
237 double
238 SGMatModel::get_coverage_m2 () const
239 {
240   return _coverage_m2;
241 }
242
243 SGMatModel::HeadingType
244 SGMatModel::get_heading_type () const
245 {
246   return _heading_type;
247 }
248
249
250 \f
251 ////////////////////////////////////////////////////////////////////////
252 // Implementation of SGMatModelGroup.
253 ////////////////////////////////////////////////////////////////////////
254
255 SGMatModelGroup::SGMatModelGroup (SGPropertyNode * node)
256   : _range_m(node->getDoubleValue("range-m", 2000))
257 {
258                                 // Load the object subnodes
259   vector<SGPropertyNode_ptr> object_nodes =
260     ((SGPropertyNode *)node)->getChildren("object");
261   for (unsigned int i = 0; i < object_nodes.size(); i++) {
262     const SGPropertyNode * object_node = object_nodes[i];
263     if (object_node->hasChild("path"))
264       _objects.push_back(new SGMatModel(object_node, _range_m));
265     else
266       SG_LOG(SG_INPUT, SG_ALERT, "No path supplied for object");
267   }
268 }
269
270 SGMatModelGroup::~SGMatModelGroup ()
271 {
272 }
273
274 double
275 SGMatModelGroup::get_range_m () const
276 {
277   return _range_m;
278 }
279
280 int
281 SGMatModelGroup::get_object_count () const
282 {
283   return _objects.size();
284 }
285
286 SGMatModel *
287 SGMatModelGroup::get_object (int index) const
288 {
289   return _objects[index];
290 }
291
292
293
294
295 // end of matmodel.cxx