Make SGLight configurable at runtime.

This commit is contained in:
Henning Stahlke
2021-01-30 11:45:18 +01:00
parent 9561895335
commit fd0e7ac2d8
5 changed files with 163 additions and 128 deletions

View File

@@ -2800,12 +2800,12 @@ namespace simgear
} }
void SGPropertyNode::copy(SGPropertyNode *to) void SGPropertyNode::copy(SGPropertyNode *to) const
{ {
if (nChildren()) if (nChildren())
{ {
for (int i = 0; i < nChildren(); i++) { for (int i = 0; i < nChildren(); i++) {
SGPropertyNode *child = getChild(i); const SGPropertyNode *child = getChild(i);
SGPropertyNode *to_child = to->getChild(child->getName()); SGPropertyNode *to_child = to->getChild(child->getName());
if (!to_child) if (!to_child)
to_child = to->addChild(child->getName()); to_child = to->addChild(child->getName());

View File

@@ -1098,7 +1098,7 @@ public:
/** /**
* deep copy one node to another. * deep copy one node to another.
*/ */
void copy(SGPropertyNode *to); void copy(SGPropertyNode *to) const;
/** /**
* Get a pointer to another node by relative path. * Get a pointer to another node by relative path.

View File

@@ -39,99 +39,74 @@ private:
osg::ref_ptr<osg::Switch> _sw; 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 * osg::Node *
SGLight::appendLight(const SGPropertyNode *configNode, SGLight::appendLight(const SGPropertyNode *configNode,
SGPropertyNode *modelRoot, SGPropertyNode *modelRoot,
const osgDB::Options *options) const osgDB::Options *options)
{ {
SGConstPropertyNode_ptr p; //-- create return variable
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"));
osg::MatrixTransform *align = new osg::MatrixTransform; osg::MatrixTransform *align = new osg::MatrixTransform;
SGLight *light = new SGLight(align);
align->addChild(light); align->addChild(light);
osg::Matrix t; //-- copy config to prop tree --
osg::Vec3 pos(configNode->getFloatValue("position/x-m"), const std::string propPath {"/scenery/lights"};
configNode->getFloatValue("position/y-m"), const std::string propName {"light"};
configNode->getFloatValue("position/z-m")); SGPropertyNode *_pConfig = simgear::getPropertyRoot()->getNode(propPath, true)
t.makeTranslate(pos); ->addChild(propName);
configNode->copy(_pConfig);
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);
//-- setup osg::NodeCallback --
const SGPropertyNode *dim_factor = configNode->getChild("dim-factor"); const SGPropertyNode *dim_factor = configNode->getChild("dim-factor");
if (dim_factor) { if (dim_factor) {
const SGExpressiond *expression = const SGExpressiond *expression =
read_value(dim_factor, modelRoot, "", 0, 1); read_value(dim_factor, modelRoot, "", 0, 1);
if (expression) { if (expression)
light->setUpdateCallback( light->setUpdateCallback(new SGLight::UpdateCallback(expression));
new SGLight::UpdateCallback(expression, }
light->getAmbient(), else {
light->getDiffuse(), //need UpdateCallback to activate config changes at runtime
light->getSpecular())); 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; osg::Shape *debug_shape = nullptr;
if (light->getType() == SGLight::Type::POINT) { if (light->getType() == SGLight::Type::POINT) {
debug_shape = new osg::Sphere(osg::Vec3(0, 0, 0), light->getRange()); 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)-> simgear::getPropertyRoot()->getNode("/sim/debug/show-light-volumes", true)->
addChangeListener(new SGLightDebugListener(debug_switch), true); addChangeListener(new SGLightDebugListener(debug_switch), true);
align->addChild(debug_switch); align->addChild(debug_switch);
//-- end debug visualisation --
SGConstPropertyNode_ptr p;
if ((p = configNode->getNode("name")) != NULL) if ((p = configNode->getNode("name")) != NULL)
align->setName(p->getStringValue()); align->setName(p->getStringValue());
else else
@@ -174,21 +151,16 @@ SGLight::appendLight(const SGPropertyNode *configNode,
return align; return align;
} }
SGLight::SGLight() : SGLight::SGLight()
_type(Type::POINT),
_range(0.0f),
_priority(Priority::LOW)
{ {
// Default values taken from osg::Light // Default values taken from osg::Light
// They don't matter anyway as they are overwritten by the XML config values // 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); _ambient.set(0.05f, 0.05f, 0.05f, 1.0f);
_diffuse.set(0.8f, 0.8f, 0.8f, 1.0f); _diffuse.set(0.8f, 0.8f, 0.8f, 1.0f);
_specular.set(0.05f, 0.05f, 0.05f, 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 // Do not let OSG cull lights by default, we usually leave that job to
// other algorithms, like clustered shading. // other algorithms, like clustered shading.
setCullingActive(false); 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);
}

View File

@@ -38,12 +38,20 @@ public:
class UpdateCallback : public osg::NodeCallback { class UpdateCallback : public osg::NodeCallback {
public: public:
UpdateCallback(const SGExpressiond *expression, UpdateCallback(const SGExpressiond *expression) : _expression(expression) {}
osg::Vec4 ambient, osg::Vec4 diffuse, osg::Vec4 specular) : UpdateCallback() {}
_expression(expression),
_ambient(ambient), _diffuse(diffuse), _specular(specular) {} 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) { 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) { if (value != _prev_value) {
SGLight *light = dynamic_cast<SGLight *>(node); SGLight *light = dynamic_cast<SGLight *>(node);
if (light) { if (light) {
@@ -51,30 +59,18 @@ public:
light->setDiffuse(_diffuse * value); light->setDiffuse(_diffuse * value);
light->setSpecular(_specular * value); light->setSpecular(_specular * value);
} }
_prev_value = value;
} }
} }
private: private:
SGSharedPtr<SGExpressiond const> _expression; SGSharedPtr<SGExpressiond const> _expression {nullptr};
osg::Vec4 _ambient, _diffuse, _specular; osg::Vec4 _ambient, _diffuse, _specular;
double _prev_value = -1.0; double _prev_value = -1.0;
}; };
SGLight(); SGLight();
SGLight(const SGLight& l, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
SGLight(const SGLight& l, SGLight(osg::MatrixTransform *transform) : SGLight() { _transform = transform; };
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)
{}
META_Node(simgear, SGLight); META_Node(simgear, SGLight);
@@ -82,6 +78,8 @@ public:
SGPropertyNode *modelRoot, SGPropertyNode *modelRoot,
const osgDB::Options *options); const osgDB::Options *options);
void configure(const SGPropertyNode *config_root);
void setType(Type type) { _type = type; } void setType(Type type) { _type = type; }
Type getType() const { return _type; } Type getType() const { return _type; }
@@ -118,21 +116,21 @@ public:
protected: protected:
virtual ~SGLight(); virtual ~SGLight();
Type _type; Type _type {Type::POINT};
float _range; float _range {0.0f};
osg::Vec4 _ambient; osg::Vec4 _ambient;
osg::Vec4 _diffuse; osg::Vec4 _diffuse;
osg::Vec4 _specular; osg::Vec4 _specular;
float _constant_attenuation; float _constant_attenuation {1.0f};
float _linear_attenuation; float _linear_attenuation {0.0f};
float _quadratic_attenuation; float _quadratic_attenuation {0.0f};
float _spot_exponent; float _spot_exponent {0.0f};
float _spot_cutoff; float _spot_cutoff {180.0f};
Priority _priority {Priority::LOW};
Priority _priority; osg::MatrixTransform *_transform {nullptr};
}; };
typedef std::vector<osg::ref_ptr<SGLight>> SGLightList; typedef std::vector<osg::ref_ptr<SGLight>> SGLightList;

View File

@@ -56,13 +56,8 @@ SGLightAnimation::SGLightAnimation(simgear::SGTransientModelData &modelData) :
if (dim_factor) { if (dim_factor) {
const SGExpressiond *expression = const SGExpressiond *expression =
read_value(dim_factor, modelData.getModelRoot(), "", 0, 1); read_value(dim_factor, modelData.getModelRoot(), "", 0, 1);
if (expression) { if (expression)
light->setUpdateCallback( light->setUpdateCallback(new SGLight::UpdateCallback(expression));
new SGLight::UpdateCallback(expression,
light->getAmbient(),
light->getDiffuse(),
light->getSpecular()));
}
} }
align->setName(getConfig()->getStringValue("name")); align->setName(getConfig()->getStringValue("name"));