Am 17.10.2017 um 18:06 schrieb Robert Osfield:
> With ShaderGen it's there as stop gap to generate some shaders for a
> subset of the fixed function pipeline that is common in a subset of the
> OSG loaders. It is just a means of getting "something" on the screen on
> platforms like GL core profile and GLESL 2+, it's not an all purpose
> fully functioning solution.

The appended patch adds build in support of diffuse and specular
textures for obj by extending ShaderGen and the obj plugin. Adding
shaders is disabled by default and needs to be enabled with a plugin
option:

osgviewer -O useBuildInShader plane-with-uv-map.obj

I added the changed files and the related git patches which contains
explanations about how this has been implemented. A test obj file has
also be added. The used images are not added but downloadable from
http://www.rastertek.com/dx10tut21.html because I did not find any
related copyright info.

>With the shader_pipeline work one would need to have a top level shader
>that is able to handle the diffuse and specular texture maps in an
>appropriate way. The current top level shader I've worked on so far is
>OpenSceneGraph-Data/shaders/shaderpipeline.vert and
>shaderpipeline.frag, this is just early days though, but might give you
>an idea where this functionality is going and how one might add support
>for different texturing and lighting approaches.

Looking at the patch I'm currently not sure if all ShaderGen state mask
combinations are working correctly and the question here is if it would
make more sense to refactor ShaderGen to use define based shaders
instead of adding all the remaining combinations to the recent
implementation.

