]> git.mxchange.org Git - simgear.git/commitdiff
Merge branch 'timoore/effects'
authorTim Moore <timoore33@gmail.com>
Sun, 20 Dec 2009 15:07:00 +0000 (16:07 +0100)
committerTim Moore <timoore33@gmail.com>
Sun, 20 Dec 2009 15:07:00 +0000 (16:07 +0100)
Conflicts:
simgear/scene/model/model.cxx
simgear/scene/sky/newcloud.cxx

54 files changed:
acinclude.m4
configure.ac
projects/VC7.1/SimGear.vcproj
projects/VC90/SimGear.vcproj
simgear/props/AtomicChangeListener.cxx [new file with mode: 0644]
simgear/props/AtomicChangeListener.hxx [new file with mode: 0644]
simgear/props/ExtendedPropertyAdapter.hxx [new file with mode: 0644]
simgear/props/Makefile.am
simgear/props/props.cxx
simgear/props/props.hxx
simgear/props/props_io.cxx
simgear/scene/material/Effect.cxx
simgear/scene/material/Effect.hxx
simgear/scene/material/EffectBuilder.cxx
simgear/scene/material/EffectBuilder.hxx
simgear/scene/material/EffectCullVisitor.cxx
simgear/scene/material/EffectGeode.cxx
simgear/scene/material/EffectGeode.hxx
simgear/scene/material/Technique.cxx
simgear/scene/material/TextureBuilder.cxx
simgear/scene/material/TextureBuilder.hxx
simgear/scene/material/makeEffect.cxx
simgear/scene/model/ModelRegistry.cxx
simgear/scene/model/ModelRegistry.hxx
simgear/scene/model/SGMaterialAnimation.cxx
simgear/scene/model/SGPagedLOD.cxx
simgear/scene/model/SGReaderWriterXML.cxx
simgear/scene/model/animation.cxx
simgear/scene/model/animation.hxx
simgear/scene/model/model.cxx
simgear/scene/model/model.hxx
simgear/scene/model/modellib.cxx
simgear/scene/sky/CloudShaderGeometry.cxx
simgear/scene/sky/CloudShaderGeometry.hxx
simgear/scene/sky/cloudfield.cxx
simgear/scene/sky/cloudfield.hxx
simgear/scene/sky/newcloud.cxx
simgear/scene/sky/newcloud.hxx
simgear/scene/tgdb/SGReaderWriterBTG.cxx
simgear/scene/tgdb/TreeBin.cxx
simgear/scene/tgdb/userdata.cxx
simgear/scene/tgdb/userdata.hxx
simgear/scene/util/CopyOp.cxx [new file with mode: 0644]
simgear/scene/util/CopyOp.hxx [new file with mode: 0644]
simgear/scene/util/Makefile.am
simgear/scene/util/SGSceneUserData.cxx
simgear/scene/util/SGSceneUserData.hxx
simgear/scene/util/SplicingVisitor.cxx [new file with mode: 0644]
simgear/scene/util/SplicingVisitor.hxx [new file with mode: 0644]
simgear/scene/util/StateAttributeFactory.cxx
simgear/scene/util/StateAttributeFactory.hxx
simgear/scene/util/UpdateOnceCallback.cxx [new file with mode: 0644]
simgear/scene/util/UpdateOnceCallback.hxx [new file with mode: 0644]
simgear/structure/OSGUtils.hxx

index af9004cb45e72d901eb8d0b3c164bdccfba2c408..9e46179f3b14e05255a5642ee5335c77a0ce2a92 100644 (file)
@@ -671,7 +671,7 @@ if test "x$want_boost" = "xyes"; then
                if test "$_version" = "0" ; then
                        AC_MSG_ERROR([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option.  If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation.]])
                else
-                       AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).])
+                       AC_MSG_ERROR([Your boost libraries seems to old (version $_version).])
                fi
        else
                AC_SUBST(BOOST_CPPFLAGS)
index e82399f41fa3ba34730881dfd9924f8830ed0cff..ca19e4cf542dd54494d4c3c44ef553192ccc6d14 100644 (file)
@@ -37,7 +37,7 @@ AC_PROG_CXX
 AC_PROG_RANLIB
 AC_PROG_INSTALL
 AC_PROG_LN_S
-AX_BOOST_BASE([1.34.0])
+AX_BOOST_BASE([1.37.0])
 
 if test "x$BOOST_CPPFLAGS" != "x-I/usr/include" ; then
    CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
index 36dc8e09b5cb45147d1e6fbeb4f15e058559520a..783ac4d05ace81dfda35c470e7f252238c4dba94 100755 (executable)
                <Filter
                        Name="Lib_sgprops"
                        Filter="">
+                       <File
+                               RelativePath="..\..\simgear\props\AtomicChangeListener.cxx">
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\props\AtomicChangeListener.hxx">
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\props\ExtendedPropertyAdapter.hxx">
+                       </File>
                        <File
                                RelativePath="..\..\simgear\props\condition.cxx">
                        </File>
                <Filter
                        Name="Lib_sgutil"
                        Filter="">
+                       <File
+                               RelativePath="..\..\simgear\scene\util\CopyOp.cxx">
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\CopyOp.hxx">
+                       </File>
                        <File
                                RelativePath="..\..\simgear\scene\util\NodeAndDrawableVisitor.cxx">
                        </File>
                        <File
                                RelativePath="..\..\simgear\scene\util\SGUpdateVisitor.hxx">
                        </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\SplicingVisitor.cxx">
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\SplicingVisitor.hxx">
+                       </File>
                        <File
                                RelativePath="..\..\simgear\scene\util\StateAttributeFactory.cxx">
                        </File>
                        <File
                                RelativePath="..\..\simgear\scene\util\StateAttributeFactory.hxx">
                        </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\UpdateOnceCallback.cxx">
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\UpdateOnceCallback.hxx">
+                       </File>
                </Filter>
                <Filter
                        Name="Lib_sgbvh"
index bf0336bbf40de32050203f912ea491fe125477a6..14805d89dfaea63584effba876622508d67e2825 100644 (file)
                <Filter
                        Name="Lib_sgprops"
                        >
+                       <File
+                               RelativePath="..\..\simgear\props\AtomicChangeListener.cxx"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\props\AtomicChangeListener.hxx"
+                               >
+                       </File>
                        <File
                                RelativePath="..\..\simgear\props\condition.cxx"
                                >
                                RelativePath="..\..\simgear\props\condition.hxx"
                                >
                        </File>
+                       <File
+                               RelativePath="..\..\simgear\props\ExtendedPropertyAdapter.hxx"
+                               >
+                       </File>
+
                        <File
                                RelativePath="..\..\simgear\props\props.cxx"
                                >
                <Filter
                        Name="Lib_sgutil"
                        >
+                       <File
+                               RelativePath="..\..\simgear\scene\util\CopyOp.cxx"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\CopyOp.hxx"
+                               >
+                       </File>
                        <File
                                RelativePath="..\..\simgear\scene\util\NodeAndDrawableVisitor.cxx"
                                >
                                RelativePath="..\..\simgear\scene\util\SGUpdateVisitor.hxx"
                                >
                        </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\SplicingVisitor.cxx"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\SplicingVisitor.hxx"
+                               >
+                       </File>
                        <File
                                RelativePath="..\..\simgear\scene\util\StateAttributeFactory.cxx"
                                >
                                RelativePath="..\..\simgear\scene\util\StateAttributeFactory.hxx"
                                >
                        </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\UpdateOnceCallback.cxx"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\simgear\scene\util\UpdateOnceCallback.hxx"
+                               >
+                       </File>
                </Filter>
                <Filter
                        Name="Lib_sgbvh"
diff --git a/simgear/props/AtomicChangeListener.cxx b/simgear/props/AtomicChangeListener.cxx
new file mode 100644 (file)
index 0000000..e0617d7
--- /dev/null
@@ -0,0 +1,71 @@
+#include "AtomicChangeListener.hxx"
+
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+#include <boost/bind.hpp>
+
+#include <simgear/structure/Singleton.hxx>
+
+namespace simgear
+{
+using namespace std;
+
+MultiChangeListener::MultiChangeListener()
+{
+}
+
+void MultiChangeListener::valueChanged(SGPropertyNode* node)
+{
+    valueChangedImplementation();
+}
+
+void MultiChangeListener::valueChangedImplementation()
+{
+}
+
+AtomicChangeListener::AtomicChangeListener(std::vector<SGPropertyNode*>& nodes)
+    :  _dirty(false), _valid(true)
+{
+    listenToProperties(nodes.begin(), nodes.end());
+}
+
+void AtomicChangeListener::unregister_property(SGPropertyNode* node)
+{
+    _valid = false;
+    // not necessary, but good hygine
+    vector<SGPropertyNode*>::iterator itr
+        = find(_watched.begin(), _watched.end(), node);
+    if (itr != _watched.end())
+        *itr = 0;
+    MultiChangeListener::unregister_property(node);
+}
+
+void AtomicChangeListener::fireChangeListeners()
+{
+    vector<SGSharedPtr<AtomicChangeListener> >& listeners
+        = ListenerListSingleton::instance()->listeners;
+    for (vector<SGSharedPtr<AtomicChangeListener> >::iterator itr = listeners.begin(),
+             end = listeners.end();
+         itr != end;
+         ++itr) {
+        (*itr)->valuesChanged();
+        (*itr)->_dirty = false;
+    }
+    listeners.clear();
+}
+
+void AtomicChangeListener::valueChangedImplementation()
+{
+    if (!_dirty) {
+        _dirty = true;
+        if (_valid)
+            ListenerListSingleton::instance()->listeners.push_back(this);
+    }
+}
+
+void AtomicChangeListener::valuesChanged()
+{
+}
+}
diff --git a/simgear/props/AtomicChangeListener.hxx b/simgear/props/AtomicChangeListener.hxx
new file mode 100644 (file)
index 0000000..a287738
--- /dev/null
@@ -0,0 +1,104 @@
+#ifndef SIMGEAR_ATOMICCHANGELISTENER_HXX
+#define SIMGEAR_ATOMICCHANGELISTENER_HXX 1
+
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+#include <boost/bind.hpp>
+
+#include <simgear/structure/Singleton.hxx>
+
+#include "props.hxx"
+#include "ExtendedPropertyAdapter.hxx"
+
+namespace simgear
+{
+// Performs an action when one of several nodes changes
+class MultiChangeListener : public SGPropertyChangeListener
+{
+public:
+    MultiChangeListener();
+    template<typename Pitr>
+    void listenToProperties(Pitr propsBegin, Pitr propsEnd)
+    {
+        for (Pitr itr = propsBegin, end = propsEnd; itr != end; ++itr)
+            (*itr)->addChangeListener(this);
+    }
+private:
+    void valueChanged(SGPropertyNode* node);
+    virtual void valueChangedImplementation();
+
+};
+
+class AtomicChangeListener : public MultiChangeListener,
+                             public virtual SGReferenced
+{
+public:
+    AtomicChangeListener(std::vector<SGPropertyNode*>& nodes);
+    /**
+     * Lookup / create child nodes from their relative names.
+     */
+    template<typename Itr>
+    AtomicChangeListener(SGPropertyNode* parent, Itr childNamesBegin,
+                         Itr childNamesEnd)
+        : _dirty(false), _valid(true)
+    {
+        using namespace std;
+        for (Itr itr = childNamesBegin, end = childNamesEnd;
+             itr != end;
+             ++itr)
+            _watched.push_back(makeNode(parent, *itr));
+        listenToProperties(_watched.begin(), _watched.end());
+    }
+    bool isDirty() { return _dirty; }
+    bool isValid() { return _valid; }
+    void unregister_property(SGPropertyNode* node);
+    static void fireChangeListeners();
+private:
+    virtual void valueChangedImplementation();
+    virtual void valuesChanged();
+    bool _dirty;
+    bool _valid;
+    struct ListenerListSingleton : public Singleton<ListenerListSingleton>
+    {
+        std::vector<SGSharedPtr<AtomicChangeListener> > listeners;
+    };
+protected:
+    std::vector<SGPropertyNode*> _watched;
+};
+
+template<typename T, typename Func>
+class ExtendedPropListener : public AtomicChangeListener
+{
+public:
+    ExtendedPropListener(std::vector<SGPropertyNode*>& nodes, const Func& func,
+                         bool initial = false)
+        : AtomicChangeListener(nodes), _func(func)
+    {
+        if (initial)
+            valuesChanged();
+
+    }
+    template<typename Itr>
+    ExtendedPropListener(SGPropertyNode* parent, Itr childNamesBegin,
+                         Itr childNamesEnd, const Func& func,
+                         bool initial = false)
+        : AtomicChangeListener(parent, childNamesBegin, childNamesEnd),
+          _func(func)
+    {
+        if (initial)
+            valuesChanged();
+    }
+    virtual void valuesChanged()
+    {
+        ExtendedPropertyAdapter<T, std::vector<SGPropertyNode*> > adaptor(_watched);
+        T val = adaptor();
+        _func(val);
+    }
+private:
+    Func _func;
+};
+
+}
+#endif
diff --git a/simgear/props/ExtendedPropertyAdapter.hxx b/simgear/props/ExtendedPropertyAdapter.hxx
new file mode 100644 (file)
index 0000000..064f407
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef SIMGEAR_EXTENDEDPROPERTYADAPTER_HXX
+#define SIMGEAR_EXTENDEDPROPERTYADAPTER_HXX 1
+
+#include <algorithm>
+
+#include <boost/bind.hpp>
+
+#include <simgear/math/SGMath.hxx>
+#include <simgear/structure/exception.hxx>
+
+#include "props.hxx"
+
+namespace simgear
+{
+
+namespace props
+{
+// This should be in simgear/math/SGVec.hxx and friends
+
+template<typename T> struct NumComponents;
+
+template<> struct NumComponents<SGVec3d>
+{
+    enum { num_components = 3 };
+};
+
+template<> struct NumComponents<SGVec4d>
+{
+    enum { num_components = 4 };
+};
+
+}
+
+template<typename T, typename NodeContainer>
+class ExtendedPropertyAdapter
+{
+public:
+    enum { num_components = props::NumComponents<T>::num_components };
+    ExtendedPropertyAdapter(const NodeContainer& elements)
+        : _elements(elements)
+    {
+    }
+    T operator()() const
+    {
+        T result;
+        if (_elements.size() < num_components)
+            throw sg_exception();
+        for (int i = 0; i < num_components; ++i)
+            result[i] = _elements[i]->getValue<double>();
+        return result;
+    }
+    void set(const T& val)
+    {
+        if (_elements.size() < num_components)
+            throw sg_exception();
+        for (int i = 0; i < num_components; ++i)
+            _elements[i]->setValue(val[i]);
+    }
+private:
+    const NodeContainer& _elements;
+};
+
+template<typename InIterator, typename OutIterator>
+inline void makeChildList(SGPropertyNode* prop, InIterator inBegin,
+                          InIterator inEnd, OutIterator outBegin)
+{
+    std::transform(inBegin, inEnd, outBegin,
+                   boost::bind(static_cast<SGPropertyNode* (SGPropertyNode::*)(const char*, int, bool)>(&SGPropertyNode::getChild), prop, _1, 0, true));
+}
+
+}
+#endif
index 8e726f15a96a822ad73b25c2740923fdd90ab82e..acf5201711ec4dfc6eda70fd44fa4af07335467d 100644 (file)
@@ -5,12 +5,15 @@ lib_LIBRARIES = libsgprops.a
 include_HEADERS = \
        condition.hxx \
        props.hxx \
-       props_io.hxx
+       props_io.hxx \
+       AtomicChangeListener.hxx \
+       ExtendedPropertyAdapter.hxx
 
 libsgprops_a_SOURCES = \
        condition.cxx \
        props.cxx \
-       props_io.cxx
+       props_io.cxx \
+       AtomicChangeListener.cxx
 
 noinst_PROGRAMS = props_test
 
index 0bc56dcd6fd786241e9b9b57468e95b23e3338af..c662eb7f95f808ab54af4f33b4453c08c015063e 100644 (file)
 
 #include <sstream>
 #include <iomanip>
+#include <iterator>
 #include <stdio.h>
 #include <string.h>
 
+#include <boost/algorithm/string/find_iterator.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/bind.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/range.hpp>
+
 #include <simgear/math/SGMath.hxx>
 
 #if PROPS_STANDALONE
@@ -78,49 +86,36 @@ public:
 // Local path normalization code.
 ////////////////////////////////////////////////////////////////////////
 
-/**
- * A component in a path.
- */
-struct PathComponent
-{
-  string name;
-  int index;
-};
-
 /**
  * Parse the name for a path component.
  *
  * Name: [_a-zA-Z][-._a-zA-Z0-9]*
  */
