Merge branch 'timoore/effects'

Conflicts:
	simgear/scene/model/model.cxx
	simgear/scene/sky/newcloud.cxx
This commit is contained in:
Tim Moore
2009-12-20 16:07:00 +01:00
54 changed files with 3708 additions and 965 deletions

View File

@@ -671,7 +671,7 @@ if test "x$want_boost" = "xyes"; then
if test "$_version" = "0" ; then
AC_MSG_ERROR([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation.]])
else
AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).])
AC_MSG_ERROR([Your boost libraries seems to old (version $_version).])
fi
else
AC_SUBST(BOOST_CPPFLAGS)

View File

@@ -37,7 +37,7 @@ AC_PROG_CXX
AC_PROG_RANLIB
AC_PROG_INSTALL
AC_PROG_LN_S
AX_BOOST_BASE([1.34.0])
AX_BOOST_BASE([1.37.0])
if test "x$BOOST_CPPFLAGS" != "x-I/usr/include" ; then
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"

View File

@@ -643,6 +643,15 @@
<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>
@@ -1171,6 +1180,12 @@
<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>
@@ -1231,12 +1246,24 @@
<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"

View File

@@ -983,6 +983,14 @@
<Filter
Name="Lib_sgprops"
>
<File
RelativePath="..\..\simgear\props\AtomicChangeListener.cxx"
>
</File>
<File
RelativePath="..\..\simgear\props\AtomicChangeListener.hxx"
>
</File>
<File
RelativePath="..\..\simgear\props\condition.cxx"
>
@@ -991,6 +999,11 @@
RelativePath="..\..\simgear\props\condition.hxx"
>
</File>
<File
RelativePath="..\..\simgear\props\ExtendedPropertyAdapter.hxx"
>
</File>
<File
RelativePath="..\..\simgear\props\props.cxx"
>
@@ -1705,6 +1718,14 @@
<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"
>
@@ -1785,6 +1806,14 @@
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"
>
@@ -1793,6 +1822,14 @@
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"

View File

@@ -0,0 +1,71 @@
#include "AtomicChangeListener.hxx"
#include <algorithm>
#include <iterator>
#include <vector>
#include <boost/bind.hpp>
#include <simgear/structure/Singleton.hxx>
namespace simgear
{
using namespace std;
MultiChangeListener::MultiChangeListener()
{
}
void MultiChangeListener::valueChanged(SGPropertyNode* node)
{
valueChangedImplementation();
}
void MultiChangeListener::valueChangedImplementation()
{
}
AtomicChangeListener::AtomicChangeListener(std::vector<SGPropertyNode*>& nodes)
: _dirty(false), _valid(true)
{
listenToProperties(nodes.begin(), nodes.end());
}
void AtomicChangeListener::unregister_property(SGPropertyNode* node)
{
_valid = false;
// not necessary, but good hygine
vector<SGPropertyNode*>::iterator itr
= find(_watched.begin(), _watched.end(), node);
if (itr != _watched.end())
*itr = 0;
MultiChangeListener::unregister_property(node);
}
void AtomicChangeListener::fireChangeListeners()
{
vector<SGSharedPtr<AtomicChangeListener> >& listeners
= ListenerListSingleton::instance()->listeners;
for (vector<SGSharedPtr<AtomicChangeListener> >::iterator itr = listeners.begin(),
end = listeners.end();
itr != end;
++itr) {
(*itr)->valuesChanged();
(*itr)->_dirty = false;
}
listeners.clear();
}
void AtomicChangeListener::valueChangedImplementation()
{
if (!_dirty) {
_dirty = true;
if (_valid)
ListenerListSingleton::instance()->listeners.push_back(this);
}
}
void AtomicChangeListener::valuesChanged()
{
}
}

View File

@@ -0,0 +1,104 @@
#ifndef SIMGEAR_ATOMICCHANGELISTENER_HXX
#define SIMGEAR_ATOMICCHANGELISTENER_HXX 1
#include <algorithm>
#include <iterator>
#include <vector>
#include <boost/bind.hpp>
#include <simgear/structure/Singleton.hxx>
#include "props.hxx"
#include "ExtendedPropertyAdapter.hxx"
namespace simgear
{
// Performs an action when one of several nodes changes
class MultiChangeListener : public SGPropertyChangeListener
{
public:
MultiChangeListener();
template<typename Pitr>
void listenToProperties(Pitr propsBegin, Pitr propsEnd)
{
for (Pitr itr = propsBegin, end = propsEnd; itr != end; ++itr)
(*itr)->addChangeListener(this);
}
private:
void valueChanged(SGPropertyNode* node);
virtual void valueChangedImplementation();
};
class AtomicChangeListener : public MultiChangeListener,
public virtual SGReferenced
{
public:
AtomicChangeListener(std::vector<SGPropertyNode*>& nodes);
/**
* Lookup / create child nodes from their relative names.
*/
template<typename Itr>
AtomicChangeListener(SGPropertyNode* parent, Itr childNamesBegin,
Itr childNamesEnd)
: _dirty(false), _valid(true)
{
using namespace std;
for (Itr itr = childNamesBegin, end = childNamesEnd;
itr != end;
++itr)
_watched.push_back(makeNode(parent, *itr));
listenToProperties(_watched.begin(), _watched.end());
}
bool isDirty() { return _dirty; }
bool isValid() { return _valid; }
void unregister_property(SGPropertyNode* node);
static void fireChangeListeners();
private:
virtual void valueChangedImplementation();
virtual void valuesChanged();
bool _dirty;
bool _valid;
struct ListenerListSingleton : public Singleton<ListenerListSingleton>
{
std::vector<SGSharedPtr<AtomicChangeListener> > listeners;
};
protected:
std::vector<SGPropertyNode*> _watched;
};
template<typename T, typename Func>
class ExtendedPropListener : public AtomicChangeListener
{
public:
ExtendedPropListener(std::vector<SGPropertyNode*>& nodes, const Func& func,
bool initial = false)
: AtomicChangeListener(nodes), _func(func)
{
if (initial)
valuesChanged();
}
template<typename Itr>
ExtendedPropListener(SGPropertyNode* parent, Itr childNamesBegin,
Itr childNamesEnd, const Func& func,
bool initial = false)
: AtomicChangeListener(parent, childNamesBegin, childNamesEnd),
_func(func)
{
if (initial)
valuesChanged();
}
virtual void valuesChanged()
{
ExtendedPropertyAdapter<T, std::vector<SGPropertyNode*> > adaptor(_watched);
T val = adaptor();
_func(val);
}
private:
Func _func;
};
}
#endif

View File

@@ -0,0 +1,72 @@
#ifndef SIMGEAR_EXTENDEDPROPERTYADAPTER_HXX
#define SIMGEAR_EXTENDEDPROPERTYADAPTER_HXX 1
#include <algorithm>
#include <boost/bind.hpp>
#include <simgear/math/SGMath.hxx>
#include <simgear/structure/exception.hxx>
#include "props.hxx"
namespace simgear
{
namespace props
{
// This should be in simgear/math/SGVec.hxx and friends
template<typename T> struct NumComponents;
template<> struct NumComponents<SGVec3d>
{
enum { num_components = 3 };
};
template<> struct NumComponents<SGVec4d>
{
enum { num_components = 4 };
};
}
template<typename T, typename NodeContainer>
class ExtendedPropertyAdapter
{
public:
enum { num_components = props::NumComponents<T>::num_components };
ExtendedPropertyAdapter(const NodeContainer& elements)
: _elements(elements)
{
}
T operator()() const
{
T result;
if (_elements.size() < num_components)
throw sg_exception();
for (int i = 0; i < num_components; ++i)
result[i] = _elements[i]->getValue<double>();
return result;
}
void set(const T& val)
{
if (_elements.size() < num_components)
throw sg_exception();
for (int i = 0; i < num_components; ++i)
_elements[i]->setValue(val[i]);
}
private:
const NodeContainer& _elements;
};
template<typename InIterator, typename OutIterator>
inline void makeChildList(SGPropertyNode* prop, InIterator inBegin,
InIterator inEnd, OutIterator outBegin)
{
std::transform(inBegin, inEnd, outBegin,
boost::bind(static_cast<SGPropertyNode* (SGPropertyNode::*)(const char*, int, bool)>(&SGPropertyNode::getChild), prop, _1, 0, true));
}
}
#endif

View File

@@ -5,12 +5,15 @@ lib_LIBRARIES = libsgprops.a
include_HEADERS = \
condition.hxx \
props.hxx \
props_io.hxx
props_io.hxx \
AtomicChangeListener.hxx \
ExtendedPropertyAdapter.hxx
libsgprops_a_SOURCES = \
condition.cxx \
props.cxx \
props_io.cxx
props_io.cxx \
AtomicChangeListener.cxx
noinst_PROGRAMS = props_test

View File

@@ -16,9 +16,17 @@
#include <sstream>
#include <iomanip>
#include <iterator>
#include <stdio.h>
#include <string.h>
#include <boost/algorithm/string/find_iterator.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/bind.hpp>
#include <boost/functional/hash.hpp>
#include <boost/range.hpp>
#include <simgear/math/SGMath.hxx>
#if PROPS_STANDALONE
@@ -78,49 +86,36 @@ public:
// Local path normalization code.
////////////////////////////////////////////////////////////////////////
/**
* A component in a path.
*/
struct PathComponent
{
string name;
int index;
};
/**
* Parse the name for a path component.
*
* Name: [_a-zA-Z][-._a-zA-Z0-9]*
*/
static inline const string
parse_name (const string &path, int &i)
template<typename Range>
inline Range
parse_name (const Range &path)
{
string name = "";
int max = path.size();
typename Range::iterator i = path.begin();
typename Range::iterator max = path.end();
if (path[i] == '.') {
if (*i == '.') {
i++;
if (i < max && path[i] == '.') {
if (i != path.end() && *i == '.') {
i++;
name = "..";
} else {
name = ".";
}
if (i < max && path[i] != '/')
throw string("illegal character after " + name);
}
else if (isalpha(path[i]) || path[i] == '_') {
name += path[i];
if (i != max && *i != '/')
throw string("illegal character after . or ..");
} else if (isalpha(*i) || *i == '_') {
i++;
// The rules inside a name are a little
// less restrictive.
while (i < max) {
if (isalpha(path[i]) || isdigit(path[i]) || path[i] == '_' ||
path[i] == '-' || path[i] == '.') {
name += path[i];
} else if (path[i] == '[' || path[i] == '/') {
while (i != max) {
if (isalpha(*i) || isdigit(*i) || *i == '_' ||
*i == '-' || *i == '.') {
// name += path[i];
} else if (*i == '[' || *i == '/') {
break;
} else {
throw string("name may contain only ._- and alphanumeric characters");
@@ -130,90 +125,24 @@ parse_name (const string &path, int &i)
}
else {
if (name.size() == 0)
if (path.begin() == i)
throw string("name must begin with alpha or '_'");
}
return name;
return Range(path.begin(), i);
}
/**
* Parse the optional integer index for a path component.
*
* Index: "[" [0-9]+ "]"
*/
static inline int
parse_index (const string &path, int &i)
// Validate the name of a single node
inline bool validateName(const string& name)
{
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 ']')");
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("_-."));
}
/**
* 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;
}
/**
* Parse a path into its components.
*/
static void
parse_path (const string &path, vector<PathComponent> &components)
{
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++;
}
}
////////////////////////////////////////////////////////////////////////
// Other static utility functions.
@@ -242,16 +171,18 @@ compare_strings (const char * s1, const char * s2)
/**
* Locate a child node by name and index.
*/
template<typename Itr>
static int
find_child (const char * name, int index, const PropertyList& nodes)
find_child (Itr begin, Itr end, int index, const PropertyList& nodes)
{
int nNodes = nodes.size();
boost::iterator_range<Itr> name(begin, end);
for (int i = 0; i < nNodes; i++) {
SGPropertyNode * node = nodes[i];
// searching for a mathing index is a lot less time consuming than
// comparing two strings so do that first.
if (node->getIndex() == index && compare_strings(node->getName(), name))
if (node->getIndex() == index && boost::equals(node->getName(), name))
return i;
}
return -1;
@@ -277,55 +208,137 @@ find_last_child (const char * name, const PropertyList& nodes)
return index;
}
/**
* Locate another node, given a relative path.
*/
static SGPropertyNode *
find_node (SGPropertyNode * current,
const vector<PathComponent> &components,
int position,
bool create)
template<typename Itr>
inline SGPropertyNode*
SGPropertyNode::getExistingChild (Itr begin, Itr end, int index, bool create)
{
// Run off the end of the list
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);
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;
}
}
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;
}
// Success! This is the one we want.
else if (position >= (int)components.size()) {
return (current->getAttribute(SGPropertyNode::REMOVED) ? 0 : current);
}
// Empty component means root.
else if (components[position].name == "") {
return find_node(current->getRootNode(), components, position + 1, create);
}
// . means current directory
else if (components[position].name == ".") {
return find_node(current, components, position + 1, create);
}
// .. 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);
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;
}
// 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);
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);
}
////////////////////////////////////////////////////////////////////////
@@ -695,10 +708,12 @@ SGPropertyNode::SGPropertyNode (const SGPropertyNode &node)
/**
* Convenience constructor.
*/
SGPropertyNode::SGPropertyNode (const char * name,
template<typename Itr>
SGPropertyNode::SGPropertyNode (Itr begin, Itr end,
int index,
SGPropertyNode * parent)
: _index(index),
_name(begin, end),
_parent(parent),
_path_cache(0),
_type(props::NONE),
@@ -706,14 +721,29 @@ SGPropertyNode::SGPropertyNode (const char * name,
_attr(READ|WRITE),
_listeners(0)
{
int i = 0;
_local_val.string_val = 0;
_value.val = 0;
_name = parse_name(name, i);
if (i != int(strlen(name)) || name[0] == '.')
throw string("plain name expected instead of '") + name + '\'';
if (!validateName(_name))
throw string("plain name expected instead of '") + _name + '\'';
}
SGPropertyNode::SGPropertyNode (const string& name,
int index,
SGPropertyNode * parent)
: _index(index),
_name(name),
_parent(parent),
_path_cache(0),
_type(props::NONE),
_tied(false),
_attr(READ|WRITE),
_listeners(0)
{
_local_val.string_val = 0;
_value.val = 0;
if (!validateName(name))
throw string("plain name expected instead of '") + _name + '\'';
}
/**
* Destructor.
@@ -801,7 +831,7 @@ SGPropertyNode::addChild (const char * name)
int pos = find_last_child(name, _children)+1;
SGPropertyNode_ptr node;
node = new SGPropertyNode(name, pos, this);
node = new SGPropertyNode(name, name + strlen(name), pos, this);
_children.push_back(node);
fireChildAdded(node);
return node;
@@ -837,32 +867,29 @@ SGPropertyNode::getChild (int position) const
/**
* Get a non-const child by name and index, creating if necessary.
*/
SGPropertyNode *
SGPropertyNode::getChild (const char * name, int index, bool create)
{
int pos = find_child(name, index, _children);
if (pos >= 0) {
return _children[pos];
} else if (create) {
SGPropertyNode_ptr node;
pos = find_child(name, index, _removedChildren);
if (pos >= 0) {
PropertyList::iterator it = _removedChildren.begin();
it += pos;
node = _removedChildren[pos];
_removedChildren.erase(it);
node->setAttribute(REMOVED, false);
} else {
node = new SGPropertyNode(name, index, this);
}
_children.push_back(node);
fireChildAdded(node);
return node;
} else {
return 0;
}
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;
}
}
/**
* Get a const child by name and index.
@@ -870,7 +897,7 @@ SGPropertyNode::getChild (const char * name, int index, bool create)
const SGPropertyNode *
SGPropertyNode::getChild (const char * name, int index) const
{
int pos = find_child(name, index, _children);
int pos = find_child(name, name + strlen(name), index, _children);
if (pos >= 0)
return _children[pos];
else
@@ -945,7 +972,7 @@ SGPropertyNode_ptr
SGPropertyNode::removeChild (const char * name, int index, bool keep)
{
SGPropertyNode_ptr ret;
int pos = find_child(name, index, _children);
int pos = find_child(name, name + strlen(name), index, _children);
if (pos >= 0)
ret = removeChild(pos, keep);
return ret;
@@ -1710,14 +1737,16 @@ SGPropertyNode::getRootNode () const
SGPropertyNode *
SGPropertyNode::getNode (const char * relative_path, bool create)
{
using namespace boost;
if (_path_cache == 0)
_path_cache = new hash_table;
SGPropertyNode * result = _path_cache->get(relative_path);
if (result == 0) {
vector<PathComponent> components;
parse_path(relative_path, components);
result = find_node(this, components, 0, create);
result = find_node(this,
make_iterator_range(relative_path, relative_path
+ strlen(relative_path)),
create);
if (result != 0)
_path_cache->put(relative_path, result);
}
@@ -1728,11 +1757,10 @@ SGPropertyNode::getNode (const char * relative_path, bool create)
SGPropertyNode *
SGPropertyNode::getNode (const char * relative_path, int index, bool create)
{
vector<PathComponent> components;
parse_path(relative_path, components);
if (components.size() > 0)
components.back().index = index;
return find_node(this, components, 0, create);
using namespace boost;
return find_node(this, make_iterator_range(relative_path, relative_path
+ strlen(relative_path)),
create, index);
}
const SGPropertyNode *
@@ -2354,6 +2382,146 @@ std::istream& readFrom<SGVec4d>(std::istream& stream, SGVec4d& result)
}
return stream;
}
namespace
{
bool compareNodeValue(const SGPropertyNode& lhs, const SGPropertyNode& rhs)
{
props::Type ltype = lhs.getType();
props::Type rtype = rhs.getType();
if (ltype != rtype)
return false;
switch (ltype) {
case props::NONE:
return true;
case props::ALIAS:
return false; // XXX Should we look in aliases?
case props::BOOL:
return lhs.getValue<bool>() == rhs.getValue<bool>();
case props::INT:
return lhs.getValue<int>() == rhs.getValue<int>();
case props::LONG:
return lhs.getValue<long>() == rhs.getValue<long>();
case props::FLOAT:
return lhs.getValue<float>() == rhs.getValue<float>();
case props::DOUBLE:
return lhs.getValue<double>() == rhs.getValue<double>();
case props::STRING:
case props::UNSPECIFIED:
return !strcmp(lhs.getStringValue(), rhs.getStringValue());
case props::VEC3D:
return lhs.getValue<SGVec3d>() == rhs.getValue<SGVec3d>();
case props::VEC4D:
return lhs.getValue<SGVec4d>() == rhs.getValue<SGVec4d>();
default:
return false;
}
}
}
}
bool SGPropertyNode::compare(const SGPropertyNode& lhs,
const SGPropertyNode& rhs)
{
if (&lhs == &rhs)
return true;
int lhsChildren = lhs.nChildren();
int rhsChildren = rhs.nChildren();
if (lhsChildren != rhsChildren)
return false;
if (lhsChildren == 0)
return compareNodeValue(lhs, rhs);
for (size_t i = 0; i < lhs._children.size(); ++i) {
const SGPropertyNode* lchild = lhs._children[i];
const SGPropertyNode* rchild = rhs._children[i];
// I'm guessing that the nodes will usually be in the same
// order.
if (lchild->getIndex() != rchild->getIndex()
|| lchild->getNameString() != rchild->getNameString()) {
rchild = 0;
for (PropertyList::const_iterator itr = rhs._children.begin(),
end = rhs._children.end();
itr != end;
++itr)
if (lchild->getIndex() == (*itr)->getIndex()
&& lchild->getNameString() == (*itr)->getNameString()) {
rchild = *itr;
break;
}
if (!rchild)
return false;
}
if (!compare(*lchild, *rchild))
return false;
}
return true;
}
struct PropertyPlaceLess {
typedef bool result_type;
bool operator()(SGPropertyNode_ptr lhs, SGPropertyNode_ptr rhs) const
{
int comp = lhs->getNameString().compare(rhs->getNameString());
if (comp == 0)
return lhs->getIndex() < rhs->getIndex();
else
return comp < 0;
}
};
size_t hash_value(const SGPropertyNode& node)
{
using namespace boost;
if (node.nChildren() == 0) {
switch (node.getType()) {
case props::NONE:
return 0;
case props::BOOL:
return hash_value(node.getValue<bool>());
case props::INT:
return hash_value(node.getValue<int>());
case props::LONG:
return hash_value(node.getValue<long>());
case props::FLOAT:
return hash_value(node.getValue<float>());
case props::DOUBLE:
return hash_value(node.getValue<double>());
case props::STRING:
case props::UNSPECIFIED:
{
const char *val = node.getStringValue();
return hash_range(val, val + strlen(val));
}
case props::VEC3D:
{
const SGVec3d val = node.getValue<SGVec3d>();
return hash_range(&val[0], &val[3]);
}
case props::VEC4D:
{
const SGVec4d val = node.getValue<SGVec4d>();
return hash_range(&val[0], &val[4]);
}
case props::ALIAS: // XXX Should we look in aliases?
default:
return 0;
}
} else {
size_t seed = 0;
PropertyList children(node._children.begin(), node._children.end());
sort(children.begin(), children.end(), PropertyPlaceLess());
for (PropertyList::const_iterator itr = children.begin(),
end = children.end();
itr != end;
++itr) {
hash_combine(seed, (*itr)->_name);
hash_combine(seed, (*itr)->_index);
hash_combine(seed, hash_value(**itr));
}
return seed;
}
}
// end of props.cxx

View File

@@ -161,6 +161,7 @@ DEFINTERNALPROP(long, LONG);
DEFINTERNALPROP(float, FLOAT);
DEFINTERNALPROP(double, DOUBLE);
DEFINTERNALPROP(const char *, STRING);
DEFINTERNALPROP(const char[], STRING);
#undef DEFINTERNALPROP
template<>
@@ -804,6 +805,11 @@ public:
*/
const char * getName () const { return _name.c_str(); }
/**
* Get the node's simple name as a string.
*/
const std::string& getNameString () const { return _name; }
/**
* Get the node's pretty display name, with subscript when needed.
*/
@@ -875,17 +881,10 @@ public:
/**
* Get a child node by name and index.
*/
SGPropertyNode * getChild (const char * name, int index = 0,
bool create = false);
/**
* Get a child node by name and index.
*/
SGPropertyNode * getChild (const char* name, int index = 0,
bool create = false);
SGPropertyNode * getChild (const std::string& name, int index = 0,
bool create = false)
{ return getChild(name.c_str(), index, create); }
bool create = false);
/**
* Get a const child node by name and index.
*/
@@ -1223,6 +1222,12 @@ public:
bool setValue(const T& val,
typename boost::disable_if_c<simgear::props::PropertyTraits<T>::Internal>
::type* dummy = 0);
template<int N>
bool setValue(const char (&val)[N])
{
return setValue(&val[0]);
}
/**
* Print the value of the property to a stream.
@@ -1594,6 +1599,16 @@ public:
*/
void clearValue ();
/**
* Compare two property trees. The property trees are equal if: 1)
* They have no children, and have the same type and the values are
* equal, or 2) have the same number of children, and the
* corresponding children in each tree are equal. "corresponding"
* means have the same name and index.
*
* Attributes, removed children, and aliases aren't considered.
*/
static bool compare (const SGPropertyNode& lhs, const SGPropertyNode& rhs);
protected:
@@ -1604,12 +1619,13 @@ protected:
/**
* Protected constructor for making new nodes on demand.
*/
SGPropertyNode (const char * name, int index, SGPropertyNode * parent);
SGPropertyNode (const std::string& name, int index, SGPropertyNode * parent);
template<typename Itr>
SGPropertyNode (Itr begin, Itr end, int index, SGPropertyNode * parent);
private:
// Get the raw value
// Get the raw value
bool get_bool () const;
int get_int () const;
long get_long () const;
@@ -1617,7 +1633,7 @@ private:
double get_double () const;
const char * get_string () const;
// Set the raw value
// Set the raw value
bool set_bool (bool value);
bool set_int (int value);
bool set_long (long value);
@@ -1666,7 +1682,7 @@ private:
bool _tied;
int _attr;
// The right kind of pointer...
// The right kind of pointer...
union {
SGPropertyNode * alias;
SGRaw* val;
@@ -1685,8 +1701,8 @@ private:
/**
* Register/unregister node that links to this node in its path cache.
*/
* Register/unregister node that links to this node in its path cache.
*/
void add_linked_node (hash_table * node) { _linkedNodes.push_back(node); }
bool remove_linked_node (hash_table * node);
@@ -1743,7 +1759,18 @@ private:
unsigned int _data_length;
bucket ** _data;
};
// Pass name as a pair of iterators
template<typename Itr>
SGPropertyNode * getChildImpl (Itr begin, Itr end, int index = 0, bool create = false);
// very internal method
template<typename Itr>
SGPropertyNode* getExistingChild (Itr begin, Itr end, int index, bool create);
// very internal path parsing function
template<typename SplitItr>
friend SGPropertyNode* find_node_aux(SGPropertyNode * current, SplitItr& itr,
bool create, int last_index);
// For boost
friend size_t hash_value(const SGPropertyNode& node);
};
// Convenience functions for use in templates
@@ -1912,13 +1939,69 @@ inline bool SGPropertyNode::setValue(const T& val,
}
/**
* Utility function for creation of a child property node
* Utility function for creation of a child property node.
*/
inline SGPropertyNode* makeChild(SGPropertyNode* parent, const char* name,
int index = 0)
{
return parent->getChild(name, index, true);
}
/**
* Utility function for creation of a child property node using a
* relative path.
*/
namespace simgear
{
template<typename StringType>
inline SGPropertyNode* makeNode(SGPropertyNode* parent, const StringType& name)
{
return parent->getNode(name, true);
}
}
// For boost::hash
size_t hash_value(const SGPropertyNode& node);
// Helper comparison and hash functions for common cases
namespace simgear
{
namespace props
{
struct Compare
{
bool operator()(const SGPropertyNode* lhs, const SGPropertyNode* rhs) const
{
return SGPropertyNode::compare(*lhs, *rhs);
}
bool operator()(SGPropertyNode_ptr lhs, const SGPropertyNode* rhs) const
{
return SGPropertyNode::compare(*lhs, *rhs);
}
bool operator()(const SGPropertyNode* lhs, SGPropertyNode_ptr rhs) const
{
return SGPropertyNode::compare(*lhs, *rhs);
}
bool operator()(SGPropertyNode_ptr lhs, SGPropertyNode_ptr rhs) const
{
return SGPropertyNode::compare(*lhs, *rhs);
}
};
struct Hash
{
size_t operator()(const SGPropertyNode* node) const
{
return hash_value(*node);
}
size_t operator()(SGPropertyNode_ptr node) const
{
return hash_value(*node);
}
};
}
}
#endif // __PROPS_HXX
// end of props.hxx