All the more because there are already 'define' based shaders available
for osg (see https://github.com/OpenMW/openmw/tree/master/files/shaders)

Regards
 Ralf








Attachment: plane-with-uv-map.obj
Description: application/tgif

# Blender MTL File: 'plane-with-uv-map.blend'
# Material Count: 1

newmtl Material.001
Ns 1000
Ka 0.000000 0.000000 0.000000
Kd 1.000000 1.000000 1.000000
Ks 1.000000 1.000000 1.000000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
map_Kd diffuse.jpg
map_Ks specular.jpg
From 62125d95e47105680bafa1eecf62fcaa75647b0d Mon Sep 17 00:00:00 2001
From: Ralf Habacker <[email protected]>
Date: Fri, 20 Oct 2017 10:33:46 +0200
Subject: [PATCH 2/3] Add support for displaying diffuse and specular textures
 to obj plugin

If enabled with the new plugin option 'useBuildInShader' the obj plugin is
able to display diffuse and specular textures provided by obj file attributes
map_Kd and map_Ks.

The plugin tracks texture type and texture unit assignments on reading
the obj file and installs an event handler after reading to create initial
shader and to track lightning state changes. On state change the shaders
are recreated to reflect recent state.
---
 src/osgPlugins/obj/ReaderWriterOBJ.cpp | 100 ++++++++++++++++++++++++++++++++-
 1 file changed, 97 insertions(+), 3 deletions(-)

diff --git a/src/osgPlugins/obj/ReaderWriterOBJ.cpp 
b/src/osgPlugins/obj/ReaderWriterOBJ.cpp
index 5bf00c3c8c..a7ab3055b0 100644
--- a/src/osgPlugins/obj/ReaderWriterOBJ.cpp
+++ b/src/osgPlugins/obj/ReaderWriterOBJ.cpp
@@ -41,16 +41,93 @@
 #include <osgDB/FileUtils>
 #include <osgDB/FileNameUtils>
 
+#include <osgGA/GUIEventHandler>
+
 #include <osgUtil/TriStripVisitor>
+#include <osgUtil/ShaderGen>
 #include <osgUtil/SmoothingVisitor>
 #include <osgUtil/Tessellator>
 
+#include <osgViewer/Viewer>
+
 #include "obj.h"
 #include "OBJWriterNodeVisitor.h"
 
 #include <map>
 #include <set>
 
+/**
+ * @brief convert ReaderWriterOBJ related material type to ShaderGenCache 
related type
+ * @param ReaderWriterOBJ related material type
+ * @return ShaderGenCache related type
+ */
+osgUtil::ShaderGenCache::StateMask 
toShaderGenStateMask(obj::Material::Map::TextureMapType type)
+{
+    switch(type)
+    {
+        case obj::Material::Map::DIFFUSE: return 
osgUtil::ShaderGenCache::DIFFUSE_MAP;
+        case obj::Material::Map::SPECULAR: return 
osgUtil::ShaderGenCache::SPECULAR_MAP;
+        default: return osgUtil::ShaderGenCache::UNDEFINED;
+    }
+}
+
+class EventHandler : public osgGA::GUIEventHandler
+{
+public:
+    EventHandler(osgUtil::ShaderGenCache::TextureUnitsMap &map)
+     :  _done(false),
+        _lightState(false)
+    {
+        _visitor = new osgUtil::ShaderGenVisitor(new 
osgUtil::ShaderGenCache(map));
+    }
+
+    bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa, 
osg::Object* obj, osg::NodeVisitor*)
+    {
+        if (ea.getHandled())
+            return false;
+
+        if (!_visitor.get())
+            return false;
+
+        switch(ea.getEventType())
+        {
+            case(osgGA::GUIEventAdapter::FRAME):
+            {
+                osgViewer::View *view = dynamic_cast<osgViewer::View*>(&aa);
+                if (!view)
+                    return false;
+
+                int lightState = 
view->getCamera()->getOrCreateStateSet()->getMode(GL_LIGHTING);
+                if (lightState != _lightState)
+                {
+                    _done = false;
+                    _lightState = lightState;
+                }
+                if (!_done)
+                {
+                    osg::Node *node = obj->asNode();
+                    if (node)
+                    {
+                        _visitor->reset();
+                        
_visitor->setRootStateSet(view->getCamera()->getOrCreateStateSet());
+                        node->accept(*_visitor);
+                        _done = true;
+                    }
+                    return true;
+                }
+                return true;
+            }
+            default:
+                return false;
+        }
+        return false;
+    }
+
+    osg::ref_ptr<osgUtil::ShaderGenVisitor> _visitor;
+    bool _done;
+    int _lightState;
+};
+
 class ReaderWriterOBJ : public osgDB::ReaderWriter
 {
 public:
@@ -62,6 +139,7 @@ public:
         supportsOption("noTriStripPolygons","Do not do the default tri 
stripping of polygons");
         supportsOption("generateFacetNormals","generate facet normals for 
verticies without normals");
         supportsOption("noReverseFaces","avoid to reverse faces when normals 
and triangles orientation are reversed");
+        supportsOption("useBuildInShader","use build in shader for displaying 
textures (currently diffuse and specular)");
 
         supportsOption("DIFFUSE=<unit>", "Set texture unit for diffuse 
texture");
         supportsOption("AMBIENT=<unit>", "Set texture unit for ambient 
texture");
@@ -141,10 +219,12 @@ protected:
         bool generateFacetNormals;
         bool fixBlackMaterials;
         bool noReverseFaces;
+        bool useBuildInShader;
         // This is the order in which the materials will be assigned to 
texture maps, unless
         // otherwise overriden
         typedef std::vector< std::pair<int,obj::Material::Map::TextureMapType> 
> TextureAllocationMap;
         TextureAllocationMap textureUnitAllocation;
+        osgUtil::ShaderGenCache::TextureUnitsMap shaderGenUnitsMap;
 
         ObjOptionsStruct()
         {
@@ -154,6 +234,7 @@ protected:
             generateFacetNormals = false;
             fixBlackMaterials = true;
             noReverseFaces = false;
+            useBuildInShader = false;
         }
     };
 
@@ -380,7 +461,11 @@ void 
ReaderWriterOBJ::buildMaterialToStateSetMap(obj::Model& model, MaterialToSt
                         break;
                     }
                 }
-                if(index>=0) load_material_texture( model, 
material.maps[index], stateset.get(), unit, options );
+                if(index>=0)
+                {
+                    load_material_texture( model, material.maps[index], 
stateset.get(), unit, options );
+                    localOptions.shaderGenUnitsMap[unit] = 
toShaderGenStateMask(type);
+                }
             }
         }
         // If the user has set no options, then we load them up in the order 
contained in the enum. This
@@ -404,6 +489,7 @@ void 
ReaderWriterOBJ::buildMaterialToStateSetMap(obj::Model& model, MaterialToSt
                 if(index>=0)
                 {
                     load_material_texture( model, material.maps[index], 
stateset.get(), unit, options );
+                    localOptions.shaderGenUnitsMap[unit] = 
toShaderGenStateMask(type);
                     unit++;
                 }
             }
@@ -873,6 +959,10 @@ ReaderWriterOBJ::ObjOptionsStruct 
ReaderWriterOBJ::parseOptions(const osgDB::Rea
             {
                 localOptions.noReverseFaces = true;
             }
+            else if (pre_equals == "useBuildInShader")
+            {
+                localOptions.useBuildInShader = true;
+            }
             else if (post_equals.length()>0)
             {
                 obj::Material::Map::TextureMapType type = 
obj::Material::Map::UNKNOWN;
@@ -922,8 +1012,10 @@ osgDB::ReaderWriter::ReadResult 
ReaderWriterOBJ::readNode(const std::string& fil
         model.readOBJ(fin, local_opt.get());
 
         ObjOptionsStruct localOptions = parseOptions(options);
-
         osg::Node* node = convertModelToSceneGraph(model, localOptions, 
local_opt.get());
+
+        if (localOptions.useBuildInShader)
+            node->addEventCallback(new 
EventHandler(localOptions.shaderGenUnitsMap));
         return node;
     }
 
@@ -940,8 +1032,10 @@ osgDB::ReaderWriter::ReadResult 
ReaderWriterOBJ::readNode(std::istream& fin, con
         model.readOBJ(fin, options);
 
         ObjOptionsStruct localOptions = parseOptions(options);
+        osg::Node* node= convertModelToSceneGraph(model, localOptions, 
options);
 
-        osg::Node* node = convertModelToSceneGraph(model, localOptions, 
options);
+        if (localOptions.useBuildInShader)
+            node->addEventCallback(new 
EventHandler(localOptions.shaderGenUnitsMap));
         return node;
     }
 
-- 
2.12.3

From 6f20ad0a4b4b34b5d7a3bd298897dd69eb99e6f6 Mon Sep 17 00:00:00 2001
From: Ralf Habacker <[email protected]>
Date: Fri, 22 Sep 2017 13:39:22 +0200
Subject: [PATCH 1/3] Add support for displaying specular textures to
 osgUtil::ShaderGenCache

This patch adds a type osgUtil::ShaderGenCache::TextureUnitsMap to hold 
assignments
between texture units and texture types. These assignment could be specified in 
the
constructor and are handled inside the class to provide required information to
shaders for displaying diffuse and specular textures using the Phong shading 
model
for one point light (for details see
https://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/lighting.php).

Skipping state sets with already attached programs has been removed because to
reflect state changes it is required that ShaderGenCache recreates the shader on
each run.
---
 include/osgUtil/ShaderGen |  14 ++++-
 src/osgUtil/ShaderGen.cpp | 142 +++++++++++++++++++++++++++++++---------------
 2 files changed, 108 insertions(+), 48 deletions(-)

diff --git a/include/osgUtil/ShaderGen b/include/osgUtil/ShaderGen
index 9beb717c35..6a544e5774 100644
--- a/include/osgUtil/ShaderGen
+++ b/include/osgUtil/ShaderGen
@@ -31,25 +31,33 @@ class OSGUTIL_EXPORT ShaderGenCache : public osg::Referenced
 public:
     enum StateMask
     {
+        UNDEFINED = 0,
         BLEND = 1,
         LIGHTING = 2,
         FOG = 4,
-        DIFFUSE_MAP = 8, //< Texture in unit 0
-        NORMAL_MAP = 16  //< Texture in unit 1 and vertex attribute array 6
+        DIFFUSE_MAP = 8, //< Texture in unit 0 if not TextureUnitsMap has been 
provided
+        NORMAL_MAP = 16,  //< Texture in unit 1 and vertex attribute array 6 
if not TextureUnitsMap has been provided
+        SPECULAR_MAP = 32
     };
 
     typedef std::map<int, osg::ref_ptr<osg::StateSet> > StateSetMap;
+    typedef std::map<int, enum StateMask> TextureUnitsMap;
 
-    ShaderGenCache() {};
+    ShaderGenCache(const TextureUnitsMap &textureUnits = TextureUnitsMap())
+      : _textureUnits(textureUnits)
+    {
+    }
 
     void setStateSet(int stateMask, osg::StateSet *program);
     osg::StateSet *getStateSet(int stateMask) const;
     osg::StateSet *getOrCreateStateSet(int stateMask);
+    TextureUnitsMap &getTextureUnits();
 
 protected:
     osg::StateSet *createStateSet(int stateMask) const;
     mutable OpenThreads::Mutex _mutex;
     StateSetMap _stateSetMap;
+    TextureUnitsMap _textureUnits;
 
 };
 
diff --git a/src/osgUtil/ShaderGen.cpp b/src/osgUtil/ShaderGen.cpp
index 25329f83cc..f2e2f925d0 100644
--- a/src/osgUtil/ShaderGen.cpp
+++ b/src/osgUtil/ShaderGen.cpp
@@ -110,6 +110,11 @@ osg::StateSet *ShaderGenCache::getOrCreateStateSet(int 
stateMask)
     return it->second.get();
 }
 
+ShaderGenCache::TextureUnitsMap &ShaderGenCache::getTextureUnits()
+{
+    return _textureUnits;
+}
+
 osg::StateSet *ShaderGenCache::createStateSet(int stateMask) const
 {
     osg::StateSet *stateSet = new osg::StateSet;
@@ -149,21 +154,31 @@ osg::StateSet *ShaderGenCache::createStateSet(int 
stateMask) const
     frag << vert_frag.str();
 
     // write uniforms and attributes
-    int unit = 0;
-    if (stateMask & DIFFUSE_MAP)
-    {
-        osg::Uniform *diffuseMap = new osg::Uniform("diffuseMap", unit++);
-        stateSet->addUniform(diffuseMap);
-        frag << "uniform sampler2D diffuseMap;\n";
-    }
-
-    if (stateMask & NORMAL_MAP)
+    TextureUnitsMap::const_iterator it = _textureUnits.begin();
+    for (; it != _textureUnits.end(); it++)
     {
-        osg::Uniform *normalMap = new osg::Uniform("normalMap", unit++);
-        stateSet->addUniform(normalMap);
-        frag << "uniform sampler2D normalMap;\n";
-        program->addBindAttribLocation("tangent", 6);
-        vert << "attribute vec3 tangent;\n";
+        if (it->second == DIFFUSE_MAP && (stateMask & DIFFUSE_MAP))
+        {
+            osg::Uniform *diffuseMap = new osg::Uniform("diffuseMap", 
it->first);
+            stateSet->addUniform(diffuseMap);
+            frag << "uniform sampler2D diffuseMap;\n";
+        }
+
+        if (it->second == SPECULAR_MAP && (stateMask & LIGHTING) && (stateMask 
& SPECULAR_MAP))
+        {
+            osg::Uniform *specularMap = new osg::Uniform("specularMap", 
it->first);
+            stateSet->addUniform(specularMap);
+            frag << "uniform sampler2D specularMap;\n";
+        }
+
+        if (it->second == NORMAL_MAP && (stateMask & NORMAL_MAP))
+        {
+            osg::Uniform *normalMap = new osg::Uniform("normalMap", it->first);
+            stateSet->addUniform(normalMap);
+            frag << "uniform sampler2D normalMap;\n";
+            program->addBindAttribLocation("tangent", 6);
+            vert << "attribute vec3 tangent;\n";
+        }
     }
 
     vert << "\n"\
@@ -171,8 +186,9 @@ osg::StateSet *ShaderGenCache::createStateSet(int 
stateMask) const
         "{\n"\
         "  gl_Position = ftransform();\n";
 
-    if (stateMask & (DIFFUSE_MAP | NORMAL_MAP))
+    if ((stateMask & DIFFUSE_MAP) || (stateMask & NORMAL_MAP) || (stateMask & 
SPECULAR_MAP))
     {
+        // TODO support different texture sizes by using the related uniform 
index
         vert << "  gl_TexCoord[0] = gl_MultiTexCoord0;\n";
     }
 
@@ -206,7 +222,8 @@ osg::StateSet *ShaderGenCache::createStateSet(int 
stateMask) const
             "  if (lpos.w == 0.0)\n"\
             "    lightDir = lpos.xyz;\n"\
             "  else\n"\
-            "    lightDir = lpos.xyz + dir;\n";
+            "    lightDir = lpos.xyz + dir;\n"\
+            "  vertexColor = gl_Color;\n";
     }
     else if (stateMask & FOG)
     {
@@ -228,36 +245,51 @@ osg::StateSet *ShaderGenCache::createStateSet(int 
stateMask) const
 
     if (stateMask & DIFFUSE_MAP)
     {
-        frag << "  vec4 base = vertexColor * texture2D(diffuseMap, 
gl_TexCoord[0].st);\n";
+        frag << "  vec4 base = texture2D(diffuseMap, gl_TexCoord[0].st);\n";
     }
     else
     {
         frag << "  vec4 base = vertexColor;\n";
     }
 
+    if (stateMask & LIGHTING)
+    {
+        if (stateMask & SPECULAR_MAP)
+        {
+            frag << "  vec4 spec = texture2D(specularMap, 
gl_TexCoord[0].st);\n";
+        }
+        else
+        {
+            frag << "  vec4 spec = vec4(1.0);\n";
+        }
+    }
+
     if (stateMask & NORMAL_MAP)
     {
         frag << "  vec3 normalDir = texture2D(normalMap, 
gl_TexCoord[0].st).xyz*2.0-1.0;\n";
     }
 
 #if !OSG_GLES2_FEATURES && !OSG_GLES3_FEATURES && !OSG_GL2_FEATURES
-    if (stateMask & (LIGHTING | NORMAL_MAP))
+    if (stateMask & LIGHTING)
     {
-        frag <<
-            "  vec3 nd = normalize(normalDir);\n"\
-            "  vec3 ld = normalize(lightDir);\n"\
-            "  vec3 vd = normalize(viewDir);\n"\
-            "  vec4 color = gl_FrontLightModelProduct.sceneColor;\n"\
-            "  color += gl_FrontLightProduct[0].ambient;\n"\
-            "  float diff = max(dot(ld, nd), 0.0);\n"\
-            "  color += gl_FrontLightProduct[0].diffuse * diff;\n"\
-            "  color *= base;\n"\
-            "  if (diff > 0.0)\n"\
-            "  {\n"\
-            "    vec3 halfDir = normalize(ld+vd);\n"\
-            "    color.rgb += base.a * gl_FrontLightProduct[0].specular.rgb * 
\n"\
-            "      pow(max(dot(halfDir, nd), 0.0), 
gl_FrontMaterial.shininess);\n"\
-            "  }\n";
+        if ((stateMask & NORMAL_MAP) || (stateMask & DIFFUSE_MAP) || 
(stateMask & SPECULAR_MAP))
+        {
+            frag <<
+                "  vec3 nd = normalize(normalDir);\n"\
+                "  vec3 ld = normalize(lightDir);\n"\
+                "  vec3 vd = normalize(viewDir);\n"\
+                "  vec4 Iamb = gl_FrontLightProduct[0].ambient;\n"\
+                "  float diff = max(dot(ld, nd), 0.0);\n"\
+                "  vec4 Idiff = gl_FrontLightProduct[0].diffuse * diff;\n"\
+                "  vec4 Ispec; \n"\
+                "  if (diff > 0.0)\n"\
+                "  {\n"\
+                "    vec3 halfDir = normalize(ld+vd);\n"\
+                "    Ispec = gl_FrontLightProduct[0].specular * \n"\
+                "      pow(max(dot(halfDir, nd), 0.0), 
gl_FrontMaterial.shininess);\n"\
+                "  }\n"
+                "  vec4 color = gl_FrontLightModelProduct.sceneColor + Iamb + 
Idiff * base + Ispec * spec;\n";
+        }
     }
     else
 #endif
