From c5e543b91b75f2131efb9816b6cb63e073f21d10 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 6 Dec 2013 17:25:32 +0100 Subject: [PATCH 1/3] Implement NiGeomMorpherController --- components/nifogre/mesh.cpp | 7 +++ components/nifogre/ogrenifloader.cpp | 86 ++++++++++++++++++++++++++-- 2 files changed, 88 insertions(+), 5 deletions(-) diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index ca92f62d4..ef4fbbe8d 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -116,6 +116,7 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC; bool vertShadowBuffer = false; + bool geomMorpherController = false; if(!shape->controller.empty()) { Nif::ControllerPtr ctrl = shape->controller; @@ -124,6 +125,7 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape { vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; vertShadowBuffer = true; + geomMorpherController = true; break; } } while(!(ctrl=ctrl->next).empty()); @@ -347,6 +349,11 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape if (!mesh->suggestTangentVectorBuildParams(Ogre::VES_TANGENT, src,dest)) mesh->buildTangentVectors(Ogre::VES_TANGENT, src,dest); } + + // Create a dummy vertex animation track if there's a geom morpher controller + // This is required to make Ogre create the buffers we will use for software vertex animation + if (srcVerts.size() && geomMorpherController) + mesh->createAnimation("dummy", 0)->createVertexTrack(1, sub->vertexData, Ogre::VAT_MORPH); } diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index a530d060d..ec24089b8 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -385,12 +385,35 @@ public: private: Ogre::SubEntity *mSubEntity; std::vector mMorphs; + std::vector mValues; + + std::vector mVertices; + + static float interpKey(const Nif::FloatKeyList::VecType &keys, float time) + { + if(time <= keys.front().mTime) + return keys.front().mValue; + + Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1); + for(;iter != keys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::FloatKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return last->mValue + ((iter->mValue - last->mValue)*a); + } + return keys.back().mValue; + } public: Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data) : mSubEntity(subent) , mMorphs(data->mMorphs) - { } + { + mValues.resize(mMorphs.size()-1, 0.f); + } virtual Ogre::Real getValue() const { @@ -398,9 +421,63 @@ public: return 0.0f; } - virtual void setValue(Ogre::Real value) + virtual void setValue(Ogre::Real time) { - // TODO: Implement + if (mMorphs.size() <= 1) + return; + +#if OGRE_DOUBLE_PRECISION +#error "This code needs to be rewritten for double precision mode" +#endif + + Ogre::VertexData* data = mSubEntity->_getSoftwareVertexAnimVertexData(); + + const Ogre::VertexElement* posElem = + data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); + + Ogre::HardwareVertexBufferSharedPtr vbuf = + data->vertexBufferBinding->getBuffer(posElem->getSource()); + + bool needToUpdate = false; + int i=0; + for (std::vector::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)); + + if (val != mValues[i]) + needToUpdate = true; + mValues[i] = val; + } + if (!needToUpdate) + return; + + // The first morph key always contains the original positions + mVertices = mMorphs[0].mVertices; + + i = 0; + for (std::vector::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i) + { + float val = mValues[i]; + + if (it->mVertices.size() != mMorphs[0].mVertices.size()) + continue; + + if (val != 0) + { + for (unsigned int v=0; vmVertices[v] * val; + } + } + + if (mVertices.size() * sizeof(float)*3 != vbuf->getSizeInBytes()) + return; + + vbuf->writeData(0, vbuf->getSizeInBytes(), &mVertices[0]); + + mSubEntity->_markBuffersUsedForAnimation(); } }; @@ -480,11 +557,10 @@ class NIFObjectLoader { const Nif::NiGeomMorpherController *geom = static_cast(ctrl.getPtr()); - Ogre::SubEntity *subent = entity->getSubEntity(0); Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(subent, geom->data.getPtr())); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(entity->getSubEntity(0), geom->data.getPtr())); GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); objectlist.mMaxControllerLength = std::max(function->mStopTime, objectlist.mMaxControllerLength); From bb70deabb1e06606484fb7ffdec2a8452e07936f Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 7 Dec 2013 14:11:06 +0100 Subject: [PATCH 2/3] Add an incomplete implementation of SayAnimationValue (lip animation) --- apps/openmw/mwrender/npcanimation.cpp | 18 ++++++++++++++++-- apps/openmw/mwrender/npcanimation.hpp | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 50e41062a..4ba49cf55 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -59,6 +59,15 @@ std::string getVampireHead(const std::string& race, bool female) namespace MWRender { +float SayAnimationValue::getValue() const +{ + if (MWBase::Environment::get().getSoundManager()->sayDone(mReference)) + return 0; + else + // TODO: Use the loudness of the currently playing sound + return 1; +} + static NpcAnimation::PartBoneMap createPartListMap() { NpcAnimation::PartBoneMap result; @@ -115,6 +124,8 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v { mNpc = mPtr.get()->mBase; + mSayAnimationValue = Ogre::SharedPtr(new SayAnimationValue(mPtr)); + for(size_t i = 0;i < ESM::PRT_Count;i++) { mPartslots[i] = -1; //each slot is empty @@ -558,15 +569,18 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g } // TODO: - // type == ESM::PRT_Head should get an animation source based on the current output of - // the actor's 'say' sound (0 = silent, 1 = loud?). // type == ESM::PRT_Weapon should get an animation source based on the current offset // of the weapon attack animation (from its beginning, or start marker?) std::vector >::iterator ctrl(mObjectParts[type].mControllers.begin()); for(;ctrl != mObjectParts[type].mControllers.end();ctrl++) { if(ctrl->getSource().isNull()) + { ctrl->setSource(mNullAnimationValuePtr); + + if (type == ESM::PRT_Head) + ctrl->setSource(mSayAnimationValue); + } } return true; diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index c33d511ec..4e252d9d0 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -13,6 +13,18 @@ namespace ESM namespace MWRender { +class SayAnimationValue : public Ogre::ControllerValue +{ +private: + MWWorld::Ptr mReference; +public: + SayAnimationValue(MWWorld::Ptr reference) : mReference(reference) {} + + virtual Ogre::Real getValue() const; + virtual void setValue(Ogre::Real value) + { } +}; + class NpcAnimation : public Animation, public MWWorld::InventoryStoreListener { public: @@ -50,6 +62,8 @@ private: Ogre::Vector3 mFirstPersonOffset; + Ogre::SharedPtr mSayAnimationValue; + void updateNpcBase(); NifOgre::ObjectList insertBoundedPart(const std::string &model, int group, const std::string &bonename, From 742e0e014da428c58dd1361f5702ed533f56fe93 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 7 Dec 2013 14:14:05 +0100 Subject: [PATCH 3/3] Remove more cruft in MessageBox. Fixes inconsistent sizing when close to a newline. --- apps/openmw/mwgui/messagebox.cpp | 31 +++++----------------------- apps/openmw/mwgui/messagebox.hpp | 2 -- files/mygui/openmw_messagebox.layout | 10 ++++----- 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 8486218f0..671845552 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -141,7 +141,6 @@ namespace MWGui , mMaxTime(0) { // defines - mFixedWidth = 300; mBottomPadding = 20; mNextBoxPadding = 20; @@ -149,41 +148,21 @@ namespace MWGui mMessageWidget->setOverflowToTheLeft(true); mMessageWidget->setCaptionWithReplacing(mMessage); - - MyGUI::IntSize size; - size.width = mFixedWidth; - size.height = 100; // dummy - - mMessageWidget->setSize(size); - - MyGUI::IntSize textSize = mMessageWidget->getTextSize(); - - size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box - - mMainWidget->setSize(size); - size.width -= 15; // this is to center the text (see messagebox.layout, Widget type="Edit" position="-2 -3 0 0") - mMessageWidget->setSize(size); } void MessageBox::update (int height) { MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); - MyGUI::IntCoord coord; - coord.left = (gameWindowSize.width - mFixedWidth)/2; - coord.top = (gameWindowSize.height - mHeight - height - mBottomPadding); + MyGUI::IntPoint pos; + pos.left = (gameWindowSize.width - mMainWidget->getWidth())/2; + pos.top = (gameWindowSize.height - mMainWidget->getHeight() - height - mBottomPadding); - MyGUI::IntSize size; - size.width = mFixedWidth; - size.height = mHeight; - - mMainWidget->setCoord(coord); - mMainWidget->setSize(size); - mMainWidget->setVisible(true); + mMainWidget->setPosition(pos); } int MessageBox::getHeight () { - return mHeight+mNextBoxPadding; // 20 is the padding between this and the next MessageBox + return mMainWidget->getHeight()+mNextBoxPadding; // 20 is the padding between this and the next MessageBox } diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index ce3a85ab3..aac4704fa 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -63,10 +63,8 @@ namespace MWGui protected: MessageBoxManager& mMessageBoxManager; - int mHeight; const std::string& mMessage; MyGUI::EditBox* mMessageWidget; - int mFixedWidth; int mBottomPadding; int mNextBoxPadding; }; diff --git a/files/mygui/openmw_messagebox.layout b/files/mygui/openmw_messagebox.layout index dfdb57648..b2d29271b 100644 --- a/files/mygui/openmw_messagebox.layout +++ b/files/mygui/openmw_messagebox.layout @@ -1,11 +1,11 @@ - - - + + + + +