-static inline const string
-parse_name (const string &path, int &i)
+
+template<typename Range>
+inline Range
+parse_name (const Range &path)
 {
-  string name = "";
-  int max = path.size();
+  typename Range::iterator i = path.begin();
+  typename Range::iterator max = path.end();
 
-  if (path[i] == '.') {
+  if (*i == '.') {
     i++;
-    if (i < max && path[i] == '.') {
+    if (i != path.end() && *i == '.') {
       i++;
-      name = "..";
-    } else {
-      name = ".";
     }
-    if (i < max && path[i] != '/')
-      throw string("illegal character after " + name);
-  }
-
-  else if (isalpha(path[i]) || path[i] == '_') {
-    name += path[i];
+    if (i != max && *i != '/')
+      throw string("illegal character after . or ..");
+  } else if (isalpha(*i) || *i == '_') {
     i++;
 
                                // The rules inside a name are a little
                                // less restrictive.
-    while (i < max) {
-      if (isalpha(path[i]) || isdigit(path[i]) || path[i] == '_' ||
-         path[i] == '-' || path[i] == '.') {
-       name += path[i];
-      } else if (path[i] == '[' || path[i] == '/') {
+    while (i != max) {
+      if (isalpha(*i) || isdigit(*i) || *i == '_' ||
+         *i == '-' || *i == '.') {
+       // name += path[i];
+      } else if (*i == '[' || *i == '/') {
        break;
       } else {
        throw string("name may contain only ._- and alphanumeric characters");
@@ -130,90 +125,24 @@ parse_name (const string &path, int &i)
   }
 
   else {
-    if (name.size() == 0)
+    if (path.begin() == i)
       throw string("name must begin with alpha or '_'");
   }
-
-  return name;
-}
-
-
-/**
- * Parse the optional integer index for a path component.
- *
- * Index: "[" [0-9]+ "]"
- */
-static inline int
-parse_index (const string &path, int &i)
-{
-  int index = 0;
-
-  if (path[i] != '[')
-    return 0;
-  else
-    i++;
-
-  for (int max = path.size(); i < max; i++) {
-    if (isdigit(path[i])) {
-      index = (index * 10) + (path[i] - '0');
-    } else if (path[i] == ']') {
-      i++;
-      return index;
-    } else {
-      break;
-    }
-  }
-
-  throw string("unterminated index (looking for ']')");
-}
-
-
-/**
- * Parse a single path component.
- *
- * Component: Name Index?
- */
-static inline PathComponent
-parse_component (const string &path, int &i)
-{
-  PathComponent component;
-  component.name = parse_name(path, i);
-  if (component.name[0] != '.')
-    component.index = parse_index(path, i);
-  else
-    component.index = -1;
-  return component;
+  return Range(path.begin(), i);
 }
 
-
-/**
- * Parse a path into its components.
- */
-static void
-parse_path (const string &path, vector<PathComponent> &components)
+// Validate the name of a single node
+inline bool validateName(const string& name)
 {
-  int pos = 0;
-  int max = path.size();
-
-                               // Check for initial '/'
-  if (path[pos] == '/') {
-    PathComponent root;
-    root.name = "";
-    root.index = -1;
-    components.push_back(root);
-    pos++;
-    while (pos < max && path[pos] == '/')
-      pos++;
-  }
-
-  while (pos < max) {
-    components.push_back(parse_component(path, pos));
-    while (pos < max && path[pos] == '/')
-      pos++;
-  }
+  using namespace boost;
+  if (name.empty())
+    return false;
+  if (!isalpha(name[0]) && name[0] != '_')
+    return false;
+  return all(make_iterator_range(name.begin(), name.end()),
+             is_alnum() || is_any_of("_-."));
 }
 
-
 \f
 ////////////////////////////////////////////////////////////////////////
 // Other static utility functions.
@@ -242,16 +171,18 @@ compare_strings (const char * s1, const char * s2)
 /**
  * Locate a child node by name and index.
  */
+template<typename Itr>
 static int
-find_child (const char * name, int index, const PropertyList& nodes)
+find_child (Itr begin, Itr end, int index, const PropertyList& nodes)
 {
   int nNodes = nodes.size();
+  boost::iterator_range<Itr> name(begin, end);
   for (int i = 0; i < nNodes; i++) {
     SGPropertyNode * node = nodes[i];
 
     // searching for a mathing index is a lot less time consuming than
     // comparing two strings so do that first.
-    if (node->getIndex() == index && compare_strings(node->getName(), name))
+    if (node->getIndex() == index && boost::equals(node->getName(), name))
       return i;
   }
   return -1;
@@ -277,55 +208,137 @@ find_last_child (const char * name, const PropertyList& nodes)
   return index;
 }
 
-
-/**
- * Locate another node, given a relative path.
- */
-static SGPropertyNode *
-find_node (SGPropertyNode * current,
-          const vector<PathComponent> &components,
-          int position,
-          bool create)
+template<typename Itr>
+inline SGPropertyNode*
+SGPropertyNode::getExistingChild (Itr begin, Itr end, int index, bool create)
 {
-                               // Run off the end of the list
-  if (current == 0) {
-    return 0;
-  }
-
-                               // Success! This is the one we want.
-  else if (position >= (int)components.size()) {
-    return (current->getAttribute(SGPropertyNode::REMOVED) ? 0 : current);
+  int pos = find_child(begin, end, index, _children);
+  if (pos >= 0) {
+    return _children[pos];
+  } else if (create) {
+    SGPropertyNode_ptr node;
+    pos = find_child(begin, end, index, _removedChildren);
+    if (pos >= 0) {
+      PropertyList::iterator it = _removedChildren.begin();
+      it += pos;
+      node = _removedChildren[pos];
+      _removedChildren.erase(it);
+      node->setAttribute(REMOVED, false);
+      _children.push_back(node);
+      fireChildAdded(node);
+      return node;      
+    }
   }
+  return 0;
+}
+    
+template<typename Itr>
+SGPropertyNode *
+SGPropertyNode::getChildImpl (Itr begin, Itr end, int index, bool create)
+{
+    SGPropertyNode* node = getExistingChild(begin, end, index, create);
 
-                               // Empty component means root.
-  else if (components[position].name == "") {
-    return find_node(current->getRootNode(), components, position + 1, create);
-  }
+    if (node) {
+      return node;
+    } else if (create) {
+      node = new SGPropertyNode(begin, end, index, this);
+      _children.push_back(node);
+      fireChildAdded(node);
+      return node;
+    } else {
+      return 0;
+    }
+}
 
-                               // . means current directory
-  else if (components[position].name == ".") {
-    return find_node(current, components, position + 1, create);
+template<typename SplitItr>
+SGPropertyNode*
+find_node_aux(SGPropertyNode * current, SplitItr& itr, bool create,
+              int last_index)
+{
+  typedef typename SplitItr::value_type Range;
+  // Run off the end of the list
+  if (current == 0) {
+    return 0;
   }
 
-                               // .. means parent directory
-  else if (components[position].name == "..") {
+  // Success! This is the one we want.
+  if (itr.eof())
+    return current;
+  Range token = *itr;
+  // Empty name at this point is empty, not root.
+  if (token.empty())
+    return find_node_aux(current, ++itr, create, last_index);
+  Range name = parse_name(token);
+  if (equals(name, "."))
+    return find_node_aux(current, ++itr, create, last_index);
+  if (equals(name, "..")) {
     SGPropertyNode * parent = current->getParent();
     if (parent == 0)
       throw string("attempt to move past root with '..'");
-    else
-      return find_node(parent, components, position + 1, create);
-  }
-
-                               // Otherwise, a child name
-  else {
-    SGPropertyNode * child =
-      current->getChild(components[position].name.c_str(),
-                       components[position].index,
-                       create);
-    return find_node(child, components, position + 1, create);
+    return find_node_aux(parent, ++itr, create, last_index);
+  }
+  int index = -1;
+  if (last_index >= 0) {
+    // If we are at the last token and last_index is valid, use
+    // last_index as the index value
+    bool lastTok = true;
+    while (!(++itr).eof()) {
+      if (!itr->empty()) {
+        lastTok = false;
+        break;
+      }
+    }
+    if (lastTok)
+      index = last_index;
+  } else {
+    ++itr;
+  }
+
+  if (index < 0) {
+    index = 0;
+    if (name.end() != token.end()) {
+      if (*name.end() == '[') {
+        typename Range::iterator i = name.end() + 1, end = token.end();
+        for (;i != end; ++i) {
+          if (isdigit(*i)) {
+            index = (index * 10) + (*i - '0');
+          } else {
+            break;
+          }
+        }
+        if (i == token.end() || *i != ']')
+          throw string("unterminated index (looking for ']')");
+      } else {
+        throw string("illegal characters in token: ")
+          + string(name.begin(), name.end());
+      }
+    }
   }
+  return find_node_aux(current->getChildImpl(name.begin(), name.end(),
+                                             index, create), itr, create,
+                       last_index);
 }
 
+// Internal function for parsing property paths. last_index provides
+// and index value for the last node name token, if supplied.
+template<typename Range>
+SGPropertyNode*
+find_node (SGPropertyNode * current,
+           const Range& path,
+          bool create,
+           int last_index = -1)
+{
+  using namespace boost;
+  typedef split_iterator<typename range_result_iterator<Range>::type>
+    PathSplitIterator;
+  
+  PathSplitIterator itr
+    = make_split_iterator(path, first_finder("/", is_equal()));
+  if (*path.begin() == '/')
+    return find_node_aux(current->getRootNode(), itr, create, last_index);
+   else
+     return find_node_aux(current, itr, create, last_index);
+}
 
 \f
 ////////////////////////////////////////////////////////////////////////
@@ -695,10 +708,12 @@ SGPropertyNode::SGPropertyNode (const SGPropertyNode &node)
 /**
  * Convenience constructor.
  */
-SGPropertyNode::SGPropertyNode (const char * name,
+template<typename Itr>
+SGPropertyNode::SGPropertyNode (Itr begin, Itr end,
                                int index,
                                SGPropertyNode * parent)
   : _index(index),
+    _name(begin, end),
     _parent(parent),
     _path_cache(0),
     _type(props::NONE),
@@ -706,14 +721,29 @@ SGPropertyNode::SGPropertyNode (const char * name,
     _attr(READ|WRITE),
     _listeners(0)
 {
-  int i = 0;
   _local_val.string_val = 0;
   _value.val = 0;
-  _name = parse_name(name, i);
-  if (i != int(strlen(name)) || name[0] == '.')
-    throw string("plain name expected instead of '") + name + '\'';
+  if (!validateName(_name))
+    throw string("plain name expected instead of '") + _name + '\'';
 }
 
+SGPropertyNode::SGPropertyNode (const string& name,
+                               int index,
+                               SGPropertyNode * parent)
+  : _index(index),
+    _name(name),
+    _parent(parent),
+    _path_cache(0),
+    _type(props::NONE),
+    _tied(false),
+    _attr(READ|WRITE),
+    _listeners(0)
+{
+  _local_val.string_val = 0;
+  _value.val = 0;
+  if (!validateName(name))
+    throw string("plain name expected instead of '") + _name + '\'';
+}
 
 /**
  * Destructor.
@@ -801,7 +831,7 @@ SGPropertyNode::addChild (const char * name)
   int pos = find_last_child(name, _children)+1;
 
   SGPropertyNode_ptr node;
-  node = new SGPropertyNode(name, pos, this);
+  node = new SGPropertyNode(name, name + strlen(name), pos, this);
   _children.push_back(node);
   fireChildAdded(node);
   return node;
@@ -837,40 +867,37 @@ SGPropertyNode::getChild (int position) const
 /**
  * Get a non-const child by name and index, creating if necessary.
  */
+
 SGPropertyNode *
 SGPropertyNode::getChild (const char * name, int index, bool create)
 {
-  int pos = find_child(name, index, _children);
-  if (pos >= 0) {
-    return _children[pos];
-  } else if (create) {
-    SGPropertyNode_ptr node;
-    pos = find_child(name, index, _removedChildren);
-    if (pos >= 0) {
-      PropertyList::iterator it = _removedChildren.begin();
-      it += pos;
-      node = _removedChildren[pos];
-      _removedChildren.erase(it);
-      node->setAttribute(REMOVED, false);
-    } else {
+  return getChildImpl(name, name + strlen(name), index, create);
+}
+
+SGPropertyNode *
+SGPropertyNode::getChild (const string& name, int index, bool create)
+{
+  SGPropertyNode* node = getExistingChild(name.begin(), name.end(), index,
+                                          create);
+  if (node) {
+      return node;
+    } else if (create) {
       node = new SGPropertyNode(name, index, this);
+      _children.push_back(node);
+      fireChildAdded(node);
+      return node;
+    } else {
+      return 0;
     }
-    _children.push_back(node);
-    fireChildAdded(node);
-    return node;
-  } else {
-    return 0;
-  }
 }
 
-
 /**
  * Get a const child by name and index.
  */
 const SGPropertyNode *
 SGPropertyNode::getChild (const char * name, int index) const
 {
-  int pos = find_child(name, index, _children);
+  int pos = find_child(name, name + strlen(name), index, _children);
   if (pos >= 0)
     return _children[pos];
   else
@@ -945,7 +972,7 @@ SGPropertyNode_ptr
 SGPropertyNode::removeChild (const char * name, int index, bool keep)
 {
   SGPropertyNode_ptr ret;
-  int pos = find_child(name, index, _children);
+  int pos = find_child(name, name + strlen(name), index, _children);
   if (pos >= 0)
     ret = removeChild(pos, keep);
   return ret;
@@ -1710,14 +1737,16 @@ SGPropertyNode::getRootNode () const
 SGPropertyNode *
 SGPropertyNode::getNode (const char * relative_path, bool create)
 {
+  using namespace boost;
   if (_path_cache == 0)
     _path_cache = new hash_table;
 
   SGPropertyNode * result = _path_cache->get(relative_path);
   if (result == 0) {
-    vector<PathComponent> components;
-    parse_path(relative_path, components);
-    result = find_node(this, components, 0, create);
+    result = find_node(this,
+                       make_iterator_range(relative_path, relative_path
+                                           + strlen(relative_path)),
+                       create);
     if (result != 0)
       _path_cache->put(relative_path, result);
   }
@@ -1728,11 +1757,10 @@ SGPropertyNode::getNode (const char * relative_path, bool create)
 SGPropertyNode *
 SGPropertyNode::getNode (const char * relative_path, int index, bool create)
 {
-  vector<PathComponent> components;
-  parse_path(relative_path, components);
-  if (components.size() > 0)
-    components.back().index = index;
-  return find_node(this, components, 0, create);
+  using namespace boost;
+  return find_node(this, make_iterator_range(relative_path, relative_path
+                                             + strlen(relative_path)),
+                   create, index);
 }
 
 const SGPropertyNode *
@@ -2354,6 +2382,146 @@ std::istream& readFrom<SGVec4d>(std::istream& stream, SGVec4d& result)
     }
     return stream;
 }
+
+namespace
+{
+bool compareNodeValue(const SGPropertyNode& lhs, const SGPropertyNode& rhs)
+{
+    props::Type ltype = lhs.getType();
+    props::Type rtype = rhs.getType();
+    if (ltype != rtype)
+        return false;
+    switch (ltype) {
+    case props::NONE:
+        return true;
+    case props::ALIAS:
+        return false;           // XXX Should we look in aliases?
+    case props::BOOL:
+        return lhs.getValue<bool>() == rhs.getValue<bool>();
+    case props::INT:
+        return lhs.getValue<int>() == rhs.getValue<int>();
+    case props::LONG:
+        return lhs.getValue<long>() == rhs.getValue<long>();
+    case props::FLOAT:
+        return lhs.getValue<float>() == rhs.getValue<float>();
+    case props::DOUBLE:
+        return lhs.getValue<double>() == rhs.getValue<double>();
+    case props::STRING:
+    case props::UNSPECIFIED:
+        return !strcmp(lhs.getStringValue(), rhs.getStringValue());
+    case props::VEC3D:
+        return lhs.getValue<SGVec3d>() == rhs.getValue<SGVec3d>();
+    case props::VEC4D:
+        return lhs.getValue<SGVec4d>() == rhs.getValue<SGVec4d>();
+    default:
+        return false;
+    }
+}
+}
+}
+
+bool SGPropertyNode::compare(const SGPropertyNode& lhs,
+                             const SGPropertyNode& rhs)
+{
+    if (&lhs == &rhs)
+        return true;
+    int lhsChildren = lhs.nChildren();
+    int rhsChildren = rhs.nChildren();
+    if (lhsChildren != rhsChildren)
+        return false;
+    if (lhsChildren == 0)
+        return compareNodeValue(lhs, rhs);
+    for (size_t i = 0; i < lhs._children.size(); ++i) {
+        const SGPropertyNode* lchild = lhs._children[i];
+        const SGPropertyNode* rchild = rhs._children[i];
+        // I'm guessing that the nodes will usually be in the same
+        // order.
+        if (lchild->getIndex() != rchild->getIndex()
+            || lchild->getNameString() != rchild->getNameString()) {
+            rchild = 0;
+            for (PropertyList::const_iterator itr = rhs._children.begin(),
+                     end = rhs._children.end();
+                 itr != end;
+                ++itr)
+                if (lchild->getIndex() == (*itr)->getIndex()
+                    && lchild->getNameString() == (*itr)->getNameString()) {
+                    rchild = *itr;
+                    break;
+                }
+            if (!rchild)
+                return false;
+        }
+        if (!compare(*lchild, *rchild))
+            return false;
+    }
+    return true;
+}
+
+struct PropertyPlaceLess {
+    typedef bool result_type;
+    bool operator()(SGPropertyNode_ptr lhs, SGPropertyNode_ptr rhs) const
+    {
+        int comp = lhs->getNameString().compare(rhs->getNameString());
+        if (comp == 0)
+            return lhs->getIndex() < rhs->getIndex();
+        else
+            return comp < 0;
+    }
+};
+
+size_t hash_value(const SGPropertyNode& node)
+{
+    using namespace boost;
+
+    if (node.nChildren() == 0) {
+        switch (node.getType()) {
+        case props::NONE:
+            return 0;
+
+        case props::BOOL:
+            return hash_value(node.getValue<bool>());
+        case props::INT:
+            return hash_value(node.getValue<int>());
+        case props::LONG:
+            return hash_value(node.getValue<long>());
+        case props::FLOAT:
+            return hash_value(node.getValue<float>());
+        case props::DOUBLE:
+            return hash_value(node.getValue<double>());
+        case props::STRING:
+        case props::UNSPECIFIED:
+        {
+            const char *val = node.getStringValue();
+            return hash_range(val, val + strlen(val));
+        }
+        case props::VEC3D:
+        {
+            const SGVec3d val = node.getValue<SGVec3d>();
+            return hash_range(&val[0], &val[3]);
+        }
+        case props::VEC4D:
+        {
+            const SGVec4d val = node.getValue<SGVec4d>();
+            return hash_range(&val[0], &val[4]);
+        }
+        case props::ALIAS:      // XXX Should we look in aliases?
+        default:
+            return 0;
+        }
+    } else {
+        size_t seed = 0;
+        PropertyList children(node._children.begin(), node._children.end());
+        sort(children.begin(), children.end(), PropertyPlaceLess());
+        for (PropertyList::const_iterator itr  = children.begin(),
+                 end = children.end();
+             itr != end;
+             ++itr) {
+            hash_combine(seed, (*itr)->_name);
+            hash_combine(seed, (*itr)->_index);
+            hash_combine(seed, hash_value(**itr));
+        }
+        return seed;
+    }
 }
 
 // end of props.cxx
index dc9871343b176c8f8d309b68c0b13aa1a837cf42..984bb1b5717b37657b03ff9db88714910a39ca4c 100644 (file)
@@ -161,6 +161,7 @@ DEFINTERNALPROP(long, LONG);
 DEFINTERNALPROP(float, FLOAT);
 DEFINTERNALPROP(double, DOUBLE);
 DEFINTERNALPROP(const char *, STRING);
+DEFINTERNALPROP(const char[], STRING);
 #undef DEFINTERNALPROP
 
 template<>
@@ -804,6 +805,11 @@ public:
    */
   const char * getName () const { return _name.c_str(); }
 
+  /**
+   * Get the node's simple name as a string.
+   */
+  const std::string& getNameString () const { return _name; }
+
   /**
    * Get the node's pretty display name, with subscript when needed.
    */
@@ -875,17 +881,10 @@ public:
   /**
    * Get a child node by name and index.
    */
-  SGPropertyNode * getChild (const char * name, int index = 0,
-                            bool create = false);
-
-  /**
-   * Get a child node by name and index.
-   */
+  SGPropertyNode * getChild (const char* name, int index = 0,
+                             bool create = false);
   SGPropertyNode * getChild (const std::string& name, int index = 0,
-                            bool create = false)
-  { return getChild(name.c_str(), index, create); }
-
-
+                             bool create = false);
   /**
    * Get a const child node by name and index.
    */
@@ -1223,6 +1222,12 @@ public:
   bool setValue(const T& val,
                 typename boost::disable_if_c<simgear::props::PropertyTraits<T>::Internal>
                 ::type* dummy = 0);
+
+  template<int N>
+  bool setValue(const char (&val)[N])
+  {
+    return setValue(&val[0]);
+  }
   
   /**
    * Print the value of the property to a stream.
@@ -1594,6 +1599,16 @@ public:
    */
   void clearValue ();
 
+  /**
+   * Compare two property trees. The property trees are equal if: 1)
+   * They have no children, and have the same type and the values are
+   * equal, or 2) have the same number of children, and the
+   * corresponding children in each tree are equal. "corresponding"
+   * means have the same name and index.
+   *
+   * Attributes, removed children, and aliases aren't considered.
+   */
+  static bool compare (const SGPropertyNode& lhs, const SGPropertyNode& rhs);
 
 protected:
 
@@ -1604,12 +1619,13 @@ protected:
   /**
    * Protected constructor for making new nodes on demand.
    */
-  SGPropertyNode (const char * name, int index, SGPropertyNode * parent);
-
+  SGPropertyNode (const std::string& name, int index, SGPropertyNode * parent);
+  template<typename Itr>
+  SGPropertyNode (Itr begin, Itr end, int index, SGPropertyNode * parent);
 
 private:
 
-                               // Get the raw value
+  // Get the raw value
   bool get_bool () const;
   int get_int () const;
   long get_long () const;
@@ -1617,7 +1633,7 @@ private:
   double get_double () const;
   const char * get_string () const;
 
-                               // Set the raw value
+  // Set the raw value
   bool set_bool (bool value);
   bool set_int (int value);
   bool set_long (long value);
@@ -1666,7 +1682,7 @@ private:
   bool _tied;
   int _attr;
 
-                               // The right kind of pointer...
+  // The right kind of pointer...
   union {
     SGPropertyNode * alias;
     SGRaw* val;
@@ -1685,8 +1701,8 @@ private:
 
 
   /**
-    * Register/unregister node that links to this node in its path cache.
-    */
+   * Register/unregister node that links to this node in its path cache.
+   */
   void add_linked_node (hash_table * node) { _linkedNodes.push_back(node); }
   bool remove_linked_node (hash_table * node);
 
@@ -1743,7 +1759,18 @@ private:
     unsigned int _data_length;
     bucket ** _data;
   };
-
+  // Pass name as a pair of iterators
+  template<typename Itr>
+  SGPropertyNode * getChildImpl (Itr begin, Itr end, int index = 0, bool create = false);
+  // very internal method
+  template<typename Itr>
+  SGPropertyNode* getExistingChild (Itr begin, Itr end, int index, bool create);
+  // very internal path parsing function
+  template<typename SplitItr>
+  friend SGPropertyNode* find_node_aux(SGPropertyNode * current, SplitItr& itr,
+                                       bool create, int last_index);
+  // For boost
+  friend size_t hash_value(const SGPropertyNode& node);
 };
 
 // Convenience functions for use in templates
@@ -1912,13 +1939,69 @@ inline bool SGPropertyNode::setValue(const T& val,
 }
 
 /**
- * Utility function for creation of a child property node
+ * Utility function for creation of a child property node.
  */
 inline SGPropertyNode* makeChild(SGPropertyNode* parent, const char* name,
                                  int index = 0)
 {
     return parent->getChild(name, index, true);
 }
+
+/**
+ * Utility function for creation of a child property node using a
+ * relative path.
+ */
+namespace simgear
+{
+template<typename StringType>
+inline SGPropertyNode* makeNode(SGPropertyNode* parent, const StringType& name)
+{
+    return parent->getNode(name, true);
+}
+}
+
+// For boost::hash
+size_t hash_value(const SGPropertyNode& node);
+
+// Helper comparison and hash functions for common cases
+
+namespace simgear
+{
+namespace props
+{
+struct Compare
+{
+    bool operator()(const SGPropertyNode* lhs, const SGPropertyNode* rhs) const
+    {
+        return SGPropertyNode::compare(*lhs, *rhs);
+    }
+    bool operator()(SGPropertyNode_ptr lhs, const SGPropertyNode* rhs) const
+    {
+        return SGPropertyNode::compare(*lhs, *rhs);
+    }
+    bool operator()(const SGPropertyNode* lhs, SGPropertyNode_ptr rhs) const
+    {
+        return SGPropertyNode::compare(*lhs, *rhs);
+    }
+    bool operator()(SGPropertyNode_ptr lhs, SGPropertyNode_ptr rhs) const
+    {
+        return SGPropertyNode::compare(*lhs, *rhs);
+    }
+};
+
+struct Hash
+{
+    size_t operator()(const SGPropertyNode* node) const
+    {
+        return hash_value(*node);
+    }
+    size_t operator()(SGPropertyNode_ptr node) const
+    {
+        return hash_value(*node);
+    }
+};
+}
+}
 #endif // __PROPS_HXX
 
 // end of props.hxx
index 232e3c2c79f4b2c4c4da20d00fa2c54e1c00d5f7..48244c7b11ee7cea442681e0944638cd6828cbd0 100644 (file)
@@ -187,16 +187,17 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
                                // Get the index.
     attval = atts.getValue("n");
     int index = 0;
+    string strName(name);
     if (attval != 0) {
       index = atoi(attval);
-      st.counters[name] = SG_MAX2(st.counters[name], index+1);
+      st.counters[strName] = SG_MAX2(st.counters[strName], index+1);
     } else {
-      index = st.counters[name];
-      st.counters[name]++;
+      index = st.counters[strName];
+      st.counters[strName]++;
     }
 
                                // Got the index, so grab the node.
-    SGPropertyNode * node = st.node->getChild(name, index, true);
+    SGPropertyNode * node = st.node->getChild(strName, index, true);
     if (!node->getAttribute(SGPropertyNode::WRITE)) {
       SG_LOG(SG_INPUT, SG_ALERT, "Not overwriting write-protected property "
           << node->getPath(true));
@@ -601,6 +602,13 @@ writeProperties (const string &file, const SGPropertyNode * start_node,
   }
 }
 
+// Another variation, useful when called from gdb
+void
+writeProperties (const char* file, const SGPropertyNode * start_node)
+{
+    writeProperties(string(file), start_node, true);
+}
+
 
 \f
 ////////////////////////////////////////////////////////////////////////
@@ -678,7 +686,7 @@ copyProperties (const SGPropertyNode *in, SGPropertyNode *out)
   int nChildren = in->nChildren();
   for (int i = 0; i < nChildren; i++) {
     const SGPropertyNode * in_child = in->getChild(i);
-    SGPropertyNode * out_child = out->getChild(in_child->getName(),
+    SGPropertyNode * out_child = out->getChild(in_child->getNameString(),
                                               in_child->getIndex(),
                                               true);
     if (!copyProperties(in_child, out_child))
index b36702adaaebdc95e0d299d5c720bd2c25c5f19b..940b1f18b15873bd1722f6460287235aaa21683a 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "Effect.hxx"
 #include "EffectBuilder.hxx"
+#include "EffectGeode.hxx"
 #include "Technique.hxx"
 #include "Pass.hxx"
 #include "TextureBuilder.hxx"
 #include <iterator>
 #include <map>
 #include <utility>
+#include <boost/tr1/unordered_map.hpp>
 
 #include <boost/bind.hpp>
 #include <boost/foreach.hpp>
-#include <boost/lexical_cast.hpp>
+#include <boost/functional/hash.hpp>
 #include <boost/tuple/tuple.hpp>
 #include <boost/tuple/tuple_comparison.hpp>
 
+#include <osg/AlphaFunc>
+#include <osg/BlendFunc>
 #include <osg/CullFace>
+#include <osg/Depth>
 #include <osg/Drawable>
 #include <osg/Material>
+#include <osg/Math>
 #include <osg/PolygonMode>
 #include <osg/Program>
 #include <osg/Referenced>
@@ -59,6 +65,7 @@
 #include <osgDB/ReadFile>
 #include <osgDB/Registry>
 
+#include <simgear/scene/tgdb/userdata.hxx>
 #include <simgear/scene/util/SGSceneFeatures.hxx>
 #include <simgear/scene/util/StateAttributeFactory.hxx>
 #include <simgear/structure/OSGUtils.hxx>
@@ -72,17 +79,23 @@ using namespace std;
 using namespace osg;
 using namespace osgUtil;
 
+using namespace effect;
+
 Effect::Effect()
+    : _cache(0), _isRealized(false)
 {
 }
 
 Effect::Effect(const Effect& rhs, const CopyOp& copyop)
-    : root(rhs.root), parametersProp(rhs.parametersProp)
+    : root(rhs.root), parametersProp(rhs.parametersProp), _cache(0),
+      _isRealized(rhs._isRealized)
 {
-    using namespace boost;
-    transform(rhs.techniques.begin(), rhs.techniques.end(),
-              back_inserter(techniques),
-              bind(simgear::clone_ref<Technique>, _1, copyop));
+    typedef vector<ref_ptr<Technique> > TechniqueList;
+    for (TechniqueList::const_iterator itr = rhs.techniques.begin(),
+             end = rhs.techniques.end();
+         itr != end;
+         ++itr)
+        techniques.push_back(static_cast<Technique*>(copyop(itr->get())));
 }
 
 // Assume that the last technique is always valid.
@@ -125,29 +138,9 @@ void Effect::releaseGLObjects(osg::State* state) const
 
 Effect::~Effect()
 {
+    delete _cache;
 }
 
-class PassAttributeBuilder : public Referenced
-{
-public:
-    virtual void buildAttribute(Effect* effect, Pass* pass,
-                                const SGPropertyNode* prop,
-                                const osgDB::ReaderWriter::Options* options)
-    = 0;
-};
-
-typedef map<const string, ref_ptr<PassAttributeBuilder> > PassAttrMap;
-PassAttrMap passAttrMap;
-
-template<typename T>
-struct InstallAttributeBuilder
-{
-    InstallAttributeBuilder(const string& name)
-    {
-        passAttrMap.insert(make_pair(name, new T));
-    }
-};
-
 void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
                const osgDB::ReaderWriter::Options* options)
 {
@@ -155,9 +148,10 @@ void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
     tniq->passes.push_back(pass);
     for (int i = 0; i < prop->nChildren(); ++i) {
         const SGPropertyNode* attrProp = prop->getChild(i);
-        PassAttrMap::iterator itr = passAttrMap.find(attrProp->getName());
-        if (itr != passAttrMap.end())
-            itr->second->buildAttribute(effect, pass, attrProp, options);
+        PassAttributeBuilder* builder
+            = PassAttributeBuilder::find(attrProp->getNameString());
+        if (builder)
+            builder->buildAttribute(effect, pass, attrProp, options);
         else
             SG_LOG(SG_INPUT, SG_ALERT,
                    "skipping unknown pass attribute " << attrProp->getName());
@@ -237,8 +231,10 @@ struct CullFaceBuilder : PassAttributeBuilder
                         const osgDB::ReaderWriter::Options* options)
     {
         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
-        if (!realProp)
+        if (!realProp) {
+            pass->setMode(GL_CULL_FACE, StateAttribute::OFF);
             return;
+        }
         StateAttributeFactory *attrFact = StateAttributeFactory::instance();
         string propVal = realProp->getStringValue();
         if (propVal == "front")
@@ -247,6 +243,8 @@ struct CullFaceBuilder : PassAttributeBuilder
             pass->setAttributeAndModes(attrFact->getCullFaceBack());
         else if (propVal == "front-back")
             pass->setAttributeAndModes(new CullFace(CullFace::FRONT_AND_BACK));
+        else if (propVal == "off")
+            pass->setMode(GL_CULL_FACE, StateAttribute::OFF);
         else
             SG_LOG(SG_INPUT, SG_ALERT,
                    "invalid cull face property " << propVal);            
@@ -255,6 +253,15 @@ struct CullFaceBuilder : PassAttributeBuilder
 
 InstallAttributeBuilder<CullFaceBuilder> installCullFace("cull-face");
 
+EffectNameValue<StateSet::RenderingHint> renderingHintInit[] =
+{
+    { "default", StateSet::DEFAULT_BIN },
+    { "opaque", StateSet::OPAQUE_BIN },
+    { "transparent", StateSet::TRANSPARENT_BIN }
+};
+
+EffectPropertyMap<StateSet::RenderingHint> renderingHints(renderingHintInit);
+
 struct HintBuilder : public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
@@ -263,11 +270,9 @@ struct HintBuilder : public PassAttributeBuilder
         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
         if (!realProp)
             return;
-        string propVal = realProp->getStringValue();
-        if (propVal == "opaque")
-            pass->setRenderingHint(StateSet::OPAQUE_BIN);
-        else if (propVal == "transparent")
-            pass->setRenderingHint(StateSet::TRANSPARENT_BIN);
+        StateSet::RenderingHint renderingHint = StateSet::DEFAULT_BIN;
+        findAttr(renderingHints, realProp, renderingHint);
+        pass->setRenderingHint(renderingHint);
     }    
 };
 
@@ -278,6 +283,8 @@ struct RenderBinBuilder : public PassAttributeBuilder
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
                         const osgDB::ReaderWriter::Options* options)
     {
+        if (!isAttributeActive(effect, prop))
+            return;
         const SGPropertyNode* binProp = prop->getChild("bin-number");
         binProp = getEffectPropertyNode(effect, binProp);
         const SGPropertyNode* nameProp = prop->getChild("bin-name");
@@ -304,7 +311,7 @@ struct MaterialBuilder : public PassAttributeBuilder
                         const osgDB::ReaderWriter::Options* options);
 };
 
-EffectNameValue<Material::ColorMode> colorModes[] =
+EffectNameValue<Material::ColorMode> colorModeInit[] =
 {
     { "ambient", Material::AMBIENT },
     { "ambient-and-diffuse", Material::AMBIENT_AND_DIFFUSE },
@@ -313,11 +320,14 @@ EffectNameValue<Material::ColorMode> colorModes[] =
     { "specular", Material::SPECULAR },
     { "off", Material::OFF }
 };
+EffectPropertyMap<Material::ColorMode> colorModes(colorModeInit);
 
 void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass,
                                      const SGPropertyNode* prop,
                                      const osgDB::ReaderWriter::Options* options)
 {
+    if (!isAttributeActive(effect, prop))
+        return;
     Material* mat = new Material;
     const SGPropertyNode* color = 0;
     if ((color = getEffectPropertyChild(effect, prop, "ambient")))
@@ -361,125 +371,221 @@ void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass,
 
 InstallAttributeBuilder<MaterialBuilder> installMaterial("material");
 
+EffectNameValue<BlendFunc::BlendFuncMode> blendFuncModesInit[] =
+{
+    {"dst-alpha", BlendFunc::DST_ALPHA},
+    {"dst-color", BlendFunc::DST_COLOR},
+    {"one", BlendFunc::ONE},
+    {"one-minus-dst-alpha", BlendFunc::ONE_MINUS_DST_ALPHA},
+    {"one-minus-dst-color", BlendFunc::ONE_MINUS_DST_COLOR},
+    {"one-minus-src-alpha", BlendFunc::ONE_MINUS_SRC_ALPHA},
+    {"one-minus-src-color", BlendFunc::ONE_MINUS_SRC_COLOR},
+    {"src-alpha", BlendFunc::SRC_ALPHA},
+    {"src-alpha-saturate", BlendFunc::SRC_ALPHA_SATURATE},
+    {"src-color", BlendFunc::SRC_COLOR},
+    {"constant-color", BlendFunc::CONSTANT_COLOR},
+    {"one-minus-constant-color", BlendFunc::ONE_MINUS_CONSTANT_COLOR},
+    {"constant-alpha", BlendFunc::CONSTANT_ALPHA},
+    {"one-minus-constant-alpha", BlendFunc::ONE_MINUS_CONSTANT_ALPHA},
+    {"zero", BlendFunc::ZERO}
+};
+EffectPropertyMap<BlendFunc::BlendFuncMode> blendFuncModes(blendFuncModesInit);
+
 struct BlendBuilder : public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
                         const osgDB::ReaderWriter::Options* options)
     {
+        if (!isAttributeActive(effect, prop))
+            return;
+        // XXX Compatibility with early <blend> syntax; should go away
+        // before a release
         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
         if (!realProp)
             return;
-        pass->setMode(GL_BLEND, (realProp->getBoolValue()
-                                 ? StateAttribute::ON
-                                 : StateAttribute::OFF));
+        if (realProp->nChildren() == 0) {
+            pass->setMode(GL_BLEND, (realProp->getBoolValue()
+                                     ? StateAttribute::ON
+                                     : StateAttribute::OFF));
+            return;
+        }
+
+        const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop,
+                                                             "mode");
+        // XXX When dynamic parameters are supported, this code should
+        // create the blend function even if the mode is off.
+        if (pmode && !pmode->getValue<bool>()) {
+            pass->setMode(GL_BLEND, StateAttribute::OFF);
+            return;
+        }
+        const SGPropertyNode* psource
+            = getEffectPropertyChild(effect, prop, "source");
+        const SGPropertyNode* pdestination
+            = getEffectPropertyChild(effect, prop, "destination");
+        const SGPropertyNode* psourceRGB
+            = getEffectPropertyChild(effect, prop, "source-rgb");
+        const SGPropertyNode* psourceAlpha
+            = getEffectPropertyChild(effect, prop, "source-alpha");
+        const SGPropertyNode* pdestRGB
+            = getEffectPropertyChild(effect, prop, "destination-rgb");
+        const SGPropertyNode* pdestAlpha
+            = getEffectPropertyChild(effect, prop, "destination-alpha");
+        BlendFunc::BlendFuncMode sourceMode = BlendFunc::ONE;
+        BlendFunc::BlendFuncMode destMode = BlendFunc::ZERO;
+        if (psource)
+            findAttr(blendFuncModes, psource, sourceMode);
+        if (pdestination)
+            findAttr(blendFuncModes, pdestination, destMode);
+        if (psource && pdestination
+            && !(psourceRGB || psourceAlpha || pdestRGB || pdestAlpha)
+            && sourceMode == BlendFunc::SRC_ALPHA
+            && destMode == BlendFunc::ONE_MINUS_SRC_ALPHA) {
+            pass->setAttributeAndModes(StateAttributeFactory::instance()
+                                       ->getStandardBlendFunc());
+            return;
+        }
+        BlendFunc* blendFunc = new BlendFunc;
+        if (psource)
+            blendFunc->setSource(sourceMode);
+        if (pdestination)
+            blendFunc->setDestination(destMode);
+        if (psourceRGB) {
+            BlendFunc::BlendFuncMode sourceRGBMode;
+            findAttr(blendFuncModes, psourceRGB, sourceRGBMode);
+            blendFunc->setSourceRGB(sourceRGBMode);
+        }
+        if (pdestRGB) {
+            BlendFunc::BlendFuncMode destRGBMode;
+            findAttr(blendFuncModes, pdestRGB, destRGBMode);
+            blendFunc->setDestinationRGB(destRGBMode);
+        }
+        if (psourceAlpha) {
+            BlendFunc::BlendFuncMode sourceAlphaMode;
+            findAttr(blendFuncModes, psourceAlpha, sourceAlphaMode);
+            blendFunc->setSourceAlpha(sourceAlphaMode);
+        }
+        if (pdestAlpha) {
+            BlendFunc::BlendFuncMode destAlphaMode;
+            findAttr(blendFuncModes, pdestAlpha, destAlphaMode);
+            blendFunc->setDestinationAlpha(destAlphaMode);
+        }
+        pass->setAttributeAndModes(blendFunc);
     }
 };
 
 InstallAttributeBuilder<BlendBuilder> installBlend("blend");
 
+EffectNameValue<AlphaFunc::ComparisonFunction> alphaComparisonInit[] =
+{
+    {"never", AlphaFunc::NEVER},
+    {"less", AlphaFunc::LESS},
+    {"equal", AlphaFunc::EQUAL},
+    {"lequal", AlphaFunc::LEQUAL},
+    {"greater", AlphaFunc::GREATER},
+    {"notequal", AlphaFunc::NOTEQUAL},
+    {"gequal", AlphaFunc::GEQUAL},
+    {"always", AlphaFunc::ALWAYS}
+};
+EffectPropertyMap<AlphaFunc::ComparisonFunction>
+alphaComparison(alphaComparisonInit);
+
 struct AlphaTestBuilder : public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
                         const osgDB::ReaderWriter::Options* options)
     {
+        if (!isAttributeActive(effect, prop))
+            return;
+        // XXX Compatibility with early <alpha-test> syntax; should go away
+        // before a release
         const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
         if (!realProp)
             return;
-        pass->setMode(GL_ALPHA_TEST, (realProp->getBoolValue()
-                                      ? StateAttribute::ON
-                                      : StateAttribute::OFF));
+        if (realProp->nChildren() == 0) {
+            pass->setMode(GL_ALPHA_TEST, (realProp->getBoolValue()
+                                     ? StateAttribute::ON
+                                     : StateAttribute::OFF));
+            return;
+        }
+
+        const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop,
+                                                             "mode");
+        // XXX When dynamic parameters are supported, this code should
+        // create the blend function even if the mode is off.
+        if (pmode && !pmode->getValue<bool>()) {
+            pass->setMode(GL_ALPHA_TEST, StateAttribute::OFF);
+            return;
+        }
+        const SGPropertyNode* pComp = getEffectPropertyChild(effect, prop,
+                                                             "comparison");
+        const SGPropertyNode* pRef = getEffectPropertyChild(effect, prop,
+                                                             "reference");
+        AlphaFunc::ComparisonFunction func = AlphaFunc::ALWAYS;
+        float refValue = 1.0f;
+        if (pComp)
+            findAttr(alphaComparison, pComp, func);
+        if (pRef)
+            refValue = pRef->getValue<float>();
+        if (func == AlphaFunc::GREATER && osg::equivalent(refValue, 1.0f)) {
+            pass->setAttributeAndModes(StateAttributeFactory::instance()
+                                       ->getStandardAlphaFunc());
+        } else {
+            AlphaFunc* alphaFunc = new AlphaFunc;
+            alphaFunc->setFunction(func);
+            alphaFunc->setReferenceValue(refValue);
+            pass->setAttributeAndModes(alphaFunc);
+        }
     }
 };
 
 InstallAttributeBuilder<AlphaTestBuilder> installAlphaTest("alpha-test");
 
-EffectNameValue<TexEnv::Mode> texEnvModes[] =
-{
-    {"add", TexEnv::ADD},
-    {"blend", TexEnv::BLEND},
-    {"decal", TexEnv::DECAL},
-    {"modulate", TexEnv::MODULATE},
-    {"replace", TexEnv::REPLACE}
-};
-
-TexEnv* buildTexEnv(Effect* effect, const SGPropertyNode* prop)
-{
-    const SGPropertyNode* modeProp = getEffectPropertyChild(effect, prop,
-                                                            "mode");
-    const SGPropertyNode* colorProp = getEffectPropertyChild(effect, prop,
-                                                             "color");
-    if (!modeProp)
-        return 0;
-    TexEnv::Mode mode = TexEnv::MODULATE;
-    findAttr(texEnvModes, modeProp, mode);
-    if (mode == TexEnv::MODULATE) {
-        return StateAttributeFactory::instance()->getStandardTexEnv();
-    }
-    TexEnv* env = new TexEnv(mode);
-    if (colorProp)
-        env->setColor(toOsg(colorProp->getValue<SGVec4d>()));
-    return env;
- }
+InstallAttributeBuilder<TextureUnitBuilder> textureUnitBuilder("texture-unit");
 
+// Shader key, used both for shaders with relative and absolute names
+typedef pair<string, Shader::Type> ShaderKey;
 
-struct TextureUnitBuilder : PassAttributeBuilder
+struct ProgramKey
 {
-    void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
-                        const osgDB::ReaderWriter::Options* options);
+    typedef pair<string, int> AttribKey;
+    osgDB::FilePathList paths;
+    vector<ShaderKey> shaders;
+    vector<AttribKey> attributes;
+    struct EqualTo
+    {
+        bool operator()(const ProgramKey& lhs, const ProgramKey& rhs) const
+        {
+            return (lhs.paths.size() == rhs.paths.size()
+                    && equal(lhs.paths.begin(), lhs.paths.end(),
+                             rhs.paths.begin())
+                    && lhs.shaders.size() == rhs.shaders.size()
+                    && equal (lhs.shaders.begin(), lhs.shaders.end(),
+                              rhs.shaders.begin())
+                    && lhs.attributes.size() == rhs.attributes.size()
+                    && equal(lhs.attributes.begin(), lhs.attributes.end(),
+                             rhs.attributes.begin()));
+        }
+    };
 };
 
-void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass,
-                                        const SGPropertyNode* prop,
-                                        const osgDB::ReaderWriter::Options* options)
+size_t hash_value(const ProgramKey& key)
 {
-
-    // Decode the texture unit
-    int unit = 0;
-    const SGPropertyNode* pUnit = prop->getChild("unit");
-    if (pUnit) {
-        unit = pUnit->getValue<int>();
-    } else {
-        const SGPropertyNode* pName = prop->getChild("name");
-        if (pName)
-            try {
-                unit = boost::lexical_cast<int>(pName->getStringValue());
-            } catch (boost::bad_lexical_cast& lex) {
-                SG_LOG(SG_INPUT, SG_ALERT, "can't decode name as texture unit "
-                       << lex.what());
-            }
-    }
-    const SGPropertyNode* pType = prop->getChild("type");
-    string type;
-    if (!pType)
-        type = "2d";
-    else
-        type = pType->getStringValue();
-    Texture* texture = 0;
-    try {
-        texture = TextureBuilder::buildFromType(effect, type, prop,
-                                                options);
-    }
-    catch (BuilderException& e) {
-        SG_LOG(SG_INPUT, SG_ALERT, "No image file for texture, using white ");
-        texture = StateAttributeFactory::instance()->getWhiteTexture();
-    }
-    pass->setTextureAttributeAndModes(unit, texture);
-    const SGPropertyNode* envProp = prop->getChild("environment");
-    if (envProp) {
-        TexEnv* env = buildTexEnv(effect, envProp);
-        if (env)
-            pass->setTextureAttributeAndModes(unit, env);
-    }
+    size_t seed = 0;
+    boost::hash_range(seed, key.paths.begin(), key.paths.end());
+    boost::hash_range(seed, key.shaders.begin(), key.shaders.end());
+    boost::hash_range(seed, key.attributes.begin(), key.attributes.end());
+    return seed;
 }
 
+// XXX Should these be protected by a mutex? Probably
 
-
-InstallAttributeBuilder<TextureUnitBuilder> textureUnitBuilder("texture-unit");
-
-typedef map<string, ref_ptr<Program> > ProgramMap;
+typedef tr1::unordered_map<ProgramKey, ref_ptr<Program>,
+                           boost::hash<ProgramKey>, ProgramKey::EqualTo>
+ProgramMap;
 ProgramMap programMap;
 
-typedef map<string, ref_ptr<Shader> > ShaderMap;
+typedef tr1::unordered_map<ShaderKey, ref_ptr<Shader>, boost::hash<ShaderKey> >
+ShaderMap;
 ShaderMap shaderMap;
 
 void reload_shaders()
@@ -487,7 +593,7 @@ void reload_shaders()
     for(ShaderMap::iterator sitr = shaderMap.begin(); sitr != shaderMap.end(); ++sitr)
     {
        Shader *shader = sitr->second.get();
-        string fileName = osgDB::findDataFile(sitr->first);
+        string fileName = osgDB::findDataFile(sitr->first.first);
         if (!fileName.empty()) {
            shader->loadShaderSourceFromFile(fileName);
         }
@@ -505,32 +611,47 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
                                           const osgDB::ReaderWriter::Options*
                                           options)
 {
+    using namespace boost;
+    if (!isAttributeActive(effect, prop))
+        return;
     PropertyList pVertShaders = prop->getChildren("vertex-shader");
     PropertyList pFragShaders = prop->getChildren("fragment-shader");
-    string programKey;
+    PropertyList pAttributes = prop->getChildren("attribute");
+    ProgramKey prgKey;
     for (PropertyList::iterator itr = pVertShaders.begin(),
              e = pVertShaders.end();
          itr != e;
          ++itr)
-    {
-        programKey += (*itr)->getStringValue();
-        programKey += ";";
-    }
+        prgKey.shaders.push_back(ShaderKey((*itr)->getStringValue(),
+                                           Shader::VERTEX));
     for (PropertyList::iterator itr = pFragShaders.begin(),
              e = pFragShaders.end();
          itr != e;
          ++itr)
-    {
-        programKey += (*itr)->getStringValue();
-        programKey += ";";
+        prgKey.shaders.push_back(ShaderKey((*itr)->getStringValue(),
+                                           Shader::FRAGMENT));
+    for (PropertyList::iterator itr = pAttributes.begin(),
+             e = pAttributes.end();
+         itr != e;
+         ++itr) {
+        const SGPropertyNode* pName = getEffectPropertyChild(effect, *itr,
+                                                             "name");
+        const SGPropertyNode* pIndex = getEffectPropertyChild(effect, *itr,
+                                                              "index");
+        if (!pName || ! pIndex)
+            throw BuilderException("malformed attribute property");
+        prgKey.attributes
+            .push_back(ProgramKey::AttribKey(pName->getStringValue(),
+                                             pIndex->getValue<int>()));
     }
+    if (options)
+        prgKey.paths = options->getDatabasePathList();
     Program* program = 0;
-    ProgramMap::iterator pitr = programMap.find(programKey);
+    ProgramMap::iterator pitr = programMap.find(prgKey);
     if (pitr != programMap.end()) {
         program = pitr->second.get();
     } else {
         program = new Program;
-        program->setName(programKey);
         // Add vertex shaders, then fragment shaders
         PropertyList& pvec = pVertShaders;
         Shader::Type stype = Shader::VERTEX;
@@ -539,31 +660,36 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
                  nameItr != e;
                  ++nameItr) {
                 string shaderName = (*nameItr)->getStringValue();
-                ShaderMap::iterator sitr = shaderMap.find(shaderName);
+                string fileName = osgDB::findDataFile(shaderName, options);
+                if (fileName.empty())
+                    throw BuilderException(string("couldn't find shader ") +
+                                           shaderName);
+                ShaderKey skey(fileName, stype);
+                ShaderMap::iterator sitr = shaderMap.find(skey);
                 if (sitr != shaderMap.end()) {
                     program->addShader(sitr->second.get());
                 } else {
-                    string fileName = osgDB::findDataFile(shaderName, options);
-                    if (!fileName.empty()) {
-                        ref_ptr<Shader> shader = new Shader(stype);
-                        if (shader->loadShaderSourceFromFile(fileName)) {
-                            program->addShader(shader.get());
-                            shaderMap.insert(make_pair(shaderName, shader));
-                        }
+                    ref_ptr<Shader> shader = new Shader(stype);
+                    if (shader->loadShaderSourceFromFile(fileName)) {
+                        program->addShader(shader.get());
+                        shaderMap.insert(ShaderMap::value_type(skey, shader));
                     }
                 }
             }
             pvec = pFragShaders;
             stype = Shader::FRAGMENT;
         }
-        programMap.insert(make_pair(programKey, program));
+        BOOST_FOREACH(const ProgramKey::AttribKey& key, prgKey.attributes) {
+            program->addBindAttribLocation(key.first, key.second);
+        }
+       programMap.insert(ProgramMap::value_type(prgKey, program));
     }
     pass->setAttributeAndModes(program);
 }
 
 InstallAttributeBuilder<ShaderProgramBuilder> installShaderProgram("program");
 
-EffectNameValue<Uniform::Type> uniformTypes[] =
+EffectNameValue<Uniform::Type> uniformTypesInit[] =
 {
     {"float", Uniform::FLOAT},
     {"float-vec3", Uniform::FLOAT_VEC3},
@@ -572,12 +698,15 @@ EffectNameValue<Uniform::Type> uniformTypes[] =
     {"sampler-2d", Uniform::SAMPLER_2D},
     {"sampler-3d", Uniform::SAMPLER_3D}
 };
+EffectPropertyMap<Uniform::Type> uniformTypes(uniformTypesInit);
 
 struct UniformBuilder :public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
                         const osgDB::ReaderWriter::Options* options)
     {
+        if (!isAttributeActive(effect, prop))
+            return;
         const SGPropertyNode* nameProp = prop->getChild("name");
         const SGPropertyNode* typeProp = prop->getChild("type");
         const SGPropertyNode* valProp
@@ -659,18 +788,21 @@ struct NameBuilder : public PassAttributeBuilder
 
 InstallAttributeBuilder<NameBuilder> installName("name");
 
-EffectNameValue<PolygonMode::Mode> polygonModeModes[] =
+EffectNameValue<PolygonMode::Mode> polygonModeModesInit[] =
 {
     {"fill", PolygonMode::FILL},
     {"line", PolygonMode::LINE},
     {"point", PolygonMode::POINT}
 };
+EffectPropertyMap<PolygonMode::Mode> polygonModeModes(polygonModeModesInit);
 
 struct PolygonModeBuilder : public PassAttributeBuilder
 {
     void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
                         const osgDB::ReaderWriter::Options* options)
     {
+        if (!isAttributeActive(effect, prop))
+            return;
         const SGPropertyNode* frontProp
             = getEffectPropertyChild(effect, prop, "front");
         const SGPropertyNode* backProp
@@ -691,6 +823,87 @@ struct PolygonModeBuilder : public PassAttributeBuilder
 };
 
 InstallAttributeBuilder<PolygonModeBuilder> installPolygonMode("polygon-mode");
+
+struct VertexProgramTwoSideBuilder : public PassAttributeBuilder
+{
+    void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
+                        const osgDB::ReaderWriter::Options* options)
+    {
+        const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
+        if (!realProp)
+            return;
+        pass->setMode(GL_VERTEX_PROGRAM_TWO_SIDE,
+                      (realProp->getValue<bool>()
+                       ? StateAttribute::ON : StateAttribute::OFF));
+    }
+};
+
+InstallAttributeBuilder<VertexProgramTwoSideBuilder>
+installTwoSide("vertex-program-two-side");
+
+struct VertexProgramPointSizeBuilder : public PassAttributeBuilder
+{
+    void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
+                        const osgDB::ReaderWriter::Options* options)
+    {
+        const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
+        if (!realProp)
+            return;
+        pass->setMode(GL_VERTEX_PROGRAM_POINT_SIZE,
+                      (realProp->getValue<bool>()
+                       ? StateAttribute::ON : StateAttribute::OFF));
+    }
+};
+
+InstallAttributeBuilder<VertexProgramPointSizeBuilder>
+installPointSize("vertex-program-point-size");
+
+EffectNameValue<Depth::Function> depthFunctionInit[] =
+{
+    {"never", Depth::NEVER},
+    {"less", Depth::LESS},
+    {"equal", Depth::EQUAL},
+    {"lequal", Depth::LEQUAL},
+    {"greater", Depth::GREATER},
+    {"notequal", Depth::NOTEQUAL},
+    {"gequal", Depth::GEQUAL},
+    {"always", Depth::ALWAYS}
+};
+EffectPropertyMap<Depth::Function> depthFunction(depthFunctionInit);
+
+struct DepthBuilder : public PassAttributeBuilder
+{
+    void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
+                        const osgDB::ReaderWriter::Options* options)
+    {
+        if (!isAttributeActive(effect, prop))
+            return;
+        ref_ptr<Depth> depth = new Depth;
+        const SGPropertyNode* pfunc
+            = getEffectPropertyChild(effect, prop, "function");
+        if (pfunc) {
+            Depth::Function func = Depth::LESS;
+            findAttr(depthFunction, pfunc, func);
+            depth->setFunction(func);
+        }
+        const SGPropertyNode* pnear
+            = getEffectPropertyChild(effect, prop, "near");
+        if (pnear)
+            depth->setZNear(pnear->getValue<double>());
+        const SGPropertyNode* pfar
+            = getEffectPropertyChild(effect, prop, "far");
+        if (pfar)
+            depth->setZFar(pnear->getValue<double>());
+        const SGPropertyNode* pmask
+            = getEffectPropertyChild(effect, prop, "write-mask");
+        if (pmask)
+            depth->setWriteMask(pmask->getValue<bool>());
+        pass->setAttribute(depth.get());
+    }
+};
+
+InstallAttributeBuilder<DepthBuilder> installDepth("depth");
+
 void buildTechnique(Effect* effect, const SGPropertyNode* prop,
                     const osgDB::ReaderWriter::Options* options)
 {
@@ -728,18 +941,130 @@ void buildTechnique(Effect* effect, const SGPropertyNode* prop,
     }
 }
 