@@ -265,7 +297,6 @@ osg::StateSet *ShaderGenCache::createStateSet(int 
stateMask) const
         frag << "  vec4 color = base;\n";
     }
 
-
 #if !OSG_GLES2_FEATURES && !OSG_GLES3_FEATURES && !OSG_GL2_FEATURES
     if (stateMask & FOG)
     {
@@ -371,10 +402,6 @@ void ShaderGenVisitor::update(osg::Drawable *drawable)
     if (state->getStateSetStackSize() == (_rootStateSet.valid() ? 1u : 0u))
         return;
 
-    // skip state sets with already attached programs
-    if (state->getAttribute(osg::StateAttribute::PROGRAM))
-        return;
-
     int stateMask = 0;
     //if (state->getMode(GL_BLEND) & osg::StateAttribute::ON)
     //    stateMask |= ShaderGen::BLEND;
@@ -382,12 +409,25 @@ void ShaderGenVisitor::update(osg::Drawable *drawable)
         stateMask |= ShaderGenCache::LIGHTING;
     if (state->getMode(GL_FOG) & osg::StateAttribute::ON)
         stateMask |= ShaderGenCache::FOG;
-    if (state->getTextureAttribute(0, osg::StateAttribute::TEXTURE))
-        stateMask |= ShaderGenCache::DIFFUSE_MAP;
 
-    if (state->getTextureAttribute(1, osg::StateAttribute::TEXTURE) && 
geometry!=0 &&
-        geometry->getVertexAttribArray(6)) //tangent
-        stateMask |= ShaderGenCache::NORMAL_MAP;
+    ShaderGenCache::TextureUnitsMap &_textureUnits = 
_stateCache->getTextureUnits();
+    if (_textureUnits.empty())
+    {
+        if (state->getTextureAttribute(0, osg::StateAttribute::TEXTURE))
+            stateMask |= ShaderGenCache::DIFFUSE_MAP;
+        if (state->getTextureAttribute(1, osg::StateAttribute::TEXTURE) && 
geometry!=0 &&
+                geometry->getVertexAttribArray(6)) //tangent
+            stateMask |= ShaderGenCache::NORMAL_MAP;
+    }
+    else
+    {
+        ShaderGenCache::TextureUnitsMap::const_iterator it = 
_textureUnits.begin();
+        for (;it != _textureUnits.end(); it++)
+        {
+            if (state->getTextureAttribute(it->first, 
osg::StateAttribute::TEXTURE))
+                stateMask |= it->second;
+        }
+    }
 
     // Get program and uniforms for accumulated state.
     osg::StateSet *progss = _stateCache->getOrCreateStateSet(stateMask);
@@ -406,6 +446,18 @@ void ShaderGenVisitor::update(osg::Drawable *drawable)
     {
         ss->removeMode(GL_FOG);
     }
-    if ((stateMask&ShaderGenCache::DIFFUSE_MAP)!=0) ss->removeTextureMode(0, 
GL_TEXTURE_2D);
-    if ((stateMask&ShaderGenCache::NORMAL_MAP)!=0) ss->removeTextureMode(1, 
GL_TEXTURE_2D);
+    if (_textureUnits.empty())
+    {
+        if ((stateMask&ShaderGenCache::DIFFUSE_MAP)!=0) 
ss->removeTextureMode(0, GL_TEXTURE_2D);
+        if ((stateMask&ShaderGenCache::NORMAL_MAP)!=0) 
ss->removeTextureMode(1, GL_TEXTURE_2D);
+    }
+    else
+    {
+        ShaderGenCache::TextureUnitsMap::const_iterator it = 
_textureUnits.begin();
+        for (;it != _textureUnits.end(); it++)
+        {
+            if ((stateMask & it->second)!=0)
+                ss->removeTextureMode(it->first, GL_TEXTURE_2D);
+        }
+    }
 }
-- 
2.12.3

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/

/**
 * \brief    Shader generator framework.
 * \author   Maciej Krol
 */

#ifndef OSGUTIL_SHADER_STATE_
#define OSGUTIL_SHADER_STATE_ 1

#include <osgUtil/Export>
#include <osg/NodeVisitor>
#include <osg/State>

namespace osgUtil
{

class OSGUTIL_EXPORT ShaderGenCache : public osg::Referenced
{
public:
    enum StateMask
    {
        UNDEFINED = 0,
        BLEND = 1,
        LIGHTING = 2,
        FOG = 4,
        DIFFUSE_MAP = 8, //< Texture in unit 0 if not TextureUnitsMap has been 
provided
        NORMAL_MAP = 16,  //< Texture in unit 1 and vertex attribute array 6 if 
not TextureUnitsMap has been provided
        SPECULAR_MAP = 32
    };

    typedef std::map<int, osg::ref_ptr<osg::StateSet> > StateSetMap;
    typedef std::map<int, enum StateMask> TextureUnitsMap;

    ShaderGenCache(const TextureUnitsMap &textureUnits = TextureUnitsMap())
      : _textureUnits(textureUnits)
    {
    }

    void setStateSet(int stateMask, osg::StateSet *program);
    osg::StateSet *getStateSet(int stateMask) const;
    osg::StateSet *getOrCreateStateSet(int stateMask);
    TextureUnitsMap &getTextureUnits();

protected:
    osg::StateSet *createStateSet(int stateMask) const;
    mutable OpenThreads::Mutex _mutex;
    StateSetMap _stateSetMap;
    TextureUnitsMap _textureUnits;

};

class OSGUTIL_EXPORT ShaderGenVisitor : public osg::NodeVisitor
{
public:
    ShaderGenVisitor();
    ShaderGenVisitor(ShaderGenCache *stateCache);

    void setStateCache(ShaderGenCache *stateCache) { _stateCache = stateCache; }
    ShaderGenCache *getStateCache() const { return _stateCache.get(); }

    /// Top level state set applied as the first one.
    void setRootStateSet(osg::StateSet *stateSet);
    osg::StateSet *getRootStateSet() const { return _rootStateSet.get(); }

    void apply(osg::Node &node);
    void apply(osg::Geode &geode);

    void reset();

protected:
    void update(osg::Drawable *drawable);

    osg::ref_ptr<ShaderGenCache> _stateCache;
    osg::ref_ptr<osg::State> _state;
    osg::ref_ptr<osg::StateSet> _rootStateSet;
};

}

#endif
// -*-c++-*-

/*
 * Wavefront OBJ loader for Open Scene Graph
 *
 * Copyright (C) 2001,2007 Ulrich Hertlein <[email protected]>
 *
 * Modified by Robert Osfield to support per Drawable coord, normal and
 * texture coord arrays, bug fixes, and support for texture mapping.
 *
 * Writing support added 2007 by Stephan Huber, http://digitalmind.de,
 * some ideas taken from the dae-plugin
 *
 * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for
 * real-time rendering of large 3D photo-realistic models.
 * The OSG homepage is http://www.openscenegraph.org/
 */

#if defined(_MSC_VER)
    #pragma warning( disable : 4786 )
#endif

#include <stdlib.h>
#include <string>

#include <osg/Notify>
#include <osg/Node>
#include <osg/MatrixTransform>
#include <osg/Geode>
#include <osg/Vec3f>

#include <osg/Geometry>
#include <osg/StateSet>
#include <osg/Material>
#include <osg/Texture2D>
#include <osg/TexGen>
#include <osg/TexMat>

#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>

#include <osgGA/GUIEventHandler>

#include <osgUtil/TriStripVisitor>
#include <osgUtil/ShaderGen>
#include <osgUtil/SmoothingVisitor>
#include <osgUtil/Tessellator>

#include <osgViewer/Viewer>

#include "obj.h"
#include "OBJWriterNodeVisitor.h"

#include <map>
#include <set>

/**
 * @brief convert ReaderWriterOBJ related material type to ShaderGenCache related type
 * @param ReaderWriterOBJ related material type
 * @return ShaderGenCache related type
 */
osgUtil::ShaderGenCache::StateMask toShaderGenStateMask(obj::Material::Map::TextureMapType type)
{
    switch(type)
    {
        case obj::Material::Map::DIFFUSE: return osgUtil::ShaderGenCache::DIFFUSE_MAP;
        case obj::Material::Map::SPECULAR: return osgUtil::ShaderGenCache::SPECULAR_MAP;
        default: return osgUtil::ShaderGenCache::UNDEFINED;
    }
}

class EventHandler : public osgGA::GUIEventHandler
{
public:
    EventHandler(osgUtil::ShaderGenCache::TextureUnitsMap &map)
     :  _done(false),
        _lightState(false)
    {
        _visitor = new osgUtil::ShaderGenVisitor(new osgUtil::ShaderGenCache(map));
    }

    bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa, osg::Object* obj, osg::NodeVisitor*)
    {
        if (ea.getHandled())
            return false;

        if (!_visitor.get())
            return false;

        switch(ea.getEventType())
        {
            case(osgGA::GUIEventAdapter::FRAME):
            {
                osgViewer::View *view = dynamic_cast<osgViewer::View*>(&aa);
                if (!view)
                    return false;

                int lightState = view->getCamera()->getOrCreateStateSet()->getMode(GL_LIGHTING);
                if (lightState != _lightState)
                {
                    _done = false;
                    _lightState = lightState;
                }
                if (!_done)
                {
                    osg::Node *node = obj->asNode();
                    if (node)
                    {
                        _visitor->reset();
                        _visitor->setRootStateSet(view->getCamera()->getOrCreateStateSet());
                        node->accept(*_visitor);
                        _done = true;
                    }
                    return true;
                }
                return true;
            }
            default:
                return false;
        }
        return false;
    }

    osg::ref_ptr<osgUtil::ShaderGenVisitor> _visitor;
    bool _done;
    int _lightState;
};

class ReaderWriterOBJ : public osgDB::ReaderWriter
{
public:
    ReaderWriterOBJ()
    {
        supportsExtension("obj","Alias Wavefront OBJ format");
        supportsOption("noRotation","Do not do the default rotate about X axis");
        supportsOption("noTesselateLargePolygons","Do not do the default tesselation of large polygons");
        supportsOption("noTriStripPolygons","Do not do the default tri stripping of polygons");
        supportsOption("generateFacetNormals","generate facet normals for verticies without normals");
        supportsOption("noReverseFaces","avoid to reverse faces when normals and triangles orientation are reversed");
        supportsOption("useBuildInShader","use build in shader for displaying textures (currently diffuse and specular)");

        supportsOption("DIFFUSE=<unit>", "Set texture unit for diffuse texture");
        supportsOption("AMBIENT=<unit>", "Set texture unit for ambient texture");
        supportsOption("SPECULAR=<unit>", "Set texture unit for specular texture");
        supportsOption("SPECULAR_EXPONENT=<unit>", "Set texture unit for specular exponent texture");
        supportsOption("OPACITY=<unit>", "Set texture unit for opacity/dissolve texture");
        supportsOption("BUMP=<unit>", "Set texture unit for bumpmap texture");
        supportsOption("DISPLACEMENT=<unit>", "Set texture unit for displacement texture");
        supportsOption("REFLECTION=<unit>", "Set texture unit for reflection texture");

    }

    virtual const char* className() const { return "Wavefront OBJ Reader"; }

    virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const;

    virtual ReadResult readNode(std::istream& fin, const Options* options) const;

    virtual WriteResult writeObject(const osg::Object& obj,const std::string& fileName,const Options* options=NULL) const
    {
        const osg::Node* node = dynamic_cast<const osg::Node*>(&obj);
        if (node)
            return writeNode(*node, fileName, options);
        else
            return WriteResult(WriteResult::FILE_NOT_HANDLED);
    }

    virtual WriteResult writeNode(const osg::Node& node,const std::string& fileName,const Options* options =NULL) const
    {
        if (!acceptsExtension(osgDB::getFileExtension(fileName)))
            return WriteResult(WriteResult::FILE_NOT_HANDLED);

        osgDB::ofstream f(fileName.c_str());
        std::string materialFile = osgDB::getNameLessExtension(fileName) + ".mtl";
        OBJWriterNodeVisitor nv(f, osgDB::getSimpleFileName(materialFile));

        // we must cast away constness
        (const_cast<osg::Node*>(&node))->accept(nv);

        osgDB::ofstream mf(materialFile.c_str());
        nv.writeMaterials(mf);

        return WriteResult(WriteResult::FILE_SAVED);
    }


    virtual WriteResult writeObject(const osg::Object& obj,std::ostream& fout,const Options* options=NULL) const
    {
        const osg::Node* node = dynamic_cast<const osg::Node*>(&obj);
        if (node)
            return writeNode(*node, fout, options);
        else
            return WriteResult(WriteResult::FILE_NOT_HANDLED);
    }

    virtual WriteResult writeNode(const osg::Node& node,std::ostream& fout,const Options* =NULL) const
    {
        // writing to a stream does not support materials

        OBJWriterNodeVisitor nv(fout);

        // we must cast away constness
        (const_cast<osg::Node*>(&node))->accept(nv);

        return WriteResult(WriteResult::FILE_SAVED);
    }



protected:

     class ObjOptionsStruct {
     public:
        bool rotate;
        bool noTesselateLargePolygons;
        bool noTriStripPolygons;
        bool generateFacetNormals;
        bool fixBlackMaterials;
        bool noReverseFaces;
        bool useBuildInShader;
        // This is the order in which the materials will be assigned to texture maps, unless
        // otherwise overriden
        typedef std::vector< std::pair<int,obj::Material::Map::TextureMapType> > TextureAllocationMap;
        TextureAllocationMap textureUnitAllocation;
        osgUtil::ShaderGenCache::TextureUnitsMap shaderGenUnitsMap;

        ObjOptionsStruct()
        {
            rotate = true;
            noTesselateLargePolygons = false;
            noTriStripPolygons = false;
            generateFacetNormals = false;
            fixBlackMaterials = true;
            noReverseFaces = false;
            useBuildInShader = false;
        }
    };

    typedef std::map< std::string, osg::ref_ptr<osg::StateSet> > MaterialToStateSetMap;

    void buildMaterialToStateSetMap(obj::Model& model, MaterialToStateSetMap& materialToSetSetMapObj, ObjOptionsStruct& localOptions, const Options* options) const;

    osg::Geometry* convertElementListToGeometry(obj::Model& model, obj::Model::ElementList& elementList, ObjOptionsStruct& localOptions) const;

    osg::Node* convertModelToSceneGraph(obj::Model& model, ObjOptionsStruct& localOptions, const Options* options) const;

    inline osg::Vec3 transformVertex(const osg::Vec3& vec, const bool rotate) const ;
    inline osg::Vec3 transformNormal(const osg::Vec3& vec, const bool rotate) const ;

    ObjOptionsStruct parseOptions(const Options* options) const;


};

inline osg::Vec3 ReaderWriterOBJ::transformVertex(const osg::Vec3& vec, const bool rotate) const
{
    if(rotate==true)
    {
        return osg::Vec3(vec.x(),-vec.z(),vec.y());
    }
    else
    {
        return vec;
    }
}

inline osg::Vec3 ReaderWriterOBJ::transformNormal(const osg::Vec3& vec, const bool rotate) const
{
    if(rotate==true)
    {
        return osg::Vec3(vec.x(),-vec.z(),vec.y());
    }
    else
    {
        return vec;
    }
}


// register with Registry to instantiate the above reader/writer.
REGISTER_OSGPLUGIN(obj, ReaderWriterOBJ)

static void load_material_texture(    obj::Model &model,
                                    obj::Material::Map &map,
                                    osg::StateSet *stateset,
                                    const unsigned int texture_unit,
                                    const osgDB::Options* options)
{
    std::string filename = map.name;
    if (!filename.empty())
    {
        osg::ref_ptr< osg::Image > image;
        if ( !model.getDatabasePath().empty() )
        {
            // first try with database path of parent.
            image = osgDB::readRefImageFile(model.getDatabasePath()+'/'+filename, options);
        }

        if ( !image.valid() )
        {
            // if not already set then try the filename as is.
            image = osgDB::readRefImageFile(filename, options);
        }

        if ( image.valid() )
        {
            osg::Texture2D* texture = new osg::Texture2D( image.get() );
            osg::Texture::WrapMode textureWrapMode;
            if(map.clamp == true)
            {
                textureWrapMode = osg::Texture::CLAMP_TO_BORDER;
                texture->setBorderColor(osg::Vec4(0.0,0.0,0.0,0.0));    // transparent
                //stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
                //stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
            }
            else
            {
                textureWrapMode = osg::Texture::REPEAT;
            }

            texture->setWrap(osg::Texture2D::WRAP_R, textureWrapMode);
            texture->setWrap(osg::Texture2D::WRAP_S, textureWrapMode);
            texture->setWrap(osg::Texture2D::WRAP_T, textureWrapMode);
            stateset->setTextureAttributeAndModes( texture_unit, texture,osg::StateAttribute::ON );

            if ( map.type == obj::Material::Map::REFLECTION )
            {
                osg::TexGen* texgen = new osg::TexGen;
                texgen->setMode(osg::TexGen::SPHERE_MAP);
                stateset->setTextureAttributeAndModes( texture_unit,texgen,osg::StateAttribute::ON );
            }

            if  ( image->isImageTranslucent())
            {
                OSG_INFO<<"Found transparent image"<<std::endl;
                stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
                stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
            }
        }
    }

    if (map.uScale != 1.0f || map.vScale != 1.0f ||
            map.uOffset != 0.0f || map.vOffset != 0.0f)
    {
        osg::Matrix mat;
        if (map.uScale != 1.0f || map.vScale != 1.0f)
        {
            OSG_DEBUG << "Obj TexMat scale=" << map.uScale << "," << map.vScale << std::endl;
            mat *= osg::Matrix::scale(map.uScale, map.vScale, 1.0);
        }
        if (map.uOffset != 0.0f || map.vOffset != 0.0f)
        {
            OSG_DEBUG << "Obj TexMat offset=" << map.uOffset << "," << map.uOffset << std::endl;
            mat *= osg::Matrix::translate(map.uOffset, map.vOffset, 0.0);
        }

        osg::TexMat* texmat = new osg::TexMat;
        texmat->setMatrix(mat);
        stateset->setTextureAttributeAndModes( texture_unit,texmat,osg::StateAttribute::ON );
    }
}


