mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-15 09:39:53 +00:00
Refactor controllers, now part of the scene graph as UpdateCallbacks
Practical benefits: - The controller update is now run automatically - Creating an instance of a scene graph should now work properly using the defined copy constructors
This commit is contained in:
parent
af27a10b0c
commit
4957ceeb1d
@ -112,15 +112,11 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
//osgDB::writeNodeFile(*newNode, "out.osg");
|
//osgDB::writeNodeFile(*newNode, "out.osg");
|
||||||
|
|
||||||
std::vector<NifOsg::Controller > controllers;
|
|
||||||
osg::Group* newNode = new osg::Group;
|
osg::Group* newNode = new osg::Group;
|
||||||
NifOsg::Loader loader;
|
NifOsg::Loader loader;
|
||||||
loader.resourceManager = &resourceMgr;
|
loader.resourceManager = &resourceMgr;
|
||||||
loader.loadAsSkeleton(nif, newNode);
|
loader.loadAsSkeleton(nif, newNode);
|
||||||
|
|
||||||
for (unsigned int i=0; i<loader.mControllers.size(); ++i)
|
|
||||||
controllers.push_back(loader.mControllers[i]);
|
|
||||||
|
|
||||||
osg::PositionAttitudeTransform* trans = new osg::PositionAttitudeTransform;
|
osg::PositionAttitudeTransform* trans = new osg::PositionAttitudeTransform;
|
||||||
root->addChild(trans);
|
root->addChild(trans);
|
||||||
|
|
||||||
@ -137,6 +133,8 @@ int main(int argc, char** argv)
|
|||||||
viewer.setCameraManipulator(new osgGA::TrackballManipulator());
|
viewer.setCameraManipulator(new osgGA::TrackballManipulator());
|
||||||
viewer.addEventHandler(new WireframeKeyHandler(root));
|
viewer.addEventHandler(new WireframeKeyHandler(root));
|
||||||
|
|
||||||
|
//viewer.getCamera()->setCullMask()
|
||||||
|
|
||||||
// We're going to change this from the event callback, set the variance to DYNAMIC so that
|
// We're going to change this from the event callback, set the variance to DYNAMIC so that
|
||||||
// we don't interfere with the draw thread.
|
// we don't interfere with the draw thread.
|
||||||
root->getOrCreateStateSet()->setDataVariance(osg::Node::DYNAMIC);
|
root->getOrCreateStateSet()->setDataVariance(osg::Node::DYNAMIC);
|
||||||
@ -148,9 +146,6 @@ int main(int argc, char** argv)
|
|||||||
//trans->setAttitude(osg::Quat(viewer.getFrameStamp()->getSimulationTime()*5, osg::Vec3f(0,0,1)));
|
//trans->setAttitude(osg::Quat(viewer.getFrameStamp()->getSimulationTime()*5, osg::Vec3f(0,0,1)));
|
||||||
|
|
||||||
viewer.frame();
|
viewer.frame();
|
||||||
|
|
||||||
for (unsigned int i=0; i<controllers.size(); ++i)
|
|
||||||
controllers[i].update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -30,7 +30,7 @@ void CSVRender::UnpagedWorldspaceWidget::update()
|
|||||||
|
|
||||||
/// \todo deal with mSunlight and mFog/mForDensity
|
/// \todo deal with mSunlight and mFog/mForDensity
|
||||||
|
|
||||||
//flagAsModified();
|
flagAsModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document, QWidget* parent)
|
CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document, QWidget* parent)
|
||||||
|
@ -48,7 +48,53 @@ float ControllerFunction::calculate(float value)
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Quat KeyframeControllerValue::interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time)
|
FrameTimeSource::FrameTimeSource()
|
||||||
|
: mLastTime(0.0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float FrameTimeSource::getValue(osg::NodeVisitor *nv)
|
||||||
|
{
|
||||||
|
// TODO: dt could be computed globally instead of once per instance
|
||||||
|
double time = nv->getFrameStamp()->getReferenceTime();
|
||||||
|
float dt = static_cast<float>(time - mLastTime);
|
||||||
|
mLastTime = time;
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyframeController::KeyframeController()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyframeController::KeyframeController(const KeyframeController ©, const osg::CopyOp ©op)
|
||||||
|
: osg::NodeCallback(copy, copyop)
|
||||||
|
, Controller(copy)
|
||||||
|
, mRotations(copy.mRotations)
|
||||||
|
, mXRotations(copy.mXRotations)
|
||||||
|
, mYRotations(copy.mYRotations)
|
||||||
|
, mZRotations(copy.mZRotations)
|
||||||
|
, mTranslations(copy.mTranslations)
|
||||||
|
, mScales(copy.mScales)
|
||||||
|
, mNif(copy.mNif)
|
||||||
|
, mInitialQuat(copy.mInitialQuat)
|
||||||
|
, mInitialScale(copy.mInitialScale)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyframeController::KeyframeController(const Nif::NIFFilePtr &nif, const Nif::NiKeyframeData *data,
|
||||||
|
osg::Quat initialQuat, float initialScale)
|
||||||
|
: mRotations(&data->mRotations)
|
||||||
|
, mXRotations(&data->mXRotations)
|
||||||
|
, mYRotations(&data->mYRotations)
|
||||||
|
, mZRotations(&data->mZRotations)
|
||||||
|
, mTranslations(&data->mTranslations)
|
||||||
|
, mScales(&data->mScales)
|
||||||
|
, mNif(nif)
|
||||||
|
, mInitialQuat(initialQuat)
|
||||||
|
, mInitialScale(initialScale)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
osg::Quat KeyframeController::interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time)
|
||||||
{
|
{
|
||||||
if(time <= keys.begin()->first)
|
if(time <= keys.begin()->first)
|
||||||
return keys.begin()->second.mValue;
|
return keys.begin()->second.mValue;
|
||||||
@ -81,7 +127,7 @@ osg::Quat KeyframeControllerValue::interpKey(const Nif::QuaternionKeyMap::MapTyp
|
|||||||
return keys.rbegin()->second.mValue;
|
return keys.rbegin()->second.mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Quat KeyframeControllerValue::getXYZRotation(float time) const
|
osg::Quat KeyframeController::getXYZRotation(float time) const
|
||||||
{
|
{
|
||||||
float xrot = interpKey(mXRotations->mKeys, time);
|
float xrot = interpKey(mXRotations->mKeys, time);
|
||||||
float yrot = interpKey(mYRotations->mKeys, time);
|
float yrot = interpKey(mYRotations->mKeys, time);
|
||||||
@ -92,96 +138,108 @@ osg::Quat KeyframeControllerValue::getXYZRotation(float time) const
|
|||||||
return (zr*yr*xr);
|
return (zr*yr*xr);
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyframeControllerValue::KeyframeControllerValue(osg::Node *target, const Nif::NIFFilePtr &nif, const Nif::NiKeyframeData *data,
|
osg::Vec3f KeyframeController::getTranslation(float time) const
|
||||||
osg::Quat initialQuat, float initialScale)
|
|
||||||
: NodeTargetValue(target)
|
|
||||||
, mRotations(&data->mRotations)
|
|
||||||
, mXRotations(&data->mXRotations)
|
|
||||||
, mYRotations(&data->mYRotations)
|
|
||||||
, mZRotations(&data->mZRotations)
|
|
||||||
, mTranslations(&data->mTranslations)
|
|
||||||
, mScales(&data->mScales)
|
|
||||||
, mNif(nif)
|
|
||||||
, mInitialQuat(initialQuat)
|
|
||||||
, mInitialScale(initialScale)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
osg::Vec3f KeyframeControllerValue::getTranslation(float time) const
|
|
||||||
{
|
{
|
||||||
if(mTranslations->mKeys.size() > 0)
|
if(mTranslations->mKeys.size() > 0)
|
||||||
return interpKey(mTranslations->mKeys, time);
|
return interpKey(mTranslations->mKeys, time);
|
||||||
osg::MatrixTransform* trans = static_cast<osg::MatrixTransform*>(mNode);
|
return osg::Vec3f();
|
||||||
return trans->getMatrix().getTrans();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyframeControllerValue::setValue(float time)
|
void KeyframeController::operator() (osg::Node* node, osg::NodeVisitor* nv)
|
||||||
{
|
{
|
||||||
osg::MatrixTransform* trans = static_cast<osg::MatrixTransform*>(mNode);
|
if (hasInput())
|
||||||
osg::Matrix mat = trans->getMatrix();
|
|
||||||
|
|
||||||
if(mRotations->mKeys.size() > 0)
|
|
||||||
mat.setRotate(interpKey(mRotations->mKeys, time));
|
|
||||||
else if (!mXRotations->mKeys.empty() || !mYRotations->mKeys.empty() || !mZRotations->mKeys.empty())
|
|
||||||
mat.setRotate(getXYZRotation(time));
|
|
||||||
else
|
|
||||||
mat.setRotate(mInitialQuat);
|
|
||||||
|
|
||||||
// let's hope no one's using multiple KeyframeControllers on the same node (not that would make any sense...)
|
|
||||||
float scale = mInitialScale;
|
|
||||||
if(mScales->mKeys.size() > 0)
|
|
||||||
scale = interpKey(mScales->mKeys, time);
|
|
||||||
|
|
||||||
for (int i=0;i<3;++i)
|
|
||||||
for (int j=0;j<3;++j)
|
|
||||||
mat(i,j) *= scale;
|
|
||||||
|
|
||||||
if(mTranslations->mKeys.size() > 0)
|
|
||||||
mat.setTrans(interpKey(mTranslations->mKeys, time));
|
|
||||||
trans->setMatrix(mat);
|
|
||||||
}
|
|
||||||
|
|
||||||
Controller::Controller(boost::shared_ptr<ControllerSource> src, boost::shared_ptr<ControllerValue> dest, boost::shared_ptr<ControllerFunction> function)
|
|
||||||
: mSource(src)
|
|
||||||
, mDestValue(dest)
|
|
||||||
, mFunction(function)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Controller::update()
|
|
||||||
{
|
|
||||||
if (mSource.get())
|
|
||||||
{
|
{
|
||||||
mDestValue->setValue(mFunction->calculate(mSource->getValue()));
|
osg::MatrixTransform* trans = static_cast<osg::MatrixTransform*>(node);
|
||||||
|
osg::Matrix mat = trans->getMatrix();
|
||||||
|
|
||||||
|
float time = getInputValue(nv);
|
||||||
|
|
||||||
|
if(mRotations->mKeys.size() > 0)
|
||||||
|
mat.setRotate(interpKey(mRotations->mKeys, time));
|
||||||
|
else if (!mXRotations->mKeys.empty() || !mYRotations->mKeys.empty() || !mZRotations->mKeys.empty())
|
||||||
|
mat.setRotate(getXYZRotation(time));
|
||||||
|
else
|
||||||
|
mat.setRotate(mInitialQuat);
|
||||||
|
|
||||||
|
// let's hope no one's using multiple KeyframeControllers on the same node (not that would make any sense...)
|
||||||
|
float scale = mInitialScale;
|
||||||
|
if(mScales->mKeys.size() > 0)
|
||||||
|
scale = interpKey(mScales->mKeys, time);
|
||||||
|
|
||||||
|
for (int i=0;i<3;++i)
|
||||||
|
for (int j=0;j<3;++j)
|
||||||
|
mat(i,j) *= scale;
|
||||||
|
|
||||||
|
if(mTranslations->mKeys.size() > 0)
|
||||||
|
mat.setTrans(interpKey(mTranslations->mKeys, time));
|
||||||
|
trans->setMatrix(mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(node, nv);
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller::Controller()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Controller::hasInput() const
|
||||||
|
{
|
||||||
|
return mSource.get() != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Controller::getInputValue(osg::NodeVisitor* nv)
|
||||||
|
{
|
||||||
|
return mFunction->calculate(mSource->getValue(nv));
|
||||||
|
}
|
||||||
|
|
||||||
|
GeomMorpherController::GeomMorpherController()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GeomMorpherController::GeomMorpherController(const GeomMorpherController ©, const osg::CopyOp ©op)
|
||||||
|
: osg::Drawable::UpdateCallback(copy, copyop)
|
||||||
|
, Controller(copy)
|
||||||
|
, mMorphs(copy.mMorphs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GeomMorpherController::GeomMorpherController(const Nif::NiMorphData *data)
|
||||||
|
: mMorphs(data->mMorphs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable)
|
||||||
|
{
|
||||||
|
osgAnimation::MorphGeometry* morphGeom = dynamic_cast<osgAnimation::MorphGeometry*>(drawable);
|
||||||
|
if (morphGeom)
|
||||||
|
{
|
||||||
|
if (hasInput())
|
||||||
|
{
|
||||||
|
if (mMorphs.size() <= 1)
|
||||||
|
return;
|
||||||
|
float input = getInputValue(nv);
|
||||||
|
int i = 0;
|
||||||
|
for (std::vector<Nif::NiMorphData::MorphData>::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i)
|
||||||
|
{
|
||||||
|
float val = 0;
|
||||||
|
if (!it->mData.mKeys.empty())
|
||||||
|
val = interpKey(it->mData.mKeys, input);
|
||||||
|
val = std::max(0.f, std::min(1.f, val));
|
||||||
|
|
||||||
|
morphGeom->setWeight(i, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
morphGeom->transformSoftwareMethod();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GeomMorpherControllerValue::GeomMorpherControllerValue(osgAnimation::MorphGeometry *geom, const Nif::NiMorphData* morphData)
|
UVController::UVController()
|
||||||
: mGeom(geom)
|
|
||||||
, mMorphs(morphData->mMorphs)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeomMorpherControllerValue::setValue(float time)
|
UVController::UVController(const Nif::NiUVData *data, std::set<int> textureUnits)
|
||||||
{
|
: mUTrans(data->mKeyList[0])
|
||||||
if (mMorphs.size() <= 1)
|
|
||||||
return;
|
|
||||||
int i = 0;
|
|
||||||
for (std::vector<Nif::NiMorphData::MorphData>::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i)
|
|
||||||
{
|
|
||||||
float val = 0;
|
|
||||||
if (!it->mData.mKeys.empty())
|
|
||||||
val = interpKey(it->mData.mKeys, time);
|
|
||||||
val = std::max(0.f, std::min(1.f, val));
|
|
||||||
|
|
||||||
mGeom->setWeight(i, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UVControllerValue::UVControllerValue(osg::StateSet *target, const Nif::NiUVData *data, std::set<int> textureUnits)
|
|
||||||
: mStateSet(target)
|
|
||||||
, mUTrans(data->mKeyList[0])
|
|
||||||
, mVTrans(data->mKeyList[1])
|
, mVTrans(data->mKeyList[1])
|
||||||
, mUScale(data->mKeyList[2])
|
, mUScale(data->mKeyList[2])
|
||||||
, mVScale(data->mKeyList[3])
|
, mVScale(data->mKeyList[3])
|
||||||
@ -189,26 +247,58 @@ UVControllerValue::UVControllerValue(osg::StateSet *target, const Nif::NiUVData
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void UVControllerValue::setValue(float value)
|
UVController::UVController(const UVController& copy, const osg::CopyOp& copyop)
|
||||||
|
: osg::Object(copy, copyop), osg::NodeCallback(copy, copyop), Controller(copy)
|
||||||
|
, mUTrans(copy.mUTrans)
|
||||||
|
, mVTrans(copy.mVTrans)
|
||||||
|
, mUScale(copy.mUScale)
|
||||||
|
, mVScale(copy.mVScale)
|
||||||
|
, mTextureUnits(copy.mTextureUnits)
|
||||||
{
|
{
|
||||||
float uTrans = interpKey(mUTrans.mKeys, value, 0.0f);
|
|
||||||
float vTrans = interpKey(mVTrans.mKeys, value, 0.0f);
|
|
||||||
float uScale = interpKey(mUScale.mKeys, value, 1.0f);
|
|
||||||
float vScale = interpKey(mVScale.mKeys, value, 1.0f);
|
|
||||||
|
|
||||||
osg::Matrixf mat = osg::Matrixf::scale(uScale, vScale, 1);
|
|
||||||
mat.setTrans(uTrans, vTrans, 0);
|
|
||||||
|
|
||||||
osg::TexMat* texMat = new osg::TexMat;
|
|
||||||
texMat->setMatrix(mat);
|
|
||||||
|
|
||||||
for (std::set<int>::const_iterator it = mTextureUnits.begin(); it != mTextureUnits.end(); ++it)
|
|
||||||
{
|
|
||||||
mStateSet->setTextureAttributeAndModes(*it, texMat, osg::StateAttribute::ON);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VisControllerValue::calculate(float time) const
|
void UVController::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
|
{
|
||||||
|
if (hasInput())
|
||||||
|
{
|
||||||
|
osg::StateSet* stateset = node->getStateSet();
|
||||||
|
float value = getInputValue(nv);
|
||||||
|
float uTrans = interpKey(mUTrans.mKeys, value, 0.0f);
|
||||||
|
float vTrans = interpKey(mVTrans.mKeys, value, 0.0f);
|
||||||
|
float uScale = interpKey(mUScale.mKeys, value, 1.0f);
|
||||||
|
float vScale = interpKey(mVScale.mKeys, value, 1.0f);
|
||||||
|
|
||||||
|
osg::Matrixf mat = osg::Matrixf::scale(uScale, vScale, 1);
|
||||||
|
mat.setTrans(uTrans, vTrans, 0);
|
||||||
|
|
||||||
|
osg::TexMat* texMat = new osg::TexMat;
|
||||||
|
texMat->setMatrix(mat);
|
||||||
|
|
||||||
|
for (std::set<int>::const_iterator it = mTextureUnits.begin(); it != mTextureUnits.end(); ++it)
|
||||||
|
{
|
||||||
|
stateset->setTextureAttributeAndModes(*it, texMat, osg::StateAttribute::ON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traverse(node, nv);
|
||||||
|
}
|
||||||
|
|
||||||
|
VisController::VisController(const Nif::NiVisData *data)
|
||||||
|
: mData(data->mVis)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
VisController::VisController()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
VisController::VisController(const VisController ©, const osg::CopyOp ©op)
|
||||||
|
: osg::NodeCallback(copy, copyop)
|
||||||
|
, Controller(copy)
|
||||||
|
, mData(copy.mData)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VisController::calculate(float time) const
|
||||||
{
|
{
|
||||||
if(mData.size() == 0)
|
if(mData.size() == 0)
|
||||||
return true;
|
return true;
|
||||||
@ -221,77 +311,140 @@ bool VisControllerValue::calculate(float time) const
|
|||||||
return mData.back().isSet;
|
return mData.back().isSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisControllerValue::setValue(float time)
|
void VisController::operator() (osg::Node* node, osg::NodeVisitor* nv)
|
||||||
{
|
{
|
||||||
bool vis = calculate(time);
|
if (hasInput())
|
||||||
mNode->setNodeMask(vis ? ~0 : 0);
|
{
|
||||||
|
bool vis = calculate(getInputValue(nv));
|
||||||
|
node->setNodeMask(vis ? ~0 : 0);
|
||||||
|
}
|
||||||
|
traverse(node, nv);
|
||||||
}
|
}
|
||||||
|
|
||||||
AlphaControllerValue::AlphaControllerValue(osg::StateSet *target, const Nif::NiFloatData *data)
|
AlphaController::AlphaController(const Nif::NiFloatData *data)
|
||||||
: mTarget(target)
|
: mData(data->mKeyList)
|
||||||
, mData(data->mKeyList)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void AlphaControllerValue::setValue(float time)
|
|
||||||
{
|
|
||||||
float value = interpKey(mData.mKeys, time);
|
|
||||||
osg::Material* mat = dynamic_cast<osg::Material*>(mTarget->getAttribute(osg::StateAttribute::MATERIAL));
|
|
||||||
if (!mat)
|
|
||||||
return;
|
|
||||||
|
|
||||||
osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);
|
|
||||||
diffuse.a() = value;
|
|
||||||
mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialColorControllerValue::MaterialColorControllerValue(osg::StateSet *target, const Nif::NiPosData *data)
|
|
||||||
: mTarget(target), mData(data->mKeyList)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialColorControllerValue::setValue(float time)
|
AlphaController::AlphaController()
|
||||||
{
|
{
|
||||||
osg::Vec3f value = interpKey(mData.mKeys, time);
|
|
||||||
osg::Material* mat = dynamic_cast<osg::Material*>(mTarget->getAttribute(osg::StateAttribute::MATERIAL));
|
|
||||||
if (!mat)
|
|
||||||
return;
|
|
||||||
|
|
||||||
osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);
|
|
||||||
diffuse.set(value.x(), value.y(), value.z(), diffuse.a());
|
|
||||||
mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FlipControllerValue::FlipControllerValue(osg::StateSet* target, const Nif::NiFlipController *ctrl,
|
AlphaController::AlphaController(const AlphaController ©, const osg::CopyOp ©op)
|
||||||
std::vector<osg::ref_ptr<osg::Image> > textures)
|
: osg::NodeCallback(copy, copyop), ValueInterpolator(), Controller(copy)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlphaController::operator () (osg::Node* node, osg::NodeVisitor* nv)
|
||||||
|
{
|
||||||
|
if (hasInput())
|
||||||
|
{
|
||||||
|
osg::StateSet* stateset = node->getStateSet();
|
||||||
|
float value = interpKey(mData.mKeys, getInputValue(nv));
|
||||||
|
osg::Material* mat = dynamic_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
|
||||||
|
if (mat)
|
||||||
|
{
|
||||||
|
osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);
|
||||||
|
diffuse.a() = value;
|
||||||
|
mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traverse(node, nv);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialColorController::MaterialColorController(const Nif::NiPosData *data)
|
||||||
|
: mData(data->mKeyList)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialColorController::MaterialColorController()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialColorController::MaterialColorController(const MaterialColorController ©, const osg::CopyOp ©op)
|
||||||
|
: osg::NodeCallback(copy, copyop), Controller(copy)
|
||||||
|
, mData(copy.mData)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialColorController::operator() (osg::Node* node, osg::NodeVisitor* nv)
|
||||||
|
{
|
||||||
|
if (hasInput())
|
||||||
|
{
|
||||||
|
osg::StateSet* stateset = node->getStateSet();
|
||||||
|
osg::Vec3f value = interpKey(mData.mKeys, getInputValue(nv));
|
||||||
|
osg::Material* mat = dynamic_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
|
||||||
|
if (mat)
|
||||||
|
{
|
||||||
|
osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);
|
||||||
|
diffuse.set(value.x(), value.y(), value.z(), diffuse.a());
|
||||||
|
mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traverse(node, nv);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlipController::FlipController(const Nif::NiFlipController *ctrl, std::vector<osg::ref_ptr<osg::Image> > textures)
|
||||||
: mTexSlot(ctrl->mTexSlot)
|
: mTexSlot(ctrl->mTexSlot)
|
||||||
, mDelta(ctrl->mDelta)
|
, mDelta(ctrl->mDelta)
|
||||||
, mTextures(textures)
|
, mTextures(textures)
|
||||||
, mTarget(target)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlipControllerValue::setValue(float time)
|
FlipController::FlipController()
|
||||||
{
|
|
||||||
if (mDelta == 0)
|
|
||||||
return;
|
|
||||||
int curTexture = int(time / mDelta) % mTextures.size();
|
|
||||||
osg::Texture2D* tex = dynamic_cast<osg::Texture2D*>(mTarget->getAttribute(osg::StateAttribute::TEXTURE));
|
|
||||||
if (!tex)
|
|
||||||
return;
|
|
||||||
tex->setImage(mTextures[curTexture].get());
|
|
||||||
}
|
|
||||||
|
|
||||||
ParticleSystemControllerValue::ParticleSystemControllerValue(osgParticle::Emitter *emitter, const Nif::NiParticleSystemController *ctrl)
|
|
||||||
: mEmitter(emitter), mEmitStart(ctrl->startTime), mEmitStop(ctrl->stopTime)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParticleSystemControllerValue::setValue(float time)
|
FlipController::FlipController(const FlipController ©, const osg::CopyOp ©op)
|
||||||
|
: osg::NodeCallback(copy, copyop)
|
||||||
|
, Controller(copy)
|
||||||
|
, mTexSlot(copy.mTexSlot)
|
||||||
|
, mDelta(copy.mDelta)
|
||||||
|
, mTextures(copy.mTextures)
|
||||||
{
|
{
|
||||||
mEmitter->setEnabled(time >= mEmitStart && time < mEmitStop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FlipController::operator() (osg::Node* node, osg::NodeVisitor* nv)
|
||||||
|
{
|
||||||
|
if (hasInput() && mDelta != 0)
|
||||||
|
{
|
||||||
|
osg::StateSet* stateset = node->getStateSet();
|
||||||
|
int curTexture = int(getInputValue(nv) / mDelta) % mTextures.size();
|
||||||
|
osg::Texture2D* tex = dynamic_cast<osg::Texture2D*>(stateset->getAttribute(osg::StateAttribute::TEXTURE));
|
||||||
|
if (tex)
|
||||||
|
tex->setImage(mTextures[curTexture].get());
|
||||||
|
}
|
||||||
|
traverse(node, nv);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticleSystemController::ParticleSystemController(const Nif::NiParticleSystemController *ctrl)
|
||||||
|
: mEmitStart(ctrl->startTime), mEmitStop(ctrl->stopTime)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticleSystemController::ParticleSystemController()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticleSystemController::ParticleSystemController(const ParticleSystemController ©, const osg::CopyOp ©op)
|
||||||
|
: osg::NodeCallback(copy, copyop)
|
||||||
|
, Controller(copy)
|
||||||
|
, mEmitStart(copy.mEmitStart)
|
||||||
|
, mEmitStop(copy.mEmitStop)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystemController::operator() (osg::Node* node, osg::NodeVisitor* nv)
|
||||||
|
{
|
||||||
|
if (hasInput())
|
||||||
|
{
|
||||||
|
osgParticle::ParticleProcessor* emitter = dynamic_cast<osgParticle::ParticleProcessor*>(node);
|
||||||
|
float time = getInputValue(nv);
|
||||||
|
if (emitter)
|
||||||
|
emitter->setEnabled(time >= mEmitStart && time < mEmitStop);
|
||||||
|
}
|
||||||
|
traverse(node, nv);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
#include <osg/Timer>
|
#include <osg/Timer>
|
||||||
|
#include <osg/StateSet>
|
||||||
|
#include <osg/NodeCallback>
|
||||||
|
#include <osg/Drawable>
|
||||||
|
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
@ -93,76 +96,49 @@ namespace NifOsg
|
|||||||
class ControllerSource
|
class ControllerSource
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual float getValue() const = 0;
|
virtual float getValue(osg::NodeVisitor* nv) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: Should return a dt instead of time
|
|
||||||
class FrameTimeSource : public ControllerSource
|
class FrameTimeSource : public ControllerSource
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
FrameTimeSource();
|
||||||
virtual float getValue() const
|
virtual float getValue(osg::NodeVisitor* nv);
|
||||||
{
|
|
||||||
return mTimer.time_s();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
osg::Timer mTimer;
|
double mLastTime;
|
||||||
};
|
|
||||||
|
|
||||||
class ControllerValue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual void setValue(float value) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Controller
|
class Controller
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Controller (boost::shared_ptr<ControllerSource> src, boost::shared_ptr<ControllerValue> dest,
|
Controller();
|
||||||
boost::shared_ptr<ControllerFunction> function);
|
|
||||||
|
|
||||||
virtual void update();
|
bool hasInput() const;
|
||||||
|
|
||||||
|
float getInputValue(osg::NodeVisitor* nv);
|
||||||
|
|
||||||
boost::shared_ptr<ControllerSource> mSource;
|
boost::shared_ptr<ControllerSource> mSource;
|
||||||
boost::shared_ptr<ControllerValue> mDestValue;
|
|
||||||
|
|
||||||
// The source value gets passed through this function before it's passed on to the DestValue.
|
// The source value gets passed through this function before it's passed on to the DestValue.
|
||||||
boost::shared_ptr<ControllerFunction> mFunction;
|
boost::shared_ptr<ControllerFunction> mFunction;
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: Should be with other general extensions.
|
class GeomMorpherController : public osg::Drawable::UpdateCallback, public Controller, public ValueInterpolator
|
||||||
class NodeTargetValue : public ControllerValue
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
// TODO: get rid of target pointers, which are incompatible with a copy constructor we will need later
|
|
||||||
// instead, controllers can be a Node added as child of their target
|
|
||||||
osg::Node *mNode;
|
|
||||||
|
|
||||||
public:
|
|
||||||
NodeTargetValue(osg::Node *target) : mNode(target)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
virtual osg::Vec3f getTranslation(float value) const = 0;
|
|
||||||
|
|
||||||
osg::Node *getNode() const
|
|
||||||
{ return mNode; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class GeomMorpherControllerValue : public ControllerValue, public ValueInterpolator
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// FIXME: don't copy the morph data?
|
GeomMorpherController(const Nif::NiMorphData* data);
|
||||||
GeomMorpherControllerValue(osgAnimation::MorphGeometry* geom, const Nif::NiMorphData *data);
|
GeomMorpherController();
|
||||||
|
GeomMorpherController(const GeomMorpherController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
virtual void setValue(float time);
|
META_Object(NifOsg, GeomMorpherController)
|
||||||
|
|
||||||
|
virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
osgAnimation::MorphGeometry* mGeom;
|
|
||||||
std::vector<Nif::NiMorphData::MorphData> mMorphs;
|
std::vector<Nif::NiMorphData::MorphData> mMorphs;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeyframeControllerValue : public NodeTargetValue, public ValueInterpolator
|
class KeyframeController : public osg::NodeCallback, public Controller, public ValueInterpolator
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const Nif::QuaternionKeyMap* mRotations;
|
const Nif::QuaternionKeyMap* mRotations;
|
||||||
@ -185,31 +161,39 @@ namespace NifOsg
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
/// @note The NiKeyFrameData must be valid as long as this KeyframeController exists.
|
/// @note The NiKeyFrameData must be valid as long as this KeyframeController exists.
|
||||||
KeyframeControllerValue(osg::Node *target, const Nif::NIFFilePtr& nif, const Nif::NiKeyframeData *data,
|
KeyframeController(const Nif::NIFFilePtr& nif, const Nif::NiKeyframeData *data,
|
||||||
osg::Quat initialQuat, float initialScale);
|
osg::Quat initialQuat, float initialScale);
|
||||||
|
KeyframeController();
|
||||||
|
KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
|
META_Object(NifOsg, KeyframeController)
|
||||||
|
|
||||||
virtual osg::Vec3f getTranslation(float time) const;
|
virtual osg::Vec3f getTranslation(float time) const;
|
||||||
|
|
||||||
virtual void setValue(float time);
|
virtual void operator() (osg::Node*, osg::NodeVisitor*);
|
||||||
};
|
};
|
||||||
|
|
||||||
class UVControllerValue : public ControllerValue, ValueInterpolator
|
// Note we're using NodeCallback instead of StateSet::Callback because the StateSet callback doesn't support nesting
|
||||||
|
struct UVController : public osg::NodeCallback, public Controller, public ValueInterpolator
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
UVController();
|
||||||
|
UVController(const UVController&,const osg::CopyOp& = osg::CopyOp::SHALLOW_COPY);
|
||||||
|
UVController(const Nif::NiUVData *data, std::set<int> textureUnits);
|
||||||
|
|
||||||
|
META_Object(NifOsg,UVController)
|
||||||
|
|
||||||
|
virtual void operator() (osg::Node*, osg::NodeVisitor*);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
osg::StateSet* mStateSet;
|
|
||||||
Nif::FloatKeyMap mUTrans;
|
Nif::FloatKeyMap mUTrans;
|
||||||
Nif::FloatKeyMap mVTrans;
|
Nif::FloatKeyMap mVTrans;
|
||||||
Nif::FloatKeyMap mUScale;
|
Nif::FloatKeyMap mUScale;
|
||||||
Nif::FloatKeyMap mVScale;
|
Nif::FloatKeyMap mVScale;
|
||||||
std::set<int> mTextureUnits;
|
std::set<int> mTextureUnits;
|
||||||
|
|
||||||
public:
|
|
||||||
UVControllerValue(osg::StateSet* target, const Nif::NiUVData *data, std::set<int> textureUnits);
|
|
||||||
|
|
||||||
virtual void setValue(float value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class VisControllerValue : public NodeTargetValue
|
class VisController : public osg::NodeCallback, public Controller
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::vector<Nif::NiVisData::VisData> mData;
|
std::vector<Nif::NiVisData::VisData> mData;
|
||||||
@ -217,65 +201,75 @@ namespace NifOsg
|
|||||||
bool calculate(float time) const;
|
bool calculate(float time) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VisControllerValue(osg::Node *target, const Nif::NiVisData *data)
|
VisController(const Nif::NiVisData *data);
|
||||||
: NodeTargetValue(target)
|
VisController();
|
||||||
, mData(data->mVis)
|
VisController(const VisController& copy, const osg::CopyOp& copyop);
|
||||||
{ }
|
|
||||||
|
|
||||||
virtual osg::Vec3f getTranslation(float time) const
|
META_Object(NifOsg, VisController)
|
||||||
{ return osg::Vec3f(); }
|
|
||||||
|
|
||||||
virtual void setValue(float time);
|
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv);
|
||||||
};
|
};
|
||||||
|
|
||||||
class AlphaControllerValue : public ControllerValue, public ValueInterpolator
|
class AlphaController : public osg::NodeCallback, public Controller, public ValueInterpolator
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
osg::StateSet* mTarget;
|
|
||||||
Nif::FloatKeyMap mData;
|
Nif::FloatKeyMap mData;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AlphaControllerValue(osg::StateSet* target, const Nif::NiFloatData *data);
|
AlphaController(const Nif::NiFloatData *data);
|
||||||
|
AlphaController();
|
||||||
|
AlphaController(const AlphaController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
virtual void setValue(float time);
|
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv);
|
||||||
|
|
||||||
|
META_Object(NifOsg, AlphaController)
|
||||||
};
|
};
|
||||||
|
|
||||||
class MaterialColorControllerValue : public ControllerValue, public ValueInterpolator
|
class MaterialColorController : public osg::NodeCallback, public Controller, public ValueInterpolator
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
osg::StateSet* mTarget;
|
|
||||||
Nif::Vector3KeyMap mData;
|
Nif::Vector3KeyMap mData;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MaterialColorControllerValue(osg::StateSet* target, const Nif::NiPosData *data);
|
MaterialColorController(const Nif::NiPosData *data);
|
||||||
|
MaterialColorController();
|
||||||
|
MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
virtual void setValue(float time);
|
META_Object(NifOsg, MaterialColorController)
|
||||||
|
|
||||||
|
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv);
|
||||||
};
|
};
|
||||||
|
|
||||||
// untested
|
// untested
|
||||||
class FlipControllerValue : public ControllerValue
|
class FlipController : public osg::NodeCallback, public Controller
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
osg::StateSet* mTarget;
|
|
||||||
int mTexSlot;
|
int mTexSlot;
|
||||||
float mDelta;
|
float mDelta;
|
||||||
std::vector<osg::ref_ptr<osg::Image> > mTextures;
|
std::vector<osg::ref_ptr<osg::Image> > mTextures;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FlipControllerValue(osg::StateSet* target, const Nif::NiFlipController* ctrl, std::vector<osg::ref_ptr<osg::Image> > textures);
|
FlipController(const Nif::NiFlipController* ctrl, std::vector<osg::ref_ptr<osg::Image> > textures);
|
||||||
|
FlipController();
|
||||||
|
FlipController(const FlipController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
virtual void setValue(float time);
|
META_Object(NifOsg, FlipController)
|
||||||
|
|
||||||
|
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ParticleSystemControllerValue : public ControllerValue
|
class ParticleSystemController : public osg::NodeCallback, public Controller
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ParticleSystemControllerValue(osgParticle::Emitter* emitter, const Nif::NiParticleSystemController* ctrl);
|
ParticleSystemController(const Nif::NiParticleSystemController* ctrl);
|
||||||
|
ParticleSystemController();
|
||||||
|
ParticleSystemController(const ParticleSystemController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
virtual void setValue(float time);
|
META_Object(NifOsg, ParticleSystemController)
|
||||||
|
|
||||||
|
virtual void operator() (osg::Node* node, osg::NodeVisitor* nv);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
osgParticle::Emitter* mEmitter;
|
|
||||||
float mEmitStart;
|
float mEmitStart;
|
||||||
float mEmitStop;
|
float mEmitStop;
|
||||||
};
|
};
|
||||||
|
@ -285,17 +285,13 @@ namespace NifOsg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::createController(const Nif::Controller *ctrl, boost::shared_ptr<ControllerValue> value, int animflags)
|
void Loader::setupController(const Nif::Controller* ctrl, Controller* toSetup, int animflags)
|
||||||
{
|
{
|
||||||
// FIXME animflags currently not passed to this function
|
|
||||||
//bool autoPlay = animflags & Nif::NiNode::AnimFlag_AutoPlay;
|
//bool autoPlay = animflags & Nif::NiNode::AnimFlag_AutoPlay;
|
||||||
boost::shared_ptr<ControllerSource> src(new FrameTimeSource); // if autoPlay
|
//if (autoPlay)
|
||||||
|
toSetup->mSource = boost::shared_ptr<ControllerSource>(new FrameTimeSource);
|
||||||
|
|
||||||
boost::shared_ptr<ControllerFunction> function (new ControllerFunction(ctrl
|
toSetup->mFunction = boost::shared_ptr<ControllerFunction>(new ControllerFunction(ctrl, 1 /*autoPlay*/));
|
||||||
, 0/*autoPlay*/));
|
|
||||||
//scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength);
|
|
||||||
|
|
||||||
mControllers.push_back(Controller(src, value, function));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton,
|
void Loader::handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton,
|
||||||
@ -308,7 +304,6 @@ namespace NifOsg
|
|||||||
transformNode = bone;
|
transformNode = bone;
|
||||||
bone->setMatrix(toMatrix(nifNode->trafo));
|
bone->setMatrix(toMatrix(nifNode->trafo));
|
||||||
bone->setName(nifNode->name);
|
bone->setName(nifNode->name);
|
||||||
bone->setUpdateCallback(new UpdateBone);
|
|
||||||
bone->setInvBindMatrixInSkeletonSpace(osg::Matrixf::inverse(getWorldTransform(nifNode)));
|
bone->setInvBindMatrixInSkeletonSpace(osg::Matrixf::inverse(getWorldTransform(nifNode)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -332,6 +327,7 @@ namespace NifOsg
|
|||||||
|
|
||||||
// We could probably skip hidden nodes entirely if they don't have a VisController that
|
// We could probably skip hidden nodes entirely if they don't have a VisController that
|
||||||
// might make them visible later
|
// might make them visible later
|
||||||
|
// FIXME: this disables update callbacks, so VisController no longer works
|
||||||
if (nifNode->flags & Nif::NiNode::Flag_Hidden)
|
if (nifNode->flags & Nif::NiNode::Flag_Hidden)
|
||||||
transformNode->setNodeMask(0);
|
transformNode->setNodeMask(0);
|
||||||
|
|
||||||
@ -358,6 +354,10 @@ namespace NifOsg
|
|||||||
if (!nifNode->controller.empty())
|
if (!nifNode->controller.empty())
|
||||||
handleNodeControllers(nifNode, transformNode, animflags);
|
handleNodeControllers(nifNode, transformNode, animflags);
|
||||||
|
|
||||||
|
// Added last so the changes from KeyframeControllers are taken into account
|
||||||
|
if (osgAnimation::Bone* bone = dynamic_cast<osgAnimation::Bone*>(transformNode.get()))
|
||||||
|
bone->addUpdateCallback(new UpdateBone);
|
||||||
|
|
||||||
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(nifNode);
|
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(nifNode);
|
||||||
if(ninode)
|
if(ninode)
|
||||||
{
|
{
|
||||||
@ -382,9 +382,12 @@ namespace NifOsg
|
|||||||
std::set<int> texUnits;
|
std::set<int> texUnits;
|
||||||
for (std::map<int, int>::const_iterator it = boundTextures.begin(); it != boundTextures.end(); ++it)
|
for (std::map<int, int>::const_iterator it = boundTextures.begin(); it != boundTextures.end(); ++it)
|
||||||
texUnits.insert(it->first);
|
texUnits.insert(it->first);
|
||||||
boost::shared_ptr<ControllerValue> dest(new UVControllerValue(transformNode->getOrCreateStateSet()
|
|
||||||
, uvctrl->data.getPtr(), texUnits));
|
osg::ref_ptr<UVController> ctrl = new UVController(uvctrl->data.getPtr(), texUnits);
|
||||||
createController(uvctrl, dest, animflags);
|
setupController(uvctrl, ctrl, animflags);
|
||||||
|
transformNode->getOrCreateStateSet()->setDataVariance(osg::StateSet::DYNAMIC);
|
||||||
|
|
||||||
|
transformNode->addUpdateCallback(ctrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -406,23 +409,27 @@ namespace NifOsg
|
|||||||
std::cerr << "Warning: multiple KeyframeControllers on the same node" << std::endl;
|
std::cerr << "Warning: multiple KeyframeControllers on the same node" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
boost::shared_ptr<ControllerValue> dest(new KeyframeControllerValue(transformNode, mNif, key->data.getPtr(),
|
osg::ref_ptr<KeyframeController> callback(new KeyframeController(mNif, key->data.getPtr(),
|
||||||
transformNode->getMatrix().getRotate(), nifNode->trafo.scale));
|
transformNode->getMatrix().getRotate(), nifNode->trafo.scale));
|
||||||
|
|
||||||
|
setupController(key, callback, animflags);
|
||||||
|
transformNode->addUpdateCallback(callback);
|
||||||
|
|
||||||
createController(key, dest, animflags);
|
|
||||||
seenKeyframeCtrl = true;
|
seenKeyframeCtrl = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ctrl->recType == Nif::RC_NiVisController)
|
else if (ctrl->recType == Nif::RC_NiVisController)
|
||||||
{
|
{
|
||||||
const Nif::NiVisController* visctrl = static_cast<const Nif::NiVisController*>(ctrl.getPtr());
|
const Nif::NiVisController* visctrl = static_cast<const Nif::NiVisController*>(ctrl.getPtr());
|
||||||
boost::shared_ptr<ControllerValue> dest(new VisControllerValue(transformNode, visctrl->data.getPtr()));
|
|
||||||
createController(visctrl, dest, animflags);
|
osg::ref_ptr<VisController> callback(new VisController(visctrl->data.getPtr()));
|
||||||
|
setupController(visctrl, callback, animflags);
|
||||||
|
transformNode->addUpdateCallback(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::handleMaterialControllers(const Nif::Property *materialProperty, osg::StateSet *stateset, int animflags)
|
void Loader::handleMaterialControllers(const Nif::Property *materialProperty, osg::Node* node, osg::StateSet *stateset, int animflags)
|
||||||
{
|
{
|
||||||
for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next)
|
for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next)
|
||||||
{
|
{
|
||||||
@ -431,21 +438,25 @@ namespace NifOsg
|
|||||||
if (ctrl->recType == Nif::RC_NiAlphaController)
|
if (ctrl->recType == Nif::RC_NiAlphaController)
|
||||||
{
|
{
|
||||||
const Nif::NiAlphaController* alphactrl = static_cast<const Nif::NiAlphaController*>(ctrl.getPtr());
|
const Nif::NiAlphaController* alphactrl = static_cast<const Nif::NiAlphaController*>(ctrl.getPtr());
|
||||||
boost::shared_ptr<ControllerValue> dest(new AlphaControllerValue(stateset, alphactrl->data.getPtr()));
|
osg::ref_ptr<AlphaController> ctrl(new AlphaController(alphactrl->data.getPtr()));
|
||||||
createController(alphactrl, dest, animflags);
|
setupController(alphactrl, ctrl, animflags);
|
||||||
|
stateset->setDataVariance(osg::StateSet::DYNAMIC);
|
||||||
|
node->addUpdateCallback(ctrl);
|
||||||
}
|
}
|
||||||
else if (ctrl->recType == Nif::RC_NiMaterialColorController)
|
else if (ctrl->recType == Nif::RC_NiMaterialColorController)
|
||||||
{
|
{
|
||||||
const Nif::NiMaterialColorController* matctrl = static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr());
|
const Nif::NiMaterialColorController* matctrl = static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr());
|
||||||
boost::shared_ptr<ControllerValue> dest(new MaterialColorControllerValue(stateset, matctrl->data.getPtr()));
|
osg::ref_ptr<MaterialColorController> ctrl(new MaterialColorController(matctrl->data.getPtr()));
|
||||||
createController(matctrl, dest, animflags);
|
setupController(matctrl, ctrl, animflags);
|
||||||
|
stateset->setDataVariance(osg::StateSet::DYNAMIC);
|
||||||
|
node->addUpdateCallback(ctrl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
std::cerr << "Unexpected material controller " << ctrl->recType << std::endl;
|
std::cerr << "Unexpected material controller " << ctrl->recType << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::handleTextureControllers(const Nif::Property *texProperty, osg::StateSet *stateset, int animflags)
|
void Loader::handleTextureControllers(const Nif::Property *texProperty, osg::Node* node, osg::StateSet *stateset, int animflags)
|
||||||
{
|
{
|
||||||
for (Nif::ControllerPtr ctrl = texProperty->controller; !ctrl.empty(); ctrl = ctrl->next)
|
for (Nif::ControllerPtr ctrl = texProperty->controller; !ctrl.empty(); ctrl = ctrl->next)
|
||||||
{
|
{
|
||||||
@ -470,8 +481,10 @@ namespace NifOsg
|
|||||||
osgDB::ReaderWriter::ReadResult result = reader->readImage(*resourceManager->get(filename.c_str()), opts);
|
osgDB::ReaderWriter::ReadResult result = reader->readImage(*resourceManager->get(filename.c_str()), opts);
|
||||||
textures.push_back(osg::ref_ptr<osg::Image>(result.getImage()));
|
textures.push_back(osg::ref_ptr<osg::Image>(result.getImage()));
|
||||||
}
|
}
|
||||||
boost::shared_ptr<ControllerValue> dest(new FlipControllerValue(stateset, flipctrl, textures));
|
osg::ref_ptr<FlipController> callback(new FlipController(flipctrl, textures));
|
||||||
createController(flipctrl, dest, animflags);
|
setupController(ctrl.getPtr(), callback, animflags);
|
||||||
|
stateset->setDataVariance(osg::StateSet::DYNAMIC);
|
||||||
|
node->addUpdateCallback(callback);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
std::cerr << "Unexpected texture controller " << ctrl->recName << std::endl;
|
std::cerr << "Unexpected texture controller " << ctrl->recName << std::endl;
|
||||||
@ -577,7 +590,9 @@ namespace NifOsg
|
|||||||
// If something ever violates this assumption, the worst that could happen is the culling being one frame late, which wouldn't be a disaster.
|
// If something ever violates this assumption, the worst that could happen is the culling being one frame late, which wouldn't be a disaster.
|
||||||
parentNode->addChild(emitter);
|
parentNode->addChild(emitter);
|
||||||
|
|
||||||
createController(partctrl, boost::shared_ptr<ControllerValue>(new ParticleSystemControllerValue(emitter, partctrl)), animflags);
|
osg::ref_ptr<ParticleSystemController> callback(new ParticleSystemController(partctrl));
|
||||||
|
setupController(partctrl, callback, animflags);
|
||||||
|
emitter->setUpdateCallback(callback);
|
||||||
|
|
||||||
// ----------- affector (must be after emitters in the scene graph)
|
// ----------- affector (must be after emitters in the scene graph)
|
||||||
osgParticle::ModularProgram* program = new osgParticle::ModularProgram;
|
osgParticle::ModularProgram* program = new osgParticle::ModularProgram;
|
||||||
@ -615,14 +630,16 @@ namespace NifOsg
|
|||||||
|
|
||||||
// -----------
|
// -----------
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
|
||||||
|
geode->addDrawable(partsys);
|
||||||
|
|
||||||
std::vector<const Nif::Property*> materialProps;
|
std::vector<const Nif::Property*> materialProps;
|
||||||
collectMaterialProperties(nifNode, materialProps);
|
collectMaterialProperties(nifNode, materialProps);
|
||||||
applyMaterialProperties(partsys->getOrCreateStateSet(), materialProps, true, animflags);
|
applyMaterialProperties(geode, materialProps, true, animflags);
|
||||||
|
|
||||||
partsys->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
partsys->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||||
partsys->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
|
partsys->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
|
||||||
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
|
|
||||||
geode->addDrawable(partsys);
|
|
||||||
|
|
||||||
if (rf == osgParticle::ParticleProcessor::RELATIVE_RF)
|
if (rf == osgParticle::ParticleProcessor::RELATIVE_RF)
|
||||||
parentNode->addChild(geode);
|
parentNode->addChild(geode);
|
||||||
@ -640,7 +657,7 @@ namespace NifOsg
|
|||||||
parentNode->addChild(updater);
|
parentNode->addChild(updater);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::triShapeToGeometry(const Nif::NiTriShape *triShape, osg::Geometry *geometry, const std::map<int, int>& boundTextures, int animflags)
|
void Loader::triShapeToGeometry(const Nif::NiTriShape *triShape, osg::Geometry *geometry, osg::Geode* parentGeode, const std::map<int, int>& boundTextures, int animflags)
|
||||||
{
|
{
|
||||||
const Nif::NiTriShapeData* data = triShape->data.getPtr();
|
const Nif::NiTriShapeData* data = triShape->data.getPtr();
|
||||||
|
|
||||||
@ -720,7 +737,7 @@ namespace NifOsg
|
|||||||
// above the actual renderable would be tedious.
|
// above the actual renderable would be tedious.
|
||||||
std::vector<const Nif::Property*> materialProps;
|
std::vector<const Nif::Property*> materialProps;
|
||||||
collectMaterialProperties(triShape, materialProps);
|
collectMaterialProperties(triShape, materialProps);
|
||||||
applyMaterialProperties(geometry->getOrCreateStateSet(), materialProps, !data->colors.empty(), animflags);
|
applyMaterialProperties(parentGeode, materialProps, !data->colors.empty(), animflags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, const std::map<int, int>& boundTextures, int animflags)
|
void Loader::handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, const std::map<int, int>& boundTextures, int animflags)
|
||||||
@ -733,10 +750,11 @@ namespace NifOsg
|
|||||||
if(ctrl->recType == Nif::RC_NiGeomMorpherController && ctrl->flags & Nif::NiNode::ControllerFlag_Active)
|
if(ctrl->recType == Nif::RC_NiGeomMorpherController && ctrl->flags & Nif::NiNode::ControllerFlag_Active)
|
||||||
{
|
{
|
||||||
geometry = handleMorphGeometry(static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr()));
|
geometry = handleMorphGeometry(static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr()));
|
||||||
boost::shared_ptr<ControllerValue> value(
|
|
||||||
new GeomMorpherControllerValue(static_cast<osgAnimation::MorphGeometry*>(geometry.get()),
|
osg::ref_ptr<GeomMorpherController> morphctrl = new GeomMorpherController(
|
||||||
static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr())->data.getPtr()));
|
static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr())->data.getPtr());
|
||||||
createController(ctrl.getPtr(), value, 0);
|
setupController(ctrl.getPtr(), morphctrl, animflags);
|
||||||
|
geometry->setUpdateCallback(morphctrl);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while(!(ctrl=ctrl->next).empty());
|
} while(!(ctrl=ctrl->next).empty());
|
||||||
@ -744,9 +762,10 @@ namespace NifOsg
|
|||||||
|
|
||||||
if (!geometry.get())
|
if (!geometry.get())
|
||||||
geometry = new osg::Geometry;
|
geometry = new osg::Geometry;
|
||||||
triShapeToGeometry(triShape, geometry, boundTextures, animflags);
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
|
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
|
||||||
|
triShapeToGeometry(triShape, geometry, geode, boundTextures, animflags);
|
||||||
|
|
||||||
geode->addDrawable(geometry);
|
geode->addDrawable(geometry);
|
||||||
|
|
||||||
parentNode->addChild(geode);
|
parentNode->addChild(geode);
|
||||||
@ -754,8 +773,9 @@ namespace NifOsg
|
|||||||
|
|
||||||
void Loader::handleSkinnedTriShape(const Nif::NiTriShape *triShape, osg::Group *parentNode, const std::map<int, int>& boundTextures, int animflags)
|
void Loader::handleSkinnedTriShape(const Nif::NiTriShape *triShape, osg::Group *parentNode, const std::map<int, int>& boundTextures, int animflags)
|
||||||
{
|
{
|
||||||
|
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
|
||||||
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
|
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
|
||||||
triShapeToGeometry(triShape, geometry, boundTextures, animflags);
|
triShapeToGeometry(triShape, geometry, geode, boundTextures, animflags);
|
||||||
|
|
||||||
osg::ref_ptr<osgAnimation::RigGeometry> rig(new osgAnimation::RigGeometry);
|
osg::ref_ptr<osgAnimation::RigGeometry> rig(new osgAnimation::RigGeometry);
|
||||||
rig->setSourceGeometry(geometry);
|
rig->setSourceGeometry(geometry);
|
||||||
@ -795,7 +815,6 @@ namespace NifOsg
|
|||||||
osg::ref_ptr<osg::MatrixTransform> trans(new osg::MatrixTransform);
|
osg::ref_ptr<osg::MatrixTransform> trans(new osg::MatrixTransform);
|
||||||
trans->setUpdateCallback(new InvertBoneMatrix());
|
trans->setUpdateCallback(new InvertBoneMatrix());
|
||||||
|
|
||||||
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
|
|
||||||
geode->addDrawable(rig);
|
geode->addDrawable(rig);
|
||||||
|
|
||||||
trans->addChild(geode);
|
trans->addChild(geode);
|
||||||
@ -987,7 +1006,7 @@ namespace NifOsg
|
|||||||
stateset->setTextureAttributeAndModes(i, new osg::Texture2D, osg::StateAttribute::OFF);
|
stateset->setTextureAttributeAndModes(i, new osg::Texture2D, osg::StateAttribute::OFF);
|
||||||
boundTextures.erase(i);
|
boundTextures.erase(i);
|
||||||
}
|
}
|
||||||
handleTextureControllers(texprop, stateset, animflags);
|
handleTextureControllers(texprop, node, stateset, animflags);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1003,9 +1022,11 @@ namespace NifOsg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::applyMaterialProperties(osg::StateSet* stateset, const std::vector<const Nif::Property*>& properties,
|
void Loader::applyMaterialProperties(osg::Node* node, const std::vector<const Nif::Property*>& properties,
|
||||||
bool hasVertexColors, int animflags)
|
bool hasVertexColors, int animflags)
|
||||||
{
|
{
|
||||||
|
osg::StateSet* stateset = node->getOrCreateStateSet();
|
||||||
|
|
||||||
int specFlags = 0; // Specular is disabled by default, even if there's a specular color in the NiMaterialProperty
|
int specFlags = 0; // Specular is disabled by default, even if there's a specular color in the NiMaterialProperty
|
||||||
osg::Material* mat = new osg::Material;
|
osg::Material* mat = new osg::Material;
|
||||||
mat->setColorMode(hasVertexColors ? osg::Material::AMBIENT_AND_DIFFUSE : osg::Material::OFF);
|
mat->setColorMode(hasVertexColors ? osg::Material::AMBIENT_AND_DIFFUSE : osg::Material::OFF);
|
||||||
@ -1032,7 +1053,7 @@ namespace NifOsg
|
|||||||
mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness);
|
mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness);
|
||||||
|
|
||||||
if (!matprop->controller.empty())
|
if (!matprop->controller.empty())
|
||||||
handleMaterialControllers(matprop, stateset, animflags);
|
handleMaterialControllers(matprop, node, stateset, animflags);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,6 @@
|
|||||||
|
|
||||||
#include <osg/Group>
|
#include <osg/Group>
|
||||||
|
|
||||||
#include "controller.hpp"
|
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
{
|
{
|
||||||
class Geometry;
|
class Geometry;
|
||||||
@ -25,10 +23,12 @@ namespace Nif
|
|||||||
class Node;
|
class Node;
|
||||||
class NiTriShape;
|
class NiTriShape;
|
||||||
class Property;
|
class Property;
|
||||||
|
class Controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace NifOsg
|
namespace NifOsg
|
||||||
{
|
{
|
||||||
|
class Controller;
|
||||||
|
|
||||||
/// The main class responsible for loading NIF files into an OSG-Scenegraph.
|
/// The main class responsible for loading NIF files into an OSG-Scenegraph.
|
||||||
class Loader
|
class Loader
|
||||||
@ -41,9 +41,6 @@ namespace NifOsg
|
|||||||
|
|
||||||
const VFS::Manager* resourceManager;
|
const VFS::Manager* resourceManager;
|
||||||
|
|
||||||
// FIXME move
|
|
||||||
std::vector<Controller> mControllers;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// @param createSkeleton If true, use an osgAnimation::Bone for NIF nodes, otherwise an osg::MatrixTransform.
|
/// @param createSkeleton If true, use an osgAnimation::Bone for NIF nodes, otherwise an osg::MatrixTransform.
|
||||||
@ -54,9 +51,9 @@ namespace NifOsg
|
|||||||
|
|
||||||
void handleNodeControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode, int animflags);
|
void handleNodeControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode, int animflags);
|
||||||
|
|
||||||
void handleMaterialControllers(const Nif::Property* materialProperty, osg::StateSet* stateset, int animflags);
|
void handleMaterialControllers(const Nif::Property* materialProperty, osg::Node* node, osg::StateSet* stateset, int animflags);
|
||||||
|
|
||||||
void handleTextureControllers(const Nif::Property* texProperty, osg::StateSet* stateset, int animflags);
|
void handleTextureControllers(const Nif::Property* texProperty, osg::Node* node, osg::StateSet* stateset, int animflags);
|
||||||
|
|
||||||
void handleProperty (const Nif::Property* property, const Nif::Node* nifNode,
|
void handleProperty (const Nif::Property* property, const Nif::Node* nifNode,
|
||||||
osg::Node* node, std::map<int, int>& boundTextures, int animflags);
|
osg::Node* node, std::map<int, int>& boundTextures, int animflags);
|
||||||
@ -67,7 +64,7 @@ namespace NifOsg
|
|||||||
void handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, const std::map<int, int>& boundTextures, int animflags);
|
void handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, const std::map<int, int>& boundTextures, int animflags);
|
||||||
|
|
||||||
// Fills the vertex data for the given TriShape into the given Geometry.
|
// Fills the vertex data for the given TriShape into the given Geometry.
|
||||||
void triShapeToGeometry(const Nif::NiTriShape* triShape, osg::Geometry* geom, const std::map<int, int>& boundTextures, int animflags);
|
void triShapeToGeometry(const Nif::NiTriShape* triShape, osg::Geometry* geom, osg::Geode* parentGeode, const std::map<int, int>& boundTextures, int animflags);
|
||||||
|
|
||||||
// Creates a skinned osg::Geometry object for the given TriShape, populates it, and attaches it to the given node.
|
// Creates a skinned osg::Geometry object for the given TriShape, populates it, and attaches it to the given node.
|
||||||
void handleSkinnedTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, const std::map<int, int>& boundTextures, int animflags);
|
void handleSkinnedTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, const std::map<int, int>& boundTextures, int animflags);
|
||||||
@ -75,10 +72,11 @@ namespace NifOsg
|
|||||||
// Applies the Properties of the given nifNode onto the StateSet of the given OSG node.
|
// Applies the Properties of the given nifNode onto the StateSet of the given OSG node.
|
||||||
void applyNodeProperties(const Nif::Node* nifNode, osg::Node* applyTo, std::map<int, int>& boundTextures, int animflags);
|
void applyNodeProperties(const Nif::Node* nifNode, osg::Node* applyTo, std::map<int, int>& boundTextures, int animflags);
|
||||||
|
|
||||||
void applyMaterialProperties(osg::StateSet* stateset, const std::vector<const Nif::Property*>& properties,
|
void applyMaterialProperties(osg::Node* node, const std::vector<const Nif::Property*>& properties,
|
||||||
bool hasVertexColors, int animflags);
|
bool hasVertexColors, int animflags);
|
||||||
|
|
||||||
void createController(const Nif::Controller* ctrl, boost::shared_ptr<ControllerValue> value, int animflags);
|
// Set up the default input and controller function for the given controller.
|
||||||
|
void setupController(const Nif::Controller* ctrl, Controller* toSetup, int animflags);
|
||||||
|
|
||||||
Nif::NIFFilePtr mNif;
|
Nif::NIFFilePtr mNif;
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ ParticleShooter::ParticleShooter()
|
|||||||
}
|
}
|
||||||
|
|
||||||
ParticleShooter::ParticleShooter(const osgParticle::Shooter ©, const osg::CopyOp ©op)
|
ParticleShooter::ParticleShooter(const osgParticle::Shooter ©, const osg::CopyOp ©op)
|
||||||
|
: osgParticle::Shooter(copy, copyop)
|
||||||
{
|
{
|
||||||
*this = copy;
|
*this = copy;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user