+// Specifically for .ac files...
+bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss)
+{
+    SGPropertyNode* paramRoot = makeChild(effectRoot, "parameters");
+    SGPropertyNode* matNode = paramRoot->getChild("material", 0, true);
+    Vec4f ambVal, difVal, specVal, emisVal;
+    float shininess = 0.0f;
+    const Material* mat = getStateAttribute<Material>(ss);
+    if (mat) {
+        ambVal = mat->getAmbient(Material::FRONT_AND_BACK);
+        difVal = mat->getDiffuse(Material::FRONT_AND_BACK);
+        specVal = mat->getSpecular(Material::FRONT_AND_BACK);
+        emisVal = mat->getEmission(Material::FRONT_AND_BACK);
+        shininess = mat->getShininess(Material::FRONT_AND_BACK);
+        makeChild(matNode, "active")->setValue(true);
+        makeChild(matNode, "ambient")->setValue(toVec4d(toSG(ambVal)));
+        makeChild(matNode, "diffuse")->setValue(toVec4d(toSG(difVal)));
+        makeChild(matNode, "specular")->setValue(toVec4d(toSG(specVal)));
+        makeChild(matNode, "emissive")->setValue(toVec4d(toSG(emisVal)));
+        makeChild(matNode, "shininess")->setValue(shininess);
+        matNode->getChild("color-mode", 0, true)->setStringValue("diffuse");
+    } else {
+        makeChild(matNode, "active")->setValue(false);
+    }
+    const ShadeModel* sm = getStateAttribute<ShadeModel>(ss);
+    string shadeModelString("smooth");
+    if (sm) {
+        ShadeModel::Mode smMode = sm->getMode();
+        if (smMode == ShadeModel::FLAT)
+            shadeModelString = "flat";
+    }
+    makeChild(paramRoot, "shade-model")->setStringValue(shadeModelString);
+    string cullFaceString("off");
+    const CullFace* cullFace = getStateAttribute<CullFace>(ss);
+    if (cullFace) {
+        switch (cullFace->getMode()) {
+        case CullFace::FRONT:
+            cullFaceString = "front";
+            break;
+        case CullFace::BACK:
+            cullFaceString = "back";
+            break;
+        case CullFace::FRONT_AND_BACK:
+            cullFaceString = "front-back";
+            break;
+        default:
+            break;
+        }
+    }
+    makeChild(paramRoot, "cull-face")->setStringValue(cullFaceString);
+    const BlendFunc* blendFunc = getStateAttribute<BlendFunc>(ss);
+    SGPropertyNode* blendNode = makeChild(paramRoot, "blend");
+    if (blendFunc) {
+        string sourceMode = findName(blendFuncModes, blendFunc->getSource());
+        string destMode = findName(blendFuncModes, blendFunc->getDestination());
+        makeChild(blendNode, "active")->setValue(true);
+        makeChild(blendNode, "source")->setStringValue(sourceMode);
+        makeChild(blendNode, "destination")->setStringValue(destMode);
+        makeChild(blendNode, "mode")->setValue(true);
+    } else {
+        makeChild(blendNode, "active")->setValue(false);
+    }
+    string renderingHint = findName(renderingHints, ss->getRenderingHint());
+    makeChild(paramRoot, "rendering-hint")->setStringValue(renderingHint);
+    makeTextureParameters(paramRoot, ss);
+    return true;
+}
+
 // Walk the techniques property tree, building techniques and
 // passes.
 bool Effect::realizeTechniques(const osgDB::ReaderWriter::Options* options)
 {
+    if (_isRealized)
+        return true;
     PropertyList tniqList = root->getChildren("technique");
     for (PropertyList::iterator itr = tniqList.begin(), e = tniqList.end();
          itr != e;
          ++itr)
         buildTechnique(this, *itr, options);
+    _isRealized = true;
     return true;
 }
 
+void Effect::InitializeCallback::doUpdate(osg::Node* node, osg::NodeVisitor* nv)
+{
+    EffectGeode* eg = dynamic_cast<EffectGeode*>(node);
+    if (!eg)
+        return;
+    Effect* effect = eg->getEffect();
+    if (!effect)
+        return;
+    SGPropertyNode* root = getPropertyRoot();
+    for (vector<SGSharedPtr<Updater> >::iterator itr = effect->_extraData.begin(),
+             end = effect->_extraData.end();
+         itr != end;
+         ++itr) {
+        InitializeWhenAdded* adder
+            = dynamic_cast<InitializeWhenAdded*>(itr->ptr());
+        if (adder)
+            adder->initOnAdd(effect, root);
+    }
+}
+
+bool Effect::Key::EqualTo::operator()(const Effect::Key& lhs,
+                                      const Effect::Key& rhs) const
+{
+    if (lhs.paths.size() != rhs.paths.size()
+        || !equal(lhs.paths.begin(), lhs.paths.end(), rhs.paths.begin()))
+        return false;
+    if (lhs.unmerged.valid() && rhs.unmerged.valid())
+        return props::Compare()(lhs.unmerged, rhs.unmerged);
+    else
+        return lhs.unmerged == rhs.unmerged;
+}
+
+size_t hash_value(const Effect::Key& key)
+{
+    size_t seed = 0;
+    if (key.unmerged.valid())
+        boost::hash_combine(seed, *key.unmerged);
+    boost::hash_range(seed, key.paths.begin(), key.paths.end());
+    return seed;
+}
+
 bool Effect_writeLocalData(const Object& obj, osgDB::Output& fw)
 {
     const Effect& effect = static_cast<const Effect&>(obj);
@@ -762,4 +1087,49 @@ osgDB::RegisterDotOsgWrapperProxy effectProxy
     &Effect_writeLocalData
     );
 }
+
+// Property expressions for technique predicates
+class PropertyExpression : public SGExpression<bool>
+{
+public:
+    PropertyExpression(SGPropertyNode* pnode) : _pnode(pnode) {}
+    
+    void eval(bool& value, const expression::Binding*) const
+    {
+        value = _pnode->getValue<bool>();
+    }
+protected:
+    SGPropertyNode_ptr _pnode;
+};
+
+class EffectPropertyListener : public SGPropertyChangeListener
+{
+public:
+    EffectPropertyListener(Technique* tniq) : _tniq(tniq) {}
+    
+    void valueChanged(SGPropertyNode* node)
+    {
+        _tniq->refreshValidity();
+    }
+protected:
+    osg::ref_ptr<Technique> _tniq;
+};
+
+Expression* propertyExpressionParser(const SGPropertyNode* exp,
+                                     expression::Parser* parser)
+{
+    SGPropertyNode_ptr pnode = getPropertyRoot()->getNode(exp->getStringValue(),
+                                                          true);
+    PropertyExpression* pexp = new PropertyExpression(pnode);
+    TechniquePredParser* predParser
+        = dynamic_cast<TechniquePredParser*>(parser);
+    if (predParser)
+        pnode->addChangeListener(new EffectPropertyListener(predParser
+                                                            ->getTechnique()));
+    return pexp;
+}
+
+expression::ExpParserRegistrar propertyRegistrar("property",
+                                                 propertyExpressionParser);
+
 }
index 2a3c896ec58d2a9b8c59f5f8318245cbee902318..1f33b9e86d61209f7a0b1f6cb5c4a08b7aa64446 100644 (file)
 
 #include <vector>
 #include <string>
+#include <boost/tr1/unordered_map.hpp>
+
+#include <boost/functional/hash.hpp>
 
 #include <osg/Object>
 #include <osgDB/ReaderWriter>
 
 #include <simgear/props/props.hxx>