void ReaderWriterOBJ::buildMaterialToStateSetMap(obj::Model& model, MaterialToStateSetMap& materialToStateSetMap, ObjOptionsStruct& localOptions, const Options* options) const
{
    if (localOptions.fixBlackMaterials)
    {
        // hack to fix Maya exported models that contian all black materials.
        int numBlack = 0;
        int numNotBlack = 0;
        obj::Model::MaterialMap::iterator itr;
        for(itr = model.materialMap.begin();
            itr != model.materialMap.end();
            ++itr)
        {
            obj::Material& material = itr->second;
            if (material.ambient==osg::Vec4(0.0f,0.0f,0.0f,1.0f) &&
                material.diffuse==osg::Vec4(0.0f,0.0f,0.0f,1.0f))
            {
                ++numBlack;
            }
            else
            {
                ++numNotBlack;
            }
        }

        if (numNotBlack==0 && numBlack!=0)
        {
            for(itr = model.materialMap.begin();
                itr != model.materialMap.end();
                ++itr)
            {
                obj::Material& material = itr->second;
                if (material.ambient==osg::Vec4(0.0f,0.0f,0.0f,1.0f) &&
                    material.diffuse==osg::Vec4(0.0f,0.0f,0.0f,1.0f))
                {
                    material.ambient.set(0.3f,0.3f,0.3f,1.0f);
                    material.diffuse.set(1.0f,1.0f,1.0f,1.0f);
                }
            }
        }
    }

    for(obj::Model::MaterialMap::iterator itr = model.materialMap.begin();
        itr != model.materialMap.end();
        ++itr)
    {
        obj::Material& material = itr->second;

        osg::ref_ptr< osg::StateSet > stateset = new osg::StateSet;

        bool isTransparent = false;

        // handle material colors
        // http://java3d.j3d.org/utilities/loaders/obj/sun.html
        if (material.illum != 0)
        {
            osg::Material* osg_material = new osg::Material;
            stateset->setAttribute(osg_material);
            osg_material->setName(material.name);
            osg_material->setAmbient(osg::Material::FRONT_AND_BACK,material.ambient);
            osg_material->setDiffuse(osg::Material::FRONT_AND_BACK,material.diffuse);
            osg_material->setEmission(osg::Material::FRONT_AND_BACK,material.emissive);

            if (material.illum == 2) {
                osg_material->setSpecular(osg::Material::FRONT_AND_BACK,material.specular);
            } else {
                osg_material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4(0,0,0,1));
            }
            osg_material->setShininess(osg::Material::FRONT_AND_BACK,(material.Ns/1000.0f)*128.0f ); // note OBJ shiniess is 0..1000.

            if (material.ambient[3]!=1.0 ||
                material.diffuse[3]!=1.0 ||
                material.specular[3]!=1.0||
                material.emissive[3]!=1.0)
            {
                OSG_INFO<<"Found transparent material"<<std::endl;
                isTransparent = true;
            }
        }

        // If the user has explicitly set the required texture type to unit map via the options
        // string, then we load ONLY those textures that are in the map.
        if(localOptions.textureUnitAllocation.size()>0)
        {
            for(unsigned int i=0;i<localOptions.textureUnitAllocation.size();i++)
            {
                // firstly, get the option set pair
                int unit = localOptions.textureUnitAllocation[i].first;
                obj::Material::Map::TextureMapType type = localOptions.textureUnitAllocation[i].second;
                // secondly, see if this texture type (e.g. DIFFUSE) is one of those in the material
                int index = -1;
                for(unsigned int j=0;j<material.maps.size();j++)
                {
                    if(material.maps[j].type == type)
                    {
                        index = (int) j;
                        break;
                    }
                }
                if(index>=0)
                {
                    load_material_texture( model, material.maps[index], stateset.get(), unit, options );
                    localOptions.shaderGenUnitsMap[unit] = toShaderGenStateMask(type);
                }
            }
        }
        // If the user has set no options, then we load them up in the order contained in the enum. This
        // latter method is an attempt not to break user's existing code
        else
        {
            int unit = 0;
            for(int i=0;i<(int) obj::Material::Map::UNKNOWN;i++) // for each type
            {
                obj::Material::Map::TextureMapType type = (obj::Material::Map::TextureMapType) i;
                // see if this texture type (e.g. DIFFUSE) is one of those in the material
                int index = -1;
                for(unsigned int j=0;j<material.maps.size();j++)
                {
                    if(material.maps[j].type == type)
                    {
                        index = (int) j;
                        break;
                    }
                }
                if(index>=0)
                {
                    load_material_texture( model, material.maps[index], stateset.get(), unit, options );
                    localOptions.shaderGenUnitsMap[unit] = toShaderGenStateMask(type);
                    unit++;
                }
            }
        }

        if (isTransparent)
        {
            stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
            stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
        }

        materialToStateSetMap[material.name] = stateset.get();
    }
}

