From 35498cd487089b281e975c8d812f7694f3a05ddf Mon Sep 17 00:00:00 2001
From: buti <buti@bux.at>
Date: Wed, 27 Feb 2008 21:45:55 +0100
Subject: [PATCH] model-paging v0.2.2 (beta)

---
 simgear/scene/model/Makefile.am                  |    5 +
 simgear/scene/model/SGPagedLOD.cxx               |  145 ++++++++++++++++++++++
 simgear/scene/model/SGPagedLOD.hxx               |   42 ++++++
 simgear/scene/model/SGReaderWriterXML.cxx        |   90 +++++++++++++
 simgear/scene/model/SGReaderWriterXML.hxx        |   37 ++++++
 simgear/scene/model/SGReaderWriterXMLOptions.hxx |   84 +++++++++++++
 simgear/scene/model/model.cxx                    |   42 ++++++-
 7 files changed, 443 insertions(+), 2 deletions(-)
 create mode 100644 simgear/scene/model/SGPagedLOD.cxx
 create mode 100644 simgear/scene/model/SGPagedLOD.hxx
 create mode 100644 simgear/scene/model/SGReaderWriterXML.cxx
 create mode 100644 simgear/scene/model/SGReaderWriterXML.hxx
 create mode 100644 simgear/scene/model/SGReaderWriterXMLOptions.hxx

diff --git a/simgear/scene/model/Makefile.am b/simgear/scene/model/Makefile.am
index db102d9..2f112c9 100644
--- a/simgear/scene/model/Makefile.am
+++ b/simgear/scene/model/Makefile.am
@@ -17,6 +17,9 @@ include_HEADERS = \
 	SGClipGroup.hxx \
 	SGMaterialAnimation.hxx \
 	SGOffsetTransform.hxx \
+        SGPagedLOD.hxx \
+        SGReaderWriterXML.hxx \
+        SGReaderWriterXMLOptions.hxx \
 	SGRotateTransform.hxx \
 	SGScaleTransform.hxx \
 	SGTranslateTransform.hxx
@@ -35,6 +38,8 @@ libsgmodel_a_SOURCES = \
 	SGClipGroup.cxx \
 	SGMaterialAnimation.cxx \
 	SGOffsetTransform.cxx \
+        SGPagedLOD.cxx \
+        SGReaderWriterXML.cxx \
 	SGRotateTransform.cxx \
 	SGScaleTransform.cxx \
 	SGTranslateTransform.cxx
