Added preliminary support for <property_animation>
This commit is contained in:
@@ -62,7 +62,7 @@ protected:
|
||||
|
||||
};
|
||||
|
||||
class PropertyAnimation : public osg::NodeCallback
|
||||
class OSGPRESENTATION_EXPORT PropertyAnimation : public osg::NodeCallback
|
||||
{
|
||||
public:
|
||||
PropertyAnimation():
|
||||
@@ -93,12 +93,16 @@ public:
|
||||
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
|
||||
|
||||
virtual void update();
|
||||
virtual void update(osg::Node* node);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
osg::ref_ptr<PropertyManager> _pm;
|
||||
|
||||
void assign(osg::UserDataContainer* destination, osg::UserDataContainer* source);
|
||||
void assign(osg::UserDataContainer* udc, osg::Object* obj);
|
||||
|
||||
KeyFrameMap _keyFrameMap;
|
||||
|
||||
double _firstTime;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <osgPresentation/CompileSlideCallback>
|
||||
#include <osgPresentation/PropertyManager>
|
||||
|
||||
namespace osgPresentation
|
||||
{
|
||||
@@ -237,69 +238,6 @@ protected:
|
||||
|
||||
};
|
||||
|
||||
class PropertyManager : protected osg::Object
|
||||
{
|
||||
public:
|
||||
|
||||
PropertyManager() {}
|
||||
PropertyManager(const PropertyManager& pm, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY):
|
||||
osg::Object(pm,copyop) {}
|
||||
|
||||
META_Object(osgPresentation, PropertyManager)
|
||||
|
||||
/** Convinience method that casts the named UserObject to osg::TemplateValueObject<T> and gets the value.
|
||||
* To use this template method you need to include the osg/ValueObject header.*/
|
||||
template<typename T>
|
||||
bool getProperty(const std::string& name, T& value) const
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
||||
return getUserValue(name, value);
|
||||
}
|
||||
|
||||
/** Convinience method that creates the osg::TemplateValueObject<T> to store the
|
||||
* specified value and adds it as a named UserObject.
|
||||
* To use this template method you need to include the osg/ValueObject header. */
|
||||
template<typename T>
|
||||
void setProperty(const std::string& name, const T& value)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
||||
return setUserValue(name, value);
|
||||
}
|
||||
|
||||
int ref() const { return osg::Referenced::ref(); }
|
||||
int unref() const { return osg::Referenced::unref(); }
|
||||
|
||||
protected:
|
||||
|
||||
mutable OpenThreads::Mutex _mutex;
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct OSGPRESENTATION_EXPORT ImageSequenceUpdateCallback : public osg::NodeCallback
|
||||
{
|
||||
ImageSequenceUpdateCallback(osg::ImageSequence* is, PropertyManager* pm, const std::string& propertyName):
|
||||
_imageSequence(is),
|
||||
_propertyManager(pm),
|
||||
_propertyName(propertyName) {}
|
||||
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
|
||||
|
||||
osg::ref_ptr<osg::ImageSequence> _imageSequence;
|
||||
osg::ref_ptr<PropertyManager> _propertyManager;
|
||||
std::string _propertyName;
|
||||
};
|
||||
|
||||
struct OSGPRESENTATION_EXPORT PropertyEventCallback : public osgGA::GUIEventHandler
|
||||
{
|
||||
PropertyEventCallback(PropertyManager* pm):
|
||||
_propertyManager(pm) {}
|
||||
|
||||
virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&);
|
||||
|
||||
osg::ref_ptr<PropertyManager> _propertyManager;
|
||||
};
|
||||
|
||||
class OSGPRESENTATION_EXPORT SlideEventHandler : public osgGA::GUIEventHandler
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <osgPresentation/AnimationMaterial>
|
||||
#include <osgPresentation/SlideEventHandler>
|
||||
#include <osgPresentation/PropertyManager>
|
||||
|
||||
namespace osgPresentation
|
||||
{
|
||||
@@ -436,6 +437,8 @@ public:
|
||||
void layerClickToDoOperation(const std::string& command, Operation operation, const JumpData& jumpData=JumpData());
|
||||
void layerClickEventOperation(const KeyPosition& keyPos, const JumpData& jumpData=JumpData());
|
||||
|
||||
void addPropertyAnimation(PresentationContext presentationContext, PropertyAnimation* propertyAnimation);
|
||||
|
||||
void addToCurrentLayer(osg::Node* subgraph);
|
||||
|
||||
void addBullet(const std::string& bullet, PositionData& positionData, FontData& fontData);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield
|
||||
/* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield
|
||||
*
|
||||
* This software is open source and may be redistributed and/or modified under
|
||||
* the terms of the GNU General Public License (GPL) version 2.0.
|
||||
@@ -122,6 +122,9 @@ public:
|
||||
|
||||
osg::Node* parseXmlGraph(osgDB::XmlNode* root, bool readOnlyHoldingPage, osgDB::Options* options) const;
|
||||
|
||||
bool parseProperties(osgDB::XmlNode* root, osg::UserDataContainer& udc) const;
|
||||
bool parsePropertyAnimation(osgDB::XmlNode* root, osgPresentation::PropertyAnimation& pa) const;
|
||||
|
||||
void parseModel(osgPresentation::SlideShowConstructor& constructor, osgDB::XmlNode*cur) const;
|
||||
|
||||
osg::TransferFunction1D* readTransferFunctionFile(const std::string& filename, float scale) const;
|
||||
@@ -196,6 +199,7 @@ public:
|
||||
bool getProperties(osgDB::XmlNode*cur, osgPresentation::SlideShowConstructor::FontData& value) const;
|
||||
bool getProperties(osgDB::XmlNode*cur, osgPresentation::SlideShowConstructor::ModelData& value) const;
|
||||
bool getProperties(osgDB::XmlNode*cur, osgPresentation::SlideShowConstructor::ImageData& value) const;
|
||||
|
||||
bool getJumpProperties(osgDB::XmlNode*cur, osgPresentation::JumpData& jumpData) const;
|
||||
|
||||
bool getKeyPositionInner(osgDB::XmlNode*cur, osgPresentation::KeyPosition& keyPosition) const;
|
||||
@@ -967,7 +971,101 @@ bool ReaderWriterP3DXML::getProperties(osgDB::XmlNode*cur, osgPresentation::Slid
|
||||
return propertiesRead;
|
||||
}
|
||||
|
||||
bool ReaderWriterP3DXML::getJumpProperties(osgDB::XmlNode*cur, osgPresentation::JumpData& jumpData) const
|
||||
bool ReaderWriterP3DXML::parseProperties(osgDB::XmlNode* root, osg::UserDataContainer& udc) const
|
||||
{
|
||||
bool readProperties = false;
|
||||
OSG_NOTICE<<"Doing parseProperties()"<<std::endl;
|
||||
for(osgDB::XmlNode::Children::iterator itr = root->children.begin();
|
||||
itr != root->children.end();
|
||||
++itr)
|
||||
{
|
||||
osgDB::XmlNode* cur = itr->get();
|
||||
|
||||
if (cur->name == "property")
|
||||
{
|
||||
std::string name;
|
||||
std::string type;
|
||||
|
||||
getProperty(cur, "name", name);
|
||||
getProperty(cur, "type", type);
|
||||
|
||||
if (type=="float")
|
||||
{
|
||||
float value;
|
||||
std::stringstream str(cur->contents);
|
||||
str>>value;
|
||||
|
||||
udc.setUserValue(name, value);
|
||||
readProperties = true;
|
||||
|
||||
OSG_NOTICE<<"Adding property float "<<value<<std::endl;
|
||||
}
|
||||
else if (type=="int")
|
||||
{
|
||||
int value;
|
||||
std::stringstream str(cur->contents);
|
||||
str>>value;
|
||||
|
||||
udc.setUserValue(name, value);
|
||||
readProperties = true;
|
||||
|
||||
OSG_NOTICE<<"Adding property int "<<value<<std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
udc.setUserValue(name, cur->contents);
|
||||
readProperties = true;
|
||||
OSG_NOTICE<<"Adding property string "<<cur->contents<<std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OSG_NOTICE<<"Unhandled tag["<<cur->name<<"] expecting <property>"<<std::endl;
|
||||
}
|
||||
}
|
||||
return readProperties;
|
||||
}
|
||||
|
||||
bool ReaderWriterP3DXML::parsePropertyAnimation(osgDB::XmlNode* root, osgPresentation::PropertyAnimation& pa) const
|
||||
{
|
||||
bool readKeyframes = false;
|
||||
OSG_NOTICE<<"Doing parsePropertyAnimation()"<<std::endl;
|
||||
for(osgDB::XmlNode::Children::iterator itr = root->children.begin();
|
||||
itr != root->children.end();
|
||||
++itr)
|
||||
{
|
||||
osgDB::XmlNode* cur = itr->get();
|
||||
|
||||
if (cur->name == "key_frame")
|
||||
{
|
||||
|
||||
double time;
|
||||
if (getProperty(cur, "time", time))
|
||||
{
|
||||
osg::ref_ptr<osg::UserDataContainer> udc = new osg::DefaultUserDataContainer;
|
||||
if (parseProperties(cur, *udc))
|
||||
{
|
||||
OSG_NOTICE<<"Adding keyframe"<<std::endl;
|
||||
pa.addKeyFrame(time, udc);
|
||||
readKeyframes = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OSG_NOTICE<<"No time assigned to key_frame, ignoring <key_frame>"<<std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OSG_NOTICE<<"Unhandled tag["<<cur->name<<"] expecting <key_frame>"<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return readKeyframes;
|
||||
}
|
||||
|
||||
|
||||
bool ReaderWriterP3DXML::getJumpProperties(osgDB::XmlNode* cur, osgPresentation::JumpData& jumpData) const
|
||||
{
|
||||
bool propertyRead = false;
|
||||
|
||||
@@ -1556,6 +1654,14 @@ void ReaderWriterP3DXML::parseLayer(osgPresentation::SlideShowConstructor& const
|
||||
{
|
||||
constructor.setLayerDuration(osg::asciiToDouble(cur->contents.c_str()));
|
||||
}
|
||||
else if (cur->name == "property_animation")
|
||||
{
|
||||
osg::ref_ptr<osgPresentation::PropertyAnimation> pa = new osgPresentation::PropertyAnimation;
|
||||
if (parsePropertyAnimation(cur,*pa))
|
||||
{
|
||||
constructor.addPropertyAnimation(osgPresentation::SlideShowConstructor::CURRENT_LAYER, pa.get());
|
||||
}
|
||||
}
|
||||
else if (getKeyPosition(cur, keyPosition))
|
||||
{
|
||||
constructor.addLayerKey(keyPosition);
|
||||
@@ -1822,6 +1928,14 @@ void ReaderWriterP3DXML::parseSlide (osgPresentation::SlideShowConstructor& cons
|
||||
{
|
||||
constructor.setSlideDuration(osg::asciiToDouble(cur->contents.c_str()));
|
||||
}
|
||||
else if (cur->name == "property_animation")
|
||||
{
|
||||
osg::ref_ptr<osgPresentation::PropertyAnimation> pa = new osgPresentation::PropertyAnimation;
|
||||
if (parsePropertyAnimation(cur,*pa))
|
||||
{
|
||||
constructor.addPropertyAnimation(osgPresentation::SlideShowConstructor::CURRENT_SLIDE, pa.get());
|
||||
}
|
||||
}
|
||||
else if (getKeyPosition(cur, keyPosition))
|
||||
{
|
||||
constructor.addSlideKey(keyPosition);
|
||||
@@ -2518,6 +2632,14 @@ osg::Node* ReaderWriterP3DXML::parseXmlGraph(osgDB::XmlNode* root, bool readOnly
|
||||
std::cout<<"Defining template slide "<<name<<std::endl;
|
||||
}
|
||||
}
|
||||
else if (!readOnlyHoldingPage && cur->name == "property_animation")
|
||||
{
|
||||
osg::ref_ptr<osgPresentation::PropertyAnimation> pa = new osgPresentation::PropertyAnimation;
|
||||
if (parsePropertyAnimation(cur,*pa))
|
||||
{
|
||||
constructor.addPropertyAnimation(osgPresentation::SlideShowConstructor::CURRENT_PRESENTATION, pa.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osgDB::getDataFilePathList() = previousPaths;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <osgPresentation/PropertyManager>
|
||||
#include <osg/io_utils>
|
||||
|
||||
using namespace osgPresentation;
|
||||
|
||||
@@ -61,7 +62,7 @@ void PropertyAnimation::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||
{
|
||||
// Only update _firstTime the first time, when its value is still DBL_MAX
|
||||
if (_firstTime==DBL_MAX) _firstTime = time;
|
||||
update();
|
||||
update(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +73,8 @@ class MySetValueVisitor : public osg::ValueObject::SetValueVisitor
|
||||
{
|
||||
public:
|
||||
|
||||
MySetValueVisitor(double r1, double r2, osg::ValueObject* object2)
|
||||
MySetValueVisitor(double in_r1, double in_r2, osg::ValueObject* in_object2):
|
||||
_r1(in_r1), _r2(in_r2), _object2(in_object2)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -80,22 +82,24 @@ public:
|
||||
void combineRealUserValue(T& value) const
|
||||
{
|
||||
typedef osg::TemplateValueObject<T> UserValueObject;
|
||||
const UserValueObject* uvo = object2 ? dynamic_cast<const UserValueObject*>(object2) : 0;
|
||||
const UserValueObject* uvo = _object2 ? dynamic_cast<const UserValueObject*>(_object2) : 0;
|
||||
if (uvo)
|
||||
{
|
||||
value = value*_r1 + uvo->getValue()*_r2;
|
||||
}
|
||||
OSG_NOTICE<<"combineRealUserValue r1="<<_r1<<", r2="<<_r2<<", value="<<value<<std::endl;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void combineIntegerUserValue(T& value) const
|
||||
{
|
||||
typedef osg::TemplateValueObject<T> UserValueObject;
|
||||
const UserValueObject* uvo = object2 ? dynamic_cast<const UserValueObject*>(object2) : 0;
|
||||
const UserValueObject* uvo = _object2 ? dynamic_cast<const UserValueObject*>(_object2) : 0;
|
||||
if (uvo)
|
||||
{
|
||||
value = static_cast<T>(static_cast<double>(value)*_r1 + static_cast<double>(uvo->getValue())*_r2);
|
||||
}
|
||||
OSG_NOTICE<<"combineIntegerUserValue "<<value<<std::endl;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -104,12 +108,13 @@ public:
|
||||
if (_r1<_r2) // choose value2 if possible
|
||||
{
|
||||
typedef osg::TemplateValueObject<T> UserValueObject;
|
||||
const UserValueObject* uvo = object2 ? dynamic_cast<const UserValueObject*>(object2) : 0;
|
||||
const UserValueObject* uvo = _object2 ? dynamic_cast<const UserValueObject*>(_object2) : 0;
|
||||
if (uvo)
|
||||
{
|
||||
value = uvo->getValue();
|
||||
}
|
||||
}
|
||||
OSG_NOTICE<<"combineDiscretUserValue "<<value<<std::endl;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -153,14 +158,14 @@ public:
|
||||
virtual void apply(osg::Matrixd& value) { combineMatrixUserValue(value); }
|
||||
|
||||
double _r1, _r2;
|
||||
osg::ValueObject* object2;
|
||||
osg::ValueObject* _object2;
|
||||
};
|
||||
|
||||
void PropertyAnimation::update()
|
||||
void PropertyAnimation::update(osg::Node* node)
|
||||
{
|
||||
double time = getAnimationTime();
|
||||
OSG_NOTICE<<"PropertyAnimation::update()"<<this<<std::endl;
|
||||
|
||||
osg::ref_ptr<osg::UserDataContainer> result;
|
||||
double time = getAnimationTime();
|
||||
|
||||
if (_keyFrameMap.empty()) return;
|
||||
|
||||
@@ -169,7 +174,7 @@ void PropertyAnimation::update()
|
||||
{
|
||||
// need to copy first UserDataContainer
|
||||
OSG_NOTICE<<"PropertyAnimation::update() : copy first UserDataContainer"<<std::endl;
|
||||
result = osg::clone(itr->second.get(), osg::CopyOp::DEEP_COPY_ALL);
|
||||
assign(node->getOrCreateUserDataContainer(), itr->second.get());
|
||||
}
|
||||
else if (itr!=_keyFrameMap.end())
|
||||
{
|
||||
@@ -186,51 +191,95 @@ void PropertyAnimation::update()
|
||||
}
|
||||
else
|
||||
{
|
||||
r1 = (time - itr_1->first)/delta_time;
|
||||
r2 = 1.0-r1;
|
||||
r2 = (time - itr_1->first)/delta_time;
|
||||
r1 = 1.0-r2;
|
||||
}
|
||||
|
||||
osg::UserDataContainer* p1 = itr_1->second.get();
|
||||
osg::UserDataContainer* p2 = itr_2->second.get();
|
||||
|
||||
// clone all the properties from p1;
|
||||
result = osg::clone(p1, osg::CopyOp::DEEP_COPY_ALL);
|
||||
|
||||
osg::ref_ptr<osg::UserDataContainer> destination = node->getOrCreateUserDataContainer();
|
||||
|
||||
assign(destination.get(), p1);
|
||||
|
||||
for(unsigned int i2=0; i2<p2->getNumUserObjects(); ++i2)
|
||||
{
|
||||
osg::Object* obj_2 = p2->getUserObject(i2);
|
||||
unsigned int i1 = result->getUserObjectIndex(obj_2->getName());
|
||||
if (i1<result->getNumUserObjects())
|
||||
unsigned int i1 = p1->getUserObjectIndex(obj_2->getName());
|
||||
if (i1<p1->getNumUserObjects())
|
||||
{
|
||||
osg::Object* obj_1 = result->getUserObject(i1);
|
||||
osg::Object* obj_1 = p1->getUserObject(i1);
|
||||
osg::ValueObject* valueobject_1 = dynamic_cast<osg::ValueObject*>(obj_1);
|
||||
osg::ValueObject* valueobject_2 = dynamic_cast<osg::ValueObject*>(obj_2);
|
||||
if (valueobject_1)
|
||||
if (valueobject_1 && valueobject_2)
|
||||
{
|
||||
osg::ref_ptr<osg::ValueObject> vo = osg::clone(valueobject_1);
|
||||
MySetValueVisitor mySetValue(r1, r2, valueobject_2);
|
||||
valueobject_1->set(mySetValue);
|
||||
vo->set(mySetValue);
|
||||
assign(destination.get(), vo.get());
|
||||
}
|
||||
else if (obj_1)
|
||||
{
|
||||
assign(destination.get(), obj_1);
|
||||
}
|
||||
else if (obj_2)
|
||||
{
|
||||
assign(destination.get(), obj_2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// need to insert property;
|
||||
result->addUserObject(obj_2->clone(osg::CopyOp::DEEP_COPY_ALL));
|
||||
assign(destination.get(), obj_2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
OSG_NOTICE<<"PropertyAnimation::update() : Need to interpolate between two UserDataContainer, r1="<<r1<<", r2="<<r2<<std::endl;
|
||||
|
||||
}
|
||||
else // (itr==_keyFrameMap.end())
|
||||
{
|
||||
OSG_NOTICE<<"PropertyAnimation::update() : copy last UserDataContainer"<<std::endl;
|
||||
result = osg::clone(_keyFrameMap.rbegin()->second.get(), osg::CopyOp::DEEP_COPY_ALL);
|
||||
assign(node->getOrCreateUserDataContainer(), _keyFrameMap.rbegin()->second.get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PropertyAnimation::assign(osg::UserDataContainer* destination, osg::UserDataContainer* source)
|
||||
{
|
||||
if (!destination) return;
|
||||
if (!source) return;
|
||||
|
||||
for(unsigned int i=0; i<source->getNumUserObjects(); ++i)
|
||||
{
|
||||
assign(destination, source->getUserObject(i));
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyAnimation::assign(osg::UserDataContainer* udc, osg::Object* obj)
|
||||
{
|
||||
if (!obj) return;
|
||||
|
||||
unsigned int index = udc->getUserObjectIndex(obj);
|
||||
if (index != udc->getNumUserObjects())
|
||||
{
|
||||
OSG_NOTICE<<"Object already assigned to UserDataContainer"<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
index = udc->getUserObjectIndex(obj->getName());
|
||||
if (index != udc->getNumUserObjects())
|
||||
{
|
||||
OSG_NOTICE<<"Replacing object in UserDataContainer"<<std::endl;
|
||||
udc->setUserObject(index, obj);
|
||||
return;
|
||||
}
|
||||
|
||||
OSG_NOTICE<<"Assigned object to UserDataContainer"<<std::endl;
|
||||
udc->addUserObject(obj);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ImageSequenceUpdateCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||
|
||||
@@ -624,6 +624,29 @@ void SlideShowConstructor::layerClickEventOperation(const KeyPosition& keyPos, c
|
||||
}
|
||||
|
||||
|
||||
void SlideShowConstructor::addPropertyAnimation(PresentationContext presentationContext, PropertyAnimation* propertyAnimation)
|
||||
{
|
||||
switch(presentationContext)
|
||||
{
|
||||
case(CURRENT_PRESENTATION):
|
||||
OSG_NOTICE<<"Need to add PropertyAnimation to presentation."<<std::endl;
|
||||
if (!_presentationSwitch) createPresentation();
|
||||
if (_presentationSwitch.valid()) _presentationSwitch->addUpdateCallback(propertyAnimation);
|
||||
break;
|
||||
case(CURRENT_SLIDE):
|
||||
OSG_NOTICE<<"Need to add PropertyAnimation to slide."<<std::endl;
|
||||
if (!_slide) addSlide();
|
||||
if (_slide.valid()) _slide->addUpdateCallback(propertyAnimation);
|
||||
break;
|
||||
case(CURRENT_LAYER):
|
||||
OSG_NOTICE<<"Need to add PropertyAnimation to layer."<<std::endl;
|
||||
if (!_currentLayer) addLayer();
|
||||
if (_currentLayer.valid()) _currentLayer->addUpdateCallback(propertyAnimation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
osg::Node* SlideShowConstructor::decorateSubgraphForPosition(osg::Node* node, PositionData& positionData)
|
||||
{
|
||||
osg::Node* subgraph = node;
|
||||
|
||||
Reference in New Issue
Block a user