osg::Geometry* ReaderWriterOBJ::convertElementListToGeometry(obj::Model& model, obj::Model::ElementList& elementList, ObjOptionsStruct& localOptions) const
{

    unsigned int numVertexIndices = 0;
    unsigned int numNormalIndices = 0;
    unsigned int numTexCoordIndices = 0;

    unsigned int numPointElements = 0;
    unsigned int numPolylineElements = 0;
    unsigned int numPolygonElements = 0;

    obj::Model::ElementList::iterator itr;

    if (localOptions.generateFacetNormals == true) {
        for(itr=elementList.begin();
                itr!=elementList.end();
                    ++itr)
        {
            obj::Element& element = *(*itr);
            if (element.dataType==obj::Element::POINTS || element.dataType==obj::Element::POLYLINE)
                continue;

            if (element.normalIndices.size() == 0) {
                // fill in the normals
                int a = element.vertexIndices[0];
                int b = element.vertexIndices[1];
                int c = element.vertexIndices[2];

                osg::Vec3f ab(model.vertices[b]);
                osg::Vec3f ac(model.vertices[c]);

                ab -= model.vertices[a];
                ac -= model.vertices[a];

                osg::Vec3f Norm( ab ^ ac );
                Norm.normalize();
                int normal_idx = model.normals.size();
                model.normals.push_back(Norm);

                for (unsigned i=0 ; i < element.vertexIndices.size() ; i++)
                    element.normalIndices.push_back(normal_idx);
            }
        }
    }



    for(itr=elementList.begin();
        itr!=elementList.end();
        ++itr)
    {
        obj::Element& element = *(*itr);

        numVertexIndices += element.vertexIndices.size();
        numNormalIndices += element.normalIndices.size();
        numTexCoordIndices += element.texCoordIndices.size();

        numPointElements += (element.dataType==obj::Element::POINTS) ? 1 : 0;
        numPolylineElements += (element.dataType==obj::Element::POLYLINE) ? 1 : 0;
        numPolygonElements += (element.dataType==obj::Element::POLYGON) ? 1 : 0;

    }

    if (numVertexIndices==0) return 0;

    if (numNormalIndices!=0 && numNormalIndices!=numVertexIndices)
    {
        OSG_NOTICE<<"Incorrect number of normals, ignore them"<<std::endl;
        numNormalIndices = 0;
    }

    if (numTexCoordIndices!=0 && numTexCoordIndices!=numVertexIndices)
    {
        OSG_NOTICE<<"Incorrect number of normals, ignore them"<<std::endl;
        numTexCoordIndices = 0;
    }

    osg::Vec3Array* vertices = numVertexIndices ? new osg::Vec3Array : 0;
    osg::Vec3Array* normals = numNormalIndices ? new osg::Vec3Array : 0;
    osg::Vec2Array* texcoords = numTexCoordIndices ? new osg::Vec2Array : 0;
    osg::Vec4Array* colors = (!model.colors.empty()) ? new osg::Vec4Array : 0;

    if (vertices) vertices->reserve(numVertexIndices);
    if (normals) normals->reserve(numNormalIndices);
    if (texcoords) texcoords->reserve(numTexCoordIndices);
    if (colors) colors->reserve(numVertexIndices);


    osg::Geometry* geometry = new osg::Geometry;
    if (vertices) geometry->setVertexArray(vertices);
    if (normals)
    {
        geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX);
    }

    if (texcoords)
    {
        geometry->setTexCoordArray(0,texcoords);
    }

    if (colors)
    {
        geometry->setColorArray(colors);
        geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
    }

    if (numPointElements>0)
    {
        unsigned int startPos = vertices->size();
        unsigned int numPoints = 0;
        for(itr=elementList.begin();
            itr!=elementList.end();
            ++itr)
        {
            obj::Element& element = *(*itr);
            if (element.dataType==obj::Element::POINTS)
            {
                for(obj::Element::IndexList::iterator index_itr = element.vertexIndices.begin();
                    index_itr != element.vertexIndices.end();
                    ++index_itr)
                {
                    // if use color extension ( not standard but used by meshlab)
                    if (colors)
                    {
                        colors->push_back(model.colors[*index_itr]);
                    }
                    vertices->push_back(transformVertex(model.vertices[*index_itr],localOptions.rotate));
                    ++numPoints;
                }
                if (numNormalIndices)
                {
                    for(obj::Element::IndexList::iterator index_itr = element.normalIndices.begin();
                        index_itr != element.normalIndices.end();
                        ++index_itr)
                    {
                        normals->push_back(transformNormal(model.normals[*index_itr],localOptions.rotate));
                    }
                }
                if (numTexCoordIndices)
                {
                    for(obj::Element::IndexList::iterator index_itr = element.texCoordIndices.begin();
                        index_itr != element.texCoordIndices.end();
                        ++index_itr)
                    {
                        texcoords->push_back(model.texcoords[*index_itr]);
                    }
                }
            }
        }

        osg::DrawArrays* drawArrays = new osg::DrawArrays(GL_POINTS,startPos,numPoints);
        geometry->addPrimitiveSet(drawArrays);
    }

    if (numPolylineElements>0)
    {
        unsigned int startPos = vertices->size();
        osg::DrawArrayLengths* drawArrayLengths = new osg::DrawArrayLengths(GL_LINES,startPos);

        for(itr=elementList.begin();
            itr!=elementList.end();
            ++itr)
        {
            obj::Element& element = *(*itr);
            if (element.dataType==obj::Element::POLYLINE)
            {
                drawArrayLengths->push_back(element.vertexIndices.size());

                for(obj::Element::IndexList::iterator index_itr = element.vertexIndices.begin();
                    index_itr != element.vertexIndices.end();
                    ++index_itr)
                {
                    // if use color extension ( not standard but used by meshlab)
                    if (colors)
                    {
                        colors->push_back(model.colors[*index_itr]);
                    }

                    vertices->push_back(transformVertex(model.vertices[*index_itr],localOptions.rotate));
                }
                if (numNormalIndices)
                {
                    for(obj::Element::IndexList::iterator index_itr = element.normalIndices.begin();
                        index_itr != element.normalIndices.end();
                        ++index_itr)
                    {
                        normals->push_back(transformNormal(model.normals[*index_itr],localOptions.rotate));
                    }
                }
                if (numTexCoordIndices)
                {
                    for(obj::Element::IndexList::iterator index_itr = element.texCoordIndices.begin();
                        index_itr != element.texCoordIndices.end();
                        ++index_itr)
                    {
                        texcoords->push_back(model.texcoords[*index_itr]);
                    }
                }
            }
        }

        geometry->addPrimitiveSet(drawArrayLengths);

    }

    // #define USE_DRAWARRAYLENGTHS
    bool hasReversedFaces = false ;
    if (numPolygonElements>0)
    {
        unsigned int startPos = vertices->size();

        #ifdef USE_DRAWARRAYLENGTHS
            osg::DrawArrayLengths* drawArrayLengths = new osg::DrawArrayLengths(GL_POLYGON,startPos);
            geometry->addPrimitiveSet(drawArrayLengths);
        #endif

        for(itr=elementList.begin();
            itr!=elementList.end();
            ++itr)
        {
            obj::Element& element = *(*itr);
            if (element.dataType==obj::Element::POLYGON)
            {






                #ifdef USE_DRAWARRAYLENGTHS
                    drawArrayLengths->push_back(element.vertexIndices.size());
                #else
                    if (element.vertexIndices.size()>4)
                    {
                        osg::DrawArrays* drawArrays = new osg::DrawArrays(GL_POLYGON,startPos,element.vertexIndices.size());
                        startPos += element.vertexIndices.size();
                        geometry->addPrimitiveSet(drawArrays);
                    }
                    else
                    {
                        osg::DrawArrays* drawArrays = new osg::DrawArrays(GL_TRIANGLE_FAN,startPos,element.vertexIndices.size());
                        startPos += element.vertexIndices.size();
                        geometry->addPrimitiveSet(drawArrays);
                    }
                #endif


                if (model.needReverse(element) && !localOptions.noReverseFaces)
                {
                    hasReversedFaces = true;
                    // need to reverse so add to OSG arrays in same order as in OBJ, as OSG assume anticlockwise ordering.
                    for(obj::Element::IndexList::reverse_iterator index_itr = element.vertexIndices.rbegin();
                        index_itr != element.vertexIndices.rend();
                        ++index_itr)
                    {
                        // if use color extension ( not standard but used by meshlab)
                        if (colors)
                        {
                            colors->push_back(model.colors[*index_itr]);
                        }

                        vertices->push_back(transformVertex(model.vertices[*index_itr],localOptions.rotate));
                    }
                    if (numNormalIndices)
                    {
                        for(obj::Element::IndexList::reverse_iterator index_itr = element.normalIndices.rbegin();
                            index_itr != element.normalIndices.rend();
                            ++index_itr)
                        {
                            normals->push_back(transformNormal(model.normals[*index_itr],localOptions.rotate));
                        }
                    }


                    if (numTexCoordIndices)
                    {
                        for(obj::Element::IndexList::reverse_iterator index_itr = element.texCoordIndices.rbegin();
                            index_itr != element.texCoordIndices.rend();
                            ++index_itr)
                        {
                            texcoords->push_back(model.texcoords[*index_itr]);
                        }
                    }
                }
                else
                {
                    // no need to reverse so add to OSG arrays in same order as in OBJ.
                    for(obj::Element::IndexList::iterator index_itr = element.vertexIndices.begin();
                        index_itr != element.vertexIndices.end();
                        ++index_itr)
                    {
                        // if use color extension ( not standard but used by meshlab)
                        if (colors)
                        {
                            colors->push_back(model.colors[*index_itr]);
                        }

                        vertices->push_back(transformVertex(model.vertices[*index_itr],localOptions.rotate));
                    }
                    if (numNormalIndices)
                    {
                        for(obj::Element::IndexList::iterator index_itr = element.normalIndices.begin();
                            index_itr != element.normalIndices.end();
                            ++index_itr)
                        {
                            normals->push_back(transformNormal(model.normals[*index_itr],localOptions.rotate));
                        }
                    }
                    if (numTexCoordIndices)
                    {
                        for(obj::Element::IndexList::iterator index_itr = element.texCoordIndices.begin();
                            index_itr != element.texCoordIndices.end();
                            ++index_itr)
                        {
                            texcoords->push_back(model.texcoords[*index_itr]);
                        }
                    }
                }
            }
        }
    }

    if(hasReversedFaces)
    {
        OSG_WARN << "Warning: [ReaderWriterOBJ::convertElementListToGeometry] Some faces from geometry '" << geometry->getName() << "' were reversed by the plugin" << std::endl;
    }

    return geometry;
}

osg::Node* ReaderWriterOBJ::convertModelToSceneGraph(obj::Model& model, ObjOptionsStruct& localOptions, const Options* options) const
{

    if (model.elementStateMap.empty()) return 0;

    osg::Group* group = new osg::Group;

    // set up the materials
    MaterialToStateSetMap materialToStateSetMap;
    buildMaterialToStateSetMap(model, materialToStateSetMap, localOptions, options);

    // go through the groups of related elements and build geometry from them.
    for(obj::Model::ElementStateMap::iterator itr=model.elementStateMap.begin();
        itr!=model.elementStateMap.end();
        ++itr)
    {

        const obj::ElementState& es = itr->first;
        obj::Model::ElementList& el = itr->second;

        osg::Geometry* geometry = convertElementListToGeometry(model,el,localOptions);

        if (geometry)
        {
            MaterialToStateSetMap::const_iterator it = materialToStateSetMap.find(es.materialName);
            if (it == materialToStateSetMap.end())
            {
                OSG_WARN << "Obj unable to find material '" << es.materialName << "'" << std::endl;
            }

            osg::StateSet* stateset = materialToStateSetMap[es.materialName].get();
            geometry->setStateSet(stateset);

            // tesseleate any large polygons
            if (!localOptions.noTesselateLargePolygons)
            {
                osgUtil::Tessellator tessellator;
                tessellator.retessellatePolygons(*geometry);
            }

            // tri strip polygons to improve graphics peformance
            if (!localOptions.noTriStripPolygons)
            {
                osgUtil::TriStripVisitor tsv;
                tsv.stripify(*geometry);
            }

            // if no normals present add them.
            if (localOptions.generateFacetNormals==false && (!geometry->getNormalArray() || geometry->getNormalArray()->getNumElements()==0))
            {
                osgUtil::SmoothingVisitor sv;
                sv.smooth(*geometry);
            }


            osg::Geode* geode = new osg::Geode;
            geode->addDrawable(geometry);

            if (es.objectName.empty())
            {
                geode->setName(es.groupName);
            }
            else if (es.groupName.empty())
            {
                geode->setName(es.objectName);
            }
            else
            {
                geode->setName(es.groupName + std::string(":") + es.objectName);
            }

            group->addChild(geode);

        }
    }

    return group;
}

