From fd0e7ac2d8bb41fcf0f61317dfa64e98cd2a7e64 Mon Sep 17 00:00:00 2001 From: Henning Stahlke Date: Sat, 30 Jan 2021 11:45:18 +0100 Subject: [PATCH] Make SGLight configurable at runtime. --- simgear/props/props.cxx | 4 +- simgear/props/props.hxx | 2 +- simgear/scene/model/SGLight.cxx | 218 ++++++++++++++--------- simgear/scene/model/SGLight.hxx | 58 +++--- simgear/scene/model/SGLightAnimation.cxx | 9 +- 5 files changed, 163 insertions(+), 128 deletions(-) diff --git a/simgear/props/props.cxx b/simgear/props/props.cxx index fac7796f..72095259 100755 --- a/simgear/props/props.cxx +++ b/simgear/props/props.cxx @@ -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()); diff --git a/simgear/props/props.hxx b/simgear/props/props.hxx index f3a9db96..bba11fd4 100644 --- a/simgear/props/props.hxx +++ b/simgear/props/props.hxx @@ -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. diff --git a/simgear/scene/model/SGLight.cxx b/simgear/scene/model/SGLight.cxx index 93967c0f..44a9f5a7 100644 --- a/simgear/scene/model/SGLight.cxx +++ b/simgear/scene/model/SGLight.cxx @@ -39,99 +39,74 @@ private: osg::ref_ptr _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(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); +} \ No newline at end of file diff --git a/simgear/scene/model/SGLight.hxx b/simgear/scene/model/SGLight.hxx index 407fe57e..25cb0622 100644 --- a/simgear/scene/model/SGLight.hxx +++ b/simgear/scene/model/SGLight.hxx @@ -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(node); if (light) { @@ -51,30 +59,18 @@ public: light->setDiffuse(_diffuse * value); light->setSpecular(_specular * value); } + _prev_value = value; } } private: - SGSharedPtr _expression; + SGSharedPtr _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> SGLightList; diff --git a/simgear/scene/model/SGLightAnimation.cxx b/simgear/scene/model/SGLightAnimation.cxx index 961fa4b1..429e1bc0 100644 --- a/simgear/scene/model/SGLightAnimation.cxx +++ b/simgear/scene/model/SGLightAnimation.cxx @@ -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"));