]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/mat.cxx
Use Effect to implement point lights
[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 "mat.hxx"
36
37 #include <osg/CullFace>
38 #include <osg/Material>
39 #include <osg/ShadeModel>
40 #include <osg/StateSet>
41 #include <osg/TexEnv>
42 #include <osg/Texture2D>
43 #include <osgDB/ReadFile>
44 #include <osgDB/FileUtils>
45
46 #include <simgear/debug/logstream.hxx>
47 #include <simgear/misc/sg_path.hxx>
48 #include <simgear/misc/sgstream.hxx>
49
50 #include <simgear/scene/model/model.hxx>
51 #include <simgear/scene/util/StateAttributeFactory.hxx>
52
53 #include "Effect.hxx"
54 #include "Technique.hxx"
55 #include "Pass.hxx"
56
57 using std::map;
58 using namespace simgear;
59
60 \f
61 ////////////////////////////////////////////////////////////////////////
62 // Constructors and destructor.
63 ////////////////////////////////////////////////////////////////////////
64
65 SGMaterial::_internal_state::_internal_state(osg::StateSet *s,
66                                              const std::string &t, bool l ) :
67     state(s), texture_path(t), texture_loaded(l)
68 {
69 }
70
71 SGMaterial::SGMaterial( const string &fg_root, const SGPropertyNode *props )
72 {
73     init();
74     read_properties( fg_root, props );
75     build_state( false );
76 }
77
78 SGMaterial::SGMaterial( const string &texpath )
79 {
80     init();
81
82     _internal_state st( NULL, texpath, false );
83     _status.push_back( st );
84
85     build_state( true );
86 }
87
88 SGMaterial::SGMaterial( osg::StateSet *s )
89 {
90     init();
91     set_state( s );
92 }
93
94 SGMaterial::~SGMaterial (void)
95 {
96 }
97
98 \f
99 ////////////////////////////////////////////////////////////////////////
100 // Public methods.
101 ////////////////////////////////////////////////////////////////////////
102
103 void
104 SGMaterial::read_properties( const string &fg_root, const SGPropertyNode *props)
105 {
106                                 // Gather the path(s) to the texture(s)
107   vector<SGPropertyNode_ptr> textures = props->getChildren("texture");
108   for (unsigned int i = 0; i < textures.size(); i++)
109   {
110     string tname = textures[i]->getStringValue();
111     if (tname.empty()) {
112         tname = "unknown.rgb";
113     }
114
115     SGPath tpath( fg_root );
116     tpath.append("Textures.high");
117     tpath.append(tname);
118     if ( !osgDB::fileExists(tpath.str()) ) {
119       tpath = SGPath( fg_root );
120       tpath.append("Textures");
121       tpath.append(tname);
122     }
123
124     if ( osgDB::fileExists(tpath.str()) ) {
125       _internal_state st( NULL, tpath.str(), false );
126       _status.push_back( st );
127     }
128   }
129
130   if (textures.size() == 0) {
131     string tname = "unknown.rgb";
132     SGPath tpath( fg_root );
133     tpath.append("Textures");
134     tpath.append("Terrain");
135     tpath.append(tname);
136     _internal_state st( NULL, tpath.str(), true );
137     _status.push_back( st );
138   }
139
140   xsize = props->getDoubleValue("xsize", 0.0);
141   ysize = props->getDoubleValue("ysize", 0.0);
142   wrapu = props->getBoolValue("wrapu", true);
143   wrapv = props->getBoolValue("wrapv", true);
144   mipmap = props->getBoolValue("mipmap", true);
145   light_coverage = props->getDoubleValue("light-coverage", 0.0);
146   tree_coverage = props->getDoubleValue("tree-coverage", 0.0);
147   tree_height = props->getDoubleValue("tree-height-m", 0.0);
148   tree_width = props->getDoubleValue("tree-width-m", 0.0);
149   tree_range = props->getDoubleValue("tree-range-m", 0.0);
150   tree_varieties = props->getIntValue("tree-varieties", 1);
151
152   SGPath tpath( fg_root );
153   tpath.append(props->getStringValue("tree-texture"));
154   tree_texture = tpath.str();
155
156   // surface values for use with ground reactions
157   solid = props->getBoolValue("solid", true);
158   friction_factor = props->getDoubleValue("friction-factor", 1.0);
159   rolling_friction = props->getDoubleValue("rolling-friction", 0.02);
160   bumpiness = props->getDoubleValue("bumpiness", 0.0);
161   load_resistance = props->getDoubleValue("load-resistance", 1e30);
162
163   // Taken from default values as used in ac3d
164   ambient[0] = props->getDoubleValue("ambient/r", 0.2);
165   ambient[1] = props->getDoubleValue("ambient/g", 0.2);
166   ambient[2] = props->getDoubleValue("ambient/b", 0.2);
167   ambient[3] = props->getDoubleValue("ambient/a", 1.0);
168
169   diffuse[0] = props->getDoubleValue("diffuse/r", 0.8);
170   diffuse[1] = props->getDoubleValue("diffuse/g", 0.8);
171   diffuse[2] = props->getDoubleValue("diffuse/b", 0.8);
172   diffuse[3] = props->getDoubleValue("diffuse/a", 1.0);
173
174   specular[0] = props->getDoubleValue("specular/r", 0.0);
175   specular[1] = props->getDoubleValue("specular/g", 0.0);
176   specular[2] = props->getDoubleValue("specular/b", 0.0);
177   specular[3] = props->getDoubleValue("specular/a", 1.0);
178
179   emission[0] = props->getDoubleValue("emissive/r", 0.0);
180   emission[1] = props->getDoubleValue("emissive/g", 0.0);
181   emission[2] = props->getDoubleValue("emissive/b", 0.0);
182   emission[3] = props->getDoubleValue("emissive/a", 1.0);
183
184   shininess = props->getDoubleValue("shininess", 1.0);
185
186   vector<SGPropertyNode_ptr> object_group_nodes =
187     ((SGPropertyNode *)props)->getChildren("object-group");
188   for (unsigned int i = 0; i < object_group_nodes.size(); i++)
189     object_groups.push_back(new SGMatModelGroup(object_group_nodes[i]));
190
191   // read glyph table for taxi-/runway-signs
192   vector<SGPropertyNode_ptr> glyph_nodes = props->getChildren("glyph");
193   for (unsigned int i = 0; i < glyph_nodes.size(); i++) {
194     const char *name = glyph_nodes[i]->getStringValue("name");
195     if (name)
196       glyphs[name] = new SGMaterialGlyph(glyph_nodes[i]);
197   }
198 }
199
200
201 \f
202 ////////////////////////////////////////////////////////////////////////
203 // Private methods.
204 ////////////////////////////////////////////////////////////////////////
205
206 void 
207 SGMaterial::init ()
208 {
209     _status.clear();
210     _current_ptr = 0;
211     xsize = 0;
212     ysize = 0;
213     wrapu = true;
214     wrapv = true;
215
216     mipmap = true;
217     light_coverage = 0.0;
218
219     solid = true;
220     friction_factor = 1;
221     rolling_friction = 0.02;
222     bumpiness = 0;
223     load_resistance = 1e30;
224
225     shininess = 1.0;
226     for (int i = 0; i < 4; i++) {
227         ambient[i]  = (i < 3) ? 0.2 : 1.0;
228         specular[i] = (i < 3) ? 0.0 : 1.0;
229         diffuse[i]  = (i < 3) ? 0.8 : 1.0;
230         emission[i] = (i < 3) ? 0.0 : 1.0;
231     }
232 }
233
234 osg::StateSet *
235 SGMaterial::get_state (int n)
236 {
237     if (_status.size() == 0) {
238         SG_LOG( SG_GENERAL, SG_WARN, "No state available.");
239         return NULL;
240     }
241     
242     int i = n >= 0 ? n : _current_ptr;
243
244     if(!_status[i].texture_loaded)
245     {
246         assignTexture(_status[i].state.get(), _status[i].texture_path,
247                       wrapu, wrapv, mipmap);
248         _status[i].texture_loaded = true;
249     }
250     osg::StateSet *st = _status[i].state.get();
251
252     _current_ptr += 1;
253     if (_current_ptr >= _status.size())
254         _current_ptr = 0;
255
256     return st;
257 }
258
259 Effect* SGMaterial::get_effect(int n)
260 {
261     if (_status.size() == 0) {
262         SG_LOG( SG_GENERAL, SG_WARN, "No effect available.");
263         return 0;
264     }
265     int i = n >= 0 ? n : _current_ptr;
266     if(!_status[i].texture_loaded) {
267         assignTexture(_status[i].state.get(), _status[i].texture_path,
268                       wrapu, wrapv, mipmap);
269         _status[i].texture_loaded = true;
270     }
271     // XXX This business of returning a "random" alternate texture is
272     // really bogus. It means that the appearance of the terrain
273     // depends on the order in which it is paged in!
274     _current_ptr = (_current_ptr + 1) % _status.size();
275     return _status[i].effect.get();
276 }
277
278 void 
279 SGMaterial::build_state( bool defer_tex_load )
280 {
281     StateAttributeFactory *attrFact = StateAttributeFactory::instance();
282     SGMaterialUserData* user = new SGMaterialUserData(this);
283     for (unsigned int i = 0; i < _status.size(); i++)
284     {
285         Pass *pass = new Pass;
286         pass->setUserData(user);
287
288         // Set up the textured state
289         pass->setAttribute(attrFact->getSmoothShadeModel());
290         pass->setAttributeAndModes(attrFact->getCullFaceBack());
291
292         pass->setMode(GL_LIGHTING, osg::StateAttribute::ON);
293
294         _status[i].texture_loaded = false;
295
296         osg::Material* material = new osg::Material;
297         material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
298         material->setAmbient(osg::Material::FRONT_AND_BACK, ambient.osg());
299         material->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse.osg());
300         material->setSpecular(osg::Material::FRONT_AND_BACK, specular.osg());
301         material->setEmission(osg::Material::FRONT_AND_BACK, emission.osg());
302         material->setShininess(osg::Material::FRONT_AND_BACK, shininess );
303         pass->setAttribute(material);
304
305         if (ambient[3] < 1 || diffuse[3] < 1 ||
306             specular[3] < 1 || emission[3] < 1) {
307           pass->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
308           pass->setMode(GL_BLEND, osg::StateAttribute::ON);
309           pass->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
310         } else {
311           pass->setRenderingHint(osg::StateSet::OPAQUE_BIN);
312           pass->setMode(GL_BLEND, osg::StateAttribute::OFF);
313           pass->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);
314         }
315
316         _status[i].state = pass;
317         Technique* tniq = new Technique(true);
318         tniq->passes.push_back(pass);
319         Effect* effect = new Effect;
320         effect->techniques.push_back(tniq);
321         effect->setUserData(user);
322         _status[i].effect = effect;
323     }
324 }
325
326
327 void SGMaterial::set_state( osg::StateSet *s )
328 {
329     _status.push_back( _internal_state( s, "", true ) );
330 }
331
332 void SGMaterial::assignTexture( osg::StateSet *state, const std::string &fname,
333                  bool _wrapu, bool _wrapv, bool _mipmap )
334 {
335    osg::Texture2D* texture = SGLoadTexture2D(fname, 0, _wrapu, _wrapv,
336                                              mipmap ? -1 : 0);
337    texture->setMaxAnisotropy( SGGetTextureFilter());
338    state->setTextureAttributeAndModes(0, texture);
339
340    StateAttributeFactory *attrFact = StateAttributeFactory::instance();
341    state->setTextureAttributeAndModes(0, attrFact->getStandardTexEnv());
342 }
343
344 SGMaterialGlyph* SGMaterial::get_glyph (const string& name) const
345 {
346   map<string, SGSharedPtr<SGMaterialGlyph> >::const_iterator it;
347   it = glyphs.find(name);
348   if (it == glyphs.end())
349     return 0;
350
351   return it->second;
352 }
353
354 \f
355 ////////////////////////////////////////////////////////////////////////
356 // SGMaterialGlyph.
357 ////////////////////////////////////////////////////////////////////////
358
359 SGMaterialGlyph::SGMaterialGlyph(SGPropertyNode *p) :
360     _left(p->getDoubleValue("left", 0.0)),
361     _right(p->getDoubleValue("right", 1.0))
362 {
363 }
364
365 void
366 SGSetTextureFilter( int max) {
367         SGSceneFeatures::instance()->setTextureFilter( max);
368 }
369
370 int
371 SGGetTextureFilter() {
372         return SGSceneFeatures::instance()->getTextureFilter();
373 }