View File

@@ -187,16 +187,17 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
// Get the index.
attval = atts.getValue("n");
int index = 0;
string strName(name);
if (attval != 0) {
index = atoi(attval);
st.counters[name] = SG_MAX2(st.counters[name], index+1);
st.counters[strName] = SG_MAX2(st.counters[strName], index+1);
} else {
index = st.counters[name];
st.counters[name]++;
index = st.counters[strName];
st.counters[strName]++;
}
// Got the index, so grab the node.
SGPropertyNode * node = st.node->getChild(name, index, true);
SGPropertyNode * node = st.node->getChild(strName, index, true);
if (!node->getAttribute(SGPropertyNode::WRITE)) {
SG_LOG(SG_INPUT, SG_ALERT, "Not overwriting write-protected property "
<< node->getPath(true));
@@ -601,6 +602,13 @@ writeProperties (const string &file, const SGPropertyNode * start_node,
}
}
// Another variation, useful when called from gdb
void
writeProperties (const char* file, const SGPropertyNode * start_node)
{
writeProperties(string(file), start_node, true);
}
////////////////////////////////////////////////////////////////////////
@@ -678,7 +686,7 @@ copyProperties (const SGPropertyNode *in, SGPropertyNode *out)
int nChildren = in->nChildren();
for (int i = 0; i < nChildren; i++) {
const SGPropertyNode * in_child = in->getChild(i);
SGPropertyNode * out_child = out->getChild(in_child->getName(),
SGPropertyNode * out_child = out->getChild(in_child->getNameString(),
in_child->getIndex(),
true);
if (!copyProperties(in_child, out_child))

View File

@@ -20,6 +20,7 @@
#include "Effect.hxx"
#include "EffectBuilder.hxx"
#include "EffectGeode.hxx"
#include "Technique.hxx"
#include "Pass.hxx"
#include "TextureBuilder.hxx"
@@ -29,16 +30,21 @@
#include <iterator>
#include <map>
#include <utility>
#include <boost/tr1/unordered_map.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/functional/hash.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <osg/AlphaFunc>
#include <osg/BlendFunc>
#include <osg/CullFace>
#include <osg/Depth>
#include <osg/Drawable>
#include <osg/Material>
#include <osg/Math>
#include <osg/PolygonMode>
#include <osg/Program>
#include <osg/Referenced>
@@ -59,6 +65,7 @@
#include <osgDB/ReadFile>
#include <osgDB/Registry>
#include <simgear/scene/tgdb/userdata.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx>
#include <simgear/structure/OSGUtils.hxx>
@@ -72,17 +79,23 @@ using namespace std;
using namespace osg;
using namespace osgUtil;
using namespace effect;
Effect::Effect()
: _cache(0), _isRealized(false)
{
}
Effect::Effect(const Effect& rhs, const CopyOp& copyop)
: root(rhs.root), parametersProp(rhs.parametersProp)
: root(rhs.root), parametersProp(rhs.parametersProp), _cache(0),
_isRealized(rhs._isRealized)
{
using namespace boost;
transform(rhs.techniques.begin(), rhs.techniques.end(),
back_inserter(techniques),
bind(simgear::clone_ref<Technique>, _1, copyop));
typedef vector<ref_ptr<Technique> > TechniqueList;
for (TechniqueList::const_iterator itr = rhs.techniques.begin(),
end = rhs.techniques.end();
itr != end;
++itr)
techniques.push_back(static_cast<Technique*>(copyop(itr->get())));
}
// Assume that the last technique is always valid.
@@ -125,29 +138,9 @@ void Effect::releaseGLObjects(osg::State* state) const
Effect::~Effect()
{
delete _cache;
}
class PassAttributeBuilder : public Referenced
{
public:
virtual void buildAttribute(Effect* effect, Pass* pass,
const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
= 0;
};
typedef map<const string, ref_ptr<PassAttributeBuilder> > PassAttrMap;
PassAttrMap passAttrMap;
template<typename T>
struct InstallAttributeBuilder
{
InstallAttributeBuilder(const string& name)
{
passAttrMap.insert(make_pair(name, new T));
}
};
void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
@@ -155,9 +148,10 @@ void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
tniq->passes.push_back(pass);
for (int i = 0; i < prop->nChildren(); ++i) {
const SGPropertyNode* attrProp = prop->getChild(i);
PassAttrMap::iterator itr = passAttrMap.find(attrProp->getName());
if (itr != passAttrMap.end())
itr->second->buildAttribute(effect, pass, attrProp, options);
PassAttributeBuilder* builder
= PassAttributeBuilder::find(attrProp->getNameString());
if (builder)
builder->buildAttribute(effect, pass, attrProp, options);
else
SG_LOG(SG_INPUT, SG_ALERT,
"skipping unknown pass attribute " << attrProp->getName());
@@ -237,8 +231,10 @@ struct CullFaceBuilder : PassAttributeBuilder
const osgDB::ReaderWriter::Options* options)
{
const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
if (!realProp)
if (!realProp) {
pass->setMode(GL_CULL_FACE, StateAttribute::OFF);
return;
}
StateAttributeFactory *attrFact = StateAttributeFactory::instance();
string propVal = realProp->getStringValue();
if (propVal == "front")
@@ -247,6 +243,8 @@ struct CullFaceBuilder : PassAttributeBuilder
pass->setAttributeAndModes(attrFact->getCullFaceBack());
else if (propVal == "front-back")
pass->setAttributeAndModes(new CullFace(CullFace::FRONT_AND_BACK));
else if (propVal == "off")
pass->setMode(GL_CULL_FACE, StateAttribute::OFF);
else
SG_LOG(SG_INPUT, SG_ALERT,
"invalid cull face property " << propVal);
@@ -255,6 +253,15 @@ struct CullFaceBuilder : PassAttributeBuilder
InstallAttributeBuilder<CullFaceBuilder> installCullFace("cull-face");
EffectNameValue<StateSet::RenderingHint> renderingHintInit[] =
{
{ "default", StateSet::DEFAULT_BIN },
{ "opaque", StateSet::OPAQUE_BIN },
{ "transparent", StateSet::TRANSPARENT_BIN }
};
EffectPropertyMap<StateSet::RenderingHint> renderingHints(renderingHintInit);
struct HintBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
@@ -263,11 +270,9 @@ struct HintBuilder : public PassAttributeBuilder
const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
if (!realProp)
return;
string propVal = realProp->getStringValue();
if (propVal == "opaque")
pass->setRenderingHint(StateSet::OPAQUE_BIN);
else if (propVal == "transparent")
pass->setRenderingHint(StateSet::TRANSPARENT_BIN);
StateSet::RenderingHint renderingHint = StateSet::DEFAULT_BIN;
findAttr(renderingHints, realProp, renderingHint);
pass->setRenderingHint(renderingHint);
}
};
@@ -278,6 +283,8 @@ struct RenderBinBuilder : public PassAttributeBuilder
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
if (!isAttributeActive(effect, prop))
return;
const SGPropertyNode* binProp = prop->getChild("bin-number");
binProp = getEffectPropertyNode(effect, binProp);
const SGPropertyNode* nameProp = prop->getChild("bin-name");
@@ -304,7 +311,7 @@ struct MaterialBuilder : public PassAttributeBuilder
const osgDB::ReaderWriter::Options* options);
};
EffectNameValue<Material::ColorMode> colorModes[] =
EffectNameValue<Material::ColorMode> colorModeInit[] =
{
{ "ambient", Material::AMBIENT },
{ "ambient-and-diffuse", Material::AMBIENT_AND_DIFFUSE },
@@ -313,11 +320,14 @@ EffectNameValue<Material::ColorMode> colorModes[] =
{ "specular", Material::SPECULAR },
{ "off", Material::OFF }
};
EffectPropertyMap<Material::ColorMode> colorModes(colorModeInit);
void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass,
const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
if (!isAttributeActive(effect, prop))
return;
Material* mat = new Material;
const SGPropertyNode* color = 0;
if ((color = getEffectPropertyChild(effect, prop, "ambient")))
@@ -361,125 +371,221 @@ void MaterialBuilder::buildAttribute(Effect* effect, Pass* pass,
InstallAttributeBuilder<MaterialBuilder> installMaterial("material");
EffectNameValue<BlendFunc::BlendFuncMode> blendFuncModesInit[] =
{
{"dst-alpha", BlendFunc::DST_ALPHA},
{"dst-color", BlendFunc::DST_COLOR},
{"one", BlendFunc::ONE},
{"one-minus-dst-alpha", BlendFunc::ONE_MINUS_DST_ALPHA},
{"one-minus-dst-color", BlendFunc::ONE_MINUS_DST_COLOR},
{"one-minus-src-alpha", BlendFunc::ONE_MINUS_SRC_ALPHA},
{"one-minus-src-color", BlendFunc::ONE_MINUS_SRC_COLOR},
{"src-alpha", BlendFunc::SRC_ALPHA},
{"src-alpha-saturate", BlendFunc::SRC_ALPHA_SATURATE},
{"src-color", BlendFunc::SRC_COLOR},
{"constant-color", BlendFunc::CONSTANT_COLOR},
{"one-minus-constant-color", BlendFunc::ONE_MINUS_CONSTANT_COLOR},
{"constant-alpha", BlendFunc::CONSTANT_ALPHA},
{"one-minus-constant-alpha", BlendFunc::ONE_MINUS_CONSTANT_ALPHA},
{"zero", BlendFunc::ZERO}
};
EffectPropertyMap<BlendFunc::BlendFuncMode> blendFuncModes(blendFuncModesInit);
struct BlendBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
if (!isAttributeActive(effect, prop))
return;
// XXX Compatibility with early <blend> syntax; should go away
// before a release
const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
if (!realProp)
return;
pass->setMode(GL_BLEND, (realProp->getBoolValue()
? StateAttribute::ON
: StateAttribute::OFF));
if (realProp->nChildren() == 0) {
pass->setMode(GL_BLEND, (realProp->getBoolValue()
? StateAttribute::ON
: StateAttribute::OFF));
return;
}
const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop,
"mode");
// XXX When dynamic parameters are supported, this code should
// create the blend function even if the mode is off.
if (pmode && !pmode->getValue<bool>()) {
pass->setMode(GL_BLEND, StateAttribute::OFF);
return;
}
const SGPropertyNode* psource
= getEffectPropertyChild(effect, prop, "source");
const SGPropertyNode* pdestination
= getEffectPropertyChild(effect, prop, "destination");
const SGPropertyNode* psourceRGB
= getEffectPropertyChild(effect, prop, "source-rgb");
const SGPropertyNode* psourceAlpha
= getEffectPropertyChild(effect, prop, "source-alpha");
const SGPropertyNode* pdestRGB
= getEffectPropertyChild(effect, prop, "destination-rgb");
const SGPropertyNode* pdestAlpha
= getEffectPropertyChild(effect, prop, "destination-alpha");
BlendFunc::BlendFuncMode sourceMode = BlendFunc::ONE;
BlendFunc::BlendFuncMode destMode = BlendFunc::ZERO;
if (psource)
findAttr(blendFuncModes, psource, sourceMode);
if (pdestination)
findAttr(blendFuncModes, pdestination, destMode);
if (psource && pdestination
&& !(psourceRGB || psourceAlpha || pdestRGB || pdestAlpha)
&& sourceMode == BlendFunc::SRC_ALPHA
&& destMode == BlendFunc::ONE_MINUS_SRC_ALPHA) {
pass->setAttributeAndModes(StateAttributeFactory::instance()
->getStandardBlendFunc());
return;
}
BlendFunc* blendFunc = new BlendFunc;
if (psource)
blendFunc->setSource(sourceMode);
if (pdestination)
blendFunc->setDestination(destMode);
if (psourceRGB) {
BlendFunc::BlendFuncMode sourceRGBMode;
findAttr(blendFuncModes, psourceRGB, sourceRGBMode);
blendFunc->setSourceRGB(sourceRGBMode);
}
if (pdestRGB) {
BlendFunc::BlendFuncMode destRGBMode;
findAttr(blendFuncModes, pdestRGB, destRGBMode);
blendFunc->setDestinationRGB(destRGBMode);
}
if (psourceAlpha) {
BlendFunc::BlendFuncMode sourceAlphaMode;
findAttr(blendFuncModes, psourceAlpha, sourceAlphaMode);
blendFunc->setSourceAlpha(sourceAlphaMode);
}
if (pdestAlpha) {
BlendFunc::BlendFuncMode destAlphaMode;
findAttr(blendFuncModes, pdestAlpha, destAlphaMode);
blendFunc->setDestinationAlpha(destAlphaMode);
}
pass->setAttributeAndModes(blendFunc);
}
};
InstallAttributeBuilder<BlendBuilder> installBlend("blend");
EffectNameValue<AlphaFunc::ComparisonFunction> alphaComparisonInit[] =
{
{"never", AlphaFunc::NEVER},
{"less", AlphaFunc::LESS},
{"equal", AlphaFunc::EQUAL},
{"lequal", AlphaFunc::LEQUAL},
{"greater", AlphaFunc::GREATER},
{"notequal", AlphaFunc::NOTEQUAL},
{"gequal", AlphaFunc::GEQUAL},
{"always", AlphaFunc::ALWAYS}
};
EffectPropertyMap<AlphaFunc::ComparisonFunction>
alphaComparison(alphaComparisonInit);
struct AlphaTestBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
if (!isAttributeActive(effect, prop))
return;
// XXX Compatibility with early <alpha-test> syntax; should go away
// before a release
const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
if (!realProp)
return;
pass->setMode(GL_ALPHA_TEST, (realProp->getBoolValue()
? StateAttribute::ON
: StateAttribute::OFF));
if (realProp->nChildren() == 0) {
pass->setMode(GL_ALPHA_TEST, (realProp->getBoolValue()
? StateAttribute::ON
: StateAttribute::OFF));
return;
}
const SGPropertyNode* pmode = getEffectPropertyChild(effect, prop,
"mode");
// XXX When dynamic parameters are supported, this code should
// create the blend function even if the mode is off.
if (pmode && !pmode->getValue<bool>()) {
pass->setMode(GL_ALPHA_TEST, StateAttribute::OFF);
return;
}
const SGPropertyNode* pComp = getEffectPropertyChild(effect, prop,
"comparison");
const SGPropertyNode* pRef = getEffectPropertyChild(effect, prop,
"reference");
AlphaFunc::ComparisonFunction func = AlphaFunc::ALWAYS;
float refValue = 1.0f;
if (pComp)
findAttr(alphaComparison, pComp, func);
if (pRef)
refValue = pRef->getValue<float>();
if (func == AlphaFunc::GREATER && osg::equivalent(refValue, 1.0f)) {
pass->setAttributeAndModes(StateAttributeFactory::instance()
->getStandardAlphaFunc());
} else {
AlphaFunc* alphaFunc = new AlphaFunc;
alphaFunc->setFunction(func);
alphaFunc->setReferenceValue(refValue);
pass->setAttributeAndModes(alphaFunc);
}
}
};
InstallAttributeBuilder<AlphaTestBuilder> installAlphaTest("alpha-test");
EffectNameValue<TexEnv::Mode> texEnvModes[] =
{
{"add", TexEnv::ADD},
{"blend", TexEnv::BLEND},
{"decal", TexEnv::DECAL},
{"modulate", TexEnv::MODULATE},
{"replace", TexEnv::REPLACE}
};
TexEnv* buildTexEnv(Effect* effect, const SGPropertyNode* prop)
{
const SGPropertyNode* modeProp = getEffectPropertyChild(effect, prop,
"mode");
const SGPropertyNode* colorProp = getEffectPropertyChild(effect, prop,
"color");
if (!modeProp)
return 0;
TexEnv::Mode mode = TexEnv::MODULATE;
findAttr(texEnvModes, modeProp, mode);
if (mode == TexEnv::MODULATE) {
return StateAttributeFactory::instance()->getStandardTexEnv();
}
TexEnv* env = new TexEnv(mode);
if (colorProp)
env->setColor(toOsg(colorProp->getValue<SGVec4d>()));
return env;
}
struct TextureUnitBuilder : PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options);
};
void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass,
const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
// 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);
}
}
InstallAttributeBuilder<TextureUnitBuilder> textureUnitBuilder("texture-unit");
typedef map<string, ref_ptr<Program> > ProgramMap;
// Shader key, used both for shaders with relative and absolute names
typedef pair<string, Shader::Type> ShaderKey;
struct ProgramKey
{
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()));
}
};
};
size_t hash_value(const ProgramKey& key)
{
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
typedef tr1::unordered_map<ProgramKey, ref_ptr<Program>,
boost::hash<ProgramKey>, ProgramKey::EqualTo>
ProgramMap;
ProgramMap programMap;
typedef map<string, ref_ptr<Shader> > ShaderMap;
typedef tr1::unordered_map<ShaderKey, ref_ptr<Shader>, boost::hash<ShaderKey> >
ShaderMap;
ShaderMap shaderMap;
void reload_shaders()
@@ -487,7 +593,7 @@ void reload_shaders()
for(ShaderMap::iterator sitr = shaderMap.begin(); sitr != shaderMap.end(); ++sitr)
{
Shader *shader = sitr->second.get();
string fileName = osgDB::findDataFile(sitr->first);
string fileName = osgDB::findDataFile(sitr->first.first);
if (!fileName.empty()) {
shader->loadShaderSourceFromFile(fileName);
}
@@ -505,32 +611,47 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
const osgDB::ReaderWriter::Options*
options)
{
using namespace boost;
if (!isAttributeActive(effect, prop))
return;
PropertyList pVertShaders = prop->getChildren("vertex-shader");
PropertyList pFragShaders = prop->getChildren("fragment-shader");
string programKey;
PropertyList pAttributes = prop->getChildren("attribute");
ProgramKey prgKey;
for (PropertyList::iterator itr = pVertShaders.begin(),
e = pVertShaders.end();
itr != e;
++itr)
{
programKey += (*itr)->getStringValue();
programKey += ";";
}
prgKey.shaders.push_back(ShaderKey((*itr)->getStringValue(),
Shader::VERTEX));
for (PropertyList::iterator itr = pFragShaders.begin(),
e = pFragShaders.end();
itr != e;
++itr)
{
programKey += (*itr)->getStringValue();
programKey += ";";
prgKey.shaders.push_back(ShaderKey((*itr)->getStringValue(),
Shader::FRAGMENT));
for (PropertyList::iterator itr = pAttributes.begin(),
e = pAttributes.end();
itr != e;
++itr) {
const SGPropertyNode* pName = getEffectPropertyChild(effect, *itr,
"name");
const SGPropertyNode* pIndex = getEffectPropertyChild(effect, *itr,
"index");
if (!pName || ! pIndex)
throw BuilderException("malformed attribute property");
prgKey.attributes
.push_back(ProgramKey::AttribKey(pName->getStringValue(),
pIndex->getValue<int>()));
}
if (options)
prgKey.paths = options->getDatabasePathList();
Program* program = 0;
ProgramMap::iterator pitr = programMap.find(programKey);
ProgramMap::iterator pitr = programMap.find(prgKey);
if (pitr != programMap.end()) {
program = pitr->second.get();
} else {
program = new Program;
program->setName(programKey);
// Add vertex shaders, then fragment shaders
PropertyList& pvec = pVertShaders;
Shader::Type stype = Shader::VERTEX;
@@ -539,31 +660,36 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
nameItr != e;
++nameItr) {
string shaderName = (*nameItr)->getStringValue();
ShaderMap::iterator sitr = shaderMap.find(shaderName);
string fileName = osgDB::findDataFile(shaderName, options);
if (fileName.empty())
throw BuilderException(string("couldn't find shader ") +
shaderName);
ShaderKey skey(fileName, stype);
ShaderMap::iterator sitr = shaderMap.find(skey);
if (sitr != shaderMap.end()) {
program->addShader(sitr->second.get());
} else {
string fileName = osgDB::findDataFile(shaderName, options);
if (!fileName.empty()) {
ref_ptr<Shader> shader = new Shader(stype);
if (shader->loadShaderSourceFromFile(fileName)) {
program->addShader(shader.get());
shaderMap.insert(make_pair(shaderName, shader));
}
ref_ptr<Shader> shader = new Shader(stype);
if (shader->loadShaderSourceFromFile(fileName)) {
program->addShader(shader.get());
shaderMap.insert(ShaderMap::value_type(skey, shader));
}
}
}
pvec = pFragShaders;
stype = Shader::FRAGMENT;
}
programMap.insert(make_pair(programKey, program));
BOOST_FOREACH(const ProgramKey::AttribKey& key, prgKey.attributes) {
program->addBindAttribLocation(key.first, key.second);
}
programMap.insert(ProgramMap::value_type(prgKey, program));
}
pass->setAttributeAndModes(program);
}
InstallAttributeBuilder<ShaderProgramBuilder> installShaderProgram("program");
EffectNameValue<Uniform::Type> uniformTypes[] =
EffectNameValue<Uniform::Type> uniformTypesInit[] =
{
{"float", Uniform::FLOAT},
{"float-vec3", Uniform::FLOAT_VEC3},
@@ -572,12 +698,15 @@ EffectNameValue<Uniform::Type> uniformTypes[] =
{"sampler-2d", Uniform::SAMPLER_2D},
{"sampler-3d", Uniform::SAMPLER_3D}
};
EffectPropertyMap<Uniform::Type> uniformTypes(uniformTypesInit);
struct UniformBuilder :public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
if (!isAttributeActive(effect, prop))
return;
const SGPropertyNode* nameProp = prop->getChild("name");
const SGPropertyNode* typeProp = prop->getChild("type");
const SGPropertyNode* valProp
@@ -659,18 +788,21 @@ struct NameBuilder : public PassAttributeBuilder
InstallAttributeBuilder<NameBuilder> installName("name");
EffectNameValue<PolygonMode::Mode> polygonModeModes[] =
EffectNameValue<PolygonMode::Mode> polygonModeModesInit[] =
{
{"fill", PolygonMode::FILL},
{"line", PolygonMode::LINE},
{"point", PolygonMode::POINT}
};
EffectPropertyMap<PolygonMode::Mode> polygonModeModes(polygonModeModesInit);
struct PolygonModeBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
if (!isAttributeActive(effect, prop))
return;
const SGPropertyNode* frontProp
= getEffectPropertyChild(effect, prop, "front");
const SGPropertyNode* backProp
@@ -691,6 +823,87 @@ struct PolygonModeBuilder : public PassAttributeBuilder
};
InstallAttributeBuilder<PolygonModeBuilder> installPolygonMode("polygon-mode");
struct VertexProgramTwoSideBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
if (!realProp)
return;
pass->setMode(GL_VERTEX_PROGRAM_TWO_SIDE,
(realProp->getValue<bool>()
? StateAttribute::ON : StateAttribute::OFF));
}
};
InstallAttributeBuilder<VertexProgramTwoSideBuilder>
installTwoSide("vertex-program-two-side");
struct VertexProgramPointSizeBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
const SGPropertyNode* realProp = getEffectPropertyNode(effect, prop);
if (!realProp)
return;
pass->setMode(GL_VERTEX_PROGRAM_POINT_SIZE,
(realProp->getValue<bool>()
? StateAttribute::ON : StateAttribute::OFF));
}
};
InstallAttributeBuilder<VertexProgramPointSizeBuilder>
installPointSize("vertex-program-point-size");
EffectNameValue<Depth::Function> depthFunctionInit[] =
{
{"never", Depth::NEVER},
{"less", Depth::LESS},
{"equal", Depth::EQUAL},
{"lequal", Depth::LEQUAL},
{"greater", Depth::GREATER},
{"notequal", Depth::NOTEQUAL},
{"gequal", Depth::GEQUAL},
{"always", Depth::ALWAYS}
};
EffectPropertyMap<Depth::Function> depthFunction(depthFunctionInit);
struct DepthBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
if (!isAttributeActive(effect, prop))
return;
ref_ptr<Depth> depth = new Depth;
const SGPropertyNode* pfunc
= getEffectPropertyChild(effect, prop, "function");
if (pfunc) {
Depth::Function func = Depth::LESS;
findAttr(depthFunction, pfunc, func);
depth->setFunction(func);
}
const SGPropertyNode* pnear
= getEffectPropertyChild(effect, prop, "near");
if (pnear)
depth->setZNear(pnear->getValue<double>());
const SGPropertyNode* pfar
= getEffectPropertyChild(effect, prop, "far");
if (pfar)
depth->setZFar(pnear->getValue<double>());
const SGPropertyNode* pmask
= getEffectPropertyChild(effect, prop, "write-mask");
if (pmask)
depth->setWriteMask(pmask->getValue<bool>());
pass->setAttribute(depth.get());
}
};
InstallAttributeBuilder<DepthBuilder> installDepth("depth");
void buildTechnique(Effect* effect, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
@@ -728,18 +941,130 @@ void buildTechnique(Effect* effect, const SGPropertyNode* prop,
}
}
// Specifically for .ac files...
bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss)
{
SGPropertyNode* paramRoot = makeChild(effectRoot, "parameters");
SGPropertyNode* matNode = paramRoot->getChild("material", 0, true);
Vec4f ambVal, difVal, specVal, emisVal;
float shininess = 0.0f;
const Material* mat = getStateAttribute<Material>(ss);
if (mat) {
ambVal = mat->getAmbient(Material::FRONT_AND_BACK);
difVal = mat->getDiffuse(Material::FRONT_AND_BACK);
specVal = mat->getSpecular(Material::FRONT_AND_BACK);
emisVal = mat->getEmission(Material::FRONT_AND_BACK);
shininess = mat->getShininess(Material::FRONT_AND_BACK);
makeChild(matNode, "active")->setValue(true);
makeChild(matNode, "ambient")->setValue(toVec4d(toSG(ambVal)));
makeChild(matNode, "diffuse")->setValue(toVec4d(toSG(difVal)));
makeChild(matNode, "specular")->setValue(toVec4d(toSG(specVal)));
makeChild(matNode, "emissive")->setValue(toVec4d(toSG(emisVal)));
makeChild(matNode, "shininess")->setValue(shininess);
matNode->getChild("color-mode", 0, true)->setStringValue("diffuse");
} else {
makeChild(matNode, "active")->setValue(false);
}
const ShadeModel* sm = getStateAttribute<ShadeModel>(ss);
string shadeModelString("smooth");
if (sm) {
ShadeModel::Mode smMode = sm->getMode();
if (smMode == ShadeModel::FLAT)
shadeModelString = "flat";
}
makeChild(paramRoot, "shade-model")->setStringValue(shadeModelString);
string cullFaceString("off");
const CullFace* cullFace = getStateAttribute<CullFace>(ss);
if (cullFace) {
switch (cullFace->getMode()) {
case CullFace::FRONT:
cullFaceString = "front";
break;
case CullFace::BACK:
cullFaceString = "back";
break;
case CullFace::FRONT_AND_BACK:
cullFaceString = "front-back";
break;
default:
break;
}
}
makeChild(paramRoot, "cull-face")->setStringValue(cullFaceString);
const BlendFunc* blendFunc = getStateAttribute<BlendFunc>(ss);
SGPropertyNode* blendNode = makeChild(paramRoot, "blend");
if (blendFunc) {
string sourceMode = findName(blendFuncModes, blendFunc->getSource());
string destMode = findName(blendFuncModes, blendFunc->getDestination());
makeChild(blendNode, "active")->setValue(true);
makeChild(blendNode, "source")->setStringValue(sourceMode);
makeChild(blendNode, "destination")->setStringValue(destMode);
makeChild(blendNode, "mode")->setValue(true);
} else {
makeChild(blendNode, "active")->setValue(false);
}
string renderingHint = findName(renderingHints, ss->getRenderingHint());
makeChild(paramRoot, "rendering-hint")->setStringValue(renderingHint);
makeTextureParameters(paramRoot, ss);
return true;
}
// Walk the techniques property tree, building techniques and
// passes.
bool Effect::realizeTechniques(const osgDB::ReaderWriter::Options* options)
{
if (_isRealized)
return true;
PropertyList tniqList = root->getChildren("technique");
for (PropertyList::iterator itr = tniqList.begin(), e = tniqList.end();
itr != e;
++itr)
buildTechnique(this, *itr, options);
_isRealized = true;
return true;
}
void Effect::InitializeCallback::doUpdate(osg::Node* node, osg::NodeVisitor* nv)
{
EffectGeode* eg = dynamic_cast<EffectGeode*>(node);
if (!eg)
return;
Effect* effect = eg->getEffect();
if (!effect)
return;
SGPropertyNode* root = getPropertyRoot();
for (vector<SGSharedPtr<Updater> >::iterator itr = effect->_extraData.begin(),
end = effect->_extraData.end();
itr != end;
++itr) {
InitializeWhenAdded* adder
= dynamic_cast<InitializeWhenAdded*>(itr->ptr());
if (adder)
adder->initOnAdd(effect, root);
}
}
bool Effect::Key::EqualTo::operator()(const Effect::Key& lhs,
const Effect::Key& rhs) const
{
if (lhs.paths.size() != rhs.paths.size()
|| !equal(lhs.paths.begin(), lhs.paths.end(), rhs.paths.begin()))
return false;
if (lhs.unmerged.valid() && rhs.unmerged.valid())
return props::Compare()(lhs.unmerged, rhs.unmerged);
else
return lhs.unmerged == rhs.unmerged;
}
size_t hash_value(const Effect::Key& key)
{
size_t seed = 0;
if (key.unmerged.valid())
boost::hash_combine(seed, *key.unmerged);
boost::hash_range(seed, key.paths.begin(), key.paths.end());
return seed;
}
bool Effect_writeLocalData(const Object& obj, osgDB::Output& fw)
{
const Effect& effect = static_cast<const Effect&>(obj);
@@ -762,4 +1087,49 @@ osgDB::RegisterDotOsgWrapperProxy effectProxy
&Effect_writeLocalData
);
}
// Property expressions for technique predicates
class PropertyExpression : public SGExpression<bool>
{
public:
PropertyExpression(SGPropertyNode* pnode) : _pnode(pnode) {}
void eval(bool& value, const expression::Binding*) const
{
value = _pnode->getValue<bool>();
}
protected:
SGPropertyNode_ptr _pnode;
};
class EffectPropertyListener : public SGPropertyChangeListener
{
public:
EffectPropertyListener(Technique* tniq) : _tniq(tniq) {}
void valueChanged(SGPropertyNode* node)
{
_tniq->refreshValidity();
}
protected:
osg::ref_ptr<Technique> _tniq;
};
Expression* propertyExpressionParser(const SGPropertyNode* exp,
expression::Parser* parser)
{
SGPropertyNode_ptr pnode = getPropertyRoot()->getNode(exp->getStringValue(),
true);
PropertyExpression* pexp = new PropertyExpression(pnode);
TechniquePredParser* predParser
= dynamic_cast<TechniquePredParser*>(parser);
if (predParser)
pnode->addChangeListener(new EffectPropertyListener(predParser
->getTechnique()));
return pexp;
}
expression::ExpParserRegistrar propertyRegistrar("property",
propertyExpressionParser);
}

