]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/TextureBuilder.cxx
Partial fix for crash in SGPropertyNode::fireValueChanged
[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 #include "mipmap.hxx"
23
24 #include "Pass.hxx"
25
26 #include <osg/PointSprite>
27 #include <osg/TexEnv>
28 #include <osg/TexEnvCombine>
29 #include <osg/TexGen>
30 #include <osg/Texture1D>
31 #include <osg/Texture2D>
32 #include <osg/Texture3D>
33 #include <osg/TextureRectangle>
34 #include <osg/TextureCubeMap>
35 #include <osgDB/FileUtils>
36 #include <osgDB/ReadFile>
37
38 #include <OpenThreads/Mutex>
39 #include <OpenThreads/ScopedLock>
40
41 #include <boost/lexical_cast.hpp>
42 #include <boost/tuple/tuple.hpp>
43 #include <boost/tuple/tuple_comparison.hpp>
44
45 #include <simgear/scene/util/OsgMath.hxx>
46 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
47 #include <simgear/scene/util/SGSceneFeatures.hxx>
48 #include <simgear/scene/util/StateAttributeFactory.hxx>
49 #include <simgear/structure/OSGUtils.hxx>
50 #include <simgear/props/vectorPropTemplates.hxx>
51
52 namespace simgear
53 {
54
55 using OpenThreads::Mutex;
56 using OpenThreads::ScopedLock;
57
58 using namespace std;
59 using namespace osg;
60
61 using namespace effect;
62
63 TexEnvCombine* buildTexEnvCombine(Effect* effect,
64                                   const SGPropertyNode* envProp,
65                                   const SGReaderWriterOptions* options);
66 TexGen* buildTexGen(Effect* Effect, const SGPropertyNode* tgenProp);
67
68 // Hack to force inclusion of TextureBuilder.cxx in library
69 osg::Texture* TextureBuilder::buildFromType(Effect* effect, Pass* pass, const string& type,
70                                             const SGPropertyNode*props,
71                                             const SGReaderWriterOptions*
72                                             options)
73 {
74     return EffectBuilder<Texture>::buildFromType(effect, pass, type, props, options);
75 }
76
77 typedef boost::tuple<string, Texture::FilterMode, Texture::FilterMode,
78                      Texture::WrapMode, Texture::WrapMode, Texture::WrapMode,
79                      string, MipMapTuple> TexTuple;
80
81 EffectNameValue<TexEnv::Mode> texEnvModesInit[] =
82 {
83     {"add", TexEnv::ADD},
84     {"blend", TexEnv::BLEND},
85     {"decal", TexEnv::DECAL},
86     {"modulate", TexEnv::MODULATE},
87     {"replace", TexEnv::REPLACE}
88 };
89 EffectPropertyMap<TexEnv::Mode> texEnvModes(texEnvModesInit);
90
91 TexEnv* buildTexEnv(Effect* effect, const SGPropertyNode* prop)
92 {
93     const SGPropertyNode* modeProp = getEffectPropertyChild(effect, prop,
94                                                             "mode");
95     const SGPropertyNode* colorProp = getEffectPropertyChild(effect, prop,
96                                                              "color");
97     if (!modeProp)
98         return 0;
99     TexEnv::Mode mode = TexEnv::MODULATE;
100     findAttr(texEnvModes, modeProp, mode);
101     if (mode == TexEnv::MODULATE) {
102         return StateAttributeFactory::instance()->getStandardTexEnv();
103     }
104     TexEnv* env = new TexEnv(mode);
105     if (colorProp)
106         env->setColor(toOsg(colorProp->getValue<SGVec4d>()));
107     return env;
108 }
109
110
111 void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass,
112                                         const SGPropertyNode* prop,
113                                         const SGReaderWriterOptions* options)
114 {
115     if (!isAttributeActive(effect, prop))
116         return;
117     // Decode the texture unit
118     int unit = 0;
119     const SGPropertyNode* pUnit = prop->getChild("unit");
120     if (pUnit) {
121         unit = pUnit->getValue<int>();
122     } else {
123         const SGPropertyNode* pName = prop->getChild("name");
124         if (pName)
125             try {
126                 unit = boost::lexical_cast<int>(pName->getStringValue());
127             } catch (boost::bad_lexical_cast& lex) {
128                 SG_LOG(SG_INPUT, SG_ALERT, "can't decode name as texture unit "
129                        << lex.what());
130             }
131     }
132     const SGPropertyNode* pType = getEffectPropertyChild(effect, prop, "type");
133     string type;
134     if (!pType)
135         type = "2d";
136     else
137         type = pType->getStringValue();
138     Texture* texture = 0;
139     try {
140         texture = TextureBuilder::buildFromType(effect, pass, type, prop,
141                                                 options);
142     }
143     catch (BuilderException& e) {
144         SG_LOG(SG_INPUT, SG_DEBUG, e.getFormattedMessage() << ", "
145             << "maybe the reader did not set the filename attribute, "
146             << "using white for type '" << type << "' on '" << pass->getName() << "', in " << prop->getPath() );
147         texture = StateAttributeFactory::instance()->getWhiteTexture();
148     }
149
150     const SGPropertyNode* pPoint = getEffectPropertyChild(effect, prop, "point-sprite");
151     if (pPoint && pPoint->getBoolValue()) {
152         ref_ptr<PointSprite> pointSprite = new PointSprite;
153         pass->setTextureAttributeAndModes(unit, pointSprite.get(), osg::StateAttribute::ON);
154     }
155
156     pass->setTextureAttributeAndModes(unit, texture);
157
158     const SGPropertyNode* envProp = prop->getChild("environment");
159     if (envProp) {
160         TexEnv* env = buildTexEnv(effect, envProp);
161         if (env)
162             pass->setTextureAttributeAndModes(unit, env);
163     }
164     const SGPropertyNode* combineProp = prop->getChild("texenv-combine");
165     TexEnvCombine* combiner = 0;
166     if (combineProp && ((combiner = buildTexEnvCombine(effect, combineProp,
167                                                        options))))
168         pass->setTextureAttributeAndModes(unit, combiner);
169     const SGPropertyNode* tgenProp = prop->getChild("texgen");
170     TexGen* tgen = 0;
171     if (tgenProp && (tgen = buildTexGen(effect, tgenProp)))
172         pass->setTextureAttributeAndModes(unit, tgen);
173 }
174
175 // InstallAttributeBuilder call is in Effect.cxx to force this file to
176 // be linked in.
177
178 namespace
179 {
180 EffectNameValue<Texture::FilterMode> filterModesInit[] =
181 {
182     { "linear", Texture::LINEAR },
183     { "linear-mipmap-linear", Texture::LINEAR_MIPMAP_LINEAR},
184     { "linear-mipmap-nearest", Texture::LINEAR_MIPMAP_NEAREST},
185     { "nearest", Texture::NEAREST},
186     { "nearest-mipmap-linear", Texture::NEAREST_MIPMAP_LINEAR},
187     { "nearest-mipmap-nearest", Texture::NEAREST_MIPMAP_NEAREST}
188 };
189 EffectPropertyMap<Texture::FilterMode> filterModes(filterModesInit);
190
191 EffectNameValue<Texture::WrapMode> wrapModesInit[] =
192 {
193     {"clamp", Texture::CLAMP},
194     {"clamp-to-border", Texture::CLAMP_TO_BORDER},
195     {"clamp-to-edge", Texture::CLAMP_TO_EDGE},
196     {"mirror", Texture::MIRROR},
197     {"repeat", Texture::REPEAT}
198 };
199 EffectPropertyMap<Texture::WrapMode> wrapModes(wrapModesInit);
200
201 TexTuple makeTexTuple(Effect* effect, const SGPropertyNode* props,
202                       const SGReaderWriterOptions* options,
203                       const string& texType)
204 {
205     Texture::FilterMode minFilter = Texture::LINEAR_MIPMAP_LINEAR;
206     const SGPropertyNode* ep = 0;
207     if ((ep = getEffectPropertyChild(effect, props, "filter")))
208         findAttr(filterModes, ep, minFilter);
209     Texture::FilterMode magFilter = Texture::LINEAR;
210     if ((ep = getEffectPropertyChild(effect, props, "mag-filter")))
211         findAttr(filterModes, ep, magFilter);
212     const SGPropertyNode* pWrapS
213         = getEffectPropertyChild(effect, props, "wrap-s");
214     Texture::WrapMode sWrap = Texture::CLAMP;
215     if (pWrapS)
216         findAttr(wrapModes, pWrapS, sWrap);
217     const SGPropertyNode* pWrapT
218         = getEffectPropertyChild(effect, props, "wrap-t");
219     Texture::WrapMode tWrap = Texture::CLAMP;
220     if (pWrapT)
221         findAttr(wrapModes, pWrapT, tWrap);
222     const SGPropertyNode* pWrapR
223         = getEffectPropertyChild(effect, props, "wrap-r");
224     Texture::WrapMode rWrap = Texture::CLAMP;
225     if (pWrapR)
226         findAttr(wrapModes, pWrapR, rWrap);
227     const SGPropertyNode* pImage
228         = getEffectPropertyChild(effect, props, "image");
229     string imageName;
230     string absFileName;
231     if (pImage)
232     {
233         imageName = pImage->getStringValue();
234         absFileName = SGModelLib::findDataFile(imageName, options);
235         if (absFileName.empty())
236         {
237             SG_LOG(SG_INPUT, SG_ALERT, "Texture file not found: '"
238                    << imageName << "'");
239         }
240     }
241
242     const SGPropertyNode* pMipmapControl
243         = getEffectPropertyChild(effect, props, "mipmap-control");
244     MipMapTuple mipmapFunctions( AUTOMATIC, AUTOMATIC, AUTOMATIC, AUTOMATIC );
245     if ( pMipmapControl )
246         mipmapFunctions = makeMipMapTuple(effect, pMipmapControl, options);
247
248     return TexTuple(absFileName, minFilter, magFilter, sWrap, tWrap, rWrap,
249                     texType, mipmapFunctions);
250 }
251
252 bool setAttrs(const TexTuple& attrs, Texture* tex,
253               const SGReaderWriterOptions* options)
254 {
255     const string& imageName = attrs.get<0>();
256     if (imageName.empty())
257         return false;
258
259     osgDB::ReaderWriter::ReadResult result;
260     result = osgDB::readImageFile(imageName, options);
261     osg::ref_ptr<osg::Image> image;
262     if (result.success())
263         image = result.getImage();
264     if (image.valid())
265     {
266         image = computeMipmap( image.get(), attrs.get<7>() );
267         tex->setImage(GL_FRONT_AND_BACK, image.get());
268         int s = image->s();
269         int t = image->t();
270         if (s <= t && 32 <= s) {
271             SGSceneFeatures::instance()->setTextureCompression(tex);
272         } else if (t < s && 32 <= t) {
273             SGSceneFeatures::instance()->setTextureCompression(tex);
274         }
275         tex->setMaxAnisotropy(SGSceneFeatures::instance()
276                               ->getTextureFilter());
277     } else {
278         SG_LOG(SG_INPUT, SG_ALERT, "failed to load effect texture file "
279                << imageName);
280         return false;
281     }
282
283     // texture->setDataVariance(osg::Object::STATIC);
284     tex->setFilter(Texture::MIN_FILTER, attrs.get<1>());
285     tex->setFilter(Texture::MAG_FILTER, attrs.get<2>());
286     tex->setWrap(Texture::WRAP_S, attrs.get<3>());
287     tex->setWrap(Texture::WRAP_T, attrs.get<4>());
288     tex->setWrap(Texture::WRAP_R, attrs.get<5>());
289     return true;
290 }
291
292 } // of anonymous namespace
293
294 template<typename T>
295 class TexBuilder : public TextureBuilder
296 {
297 public:
298     TexBuilder(const string& texType) : _type(texType) {}
299     Texture* build(Effect* effect, Pass* pass, const SGPropertyNode*,
300                    const SGReaderWriterOptions* options);
301 protected:
302     typedef map<TexTuple, observer_ptr<T> > TexMap;
303     TexMap texMap;
304     const string _type;
305 };
306
307 template<typename T>
308 Texture* TexBuilder<T>::build(Effect* effect, Pass* pass, const SGPropertyNode* props,
309                               const SGReaderWriterOptions* options)
310 {
311     TexTuple attrs = makeTexTuple(effect, props, options, _type);
312     typename TexMap::iterator itr = texMap.find(attrs);
313
314     ref_ptr<T> tex;
315     if ((itr != texMap.end())&&
316         (itr->second.lock(tex)))
317     {
318         return tex.release();
319     }
320
321     tex = new T;
322     if (!setAttrs(attrs, tex, options))
323         return NULL;
324
325     if (itr == texMap.end())
326         texMap.insert(make_pair(attrs, tex));
327     else
328         itr->second = tex; // update existing, but empty observer
329     return tex.release();
330 }
331
332
333 namespace
334 {
335 TextureBuilder::Registrar install1D("1d", new TexBuilder<Texture1D>("1d"));
336 TextureBuilder::Registrar install2D("2d", new TexBuilder<Texture2D>("2d"));
337 TextureBuilder::Registrar install3D("3d", new TexBuilder<Texture3D>("3d"));
338 }
339
340 class WhiteTextureBuilder : public TextureBuilder
341 {
342 public:
343     Texture* build(Effect* effect, Pass* pass, const SGPropertyNode*,
344                    const SGReaderWriterOptions* options);
345 };
346
347 Texture* WhiteTextureBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode*,
348                                     const SGReaderWriterOptions* options)
349 {
350     return StateAttributeFactory::instance()->getWhiteTexture();
351 }
352
353 namespace
354 {
355 TextureBuilder::Registrar installWhite("white", new WhiteTextureBuilder);
356 }
357
358 class TransparentTextureBuilder : public TextureBuilder
359 {
360 public:
361     Texture* build(Effect* effect, Pass* pass, const SGPropertyNode*,
362                    const SGReaderWriterOptions* options);
363 };
364
365 Texture* TransparentTextureBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode*,
366                                     const SGReaderWriterOptions* options)
367 {
368     return StateAttributeFactory::instance()->getTransparentTexture();
369 }
370
371 namespace
372 {
373 TextureBuilder::Registrar installTransparent("transparent",
374                                              new TransparentTextureBuilder);
375 }
376
377 class NoiseBuilder : public TextureBuilder
378 {
379 public:
380     Texture* build(Effect* effect, Pass* pass, const SGPropertyNode*,
381                    const SGReaderWriterOptions* options);
382 protected:
383     typedef map<int, ref_ptr<Texture3D> > NoiseMap;
384     NoiseMap _noises;
385 };
386
387 Texture* NoiseBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode* props,
388                              const SGReaderWriterOptions* options)
389 {
390     int texSize = 64;
391     const SGPropertyNode* sizeProp = getEffectPropertyChild(effect, props,
392                                                             "size");
393     if (sizeProp)
394         texSize = sizeProp->getValue<int>();
395
396     return StateAttributeFactory::instance()->getNoiseTexture(texSize);
397 }
398
399 namespace
400 {
401 TextureBuilder::Registrar installNoise("noise", new NoiseBuilder);
402 }
403
404 class LightSpriteBuilder : public TextureBuilder
405 {
406 public:
407     Texture* build(Effect* effect, Pass* pass, const SGPropertyNode*,
408                    const SGReaderWriterOptions* options);
409 protected:
410     Mutex lightMutex;
411     void setPointSpriteImage(unsigned char* data, unsigned log2resolution,
412                     unsigned charsPerPixel);
413     osg::Image* getPointSpriteImage(int logResolution);
414 };
415
416 Texture* LightSpriteBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode* props,
417                              const SGReaderWriterOptions* options)
418 {
419     ScopedLock<Mutex> lock(lightMutex);
420
421     // Always called from when the lightMutex is already taken
422     static osg::ref_ptr<osg::Texture2D> texture;
423
424     if (texture.valid())
425       return texture.get();
426
427     texture = new osg::Texture2D;
428     texture->setImage(getPointSpriteImage(6));
429     texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
430     texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
431
432     return texture.get();
433 }
434
435 void
436 LightSpriteBuilder::setPointSpriteImage(unsigned char* data, unsigned log2resolution,
437                     unsigned charsPerPixel)
438 {
439   int env_tex_res = (1 << log2resolution);
440   for (int i = 0; i < env_tex_res; ++i) {
441     for (int j = 0; j < env_tex_res; ++j) {
442       int xi = 2*i + 1 - env_tex_res;
443       int yi = 2*j + 1 - env_tex_res;
444       if (xi < 0)
445         xi = -xi;
446       if (yi < 0)
447         yi = -yi;
448
449       xi -= 1;
450       yi -= 1;
451
452       if (xi < 0)
453         xi = 0;
454       if (yi < 0)
455         yi = 0;
456
457       float x = 1.5*xi/(float)(env_tex_res);
458       float y = 1.5*yi/(float)(env_tex_res);
459       //       float x = 2*xi/(float)(env_tex_res);
460       //       float y = 2*yi/(float)(env_tex_res);
461       float dist = sqrt(x*x + y*y);
462       float bright = SGMiscf::clip(255*(1-dist), 0, 255);
463       for (unsigned l = 0; l < charsPerPixel; ++l)
464         data[charsPerPixel*(i*env_tex_res + j) + l] = (unsigned char)bright;
465     }
466   }
467 }
468
469 osg::Image*
470 LightSpriteBuilder::getPointSpriteImage(int logResolution)
471 {
472   osg::Image* image = new osg::Image;
473
474   osg::Image::MipmapDataType mipmapOffsets;
475   unsigned off = 0;
476   for (int i = logResolution; 0 <= i; --i) {
477     unsigned res = 1 << i;
478     off += res*res;
479     mipmapOffsets.push_back(off);
480   }
481
482   int env_tex_res = (1 << logResolution);
483
484   unsigned char* imageData = new unsigned char[off];
485   image->setImage(env_tex_res, env_tex_res, 1,
486                   GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, imageData,
487                   osg::Image::USE_NEW_DELETE);
488   image->setMipmapLevels(mipmapOffsets);
489
490   for (int k = logResolution; 0 <= k; --k) {
491     setPointSpriteImage(image->getMipmapData(logResolution - k), k, 1);
492   }
493
494   return image;
495 }
496
497 namespace
498 {
499 TextureBuilder::Registrar installLightSprite("light-sprite", new LightSpriteBuilder);
500 }
501
502 // Image names for all sides
503 typedef boost::tuple<string, string, string, string, string, string> CubeMapTuple;
504
505 CubeMapTuple makeCubeMapTuple(Effect* effect, const SGPropertyNode* props)
506 {
507     const SGPropertyNode* ep = 0;
508
509     string positive_x;
510     if ((ep = getEffectPropertyChild(effect, props, "positive-x")))
511         positive_x = ep->getStringValue();
512     string negative_x;
513     if ((ep = getEffectPropertyChild(effect, props, "negative-x")))
514         negative_x = ep->getStringValue();
515     string positive_y;
516     if ((ep = getEffectPropertyChild(effect, props, "positive-y")))
517         positive_y = ep->getStringValue();
518     string negative_y;
519     if ((ep = getEffectPropertyChild(effect, props, "negative-y")))
520         negative_y = ep->getStringValue();
521     string positive_z;
522     if ((ep = getEffectPropertyChild(effect, props, "positive-z")))
523         positive_z = ep->getStringValue();
524     string negative_z;
525     if ((ep = getEffectPropertyChild(effect, props, "negative-z")))
526         negative_z = ep->getStringValue();
527     return CubeMapTuple(positive_x, negative_x, positive_y, negative_y, positive_z, negative_z);
528 }
529
530
531 class CubeMapBuilder : public TextureBuilder
532 {
533 public:
534     Texture* build(Effect* effect, Pass* pass, const SGPropertyNode*,
535                    const SGReaderWriterOptions* options);
536 protected:
537     typedef map<CubeMapTuple, observer_ptr<TextureCubeMap> > CubeMap;
538     typedef map<string, observer_ptr<TextureCubeMap> > CrossCubeMap;
539     CubeMap _cubemaps;
540     CrossCubeMap _crossmaps;
541 };
542
543 // I use this until osg::CopyImage is fixed
544 // This one assumes images are the same format and sizes are correct
545 void copySubImage(const osg::Image* srcImage, int src_s, int src_t, int width, int height,
546                  osg::Image* destImage, int dest_s, int dest_t)
547 {
548     for(int row = 0; row<height; ++row)
549     {
550         const unsigned char* srcData = srcImage->data(src_s, src_t+row, 0);
551         unsigned char* destData = destImage->data(dest_s, dest_t+row, 0);
552         memcpy(destData, srcData, (width*destImage->getPixelSizeInBits())/8);
553     }
554 }
555
556
557 Texture* CubeMapBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode* props,
558                                const SGReaderWriterOptions* options)
559 {
560     // First check that there is a <images> tag
561     const SGPropertyNode* texturesProp = getEffectPropertyChild(effect, props, "images");
562     const SGPropertyNode* crossProp = getEffectPropertyChild(effect, props, "image");
563     if (!texturesProp && !crossProp) {
564         throw BuilderException("no images defined for cube map");
565         return NULL; // This is redundant
566     }
567
568     // Using 6 separate images
569     if(texturesProp) {
570         CubeMapTuple _tuple = makeCubeMapTuple(effect, texturesProp);
571
572         CubeMap::iterator itr = _cubemaps.find(_tuple);
573         ref_ptr<TextureCubeMap> cubeTexture;
574         
575         if (itr != _cubemaps.end() && itr->second.lock(cubeTexture))
576             return cubeTexture.release();
577
578         cubeTexture = new osg::TextureCubeMap;
579
580         // TODO: Read these from effect file? Maybe these are sane for all cuebmaps?
581         cubeTexture->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
582         cubeTexture->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture::LINEAR);
583         cubeTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
584         cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
585         cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
586
587         osgDB::ReaderWriter::ReadResult result;
588         result = osgDB::readImageFile(_tuple.get<0>(), options);
589         if(result.success()) {
590             osg::Image* image = result.getImage();
591             cubeTexture->setImage(TextureCubeMap::POSITIVE_X, image);
592         }
593         result = osgDB::readImageFile(_tuple.get<1>(), options);
594         if(result.success()) {
595             osg::Image* image = result.getImage();
596             cubeTexture->setImage(TextureCubeMap::NEGATIVE_X, image);
597         }
598         result = osgDB::readImageFile(_tuple.get<2>(), options);
599         if(result.success()) {
600             osg::Image* image = result.getImage();
601             cubeTexture->setImage(TextureCubeMap::POSITIVE_Y, image);
602         }
603         result = osgDB::readImageFile(_tuple.get<3>(), options);
604         if(result.success()) {
605             osg::Image* image = result.getImage();
606             cubeTexture->setImage(TextureCubeMap::NEGATIVE_Y, image);
607         }
608         result = osgDB::readImageFile(_tuple.get<4>(), options);
609         if(result.success()) {
610             osg::Image* image = result.getImage();
611             cubeTexture->setImage(TextureCubeMap::POSITIVE_Z, image);
612         }
613         result = osgDB::readImageFile(_tuple.get<5>(), options);
614         if(result.success()) {
615             osg::Image* image = result.getImage();
616             cubeTexture->setImage(TextureCubeMap::NEGATIVE_Z, image);
617         }
618
619         if (itr == _cubemaps.end())
620             _cubemaps[_tuple] = cubeTexture;
621         else
622             itr->second = cubeTexture; // update existing
623         return cubeTexture.release();
624     }
625
626     // Using 1 cross image
627     else if(crossProp) {
628         std::string texname = crossProp->getStringValue();
629
630         // Try to find existing cube map
631         ref_ptr<TextureCubeMap> cubeTexture;
632         CrossCubeMap::iterator itr = _crossmaps.find(texname);
633         if ((itr != _crossmaps.end()) && itr->second.lock(cubeTexture))
634             return cubeTexture.release();
635
636         osgDB::ReaderWriter::ReadResult result;
637         result = osgDB::readImageFile(texname, options);
638         if(result.success()) {
639             osg::Image* image = result.getImage();
640             image->flipVertical();   // Seems like the image coordinates are somewhat funny, flip to get better ones
641
642             //cubeTexture->setResizeNonPowerOfTwoHint(false);
643
644             // Size of a single image, 4 rows and 3 columns
645             int width = image->s() / 3;
646             int height = image->t() / 4;
647             int depth = image->r();
648
649             cubeTexture = new osg::TextureCubeMap;
650
651             // Copy the 6 sub-images and push them
652             for(int n=0; n<6; n++) {
653
654                 SG_LOG(SG_INPUT, SG_DEBUG, "Copying the " << n << "th sub-images and pushing it" );
655
656                 osg::ref_ptr<osg::Image> subimg = new osg::Image();
657                 subimg->allocateImage(width, height, depth, image->getPixelFormat(), image->getDataType());  // Copy attributes
658
659                 // Choose correct image
660                 switch(n) {
661                 case 0:  // Front
662                     copySubImage(image, width, 0, width, height, subimg.get(), 0, 0);
663                     cubeTexture->setImage(TextureCubeMap::POSITIVE_Y, subimg.get());
664                     cubeTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
665                     cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
666                     cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
667                     break;
668                 case 1:  // Left
669                     copySubImage(image, 0, height, width, height, subimg.get(), 0, 0);
670                     cubeTexture->setImage(TextureCubeMap::NEGATIVE_X, subimg.get());
671                     cubeTexture->setWrap(osg::Texture2D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
672                     cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
673                     cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
674                     break;
675                 case 2:  // Top
676                     copySubImage(image, width, height, width, height, subimg.get(), 0, 0);
677                     cubeTexture->setImage(TextureCubeMap::POSITIVE_Z, subimg.get());
678                     cubeTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
679                     cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
680                     cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
681                     break;
682                 case 3:  // Right
683                     copySubImage(image, width*2, height, width, height, subimg.get(), 0, 0);
684                     cubeTexture->setImage(TextureCubeMap::POSITIVE_X, subimg.get());
685                     cubeTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
686                     cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
687                     cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
688                     break;
689                 case 4:  // Back
690                     copySubImage(image, width, height*2, width, height, subimg.get(), 0, 0);
691                     cubeTexture->setImage(TextureCubeMap::NEGATIVE_Y, subimg.get());
692                     cubeTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
693                     cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
694                     cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
695                     break;
696                 case 5:  // Bottom
697                     copySubImage(image, width, height*3, width, height, subimg.get(), 0, 0);
698                     cubeTexture->setImage(TextureCubeMap::NEGATIVE_Z, subimg.get());
699                     cubeTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
700                     cubeTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
701                     cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
702                     break;
703                 };
704
705             }
706
707             if (itr == _crossmaps.end())
708                 _crossmaps[texname] = cubeTexture;
709             else
710                 itr->second = cubeTexture; // update existing
711             return cubeTexture.release();
712
713         } else {
714             throw BuilderException("Could not load cube cross");
715         }
716     }
717
718     return NULL;
719 }
720
721 namespace {
722 TextureBuilder::Registrar installCubeMap("cubemap", new CubeMapBuilder);
723 }
724
725 EffectNameValue<TexEnvCombine::CombineParam> combineParamInit[] =
726 {
727     {"replace", TexEnvCombine::REPLACE},
728     {"modulate", TexEnvCombine::MODULATE},
729     {"add", TexEnvCombine::ADD},
730     {"add-signed", TexEnvCombine::ADD_SIGNED},
731     {"interpolate", TexEnvCombine::INTERPOLATE},
732     {"subtract", TexEnvCombine::SUBTRACT},
733     {"dot3-rgb", TexEnvCombine::DOT3_RGB},
734     {"dot3-rgba", TexEnvCombine::DOT3_RGBA}
735 };
736
737 EffectPropertyMap<TexEnvCombine::CombineParam> combineParams(combineParamInit);
738
739 EffectNameValue<TexEnvCombine::SourceParam> sourceParamInit[] =
740 {
741     {"constant", TexEnvCombine::CONSTANT},
742     {"primary_color", TexEnvCombine::PRIMARY_COLOR},
743     {"previous", TexEnvCombine::PREVIOUS},
744     {"texture", TexEnvCombine::TEXTURE},
745     {"texture0", TexEnvCombine::TEXTURE0},
746     {"texture1", TexEnvCombine::TEXTURE1},
747     {"texture2", TexEnvCombine::TEXTURE2},
748     {"texture3", TexEnvCombine::TEXTURE3},
749     {"texture4", TexEnvCombine::TEXTURE4},
750     {"texture5", TexEnvCombine::TEXTURE5},
751     {"texture6", TexEnvCombine::TEXTURE6},
752     {"texture7", TexEnvCombine::TEXTURE7}
753 };
754
755 EffectPropertyMap<TexEnvCombine::SourceParam> sourceParams(sourceParamInit);
756
757 EffectNameValue<TexEnvCombine::OperandParam> opParamInit[] =
758 {
759     {"src-color", TexEnvCombine::SRC_COLOR},
760     {"one-minus-src-color", TexEnvCombine::ONE_MINUS_SRC_COLOR},
761     {"src-alpha", TexEnvCombine::SRC_ALPHA},
762     {"one-minus-src-alpha", TexEnvCombine::ONE_MINUS_SRC_ALPHA}
763 };
764
765 EffectPropertyMap<TexEnvCombine::OperandParam> operandParams(opParamInit);
766
767 TexEnvCombine* buildTexEnvCombine(Effect* effect, const SGPropertyNode* envProp,
768                                   const SGReaderWriterOptions* options)
769 {
770     if (!isAttributeActive(effect, envProp))
771         return 0;
772     TexEnvCombine* result = new TexEnvCombine;
773     const SGPropertyNode* p = 0;
774     if ((p = getEffectPropertyChild(effect, envProp, "combine-rgb"))) {
775         TexEnvCombine::CombineParam crgb = TexEnvCombine::MODULATE;
776         findAttr(combineParams, p, crgb);
777         result->setCombine_RGB(crgb);
778     }
779     if ((p = getEffectPropertyChild(effect, envProp, "combine-alpha"))) {
780         TexEnvCombine::CombineParam calpha = TexEnvCombine::MODULATE;
781         findAttr(combineParams, p, calpha);
782         result->setCombine_Alpha(calpha);
783     }
784     if ((p = getEffectPropertyChild(effect, envProp, "source0-rgb"))) {
785         TexEnvCombine::SourceParam source = TexEnvCombine::TEXTURE;
786         findAttr(sourceParams, p, source);
787         result->setSource0_RGB(source);
788     }
789     if ((p = getEffectPropertyChild(effect, envProp, "source1-rgb"))) {
790         TexEnvCombine::SourceParam source = TexEnvCombine::PREVIOUS;
791         findAttr(sourceParams, p, source);
792         result->setSource1_RGB(source);
793     }
794     if ((p = getEffectPropertyChild(effect, envProp, "source2-rgb"))) {
795         TexEnvCombine::SourceParam source = TexEnvCombine::CONSTANT;
796         findAttr(sourceParams, p, source);
797         result->setSource2_RGB(source);
798     }
799     if ((p = getEffectPropertyChild(effect, envProp, "source0-alpha"))) {
800         TexEnvCombine::SourceParam source = TexEnvCombine::TEXTURE;
801         findAttr(sourceParams, p, source);
802         result->setSource0_Alpha(source);
803     }
804     if ((p = getEffectPropertyChild(effect, envProp, "source1-alpha"))) {
805         TexEnvCombine::SourceParam source = TexEnvCombine::PREVIOUS;
806         findAttr(sourceParams, p, source);
807         result->setSource1_Alpha(source);
808     }
809     if ((p = getEffectPropertyChild(effect, envProp, "source2-alpha"))) {
810         TexEnvCombine::SourceParam source = TexEnvCombine::CONSTANT;
811         findAttr(sourceParams, p, source);
812         result->setSource2_Alpha(source);
813     }
814     if ((p = getEffectPropertyChild(effect, envProp, "operand0-rgb"))) {
815         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_COLOR;
816         findAttr(operandParams, p, op);
817         result->setOperand0_RGB(op);
818     }
819     if ((p = getEffectPropertyChild(effect, envProp, "operand1-rgb"))) {
820         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_COLOR;
821         findAttr(operandParams, p, op);
822         result->setOperand1_RGB(op);
823     }
824     if ((p = getEffectPropertyChild(effect, envProp, "operand2-rgb"))) {
825         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
826         findAttr(operandParams, p, op);
827         result->setOperand2_RGB(op);
828     }
829     if ((p = getEffectPropertyChild(effect, envProp, "operand0-alpha"))) {
830         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
831         findAttr(operandParams, p, op);
832         result->setOperand0_Alpha(op);
833     }
834     if ((p = getEffectPropertyChild(effect, envProp, "operand1-alpha"))) {
835         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
836         findAttr(operandParams, p, op);
837         result->setOperand1_Alpha(op);
838     }
839     if ((p = getEffectPropertyChild(effect, envProp, "operand2-alpha"))) {
840         TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
841         findAttr(operandParams, p, op);
842         result->setOperand2_Alpha(op);
843     }
844     if ((p = getEffectPropertyChild(effect, envProp, "scale-rgb"))) {
845         result->setScale_RGB(p->getValue<float>());
846     }
847     if ((p = getEffectPropertyChild(effect, envProp, "scale-alpha"))) {
848         result->setScale_Alpha(p->getValue<float>());
849     }
850 #if 0
851     if ((p = getEffectPropertyChild(effect, envProp, "constant-color"))) {
852         SGVec4d color = p->getValue<SGVec4d>();
853         result->setConstantColor(toOsg(color));
854     } else if ((p = getEffectPropertyChild(effect, envProp,
855                                            "light-direction"))) {
856         SGVec3d direction = p->getValue<SGVec3d>();
857         result->setConstantColorAsLightDirection(toOsg(direction));
858     }
859 #endif
860     const SGPropertyNode* colorNode = envProp->getChild("constant-color");
861     if (colorNode)
862         initFromParameters(effect, colorNode, result,
863                            &TexEnvCombine::setConstantColor, colorFields,
864                            options);
865     return result;
866 }
867
868 EffectNameValue<TexGen::Mode> tgenModeInit[] =
869 {
870     { "object-linear", TexGen::OBJECT_LINEAR},
871     { "eye-linear", TexGen::EYE_LINEAR},
872     { "sphere-map", TexGen::SPHERE_MAP},
873     { "normal-map", TexGen::NORMAL_MAP},
874     { "reflection-map", TexGen::REFLECTION_MAP}
875 };
876
877 EffectPropertyMap<TexGen::Mode> tgenModes(tgenModeInit);
878
879 EffectNameValue<TexGen::Coord> tgenCoordInit[] =
880 {
881     {"s", TexGen::S},
882     {"t", TexGen::T},
883     {"r", TexGen::R},
884     {"q", TexGen::Q}
885 };
886
887 EffectPropertyMap<TexGen::Coord> tgenCoords(tgenCoordInit);
888
889 TexGen* buildTexGen(Effect* effect, const SGPropertyNode* tgenProp)
890 {
891     if (!isAttributeActive(effect, tgenProp))
892         return 0;
893     TexGen* result = new TexGen;
894     TexGen::Mode mode = TexGen::OBJECT_LINEAR;
895     findAttr(tgenModes, getEffectPropertyChild(effect, tgenProp, "mode"), mode);
896     result->setMode(mode);
897     const SGPropertyNode* planesNode = tgenProp->getChild("planes");
898     if (planesNode) {
899         for (int i = 0; i < planesNode->nChildren(); ++i) {
900             const SGPropertyNode* planeNode = planesNode->getChild(i);
901             TexGen::Coord coord;
902             findAttr(tgenCoords, planeNode->getName(), coord);
903             const SGPropertyNode* realNode
904                 = getEffectPropertyNode(effect, planeNode);
905             SGVec4d plane = realNode->getValue<SGVec4d>();
906             result->setPlane(coord, toOsg(plane));
907         }
908     }
909     return result;
910 }
911
912 bool makeTextureParameters(SGPropertyNode* paramRoot, const StateSet* ss)
913 {
914     SGPropertyNode* texUnit = makeChild(paramRoot, "texture");
915     const Texture* tex = getStateAttribute<Texture>(0, ss);
916     const Texture2D* texture = dynamic_cast<const Texture2D*>(tex);
917     makeChild(texUnit, "unit")->setValue(0);
918     if (!tex) {
919         // The default shader-based technique ignores active
920         makeChild(texUnit, "active")->setValue(false);
921         return false;
922     }
923     const Image* image = texture->getImage();
924     string imageName;
925     if (image) {
926         imageName = image->getFileName();
927     } else {
928         makeChild(texUnit, "active")->setValue(false);
929         makeChild(texUnit, "type")->setValue("white");
930         return false;
931     }
932     makeChild(texUnit, "active")->setValue(true);
933     makeChild(texUnit, "type")->setValue("2d");
934     string filter = findName(filterModes,
935                              texture->getFilter(Texture::MIN_FILTER));
936     string magFilter = findName(filterModes,
937                              texture->getFilter(Texture::MAG_FILTER));
938     string wrapS = findName(wrapModes, texture->getWrap(Texture::WRAP_S));
939     string wrapT = findName(wrapModes, texture->getWrap(Texture::WRAP_T));
940     string wrapR = findName(wrapModes, texture->getWrap(Texture::WRAP_R));
941     makeChild(texUnit, "image")->setStringValue(imageName);
942     makeChild(texUnit, "filter")->setStringValue(filter);
943     makeChild(texUnit, "mag-filter")->setStringValue(magFilter);
944     makeChild(texUnit, "wrap-s")->setStringValue(wrapS);
945     makeChild(texUnit, "wrap-t")->setStringValue(wrapT);
946     makeChild(texUnit, "wrap-r")->setStringValue(wrapR);
947     return true;
948 }
949
950 class GBufferBuilder : public TextureBuilder
951 {
952 public:
953     GBufferBuilder() {}
954     Texture* build(Effect* effect, Pass* pass, const SGPropertyNode*,
955                    const SGReaderWriterOptions* options);
956 private:
957     string buffer;
958 };
959
960 class BufferNameChangeListener : public SGPropertyChangeListener, public InitializeWhenAdded,
961       public Effect::Updater {
962 public:
963     BufferNameChangeListener(Pass* p, int u, const std::string& pn) : pass(p), unit(u)
964     {
965         propName = new std::string(pn);
966     }
967     ~BufferNameChangeListener()
968     {
969         delete propName;
970         propName = 0;
971     }
972     void valueChanged(SGPropertyNode* node)
973     {
974         const char* buffer = node->getStringValue();
975         pass->setBufferUnit(unit, buffer);
976     }
977     void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot)
978     {
979         SGPropertyNode* listenProp = makeNode(propRoot, *propName);
980         delete propName;
981         propName = 0;
982         if (listenProp)
983             listenProp->addChangeListener(this, true);
984     }
985
986 private:
987     ref_ptr<Pass> pass;
988     int unit;
989     std::string* propName;
990 };
991
992 Texture* GBufferBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode* prop,
993                                     const SGReaderWriterOptions* options)
994 {
995     int unit = 0;
996     const SGPropertyNode* pUnit = prop->getChild("unit");
997     if (pUnit) {
998         unit = pUnit->getValue<int>();
999     } else {
1000         SG_LOG(SG_INPUT, SG_ALERT, "no texture unit");
1001     }
1002     const SGPropertyNode* nameProp = getEffectPropertyChild(effect, prop,
1003                                                             "name");
1004     if (!nameProp)
1005         return 0;
1006
1007     if (nameProp->nChildren() == 0) {
1008         buffer = nameProp->getStringValue();
1009         pass->setBufferUnit( unit, buffer );
1010     } else {
1011         std::string propName = getGlobalProperty(nameProp, options);
1012         BufferNameChangeListener* listener = new BufferNameChangeListener(pass, unit, propName);
1013         effect->addUpdater(listener);
1014     }
1015
1016     // Return white for now. Would be overridden in Technique::ProcessDrawable
1017     return StateAttributeFactory::instance()->getWhiteTexture();
1018 }
1019
1020 namespace
1021 {
1022     TextureBuilder::Registrar installBuffer("buffer", new GBufferBuilder);
1023 }
1024
1025 }