]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/mat.cxx
Moved fgfs_src/Object/newmat.[ch]xx and fgfs_src/Object/matlib.[ch]xx into
[simgear.git] / simgear / scene / material / mat.cxx
1 // newmat.cxx -- class to handle material properties
2 //
3 // Written by Curtis Olson, started May 1998.
4 //
5 // Copyright (C) 1998 - 2000  Curtis L. Olson  - curt@flightgear.org
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., 675 Mass Ave, Cambridge, MA 02139, 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 <simgear/compiler.h>
34
35 #ifdef SG_MATH_EXCEPTION_CLASH
36 #  include <math.h>
37 #endif
38
39 #include <simgear/debug/logstream.hxx>
40 #include <simgear/math/sg_random.h>
41 #include <simgear/misc/sg_path.hxx>
42 #include <simgear/misc/sgstream.hxx>
43 #include <simgear/scene/model/loader.hxx>
44
45 #include "mat.hxx"
46
47 \f
48 ////////////////////////////////////////////////////////////////////////
49 // Local static functions.
50 ////////////////////////////////////////////////////////////////////////
51
52 /**
53  * Internal method to test whether a file exists.
54  *
55  * TODO: this should be moved to a SimGear library of local file
56  * functions.
57  */
58 static inline bool
59 local_file_exists( const string& path ) {
60     sg_gzifstream in( path );
61     if ( ! in.is_open() ) {
62         return false;
63     } else {
64         return true;
65     }
66 }
67
68
69 \f
70 ////////////////////////////////////////////////////////////////////////
71 // Implementation of FGNewMat::Object.
72 ////////////////////////////////////////////////////////////////////////
73
74 FGNewMat::Object::Object (const SGPropertyNode * node, double range_m)
75   : _models_loaded(false),
76     _coverage_m2(node->getDoubleValue("coverage-m2", 1000000)),
77     _range_m(range_m)
78 {
79                                 // Sanity check
80   if (_coverage_m2 < 1000) {
81     SG_LOG(SG_INPUT, SG_ALERT, "Random object coverage " << _coverage_m2
82            << " is too small, forcing, to 1000");
83     _coverage_m2 = 1000;
84   }
85
86                                 // Note all the model paths
87   vector <SGPropertyNode_ptr> path_nodes = node->getChildren("path");
88   for (unsigned int i = 0; i < path_nodes.size(); i++)
89     _paths.push_back(path_nodes[i]->getStringValue());
90
91                                 // Note the heading type
92   string hdg = node->getStringValue("heading-type", "fixed");
93   if (hdg == "fixed") {
94     _heading_type = HEADING_FIXED;
95   } else if (hdg == "billboard") {
96     _heading_type = HEADING_BILLBOARD;
97   } else if (hdg == "random") {
98     _heading_type = HEADING_RANDOM;
99   } else {
100     _heading_type = HEADING_FIXED;
101     SG_LOG(SG_INPUT, SG_ALERT, "Unknown heading type: " << hdg
102            << "; using 'fixed' instead.");
103   }
104
105   // uncomment to preload models
106   // load_models();
107 }
108
109 FGNewMat::Object::~Object ()
110 {
111   for (unsigned int i = 0; i < _models.size(); i++) {
112     if (_models[i] != 0) {
113       _models[i]->deRef();
114       _models[i] = 0;
115     }
116   }
117 }
118
119 int
120 FGNewMat::Object::get_model_count( FGModelLoader *loader,
121                                    const string &fg_root,
122                                    SGPropertyNode *prop_root,
123                                    double sim_time_sec )
124 {
125   load_models( loader, fg_root, prop_root, sim_time_sec );
126   return _models.size();
127 }
128
129 inline void
130 FGNewMat::Object::load_models ( FGModelLoader *loader,
131                                 const string &fg_root,
132                                 SGPropertyNode *prop_root,
133                                 double sim_time_sec )
134 {
135                                 // Load model only on demand
136   if (!_models_loaded) {
137     for (unsigned int i = 0; i < _paths.size(); i++) {
138       ssgEntity *entity = loader->load_model( fg_root, _paths[i],
139                                               prop_root, sim_time_sec );
140       if (entity != 0) {
141                                 // FIXME: this stuff can be handled
142                                 // in the XML wrapper as well (at least,
143                                 // the billboarding should be handled
144                                 // there).
145         float ranges[] = {0, _range_m};
146         ssgRangeSelector * lod = new ssgRangeSelector;
147         lod->ref();
148         lod->setRanges(ranges, 2);
149         if (_heading_type == HEADING_BILLBOARD) {
150           ssgCutout * cutout = new ssgCutout(false);
151           cutout->addKid(entity);
152           lod->addKid(cutout);
153         } else {
154           lod->addKid(entity);
155         }
156         _models.push_back(lod);
157       } else {
158         SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]);
159       }
160     }
161   }
162   _models_loaded = true;
163 }
164
165 ssgEntity *
166 FGNewMat::Object::get_model( int index,
167                              FGModelLoader *loader,
168                              const string &fg_root,
169                              SGPropertyNode *prop_root,
170                              double sim_time_sec )
171 {
172   load_models( loader, fg_root, prop_root, sim_time_sec ); // comment this out if preloading models
173   return _models[index];
174 }
175
176 ssgEntity *
177 FGNewMat::Object::get_random_model( FGModelLoader *loader,
178                                     const string &fg_root,
179                                     SGPropertyNode *prop_root,
180                                     double sim_time_sec )
181 {
182   load_models( loader, fg_root, prop_root, sim_time_sec ); // comment this out if preloading models
183   int nModels = _models.size();
184   int index = int(sg_random() * nModels);
185   if (index >= nModels)
186     index = 0;
187   return _models[index];
188 }
189
190 double
191 FGNewMat::Object::get_coverage_m2 () const
192 {
193   return _coverage_m2;
194 }
195
196 FGNewMat::Object::HeadingType
197 FGNewMat::Object::get_heading_type () const
198 {
199   return _heading_type;
200 }
201
202
203 \f
204 ////////////////////////////////////////////////////////////////////////
205 // Implementation of FGNewMat::ObjectGroup.
206 ////////////////////////////////////////////////////////////////////////
207
208 FGNewMat::ObjectGroup::ObjectGroup (SGPropertyNode * node)
209   : _range_m(node->getDoubleValue("range-m", 2000))
210 {
211                                 // Load the object subnodes
212   vector<SGPropertyNode_ptr> object_nodes =
213     ((SGPropertyNode *)node)->getChildren("object");
214   for (unsigned int i = 0; i < object_nodes.size(); i++) {
215     const SGPropertyNode * object_node = object_nodes[i];
216     if (object_node->hasChild("path"))
217       _objects.push_back(new Object(object_node, _range_m));
218     else
219       SG_LOG(SG_INPUT, SG_ALERT, "No path supplied for object");
220   }
221 }
222
223 FGNewMat::ObjectGroup::~ObjectGroup ()
224 {
225   for (unsigned int i = 0; i < _objects.size(); i++) {
226     delete _objects[i];
227     _objects[i] = 0;
228   }
229 }
230
231 double
232 FGNewMat::ObjectGroup::get_range_m () const
233 {
234   return _range_m;
235 }
236
237 int
238 FGNewMat::ObjectGroup::get_object_count () const
239 {
240   return _objects.size();
241 }
242
243 FGNewMat::Object *
244 FGNewMat::ObjectGroup::get_object (int index) const
245 {
246   return _objects[index];
247 }
248
249
250 \f
251 ////////////////////////////////////////////////////////////////////////
252 // Constructors and destructor.
253 ////////////////////////////////////////////////////////////////////////
254
255
256 FGNewMat::FGNewMat( const string &fg_root,
257                     const SGPropertyNode *props,
258                     bool smooth_shading,
259                     bool use_textures )
260 {
261     init();
262     read_properties( fg_root, props );
263     build_ssg_state( false, smooth_shading, use_textures );
264 }
265
266 FGNewMat::FGNewMat( const string &texpath,
267                     bool smooth_shading,
268                     bool use_textures )
269 {
270     init();
271     texture_path = texpath;
272     build_ssg_state( true, smooth_shading, use_textures );
273 }
274
275 FGNewMat::FGNewMat( ssgSimpleState *s,
276                     bool smooth_shading,
277                     bool use_textures )
278 {
279     init();
280     set_ssg_state( s, smooth_shading, use_textures );
281 }
282
283 FGNewMat::~FGNewMat (void)
284 {
285   for (unsigned int i = 0; i < object_groups.size(); i++) {
286     delete object_groups[i];
287     object_groups[i] = 0;
288   }
289 }
290
291
292 \f
293 ////////////////////////////////////////////////////////////////////////
294 // Public methods.
295 ////////////////////////////////////////////////////////////////////////
296
297 void
298 FGNewMat::read_properties( const string &fg_root, const SGPropertyNode * props )
299 {
300                                 // Get the path to the texture
301   string tname = props->getStringValue("texture", "unknown.rgb");
302   SGPath tpath( fg_root );
303   tpath.append("Textures.high");
304   tpath.append(tname);
305   if (!local_file_exists(tpath.str())) {
306     tpath = SGPath( fg_root );
307     tpath.append("Textures");
308     tpath.append(tname);
309   }
310   texture_path = tpath.str();
311
312   xsize = props->getDoubleValue("xsize", 0.0);
313   ysize = props->getDoubleValue("ysize", 0.0);
314   wrapu = props->getBoolValue("wrapu", true);
315   wrapv = props->getBoolValue("wrapv", true);
316   mipmap = props->getBoolValue("mipmap", true);
317   light_coverage = props->getDoubleValue("light-coverage", 0.0);
318
319   ambient[0] = props->getDoubleValue("ambient/r", 0.0);
320   ambient[1] = props->getDoubleValue("ambient/g", 0.0);
321   ambient[2] = props->getDoubleValue("ambient/b", 0.0);
322   ambient[3] = props->getDoubleValue("ambient/a", 0.0);
323
324   diffuse[0] = props->getDoubleValue("diffuse/r", 0.0);
325   diffuse[1] = props->getDoubleValue("diffuse/g", 0.0);
326   diffuse[2] = props->getDoubleValue("diffuse/b", 0.0);
327   diffuse[3] = props->getDoubleValue("diffuse/a", 0.0);
328
329   specular[0] = props->getDoubleValue("specular/r", 0.0);
330   specular[1] = props->getDoubleValue("specular/g", 0.0);
331   specular[2] = props->getDoubleValue("specular/b", 0.0);
332   specular[3] = props->getDoubleValue("specular/a", 0.0);
333
334   emission[0] = props->getDoubleValue("emissive/r", 0.0);
335   emission[1] = props->getDoubleValue("emissive/g", 0.0);
336   emission[2] = props->getDoubleValue("emissive/b", 0.0);
337   emission[3] = props->getDoubleValue("emissive/a", 0.0);
338
339   shininess = props->getDoubleValue("shininess", 0.0);
340
341   vector<SGPropertyNode_ptr> object_group_nodes =
342     ((SGPropertyNode *)props)->getChildren("object-group");
343   for (unsigned int i = 0; i < object_group_nodes.size(); i++)
344     object_groups.push_back(new ObjectGroup(object_group_nodes[i]));
345 }
346
347
348 \f
349 ////////////////////////////////////////////////////////////////////////
350 // Private methods.
351 ////////////////////////////////////////////////////////////////////////
352
353 void 
354 FGNewMat::init ()
355 {
356     texture_path = "";
357     state = 0;
358     textured = 0;
359     nontextured = 0;
360     xsize = 0;
361     ysize = 0;
362     wrapu = true;
363     wrapv = true;
364     mipmap = true;
365     light_coverage = 0.0;
366     texture_loaded = false;
367     refcount = 0;
368     shininess = 0.0;
369     for (int i = 0; i < 4; i++) {
370         ambient[i] = diffuse[i] = specular[i] = emission[i] = 0.0;
371     }
372 }
373
374 bool
375 FGNewMat::load_texture ()
376 {
377     if (texture_loaded) {
378         return false;
379     } else {
380         SG_LOG( SG_GENERAL, SG_INFO, "Loading deferred texture "
381                 << texture_path );
382         textured->setTexture( (char *)texture_path.c_str(),
383                               wrapu, wrapv, mipmap );
384         texture_loaded = true;
385         return true;
386     }
387 }
388
389
390 void 
391 FGNewMat::build_ssg_state( bool defer_tex_load,
392                            bool smooth_shading,
393                            bool use_textures )
394 {
395     GLenum shade_model = ( smooth_shading ? GL_SMOOTH : GL_FLAT);
396     
397     state = new ssgStateSelector(2);
398     state->ref();
399
400     textured = new ssgSimpleState();
401     textured->ref();
402
403     nontextured = new ssgSimpleState();
404     nontextured->ref();
405
406     // Set up the textured state
407     textured->setShadeModel( shade_model );
408     textured->enable( GL_LIGHTING );
409     textured->enable ( GL_CULL_FACE ) ;
410     textured->enable( GL_TEXTURE_2D );
411     textured->disable( GL_BLEND );
412     textured->disable( GL_ALPHA_TEST );
413     if ( !defer_tex_load ) {
414         SG_LOG(SG_INPUT, SG_INFO, "    " << texture_path );
415         textured->setTexture( (char *)texture_path.c_str(), wrapu, wrapv );
416         texture_loaded = true;
417     } else {
418         texture_loaded = false;
419     }
420     textured->enable( GL_COLOR_MATERIAL );
421 #if 0
422     textured->setColourMaterial( GL_AMBIENT_AND_DIFFUSE );
423     textured->setMaterial( GL_EMISSION, 0, 0, 0, 1 );
424     textured->setMaterial( GL_SPECULAR, 0, 0, 0, 1 );
425 #else
426     textured->setMaterial ( GL_AMBIENT,
427                             ambient[0], ambient[1],
428                             ambient[2], ambient[3] ) ;
429     textured->setMaterial ( GL_DIFFUSE,
430                             diffuse[0], diffuse[1],
431                             diffuse[2], diffuse[3] ) ;
432     textured->setMaterial ( GL_SPECULAR,
433                             specular[0], specular[1],
434                             specular[2], specular[3] ) ;
435     textured->setMaterial ( GL_EMISSION,
436                             emission[0], emission[1],
437                             emission[2], emission[3] ) ;
438     textured->setShininess ( shininess );
439 #endif
440
441     // Set up the coloured state
442     nontextured->enable( GL_LIGHTING );
443     nontextured->setShadeModel( shade_model );
444     nontextured->enable ( GL_CULL_FACE      ) ;
445     nontextured->disable( GL_TEXTURE_2D );
446     nontextured->disable( GL_BLEND );
447     nontextured->disable( GL_ALPHA_TEST );
448     nontextured->disable( GL_COLOR_MATERIAL );
449
450     nontextured->setMaterial ( GL_AMBIENT, 
451                                ambient[0], ambient[1], 
452                                ambient[2], ambient[3] ) ;
453     nontextured->setMaterial ( GL_DIFFUSE, 
454                                diffuse[0], diffuse[1], 
455                                diffuse[2], diffuse[3] ) ;
456     nontextured->setMaterial ( GL_SPECULAR, 
457                                specular[0], specular[1], 
458                                specular[2], specular[3] ) ;
459     nontextured->setMaterial ( GL_EMISSION, 
460                                emission[0], emission[1], 
461                                emission[2], emission[3] ) ;
462     nontextured->setShininess ( shininess );
463
464     state->setStep( 0, textured );    // textured
465     state->setStep( 1, nontextured ); // untextured
466
467     // Choose the appropriate starting state.
468     if ( use_textures ) {
469         state->selectStep(0);
470     } else {
471         state->selectStep(1);
472     }
473 }
474
475
476 void FGNewMat::set_ssg_state( ssgSimpleState *s,
477                               bool smooth_shading, bool use_textures )
478 {
479     GLenum shade_model = ( smooth_shading ? GL_SMOOTH : GL_FLAT);
480
481     state = new ssgStateSelector(2);
482     state->ref();
483
484     textured = s;
485     texture_loaded = true;
486
487     nontextured = new ssgSimpleState();
488     nontextured->ref();
489
490     // Set up the textured state
491     textured->setShadeModel( shade_model );
492
493     // Set up the coloured state
494     nontextured->enable( GL_LIGHTING );
495     nontextured->setShadeModel( shade_model );
496     nontextured->enable ( GL_CULL_FACE      ) ;
497     nontextured->disable( GL_TEXTURE_2D );
498     nontextured->disable( GL_BLEND );
499     nontextured->disable( GL_ALPHA_TEST );
500     nontextured->disable( GL_COLOR_MATERIAL );
501
502     nontextured->setMaterial ( GL_AMBIENT, 
503                                ambient[0], ambient[1], 
504                                ambient[2], ambient[3] ) ;
505     nontextured->setMaterial ( GL_DIFFUSE, 
506                                diffuse[0], diffuse[1], 
507                                diffuse[2], diffuse[3] ) ;
508     nontextured->setMaterial ( GL_SPECULAR, 
509                                specular[0], specular[1], 
510                                specular[2], specular[3] ) ;
511     nontextured->setMaterial ( GL_EMISSION, 
512                                emission[0], emission[1], 
513                                emission[2], emission[3] ) ;
514     nontextured->setShininess ( shininess );
515
516     state->setStep( 0, textured );    // textured
517     state->setStep( 1, nontextured ); // untextured
518
519     // Choose the appropriate starting state.
520     if ( use_textures ) {
521         state->selectStep(0);
522     } else {
523         state->selectStep(1);
524     }
525 }
526
527 // end of newmat.cxx