View File

@@ -19,11 +19,15 @@
#include <vector>
#include <string>
#include <boost/tr1/unordered_map.hpp>
#include <boost/functional/hash.hpp>
#include <osg/Object>
#include <osgDB/ReaderWriter>
#include <simgear/props/props.hxx>
#include <simgear/scene/util/UpdateOnceCallback.hxx>
namespace osg
{
@@ -40,6 +44,32 @@ class CullVisitor;
namespace simgear
{
class Technique;
class Effect;
/**
* Object to be initialized at some point after an effect -- and its
* containing effect geode -- are hooked into the scene graph. Some
* things, like manipulations of the global property tree, are are
* only safe in the update process.
*/
class InitializeWhenAdded
{
public:
InitializeWhenAdded() : _initialized(false) {};
virtual ~InitializeWhenAdded() {};
void initOnAdd(Effect* effect, SGPropertyNode* propRoot)
{
if (!_initialized) {
initOnAddImpl(effect, propRoot);
_initialized = true;
}
}
bool getInitialized() const { return _initialized; }
private:
virtual void initOnAddImpl(Effect* effect, SGPropertyNode* propRoot) = 0;
bool _initialized;
};
class Effect : public osg::Object
{
@@ -56,14 +86,71 @@ public:
Technique* chooseTechnique(osg::RenderInfo* renderInfo);
virtual void resizeGLObjectBuffers(unsigned int maxSize);
virtual void releaseGLObjects(osg::State* state = 0) const;
/*
/**
* Build the techniques from the effect properties.
*/
bool realizeTechniques(const osgDB::ReaderWriter::Options* options = 0);
/**
* Updaters that should be derefed when the effect is
* deleted. Updaters arrange to be run by listening on properties
* or something.
*/
struct Updater : public virtual SGReferenced
{
virtual ~Updater() {}
};
void addUpdater(Updater* data) { _extraData.push_back(data); }
// Callback that is added to the effect geode to initialize the
// effect.
friend struct InitializeCallback;
struct InitializeCallback : public UpdateOnceCallback
{
void doUpdate(osg::Node* node, osg::NodeVisitor* nv);
};
protected:
std::vector<SGSharedPtr<Updater> > _extraData;
~Effect();
// Support for a cache of effects that inherit from this one, so
// Effect objects with the same parameters and techniques can be
// shared.
struct Key
{
Key() {}
Key(SGPropertyNode* unmerged_, const osgDB::FilePathList& paths_)
: unmerged(unmerged_), paths(paths_)
{
}
Key& operator=(const Key& rhs)
{
unmerged = rhs.unmerged;
paths = rhs.paths;
return *this;
}
SGPropertyNode_ptr unmerged;
osgDB::FilePathList paths;
struct EqualTo
{
bool operator()(const Key& lhs, const Key& rhs) const;
};
};
typedef std::tr1::unordered_map<Key, osg::ref_ptr<Effect>,
boost::hash<Key>, Key::EqualTo> Cache;
Cache* getCache()
{
if (!_cache)
_cache = new Cache;
return _cache;
}
Cache* _cache;
friend size_t hash_value(const Key& key);
friend Effect* makeEffect(SGPropertyNode* prop, bool realizeTechniques,
const osgDB::ReaderWriter::Options* options);
bool _isRealized;
};
// Automatic support for boost hash function
size_t hash_value(const Effect::Key&);
Effect* makeEffect(const std::string& name,
bool realizeTechniques,
const osgDB::ReaderWriter::Options* options = 0);
@@ -71,5 +158,18 @@ Effect* makeEffect(const std::string& name,
Effect* makeEffect(SGPropertyNode* prop,
bool realizeTechniques,
const osgDB::ReaderWriter::Options* options = 0);
bool makeParametersFromStateSet(SGPropertyNode* paramRoot,
const osg::StateSet* ss);
namespace effect
{
/**
* The function that implements effect property tree inheritance.
*/
void mergePropertyTrees(SGPropertyNode* resultNode,
const SGPropertyNode* left,
const SGPropertyNode* right);
}
}
#endif

View File

@@ -2,6 +2,8 @@
# include <simgear_config.h>
#endif
#include <simgear/scene/tgdb/userdata.hxx>
#include <simgear/math/SGMath.hxx>
#include "EffectBuilder.hxx"
@@ -39,6 +41,18 @@ const SGPropertyNode* getEffectPropertyChild(Effect* effect,
return getEffectPropertyNode(effect, child);
}
string getGlobalProperty(const SGPropertyNode* prop)
{
if (!prop)
return string();
const SGPropertyNode* useProp = prop->getChild("use");
if (!useProp)
return string();
return useProp->getStringValue();
}
namespace effect
{
BuilderException::BuilderException()
{
}
@@ -56,5 +70,19 @@ BuilderException::BuilderException(const std::string& message,
BuilderException::~BuilderException() throw()
{
}
}
bool isAttributeActive(Effect* effect, const SGPropertyNode* prop)
{
const SGPropertyNode* activeProp
= getEffectPropertyChild(effect, prop, "active");
return !activeProp || activeProp->getValue<bool>();
}
namespace effect
{
const char* colorFields[] = {"red", "green", "blue", "alpha"};
}
}

View File

@@ -18,17 +18,26 @@
#define SIMGEAR_EFFECTBUILDER_HXX 1
#include <algorithm>
#include <iterator>
#include <map>
#include <string>
#include <cstring>
#include <osgDB/Registry>
#include <boost/bind.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <simgear/math/SGMath.hxx>
#include <simgear/props/AtomicChangeListener.hxx>
#include <simgear/props/props.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/structure/Singleton.hxx>
#include "Effect.hxx"
/**
* Support classes for parsing effects.
*/
@@ -36,6 +45,7 @@
namespace simgear
{
class Effect;
class Pass;
/**
* Builder that returns an object, probably an OSG object.
@@ -79,55 +89,134 @@ protected:
}
};
// Simple tables of strings and constants. The table intialization
// *must* be in alphabetical order.
// Tables of strings and constants. We want to reconstruct the effect
// property tree from OSG state sets, so the tables should be bi-directional.
// two-way map for building StateSets from property descriptions, and
// vice versa. Mostly copied from the boost documentation.
namespace effect
{
using boost::multi_index_container;
using namespace boost::multi_index;
// tags for accessing both sides of a bidirectional map
struct from{};
struct to{};
template <typename T>
struct EffectNameValue
{
// Don't use std::pair because we want to use aggregate intialization.
const char* first;
T second;
class Compare
{
private:
static bool compare(const char* lhs, const char* rhs)
{
return std::strcmp(lhs, rhs) < 0;
}
public:
bool operator()(const EffectNameValue& lhs,
const EffectNameValue& rhs) const
{
return compare(lhs.first, rhs.first);
}
bool operator()(const char* lhs, const EffectNameValue& rhs) const
{
return compare(lhs, rhs.first);
}
bool operator()(const EffectNameValue& lhs, const char* rhs) const
{
return compare (lhs.first, rhs);
}
};
};
template<typename ENV, typename T, int N>
bool findAttr(const ENV (&attrs)[N], const SGPropertyNode* prop, T& result)
// The class template bidirectional_map wraps the specification
// of a bidirectional map based on multi_index_container.
template<typename FromType,typename ToType>
struct bidirectional_map
{
typedef std::pair<FromType,ToType> value_type;
/* A bidirectional map can be simulated as a multi_index_container
* of pairs of (FromType,ToType) with two unique indices, one
* for each member of the pair.
*/
typedef multi_index_container<
value_type,
indexed_by<
ordered_unique<
tag<from>, member<value_type, FromType, &value_type::first> >,
ordered_unique<
tag<to>, member<value_type, ToType, &value_type::second> >
>
> type;
};
template<typename T>
struct EffectPropertyMap
{
typedef typename bidirectional_map<std::string, T>::type BMap;
BMap _map;
template<int N>
EffectPropertyMap(const EffectNameValue<T> (&attrs)[N]);
};
template<typename T>
template<int N>
EffectPropertyMap<T>::EffectPropertyMap(const EffectNameValue<T> (&attrs)[N])
{
for (int i = 0; i < N; ++i)
_map.insert(typename BMap::value_type(attrs[i].first, attrs[i].second));
}
class BuilderException : public sg_exception
{
public:
BuilderException();
BuilderException(const char* message, const char* origin = 0);
BuilderException(const std::string& message, const std::string& = "");
virtual ~BuilderException() throw();
};
}
template<typename T>
void findAttr(const effect::EffectPropertyMap<T>& pMap,
const char* name,
T& result)
{
using namespace effect;
typename EffectPropertyMap<T>::BMap::iterator itr
= pMap._map.get<from>().find(name);
if (itr == pMap._map.end()) {
throw effect::BuilderException(string("findAttr: could not find attribute ")
+ string(name));
} else {
result = itr->second;
}
}
template<typename T>
inline void findAttr(const effect::EffectPropertyMap<T>& pMap,
const std::string& name,
T& result)
{
findAttr(pMap, name.c_str(), result);
}
template<typename T>
void findAttr(const effect::EffectPropertyMap<T>& pMap,
const SGPropertyNode* prop,
T& result)
{
if (!prop)
return false;
throw effect::BuilderException("findAttr: empty property");
const char* name = prop->getStringValue();
if (!name)
return false;
std::pair<const ENV*, const ENV*> itrs
= std::equal_range(&attrs[0], &attrs[N], name, typename ENV::Compare());
if (itrs.first == itrs.second) {
return false;
} else {
result = itrs.first->second;
return true;
}
throw effect::BuilderException("findAttr: no name for lookup");
findAttr(pMap, name, result);
}
template<typename T>
std::string findName(const effect::EffectPropertyMap<T>& pMap, T value)
{
using namespace effect;
std::string result;
typename EffectPropertyMap<T>::BMap::template index_iterator<to>::type itr
= pMap._map.get<to>().find(value);
if (itr != pMap._map.get<to>().end())
result = itr->first;
return result;
}
template<typename T>
std::string findName(const effect::EffectPropertyMap<T>& pMap, GLenum value)
{
return findName(pMap, static_cast<T>(value));
}
/**
@@ -145,13 +234,241 @@ const SGPropertyNode* getEffectPropertyChild(Effect* effect,
const SGPropertyNode* prop,
const char* name);
class BuilderException : public sg_exception
/**
* Get the name of a node mentioned in a <use> clause from the global property
* tree.
* @return empty if prop doesn't contain a <use> clause; otherwise the
* mentioned node name.
*/
std::string getGlobalProperty(const SGPropertyNode* prop);
class PassAttributeBuilder : public SGReferenced
{
protected:
typedef std::map<const std::string, SGSharedPtr<PassAttributeBuilder> >
PassAttrMap;
struct PassAttrMapSingleton : public simgear::Singleton<PassAttrMapSingleton>
{
PassAttrMap passAttrMap;
};
public:
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:
BuilderException();
BuilderException(const char* message, const char* origin = 0);
BuilderException(const std::string& message, const std::string& = "");
virtual ~BuilderException() throw();
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

View File

@@ -56,9 +56,11 @@ void EffectCullVisitor::apply(osg::Geode& node)
}
Effect* effect = eg->getEffect();
Technique* technique = 0;
if (!(effect && (technique = effect->chooseTechnique(&getRenderInfo())))) {
if (!effect) {
CullVisitor::apply(node);
return;
} else if (!(technique = effect->chooseTechnique(&getRenderInfo()))) {
return;
}
// push the node's state.
osg::StateSet* node_state = node.getStateSet();

View File

@@ -38,10 +38,18 @@ EffectGeode::EffectGeode()
{
}
EffectGeode::EffectGeode(const EffectGeode& rhs, const CopyOp& copyop) :
Geode(rhs, copyop)
EffectGeode::EffectGeode(const EffectGeode& rhs, const osg::CopyOp& copyop) :
Geode(rhs, copyop),
_effect(static_cast<Effect*>(copyop(rhs._effect.get())))
{
_effect = static_cast<Effect*>(rhs._effect->clone(copyop));
}
void EffectGeode::setEffect(Effect* effect)
{
_effect = effect;
if (!_effect)
return;
addUpdateCallback(new Effect::InitializeCallback);
}
void EffectGeode::resizeGLObjectBuffers(unsigned int maxSize)

View File

@@ -31,7 +31,7 @@ public:
const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
META_Node(simgear,EffectGeode);
Effect* getEffect() const { return _effect.get(); }
void setEffect(Effect* effect) { _effect = effect; }
void setEffect(Effect* effect);
virtual void resizeGLObjectBuffers(unsigned int maxSize);
virtual void releaseGLObjects(osg::State* = 0) const;
typedef DrawableList::iterator DrawablesIterator;

View File

@@ -6,7 +6,6 @@
#include "Technique.hxx"
#include "Pass.hxx"
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <iterator>
@@ -14,6 +13,7 @@
#include <string>
#include <osg/GLExtensions>
#include <osg/GL2Extensions>
#include <osg/Math>
#include <osgUtil/CullVisitor>
@@ -59,16 +59,15 @@ Technique::Technique(bool alwaysValid)
Technique::Technique(const Technique& rhs, const osg::CopyOp& copyop) :
_contextMap(rhs._contextMap), _alwaysValid(rhs._alwaysValid),
_shadowingStateSet(rhs._shadowingStateSet),
_shadowingStateSet(copyop(rhs._shadowingStateSet)),
_validExpression(rhs._validExpression),
_contextIdLocation(rhs._contextIdLocation)
{
using namespace std;
using namespace boost;
transform(rhs.passes.begin(), rhs.passes.end(),
back_inserter(passes),
bind(simgear::clone_ref<Pass>, _1, copyop));
for (std::vector<ref_ptr<Pass> >::const_iterator itr = rhs.passes.begin(),
end = rhs.passes.end();
itr != end;
++itr)
passes.push_back(static_cast<Pass*>(copyop(itr->get())));
}
Technique::~Technique()
@@ -276,6 +275,38 @@ Expression* extensionSupportedParser(const SGPropertyNode* exp,
expression::ExpParserRegistrar
extensionSupportedRegistrar("extension-supported", extensionSupportedParser);
class GLShaderLanguageExpression : public GeneralNaryExpression<float, int>
{
public:
void eval(float& value, const expression::Binding* b) const
{
value = 0.0f;
int contextId = getOperand(0)->getValue(b);
GL2Extensions* extensions
= GL2Extensions::Get(static_cast<unsigned>(contextId), true);
if (!extensions)
return;
if (!extensions->isGlslSupported())
return;
value = extensions->getLanguageVersion();
}
};
Expression* shaderLanguageParser(const SGPropertyNode* exp,
expression::Parser* parser)
{
GLShaderLanguageExpression* slexp = new GLShaderLanguageExpression;
int location = parser->getBindingLayout().addBinding("__contextId",
expression::INT);
VariableExpression<int>* contextExp = new VariableExpression<int>(location);
slexp->addOperand(contextExp);
return slexp;
}
expression::ExpParserRegistrar shaderLanguageRegistrar("shader-language",
glVersionParser);
void Technique::setGLExtensionsPred(float glVersion,
const std::vector<std::string>& extensions)
{

View File

@@ -20,19 +20,25 @@
#include "TextureBuilder.hxx"
#include "Pass.hxx"
#include <osg/TexEnv>
#include <osg/TexEnvCombine>
#include <osg/TexGen>
#include <osg/Texture1D>
#include <osg/Texture2D>
#include <osg/Texture3D>
#include <osg/TextureRectangle>
#include <osgDB/FileUtils>
#include <boost/lexical_cast.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx>
#include <simgear/math/SGMath.hxx>
#include <simgear/structure/OSGUtils.hxx>
#include "Noise.hxx"
@@ -41,6 +47,12 @@ namespace simgear
using namespace std;
using namespace osg;
using namespace effect;
TexEnvCombine* buildTexEnvCombine(Effect* effect,
const SGPropertyNode* envProp);
TexGen* buildTexGen(Effect* Effect, const SGPropertyNode* tgenProp);
// Hack to force inclusion of TextureBuilder.cxx in library
osg::Texture* TextureBuilder::buildFromType(Effect* effect, const string& type,
const SGPropertyNode*props,
@@ -54,9 +66,95 @@ typedef boost::tuple<string, Texture::FilterMode, Texture::FilterMode,
Texture::WrapMode, Texture::WrapMode, Texture::WrapMode,
string> TexTuple;
EffectNameValue<TexEnv::Mode> texEnvModesInit[] =
{
{"add", TexEnv::ADD},
{"blend", TexEnv::BLEND},
{"decal", TexEnv::DECAL},
{"modulate", TexEnv::MODULATE},
{"replace", TexEnv::REPLACE}
};
EffectPropertyMap<TexEnv::Mode> texEnvModes(texEnvModesInit);
TexEnv* buildTexEnv(Effect* effect, const SGPropertyNode* prop)
{
const SGPropertyNode* modeProp = getEffectPropertyChild(effect, prop,
"mode");
const SGPropertyNode* colorProp = getEffectPropertyChild(effect, prop,
"color");
if (!modeProp)
return 0;
TexEnv::Mode mode = TexEnv::MODULATE;
findAttr(texEnvModes, modeProp, mode);
if (mode == TexEnv::MODULATE) {
return StateAttributeFactory::instance()->getStandardTexEnv();
}
TexEnv* env = new TexEnv(mode);
if (colorProp)
env->setColor(toOsg(colorProp->getValue<SGVec4d>()));
return env;
}
void TextureUnitBuilder::buildAttribute(Effect* effect, Pass* pass,
const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options)
{
if (!isAttributeActive(effect, prop))
return;
// Decode the texture unit
int unit = 0;
const SGPropertyNode* pUnit = prop->getChild("unit");
if (pUnit) {
unit = pUnit->getValue<int>();
} else {
const SGPropertyNode* pName = prop->getChild("name");
if (pName)
try {
unit = boost::lexical_cast<int>(pName->getStringValue());
} catch (boost::bad_lexical_cast& lex) {
SG_LOG(SG_INPUT, SG_ALERT, "can't decode name as texture unit "
<< lex.what());
}
}
const SGPropertyNode* pType = getEffectPropertyChild(effect, prop, "type");
string type;
if (!pType)
type = "2d";
else
type = pType->getStringValue();
Texture* texture = 0;
try {
texture = TextureBuilder::buildFromType(effect, type, prop,
options);
}
catch (BuilderException& e) {
SG_LOG(SG_INPUT, SG_ALERT, "No image file for texture, using white ");
texture = StateAttributeFactory::instance()->getWhiteTexture();
}
pass->setTextureAttributeAndModes(unit, texture);
const SGPropertyNode* envProp = prop->getChild("environment");
if (envProp) {
TexEnv* env = buildTexEnv(effect, envProp);
if (env)
pass->setTextureAttributeAndModes(unit, env);
}
const SGPropertyNode* combineProp = prop->getChild("texenv-combine");
TexEnvCombine* combiner = 0;
if (combineProp && ((combiner = buildTexEnvCombine(effect, combineProp))))
pass->setTextureAttributeAndModes(unit, combiner);
const SGPropertyNode* tgenProp = prop->getChild("texgen");
TexGen* tgen = 0;
if (tgenProp && (tgen = buildTexGen(effect, tgenProp)))
pass->setTextureAttributeAndModes(unit, tgen);
}
// InstallAttributeBuilder call is in Effect.cxx to force this file to
// be linked in.
namespace
{
EffectNameValue<Texture::FilterMode> filterModes[] =
EffectNameValue<Texture::FilterMode> filterModesInit[] =
{
{ "linear", Texture::LINEAR },
{ "linear-mipmap-linear", Texture::LINEAR_MIPMAP_LINEAR},
@@ -65,8 +163,9 @@ EffectNameValue<Texture::FilterMode> filterModes[] =
{ "nearest-mipmap-linear", Texture::NEAREST_MIPMAP_LINEAR},
{ "nearest-mipmap-nearest", Texture::NEAREST_MIPMAP_NEAREST}
};
EffectPropertyMap<Texture::FilterMode> filterModes(filterModesInit);
EffectNameValue<Texture::WrapMode> wrapModes[] =
EffectNameValue<Texture::WrapMode> wrapModesInit[] =
{
{"clamp", Texture::CLAMP},
{"clamp-to-border", Texture::CLAMP_TO_BORDER},
@@ -74,7 +173,7 @@ EffectNameValue<Texture::WrapMode> wrapModes[] =
{"mirror", Texture::MIRROR},
{"repeat", Texture::REPEAT}
};
EffectPropertyMap<Texture::WrapMode> wrapModes(wrapModesInit);
TexTuple makeTexTuple(Effect* effect, const SGPropertyNode* props,
@@ -82,24 +181,27 @@ TexTuple makeTexTuple(Effect* effect, const SGPropertyNode* props,
const string& texType)
{
Texture::FilterMode minFilter = Texture::LINEAR_MIPMAP_LINEAR;
findAttr(filterModes, getEffectPropertyChild(effect, props, "filter"),
minFilter);
const SGPropertyNode* ep = 0;
if ((ep = getEffectPropertyChild(effect, props, "filter")))
findAttr(filterModes, ep, minFilter);
Texture::FilterMode magFilter = Texture::LINEAR;
findAttr(filterModes, getEffectPropertyChild(effect, props,
"mag-filter"),
magFilter);
if ((ep = getEffectPropertyChild(effect, props, "mag-filter")))
findAttr(filterModes, ep, magFilter);
const SGPropertyNode* pWrapS
= getEffectPropertyChild(effect, props, "wrap-s");
Texture::WrapMode sWrap = Texture::CLAMP;
findAttr(wrapModes, pWrapS, sWrap);
if (pWrapS)
findAttr(wrapModes, pWrapS, sWrap);
const SGPropertyNode* pWrapT
= getEffectPropertyChild(effect, props, "wrap-t");
Texture::WrapMode tWrap = Texture::CLAMP;
findAttr(wrapModes, pWrapT, tWrap);
if (pWrapT)
findAttr(wrapModes, pWrapT, tWrap);
const SGPropertyNode* pWrapR
= getEffectPropertyChild(effect, props, "wrap-r");
Texture::WrapMode rWrap = Texture::CLAMP;
findAttr(wrapModes, pWrapR, rWrap);
if (pWrapR)
findAttr(wrapModes, pWrapR, rWrap);
const SGPropertyNode* pImage
= getEffectPropertyChild(effect, props, "image");
string imageName;
@@ -198,6 +300,25 @@ namespace
TextureBuilder::Registrar installWhite("white", new WhiteTextureBuilder);
}
class TransparentTextureBuilder : public TextureBuilder
{
public:
Texture* build(Effect* effect, const SGPropertyNode*,
const osgDB::ReaderWriter::Options* options);
};
Texture* TransparentTextureBuilder::build(Effect* effect, const SGPropertyNode*,
const osgDB::ReaderWriter::Options* options)
{
return StateAttributeFactory::instance()->getTransparentTexture();
}
namespace
{
TextureBuilder::Registrar installTransparent("transparent",
new TransparentTextureBuilder);
}
osg::Image* make3DNoiseImage(int texSize)
{
osg::Image* image = new osg::Image;
@@ -280,4 +401,228 @@ namespace
TextureBuilder::Registrar installNoise("noise", new NoiseBuilder);
}
EffectNameValue<TexEnvCombine::CombineParam> combineParamInit[] =
{
{"replace", TexEnvCombine::REPLACE},
{"modulate", TexEnvCombine::MODULATE},
{"add", TexEnvCombine::ADD},
{"add-signed", TexEnvCombine::ADD_SIGNED},
{"interpolate", TexEnvCombine::INTERPOLATE},
{"subtract", TexEnvCombine::SUBTRACT},
{"dot3-rgb", TexEnvCombine::DOT3_RGB},
{"dot3-rgba", TexEnvCombine::DOT3_RGBA}
};
EffectPropertyMap<TexEnvCombine::CombineParam> combineParams(combineParamInit);
EffectNameValue<TexEnvCombine::SourceParam> sourceParamInit[] =
{
{"constant", TexEnvCombine::CONSTANT},
{"primary_color", TexEnvCombine::PRIMARY_COLOR},
{"previous", TexEnvCombine::PREVIOUS},
{"texture", TexEnvCombine::TEXTURE},
{"texture0", TexEnvCombine::TEXTURE0},
{"texture1", TexEnvCombine::TEXTURE1},
{"texture2", TexEnvCombine::TEXTURE2},
{"texture3", TexEnvCombine::TEXTURE3},
{"texture4", TexEnvCombine::TEXTURE4},
{"texture5", TexEnvCombine::TEXTURE5},
{"texture6", TexEnvCombine::TEXTURE6},
{"texture7", TexEnvCombine::TEXTURE7}
};
EffectPropertyMap<TexEnvCombine::SourceParam> sourceParams(sourceParamInit);
EffectNameValue<TexEnvCombine::OperandParam> opParamInit[] =
{
{"src-color", TexEnvCombine::SRC_COLOR},
{"one-minus-src-color", TexEnvCombine::ONE_MINUS_SRC_COLOR},
{"src-alpha", TexEnvCombine::SRC_ALPHA},
{"one-minus-src-alpha", TexEnvCombine::ONE_MINUS_SRC_ALPHA}
};
EffectPropertyMap<TexEnvCombine::OperandParam> operandParams(opParamInit);
TexEnvCombine* buildTexEnvCombine(Effect* effect, const SGPropertyNode* envProp)
{
if (!isAttributeActive(effect, envProp))
return 0;
TexEnvCombine* result = new TexEnvCombine;
const SGPropertyNode* p = 0;
if ((p = getEffectPropertyChild(effect, envProp, "combine-rgb"))) {
TexEnvCombine::CombineParam crgb = TexEnvCombine::MODULATE;
findAttr(combineParams, p, crgb);
result->setCombine_RGB(crgb);
}
if ((p = getEffectPropertyChild(effect, envProp, "combine-alpha"))) {
TexEnvCombine::CombineParam calpha = TexEnvCombine::MODULATE;
findAttr(combineParams, p, calpha);
result->setCombine_Alpha(calpha);
}
if ((p = getEffectPropertyChild(effect, envProp, "source0-rgb"))) {
TexEnvCombine::SourceParam source = TexEnvCombine::TEXTURE;
findAttr(sourceParams, p, source);
result->setSource0_RGB(source);
}
if ((p = getEffectPropertyChild(effect, envProp, "source1-rgb"))) {
TexEnvCombine::SourceParam source = TexEnvCombine::PREVIOUS;
findAttr(sourceParams, p, source);
result->setSource1_RGB(source);
}
if ((p = getEffectPropertyChild(effect, envProp, "source2-rgb"))) {
TexEnvCombine::SourceParam source = TexEnvCombine::CONSTANT;
findAttr(sourceParams, p, source);
result->setSource2_RGB(source);
}
if ((p = getEffectPropertyChild(effect, envProp, "source0-alpha"))) {
TexEnvCombine::SourceParam source = TexEnvCombine::TEXTURE;
findAttr(sourceParams, p, source);
result->setSource0_Alpha(source);
}
if ((p = getEffectPropertyChild(effect, envProp, "source1-alpha"))) {
TexEnvCombine::SourceParam source = TexEnvCombine::PREVIOUS;
findAttr(sourceParams, p, source);
result->setSource1_Alpha(source);
}
if ((p = getEffectPropertyChild(effect, envProp, "source2-alpha"))) {
TexEnvCombine::SourceParam source = TexEnvCombine::CONSTANT;
findAttr(sourceParams, p, source);
result->setSource2_Alpha(source);
}
if ((p = getEffectPropertyChild(effect, envProp, "operand0-rgb"))) {
TexEnvCombine::OperandParam op = TexEnvCombine::SRC_COLOR;
findAttr(operandParams, p, op);
result->setOperand0_RGB(op);
}
if ((p = getEffectPropertyChild(effect, envProp, "operand1-rgb"))) {
TexEnvCombine::OperandParam op = TexEnvCombine::SRC_COLOR;
findAttr(operandParams, p, op);
result->setOperand1_RGB(op);
}
if ((p = getEffectPropertyChild(effect, envProp, "operand2-rgb"))) {
TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
findAttr(operandParams, p, op);
result->setOperand2_RGB(op);
}
if ((p = getEffectPropertyChild(effect, envProp, "operand0-alpha"))) {
TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
findAttr(operandParams, p, op);
result->setOperand0_Alpha(op);
}
if ((p = getEffectPropertyChild(effect, envProp, "operand1-alpha"))) {
TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
findAttr(operandParams, p, op);
result->setOperand1_Alpha(op);
}
if ((p = getEffectPropertyChild(effect, envProp, "operand2-alpha"))) {
TexEnvCombine::OperandParam op = TexEnvCombine::SRC_ALPHA;
findAttr(operandParams, p, op);
result->setOperand2_Alpha(op);
}
if ((p = getEffectPropertyChild(effect, envProp, "scale-rgb"))) {
result->setScale_RGB(p->getValue<float>());
}
if ((p = getEffectPropertyChild(effect, envProp, "scale-alpha"))) {
result->setScale_Alpha(p->getValue<float>());
}
#if 0
if ((p = getEffectPropertyChild(effect, envProp, "constant-color"))) {
SGVec4d color = p->getValue<SGVec4d>();
result->setConstantColor(toOsg(color));
} else if ((p = getEffectPropertyChild(effect, envProp,
"light-direction"))) {
SGVec3d direction = p->getValue<SGVec3d>();
result->setConstantColorAsLightDirection(toOsg(direction));
}
#endif
const SGPropertyNode* colorNode = envProp->getChild("constant-color");
if (colorNode)
initFromParameters(effect, colorNode, result,
&TexEnvCombine::setConstantColor, colorFields);
return result;
}
EffectNameValue<TexGen::Mode> tgenModeInit[] =
{
{ "object-linear", TexGen::OBJECT_LINEAR},
{ "eye-linear", TexGen::EYE_LINEAR},
{ "sphere-map", TexGen::SPHERE_MAP},
{ "normal-map", TexGen::NORMAL_MAP},
{ "reflection-map", TexGen::REFLECTION_MAP}
};
EffectPropertyMap<TexGen::Mode> tgenModes(tgenModeInit);
EffectNameValue<TexGen::Coord> tgenCoordInit[] =
{
{"s", TexGen::S},
{"t", TexGen::T},
{"r", TexGen::R},
{"q", TexGen::Q}
};
EffectPropertyMap<TexGen::Coord> tgenCoords(tgenCoordInit);
TexGen* buildTexGen(Effect* effect, const SGPropertyNode* tgenProp)
{
if (!isAttributeActive(effect, tgenProp))
return 0;
TexGen* result = new TexGen;
const SGPropertyNode* p = 0;
TexGen::Mode mode = TexGen::OBJECT_LINEAR;
findAttr(tgenModes, getEffectPropertyChild(effect, tgenProp, "mode"), mode);
result->setMode(mode);
const SGPropertyNode* planesNode = tgenProp->getChild("planes");
if (planesNode) {
for (int i = 0; i < planesNode->nChildren(); ++i) {
const SGPropertyNode* planeNode = planesNode->getChild(i);
TexGen::Coord coord;
findAttr(tgenCoords, planeNode->getName(), coord);
const SGPropertyNode* realNode
= getEffectPropertyNode(effect, planeNode);
SGVec4d plane = realNode->getValue<SGVec4d>();
result->setPlane(coord, toOsg(plane));
}
}
return result;
}
bool makeTextureParameters(SGPropertyNode* paramRoot, const StateSet* ss)
{
SGPropertyNode* texUnit = makeChild(paramRoot, "texture");
const Texture* tex = getStateAttribute<Texture>(0, ss);
const Texture2D* texture = dynamic_cast<const Texture2D*>(tex);
makeChild(texUnit, "unit")->setValue(0);
if (!tex) {
// The default shader-based technique ignores active
makeChild(texUnit, "active")->setValue(false);
return false;
}
const Image* image = texture->getImage();
string imageName;
if (image) {
imageName = image->getFileName();
} else {
makeChild(texUnit, "active")->setValue(false);
makeChild(texUnit, "type")->setValue("white");
return false;
}
makeChild(texUnit, "active")->setValue(true);
makeChild(texUnit, "type")->setValue("2d");
string filter = findName(filterModes,
texture->getFilter(Texture::MIN_FILTER));
string magFilter = findName(filterModes,
texture->getFilter(Texture::MAG_FILTER));
string wrapS = findName(wrapModes, texture->getWrap(Texture::WRAP_S));
string wrapT = findName(wrapModes, texture->getWrap(Texture::WRAP_T));
string wrapR = findName(wrapModes, texture->getWrap(Texture::WRAP_R));
makeChild(texUnit, "image")->setStringValue(imageName);
makeChild(texUnit, "filter")->setStringValue(filter);
makeChild(texUnit, "mag-filter")->setStringValue(magFilter);
makeChild(texUnit, "wrap-s")->setStringValue(wrapS);
makeChild(texUnit, "wrap-t")->setStringValue(wrapT);
makeChild(texUnit, "wrap-r")->setStringValue(wrapR);
return true;
}
}

View File

@@ -17,6 +17,7 @@
#ifndef SIMGEAR_TEXTUREBUILDER_HXX
#define SIMGEAR_TEXTUREBUILDER_HXX 1
#include <osg/StateSet>
#include <osg/Texture>
#include "EffectBuilder.hxx"
@@ -30,5 +31,14 @@ public:
const SGPropertyNode*props,
const osgDB::ReaderWriter::Options* options);
};
struct TextureUnitBuilder : public PassAttributeBuilder
{
void buildAttribute(Effect* effect, Pass* pass, const SGPropertyNode* prop,
const osgDB::ReaderWriter::Options* options);
};
bool makeTextureParameters(SGPropertyNode* paramRoot, const osg::StateSet* ss);
}
#endif

View File

@@ -4,6 +4,7 @@
#endif
#include "Effect.hxx"
#include "EffectBuilder.hxx"
#include "Technique.hxx"
#include "Pass.hxx"
@@ -32,12 +33,14 @@
#include <simgear/debug/logstream.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/SplicingVisitor.hxx>
#include <simgear/structure/SGExpression.hxx>
namespace simgear
{
using namespace std;
using namespace osg;
using namespace effect;
typedef vector<const SGPropertyNode*> RawPropVector;
typedef map<const string, ref_ptr<Effect> > EffectMap;
@@ -71,6 +74,8 @@ struct PropPredicate
const SGPropertyNode* node;
};
namespace effect
{
void mergePropertyTrees(SGPropertyNode* resultNode,
const SGPropertyNode* left, const SGPropertyNode* right)
{
@@ -107,29 +112,48 @@ void mergePropertyTrees(SGPropertyNode* resultNode,
copyProperties(*itr, newChild);
}
}
}
Effect* makeEffect(const string& name,
bool realizeTechniques,
const osgDB::ReaderWriter::Options* options)
{
OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
EffectMap::iterator itr = effectMap.find(name);
if (itr != effectMap.end())
return itr->second.get();
{
OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
EffectMap::iterator itr = effectMap.find(name);
if (itr != effectMap.end())
return itr->second.get();
}
string effectFileName(name);
effectFileName += ".eff";
string absFileName
= osgDB::findDataFile(effectFileName, options);
if (absFileName.empty()) {
SG_LOG(SG_INPUT, SG_WARN, "can't find \"" << effectFileName << "\"");
SG_LOG(SG_INPUT, SG_ALERT, "can't find \"" << effectFileName << "\"");
return 0;
}
SGPropertyNode_ptr effectProps = new SGPropertyNode();
readProperties(absFileName, effectProps.ptr(), 0, true);
Effect* result = makeEffect(effectProps.ptr(), realizeTechniques, options);
if (result)
effectMap.insert(make_pair(name, result));
return result;
try {
readProperties(absFileName, effectProps.ptr(), 0, true);
}
catch (sg_io_exception& e) {
SG_LOG(SG_INPUT, SG_ALERT, "error reading \"" << absFileName << "\": "
<< e.getFormattedMessage());
return 0;
}
ref_ptr<Effect> result = makeEffect(effectProps.ptr(), realizeTechniques,
options);
if (result.valid()) {
OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
pair<EffectMap::iterator, bool> irslt
= effectMap.insert(make_pair(name, result));
if (!irslt.second) {
// Another thread beat us to it!. Discard our newly
// constructed Effect and use the one in the cache.
result = irslt.first->second;
}
}
return result.release();
}
@@ -160,29 +184,64 @@ Effect* makeEffect(SGPropertyNode* prop,
}
}
}
Effect* effect = new Effect;
ref_ptr<Effect> effect;
// Merge with the parent effect, if any
const SGPropertyNode* inheritProp = prop->getChild("inherits-from");
SGPropertyNode_ptr inheritProp = prop->getChild("inherits-from");
Effect* parent = 0;
if (inheritProp) {
parent = makeEffect(inheritProp->getStringValue(), realizeTechniques,
options);
if(parent)
{
effect->root = new SGPropertyNode;
mergePropertyTrees(effect->root, prop, parent->root);
effect->root->removeChild("inherits-from");
//prop->removeChild("inherits-from");
parent = makeEffect(inheritProp->getStringValue(), false, options);
if (parent) {
Effect::Key key;
key.unmerged = prop;
if (options) {
key.paths = options->getDatabasePathList();
}
Effect::Cache* cache = 0;
Effect::Cache::iterator itr;
{
OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
lock(effectMutex);
cache = parent->getCache();
itr = cache->find(key);
if (itr != cache->end())
effect = itr->second.get();
}
if (!effect.valid()) {
effect = new Effect;
effect->root = new SGPropertyNode;
mergePropertyTrees(effect->root, prop, parent->root);
effect->parametersProp = effect->root->getChild("parameters");
OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
lock(effectMutex);
pair<Effect::Cache::iterator, bool> irslt
= cache->insert(make_pair(key, effect));
if (!irslt.second)
effect = irslt.first->second;
}
} else {
effect->root = prop;
effect->root->removeChild("inherits-from");
SG_LOG(SG_INPUT, SG_ALERT, "can't find base effect " <<
inheritProp->getStringValue());
return 0;
}
} else {
effect = new Effect;
effect->root = prop;
effect->parametersProp = effect->root->getChild("parameters");
}
effect->parametersProp = effect->root->getChild("parameters");
if (realizeTechniques)
effect->realizeTechniques(options);
return effect;
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;
}
}
return effect.release();
}
}

