00001
00002
00003
00004
00005
00006 #ifdef HAVE_CONFIG_H
00007 # include <simgear_config.h>
00008 #endif
00009
00010 #include "SGMaterialAnimation.hxx"
00011
00012 #include <osg/AlphaFunc>
00013 #include <osg/Array>
00014 #include <osg/Drawable>
00015 #include <osg/Geode>
00016 #include <osg/Geometry>
00017 #include <osg/Material>
00018 #include <osg/StateSet>
00019 #include <osgDB/FileNameUtils>
00020 #include <osgDB/FileUtils>
00021 #include <osgDB/ReadFile>
00022
00023 #include <simgear/props/condition.hxx>
00024 #include <simgear/props/props.hxx>
00025 #include <simgear/scene/material/Effect.hxx>
00026 #include <simgear/scene/material/EffectGeode.hxx>
00027 #include <simgear/scene/material/Pass.hxx>
00028 #include <simgear/scene/material/Technique.hxx>
00029 #include <simgear/scene/model/model.hxx>
00030
00031 using namespace std;
00032 using namespace simgear;
00033
00034 namespace {
00038 struct ColorSpec {
00039 float red, green, blue;
00040 float factor;
00041 float offset;
00042 SGPropertyNode_ptr red_prop;
00043 SGPropertyNode_ptr green_prop;
00044 SGPropertyNode_ptr blue_prop;
00045 SGPropertyNode_ptr factor_prop;
00046 SGPropertyNode_ptr offset_prop;
00047 SGVec4f v;
00048
00049 ColorSpec(const SGPropertyNode* configNode, SGPropertyNode* modelRoot)
00050 {
00051 red = -1.0;
00052 green = -1.0;
00053 blue = -1.0;
00054 if (!configNode)
00055 return;
00056
00057 red = configNode->getFloatValue("red", -1.0);
00058 green = configNode->getFloatValue("green", -1.0);
00059 blue = configNode->getFloatValue("blue", -1.0);
00060 factor = configNode->getFloatValue("factor", 1.0);
00061 offset = configNode->getFloatValue("offset", 0.0);
00062
00063 if (!modelRoot)
00064 return;
00065 const SGPropertyNode *node;
00066 node = configNode->getChild("red-prop");
00067 if (node)
00068 red_prop = modelRoot->getNode(node->getStringValue(), true);
00069 node = configNode->getChild("green-prop");
00070 if (node)
00071 green_prop = modelRoot->getNode(node->getStringValue(), true);
00072 node = configNode->getChild("blue-prop");
00073 if (node)
00074 blue_prop = modelRoot->getNode(node->getStringValue(), true);
00075 node = configNode->getChild("factor-prop");
00076 if (node)
00077 factor_prop = modelRoot->getNode(node->getStringValue(), true);
00078 node = configNode->getChild("offset-prop");
00079 if (node)
00080 offset_prop = modelRoot->getNode(node->getStringValue(), true);
00081 }
00082
00083 bool dirty() {
00084 return red >= 0 || green >= 0 || blue >= 0;
00085 }
00086 bool live() {
00087 return red_prop || green_prop || blue_prop
00088 || factor_prop || offset_prop;
00089 }
00090 SGVec4f &rgba() {
00091 if (red_prop)
00092 red = red_prop->getFloatValue();
00093 if (green_prop)
00094 green = green_prop->getFloatValue();
00095 if (blue_prop)
00096 blue = blue_prop->getFloatValue();
00097 if (factor_prop)
00098 factor = factor_prop->getFloatValue();
00099 if (offset_prop)
00100 offset = offset_prop->getFloatValue();
00101 v[0] = SGMiscf::clip(red*factor + offset, 0, 1);
00102 v[1] = SGMiscf::clip(green*factor + offset, 0, 1);
00103 v[2] = SGMiscf::clip(blue*factor + offset, 0, 1);
00104 v[3] = 1;
00105 return v;
00106 }
00107
00108 osg::Vec4 rgbaVec4()
00109 {
00110 return toOsg(rgba());
00111 }
00112
00113 SGVec4f &initialRgba() {
00114 v[0] = SGMiscf::clip(red*factor + offset, 0, 1);
00115 v[1] = SGMiscf::clip(green*factor + offset, 0, 1);
00116 v[2] = SGMiscf::clip(blue*factor + offset, 0, 1);
00117 v[3] = 1;
00118 return v;
00119 }
00120 };
00121
00125 struct PropSpec {
00126 float value;
00127 float factor;
00128 float offset;
00129 float min;
00130 float max;
00131 SGPropertyNode_ptr value_prop;
00132 SGPropertyNode_ptr factor_prop;
00133 SGPropertyNode_ptr offset_prop;
00134
00135 PropSpec(const char* valueName, const char* valuePropName,
00136 const SGPropertyNode* configNode, SGPropertyNode* modelRoot)
00137 {
00138 value = -1;
00139 if (!configNode)
00140 return;
00141
00142 value = configNode->getFloatValue(valueName, -1);
00143 factor = configNode->getFloatValue("factor", 1);
00144 offset = configNode->getFloatValue("offset", 0);
00145 min = configNode->getFloatValue("min", 0);
00146 max = configNode->getFloatValue("max", 1);
00147
00148 if (!modelRoot)
00149 return;
00150 const SGPropertyNode *node;
00151 node = configNode->getChild(valuePropName);
00152 if (node)
00153 value_prop = modelRoot->getNode(node->getStringValue(), true);
00154 node = configNode->getChild("factor-prop");
00155 if (node)
00156 factor_prop = modelRoot->getNode(node->getStringValue(), true);
00157 node = configNode->getChild("offset-prop");
00158 if (node)
00159 offset_prop = modelRoot->getNode(node->getStringValue(), true);
00160 }
00161 bool dirty() { return value >= 0.0; }
00162 bool live() { return value_prop || factor_prop || offset_prop; }
00163 float getValue()
00164 {
00165 if (value_prop)
00166 value = value_prop->getFloatValue();
00167 if (offset_prop)
00168 offset = offset_prop->getFloatValue();
00169 if (factor_prop)
00170 factor = factor_prop->getFloatValue();
00171 return SGMiscf::clip(value*factor + offset, min, max);
00172 }
00173 float getInitialValue()
00174 {
00175 return SGMiscf::clip(value*factor + offset, min, max);
00176 }
00177 };
00178
00182 enum SuppliedColor {
00183 DIFFUSE = 1,
00184 AMBIENT = 2,
00185 SPECULAR = 4,
00186 EMISSION = 8,
00187 SHININESS = 16,
00188 TRANSPARENCY = 32
00189 };
00190
00191 const unsigned AMBIENT_DIFFUSE = AMBIENT | DIFFUSE;
00192
00193 const int allMaterialColors = (DIFFUSE | AMBIENT | SPECULAR | EMISSION
00194 | SHININESS);
00195
00196
00197
00198
00199
00200
00201
00202 class MaterialDefaultsVisitor : public osg::NodeVisitor {
00203 public:
00204 MaterialDefaultsVisitor()
00205 : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
00206 ambientDiffuse(-1.0f, -1.0f, -1.0f, -1.0f)
00207 {
00208 setVisitorType(osg::NodeVisitor::NODE_VISITOR);
00209 }
00210
00211 virtual void apply(osg::Node& node)
00212 {
00213 maybeGetMaterialValues(node.getStateSet());
00214 traverse(node);
00215 }
00216
00217 virtual void apply(osg::Geode& node)
00218 {
00219 using namespace simgear;
00220 EffectGeode* eg = dynamic_cast<EffectGeode*>(&node);
00221 if (eg) {
00222 const Effect* effect = eg->getEffect();
00223 if (effect)
00224 for (vector<osg::ref_ptr<Technique> >::const_iterator itr
00225 = effect->techniques.begin(), end = effect->techniques.end();
00226 itr != end;
00227 ++itr) {
00228 const Technique* tniq = itr->get();
00229 for (vector<osg::ref_ptr<Pass> >::const_iterator pitr
00230 = tniq->passes.begin(), pend = tniq->passes.end();
00231 pitr != pend;
00232 ++pitr)
00233 maybeGetMaterialValues(pitr->get());
00234 }
00235 } else {
00236 maybeGetMaterialValues(node.getStateSet());
00237 }
00238 int numDrawables = node.getNumDrawables();
00239 for (int i = 0; i < numDrawables; i++) {
00240 osg::Geometry* geom = dynamic_cast<osg::Geometry*>(node.getDrawable(i));
00241 if (!geom || geom->getColorBinding() != osg::Geometry::BIND_OVERALL)
00242 continue;
00243 maybeGetMaterialValues(geom->getStateSet());
00244 osg::Array* colorArray = geom->getColorArray();
00245 osg::Vec4Array* colorVec4 = dynamic_cast<osg::Vec4Array*>(colorArray);
00246 if (colorVec4) {
00247 ambientDiffuse = (*colorVec4)[0];
00248 break;
00249 }
00250 osg::Vec3Array* colorVec3 = dynamic_cast<osg::Vec3Array*>(colorArray);
00251 if (colorVec3) {
00252 ambientDiffuse = osg::Vec4((*colorVec3)[0], 1.0f);
00253 break;
00254 }
00255 }
00256 }
00257
00258 void maybeGetMaterialValues(const osg::StateSet* stateSet)
00259 {
00260 if (!stateSet)
00261 return;
00262 const osg::Material* nodeMat
00263 = dynamic_cast<const osg::Material*>(stateSet
00264 ->getAttribute(osg::StateAttribute
00265 ::MATERIAL));
00266 if (!nodeMat)
00267 return;
00268 material = nodeMat;
00269 }
00270
00271 osg::ref_ptr<const osg::Material> material;
00272 osg::Vec4 ambientDiffuse;
00273 };
00274
00275 class MaterialPropertyAdapter
00276 {
00277 public:
00278 MaterialPropertyAdapter(const SGPropertyNode* configNode,
00279 SGPropertyNode* modelRoot) :
00280 _ambient(configNode->getChild("ambient"), modelRoot),
00281 _diffuse(configNode->getChild("diffuse"), modelRoot),
00282 _specular(configNode->getChild("specular"), modelRoot),
00283 _emission(configNode->getChild("emission"), modelRoot),
00284 _shininess("shininess", "shininess-prop",
00285 configNode, modelRoot),
00286 _transparency("alpha", "alpha-prop",
00287 configNode->getChild("transparency"), modelRoot)
00288 {
00289 _shininess.max = 128;
00290 _isAnimated = (_ambient.live() || _diffuse.live() || _specular.live()
00291 || _emission.live() || _shininess.live()
00292 || _transparency.live());
00293 }
00294 bool isAnimated() { return _isAnimated; }
00295
00296
00297 void setMaterialValues(osg::StateSet* stateSet)
00298 {
00299 osg::StateAttribute* stateAttribute
00300 = stateSet->getAttribute(osg::StateAttribute::MATERIAL);
00301 osg::Material* material = dynamic_cast<osg::Material*>(stateAttribute);
00302 if (material) {
00303 if (_ambient.live() || _ambient.dirty())
00304 material->setAmbient(osg::Material::FRONT_AND_BACK,
00305 _ambient.rgbaVec4());
00306 if (_diffuse.live() || _diffuse.dirty())
00307 material->setDiffuse(osg::Material::FRONT_AND_BACK,
00308 _diffuse.rgbaVec4());
00309 if (_specular.live() || _specular.dirty())
00310 material->setSpecular(osg::Material::FRONT_AND_BACK,
00311 _specular.rgbaVec4());
00312 if (_emission.live() || _emission.dirty())
00313 material->setEmission(osg::Material::FRONT_AND_BACK,
00314 _emission.rgbaVec4());
00315 if (_shininess.live() || _shininess.dirty())
00316 material->setShininess(osg::Material::FRONT_AND_BACK,
00317 _shininess.getValue());
00318 if (_transparency.live() || _transparency.dirty()) {
00319 float alpha = _transparency.getValue();
00320 material->setAlpha(osg::Material::FRONT_AND_BACK, alpha);
00321 if (alpha < 1.0f) {
00322 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
00323 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
00324 } else {
00325 stateSet->setRenderingHint(osg::StateSet::DEFAULT_BIN);
00326 }
00327 }
00328 }
00329 }
00330 ColorSpec _ambient;
00331 ColorSpec _diffuse;
00332 ColorSpec _specular;
00333 ColorSpec _emission;
00334 PropSpec _shininess;
00335 PropSpec _transparency;
00336 bool _isAnimated;
00337
00338 };
00339
00340 class UpdateCallback : public osg::NodeCallback {
00341 public:
00342 UpdateCallback(const osgDB::FilePathList& texturePathList,
00343 const SGCondition* condition,
00344 const SGPropertyNode* configNode, SGPropertyNode* modelRoot) :
00345 _condition(condition),
00346 _materialProps(configNode, modelRoot),
00347 _texturePathList(texturePathList),
00348 _prevState(false)
00349 {
00350 const SGPropertyNode* node;
00351
00352 node = configNode->getChild("threshold-prop");
00353 if (node)
00354 _thresholdProp = modelRoot->getNode(node->getStringValue(), true);
00355 node = configNode->getChild("texture-prop");
00356 if (node)
00357 _textureProp = modelRoot->getNode(node->getStringValue(), true);
00358 }
00359
00360 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
00361 {
00362 osg::StateSet* stateSet = node->getStateSet();
00363 if ((!_condition || _condition->test()) && stateSet) {
00364 if (_textureProp) {
00365 std::string textureName = _textureProp->getStringValue();
00366 if (_textureName != textureName) {
00367 while (stateSet->getTextureAttribute(0,
00368 osg::StateAttribute::TEXTURE)) {
00369 stateSet->removeTextureAttribute(0, osg::StateAttribute::TEXTURE);
00370 }
00371 std::string textureFile;
00372 textureFile = osgDB::findFileInPath(textureName, _texturePathList);
00373 if (!textureFile.empty()) {
00374 osg::Texture2D* texture2D = SGLoadTexture2D(textureFile);
00375 if (texture2D) {
00376 stateSet->setTextureAttribute(0, texture2D,
00377 osg::StateAttribute::OVERRIDE);
00378 stateSet->setTextureMode(0, GL_TEXTURE_2D,
00379 osg::StateAttribute::ON);
00380 _textureName = textureName;
00381 }
00382 }
00383 }
00384 }
00385 if (_thresholdProp) {
00386 osg::StateSet* stateSet = node->getOrCreateStateSet();
00387 osg::StateAttribute* stateAttribute;
00388 stateAttribute = stateSet->getAttribute(osg::StateAttribute::ALPHAFUNC);
00389 osg::AlphaFunc* alphaFunc
00390 = dynamic_cast<osg::AlphaFunc*>(stateAttribute);
00391 assert(alphaFunc);
00392 alphaFunc->setReferenceValue(_thresholdProp->getFloatValue());
00393 }
00394 if (_materialProps.isAnimated() || !_prevState)
00395 _materialProps.setMaterialValues(stateSet);
00396 _prevState = true;
00397 } else {
00398 _prevState = false;
00399 }
00400 traverse(node, nv);
00401 }
00402 private:
00403 SGSharedPtr<const SGCondition> _condition;
00404 SGSharedPtr<const SGPropertyNode> _textureProp;
00405 SGSharedPtr<const SGPropertyNode> _thresholdProp;
00406 std::string _textureName;
00407 MaterialPropertyAdapter _materialProps;
00408 osgDB::FilePathList _texturePathList;
00409 bool _prevState;
00410 };
00411 }
00412
00413
00414 SGMaterialAnimation::SGMaterialAnimation(const SGPropertyNode* configNode,
00415 SGPropertyNode* modelRoot,
00416 const osgDB::ReaderWriter::Options*
00417 options) :
00418 SGAnimation(configNode, modelRoot),
00419 texturePathList(options->getDatabasePathList())
00420 {
00421 if (configNode->hasChild("global"))
00422 SG_LOG(SG_IO, SG_ALERT, "Use of <global> in material animation is "
00423 "no longer supported");
00424 }
00425
00426 osg::Group*
00427 SGMaterialAnimation::createAnimationGroup(osg::Group& parent)
00428 {
00429 osg::Group* group = new osg::Group;
00430 group->setName("material animation group");
00431
00432 SGPropertyNode* inputRoot = getModelRoot();
00433 const SGPropertyNode* node = getConfig()->getChild("property-base");
00434 if (node)
00435 inputRoot = getModelRoot()->getNode(node->getStringValue(), true);
00436 osg::StateSet* stateSet = group->getOrCreateStateSet();
00437 if (getConfig()->hasChild("texture")) {
00438 std::string textureName = getConfig()->getStringValue("texture");
00439 std::string textureFile;
00440 textureFile = osgDB::findFileInPath(textureName, texturePathList);
00441 if (!textureFile.empty()) {
00442 osg::Texture2D* texture2D = SGLoadTexture2D(textureFile);
00443 if (texture2D) {
00444 stateSet->setTextureAttribute(0, texture2D,
00445 osg::StateAttribute::OVERRIDE);
00446 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
00447 if (texture2D->getImage()->isImageTranslucent()) {
00448 stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
00449 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
00450 }
00451 }
00452 }
00453 }
00454 if (getConfig()->hasChild("threshold-prop") ||
00455 getConfig()->hasChild("threshold")) {
00456 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
00457 alphaFunc->setFunction(osg::AlphaFunc::GREATER);
00458 float threshold = getConfig()->getFloatValue("threshold", 0);
00459 alphaFunc->setReferenceValue(threshold);
00460 stateSet->setAttribute(alphaFunc, osg::StateAttribute::OVERRIDE);
00461 }
00462
00463 unsigned suppliedColors = 0;
00464 if (getConfig()->hasChild("ambient"))
00465 suppliedColors |= AMBIENT;
00466 if (getConfig()->hasChild("diffuse"))
00467 suppliedColors |= DIFFUSE;
00468 if (getConfig()->hasChild("specular"))
00469 suppliedColors |= SPECULAR;
00470 if (getConfig()->hasChild("emission"))
00471 suppliedColors |= EMISSION;
00472 if (getConfig()->hasChild("shininess")
00473 || getConfig()->hasChild("shininess-prop"))
00474 suppliedColors |= SHININESS;
00475 if (getConfig()->hasChild("transparency"))
00476 suppliedColors |= TRANSPARENCY;
00477 osg::Material* mat = 0;
00478 if (suppliedColors != 0) {
00479 if (defaultMaterial.valid()) {
00480 mat = defaultMaterial.get();
00481
00482 } else {
00483 mat = new osg::Material;
00484 mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
00485 }
00486 mat->setDataVariance(osg::Object::DYNAMIC);
00487 unsigned defaultColorModeMask = 0;
00488 mat->setUpdateCallback(0);
00489
00490
00491 switch (mat->getColorMode()) {
00492 case osg::Material::OFF:
00493 defaultColorModeMask = 0;
00494 break;
00495 case osg::Material::AMBIENT:
00496 defaultColorModeMask = AMBIENT;
00497 break;
00498 case osg::Material::DIFFUSE:
00499 defaultColorModeMask = DIFFUSE;
00500 break;
00501 case osg::Material::AMBIENT_AND_DIFFUSE:
00502 defaultColorModeMask = AMBIENT | DIFFUSE;
00503 break;
00504 case osg::Material::SPECULAR:
00505 defaultColorModeMask = SPECULAR;
00506 break;
00507 case osg::Material::EMISSION:
00508 defaultColorModeMask = EMISSION;
00509 break;
00510 }
00511
00512
00513
00514 if (defaultAmbientDiffuse.x() >= 0) {
00515 if (defaultColorModeMask & AMBIENT)
00516 mat->setAmbient(osg::Material::FRONT_AND_BACK, defaultAmbientDiffuse);
00517 if (defaultColorModeMask & DIFFUSE)
00518 mat->setDiffuse(osg::Material::FRONT_AND_BACK, defaultAmbientDiffuse);
00519 }
00520
00521
00522
00523 if (suppliedColors & TRANSPARENCY) {
00524
00525
00526 mat->setColorMode(osg::Material::OFF);
00527 } else if ((suppliedColors & defaultColorModeMask) != 0) {
00528
00529 if ((defaultColorModeMask & AMBIENT_DIFFUSE) != 0) {
00530
00531 unsigned matColorModeMask = ((~suppliedColors & defaultColorModeMask)
00532 & AMBIENT_DIFFUSE);
00533 if ((matColorModeMask & DIFFUSE) != 0)
00534 mat->setColorMode(osg::Material::DIFFUSE);
00535 else if ((matColorModeMask & AMBIENT) != 0)
00536 mat->setColorMode(osg::Material::AMBIENT);
00537 else
00538 mat->setColorMode(osg::Material::OFF);
00539 } else {
00540
00541 mat->setColorMode(osg::Material::OFF);
00542 }
00543 } else {
00544
00545
00546 }
00547 stateSet->setAttribute(mat,(osg::StateAttribute::ON
00548 | osg::StateAttribute::OVERRIDE));
00549 }
00550 bool matAnimated = false;
00551 if (mat) {
00552 MaterialPropertyAdapter adapter(getConfig(), inputRoot);
00553 adapter.setMaterialValues(stateSet);
00554 matAnimated = adapter.isAnimated();
00555 }
00556 if (matAnimated || getConfig()->hasChild("texture-prop")
00557 || getConfig()->hasChild("threshold-prop") || getCondition()) {
00558 stateSet->setDataVariance(osg::Object::DYNAMIC);
00559 group->setUpdateCallback(new UpdateCallback(texturePathList,
00560 getCondition(),
00561 getConfig(), inputRoot));
00562 } else {
00563 stateSet->setDataVariance(osg::Object::STATIC);
00564 }
00565 parent.addChild(group);
00566 return group;
00567 }
00568
00569 void
00570 SGMaterialAnimation::install(osg::Node& node)
00571 {
00572 SGAnimation::install(node);
00573
00574 MaterialDefaultsVisitor defaultsVisitor;
00575 node.accept(defaultsVisitor);
00576 if (defaultsVisitor.material.valid()) {
00577 defaultMaterial
00578 = static_cast<osg::Material*>(defaultsVisitor.material->clone(osg::CopyOp::SHALLOW_COPY));
00579 }
00580 defaultAmbientDiffuse = defaultsVisitor.ambientDiffuse;
00581 }
00582
00583 const char* colorNames[] =
00584 {
00585 "ambient",
00586 "diffuse",
00587 "specular",
00588 "emission"
00589 };
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600 SGPropertyNode_ptr
00601 SGMaterialAnimation::makeEffectProperties(const SGPropertyNode* animProp)
00602 {
00603 SGPropertyNode_ptr eRoot = new SGPropertyNode;
00604 SGPropertyNode* inherit = makeNode(eRoot, "inherits-from");
00605 if (animProp->hasChild("diffuse") || animProp->hasChild("transparency"))
00606 inherit->setStringValue("Effects/material-off");
00607 else
00608 inherit->setStringValue("Effects/material-diffuse");
00609 return eRoot;
00610 }