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)
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"
<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"
<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"
--- /dev/null
+#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()
+{
+}
+}
--- /dev/null
+#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
--- /dev/null
+#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
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
#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
// 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");
}
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.
/**
* 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;
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
////////////////////////////////////////////////////////////////////////
/**
* 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),
_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.
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;
/**
* 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
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;
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);
}
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 *
}
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
DEFINTERNALPROP(float, FLOAT);
DEFINTERNALPROP(double, DOUBLE);
DEFINTERNALPROP(const char *, STRING);
+DEFINTERNALPROP(const char[], STRING);
#undef DEFINTERNALPROP
template<>
*/
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.
*/
/**
* 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.
*/
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.
*/
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:
/**
* 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;
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);
bool _tied;
int _attr;
- // The right kind of pointer...
+ // The right kind of pointer...
union {
SGPropertyNode * alias;
SGRaw* val;
/**
- * 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);
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
}
/**
- * 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
// 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));
}
}
+// Another variation, useful when called from gdb
+void
+writeProperties (const char* file, const SGPropertyNode * start_node)
+{
+ writeProperties(string(file), start_node, true);
+}
+
\f
////////////////////////////////////////////////////////////////////////
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))
#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>
#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>
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.
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)
{
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());
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")
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);
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,
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);
}
};
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");
const osgDB::ReaderWriter::Options* options);
};
-EffectNameValue<Material::ColorMode> colorModes[] =
+EffectNameValue<Material::ColorMode> colorModeInit[] =
{
{ "ambient", Material::AMBIENT },
{ "ambient-and-diffuse", Material::AMBIENT_AND_DIFFUSE },
{ "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")))
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()
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);
}
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;
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},
{"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
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
};
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)
{
}
}
+// 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);
&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);
+
}
#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
{
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
{
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);
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
# include <simgear_config.h>
#endif
+#include <simgear/scene/tgdb/userdata.hxx>
+
#include <simgear/math/SGMath.hxx>
#include "EffectBuilder.hxx"
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()
{
}
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"};
}
}
#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.
*/
namespace simgear
{
class Effect;
+class Pass;
/**
* Builder that returns an object, probably an OSG object.
}
};
-// 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));
}
/**
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
}
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();
{
}
-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)
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;
#include "Technique.hxx"
#include "Pass.hxx"
-#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <iterator>
#include <string>
#include <osg/GLExtensions>
+#include <osg/GL2Extensions>
#include <osg/Math>
#include <osgUtil/CullVisitor>
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()
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)
{
#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"
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,
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},
{ "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},
{"mirror", Texture::MIRROR},
{"repeat", Texture::REPEAT}
};
-
+EffectPropertyMap<Texture::WrapMode> wrapModes(wrapModesInit);
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;
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;
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;
+}
+
}
#ifndef SIMGEAR_TEXTUREBUILDER_HXX
#define SIMGEAR_TEXTUREBUILDER_HXX 1
+#include <osg/StateSet>
#include <osg/Texture>
#include "EffectBuilder.hxx"
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
#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;
const SGPropertyNode* node;
};
+namespace effect
+{
void mergePropertyTrees(SGPropertyNode* resultNode,
const SGPropertyNode* left, const SGPropertyNode* right)
{
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();
}
}
}
}
- 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();
}
}
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,
}
};
-// 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:
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);
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)
{
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));
};
typedef ModelRegistryCallback<ACProcessPolicy, DefaultCachePolicy,
- ACOptimizePolicy, DefaultCopyPolicy,
+ ACOptimizePolicy,
OSGSubstitutePolicy, BuildLeafBVHPolicy>
ACCallback;
#ifndef _SG_MODELREGISTRY_HXX
#define _SG_MODELREGISTRY_HXX 1
-#include <OpenThreads/ReentrantMutex>
-
#include <osg/ref_ptr>
#include <osg/Node>
#include <osgDB/FileUtils>
// 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)
{
}
_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
ProcessPolicy _processPolicy;
CachePolicy _cachePolicy;
OptimizePolicy _optimizePolicy;
- CopyPolicy _copyPolicy;
SubstitutePolicy _substitutePolicy;
BVHPolicy _bvhPolicy;
virtual ~ModelRegistryCallback() {}
}
};
-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,
};
typedef ModelRegistryCallback<DefaultProcessPolicy, DefaultCachePolicy,
- OptimizeModelPolicy, DefaultCopyPolicy,
+ OptimizeModelPolicy,
OSGSubstitutePolicy, BuildLeafBVHPolicy>
DefaultCallback;
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;
#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.
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));
}
}
- 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;
};
#endif
#include <osgDB/ReadFile>
+#include <osgDB/Input>
+#include <osgDB/ParameterOutput>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/OSGVersion.hxx>
_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
+ );
+}
# 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>
#include "model.hxx"
#include "SGText.hxx"
+using namespace std;
using namespace simgear;
+using namespace osg;
static osg::Node *
sgLoad3DModel_internal(const std::string& path,
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_,
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
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());
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 {
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;
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", ""));
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,
#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>
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"));
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
{
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
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);
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
{
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))
{
// 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;
};
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
SGDistScaleAnimation(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot);
virtual osg::Group* createAnimationGroup(osg::Group& parent);
-private:
class Transform;
};
SGFlashAnimation(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot);
virtual osg::Group* createAnimationGroup(osg::Group& parent);
-private:
+public:
class Transform;
};
SGBillboardAnimation(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot);
virtual osg::Group* createAnimationGroup(osg::Group& parent);
-private:
class Transform;
};
#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>
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
#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,
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
# include <simgear_config.h>
#endif
+#include <boost/algorithm/string.hpp>
+
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgDB/Registry>
#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"
{
}
+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,
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;
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*
}
}
-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)
{
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)
{ }
_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;
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,
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();
#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,
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() {
}
#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);
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;
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,
sg->setGeometry(quad);
geode->addDrawable(sg);
geode->setName("3D cloud");
- geode->setStateSet(stateSet.get());
+ geode->setEffect(effect.get());
return geode;
}
#include "bbcache.hxx"
+#include <simgear/scene/material/Effect.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
+
using std::string;
using std::vector;
~SGNewCloud();
// Generate a Cloud
- osg::ref_ptr<osg::Geode> genCloud ();
+ osg::ref_ptr<simgear::EffectGeode> genCloud ();
static double getDensity(void)
{
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);
typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy,
- NoOptimizePolicy, NoCopyPolicy,
+ NoOptimizePolicy,
NoSubstitutePolicy, BuildGroupBVHPolicy>
BTGCallback;
#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>
#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>
(*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;
}
}
}
- 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;
}
int _varieties;
float _width;
float _height;
+ ref_ptr<Effect> _effect;
};
struct AddTreesLeafObject
// 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.
{
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.
MatrixTransform* mt = new MatrixTransform(transform);
for (size_t i = 0; i < group->getNumChildren(); ++i)
mt->addChild(group->getChild(i));
- mt->setStateSet(stateset);
return mt;
}
return obj->get_random_model( root_props );
}
+namespace simgear
+{
+SGPropertyNode* getPropertyRoot()
+{
+ return root_props;
+}
+}
*/
osg::Node* sgGetRandomModel(SGMatModel *obj);
+namespace simgear
+{
+/**
+ * Get the property root for the simulation
+ */
+SGPropertyNode* getPropertyRoot();
+}
+
#endif // _SG_USERDATA_HXX
--- /dev/null
+// 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);
+ }
+}
+}
--- /dev/null
+// 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
SGStateAttributeVisitor.hxx \
SGTextureStateAttributeVisitor.hxx \
SGUpdateVisitor.hxx \
+ CopyOp.hxx \
NodeAndDrawableVisitor.hxx \
PrimitiveUtils.hxx \
QuadTreeBuilder.hxx \
RenderConstants.hxx \
+ SplicingVisitor.hxx \
StateAttributeFactory.hxx \
+ UpdateOnceCallback.hxx \
VectorArrayAdapter.hxx
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)
# include <simgear_config.h>
#endif
+#include <osgDB/Registry>
+#include <osgDB/Input>
+#include <osgDB/ParameterOutput>
+
#include "SGSceneUserData.hxx"
SGSceneUserData*
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
+ );
+}
#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);
--- /dev/null
+#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;
+}
+}
--- /dev/null
+#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
_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);
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(); }
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;
--- /dev/null
+// 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);
+}
+}
--- /dev/null
+// 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
#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
{
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