#include <simgear_config.h>
#endif
-#include <string.h> // for strcmp()
+#include <utility>
-#include <vector>
-#include <set>
+#include <boost/foreach.hpp>
+
+#include <osg/ref_ptr>
+#include <osgDB/FileNameUtils>
+#include <osgDB/FileUtils>
+#include <osgDB/ReaderWriter>
+#include <osgDB/ReadFile>
+#include <osgDB/SharedStateManager>
+
+#include <simgear/math/SGMath.hxx>
+#include <simgear/scene/material/Effect.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
+#include <simgear/scene/util/SGSceneFeatures.hxx>
+#include <simgear/scene/util/SGSceneUserData.hxx>
+#include <simgear/scene/util/CopyOp.hxx>
+#include <simgear/scene/util/SplicingVisitor.hxx>
-#include <plib/sg.h>
-#include <plib/ssg.h>
-#include <plib/ul.h>
#include <simgear/structure/exception.hxx>
+#include <simgear/structure/Singleton.hxx>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
+#include <simgear/props/condition.hxx>
-#include "animation.hxx"
+#include "SGReaderWriterXMLOptions.hxx"
#include "model.hxx"
-SG_USING_STD(vector);
-SG_USING_STD(set);
+using std::vector;
-bool sgUseDisplayList = true;
-\f
-////////////////////////////////////////////////////////////////////////
-// Global state
-////////////////////////////////////////////////////////////////////////
-static bool
-model_filter = true;
+osg::Texture2D*
+SGLoadTexture2D(bool staticTexture, const std::string& path,
+ const osgDB::ReaderWriter::Options* options,
+ bool wrapu, bool wrapv, int)
+{
+ osg::Image* image;
+ if (options)
+ image = osgDB::readImageFile(path, options);
+ else
+ image = osgDB::readImageFile(path);
+ osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
+ texture->setImage(image);
+ if (staticTexture)
+ texture->setDataVariance(osg::Object::STATIC);
+ if (wrapu)
+ texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
+ else
+ texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
+ if (wrapv)
+ texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
+ else
+ texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
+
+ if (image) {
+ int s = image->s();
+ int t = image->t();
+
+ if (s <= t && 32 <= s) {
+ SGSceneFeatures::instance()->setTextureCompression(texture.get());
+ } else if (t < s && 32 <= t) {
+ SGSceneFeatures::instance()->setTextureCompression(texture.get());
+ }
+ }
-\f
-////////////////////////////////////////////////////////////////////////
-// Static utility functions.
-////////////////////////////////////////////////////////////////////////
+ return texture.release();
+}
-static int
-model_filter_callback (ssgEntity * entity, int mask)
+namespace simgear
{
- return model_filter ? 1 : 0;
-}
+using namespace std;
+using namespace osg;
+using simgear::CopyOp;
-/**
- * Callback to update an animation.
- */
-static int
-animation_callback (ssgEntity * entity, int mask)
+Node* copyModel(Node* model)
{
- return ((SGAnimation *)entity->getUserData())->update();
+ const CopyOp::CopyFlags flags = (CopyOp::DEEP_COPY_ALL
+ & ~CopyOp::DEEP_COPY_TEXTURES
+ & ~CopyOp::DEEP_COPY_IMAGES
+ & ~CopyOp::DEEP_COPY_STATESETS
+ & ~CopyOp::DEEP_COPY_STATEATTRIBUTES
+ & ~CopyOp::DEEP_COPY_ARRAYS
+ & ~CopyOp::DEEP_COPY_PRIMITIVES
+ // This will preserve display lists ...
+ & ~CopyOp::DEEP_COPY_DRAWABLES
+ & ~CopyOp::DEEP_COPY_SHAPES);
+ return (CopyOp(flags))(model);
}
-/**
- * Callback to restore the state after an animation.
- */
-static int
-restore_callback (ssgEntity * entity, int mask)
+TextureUpdateVisitor::TextureUpdateVisitor(const osgDB::FilePathList& pathList) :
+ NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
+ _pathList(pathList)
{
- ((SGAnimation *)entity->getUserData())->restore();
- return 1;
}
-
-/**
- * Locate a named SSG node in a branch.
- */
-static ssgEntity *
-find_named_node (ssgEntity * node, const char * name)
+void TextureUpdateVisitor::apply(Node& node)
{
- char * node_name = node->getName();
- if (node_name != 0 && !strcmp(name, node_name))
- return node;
- else if (node->isAKindOf(ssgTypeBranch())) {
- int nKids = node->getNumKids();
- for (int i = 0; i < nKids; i++) {
- ssgEntity * result =
- find_named_node(((ssgBranch*)node)->getKid(i), name);
- if (result != 0)
- return result;
- }
- }
- return 0;
+ StateSet* stateSet = cloneStateSet(node.getStateSet());
+ if (stateSet)
+ node.setStateSet(stateSet);
+ traverse(node);
}
-/**
- * Splice a branch in between all child nodes and their parents.
- */
-static void
-splice_branch (ssgBranch * branch, ssgEntity * child)
+void TextureUpdateVisitor::apply(Drawable& drawable)
{
- int nParents = child->getNumParents();
- branch->addKid(child);
- for (int i = 0; i < nParents; i++) {
- ssgBranch * parent = child->getParent(i);
- parent->replaceKid(child, branch);
- }
+ StateSet* stateSet = cloneStateSet(drawable.getStateSet());
+ if (stateSet)
+ drawable.setStateSet(stateSet);
}
-/**
- * Make an offset matrix from rotations and position offset.
- */
-void
-sgMakeOffsetsMatrix( sgMat4 * result, double h_rot, double p_rot, double r_rot,
- double x_off, double y_off, double z_off )
+Texture2D* TextureUpdateVisitor::textureReplace(int unit, const StateAttribute* attr)
{
- sgMat4 rot_matrix;
- sgMat4 pos_matrix;
- sgMakeRotMat4(rot_matrix, h_rot, p_rot, r_rot);
- sgMakeTransMat4(pos_matrix, x_off, y_off, z_off);
- sgMultMat4(*result, pos_matrix, rot_matrix);
-}
+ using namespace osgDB;
+ const Texture2D* texture = dynamic_cast<const Texture2D*>(attr);
+ if (!texture)
+ return 0;
-void
-sgMakeAnimation( ssgBranch * model,
- const char * name,
- vector<SGPropertyNode_ptr> &name_nodes,
- SGPropertyNode *prop_root,
- SGPropertyNode_ptr node,
- double sim_time_sec,
- SGPath &texture_path,
- set<ssgBranch *> &ignore_branches )
-{
- bool ignore = false;
- SGAnimation * animation = 0;
- const char * type = node->getStringValue("type", "none");
- if (!strcmp("none", type)) {
- animation = new SGNullAnimation(node);
- } else if (!strcmp("range", type)) {
- animation = new SGRangeAnimation(prop_root, node);
- } else if (!strcmp("billboard", type)) {
- animation = new SGBillboardAnimation(node);
- } else if (!strcmp("select", type)) {
- animation = new SGSelectAnimation(prop_root, node);
- } else if (!strcmp("spin", type)) {
- animation = new SGSpinAnimation(prop_root, node, sim_time_sec );
- } else if (!strcmp("timed", type)) {
- animation = new SGTimedAnimation(node);
- } else if (!strcmp("rotate", type)) {
- animation = new SGRotateAnimation(prop_root, node);
- } else if (!strcmp("translate", type)) {
- animation = new SGTranslateAnimation(prop_root, node);
- } else if (!strcmp("scale", type)) {
- animation = new SGScaleAnimation(prop_root, node);
- } else if (!strcmp("texrotate", type)) {
- animation = new SGTexRotateAnimation(prop_root, node);
- } else if (!strcmp("textranslate", type)) {
- animation = new SGTexTranslateAnimation(prop_root, node);
- } else if (!strcmp("texmultiple", type)) {
- animation = new SGTexMultipleAnimation(prop_root, node);
- } else if (!strcmp("blend", type)) {
- animation = new SGBlendAnimation(prop_root, node);
- ignore = true;
- } else if (!strcmp("alpha-test", type)) {
- animation = new SGAlphaTestAnimation(node);
- } else if (!strcmp("material", type)) {
- animation = new SGMaterialAnimation(prop_root, node, texture_path);
- } else if (!strcmp("flash", type)) {
- animation = new SGFlashAnimation(node);
- } else if (!strcmp("dist-scale", type)) {
- animation = new SGDistScaleAnimation(node);
- } else if (!strcmp("noshadow", type)) {
- animation = new SGShadowAnimation(prop_root, node);
- } else if (!strcmp("shader", type)) {
- animation = new SGShaderAnimation(prop_root, node);
- } else {
- animation = new SGNullAnimation(node);
- SG_LOG(SG_INPUT, SG_WARN, "Unknown animation type " << type);
- }
+ const Image* image = texture->getImage();
+ const string* fullFilePath = 0;
+ if (image) {
+ // The currently loaded file name
+ fullFilePath = &image->getFileName();
- if (name != 0)
- animation->setName((char *)name);
-
- ssgEntity * object;
- if (name_nodes.size() > 0) {
- object = find_named_node(model, name_nodes[0]->getStringValue());
- if (object == 0) {
- SG_LOG(SG_INPUT, SG_ALERT, "Object " << name_nodes[0]->getStringValue()
- << " not found");
- delete animation;
- animation = 0;
+ } else {
+ fullFilePath = &texture->getName();
}
- } else {
- object = model;
- }
-
- if ( animation == 0 )
- return;
-
- ssgBranch * branch = animation->getBranch();
- splice_branch(branch, object);
-
- for (unsigned int i = 1; i < name_nodes.size(); i++) {
- const char * name = name_nodes[i]->getStringValue();
- object = find_named_node(model, name);
- if (object == 0) {
- SG_LOG(SG_INPUT, SG_ALERT, "Object " << name << " not found");
- delete animation;
- animation = 0;
- } else {
- ssgBranch * oldParent = object->getParent(0);
- branch->addKid(object);
- oldParent->removeKid(object);
- }
- }
+ // The short name
+ string fileName = getSimpleFileName(*fullFilePath);
+ if (fileName.empty())
+ return 0;
+ // The name that should be found with the current database path
+ string fullLiveryFile = findFileInPath(fileName, _pathList);
+ // If it is empty or they are identical then there is nothing to do
+ if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
+ return 0;
+ Image* newImage = readImageFile(fullLiveryFile);
+ if (!newImage)
+ return 0;
+ CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
+ Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
+ if (!newTexture) {
+ return 0;
+ } else {
+ newTexture->setImage(newImage);
+ return newTexture;
+ }
+}
- if ( animation != 0 ) {
- animation->init();
- branch->setUserData(animation);
- branch->setTravCallback(SSG_CALLBACK_PRETRAV, animation_callback);
- branch->setTravCallback(SSG_CALLBACK_POSTTRAV, restore_callback);
- if ( ignore ) {
- ignore_branches.insert( branch );
+StateSet* TextureUpdateVisitor::cloneStateSet(const StateSet* stateSet)
+{
+ typedef std::pair<int, Texture2D*> Tex2D;
+ vector<Tex2D> newTextures;
+ StateSet* result = 0;
+
+ if (!stateSet)
+ return 0;
+ int numUnits = stateSet->getTextureAttributeList().size();
+ if (numUnits > 0) {
+ for (int i = 0; i < numUnits; ++i) {
+ const StateAttribute* attr
+ = stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
+ Texture2D* newTexture = textureReplace(i, attr);
+ if (newTexture)
+ newTextures.push_back(Tex2D(i, newTexture));
+ }
+ if (!newTextures.empty()) {
+ result = static_cast<StateSet*>(stateSet->clone(CopyOp()));
+ for (vector<Tex2D>::iterator i = newTextures.begin();
+ i != newTextures.end();
+ ++i) {
+ result->setTextureAttribute(i->first, i->second);
+ }
+ }
}
- }
+ return result;
}
+UserDataCopyVisitor::UserDataCopyVisitor() :
+ NodeVisitor(NodeVisitor::NODE_VISITOR,
+ NodeVisitor::TRAVERSE_ALL_CHILDREN)
+{
+}
-static void makeDList( ssgBranch *b, const set<ssgBranch *> &ignore )
+void UserDataCopyVisitor::apply(Node& node)
{
- int nb = b->getNumKids();
- for (int i = 0; i<nb; i++) {
- ssgEntity *e = b->getKid(i);
- if (e->isAKindOf(ssgTypeLeaf())) {
- if( ((ssgLeaf*)e)->getNumVertices() > 0)
- ((ssgLeaf*)e)->makeDList();
- } else if (e->isAKindOf(ssgTypeBranch()) && ignore.find((ssgBranch *)e) == ignore.end()) {
- makeDList( (ssgBranch*)e, ignore );
+ ref_ptr<SGSceneUserData> userData;
+ userData = SGSceneUserData::getSceneUserData(&node);
+ if (userData.valid()) {
+ SGSceneUserData* newUserData = new SGSceneUserData(*userData);
+ newUserData->setVelocity(0);
+ node.setUserData(newUserData);
}
- }
+ node.traverse(*this);
}
-class sgLoaderOptions : public ssgLoaderOptions {
+namespace
+{
+class MakeEffectVisitor : public SplicingVisitor
+{
public:
- void endLoad() {} // Avoid clearing the texture cache after every model load
+ typedef std::map<string, SGPropertyNode_ptr> EffectMap;
+ using SplicingVisitor::apply;
+ MakeEffectVisitor(const SGReaderWriterXMLOptions* options = 0)
+ : _options(options)
+ {
+ }
+ virtual void apply(osg::Group& node);
+ virtual void apply(osg::Geode& geode);
+ EffectMap& getEffectMap() { return _effectMap; }
+ const EffectMap& getEffectMap() const { return _effectMap; }
+ void setDefaultEffect(SGPropertyNode* effect)
+ {
+ _currentEffectParent = effect;
+ }
+ SGPropertyNode* getDefaultEffect() { return _currentEffectParent; }
+protected:
+ EffectMap _effectMap;
+ SGPropertyNode_ptr _currentEffectParent;
+ osg::ref_ptr<const SGReaderWriterXMLOptions> _options;
};
-static sgLoaderOptions loaderOptions;
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Global functions.
-////////////////////////////////////////////////////////////////////////
-
-ssgBranch *
-sgLoad3DModel( const string &fg_root, const string &path,
- SGPropertyNode *prop_root,
- double sim_time_sec, ssgEntity *(*load_panel)(SGPropertyNode *),
- SGModelData *data )
+void MakeEffectVisitor::apply(osg::Group& node)
{
- ssgBranch * model = 0;
- SGPropertyNode props;
-
- // Load the 3D aircraft object itself
- SGPath modelpath = path, texturepath = path;
- if ( !ulIsAbsolutePathName( path.c_str() ) ) {
- SGPath tmp = fg_root;
- tmp.append(modelpath.str());
- modelpath = texturepath = tmp;
- }
-
- // Check for an XML wrapper
- if (modelpath.str().substr(modelpath.str().size() - 4, 4) == ".xml") {
- readProperties(modelpath.str(), &props);
- if (props.hasValue("/path")) {
- modelpath = modelpath.dir();
- modelpath.append(props.getStringValue("/path"));
- if (props.hasValue("/texture-path")) {
- texturepath = texturepath.dir();
- texturepath.append(props.getStringValue("/texture-path"));
- }
- } else {
- if (model == 0)
- model = new ssgBranch;
+ SGPropertyNode_ptr savedEffectRoot;
+ const string& nodeName = node.getName();
+ bool restoreEffect = false;
+ if (!nodeName.empty()) {
+ EffectMap::iterator eitr = _effectMap.find(nodeName);
+ if (eitr != _effectMap.end()) {
+ savedEffectRoot = _currentEffectParent;
+ _currentEffectParent = eitr->second;
+ restoreEffect = true;
+ }
}
- }
+ SplicingVisitor::apply(node);
+ // If a new node was created, copy the user data too.
+ ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&node);
+ if (userData.valid() && _childStack.back().back().get() != &node)
+ _childStack.back().back()->setUserData(new SGSceneUserData(*userData));
+ if (restoreEffect)
+ _currentEffectParent = savedEffectRoot;
+}
- // Assume that textures are in
- // the same location as the XML file.
- if (model == 0) {
- if (texturepath.extension() != "")
- texturepath = texturepath.dir();
-
- ssgTexturePath((char *)texturepath.c_str());
- model = (ssgBranch *)ssgLoad((char *)modelpath.c_str(), &loaderOptions);
- if (model == 0)
- throw sg_io_exception("Failed to load 3D model",
- sg_location(modelpath.str()));
- }
- // Set up the alignment node
- ssgTransform * alignmainmodel = new ssgTransform;
- if ( load_panel == 0 )
- alignmainmodel->setTravCallback( SSG_CALLBACK_PRETRAV, model_filter_callback );
- alignmainmodel->addKid(model);
- sgMat4 res_matrix;
- sgMakeOffsetsMatrix(&res_matrix,
- props.getFloatValue("/offsets/heading-deg", 0.0),
- props.getFloatValue("/offsets/roll-deg", 0.0),
- props.getFloatValue("/offsets/pitch-deg", 0.0),
- props.getFloatValue("/offsets/x-m", 0.0),
- props.getFloatValue("/offsets/y-m", 0.0),
- props.getFloatValue("/offsets/z-m", 0.0));
- alignmainmodel->setTransform(res_matrix);
-
- unsigned int i;
-
- // Load sub-models
- vector<SGPropertyNode_ptr> model_nodes = props.getChildren("model");
- for (i = 0; i < model_nodes.size(); i++) {
- SGPropertyNode_ptr node = model_nodes[i];
- ssgTransform * align = new ssgTransform;
- sgMat4 res_matrix;
- sgMakeOffsetsMatrix(&res_matrix,
- node->getFloatValue("offsets/heading-deg", 0.0),
- node->getFloatValue("offsets/roll-deg", 0.0),
- node->getFloatValue("offsets/pitch-deg", 0.0),
- node->getFloatValue("offsets/x-m", 0.0),
- node->getFloatValue("offsets/y-m", 0.0),
- node->getFloatValue("offsets/z-m", 0.0));
- align->setTransform(res_matrix);
-
- ssgBranch * kid;
- const char * submodel = node->getStringValue("path");
- try {
- kid = sgLoad3DModel( fg_root, submodel, prop_root, sim_time_sec, load_panel );
-
- } catch (const sg_throwable &t) {
- SG_LOG(SG_INPUT, SG_ALERT, "Failed to load submodel: " << t.getFormattedMessage());
- throw;
+void MakeEffectVisitor::apply(osg::Geode& geode)
+{
+ if (pushNode(getNewNode(geode)))
+ return;
+ osg::StateSet* ss = geode.getStateSet();
+ if (!ss) {
+ pushNode(&geode);
+ return;
}
- align->addKid(kid);
- align->setName(node->getStringValue("name", ""));
- model->addKid(align);
- }
-
- if ( load_panel ) {
- // Load panels
- vector<SGPropertyNode_ptr> panel_nodes = props.getChildren("panel");
- for (i = 0; i < panel_nodes.size(); i++) {
- SG_LOG(SG_INPUT, SG_DEBUG, "Loading a panel");
- ssgEntity * panel = load_panel(panel_nodes[i]);
- if (panel_nodes[i]->hasValue("name"))
- panel->setName((char *)panel_nodes[i]->getStringValue("name"));
- model->addKid(panel);
+ SGPropertyNode_ptr ssRoot = new SGPropertyNode;
+ makeParametersFromStateSet(ssRoot, ss);
+ SGPropertyNode_ptr effectRoot = new SGPropertyNode;
+ effect::mergePropertyTrees(effectRoot, ssRoot, _currentEffectParent);
+ Effect* effect = makeEffect(effectRoot, true, _options.get());
+ EffectGeode* eg = dynamic_cast<EffectGeode*>(&geode);
+ if (eg) {
+ eg->setEffect(effect);
+ } else {
+ eg = new EffectGeode;
+ eg->setEffect(effect);
+ ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&geode);
+ if (userData.valid())
+ eg->setUserData(new SGSceneUserData(*userData));
+ for (unsigned i = 0; i < geode.getNumDrawables(); ++i) {
+ osg::Drawable *drawable = geode.getDrawable(i);
+ eg->addDrawable(drawable);
+
+ // Generate tangent vectors etc if needed
+ osg::Geometry *geom = dynamic_cast<osg::Geometry*>(drawable);
+ if(geom) eg->runGenerators(geom);
+ }
}
- }
+ pushResultNode(&geode, eg);
- if (data) {
- alignmainmodel->setUserData(data);
- data->modelLoaded(path, &props, alignmainmodel);
- }
- // Load animations
- set<ssgBranch *> ignore_branches;
- vector<SGPropertyNode_ptr> animation_nodes = props.getChildren("animation");
- for (i = 0; i < animation_nodes.size(); i++) {
- const char * name = animation_nodes[i]->getStringValue("name", 0);
- vector<SGPropertyNode_ptr> name_nodes =
- animation_nodes[i]->getChildren("object-name");
- sgMakeAnimation( model, name, name_nodes, prop_root, animation_nodes[i],
- sim_time_sec, texturepath, ignore_branches);
- }
-
-#if PLIB_VERSION > 183
- if ( model != 0 && sgUseDisplayList ) {
- makeDList( model, ignore_branches );
- }
-#endif
-
- int m = props.getIntValue("dump", 0);
- if (m > 0)
- model->print(stderr, "", m - 1);
+}
- return alignmainmodel;
}
-bool
-sgSetModelFilter( bool filter )
+namespace
{
- bool old = model_filter;
- model_filter = filter;
- return old;
+class DefaultEffect : public simgear::Singleton<DefaultEffect>
+{
+public:
+ DefaultEffect()
+ {
+ _effect = new SGPropertyNode;
+ makeChild(_effect.ptr(), "inherits-from")
+ ->setStringValue("Effects/model-default");
+ }
+ virtual ~DefaultEffect() {}
+ SGPropertyNode* getEffect() { return _effect.ptr(); }
+protected:
+ SGPropertyNode_ptr _effect;
+};
}
-bool
-sgCheckAnimationBranch (ssgEntity * entity)
+ref_ptr<Node> instantiateEffects(osg::Node* modelGroup,
+ PropertyList& effectProps,
+ const SGReaderWriterXMLOptions* options)
{
- return entity->getTravCallback(SSG_CALLBACK_PRETRAV) == animation_callback;
+ SGPropertyNode_ptr defaultEffectPropRoot;
+ MakeEffectVisitor visitor(options);
+ MakeEffectVisitor::EffectMap& emap = visitor.getEffectMap();
+ for (PropertyList::iterator itr = effectProps.begin(),
+ end = effectProps.end();
+ itr != end;
+ ++itr)
+ {
+ SGPropertyNode_ptr configNode = *itr;
+ std::vector<SGPropertyNode_ptr> objectNames =
+ configNode->getChildren("object-name");
+ SGPropertyNode* defaultNode = configNode->getChild("default");
+ if (defaultNode && defaultNode->getValue<bool>())
+ defaultEffectPropRoot = configNode;
+ BOOST_FOREACH(SGPropertyNode_ptr objNameNode, objectNames) {
+ emap.insert(make_pair(objNameNode->getStringValue(), configNode));
+ }
+ configNode->removeChild("default");
+ configNode->removeChildren("object-name");
+ }
+ if (!defaultEffectPropRoot)
+ defaultEffectPropRoot = DefaultEffect::instance()->getEffect();
+ visitor.setDefaultEffect(defaultEffectPropRoot.ptr());
+ modelGroup->accept(visitor);
+ osg::NodeList& result = visitor.getResults();
+ return ref_ptr<Node>(result[0].get());
+}
}
-
// end of model.cxx