View File

@@ -62,9 +62,6 @@ using namespace osgUtil;
using namespace osgDB;
using namespace simgear;
using OpenThreads::ReentrantMutex;
using OpenThreads::ScopedLock;
// Little helper class that holds an extra reference to a
// loaded 3d model.
// Since we clone all structural nodes from our 3d models,
@@ -128,125 +125,6 @@ protected:
}
};
// Change the StateSets of a model to hold different textures based on
// a livery path.
class TextureUpdateVisitor : public NodeAndDrawableVisitor {
public:
TextureUpdateVisitor(const FilePathList& pathList) :
NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
_pathList(pathList)
{
}
virtual void apply(Node& node)
{
StateSet* stateSet = cloneStateSet(node.getStateSet());
if (stateSet)
node.setStateSet(stateSet);
traverse(node);
}
virtual void apply(Drawable& drawable)
{
StateSet* stateSet = cloneStateSet(drawable.getStateSet());
if (stateSet)
drawable.setStateSet(stateSet);
}
// Copied from Mathias' earlier SGTextureUpdateVisitor
protected:
Texture2D* textureReplace(int unit, const StateAttribute* attr)
{
const Texture2D* texture = dynamic_cast<const Texture2D*>(attr);
if (!texture)
return 0;
const Image* image = texture->getImage();
const string* fullFilePath = 0;
if (image) {
// The currently loaded file name
fullFilePath = &image->getFileName();
} else {
fullFilePath = &texture->getName();
}
// The short name
string fileName = getSimpleFileName(*fullFilePath);
if (fileName.empty())
return 0;
// The name that should be found with the current database path
string fullLiveryFile = findFileInPath(fileName, _pathList);
// If it is empty or they are identical then there is nothing to do
if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
return 0;
Image* newImage = readImageFile(fullLiveryFile);
if (!newImage)
return 0;
CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
if (!newTexture) {
return 0;
} else {
newTexture->setImage(newImage);
return newTexture;
}
}
StateSet* cloneStateSet(const StateSet* stateSet)
{
typedef pair<int, Texture2D*> Tex2D;
vector<Tex2D> newTextures;
StateSet* result = 0;
if (!stateSet)
return 0;
int numUnits = stateSet->getTextureAttributeList().size();
if (numUnits > 0) {
for (int i = 0; i < numUnits; ++i) {
const StateAttribute* attr
= stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
Texture2D* newTexture = textureReplace(i, attr);
if (newTexture)
newTextures.push_back(Tex2D(i, newTexture));
}
if (!newTextures.empty()) {
result = static_cast<StateSet*>(stateSet->clone(CopyOp()));
for (vector<Tex2D>::iterator i = newTextures.begin();
i != newTextures.end();
++i) {
result->setTextureAttribute(i->first, i->second);
}
}
}
return result;
}
private:
FilePathList _pathList;
};
// Create new userdata structs in a copied model.
// The BVH trees are shared with the original model, but the velocity fields
// should usually be distinct fields for distinct models.
class UserDataCopyVisitor : public osg::NodeVisitor {
public:
UserDataCopyVisitor() :
osg::NodeVisitor(osg::NodeVisitor::NODE_VISITOR,
osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
{
}
virtual void apply(osg::Node& node)
{
osg::ref_ptr<SGSceneUserData> userData;
userData = SGSceneUserData::getSceneUserData(&node);
if (userData.valid()) {
SGSceneUserData* newUserData = new SGSceneUserData(*userData);
newUserData->setVelocity(0);
node.setUserData(newUserData);
}
node.traverse(*this);
}
};
class SGTexCompressionVisitor : public SGTextureStateAttributeVisitor {
public:
@@ -324,10 +202,8 @@ ReaderWriter::ReadResult
ModelRegistry::readImage(const string& fileName,
const ReaderWriter::Options* opt)
{
ScopedLock<ReentrantMutex> lock(readerMutex);
CallbackMap::iterator iter
= imageCallbackMap.find(getFileExtension(fileName));
// XXX Workaround for OSG plugin bug
{
if (iter != imageCallbackMap.end() && iter->second.valid())
return iter->second->readImage(fileName, opt);
@@ -416,41 +292,6 @@ osg::Node* OptimizeModelPolicy::optimize(osg::Node* node,
return node;
}
osg::Node* DefaultCopyPolicy::copy(osg::Node* model, const string& fileName,
const osgDB::ReaderWriter::Options* opt)
{
// Add an extra reference to the model stored in the database.
// That is to avoid expiring the object from the cache even if it is still
// in use. Note that the object cache will think that a model is unused
// if the reference count is 1. If we clone all structural nodes here
// we need that extra reference to the original object
SGDatabaseReference* databaseReference;
databaseReference = new SGDatabaseReference(model);
CopyOp::CopyFlags flags = CopyOp::DEEP_COPY_ALL;
flags &= ~CopyOp::DEEP_COPY_TEXTURES;
flags &= ~CopyOp::DEEP_COPY_IMAGES;
flags &= ~CopyOp::DEEP_COPY_STATESETS;
flags &= ~CopyOp::DEEP_COPY_STATEATTRIBUTES;
flags &= ~CopyOp::DEEP_COPY_ARRAYS;
flags &= ~CopyOp::DEEP_COPY_PRIMITIVES;
// This will safe display lists ...
flags &= ~CopyOp::DEEP_COPY_DRAWABLES;
flags &= ~CopyOp::DEEP_COPY_SHAPES;
osg::Node* res = CopyOp(flags)(model);
res->addObserver(databaseReference);
// Update liveries
TextureUpdateVisitor liveryUpdate(opt->getDatabasePathList());
res->accept(liveryUpdate);
// Copy the userdata fields, still sharing the boundingvolumes,
// but introducing new data for velocities.
UserDataCopyVisitor userDataCopyVisitor;
res->accept(userDataCopyVisitor);
return res;
}
string OSGSubstitutePolicy::substitute(const string& name,
const ReaderWriter::Options* opt)
{
@@ -509,10 +350,6 @@ ReaderWriter::ReadResult
ModelRegistry::readNode(const string& fileName,
const ReaderWriter::Options* opt)
{
ScopedLock<ReentrantMutex> lock(readerMutex);
// XXX Workaround for OSG plugin bug.
// Registry* registry = Registry::instance();
ReaderWriter::ReadResult res;
CallbackMap::iterator iter
= nodeCallbackMap.find(getFileExtension(fileName));
@@ -599,7 +436,7 @@ struct ACProcessPolicy {
};
typedef ModelRegistryCallback<ACProcessPolicy, DefaultCachePolicy,
ACOptimizePolicy, DefaultCopyPolicy,
ACOptimizePolicy,
OSGSubstitutePolicy, BuildLeafBVHPolicy>
ACCallback;

View File

@@ -19,8 +19,6 @@
#ifndef _SG_MODELREGISTRY_HXX
#define _SG_MODELREGISTRY_HXX 1
#include <OpenThreads/ReentrantMutex>
#include <osg/ref_ptr>
#include <osg/Node>
#include <osgDB/FileUtils>
@@ -61,12 +59,12 @@ namespace simgear
// readNode function is specified as a template with a bunch of
// pluggable (and predefined) policies.
template <typename ProcessPolicy, typename CachePolicy, typename OptimizePolicy,
typename CopyPolicy, typename SubstitutePolicy, typename BVHPolicy>
typename SubstitutePolicy, typename BVHPolicy>
class ModelRegistryCallback : public osgDB::Registry::ReadFileCallback {
public:
ModelRegistryCallback(const std::string& extension) :
_processPolicy(extension), _cachePolicy(extension),
_optimizePolicy(extension), _copyPolicy(extension),
_optimizePolicy(extension),
_substitutePolicy(extension), _bvhPolicy(extension)
{
}
@@ -100,9 +98,7 @@ public:
_bvhPolicy.buildBVH(fileName, optimizedNode.get());
_cachePolicy.addToCache(fileName, optimizedNode.get());
}
osg::ref_ptr<osg::Node> copyNode;
copyNode = _copyPolicy.copy(optimizedNode.get(), fileName, opt);
return ReaderWriter::ReadResult(copyNode);
return ReaderWriter::ReadResult(optimizedNode);
}
protected:
static osgDB::ReaderWriter::ReadResult
@@ -120,7 +116,6 @@ protected:
ProcessPolicy _processPolicy;
CachePolicy _cachePolicy;
OptimizePolicy _optimizePolicy;
CopyPolicy _copyPolicy;
SubstitutePolicy _substitutePolicy;
BVHPolicy _bvhPolicy;
virtual ~ModelRegistryCallback() {}
@@ -169,21 +164,6 @@ struct NoOptimizePolicy {
}
};
struct DefaultCopyPolicy {
DefaultCopyPolicy(const std::string& extension) {}
osg::Node* copy(osg::Node* node, const std::string& fileName,
const osgDB::ReaderWriter::Options* opt);
};
struct NoCopyPolicy {
NoCopyPolicy(const std::string& extension) {}
osg::Node* copy(osg::Node* node, const std::string& fileName,
const osgDB::ReaderWriter::Options* opt)
{
return node;
}
};
struct OSGSubstitutePolicy {
OSGSubstitutePolicy(const std::string& extension) {}
std::string substitute(const std::string& name,
@@ -215,7 +195,7 @@ struct NoBuildBVHPolicy {
};
typedef ModelRegistryCallback<DefaultProcessPolicy, DefaultCachePolicy,
OptimizeModelPolicy, DefaultCopyPolicy,
OptimizeModelPolicy,
OSGSubstitutePolicy, BuildLeafBVHPolicy>
DefaultCallback;
@@ -243,15 +223,12 @@ protected:
CallbackMap imageCallbackMap;
CallbackMap nodeCallbackMap;
osg::ref_ptr<DefaultCallback> _defaultCallback;
// Protect against simultaneous calls from main thread (MP models)
// and pager thread.
OpenThreads::ReentrantMutex readerMutex;
};
// Callback that only loads the file without any caching or
// postprocessing.
typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy,
NoOptimizePolicy, NoCopyPolicy,
NoOptimizePolicy,
NoSubstitutePolicy, BuildLeafBVHPolicy>
LoadOnlyCallback;

View File

@@ -22,8 +22,14 @@
#include <simgear/props/condition.hxx>
#include <simgear/props/props.hxx>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/material/Pass.hxx>
#include <simgear/scene/material/Technique.hxx>
#include <simgear/scene/model/model.hxx>
using namespace std;
namespace {
/**
* Get a color from properties.
@@ -209,7 +215,25 @@ public:
virtual void apply(osg::Geode& node)
{
maybeGetMaterialValues(node.getStateSet());
using namespace simgear;
EffectGeode* eg = dynamic_cast<EffectGeode*>(&node);
if (eg) {
const Effect* effect = eg->getEffect();
if (effect)
for (vector<osg::ref_ptr<Technique> >::const_iterator itr
= effect->techniques.begin(), end = effect->techniques.end();
itr != end;
++itr) {
const Technique* tniq = itr->get();
for (vector<osg::ref_ptr<Pass> >::const_iterator pitr
= tniq->passes.begin(), pend = tniq->passes.end();
pitr != pend;
++pitr)
maybeGetMaterialValues(pitr->get());
}
} else {
maybeGetMaterialValues(node.getStateSet());
}
int numDrawables = node.getNumDrawables();
for (int i = 0; i < numDrawables; i++) {
osg::Geometry* geom = dynamic_cast<osg::Geometry*>(node.getDrawable(i));
@@ -230,18 +254,20 @@ public:
}
}
void maybeGetMaterialValues(osg::StateSet* stateSet)
void maybeGetMaterialValues(const osg::StateSet* stateSet)
{
if (!stateSet)
return;
osg::Material* nodeMat
= dynamic_cast<osg::Material*>(stateSet->getAttribute(osg::StateAttribute::MATERIAL));
const osg::Material* nodeMat
= dynamic_cast<const osg::Material*>(stateSet
->getAttribute(osg::StateAttribute
::MATERIAL));
if (!nodeMat)
return;
material = nodeMat;
}
osg::ref_ptr<osg::Material> material;
osg::ref_ptr<const osg::Material> material;
osg::Vec4 ambientDiffuse;
};

View File

@@ -19,6 +19,8 @@
#endif
#include <osgDB/ReadFile>
#include <osgDB/Input>
#include <osgDB/ParameterOutput>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/OSGVersion.hxx>
@@ -70,3 +72,19 @@ void SGPagedLOD::forceLoad(osgDB::DatabasePager *dbp)
_readerWriterOptions.get());
}
bool SGPagedLOD_writeLocalData(const Object& obj, osgDB::Output& fw)
{
return true;
}
namespace
{
osgDB::RegisterDotOsgWrapperProxy sgPagedLODProxy
(
new SGPagedLOD,
"simgear::SGPagedLOD",
"Object Node LOD PagedLOD SGPagedLOD Group",
0,
&SGPagedLOD_writeLocalData
);
}

View File

@@ -20,6 +20,13 @@
# include <simgear_config.h>
#endif
#include <algorithm>
//yuck
#include <cstring>
#include <boost/bind.hpp>
#include <osg/Geode>
#include <osg/MatrixTransform>
#include <osgDB/WriteFile>
#include <osgDB/Registry>
@@ -43,7 +50,9 @@
#include "model.hxx"
#include "SGText.hxx"
using namespace std;
using namespace simgear;
using namespace osg;
static osg::Node *
sgLoad3DModel_internal(const std::string& path,
@@ -105,6 +114,71 @@ private:
SGSharedPtr<SGCondition> mCondition;
};
// Little helper class that holds an extra reference to a
// loaded 3d model.
// Since we clone all structural nodes from our 3d models,
// the database pager will only see one single reference to
// top node of the model and expire it relatively fast.
// We attach that extra reference to every model cloned from
// a base model in the pager. When that cloned model is deleted
// this extra reference is deleted too. So if there are no
// cloned models left the model will expire.
namespace {
class SGDatabaseReference : public osg::Observer
{
public:
SGDatabaseReference(osg::Referenced* referenced) :
mReferenced(referenced)
{ }
virtual void objectDeleted(void*)
{
mReferenced = 0;
}
private:
osg::ref_ptr<osg::Referenced> mReferenced;
};
void makeEffectAnimations(PropertyList& animation_nodes,
PropertyList& effect_nodes)
{
for (PropertyList::iterator itr = animation_nodes.begin();
itr != animation_nodes.end();
++itr) {
SGPropertyNode* animProp = itr->ptr();
SGPropertyNode* typeProp = animProp->getChild("type");
if (!typeProp || strcmp(typeProp->getStringValue(), "shader"))
continue;
SGPropertyNode* shaderProp = animProp->getChild("shader");
if (!shaderProp || strcmp(shaderProp->getStringValue(), "chrome"))
continue;
*itr = 0;
SGPropertyNode* textureProp = animProp->getChild("texture");
if (!textureProp)
continue;
SGPropertyNode_ptr effectProp = new SGPropertyNode();
makeChild(effectProp.ptr(), "inherits-from")
->setValue("Effects/chrome");
SGPropertyNode* paramsProp = makeChild(effectProp.get(), "parameters");
makeChild(paramsProp, "chrome-texture")
->setValue(textureProp->getStringValue());
PropertyList objectNameNodes = animProp->getChildren("object-name");
for (PropertyList::iterator objItr = objectNameNodes.begin(),
end = objectNameNodes.end();
objItr != end;
++objItr)
effectProp->addChild("object-name")
->setStringValue((*objItr)->getStringValue());
effect_nodes.push_back(effectProp);
}
animation_nodes.erase(remove_if(animation_nodes.begin(),
animation_nodes.end(),
!boost::bind(&SGPropertyNode_ptr::valid,
_1)),
animation_nodes.end());
}
}
static osg::Node *
sgLoad3DModel_internal(const string &path,
const osgDB::ReaderWriter::Options* options_,
@@ -167,6 +241,9 @@ sgLoad3DModel_internal(const string &path,
SGPropertyNode *mp = props->getNode("multiplay");
if (mp && prop_root && prop_root->getParent())
copyProperties(mp, prop_root);
} else {
SG_LOG(SG_INPUT, SG_DEBUG, "model without wrapper: "
<< modelpath.str());
}
osg::ref_ptr<SGReaderWriterXMLOptions> options
@@ -181,10 +258,31 @@ sgLoad3DModel_internal(const string &path,
texturepath = texturepath.dir();
options->setDatabasePath(texturepath.str());
model = osgDB::readNodeFile(modelpath.str(), options.get());
if (model == 0)
osgDB::ReaderWriter::ReadResult modelResult
= osgDB::Registry::instance()->readNode(modelpath.str(),
options.get());
if (!modelResult.validNode())
throw sg_io_exception("Failed to load 3D model",
sg_location(modelpath.str()));
model = copyModel(modelResult.getNode());
// Add an extra reference to the model stored in the database.
// That is to avoid expiring the object from the cache even if
// it is still in use. Note that the object cache will think
// that a model is unused if the reference count is 1. If we
// clone all structural nodes here we need that extra
// reference to the original object
SGDatabaseReference* databaseReference;
databaseReference = new SGDatabaseReference(modelResult.getNode());
model->addObserver(databaseReference);
// Update liveries
TextureUpdateVisitor liveryUpdate(options->getDatabasePathList());
model->accept(liveryUpdate);
// Copy the userdata fields, still sharing the boundingvolumes,
// but introducing new data for velocities.
UserDataCopyVisitor userDataCopyVisitor;
model->accept(userDataCopyVisitor);
}
model->setName(modelpath.str());
@@ -224,7 +322,8 @@ sgLoad3DModel_internal(const string &path,
SGPath submodelpath;
osg::ref_ptr<osg::Node> submodel;
string submodelFileName = sub_props->getStringValue("path");
if ( submodelFileName.size() > 2 && submodelFileName.substr( 0, 2 ) == "./" ) {
if (submodelFileName.size() > 2
&& !submodelFileName.compare(0, 2, "./" )) {
submodelpath = modelpath.dir();
submodelpath.append( submodelFileName.substr( 2 ) );
} else {
@@ -242,7 +341,7 @@ sgLoad3DModel_internal(const string &path,
throw;
}
osg::ref_ptr<osg::Node> submodel_final=submodel.get();
osg::ref_ptr<osg::Node> submodel_final = submodel;
SGPropertyNode *offs = sub_props->getNode("offsets", false);
if (offs) {
osg::Matrix res_matrix;
@@ -264,7 +363,7 @@ sgLoad3DModel_internal(const string &path,
offs->getDoubleValue("z-m", 0));
align->setMatrix(res_matrix*tmat);
align->addChild(submodel.get());
submodel_final=align.get();
submodel_final = align;
}
submodel_final->setName(sub_props->getStringValue("name", ""));
@@ -313,9 +412,15 @@ sgLoad3DModel_internal(const string &path,
prop_root,
options.get()));
}
std::vector<SGPropertyNode_ptr> animation_nodes;
animation_nodes = props->getChildren("animation");
PropertyList effect_nodes = props->getChildren("effect");
PropertyList animation_nodes = props->getChildren("animation");
// Some material animations (eventually all) are actually effects.
makeEffectAnimations(animation_nodes, effect_nodes);
{
ref_ptr<Node> modelWithEffects
= instantiateEffects(group.get(), effect_nodes, options.get());
group = static_cast<Group*>(modelWithEffects.get());
}
for (unsigned i = 0; i < animation_nodes.size(); ++i)
/// OSGFIXME: duh, why not only model?????
SGAnimation::animate(group.get(), animation_nodes[i], prop_root,

View File

@@ -30,6 +30,10 @@
#include <osg/Texture2D>
#include <osg/Transform>
#include <osgDB/ReadFile>
#include <osgDB/Registry>
#include <osgDB/Input>
#include <osgDB/ParameterOutput>
#include <simgear/math/interpolater.hxx>
#include <simgear/props/condition.hxx>
@@ -971,6 +975,15 @@ osg::StateSet* getNormalizeStateSet()
class SGDistScaleAnimation::Transform : public osg::Transform {
public:
Transform() : _min_v(0.0), _max_v(0.0), _factor(0.0), _offset(0.0) {}
Transform(const Transform& rhs,
const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
: osg::Transform(rhs, copyOp), _table(rhs._table), _center(rhs._center),
_min_v(rhs._min_v), _max_v(rhs._max_v), _factor(rhs._factor),
_offset(rhs._offset)
{
}
META_Node(simgear, SGDistScaleAnimation::Transform);
Transform(const SGPropertyNode* configNode)
{
setName(configNode->getStringValue("name", "dist scale animation"));
@@ -1018,6 +1031,16 @@ public:
return true;
}
static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
{
const Transform& trans = static_cast<const Transform&>(obj);
fw.indent() << "center " << trans._center << "\n";
fw.indent() << "min_v " << trans._min_v << "\n";
fw.indent() << "max_v " << trans._max_v << "\n";
fw.indent() << "factor " << trans._factor << "\n";
fw.indent() << "offset " << trans._offset << "\n";
return true;
}
private:
double computeScaleFactor(osg::NodeVisitor* nv) const
{
@@ -1061,6 +1084,17 @@ SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
return transform;
}
namespace
{
osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
(
new SGDistScaleAnimation::Transform,
"SGDistScaleAnimation::Transform",
"Object Node Transform SGDistScaleAnimation::Transform Group",
0,
&SGDistScaleAnimation::Transform::writeLocalData
);
}
////////////////////////////////////////////////////////////////////////
// Implementation of flash animation
@@ -1068,6 +1102,19 @@ SGDistScaleAnimation::createAnimationGroup(osg::Group& parent)
class SGFlashAnimation::Transform : public osg::Transform {
public:
Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
_max_v(0.0), _two_sides(false)
{}
Transform(const Transform& rhs,
const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
: osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
_power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
_min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
{
}
META_Node(simgear, SGFlashAnimation::Transform);
Transform(const SGPropertyNode* configNode)
{
setReferenceFrame(RELATIVE_RF);
@@ -1124,6 +1171,21 @@ public:
return true;
}
static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
{
const Transform& trans = static_cast<const Transform&>(obj);
fw.indent() << "center " << trans._center[0] << " "
<< trans._center[1] << " " << trans._center[2] << " " << "\n";
fw.indent() << "axis " << trans._axis[0] << " "
<< trans._axis[1] << " " << trans._axis[2] << " " << "\n";
fw.indent() << "power " << trans._power << " \n";
fw.indent() << "min_v " << trans._min_v << "\n";
fw.indent() << "max_v " << trans._max_v << "\n";
fw.indent() << "factor " << trans._factor << "\n";
fw.indent() << "offset " << trans._offset << "\n";
fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
return true;
}
private:
double computeScaleFactor(osg::NodeVisitor* nv) const
{
@@ -1178,13 +1240,29 @@ SGFlashAnimation::createAnimationGroup(osg::Group& parent)
return transform;
}
namespace
{
osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
(
new SGFlashAnimation::Transform,
"SGFlashAnimation::Transform",
"Object Node Transform SGFlashAnimation::Transform Group",
0,
&SGFlashAnimation::Transform::writeLocalData
);
}
////////////////////////////////////////////////////////////////////////
// Implementation of flash animation
// Implementation of billboard animation
////////////////////////////////////////////////////////////////////////
class SGBillboardAnimation::Transform : public osg::Transform {
public:
Transform() : _spherical(true) {}
Transform(const Transform& rhs,
const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
: osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
META_Node(simgear, SGBillboardAnimation::Transform);
Transform(const SGPropertyNode* configNode) :
_spherical(configNode->getBoolValue("spherical", true))
{
@@ -1221,7 +1299,13 @@ public:
// Hmm, don't yet know how to get that back ...
return false;
}
static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
{
const Transform& trans = static_cast<const Transform&>(obj);
fw.indent() << (trans._spherical ? "true" : "false") << "\n";
return true;
}
private:
bool _spherical;
};
@@ -1241,6 +1325,17 @@ SGBillboardAnimation::createAnimationGroup(osg::Group& parent)
return transform;
}
namespace
{
osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
(
new SGBillboardAnimation::Transform,
"SGBillboardAnimation::Transform",
"Object Node Transform SGBillboardAnimation::Transform Group",
0,
&SGBillboardAnimation::Transform::writeLocalData
);
}
////////////////////////////////////////////////////////////////////////
// Implementation of a range animation

View File

@@ -174,7 +174,6 @@ public:
SGDistScaleAnimation(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot);
virtual osg::Group* createAnimationGroup(osg::Group& parent);
private:
class Transform;
};
@@ -188,7 +187,7 @@ public:
SGFlashAnimation(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot);
virtual osg::Group* createAnimationGroup(osg::Group& parent);
private:
public:
class Transform;
};
@@ -202,7 +201,6 @@ public:
SGBillboardAnimation(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot);
virtual osg::Group* createAnimationGroup(osg::Group& parent);
private:
class Transform;
};

View File

@@ -7,14 +7,28 @@
#include <simgear_config.h>
#endif
#include <utility>
#include <boost/foreach.hpp>
#include <osg/ref_ptr>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/ReaderWriter>
#include <osgDB/ReadFile>
#include <osgDB/SharedStateManager>
#include <simgear/math/SGMath.hxx>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx>
#include <simgear/scene/util/SGSceneUserData.hxx>
#include <simgear/scene/util/CopyOp.hxx>
#include <simgear/scene/util/SplicingVisitor.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/structure/Singleton.hxx>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/props/condition.hxx>
@@ -60,4 +74,262 @@ SGLoadTexture2D(bool staticTexture, const std::string& path,
return texture.release();
}
namespace simgear
{
using namespace std;
using namespace osg;
using simgear::CopyOp;
Node* copyModel(Node* model)
{
const CopyOp::CopyFlags flags = (CopyOp::DEEP_COPY_ALL
& ~CopyOp::DEEP_COPY_TEXTURES
& ~CopyOp::DEEP_COPY_IMAGES
& ~CopyOp::DEEP_COPY_STATESETS
& ~CopyOp::DEEP_COPY_STATEATTRIBUTES
& ~CopyOp::DEEP_COPY_ARRAYS
& ~CopyOp::DEEP_COPY_PRIMITIVES
// This will preserve display lists ...
& ~CopyOp::DEEP_COPY_DRAWABLES
& ~CopyOp::DEEP_COPY_SHAPES);
return (CopyOp(flags))(model);
}
TextureUpdateVisitor::TextureUpdateVisitor(const osgDB::FilePathList& pathList) :
NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
_pathList(pathList)
{
}
void TextureUpdateVisitor::apply(Node& node)
{
StateSet* stateSet = cloneStateSet(node.getStateSet());
if (stateSet)
node.setStateSet(stateSet);
traverse(node);
}
void TextureUpdateVisitor::apply(Drawable& drawable)
{
StateSet* stateSet = cloneStateSet(drawable.getStateSet());
if (stateSet)
drawable.setStateSet(stateSet);
}
Texture2D* TextureUpdateVisitor::textureReplace(int unit, const StateAttribute* attr)
{
using namespace osgDB;
const Texture2D* texture = dynamic_cast<const Texture2D*>(attr);
if (!texture)
return 0;
const Image* image = texture->getImage();
const string* fullFilePath = 0;
if (image) {
// The currently loaded file name
fullFilePath = &image->getFileName();
} else {
fullFilePath = &texture->getName();
}
// The short name
string fileName = getSimpleFileName(*fullFilePath);
if (fileName.empty())
return 0;
// The name that should be found with the current database path
string fullLiveryFile = findFileInPath(fileName, _pathList);
// If it is empty or they are identical then there is nothing to do
if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
return 0;
Image* newImage = readImageFile(fullLiveryFile);
if (!newImage)
return 0;
CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
if (!newTexture) {
return 0;
} else {
newTexture->setImage(newImage);
return newTexture;
}
}
StateSet* TextureUpdateVisitor::cloneStateSet(const StateSet* stateSet)
{
typedef std::pair<int, Texture2D*> Tex2D;
vector<Tex2D> newTextures;
StateSet* result = 0;
if (!stateSet)
return 0;
int numUnits = stateSet->getTextureAttributeList().size();
if (numUnits > 0) {
for (int i = 0; i < numUnits; ++i) {
const StateAttribute* attr
= stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
Texture2D* newTexture = textureReplace(i, attr);
if (newTexture)
newTextures.push_back(Tex2D(i, newTexture));
}
if (!newTextures.empty()) {
result = static_cast<StateSet*>(stateSet->clone(CopyOp()));
for (vector<Tex2D>::iterator i = newTextures.begin();
i != newTextures.end();
++i) {
result->setTextureAttribute(i->first, i->second);
}
}
}
return result;
}
UserDataCopyVisitor::UserDataCopyVisitor() :
NodeVisitor(NodeVisitor::NODE_VISITOR,
NodeVisitor::TRAVERSE_ALL_CHILDREN)
{
}
void UserDataCopyVisitor::apply(Node& node)
{
ref_ptr<SGSceneUserData> userData;
userData = SGSceneUserData::getSceneUserData(&node);
if (userData.valid()) {
SGSceneUserData* newUserData = new SGSceneUserData(*userData);
newUserData->setVelocity(0);
node.setUserData(newUserData);
}
node.traverse(*this);
}
namespace
{
class MakeEffectVisitor : public SplicingVisitor
{
public:
typedef std::map<string, SGPropertyNode_ptr> EffectMap;
using SplicingVisitor::apply;
MakeEffectVisitor(const osgDB::ReaderWriter::Options* options = 0)
: _options(options)
{
}
virtual void apply(osg::Group& node);
virtual void apply(osg::Geode& geode);
EffectMap& getEffectMap() { return _effectMap; }
const EffectMap& getEffectMap() const { return _effectMap; }
void setDefaultEffect(SGPropertyNode* effect)
{
_currentEffectParent = effect;
}
SGPropertyNode* getDefaultEffect() { return _currentEffectParent; }
protected:
EffectMap _effectMap;
SGPropertyNode_ptr _currentEffectParent;
osg::ref_ptr<const osgDB::ReaderWriter::Options> _options;
};
void MakeEffectVisitor::apply(osg::Group& node)
{
SGPropertyNode_ptr savedEffectRoot;
const string& nodeName = node.getName();
bool restoreEffect = false;
if (!nodeName.empty()) {
EffectMap::iterator eitr = _effectMap.find(nodeName);
if (eitr != _effectMap.end()) {
savedEffectRoot = _currentEffectParent;
_currentEffectParent = eitr->second;
restoreEffect = true;
}
}
SplicingVisitor::apply(node);
// If a new node was created, copy the user data too.
ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&node);
if (userData.valid() && _childStack.back().back().get() != &node)
_childStack.back().back()->setUserData(new SGSceneUserData(*userData));
if (restoreEffect)
_currentEffectParent = savedEffectRoot;
}
void MakeEffectVisitor::apply(osg::Geode& geode)
{
if (pushNode(getNewNode(geode)))
return;
osg::StateSet* ss = geode.getStateSet();
if (!ss) {
pushNode(&geode);
return;
}
SGPropertyNode_ptr ssRoot = new SGPropertyNode;
makeParametersFromStateSet(ssRoot, ss);
SGPropertyNode_ptr effectRoot = new SGPropertyNode;
effect::mergePropertyTrees(effectRoot, ssRoot, _currentEffectParent);
Effect* effect = makeEffect(effectRoot, true, _options);
EffectGeode* eg = dynamic_cast<EffectGeode*>(&geode);
if (eg) {
eg->setEffect(effect);
} else {
eg = new EffectGeode;
eg->setEffect(effect);
ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&geode);
if (userData.valid())
eg->setUserData(new SGSceneUserData(*userData));
for (int i = 0; i < geode.getNumDrawables(); ++i)
eg->addDrawable(geode.getDrawable(i));
}
pushResultNode(&geode, eg);
}
}
namespace
{
class DefaultEffect : public simgear::Singleton<DefaultEffect>
{
public:
DefaultEffect()
{
_effect = new SGPropertyNode;
makeChild(_effect.ptr(), "inherits-from")
->setStringValue("Effects/model-default");
}
virtual ~DefaultEffect() {}
SGPropertyNode* getEffect() { return _effect.ptr(); }
protected:
SGPropertyNode_ptr _effect;
};
}
ref_ptr<Node> instantiateEffects(osg::Node* modelGroup,
PropertyList& effectProps,
const osgDB::ReaderWriter::Options* options)
{
SGPropertyNode_ptr defaultEffectPropRoot;
MakeEffectVisitor visitor(options);
MakeEffectVisitor::EffectMap& emap = visitor.getEffectMap();
for (PropertyList::iterator itr = effectProps.begin(),
end = effectProps.end();
itr != end;
++itr)
{
SGPropertyNode_ptr configNode = *itr;
std::vector<SGPropertyNode_ptr> objectNames =
configNode->getChildren("object-name");
SGPropertyNode* defaultNode = configNode->getChild("default");
if (defaultNode && defaultNode->getValue<bool>())
defaultEffectPropRoot = configNode;
BOOST_FOREACH(SGPropertyNode_ptr objNameNode, objectNames) {
emap.insert(make_pair(objNameNode->getStringValue(), configNode));
}
configNode->removeChild("default");
configNode->removeChildren("object-name");
}
if (!defaultEffectPropRoot)
defaultEffectPropRoot = DefaultEffect::instance()->getEffect();
visitor.setDefaultEffect(defaultEffectPropRoot.ptr());
modelGroup->accept(visitor);
osg::NodeList& result = visitor.getResults();
return ref_ptr<Node>(result[0].get());
}
}
// end of model.cxx

View File

@@ -20,6 +20,8 @@
#include <osgDB/ReaderWriter>
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
#include <simgear/scene/util/NodeAndDrawableVisitor.hxx>
osg::Texture2D*
SGLoadTexture2D(bool staticTexture, const std::string& path,
@@ -54,4 +56,64 @@ SGLoadTexture2D(bool staticTexture, const SGPath& path,
mipmaplevels);
}
namespace simgear
{
osg::Node* copyModel(osg::Node* model);
// Change the StateSets of a model to hold different textures based on
// a livery path.
class TextureUpdateVisitor : public NodeAndDrawableVisitor
{
public:
TextureUpdateVisitor(const osgDB::FilePathList& pathList);
virtual void apply(osg::Node& node);
virtual void apply(osg::Drawable& drawable);
// Copied from Mathias' earlier SGTextureUpdateVisitor
protected:
osg::Texture2D* textureReplace(int unit, const osg::StateAttribute* attr);
osg::StateSet* cloneStateSet(const osg::StateSet* stateSet);
private:
osgDB::FilePathList _pathList;
};
// Create new userdata structs in a copied model.
// The BVH trees are shared with the original model, but the velocity fields
// should usually be distinct fields for distinct models.
class UserDataCopyVisitor : public osg::NodeVisitor
{
public:
UserDataCopyVisitor();
virtual void apply(osg::Node& node);
};
/**
* Transform an OSG subgraph by substituting Effects and EffectGeodes
* for osg::Geodes with osg::StateSets. This is only guaranteed to
* work for models prouced by the .ac loader.
*
* returns a copy if any nodes are changed
*/
osg::ref_ptr<osg::Node>
instantiateEffects(osg::Node* model,
PropertyList& effectProps,
const osgDB::ReaderWriter::Options* options);
/**
* Transform an OSG subgraph by substituting the Effects and
* EffectGeodes for osg::Geodes with osg::StateSets, inheriting from
* the default model effect. This is only guaranteed to work for
* models prouced by the .ac loader.
*
* returns a copy if any nodes are changed
*/
inline osg::ref_ptr<osg::Node>
instantiateEffects(osg::Node* model,
const osgDB::ReaderWriter::Options* options)
{
PropertyList effectProps;
return instantiateEffects(model, effectProps, options);
}
}
#endif // __MODEL_HXX

View File

@@ -19,6 +19,8 @@
# include <simgear_config.h>
#endif
#include <boost/algorithm/string.hpp>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgDB/Registry>
@@ -26,6 +28,7 @@
#include <simgear/constants.h>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/scene/model/model.hxx>
#include <simgear/scene/model/ModelRegistry.hxx>
#include "SGPagedLOD.hxx"
@@ -59,6 +62,21 @@ SGModelLib::~SGModelLib()
{
}
namespace
{
osg::Node* loadFile(const string& path, osgDB::ReaderWriter::Options* options)
{
using namespace osg;
using namespace osgDB;
ref_ptr<Node> model = readRefNodeFile(path, options);
if (!model)
return 0;
if (boost::iends_with(path, ".ac"))
model = instantiateEffects(model.get(), options);
return model.release();
}
}
osg::Node*
SGModelLib::loadModel(const string &path,
SGPropertyNode *prop_root,
@@ -67,7 +85,7 @@ SGModelLib::loadModel(const string &path,
osg::ref_ptr<SGReaderWriterXMLOptions> opt = new SGReaderWriterXMLOptions(*(osgDB::Registry::instance()->getOptions()));
opt->setPropRoot(prop_root);
opt->setModelData(data);
osg::Node *n = readNodeFile(path, opt.get());
osg::Node *n = loadFile(path, opt.get());
if (n && n->getName().empty())
n->setName("Direct loaded model \"" + path + "\"");
return n;
@@ -82,7 +100,7 @@ SGModelLib::loadModel(const string &path,
osg::ref_ptr<SGReaderWriterXMLOptions> opt = new SGReaderWriterXMLOptions(*(osgDB::Registry::instance()->getOptions()));
opt->setPropRoot(prop_root);
opt->setLoadPanel(pf);
return readNodeFile(path, opt.get());
return loadFile(path, opt.get());
}
osg::Node*

View File

@@ -121,7 +121,7 @@ void CloudShaderGeometry::drawImplementation(RenderInfo& renderInfo) const
}
}
void CloudShaderGeometry::addSprite(SGVec3f& p, int tx, int ty,
void CloudShaderGeometry::addSprite(const SGVec3f& p, int tx, int ty,
float w, float h,
float s, float cull, float cloud_height)
{

View File

@@ -69,7 +69,8 @@ class CloudShaderGeometry : public osg::Drawable
META_Object(flightgear, CloudShaderGeometry);
struct CloudSprite {
CloudSprite(SGVec3f& p, int tx, int ty, float w, float h, float s, float ch) :
CloudSprite(const SGVec3f& p, int tx, int ty, float w, float h,
float s, float ch) :
position(p), texture_index_x(tx), texture_index_y(ty), width(w), height(h), shade(s), cloud_height(ch)
{ }
@@ -106,7 +107,8 @@ class CloudShaderGeometry : public osg::Drawable
_geometry = geometry;
}
void addSprite(SGVec3f& p, int tx, int ty, float w, float h, float s, float cull, float cloud_height);
void addSprite(const SGVec3f& p, int tx, int ty, float w, float h,
float s, float cull, float cloud_height);
osg::ref_ptr<osg::Drawable> _geometry;

View File

@@ -71,7 +71,6 @@ float SGCloudField::fieldSize = 50000.0f;
double SGCloudField::timer_dt = 0.0;
float SGCloudField::view_distance = 20000.0f;
sgVec3 SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y;
SGCloudField::StateSetMap SGCloudField::cloudTextureMap;
// reposition the cloud layer at the specified origin and orientation
bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat,

View File

@@ -137,9 +137,6 @@ public:
void applyCoverage(void);
void applyVisRange(void);
typedef std::map<std::string, osg::ref_ptr<osg::StateSet> > StateSetMap;
static StateSetMap cloudTextureMap;
static osg::Fog* getFog()
{
return CloudFog::instance()->fog.get();

View File

@@ -44,6 +44,7 @@
#include <simgear/math/sg_random.h>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/PathOptions.hxx>
#include <simgear/props/props.hxx>
#include <simgear/scene/model/model.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx>
#include <simgear/scene/util/SGUpdateVisitor.hxx>
@@ -60,75 +61,14 @@
using namespace simgear;
using namespace osg;
typedef std::map<std::string, osg::ref_ptr<osg::StateSet> > StateSetMap;
namespace
{
typedef std::map<std::string, osg::ref_ptr<Effect> > EffectMap;
EffectMap effectMap;
}
StateSetMap cloudTextureMap;
double SGNewCloud::sprite_density = 1.0;
static char vertexShaderSource[] =
"#version 120\n"
"\n"
"varying float fogFactor;\n"
"attribute vec3 usrAttr1;\n"
"attribute vec3 usrAttr2;\n"
"float textureIndexX = usrAttr1.r;\n"
"float textureIndexY = usrAttr1.g;\n"
"float wScale = usrAttr1.b;\n"
"float hScale = usrAttr2.r;\n"
"float shade = usrAttr2.g;\n"
"float cloud_height = usrAttr2.b;\n"
"void main(void)\n"
"{\n"
" gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndexX, textureIndexY, 0.0, 0.0);\n"
" vec4 ep = gl_ModelViewMatrixInverse * vec4(0.0,0.0,0.0,1.0);\n"
" vec4 l = gl_ModelViewMatrixInverse * vec4(0.0,0.0,1.0,1.0);\n"
" vec3 u = normalize(ep.xyz - l.xyz);\n"
// Find a rotation matrix that rotates 1,0,0 into u. u, r and w are
// the columns of that matrix.
" vec3 absu = abs(u);\n"
" vec3 r = normalize(vec3(-u.y, u.x, 0));\n"
" vec3 w = cross(u, r);\n"
// Do the matrix multiplication by [ u r w pos]. Assume no
// scaling in the homogeneous component of pos.
" gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
" gl_Position.xyz = gl_Vertex.x * u;\n"
" gl_Position.xyz += gl_Vertex.y * r * wScale;\n"
" gl_Position.xyz += gl_Vertex.z * w * hScale;\n"
" gl_Position.xyz += gl_Color.xyz;\n"
// Determine a lighting normal based on the vertex position from the
// center of the cloud, so that sprite on the opposite side of the cloud to the sun are darker.
" float n = dot(normalize(- gl_LightSource[0].position.xyz), normalize(mat3x3(gl_ModelViewMatrix) * (- gl_Position.xyz)));\n"
// Determine the position - used for fog and shading calculations
" vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Position);\n"
" float fogCoord = abs(ecPosition.z);\n"
" float fract = smoothstep(0.0, cloud_height, gl_Position.z + cloud_height);\n"
// Final position of the sprite
" gl_Position = gl_ModelViewProjectionMatrix * gl_Position;\n"
// Determine the shading of the sprite based on its vertical position and position relative to the sun.
" n = min(smoothstep(-0.5, 0.0, n), fract);\n"
// Determine the shading based on a mixture from the backlight to the front
" vec4 backlight = gl_LightSource[0].diffuse * shade;\n"
" gl_FrontColor = mix(backlight, gl_LightSource[0].diffuse, n);\n"
" gl_FrontColor += gl_FrontLightModelProduct.sceneColor;\n"
// As we get within 100m of the sprite, it is faded out. Equally at large distances it also fades out.
" gl_FrontColor.a = min(smoothstep(10.0, 100.0, fogCoord), 1 - smoothstep(15000.0, 20000.0, fogCoord));\n"
" gl_BackColor = gl_FrontColor;\n"
// Fog doesn't affect clouds as much as other objects.
" fogFactor = exp( -gl_Fog.density * fogCoord * 0.5);\n"
" fogFactor = clamp(fogFactor, 0.0, 1.0);\n"
"}\n";
static char fragmentShaderSource[] =
"uniform sampler2D baseTexture; \n"
"varying float fogFactor;\n"
"\n"
"void main(void)\n"
"{\n"
" vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);\n"
" vec4 finalColor = base * gl_Color;\n"
" gl_FragColor.rgb = mix(gl_Fog.color.rgb, finalColor.rgb, fogFactor );\n"
" gl_FragColor.a = mix(0.0, finalColor.a, fogFactor);\n"
"}\n";
SGNewCloud::SGNewCloud(string type,
const SGPath &tex_path,
@@ -160,75 +100,24 @@ SGNewCloud::SGNewCloud(string type,
texture(tex),
name(type)
{
// Create a new StateSet for the texture, if required.
StateSetMap::iterator iter = SGCloudField::cloudTextureMap.find(texture);
if (iter == SGCloudField::cloudTextureMap.end()) {
stateSet = new osg::StateSet;
osg::ref_ptr<osgDB::ReaderWriter::Options> options = makeOptionsFromPath(tex_path);
osg::Texture2D *tex = new osg::Texture2D;
tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP );
tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP );
tex->setImage(osgDB::readImageFile(texture, options.get()));
StateAttributeFactory* attribFactory = StateAttributeFactory::instance();
stateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);
stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
// Fog handling
stateSet->setAttributeAndModes(attribFactory->getSmoothShadeModel());
stateSet->setAttributeAndModes(attribFactory->getStandardBlendFunc());
stateSet->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
stateSet->setRenderBinDetails(osg::StateSet::TRANSPARENT_BIN, "DepthSortedBin");
// Turn off z buffer writes. Standard hack for
// semi-transparent geometry to avoid sorting / flickering
// artifacts.
stateSet->setAttributeAndModes(attribFactory->getDepthWritesDisabled());
static ref_ptr<AlphaFunc> alphaFunc;
static ref_ptr<Program> program;
static ref_ptr<Uniform> baseTextureSampler;
static ref_ptr<Material> material;
// Generate the shader etc, if we don't already have one.
if (!program.valid()) {
alphaFunc = new AlphaFunc;
alphaFunc->setFunction(AlphaFunc::GREATER,0.01f);
program = new Program;
baseTextureSampler = new osg::Uniform("baseTexture", 0);
Shader* vertex_shader = new Shader(Shader::VERTEX, vertexShaderSource);
program->addShader(vertex_shader);
program->addBindAttribLocation("usrAttr1", CloudShaderGeometry::USR_ATTR_1);
program->addBindAttribLocation("usrAttr2", CloudShaderGeometry::USR_ATTR_2);
Shader* fragment_shader = new Shader(Shader::FRAGMENT, fragmentShaderSource);
program->addShader(fragment_shader);
material = new Material;
// Don´t track vertex color
material->setColorMode(Material::OFF);
// We don't actually use the material information either - see shader.
material->setAmbient(Material::FRONT_AND_BACK,
Vec4(0.5f, 0.5f, 0.5f, 1.0f));
material->setDiffuse(Material::FRONT_AND_BACK,
Vec4(0.5f, 0.5f, 0.5f, 1.0f));
}
stateSet->setAttributeAndModes(alphaFunc.get());
stateSet->setAttribute(program.get());
stateSet->addUniform(baseTextureSampler.get());
stateSet->setMode(GL_VERTEX_PROGRAM_TWO_SIDE, StateAttribute::ON);
stateSet->setAttribute(material.get());
// Add the newly created texture to the map for use later.
SGCloudField::cloudTextureMap.insert(StateSetMap::value_type(texture, stateSet));
// Create a new Effect for the texture, if required.
EffectMap::iterator iter = effectMap.find(texture);
if (iter == effectMap.end()) {
SGPropertyNode_ptr pcloudEffect = new SGPropertyNode;
makeChild(pcloudEffect, "inherits-from")->setValue("Effects/cloud");
setValue(makeChild(makeChild(makeChild(pcloudEffect, "parameters"),
"texture"),
"image"),
texture);
osg::ref_ptr<osgDB::ReaderWriter::Options> options
= makeOptionsFromPath(tex_path);
if ((effect = makeEffect(pcloudEffect, true, options)))
effectMap.insert(EffectMap::value_type(texture, effect));
} else {
stateSet = iter->second.get();
effect = iter->second.get();
}
quad = createOrthQuad(min_sprite_width, min_sprite_height, num_textures_x, num_textures_y);
quad = createOrthQuad(min_sprite_width, min_sprite_height,
num_textures_x, num_textures_y);
}
SGNewCloud::~SGNewCloud() {
@@ -285,9 +174,9 @@ static float Rnd(float n) {
}
#endif
osg::ref_ptr<Geode> SGNewCloud::genCloud() {
osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
osg::ref_ptr<osg::Geode> geode = new Geode;
osg::ref_ptr<EffectGeode> geode = new EffectGeode;
CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y, max_width, max_height);
@@ -325,9 +214,7 @@ osg::ref_ptr<Geode> SGNewCloud::genCloud() {
z = height * cos(elev) * 0.5f;
}
SGVec3f *pos = new SGVec3f(x, y, z);
// Determine the height and width as scaling factors on the minimum size (used to create the quad).
// Determine the height and width as scaling factors on the minimum size (used to create the quad)
float sprite_width = 1.0f + sg_random() * (max_sprite_width - min_sprite_width) / min_sprite_width;
float sprite_height = 1.0f + sg_random() * (max_sprite_height - min_sprite_height) / min_sprite_height;
@@ -353,7 +240,7 @@ osg::ref_ptr<Geode> SGNewCloud::genCloud() {
int index_y = (int) floor((z / height + 0.5f) * num_textures_y);
if (index_y == num_textures_y) { index_y--; }
sg->addSprite(*pos,
sg->addSprite(SGVec3f(x, y, z),
index_x,
index_y,
sprite_width,
@@ -366,7 +253,7 @@ osg::ref_ptr<Geode> SGNewCloud::genCloud() {
sg->setGeometry(quad);
geode->addDrawable(sg);
geode->setName("3D cloud");
geode->setStateSet(stateSet.get());
geode->setEffect(effect.get());
return geode;
}

View File

@@ -31,6 +31,9 @@
#include "bbcache.hxx"
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
using std::string;
using std::vector;
@@ -59,7 +62,7 @@ public:
~SGNewCloud();
// Generate a Cloud
osg::ref_ptr<osg::Geode> genCloud ();
osg::ref_ptr<simgear::EffectGeode> genCloud ();
static double getDensity(void)
{
@@ -90,7 +93,7 @@ private:
const string texture;
const string name;
osg::Geometry* quad;
osg::ref_ptr<osg::StateSet> stateSet;
osg::ref_ptr<simgear::Effect> effect;
static double sprite_density;
osg::Geometry* createOrthQuad(float w, float h, int varieties_x, int varieties_y);

View File

@@ -76,7 +76,7 @@ SGReaderWriterBTG::readNode(const std::string& fileName,
typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy,
NoOptimizePolicy, NoCopyPolicy,
NoOptimizePolicy,
NoSubstitutePolicy, BuildGroupBVHPolicy>
BTGCallback;

View File

@@ -26,18 +26,11 @@
#include <boost/tuple/tuple_comparison.hpp>
#include <osg/AlphaFunc>
#include <osg/Billboard>
#include <osg/BlendFunc>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Material>
#include <osg/Math>
#include <osg/MatrixTransform>
#include <osg/Matrix>
#include <osg/StateSet>
#include <osg/Texture2D>
#include <osg/TexEnv>
#include <osgDB/ReadFile>
#include <osgDB/FileUtils>
@@ -45,6 +38,9 @@
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/misc/sg_path.hxx>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/props/props.hxx>
#include <simgear/scene/util/QuadTreeBuilder.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx>
@@ -167,14 +163,17 @@ Geometry* createTreeGeometry(float width, float height, int varieties)
(*rotation)[1] = PI_2;
quadGeom->setFogCoordArray(rotation);
quadGeom->setFogCoordBinding(Geometry::BIND_PER_PRIMITIVE_SET);
// The primitive sets render the same geometry, but the second
// will rotated 90 degrees by the vertex shader, which uses the
// fog coordinate as a rotation.
for (int i = 0; i < 2; ++i)
quadGeom->addPrimitiveSet(new DrawArrays(PrimitiveSet::QUADS));
return quadGeom;
}
Geode* createTreeGeode(float width, float height, int varieties)
EffectGeode* createTreeGeode(float width, float height, int varieties)
{
Geode* result = new Geode;
EffectGeode* result = new EffectGeode;
result->addDrawable(createTreeGeometry(width, height, varieties));
return result;
}
@@ -204,67 +203,31 @@ void addTreeToLeafGeode(Geode* geode, const SGVec3f& p)
}
}
static char vertexShaderSource[] =
"varying float fogFactor;\n"
"\n"
"void main(void)\n"
"{\n"
" float numVarieties = gl_Normal.z;\n"
" float texFract = floor(fract(gl_MultiTexCoord0.x) * numVarieties) / numVarieties;\n"
" texFract += floor(gl_MultiTexCoord0.x) / numVarieties;\n"
" float sr = sin(gl_FogCoord);\n"
" float cr = cos(gl_FogCoord);\n"
" gl_TexCoord[0] = vec4(texFract, gl_MultiTexCoord0.y, 0.0, 0.0);\n"
// scaling
" vec3 position = gl_Vertex.xyz * gl_Normal.xxy;\n"
// Rotation of the generic quad to specific one for the tree.
" position.xy = vec2(dot(position.xy, vec2(cr, sr)), dot(position.xy, vec2(-sr, cr)));\n"
" position = position + gl_Color.xyz;\n"
" gl_Position = gl_ModelViewProjectionMatrix * vec4(position,1.0);\n"
" vec3 ecPosition = vec3(gl_ModelViewMatrix * vec4(position, 1.0));\n"
" float n = dot(normalize(gl_LightSource[0].position.xyz), normalize(-ecPosition));\n"
" vec3 diffuse = gl_FrontMaterial.diffuse.rgb * max(0.1, n);\n"
" vec4 ambientColor = gl_FrontLightModelProduct.sceneColor + gl_LightSource[0].ambient * gl_FrontMaterial.ambient;\n"
" gl_FrontColor = ambientColor + gl_LightSource[0].diffuse * vec4(diffuse, 1.0);\n"
" gl_BackColor = gl_FrontColor;\n"
" float fogCoord = abs(ecPosition.z);\n"
" fogFactor = exp( -gl_Fog.density * gl_Fog.density * fogCoord * fogCoord);\n"
" fogFactor = clamp(fogFactor, 0.0, 1.0);\n"
"}\n";
typedef std::map<std::string, osg::ref_ptr<Effect> > EffectMap;
static char fragmentShaderSource[] =
"uniform sampler2D baseTexture; \n"
"varying float fogFactor;\n"
"\n"
"void main(void) \n"
"{ \n"
" vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);\n"
" vec4 finalColor = base * gl_Color;\n"
" gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor );\n"
"}\n";
typedef std::map<std::string, osg::ref_ptr<StateSet> > StateSetMap;
static StateSetMap treeTextureMap;
static EffectMap treeEffectMap;
// Helper classes for creating the quad tree
namespace
{
struct MakeTreesLeaf
{
MakeTreesLeaf(float range, int varieties, float width, float height) :
MakeTreesLeaf(float range, int varieties, float width, float height,
Effect* effect) :
_range(range), _varieties(varieties),
_width(width), _height(height) {}
_width(width), _height(height), _effect(effect) {}
MakeTreesLeaf(const MakeTreesLeaf& rhs) :
_range(rhs._range),
_varieties(rhs._varieties), _width(rhs._width), _height(rhs._height)
_varieties(rhs._varieties), _width(rhs._width), _height(rhs._height),
_effect(rhs._effect)
{}
LOD* operator() () const
{
LOD* result = new LOD;
Geode* geode = createTreeGeode(_width, _height, _varieties);
EffectGeode* geode = createTreeGeode(_width, _height, _varieties);
geode->setEffect(_effect.get());
result->addChild(geode, 0, _range);
return result;
}
@@ -272,6 +235,7 @@ struct MakeTreesLeaf
int _varieties;
float _width;
float _height;
ref_ptr<Effect> _effect;
};
struct AddTreesLeafObject
@@ -317,51 +281,19 @@ osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform)
// Set up some shared structures.
ref_ptr<Group> group;
osg::StateSet* stateset = 0;
StateSetMap::iterator iter = treeTextureMap.find(forest.texture);
if (iter == treeTextureMap.end()) {
osg::Texture2D *tex = new osg::Texture2D;
tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP );
tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP );
tex->setImage(osgDB::readImageFile(forest.texture));
static ref_ptr<AlphaFunc> alphaFunc;
static ref_ptr<Program> program;
static ref_ptr<Uniform> baseTextureSampler;
static ref_ptr<Material> material;
stateset = new osg::StateSet;
stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
stateset->setRenderBinDetails(RANDOM_OBJECTS_BIN, "DepthSortedBin");
if (!program.valid()) {
alphaFunc = new AlphaFunc;
alphaFunc->setFunction(AlphaFunc::GEQUAL,0.33f);
program = new Program;
baseTextureSampler = new osg::Uniform("baseTexture", 0);
Shader* vertex_shader = new Shader(Shader::VERTEX,
vertexShaderSource);
program->addShader(vertex_shader);
Shader* fragment_shader = new Shader(Shader::FRAGMENT,
fragmentShaderSource);
program->addShader(fragment_shader);
material = new Material;
// Don´t track vertex color
material->setColorMode(Material::OFF);
material->setAmbient(Material::FRONT_AND_BACK,
Vec4(1.0f, 1.0f, 1.0f, 1.0f));
material->setDiffuse(Material::FRONT_AND_BACK,
Vec4(1.0f, 1.0f, 1.0f, 1.0f));
}
stateset->setAttributeAndModes(alphaFunc.get());
stateset->setAttribute(program.get());
stateset->addUniform(baseTextureSampler.get());
stateset->setMode(GL_VERTEX_PROGRAM_TWO_SIDE, StateAttribute::ON);
stateset->setAttribute(material.get());
treeTextureMap.insert(StateSetMap::value_type(forest.texture,
stateset));
Effect* effect = 0;
EffectMap::iterator iter = treeEffectMap.find(forest.texture);
if (iter == treeEffectMap.end()) {
SGPropertyNode_ptr effectProp = new SGPropertyNode;
makeChild(effectProp, "inherits-from")->setStringValue("Effects/tree");
SGPropertyNode* params = makeChild(effectProp, "parameters");
// emphasize n = 0
params->getChild("texture", 0, true)->getChild("image", 0, true)
->setStringValue(forest.texture);
effect = makeEffect(effectProp, true);
treeEffectMap.insert(EffectMap::value_type(forest.texture, effect));
} else {
stateset = iter->second.get();
effect = iter->second.get();
}
// Now, create a quadtree for the forest.
{
@@ -369,7 +301,7 @@ osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform)
quadtree(GetTreeCoord(), AddTreesLeafObject(),
SG_TREE_QUAD_TREE_DEPTH,
MakeTreesLeaf(forest.range, forest.texture_varieties,
forest.width, forest.height));
forest.width, forest.height, effect));
// Transform tree positions from the "geocentric" positions we
// get from the scenery polys into the local Z-up coordinate
// system.
@@ -384,7 +316,6 @@ osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform)
MatrixTransform* mt = new MatrixTransform(transform);
for (size_t i = 0; i < group->getNumChildren(); ++i)
mt->addChild(group->getChild(i));
mt->setStateSet(stateset);
return mt;
}

View File

@@ -64,3 +64,10 @@ osg::Node* sgGetRandomModel(SGMatModel *obj) {
return obj->get_random_model( root_props );
}
namespace simgear
{
SGPropertyNode* getPropertyRoot()
{
return root_props;
}
}

View File

@@ -44,4 +44,12 @@ void sgUserDataInit(SGPropertyNode *p);
*/
osg::Node* sgGetRandomModel(SGMatModel *obj);
namespace simgear
{
/**
* Get the property root for the simulation
*/
SGPropertyNode* getPropertyRoot();
}
#endif // _SG_USERDATA_HXX

View File

@@ -0,0 +1,40 @@
// CopyOp.cxx - Simgear CopyOp for copying our own classes
//
// Copyright (C) 2009 Tim Moore timoore@redhat.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
#include "CopyOp.hxx"
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/Technique.hxx>
namespace simgear
{
osg::Object* CopyOp::operator()(const osg::Object* obj) const
{
if (dynamic_cast<const Effect*>(obj)
|| dynamic_cast<const Technique*>(obj)) {
if (_flags & DEEP_COPY_STATESETS)
return obj->clone(*this);
else
return const_cast<osg::Object*>(obj);
}
else {
return osg::CopyOp::operator()(obj);
}
}
}

View File

@@ -0,0 +1,37 @@
// CopyOp.hxx - Simgear CopyOp for copying our own classes
//
// Copyright (C) 2009 Tim Moore timoore@redhat.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
#ifndef SIMGEAR_COPYOP_HXX
#define SIMGEAR_COPYOP_HXX 1
#include <osg/CopyOp>
namespace simgear
{
class CopyOp : public osg::CopyOp
{
public:
CopyOp(osg::CopyOp::CopyFlags flags = osg::CopyOp::SHALLOW_COPY)
: osg::CopyOp(flags)
{
}
using osg::CopyOp::operator();
virtual osg::Object* operator()(const osg::Object* obj) const;
};
}
#endif

View File

@@ -14,11 +14,14 @@ include_HEADERS = \
SGStateAttributeVisitor.hxx \
SGTextureStateAttributeVisitor.hxx \
SGUpdateVisitor.hxx \
CopyOp.hxx \
NodeAndDrawableVisitor.hxx \
PrimitiveUtils.hxx \
QuadTreeBuilder.hxx \
RenderConstants.hxx \
SplicingVisitor.hxx \
StateAttributeFactory.hxx \
UpdateOnceCallback.hxx \
VectorArrayAdapter.hxx
@@ -28,9 +31,12 @@ libsgutil_a_SOURCES = \
SGSceneUserData.cxx \
SGStateAttributeVisitor.cxx \
SGTextureStateAttributeVisitor.cxx \
CopyOp.cxx \
NodeAndDrawableVisitor.cxx \
PrimitiveUtils.cxx \
SplicingVisitor.cxx \
StateAttributeFactory.cxx \
QuadTreeBuilder.cxx
QuadTreeBuilder.cxx \
UpdateOnceCallback.cxx
INCLUDES = -I$(top_srcdir)

View File

@@ -23,6 +23,10 @@
# include <simgear_config.h>
#endif
#include <osgDB/Registry>
#include <osgDB/Input>
#include <osgDB/ParameterOutput>
#include "SGSceneUserData.hxx"
SGSceneUserData*
@@ -86,3 +90,38 @@ SGSceneUserData::addPickCallback(SGPickCallback* pickCallback)
return;
_pickCallbacks.push_back(pickCallback);
}
bool SGSceneUserData_writeLocalData(const osg::Object& obj, osgDB::Output& fw)
{
const SGSceneUserData& data = static_cast<const SGSceneUserData&>(obj);
unsigned numPickCallbacks = data.getNumPickCallbacks();
if (numPickCallbacks > 0)
fw.indent() << "num_pickCallbacks " << numPickCallbacks << "\n";
if (data.getBVHNode())
fw.indent() << "hasBVH true\n";
const SGSceneUserData::Velocity* vel = data.getVelocity();
if (vel) {
fw.indent() << "velocity {\n";
fw.moveIn();
fw.indent() << "linear " << vel->linear << "\n";
fw.indent() << "angular " << vel->angular << "\n";
fw.indent() << "referenceTime " << vel->referenceTime << "\n";
fw.indent() << "id " << static_cast<unsigned>(vel->id) << "\n";
fw.moveOut();
fw.indent() << "}\n";
}
return true;
}
namespace
{
osgDB::RegisterDotOsgWrapperProxy SGSceneUserDataProxy
(
new SGSceneUserData,
"simgear::SGSceneUserData",
"Object simgear::SGSceneUserData",
0,
&SGSceneUserData_writeLocalData
);
}

View File

@@ -23,13 +23,21 @@
#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);

View File

@@ -0,0 +1,113 @@
#include "SplicingVisitor.hxx"
namespace simgear
{
using namespace osg;
SplicingVisitor::SplicingVisitor()
: NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN)
{
_childStack.push_back(NodeList());
}
void SplicingVisitor::reset()
{
_childStack.clear();
NodeVisitor::reset();
}
NodeList SplicingVisitor::traverse(Node& node)
{
NodeList result;
_childStack.push_back(NodeList());
NodeVisitor::traverse(node);
result = _childStack.back();
_childStack.pop_back();
return result;
}
void SplicingVisitor::apply(Node& node)
{
NodeVisitor::traverse(node);
pushNode(&node);
}
void SplicingVisitor::apply(Group& node)
{
if (pushNode(getNewNode(node)))
return;
pushResultNode(&node, &node, traverse(node));
}
Group* SplicingVisitor::pushResultNode(Group* node, Group* newNode,
const NodeList& children)
{
ref_ptr<Group> result;
if (node == newNode) {
result = copyIfNeeded(*node, children);
} else {
result = newNode;
for (NodeList::const_iterator itr = children.begin(), end = children.end();
itr != end;
++itr)
result->addChild(itr->get());
}
_childStack.back().push_back(result);
recordNewNode(node, result);
return result;
}
Node* SplicingVisitor::pushResultNode(Node* node, Node* newNode)
{
_childStack.back().push_back(newNode);
recordNewNode(node, newNode);
return newNode;
}
Node* SplicingVisitor::pushNode(Node* node)
{
if (node)
_childStack.back().push_back(node);
return node;
}
Node* SplicingVisitor::getResult()
{
NodeList& top = _childStack.at(0);
if (top.empty()) {
return 0;
} else if (top.size() == 1) {
return top[0].get();
} else {
Group* result = new Group;
for (NodeList::iterator itr = top.begin(), end = top.end();
itr != end;
++itr)
result->addChild(itr->get());
return result;
}
}
Node* SplicingVisitor::getNewNode(osg::Node* node)
{
ref_ptr<Node> tmpPtr(node);
NodeMap::iterator itr;
try {
itr = _visited.find(tmpPtr);
}
catch (...) {
tmpPtr.release();
throw;
}
if (itr == _visited.end())
return 0;
else
return itr->second.get();
}
bool SplicingVisitor::recordNewNode(osg::Node* oldNode, osg::Node* newNode)
{
ref_ptr<Node> oldTmp(oldNode);
ref_ptr<Node> newTmp(newNode);
return _visited.insert(std::make_pair(oldTmp, newTmp)).second;
}
}

View File

@@ -0,0 +1,93 @@
#ifndef SIMGEAR_SPLICINGVISITOR_HXX
#define SIMGEAR_SPLICINGVISITOR_HXX 1
#include <cstddef>
#include <map>
#include <vector>
#include <osg/NodeVisitor>
#include <osg/Group>
namespace simgear
{
class SplicingVisitor : public osg::NodeVisitor
{
public:
META_NodeVisitor(simgear,SplicingVisitor);
SplicingVisitor();
virtual ~SplicingVisitor() {}
virtual void reset();
osg::NodeList traverse(osg::Node& node);
using osg::NodeVisitor::apply;
virtual void apply(osg::Node& node);
virtual void apply(osg::Group& node);
template<typename T>
static T* copyIfNeeded(T& node, const osg::NodeList& children);
template<typename T>
static T* copy(T& node, const osg::NodeList& children);
/**
* Push the result of processing this node.
*
* If the child list is not equal to the node's current children,
* make a copy of the node. Record the (possibly new) node which
* should be the returned result if the node is visited again.
*/
osg::Group* pushResultNode(osg::Group* node, osg::Group* newNode,
const osg::NodeList& children);
/**
* Push the result of processing this node.
*
* Record the (possibly new) node which should be the returned
* result if the node is visited again.
*/
osg::Node* pushResultNode(osg::Node* node, osg::Node* newNode);
/**
* Push some node onto the list of result nodes.
*/
osg::Node* pushNode(osg::Node* node);
osg::Node* getResult();
osg::Node* getNewNode(osg::Node& node)
{
return getNewNode(&node);
}
osg::Node* getNewNode(osg::Node* node);
bool recordNewNode(osg::Node* oldNode, osg::Node* newNode);
osg::NodeList& getResults() { return _childStack.back(); }
protected:
std::vector<osg::NodeList> _childStack;
typedef std::map<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::Node> > NodeMap;
NodeMap _visited;
};
template<typename T>
T* SplicingVisitor::copyIfNeeded(T& node, const osg::NodeList& children)
{
bool copyNeeded = false;
if (node.getNumChildren() == children.size()) {
for (std::size_t i = 0; i < children.size(); ++i)
if (node.getChild(i) != children[i].get()) {
copyNeeded = true;
break;
}
} else {
copyNeeded = true;
}
if (copyNeeded)
return copy(node, children);
else
return &node;
}
template<typename T>
T* SplicingVisitor::copy(T& node, const osg::NodeList& children)
{
T* result = osg::clone(&node, osg::CopyOp::SHALLOW_COPY);
result->removeChildren(0, result->getNumChildren());
for (osg::NodeList::const_iterator itr = children.begin(),
end = children.end();
itr != end;
++itr)
result->addChild(itr->get());
return result;
}
}
#endif

View File

@@ -64,6 +64,17 @@ StateAttributeFactory::StateAttributeFactory()
_whiteTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
_whiteTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
_whiteTexture->setDataVariance(osg::Object::STATIC);
// And now the transparent texture
dummyImage = new osg::Image;
dummyImage->allocateImage(1, 1, 1, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE);
imageBytes = dummyImage->data(0, 0);
imageBytes[0] = 255;
imageBytes[1] = 0;
_transparentTexture = new osg::Texture2D;
_transparentTexture->setImage(dummyImage);
_transparentTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
_transparentTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
_transparentTexture->setDataVariance(osg::Object::STATIC);
_white = new Vec4Array(1);
(*_white)[0].set(1.0f, 1.0f, 1.0f, 1.0f);
_white->setDataVariance(Object::STATIC);

View File

@@ -57,6 +57,11 @@ public:
osg::Texture2D* getWhiteTexture() { return _whiteTexture.get(); }
// White color
osg::Vec4Array* getWhiteColor() {return _white.get(); }
// A white, completely transparent texture
osg::Texture2D* getTransparentTexture()
{
return _transparentTexture.get();
}
// cull front and back facing polygons
osg::CullFace* getCullFaceFront() { return _cullFaceFront.get(); }
osg::CullFace* getCullFaceBack() { return _cullFaceBack.get(); }
@@ -70,6 +75,7 @@ protected:
osg::ref_ptr<osg::BlendFunc> _standardBlendFunc;
osg::ref_ptr<osg::TexEnv> _standardTexEnv;
osg::ref_ptr<osg::Texture2D> _whiteTexture;
osg::ref_ptr<osg::Texture2D> _transparentTexture;
osg::ref_ptr<osg::Vec4Array> _white;
osg::ref_ptr<osg::CullFace> _cullFaceFront;
osg::ref_ptr<osg::CullFace> _cullFaceBack;

View File

@@ -0,0 +1,39 @@
// UpdateOnceCallback.hxx
//
// Copyright (C) 2009 Tim Moore timoore@redhat.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
#include "UpdateOnceCallback.hxx"
#include <osg/Node>
namespace simgear
{
using namespace osg;
void UpdateOnceCallback::operator()(Node* node, NodeVisitor* nv)
{
doUpdate(node, nv);
node->removeUpdateCallback(this);
// The callback could be deleted now.
}
void UpdateOnceCallback::doUpdate(Node* node, NodeVisitor* nv)
{
traverse(node, nv);
}
}

View File

@@ -0,0 +1,44 @@
// UpdateOnceCallback.hxx
//
// Copyright (C) 2009 Tim Moore timoore@redhat.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
#ifndef SIMGEAR_UPDATEONCECALLBACK_HXX
#define SIMGEAR_UPDATEONCECALLBACK_HXX 1
#include <osg/NodeCallback>
namespace simgear
{
class UpdateOnceCallback : public osg::NodeCallback
{
public:
UpdateOnceCallback() {}
UpdateOnceCallback(const UpdateOnceCallback& nc, const osg::CopyOp& copyop)
: osg::NodeCallback(nc, copyop)
{
}
META_Object(simgear,UpdateOnceCallback);
virtual void doUpdate(osg::Node* node, osg::NodeVisitor* nv);
/**
* Do not override; use doUpdate instead!
*/
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
};
}
#endif

View File

@@ -20,8 +20,9 @@
#ifndef SIMGEAR_OSGUTILS_HXX
#define SIMGEAR_OSGUTILS_HXX 1
#include <boost/iterator/iterator_facade.hpp>
#include <osg/CopyOp>
#include <osg/StateAttribute>
#include <osg/StateSet>
namespace simgear
{
@@ -99,59 +100,280 @@ T* clone_ref(const osg::ref_ptr<T>& object,
return static_cast<T*>(object->clone(copyop));
}
template<typename Container>
class BackRefInsertIterator
: public boost::iterator_facade<BackRefInsertIterator<Container>,
BackRefInsertIterator<Container>,
boost::incrementable_traversal_tag
>
}
namespace osg
{
public:
typedef typename Container::value_type::element_type* PtrType;
BackRefInsertIterator() : _container(0) {}
explicit BackRefInsertIterator(Container& container)
: _container(&container)
{
}
class AlphaFunc;
class BlendColor;
class BlendEquation;
class BlendFunc;
class ClampColor;
class ColorMask;
class ColorMatrix;
class CullFace;
class Depth;
class Fog;
class FragmentProgram;
class FrontFace;
class LightModel;
class LineStipple;
class LineWidth;
class LogicOp;
class Material;
class Multisample;
class Point;
class PointSprite;
class PolygonMode;
class PolygonOffset;
class PolygonStipple;
class Program;
class Scissor;
class ShadeModel;
class Stencil;
class StencilTwoSided;
class TexEnv;
class TexEnvCombine;
class TexEnvFilter;
class TexGen;
class TexMat;
class Texture1D;
class Texture2D;
class Texture2DArray;
class Texture3D;
class TextureCubeMap;
class TextureRectangle;
class VertexProgram;
class Viewport;
}
BackRefInsertIterator&
operator=(const PtrType ptr)
{
_container->push_back(ptr);
return *this;
}
private:
friend class boost::iterator_core_access;
namespace simgear
{
namespace osgutils
{
using namespace osg;
void increment()
{
}
BackRefInsertIterator& dereference()
{
return *this;
}
BackRefInsertIterator& dereference() const
{
return const_cast<BackRefInsertIterator&>(*this);
}
bool equal(const BackRefInsertIterator& rhs)
{
return _container == rhs._container;
}
Container* _container;
template<StateAttribute::Type T>
struct TypeHolder
{
static const StateAttribute::Type type = T;
};
template<typename AT> struct AttributeType;
template<typename AT> struct TexAttributeType;
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<>
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 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
template<typename Container>
inline BackRefInsertIterator<Container>
backRefInsertIterator(Container& container)
{
return BackRefInsertIterator<Container>(container);
}
}
#endif