]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/TextureBuilder.cxx
Merge branch 'next' of gitorious.org:fg/simgear into next
[simgear.git] / simgear / scene / material / TextureBuilder.cxx
1 // Copyright (C) 2009  Tim Moore timoore@redhat.com
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Library General Public
5 // License as published by the Free Software Foundation; either
6 // version 2 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Library General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16
17 #ifdef HAVE_CONFIG_H
18 #  include <simgear_config.h>
19 #endif
20
21 #include "TextureBuilder.hxx"
22
23 #include "Pass.hxx"
24
25 #include <osg/TexEnv>
26 #include <osg/TexEnvCombine>
27 #include <osg/TexGen>
28 #include <osg/Texture1D>
29 #include <osg/Texture2D>
30 #include <osg/Texture3D>
31 #include <osg/TextureRectangle>
32 #include <osg/TextureCubeMap>
33 #include <osgDB/FileUtils>
34
35 #include <boost/lexical_cast.hpp>
36 #include <boost/tuple/tuple.hpp>
37 #include <boost/tuple/tuple_comparison.hpp>
38
39 #include <simgear/scene/model/SGReaderWriterXMLOptions.hxx>
40 #include <simgear/scene/util/SGSceneFeatures.hxx>
41 #include <simgear/scene/util/StateAttributeFactory.hxx>
42 #include <simgear/math/SGMath.hxx>
43 #include <simgear/structure/OSGUtils.hxx>
44
45 #include "Noise.hxx"
46
47 namespace simgear
48 {
49 using namespace std;
50 using namespace osg;
51
52 using namespace effect;
53
54 TexEnvCombine* buildTexEnvCombine(Effect* effect,
55                                   const SGPropertyNode* envProp,
56                                   const SGReaderWriterXMLOptions* options);
57 TexGen* buildTexGen(Effect* Effect, const SGPropertyNode* tgenProp);
58
59 // Hack to force inclusion of TextureBuilder.cxx in library
60 osg::Texture* TextureBuilder::buildFromType(Effect* effect, const string& type,
61                                             const SGPropertyNode*props,
62                                             const SGReaderWriterXMLOptions*
63                                             options)
64 {
65     return EffectBuilder<Texture>::buildFromType(effect, type, props, options);
66 }
67
68 typedef boost::tuple<string, Texture::FilterMode, Texture::FilterMode,
69                      Texture::WrapMode, Texture::WrapMode, Texture::WrapMode,
70                      string> TexTuple;
71
72 EffectNameValue<TexEnv::Mode> texEnvModesInit[] =
73 {
74     {"add", TexEnv::ADD},
75     {"blend", TexEnv::BLEND},
76     {"decal", TexEnv::DECAL},
77     {"modulate", TexEnv::MODULATE},
78     {"replace", TexEnv::REPLACE}
79 };
80 EffectPropertyMap<TexEnv::Mode> texEnvModes(texEnvModesInit);
81
82 TexEnv* buildTexEnv(Effect* effect, const SGPropertyNode* prop)
83 {
84     const SGPropertyNode* modeProp = getEffectPropertyChild(effect, prop,
85                                                             "mode");
86     const SGPropertyNode* colorProp = getEffectPropertyChild(effect, prop,
87                                                              "color");
88     if (!modeProp)
89         return 0;
90     TexEnv::Mode mode = TexEnv::MODULATE;
91     findAttr(texEnvModes, modeProp, mode);
92     if (mode == TexEnv::MODULATE) {
93         return StateAttributeFactory::instance()->getStandardTexEnv();
94     }
95     TexEnv* env = new TexEnv(mode);
96     if (colorProp)
97         env->setColor(toOsg(colorProp->getValue<SGVec4d>()));
98     return env;
99 }
100
101
102 void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass,
103                                         const SGPropertyNode* prop,
104                                         const SGReaderWriterXMLOptions* options)
105 {
106     if (!isAttributeActive(effect, prop))
107         return;
108     // Decode the texture unit
109     int unit = 0;
110     const SGPropertyNode* pUnit = prop->getChild("unit");
111     if (pUnit) {
112         unit = pUnit->getValue<int>();
113     } else {
114         const SGPropertyNode* pName = prop->getChild("name");
115         if (pName)
116             try {
117                 unit = boost::lexical_cast<int>(pName->getStringValue());
118             } catch (boost::bad_lexical_cast& lex) {
119                 SG_LOG(SG_INPUT, SG_ALERT, "can't decode name as texture unit "
120                        << lex.what());
121             }
122     }
123     const SGPropertyNode* pType = getEffectPropertyChild(effect, prop, "type");
124     string type;
125     if (!pType)
126         type = "2d";
127     else
128         type = pType->getStringValue();
129     Texture* texture = 0;
130     try {
131         texture = TextureBuilder::buildFromType(effect, type, prop,
132                                                 options);
133     }
134     catch (BuilderException& ) {
135         SG_LOG(SG_INPUT, SG_ALERT, "No image file, "
136             << "maybe the reader did not set the filename attribute, "
137             << "using white on " << pass->getName());
138         texture = StateAttributeFactory::instance()->getWhiteTexture();
139     }
140     pass->setTextureAttributeAndModes(unit, texture);
141     const SGPropertyNode* envProp = prop->getChild("environment");
142     if (envProp) {
143         TexEnv* env = buildTexEnv(effect, envProp);
144         if (env)
145             pass->setTextureAttributeAndModes(unit, env);
146     }
147     const SGPropertyNode* combineProp = prop->getChild("texenv-combine");
148     TexEnvCombine* combiner = 0;
149     if (combineProp && ((combiner = buildTexEnvCombine(effect, combineProp,
150                                                        options))))
151         pass->setTextureAttributeAndModes(unit, combiner);
152     const SGPropertyNode* tgenProp = prop->getChild("texgen");
153     TexGen* tgen = 0;
154     if (tgenProp && (tgen = buildTexGen(effect, tgenProp)))
155         pass->setTextureAttributeAndModes(unit, tgen);
156 }
157
158 // InstallAttributeBuilder call is in Effect.cxx to force this file to
159 // be linked in.
160
161 namespace
162 {
163 EffectNameValue<Texture::FilterMode> filterModesInit[] =
164 {
165     { "linear", Texture::LINEAR },
166     { "linear-mipmap-linear", Texture::LINEAR_MIPMAP_LINEAR},
167     { "linear-mipmap-nearest", Texture::LINEAR_MIPMAP_NEAREST},
168     { "nearest", Texture::NEAREST},
169     { "nearest-mipmap-linear", Texture::NEAREST_MIPMAP_LINEAR},
170     { "nearest-mipmap-nearest", Texture::NEAREST_MIPMAP_NEAREST}
171 };
172 EffectPropertyMap<Texture::FilterMode> filterModes(filterModesInit);
173
174 EffectNameValue<Texture::WrapMode> wrapModesInit[] =
175 {
176     {"clamp", Texture::CLAMP},
177     {"clamp-to-border", Texture::CLAMP_TO_BORDER},
178     {"clamp-to-edge", Texture::CLAMP_TO_EDGE},
179     {"mirror", Texture::MIRROR},
180     {"repeat", Texture::REPEAT}
181 };
182 EffectPropertyMap<Texture::WrapMode> wrapModes(wrapModesInit);
183
184
185 TexTuple makeTexTuple(Effect* effect, const SGPropertyNode* props,
186                       const SGReaderWriterXMLOptions* options,
187                       const string& texType)
188 {
189     Texture::FilterMode minFilter = Texture::LINEAR_MIPMAP_LINEAR;
190     const SGPropertyNode* ep = 0;
191     if ((ep = getEffectPropertyChild(effect, props, "filter")))
192         findAttr(filterModes, ep, minFilter);
193     Texture::FilterMode magFilter = Texture::LINEAR;
194     if ((ep = getEffectPropertyChild(effect, props, "mag-filter")))
195         findAttr(filterModes, ep, magFilter);
196     const SGPropertyNode* pWrapS
197         = getEffectPropertyChild(effect, props, "wrap-s");
198     Texture::WrapMode sWrap = Texture::CLAMP;
199     if (pWrapS)
200         findAttr(wrapModes, pWrapS, sWrap);
201     const SGPropertyNode* pWrapT
202         = getEffectPropertyChild(effect, props, "wrap-t");
203     Texture::WrapMode tWrap = Texture::CLAMP;
204     if (pWrapT)
205         findAttr(wrapModes, pWrapT, tWrap);
206     const SGPropertyNode* pWrapR
207         = getEffectPropertyChild(effect, props, "wrap-r");
208     Texture::WrapMode rWrap = Texture::CLAMP;
209     if (pWrapR)
210         findAttr(wrapModes, pWrapR, rWrap);
211     const SGPropertyNode* pImage
212         = getEffectPropertyChild(effect, props, "image");
213     string imageName;
214     if (pImage)
215         imageName = pImage->getStringValue();
216     string absFileName = osgDB::findDataFile(imageName, options);
217     return TexTuple(absFileName, minFilter, magFilter, sWrap, tWrap, rWrap,
218                     texType);
219 }
220
221 void setAttrs(const TexTuple& attrs, Texture* tex,
222               const SGReaderWriterXMLOptions* options)
223 {
224     const string& imageName = attrs.get<0>();
225     if (imageName.empty()) {
226         throw BuilderException("no image file");
227     } else {
228         osgDB::ReaderWriter::ReadResult result
229             = osgDB::Registry::instance()->readImage(imageName, options);
230         if (result.success()) {
231             osg::Image* image = result.getImage();
232             tex->setImage(GL_FRONT_AND_BACK, image);
233             int s = image->s();
234             int t = image->t();
235             if (s <= t && 32 <= s) {
236                 SGSceneFeatures::instance()->setTextureCompression(tex);
237             } else if (t < s && 32 <= t) {
238                 SGSceneFeatures::instance()->setTextureCompression(tex);
239             }
240             tex->setMaxAnisotropy(SGSceneFeatures::instance()
241                                   ->getTextureFilter());
242         } else {
243             SG_LOG(SG_INPUT, SG_ALERT, "failed to load effect texture file "
244                    << imageName);
245         }
246     }
247     // texture->setDataVariance(osg::Object::STATIC);
248     tex->setFilter(Texture::MIN_FILTER, attrs.get<1>());
249     tex->setFilter(Texture::MAG_FILTER, attrs.get<2>());
250     tex->setWrap(Texture::WRAP_S, attrs.get<3>());
251     tex->setWrap(Texture::WRAP_T, attrs.get<4>());
252     tex->setWrap(Texture::WRAP_R, attrs.get<5>());
253 }
254 }
255
256 template<typename T>
257 class TexBuilder : public TextureBuilder
258 {
259 public:
260     TexBuilder(const string& texType) : _type(texType) {}
261     Texture* build(Effect* effect, const SGPropertyNode*,
262                    const SGReaderWriterXMLOptions* options);
263 protected:
264     typedef map<TexTuple, ref_ptr<T> > TexMap;
265     TexMap texMap;
266     const string _type;
267 };
268
269 template<typename T>
270 Texture* TexBuilder<T>::build(Effect* effect, const SGPropertyNode* props,
271                               const SGReaderWriterXMLOptions* options)
272 {
273     TexTuple attrs = makeTexTuple(effect, props, options, _type);
274     typename TexMap::iterator itr = texMap.find(attrs);
275     if (itr != texMap.end())
276         return itr->second.get();
277     T* tex = new T;
278     setAttrs(attrs, tex, options);
279     texMap.insert(make_pair(attrs, tex));
280     return tex;
281 }
282
283
284 namespace
285 {
286 TextureBuilder::Registrar install1D("1d", new TexBuilder<Texture1D>("1d"));
287 TextureBuilder::Registrar install2D("2d", new TexBuilder<Texture2D>("2d"));
288 TextureBuilder::Registrar install3D("3d", new TexBuilder<Texture3D>("3d"));
289 }
290
291 class WhiteTextureBuilder : public TextureBuilder
292 {
293 public:
294     Texture* build(Effect* effect, const SGPropertyNode*,
295                    const SGReaderWriterXMLOptions* options);
296 };
297
298 Texture* WhiteTextureBuilder::build(Effect* effect, const SGPropertyNode*,
299                                     const SGReaderWriterXMLOptions* options)
300 {
301     return StateAttributeFactory::instance()->getWhiteTexture();
302 }
303
304 namespace
305 {
306 TextureBuilder::Registrar installWhite("white", new WhiteTextureBuilder);
307 }
308
309 class TransparentTextureBuilder : public TextureBuilder
310 {
311 public:
312     Texture* build(Effect* effect, const SGPropertyNode*,
313                    const SGReaderWriterXMLOptions* options);
314 };
315
316 Texture* TransparentTextureBuilder::build(Effect* effect, const SGPropertyNode*,
317                                     const SGReaderWriterXMLOptions* options)
318 {
319     return StateAttributeFactory::instance()->getTransparentTexture();
320 }
321
322 namespace
323 {
324 TextureBuilder::Registrar installTransparent("transparent",
325                                              new TransparentTextureBuilder);
326 }
327
328 osg::Image* make3DNoiseImage(int texSize)
329 {
330     osg::Image* image = new osg::Image;
331     image->setImage(texSize, texSize, texSize,
332                     4, GL_RGBA, GL_UNSIGNED_BYTE,
333                     new unsigned char[4 * texSize * texSize * texSize],
334                     osg::Image::USE_NEW_DELETE);
335
336     const int startFrequency = 4;
337     const int numOctaves = 4;
338
339     int f, i, j, k, inc;
340     double ni[3];
341     double inci, incj, inck;
342     int frequency = startFrequency;
343     GLubyte *ptr;
344     double amp = 0.5;
345
346     osg::notify(osg::WARN) << "creating 3D noise texture... ";
347
348     for (f = 0, inc = 0; f < numOctaves; ++f, frequency *= 2, ++inc, amp *= 0.5)
349     {
350         SetNoiseFrequency(frequency);
351         ptr = image->data();
352         ni[0] = ni[1] = ni[2] = 0;
353
354         inci = 1.0 / (texSize / frequency);
355         for (i = 0; i < texSize; ++i, ni[0] += inci)
356         {
357             incj = 1.0 / (texSize / frequency);
358             for (j = 0; j < texSize; ++j, ni[1] += incj)
359             {
360                 inck = 1.0 / (texSize / frequency);
361                 for (k = 0; k < texSize; ++k, ni[2] += inck, ptr += 4)
362                 {
363                     *(ptr+inc) = (GLubyte) (((noise3(ni) + 1.0) * amp) * 128.0);
364                 }
365             }
366         }
367     }
368
369     osg::notify(osg::WARN) << "DONE" << std::endl;
370     return image;
371 }
372
373 class NoiseBuilder : public TextureBuilder
374 {
375 public:
376     Texture* build(Effect* effect, const SGPropertyNode*,
377                    const SGReaderWriterXMLOptions* options);
378 protected:
379     typedef map<int, ref_ptr<Texture3D> > NoiseMap;
380     NoiseMap _noises;
381 };
382
383 Texture* NoiseBuilder::build(Effect* effect, const SGPropertyNode* props,
384                              const SGReaderWriterXMLOptions* options)
385 {
386     int texSize = 64;
387     const SGPropertyNode* sizeProp = getEffectPropertyChild(effect, props,
388                                                             "size");
389     if (sizeProp)
390         texSize = sizeProp->getValue<int>();
391     NoiseMap::iterator itr = _noises.find(texSize);
392     if (itr != _noises.end())
393         return itr->second.get();
394     Texture3D* noiseTexture = new osg::Texture3D;
395     noiseTexture->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture3D::LINEAR);
396     noiseTexture->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture3D::LINEAR);
397     noiseTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture3D::REPEAT);
398     noiseTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture3D::REPEAT);
399     noiseTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture3D::REPEAT);
400     noiseTexture->setImage( make3DNoiseImage(texSize) );
401     _noises.insert(make_pair(texSize, noiseTexture));
402     return noiseTexture;
403 }
404
405 namespace
406 {
407 TextureBuilder::Registrar installNoise("noise", new NoiseBuilder);
408 }
409
410
411 // Image names for all sides
412 typedef boost::tuple<string, string, string, string, string, string> CubeMapTuple;
413
414 CubeMapTuple makeCubeMapTuple(Effect* effect, const SGPropertyNode* props)
415 {
416     const SGPropertyNode* ep = 0;
417
418     string positive_x;
419     if ((ep = getEffectPropertyChild(effect, props, "positive-x")))
420         positive_x = ep->getStringValue();
421     string negative_x;
422     if ((ep = getEffectPropertyChild(effect, props, "negative-x")))
423         negative_x = ep->getStringValue();
424     string positive_y;
425     if ((ep = getEffectPropertyChild(effect, props, "positive-y")))
426         positive_y = ep->getStringValue();
427     string negative_y;
428     if ((ep = getEffectPropertyChild(effect, props, "negative-y")))
429         negative_y = ep->getStringValue();
430     string positive_z;
431     if ((ep = getEffectPropertyChild(effect, props, "positive-z")))
432         positive_z = ep->getStringValue();
433     string negative_z;
434     if ((ep = getEffectPropertyChild(effect, props, "negative-z")))
435         negative_z = ep->getStringValue();
436     return CubeMapTuple(positive_x, negative_x, positive_y, negative_y, positive_z, negative_z);
437 }
438
439
440 class CubeMapBuilder : public TextureBuilder
441 {
442 public:
443     Texture* build(Effect* effect, const SGPropertyNode*,
444                    const SGReaderWriterXMLOptions* options);
445 protected:
446     typedef map<CubeMapTuple, ref_ptr<TextureCubeMap> > CubeMap;
447     CubeMap _cubemaps;
448 };
449
450 Texture* CubeMapBuilder::build(Effect* effect, const SGPropertyNode* props,
451                              const SGReaderWriterXMLOptions* options)
452 {
453     // First check that there is a <images> tag
454     const SGPropertyNode* texturesProp = getEffectPropertyChild(effect, props, "images");
455     if (!texturesProp) {
456         throw BuilderException("no <images> for cube map");
457         return NULL; // This is redundant
458     }
459
460     CubeMapTuple _tuple = makeCubeMapTuple(effect, texturesProp);
461
462     CubeMap::iterator itr = _cubemaps.find(_tuple);
463     if (itr != _cubemaps.end())
464         return itr->second.get();
465
466     TextureCubeMap* cubeTexture = new osg::TextureCubeMap;
467
468     // TODO: Read these from effect file? Maybe these are sane for all cuebmaps?
469     cubeTexture->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
470     cubeTexture->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture::LINEAR);
471     cubeTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
472     cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
473     cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
474
475     osgDB::ReaderWriter::ReadResult result =
476                 osgDB::Registry::instance()->readImage(_tuple.get<0>(), options);
477     if(result.success()) {
478         osg::Image* image = result.getImage();
479         cubeTexture->setImage(TextureCubeMap::POSITIVE_X, image);
480     }
481     result = osgDB::Registry::instance()->readImage(_tuple.get<1>(), options);
482     if(result.success()) {
483         osg::Image* image = result.getImage();
484         cubeTexture->setImage(TextureCubeMap::NEGATIVE_X, image);
485     }
486     result = osgDB::Registry::instance()->readImage(_tuple.get<2>(), options);
487     if(result.success()) {
488         osg::Image* image = result.getImage();
489         cubeTexture->setImage(TextureCubeMap::POSITIVE_Y, image);
490     }
491     result = osgDB::Registry::instance()->readImage(_tuple.get<3>(), options);
492     if(result.success()) {
493         osg::Image* image = result.getImage();
494         cubeTexture->setImage(TextureCubeMap::NEGATIVE_Y, image);
495     }
496     result = osgDB::Registry::instance()->readImage(_tuple.get<4>(), options);
497     if(result.success()) {
498         osg::Image* image = result.getImage();
499         cubeTexture->setImage(TextureCubeMap::POSITIVE_Z, image);
500     }
501     result = osgDB::Registry::instance()->readImage(_tuple.get<5>(), options);
502     if(result.success()) {
503         osg::Image* image = result.getImage();
504         cubeTexture->setImage(TextureCubeMap::NEGATIVE_Z, image);
505     }
506
507     _cubemaps[_tuple] = cubeTexture;
508
509     return cubeTexture;
510 }
511
512 namespace {
513 TextureBuilder::Registrar installCubeMap("cubemap", new CubeMapBuilder);
514 }
515
516
517
518 EffectNameValue<TexEnvCombine::CombineParam> combineParamInit[] =
519 {
520     {"replace", TexEnvCombine::REPLACE},
521     {"modulate", TexEnvCombine::MODULATE},
522     {"add", TexEnvCombine::ADD},
523     {"add-signed", TexEnvCombine::ADD_SIGNED},
524     {"interpolate", TexEnvCombine::INTERPOLATE},
525     {"subtract", TexEnvCombine::SUBTRACT},
526     {"dot3-rgb", TexEnvCombine::DOT3_RGB},
527     {"dot3-rgba", TexEnvCombine::DOT3_RGBA}
528 };
529
530 EffectPropertyMap<TexEnvCombine::CombineParam> combineParams(combineParamInit);
531
532 EffectNameValue<TexEnvCombine::SourceParam> sourceParamInit[] =
533 {
534     {"constant", TexEnvCombine::CONSTANT},
535     {"primary_color", TexEnvCombine::PRIMARY_COLOR},
536     {"previous", TexEnvCombine::PREVIOUS},
537     {"texture", TexEnvCombine::TEXTURE},
538     {"texture0", TexEnvCombine::TEXTURE0},
539     {"texture1", TexEnvCombine::TEXTURE1},
540     {"texture2", TexEnvCombine::TEXTURE2},
541     {"texture3", TexEnvCombine::TEXTURE3},
542     {"texture4", TexEnvCombine::TEXTURE4},
543     {"texture5", TexEnvCombine::TEXTURE5},
544     {"texture6", TexEnvCombine::TEXTURE6},
545     {"texture7", TexEnvCombine::TEXTURE7}
546 };
547
548 EffectPropertyMap<TexEnvCombine::SourceParam> sourceParams(sourceParamInit);
549
550 EffectNameValue<TexEnvCombine::OperandParam> opParamInit[] =
551 {
552     {"src-color", TexEnvCombine::SRC_COLOR},
553     {"one-minus-src-color", TexEnvCombine::ONE_MINUS_SRC_COLOR},
554     {"src-alpha", TexEnvCombine::SRC_ALPHA},
555     {"one-minus-src-alpha", TexEnvCombine::ONE_MINUS_SRC_ALPHA}
556 };
557
558 EffectPropertyMap<TexEnvCombine::OperandParam> operandParams(opParamInit);
559
560 TexEnvCombine* buildTexEnvCombine(Effect* effect, const SGPropertyNode* envProp,
561                                   const SGReaderWriterXMLOptions* options)
562 {
563     if (!isAttributeActive(effect, envProp))
564         return 0;
565     TexEnvCombine* result = new TexEnvCombine;
566     const SGPropertyNode* p = 0;
567     if ((p = getEffectPropertyChild(effect, envProp, "combine-rgb"))) {
568         TexEnvCombine::CombineParam crgb = TexEnvCombine::MODULATE;
569         findAttr(combineParams, p, crgb);
570         result->setCombine_RGB(crgb);
571     }
572     if ((p = getEffectPropertyChild(effect, envProp, "combine-alpha"))) {
573         TexEnvCombine::CombineParam calpha = TexEnvCombine::MODULATE;
574         findAttr(combineParams, p, calpha);
575         result->setCombine_Alpha(calpha);
576     }
577     if ((p = getEffectPropertyChild(effect, envProp, "source0-rgb"))) {
578         TexEnvCombine::SourceParam source = TexEnvCombine::TEXTURE;
579         findAttr(sourceParams, p, source);
580         result->setSource0_RGB(source);
581     }
582     if ((p = getEffectPropertyChild(effect, envProp, "source1-rgb"))) {
583         TexEnvCombine::SourceParam source = TexEnvCombine::PREVIOUS;
584         findAttr(sourceParams, p, source);
585         result->setSource1_RGB(source);
586     }
587     if ((p = getEffectPropertyChild(effect, envProp, "source2-rgb"))) {
588         TexEnvCombine::SourceParam source = TexEnvCombine::CONSTANT;
589         findAttr(sourceParams, p, source);
590         result->setSource2_RGB(source);
591     }
592     if ((p = getEffectPropertyChild(effect, envProp, "source0-alpha"))) {
593         TexEnvCombine::SourceParam source = TexEnvCombine::TEXTURE;
594         findAttr(sourceParams, p, source);
595         result->setSource0_Alpha(source);
596     }
597     if ((p = getEffectPropertyChild(effect, envProp, "source1-alpha"))) {
598         TexEnvCombine::SourceParam source = TexEnvCombine::PREVIOUS;
599         findAttr(sourceParams, p, source);
600         result->setSource1_Alpha(source);
601     }
602     if ((p = getEffectPropertyChild(effect, envProp, "source2-alpha"))) {
603         TexEnvCombine::SourceParam source = TexEnvCombine::CONSTANT;
604         findAttr(sourceParams, p, source);
605         result->setSource2_Alpha(source);
606     }
607     if ((p = getEffectPropertyChild(effect, envProp, "operand0-rgb"))) {
608         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_COLOR;
609         findAttr(operandParams, p, op);
610         result->setOperand0_RGB(op);
611     }
612     if ((p = getEffectPropertyChild(effect, envProp, "operand1-rgb"))) {
613         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_COLOR;
614         findAttr(operandParams, p, op);
615         result->setOperand1_RGB(op);
616     }
617     if ((p = getEffectPropertyChild(effect, envProp, "operand2-rgb"))) {
618         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
619         findAttr(operandParams, p, op);
620         result->setOperand2_RGB(op);
621     }
622     if ((p = getEffectPropertyChild(effect, envProp, "operand0-alpha"))) {
623         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
624         findAttr(operandParams, p, op);
625         result->setOperand0_Alpha(op);
626     }
627     if ((p = getEffectPropertyChild(effect, envProp, "operand1-alpha"))) {
628         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
629         findAttr(operandParams, p, op);
630         result->setOperand1_Alpha(op);
631     }
632     if ((p = getEffectPropertyChild(effect, envProp, "operand2-alpha"))) {
633         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
634         findAttr(operandParams, p, op);
635         result->setOperand2_Alpha(op);
636     }
637     if ((p = getEffectPropertyChild(effect, envProp, "scale-rgb"))) {
638         result->setScale_RGB(p->getValue<float>());
639     }
640     if ((p = getEffectPropertyChild(effect, envProp, "scale-alpha"))) {
641         result->setScale_Alpha(p->getValue<float>());
642     }
643 #if 0
644     if ((p = getEffectPropertyChild(effect, envProp, "constant-color"))) {
645         SGVec4d color = p->getValue<SGVec4d>();
646         result->setConstantColor(toOsg(color));
647     } else if ((p = getEffectPropertyChild(effect, envProp,
648                                            "light-direction"))) {
649         SGVec3d direction = p->getValue<SGVec3d>();
650         result->setConstantColorAsLightDirection(toOsg(direction));
651     }
652 #endif
653     const SGPropertyNode* colorNode = envProp->getChild("constant-color");
654     if (colorNode)
655         initFromParameters(effect, colorNode, result,
656                            &TexEnvCombine::setConstantColor, colorFields,
657                            options);
658     return result;
659 }
660
661 EffectNameValue<TexGen::Mode> tgenModeInit[] =
662 {
663     { "object-linear", TexGen::OBJECT_LINEAR},
664     { "eye-linear", TexGen::EYE_LINEAR},
665     { "sphere-map", TexGen::SPHERE_MAP},
666     { "normal-map", TexGen::NORMAL_MAP},
667     { "reflection-map", TexGen::REFLECTION_MAP}
668 };
669
670 EffectPropertyMap<TexGen::Mode> tgenModes(tgenModeInit);
671
672 EffectNameValue<TexGen::Coord> tgenCoordInit[] =
673 {
674     {"s", TexGen::S},
675     {"t", TexGen::T},
676     {"r", TexGen::R},
677     {"q", TexGen::Q}
678 };
679
680 EffectPropertyMap<TexGen::Coord> tgenCoords(tgenCoordInit);
681
682 TexGen* buildTexGen(Effect* effect, const SGPropertyNode* tgenProp)
683 {
684     if (!isAttributeActive(effect, tgenProp))
685         return 0;
686     TexGen* result = new TexGen;
687     TexGen::Mode mode = TexGen::OBJECT_LINEAR;
688     findAttr(tgenModes, getEffectPropertyChild(effect, tgenProp, "mode"), mode);
689     result->setMode(mode);
690     const SGPropertyNode* planesNode = tgenProp->getChild("planes");
691     if (planesNode) {
692         for (int i = 0; i < planesNode->nChildren(); ++i) {
693             const SGPropertyNode* planeNode = planesNode->getChild(i);
694             TexGen::Coord coord;
695             findAttr(tgenCoords, planeNode->getName(), coord);
696             const SGPropertyNode* realNode
697                 = getEffectPropertyNode(effect, planeNode);
698             SGVec4d plane = realNode->getValue<SGVec4d>();
699             result->setPlane(coord, toOsg(plane));
700         }
701     }
702     return result;
703 }
704
705 bool makeTextureParameters(SGPropertyNode* paramRoot, const StateSet* ss)
706 {
707     SGPropertyNode* texUnit = makeChild(paramRoot, "texture");
708     const Texture* tex = getStateAttribute<Texture>(0, ss);
709     const Texture2D* texture = dynamic_cast<const Texture2D*>(tex);
710     makeChild(texUnit, "unit")->setValue(0);
711     if (!tex) {
712         // The default shader-based technique ignores active
713         makeChild(texUnit, "active")->setValue(false);
714         return false;
715     }
716     const Image* image = texture->getImage();
717     string imageName;
718     if (image) {
719         imageName = image->getFileName();
720     } else {
721         makeChild(texUnit, "active")->setValue(false);
722         makeChild(texUnit, "type")->setValue("white");
723         return false;
724     }
725     makeChild(texUnit, "active")->setValue(true);
726     makeChild(texUnit, "type")->setValue("2d");
727     string filter = findName(filterModes,
728                              texture->getFilter(Texture::MIN_FILTER));
729     string magFilter = findName(filterModes,
730                              texture->getFilter(Texture::MAG_FILTER));
731     string wrapS = findName(wrapModes, texture->getWrap(Texture::WRAP_S));
732     string wrapT = findName(wrapModes, texture->getWrap(Texture::WRAP_T));
733     string wrapR = findName(wrapModes, texture->getWrap(Texture::WRAP_R));
734     makeChild(texUnit, "image")->setStringValue(imageName);
735     makeChild(texUnit, "filter")->setStringValue(filter);
736     makeChild(texUnit, "mag-filter")->setStringValue(magFilter);
737     makeChild(texUnit, "wrap-s")->setStringValue(wrapS);
738     makeChild(texUnit, "wrap-t")->setStringValue(wrapT);
739     makeChild(texUnit, "wrap-r")->setStringValue(wrapR);
740     return true;
741 }
742
743 }