]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/TextureBuilder.cxx
Provide a little more descriptive error message if TextureBuilder fails
[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 for type '" << type << "' on '" << pass->getName() << "', in " << prop->getPath() );
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
412 // Image names for all sides
413 typedef boost::tuple<string, string, string, string, string, string> CubeMapTuple;
414
415 CubeMapTuple makeCubeMapTuple(Effect* effect, const SGPropertyNode* props)
416 {
417     const SGPropertyNode* ep = 0;
418
419     string positive_x;
420     if ((ep = getEffectPropertyChild(effect, props, "positive-x")))
421         positive_x = ep->getStringValue();
422     string negative_x;
423     if ((ep = getEffectPropertyChild(effect, props, "negative-x")))
424         negative_x = ep->getStringValue();
425     string positive_y;
426     if ((ep = getEffectPropertyChild(effect, props, "positive-y")))
427         positive_y = ep->getStringValue();
428     string negative_y;
429     if ((ep = getEffectPropertyChild(effect, props, "negative-y")))
430         negative_y = ep->getStringValue();
431     string positive_z;
432     if ((ep = getEffectPropertyChild(effect, props, "positive-z")))
433         positive_z = ep->getStringValue();
434     string negative_z;
435     if ((ep = getEffectPropertyChild(effect, props, "negative-z")))
436         negative_z = ep->getStringValue();
437     return CubeMapTuple(positive_x, negative_x, positive_y, negative_y, positive_z, negative_z);
438 }
439
440
441 class CubeMapBuilder : public TextureBuilder
442 {
443 public:
444     Texture* build(Effect* effect, const SGPropertyNode*,
445                    const SGReaderWriterXMLOptions* options);
446 protected:
447     typedef map<CubeMapTuple, ref_ptr<TextureCubeMap> > CubeMap;
448     typedef map<string, ref_ptr<TextureCubeMap> > CrossCubeMap;
449     CubeMap _cubemaps;
450     CrossCubeMap _crossmaps;
451 };
452
453 // I use this until osg::CopyImage is fixed
454 // This one assumes images are the same format and sizes are correct
455 void copySubImage(const osg::Image* srcImage, int src_s, int src_t, int width, int height, 
456                  osg::Image* destImage, int dest_s, int dest_t)
457 {
458     for(int row = 0; row<height; ++row)
459     {
460         const unsigned char* srcData = srcImage->data(src_s, src_t+row, 0);
461         unsigned char* destData = destImage->data(dest_s, dest_t+row, 0);
462         memcpy(destData, srcData, (width*destImage->getPixelSizeInBits())/8);
463     }
464 }
465
466
467 Texture* CubeMapBuilder::build(Effect* effect, const SGPropertyNode* props,
468                                                            const SGReaderWriterXMLOptions* options)
469 {
470         // First check that there is a <images> tag
471         const SGPropertyNode* texturesProp = getEffectPropertyChild(effect, props, "images");
472         const SGPropertyNode* crossProp = getEffectPropertyChild(effect, props, "image");
473         if (!texturesProp && !crossProp) {
474                 throw BuilderException("no images defined for cube map");
475                 return NULL; // This is redundant
476         }
477
478         // Using 6 separate images
479         if(texturesProp) {
480
481                 SG_LOG(SG_INPUT, SG_DEBUG, "try 6 images ");
482
483                 CubeMapTuple _tuple = makeCubeMapTuple(effect, texturesProp);
484
485                 CubeMap::iterator itr = _cubemaps.find(_tuple);
486                 if (itr != _cubemaps.end())
487                         return itr->second.get();
488
489                 TextureCubeMap* cubeTexture = new osg::TextureCubeMap;
490
491                 // TODO: Read these from effect file? Maybe these are sane for all cuebmaps?
492                 cubeTexture->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
493                 cubeTexture->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture::LINEAR);
494                 cubeTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
495                 cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
496                 cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
497
498                 osgDB::ReaderWriter::ReadResult result =
499                         osgDB::Registry::instance()->readImage(_tuple.get<0>(), options);
500                 if(result.success()) {
501                         osg::Image* image = result.getImage();
502                         cubeTexture->setImage(TextureCubeMap::POSITIVE_X, image);
503                 }
504                 result = osgDB::Registry::instance()->readImage(_tuple.get<1>(), options);
505                 if(result.success()) {
506                         osg::Image* image = result.getImage();
507                         cubeTexture->setImage(TextureCubeMap::NEGATIVE_X, image);
508                 }
509                 result = osgDB::Registry::instance()->readImage(_tuple.get<2>(), options);
510                 if(result.success()) {
511                         osg::Image* image = result.getImage();
512                         cubeTexture->setImage(TextureCubeMap::POSITIVE_Y, image);
513                 }
514                 result = osgDB::Registry::instance()->readImage(_tuple.get<3>(), options);
515                 if(result.success()) {
516                         osg::Image* image = result.getImage();
517                         cubeTexture->setImage(TextureCubeMap::NEGATIVE_Y, image);
518                 }
519                 result = osgDB::Registry::instance()->readImage(_tuple.get<4>(), options);
520                 if(result.success()) {
521                         osg::Image* image = result.getImage();
522                         cubeTexture->setImage(TextureCubeMap::POSITIVE_Z, image);
523                 }
524                 result = osgDB::Registry::instance()->readImage(_tuple.get<5>(), options);
525                 if(result.success()) {
526                         osg::Image* image = result.getImage();
527                         cubeTexture->setImage(TextureCubeMap::NEGATIVE_Z, image);
528                 }
529
530                 _cubemaps[_tuple] = cubeTexture;
531
532                 return cubeTexture;
533         }
534
535
536         // Using 1 cross image
537         else if(crossProp) {
538                 SG_LOG(SG_INPUT, SG_DEBUG, "try cross map ");
539
540                 std::string texname = crossProp->getStringValue();
541
542                 // Try to find existing cube map
543                 CrossCubeMap::iterator itr = _crossmaps.find(texname);
544                 if (itr != _crossmaps.end())
545                         return itr->second.get();
546
547                 osgDB::ReaderWriter::ReadResult result =
548                         osgDB::Registry::instance()->readImage(texname, options);
549                 if(result.success()) {
550                         osg::Image* image = result.getImage();
551                         image->flipVertical();   // Seems like the image coordinates are somewhat funny, flip to get better ones
552
553                         //cubeTexture->setResizeNonPowerOfTwoHint(false);
554
555                         // Size of a single image, 4 rows and 3 columns
556                         int width = image->s() / 3;
557                         int height = image->t() / 4;
558                         int depth = image->r();
559
560                         TextureCubeMap* cubeTexture = new osg::TextureCubeMap;
561
562                         // Copy the 6 sub-images and push them
563                         for(int n=0; n<6; n++) {
564
565                                 SG_LOG(SG_INPUT, SG_DEBUG, "Copying the " << n << "th sub-images and pushing it" );
566
567                                 osg::ref_ptr<osg::Image> subimg = new osg::Image();
568                                 subimg->allocateImage(width, height, depth, image->getPixelFormat(), image->getDataType());  // Copy attributes
569
570                                 // Choose correct image
571                                 switch(n) {
572                                 case 0:  // Front
573                                         copySubImage(image, width, 0, width, height, subimg.get(), 0, 0);
574                                         cubeTexture->setImage(TextureCubeMap::POSITIVE_Y, subimg.get());
575                                         cubeTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
576                                         cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
577                                         cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
578                                         break;
579                                 case 1:  // Left
580                                         copySubImage(image, 0, height, width, height, subimg.get(), 0, 0);
581                                         cubeTexture->setImage(TextureCubeMap::NEGATIVE_X, subimg.get());
582                                         cubeTexture->setWrap(osg::Texture2D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
583                                         cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
584                                         cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
585                                         break;
586                                 case 2:  // Top
587                                         copySubImage(image, width, height, width, height, subimg.get(), 0, 0);
588                                         cubeTexture->setImage(TextureCubeMap::POSITIVE_Z, subimg.get());
589                                         cubeTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
590                                         cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
591                                         cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
592                                         break;
593                                 case 3:  // Right
594                                         copySubImage(image, width*2, height, width, height, subimg.get(), 0, 0);
595                                         cubeTexture->setImage(TextureCubeMap::POSITIVE_X, subimg.get());
596                                         cubeTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
597                                         cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
598                                         cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
599                                         break;
600                                 case 4:  // Back
601                                         copySubImage(image, width, height*2, width, height, subimg.get(), 0, 0);
602                                         cubeTexture->setImage(TextureCubeMap::NEGATIVE_Y, subimg.get());
603                                         cubeTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
604                                         cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
605                                         cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
606                                         break;
607                                 case 5:  // Bottom
608                                         copySubImage(image, width, height*3, width, height, subimg.get(), 0, 0);
609                                         cubeTexture->setImage(TextureCubeMap::NEGATIVE_Z, subimg.get());
610                                         cubeTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
611                                         cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
612                                         cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
613                                         break;
614                                 };
615
616                         }
617
618                         _crossmaps[texname] = cubeTexture;
619
620                         return cubeTexture;
621
622                 } else {
623                         throw BuilderException("Could not load cube cross");
624                 }
625         }
626
627         return NULL;
628 }
629
630 namespace {
631 TextureBuilder::Registrar installCubeMap("cubemap", new CubeMapBuilder);
632 }
633
634 EffectNameValue<TexEnvCombine::CombineParam> combineParamInit[] =
635 {
636     {"replace", TexEnvCombine::REPLACE},
637     {"modulate", TexEnvCombine::MODULATE},
638     {"add", TexEnvCombine::ADD},
639     {"add-signed", TexEnvCombine::ADD_SIGNED},
640     {"interpolate", TexEnvCombine::INTERPOLATE},
641     {"subtract", TexEnvCombine::SUBTRACT},
642     {"dot3-rgb", TexEnvCombine::DOT3_RGB},
643     {"dot3-rgba", TexEnvCombine::DOT3_RGBA}
644 };
645
646 EffectPropertyMap<TexEnvCombine::CombineParam> combineParams(combineParamInit);
647
648 EffectNameValue<TexEnvCombine::SourceParam> sourceParamInit[] =
649 {
650     {"constant", TexEnvCombine::CONSTANT},
651     {"primary_color", TexEnvCombine::PRIMARY_COLOR},
652     {"previous", TexEnvCombine::PREVIOUS},
653     {"texture", TexEnvCombine::TEXTURE},
654     {"texture0", TexEnvCombine::TEXTURE0},
655     {"texture1", TexEnvCombine::TEXTURE1},
656     {"texture2", TexEnvCombine::TEXTURE2},
657     {"texture3", TexEnvCombine::TEXTURE3},
658     {"texture4", TexEnvCombine::TEXTURE4},
659     {"texture5", TexEnvCombine::TEXTURE5},
660     {"texture6", TexEnvCombine::TEXTURE6},
661     {"texture7", TexEnvCombine::TEXTURE7}
662 };
663
664 EffectPropertyMap<TexEnvCombine::SourceParam> sourceParams(sourceParamInit);
665
666 EffectNameValue<TexEnvCombine::OperandParam> opParamInit[] =
667 {
668     {"src-color", TexEnvCombine::SRC_COLOR},
669     {"one-minus-src-color", TexEnvCombine::ONE_MINUS_SRC_COLOR},
670     {"src-alpha", TexEnvCombine::SRC_ALPHA},
671     {"one-minus-src-alpha", TexEnvCombine::ONE_MINUS_SRC_ALPHA}
672 };
673
674 EffectPropertyMap<TexEnvCombine::OperandParam> operandParams(opParamInit);
675
676 TexEnvCombine* buildTexEnvCombine(Effect* effect, const SGPropertyNode* envProp,
677                                   const SGReaderWriterXMLOptions* options)
678 {
679     if (!isAttributeActive(effect, envProp))
680         return 0;
681     TexEnvCombine* result = new TexEnvCombine;
682     const SGPropertyNode* p = 0;
683     if ((p = getEffectPropertyChild(effect, envProp, "combine-rgb"))) {
684         TexEnvCombine::CombineParam crgb = TexEnvCombine::MODULATE;
685         findAttr(combineParams, p, crgb);
686         result->setCombine_RGB(crgb);
687     }
688     if ((p = getEffectPropertyChild(effect, envProp, "combine-alpha"))) {
689         TexEnvCombine::CombineParam calpha = TexEnvCombine::MODULATE;
690         findAttr(combineParams, p, calpha);
691         result->setCombine_Alpha(calpha);
692     }
693     if ((p = getEffectPropertyChild(effect, envProp, "source0-rgb"))) {
694         TexEnvCombine::SourceParam source = TexEnvCombine::TEXTURE;
695         findAttr(sourceParams, p, source);
696         result->setSource0_RGB(source);
697     }
698     if ((p = getEffectPropertyChild(effect, envProp, "source1-rgb"))) {
699         TexEnvCombine::SourceParam source = TexEnvCombine::PREVIOUS;
700         findAttr(sourceParams, p, source);
701         result->setSource1_RGB(source);
702     }
703     if ((p = getEffectPropertyChild(effect, envProp, "source2-rgb"))) {
704         TexEnvCombine::SourceParam source = TexEnvCombine::CONSTANT;
705         findAttr(sourceParams, p, source);
706         result->setSource2_RGB(source);
707     }
708     if ((p = getEffectPropertyChild(effect, envProp, "source0-alpha"))) {
709         TexEnvCombine::SourceParam source = TexEnvCombine::TEXTURE;
710         findAttr(sourceParams, p, source);
711         result->setSource0_Alpha(source);
712     }
713     if ((p = getEffectPropertyChild(effect, envProp, "source1-alpha"))) {
714         TexEnvCombine::SourceParam source = TexEnvCombine::PREVIOUS;
715         findAttr(sourceParams, p, source);
716         result->setSource1_Alpha(source);
717     }
718     if ((p = getEffectPropertyChild(effect, envProp, "source2-alpha"))) {
719         TexEnvCombine::SourceParam source = TexEnvCombine::CONSTANT;
720         findAttr(sourceParams, p, source);
721         result->setSource2_Alpha(source);
722     }
723     if ((p = getEffectPropertyChild(effect, envProp, "operand0-rgb"))) {
724         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_COLOR;
725         findAttr(operandParams, p, op);
726         result->setOperand0_RGB(op);
727     }
728     if ((p = getEffectPropertyChild(effect, envProp, "operand1-rgb"))) {
729         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_COLOR;
730         findAttr(operandParams, p, op);
731         result->setOperand1_RGB(op);
732     }
733     if ((p = getEffectPropertyChild(effect, envProp, "operand2-rgb"))) {
734         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
735         findAttr(operandParams, p, op);
736         result->setOperand2_RGB(op);
737     }
738     if ((p = getEffectPropertyChild(effect, envProp, "operand0-alpha"))) {
739         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
740         findAttr(operandParams, p, op);
741         result->setOperand0_Alpha(op);
742     }
743     if ((p = getEffectPropertyChild(effect, envProp, "operand1-alpha"))) {
744         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
745         findAttr(operandParams, p, op);
746         result->setOperand1_Alpha(op);
747     }
748     if ((p = getEffectPropertyChild(effect, envProp, "operand2-alpha"))) {
749         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
750         findAttr(operandParams, p, op);
751         result->setOperand2_Alpha(op);
752     }
753     if ((p = getEffectPropertyChild(effect, envProp, "scale-rgb"))) {
754         result->setScale_RGB(p->getValue<float>());
755     }
756     if ((p = getEffectPropertyChild(effect, envProp, "scale-alpha"))) {
757         result->setScale_Alpha(p->getValue<float>());
758     }
759 #if 0
760     if ((p = getEffectPropertyChild(effect, envProp, "constant-color"))) {
761         SGVec4d color = p->getValue<SGVec4d>();
762         result->setConstantColor(toOsg(color));
763     } else if ((p = getEffectPropertyChild(effect, envProp,
764                                            "light-direction"))) {
765         SGVec3d direction = p->getValue<SGVec3d>();
766         result->setConstantColorAsLightDirection(toOsg(direction));
767     }
768 #endif
769     const SGPropertyNode* colorNode = envProp->getChild("constant-color");
770     if (colorNode)
771         initFromParameters(effect, colorNode, result,
772                            &TexEnvCombine::setConstantColor, colorFields,
773                            options);
774     return result;
775 }
776
777 EffectNameValue<TexGen::Mode> tgenModeInit[] =
778 {
779     { "object-linear", TexGen::OBJECT_LINEAR},
780     { "eye-linear", TexGen::EYE_LINEAR},
781     { "sphere-map", TexGen::SPHERE_MAP},
782     { "normal-map", TexGen::NORMAL_MAP},
783     { "reflection-map", TexGen::REFLECTION_MAP}
784 };
785
786 EffectPropertyMap<TexGen::Mode> tgenModes(tgenModeInit);
787
788 EffectNameValue<TexGen::Coord> tgenCoordInit[] =
789 {
790     {"s", TexGen::S},
791     {"t", TexGen::T},
792     {"r", TexGen::R},
793     {"q", TexGen::Q}
794 };
795
796 EffectPropertyMap<TexGen::Coord> tgenCoords(tgenCoordInit);
797
798 TexGen* buildTexGen(Effect* effect, const SGPropertyNode* tgenProp)
799 {
800     if (!isAttributeActive(effect, tgenProp))
801         return 0;
802     TexGen* result = new TexGen;
803     TexGen::Mode mode = TexGen::OBJECT_LINEAR;
804     findAttr(tgenModes, getEffectPropertyChild(effect, tgenProp, "mode"), mode);
805     result->setMode(mode);
806     const SGPropertyNode* planesNode = tgenProp->getChild("planes");
807     if (planesNode) {
808         for (int i = 0; i < planesNode->nChildren(); ++i) {
809             const SGPropertyNode* planeNode = planesNode->getChild(i);
810             TexGen::Coord coord;
811             findAttr(tgenCoords, planeNode->getName(), coord);
812             const SGPropertyNode* realNode
813                 = getEffectPropertyNode(effect, planeNode);
814             SGVec4d plane = realNode->getValue<SGVec4d>();
815             result->setPlane(coord, toOsg(plane));
816         }
817     }
818     return result;
819 }
820
821 bool makeTextureParameters(SGPropertyNode* paramRoot, const StateSet* ss)
822 {
823     SGPropertyNode* texUnit = makeChild(paramRoot, "texture");
824     const Texture* tex = getStateAttribute<Texture>(0, ss);
825     const Texture2D* texture = dynamic_cast<const Texture2D*>(tex);
826     makeChild(texUnit, "unit")->setValue(0);
827     if (!tex) {
828         // The default shader-based technique ignores active
829         makeChild(texUnit, "active")->setValue(false);
830         return false;
831     }
832     const Image* image = texture->getImage();
833     string imageName;
834     if (image) {
835         imageName = image->getFileName();
836     } else {
837         makeChild(texUnit, "active")->setValue(false);
838         makeChild(texUnit, "type")->setValue("white");
839         return false;
840     }
841     makeChild(texUnit, "active")->setValue(true);
842     makeChild(texUnit, "type")->setValue("2d");
843     string filter = findName(filterModes,
844                              texture->getFilter(Texture::MIN_FILTER));
845     string magFilter = findName(filterModes,
846                              texture->getFilter(Texture::MAG_FILTER));
847     string wrapS = findName(wrapModes, texture->getWrap(Texture::WRAP_S));
848     string wrapT = findName(wrapModes, texture->getWrap(Texture::WRAP_T));
849     string wrapR = findName(wrapModes, texture->getWrap(Texture::WRAP_R));
850     makeChild(texUnit, "image")->setStringValue(imageName);
851     makeChild(texUnit, "filter")->setStringValue(filter);
852     makeChild(texUnit, "mag-filter")->setStringValue(magFilter);
853     makeChild(texUnit, "wrap-s")->setStringValue(wrapS);
854     makeChild(texUnit, "wrap-t")->setStringValue(wrapT);
855     makeChild(texUnit, "wrap-r")->setStringValue(wrapR);
856     return true;
857 }
858
859 }