Make SGLight configurable at runtime.
This commit is contained in:
@@ -2800,12 +2800,12 @@ namespace simgear
|
||||
}
|
||||
|
||||
|
||||
void SGPropertyNode::copy(SGPropertyNode *to)
|
||||
void SGPropertyNode::copy(SGPropertyNode *to) const
|
||||
{
|
||||
if (nChildren())
|
||||
{
|
||||
for (int i = 0; i < nChildren(); i++) {
|
||||
SGPropertyNode *child = getChild(i);
|
||||
const SGPropertyNode *child = getChild(i);
|
||||
SGPropertyNode *to_child = to->getChild(child->getName());
|
||||
if (!to_child)
|
||||
to_child = to->addChild(child->getName());
|
||||
|
||||
@@ -1098,7 +1098,7 @@ public:
|
||||
/**
|
||||
* deep copy one node to another.
|
||||
*/
|
||||
void copy(SGPropertyNode *to);
|
||||
void copy(SGPropertyNode *to) const;
|
||||
|
||||
/**
|
||||
* Get a pointer to another node by relative path.
|
||||
|
||||
@@ -39,99 +39,74 @@ private:
|
||||
osg::ref_ptr<osg::Switch> _sw;
|
||||
};
|
||||
|
||||
class SGLightConfigListener : public SGPropertyChangeListener {
|
||||
public:
|
||||
SGLightConfigListener(SGLight *light) : _light(light) {}
|
||||
|
||||
virtual void valueChanged(SGPropertyNode *node) {
|
||||
while (strcmp(node->getName(), "light") && node->getParent()) {
|
||||
node = node->getParent();
|
||||
}
|
||||
_light->configure(node);
|
||||
auto *cb = _light->getUpdateCallback();
|
||||
if (cb != nullptr) {
|
||||
dynamic_cast<SGLight::UpdateCallback*>(cb)->configure(_light->getAmbient(), _light->getDiffuse(), _light->getSpecular());
|
||||
}
|
||||
}
|
||||
private:
|
||||
SGLight *_light;
|
||||
};
|
||||
|
||||
SGLight::SGLight(const SGLight& l, const osg::CopyOp& copyop) :
|
||||
osg::Node(l, copyop),
|
||||
_type(l._type),
|
||||
_range(l._range),
|
||||
_ambient(l._ambient),
|
||||
_diffuse(l._diffuse),
|
||||
_specular(l._specular),
|
||||
_constant_attenuation(l._constant_attenuation),
|
||||
_linear_attenuation(l._linear_attenuation),
|
||||
_quadratic_attenuation(l._quadratic_attenuation),
|
||||
_spot_exponent(l._spot_exponent),
|
||||
_spot_cutoff(l._spot_cutoff)
|
||||
{}
|
||||
|
||||
osg::Node *
|
||||
SGLight::appendLight(const SGPropertyNode *configNode,
|
||||
SGPropertyNode *modelRoot,
|
||||
const osgDB::Options *options)
|
||||
{
|
||||
SGConstPropertyNode_ptr p;
|
||||
|
||||
SGLight *light = new SGLight;
|
||||
|
||||
if((p = configNode->getNode("type")) != NULL) {
|
||||
std::string type = p->getStringValue();
|
||||
if (type == "point")
|
||||
light->setType(SGLight::POINT);
|
||||
else if (type == "spot")
|
||||
light->setType(SGLight::SPOT);
|
||||
else
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "ignoring unknown light type '" << type << "'");
|
||||
}
|
||||
|
||||
light->setRange(configNode->getFloatValue("range-m"));
|
||||
|
||||
std::string priority = configNode->getStringValue("priority", "low");
|
||||
if (priority == "high")
|
||||
light->setPriority(SGLight::HIGH);
|
||||
else if (priority == "medium")
|
||||
light->setPriority(SGLight::MEDIUM);
|
||||
else
|
||||
light->setPriority(SGLight::LOW);
|
||||
|
||||
#define SGLIGHT_GET_COLOR_VALUE(n) \
|
||||
osg::Vec4(configNode->getFloatValue(n "/r"), \
|
||||
configNode->getFloatValue(n "/g"), \
|
||||
configNode->getFloatValue(n "/b"), \
|
||||
configNode->getFloatValue(n "/a"))
|
||||
light->setAmbient(SGLIGHT_GET_COLOR_VALUE("ambient"));
|
||||
light->setDiffuse(SGLIGHT_GET_COLOR_VALUE("diffuse"));
|
||||
light->setSpecular(SGLIGHT_GET_COLOR_VALUE("specular"));
|
||||
#undef SGLIGHT_GET_COLOR_VALUE
|
||||
|
||||
light->setConstantAttenuation(configNode->getFloatValue("attenuation/c"));
|
||||
light->setLinearAttenuation(configNode->getFloatValue("attenuation/l"));
|
||||
light->setQuadraticAttenuation(configNode->getFloatValue("attenuation/q"));
|
||||
|
||||
light->setSpotExponent(configNode->getFloatValue("spot-exponent"));
|
||||
light->setSpotCutoff(configNode->getFloatValue("spot-cutoff"));
|
||||
|
||||
//-- create return variable
|
||||
osg::MatrixTransform *align = new osg::MatrixTransform;
|
||||
|
||||
SGLight *light = new SGLight(align);
|
||||
align->addChild(light);
|
||||
|
||||
osg::Matrix t;
|
||||
osg::Vec3 pos(configNode->getFloatValue("position/x-m"),
|
||||
configNode->getFloatValue("position/y-m"),
|
||||
configNode->getFloatValue("position/z-m"));
|
||||
t.makeTranslate(pos);
|
||||
|
||||
osg::Matrix r;
|
||||
if (const SGPropertyNode *dirNode = configNode->getNode("direction")) {
|
||||
if (dirNode->hasValue("pitch-deg")) {
|
||||
r.makeRotate(
|
||||
dirNode->getFloatValue("pitch-deg")*SG_DEGREES_TO_RADIANS,
|
||||
osg::Vec3(0, 1, 0),
|
||||
dirNode->getFloatValue("roll-deg")*SG_DEGREES_TO_RADIANS,
|
||||
osg::Vec3(1, 0, 0),
|
||||
dirNode->getFloatValue("heading-deg")*SG_DEGREES_TO_RADIANS,
|
||||
osg::Vec3(0, 0, 1));
|
||||
} else if (dirNode->hasValue("lookat-x-m")) {
|
||||
osg::Vec3 lookAt(dirNode->getFloatValue("lookat-x-m"),
|
||||
dirNode->getFloatValue("lookat-y-m"),
|
||||
dirNode->getFloatValue("lookat-z-m"));
|
||||
osg::Vec3 dir = lookAt - pos;
|
||||
r.makeRotate(osg::Vec3(0, 0, -1), dir);
|
||||
} else {
|
||||
r.makeRotate(osg::Vec3(0, 0, -1),
|
||||
osg::Vec3(dirNode->getFloatValue("x"),
|
||||
dirNode->getFloatValue("y"),
|
||||
dirNode->getFloatValue("z")));
|
||||
}
|
||||
}
|
||||
align->setMatrix(r * t);
|
||||
|
||||
//-- copy config to prop tree --
|
||||
const std::string propPath {"/scenery/lights"};
|
||||
const std::string propName {"light"};
|
||||
SGPropertyNode *_pConfig = simgear::getPropertyRoot()->getNode(propPath, true)
|
||||
->addChild(propName);
|
||||
configNode->copy(_pConfig);
|
||||
|
||||
//-- setup osg::NodeCallback --
|
||||
const SGPropertyNode *dim_factor = configNode->getChild("dim-factor");
|
||||
if (dim_factor) {
|
||||
const SGExpressiond *expression =
|
||||
read_value(dim_factor, modelRoot, "", 0, 1);
|
||||
if (expression) {
|
||||
light->setUpdateCallback(
|
||||
new SGLight::UpdateCallback(expression,
|
||||
light->getAmbient(),
|
||||
light->getDiffuse(),
|
||||
light->getSpecular()));
|
||||
}
|
||||
if (expression)
|
||||
light->setUpdateCallback(new SGLight::UpdateCallback(expression));
|
||||
}
|
||||
else {
|
||||
//need UpdateCallback to activate config changes at runtime
|
||||
light->setUpdateCallback(new SGLight::UpdateCallback());
|
||||
}
|
||||
|
||||
//-- configure light and add listener to conf in prop tree
|
||||
light->configure(configNode);
|
||||
_pConfig->addChangeListener(new SGLightConfigListener(light), true);
|
||||
|
||||
//-- debug visualisation --
|
||||
osg::Shape *debug_shape = nullptr;
|
||||
if (light->getType() == SGLight::Type::POINT) {
|
||||
debug_shape = new osg::Sphere(osg::Vec3(0, 0, 0), light->getRange());
|
||||
@@ -165,7 +140,9 @@ SGLight::appendLight(const SGPropertyNode *configNode,
|
||||
simgear::getPropertyRoot()->getNode("/sim/debug/show-light-volumes", true)->
|
||||
addChangeListener(new SGLightDebugListener(debug_switch), true);
|
||||
align->addChild(debug_switch);
|
||||
//-- end debug visualisation --
|
||||
|
||||
SGConstPropertyNode_ptr p;
|
||||
if ((p = configNode->getNode("name")) != NULL)
|
||||
align->setName(p->getStringValue());
|
||||
else
|
||||
@@ -174,21 +151,16 @@ SGLight::appendLight(const SGPropertyNode *configNode,
|
||||
return align;
|
||||
}
|
||||
|
||||
SGLight::SGLight() :
|
||||
_type(Type::POINT),
|
||||
_range(0.0f),
|
||||
_priority(Priority::LOW)
|
||||
SGLight::SGLight()
|
||||
{
|
||||
// Default values taken from osg::Light
|
||||
// They don't matter anyway as they are overwritten by the XML config values
|
||||
|
||||
//TODO: can this be moved to initalizer in hxx?
|
||||
_ambient.set(0.05f, 0.05f, 0.05f, 1.0f);
|
||||
_diffuse.set(0.8f, 0.8f, 0.8f, 1.0f);
|
||||
_specular.set(0.05f, 0.05f, 0.05f, 1.0f);
|
||||
_constant_attenuation = 1.0f;
|
||||
_linear_attenuation = 0.0f;
|
||||
_quadratic_attenuation = 0.0f;
|
||||
_spot_exponent = 0.0f;
|
||||
_spot_cutoff = 180.0f;
|
||||
|
||||
// Do not let OSG cull lights by default, we usually leave that job to
|
||||
// other algorithms, like clustered shading.
|
||||
setCullingActive(false);
|
||||
@@ -198,3 +170,73 @@ SGLight::~SGLight()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SGLight::configure(const SGPropertyNode *configNode)
|
||||
{
|
||||
SGConstPropertyNode_ptr p;
|
||||
if((p = configNode->getNode("type")) != NULL) {
|
||||
std::string type = p->getStringValue();
|
||||
if (type == "point")
|
||||
setType(SGLight::Type::POINT);
|
||||
else if (type == "spot")
|
||||
setType(SGLight::Type::SPOT);
|
||||
else
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "ignoring unknown light type '" << type << "'");
|
||||
}
|
||||
|
||||
std::string priority = configNode->getStringValue("priority", "low");
|
||||
if (priority == "high")
|
||||
setPriority(SGLight::HIGH);
|
||||
else if (priority == "medium")
|
||||
setPriority(SGLight::MEDIUM);
|
||||
|
||||
setRange(configNode->getFloatValue("range-m"));
|
||||
|
||||
#define SGLIGHT_GET_COLOR_VALUE(n) \
|
||||
osg::Vec4(configNode->getFloatValue(n "/r"), \
|
||||
configNode->getFloatValue(n "/g"), \
|
||||
configNode->getFloatValue(n "/b"), \
|
||||
configNode->getFloatValue(n "/a"))
|
||||
setAmbient(SGLIGHT_GET_COLOR_VALUE("ambient"));
|
||||
setDiffuse(SGLIGHT_GET_COLOR_VALUE("diffuse"));
|
||||
setSpecular(SGLIGHT_GET_COLOR_VALUE("specular"));
|
||||
#undef SGLIGHT_GET_COLOR_VALUE
|
||||
|
||||
setConstantAttenuation(configNode->getFloatValue("attenuation/c"));
|
||||
setLinearAttenuation(configNode->getFloatValue("attenuation/l"));
|
||||
setQuadraticAttenuation(configNode->getFloatValue("attenuation/q"));
|
||||
|
||||
setSpotExponent(configNode->getFloatValue("spot-exponent"));
|
||||
setSpotCutoff(configNode->getFloatValue("spot-cutoff"));
|
||||
|
||||
osg::Matrix t;
|
||||
osg::Vec3 pos(configNode->getFloatValue("position/x-m"),
|
||||
configNode->getFloatValue("position/y-m"),
|
||||
configNode->getFloatValue("position/z-m"));
|
||||
t.makeTranslate(pos);
|
||||
|
||||
osg::Matrix r;
|
||||
if (const SGPropertyNode *dirNode = configNode->getNode("direction")) {
|
||||
if (dirNode->hasValue("pitch-deg")) {
|
||||
r.makeRotate(
|
||||
dirNode->getFloatValue("pitch-deg")*SG_DEGREES_TO_RADIANS,
|
||||
osg::Vec3(0, 1, 0),
|
||||
dirNode->getFloatValue("roll-deg")*SG_DEGREES_TO_RADIANS,
|
||||
osg::Vec3(1, 0, 0),
|
||||
dirNode->getFloatValue("heading-deg")*SG_DEGREES_TO_RADIANS,
|
||||
osg::Vec3(0, 0, 1));
|
||||
} else if (dirNode->hasValue("lookat-x-m")) {
|
||||
osg::Vec3 lookAt(dirNode->getFloatValue("lookat-x-m"),
|
||||
dirNode->getFloatValue("lookat-y-m"),
|
||||
dirNode->getFloatValue("lookat-z-m"));
|
||||
osg::Vec3 dir = lookAt - pos;
|
||||
r.makeRotate(osg::Vec3(0, 0, -1), dir);
|
||||
} else {
|
||||
r.makeRotate(osg::Vec3(0, 0, -1),
|
||||
osg::Vec3(dirNode->getFloatValue("x"),
|
||||
dirNode->getFloatValue("y"),
|
||||
dirNode->getFloatValue("z")));
|
||||
}
|
||||
}
|
||||
if (_transform) _transform->setMatrix(r * t);
|
||||
}
|
||||
@@ -38,12 +38,20 @@ public:
|
||||
|
||||
class UpdateCallback : public osg::NodeCallback {
|
||||
public:
|
||||
UpdateCallback(const SGExpressiond *expression,
|
||||
osg::Vec4 ambient, osg::Vec4 diffuse, osg::Vec4 specular) :
|
||||
_expression(expression),
|
||||
_ambient(ambient), _diffuse(diffuse), _specular(specular) {}
|
||||
UpdateCallback(const SGExpressiond *expression) : _expression(expression) {}
|
||||
UpdateCallback() {}
|
||||
|
||||
void configure(const osg::Vec4 ambient, const osg::Vec4 diffuse, const osg::Vec4 specular) {
|
||||
_ambient = ambient;
|
||||
_diffuse = diffuse;
|
||||
_specular = specular;
|
||||
}
|
||||
|
||||
virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) {
|
||||
double value = _expression->getValue();
|
||||
double value = 1;
|
||||
if (_expression) {
|
||||
value = _expression->getValue();
|
||||
}
|
||||
if (value != _prev_value) {
|
||||
SGLight *light = dynamic_cast<SGLight *>(node);
|
||||
if (light) {
|
||||
@@ -51,30 +59,18 @@ public:
|
||||
light->setDiffuse(_diffuse * value);
|
||||
light->setSpecular(_specular * value);
|
||||
}
|
||||
_prev_value = value;
|
||||
}
|
||||
}
|
||||
private:
|
||||
SGSharedPtr<SGExpressiond const> _expression;
|
||||
SGSharedPtr<SGExpressiond const> _expression {nullptr};
|
||||
osg::Vec4 _ambient, _diffuse, _specular;
|
||||
double _prev_value = -1.0;
|
||||
};
|
||||
|
||||
SGLight();
|
||||
|
||||
SGLight(const SGLight& l,
|
||||
const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) :
|
||||
osg::Node(l, copyop),
|
||||
_type(l._type),
|
||||
_range(l._range),
|
||||
_ambient(l._ambient),
|
||||
_diffuse(l._diffuse),
|
||||
_specular(l._specular),
|
||||
_constant_attenuation(l._constant_attenuation),
|
||||
_linear_attenuation(l._linear_attenuation),
|
||||
_quadratic_attenuation(l._quadratic_attenuation),
|
||||
_spot_exponent(l._spot_exponent),
|
||||
_spot_cutoff(l._spot_cutoff)
|
||||
{}
|
||||
SGLight(const SGLight& l, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
|
||||
SGLight(osg::MatrixTransform *transform) : SGLight() { _transform = transform; };
|
||||
|
||||
META_Node(simgear, SGLight);
|
||||
|
||||
@@ -82,6 +78,8 @@ public:
|
||||
SGPropertyNode *modelRoot,
|
||||
const osgDB::Options *options);
|
||||
|
||||
void configure(const SGPropertyNode *config_root);
|
||||
|
||||
void setType(Type type) { _type = type; }
|
||||
Type getType() const { return _type; }
|
||||
|
||||
@@ -118,21 +116,21 @@ public:
|
||||
protected:
|
||||
virtual ~SGLight();
|
||||
|
||||
Type _type;
|
||||
Type _type {Type::POINT};
|
||||
|
||||
float _range;
|
||||
float _range {0.0f};
|
||||
|
||||
osg::Vec4 _ambient;
|
||||
osg::Vec4 _diffuse;
|
||||
osg::Vec4 _specular;
|
||||
|
||||
float _constant_attenuation;
|
||||
float _linear_attenuation;
|
||||
float _quadratic_attenuation;
|
||||
float _spot_exponent;
|
||||
float _spot_cutoff;
|
||||
|
||||
Priority _priority;
|
||||
float _constant_attenuation {1.0f};
|
||||
float _linear_attenuation {0.0f};
|
||||
float _quadratic_attenuation {0.0f};
|
||||
float _spot_exponent {0.0f};
|
||||
float _spot_cutoff {180.0f};
|
||||
Priority _priority {Priority::LOW};
|
||||
osg::MatrixTransform *_transform {nullptr};
|
||||
};
|
||||
|
||||
typedef std::vector<osg::ref_ptr<SGLight>> SGLightList;
|
||||
|
||||
@@ -56,13 +56,8 @@ SGLightAnimation::SGLightAnimation(simgear::SGTransientModelData &modelData) :
|
||||
if (dim_factor) {
|
||||
const SGExpressiond *expression =
|
||||
read_value(dim_factor, modelData.getModelRoot(), "", 0, 1);
|
||||
if (expression) {
|
||||
light->setUpdateCallback(
|
||||
new SGLight::UpdateCallback(expression,
|
||||
light->getAmbient(),
|
||||
light->getDiffuse(),
|
||||
light->getSpecular()));
|
||||
}
|
||||
if (expression)
|
||||
light->setUpdateCallback(new SGLight::UpdateCallback(expression));
|
||||
}
|
||||
|
||||
align->setName(getConfig()->getStringValue("name"));
|
||||
|
||||
Reference in New Issue
Block a user