ReaderWriterOBJ::ObjOptionsStruct ReaderWriterOBJ::parseOptions(const osgDB::ReaderWriter::Options* options) const
{
    ObjOptionsStruct localOptions;

    if (options!=NULL)
    {
        std::istringstream iss(options->getOptionString());
        std::string opt;
        while (iss >> opt)
        {
            // split opt into pre= and post=
            std::string pre_equals;
            std::string post_equals;

            size_t found = opt.find("=");
            if(found!=std::string::npos)
            {
                pre_equals = opt.substr(0,found);
                post_equals = opt.substr(found+1);
            }
            else
            {
                pre_equals = opt;
            }

            if (pre_equals == "noRotation")
            {
                localOptions.rotate = false;
            }
            else if (pre_equals == "noTesselateLargePolygons")
            {
                localOptions.noTesselateLargePolygons = true;
            }
            else if (pre_equals == "noTriStripPolygons")
            {
                localOptions.noTriStripPolygons = true;
            }
            else if (pre_equals == "generateFacetNormals")
            {
                localOptions.generateFacetNormals = true;
            }
            else if (pre_equals == "noReverseFaces")
            {
                localOptions.noReverseFaces = true;
            }
            else if (pre_equals == "useBuildInShader")
            {
                localOptions.useBuildInShader = true;
            }
            else if (post_equals.length()>0)
            {
                obj::Material::Map::TextureMapType type = obj::Material::Map::UNKNOWN;
                // Now we check to see if we have anything forcing a texture allocation
                if        (pre_equals == "DIFFUSE")            type = obj::Material::Map::DIFFUSE;
                else if (pre_equals == "AMBIENT")            type = obj::Material::Map::AMBIENT;
                else if (pre_equals == "SPECULAR")            type = obj::Material::Map::SPECULAR;
                else if (pre_equals == "SPECULAR_EXPONENT") type = obj::Material::Map::SPECULAR_EXPONENT;
                else if (pre_equals == "OPACITY")            type = obj::Material::Map::OPACITY;
                else if (pre_equals == "BUMP")                type = obj::Material::Map::BUMP;
                else if (pre_equals == "DISPLACEMENT")        type = obj::Material::Map::DISPLACEMENT;
                else if (pre_equals == "REFLECTION")        type = obj::Material::Map::REFLECTION;

                if (type!=obj::Material::Map::UNKNOWN)
                {
                    int unit = atoi(post_equals.c_str());    // (probably should use istringstream rather than atoi)
                    localOptions.textureUnitAllocation.push_back(std::make_pair(unit,(obj::Material::Map::TextureMapType) type));
                    OSG_NOTICE<<"Obj Found map in options, ["<<pre_equals<<"]="<<unit<<std::endl;
                }
            }
        }
    }
    return localOptions;
}


// read file and convert to OSG.
osgDB::ReaderWriter::ReadResult ReaderWriterOBJ::readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const
{
    std::string ext = osgDB::getLowerCaseFileExtension(file);
    if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;

    std::string fileName = osgDB::findDataFile( file, options );
    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;


    osgDB::ifstream fin(fileName.c_str());
    if (fin)
    {

        // code for setting up the database path so that internally referenced file are searched for on relative paths.
        osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
        local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));

        obj::Model model;
        model.setDatabasePath(osgDB::getFilePath(fileName.c_str()));
        model.readOBJ(fin, local_opt.get());

        ObjOptionsStruct localOptions = parseOptions(options);
        osg::Node* node = convertModelToSceneGraph(model, localOptions, local_opt.get());

        if (localOptions.useBuildInShader)
            node->addEventCallback(new EventHandler(localOptions.shaderGenUnitsMap));
        return node;
    }

    return ReadResult::FILE_NOT_HANDLED;
}

osgDB::ReaderWriter::ReadResult ReaderWriterOBJ::readNode(std::istream& fin, const Options* options) const
{
    if (fin)
    {
        fin.imbue(std::locale::classic());

        obj::Model model;
        model.readOBJ(fin, options);

        ObjOptionsStruct localOptions = parseOptions(options);
        osg::Node* node= convertModelToSceneGraph(model, localOptions, options);

        if (localOptions.useBuildInShader)
            node->addEventCallback(new EventHandler(localOptions.shaderGenUnitsMap));
        return node;
    }

    return ReadResult::FILE_NOT_HANDLED;
}
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/

/**
 * \brief    Shader generator framework.
 * \author   Maciej Krol
 */

#include <osgUtil/ShaderGen>
#include <osg/Geode>
#include <osg/Geometry> // for ShaderGenVisitor::update
#include <osg/Fog>
#include <sstream>

using namespace osgUtil;

#undef OSG_GL2_FEATURES
#define OSG_GL2_FEATURES 0

namespace osgUtil
{

/// State extended by mode/attribute accessors
class StateEx : public osg::State
{
public:
    StateEx() : State() {}

    osg::StateAttribute::GLModeValue getMode(osg::StateAttribute::GLMode mode,
        osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const
    {
        return getMode(_modeMap, mode, def);
    }

    osg::StateAttribute *getAttribute(osg::StateAttribute::Type type, unsigned int member = 0) const
    {
        return getAttribute(_attributeMap, type, member);
    }

    osg::StateAttribute::GLModeValue getTextureMode(unsigned int unit,
        osg::StateAttribute::GLMode mode,
        osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const
    {
        return unit < _textureModeMapList.size() ? getMode(_textureModeMapList[unit], mode, def) : def;
    }

    osg::StateAttribute *getTextureAttribute(unsigned int unit, osg::StateAttribute::Type type) const
    {
        return unit < _textureAttributeMapList.size() ? getAttribute(_textureAttributeMapList[unit], type, 0) : 0;
    }

    osg::Uniform *getUniform(const std::string& name) const
    {
        UniformMap::const_iterator it = _uniformMap.find(name);
        return it != _uniformMap.end() ?
            const_cast<osg::Uniform *>(it->second.uniformVec.back().first) : 0;
    }

protected:

    osg::StateAttribute::GLModeValue getMode(const ModeMap &modeMap,
        osg::StateAttribute::GLMode mode,
        osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const
    {
        ModeMap::const_iterator it = modeMap.find(mode);
        return (it != modeMap.end() && it->second.valueVec.size()) ? it->second.valueVec.back() : def;
    }

    osg::StateAttribute *getAttribute(const AttributeMap &attributeMap,
        osg::StateAttribute::Type type, unsigned int member = 0) const
    {
        AttributeMap::const_iterator it = attributeMap.find(std::make_pair(type, member));
        return (it != attributeMap.end() && it->second.attributeVec.size()) ?
            const_cast<osg::StateAttribute*>(it->second.attributeVec.back().first) : 0;
    }
};

}

void ShaderGenCache::setStateSet(int stateMask, osg::StateSet *stateSet)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
    _stateSetMap[stateMask] = stateSet;
}

osg::StateSet *ShaderGenCache::getStateSet(int stateMask) const
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
    StateSetMap::const_iterator it = _stateSetMap.find(stateMask);
    return (it != _stateSetMap.end()) ? it->second.get() : 0;
}

osg::StateSet *ShaderGenCache::getOrCreateStateSet(int stateMask)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
    StateSetMap::iterator it = _stateSetMap.find(stateMask);
    if (it == _stateSetMap.end())
    {
        osg::StateSet *stateSet = createStateSet(stateMask);
        _stateSetMap.insert(it, StateSetMap::value_type(stateMask, stateSet));
        return stateSet;
    }
    return it->second.get();
}

ShaderGenCache::TextureUnitsMap &ShaderGenCache::getTextureUnits()
{
    return _textureUnits;
}

