]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/mat.cxx
51b8bbe5d39ab86d3af9e1958e8116e153c5e3ea
[simgear.git] / simgear / scene / material / mat.cxx
1 // mat.cxx -- class to handle material properties
2 //
3 // Written by Curtis Olson, started May 1998.
4 //
5 // Copyright (C) 1998 - 2000  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 <string.h>
31 #include <map>
32 #include <vector>
33 #include <string>
34
35 #include <boost/foreach.hpp>
36 #include "mat.hxx"
37
38 #include <osg/CullFace>
39 #include <osg/Material>
40 #include <osg/ShadeModel>
41 #include <osg/StateSet>
42 #include <osg/TexEnv>
43 #include <osg/Texture>
44 #include <osg/Texture2D>
45 #include <osgDB/ReaderWriter>
46 #include <osgDB/ReadFile>
47 #include <osgDB/Registry>
48 #include <osgDB/FileUtils>
49
50 #include <simgear/debug/logstream.hxx>
51 #include <simgear/misc/sg_path.hxx>
52 #include <simgear/misc/sgstream.hxx>
53 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
54 #include <simgear/props/props_io.hxx>
55 #include <simgear/scene/model/model.hxx>
56 #include <simgear/scene/util/RenderConstants.hxx>
57 #include <simgear/scene/util/StateAttributeFactory.hxx>
58
59 #include "Effect.hxx"
60 #include "Technique.hxx"
61 #include "Pass.hxx"
62
63 using std::map;
64 using std::string;
65 using namespace simgear;
66
67 \f
68 ////////////////////////////////////////////////////////////////////////
69 // Constructors and destructor.
70 ////////////////////////////////////////////////////////////////////////
71
72 SGMaterial::_internal_state::_internal_state(Effect *e, bool l,
73                                              const SGReaderWriterOptions* o)
74     : effect(e), effect_realized(l), options(o)
75 {
76 }
77
78 SGMaterial::_internal_state::_internal_state(Effect *e, const string &t, bool l, 
79                                              const SGReaderWriterOptions* o)
80     : effect(e), effect_realized(l), options(o)
81 {
82     texture_paths.push_back(std::make_pair(t,0));
83 }
84
85 void SGMaterial::_internal_state::add_texture(const std::string &t, int i)
86 {
87     texture_paths.push_back(std::make_pair(t,i));
88 }
89
90 SGMaterial::SGMaterial( const SGReaderWriterOptions* options,
91                         const SGPropertyNode *props,
92                         SGPropertyNode *prop_root )
93 {
94     init();
95     read_properties( options, props, prop_root );
96     buildEffectProperties(options);
97 }
98
99 SGMaterial::SGMaterial( const osgDB::Options* options,
100                         const SGPropertyNode *props, 
101                         SGPropertyNode *prop_root)
102 {
103     osg::ref_ptr<const SGReaderWriterOptions> sgOptions;
104     if (options)
105         sgOptions = new SGReaderWriterOptions(*options);
106     init();
107     read_properties( sgOptions.get(), props, prop_root);
108     buildEffectProperties(sgOptions.get());
109 }
110
111 SGMaterial::~SGMaterial (void)
112 {
113 }
114
115 \f
116 ////////////////////////////////////////////////////////////////////////
117 // Public methods.
118 ////////////////////////////////////////////////////////////////////////
119
120 void
121 SGMaterial::read_properties(const SGReaderWriterOptions* options,
122                             const SGPropertyNode *props,
123                             SGPropertyNode *prop_root)
124 {
125   std::vector<bool> dds;
126   std::vector<SGPropertyNode_ptr> textures = props->getChildren("texture");
127   for (unsigned int i = 0; i < textures.size(); i++)
128   {
129     string tname = textures[i]->getStringValue();
130     
131     if (tname.empty()) {
132         tname = "unknown.rgb";
133     }
134     
135     SGPath tpath("Textures.high");
136     tpath.append(tname);
137     string fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
138     if (fullTexPath.empty()) {
139       tpath = SGPath("Textures");
140       tpath.append(tname);
141       fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
142     }
143     
144     if (tpath.lower_extension() == "dds") {
145       dds.push_back(true);
146     } else {
147       dds.push_back(false);      
148     }  
149     
150     if (!fullTexPath.empty() ) {
151       _internal_state st( NULL, fullTexPath, false, options );
152       _status.push_back( st );
153     }
154   }
155
156   std::vector<SGPropertyNode_ptr> texturesets = props->getChildren("texture-set");
157   for (unsigned int i = 0; i < texturesets.size(); i++)
158   {
159     _internal_state st( NULL, false, options );
160     std::vector<SGPropertyNode_ptr> textures = texturesets[i]->getChildren("texture");
161     for (unsigned int j = 0; j < textures.size(); j++)
162     {
163       string tname = textures[j]->getStringValue();
164       if (tname.empty()) {
165           tname = "unknown.rgb";
166       }
167
168       SGPath tpath("Textures.high");
169       tpath.append(tname);
170       string fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
171       if (fullTexPath.empty()) {
172         tpath = SGPath("Textures");
173         tpath.append(tname);
174         fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
175       }
176       
177       if (j == 0) {
178         if (tpath.lower_extension() == "dds") {
179           dds.push_back(true);
180         } else {
181           dds.push_back(false);      
182         }  
183       }
184       
185       st.add_texture(fullTexPath, textures[j]->getIndex());
186     }
187
188     if (!st.texture_paths.empty() ) {
189       _status.push_back( st );
190     }
191   }
192
193   if (textures.size() == 0 && texturesets.size() == 0) {
194     SGPath tpath("Textures");
195     tpath.append("Terrain");
196     tpath.append("unknown.rgb");
197     _internal_state st( NULL, tpath.str(), true, options );
198     _status.push_back( st );
199   }
200   
201   std::vector<SGPropertyNode_ptr> masks = props->getChildren("object-mask");
202   for (unsigned int i = 0; i < masks.size(); i++)
203   {
204     string omname = masks[i]->getStringValue();
205     
206     if (! omname.empty()) {
207       SGPath ompath("Textures.high");
208       ompath.append(omname);
209       string fullMaskPath = SGModelLib::findDataFile(ompath.str(), options);
210       
211       if (fullMaskPath.empty()) {
212         ompath = SGPath("Textures");
213         ompath.append(omname);
214         fullMaskPath = SGModelLib::findDataFile(ompath.str(), options);
215       }    
216       
217       osg::Image* image = osgDB::readImageFile(fullMaskPath, options);
218       if (image->valid())
219       {
220         osg::Texture2D* object_mask = new osg::Texture2D;
221         
222         bool dds_mask = (ompath.lower_extension() == "dds");
223         
224         if (dds[i] != dds_mask) {
225           // Texture format does not match mask format. This is relevant for 
226           // the object mask, as DDS textures have an origin at the bottom 
227           // left rather than top left, therefore we flip the object mask 
228           // vertically.
229           image->flipVertical();          
230         }
231         
232         object_mask->setImage(image);
233         
234         // We force the filtering to be nearest, as the red channel (rotation)
235         // in particular, doesn't make sense to be interpolated between pixels.
236         object_mask->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST);
237         object_mask->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST);
238         
239         object_mask->setDataVariance(osg::Object::STATIC);
240         object_mask->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
241         object_mask->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
242         _masks.push_back(object_mask);
243       } 
244     }
245   } 
246
247   xsize = props->getDoubleValue("xsize", 0.0);
248   ysize = props->getDoubleValue("ysize", 0.0);
249   wrapu = props->getBoolValue("wrapu", true);
250   wrapv = props->getBoolValue("wrapv", true);
251   mipmap = props->getBoolValue("mipmap", true);
252   light_coverage = props->getDoubleValue("light-coverage", 0.0);
253   wood_coverage = props->getDoubleValue("wood-coverage", 0.0);
254   tree_height = props->getDoubleValue("tree-height-m", 0.0);
255   tree_width = props->getDoubleValue("tree-width-m", 0.0);
256   tree_range = props->getDoubleValue("tree-range-m", 0.0);
257   tree_varieties = props->getIntValue("tree-varieties", 1);
258
259   const SGPropertyNode* treeTexNode = props->getChild("tree-texture");
260   
261   if (treeTexNode) {
262     string treeTexPath = props->getStringValue("tree-texture");
263     
264     if (! treeTexPath.empty()) {
265       SGPath treePath("Textures.high");
266       treePath.append(treeTexPath);
267       tree_texture = SGModelLib::findDataFile(treePath.str(), options);
268       
269       if (tree_texture.empty()) {
270         treePath = SGPath("Textures");
271         treePath.append(treeTexPath);
272         tree_texture = SGModelLib::findDataFile(treePath.str(), options);
273       }    
274     }
275   }
276   
277   // surface values for use with ground reactions
278   solid = props->getBoolValue("solid", true);
279   friction_factor = props->getDoubleValue("friction-factor", 1.0);
280   rolling_friction = props->getDoubleValue("rolling-friction", 0.02);
281   bumpiness = props->getDoubleValue("bumpiness", 0.0);
282   load_resistance = props->getDoubleValue("load-resistance", 1e30);
283
284   // Taken from default values as used in ac3d
285   ambient[0] = props->getDoubleValue("ambient/r", 0.2);
286   ambient[1] = props->getDoubleValue("ambient/g", 0.2);
287   ambient[2] = props->getDoubleValue("ambient/b", 0.2);
288   ambient[3] = props->getDoubleValue("ambient/a", 1.0);
289
290   diffuse[0] = props->getDoubleValue("diffuse/r", 0.8);
291   diffuse[1] = props->getDoubleValue("diffuse/g", 0.8);
292   diffuse[2] = props->getDoubleValue("diffuse/b", 0.8);
293   diffuse[3] = props->getDoubleValue("diffuse/a", 1.0);
294
295   specular[0] = props->getDoubleValue("specular/r", 0.0);
296   specular[1] = props->getDoubleValue("specular/g", 0.0);
297   specular[2] = props->getDoubleValue("specular/b", 0.0);
298   specular[3] = props->getDoubleValue("specular/a", 1.0);
299
300   emission[0] = props->getDoubleValue("emissive/r", 0.0);
301   emission[1] = props->getDoubleValue("emissive/g", 0.0);
302   emission[2] = props->getDoubleValue("emissive/b", 0.0);
303   emission[3] = props->getDoubleValue("emissive/a", 1.0);
304
305   shininess = props->getDoubleValue("shininess", 1.0);
306
307   if (props->hasChild("effect"))
308       effect = props->getStringValue("effect");
309   
310   std::vector<SGPropertyNode_ptr> object_group_nodes =
311     ((SGPropertyNode *)props)->getChildren("object-group");
312   for (unsigned int i = 0; i < object_group_nodes.size(); i++)
313     object_groups.push_back(new SGMatModelGroup(object_group_nodes[i]));
314
315   // read glyph table for taxi-/runway-signs
316   std::vector<SGPropertyNode_ptr> glyph_nodes = props->getChildren("glyph");
317   for (unsigned int i = 0; i < glyph_nodes.size(); i++) {
318     const char *name = glyph_nodes[i]->getStringValue("name");
319     if (name)
320       glyphs[name] = new SGMaterialGlyph(glyph_nodes[i]);
321   }
322   
323   // Read conditions node  
324   const SGPropertyNode *conditionNode = props->getChild("condition");
325   if (conditionNode) {
326     condition = sgReadCondition(prop_root, conditionNode);
327   } 
328   
329   
330 }
331
332
333 \f
334 ////////////////////////////////////////////////////////////////////////
335 // Private methods.
336 ////////////////////////////////////////////////////////////////////////
337
338 void 
339 SGMaterial::init ()
340 {
341     _status.clear();
342     xsize = 0;
343     ysize = 0;
344     wrapu = true;
345     wrapv = true;
346
347     mipmap = true;
348     light_coverage = 0.0;
349
350     solid = true;
351     friction_factor = 1;
352     rolling_friction = 0.02;
353     bumpiness = 0;
354     load_resistance = 1e30;
355
356     shininess = 1.0;
357     for (int i = 0; i < 4; i++) {
358         ambient[i]  = (i < 3) ? 0.2 : 1.0;
359         specular[i] = (i < 3) ? 0.0 : 1.0;
360         diffuse[i]  = (i < 3) ? 0.8 : 1.0;
361         emission[i] = (i < 3) ? 0.0 : 1.0;
362     }
363     effect = "Effects/terrain-default";
364 }
365
366 Effect* SGMaterial::get_effect(int i)
367 {    
368     if(!_status[i].effect_realized) {
369         _status[i].effect->realizeTechniques(_status[i].options.get());
370         _status[i].effect_realized = true;
371     }
372     return _status[i].effect.get();
373 }
374
375 Effect* SGMaterial::get_effect(SGTexturedTriangleBin triangleBin)
376 {
377     if (_status.size() == 0) {
378         SG_LOG( SG_GENERAL, SG_WARN, "No effect available.");
379         return 0;
380     }
381     
382     int i = triangleBin.getTextureIndex() % _status.size();
383     return get_effect(i);
384 }
385
386 Effect* SGMaterial::get_effect()
387 {
388     return get_effect(0);
389 }
390
391
392 osg::Texture2D* SGMaterial::get_object_mask(SGTexturedTriangleBin triangleBin)
393 {
394     if (_status.size() == 0) {
395         SG_LOG( SG_GENERAL, SG_WARN, "No mask available.");
396         return 0;
397     }
398     
399     // Note that the object mask is closely linked to the texture/effect
400     // so we index based on the texture index, 
401     unsigned int i = triangleBin.getTextureIndex() % _status.size();
402     if (i < _masks.size()) {
403         return _masks[i];      
404     } else {
405         return 0;      
406     }
407 }
408
409 void SGMaterial::buildEffectProperties(const SGReaderWriterOptions* options)
410 {
411     using namespace osg;
412     ref_ptr<SGReaderWriterOptions> xmlOptions;
413     if (options)
414         xmlOptions = new SGReaderWriterOptions(*options);
415     ref_ptr<SGMaterialUserData> user = new SGMaterialUserData(this);
416     SGPropertyNode_ptr propRoot = new SGPropertyNode();
417     makeChild(propRoot, "inherits-from")->setStringValue(effect);
418     SGPropertyNode* paramProp = makeChild(propRoot, "parameters");
419     SGPropertyNode* materialProp = makeChild(paramProp, "material");
420     makeChild(materialProp, "ambient")->setValue(SGVec4d(ambient));    
421     makeChild(materialProp, "diffuse")->setValue(SGVec4d(diffuse));
422     makeChild(materialProp, "specular")->setValue(SGVec4d(specular));
423     makeChild(materialProp, "emissive")->setValue(SGVec4d(emission));
424     makeChild(materialProp, "shininess")->setFloatValue(shininess);
425     if (ambient[3] < 1 || diffuse[3] < 1 ||
426         specular[3] < 1 || emission[3] < 1) {
427         makeChild(paramProp, "transparent")->setBoolValue(true);
428         SGPropertyNode* binProp = makeChild(paramProp, "render-bin");
429         makeChild(binProp, "bin-number")->setIntValue(TRANSPARENT_BIN);
430         makeChild(binProp, "bin-name")->setStringValue("DepthSortedBin");
431     }
432     BOOST_FOREACH(_internal_state& matState, _status)
433     {
434         SGPropertyNode_ptr effectProp = new SGPropertyNode();
435         copyProperties(propRoot, effectProp);
436         SGPropertyNode* effectParamProp = effectProp->getChild("parameters", 0);
437         for (unsigned int i = 0; i < matState.texture_paths.size(); i++) {
438             SGPropertyNode* texProp = makeChild(effectParamProp, "texture", matState.texture_paths[i].second);
439             makeChild(texProp, "image")->setStringValue(matState.texture_paths[i].first);
440             makeChild(texProp, "filter")
441                 ->setStringValue(mipmap ? "linear-mipmap-linear" : "nearest");
442             makeChild(texProp, "wrap-s")
443                 ->setStringValue(wrapu ? "repeat" : "clamp");
444             makeChild(texProp, "wrap-t")
445                 ->setStringValue(wrapv ? "repeat" : "clamp");
446         }
447         makeChild(effectParamProp, "xsize")->setDoubleValue(xsize);
448         makeChild(effectParamProp, "ysize")->setDoubleValue(ysize);
449         makeChild(effectParamProp, "scale")->setValue(SGVec3d(xsize,ysize,0.0));
450         makeChild(effectParamProp, "light-coverage")->setDoubleValue(light_coverage);
451
452         matState.effect = makeEffect(effectProp, false, xmlOptions.get());
453         matState.effect->setUserData(user.get());
454     }
455 }
456
457 SGMaterialGlyph* SGMaterial::get_glyph (const string& name) const
458 {
459   map<string, SGSharedPtr<SGMaterialGlyph> >::const_iterator it;
460   it = glyphs.find(name);
461   if (it == glyphs.end())
462     return 0;
463
464   return it->second;
465 }
466
467 \f
468 ////////////////////////////////////////////////////////////////////////
469 // SGMaterialGlyph.
470 ////////////////////////////////////////////////////////////////////////
471
472 SGMaterialGlyph::SGMaterialGlyph(SGPropertyNode *p) :
473     _left(p->getDoubleValue("left", 0.0)),
474     _right(p->getDoubleValue("right", 1.0))
475 {
476 }
477
478 void
479 SGSetTextureFilter( int max) {
480         SGSceneFeatures::instance()->setTextureFilter( max);
481 }
482
483 int
484 SGGetTextureFilter() {
485         return SGSceneFeatures::instance()->getTextureFilter();
486 }