+#include <simgear/scene/util/UpdateOnceCallback.hxx>
 
 namespace osg
 {
@@ -40,6 +44,32 @@ class CullVisitor;
 namespace simgear
 {
 class Technique;
+class Effect;
+
+/**
+ * Object to be initialized at some point after an effect -- and its
+ * containing effect geode -- are hooked into the scene graph. Some
+ * things, like manipulations of the global property tree, are are
+ * only safe in the update process.
+ */
+
+class InitializeWhenAdded
+{
+public:
+    InitializeWhenAdded() : _initialized(false) {};
+    virtual ~InitializeWhenAdded() {};
+    void initOnAdd(Effect* effect, SGPropertyNode* propRoot)
+    {
+        if (!_initialized) {
+            initOnAddImpl(effect, propRoot);
+            _initialized = true;
+        }
+    }
+    bool getInitialized() const { return _initialized; }
+private:
+    virtual void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot) = 0;
+    bool _initialized;
+};
 
 class Effect : public osg::Object
 {
@@ -56,14 +86,71 @@ public:
     Technique* chooseTechnique(osg::RenderInfo* renderInfo);
     virtual void resizeGLObjectBuffers(unsigned int maxSize);
     virtual void releaseGLObjects(osg::State* state = 0) const;
-    /*
+    /**
      * Build the techniques from the effect properties.
      */
     bool realizeTechniques(const osgDB::ReaderWriter::Options* options = 0);
-    
+    /**
+     * Updaters that should be derefed when the effect is
+     * deleted. Updaters arrange to be run by listening on properties
+     * or something.
+     */
+    struct Updater : public virtual SGReferenced
+    {
+        virtual ~Updater() {}
+    };
+    void addUpdater(Updater* data) { _extraData.push_back(data); }
+    // Callback that is added to the effect geode to initialize the
+    // effect.
+    friend struct InitializeCallback;
+    struct InitializeCallback : public UpdateOnceCallback
+    {
+        void doUpdate(osg::Node* node, osg::NodeVisitor* nv);
+    };
 protected:
+    std::vector<SGSharedPtr<Updater> > _extraData;
     ~Effect();
+    // Support for a cache of effects that inherit from this one, so
+    // Effect objects with the same parameters and techniques can be
+    // shared.
+    struct Key
+    {
+        Key() {}
+        Key(SGPropertyNode* unmerged_, const osgDB::FilePathList& paths_)
+            : unmerged(unmerged_), paths(paths_)
+        {
+        }
+        Key& operator=(const Key& rhs)
+        {
+            unmerged = rhs.unmerged;
+            paths = rhs.paths;
+            return *this;
+        }
+        SGPropertyNode_ptr unmerged;
+        osgDB::FilePathList paths;
+        struct EqualTo
+        {
+            bool operator()(const Key& lhs, const Key& rhs) const;
+        };
+    };
+    typedef std::tr1::unordered_map<Key, osg::ref_ptr<Effect>,
+                                    boost::hash<Key>, Key::EqualTo> Cache;
+    Cache* getCache()
+    {
+        if (!_cache)
+            _cache = new Cache;
+        return _cache;
+    }
+    Cache* _cache;
+    friend size_t hash_value(const Key& key);
+    friend Effect* makeEffect(SGPropertyNode* prop, bool realizeTechniques,
+                              const osgDB::ReaderWriter::Options* options);
+    bool _isRealized;
 };
+// Automatic support for boost hash function
+size_t hash_value(const Effect::Key&);
+
+
 Effect* makeEffect(const std::string& name,
                    bool realizeTechniques,
                    const osgDB::ReaderWriter::Options* options = 0);
@@ -71,5 +158,18 @@ Effect* makeEffect(const std::string& name,
 Effect* makeEffect(SGPropertyNode* prop,
                    bool realizeTechniques,
                    const osgDB::ReaderWriter::Options* options = 0);
+
+bool makeParametersFromStateSet(SGPropertyNode* paramRoot,
+                                const osg::StateSet* ss);
+
+namespace effect
+{
+/**
+ * The function that implements effect property tree inheritance.
+ */
+void mergePropertyTrees(SGPropertyNode* resultNode,
+                        const SGPropertyNode* left,
+                        const SGPropertyNode* right);
+}
 }
 #endif
index 32168a9c9562b46593175c7b245171ec24233213..34f14ac84aed8f862dd0bc7591121465377e16c2 100644 (file)
@@ -2,6 +2,8 @@
 #  include <simgear_config.h>
 #endif
 
+#include <simgear/scene/tgdb/userdata.hxx>
+
 #include <simgear/math/SGMath.hxx>
 
 #include "EffectBuilder.hxx"
@@ -39,6 +41,18 @@ const SGPropertyNode* getEffectPropertyChild(Effect* effect,
         return getEffectPropertyNode(effect, child);
 }
 
+string getGlobalProperty(const SGPropertyNode* prop)
+{
+    if (!prop)
+        return string();
+    const SGPropertyNode* useProp = prop->getChild("use");
+    if (!useProp)
+        return string();
+    return useProp->getStringValue();
+}
+
+namespace effect
+{
 BuilderException::BuilderException()
 {
 }
@@ -56,5 +70,19 @@ BuilderException::BuilderException(const std::string& message,
 
 BuilderException::~BuilderException() throw()
 {
+
+}
+}
+
+bool isAttributeActive(Effect* effect, const SGPropertyNode* prop)
+{
+    const SGPropertyNode* activeProp
+        = getEffectPropertyChild(effect, prop, "active");
+    return !activeProp || activeProp->getValue<bool>();
+}
+
+namespace effect
+{
+const char* colorFields[] = {"red", "green", "blue", "alpha"};
 }
 }
index 831c9fabe078a60f02662e9540f024a7230b624e..833ff515a7844cf14c11181081fa2fc94a8ee7d9 100644 (file)
 #define SIMGEAR_EFFECTBUILDER_HXX 1
 
 #include <algorithm>
+#include <iterator>
 #include <map>
 #include <string>
 #include <cstring>
 
 #include <osgDB/Registry>
 
+#include <boost/bind.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+
+#include <simgear/math/SGMath.hxx>
+#include <simgear/props/AtomicChangeListener.hxx>
 #include <simgear/props/props.hxx>
 #include <simgear/structure/exception.hxx>
 #include <simgear/structure/SGSharedPtr.hxx>
 #include <simgear/structure/Singleton.hxx>
 
+#include "Effect.hxx"
 /**
  * Support classes for parsing effects.
  */
@@ -36,6 +45,7 @@
 namespace simgear
 {
 class Effect;
+class Pass;
 
 /**
  * Builder that returns an object, probably an OSG object.
@@ -79,55 +89,134 @@ protected:
     }
 };
 
-// Simple tables of strings and constants. The table intialization
-// *must* be in alphabetical order.
+// Tables of strings and constants. We want to reconstruct the effect
+// property tree from OSG state sets, so the tables should be bi-directional.
+
+// two-way map for building StateSets from property descriptions, and
+// vice versa. Mostly copied from the boost documentation.
+
+namespace effect
+{
+using boost::multi_index_container;
+using namespace boost::multi_index;
+
+// tags for accessing both sides of a bidirectional map
+
+struct from{};
+struct to{};
+
 template <typename T>
 struct EffectNameValue
 {
     // Don't use std::pair because we want to use aggregate intialization.
-
     const char* first;
     T second;
-    class Compare
-    {
-    private:
-        static bool compare(const char* lhs, const char* rhs)
-        {
-            return std::strcmp(lhs, rhs) < 0;
-        }
-    public:
-        bool operator()(const EffectNameValue& lhs,
-                        const EffectNameValue& rhs) const
-        {
-            return compare(lhs.first, rhs.first);
-        }
-        bool operator()(const char* lhs, const EffectNameValue& rhs) const
-        {
-            return compare(lhs, rhs.first);
-        }
-        bool operator()(const EffectNameValue& lhs, const char* rhs) const
-        {
-            return compare (lhs.first, rhs);
-        }
-    };
 };
 
-template<typename ENV, typename T, int N>
-bool findAttr(const ENV (&attrs)[N], const SGPropertyNode* prop, T& result)
+// The class template bidirectional_map wraps the specification
+// of a bidirectional map based on multi_index_container.
+
+template<typename FromType,typename ToType>
+struct bidirectional_map
+{
+    typedef std::pair<FromType,ToType> value_type;
+
+    /* A bidirectional map can be simulated as a multi_index_container
+     * of pairs of (FromType,ToType) with two unique indices, one
+     * for each member of the pair.
+     */
+
+    typedef multi_index_container<
+        value_type,
+        indexed_by<
+            ordered_unique<
+                tag<from>, member<value_type, FromType, &value_type::first> >,
+            ordered_unique<
+                tag<to>,  member<value_type, ToType, &value_type::second> >
+            >
+        > type;
+};
+
+template<typename T>
+struct EffectPropertyMap
+{
+    typedef typename bidirectional_map<std::string, T>::type BMap;
+    BMap _map;
+    template<int N>
+    EffectPropertyMap(const EffectNameValue<T> (&attrs)[N]);
+};
+
+template<typename T>
+template<int N>
+EffectPropertyMap<T>::EffectPropertyMap(const EffectNameValue<T> (&attrs)[N])
+{
+    for (int i = 0; i < N; ++i)
+        _map.insert(typename BMap::value_type(attrs[i].first, attrs[i].second));
+}
+
+class BuilderException : public sg_exception
+{
+public:
+    BuilderException();
+    BuilderException(const char* message, const char* origin = 0);
+    BuilderException(const std::string& message, const std::string& = "");
+    virtual ~BuilderException() throw();
+};
+}
+
+template<typename T>
+void findAttr(const effect::EffectPropertyMap<T>& pMap,
+              const char* name,
+              T& result)
+{
+    using namespace effect;
+    typename EffectPropertyMap<T>::BMap::iterator itr
+        = pMap._map.get<from>().find(name);
+    if (itr == pMap._map.end()) {
+        throw effect::BuilderException(string("findAttr: could not find attribute ")
+                               + string(name));
+    } else {
+        result = itr->second;
+    }
+}
+
+template<typename T>
+inline void findAttr(const effect::EffectPropertyMap<T>& pMap,
+                     const std::string& name,
+                     T& result)
+{
+    findAttr(pMap, name.c_str(), result);
+}
+
+template<typename T>
+void findAttr(const effect::EffectPropertyMap<T>& pMap,
+              const SGPropertyNode* prop,
+              T& result)
 {
     if (!prop)
-        return false;
+        throw effect::BuilderException("findAttr: empty property");
     const char* name = prop->getStringValue();
     if (!name)
-        return false;
-    std::pair<const ENV*, const ENV*> itrs
-        = std::equal_range(&attrs[0], &attrs[N], name, typename ENV::Compare());
-    if (itrs.first == itrs.second) {
-        return false;
-    } else {
-        result = itrs.first->second;
-        return true;
-    }
+        throw effect::BuilderException("findAttr: no name for lookup");
+    findAttr(pMap, name, result);
+}
+
+template<typename T>
+std::string findName(const effect::EffectPropertyMap<T>& pMap, T value)
+{
+    using namespace effect;
+    std::string result;
+    typename EffectPropertyMap<T>::BMap::template index_iterator<to>::type itr
+        = pMap._map.get<to>().find(value);
+    if (itr != pMap._map.get<to>().end())
+        result = itr->first;
+    return result;
+}
+
+template<typename T>
+std::string findName(const effect::EffectPropertyMap<T>& pMap, GLenum value)
+{
+    return findName(pMap, static_cast<T>(value));
 }
 
 /**
@@ -145,13 +234,241 @@ const SGPropertyNode* getEffectPropertyChild(Effect* effect,
                                              const SGPropertyNode* prop,
                                              const char* name);
 
-class BuilderException : public sg_exception
+/**
+ * Get the name of a node mentioned in a <use> clause from the global property
+ * tree.
+ * @return empty if prop doesn't contain a <use> clause; otherwise the
+ * mentioned node name.
+ */
+std::string getGlobalProperty(const SGPropertyNode* prop);
+
+class PassAttributeBuilder : public SGReferenced
 {
+protected:
+    typedef std::map<const std::string, SGSharedPtr<PassAttributeBuilder> >
+    PassAttrMap;
+
+    struct PassAttrMapSingleton : public simgear::Singleton<PassAttrMapSingleton>
+    {
+        PassAttrMap passAttrMap;
+    };
 public:
-    BuilderException();
-    BuilderException(const char* message, const char* origin = 0);
-    BuilderException(const std::string& message, const std::string& = "");
-    virtual ~BuilderException() throw();
+    virtual void buildAttribute(Effect* effect, Pass* pass,
+                                const SGPropertyNode* prop,
+                                const osgDB::ReaderWriter::Options* options)
+    = 0;
+    static PassAttributeBuilder* find(const std::string& str)
+    {
+        PassAttrMap::iterator itr
+            = PassAttrMapSingleton::instance()->passAttrMap.find(str);
+        if (itr == PassAttrMapSingleton::instance()->passAttrMap.end())
+            return 0;
+        else
+            return itr->second.ptr();
+    }
+    template<typename T> friend class InstallAttributeBuilder;
+};
+
+template<typename T>
+struct InstallAttributeBuilder
+{
+    InstallAttributeBuilder(const string& name)
+    {
+        PassAttributeBuilder::PassAttrMapSingleton::instance()
+            ->passAttrMap.insert(make_pair(name, new T));
+    }
+};
+
+// The description of an attribute may exist in a pass' XML, but a
+// derived effect might want to disable the attribute altogether. So,
+// some attributes have an "active" property; if it exists and is
+// false, the OSG attribute is not built at all. This is different
+// from any OSG mode settings that might be around.
+bool isAttributeActive(Effect* effect, const SGPropertyNode* prop);
+
+namespace effect
+{
+/**
+ * Bridge between types stored in properties and what OSG wants.
+ */
+template<typename T> struct OSGBridge;
+
+template<typename T>
+struct OSGBridge<const T> : public OSGBridge<T>
+{
+};
+
+template<>
+struct OSGBridge<osg::Vec3f>
+{
+    typedef SGVec3d sg_type;
+    static osg::Vec3f getOsgType(const SGVec3d& val) { return toOsg(val); }
+};
+
+template<>
+struct OSGBridge<osg::Vec3d>
+{
+    typedef SGVec3d sg_type;
+    static osg::Vec3d getOsgType(const SGVec3d& val) { return toOsg(val); }
+};
+
+template<>
+struct OSGBridge<osg::Vec4f>
+{
+    typedef SGVec4d sg_type;
+    static osg::Vec4f getOsgType(const SGVec4d& val) { return toOsg(val); }
+};
+
+template<>
+struct OSGBridge<osg::Vec4d>
+{
+    typedef SGVec4d sg_type;
+    static osg::Vec4d getOsgType(const SGVec4d& val) { return toOsg(val); }
+};
+
+template<typename Obj, typename OSGParam>
+struct OSGFunctor : public OSGBridge<OSGParam>
+{
+    OSGFunctor(Obj* obj, void (Obj::*func)(const OSGParam&))
+        : _obj(obj), _func(func) {}
+    void operator()(const typename OSGBridge<OSGParam>::sg_type& val) const
+    {
+        ((_obj.get())->*_func)(this->getOsgType(val));
+    }
+    osg::ref_ptr<Obj>_obj;
+    void (Obj::*_func)(const OSGParam&);
 };
+
+template<typename ObjType, typename OSGParamType>
+class ScalarChangeListener
+    : public SGPropertyChangeListener, public InitializeWhenAdded,
+      public Effect::Updater
+{
+public:
+    typedef void (ObjType::*setter_type)(const OSGParamType);
+    ScalarChangeListener(ObjType* obj, setter_type setter,
+                         const std::string& propName)
+        : _obj(obj), _setter(setter)
+    {
+        _propName = new std::string(propName);
+    }
+    virtual ~ScalarChangeListener()
+    {
+        delete _propName;
+        _propName = 0;
+    }
+    void valueChanged(SGPropertyNode* node)
+    {
+        _obj->*setter(node->getValue<OSGParamType>());
+    }
+    void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot)
+    {
+        SGPropertyNode* listenProp = makeNode(propRoot, *_propName);
+        delete _propName;
+        _propName = 0;
+        if (listenProp)
+            listenProp->addChangeListener(this, true);
+    }
+private:
+    osg::ref_ptr<ObjType> _obj;
+    setter_type _setter;
+    std::string* _propName;
+};
+
+template<typename T, typename Func>
+class EffectExtendedPropListener : public InitializeWhenAdded,
+                                   public Effect::Updater
+{
+public:
+    template<typename Itr>
+    EffectExtendedPropListener(const Func& func,
+                               const std::string& propName, Itr childNamesBegin,
+                               Itr childNamesEnd)
+        : _func(func)
+    {
+        _propName = new std::string(propName);
+        _childNames = new std::vector<std::string>(childNamesBegin,
+                                                   childNamesEnd);
+    }
+    virtual ~EffectExtendedPropListener()
+    {
+        delete _propName;
+        delete _childNames;
+    }
+    void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot)
+    {
+        SGPropertyNode* parent = propRoot->getNode(*_propName, true);
+        _propListener
+            = new ExtendedPropListener<T, Func>(parent, _childNames->begin(),
+                                                _childNames->end(),
+                                                _func, true);
+        delete _propName;
+        _propName = 0;
+        delete _childNames;
+        _childNames = 0;
+    }
+private:
+    std::string* _propName;
+    std::vector<std::string>* _childNames;
+    SGSharedPtr<ExtendedPropListener<T, Func> > _propListener;
+    Func _func;
+};
+
+/**
+ * Initialize the value and the possible updating of an effect
+ * attribute. If the value is specified directly, set it. Otherwise,
+ * use the <use> tag to look at the parameters. Again, if there is a
+ * value there set it directly. Otherwise, the parameter contains its
+ * own <use> tag referring to a property in the global property tree;
+ * install a change listener that will set the attribute when the
+ * property changes.
+ */
+template<typename ObjType, typename OSGParamType>
+void
+initFromParameters(Effect* effect, const SGPropertyNode* prop, ObjType* obj,
+                   void (ObjType::*setter)(const OSGParamType))
+{
+    const SGPropertyNode* valProp = getEffectPropertyNode(effect, prop);
+    if (!valProp)
+        return;
+    if (valProp->nChildren() == 0) {
+        obj->*setter(valProp->getValue<OSGParamType>());
+    } else {
+        std::string propName = getGlobalProperty(prop);
+        ScalarChangeListener<ObjType, OSGParamType>* listener
+            = new ScalarChangeListener<ObjType, OSGParamType>(obj, setter,
+                                                              propName);
+        effect->addUpdater(listener);
+    }
+}
+
+template<typename ObjType, typename OSGParamType, typename NameItrType>
+void
+initFromParameters(Effect* effect, const SGPropertyNode* prop, ObjType* obj,
+                   void (ObjType::*setter)(const OSGParamType&),
+                   NameItrType nameItr)
+{
+    typedef typename OSGBridge<OSGParamType>::sg_type sg_type;
+    const SGPropertyNode* valProp = getEffectPropertyNode(effect, prop);
+    if (!valProp)
+        return;
+    if (valProp->nChildren() == 0) {
+        (obj->*setter)(OSGBridge<OSGParamType>
+                     ::getOsgType(valProp->getValue<sg_type>()));
+    } else {
+        string listenPropName = getGlobalProperty(valProp);
+        if (listenPropName.empty())
+            return;
+        typedef OSGFunctor<ObjType, OSGParamType> Functor;
+        Effect::Updater* listener
+            =  new EffectExtendedPropListener<sg_type, Functor>
+            (Functor(obj, setter), listenPropName, nameItr,
+             nameItr + props::NumComponents<sg_type>::num_components);
+        effect->addUpdater(listener);
+    }
+}
+
+extern const char* colorFields[];
+}
 }
 #endif
index 62952a9fe8b56944d6b37ae5cc4bdcbfdf7299ec..efac70b7e7809f6c808798a29ab12f8023e68464 100644 (file)
@@ -56,9 +56,11 @@ void EffectCullVisitor::apply(osg::Geode& node)
     }
     Effect* effect = eg->getEffect();
     Technique* technique = 0;
-    if (!(effect && (technique = effect->chooseTechnique(&getRenderInfo())))) {
+    if (!effect) {
         CullVisitor::apply(node);
         return;
+    } else if (!(technique = effect->chooseTechnique(&getRenderInfo()))) {
+        return;
     }
     // push the node's state.
     osg::StateSet* node_state = node.getStateSet();
index 799b567c76a8adf5b7399ae1c8cab6cdd265051a..aff9aebc4469c077f69952db4010199d68a49380 100644 (file)
@@ -38,10 +38,18 @@ EffectGeode::EffectGeode()
 {
 }
 
-EffectGeode::EffectGeode(const EffectGeode& rhs, const CopyOp& copyop) :
-    Geode(rhs, copyop)
+EffectGeode::EffectGeode(const EffectGeode& rhs, const osg::CopyOp& copyop) :
+    Geode(rhs, copyop),
+    _effect(static_cast<Effect*>(copyop(rhs._effect.get())))
 {
-    _effect = static_cast<Effect*>(rhs._effect->clone(copyop));
+}
+
+void EffectGeode::setEffect(Effect* effect)
+{
+    _effect = effect;
+    if (!_effect)
+        return;
+    addUpdateCallback(new Effect::InitializeCallback);
 }
 
 void EffectGeode::resizeGLObjectBuffers(unsigned int maxSize)
index ad35beb95685e483fb12615f4ec010c4b0f7d463..93c552a37558b194bd7d70d6182bed8af202136b 100644 (file)
@@ -31,7 +31,7 @@ public:
                 const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
     META_Node(simgear,EffectGeode);
     Effect* getEffect() const { return _effect.get(); }
-    void setEffect(Effect* effect) { _effect = effect; }
+    void setEffect(Effect* effect);
     virtual void resizeGLObjectBuffers(unsigned int maxSize);
     virtual void releaseGLObjects(osg::State* = 0) const;
     typedef DrawableList::iterator DrawablesIterator;
index 4c1dbe169c3a6f0eef2f5009fe168ccdcc086f87..b8093f353088017104fb5fc1738d9a2897dd2131 100644 (file)
@@ -6,7 +6,6 @@
 #include "Technique.hxx"
 #include "Pass.hxx"
 
-#include <boost/bind.hpp>
 #include <boost/foreach.hpp>
 
 #include <iterator>
@@ -14,6 +13,7 @@
 #include <string>
 
 #include <osg/GLExtensions>
+#include <osg/GL2Extensions>
 #include <osg/Math>
 #include <osgUtil/CullVisitor>
 
@@ -59,16 +59,15 @@ Technique::Technique(bool alwaysValid)
 
 Technique::Technique(const Technique& rhs, const osg::CopyOp& copyop) :
     _contextMap(rhs._contextMap), _alwaysValid(rhs._alwaysValid),
-    _shadowingStateSet(rhs._shadowingStateSet),
+    _shadowingStateSet(copyop(rhs._shadowingStateSet)),
     _validExpression(rhs._validExpression),
     _contextIdLocation(rhs._contextIdLocation)
 {
-    using namespace std;
-    using namespace boost;
-    transform(rhs.passes.begin(), rhs.passes.end(),
-              back_inserter(passes),
-              bind(simgear::clone_ref<Pass>, _1, copyop));
-
+    for (std::vector<ref_ptr<Pass> >::const_iterator itr = rhs.passes.begin(),
+             end = rhs.passes.end();
+         itr != end;
+         ++itr)
+        passes.push_back(static_cast<Pass*>(copyop(itr->get())));
 }
 
 Technique::~Technique()
@@ -276,6 +275,38 @@ Expression* extensionSupportedParser(const SGPropertyNode* exp,
 expression::ExpParserRegistrar
 extensionSupportedRegistrar("extension-supported", extensionSupportedParser);
 
+class GLShaderLanguageExpression : public GeneralNaryExpression<float, int>
+{
+public:
+    void eval(float& value, const expression::Binding* b) const
+    {
+        value = 0.0f;
+        int contextId = getOperand(0)->getValue(b);
+        GL2Extensions* extensions
+            = GL2Extensions::Get(static_cast<unsigned>(contextId), true);
+        if (!extensions)
+            return;
+        if (!extensions->isGlslSupported())
+            return;
+        value = extensions->getLanguageVersion();
+    }
+};
+
+Expression* shaderLanguageParser(const SGPropertyNode* exp,
+                                 expression::Parser* parser)
+{
+    GLShaderLanguageExpression* slexp = new GLShaderLanguageExpression;
+    int location = parser->getBindingLayout().addBinding("__contextId",
+                                                         expression::INT);
+    VariableExpression<int>* contextExp = new VariableExpression<int>(location);
+    slexp->addOperand(contextExp);
+    return slexp;
+}
+
+expression::ExpParserRegistrar shaderLanguageRegistrar("shader-language",
+                                                       glVersionParser);
+
+    
 void Technique::setGLExtensionsPred(float glVersion,
                                     const std::vector<std::string>& extensions)
 {
index a6490c92d32ac44c10707df5426302b72c0a6452..a924b95571055057d3e378b86ff0ba10ad7a439a 100644 (file)
 
 #include "TextureBuilder.hxx"
 
+#include "Pass.hxx"
+
+#include <osg/TexEnv>
+#include <osg/TexEnvCombine>
+#include <osg/TexGen>
 #include <osg/Texture1D>
 #include <osg/Texture2D>
 #include <osg/Texture3D>
 #include <osg/TextureRectangle>
 #include <osgDB/FileUtils>
 
+#include <boost/lexical_cast.hpp>
 #include <boost/tuple/tuple.hpp>
 #include <boost/tuple/tuple_comparison.hpp>
 
 #include <simgear/scene/util/SGSceneFeatures.hxx>
 #include <simgear/scene/util/StateAttributeFactory.hxx>
-
 #include <simgear/math/SGMath.hxx>
+#include <simgear/structure/OSGUtils.hxx>
 
 #include "Noise.hxx"
 
@@ -41,6 +47,12 @@ namespace simgear
 using namespace std;
 using namespace osg;
 
+using namespace effect;
+
+TexEnvCombine* buildTexEnvCombine(Effect* effect,
+                                  const SGPropertyNode* envProp);
+TexGen* buildTexGen(Effect* Effect, const SGPropertyNode* tgenProp);
+
 // Hack to force inclusion of TextureBuilder.cxx in library
 osg::Texture* TextureBuilder::buildFromType(Effect* effect, const string& type,
                                             const SGPropertyNode*props,
@@ -54,9 +66,95 @@ typedef boost::tuple<string, Texture::FilterMode, Texture::FilterMode,
                      Texture::WrapMode, Texture::WrapMode, Texture::WrapMode,
                      string> TexTuple;
 
+EffectNameValue<TexEnv::Mode> texEnvModesInit[] =
+{
+    {"add", TexEnv::ADD},
+    {"blend", TexEnv::BLEND},
+    {"decal", TexEnv::DECAL},
+    {"modulate", TexEnv::MODULATE},
+    {"replace", TexEnv::REPLACE}
+};
+EffectPropertyMap<TexEnv::Mode> texEnvModes(texEnvModesInit);
+
+TexEnv* buildTexEnv(Effect* effect, const SGPropertyNode* prop)
+{
+    const SGPropertyNode* modeProp = getEffectPropertyChild(effect, prop,
+                                                            "mode");
+    const SGPropertyNode* colorProp = getEffectPropertyChild(effect, prop,
+                                                             "color");
+    if (!modeProp)
+        return 0;
+    TexEnv::Mode mode = TexEnv::MODULATE;
+    findAttr(texEnvModes, modeProp, mode);
+    if (mode == TexEnv::MODULATE) {
+        return StateAttributeFactory::instance()->getStandardTexEnv();
+    }
+    TexEnv* env = new TexEnv(mode);
+    if (colorProp)
+        env->setColor(toOsg(colorProp->getValue<SGVec4d>()));
+    return env;
+ }
+
+
+void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass,
+                                        const SGPropertyNode* prop,
+                                        const osgDB::ReaderWriter::Options* options)
+{
+    if (!isAttributeActive(effect, prop))
+        return;
+    // Decode the texture unit
+    int unit = 0;
+    const SGPropertyNode* pUnit = prop->getChild("unit");
+    if (pUnit) {
+        unit = pUnit->getValue<int>();
+    } else {
+        const SGPropertyNode* pName = prop->getChild("name");
+        if (pName)
+            try {
+                unit = boost::lexical_cast<int>(pName->getStringValue());
+            } catch (boost::bad_lexical_cast& lex) {
+                SG_LOG(SG_INPUT, SG_ALERT, "can't decode name as texture unit "
+                       << lex.what());
+            }
+    }
+    const SGPropertyNode* pType = getEffectPropertyChild(effect, prop, "type");
+    string type;
+    if (!pType)
+        type = "2d";
+    else
+        type = pType->getStringValue();
+    Texture* texture = 0;
+    try {
+        texture = TextureBuilder::buildFromType(effect, type, prop,
+                                                options);
+    }
+    catch (BuilderException& e) {
+        SG_LOG(SG_INPUT, SG_ALERT, "No image file for texture, using white ");
+        texture = StateAttributeFactory::instance()->getWhiteTexture();
+    }
+    pass->setTextureAttributeAndModes(unit, texture);
+    const SGPropertyNode* envProp = prop->getChild("environment");
+    if (envProp) {
+        TexEnv* env = buildTexEnv(effect, envProp);
+        if (env)
+            pass->setTextureAttributeAndModes(unit, env);
+    }
+    const SGPropertyNode* combineProp = prop->getChild("texenv-combine");
+    TexEnvCombine* combiner = 0;
+    if (combineProp && ((combiner = buildTexEnvCombine(effect, combineProp))))
+        pass->setTextureAttributeAndModes(unit, combiner);
+    const SGPropertyNode* tgenProp = prop->getChild("texgen");
+    TexGen* tgen = 0;
+    if (tgenProp && (tgen = buildTexGen(effect, tgenProp)))
+        pass->setTextureAttributeAndModes(unit, tgen);
+}
+
+// InstallAttributeBuilder call is in Effect.cxx to force this file to
+// be linked in.
+
 namespace
 {
-EffectNameValue<Texture::FilterMode> filterModes[] =
+EffectNameValue<Texture::FilterMode> filterModesInit[] =
 {
     { "linear", Texture::LINEAR },
     { "linear-mipmap-linear", Texture::LINEAR_MIPMAP_LINEAR},
@@ -65,8 +163,9 @@ EffectNameValue<Texture::FilterMode> filterModes[] =
     { "nearest-mipmap-linear", Texture::NEAREST_MIPMAP_LINEAR},
     { "nearest-mipmap-nearest", Texture::NEAREST_MIPMAP_NEAREST}
 };
+EffectPropertyMap<Texture::FilterMode> filterModes(filterModesInit);
 
-EffectNameValue<Texture::WrapMode> wrapModes[] =
+EffectNameValue<Texture::WrapMode> wrapModesInit[] =
 {
     {"clamp", Texture::CLAMP},
     {"clamp-to-border", Texture::CLAMP_TO_BORDER},
@@ -74,7 +173,7 @@ EffectNameValue<Texture::WrapMode> wrapModes[] =
     {"mirror", Texture::MIRROR},
     {"repeat", Texture::REPEAT}
 };
-
+EffectPropertyMap<Texture::WrapMode> wrapModes(wrapModesInit);
 
 
 TexTuple makeTexTuple(Effect* effect, const SGPropertyNode* props,
@@ -82,24 +181,27 @@ TexTuple makeTexTuple(Effect* effect, const SGPropertyNode* props,
                       const string& texType)
 {
     Texture::FilterMode minFilter = Texture::LINEAR_MIPMAP_LINEAR;
-    findAttr(filterModes, getEffectPropertyChild(effect, props, "filter"),
-             minFilter);
+    const SGPropertyNode* ep = 0;
+    if ((ep = getEffectPropertyChild(effect, props, "filter")))
+        findAttr(filterModes, ep, minFilter);
     Texture::FilterMode magFilter = Texture::LINEAR;
-    findAttr(filterModes, getEffectPropertyChild(effect, props,
-                                                 "mag-filter"),
-             magFilter);
+    if ((ep = getEffectPropertyChild(effect, props, "mag-filter")))
+        findAttr(filterModes, ep, magFilter);
     const SGPropertyNode* pWrapS
         = getEffectPropertyChild(effect, props, "wrap-s");
     Texture::WrapMode sWrap = Texture::CLAMP;
-    findAttr(wrapModes, pWrapS, sWrap);
+    if (pWrapS)
+        findAttr(wrapModes, pWrapS, sWrap);
     const SGPropertyNode* pWrapT
         = getEffectPropertyChild(effect, props, "wrap-t");
     Texture::WrapMode tWrap = Texture::CLAMP;
-    findAttr(wrapModes, pWrapT, tWrap);
+    if (pWrapT)
+        findAttr(wrapModes, pWrapT, tWrap);
     const SGPropertyNode* pWrapR
         = getEffectPropertyChild(effect, props, "wrap-r");
     Texture::WrapMode rWrap = Texture::CLAMP;
-    findAttr(wrapModes, pWrapR, rWrap);
+    if (pWrapR)
+        findAttr(wrapModes, pWrapR, rWrap);
     const SGPropertyNode* pImage
         = getEffectPropertyChild(effect, props, "image");
     string imageName;
@@ -198,6 +300,25 @@ namespace
 TextureBuilder::Registrar installWhite("white", new WhiteTextureBuilder);
 }
 
+class TransparentTextureBuilder : public TextureBuilder
+{
+public:
+    Texture* build(Effect* effect, const SGPropertyNode*,
+                   const osgDB::ReaderWriter::Options* options);
+};
+
+Texture* TransparentTextureBuilder::build(Effect* effect, const SGPropertyNode*,
+                                    const osgDB::ReaderWriter::Options* options)
+{
+    return StateAttributeFactory::instance()->getTransparentTexture();
+}
+
+namespace
+{
+TextureBuilder::Registrar installTransparent("transparent",
+                                             new TransparentTextureBuilder);
+}
+
 osg::Image* make3DNoiseImage(int texSize)
 {
     osg::Image* image = new osg::Image;
@@ -280,4 +401,228 @@ namespace
 TextureBuilder::Registrar installNoise("noise", new NoiseBuilder);
 }
 
+EffectNameValue<TexEnvCombine::CombineParam> combineParamInit[] =
+{
+    {"replace", TexEnvCombine::REPLACE},
+    {"modulate", TexEnvCombine::MODULATE},
+    {"add", TexEnvCombine::ADD},
+    {"add-signed", TexEnvCombine::ADD_SIGNED},
+    {"interpolate", TexEnvCombine::INTERPOLATE},
+    {"subtract", TexEnvCombine::SUBTRACT},
+    {"dot3-rgb", TexEnvCombine::DOT3_RGB},
+    {"dot3-rgba", TexEnvCombine::DOT3_RGBA}
+};
+
+EffectPropertyMap<TexEnvCombine::CombineParam> combineParams(combineParamInit);
+
+EffectNameValue<TexEnvCombine::SourceParam> sourceParamInit[] =
+{
+    {"constant", TexEnvCombine::CONSTANT},
+    {"primary_color", TexEnvCombine::PRIMARY_COLOR},
+    {"previous", TexEnvCombine::PREVIOUS},
+    {"texture", TexEnvCombine::TEXTURE},
+    {"texture0", TexEnvCombine::TEXTURE0},
+    {"texture1", TexEnvCombine::TEXTURE1},
+    {"texture2", TexEnvCombine::TEXTURE2},
+    {"texture3", TexEnvCombine::TEXTURE3},
+    {"texture4", TexEnvCombine::TEXTURE4},
+    {"texture5", TexEnvCombine::TEXTURE5},
+    {"texture6", TexEnvCombine::TEXTURE6},
+    {"texture7", TexEnvCombine::TEXTURE7}
+};
+
+EffectPropertyMap<TexEnvCombine::SourceParam> sourceParams(sourceParamInit);
+
+EffectNameValue<TexEnvCombine::OperandParam> opParamInit[] =
+{
+    {"src-color", TexEnvCombine::SRC_COLOR},
+    {"one-minus-src-color", TexEnvCombine::ONE_MINUS_SRC_COLOR},
+    {"src-alpha", TexEnvCombine::SRC_ALPHA},
+    {"one-minus-src-alpha", TexEnvCombine::ONE_MINUS_SRC_ALPHA}
+};
+
+EffectPropertyMap<TexEnvCombine::OperandParam> operandParams(opParamInit);
+
+TexEnvCombine* buildTexEnvCombine(Effect* effect, const SGPropertyNode* envProp)
+{
+    if (!isAttributeActive(effect, envProp))
+        return 0;
+    TexEnvCombine* result = new TexEnvCombine;
+    const SGPropertyNode* p = 0;
+    if ((p = getEffectPropertyChild(effect, envProp, "combine-rgb"))) {
+        TexEnvCombine::CombineParam crgb = TexEnvCombine::MODULATE;
+        findAttr(combineParams, p, crgb);
+        result->setCombine_RGB(crgb);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "combine-alpha"))) {
+        TexEnvCombine::CombineParam calpha = TexEnvCombine::MODULATE;
+        findAttr(combineParams, p, calpha);
+        result->setCombine_Alpha(calpha);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "source0-rgb"))) {
+        TexEnvCombine::SourceParam source = TexEnvCombine::TEXTURE;
+        findAttr(sourceParams, p, source);
+        result->setSource0_RGB(source);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "source1-rgb"))) {
+        TexEnvCombine::SourceParam source = TexEnvCombine::PREVIOUS;
+        findAttr(sourceParams, p, source);
+        result->setSource1_RGB(source);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "source2-rgb"))) {
+        TexEnvCombine::SourceParam source = TexEnvCombine::CONSTANT;
+        findAttr(sourceParams, p, source);
+        result->setSource2_RGB(source);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "source0-alpha"))) {
+        TexEnvCombine::SourceParam source = TexEnvCombine::TEXTURE;
+        findAttr(sourceParams, p, source);
+        result->setSource0_Alpha(source);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "source1-alpha"))) {
+        TexEnvCombine::SourceParam source = TexEnvCombine::PREVIOUS;
+        findAttr(sourceParams, p, source);
+        result->setSource1_Alpha(source);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "source2-alpha"))) {
+        TexEnvCombine::SourceParam source = TexEnvCombine::CONSTANT;
+        findAttr(sourceParams, p, source);
+        result->setSource2_Alpha(source);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "operand0-rgb"))) {
+        TexEnvCombine::OperandParam op = TexEnvCombine::SRC_COLOR;
+        findAttr(operandParams, p, op);
+        result->setOperand0_RGB(op);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "operand1-rgb"))) {
+        TexEnvCombine::OperandParam op = TexEnvCombine::SRC_COLOR;
+        findAttr(operandParams, p, op);
+        result->setOperand1_RGB(op);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "operand2-rgb"))) {
+        TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
+        findAttr(operandParams, p, op);
+        result->setOperand2_RGB(op);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "operand0-alpha"))) {
+        TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
+        findAttr(operandParams, p, op);
+        result->setOperand0_Alpha(op);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "operand1-alpha"))) {
+        TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
+        findAttr(operandParams, p, op);
+        result->setOperand1_Alpha(op);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "operand2-alpha"))) {
+        TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
+        findAttr(operandParams, p, op);
+        result->setOperand2_Alpha(op);
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "scale-rgb"))) {
+        result->setScale_RGB(p->getValue<float>());
+    }
+    if ((p = getEffectPropertyChild(effect, envProp, "scale-alpha"))) {
+        result->setScale_Alpha(p->getValue<float>());
+    }
+#if 0
+    if ((p = getEffectPropertyChild(effect, envProp, "constant-color"))) {
+        SGVec4d color = p->getValue<SGVec4d>();
+        result->setConstantColor(toOsg(color));
+    } else if ((p = getEffectPropertyChild(effect, envProp,
+                                           "light-direction"))) {
+        SGVec3d direction = p->getValue<SGVec3d>();
+        result->setConstantColorAsLightDirection(toOsg(direction));
+    }
+#endif
+    const SGPropertyNode* colorNode = envProp->getChild("constant-color");
+    if (colorNode)
+        initFromParameters(effect, colorNode, result,
+                           &TexEnvCombine::setConstantColor, colorFields);
+    return result;
+}
+
+EffectNameValue<TexGen::Mode> tgenModeInit[] =
+{
+    { "object-linear", TexGen::OBJECT_LINEAR},
+    { "eye-linear", TexGen::EYE_LINEAR},
+    { "sphere-map", TexGen::SPHERE_MAP},
+    { "normal-map", TexGen::NORMAL_MAP},
+    { "reflection-map", TexGen::REFLECTION_MAP}
+};
+
+EffectPropertyMap<TexGen::Mode> tgenModes(tgenModeInit);
+
+EffectNameValue<TexGen::Coord> tgenCoordInit[] =
+{
+    {"s", TexGen::S},
+    {"t", TexGen::T},
+    {"r", TexGen::R},
+    {"q", TexGen::Q}
+};
+
+EffectPropertyMap<TexGen::Coord> tgenCoords(tgenCoordInit);
+
+TexGen* buildTexGen(Effect* effect, const SGPropertyNode* tgenProp)
+{
+    if (!isAttributeActive(effect, tgenProp))
+        return 0;
+    TexGen* result = new TexGen;
+    const SGPropertyNode* p = 0;
+    TexGen::Mode mode = TexGen::OBJECT_LINEAR;
+    findAttr(tgenModes, getEffectPropertyChild(effect, tgenProp, "mode"), mode);
+    result->setMode(mode);
+    const SGPropertyNode* planesNode = tgenProp->getChild("planes");
+    if (planesNode) {
+        for (int i = 0; i < planesNode->nChildren(); ++i) {
+            const SGPropertyNode* planeNode = planesNode->getChild(i);
+            TexGen::Coord coord;
+            findAttr(tgenCoords, planeNode->getName(), coord);
+            const SGPropertyNode* realNode
+                = getEffectPropertyNode(effect, planeNode);
+            SGVec4d plane = realNode->getValue<SGVec4d>();
+            result->setPlane(coord, toOsg(plane));
+        }
+    }
+    return result;
+}
+
+bool makeTextureParameters(SGPropertyNode* paramRoot, const StateSet* ss)
+{
+    SGPropertyNode* texUnit = makeChild(paramRoot, "texture");
+    const Texture* tex = getStateAttribute<Texture>(0, ss);
+    const Texture2D* texture = dynamic_cast<const Texture2D*>(tex);
+    makeChild(texUnit, "unit")->setValue(0);
+    if (!tex) {
+        // The default shader-based technique ignores active
+        makeChild(texUnit, "active")->setValue(false);
+        return false;
+    }
+    const Image* image = texture->getImage();
+    string imageName;
+    if (image) {
+        imageName = image->getFileName();
+    } else {
+        makeChild(texUnit, "active")->setValue(false);
+        makeChild(texUnit, "type")->setValue("white");
+        return false;
+    }
+    makeChild(texUnit, "active")->setValue(true);
+    makeChild(texUnit, "type")->setValue("2d");
+    string filter = findName(filterModes,
+                             texture->getFilter(Texture::MIN_FILTER));
+    string magFilter = findName(filterModes,
+                             texture->getFilter(Texture::MAG_FILTER));
+    string wrapS = findName(wrapModes, texture->getWrap(Texture::WRAP_S));
+    string wrapT = findName(wrapModes, texture->getWrap(Texture::WRAP_T));
+    string wrapR = findName(wrapModes, texture->getWrap(Texture::WRAP_R));
+    makeChild(texUnit, "image")->setStringValue(imageName);
+    makeChild(texUnit, "filter")->setStringValue(filter);
+    makeChild(texUnit, "mag-filter")->setStringValue(magFilter);
+    makeChild(texUnit, "wrap-s")->setStringValue(wrapS);
+    makeChild(texUnit, "wrap-t")->setStringValue(wrapT);
+    makeChild(texUnit, "wrap-r")->setStringValue(wrapR);
+    return true;
+}
+
 }