osg::StateSet *ShaderGenCache::createStateSet(int stateMask) const
{
    osg::StateSet *stateSet = new osg::StateSet;
    osg::Program *program = new osg::Program;
    stateSet->setAttribute(program);

    std::ostringstream vert_frag;

    vert_frag << "// ShaderGen shader\n";
    vert_frag << "#ifdef GL_ES\n"
                 "    precision highp float;\n"
                 "#endif\n";

    // write varyings
    if ((stateMask & LIGHTING) && !(stateMask & NORMAL_MAP))
    {
        vert_frag << "varying vec3 normalDir;\n";
    }

    if (stateMask & (LIGHTING | NORMAL_MAP))
    {
        vert_frag << "varying vec3 lightDir;\n";
    }

    if (stateMask & (LIGHTING | NORMAL_MAP | FOG))
    {
        vert_frag << "varying vec3 viewDir;\n";
    }

    vert_frag << "varying vec4 vertexColor;\n";

    std::ostringstream vert;
    std::ostringstream frag;

    // copy varying to vertex ad fragment shader
    vert << vert_frag.str();
    frag << vert_frag.str();

    // write uniforms and attributes
    TextureUnitsMap::const_iterator it = _textureUnits.begin();
    for (; it != _textureUnits.end(); it++)
    {
        if (it->second == DIFFUSE_MAP && (stateMask & DIFFUSE_MAP))
        {
            osg::Uniform *diffuseMap = new osg::Uniform("diffuseMap", it->first);
            stateSet->addUniform(diffuseMap);
            frag << "uniform sampler2D diffuseMap;\n";
        }

        if (it->second == SPECULAR_MAP && (stateMask & LIGHTING) && (stateMask & SPECULAR_MAP))
        {
            osg::Uniform *specularMap = new osg::Uniform("specularMap", it->first);
            stateSet->addUniform(specularMap);
            frag << "uniform sampler2D specularMap;\n";
        }

        if (it->second == NORMAL_MAP && (stateMask & NORMAL_MAP))
        {
            osg::Uniform *normalMap = new osg::Uniform("normalMap", it->first);
            stateSet->addUniform(normalMap);
            frag << "uniform sampler2D normalMap;\n";
            program->addBindAttribLocation("tangent", 6);
            vert << "attribute vec3 tangent;\n";
        }
    }

    vert << "\n"\
        "void main()\n"\
        "{\n"\
        "  gl_Position = ftransform();\n";

    if ((stateMask & DIFFUSE_MAP) || (stateMask & NORMAL_MAP) || (stateMask & SPECULAR_MAP))
    {
        // TODO support different texture sizes by using the related uniform index
        vert << "  gl_TexCoord[0] = gl_MultiTexCoord0;\n";
    }

    if (stateMask & NORMAL_MAP)
    {
        vert <<
            "  vec3 n = gl_NormalMatrix * gl_Normal;\n"\
            "  vec3 t = gl_NormalMatrix * tangent;\n"\
            "  vec3 b = cross(n, t);\n"\
            "  vec3 dir = -vec3(gl_ModelViewMatrix * gl_Vertex);\n"\
            "  viewDir.x = dot(dir, t);\n"\
            "  viewDir.y = dot(dir, b);\n"\
            "  viewDir.z = dot(dir, n);\n"\
            "  vec4 lpos = gl_LightSource[0].position;\n"\
            "  if (lpos.w == 0.0)\n"\
            "    dir = lpos.xyz;\n"\
            "  else\n"\
            "    dir += lpos.xyz;\n"\
            "  lightDir.x = dot(dir, t);\n"\
            "  lightDir.y = dot(dir, b);\n"\
            "  lightDir.z = dot(dir, n);\n";
    }
#if !OSG_GLES2_FEATURES && !OSG_GLES3_FEATURES && !OSG_GL2_FEATURES
    else if (stateMask & LIGHTING)
    {
        vert <<
            "  normalDir = gl_NormalMatrix * gl_Normal;\n"\
            "  vec3 dir = -vec3(gl_ModelViewMatrix * gl_Vertex);\n"\
            "  viewDir = dir;\n"\
            "  vec4 lpos = gl_LightSource[0].position;\n"\
            "  if (lpos.w == 0.0)\n"\
            "    lightDir = lpos.xyz;\n"\
            "  else\n"\
            "    lightDir = lpos.xyz + dir;\n"\
            "  vertexColor = gl_Color;\n";
    }
    else if (stateMask & FOG)
    {
        vert <<
            "  viewDir = -vec3(gl_ModelViewMatrix * gl_Vertex);\n"\
            "  vertexColor = gl_Color;\n";
    }
#endif
    else
    {
        vert << "  vertexColor = gl_Color;\n";
    }

    vert << "}\n";

    frag << "\n"\
        "void main()\n"\
        "{\n";

    if (stateMask & DIFFUSE_MAP)
    {
        frag << "  vec4 base = texture2D(diffuseMap, gl_TexCoord[0].st);\n";
    }
    else
    {
        frag << "  vec4 base = vertexColor;\n";
    }

    if (stateMask & LIGHTING)
    {
        if (stateMask & SPECULAR_MAP)
        {
            frag << "  vec4 spec = texture2D(specularMap, gl_TexCoord[0].st);\n";
        }
        else
        {
            frag << "  vec4 spec = vec4(1.0);\n";
        }
    }

    if (stateMask & NORMAL_MAP)
    {
        frag << "  vec3 normalDir = texture2D(normalMap, gl_TexCoord[0].st).xyz*2.0-1.0;\n";
    }

#if !OSG_GLES2_FEATURES && !OSG_GLES3_FEATURES && !OSG_GL2_FEATURES
    if (stateMask & LIGHTING)
    {
        if ((stateMask & NORMAL_MAP) || (stateMask & DIFFUSE_MAP) || (stateMask & SPECULAR_MAP))
        {
            frag <<
                "  vec3 nd = normalize(normalDir);\n"\
                "  vec3 ld = normalize(lightDir);\n"\
                "  vec3 vd = normalize(viewDir);\n"\
                "  vec4 Iamb = gl_FrontLightProduct[0].ambient;\n"\
                "  float diff = max(dot(ld, nd), 0.0);\n"\
                "  vec4 Idiff = gl_FrontLightProduct[0].diffuse * diff;\n"\
                "  vec4 Ispec; \n"\
                "  if (diff > 0.0)\n"\
                "  {\n"\
                "    vec3 halfDir = normalize(ld+vd);\n"\
                "    Ispec = gl_FrontLightProduct[0].specular * \n"\
                "      pow(max(dot(halfDir, nd), 0.0), gl_FrontMaterial.shininess);\n"\
                "  }\n"
                "  vec4 color = gl_FrontLightModelProduct.sceneColor + Iamb + Idiff * base + Ispec * spec;\n";
        }
    }
    else
#endif
    {
        frag << "  vec4 color = base;\n";
    }

#if !OSG_GLES2_FEATURES && !OSG_GLES3_FEATURES && !OSG_GL2_FEATURES
    if (stateMask & FOG)
    {
        frag <<
            "  float d2 = dot(viewDir, viewDir);//gl_FragCoord.z/gl_FragCoord.w;\n"\
            "  float f = exp2(-1.442695*gl_Fog.density*gl_Fog.density*d2);\n"\
            "  color.rgb = mix(gl_Fog.color.rgb, color.rgb, clamp(f, 0.0, 1.0));\n";
    }
#endif

    frag << "  gl_FragColor = color;\n";
    frag << "}\n";

    std::string vertstr = vert.str();
    std::string fragstr = frag.str();

    OSG_DEBUG << "ShaderGenCache Vertex shader:\n" << vertstr << std::endl;
    OSG_DEBUG << "ShaderGenCache Fragment shader:\n" << fragstr << std::endl;

    program->addShader(new osg::Shader(osg::Shader::VERTEX, vertstr));
    program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragstr));

    return stateSet;
}

ShaderGenVisitor::ShaderGenVisitor() :
    NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
    _stateCache(new ShaderGenCache),
    _state(new StateEx)
{
}

ShaderGenVisitor::ShaderGenVisitor(ShaderGenCache *stateCache) :
    NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
    _stateCache(stateCache),
    _state(new StateEx)
{
}

void ShaderGenVisitor::setRootStateSet(osg::StateSet *stateSet)
{
    if (_rootStateSet.valid())
        _state->removeStateSet(0);
    _rootStateSet = stateSet;
    if (_rootStateSet.valid())
        _state->pushStateSet(_rootStateSet.get());
}

void ShaderGenVisitor::reset()
{
    _state->popAllStateSets();
    if (_rootStateSet.valid())
        _state->pushStateSet(_rootStateSet.get());
}

void ShaderGenVisitor::apply(osg::Node &node)
{
    osg::StateSet *stateSet = node.getStateSet();

    if (stateSet)
        _state->pushStateSet(stateSet);

    traverse(node);

    if (stateSet)
        _state->popStateSet();
}

void ShaderGenVisitor::apply(osg::Geode &geode)
{
    osg::StateSet *stateSet = geode.getStateSet();
    if (stateSet)
        _state->pushStateSet(stateSet);

    for (unsigned int i=0; i<geode.getNumDrawables(); ++i)
    {
        osg::Drawable *drawable = geode.getDrawable(i);
        osg::StateSet *ss = drawable->getStateSet();
        if (ss)
            _state->pushStateSet(ss);

        update(drawable);

        if (ss)
            _state->popStateSet();
    }

    if (stateSet)
        _state->popStateSet();
}

void ShaderGenVisitor::update(osg::Drawable *drawable)
{
    // update only geometry due to compatibility issues with user defined drawables
    osg::Geometry *geometry = drawable->asGeometry();
#if 0
    if (!geometry)
        return;
#endif

    StateEx *state = static_cast<StateEx *>(_state.get());
    // skip nodes without state sets
    if (state->getStateSetStackSize() == (_rootStateSet.valid() ? 1u : 0u))
        return;

    int stateMask = 0;
    //if (state->getMode(GL_BLEND) & osg::StateAttribute::ON)
    //    stateMask |= ShaderGen::BLEND;
    if (state->getMode(GL_LIGHTING) & osg::StateAttribute::ON)
        stateMask |= ShaderGenCache::LIGHTING;
    if (state->getMode(GL_FOG) & osg::StateAttribute::ON)
        stateMask |= ShaderGenCache::FOG;

    ShaderGenCache::TextureUnitsMap &_textureUnits = _stateCache->getTextureUnits();
    if (_textureUnits.empty())
    {
        if (state->getTextureAttribute(0, osg::StateAttribute::TEXTURE))
            stateMask |= ShaderGenCache::DIFFUSE_MAP;
        if (state->getTextureAttribute(1, osg::StateAttribute::TEXTURE) && geometry!=0 &&
                geometry->getVertexAttribArray(6)) //tangent
            stateMask |= ShaderGenCache::NORMAL_MAP;
    }
    else
    {
        ShaderGenCache::TextureUnitsMap::const_iterator it = _textureUnits.begin();
        for (;it != _textureUnits.end(); it++)
        {
            if (state->getTextureAttribute(it->first, osg::StateAttribute::TEXTURE))
                stateMask |= it->second;
        }
    }

    // Get program and uniforms for accumulated state.
    osg::StateSet *progss = _stateCache->getOrCreateStateSet(stateMask);
    // Set program and uniforms to the last state set.
    osg::StateSet *ss = const_cast<osg::StateSet *>(state->getStateSetStack().back());
    ss->setAttribute(progss->getAttribute(osg::StateAttribute::PROGRAM));
    ss->setUniformList(progss->getUniformList());

    // remove any modes that won't be appropriate when using shaders
    if ((stateMask&ShaderGenCache::LIGHTING)!=0)
    {
        ss->removeMode(GL_LIGHTING);
        ss->removeMode(GL_LIGHT0);
    }
    if ((stateMask&ShaderGenCache::FOG)!=0)
    {
        ss->removeMode(GL_FOG);
    }
    if (_textureUnits.empty())
    {
        if ((stateMask&ShaderGenCache::DIFFUSE_MAP)!=0) ss->removeTextureMode(0, GL_TEXTURE_2D);
        if ((stateMask&ShaderGenCache::NORMAL_MAP)!=0) ss->removeTextureMode(1, GL_TEXTURE_2D);
    }
    else
    {
        ShaderGenCache::TextureUnitsMap::const_iterator it = _textureUnits.begin();
        for (;it != _textureUnits.end(); it++)
        {
            if ((stateMask & it->second)!=0)
                ss->removeTextureMode(it->first, GL_TEXTURE_2D);
        }
    }
}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org

Reply via email to