#include <string.h> // for strcmp()
#include <math.h>
#include <algorithm>
+#include <functional>
+
+#include <OpenThreads/Mutex>
+#include <OpenThreads/ReentrantMutex>
+#include <OpenThreads/ScopedLock>
#include <osg/AlphaFunc>
#include <osg/Drawable>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/LOD>
+#include <osg/Math>
#include <osg/PolygonMode>
#include <osg/PolygonOffset>
#include <osg/StateSet>
#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>
#include <simgear/props/props.hxx>
#include <simgear/structure/SGBinding.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/util/SGNodeMasks.hxx>
#include <simgear/scene/util/SGSceneUserData.hxx>
#include <simgear/scene/util/SGStateAttributeVisitor.hxx>
#include "animation.hxx"
#include "model.hxx"
+#include "SGTranslateTransform.hxx"
#include "SGMaterialAnimation.hxx"
+#include "SGRotateTransform.hxx"
+#include "SGScaleTransform.hxx"
+#include "SGInteractionAnimation.hxx"
+
+#include "ConditionNode.hxx"
+
+using OpenThreads::Mutex;
+using OpenThreads::ReentrantMutex;
+using OpenThreads::ScopedLock;
\f
////////////////////////////////////////////////////////////////////////
matrix(3, 2) = xyz[2];
}
-/**
- * Modify property value by step and scroll settings in texture translations
- */
-static double
-apply_mods(double property, double step, double scroll)
-{
-
- double modprop;
- if(step > 0) {
- double scrollval = 0.0;
- if(scroll > 0) {
- // calculate scroll amount (for odometer like movement)
- double remainder = step - fmod(fabs(property), step);
- if (remainder < scroll) {
- scrollval = (scroll - remainder) / scroll * step;
- }
- }
- // apply stepping of input value
- if(property > 0)
- modprop = ((floor(property/step) * step) + scrollval);
- else
- modprop = ((ceil(property/step) * step) + scrollval);
- } else {
- modprop = property;
- }
- return modprop;
-
-}
-
/**
* Read an interpolation table from properties.
*/
return new SGInterpTable(table_node);
}
-////////////////////////////////////////////////////////////////////////
-// Utility value classes
-////////////////////////////////////////////////////////////////////////
-class SGScaleOffsetValue : public SGDoubleValue {
-public:
- SGScaleOffsetValue(SGPropertyNode const* propertyNode) :
- _propertyNode(propertyNode),
- _scale(1),
- _offset(0),
- _min(-SGLimitsd::max()),
- _max(SGLimitsd::max())
- { }
- void setScale(double scale)
- { _scale = scale; }
- void setOffset(double offset)
- { _offset = offset; }
- void setMin(double min)
- { _min = min; }
- void setMax(double max)
- { _max = max; }
-
- virtual double getValue() const
- {
- double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
- return std::min(_max, std::max(_min, _offset + _scale*value));
- }
-private:
- SGSharedPtr<SGPropertyNode const> _propertyNode;
- double _scale;
- double _offset;
- double _min;
- double _max;
-};
+static std::string
+unit_string(const char* value, const char* unit)
+{
+ return std::string(value) + unit;
+}
-class SGPersScaleOffsetValue : public SGDoubleValue {
+class SGPersonalityScaleOffsetExpression : public SGUnaryExpression<double> {
public:
- SGPersScaleOffsetValue(SGPropertyNode const* propertyNode,
- SGPropertyNode const* config,
- const char* scalename, const char* offsetname,
- double defScale = 1, double defOffset = 0) :
- _propertyNode(propertyNode),
- _scale(config, scalename, defScale),
- _offset(config, offsetname, defOffset),
- _min(-SGLimitsd::max()),
- _max(SGLimitsd::max())
+ SGPersonalityScaleOffsetExpression(SGExpression<double>* expr,
+ SGPropertyNode const* config,
+ const std::string& scalename,
+ const std::string& offsetname,
+ double defScale = 1,
+ double defOffset = 0) :
+ SGUnaryExpression<double>(expr),
+ _scale(config, scalename.c_str(), defScale),
+ _offset(config, offsetname.c_str(), defOffset)
{ }
void setScale(double scale)
{ _scale = scale; }
void setOffset(double offset)
{ _offset = offset; }
- void setMin(double min)
- { _min = min; }
- void setMax(double max)
- { _max = max; }
- virtual double getValue() const
+ virtual void eval(double& value, const simgear::expression::Binding* b) const
{
_offset.shuffle();
_scale.shuffle();
- double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
- return SGMiscd::clip(_offset + _scale*value, _min, _max);
+ value = _offset + _scale*getOperand()->getValue(b);
}
+
+ virtual bool isConst() const { return false; }
+
private:
- SGSharedPtr<SGPropertyNode const> _propertyNode;
mutable SGPersonalityParameter<double> _scale;
mutable SGPersonalityParameter<double> _offset;
- double _min;
- double _max;
};
-class SGInterpTableValue : public SGDoubleValue {
-public:
- SGInterpTableValue(SGPropertyNode const* propertyNode,
- SGInterpTable const* interpTable) :
- _propertyNode(propertyNode),
- _interpTable(interpTable)
- { }
- virtual double getValue() const
- { return _interpTable->interpolate(_propertyNode->getDoubleValue()); }
-private:
- SGSharedPtr<SGPropertyNode const> _propertyNode;
- SGSharedPtr<SGInterpTable const> _interpTable;
-};
-class SGTexScaleOffsetValue : public SGDoubleValue {
-public:
- SGTexScaleOffsetValue(const SGPropertyNode* propertyNode) :
- _propertyNode(propertyNode),
- _scale(1),
- _offset(0),
- _step(0),
- _scroll(0),
- _min(-SGLimitsd::max()),
- _max(SGLimitsd::max())
- { }
- void setScale(double scale)
- { _scale = scale; }
- void setOffset(double offset)
- { _offset = offset; }
- void setStep(double step)
- { _step = step; }
- void setScroll(double scroll)
- { _scroll = scroll; }
- void setMin(double min)
- { _min = min; }
- void setMax(double max)
- { _max = max; }
-
- virtual double getValue() const
- {
- double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
- value = apply_mods(value, _step, _scroll);
- return SGMiscd::clip(_scale*(_offset + value), _min, _max);
- }
-private:
- SGSharedPtr<const SGPropertyNode> _propertyNode;
- double _scale;
- double _offset;
- double _step;
- double _scroll;
- double _min;
- double _max;
-};
-
-class SGTexTableValue : public SGDoubleValue {
-public:
- SGTexTableValue(const SGPropertyNode* propertyNode,
- const SGInterpTable* interpTable) :
- _propertyNode(propertyNode),
- _interpTable(interpTable)
- { }
- void setStep(double step)
- { _step = step; }
- void setScroll(double scroll)
- { _scroll = scroll; }
- virtual double getValue() const
- {
- double value = _propertyNode ? _propertyNode->getDoubleValue() : 0;
- value = apply_mods(value, _step, _scroll);
- return _interpTable->interpolate(value);
- }
-private:
- SGSharedPtr<SGPropertyNode const> _propertyNode;
- SGSharedPtr<SGInterpTable const> _interpTable;
- double _step;
- double _scroll;
-};
+static SGExpressiond*
+read_factor_offset(const SGPropertyNode* configNode, SGExpressiond* expr,
+ const std::string& factor, const std::string& offset)
+{
+ double factorValue = configNode->getDoubleValue(factor, 1);
+ if (factorValue != 1)
+ expr = new SGScaleExpression<double>(expr, factorValue);
+ double offsetValue = configNode->getDoubleValue(offset, 0);
+ if (offsetValue != 0)
+ expr = new SGBiasExpression<double>(expr, offsetValue);
+ return expr;
+}
-static std::string
-unit_string(const char* value, const char* unit)
+static SGExpressiond*
+read_offset_factor(const SGPropertyNode* configNode, SGExpressiond* expr,
+ const std::string& factor, const std::string& offset)
{
- return std::string(value) + unit;
+ double offsetValue = configNode->getDoubleValue(offset, 0);
+ if (offsetValue != 0)
+ expr = new SGBiasExpression<double>(expr, offsetValue);
+ double factorValue = configNode->getDoubleValue(factor, 1);
+ if (factorValue != 1)
+ expr = new SGScaleExpression<double>(expr, factorValue);
+ return expr;
}
-static SGDoubleValue*
+SGExpressiond*
read_value(const SGPropertyNode* configNode, SGPropertyNode* modelRoot,
const char* unit, double defMin, double defMax)
{
- std::string inputPropertyName;
- inputPropertyName = configNode->getStringValue("property", "");
- if (!inputPropertyName.empty()) {
+ SGExpression<double>* value = 0;
+
+ std::string inputPropertyName = configNode->getStringValue("property", "");
+ if (inputPropertyName.empty()) {
+ std::string spos = unit_string("starting-position", unit);
+ double initPos = configNode->getDoubleValue(spos, 0);
+ value = new SGConstExpression<double>(initPos);
+ } else {
SGPropertyNode* inputProperty;
- inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
- SGInterpTable* interpTable = read_interpolation_table(configNode);
- if (interpTable) {
- SGInterpTableValue* value;
- value = new SGInterpTableValue(inputProperty, interpTable);
- return value;
+ inputProperty = modelRoot->getNode(inputPropertyName, true);
+ value = new SGPropertyExpression<double>(inputProperty);
+ }
+
+ SGInterpTable* interpTable = read_interpolation_table(configNode);
+ if (interpTable) {
+ return new SGInterpTableExpression<double>(value, interpTable);
+ } else {
+ std::string offset = unit_string("offset", unit);
+ std::string min = unit_string("min", unit);
+ std::string max = unit_string("max", unit);
+
+ if (configNode->getBoolValue("use-personality", false)) {
+ value = new SGPersonalityScaleOffsetExpression(value, configNode,
+ "factor", offset);
} else {
- std::string offset = unit_string("offset", unit);
- std::string min = unit_string("min", unit);
- std::string max = unit_string("max", unit);
-
- if (configNode->getBoolValue("use-personality", false)) {
- SGPersScaleOffsetValue* value;
- value = new SGPersScaleOffsetValue(inputProperty, configNode,
- "factor", offset.c_str());
- value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
- value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
- return value;
- } else {
- SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
- value->setScale(configNode->getDoubleValue("factor", 1));
- value->setOffset(configNode->getDoubleValue(offset.c_str(), 0));
- value->setMin(configNode->getDoubleValue(min.c_str(), defMin));
- value->setMax(configNode->getDoubleValue(max.c_str(), defMax));
- return value;
- }
+ value = read_factor_offset(configNode, value, "factor", offset);
}
+
+ double minClip = configNode->getDoubleValue(min, defMin);
+ double maxClip = configNode->getDoubleValue(max, defMax);
+ if (minClip > SGMiscd::min(SGLimitsd::min(), -SGLimitsd::max()) ||
+ maxClip < SGLimitsd::max())
+ value = new SGClipExpression<double>(value, minClip, maxClip);
+
+ return value;
}
return 0;
}
}
};
+namespace
+{
+// Set all drawables to not use display lists. OSG will use
+// glDrawArrays instead.
+struct DoDrawArraysVisitor : public osg::NodeVisitor {
+ DoDrawArraysVisitor() :
+ osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
+ {}
+ void apply(osg::Geode& geode)
+ {
+ using namespace osg;
+ using namespace std;
+
+ for (int i = 0; i < (int)geode.getNumDrawables(); ++i)
+ geode.getDrawable(i)->setUseDisplayList(false);
+ }
+};
+}
SGAnimation::SGAnimation(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) :
bool
SGAnimation::animate(osg::Node* node, const SGPropertyNode* configNode,
- SGPropertyNode* modelRoot)
+ SGPropertyNode* modelRoot,
+ const osgDB::ReaderWriter::Options* options)
{
std::string type = configNode->getStringValue("type", "none");
if (type == "alpha-test") {
} else if (type == "flash") {
SGFlashAnimation animInst(configNode, modelRoot);
animInst.apply(node);
+ } else if (type == "interaction") {
+ SGInteractionAnimation animInst(configNode, modelRoot);
+ animInst.apply(node);
} else if (type == "material") {
- SGMaterialAnimation animInst(configNode, modelRoot);
+ SGMaterialAnimation animInst(configNode, modelRoot, options);
animInst.apply(node);
} else if (type == "noshadow") {
SGShadowAnimation animInst(configNode, modelRoot);
SGSelectAnimation animInst(configNode, modelRoot);
animInst.apply(node);
} else if (type == "shader") {
- SGShaderAnimation animInst(configNode, modelRoot);
+ SGShaderAnimation animInst(configNode, modelRoot, options);
animInst.apply(node);
} else if (type == "textranslate" || type == "texrotate" ||
type == "texmultiple") {
else
node.setNodeMask(~SG_NODEMASK_TERRAIN_BIT & node.getNodeMask());
if (!_disableShadow)
- node.setNodeMask( SG_NODEMASK_SHADOW_BIT | node.getNodeMask());
+ node.setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node.getNodeMask());
else
- node.setNodeMask(~SG_NODEMASK_SHADOW_BIT & node.getNodeMask());
+ node.setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node.getNodeMask());
}
osg::Group*
// Implementation of translate animation
////////////////////////////////////////////////////////////////////////
-class SGTranslateAnimation::Transform : public osg::Transform {
-public:
- Transform() :
- _axis(0, 0, 0),
- _value(0)
- { setReferenceFrame(RELATIVE_RF); }
- void setAxis(const SGVec3d& axis)
- { _axis = axis; dirtyBound(); }
- void setValue(double value)
- { _value = value; dirtyBound(); }
- virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
- osg::NodeVisitor* nv) const
- {
- assert(_referenceFrame == RELATIVE_RF);
- osg::Matrix tmp;
- set_translation(tmp, _value, _axis);
- matrix.preMult(tmp);
- return true;
- }
- virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
- osg::NodeVisitor* nv) const
- {
- assert(_referenceFrame == RELATIVE_RF);
- osg::Matrix tmp;
- set_translation(tmp, -_value, _axis);
- matrix.postMult(tmp);
- return true;
- }
-private:
- SGVec3d _axis;
- double _value;
-};
-
class SGTranslateAnimation::UpdateCallback : public osg::NodeCallback {
public:
UpdateCallback(SGCondition const* condition,
- SGDoubleValue const* animationValue) :
+ SGExpressiond const* animationValue) :
_condition(condition),
_animationValue(animationValue)
{ }
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (!_condition || _condition->test()) {
- SGTranslateAnimation::Transform* transform;
- transform = static_cast<SGTranslateAnimation::Transform*>(node);
+ SGTranslateTransform* transform;
+ transform = static_cast<SGTranslateTransform*>(node);
transform->setValue(_animationValue->getValue());
}
traverse(node, nv);
}
public:
SGSharedPtr<SGCondition const> _condition;
- SGSharedPtr<SGDoubleValue const> _animationValue;
+ SGSharedPtr<SGExpressiond const> _animationValue;
};
SGTranslateAnimation::SGTranslateAnimation(const SGPropertyNode* configNode,
SGAnimation(configNode, modelRoot)
{
_condition = getCondition();
- _animationValue = read_value(configNode, modelRoot, "-m",
- -SGLimitsd::max(), SGLimitsd::max());
- _axis[0] = configNode->getDoubleValue("axis/x", 0);
- _axis[1] = configNode->getDoubleValue("axis/y", 0);
- _axis[2] = configNode->getDoubleValue("axis/z", 0);
+ SGSharedPtr<SGExpressiond> value;
+ value = read_value(configNode, modelRoot, "-m",
+ -SGLimitsd::max(), SGLimitsd::max());
+ _animationValue = value->simplify();
+ if (_animationValue)
+ _initialValue = _animationValue->getValue();
+ else
+ _initialValue = 0;
+
+ if (configNode->hasValue("axis/x1-m")) {
+ SGVec3d v1, v2;
+ v1[0] = configNode->getDoubleValue("axis/x1-m", 0);
+ v1[1] = configNode->getDoubleValue("axis/y1-m", 0);
+ v1[2] = configNode->getDoubleValue("axis/z1-m", 0);
+ v2[0] = configNode->getDoubleValue("axis/x2-m", 0);
+ v2[1] = configNode->getDoubleValue("axis/y2-m", 0);
+ v2[2] = configNode->getDoubleValue("axis/z2-m", 0);
+ _axis = v2 - v1;
+ } else {
+ _axis[0] = configNode->getDoubleValue("axis/x", 0);
+ _axis[1] = configNode->getDoubleValue("axis/y", 0);
+ _axis[2] = configNode->getDoubleValue("axis/z", 0);
+ }
if (8*SGLimitsd::min() < norm(_axis))
_axis = normalize(_axis);
-
- _initialValue = configNode->getDoubleValue("starting-position-m", 0);
- _initialValue *= configNode->getDoubleValue("factor", 1);
- _initialValue += configNode->getDoubleValue("offset-m", 0);
}
osg::Group*
SGTranslateAnimation::createAnimationGroup(osg::Group& parent)
{
- Transform* transform = new Transform;
+ SGTranslateTransform* transform = new SGTranslateTransform;
transform->setName("translate animation");
- if (_animationValue) {
+ if (_animationValue && !_animationValue->isConst()) {
UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
transform->setUpdateCallback(uc);
}
// Implementation of rotate/spin animation
////////////////////////////////////////////////////////////////////////
-class SGRotateAnimation::Transform : public osg::Transform {
-public:
- Transform()
- { setReferenceFrame(RELATIVE_RF); }
- void setCenter(const SGVec3d& center)
- { _center = center; dirtyBound(); }
- void setAxis(const SGVec3d& axis)
- { _axis = axis; dirtyBound(); }
- void setAngle(double angle)
- { _angle = angle; }
- double getAngle() const
- { return _angle; }
- virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
- osg::NodeVisitor* nv) const
- {
- // This is the fast path, optimize a bit
- assert(_referenceFrame == RELATIVE_RF);
- // FIXME optimize
- osg::Matrix tmp;
- set_rotation(tmp, _angle, _center, _axis);
- matrix.preMult(tmp);
- return true;
- }
- virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
- osg::NodeVisitor* nv) const
- {
- assert(_referenceFrame == RELATIVE_RF);
- // FIXME optimize
- osg::Matrix tmp;
- set_rotation(tmp, -_angle, _center, _axis);
- matrix.postMult(tmp);
- return true;
- }
- virtual osg::BoundingSphere computeBound() const
- {
- osg::BoundingSphere bs = osg::Group::computeBound();
- osg::BoundingSphere centerbs(_center.osg(), bs.radius());
- centerbs.expandBy(bs);
- return centerbs;
- }
-
-private:
- SGVec3d _center;
- SGVec3d _axis;
- double _angle;
-};
-
class SGRotateAnimation::UpdateCallback : public osg::NodeCallback {
public:
UpdateCallback(SGCondition const* condition,
- SGDoubleValue const* animationValue) :
+ SGExpressiond const* animationValue) :
_condition(condition),
_animationValue(animationValue)
{ }
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (!_condition || _condition->test()) {
- SGRotateAnimation::Transform* transform;
- transform = static_cast<SGRotateAnimation::Transform*>(node);
- transform->setAngle(_animationValue->getValue());
+ SGRotateTransform* transform;
+ transform = static_cast<SGRotateTransform*>(node);
+ transform->setAngleDeg(_animationValue->getValue());
}
traverse(node, nv);
}
public:
SGSharedPtr<SGCondition const> _condition;
- SGSharedPtr<SGDoubleValue const> _animationValue;
+ SGSharedPtr<SGExpressiond const> _animationValue;
};
class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback {
public:
SpinUpdateCallback(SGCondition const* condition,
- SGDoubleValue const* animationValue) :
+ SGExpressiond const* animationValue) :
_condition(condition),
_animationValue(animationValue),
_lastTime(-1)
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (!_condition || _condition->test()) {
- SGRotateAnimation::Transform* transform;
- transform = static_cast<SGRotateAnimation::Transform*>(node);
+ SGRotateTransform* transform;
+ transform = static_cast<SGRotateTransform*>(node);
double t = nv->getFrameStamp()->getReferenceTime();
double dt = 0;
dt = t - _lastTime;
_lastTime = t;
double velocity_rpms = _animationValue->getValue()/60;
- double angle = transform->getAngle();
+ double angle = transform->getAngleDeg();
angle += dt*velocity_rpms*360;
angle -= 360*floor(angle/360);
- transform->setAngle(angle);
+ transform->setAngleDeg(angle);
}
traverse(node, nv);
}
public:
SGSharedPtr<SGCondition const> _condition;
- SGSharedPtr<SGDoubleValue const> _animationValue;
+ SGSharedPtr<SGExpressiond const> _animationValue;
double _lastTime;
};
-SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot) :
+SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode,
+ SGPropertyNode* modelRoot) :
SGAnimation(configNode, modelRoot)
{
std::string type = configNode->getStringValue("type", "");
_isSpin = (type == "spin");
_condition = getCondition();
- _animationValue = read_value(configNode, modelRoot, "-deg",
- -SGLimitsd::max(), SGLimitsd::max());
- _initialValue = configNode->getDoubleValue("starting-position-deg", 0);
- _initialValue *= configNode->getDoubleValue("factor", 1);
- _initialValue += configNode->getDoubleValue("offset-deg", 0);
+ SGSharedPtr<SGExpressiond> value;
+ value = read_value(configNode, modelRoot, "-deg",
+ -SGLimitsd::max(), SGLimitsd::max());
+ _animationValue = value->simplify();
+ if (_animationValue)
+ _initialValue = _animationValue->getValue();
+ else
+ _initialValue = 0;
_center = SGVec3d::zeros();
if (configNode->hasValue("axis/x1-m")) {
osg::Group*
SGRotateAnimation::createAnimationGroup(osg::Group& parent)
{
- Transform* transform = new Transform;
+ SGRotateTransform* transform = new SGRotateTransform;
transform->setName("rotate animation");
if (_isSpin) {
SpinUpdateCallback* uc;
uc = new SpinUpdateCallback(_condition, _animationValue);
transform->setUpdateCallback(uc);
- } else if (_animationValue) {
+ } else if (_animationValue || !_animationValue->isConst()) {
UpdateCallback* uc = new UpdateCallback(_condition, _animationValue);
transform->setUpdateCallback(uc);
}
transform->setCenter(_center);
transform->setAxis(_axis);
- transform->setAngle(_initialValue);
+ transform->setAngleDeg(_initialValue);
parent.addChild(transform);
return transform;
}
// Implementation of scale animation
////////////////////////////////////////////////////////////////////////
-class SGScaleAnimation::Transform : public osg::Transform {
-public:
- Transform() :
- _center(0, 0, 0),
- _scaleFactor(1, 1, 1),
- _boundScale(0)
- {
- setReferenceFrame(RELATIVE_RF);
- }
- void setCenter(const SGVec3d& center)
- {
- _center = center;
- dirtyBound();
- }
- void setScaleFactor(const SGVec3d& scaleFactor)
- {
- if (_boundScale < normI(scaleFactor))
- dirtyBound();
- _scaleFactor = scaleFactor;
- }
- void setScaleFactor(double scaleFactor)
- {
- if (_boundScale < fabs(scaleFactor))
- dirtyBound();
- _scaleFactor = SGVec3d(scaleFactor, scaleFactor, scaleFactor);
- }
- virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,
- osg::NodeVisitor* nv) const
- {
- assert(_referenceFrame == RELATIVE_RF);
- osg::Matrix transform;
- transform(0,0) = _scaleFactor[0];
- transform(1,1) = _scaleFactor[1];
- transform(2,2) = _scaleFactor[2];
- transform(3,0) = _center[0]*(1 - _scaleFactor[0]);
- transform(3,1) = _center[1]*(1 - _scaleFactor[1]);
- transform(3,2) = _center[2]*(1 - _scaleFactor[2]);
- matrix.preMult(transform);
- return true;
- }
- virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,
- osg::NodeVisitor* nv) const
- {
- assert(_referenceFrame == RELATIVE_RF);
- if (fabs(_scaleFactor[0]) < SGLimitsd::min())
- return false;
- if (fabs(_scaleFactor[1]) < SGLimitsd::min())
- return false;
- if (fabs(_scaleFactor[2]) < SGLimitsd::min())
- return false;
- SGVec3d rScaleFactor(1/_scaleFactor[0],
- 1/_scaleFactor[1],
- 1/_scaleFactor[2]);
- osg::Matrix transform;
- transform(0,0) = rScaleFactor[0];
- transform(1,1) = rScaleFactor[1];
- transform(2,2) = rScaleFactor[2];
- transform(3,0) = _center[0]*(1 - rScaleFactor[0]);
- transform(3,1) = _center[1]*(1 - rScaleFactor[1]);
- transform(3,2) = _center[2]*(1 - rScaleFactor[2]);
- matrix.postMult(transform);
- return true;
- }
- virtual osg::BoundingSphere computeBound() const
- {
- osg::BoundingSphere bs = osg::Group::computeBound();
- _boundScale = normI(_scaleFactor);
- bs.radius() *= _boundScale;
- return bs;
- }
-
-private:
- SGVec3d _center;
- SGVec3d _scaleFactor;
- mutable double _boundScale;
-};
-
class SGScaleAnimation::UpdateCallback : public osg::NodeCallback {
public:
UpdateCallback(const SGCondition* condition,
- SGSharedPtr<const SGDoubleValue> animationValue[3]) :
+ SGSharedPtr<const SGExpressiond> animationValue[3]) :
_condition(condition)
{
_animationValue[0] = animationValue[0];
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (!_condition || _condition->test()) {
- SGScaleAnimation::Transform* transform;
- transform = static_cast<SGScaleAnimation::Transform*>(node);
+ SGScaleTransform* transform;
+ transform = static_cast<SGScaleTransform*>(node);
SGVec3d scale(_animationValue[0]->getValue(),
_animationValue[1]->getValue(),
_animationValue[2]->getValue());
}
public:
SGSharedPtr<SGCondition const> _condition;
- SGSharedPtr<SGDoubleValue const> _animationValue[3];
+ SGSharedPtr<SGExpressiond const> _animationValue[3];
};
SGScaleAnimation::SGScaleAnimation(const SGPropertyNode* configNode,
_condition = getCondition();
// default offset/factor for all directions
- double offset = configNode->getDoubleValue("offset", 1);
+ double offset = configNode->getDoubleValue("offset", 0);
double factor = configNode->getDoubleValue("factor", 1);
+ SGSharedPtr<SGExpressiond> inPropExpr;
+
std::string inputPropertyName;
inputPropertyName = configNode->getStringValue("property", "");
- if (!inputPropertyName.empty()) {
+ if (inputPropertyName.empty()) {
+ inPropExpr = new SGConstExpression<double>(0);
+ } else {
SGPropertyNode* inputProperty;
- inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
- SGInterpTable* interpTable = read_interpolation_table(configNode);
- if (interpTable) {
- SGInterpTableValue* value;
- value = new SGInterpTableValue(inputProperty, interpTable);
- _animationValue[0] = value;
- _animationValue[1] = value;
- _animationValue[2] = value;
- } else if (configNode->getBoolValue("use-personality", false)) {
- SGPersScaleOffsetValue* value;
- value = new SGPersScaleOffsetValue(inputProperty, configNode,
- "x-factor", "x-offset",
- factor, offset);
- value->setMin(configNode->getDoubleValue("x-min", 0));
- value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
- _animationValue[0] = value;
- value = new SGPersScaleOffsetValue(inputProperty, configNode,
- "y-factor", "y-offset",
- factor, offset);
- value->setMin(configNode->getDoubleValue("y-min", 0));
- value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
- _animationValue[1] = value;
- value = new SGPersScaleOffsetValue(inputProperty, configNode,
- "z-factor", "z-offset",
- factor, offset);
- value->setMin(configNode->getDoubleValue("z-min", 0));
- value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
- _animationValue[2] = value;
- } else {
- SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
- value->setScale(configNode->getDoubleValue("x-factor", factor));
- value->setOffset(configNode->getDoubleValue("x-offset", offset));
- value->setMin(configNode->getDoubleValue("x-min", 0));
- value->setMax(configNode->getDoubleValue("x-max", SGLimitsd::max()));
- _animationValue[0] = value;
- value = new SGScaleOffsetValue(inputProperty);
- value->setScale(configNode->getDoubleValue("y-factor", factor));
- value->setOffset(configNode->getDoubleValue("y-offset", offset));
- value->setMin(configNode->getDoubleValue("y-min", 0));
- value->setMax(configNode->getDoubleValue("y-max", SGLimitsd::max()));
- _animationValue[1] = value;
- value = new SGScaleOffsetValue(inputProperty);
- value->setScale(configNode->getDoubleValue("z-factor", factor));
- value->setOffset(configNode->getDoubleValue("z-offset", offset));
- value->setMin(configNode->getDoubleValue("z-min", 0));
- value->setMax(configNode->getDoubleValue("z-max", SGLimitsd::max()));
- _animationValue[2] = value;
- }
+ inputProperty = modelRoot->getNode(inputPropertyName, true);
+ inPropExpr = new SGPropertyExpression<double>(inputProperty);
+ }
+
+ SGInterpTable* interpTable = read_interpolation_table(configNode);
+ if (interpTable) {
+ SGSharedPtr<SGExpressiond> value;
+ value = new SGInterpTableExpression<double>(inPropExpr, interpTable);
+ _animationValue[0] = value->simplify();
+ _animationValue[1] = value->simplify();
+ _animationValue[2] = value->simplify();
+ } else if (configNode->getBoolValue("use-personality", false)) {
+ SGSharedPtr<SGExpressiond> value;
+ value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
+ "x-factor", "x-offset",
+ factor, offset);
+ double minClip = configNode->getDoubleValue("x-min", 0);
+ double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
+ value = new SGClipExpression<double>(value, minClip, maxClip);
+ _animationValue[0] = value->simplify();
+
+ value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
+ "y-factor", "y-offset",
+ factor, offset);
+ minClip = configNode->getDoubleValue("y-min", 0);
+ maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
+ value = new SGClipExpression<double>(value, minClip, maxClip);
+ _animationValue[1] = value->simplify();
+
+ value = new SGPersonalityScaleOffsetExpression(inPropExpr, configNode,
+ "z-factor", "z-offset",
+ factor, offset);
+ minClip = configNode->getDoubleValue("z-min", 0);
+ maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
+ value = new SGClipExpression<double>(value, minClip, maxClip);
+ _animationValue[2] = value->simplify();
+ } else {
+ SGSharedPtr<SGExpressiond> value;
+ value = read_factor_offset(configNode, inPropExpr, "x-factor", "x-offset");
+ double minClip = configNode->getDoubleValue("x-min", 0);
+ double maxClip = configNode->getDoubleValue("x-max", SGLimitsd::max());
+ value = new SGClipExpression<double>(value, minClip, maxClip);
+ _animationValue[0] = value->simplify();
+
+ value = read_factor_offset(configNode, inPropExpr, "y-factor", "y-offset");
+ minClip = configNode->getDoubleValue("y-min", 0);
+ maxClip = configNode->getDoubleValue("y-max", SGLimitsd::max());
+ value = new SGClipExpression<double>(value, minClip, maxClip);
+ _animationValue[1] = value->simplify();
+
+ value = read_factor_offset(configNode, inPropExpr, "z-factor", "z-offset");
+ minClip = configNode->getDoubleValue("z-min", 0);
+ maxClip = configNode->getDoubleValue("z-max", SGLimitsd::max());
+ value = new SGClipExpression<double>(value, minClip, maxClip);
+ _animationValue[2] = value->simplify();
}
_initialValue[0] = configNode->getDoubleValue("x-starting-scale", 1);
_initialValue[0] *= configNode->getDoubleValue("x-factor", factor);
osg::Group*
SGScaleAnimation::createAnimationGroup(osg::Group& parent)
{
- Transform* transform = new Transform;
+ SGScaleTransform* transform = new SGScaleTransform;
transform->setName("scale animation");
transform->setCenter(_center);
transform->setScaleFactor(_initialValue);
}
\f
+// Don't create a new state state everytime we need GL_NORMALIZE!
+
+namespace
+{
+Mutex normalizeMutex;
+
+osg::StateSet* getNormalizeStateSet()
+{
+ static osg::ref_ptr<osg::StateSet> normalizeStateSet;
+ ScopedLock<Mutex> lock(normalizeMutex);
+ if (!normalizeStateSet.valid()) {
+ normalizeStateSet = new osg::StateSet;
+ normalizeStateSet->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
+ normalizeStateSet->setDataVariance(osg::Object::STATIC);
+ }
+ return normalizeStateSet.get();
+}
+}
+
////////////////////////////////////////////////////////////////////////
// Implementation of dist scale animation
////////////////////////////////////////////////////////////////////////
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"));
setReferenceFrame(RELATIVE_RF);
- getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
+ setStateSet(getNormalizeStateSet());
_factor = configNode->getFloatValue("factor", 1);
_offset = configNode->getFloatValue("offset", 0);
_min_v = configNode->getFloatValue("min", SGLimitsf::epsilon());
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
{
if (!nv)
return 1;
- double scale_factor = (_center.osg() - nv->getEyePoint()).length();
+ double scale_factor = (toOsg(_center) - nv->getEyePoint()).length();
if (_table == 0) {
scale_factor = _factor * scale_factor + _offset;
} else {
return transform;
}
+namespace
+{
+ osgDB::RegisterDotOsgWrapperProxy distScaleAnimationTransformProxy
+ (
+ new SGDistScaleAnimation::Transform,
+ "SGDistScaleAnimation::Transform",
+ "Object Node Transform SGDistScaleAnimation::Transform Group",
+ 0,
+ &SGDistScaleAnimation::Transform::writeLocalData
+ );
+}
\f
////////////////////////////////////////////////////////////////////////
// Implementation of flash animation
class SGFlashAnimation::Transform : public osg::Transform {
public:
+ Transform() : _power(0.0), _factor(0.0), _offset(0.0), _min_v(0.0),
+ _max_v(0.0), _two_sides(false)
+ {}
+
+ Transform(const Transform& rhs,
+ const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
+ : osg::Transform(rhs, copyOp), _center(rhs._center), _axis(rhs._axis),
+ _power(rhs._power), _factor(rhs._factor), _offset(rhs._offset),
+ _min_v(rhs._min_v), _max_v(rhs._max_v), _two_sides(rhs._two_sides)
+ {
+ }
+ META_Node(simgear, SGFlashAnimation::Transform);
+
Transform(const SGPropertyNode* configNode)
{
setReferenceFrame(RELATIVE_RF);
setName(configNode->getStringValue("name", "flash animation"));
- getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
+ setStateSet(getNormalizeStateSet());
_axis[0] = configNode->getFloatValue("axis/x", 0);
_axis[1] = configNode->getFloatValue("axis/y", 0);
return true;
}
+ static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
+ {
+ const Transform& trans = static_cast<const Transform&>(obj);
+ fw.indent() << "center " << trans._center[0] << " "
+ << trans._center[1] << " " << trans._center[2] << " " << "\n";
+ fw.indent() << "axis " << trans._axis[0] << " "
+ << trans._axis[1] << " " << trans._axis[2] << " " << "\n";
+ fw.indent() << "power " << trans._power << " \n";
+ fw.indent() << "min_v " << trans._min_v << "\n";
+ fw.indent() << "max_v " << trans._max_v << "\n";
+ fw.indent() << "factor " << trans._factor << "\n";
+ fw.indent() << "offset " << trans._offset << "\n";
+ fw.indent() << "twosides " << (trans._two_sides ? "true" : "false") << "\n";
+ return true;
+ }
private:
double computeScaleFactor(osg::NodeVisitor* nv) const
{
return transform;
}
+namespace
+{
+ osgDB::RegisterDotOsgWrapperProxy flashAnimationTransformProxy
+ (
+ new SGFlashAnimation::Transform,
+ "SGFlashAnimation::Transform",
+ "Object Node Transform SGFlashAnimation::Transform Group",
+ 0,
+ &SGFlashAnimation::Transform::writeLocalData
+ );
+}
\f
////////////////////////////////////////////////////////////////////////
-// Implementation of flash animation
+// Implementation of billboard animation
////////////////////////////////////////////////////////////////////////
class SGBillboardAnimation::Transform : public osg::Transform {
public:
+ Transform() : _spherical(true) {}
+ Transform(const Transform& rhs,
+ const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
+ : osg::Transform(rhs, copyOp), _spherical(rhs._spherical) {}
+ META_Node(simgear, SGBillboardAnimation::Transform);
Transform(const SGPropertyNode* configNode) :
_spherical(configNode->getBoolValue("spherical", true))
{
// Hmm, don't yet know how to get that back ...
return false;
}
+ static bool writeLocalData(const osg::Object& obj, osgDB::Output& fw)
+ {
+ const Transform& trans = static_cast<const Transform&>(obj);
+ fw.indent() << (trans._spherical ? "true" : "false") << "\n";
+ return true;
+ }
private:
bool _spherical;
};
return transform;
}
+namespace
+{
+ osgDB::RegisterDotOsgWrapperProxy billboardAnimationTransformProxy
+ (
+ new SGBillboardAnimation::Transform,
+ "SGBillboardAnimation::Transform",
+ "Object Node Transform SGBillboardAnimation::Transform Group",
+ 0,
+ &SGBillboardAnimation::Transform::writeLocalData
+ );
+}
\f
////////////////////////////////////////////////////////////////////////
// Implementation of a range animation
class SGRangeAnimation::UpdateCallback : public osg::NodeCallback {
public:
UpdateCallback(const SGCondition* condition,
- const SGDoubleValue* minAnimationValue,
- const SGDoubleValue* maxAnimationValue,
+ const SGExpressiond* minAnimationValue,
+ const SGExpressiond* maxAnimationValue,
double minValue, double maxValue) :
_condition(condition),
_minAnimationValue(minAnimationValue),
private:
SGSharedPtr<const SGCondition> _condition;
- SGSharedPtr<const SGDoubleValue> _minAnimationValue;
- SGSharedPtr<const SGDoubleValue> _maxAnimationValue;
+ SGSharedPtr<const SGExpressiond> _minAnimationValue;
+ SGSharedPtr<const SGExpressiond> _maxAnimationValue;
double _minStaticValue;
double _maxStaticValue;
};
inputPropertyName = configNode->getStringValue("min-property", "");
if (!inputPropertyName.empty()) {
SGPropertyNode* inputProperty;
- inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
- SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
- value->setScale(configNode->getDoubleValue("min-factor", 1));
- _minAnimationValue = value;
+ inputProperty = modelRoot->getNode(inputPropertyName, true);
+ SGSharedPtr<SGExpressiond> value;
+ value = new SGPropertyExpression<double>(inputProperty);
+
+ value = read_factor_offset(configNode, value, "min-factor", "min-offset");
+ _minAnimationValue = value->simplify();
}
inputPropertyName = configNode->getStringValue("max-property", "");
if (!inputPropertyName.empty()) {
SGPropertyNode* inputProperty;
inputProperty = modelRoot->getNode(inputPropertyName.c_str(), true);
- SGScaleOffsetValue* value = new SGScaleOffsetValue(inputProperty);
- value->setScale(configNode->getDoubleValue("max-factor", 1));
- _maxAnimationValue = value;
+
+ SGSharedPtr<SGExpressiond> value;
+ value = new SGPropertyExpression<double>(inputProperty);
+
+ value = read_factor_offset(configNode, value, "max-factor", "max-offset");
+ _maxAnimationValue = value->simplify();
}
_initialValue[0] = configNode->getDoubleValue("min-m", 0);
// Implementation of a select animation
////////////////////////////////////////////////////////////////////////
-class SGSelectAnimation::UpdateCallback : public osg::NodeCallback {
-public:
- UpdateCallback(const SGCondition* condition) :
- _condition(condition)
- {}
- virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
- {
- osg::Switch* sw = static_cast<osg::Switch*>(node);
- if (_condition->test())
- sw->setAllChildrenOn();
- else
- sw->setAllChildrenOff();
- traverse(node, nv);
- }
-
-private:
- SGSharedPtr<SGCondition const> _condition;
-};
-
SGSelectAnimation::SGSelectAnimation(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) :
SGAnimation(configNode, modelRoot)
// when the animation installer returns
if (!condition)
return new osg::Group;
-
- osg::Switch* sw = new osg::Switch;
- sw->setName("select animation node");
- sw->setUpdateCallback(new UpdateCallback(condition));
- parent.addChild(sw);
- return sw;
+ simgear::ConditionNode* cn = new simgear::ConditionNode;
+ cn->setName("select animation node");
+ cn->setCondition(condition.ptr());
+ osg::Group* grp = new osg::Group;
+ cn->addChild(grp);
+ parent.addChild(cn);
+ return grp;
}
{
}
+namespace
+{
+// Keep one copy of the most common alpha test its state set.
+ReentrantMutex alphaTestMutex;
+osg::ref_ptr<osg::AlphaFunc> standardAlphaFunc;
+osg::ref_ptr<osg::StateSet> alphaFuncStateSet;
+
+osg::AlphaFunc* makeAlphaFunc(float clamp)
+{
+ ScopedLock<ReentrantMutex> lock(alphaTestMutex);
+ if (osg::equivalent(clamp, 0.01f)) {
+ if (standardAlphaFunc.valid())
+ return standardAlphaFunc.get();
+ clamp = .01;
+ }
+ osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
+ alphaFunc->setFunction(osg::AlphaFunc::GREATER);
+ alphaFunc->setReferenceValue(clamp);
+ alphaFunc->setDataVariance(osg::Object::STATIC);
+ if (osg::equivalent(clamp, 0.01f))
+ standardAlphaFunc = alphaFunc;
+ return alphaFunc;
+}
+
+osg::StateSet* makeAlphaTestStateSet(float clamp)
+{
+ using namespace OpenThreads;
+ ScopedLock<ReentrantMutex> lock(alphaTestMutex);
+ if (osg::equivalent(clamp, 0.01f)) {
+ if (alphaFuncStateSet.valid())
+ return alphaFuncStateSet.get();
+ }
+ osg::AlphaFunc* alphaFunc = makeAlphaFunc(clamp);
+ osg::StateSet* stateSet = new osg::StateSet;
+ stateSet->setAttributeAndModes(alphaFunc,
+ (osg::StateAttribute::ON
+ | osg::StateAttribute::OVERRIDE));
+ stateSet->setDataVariance(osg::Object::STATIC);
+ if (osg::equivalent(clamp, 0.01f))
+ alphaFuncStateSet = stateSet;
+ return stateSet;
+}
+}
void
SGAlphaTestAnimation::install(osg::Node& node)
{
SGAnimation::install(node);
-
- cloneDrawables(node);
- removeMode(node, GL_ALPHA_TEST);
- removeAttribute(node, osg::StateAttribute::ALPHAFUNC);
- osg::StateSet* stateSet = node.getOrCreateStateSet();
- osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
- alphaFunc->setFunction(osg::AlphaFunc::GREATER);
float alphaClamp = getConfig()->getFloatValue("alpha-factor", 0);
- alphaFunc->setReferenceValue(alphaClamp);
- stateSet->setAttribute(alphaFunc);
- stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
+ osg::StateSet* stateSet = node.getStateSet();
+ if (!stateSet) {
+ node.setStateSet(makeAlphaTestStateSet(alphaClamp));
+ } else {
+ stateSet->setAttributeAndModes(makeAlphaFunc(alphaClamp),
+ (osg::StateAttribute::ON
+ | osg::StateAttribute::OVERRIDE));
+ }
}
-
\f
//////////////////////////////////////////////////////////////////////
// Blend animation installer
//////////////////////////////////////////////////////////////////////
+// XXX This needs to be replaced by something using TexEnvCombine to
+// change the blend factor. Changing the alpha values in the geometry
+// is bogus.
class SGBlendAnimation::BlendVisitor : public osg::NodeVisitor {
public:
BlendVisitor(float blend) :
unsigned nDrawables = node.getNumDrawables();
for (unsigned i = 0; i < nDrawables; ++i) {
osg::Drawable* drawable = node.getDrawable(i);
- updateStateSet(drawable->getStateSet());
osg::Geometry* geometry = drawable->asGeometry();
if (!geometry)
continue;
osg::Vec4Array* vec4Array = dynamic_cast<osg::Vec4Array*>(array);
if (!vec4Array)
continue;
- geometry->dirtyDisplayList();
- vec4Array->dirty();
for (unsigned k = 0; k < vec4Array->size(); ++k) {
(*vec4Array)[k][3] = _blend;
}
+ vec4Array->dirty();
+ updateStateSet(drawable->getStateSet());
}
}
void updateStateSet(osg::StateSet* stateSet)
class SGBlendAnimation::UpdateCallback : public osg::NodeCallback {
public:
- UpdateCallback(const SGPropertyNode* configNode, const SGDoubleValue* v) :
+ UpdateCallback(const SGPropertyNode* configNode, const SGExpressiond* v) :
_prev_value(-1),
_animationValue(v)
{ }
}
public:
double _prev_value;
- SGSharedPtr<SGDoubleValue const> _animationValue;
+ SGSharedPtr<SGExpressiond const> _animationValue;
};
// make sure we do not change common geometries,
// that also creates new display lists for these subgeometries.
cloneDrawables(node);
+ DoDrawArraysVisitor visitor;
+ node.accept(visitor);
}
\f
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (_condition->test())
- node->setNodeMask( SG_NODEMASK_SHADOW_BIT | node->getNodeMask());
+ node->setNodeMask( SG_NODEMASK_CASTSHADOW_BIT | node->getNodeMask());
else
- node->setNodeMask(~SG_NODEMASK_SHADOW_BIT & node->getNodeMask());
+ node->setNodeMask(~SG_NODEMASK_CASTSHADOW_BIT & node->getNodeMask());
traverse(node, nv);
}
Translation(const SGVec3d& axis) :
_axis(axis)
{ }
- void setValue(double value)
- { _value = value; }
virtual void transform(osg::Matrix& matrix)
{
osg::Matrix tmp;
for (i = _transforms.begin(); i != _transforms.end(); ++i)
i->transform->transform(texMat->getMatrix());
}
- void appendTransform(Transform* transform, SGDoubleValue* value)
+ void appendTransform(Transform* transform, SGExpressiond* value)
{
Entry entry = { transform, value };
transform->transform(_matrix);
private:
struct Entry {
SGSharedPtr<Transform> transform;
- SGSharedPtr<const SGDoubleValue> value;
+ SGSharedPtr<const SGExpressiond> value;
};
typedef std::vector<Entry> TransformList;
TransformList _transforms;
osg::Group* group = new osg::Group;
group->setName("texture transform group");
osg::StateSet* stateSet = group->getOrCreateStateSet();
+ stateSet->setDataVariance(osg::Object::DYNAMIC);
osg::TexMat* texMat = new osg::TexMat;
UpdateCallback* updateCallback = new UpdateCallback(getCondition());
// interpret the configs ...
SGTexTransformAnimation::appendTexTranslate(const SGPropertyNode* config,
UpdateCallback* updateCallback)
{
- std::string propertyName = config->getStringValue("property", "/null");
- SGPropertyNode* inputNode;
- inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
+ std::string propertyName = config->getStringValue("property", "");
+ SGSharedPtr<SGExpressiond> value;
+ if (propertyName.empty())
+ value = new SGConstExpression<double>(0);
+ else {
+ SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
+ value = new SGPropertyExpression<double>(inputProperty);
+ }
- SGDoubleValue* animationValue;
SGInterpTable* table = read_interpolation_table(config);
if (table) {
- SGTexTableValue* value;
- value = new SGTexTableValue(inputNode, table);
- value->setStep(config->getDoubleValue("step", 0));
- value->setScroll(config->getDoubleValue("scroll", 0));
- animationValue = value;
+ value = new SGInterpTableExpression<double>(value, table);
+ double biasValue = config->getDoubleValue("bias", 0);
+ if (biasValue != 0)
+ value = new SGBiasExpression<double>(value, biasValue);
+ value = new SGStepExpression<double>(value,
+ config->getDoubleValue("step", 0),
+ config->getDoubleValue("scroll", 0));
+ value = value->simplify();
} else {
- SGTexScaleOffsetValue* value;
- value = new SGTexScaleOffsetValue(inputNode);
- value->setScale(config->getDoubleValue("factor", 1));
- value->setOffset(config->getDoubleValue("offset", 0));
- value->setStep(config->getDoubleValue("step", 0));
- value->setScroll(config->getDoubleValue("scroll", 0));
- value->setMin(config->getDoubleValue("min", -SGLimitsd::max()));
- value->setMax(config->getDoubleValue("max", SGLimitsd::max()));
- animationValue = value;
+ double biasValue = config->getDoubleValue("bias", 0);
+ if (biasValue != 0)
+ value = new SGBiasExpression<double>(value, biasValue);
+ value = new SGStepExpression<double>(value,
+ config->getDoubleValue("step", 0),
+ config->getDoubleValue("scroll", 0));
+ value = read_offset_factor(config, value, "factor", "offset");
+
+ if (config->hasChild("min") || config->hasChild("max")) {
+ double minClip = config->getDoubleValue("min", -SGLimitsd::max());
+ double maxClip = config->getDoubleValue("max", SGLimitsd::max());
+ value = new SGClipExpression<double>(value, minClip, maxClip);
+ }
+ value = value->simplify();
}
SGVec3d axis(config->getDoubleValue("axis/x", 0),
config->getDoubleValue("axis/y", 0),
Translation* translation;
translation = new Translation(normalize(axis));
translation->setValue(config->getDoubleValue("starting-position", 0));
- updateCallback->appendTransform(translation, animationValue);
+ updateCallback->appendTransform(translation, value);
}
void
SGTexTransformAnimation::appendTexRotate(const SGPropertyNode* config,
UpdateCallback* updateCallback)
{
- std::string propertyName = config->getStringValue("property", "/null");
- SGPropertyNode* inputNode;
- inputNode = getModelRoot()->getNode(propertyName.c_str(), true);
+ std::string propertyName = config->getStringValue("property", "");
+ SGSharedPtr<SGExpressiond> value;
+ if (propertyName.empty())
+ value = new SGConstExpression<double>(0);
+ else {
+ SGPropertyNode* inputProperty = getModelRoot()->getNode(propertyName, true);
+ value = new SGPropertyExpression<double>(inputProperty);
+ }
- SGDoubleValue* animationValue;
SGInterpTable* table = read_interpolation_table(config);
if (table) {
- SGTexTableValue* value;
- value = new SGTexTableValue(inputNode, table);
- value->setStep(config->getDoubleValue("step", 0));
- value->setScroll(config->getDoubleValue("scroll", 0));
- animationValue = value;
+ value = new SGInterpTableExpression<double>(value, table);
+ double biasValue = config->getDoubleValue("bias", 0);
+ if (biasValue != 0)
+ value = new SGBiasExpression<double>(value, biasValue);
+ value = new SGStepExpression<double>(value,
+ config->getDoubleValue("step", 0),
+ config->getDoubleValue("scroll", 0));
+ value = value->simplify();
} else {
- SGTexScaleOffsetValue* value;
- value = new SGTexScaleOffsetValue(inputNode);
- value->setScale(config->getDoubleValue("factor", 1));
- value->setOffset(config->getDoubleValue("offset-deg", 0));
- value->setStep(config->getDoubleValue("step", 0));
- value->setScroll(config->getDoubleValue("scroll", 0));
- value->setMin(config->getDoubleValue("min-deg", -SGLimitsd::max()));
- value->setMax(config->getDoubleValue("max-deg", SGLimitsd::max()));
- animationValue = value;
+ double biasValue = config->getDoubleValue("bias", 0);
+ if (biasValue != 0)
+ value = new SGBiasExpression<double>(value, biasValue);
+ value = new SGStepExpression<double>(value,
+ config->getDoubleValue("step", 0),
+ config->getDoubleValue("scroll", 0));
+ value = read_offset_factor(config, value, "factor", "offset-deg");
+
+ if (config->hasChild("min-deg") || config->hasChild("max-deg")) {
+ double minClip = config->getDoubleValue("min-deg", -SGLimitsd::max());
+ double maxClip = config->getDoubleValue("max-deg", SGLimitsd::max());
+ value = new SGClipExpression<double>(value, minClip, maxClip);
+ }
+ value = value->simplify();
}
SGVec3d axis(config->getDoubleValue("axis/x", 0),
config->getDoubleValue("axis/y", 0),
Rotation* rotation;
rotation = new Rotation(normalize(axis), center);
rotation->setValue(config->getDoubleValue("starting-position-deg", 0));
- updateCallback->appendTransform(rotation, animationValue);
+ updateCallback->appendTransform(rotation, value);
}
public:
PickCallback(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) :
- _button(configNode->getIntValue("button", -1)),
_repeatable(configNode->getBoolValue("repeatable", false)),
_repeatInterval(configNode->getDoubleValue("interval-sec", 0.1))
{
SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
std::vector<SGPropertyNode_ptr> bindings;
+
+ bindings = configNode->getChildren("button");
+ for (unsigned int i = 0; i < bindings.size(); ++i) {
+ _buttons.push_back( bindings[i]->getIntValue() );
+ }
bindings = configNode->getChildren("binding");
for (unsigned int i = 0; i < bindings.size(); ++i) {
_bindingsDown.push_back(new SGBinding(bindings[i], modelRoot));
}
virtual bool buttonPressed(int button, const Info&)
{
- if (0 <= _button && button != _button)
+ bool found = false;
+ for( std::vector<int>::iterator it = _buttons.begin(); it != _buttons.end(); it++ ) {
+ if( *it == button ) {
+ found = true;
+ break;
+ }
+ }
+ if (!found )
return false;
SGBindingList::const_iterator i;
for (i = _bindingsDown.begin(); i != _bindingsDown.end(); ++i)
(*i)->fire();
- _repeatTime = 0;
+ _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat
return true;
}
virtual void buttonReleased(void)
private:
SGBindingList _bindingsDown;
SGBindingList _bindingsUp;
- int _button;
+ std::vector<int> _buttons;
bool _repeatable;
double _repeatInterval;
double _repeatTime;
};
+class VncVisitor : public osg::NodeVisitor {
+ public:
+ VncVisitor(double x, double y, int mask) :
+ osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+ _texX(x), _texY(y), _mask(mask), _done(false)
+ {
+ SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor constructor "
+ << x << "," << y << " mask " << mask);
+ }
+
+ virtual void apply(osg::Node &node)
+ {
+ // Some nodes have state sets attached
+ touchStateSet(node.getStateSet());
+ if (!_done)
+ traverse(node);
+ if (_done) return;
+ // See whether we are a geode worth exploring
+ osg::Geode *g = dynamic_cast<osg::Geode*>(&node);
+ if (!g) return;
+ // Go find all its drawables
+ int i = g->getNumDrawables();
+ while (--i >= 0) {
+ osg::Drawable *d = g->getDrawable(i);
+ if (d) touchDrawable(*d);
+ }
+ // Out of optimism, do the same for EffectGeode
+ simgear::EffectGeode *eg = dynamic_cast<simgear::EffectGeode*>(&node);
+ if (!eg) return;
+ for (simgear::EffectGeode::DrawablesIterator di = eg->drawablesBegin();
+ di != eg->drawablesEnd(); di++) {
+ touchDrawable(**di);
+ }
+ // Now see whether the EffectGeode has an Effect
+ simgear::Effect *e = eg->getEffect();
+ if (e) {
+ touchStateSet(e->getDefaultStateSet());
+ }
+ }
+
+ inline void touchDrawable(osg::Drawable &d)
+ {
+ osg::StateSet *ss = d.getStateSet();
+ touchStateSet(ss);
+ }
+
+ void touchStateSet(osg::StateSet *ss)
+ {
+ if (!ss) return;
+ osg::StateAttribute *sa = ss->getTextureAttribute(0,
+ osg::StateAttribute::TEXTURE);
+ if (!sa) return;
+ osg::Texture *t = sa->asTexture();
+ if (!t) return;
+ osg::Image *img = t->getImage(0);
+ if (!img) return;
+ if (!_done) {
+ int pixX = _texX * img->s();
+ int pixY = _texY * img->t();
+ _done = img->sendPointerEvent(pixX, pixY, _mask);
+ SG_LOG(SG_INPUT, SG_DEBUG, "VncVisitor image said " << _done
+ << " to coord " << pixX << "," << pixY);
+ }
+ }
+
+ inline bool wasSuccessful()
+ {
+ return _done;
+ }
+
+ private:
+ double _texX, _texY;
+ int _mask;
+ bool _done;
+};
+
+
+class SGPickAnimation::VncCallback : public SGPickCallback {
+public:
+ VncCallback(const SGPropertyNode* configNode,
+ SGPropertyNode* modelRoot,
+ osg::Group *node)
+ : _node(node)
+ {
+ SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback");
+ const char *cornernames[3] = {"top-left", "top-right", "bottom-left"};
+ SGVec3d *cornercoords[3] = {&_topLeft, &_toRight, &_toDown};
+ for (int c =0; c < 3; c++) {
+ const SGPropertyNode* cornerNode = configNode->getChild(cornernames[c]);
+ *cornercoords[c] = SGVec3d(
+ cornerNode->getDoubleValue("x"),
+ cornerNode->getDoubleValue("y"),
+ cornerNode->getDoubleValue("z"));
+ }
+ _toRight -= _topLeft;
+ _toDown -= _topLeft;
+ _squaredRight = dot(_toRight, _toRight);
+ _squaredDown = dot(_toDown, _toDown);
+ }
+
+ virtual bool buttonPressed(int button, const Info& info)
+ {
+ SGVec3d loc(info.local);
+ SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc);
+ loc -= _topLeft;
+ _x = dot(loc, _toRight) / _squaredRight;
+ _y = dot(loc, _toDown) / _squaredDown;
+ if (_x<0) _x = 0; else if (_x > 1) _x = 1;
+ if (_y<0) _y = 0; else if (_y > 1) _y = 1;
+ VncVisitor vv(_x, _y, 1 << button);
+ _node->accept(vv);
+ return vv.wasSuccessful();
+
+ }
+ virtual void buttonReleased(void)
+ {
+ SG_LOG(SG_INPUT, SG_DEBUG, "VNC release");
+ VncVisitor vv(_x, _y, 0);
+ _node->accept(vv);
+ }
+ virtual void update(double dt)
+ {
+ }
+private:
+ double _x, _y;
+ osg::ref_ptr<osg::Group> _node;
+ SGVec3d _topLeft, _toRight, _toDown;
+ double _squaredRight, _squaredDown;
+};
+
SGPickAnimation::SGPickAnimation(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) :
SGAnimation(configNode, modelRoot)
// Contains the normal geometry that is interactive
osg::ref_ptr<osg::Group> normalGroup = new osg::Group;
+ normalGroup->setName("pick normal group");
normalGroup->addChild(commonGroup);
// Used to render the geometry with just yellow edges
osg::Group* highlightGroup = new osg::Group;
+ highlightGroup->setName("pick highlight group");
highlightGroup->setNodeMask(SG_NODEMASK_PICK_BIT);
highlightGroup->addChild(commonGroup);
SGSceneUserData* ud;
- ud = SGSceneUserData::getOrCreateSceneUserData(highlightGroup);
+ ud = SGSceneUserData::getOrCreateSceneUserData(commonGroup);
+
+ // add actions that become macro and command invocations
std::vector<SGPropertyNode_ptr> actions;
actions = getConfig()->getChildren("action");
for (unsigned int i = 0; i < actions.size(); ++i)
ud->addPickCallback(new PickCallback(actions[i], getModelRoot()));
+ // Look for the VNC sessions that want raw mouse input
+ actions = getConfig()->getChildren("vncaction");
+ for (unsigned int i = 0; i < actions.size(); ++i)
+ ud->addPickCallback(new VncCallback(actions[i], getModelRoot(),
+ &parent));
// prepare a state set that paints the edges of this object yellow
osg::StateSet* stateSet = highlightGroup->getOrCreateStateSet();