index 259ef61417caad04094ff850bf43c7a511d93dc7..ca6c54945f3eb902a88eeb5f10e14e1b1c5fd066 100644 (file)
@@ -17,6 +17,7 @@
 #ifndef SIMGEAR_TEXTUREBUILDER_HXX
 #define SIMGEAR_TEXTUREBUILDER_HXX 1
 
+#include <osg/StateSet>
 #include <osg/Texture>
 #include "EffectBuilder.hxx"
 
@@ -30,5 +31,14 @@ public:
                                        const SGPropertyNode*props,
                                        const osgDB::ReaderWriter::Options* options);
 };
+
+struct TextureUnitBuilder : public PassAttributeBuilder
+{
+    void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
+                        const osgDB::ReaderWriter::Options* options);
+};
+
+
+bool makeTextureParameters(SGPropertyNode* paramRoot, const osg::StateSet* ss);
 }
 #endif
index b2d37ff21e8e683c964829aa85969a99e6b098aa..1a8efcdbb058912c2e95f3b2bc16073ee8403dcf 100644 (file)
@@ -4,6 +4,7 @@
 #endif
 
 #include "Effect.hxx"
+#include "EffectBuilder.hxx"
 #include "Technique.hxx"
 #include "Pass.hxx"
 
 #include <simgear/debug/logstream.hxx>
 #include <simgear/props/props_io.hxx>
 #include <simgear/scene/util/SGSceneFeatures.hxx>
+#include <simgear/scene/util/SplicingVisitor.hxx>
 #include <simgear/structure/SGExpression.hxx>
 
 namespace simgear
 {
 using namespace std;
 using namespace osg;
+using namespace effect;
 
 typedef vector<const SGPropertyNode*> RawPropVector;
 typedef map<const string, ref_ptr<Effect> > EffectMap;
@@ -71,6 +74,8 @@ struct PropPredicate
     const SGPropertyNode* node;
 };
 
+namespace effect
+{
 void mergePropertyTrees(SGPropertyNode* resultNode,
                         const SGPropertyNode* left, const SGPropertyNode* right)
 {
@@ -107,29 +112,48 @@ void mergePropertyTrees(SGPropertyNode* resultNode,
         copyProperties(*itr, newChild);
     }
 }
+}
 
 Effect* makeEffect(const string& name,
                    bool realizeTechniques,
                    const osgDB::ReaderWriter::Options* options)
 {
-    OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
-    EffectMap::iterator itr = effectMap.find(name);
-    if (itr != effectMap.end())
-        return itr->second.get();
+    {
+        OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
+        EffectMap::iterator itr = effectMap.find(name);
+        if (itr != effectMap.end())
+            return itr->second.get();
+    }
     string effectFileName(name);
     effectFileName += ".eff";
     string absFileName
         = osgDB::findDataFile(effectFileName, options);
     if (absFileName.empty()) {
-        SG_LOG(SG_INPUT, SG_WARN, "can't find \"" << effectFileName << "\"");
+        SG_LOG(SG_INPUT, SG_ALERT, "can't find \"" << effectFileName << "\"");
         return 0;
     }
     SGPropertyNode_ptr effectProps = new SGPropertyNode();
-    readProperties(absFileName, effectProps.ptr(), 0, true);
-    Effect* result = makeEffect(effectProps.ptr(), realizeTechniques, options);
-    if (result)
-        effectMap.insert(make_pair(name, result));
-    return result;
+    try {
+        readProperties(absFileName, effectProps.ptr(), 0, true);
+    }
+    catch (sg_io_exception& e) {
+        SG_LOG(SG_INPUT, SG_ALERT, "error reading \"" << absFileName << "\": "
+               << e.getFormattedMessage());
+        return 0;
+    }
+    ref_ptr<Effect> result = makeEffect(effectProps.ptr(), realizeTechniques,
+                                        options);
+    if (result.valid()) {
+        OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
+        pair<EffectMap::iterator, bool> irslt
+            = effectMap.insert(make_pair(name, result));
+        if (!irslt.second) {
+            // Another thread beat us to it!. Discard our newly
+            // constructed Effect and use the one in the cache.
+            result = irslt.first->second;
+        }
+    }
+    return result.release();
 }
 
 
@@ -160,29 +184,64 @@ Effect* makeEffect(SGPropertyNode* prop,
             }
         }
     }
-    Effect* effect = new Effect;
+    ref_ptr<Effect> effect;
     // Merge with the parent effect, if any
-    const SGPropertyNode* inheritProp = prop->getChild("inherits-from");
+    SGPropertyNode_ptr inheritProp = prop->getChild("inherits-from");
     Effect* parent = 0;
     if (inheritProp) {
-        parent = makeEffect(inheritProp->getStringValue(), realizeTechniques,
-                            options);
-        if(parent)
-        {
-            effect->root = new SGPropertyNode;
-            mergePropertyTrees(effect->root, prop, parent->root);
-            effect->root->removeChild("inherits-from");
+        //prop->removeChild("inherits-from");
+        parent = makeEffect(inheritProp->getStringValue(), false, options);
+        if (parent) {
+            Effect::Key key;
+            key.unmerged = prop;
+            if (options) {
+                key.paths = options->getDatabasePathList();
+            }
+            Effect::Cache* cache = 0;
+            Effect::Cache::iterator itr;
+            {
+                OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
+                    lock(effectMutex);
+                cache = parent->getCache();
+                itr = cache->find(key);
+                if (itr != cache->end()) 
+                    effect = itr->second.get();
+            }
+            if (!effect.valid()) {
+                effect = new Effect;
+                effect->root = new SGPropertyNode;
+                mergePropertyTrees(effect->root, prop, parent->root);
+                effect->parametersProp = effect->root->getChild("parameters");
+                OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
+                    lock(effectMutex);
+                pair<Effect::Cache::iterator, bool> irslt
+                    = cache->insert(make_pair(key, effect));
+                if (!irslt.second)
+                    effect = irslt.first->second;
+            }
         } else {
-            effect->root = prop;
-            effect->root->removeChild("inherits-from");
+            SG_LOG(SG_INPUT, SG_ALERT, "can't find base effect " <<
+                   inheritProp->getStringValue());
+            return 0;
         }
     } else {
+        effect = new Effect;
         effect->root = prop;
+        effect->parametersProp = effect->root->getChild("parameters");
+    }
+    if (realizeTechniques) {
+        try {
+            OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
+                lock(effectMutex);
+            effect->realizeTechniques(options);
+        }
+        catch (BuilderException& e) {
+            SG_LOG(SG_INPUT, SG_ALERT, "Error building technique: "
+                   << e.getFormattedMessage());
+            return 0;
+        }
     }
-    effect->parametersProp = effect->root->getChild("parameters");
-    if (realizeTechniques)
-        effect->realizeTechniques(options);
-    return effect;
+    return effect.release();
 }
 
 }
index 3b5b545c4101b48cd93129b34a716c9bef01cf0d..2bf8c878e0fa9c9b362a174fb3b3426147492c6d 100644 (file)
@@ -62,9 +62,6 @@ using namespace osgUtil;
 using namespace osgDB;
 using namespace simgear;
 
-using OpenThreads::ReentrantMutex;
-using OpenThreads::ScopedLock;
-
 // Little helper class that holds an extra reference to a
 // loaded 3d model.
 // Since we clone all structural nodes from our 3d models,
@@ -128,125 +125,6 @@ protected:
     }
 };
 
