Merge branch 'timoore/effects'
Conflicts: simgear/scene/model/model.cxx simgear/scene/sky/newcloud.cxx
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
71
simgear/props/AtomicChangeListener.cxx
Normal file
71
simgear/props/AtomicChangeListener.cxx
Normal 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()
|
||||
{
|
||||
}
|
||||
}
|
||||
104
simgear/props/AtomicChangeListener.hxx
Normal file
104
simgear/props/AtomicChangeListener.hxx
Normal 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
|
||||
72
simgear/props/ExtendedPropertyAdapter.hxx
Normal file
72
simgear/props/ExtendedPropertyAdapter.hxx
Normal 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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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*
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -76,7 +76,7 @@ SGReaderWriterBTG::readNode(const std::string& fileName,
|
||||
|
||||
|
||||
typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy,
|
||||
NoOptimizePolicy, NoCopyPolicy,
|
||||
NoOptimizePolicy,
|
||||
NoSubstitutePolicy, BuildGroupBVHPolicy>
|
||||
BTGCallback;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,3 +64,10 @@ osg::Node* sgGetRandomModel(SGMatModel *obj) {
|
||||
return obj->get_random_model( root_props );
|
||||
}
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
SGPropertyNode* getPropertyRoot()
|
||||
{
|
||||
return root_props;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
40
simgear/scene/util/CopyOp.cxx
Normal file
40
simgear/scene/util/CopyOp.cxx
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
simgear/scene/util/CopyOp.hxx
Normal file
37
simgear/scene/util/CopyOp.hxx
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
113
simgear/scene/util/SplicingVisitor.cxx
Normal file
113
simgear/scene/util/SplicingVisitor.cxx
Normal 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;
|
||||
}
|
||||
}
|
||||
93
simgear/scene/util/SplicingVisitor.hxx
Normal file
93
simgear/scene/util/SplicingVisitor.hxx
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
39
simgear/scene/util/UpdateOnceCallback.cxx
Normal file
39
simgear/scene/util/UpdateOnceCallback.cxx
Normal 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);
|
||||
}
|
||||
}
|
||||
44
simgear/scene/util/UpdateOnceCallback.hxx
Normal file
44
simgear/scene/util/UpdateOnceCallback.hxx
Normal 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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user