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())
{
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());

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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"));