-// Change the StateSets of a model to hold different textures based on
-// a livery path.
-
-class TextureUpdateVisitor : public NodeAndDrawableVisitor {
-public:
-    TextureUpdateVisitor(const FilePathList& pathList) :
-        NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
-        _pathList(pathList)
-    {
-    }
-    
-    virtual void apply(Node& node)
-    {
-        StateSet* stateSet = cloneStateSet(node.getStateSet());
-        if (stateSet)
-            node.setStateSet(stateSet);
-        traverse(node);
-    }
-
-    virtual void apply(Drawable& drawable)
-    {
-        StateSet* stateSet = cloneStateSet(drawable.getStateSet());
-        if (stateSet)
-            drawable.setStateSet(stateSet);
-    }
-    // Copied from Mathias' earlier SGTextureUpdateVisitor
-protected:
-    Texture2D* textureReplace(int unit, const StateAttribute* attr)
-    {
-        const Texture2D* texture = dynamic_cast<const Texture2D*>(attr);
-
-        if (!texture)
-            return 0;
-    
-        const Image* image = texture->getImage();
-        const string* fullFilePath = 0;
-        if (image) {
-            // The currently loaded file name
-            fullFilePath = &image->getFileName();
-
-        } else {
-            fullFilePath = &texture->getName();
-        }
-        // The short name
-        string fileName = getSimpleFileName(*fullFilePath);
-        if (fileName.empty())
-            return 0;
-        // The name that should be found with the current database path
-        string fullLiveryFile = findFileInPath(fileName, _pathList);
-        // If it is empty or they are identical then there is nothing to do
-        if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
-            return 0;
-        Image* newImage = readImageFile(fullLiveryFile);
-        if (!newImage)
-            return 0;
-        CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
-        Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
-        if (!newTexture) {
-            return 0;
-        } else {
-            newTexture->setImage(newImage);
-            return newTexture;
-        }
-    }
-    
-    StateSet* cloneStateSet(const StateSet* stateSet)
-    {
-        typedef pair<int, Texture2D*> Tex2D;
-        vector<Tex2D> newTextures;
-        StateSet* result = 0;
-
-        if (!stateSet)
-            return 0;
-        int numUnits = stateSet->getTextureAttributeList().size();
-        if (numUnits > 0) {
-            for (int i = 0; i < numUnits; ++i) {
-                const StateAttribute* attr
-                    = stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
-                Texture2D* newTexture = textureReplace(i, attr);
-                if (newTexture)
-                    newTextures.push_back(Tex2D(i, newTexture));
-            }
-            if (!newTextures.empty()) {
-                result = static_cast<StateSet*>(stateSet->clone(CopyOp()));
-                for (vector<Tex2D>::iterator i = newTextures.begin();
-                     i != newTextures.end();
-                     ++i) {
-                    result->setTextureAttribute(i->first, i->second);
-                }
-            }
-        }
-        return result;
-    }
-private:
-    FilePathList _pathList;
-};
-
-// Create new userdata structs in a copied model.
-// The BVH trees are shared with the original model, but the velocity fields
-// should usually be distinct fields for distinct models.
-class UserDataCopyVisitor : public osg::NodeVisitor {
-public:
-    UserDataCopyVisitor() :
-        osg::NodeVisitor(osg::NodeVisitor::NODE_VISITOR,
-                         osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
-    {
-    }
-    virtual void apply(osg::Node& node)
-    {
-        osg::ref_ptr<SGSceneUserData> userData;
-        userData = SGSceneUserData::getSceneUserData(&node);
-        if (userData.valid()) {
-            SGSceneUserData* newUserData  = new SGSceneUserData(*userData);
-            newUserData->setVelocity(0);
-            node.setUserData(newUserData);
-        }
-        node.traverse(*this);
-    }
-};
 
 class SGTexCompressionVisitor : public SGTextureStateAttributeVisitor {
 public:
@@ -324,10 +202,8 @@ ReaderWriter::ReadResult
 ModelRegistry::readImage(const string& fileName,
                          const ReaderWriter::Options* opt)
 {
-    ScopedLock<ReentrantMutex> lock(readerMutex);
     CallbackMap::iterator iter
         = imageCallbackMap.find(getFileExtension(fileName));
-    // XXX Workaround for OSG plugin bug
     {
         if (iter != imageCallbackMap.end() && iter->second.valid())
             return iter->second->readImage(fileName, opt);
@@ -416,41 +292,6 @@ osg::Node* OptimizeModelPolicy::optimize(osg::Node* node,
     return node;
 }
 
-osg::Node* DefaultCopyPolicy::copy(osg::Node* model, const string& fileName,
-                                   const osgDB::ReaderWriter::Options* opt)
-{
-    // Add an extra reference to the model stored in the database.
-    // That is to avoid expiring the object from the cache even if it is still
-    // in use. Note that the object cache will think that a model is unused
-    // if the reference count is 1. If we clone all structural nodes here
-    // we need that extra reference to the original object
-    SGDatabaseReference* databaseReference;
-    databaseReference = new SGDatabaseReference(model);
-    CopyOp::CopyFlags flags = CopyOp::DEEP_COPY_ALL;
-    flags &= ~CopyOp::DEEP_COPY_TEXTURES;
-    flags &= ~CopyOp::DEEP_COPY_IMAGES;
-    flags &= ~CopyOp::DEEP_COPY_STATESETS;
-    flags &= ~CopyOp::DEEP_COPY_STATEATTRIBUTES;
-    flags &= ~CopyOp::DEEP_COPY_ARRAYS;
-    flags &= ~CopyOp::DEEP_COPY_PRIMITIVES;
-    // This will safe display lists ...
-    flags &= ~CopyOp::DEEP_COPY_DRAWABLES;
-    flags &= ~CopyOp::DEEP_COPY_SHAPES;
-    osg::Node* res = CopyOp(flags)(model);
-    res->addObserver(databaseReference);
-
-    // Update liveries
-    TextureUpdateVisitor liveryUpdate(opt->getDatabasePathList());
-    res->accept(liveryUpdate);
-
-    // Copy the userdata fields, still sharing the boundingvolumes,
-    // but introducing new data for velocities.
-    UserDataCopyVisitor userDataCopyVisitor;
-    res->accept(userDataCopyVisitor);
-
-    return res;
-}
-
 string OSGSubstitutePolicy::substitute(const string& name,
                                        const ReaderWriter::Options* opt)
 {
@@ -509,10 +350,6 @@ ReaderWriter::ReadResult
 ModelRegistry::readNode(const string& fileName,
                         const ReaderWriter::Options* opt)
 {
-    ScopedLock<ReentrantMutex> lock(readerMutex);
-
-    // XXX Workaround for OSG plugin bug.
-//    Registry* registry = Registry::instance();
     ReaderWriter::ReadResult res;
     CallbackMap::iterator iter
         = nodeCallbackMap.find(getFileExtension(fileName));
@@ -599,7 +436,7 @@ struct ACProcessPolicy {
 };
 
 typedef ModelRegistryCallback<ACProcessPolicy, DefaultCachePolicy,
-                              ACOptimizePolicy, DefaultCopyPolicy,
+                              ACOptimizePolicy,
                               OSGSubstitutePolicy, BuildLeafBVHPolicy>
 ACCallback;
 
index 909c9460247184d6b481d2ab86dbd06b25ee7cfe..94931bca1e287674f2653111efc2dea772d3a285 100644 (file)
@@ -19,8 +19,6 @@
 #ifndef _SG_MODELREGISTRY_HXX
 #define _SG_MODELREGISTRY_HXX 1
 
-#include <OpenThreads/ReentrantMutex>
-
 #include <osg/ref_ptr>
 #include <osg/Node>
 #include <osgDB/FileUtils>
@@ -61,12 +59,12 @@ namespace simgear
 // readNode function is specified as a template with a bunch of
 // pluggable (and predefined) policies.
 template <typename ProcessPolicy, typename CachePolicy, typename OptimizePolicy,
-          typename CopyPolicy, typename SubstitutePolicy, typename BVHPolicy>
+          typename SubstitutePolicy, typename BVHPolicy>
 class ModelRegistryCallback : public osgDB::Registry::ReadFileCallback {
 public:
     ModelRegistryCallback(const std::string& extension) :
         _processPolicy(extension), _cachePolicy(extension),
-        _optimizePolicy(extension), _copyPolicy(extension),
+        _optimizePolicy(extension),
         _substitutePolicy(extension), _bvhPolicy(extension)
     {
     }
@@ -100,9 +98,7 @@ public:
             _bvhPolicy.buildBVH(fileName, optimizedNode.get());
             _cachePolicy.addToCache(fileName, optimizedNode.get());
         }
-        osg::ref_ptr<osg::Node> copyNode;
-        copyNode = _copyPolicy.copy(optimizedNode.get(), fileName, opt);
-        return ReaderWriter::ReadResult(copyNode);
+        return ReaderWriter::ReadResult(optimizedNode);
     }
 protected:
     static osgDB::ReaderWriter::ReadResult
@@ -120,7 +116,6 @@ protected:
     ProcessPolicy _processPolicy;
     CachePolicy _cachePolicy;
     OptimizePolicy _optimizePolicy;
-    CopyPolicy _copyPolicy;
     SubstitutePolicy _substitutePolicy;
     BVHPolicy _bvhPolicy;
     virtual ~ModelRegistryCallback() {}
@@ -169,21 +164,6 @@ struct NoOptimizePolicy {
     }
 };
 
-struct DefaultCopyPolicy {
-    DefaultCopyPolicy(const std::string& extension) {}
-    osg::Node* copy(osg::Node* node, const std::string& fileName,
-                    const osgDB::ReaderWriter::Options* opt);
-};
-
-struct NoCopyPolicy {
-    NoCopyPolicy(const std::string& extension) {}
-    osg::Node* copy(osg::Node* node, const std::string& fileName,
-                    const osgDB::ReaderWriter::Options* opt)
-    {
-        return node;
-    }
-};
-
 struct OSGSubstitutePolicy {
     OSGSubstitutePolicy(const std::string& extension) {}
     std::string substitute(const std::string& name,
@@ -215,7 +195,7 @@ struct NoBuildBVHPolicy {
 };
 
 typedef ModelRegistryCallback<DefaultProcessPolicy, DefaultCachePolicy,
-                              OptimizeModelPolicy, DefaultCopyPolicy,
+                              OptimizeModelPolicy,
                               OSGSubstitutePolicy, BuildLeafBVHPolicy>
 DefaultCallback;
 
@@ -243,15 +223,12 @@ protected:
     CallbackMap imageCallbackMap;
     CallbackMap nodeCallbackMap;
     osg::ref_ptr<DefaultCallback> _defaultCallback;
-    // Protect against simultaneous calls from main thread (MP models)
-    // and pager thread.
-    OpenThreads::ReentrantMutex readerMutex;
 };
 
 // Callback that only loads the file without any caching or
 // postprocessing.
 typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy,
-                              NoOptimizePolicy, NoCopyPolicy,
+                              NoOptimizePolicy,
                               NoSubstitutePolicy, BuildLeafBVHPolicy>
 LoadOnlyCallback;
 
index ca9ac364d27c42e36a5bcd00cf4590a406ecd23c..d726a59c5a6a4a0abb628f9e21a0b4596c8af7b4 100644 (file)
 
 #include <simgear/props/condition.hxx>
 #include <simgear/props/props.hxx>
+#include <simgear/scene/material/Effect.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
+#include <simgear/scene/material/Pass.hxx>
+#include <simgear/scene/material/Technique.hxx>
 #include <simgear/scene/model/model.hxx>
 
+using namespace std;
+
 namespace {
 /**
  * Get a color from properties.
@@ -209,7 +215,25 @@ public:
 
   virtual void apply(osg::Geode& node)
   {
-    maybeGetMaterialValues(node.getStateSet());
+    using namespace simgear;
+    EffectGeode* eg = dynamic_cast<EffectGeode*>(&node);
+    if (eg) {
+      const Effect* effect = eg->getEffect();
+      if (effect)
+        for (vector<osg::ref_ptr<Technique> >::const_iterator itr
+               = effect->techniques.begin(), end = effect->techniques.end();
+             itr != end;
+             ++itr) {
+          const Technique* tniq = itr->get();
+          for (vector<osg::ref_ptr<Pass> >::const_iterator pitr
+                 = tniq->passes.begin(), pend = tniq->passes.end();
+               pitr != pend;
+               ++pitr)
+            maybeGetMaterialValues(pitr->get());
+        }
+    } else {
+      maybeGetMaterialValues(node.getStateSet());
+    }
     int numDrawables = node.getNumDrawables();
     for (int i = 0; i < numDrawables; i++) {
       osg::Geometry* geom = dynamic_cast<osg::Geometry*>(node.getDrawable(i));
@@ -230,18 +254,20 @@ public:
     }
   }
   
-  void maybeGetMaterialValues(osg::StateSet* stateSet)
+  void maybeGetMaterialValues(const osg::StateSet* stateSet)
   {
     if (!stateSet)
       return;
-    osg::Material* nodeMat
-      = dynamic_cast<osg::Material*>(stateSet->getAttribute(osg::StateAttribute::MATERIAL));
+    const osg::Material* nodeMat
+      = dynamic_cast<const osg::Material*>(stateSet
+                                           ->getAttribute(osg::StateAttribute
+                                                          ::MATERIAL));
     if (!nodeMat)
       return;
     material = nodeMat;
   }
 
-  osg::ref_ptr<osg::Material> material;
+  osg::ref_ptr<const osg::Material> material;
   osg::Vec4 ambientDiffuse;
 };
 
index ddd6dabd43c57232bf75be5e5712896f43f5ea6b..faaf42bb23f3ead4667dc06cf2b2a8800858d1ed 100644 (file)
@@ -19,6 +19,8 @@
 #endif
 
 #include <osgDB/ReadFile>
+#include <osgDB/Input>
+#include <osgDB/ParameterOutput>
 
 #include <simgear/debug/logstream.hxx>
 #include <simgear/structure/OSGVersion.hxx>
@@ -70,3 +72,19 @@ void SGPagedLOD::forceLoad(osgDB::DatabasePager *dbp)
                          _readerWriterOptions.get());
 }
 
+bool SGPagedLOD_writeLocalData(const Object& obj, osgDB::Output& fw)
+{
+    return true;
+}
+
+namespace
+{
+osgDB::RegisterDotOsgWrapperProxy sgPagedLODProxy
+(
+    new SGPagedLOD,
+    "simgear::SGPagedLOD",
+    "Object Node LOD PagedLOD SGPagedLOD Group",
+    0,
+    &SGPagedLOD_writeLocalData
+    );
+}
index 31c0c56dddaf42ad27e0589a78851e9a30cc2539..a097b809741160c81183044c7ef9151405a90639 100644 (file)
 #  include <simgear_config.h>
 #endif
 
+#include <algorithm>
+//yuck
+#include <cstring>
+
+#include <boost/bind.hpp>
+
+#include <osg/Geode>
 #include <osg/MatrixTransform>
 #include <osgDB/WriteFile>
 #include <osgDB/Registry>
@@ -43,7 +50,9 @@
 #include "model.hxx"
 #include "SGText.hxx"
 
+using namespace std;
 using namespace simgear;
+using namespace osg;
 
 static osg::Node *
 sgLoad3DModel_internal(const std::string& path,
@@ -105,6 +114,71 @@ private:
     SGSharedPtr<SGCondition> mCondition;
 };
 
+
+// Little helper class that holds an extra reference to a
+// loaded 3d model.
+// Since we clone all structural nodes from our 3d models,
+// the database pager will only see one single reference to
+// top node of the model and expire it relatively fast.
+// We attach that extra reference to every model cloned from
+// a base model in the pager. When that cloned model is deleted
+// this extra reference is deleted too. So if there are no
+// cloned models left the model will expire.
+namespace {
+class SGDatabaseReference : public osg::Observer
+{
+public:
+    SGDatabaseReference(osg::Referenced* referenced) :
+        mReferenced(referenced)
+    { }
+    virtual void objectDeleted(void*)
+    {
+        mReferenced = 0;
+    }
+private:
+    osg::ref_ptr<osg::Referenced> mReferenced;
+};
+
+void makeEffectAnimations(PropertyList& animation_nodes,
+                          PropertyList& effect_nodes)
+{
+    for (PropertyList::iterator itr = animation_nodes.begin();
+         itr != animation_nodes.end();
+         ++itr) {
+        SGPropertyNode* animProp = itr->ptr();
+        SGPropertyNode* typeProp = animProp->getChild("type");
+        if (!typeProp || strcmp(typeProp->getStringValue(), "shader"))
+            continue;
+        SGPropertyNode* shaderProp = animProp->getChild("shader");
+        if (!shaderProp || strcmp(shaderProp->getStringValue(), "chrome"))
+            continue;
+        *itr = 0;
+        SGPropertyNode* textureProp = animProp->getChild("texture");
+        if (!textureProp)
+            continue;
+        SGPropertyNode_ptr effectProp = new SGPropertyNode();
+        makeChild(effectProp.ptr(), "inherits-from")
+            ->setValue("Effects/chrome");
+        SGPropertyNode* paramsProp = makeChild(effectProp.get(), "parameters");
+        makeChild(paramsProp, "chrome-texture")
+            ->setValue(textureProp->getStringValue());
+        PropertyList objectNameNodes = animProp->getChildren("object-name");
+        for (PropertyList::iterator objItr = objectNameNodes.begin(),
+                 end = objectNameNodes.end();
+             objItr != end;
+            ++objItr)
+            effectProp->addChild("object-name")
+                ->setStringValue((*objItr)->getStringValue());
+        effect_nodes.push_back(effectProp);
+    }
+    animation_nodes.erase(remove_if(animation_nodes.begin(),
+                                    animation_nodes.end(),
+                                    !boost::bind(&SGPropertyNode_ptr::valid,
+                                                 _1)),
+                          animation_nodes.end());
+}
+}
+
 static osg::Node *
 sgLoad3DModel_internal(const string &path,
                        const osgDB::ReaderWriter::Options* options_,
@@ -167,6 +241,9 @@ sgLoad3DModel_internal(const string &path,
         SGPropertyNode *mp = props->getNode("multiplay");
         if (mp && prop_root && prop_root->getParent())
             copyProperties(mp, prop_root);
+    } else {
+        SG_LOG(SG_INPUT, SG_DEBUG, "model without wrapper: "
+               << modelpath.str());
     }
 
     osg::ref_ptr<SGReaderWriterXMLOptions> options
@@ -181,10 +258,31 @@ sgLoad3DModel_internal(const string &path,
             texturepath = texturepath.dir();
 
         options->setDatabasePath(texturepath.str());
-        model = osgDB::readNodeFile(modelpath.str(), options.get());
-        if (model == 0)
+        osgDB::ReaderWriter::ReadResult modelResult
+            = osgDB::Registry::instance()->readNode(modelpath.str(),
+                                                    options.get());
+        if (!modelResult.validNode())
             throw sg_io_exception("Failed to load 3D model",
                                   sg_location(modelpath.str()));
+        model = copyModel(modelResult.getNode());
+        // Add an extra reference to the model stored in the database.
+        // That is to avoid expiring the object from the cache even if
+        // it is still in use. Note that the object cache will think
+        // that a model is unused if the reference count is 1. If we
+        // clone all structural nodes here we need that extra
+        // reference to the original object
+        SGDatabaseReference* databaseReference;
+        databaseReference = new SGDatabaseReference(modelResult.getNode());
+        model->addObserver(databaseReference);
+
+        // Update liveries
+        TextureUpdateVisitor liveryUpdate(options->getDatabasePathList());
+        model->accept(liveryUpdate);
+
+        // Copy the userdata fields, still sharing the boundingvolumes,
+        // but introducing new data for velocities.
+        UserDataCopyVisitor userDataCopyVisitor;
+        model->accept(userDataCopyVisitor);
     }
     model->setName(modelpath.str());
 
@@ -224,7 +322,8 @@ sgLoad3DModel_internal(const string &path,
         SGPath submodelpath;
         osg::ref_ptr<osg::Node> submodel;
         string submodelFileName = sub_props->getStringValue("path");
-        if ( submodelFileName.size() > 2 && submodelFileName.substr( 0, 2 ) == "./" ) {
+        if (submodelFileName.size() > 2
+            && !submodelFileName.compare(0, 2, "./" )) {
             submodelpath = modelpath.dir();
             submodelpath.append( submodelFileName.substr( 2 ) );
         } else {
@@ -242,7 +341,7 @@ sgLoad3DModel_internal(const string &path,
             throw;
         }
 
-        osg::ref_ptr<osg::Node> submodel_final=submodel.get();
+        osg::ref_ptr<osg::Node> submodel_final = submodel;
         SGPropertyNode *offs = sub_props->getNode("offsets", false);
         if (offs) {
             osg::Matrix res_matrix;
@@ -264,7 +363,7 @@ sgLoad3DModel_internal(const string &path,
                                offs->getDoubleValue("z-m", 0));
             align->setMatrix(res_matrix*tmat);
             align->addChild(submodel.get());
-            submodel_final=align.get();
+            submodel_final = align;
         }
         submodel_final->setName(sub_props->getStringValue("name", ""));
 
@@ -313,9 +412,15 @@ sgLoad3DModel_internal(const string &path,
                         prop_root,
                         options.get()));
     }
-
-    std::vector<SGPropertyNode_ptr> animation_nodes;
-    animation_nodes = props->getChildren("animation");
+    PropertyList effect_nodes = props->getChildren("effect");
+    PropertyList animation_nodes = props->getChildren("animation");
+    // Some material animations (eventually all) are actually effects.
+    makeEffectAnimations(animation_nodes, effect_nodes);
+    {
+        ref_ptr<Node> modelWithEffects
+            = instantiateEffects(group.get(), effect_nodes, options.get());
+        group = static_cast<Group*>(modelWithEffects.get());
+    }
     for (unsigned i = 0; i < animation_nodes.size(); ++i)
         /// OSGFIXME: duh, why not only model?????
         SGAnimation::animate(group.get(), animation_nodes[i], prop_root,
index 22ba28258a4bdd64209ea18de9af4963cd1be766..80c08be2bfc5b002f68728229b3644471df9b3b4 100644 (file)
 #include <osg/Texture2D>
 #include <osg/Transform>
 #include <osgDB/ReadFile>
+#include <osgDB/Registry>
+#include <osgDB/Input>
+#include <osgDB/ParameterOutput>
+
 
 #include <simgear/math/interpolater.hxx>
 #include <simgear/props/condition.hxx>
@@ -971,6 +975,15 @@ osg::StateSet* getNormalizeStateSet()
 
 class SGDistScaleAnimation::Transform : public osg::Transform {
 public:
+  Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
+  Transform(const Transform& rhs,
+            const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
+    : osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
+      _min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
+      _offset(rhs._offset)
+  {
+  }
+  META_Node(simgear, SGDistScaleAnimation::Transform);
   Transform(const SGPropertyNode* configNode)
   {
     setName(configNode->getStringValue("name", "dist scale animation"));
@@ -1018,6 +1031,16 @@ public:
     return true;
   }
 
+  static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
+  {
+    const Transform& trans = static_cast<const Transform&>(obj);
+    fw.indent() << "center " << trans._center << "\n";
+    fw.indent() << "min_v " << trans._min_v << "\n";
+    fw.indent() << "max_v " << trans._max_v << "\n";
+    fw.indent() << "factor " << trans._factor << "\n";
+    fw.indent() << "offset " << trans._offset << "\n";
+    return true;
+  }
 private:
   double computeScaleFactor(osg::NodeVisitor* nv) const
   {
@@ -1061,6 +1084,17 @@ SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
   return transform;
 }
 
+namespace
+{
+  osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
+  (
+   new SGDistScaleAnimation::Transform,
+   "SGDistScaleAnimation::Transform",
+   "Object Node Transform SGDistScaleAnimation::Transform Group",
+   0,
+   &SGDistScaleAnimation::Transform::writeLocalData
+   );
+}
 \f
 ////////////////////////////////////////////////////////////////////////
 // Implementation of flash animation
@@ -1068,6 +1102,19 @@ SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
 
 class SGFlashAnimation::Transform : public osg::Transform {
 public:
+  Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
+                _max_v(0.0), _two_sides(false)
+  {}
+
+  Transform(const Transform& rhs,
+            const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
+    : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
+      _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
+      _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
+  {
+  }
+  META_Node(simgear, SGFlashAnimation::Transform);
+
   Transform(const SGPropertyNode* configNode)
   {
     setReferenceFrame(RELATIVE_RF);
@@ -1124,6 +1171,21 @@ public:
     return true;
   }
 
+  static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
+  {
+    const Transform& trans = static_cast<const Transform&>(obj);
+    fw.indent() << "center " << trans._center[0] << " "
+                << trans._center[1] << " " << trans._center[2] << " " << "\n";
+    fw.indent() << "axis " << trans._axis[0] << " "
+                << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
+    fw.indent() << "power " << trans._power << " \n";
+    fw.indent() << "min_v " << trans._min_v << "\n";
+    fw.indent() << "max_v " << trans._max_v << "\n";
+    fw.indent() << "factor " << trans._factor << "\n";
+    fw.indent() << "offset " << trans._offset << "\n";
+    fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
+    return true;
+  }
 private:
   double computeScaleFactor(osg::NodeVisitor* nv) const
   {
@@ -1178,13 +1240,29 @@ SGFlashAnimation::createAnimationGroup(osg::Group& parent)
   return transform;
 }
 
+namespace
+{
+  osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
+  (
+   new SGFlashAnimation::Transform,
+   "SGFlashAnimation::Transform",
+   "Object Node Transform SGFlashAnimation::Transform Group",
+   0,
+   &SGFlashAnimation::Transform::writeLocalData
+   );
+}
 \f
 ////////////////////////////////////////////////////////////////////////
-// Implementation of flash animation
+// Implementation of billboard animation
 ////////////////////////////////////////////////////////////////////////
 
 class SGBillboardAnimation::Transform : public osg::Transform {
 public:
+  Transform() : _spherical(true) {}
+  Transform(const Transform& rhs,
+            const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
+    : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
+  META_Node(simgear, SGBillboardAnimation::Transform);
   Transform(const SGPropertyNode* configNode) :
     _spherical(configNode->getBoolValue("spherical", true))
   {
@@ -1221,7 +1299,13 @@ public:
     // Hmm, don't yet know how to get that back ...
     return false;
   }
+  static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
+  {
+    const Transform& trans = static_cast<const Transform&>(obj);
 
+    fw.indent() << (trans._spherical ? "true" : "false") << "\n";
+    return true;
+  }
 private:
   bool _spherical;
 };
@@ -1241,6 +1325,17 @@ SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
   return transform;
 }
 
+namespace
+{
+  osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
+  (
+   new SGBillboardAnimation::Transform,
+   "SGBillboardAnimation::Transform",
+   "Object Node Transform SGBillboardAnimation::Transform Group",
+   0,
+   &SGBillboardAnimation::Transform::writeLocalData
+   );
+}
 \f
 ////////////////////////////////////////////////////////////////////////
 // Implementation of a range animation
index eec16bc4b89fb2036e3b030d591e45a58498f2d5..4ea07fd271ca12cbd35b543e2c8e0728585c3cad 100644 (file)
@@ -174,7 +174,6 @@ public:
   SGDistScaleAnimation(const SGPropertyNode* configNode,
                        SGPropertyNode* modelRoot);
   virtual osg::Group* createAnimationGroup(osg::Group& parent);
-private:
   class Transform;
 };
 
@@ -188,7 +187,7 @@ public:
   SGFlashAnimation(const SGPropertyNode* configNode,
                    SGPropertyNode* modelRoot);
   virtual osg::Group* createAnimationGroup(osg::Group& parent);
-private:
+public:
   class Transform;
 };
 
@@ -202,7 +201,6 @@ public:
   SGBillboardAnimation(const SGPropertyNode* configNode,
                        SGPropertyNode* modelRoot);
   virtual osg::Group* createAnimationGroup(osg::Group& parent);
-private:
   class Transform;
 };
 
index d41529ad5c8b8fbf2cdeeabb508d5003c566afe4..59f33694956d600a31858e939f93bae7fd17a568 100644 (file)
@@ -7,14 +7,28 @@
 #include <simgear_config.h>
 #endif
 
+#include <utility>
+
+#include <boost/foreach.hpp>
+
 #include <osg/ref_ptr>
+#include <osgDB/FileNameUtils>
+#include <osgDB/FileUtils>
+#include <osgDB/ReaderWriter>
 #include <osgDB/ReadFile>
 #include <osgDB/SharedStateManager>
 
 #include <simgear/math/SGMath.hxx>
+#include <simgear/scene/material/Effect.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
 #include <simgear/scene/util/SGSceneFeatures.hxx>
+#include <simgear/scene/util/SGSceneUserData.hxx>
+#include <simgear/scene/util/CopyOp.hxx>
+#include <simgear/scene/util/SplicingVisitor.hxx>
+
 
 #include <simgear/structure/exception.hxx>
+#include <simgear/structure/Singleton.hxx>
 #include <simgear/props/props.hxx>
 #include <simgear/props/props_io.hxx>
 #include <simgear/props/condition.hxx>
@@ -60,4 +74,262 @@ SGLoadTexture2D(bool staticTexture, const std::string& path,
   return texture.release();
 }
 
+namespace simgear
+{
+using namespace std;
+using namespace osg;
+using simgear::CopyOp;
+
+Node* copyModel(Node* model)
+{
+    const CopyOp::CopyFlags flags = (CopyOp::DEEP_COPY_ALL
+                                     & ~CopyOp::DEEP_COPY_TEXTURES
+                                     & ~CopyOp::DEEP_COPY_IMAGES
+                                     & ~CopyOp::DEEP_COPY_STATESETS
+                                     & ~CopyOp::DEEP_COPY_STATEATTRIBUTES
+                                     & ~CopyOp::DEEP_COPY_ARRAYS
+                                     & ~CopyOp::DEEP_COPY_PRIMITIVES
+                                     // This will preserve display lists ...
+                                     & ~CopyOp::DEEP_COPY_DRAWABLES
+                                     & ~CopyOp::DEEP_COPY_SHAPES);
+    return (CopyOp(flags))(model);
+}
+
+TextureUpdateVisitor::TextureUpdateVisitor(const osgDB::FilePathList& pathList) :
+    NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
+    _pathList(pathList)
+{
+}
+
+void TextureUpdateVisitor::apply(Node& node)
+{
+    StateSet* stateSet = cloneStateSet(node.getStateSet());
+    if (stateSet)
+        node.setStateSet(stateSet);
+    traverse(node);
+}
+
+void TextureUpdateVisitor::apply(Drawable& drawable)
+{
+    StateSet* stateSet = cloneStateSet(drawable.getStateSet());
+    if (stateSet)
+        drawable.setStateSet(stateSet);
+}
+
+Texture2D* TextureUpdateVisitor::textureReplace(int unit, const StateAttribute* attr)
+{
+    using namespace osgDB;
+    const Texture2D* texture = dynamic_cast<const Texture2D*>(attr);
+
+    if (!texture)
+        return 0;
+
+    const Image* image = texture->getImage();
+    const string* fullFilePath = 0;
+    if (image) {
+        // The currently loaded file name
+        fullFilePath = &image->getFileName();
+
+    } else {
+        fullFilePath = &texture->getName();
+    }
+    // The short name
+    string fileName = getSimpleFileName(*fullFilePath);
+    if (fileName.empty())
+        return 0;
+    // The name that should be found with the current database path
+    string fullLiveryFile = findFileInPath(fileName, _pathList);
+    // If it is empty or they are identical then there is nothing to do
+    if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
+        return 0;
+    Image* newImage = readImageFile(fullLiveryFile);
+    if (!newImage)
+        return 0;
+    CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
+    Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
+    if (!newTexture) {
+        return 0;
+    } else {
+        newTexture->setImage(newImage);
+        return newTexture;
+    }
+}
+
+StateSet* TextureUpdateVisitor::cloneStateSet(const StateSet* stateSet)
+{
+    typedef std::pair<int, Texture2D*> Tex2D;
+    vector<Tex2D> newTextures;
+    StateSet* result = 0;
+
+    if (!stateSet)
+        return 0;
+    int numUnits = stateSet->getTextureAttributeList().size();
+    if (numUnits > 0) {
+        for (int i = 0; i < numUnits; ++i) {
+            const StateAttribute* attr
+                = stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
+            Texture2D* newTexture = textureReplace(i, attr);
+            if (newTexture)
+                newTextures.push_back(Tex2D(i, newTexture));
+        }
+        if (!newTextures.empty()) {
+            result = static_cast<StateSet*>(stateSet->clone(CopyOp()));
+            for (vector<Tex2D>::iterator i = newTextures.begin();
+                 i != newTextures.end();
+                 ++i) {
+                result->setTextureAttribute(i->first, i->second);
+            }
+        }
+    }
+    return result;
+}
+
+UserDataCopyVisitor::UserDataCopyVisitor() :
+    NodeVisitor(NodeVisitor::NODE_VISITOR,
+                NodeVisitor::TRAVERSE_ALL_CHILDREN)
+{
+}
+
+void UserDataCopyVisitor::apply(Node& node)
+{
+    ref_ptr<SGSceneUserData> userData;
+    userData = SGSceneUserData::getSceneUserData(&node);
+    if (userData.valid()) {
+        SGSceneUserData* newUserData  = new SGSceneUserData(*userData);
+        newUserData->setVelocity(0);
+        node.setUserData(newUserData);
+    }
+    node.traverse(*this);
+}
+
+namespace
+{
+class MakeEffectVisitor : public SplicingVisitor
+{
+public:
+    typedef std::map<string, SGPropertyNode_ptr> EffectMap;
+    using SplicingVisitor::apply;
+    MakeEffectVisitor(const osgDB::ReaderWriter::Options* options = 0)
+        : _options(options)
+    {
+    }
+    virtual void apply(osg::Group& node);
+    virtual void apply(osg::Geode& geode);
+    EffectMap& getEffectMap() { return _effectMap; }
+    const EffectMap& getEffectMap() const { return _effectMap; }
+    void setDefaultEffect(SGPropertyNode* effect)
+    {
+        _currentEffectParent = effect;
+    }
+    SGPropertyNode* getDefaultEffect() { return _currentEffectParent; }
+protected:
+    EffectMap _effectMap;
+    SGPropertyNode_ptr _currentEffectParent;
+    osg::ref_ptr<const osgDB::ReaderWriter::Options> _options;
+};
+
+void MakeEffectVisitor::apply(osg::Group& node)
+{
+    SGPropertyNode_ptr savedEffectRoot;
+    const string& nodeName = node.getName();
+    bool restoreEffect = false;
+    if (!nodeName.empty()) {
+        EffectMap::iterator eitr = _effectMap.find(nodeName);
+        if (eitr != _effectMap.end()) {
+            savedEffectRoot = _currentEffectParent;
+            _currentEffectParent = eitr->second;
+            restoreEffect = true;
+        }
+    }
+    SplicingVisitor::apply(node);
+    // If a new node was created, copy the user data too.
+    ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&node);
+    if (userData.valid() && _childStack.back().back().get() != &node)
+        _childStack.back().back()->setUserData(new SGSceneUserData(*userData));
+    if (restoreEffect)
+        _currentEffectParent = savedEffectRoot;
+}
+
+void MakeEffectVisitor::apply(osg::Geode& geode)
+{
+    if (pushNode(getNewNode(geode)))
+        return;
+    osg::StateSet* ss = geode.getStateSet();
+    if (!ss) {
+        pushNode(&geode);
+        return;
+    }
+    SGPropertyNode_ptr ssRoot = new SGPropertyNode;
+    makeParametersFromStateSet(ssRoot, ss);
+    SGPropertyNode_ptr effectRoot = new SGPropertyNode;
+    effect::mergePropertyTrees(effectRoot, ssRoot, _currentEffectParent);
+    Effect* effect = makeEffect(effectRoot, true, _options);
+    EffectGeode* eg = dynamic_cast<EffectGeode*>(&geode);
+    if (eg) {
+        eg->setEffect(effect);
+    } else {
+        eg = new EffectGeode;
+        eg->setEffect(effect);
+        ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&geode);
+        if (userData.valid())
+            eg->setUserData(new SGSceneUserData(*userData));
+        for (int i = 0; i < geode.getNumDrawables(); ++i)
+            eg->addDrawable(geode.getDrawable(i));
+    }
+    pushResultNode(&geode, eg);
+
+}
+
+}
+
+namespace
+{
+class DefaultEffect : public simgear::Singleton<DefaultEffect>
+{
+public:
+    DefaultEffect()
+    {
+        _effect = new SGPropertyNode;
+        makeChild(_effect.ptr(), "inherits-from")
+            ->setStringValue("Effects/model-default");
+    }
+    virtual ~DefaultEffect() {}
+    SGPropertyNode* getEffect() { return _effect.ptr(); }
+protected:
+    SGPropertyNode_ptr _effect;
+};
+}
+
+ref_ptr<Node> instantiateEffects(osg::Node* modelGroup,
+                                 PropertyList& effectProps,
+                                 const osgDB::ReaderWriter::Options* options)
+{
+    SGPropertyNode_ptr defaultEffectPropRoot;
+    MakeEffectVisitor visitor(options);
+    MakeEffectVisitor::EffectMap& emap = visitor.getEffectMap();
+    for (PropertyList::iterator itr = effectProps.begin(),
+             end = effectProps.end();
+         itr != end;
+        ++itr)
+    {
+        SGPropertyNode_ptr configNode = *itr;
+        std::vector<SGPropertyNode_ptr> objectNames =
+            configNode->getChildren("object-name");
+        SGPropertyNode* defaultNode = configNode->getChild("default");
+        if (defaultNode && defaultNode->getValue<bool>())
+            defaultEffectPropRoot = configNode;
+        BOOST_FOREACH(SGPropertyNode_ptr objNameNode, objectNames) {
+            emap.insert(make_pair(objNameNode->getStringValue(), configNode));
+        }
+        configNode->removeChild("default");
+        configNode->removeChildren("object-name");
+    }
+    if (!defaultEffectPropRoot)
+        defaultEffectPropRoot = DefaultEffect::instance()->getEffect();
+    visitor.setDefaultEffect(defaultEffectPropRoot.ptr());
+    modelGroup->accept(visitor);
+    osg::NodeList& result = visitor.getResults();
+    return ref_ptr<Node>(result[0].get());
+}
+}
 // end of model.cxx
index bef5a32246d65f9d8691f7eef05da70f91a89d0a..49dc0666e1307ee9245ab0e4f93bde30c9624915 100644 (file)
@@ -20,6 +20,8 @@
 #include <osgDB/ReaderWriter>
 
 #include <simgear/misc/sg_path.hxx>
+#include <simgear/props/props.hxx>
+#include <simgear/scene/util/NodeAndDrawableVisitor.hxx>
 
 osg::Texture2D*
 SGLoadTexture2D(bool staticTexture, const std::string& path,
@@ -54,4 +56,64 @@ SGLoadTexture2D(bool staticTexture, const SGPath& path,
                            mipmaplevels);
 }
 
+namespace simgear
+{
+osg::Node* copyModel(osg::Node* model);
+
+// Change the StateSets of a model to hold different textures based on
+// a livery path.
+
+class TextureUpdateVisitor : public NodeAndDrawableVisitor
+{
+public:
+    TextureUpdateVisitor(const osgDB::FilePathList& pathList);
+    virtual void apply(osg::Node& node);
+    virtual void apply(osg::Drawable& drawable);
+    // Copied from Mathias' earlier SGTextureUpdateVisitor
+protected:
+    osg::Texture2D* textureReplace(int unit, const osg::StateAttribute* attr);
+    osg::StateSet* cloneStateSet(const osg::StateSet* stateSet);
+private:
+    osgDB::FilePathList _pathList;
+};
+
+// Create new userdata structs in a copied model.
+// The BVH trees are shared with the original model, but the velocity fields
+// should usually be distinct fields for distinct models.
+class UserDataCopyVisitor : public osg::NodeVisitor
+{
+public:
+    UserDataCopyVisitor();
+    virtual void apply(osg::Node& node);
+};
+
+/**
+ * Transform an OSG subgraph by substituting Effects and EffectGeodes
+ * for osg::Geodes with osg::StateSets. This is only guaranteed to
+ * work for models prouced by the .ac loader.
+ *
+ * returns a copy if any nodes are changed
+ */
+osg::ref_ptr<osg::Node>
+instantiateEffects(osg::Node* model,
+                   PropertyList& effectProps,
+                   const osgDB::ReaderWriter::Options* options);
+
+/**
+ * Transform an OSG subgraph by substituting the Effects and
+ * EffectGeodes for osg::Geodes with osg::StateSets, inheriting from
+ * the default model effect. This is only guaranteed to work for
+ * models prouced by the .ac loader.
+ *
+ * returns a copy if any nodes are changed
+ */
+
+inline osg::ref_ptr<osg::Node>
+instantiateEffects(osg::Node* model,
+                   const osgDB::ReaderWriter::Options* options)
+{
+    PropertyList effectProps;
+    return instantiateEffects(model, effectProps, options);
+}
+}
 #endif // __MODEL_HXX
index a3a779fdff63c03c1e2291bc2773dc4d70d70052..6a13c516fdfbe5a10a15f870665d823a6be70535 100644 (file)
@@ -19,6 +19,8 @@
 #  include <simgear_config.h>
 #endif
 
+#include <boost/algorithm/string.hpp>
+
 #include <osgDB/ReadFile>
 #include <osgDB/WriteFile>
 #include <osgDB/Registry>
@@ -26,6 +28,7 @@
 #include <simgear/constants.h>
 #include <simgear/props/props.hxx>
 #include <simgear/props/props_io.hxx>
+#include <simgear/scene/model/model.hxx>
 #include <simgear/scene/model/ModelRegistry.hxx>
 
 #include "SGPagedLOD.hxx"
@@ -59,6 +62,21 @@ SGModelLib::~SGModelLib()
 {
 }
 
+namespace
+{
+osg::Node* loadFile(const string& path, osgDB::ReaderWriter::Options* options)
+{
+    using namespace osg;
+    using namespace osgDB;
+    ref_ptr<Node> model = readRefNodeFile(path, options);
+    if (!model)
+        return 0;
+    if (boost::iends_with(path, ".ac"))
+        model = instantiateEffects(model.get(), options);
+     return model.release();
+}
+}
+
 osg::Node*
 SGModelLib::loadModel(const string &path,
                        SGPropertyNode *prop_root,
@@ -67,7 +85,7 @@ SGModelLib::loadModel(const string &path,
     osg::ref_ptr<SGReaderWriterXMLOptions> opt = new SGReaderWriterXMLOptions(*(osgDB::Registry::instance()->getOptions()));
     opt->setPropRoot(prop_root);
     opt->setModelData(data);
-    osg::Node *n = readNodeFile(path, opt.get());
+    osg::Node *n = loadFile(path, opt.get());
     if (n && n->getName().empty())
         n->setName("Direct loaded model \"" + path + "\"");
     return n;
@@ -82,7 +100,7 @@ SGModelLib::loadModel(const string &path,
     osg::ref_ptr<SGReaderWriterXMLOptions> opt = new SGReaderWriterXMLOptions(*(osgDB::Registry::instance()->getOptions()));
     opt->setPropRoot(prop_root);
     opt->setLoadPanel(pf);
-    return readNodeFile(path, opt.get());
+    return loadFile(path, opt.get());
 }
 
 osg::Node*
index 10d50acbeb903e5dfbe10512ec9e91b3473baf8c..229710db32ba4fa96dad76aa3ec4bf0a59c5820d 100755 (executable)
@@ -121,7 +121,7 @@ void CloudShaderGeometry::drawImplementation(RenderInfo& renderInfo) const
     }
 }
 
-void CloudShaderGeometry::addSprite(SGVec3f& p, int tx, int ty,
+void CloudShaderGeometry::addSprite(const SGVec3f& p, int tx, int ty,
                                     float w, float h,
                                     float s, float cull, float cloud_height)
 {
index 8a9c31c5172faea4c004a602fcee972fc58df3d7..cd00da8de7139c8c252563b66e67cd00e83b3f7a 100755 (executable)
@@ -69,7 +69,8 @@ class CloudShaderGeometry : public osg::Drawable
         META_Object(flightgear, CloudShaderGeometry);
         
         struct CloudSprite {
-            CloudSprite(SGVec3f& p, int tx, int ty, float w, float h, float s, float ch) :
+            CloudSprite(const SGVec3f& p, int tx, int ty, float w, float h,
+                        float s, float ch) :
                     position(p), texture_index_x(tx), texture_index_y(ty), width(w), height(h), shade(s), cloud_height(ch)
                     { }
         
@@ -106,7 +107,8 @@ class CloudShaderGeometry : public osg::Drawable
             _geometry = geometry;
         }
         
-    void addSprite(SGVec3f& p, int tx, int ty, float w, float h, float s, float cull, float cloud_height);
+    void addSprite(const SGVec3f& p, int tx, int ty, float w, float h,
+                   float s, float cull, float cloud_height);
                 
         osg::ref_ptr<osg::Drawable> _geometry;
 
index 770f4da05db1a70e2bf100537789763d736ce4da..013efe1610c91ca408924a850799f7e6b0c3f0a4 100644 (file)
@@ -71,7 +71,6 @@ float SGCloudField::fieldSize = 50000.0f;
 double SGCloudField::timer_dt = 0.0;
 float SGCloudField::view_distance = 20000.0f;
 sgVec3 SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
-SGCloudField::StateSetMap SGCloudField::cloudTextureMap;
 
 // reposition the cloud layer at the specified origin and orientation
 bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,
index beab904d8aedeeb4c140b5fff7d027214b60d804..1b2f97f187bdaf5ed5a76bd4cdab2de803179e8f 100644 (file)
@@ -137,9 +137,6 @@ public:
         void applyCoverage(void);
         void applyVisRange(void);
         
-        typedef std::map<std::string, osg::ref_ptr<osg::StateSet> > StateSetMap;
-        static StateSetMap cloudTextureMap;
-
         static osg::Fog* getFog()
         {
                 return CloudFog::instance()->fog.get();
index d641b2c92827c8e49f0a8bb17f764775c95a48c7..f4eb8de272c6fb801ca3e4962bf9989798398adf 100644 (file)
@@ -44,6 +44,7 @@
 #include <simgear/math/sg_random.h>
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/misc/PathOptions.hxx>
+#include <simgear/props/props.hxx>
 #include <simgear/scene/model/model.hxx>
 #include <simgear/scene/util/StateAttributeFactory.hxx>
 #include <simgear/scene/util/SGUpdateVisitor.hxx>
 using namespace simgear;
 using namespace osg;
 
-typedef std::map<std::string, osg::ref_ptr<osg::StateSet> > StateSetMap;
+namespace
+{
+typedef std::map<std::string, osg::ref_ptr<Effect> > EffectMap;
+EffectMap effectMap;
+}
 
-StateSetMap cloudTextureMap;
 double SGNewCloud::sprite_density = 1.0;
 
-static char vertexShaderSource[] = 
-    "#version 120\n"
-    "\n"
-    "varying float fogFactor;\n"
-    "attribute vec3 usrAttr1;\n"
-    "attribute vec3 usrAttr2;\n"
-    "float textureIndexX = usrAttr1.r;\n"
-    "float textureIndexY = usrAttr1.g;\n"
-    "float wScale = usrAttr1.b;\n"
-    "float hScale = usrAttr2.r;\n"
-    "float shade = usrAttr2.g;\n"
-    "float cloud_height = usrAttr2.b;\n"
-    "void main(void)\n"
-    "{\n"
-    "  gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndexX, textureIndexY, 0.0, 0.0);\n"
-    "  vec4 ep = gl_ModelViewMatrixInverse * vec4(0.0,0.0,0.0,1.0);\n"
-    "  vec4 l  = gl_ModelViewMatrixInverse * vec4(0.0,0.0,1.0,1.0);\n"
-    "  vec3 u = normalize(ep.xyz - l.xyz);\n"
-// Find a rotation matrix that rotates 1,0,0 into u. u, r and w are
-// the columns of that matrix.
-    "  vec3 absu = abs(u);\n"
-    "  vec3 r = normalize(vec3(-u.y, u.x, 0));\n"
-    "  vec3 w = cross(u, r);\n"
-// Do the matrix multiplication by [ u r w pos]. Assume no
-// scaling in the homogeneous component of pos.
-    "  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
-    "  gl_Position.xyz = gl_Vertex.x * u;\n"
-    "  gl_Position.xyz += gl_Vertex.y * r * wScale;\n"
-    "  gl_Position.xyz += gl_Vertex.z * w * hScale;\n"
-    "  gl_Position.xyz += gl_Color.xyz;\n"
-// Determine a lighting normal based on the vertex position from the
-// center of the cloud, so that sprite on the opposite side of the cloud to the sun are darker.
-    "  float n = dot(normalize(- gl_LightSource[0].position.xyz), normalize(mat3x3(gl_ModelViewMatrix) * (- gl_Position.xyz)));\n"
-// Determine the position - used for fog and shading calculations        
-    "  vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Position);\n"
-    "  float fogCoord = abs(ecPosition.z);\n"
-    "  float fract = smoothstep(0.0, cloud_height, gl_Position.z + cloud_height);\n"
-// Final position of the sprite
-    "  gl_Position = gl_ModelViewProjectionMatrix * gl_Position;\n"
-// Determine the shading of the sprite based on its vertical position and position relative to the sun.
-    "  n = min(smoothstep(-0.5, 0.0, n), fract);\n"
-// Determine the shading based on a mixture from the backlight to the front
-    "  vec4 backlight = gl_LightSource[0].diffuse * shade;\n"
-    "  gl_FrontColor = mix(backlight, gl_LightSource[0].diffuse, n);\n"
-    "  gl_FrontColor += gl_FrontLightModelProduct.sceneColor;\n"
-// As we get within 100m of the sprite, it is faded out. Equally at large distances it also fades out.
-    "  gl_FrontColor.a = min(smoothstep(10.0, 100.0, fogCoord), 1 - smoothstep(15000.0, 20000.0, fogCoord));\n"
-    "  gl_BackColor = gl_FrontColor;\n"
-// Fog doesn't affect clouds as much as other objects.        
-    "  fogFactor = exp( -gl_Fog.density * fogCoord * 0.5);\n"
-    "  fogFactor = clamp(fogFactor, 0.0, 1.0);\n"
-    "}\n";
-
-static char fragmentShaderSource[] = 
-    "uniform sampler2D baseTexture; \n"
-    "varying float fogFactor;\n"
-    "\n"
-    "void main(void)\n"
-    "{\n"
-    "  vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);\n"
-    "  vec4 finalColor = base * gl_Color;\n"
-    "  gl_FragColor.rgb = mix(gl_Fog.color.rgb, finalColor.rgb, fogFactor );\n"
-    "  gl_FragColor.a = mix(0.0, finalColor.a, fogFactor);\n"
-    "}\n";
 
 SGNewCloud::SGNewCloud(string type,
                        const SGPath &tex_path, 
@@ -160,75 +100,24 @@ SGNewCloud::SGNewCloud(string type,
         texture(tex),
         name(type)
 {
-    // Create a new StateSet for the texture, if required.
-    StateSetMap::iterator iter = SGCloudField::cloudTextureMap.find(texture);
-
-    if (iter == SGCloudField::cloudTextureMap.end()) {
-        stateSet = new osg::StateSet;
-                
-        osg::ref_ptr<osgDB::ReaderWriter::Options> options = makeOptionsFromPath(tex_path);
-                
-        osg::Texture2D *tex = new osg::Texture2D;
-        tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP );
-        tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP );
-        tex->setImage(osgDB::readImageFile(texture, options.get()));
-                
-        StateAttributeFactory* attribFactory = StateAttributeFactory::instance();
-        
-        stateSet->setMode(GL_LIGHTING,  osg::StateAttribute::ON);
-        stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
-        
-        // Fog handling
-        stateSet->setAttributeAndModes(attribFactory->getSmoothShadeModel());
-        stateSet->setAttributeAndModes(attribFactory->getStandardBlendFunc());
-
-        stateSet->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
-        stateSet->setRenderBinDetails(osg::StateSet::TRANSPARENT_BIN, "DepthSortedBin");
-        // Turn off z buffer writes. Standard hack for
-        // semi-transparent geometry to avoid sorting / flickering
-        // artifacts.
-        stateSet->setAttributeAndModes(attribFactory->getDepthWritesDisabled());
-        static ref_ptr<AlphaFunc> alphaFunc;
-        static ref_ptr<Program> program;
-        static ref_ptr<Uniform> baseTextureSampler;
-        static ref_ptr<Material> material;
-                  
-        // Generate the shader etc, if we don't already have one.
-        if (!program.valid()) {
-            alphaFunc = new AlphaFunc;
-            alphaFunc->setFunction(AlphaFunc::GREATER,0.01f);
-            program  = new Program;
-            baseTextureSampler = new osg::Uniform("baseTexture", 0);
-            Shader* vertex_shader = new Shader(Shader::VERTEX, vertexShaderSource);
-            program->addShader(vertex_shader);
-            program->addBindAttribLocation("usrAttr1", CloudShaderGeometry::USR_ATTR_1);
-            program->addBindAttribLocation("usrAttr2", CloudShaderGeometry::USR_ATTR_2);
-            Shader* fragment_shader = new Shader(Shader::FRAGMENT, fragmentShaderSource);
-            program->addShader(fragment_shader);
-            material = new Material;
-            // Don´t track vertex color
-            material->setColorMode(Material::OFF);
-            
-            // We don't actually use the material information either - see shader.
-            material->setAmbient(Material::FRONT_AND_BACK,
-                                 Vec4(0.5f, 0.5f, 0.5f, 1.0f));
-            material->setDiffuse(Material::FRONT_AND_BACK,
-                                 Vec4(0.5f, 0.5f, 0.5f, 1.0f));
-        }
-                  
-        stateSet->setAttributeAndModes(alphaFunc.get());
-        stateSet->setAttribute(program.get());
-        stateSet->addUniform(baseTextureSampler.get());
-        stateSet->setMode(GL_VERTEX_PROGRAM_TWO_SIDE, StateAttribute::ON);
-        stateSet->setAttribute(material.get());
-                
-        // Add the newly created texture to the map for use later.
-        SGCloudField::cloudTextureMap.insert(StateSetMap::value_type(texture,  stateSet));
+    // Create a new Effect for the texture, if required.
+    EffectMap::iterator iter = effectMap.find(texture);
+    if (iter == effectMap.end()) {
+        SGPropertyNode_ptr pcloudEffect = new SGPropertyNode;
+        makeChild(pcloudEffect, "inherits-from")->setValue("Effects/cloud");
+        setValue(makeChild(makeChild(makeChild(pcloudEffect, "parameters"),
+                                     "texture"),
+                           "image"),
+                 texture);
+        osg::ref_ptr<osgDB::ReaderWriter::Options> options
+            = makeOptionsFromPath(tex_path);
+        if ((effect = makeEffect(pcloudEffect, true, options)))
+            effectMap.insert(EffectMap::value_type(texture, effect));
     } else {
-        stateSet = iter->second.get();
+        effect = iter->second.get();
     }
-    
-    quad = createOrthQuad(min_sprite_width, min_sprite_height, num_textures_x, num_textures_y);
+    quad = createOrthQuad(min_sprite_width, min_sprite_height,
+                          num_textures_x, num_textures_y);
 }
 
 SGNewCloud::~SGNewCloud() {
@@ -285,9 +174,9 @@ static float Rnd(float n) {
 }
 #endif
 
-osg::ref_ptr<Geode> SGNewCloud::genCloud() {
+osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
     
-    osg::ref_ptr<osg::Geode> geode = new Geode;
+    osg::ref_ptr<EffectGeode> geode = new EffectGeode;
         
     CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y, max_width, max_height);
     
@@ -325,9 +214,7 @@ osg::ref_ptr<Geode> SGNewCloud::genCloud() {
             z = height * cos(elev) * 0.5f;
         }
         
-        SGVec3f *pos = new SGVec3f(x, y, z); 
-
-        // Determine the height and width as scaling factors on the minimum size (used to create the quad).
+        // Determine the height and width as scaling factors on the minimum size (used to create the quad)
         float sprite_width = 1.0f + sg_random() * (max_sprite_width - min_sprite_width) / min_sprite_width;
         float sprite_height = 1.0f + sg_random() * (max_sprite_height - min_sprite_height) / min_sprite_height;
 
@@ -353,7 +240,7 @@ osg::ref_ptr<Geode> SGNewCloud::genCloud() {
         int index_y = (int) floor((z / height + 0.5f) * num_textures_y);
         if (index_y == num_textures_y) { index_y--; }
         
-        sg->addSprite(*pos
+        sg->addSprite(SGVec3f(x, y, z)
                     index_x, 
                     index_y, 
                     sprite_width, 
@@ -366,7 +253,7 @@ osg::ref_ptr<Geode> SGNewCloud::genCloud() {
     sg->setGeometry(quad);
     geode->addDrawable(sg);
     geode->setName("3D cloud");
-    geode->setStateSet(stateSet.get());
+    geode->setEffect(effect.get());
     
     return geode;
 }
index be5549ff1d5c6cf1522d26c0af2af9db2f5ebd81..28f8a9c38f5d2d296e9653dd0a0ad1b9c3d3af33 100644 (file)
@@ -31,6 +31,9 @@
 
 #include "bbcache.hxx"
 
+#include <simgear/scene/material/Effect.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
+
 using std::string;
 using std::vector;
 
@@ -59,7 +62,7 @@ public:
         ~SGNewCloud();
 
         // Generate a Cloud
-        osg::ref_ptr<osg::Geode> genCloud ();
+        osg::ref_ptr<simgear::EffectGeode> genCloud ();
 
         static double getDensity(void)
         {
@@ -90,7 +93,7 @@ private:
         const string texture;
         const string name;
         osg::Geometry* quad;
-        osg::ref_ptr<osg::StateSet> stateSet;
+        osg::ref_ptr<simgear::Effect> effect;
         static double sprite_density;
 
         osg::Geometry* createOrthQuad(float w, float h, int varieties_x, int varieties_y);
index 16602a5a0e4ca3929e75d10bab91f1494a2a745e..ad3a6774769c0cce68afd072016bb434e914d21f 100644 (file)
@@ -76,7 +76,7 @@ SGReaderWriterBTG::readNode(const std::string& fileName,
 
 
 typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy,
-                              NoOptimizePolicy, NoCopyPolicy,
+                              NoOptimizePolicy,
                               NoSubstitutePolicy, BuildGroupBVHPolicy>
 BTGCallback;
 
index 8c639139a1db4e0a65beb0eafcbfb7c2aa939d7d..5f0c163807312c5ee37a8cbbc0153566c6ca5cda 100644 (file)
 
 #include <boost/tuple/tuple_comparison.hpp>
 
-#include <osg/AlphaFunc>
-#include <osg/Billboard>
-#include <osg/BlendFunc>
 #include <osg/Geode>
 #include <osg/Geometry>
-#include <osg/Material>
 #include <osg/Math>
 #include <osg/MatrixTransform>
 #include <osg/Matrix>
-#include <osg/StateSet>
-#include <osg/Texture2D>
-#include <osg/TexEnv>
 
 #include <osgDB/ReadFile>
 #include <osgDB/FileUtils>
@@ -45,6 +38,9 @@
 #include <simgear/debug/logstream.hxx>
 #include <simgear/math/sg_random.h>
 #include <simgear/misc/sg_path.hxx>
+#include <simgear/scene/material/Effect.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
+#include <simgear/props/props.hxx>
 #include <simgear/scene/util/QuadTreeBuilder.hxx>
 #include <simgear/scene/util/RenderConstants.hxx>
 #include <simgear/scene/util/StateAttributeFactory.hxx>
@@ -167,14 +163,17 @@ Geometry* createTreeGeometry(float width, float height, int varieties)
     (*rotation)[1] = PI_2;
     quadGeom->setFogCoordArray(rotation);
     quadGeom->setFogCoordBinding(Geometry::BIND_PER_PRIMITIVE_SET);
+    // The primitive sets render the same geometry, but the second
+    // will rotated 90 degrees by the vertex shader, which uses the
+    // fog coordinate as a rotation.
     for (int i = 0; i < 2; ++i)
         quadGeom->addPrimitiveSet(new DrawArrays(PrimitiveSet::QUADS));
     return quadGeom;
 }
 
-Geode* createTreeGeode(float width, float height, int varieties)
+EffectGeode* createTreeGeode(float width, float height, int varieties)
 {
-    Geode* result = new Geode;
+    EffectGeode* result = new EffectGeode;
     result->addDrawable(createTreeGeometry(width, height, varieties));
     return result;
 }
@@ -204,67 +203,31 @@ void addTreeToLeafGeode(Geode* geode, const SGVec3f& p)
     }
 }
 
- static char vertexShaderSource[] = 
-    "varying float fogFactor;\n"
-    "\n"
-    "void main(void)\n"
-    "{\n"
-    "  float numVarieties = gl_Normal.z;\n"
-    "  float texFract = floor(fract(gl_MultiTexCoord0.x) * numVarieties) / numVarieties;\n"
-    "  texFract += floor(gl_MultiTexCoord0.x) / numVarieties;\n"
-    "  float sr = sin(gl_FogCoord);\n"
-    "  float cr = cos(gl_FogCoord);\n"
-    "  gl_TexCoord[0] = vec4(texFract, gl_MultiTexCoord0.y, 0.0, 0.0);\n"
-    // scaling
-    "  vec3 position = gl_Vertex.xyz * gl_Normal.xxy;\n"
-    // Rotation of the generic quad to specific one for the tree.
-    "  position.xy = vec2(dot(position.xy, vec2(cr, sr)), dot(position.xy, vec2(-sr, cr)));\n"
-    "  position = position + gl_Color.xyz;\n"
-    "  gl_Position   = gl_ModelViewProjectionMatrix * vec4(position,1.0);\n"
-    "  vec3 ecPosition = vec3(gl_ModelViewMatrix * vec4(position, 1.0));\n"
-    "  float n = dot(normalize(gl_LightSource[0].position.xyz), normalize(-ecPosition));\n"
-    "  vec3 diffuse = gl_FrontMaterial.diffuse.rgb * max(0.1, n);\n"
-    "  vec4 ambientColor = gl_FrontLightModelProduct.sceneColor + gl_LightSource[0].ambient * gl_FrontMaterial.ambient;\n"
-    "  gl_FrontColor = ambientColor + gl_LightSource[0].diffuse * vec4(diffuse, 1.0);\n"
-    "  gl_BackColor = gl_FrontColor;\n"
-    "  float fogCoord = abs(ecPosition.z);\n"
-    "  fogFactor = exp( -gl_Fog.density * gl_Fog.density * fogCoord * fogCoord);\n"
-    "  fogFactor = clamp(fogFactor, 0.0, 1.0);\n"
-    "}\n";
+typedef std::map<std::string, osg::ref_ptr<Effect> > EffectMap;
 
-static char fragmentShaderSource[] = 
-    "uniform sampler2D baseTexture; \n"
-    "varying float fogFactor;\n"
-    "\n"
-    "void main(void) \n"
-    "{ \n"
-    "  vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);\n"
-    "  vec4 finalColor = base * gl_Color;\n"
-    "  gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor );\n"
-    "}\n";
-
-typedef std::map<std::string, osg::ref_ptr<StateSet> > StateSetMap;
-
-static StateSetMap treeTextureMap;
+static EffectMap treeEffectMap;
 
 // Helper classes for creating the quad tree
 namespace
 {
 struct MakeTreesLeaf
 {
-    MakeTreesLeaf(float range, int varieties, float width, float height) :
+    MakeTreesLeaf(float range, int varieties, float width, float height,
+        Effect* effect) :
         _range(range),  _varieties(varieties),
-        _width(width), _height(height) {}
+        _width(width), _height(height), _effect(effect) {}
 
     MakeTreesLeaf(const MakeTreesLeaf& rhs) :
         _range(rhs._range),
-        _varieties(rhs._varieties), _width(rhs._width), _height(rhs._height)
+        _varieties(rhs._varieties), _width(rhs._width), _height(rhs._height),
+        _effect(rhs._effect)
     {}
 
     LOD* operator() () const
     {
         LOD* result = new LOD;
-        Geode* geode = createTreeGeode(_width, _height, _varieties);
+        EffectGeode* geode = createTreeGeode(_width, _height, _varieties);
+        geode->setEffect(_effect.get());
         result->addChild(geode, 0, _range);
         return result;
     }
@@ -272,6 +235,7 @@ struct MakeTreesLeaf
     int _varieties;
     float _width;
     float _height;
+    ref_ptr<Effect> _effect;
 };
 
 struct AddTreesLeafObject
@@ -317,51 +281,19 @@ osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform)
     // Set up some shared structures.
     ref_ptr<Group> group;
 
-    osg::StateSet* stateset = 0;
-    StateSetMap::iterator iter = treeTextureMap.find(forest.texture);
-    if (iter == treeTextureMap.end()) {
-        osg::Texture2D *tex = new osg::Texture2D;
-        tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP );
-        tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP );
-        tex->setImage(osgDB::readImageFile(forest.texture));
-
-        static ref_ptr<AlphaFunc> alphaFunc;
-        static ref_ptr<Program> program;
-        static ref_ptr<Uniform> baseTextureSampler;
-        static ref_ptr<Material> material;
-    
-        stateset = new osg::StateSet;
-        stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
-        stateset->setRenderBinDetails(RANDOM_OBJECTS_BIN, "DepthSortedBin");
-        if (!program.valid()) {
-            alphaFunc = new AlphaFunc;
-            alphaFunc->setFunction(AlphaFunc::GEQUAL,0.33f);
-            program  = new Program;
-            baseTextureSampler = new osg::Uniform("baseTexture", 0);
-            Shader* vertex_shader = new Shader(Shader::VERTEX,
-                                               vertexShaderSource);
-            program->addShader(vertex_shader);
-            Shader* fragment_shader = new Shader(Shader::FRAGMENT,
-                                                 fragmentShaderSource);
-            program->addShader(fragment_shader);
-            material = new Material;
-            // Don´t track vertex color
-            material->setColorMode(Material::OFF);
-            material->setAmbient(Material::FRONT_AND_BACK,
-                                 Vec4(1.0f, 1.0f, 1.0f, 1.0f));
-            material->setDiffuse(Material::FRONT_AND_BACK,
-                                 Vec4(1.0f, 1.0f, 1.0f, 1.0f));
-        }
-        stateset->setAttributeAndModes(alphaFunc.get());
-        stateset->setAttribute(program.get());
-        stateset->addUniform(baseTextureSampler.get());
-        stateset->setMode(GL_VERTEX_PROGRAM_TWO_SIDE, StateAttribute::ON);
-        stateset->setAttribute(material.get());
-
-        treeTextureMap.insert(StateSetMap::value_type(forest.texture,
-                                                      stateset));
+    Effect* effect = 0;
+    EffectMap::iterator iter = treeEffectMap.find(forest.texture);
+    if (iter == treeEffectMap.end()) {
+        SGPropertyNode_ptr effectProp = new SGPropertyNode;
+        makeChild(effectProp, "inherits-from")->setStringValue("Effects/tree");
+        SGPropertyNode* params = makeChild(effectProp, "parameters");
+        // emphasize n = 0
+        params->getChild("texture", 0, true)->getChild("image", 0, true)
+            ->setStringValue(forest.texture);
+        effect = makeEffect(effectProp, true);
+        treeEffectMap.insert(EffectMap::value_type(forest.texture, effect));
     } else {
-        stateset = iter->second.get();
+        effect = iter->second.get();
     }
     // Now, create a quadtree for the forest.
     {
@@ -369,7 +301,7 @@ osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform)
             quadtree(GetTreeCoord(), AddTreesLeafObject(),
                      SG_TREE_QUAD_TREE_DEPTH,
                      MakeTreesLeaf(forest.range, forest.texture_varieties,
-                                   forest.width, forest.height));
+                                   forest.width, forest.height, effect));
         // Transform tree positions from the "geocentric" positions we
         // get from the scenery polys into the local Z-up coordinate
         // system.
@@ -384,7 +316,6 @@ osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform)
     MatrixTransform* mt = new MatrixTransform(transform);
     for (size_t i = 0; i < group->getNumChildren(); ++i)
         mt->addChild(group->getChild(i));
-    mt->setStateSet(stateset);
     return mt;
 }
 
index fab67b2fcedd11bb4f67b625ed1de7d2a975ac24..11d69e12f7fafa64f66a66cf1aaa816843cc6bff 100644 (file)
@@ -64,3 +64,10 @@ osg::Node* sgGetRandomModel(SGMatModel *obj) {
    return obj->get_random_model( root_props );
  }
 
+namespace simgear
+{
+SGPropertyNode* getPropertyRoot()
+{
+    return root_props;
+}
+}
index e5a870e3ae4bc03604c2c80d72efa818b3e8ecea..ab0a6a559c2e64e41d6a4716f23d35efb267263c 100644 (file)
@@ -44,4 +44,12 @@ void sgUserDataInit(SGPropertyNode *p);
  */
 osg::Node* sgGetRandomModel(SGMatModel *obj);
 
+namespace simgear
+{
+/**
+ * Get the property root for the simulation
+ */
+SGPropertyNode* getPropertyRoot();
+}
+
 #endif // _SG_USERDATA_HXX
diff --git a/simgear/scene/util/CopyOp.cxx b/simgear/scene/util/CopyOp.cxx
new file mode 100644 (file)
index 0000000..48a5bd0
--- /dev/null
@@ -0,0 +1,40 @@
+// CopyOp.cxx - Simgear CopyOp for copying our own classes
+//
+// Copyright (C) 2009  Tim Moore timoore@redhat.com
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// 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 GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA  02111-1307, USA.
+
+#include "CopyOp.hxx"
+
+#include <simgear/scene/material/Effect.hxx>
+#include <simgear/scene/material/Technique.hxx>
+
+namespace simgear
+{
+osg::Object* CopyOp::operator()(const osg::Object* obj) const
+{
+    if (dynamic_cast<const Effect*>(obj)
+        || dynamic_cast<const Technique*>(obj)) {
+        if (_flags & DEEP_COPY_STATESETS)
+            return obj->clone(*this);
+        else
+            return const_cast<osg::Object*>(obj);
+    }
+    else {
+        return osg::CopyOp::operator()(obj);
+    }
+}
+}
diff --git a/simgear/scene/util/CopyOp.hxx b/simgear/scene/util/CopyOp.hxx
new file mode 100644 (file)
index 0000000..891b5a0
--- /dev/null
@@ -0,0 +1,37 @@
+// CopyOp.hxx - Simgear CopyOp for copying our own classes
+//
+// Copyright (C) 2009  Tim Moore timoore@redhat.com
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// 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 GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA  02111-1307, USA.
+
+#ifndef SIMGEAR_COPYOP_HXX
+#define SIMGEAR_COPYOP_HXX 1
+#include <osg/CopyOp>
+
+namespace simgear
+{
+class CopyOp : public osg::CopyOp
+{
+public:
+    CopyOp(osg::CopyOp::CopyFlags flags = osg::CopyOp::SHALLOW_COPY)
+        : osg::CopyOp(flags)
+    {
+    }
+    using osg::CopyOp::operator();
+    virtual osg::Object* operator()(const osg::Object* obj) const;
+};
+}
+#endif
index 567ec69708dbd9c223f6da3c72775e7cc833f43c..376f72f93cc846e19069daa1af4d869dd308019e 100644 (file)
@@ -14,11 +14,14 @@ include_HEADERS = \
        SGStateAttributeVisitor.hxx \
        SGTextureStateAttributeVisitor.hxx \
        SGUpdateVisitor.hxx \
+       CopyOp.hxx \
        NodeAndDrawableVisitor.hxx \
        PrimitiveUtils.hxx \
        QuadTreeBuilder.hxx \
        RenderConstants.hxx \
+       SplicingVisitor.hxx \
        StateAttributeFactory.hxx \
+       UpdateOnceCallback.hxx \
        VectorArrayAdapter.hxx
 
 
@@ -28,9 +31,12 @@ libsgutil_a_SOURCES = \
        SGSceneUserData.cxx \
        SGStateAttributeVisitor.cxx \
        SGTextureStateAttributeVisitor.cxx \
+       CopyOp.cxx \
        NodeAndDrawableVisitor.cxx \
        PrimitiveUtils.cxx \
+       SplicingVisitor.cxx \
        StateAttributeFactory.cxx \
-       QuadTreeBuilder.cxx
+       QuadTreeBuilder.cxx \
+       UpdateOnceCallback.cxx
 
 INCLUDES = -I$(top_srcdir)
index c08fce0e824c8a30ef7d7abf1a91e9a21039379b..2388f09daf8906ebc7064e9d59bf0a46db681054 100644 (file)
 #  include <simgear_config.h>
 #endif
 
+#include <osgDB/Registry>
+#include <osgDB/Input>
+#include <osgDB/ParameterOutput>
+
 #include "SGSceneUserData.hxx"
 
 SGSceneUserData*
@@ -86,3 +90,38 @@ SGSceneUserData::addPickCallback(SGPickCallback* pickCallback)
     return;
   _pickCallbacks.push_back(pickCallback);
 }
+
+bool SGSceneUserData_writeLocalData(const osg::Object& obj, osgDB::Output& fw)
+{
+    const SGSceneUserData& data = static_cast<const SGSceneUserData&>(obj);
+
+    unsigned numPickCallbacks = data.getNumPickCallbacks();
+    if (numPickCallbacks > 0)
+        fw.indent() << "num_pickCallbacks " << numPickCallbacks << "\n";
+    if (data.getBVHNode())
+        fw.indent() << "hasBVH true\n";
+    const SGSceneUserData::Velocity* vel = data.getVelocity();
+    if (vel) {
+        fw.indent() << "velocity {\n";
+        fw.moveIn();
+        fw.indent() << "linear " << vel->linear << "\n";
+        fw.indent() << "angular " << vel->angular << "\n";
+        fw.indent() << "referenceTime " << vel->referenceTime << "\n";
+        fw.indent() << "id " << static_cast<unsigned>(vel->id) << "\n";
+        fw.moveOut();
+        fw.indent() << "}\n";
+    }
+    return true;
+}
+
+namespace
+{
+osgDB::RegisterDotOsgWrapperProxy SGSceneUserDataProxy
+(
+    new SGSceneUserData,
+    "simgear::SGSceneUserData",
+    "Object simgear::SGSceneUserData",
+    0,
+    &SGSceneUserData_writeLocalData
+    );
+}
index 4f3f38fc6d2aa48ee413e83cbec541cb9ebfaf3b..1296e9c348f321719d7bcad89736c7b918c49a11 100644 (file)
 #define SG_SCENE_USERDATA_HXX
 
 #include <vector>
-#include <osg/Referenced>
 #include <osg/Node>
+#include <osg/Object>
 #include <simgear/scene/bvh/BVHNode.hxx>
 #include "SGPickCallback.hxx"
 
-class SGSceneUserData : public osg::Referenced {
+class SGSceneUserData : public osg::Object {
 public:
+  META_Object(simgear, SGSceneUserData);
+  SGSceneUserData() {}
+  SGSceneUserData(const SGSceneUserData& rhs,
+                  const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
+    : _bvhNode(rhs._bvhNode), _velocity(rhs._velocity),
+      _pickCallbacks(rhs._pickCallbacks)
+  {
+  }
   static SGSceneUserData* getSceneUserData(osg::Node* node);
   static const SGSceneUserData* getSceneUserData(const osg::Node* node);
   static SGSceneUserData* getOrCreateSceneUserData(osg::Node* node);
diff --git a/simgear/scene/util/SplicingVisitor.cxx b/simgear/scene/util/SplicingVisitor.cxx
new file mode 100644 (file)
index 0000000..6655837
--- /dev/null
@@ -0,0 +1,113 @@
+#include "SplicingVisitor.hxx"
+
+namespace simgear
+{
+using namespace osg;
+
+SplicingVisitor::SplicingVisitor()
+    : NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN)
+{
+    _childStack.push_back(NodeList());
+}
+
+void SplicingVisitor::reset()
+{
+    _childStack.clear();
+    NodeVisitor::reset();
+}
+
+NodeList SplicingVisitor::traverse(Node& node)
+{
+    NodeList result;
+    _childStack.push_back(NodeList());
+    NodeVisitor::traverse(node);
+    result = _childStack.back();
+    _childStack.pop_back();
+    return result;
+}
+void SplicingVisitor::apply(Node& node)
+{
+    NodeVisitor::traverse(node);
+    pushNode(&node);
+}
+
+void SplicingVisitor::apply(Group& node)
+{
+    if (pushNode(getNewNode(node)))
+        return;
+    pushResultNode(&node, &node, traverse(node));
+}
+
+Group* SplicingVisitor::pushResultNode(Group* node, Group* newNode,
+                                       const NodeList& children)
+{
+    ref_ptr<Group> result;
+    if (node == newNode) {
+        result = copyIfNeeded(*node, children);
+    } else {
+        result = newNode;
+        for (NodeList::const_iterator itr = children.begin(), end = children.end();
+             itr != end;
+             ++itr)
+            result->addChild(itr->get());
+    }
+    _childStack.back().push_back(result);
+    recordNewNode(node, result);
+    return result;
+}
+
+Node* SplicingVisitor::pushResultNode(Node* node, Node* newNode)
+{
+    _childStack.back().push_back(newNode);
+    recordNewNode(node, newNode);
+    return newNode;
+}
+
+Node* SplicingVisitor::pushNode(Node* node)
+{
+    if (node)
+        _childStack.back().push_back(node);
+    return node;
+}
+
+Node* SplicingVisitor::getResult()
+{
+    NodeList& top = _childStack.at(0);
+    if (top.empty()) {
+        return 0;
+    } else if (top.size() == 1) {
+        return top[0].get();
+    } else {
+        Group* result = new Group;
+        for (NodeList::iterator itr = top.begin(), end = top.end();
+             itr != end;
+             ++itr)
+            result->addChild(itr->get());
+        return result;
+    }
+}
+
+Node* SplicingVisitor::getNewNode(osg::Node* node)
+{
+    ref_ptr<Node> tmpPtr(node);
+    NodeMap::iterator itr;
+    try {
+        itr = _visited.find(tmpPtr);
+    }
+    catch (...) {
+        tmpPtr.release();
+        throw;
+    }
+    if (itr == _visited.end())
+        return 0;
+    else
+        return itr->second.get();
+}
+
+bool SplicingVisitor::recordNewNode(osg::Node* oldNode, osg::Node* newNode)
+{
+    ref_ptr<Node> oldTmp(oldNode);
+    ref_ptr<Node> newTmp(newNode);
+    return _visited.insert(std::make_pair(oldTmp, newTmp)).second;
+}
+}
diff --git a/simgear/scene/util/SplicingVisitor.hxx b/simgear/scene/util/SplicingVisitor.hxx
new file mode 100644 (file)
index 0000000..8757e94
--- /dev/null
@@ -0,0 +1,93 @@
+#ifndef SIMGEAR_SPLICINGVISITOR_HXX
+#define SIMGEAR_SPLICINGVISITOR_HXX 1
+
+#include <cstddef>
+#include <map>
+#include <vector>
+#include <osg/NodeVisitor>
+#include <osg/Group>
+
+namespace simgear
+{
+class SplicingVisitor : public osg::NodeVisitor
+{
+public:
+    META_NodeVisitor(simgear,SplicingVisitor);
+    SplicingVisitor();
+    virtual ~SplicingVisitor() {}
+    virtual void reset();
+    osg::NodeList traverse(osg::Node& node);
+    using osg::NodeVisitor::apply;
+    virtual void apply(osg::Node& node);
+    virtual void apply(osg::Group& node);
+    template<typename T>
+    static T* copyIfNeeded(T& node, const osg::NodeList& children);
+    template<typename T>
+    static T* copy(T& node, const osg::NodeList& children);
+    /**
+     * Push the result of processing this node.
+     *
+     * If the child list is not equal to the node's current children,
+     * make a copy of the node. Record the (possibly new) node which
+     * should be the returned result if the node is visited again.
+     */
+    osg::Group* pushResultNode(osg::Group* node, osg::Group* newNode,
+                               const osg::NodeList& children);
+    /**
+     * Push the result of processing this node.
+     *
+     * Record the (possibly new) node which should be the returned
+     * result if the node is visited again.
+     */
+    osg::Node* pushResultNode(osg::Node* node, osg::Node* newNode);
+    /**
+     * Push some node onto the list of result nodes.
+     */
+    osg::Node* pushNode(osg::Node* node);
+    osg::Node* getResult();
+    osg::Node* getNewNode(osg::Node& node)
+    {
+        return getNewNode(&node);
+    }
+    osg::Node* getNewNode(osg::Node* node);
+    bool recordNewNode(osg::Node* oldNode, osg::Node* newNode);
+    osg::NodeList& getResults() { return _childStack.back(); }
+protected:
+    std::vector<osg::NodeList> _childStack;
+    typedef std::map<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::Node> > NodeMap;
+    NodeMap _visited;
+};
+
+template<typename T>
+T* SplicingVisitor::copyIfNeeded(T& node, const osg::NodeList& children)
+{
+    bool copyNeeded = false;
+    if (node.getNumChildren() == children.size()) {
+        for (std::size_t i = 0; i < children.size(); ++i)
+            if (node.getChild(i) != children[i].get()) {
+                copyNeeded = true;
+                break;
+            }
+    } else {
+        copyNeeded = true;
+    }
+    if (copyNeeded)
+        return copy(node, children);
+    else
+        return &node;
+}
+
+template<typename T>
+T* SplicingVisitor::copy(T& node, const osg::NodeList& children)
+{
+    T* result = osg::clone(&node, osg::CopyOp::SHALLOW_COPY);
+    result->removeChildren(0, result->getNumChildren());
+    for (osg::NodeList::const_iterator itr = children.begin(),
+             end = children.end();
+         itr != end;
+         ++itr)
+        result->addChild(itr->get());
+    return result;
+}
+}
+#endif
index 6e121c073d0d7765c102bccddfc32311ccbffe5d..76124da06b1b1e8df83ca8f024928d979f91ff38 100644 (file)
@@ -64,6 +64,17 @@ StateAttributeFactory::StateAttributeFactory()
     _whiteTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
     _whiteTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
     _whiteTexture->setDataVariance(osg::Object::STATIC);
+    // And now the transparent texture
+    dummyImage = new osg::Image;
+    dummyImage->allocateImage(1, 1, 1, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE);
+    imageBytes = dummyImage->data(0, 0);
+    imageBytes[0] = 255;
+    imageBytes[1] = 0;
+    _transparentTexture = new osg::Texture2D;
+    _transparentTexture->setImage(dummyImage);
+    _transparentTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
+    _transparentTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
+    _transparentTexture->setDataVariance(osg::Object::STATIC);
     _white = new Vec4Array(1);
     (*_white)[0].set(1.0f, 1.0f, 1.0f, 1.0f);
     _white->setDataVariance(Object::STATIC);
index 18b1a59d811099aff1671d6071d6866c53ebaf2c..0076fa510e6befa8f07c37edc2cadf2e6a95eeab 100644 (file)
@@ -57,6 +57,11 @@ public:
     osg::Texture2D* getWhiteTexture() { return _whiteTexture.get(); }
     // White color
     osg::Vec4Array* getWhiteColor() {return _white.get(); }
+    // A white, completely transparent texture
+    osg::Texture2D* getTransparentTexture()
+    {
+        return _transparentTexture.get();
+    }
     // cull front and back facing polygons
     osg::CullFace* getCullFaceFront() { return _cullFaceFront.get(); }
     osg::CullFace* getCullFaceBack() { return _cullFaceBack.get(); }
@@ -70,6 +75,7 @@ protected:
     osg::ref_ptr<osg::BlendFunc> _standardBlendFunc;
     osg::ref_ptr<osg::TexEnv> _standardTexEnv;
     osg::ref_ptr<osg::Texture2D> _whiteTexture;
+    osg::ref_ptr<osg::Texture2D> _transparentTexture;
     osg::ref_ptr<osg::Vec4Array> _white;
     osg::ref_ptr<osg::CullFace> _cullFaceFront;
     osg::ref_ptr<osg::CullFace> _cullFaceBack;
diff --git a/simgear/scene/util/UpdateOnceCallback.cxx b/simgear/scene/util/UpdateOnceCallback.cxx
new file mode 100644 (file)
index 0000000..7130b50
--- /dev/null
@@ -0,0 +1,39 @@
+// UpdateOnceCallback.hxx
+//
+// Copyright (C) 2009  Tim Moore timoore@redhat.com
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// 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 GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA  02111-1307, USA.
+
+#include "UpdateOnceCallback.hxx"
+
+#include <osg/Node>
+
+namespace simgear
+{
+using namespace osg;
+
+void UpdateOnceCallback::operator()(Node* node, NodeVisitor* nv)
+{
+    doUpdate(node, nv);
+    node->removeUpdateCallback(this);
+    // The callback could be deleted now.
+}
+
+void UpdateOnceCallback::doUpdate(Node* node, NodeVisitor* nv)
+{
+    traverse(node, nv);
+}
+}
diff --git a/simgear/scene/util/UpdateOnceCallback.hxx b/simgear/scene/util/UpdateOnceCallback.hxx
new file mode 100644 (file)
index 0000000..0403484
--- /dev/null
@@ -0,0 +1,44 @@
+// UpdateOnceCallback.hxx
+//
+// Copyright (C) 2009  Tim Moore timoore@redhat.com
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// 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 GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA  02111-1307, USA.
+
+#ifndef SIMGEAR_UPDATEONCECALLBACK_HXX
+#define SIMGEAR_UPDATEONCECALLBACK_HXX 1
+#include <osg/NodeCallback>
+
+namespace simgear
+{
+class UpdateOnceCallback : public osg::NodeCallback
+{
+public:
+    UpdateOnceCallback() {}
+    UpdateOnceCallback(const UpdateOnceCallback& nc, const osg::CopyOp& copyop)
+        : osg::NodeCallback(nc, copyop)
+    {
+    }
+
+    META_Object(simgear,UpdateOnceCallback);
+
+    virtual void doUpdate(osg::Node* node, osg::NodeVisitor* nv);
+    /**
+     * Do not override; use doUpdate instead!
+     */
+    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+};
+}
+#endif
index 21c5d698bbce3b1bc00f643cfc49917dba6220b4..557339d0ae9d73039425ca3b630fc59130c36bea 100644 (file)
@@ -20,8 +20,9 @@
 #ifndef SIMGEAR_OSGUTILS_HXX
 #define SIMGEAR_OSGUTILS_HXX 1
 
-#include <boost/iterator/iterator_facade.hpp>
 #include <osg/CopyOp>
+#include <osg/StateAttribute>
+#include <osg/StateSet>
 
 namespace simgear
 {
@@ -99,59 +100,280 @@ T* clone_ref(const osg::ref_ptr<T>& object,
     return static_cast<T*>(object->clone(copyop));
 }
 
-template<typename Container>
-class BackRefInsertIterator
-    : public boost::iterator_facade<BackRefInsertIterator<Container>,
-                                    BackRefInsertIterator<Container>,
-                                    boost::incrementable_traversal_tag
-                                    >
+}
+
+namespace osg
 {
-public:
-    typedef typename Container::value_type::element_type* PtrType;
-    BackRefInsertIterator() : _container(0) {}
-    explicit BackRefInsertIterator(Container& container)
-        : _container(&container)
-    {
-    }
+class AlphaFunc;
+class BlendColor;
+class BlendEquation;
+class BlendFunc;
+class ClampColor;
+class ColorMask;
+class ColorMatrix;
+class CullFace;
+class Depth;
+class Fog;
+class FragmentProgram;
+class FrontFace;
+class LightModel;
+class LineStipple;
+class LineWidth;
+class LogicOp;
+class Material;
+class Multisample;
+class Point;
+class PointSprite;
+class PolygonMode;
+class PolygonOffset;
+class PolygonStipple;
+class Program;
+class Scissor;
+class ShadeModel;
+class Stencil;
+class StencilTwoSided;
+class TexEnv;
+class TexEnvCombine;
+class TexEnvFilter;
+class TexGen;
+class TexMat;
+class Texture1D;
+class Texture2D;
+class Texture2DArray;
+class Texture3D;
+class TextureCubeMap;
+class TextureRectangle;
+class VertexProgram;
+class Viewport;
+}
 
-    BackRefInsertIterator&
-    operator=(const PtrType ptr)
-    {
-        _container->push_back(ptr);
-        return *this;
-    }
-    
-private:
-    friend class boost::iterator_core_access;
+namespace simgear
+{
+namespace osgutils
+{
+using namespace osg;
 
-    void increment()
-    {
-    }
-    
-    BackRefInsertIterator& dereference()
-    {
-        return *this;
-    }
+template<StateAttribute::Type T>
+struct TypeHolder
+{
+    static const StateAttribute::Type type = T;
+};
 
-    BackRefInsertIterator& dereference() const
-    {
-        return const_cast<BackRefInsertIterator&>(*this);
-    }
+template<typename AT> struct AttributeType;
+template<typename AT> struct TexAttributeType;
 
-    bool equal(const BackRefInsertIterator& rhs)
-    {
-        return _container == rhs._container;
-    }
-    
-    Container* _container;
-};
+template<>
+struct AttributeType<AlphaFunc>
+    : public TypeHolder<StateAttribute::ALPHAFUNC>
+{};
+
+template<>
+struct AttributeType<BlendColor>
+    : public TypeHolder<StateAttribute::BLENDCOLOR>
+{};
+
+template<>
+struct AttributeType<BlendEquation>
+    : public TypeHolder<StateAttribute::BLENDEQUATION>
+{};
+
+template<>
+struct AttributeType<BlendFunc>
+    : public TypeHolder<StateAttribute::BLENDFUNC>
+{};
+
+template<>
+struct AttributeType<ClampColor>
+    : public TypeHolder<StateAttribute::CLAMPCOLOR>
+{};
+
+template<>
+struct AttributeType<ColorMask>
+    : public TypeHolder<StateAttribute::COLORMASK>
+{};
+
+template<>
+struct AttributeType<ColorMatrix>
+    : public TypeHolder<StateAttribute::COLORMATRIX>
+{};
+
+template<>
+struct AttributeType<CullFace>
+    : public TypeHolder<StateAttribute::CULLFACE>
+{};
+
+
+template<>
+struct AttributeType<osg::Depth> // Conflicts with Xlib
+    : public TypeHolder<StateAttribute::DEPTH>
+{};
+
+template<>
+struct AttributeType<Fog>
+    : public TypeHolder<StateAttribute::FOG>
+{};
+
+template<>
+struct AttributeType<FragmentProgram>
+    : public TypeHolder<StateAttribute::FRAGMENTPROGRAM>
+{};
 
+template<>
+struct AttributeType<FrontFace>
+    : public TypeHolder<StateAttribute::FRONTFACE>
+{};
 
-template<typename Container>
-inline BackRefInsertIterator<Container>
-backRefInsertIterator(Container& container)
+template<>
+struct AttributeType<LightModel>
+    : public TypeHolder<StateAttribute::LIGHTMODEL>
+{};
+
+template<>
+struct AttributeType<LineStipple>
+    : public TypeHolder<StateAttribute::LINESTIPPLE>
+{};
+
+template<>
+struct AttributeType<LineWidth>
+    : public TypeHolder<StateAttribute::LINEWIDTH>
+{};
+
+template<>
+struct AttributeType<LogicOp>
+    : public TypeHolder<StateAttribute::LOGICOP>
+{};
+
+template<>
+struct AttributeType<Material>
+    : public TypeHolder<StateAttribute::MATERIAL>
+{};
+
+template<>
+struct AttributeType<Multisample>
+    : public TypeHolder<StateAttribute::MULTISAMPLE>
+{};
+
+template<>
+struct AttributeType<Point>
+    : public TypeHolder<StateAttribute::POINT>
+{};
+
+template<>
+struct TexAttributeType<PointSprite>
+    : public TypeHolder<StateAttribute::POINTSPRITE>
+{};
+
+template<>
+struct AttributeType<PolygonMode>
+    : public TypeHolder<StateAttribute::POLYGONMODE>
+{};
+
+template<>
+struct AttributeType<PolygonOffset>
+    : public TypeHolder<StateAttribute::POLYGONOFFSET>
+{};
+
+template<>
+struct AttributeType<PolygonStipple>
+    : public TypeHolder<StateAttribute::POLYGONSTIPPLE>
+{};
+
+template<>
+struct AttributeType<Program>
+    : public TypeHolder<StateAttribute::PROGRAM>
+{};
+
+template<>
+struct AttributeType<Scissor>
+    : public TypeHolder<StateAttribute::SCISSOR>
+{};
+
+template<>
+struct AttributeType<ShadeModel>
+    : public TypeHolder<StateAttribute::SHADEMODEL>
+{};
+
+template<>
+struct AttributeType<Stencil>
+    : public TypeHolder<StateAttribute::STENCIL>
+{};
+
+template<>
+struct AttributeType<StencilTwoSided>
+    : public TypeHolder<StateAttribute::STENCIL>
+{};
+
+// TexEnvCombine is not a subclass of TexEnv, so we can't do a
+// typesafe access of the attribute.
+#if 0
+template<>
+struct TexAttributeType<TexEnv>
+    : public TypeHolder<StateAttribute::TEXENV>
+{};
+
+template<>
+struct TexAttributeType<TexEnvCombine>
+    : public TypeHolder<StateAttribute::TEXENV>
+{};
+#endif
+
+template<>
+struct TexAttributeType<TexEnvFilter>
+    : public TypeHolder<StateAttribute::TEXENVFILTER>
+{};
+
+template<>
+struct TexAttributeType<TexGen>
+    : public TypeHolder<StateAttribute::TEXGEN>
+{};
+
+template<>
+struct TexAttributeType<TexMat>
+    : public TypeHolder<StateAttribute::TEXMAT>
+{};
+
+template<>
+struct TexAttributeType<Texture>
+    : public TypeHolder<StateAttribute::TEXTURE>
+{};
+
+template<>
+struct AttributeType<VertexProgram>
+    : public TypeHolder<StateAttribute::VERTEXPROGRAM>
+{};
+
+template<>
+struct AttributeType<Viewport>
+    : public TypeHolder<StateAttribute::VIEWPORT>
+{};
+} // namespace osgutils
+
+template<typename AT>
+inline AT* getStateAttribute(osg::StateSet* ss)
 {
-    return BackRefInsertIterator<Container>(container);
+    return static_cast<AT*>(ss->getAttribute(osgutils::AttributeType<AT>::type));
 }
+
+template<typename AT>
+inline const AT* getStateAttribute(const osg::StateSet* ss)
+{
+    return static_cast<const AT*>(ss->getAttribute(osgutils::AttributeType<AT>::type));
+}
+
+template<typename AT>
+inline AT* getStateAttribute(unsigned int unit, osg::StateSet* ss)
+{
+    return static_cast<AT*>(ss->getTextureAttribute(unit, osgutils::TexAttributeType<AT>
+                                                    ::type));
 }
+
+template<typename AT>
+inline const AT* getStateAttribute(unsigned int unit, const osg::StateSet* ss)
+{
+    return static_cast<const AT*>(ss->getTextureAttribute(unit,
+                                                          osgutils::TexAttributeType<AT>
+                                                          ::type));
+}
+} // namespace simgear
+
 #endif