diff --git a/simgear/scene/model/SGPagedLOD.cxx b/simgear/scene/model/SGPagedLOD.cxx
new file mode 100644
index 0000000..28b8e31
--- /dev/null
+++ b/simgear/scene/model/SGPagedLOD.cxx
@@ -0,0 +1,145 @@
+/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
+ *
+ * This library is open source and may be redistributed and/or modified under
+ * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
+ * (at your option) any later version.  The full license is in LICENSE file
+ * included with this distribution, and on the openscenegraph.org website.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * OpenSceneGraph Public License for more details.
+*/
+
+#include <osg/CullStack>
+#include <osg/Notify>
+#include <osgDB/DatabasePager>
+
+#include <algorithm>
+#include "SGPagedLOD.hxx"
+
+using namespace osg;
+
+SGPagedLOD::SGPagedLOD()
+{
+    _frameNumberOfLastTraversal = 0;
+    _centerMode = USER_DEFINED_CENTER;
+    _radius = -1;
+    _numChildrenThatCannotBeExpired = 0;
+}
+
+SGPagedLOD::SGPagedLOD(const SGPagedLOD& plod,const CopyOp& copyop):
+    osg::PagedLOD(plod, copyop),
+    _readerWriterOptions(plod._readerWriterOptions)
+{
+}
+
+void SGPagedLOD::traverse(osg::NodeVisitor& nv)
+{
+    // set the frame number of the traversal so that external nodes can find out how active this
+    // node is.
+    if (nv.getFrameStamp()) setFrameNumberOfLastTraversal(nv.getFrameStamp()->getFrameNumber());
+
+    double timeStamp = nv.getFrameStamp()?nv.getFrameStamp()->getReferenceTime():0.0;
+    bool updateTimeStamp = nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR;
+
+    switch(nv.getTraversalMode())
+    {
+        case(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN):
+            std::for_each(_children.begin(),_children.end(),NodeAcceptOp(nv));
+            break;
+        case(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN):
+        {
+            float required_range = 0;
+            if (_rangeMode==DISTANCE_FROM_EYE_POINT)
+            {
+                required_range = nv.getDistanceToViewPoint(getCenter(),true);
+            }
+            else
+            {
+                osg::CullStack* cullStack = dynamic_cast<osg::CullStack*>(&nv);
+                if (cullStack && cullStack->getLODScale()>0.0f)
+                {
+                    required_range = cullStack->clampedPixelSize(getBound()) / cullStack->getLODScale();
+                }
+                else
+                {
+                    // fallback to selecting the highest res tile by
+                    // finding out the max range
+                    for(unsigned int i=0;i<_rangeList.size();++i)
+                    {
+                        required_range = osg::maximum(required_range,_rangeList[i].first);
+                    }
+                }
+            }
+
+            int lastChildTraversed = -1;
+            bool needToLoadChild = false;
+            for(unsigned int i=0;i<_rangeList.size();++i)
+            {
+                if (_rangeList[i].first<=required_range && required_range<_rangeList[i].second)
+                {
+                    if (i<_children.size())
+                    {
+                        if (updateTimeStamp) _perRangeDataList[i]._timeStamp=timeStamp;
+
+                        _children[i]->accept(nv);
+                        lastChildTraversed = (int)i;
+                    }
+                    else
+                    {
+                        needToLoadChild = true;
+                    }
+                }
+            }
+
+            if (needToLoadChild)
+            {
+                unsigned int numChildren = _children.size();
+
+                // select the last valid child.
+                if (numChildren>0 && ((int)numChildren-1)!=lastChildTraversed)
+                {
+                    if (updateTimeStamp) _perRangeDataList[numChildren-1]._timeStamp=timeStamp;
+                    _children[numChildren-1]->accept(nv);
+                }
+
+		osgDB::DatabasePager *dbp = dynamic_cast<osgDB::DatabasePager *>(nv.getDatabaseRequestHandler());
+		
+                // now request the loading of the next unloaded child.
+                if (dbp && numChildren<_perRangeDataList.size())
+                {
+                    // compute priority from where abouts in the required range the distance falls.
+                    float priority = (_rangeList[numChildren].second-required_range)/(_rangeList[numChildren].second-_rangeList[numChildren].first);
+
+                    // invert priority for PIXEL_SIZE_ON_SCREEN mode
+                    if(_rangeMode==PIXEL_SIZE_ON_SCREEN)
+                    {
+                        priority = -priority;
+                    }
+
+                    // modify the priority according to the child's priority offset and scale.
+                    priority = _perRangeDataList[numChildren]._priorityOffset + priority * _perRangeDataList[numChildren]._priorityScale;
+
+                    if (_databasePath.empty())
+                    {
+                        dbp->requestNodeFile(_perRangeDataList[numChildren]._filename,this,priority,nv.getFrameStamp(), _readerWriterOptions.get());
+                    }
+                    else
+                    {
+                        // prepend the databasePath to the child's filename.
+                        dbp->requestNodeFile(_databasePath+_perRangeDataList[numChildren]._filename,this,priority,nv.getFrameStamp(),_readerWriterOptions.get());
+                    }
+                }
+
+
+            }
+
+
+           break;
+        }
+        default:
+            break;
+    }
+}
+
diff --git a/simgear/scene/model/SGPagedLOD.hxx b/simgear/scene/model/SGPagedLOD.hxx
new file mode 100644
index 0000000..1398cf9
--- /dev/null
+++ b/simgear/scene/model/SGPagedLOD.hxx
@@ -0,0 +1,42 @@
+/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield 
+ *
+ * This library is open source and may be redistributed and/or modified under  
+ * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
+ * (at your option) any later version.  The full license is in LICENSE file
+ * included with this distribution, and on the openscenegraph.org website.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
+ * OpenSceneGraph Public License for more details.
+*/
+
+#ifndef SGPAGEDLOD_HXX
+#define SGPAGEDLOD_HXX 1
+
+#include <osg/PagedLOD>
+#include <osgDB/ReaderWriter>
+
+class SGPagedLOD : public osg::PagedLOD
+{
+    public :
+    
+        SGPagedLOD();
+
+        /** Copy constructor using CopyOp to manage deep vs shallow copy.*/
+        SGPagedLOD(const SGPagedLOD&,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
+
+        META_Node(osg, PagedLOD);
+        
+        virtual void traverse(osg::NodeVisitor& nv);
+
+	void setReaderWriterOptions(osgDB::ReaderWriter::Options *o)
+            { _readerWriterOptions=o; }
+
+    protected :
+    
+        virtual ~SGPagedLOD() {}
+        osg::ref_ptr<osgDB::ReaderWriter::Options> _readerWriterOptions;
+};
+
+#endif
diff --git a/simgear/scene/model/SGReaderWriterXML.cxx b/simgear/scene/model/SGReaderWriterXML.cxx
new file mode 100644
index 0000000..6bbdc0f
--- /dev/null
+++ b/simgear/scene/model/SGReaderWriterXML.cxx
@@ -0,0 +1,90 @@
+// Copyright (C) 2007 Tim Moore timoore@redhat.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+
+#include <osgDB/FileNameUtils>
+#include <osgDB/Registry>
+#include <simgear/scene/model/model.hxx>
+#include <simgear/scene/model/ModelRegistry.hxx>
+#include <simgear/structure/exception.hxx>
+
+#include "SGReaderWriterXMLOptions.hxx"
+#include "SGReaderWriterXML.hxx"
+
+osg::Node *
+sgLoad3DModel_internal( const string &fg_root, const string &path,
+               SGPropertyNode *prop_root,
+               double sim_time_sec, osg::Node *(*load_panel)(SGPropertyNode *),
+               SGModelData *data,
+               const SGPath& externalTexturePath );
+
+using namespace simgear;
+
+const char* SGReaderWriterXML::className() const
+{
+    return "XML database reader";
+}
+
+bool SGReaderWriterXML::acceptsExtension(const std::string& extension) const
+{
+    return (osgDB::equalCaseInsensitive(extension, "xml"));
+}
+
+osgDB::ReaderWriter::ReadResult
+SGReaderWriterXML::readNode(const std::string& fileName,
+                            const osgDB::ReaderWriter::Options* options) const
+{
+    std::string ext = osgDB::getLowerCaseFileExtension(fileName);
+    if(!acceptsExtension(ext))
+        return ReadResult::FILE_NOT_HANDLED;
+
+    const SGReaderWriterXMLOptions* xmlOptions
+        = dynamic_cast<const SGReaderWriterXMLOptions*>(options);
+
+    string fg_root;
+    SGPropertyNode *prop_root=0;
+    osg::Node *(*load_panel)(SGPropertyNode *)=0;
+    SGModelData *model_data=0;
+    SGPath externalTexturePath;
+
+    if (xmlOptions) {
+        fg_root = xmlOptions->getFGRoot();
+        prop_root = xmlOptions->getPropRoot();
+        load_panel = xmlOptions->getLoadPanel();
+        model_data = xmlOptions->getModelData();
+        externalTexturePath = xmlOptions->getExternalTexturePath();
+    }
+
+    osg::Node *result=0;
+
+    try {
+        result=sgLoad3DModel_internal(fg_root, fileName, prop_root, 0.0, load_panel,
+            model_data, externalTexturePath);
+    } catch (const sg_throwable &t) {
+        SG_LOG(SG_INPUT, SG_ALERT, "Failed to load model: " << t.getFormattedMessage());
+        result=new osg::Node;
+    }
+    if (result)
+        return result;
+    else
+        return ReadResult::FILE_NOT_HANDLED;
+}
+
+namespace
+{
+ModelRegistryCallbackProxy<LoadOnlyCallback> g_xmlCallbackProxy("xml");
+}
+
diff --git a/simgear/scene/model/SGReaderWriterXML.hxx b/simgear/scene/model/SGReaderWriterXML.hxx
new file mode 100644
index 0000000..e7ce8b7
--- /dev/null
+++ b/simgear/scene/model/SGReaderWriterXML.hxx
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2006-2007 Tim Moore timoore@redhat.com
+ * Copyright (C) 2008 Till Busch buti@bux.at
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+#ifndef SGREADERWRITERXML_HXX
+#define SGREADERWRITERXML_HXX 1
+#include <osgDB/Registry>
+
+class SGReaderWriterXML : public osgDB::ReaderWriter {
+public:
+    virtual const char* className() const;
+ 
+    virtual bool acceptsExtension(const std::string& extension) const;
+
+    virtual ReadResult readNode(const std::string& fileName,
+                                const osgDB::ReaderWriter::Options* options)
+        const;
+};
+
+#endif
+    
diff --git a/simgear/scene/model/SGReaderWriterXMLOptions.hxx b/simgear/scene/model/SGReaderWriterXMLOptions.hxx
new file mode 100644
index 0000000..584c84f
--- /dev/null
+++ b/simgear/scene/model/SGReaderWriterXMLOptions.hxx
@@ -0,0 +1,84 @@
+// Copyright (C) 2007 Tim Moore timoore@redhat.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+#ifndef SGREADERWRITERXMLOPTIONS_HXX
+#define SGREADERWRITERXMLOPTIONS_HXX 1
+
+#include <osgDB/ReaderWriter>
+#include <simgear/misc/sg_path.hxx>
+
+class SGPropertyNode;
+class SGModelData;
+
+class SGReaderWriterXMLOptions : public osgDB::ReaderWriter::Options {
+public:
+    SGReaderWriterXMLOptions():
+        osgDB::ReaderWriter::Options(),
+        _prop_root(0),
+        _load_panel(0),
+        _model_data(0)
+    {}
+
+    SGReaderWriterXMLOptions(const std::string& str):
+        osgDB::ReaderWriter::Options(str),
+        _prop_root(0),
+        _load_panel(0),
+        _model_data(0),
+        _external_texture_path(0)
+    {}
+    
+    SGReaderWriterXMLOptions(const SGReaderWriterXMLOptions& options,
+            const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY):
+        osgDB::ReaderWriter::Options(options, copyop),
+        _fg_root(options._fg_root),
+        _prop_root(options._prop_root),
+        _load_panel(options._load_panel),
+        _model_data(options._model_data),
+        _external_texture_path(options._external_texture_path)
+    {
+    }
+
+/*
+‘getFGRoot’
+SGReaderWriterXML.cxx:56: error: ‘const class SGReaderWriterXMLOptions’ has no member named ‘getPropRoot’
+SGReaderWriterXML.cxx:58: error: ‘const class SGReaderWriterXMLOptions’ has no member named ‘getLoadPanel’
+SGReaderWriterXML.cxx:59: error: ‘const class SGReaderWriterXMLOptions’ has no member named ‘getModelData’
+SGReaderWriterXML.cxx:60: error: ‘const class SGReaderWriterXMLOptions’ has no member named ‘getExternalTexturePath’
+*/
+    typedef osg::Node *(*panel_func)(SGPropertyNode *);
+
+    std::string getFGRoot() const { return _fg_root; }
+    SGPropertyNode *getPropRoot() const { return _prop_root; }
+    panel_func getLoadPanel() const { return _load_panel; }
+    SGModelData *getModelData() const { return _model_data.get(); }
+    const SGPath &getExternalTexturePath() const { return _external_texture_path; }
+
+    void setFGRoot(const std::string& p) { _fg_root=p; }
+    void setPropRoot(SGPropertyNode *p) { _prop_root=p; }
+    void setLoadPanel(panel_func pf) { _load_panel=pf; }
+    void setModelData(SGModelData *d) { _model_data=d;}
+    void setExternalTexturePath(const SGPath &p) { _external_texture_path=p;}
+
+protected:
+    virtual ~SGReaderWriterXMLOptions() {}
+
+    std::string _fg_root;
+    SGPropertyNode *_prop_root;
+    osg::Node *(*_load_panel)(SGPropertyNode *);
+    osg::ref_ptr<SGModelData> _model_data;
+    SGPath _external_texture_path;
+};
+#endif
diff --git a/simgear/scene/model/model.cxx b/simgear/scene/model/model.cxx
index fd4962c..87ea611 100644
--- a/simgear/scene/model/model.cxx
+++ b/simgear/scene/model/model.cxx
@@ -31,6 +31,9 @@
 #include <simgear/props/props_io.hxx>
 #include <simgear/props/condition.hxx>
 
+#include "SGPagedLOD.hxx"
+#include "SGReaderWriterXML.hxx"
+#include "SGReaderWriterXMLOptions.hxx"
 #include "animation.hxx"
 #include "model.hxx"
 #include "particles.hxx"
@@ -39,6 +42,8 @@ SG_USING_STD(vector);
 
 using namespace simgear;
 
+osgDB::RegisterReaderWriterProxy<SGReaderWriterXML> g_readerWriter_XML_Proxy;
+
 osg::Texture2D*
 SGLoadTexture2D(bool staticTexture, const std::string& path,
                 const osgDB::ReaderWriter::Options* options,
@@ -131,6 +136,34 @@ sgLoad3DModel( const string &fg_root, const string &path,
                SGModelData *data,
                const SGPath& externalTexturePath )
 {
+  SGPath modelpath = path;
+  if ( !ulIsAbsolutePathName( path.c_str() ) ) {
+    SGPath tmp = fg_root;
+    tmp.append(modelpath.str());
+    modelpath = tmp;
+  }
+
+  SGPagedLOD *plod = new SGPagedLOD;
+  plod->setFileName(0, modelpath.str());
+  plod->setRange(0, 0.0, 9000.0);
+
+  SGReaderWriterXMLOptions *opt = new SGReaderWriterXMLOptions;
+  opt->setFGRoot(fg_root);
+  opt->setPropRoot(prop_root);
+  opt->setLoadPanel(load_panel);
+  opt->setModelData(data);
+  opt->setExternalTexturePath(externalTexturePath);
+  plod->setReaderWriterOptions(opt);
+  return plod;
+}
+
+osg::Node *
+sgLoad3DModel_internal( const string &fg_root, const string &path,
+               SGPropertyNode *prop_root,
+               double sim_time_sec, osg::Node *(*load_panel)(SGPropertyNode *),
+               SGModelData *data=0,
+               const SGPath& externalTexturePath=SGPath() )
+{
   osg::ref_ptr<osg::Node> model;
   SGPropertyNode props;
 
@@ -144,7 +177,12 @@ sgLoad3DModel( const string &fg_root, const string &path,
 
   // Check for an XML wrapper
   if (modelpath.str().substr(modelpath.str().size() - 4, 4) == ".xml") {
-    readProperties(modelpath.str(), &props);
+    try {
+      readProperties(modelpath.str(), &props);
+    } catch (const sg_throwable &t) {
+      SG_LOG(SG_INPUT, SG_ALERT, "Failed to load xml: " << t.getFormattedMessage());
+      throw;
+    }
     if (props.hasValue("/path")) {
       modelpath = modelpath.dir();
       modelpath.append(props.getStringValue("/path"));
@@ -219,7 +257,7 @@ sgLoad3DModel( const string &fg_root, const string &path,
     osg::ref_ptr<osg::Node> kid;
     const char* submodel = node->getStringValue("path");
     try {
-      kid = sgLoad3DModel( fg_root, submodel, prop_root, sim_time_sec, load_panel );
+      kid = sgLoad3DModel_internal( 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());
-- 
1.5.2.5


