From eab4e09566b815f615463a1b6fa0d9d6bd3942bd Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 2 Aug 2013 18:21:42 -0700 Subject: [PATCH 01/15] Handle the pick/probe in the character controller --- apps/openmw/mwinput/inputmanagerimp.cpp | 4 -- apps/openmw/mwmechanics/character.cpp | 33 ++++++++++++++++- apps/openmw/mwworld/player.cpp | 49 ------------------------- apps/openmw/mwworld/player.hpp | 3 -- 4 files changed, 32 insertions(+), 57 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index d15e95171..573fe389c 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -268,10 +268,6 @@ namespace MWInput case A_ToggleHUD: mWindows.toggleHud(); break; - case A_Use: - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - mPlayer.use(); - break; } } } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0a547b4d3..8aa797b0c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -24,12 +24,14 @@ #include "movement.hpp" #include "npcstats.hpp" #include "creaturestats.hpp" +#include "security.hpp" #include "../mwrender/animation.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -465,7 +467,36 @@ bool CharacterController::updateNpcState() sndMgr->playSound3D(mPtr, schools[effect->mData.mSchool]+" cast", 1.0f, 1.0f); } } - else if(mWeaponType != WeapType_PickProbe) + else if(mWeaponType == WeapType_PickProbe) + { + MWWorld::Ptr item = *weapon; + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject(); + std::string resultMessage, resultSound; + + if(!target.isEmpty()) + { + if(item.getTypeName() == typeid(ESM::Lockpick).name()) + Security(mPtr).pickLock(target, item, resultMessage, resultSound); + else if(item.getTypeName() == typeid(ESM::Probe).name()) + Security(mPtr).probeTrap(target, item, resultMessage, resultSound); + } + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + 1.0f, "start", "stop", 0.0, 0); + mUpperBodyState = UpperCharState_FollowStartToFollowStop; + + if(!resultMessage.empty()) + MWBase::Environment::get().getWindowManager()->messageBox(resultMessage); + if(!resultSound.empty()) + MWBase::Environment::get().getSoundManager()->playSound(resultSound, 1.0f, 1.0f); + + // tool used up? + if(!item.getRefData().getCount()) + MWBase::Environment::get().getWindowManager()->unsetSelectedWeapon(); + else + MWBase::Environment::get().getWindowManager()->setSelectedWeapon(item); + } + else { if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_ThowWeapon) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 0d9399c61..e26c2e2a5 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -11,10 +11,6 @@ #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" -#include "../mwmechanics/character.hpp" -#include "../mwmechanics/security.hpp" - -#include "../mwrender/animation.hpp" #include "class.hpp" @@ -144,51 +140,6 @@ namespace MWWorld MWWorld::Class::get(ptr).getMovementSettings(ptr).mRotation[1] += roll; } - void Player::use() - { - MWWorld::InventoryStore& store = MWWorld::Class::get(getPlayer()).getInventoryStore(getPlayer()); - MWWorld::ContainerStoreIterator equipped = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - - if (getDrawState() == MWMechanics::DrawState_Weapon) - { - if (equipped != store.end()) - { - MWWorld::Ptr item = *equipped; - MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(getPlayer()); - MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject(); - - if (anim->isPriorityActive(MWMechanics::Priority_Weapon)) - return; - - std::string resultMessage, resultSound; - - if (item.getTypeName() == typeid(ESM::Lockpick).name()) - { - if (!target.isEmpty()) - MWMechanics::Security(getPlayer()).pickLock(target, item, resultMessage, resultSound); - anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, 1.0f, "start", "stop", 0.0, 0); - } - else if (item.getTypeName() == typeid(ESM::Probe).name()) - { - if (!target.isEmpty()) - MWMechanics::Security(getPlayer()).probeTrap(target, item, resultMessage, resultSound); - anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, 1.0f, "start", "stop", 0.0, 0); - } - - if (!resultMessage.empty()) - MWBase::Environment::get().getWindowManager()->messageBox(resultMessage); - if (!resultSound.empty()) - MWBase::Environment::get().getSoundManager()->playSound(resultSound,1,1); - - // tool used up? - if (!item.getRefData().getCount()) - MWBase::Environment::get().getWindowManager()->unsetSelectedWeapon(); - else - MWBase::Environment::get().getWindowManager()->setSelectedWeapon(item); - } - } - } - MWMechanics::DrawState_ Player::getDrawState() { MWWorld::Ptr ptr = getPlayer(); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 4660c6e9a..d78b1901c 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -58,9 +58,6 @@ namespace MWWorld void setForwardBackward (int value); void setUpDown(int value); - void use (); - ///< Use item equipped on right hand, or fists - void setRunState(bool run); void setSneak(bool sneak); From 2c03fec4bb3ede59fef6b25fdd49d4d3ca564937 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 3 Aug 2013 00:07:52 -0700 Subject: [PATCH 02/15] Use the fatigue term for jumping --- apps/openmw/mwclass/npc.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4dd4f6f36..b9c463432 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -725,15 +725,15 @@ namespace MWClass float x = fJumpAcrobaticsBase->getFloat() + std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat()); - x += 3 * b * fJumpAcroMultiplier->getFloat(); - x += mageffects.get(MWMechanics::EffectKey(9/*jump*/)).mMagnitude * 64; + x += 3.0f * b * fJumpAcroMultiplier->getFloat(); + x += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Jump)).mMagnitude * 64; x *= encumbranceTerm; if(Npc::getStance(ptr, Run, false)) x *= fJumpRunMultiplier->getFloat(); - x *= 1.25f;//fatigueTerm; - x -= -627.2/*gravity constant*/; - x /= 3; + x *= npcdata->mCreatureStats.getFatigueTerm(); + x -= -627.2f;/*gravity constant*/ + x /= 3.0f; return x; } From 8f69c51b2452ddee2e5608f29ffd2356cdf7ed64 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 3 Aug 2013 19:29:44 -0700 Subject: [PATCH 03/15] Avoid some unnecessary references --- apps/openmw/mwmechanics/creaturestats.cpp | 4 ++-- apps/openmw/mwmechanics/creaturestats.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 93fe495c9..d7b254fee 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -110,7 +110,7 @@ namespace MWMechanics return mMagicEffects; } - const bool &CreatureStats::getAttackingOrSpell() const + bool CreatureStats::getAttackingOrSpell() const { return mAttackingOrSpell; } @@ -216,7 +216,7 @@ namespace MWMechanics mMagicEffects = effects; } - void CreatureStats::setAttackingOrSpell(const bool &attackingOrSpell) + void CreatureStats::setAttackingOrSpell(bool attackingOrSpell) { mAttackingOrSpell = attackingOrSpell; } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 4e18faf9a..c7f3ab94a 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -59,7 +59,7 @@ namespace MWMechanics const MagicEffects & getMagicEffects() const; - const bool & getAttackingOrSpell() const; + bool getAttackingOrSpell() const; int getLevel() const; @@ -90,7 +90,7 @@ namespace MWMechanics void setMagicEffects(const MagicEffects &effects); - void setAttackingOrSpell(const bool &attackingOrSpell); + void setAttackingOrSpell(bool attackingOrSpell); enum AttackType { From 643e7651e0980d93bdd2e6c63351461c405fe22e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 4 Aug 2013 18:31:12 -0700 Subject: [PATCH 04/15] Use a null material for non-SubEntities in the selection buffer --- libs/openengine/ogre/selectionbuffer.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libs/openengine/ogre/selectionbuffer.cpp b/libs/openengine/ogre/selectionbuffer.cpp index 30e7b9e1e..69375b74d 100644 --- a/libs/openengine/ogre/selectionbuffer.cpp +++ b/libs/openengine/ogre/selectionbuffer.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -100,7 +101,16 @@ namespace Render return m->getTechnique(1); } else - throw std::runtime_error("selectionbuffer only works with entities"); + { + m = Ogre::MaterialManager::getSingleton().getByName("NullMaterial"); + if(m.isNull()) + { + m = Ogre::MaterialManager::getSingleton().create("NullMaterial", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + m->getTechnique(0)->getPass(0)->setDepthCheckEnabled(true); + m->getTechnique(0)->getPass(0)->setDepthFunction(Ogre::CMPF_ALWAYS_FAIL); + } + return m->getTechnique(0); + } } return NULL; } From 1f436f98869ab9a003a11ccf653c0194aaa1b955 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 4 Aug 2013 18:58:10 -0700 Subject: [PATCH 05/15] Render torches --- apps/openmw/mwrender/npcanimation.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index e63a10160..0232c71c5 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -267,6 +267,18 @@ void NpcAnimation::updateParts(bool forceupdate) if(mViewMode == VM_HeadOnly) return; + if(mPartPriorities[ESM::PRT_Shield] < 1) + { + MWWorld::ContainerStoreIterator store = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + MWWorld::Ptr part; + if(store != inv.end() && (part=*store).getTypeName() == typeid(ESM::Light).name()) + { + const ESM::Light *light = part.get()->mBase; + addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, + 1, "meshes\\"+light->mModel); + } + } + showWeapons(mShowWeapons); const int Flag_Female = 0x01; @@ -485,6 +497,21 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, if(type == sPartList[i].type) { mObjectParts[i] = insertBoundedPart(mesh, group, sPartList[i].name); + if(mObjectParts[i].mSkelBase && mObjectParts[i].mSkelBase->isParentTagPoint()) + { + Ogre::Node *root = mObjectParts[i].mSkelBase->getParentNode(); + Ogre::SkeletonInstance *skel = mObjectParts[i].mSkelBase->getSkeleton(); + if(skel->hasBone("BoneOffset")) + { + Ogre::Bone *offset = skel->getBone("BoneOffset"); + root->translate(offset->getPosition()); + root->rotate(offset->getOrientation()); + // HACK: Why an extra -90 degree rotation? + root->pitch(Ogre::Degree(-90.0f)); + root->scale(offset->getScale()); + root->setInitialState(); + } + } // TODO: // type == ESM::PRT_Head should get an animation source based on the current output of From a9cc3a284446dcc4d5e7a7588690a4cb6472e071 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 4 Aug 2013 21:33:11 -0700 Subject: [PATCH 06/15] Ensure a valid CellStore for Ptr::isInCell --- apps/openmw/mwworld/ptr.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index 14e2c76a4..2cb92ce2f 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -74,7 +74,7 @@ namespace MWWorld bool isInCell() const { - return (mContainerStore == 0); + return (mContainerStore == 0) && (mCell != 0); } void setContainerStore (ContainerStore *store); From 57fb065a86cf02ef6a23b03584014aa0589969e5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 5 Aug 2013 16:17:40 -0700 Subject: [PATCH 07/15] Add Ogre::Light objects to the object list Note that NIFs actually have NiLight-based light records which could be used to create Ogre::Light objects. However, no Morrowind NIF uses them, as far as I can tell. --- apps/openmw/mwrender/animation.cpp | 3 +++ apps/openmw/mwrender/objects.cpp | 2 +- components/nifogre/ogrenifloader.hpp | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 0bb748572..b3f601270 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -35,11 +35,14 @@ void Animation::AnimationValue::setValue(Ogre::Real) void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects) { + for(size_t i = 0;i < objects.mLights.size();i++) + sceneMgr->destroyLight(objects.mLights[i]); for(size_t i = 0;i < objects.mParticles.size();i++) sceneMgr->destroyParticleSystem(objects.mParticles[i]); for(size_t i = 0;i < objects.mEntities.size();i++) sceneMgr->destroyEntity(objects.mEntities[i]); objects.mControllers.clear(); + objects.mLights.clear(); objects.mParticles.clear(); objects.mEntities.clear(); objects.mSkelBase = NULL; diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index b92b17ba8..bede95a26 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -159,7 +159,7 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool } if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects") || - anyTransparency || !objects.mParticles.empty()) + anyTransparency || !objects.mParticles.empty() || !objects.mLights.empty()) { for(size_t i = 0;i < objects.mEntities.size();i++) { diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index 45f3cbcd8..edad13a9a 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -43,6 +43,7 @@ struct ObjectList { Ogre::Entity *mSkelBase; std::vector mEntities; std::vector mParticles; + std::vector mLights; std::map mTextKeys; From e976bb16c5b195d44ce0dfe1b7d390cbecae6fbc Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 5 Aug 2013 17:46:38 -0700 Subject: [PATCH 08/15] Add a light for torches --- apps/openmw/mwrender/animation.cpp | 86 ++++++++++++++++++++++++++- apps/openmw/mwrender/animation.hpp | 3 + apps/openmw/mwrender/npcanimation.cpp | 8 +++ 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index b3f601270..adee771f6 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -16,6 +16,7 @@ #include "../mwmechanics/character.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/fallback.hpp" namespace MWRender { @@ -36,7 +37,13 @@ void Animation::AnimationValue::setValue(Ogre::Real) void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects) { for(size_t i = 0;i < objects.mLights.size();i++) - sceneMgr->destroyLight(objects.mLights[i]); + { + Ogre::Light *light = objects.mLights[i]; + // If parent is a scene node, it was created specifically for this light. Destroy it now. + if(light->isAttached() && !light->isParentTagPoint()) + sceneMgr->destroySceneNode(light->getParentSceneNode()); + sceneMgr->destroyLight(light); + } for(size_t i = 0;i < objects.mParticles.size();i++) sceneMgr->destroyParticleSystem(objects.mParticles[i]); for(size_t i = 0;i < objects.mEntities.size();i++) @@ -257,6 +264,83 @@ void Animation::clearAnimSources() } +void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objlist, const ESM::Light *light) +{ + const MWWorld::Fallback *fallback = MWBase::Environment::get().getWorld()->getFallback(); + + const int clr = light->mData.mColor; + Ogre::ColourValue color(((clr >> 0) & 0xFF) / 255.0f, + ((clr >> 8) & 0xFF) / 255.0f, + ((clr >> 16) & 0xFF) / 255.0f); + const float radius = float(light->mData.mRadius); + + if((light->mData.mFlags&ESM::Light::Negative)) + color *= -1; + + objlist.mLights.push_back(sceneMgr->createLight()); + Ogre::Light *olight = objlist.mLights.back(); + olight->setDiffuseColour(color); + + bool interior = !(mPtr.isInCell() && mPtr.getCell()->mCell->isExterior()); + + // TODO: Create Controllers for these +#if 0 + // randomize lights animations + info.time = Ogre::Math::RangeRandom(-500, +500); + info.phase = Ogre::Math::RangeRandom(-500, +500); + + if((light->mData.mFlags&ESM::Light::Flicker)) + info.type = LT_Flicker; + else if((light->mData.mFlags&ESM::Light::FlickerSlow)) + info.type = LT_FlickerSlow; + else if((light->mData.mFlags&ESM::Light::Pulse)) + info.type = LT_Pulse; + else if((light->mData.mFlags&ESM::Light::PulseSlow)) + info.type = LT_PulseSlow; + else + info.type = LT_Normal; +#endif + + bool quadratic = fallback->getFallbackBool("LightAttenuation_OutQuadInLin") ? + !interior : fallback->getFallbackBool("LightAttenuation_UseQuadratic"); + + // with the standard 1 / (c + d*l + d*d*q) equation the attenuation factor never becomes zero, + // so we ignore lights if their attenuation falls below this factor. + const float threshold = 0.03; + + if (!quadratic) + { + float r = radius * fallback->getFallbackFloat("LightAttenuation_LinearRadiusMult"); + float attenuation = fallback->getFallbackFloat("LightAttenuation_LinearValue") / r; + float activationRange = 1.0f / (threshold * attenuation); + olight->setAttenuation(activationRange, 0, attenuation, 0); + } + else + { + float r = radius * fallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult"); + float attenuation = fallback->getFallbackFloat("LightAttenuation_QuadraticValue") / std::pow(r, 2); + float activationRange = std::sqrt(1.0f / (threshold * attenuation)); + olight->setAttenuation(activationRange, 0, 0, attenuation); + } + + // If there's an AttachLight bone, attach the light to that, otherwise put it in the center, + if(objlist.mSkelBase && objlist.mSkelBase->getSkeleton()->hasBone("AttachLight")) + objlist.mSkelBase->attachObjectToBone("AttachLight", olight); + else + { + Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; + for(size_t i = 0;i < objlist.mEntities.size();i++) + { + Ogre::Entity *ent = objlist.mEntities[i]; + bounds.merge(ent->getBoundingBox()); + } + + Ogre::SceneNode *node = mInsert->createChildSceneNode(bounds.getCenter()); + node->attachObject(olight); + } +} + + Ogre::Node *Animation::getNode(const std::string &name) { if(mSkelBase) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 0b8f3a02f..541509fb6 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -158,6 +158,9 @@ protected: * extension will be replaced with .kf. */ void addAnimSource(const std::string &model); + /** Adds an additional light to the given object list using the specified ESM record. */ + void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objlist, const ESM::Light *light); + static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 0232c71c5..6611b3694 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -276,6 +276,14 @@ void NpcAnimation::updateParts(bool forceupdate) const ESM::Light *light = part.get()->mBase; addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, "meshes\\"+light->mModel); + for(size_t i = 0;i < sPartListSize;i++) + { + if(ESM::PRT_Shield == sPartList[i].type) + { + addExtraLight(mInsert->getCreator(), mObjectParts[i], light); + break; + } + } } } From 8984d8f8ee3efcccea658ac84ccbaa4a21e89e3e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 5 Aug 2013 18:59:55 -0700 Subject: [PATCH 09/15] Use a map to simplify NPC part referencing --- apps/openmw/mwrender/npcanimation.cpp | 166 ++++++++++++-------------- apps/openmw/mwrender/npcanimation.hpp | 30 ++--- 2 files changed, 88 insertions(+), 108 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 6611b3694..2d3cea12f 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -20,40 +20,44 @@ namespace MWRender { -const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize] = { - { ESM::PRT_Head, "Head" }, - { ESM::PRT_Hair, "Head" }, - { ESM::PRT_Neck, "Neck" }, - { ESM::PRT_Cuirass, "Chest" }, - { ESM::PRT_Groin, "Groin" }, - { ESM::PRT_Skirt, "Groin" }, - { ESM::PRT_RHand, "Right Hand" }, - { ESM::PRT_LHand, "Left Hand" }, - { ESM::PRT_RWrist, "Right Wrist" }, - { ESM::PRT_LWrist, "Left Wrist" }, - { ESM::PRT_Shield, "Shield Bone" }, - { ESM::PRT_RForearm, "Right Forearm" }, - { ESM::PRT_LForearm, "Left Forearm" }, - { ESM::PRT_RUpperarm, "Right Upper Arm" }, - { ESM::PRT_LUpperarm, "Left Upper Arm" }, - { ESM::PRT_RFoot, "Right Foot" }, - { ESM::PRT_LFoot, "Left Foot" }, - { ESM::PRT_RAnkle, "Right Ankle" }, - { ESM::PRT_LAnkle, "Left Ankle" }, - { ESM::PRT_RKnee, "Right Knee" }, - { ESM::PRT_LKnee, "Left Knee" }, - { ESM::PRT_RLeg, "Right Upper Leg" }, - { ESM::PRT_LLeg, "Left Upper Leg" }, - { ESM::PRT_RPauldron, "Right Clavicle" }, - { ESM::PRT_LPauldron, "Left Clavicle" }, - { ESM::PRT_Weapon, "Weapon Bone" }, - { ESM::PRT_Tail, "Tail" } -}; +static NpcAnimation::PartBoneMap createPartListMap() +{ + NpcAnimation::PartBoneMap result; + result.insert(std::make_pair(ESM::PRT_Head, "Head")); + result.insert(std::make_pair(ESM::PRT_Hair, "Head")); + result.insert(std::make_pair(ESM::PRT_Neck, "Neck")); + result.insert(std::make_pair(ESM::PRT_Cuirass, "Chest")); + result.insert(std::make_pair(ESM::PRT_Groin, "Groin")); + result.insert(std::make_pair(ESM::PRT_Skirt, "Groin")); + result.insert(std::make_pair(ESM::PRT_RHand, "Right Hand")); + result.insert(std::make_pair(ESM::PRT_LHand, "Left Hand")); + result.insert(std::make_pair(ESM::PRT_RWrist, "Right Wrist")); + result.insert(std::make_pair(ESM::PRT_LWrist, "Left Wrist")); + result.insert(std::make_pair(ESM::PRT_Shield, "Shield Bone")); + result.insert(std::make_pair(ESM::PRT_RForearm, "Right Forearm")); + result.insert(std::make_pair(ESM::PRT_LForearm, "Left Forearm")); + result.insert(std::make_pair(ESM::PRT_RUpperarm, "Right Upper Arm")); + result.insert(std::make_pair(ESM::PRT_LUpperarm, "Left Upper Arm")); + result.insert(std::make_pair(ESM::PRT_RFoot, "Right Foot")); + result.insert(std::make_pair(ESM::PRT_LFoot, "Left Foot")); + result.insert(std::make_pair(ESM::PRT_RAnkle, "Right Ankle")); + result.insert(std::make_pair(ESM::PRT_LAnkle, "Left Ankle")); + result.insert(std::make_pair(ESM::PRT_RKnee, "Right Knee")); + result.insert(std::make_pair(ESM::PRT_LKnee, "Left Knee")); + result.insert(std::make_pair(ESM::PRT_RLeg, "Right Upper Leg")); + result.insert(std::make_pair(ESM::PRT_LLeg, "Left Upper Leg")); + result.insert(std::make_pair(ESM::PRT_RPauldron, "Right Clavicle")); + result.insert(std::make_pair(ESM::PRT_LPauldron, "Left Clavicle")); + result.insert(std::make_pair(ESM::PRT_Weapon, "Weapon Bone")); + result.insert(std::make_pair(ESM::PRT_Tail, "Tail")); + return result; +} +const NpcAnimation::PartBoneMap NpcAnimation::sPartList = createPartListMap(); NpcAnimation::~NpcAnimation() { Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - for(size_t i = 0;i < sPartListSize;i++) + for(size_t i = 0;i < ESM::PRT_Count;i++) destroyObjectList(sceneMgr, mObjectParts[i]); } @@ -82,7 +86,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor { mNpc = mPtr.get()->mBase; - for(size_t i = 0;i < sPartListSize;i++) + for(size_t i = 0;i < ESM::PRT_Count;i++) { mPartslots[i] = -1; //each slot is empty mPartPriorities[i] = 0; @@ -168,8 +172,8 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) } MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); - for(size_t i = 0;i < sPartListSize;i++) - removeIndividualPart(i); + for(size_t i = 0;i < ESM::PRT_Count;i++) + removeIndividualPart((ESM::PartReferenceType)i); forceUpdate(); } @@ -276,14 +280,7 @@ void NpcAnimation::updateParts(bool forceupdate) const ESM::Light *light = part.get()->mBase; addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, "meshes\\"+light->mModel); - for(size_t i = 0;i < sPartListSize;i++) - { - if(ESM::PRT_Shield == sPartList[i].type) - { - addExtraLight(mInsert->getCreator(), mObjectParts[i], light); - break; - } - } + addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], light); } } @@ -386,7 +383,8 @@ void NpcAnimation::updateParts(bool forceupdate) { const ESM::BodyPart* bodypart = parts[part]; if(bodypart) - addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel); + addOrReplaceIndividualPart((ESM::PartReferenceType)part, -1, 1, + "meshes\\"+bodypart->mModel); } } } @@ -442,7 +440,7 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) node->pitch(Ogre::Radian(pitch*0.75f), Ogre::Node::TS_WORLD); } - for(size_t i = 0;i < sPartListSize;i++) + for(size_t i = 0;i < ESM::PRT_Count;i++) { std::vector >::iterator ctrl(mObjectParts[i].mControllers.begin()); for(;ctrl != mObjectParts[i].mControllers.end();ctrl++) @@ -457,22 +455,15 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) return ret; } -void NpcAnimation::removeIndividualPart(int type) +void NpcAnimation::removeIndividualPart(ESM::PartReferenceType type) { mPartPriorities[type] = 0; mPartslots[type] = -1; - for(size_t i = 0;i < sPartListSize;i++) - { - if(type == sPartList[i].type) - { - destroyObjectList(mInsert->getCreator(), mObjectParts[i]); - break; - } - } + destroyObjectList(mInsert->getCreator(), mObjectParts[type]); } -void NpcAnimation::reserveIndividualPart(int type, int group, int priority) +void NpcAnimation::reserveIndividualPart(ESM::PartReferenceType type, int group, int priority) { if(priority > mPartPriorities[type]) { @@ -484,14 +475,14 @@ void NpcAnimation::reserveIndividualPart(int type, int group, int priority) void NpcAnimation::removePartGroup(int group) { - for(int i = 0; i < 27; i++) + for(int i = 0; i < ESM::PRT_Count; i++) { if(mPartslots[i] == group) - removeIndividualPart(i); + removeIndividualPart((ESM::PartReferenceType)i); } } -bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh) +bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh) { if(priority <= mPartPriorities[type]) return false; @@ -500,43 +491,36 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, mPartslots[type] = group; mPartPriorities[type] = priority; - for(size_t i = 0;i < sPartListSize;i++) + mObjectParts[type] = insertBoundedPart(mesh, group, sPartList.at(type)); + if(mObjectParts[type].mSkelBase && mObjectParts[type].mSkelBase->isParentTagPoint()) { - if(type == sPartList[i].type) + Ogre::Node *root = mObjectParts[type].mSkelBase->getParentNode(); + Ogre::SkeletonInstance *skel = mObjectParts[type].mSkelBase->getSkeleton(); + if(skel->hasBone("BoneOffset")) { - mObjectParts[i] = insertBoundedPart(mesh, group, sPartList[i].name); - if(mObjectParts[i].mSkelBase && mObjectParts[i].mSkelBase->isParentTagPoint()) - { - Ogre::Node *root = mObjectParts[i].mSkelBase->getParentNode(); - Ogre::SkeletonInstance *skel = mObjectParts[i].mSkelBase->getSkeleton(); - if(skel->hasBone("BoneOffset")) - { - Ogre::Bone *offset = skel->getBone("BoneOffset"); - root->translate(offset->getPosition()); - root->rotate(offset->getOrientation()); - // HACK: Why an extra -90 degree rotation? - root->pitch(Ogre::Degree(-90.0f)); - root->scale(offset->getScale()); - root->setInitialState(); - } - } - - // 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[i].mControllers.begin()); - for(;ctrl != mObjectParts[i].mControllers.end();ctrl++) - { - if(ctrl->getSource().isNull()) - ctrl->setSource(mNullAnimationValuePtr); - } - - break; + Ogre::Bone *offset = skel->getBone("BoneOffset"); + root->translate(offset->getPosition()); + root->rotate(offset->getOrientation()); + // HACK: Why an extra -90 degree rotation? + root->pitch(Ogre::Degree(-90.0f)); + root->scale(offset->getScale()); + root->setInitialState(); } } - return true; + + // 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); + } + + return true; } void NpcAnimation::addPartGroup(int group, int priority, const std::vector &parts) @@ -577,9 +561,9 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormPart, group, priority, "meshes\\"+bodypart->mModel); + addOrReplaceIndividualPart((ESM::PartReferenceType)part->mPart, group, priority, "meshes\\"+bodypart->mModel); else - reserveIndividualPart(part->mPart, group, priority); + reserveIndividualPart((ESM::PartReferenceType)part->mPart, group, priority); } } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index e72fa56ed..aa51ffbe9 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -21,25 +21,21 @@ namespace MWRender class NpcAnimation : public Animation { public: -struct PartInfo { - ESM::PartReferenceType type; - const char name[32]; -}; + typedef std::map PartBoneMap; -enum ViewMode { - VM_Normal, - VM_FirstPerson, - VM_HeadOnly -}; + enum ViewMode { + VM_Normal, + VM_FirstPerson, + VM_HeadOnly + }; private: - static const size_t sPartListSize = 27; - static const PartInfo sPartList[sPartListSize]; + static const PartBoneMap sPartList; int mStateID; // Bounded Parts - NifOgre::ObjectList mObjectParts[sPartListSize]; + NifOgre::ObjectList mObjectParts[ESM::PRT_Count]; const ESM::NPC *mNpc; std::string mHeadModel; @@ -66,17 +62,17 @@ private: int mVisibilityFlags; - int mPartslots[sPartListSize]; //Each part slot is taken by clothing, armor, or is empty - int mPartPriorities[sPartListSize]; + int mPartslots[ESM::PRT_Count]; //Each part slot is taken by clothing, armor, or is empty + int mPartPriorities[ESM::PRT_Count]; NifOgre::ObjectList insertBoundedPart(const std::string &model, int group, const std::string &bonename); void updateParts(bool forceupdate = false); - void removeIndividualPart(int type); - void reserveIndividualPart(int type, int group, int priority); + void removeIndividualPart(ESM::PartReferenceType type); + void reserveIndividualPart(ESM::PartReferenceType type, int group, int priority); - bool addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh); + bool addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh); void removePartGroup(int group); void addPartGroup(int group, int priority, const std::vector &parts); From de95926e9ff4e8e42e4d6ef030d21d184249882d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 6 Aug 2013 00:04:39 -0700 Subject: [PATCH 10/15] Use controllers to animate lights in ObjectLists --- CMakeLists.txt | 1 + apps/openmw/mwrender/animation.cpp | 33 ++++---- apps/openmw/mwrender/animation.hpp | 2 + libs/openengine/ogre/lights.cpp | 118 +++++++++++++++++++++++++++++ libs/openengine/ogre/lights.hpp | 51 +++++++++++++ 5 files changed, 186 insertions(+), 19 deletions(-) create mode 100644 libs/openengine/ogre/lights.cpp create mode 100644 libs/openengine/ogre/lights.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fc37b413..575fecd0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,7 @@ set(LIBDIR ${CMAKE_SOURCE_DIR}/libs) set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/fader.cpp + ${LIBDIR}/openengine/ogre/lights.cpp ${LIBDIR}/openengine/ogre/particles.cpp ${LIBDIR}/openengine/ogre/selectionbuffer.cpp ${LIBDIR}/openengine/ogre/imagerotate.cpp diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index adee771f6..533a7eab3 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -8,6 +8,9 @@ #include #include #include +#include + +#include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -281,26 +284,18 @@ void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList Ogre::Light *olight = objlist.mLights.back(); olight->setDiffuseColour(color); - bool interior = !(mPtr.isInCell() && mPtr.getCell()->mCell->isExterior()); - - // TODO: Create Controllers for these -#if 0 - // randomize lights animations - info.time = Ogre::Math::RangeRandom(-500, +500); - info.phase = Ogre::Math::RangeRandom(-500, +500); - - if((light->mData.mFlags&ESM::Light::Flicker)) - info.type = LT_Flicker; - else if((light->mData.mFlags&ESM::Light::FlickerSlow)) - info.type = LT_FlickerSlow; - else if((light->mData.mFlags&ESM::Light::Pulse)) - info.type = LT_Pulse; - else if((light->mData.mFlags&ESM::Light::PulseSlow)) - info.type = LT_PulseSlow; - else - info.type = LT_Normal; -#endif + Ogre::ControllerValueRealPtr src(Ogre::ControllerManager::getSingleton().getFrameTimeSource()); + Ogre::ControllerValueRealPtr dest(OGRE_NEW OEngine::Render::LightValue(olight, color)); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW OEngine::Render::LightFunction( + (light->mData.mFlags&ESM::Light::Flicker) ? OEngine::Render::LT_Flicker : + (light->mData.mFlags&ESM::Light::FlickerSlow) ? OEngine::Render::LT_FlickerSlow : + (light->mData.mFlags&ESM::Light::Pulse) ? OEngine::Render::LT_Pulse : + (light->mData.mFlags&ESM::Light::PulseSlow) ? OEngine::Render::LT_PulseSlow : + OEngine::Render::LT_Normal + )); + objlist.mControllers.push_back(Ogre::Controller(src, dest, func)); + bool interior = !(mPtr.isInCell() && mPtr.getCell()->mCell->isExterior()); bool quadratic = fallback->getFallbackBool("LightAttenuation_OutQuadInLin") ? !interior : fallback->getFallbackBool("LightAttenuation_UseQuadratic"); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 541509fb6..ceda463ff 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -52,6 +52,7 @@ protected: virtual void setValue(Ogre::Real value); }; + class NullAnimationValue : public Ogre::ControllerValue { public: @@ -61,6 +62,7 @@ protected: { } }; + struct AnimSource : public Ogre::AnimationAlloc { NifOgre::TextKeyMap mTextKeys; std::vector > mControllers[sNumGroups]; diff --git a/libs/openengine/ogre/lights.cpp b/libs/openengine/ogre/lights.cpp new file mode 100644 index 000000000..52aca6a70 --- /dev/null +++ b/libs/openengine/ogre/lights.cpp @@ -0,0 +1,118 @@ +#include "lights.hpp" + +#include +#include + +namespace OEngine { +namespace Render { + + +LightFunction::LightFunction(LightType type) + : ControllerFunction(true) + , mType(type) + , mPhase(Ogre::Math::RangeRandom(-500.0f, +500.0f)) + , mDirection(1.0f) +{ +} + +Ogre::Real LightFunction::pulseAmplitude(Ogre::Real time) +{ + return std::sin(time); +} + +Ogre::Real LightFunction::flickerAmplitude(Ogre::Real time) +{ + static const float fb = 1.17024f; + static const float f[3] = { 1.5708f, 4.18774f, 5.19934f }; + static const float o[3] = { 0.804248f, 2.11115f, 3.46832f }; + static const float m[3] = { 1.0f, 0.785f, 0.876f }; + static const float s = 0.394f; + + float v = 0.0f; + for(int i = 0;i < 3;++i) + v += std::sin(fb*time*f[i] + o[1])*m[i]; + return v * s; +} + +Ogre::Real LightFunction::flickerFrequency(Ogre::Real phase) +{ + static const float fa = 0.785398f; + static const float tdo = 0.94f; + static const float tdm = 2.48f; + + return tdo + tdm*std::sin(fa * phase); +} + +Ogre::Real LightFunction::calculate(Ogre::Real value) +{ + Ogre::Real brightness = 1.0f; + float cycle_time; + float time_distortion; + + if(mType == LT_Pulse || mType == LT_PulseSlow) + { + cycle_time = 2.0f * Ogre::Math::PI; + time_distortion = 20.0f; + } + else + { + static const float fa = 0.785398f; + static const float phase_wavelength = 120.0f * 3.14159265359f / fa; + + cycle_time = 500.0f; + mPhase = std::fmod(mPhase + value, phase_wavelength); + time_distortion = flickerFrequency(mPhase); + } + + mDeltaCount += mDirection*value*time_distortion; + if(mDirection > 0 && mDeltaCount > +cycle_time) + { + mDirection = -1.0f; + mDeltaCount = 2.0f*cycle_time - mDeltaCount; + } + if(mDirection < 0 && mDeltaCount < -cycle_time) + { + mDirection = +1.0f; + mDeltaCount = -2.0f*cycle_time - mDeltaCount; + } + + static const float fast = 4.0f/1.0f; + static const float slow = 1.0f/1.0f; + + // These formulas are just guesswork, but they work pretty well + if(mType == LT_Normal) + { + // Less than 1/255 light modifier for a constant light: + brightness = 1.0 + flickerAmplitude(mDeltaCount*slow)/255.0f; + } + else if(mType == LT_Flicker) + brightness = 0.75 + flickerAmplitude(mDeltaCount*fast)*0.25; + else if(mType == LT_FlickerSlow) + brightness = 0.75 + flickerAmplitude(mDeltaCount*slow)*0.25; + else if(mType == LT_Pulse) + brightness = 1.0 + pulseAmplitude(mDeltaCount*fast)*0.25; + else if(mType == LT_PulseSlow) + brightness = 1.0 + pulseAmplitude(mDeltaCount*slow)*0.25; + + return brightness; +} + + +LightValue::LightValue(Ogre::Light *light, const Ogre::ColourValue &color) + : mTarget(light) + , mColor(color) +{ +} + +Ogre::Real LightValue::getValue() const +{ + return 0.0f; +} + +void LightValue::setValue(Ogre::Real value) +{ + mTarget->setDiffuseColour(mColor * value); +} + +} +} diff --git a/libs/openengine/ogre/lights.hpp b/libs/openengine/ogre/lights.hpp new file mode 100644 index 000000000..c63f16425 --- /dev/null +++ b/libs/openengine/ogre/lights.hpp @@ -0,0 +1,51 @@ +#ifndef OENGINE_OGRE_LIGHTS_H +#define OENGINE_OGRE_LIGHTS_H + +#include +#include + +/* + * Controller classes to handle pulsing and flicker lights + */ + +namespace OEngine { +namespace Render { + enum LightType { + LT_Normal, + LT_Flicker, + LT_FlickerSlow, + LT_Pulse, + LT_PulseSlow + }; + + class LightFunction : public Ogre::ControllerFunction + { + LightType mType; + Ogre::Real mPhase; + Ogre::Real mDirection; + + static Ogre::Real pulseAmplitude(Ogre::Real time); + + static Ogre::Real flickerAmplitude(Ogre::Real time); + static Ogre::Real flickerFrequency(Ogre::Real phase); + + public: + LightFunction(LightType type); + virtual Ogre::Real calculate(Ogre::Real value); + }; + + class LightValue : public Ogre::ControllerValue + { + Ogre::Light *mTarget; + Ogre::ColourValue mColor; + + public: + LightValue(Ogre::Light *light, const Ogre::ColourValue &color); + + virtual Ogre::Real getValue() const; + virtual void setValue(Ogre::Real value); + }; +} +} + +#endif From 48784c7e2ffb56314cd78c7e84f084f77d9a6094 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 6 Aug 2013 01:00:46 -0700 Subject: [PATCH 11/15] Use for_each and functors to avoid some for loops --- apps/openmw/mwrender/animation.cpp | 44 ++++++++++++++++++--------- apps/openmw/mwrender/npcanimation.cpp | 24 ++++++++++----- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 533a7eab3..737d4da64 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -153,28 +153,44 @@ void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, b } } -void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue) -{ - for(size_t i = 0;i < objlist.mEntities.size();i++) + +class VisQueueSet { + Ogre::uint32 mVisFlags; + Ogre::uint8 mSolidQueue, mTransQueue; + +public: + VisQueueSet(Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue) + : mVisFlags(visflags), mSolidQueue(solidqueue), mTransQueue(transqueue) + { } + + void operator()(Ogre::Entity *entity) const { - Ogre::Entity *ent = objlist.mEntities[i]; - if(visflags != 0) - ent->setVisibilityFlags(visflags); + if(mVisFlags != 0) + entity->setVisibilityFlags(mVisFlags); - for(unsigned int j = 0;j < ent->getNumSubEntities();++j) + unsigned int numsubs = entity->getNumSubEntities(); + for(unsigned int i = 0;i < numsubs;++i) { - Ogre::SubEntity* subEnt = ent->getSubEntity(j); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? transqueue : solidqueue); + Ogre::SubEntity* subEnt = entity->getSubEntity(i); + subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? mTransQueue : mSolidQueue); } } - for(size_t i = 0;i < objlist.mParticles.size();i++) + + void operator()(Ogre::ParticleSystem *psys) const { - Ogre::ParticleSystem *part = objlist.mParticles[i]; - if(visflags != 0) - part->setVisibilityFlags(visflags); + if(mVisFlags != 0) + psys->setVisibilityFlags(mVisFlags); // TODO: Check particle material for actual transparency - part->setRenderQueueGroup(transqueue); + psys->setRenderQueueGroup(mTransQueue); } +}; + +void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue) +{ + std::for_each(objlist.mEntities.begin(), objlist.mEntities.end(), + VisQueueSet(visflags, solidqueue, transqueue)); + std::for_each(objlist.mParticles.begin(), objlist.mParticles.end(), + VisQueueSet(visflags, solidqueue, transqueue)); } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 2d3cea12f..971cf58fe 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -389,18 +389,25 @@ void NpcAnimation::updateParts(bool forceupdate) } } +class SetObjectGroup { + int mGroup; + +public: + SetObjectGroup(int group) : mGroup(group) { } + + void operator()(Ogre::MovableObject *obj) const + { + obj->getUserObjectBindings().setUserAny(Ogre::Any(mGroup)); + } +}; + NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) { NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); setRenderProperties(objects, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha); - for(size_t i = 0;i < objects.mEntities.size();i++) - { - Ogre::Entity *ent = objects.mEntities[i]; - ent->getUserObjectBindings().setUserAny(Ogre::Any(group)); - } - for(size_t i = 0;i < objects.mParticles.size();i++) - objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); + std::for_each(objects.mEntities.begin(), objects.mEntities.end(), SetObjectGroup(group)); + std::for_each(objects.mParticles.begin(), objects.mParticles.end(), SetObjectGroup(group)); if(objects.mSkelBase) { @@ -576,7 +583,8 @@ void NpcAnimation::showWeapons(bool showWeapon) mWeapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if(mWeapon != inv.end()) // special case for weapons { - std::string mesh = MWWorld::Class::get(*mWeapon).getModel(*mWeapon); + MWWorld::Ptr weapon = *mWeapon; + std::string mesh = MWWorld::Class::get(weapon).getModel(weapon); addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh); } } From a9dca21d052ba857b7b76b5b0877fc15801f8e38 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 6 Aug 2013 01:41:47 -0700 Subject: [PATCH 12/15] Create the Animation SceneNode in the constructor --- apps/openmw/mwrender/activatoranimation.cpp | 6 +++--- apps/openmw/mwrender/animation.cpp | 8 ++++---- apps/openmw/mwrender/animation.hpp | 4 ++-- apps/openmw/mwrender/creatureanimation.cpp | 4 ++-- apps/openmw/mwrender/npcanimation.cpp | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index f3d0ec3ff..7f4be9a68 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -12,16 +12,16 @@ ActivatorAnimation::~ActivatorAnimation() } ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) - : Animation(ptr) + : Animation(ptr, ptr.getRefData().getBaseNode()) { MWWorld::LiveCellRef *ref = mPtr.get(); - assert (ref->mBase != NULL); + assert(ref->mBase != NULL); if(!ref->mBase->mModel.empty()) { const std::string name = "meshes\\"+ref->mBase->mModel; - setObjectRoot(mPtr.getRefData().getBaseNode(), name, false); + setObjectRoot(name, false); setRenderProperties(mObjectRoot, RV_Misc, RQG_Main, RQG_Alpha); addAnimSource(name); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 737d4da64..a91679dc6 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -58,7 +58,7 @@ void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectL objects.mSkelBase = NULL; } -Animation::Animation(const MWWorld::Ptr &ptr) +Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node) : mPtr(ptr) , mCamera(NULL) , mInsert(NULL) @@ -71,6 +71,8 @@ Animation::Animation(const MWWorld::Ptr &ptr) { for(size_t i = 0;i < sNumGroups;i++) mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this)); + mInsert = node ? node->createChildSceneNode() : + mPtr.getRefData().getBaseNode()->createChildSceneNode(); } Animation::~Animation() @@ -85,11 +87,9 @@ Animation::~Animation() } -void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly) +void Animation::setObjectRoot(const std::string &model, bool baseonly) { OgreAssert(mAnimSources.empty(), "Setting object root while animation sources are set!"); - if(!mInsert) - mInsert = node->createChildSceneNode(); std::string mdlname = Misc::StringUtils::lowerCase(model); std::string::size_type p = mdlname.rfind('\\'); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index ceda463ff..42f57d5f1 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -153,7 +153,7 @@ protected: * Note that you must make sure all animation sources are cleared before reseting the object * root. All nodes previously retrieved with getNode will also become invalidated. */ - void setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly); + void setObjectRoot(const std::string &model, bool baseonly); /* Adds the keyframe controllers in the specified model as a new animation source. Note that * the filename portion of the provided model name will be prepended with 'x', and the .nif @@ -170,7 +170,7 @@ protected: void clearAnimSources(); public: - Animation(const MWWorld::Ptr &ptr); + Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node); virtual ~Animation(); void updatePtr(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 7817c23c9..c3ad512dd 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -12,7 +12,7 @@ CreatureAnimation::~CreatureAnimation() } CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) - : Animation(ptr) + : Animation(ptr, ptr.getRefData().getBaseNode()) { MWWorld::LiveCellRef *ref = mPtr.get(); @@ -21,7 +21,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) { std::string model = "meshes\\"+ref->mBase->mModel; - setObjectRoot(mPtr.getRefData().getBaseNode(), model, false); + setObjectRoot(model, false); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); if((ref->mBase->mFlags&ESM::Creature::Biped)) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 971cf58fe..24f77a512 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -63,7 +63,7 @@ NpcAnimation::~NpcAnimation() NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags, ViewMode viewMode) - : Animation(ptr), + : Animation(ptr, node), mStateID(-1), mTimeToChange(0), mVisibilityFlags(visibilityFlags), @@ -106,7 +106,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor std::string smodel = (viewMode != VM_FirstPerson) ? (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif") : (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif") ; - setObjectRoot(node, smodel, true); + setObjectRoot(smodel, true); if(mViewMode != VM_FirstPerson) { @@ -147,7 +147,7 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) std::string smodel = (viewMode != VM_FirstPerson) ? (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif") : (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif") ; - setObjectRoot(mInsert->getParentSceneNode(), smodel, true); + setObjectRoot(smodel, true); if(mViewMode != VM_FirstPerson) { From 3555476dfdb591f86c7d5470b44927a259074b1d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 6 Aug 2013 19:45:07 -0700 Subject: [PATCH 13/15] Use Animation-derived objects for generic objects --- apps/openmw/mwclass/light.cpp | 7 +- apps/openmw/mwrender/animation.cpp | 123 +++++++++- apps/openmw/mwrender/animation.hpp | 16 +- apps/openmw/mwrender/objects.cpp | 375 ++++++----------------------- apps/openmw/mwrender/objects.hpp | 20 +- 5 files changed, 216 insertions(+), 325 deletions(-) diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index b5ad0fe66..904931ab8 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -34,11 +34,10 @@ namespace MWClass MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - - if (!model.empty()) - objects.insertMesh(ptr, "meshes\\" + model, true); + if(!model.empty()) + objects.insertMesh(ptr, "meshes\\" + model); else - objects.insertLight(ptr); + objects.insertMesh(ptr, ""); } void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a91679dc6..7e1a0aebe 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -21,6 +22,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" +#include "renderconst.hpp" + namespace MWRender { @@ -91,6 +94,12 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) { OgreAssert(mAnimSources.empty(), "Setting object root while animation sources are set!"); + mSkelBase = NULL; + destroyObjectList(mInsert->getCreator(), mObjectRoot); + + if(model.empty()) + return; + std::string mdlname = Misc::StringUtils::lowerCase(model); std::string::size_type p = mdlname.rfind('\\'); if(p == std::string::npos) @@ -105,9 +114,6 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) Misc::StringUtils::toLower(mdlname); } - mSkelBase = NULL; - destroyObjectList(mInsert->getCreator(), mObjectRoot); - mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) : NifOgre::Loader::createObjectBase(mInsert, mdlname)); if(mObjectRoot.mSkelBase) @@ -157,16 +163,18 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) class VisQueueSet { Ogre::uint32 mVisFlags; Ogre::uint8 mSolidQueue, mTransQueue; + Ogre::Real mDist; public: - VisQueueSet(Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue) - : mVisFlags(visflags), mSolidQueue(solidqueue), mTransQueue(transqueue) + VisQueueSet(Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist) + : mVisFlags(visflags), mSolidQueue(solidqueue), mTransQueue(transqueue), mDist(dist) { } void operator()(Ogre::Entity *entity) const { if(mVisFlags != 0) entity->setVisibilityFlags(mVisFlags); + entity->setRenderingDistance(mDist); unsigned int numsubs = entity->getNumSubEntities(); for(unsigned int i = 0;i < numsubs;++i) @@ -180,17 +188,18 @@ public: { if(mVisFlags != 0) psys->setVisibilityFlags(mVisFlags); + psys->setRenderingDistance(mDist); // TODO: Check particle material for actual transparency psys->setRenderQueueGroup(mTransQueue); } }; -void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue) +void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist) { std::for_each(objlist.mEntities.begin(), objlist.mEntities.end(), - VisQueueSet(visflags, solidqueue, transqueue)); + VisQueueSet(visflags, solidqueue, transqueue, dist)); std::for_each(objlist.mParticles.begin(), objlist.mParticles.end(), - VisQueueSet(visflags, solidqueue, transqueue)); + VisQueueSet(visflags, solidqueue, transqueue, dist)); } @@ -903,6 +912,43 @@ void Animation::showWeapons(bool showWeapon) { } + +class ToggleLight { + bool mEnable; + +public: + ToggleLight(bool enable) : mEnable(enable) { } + + void operator()(Ogre::Light *light) const + { light->setVisible(mEnable); } +}; + +void Animation::enableLights(bool enable) +{ + std::for_each(mObjectRoot.mLights.begin(), mObjectRoot.mLights.end(), ToggleLight(enable)); +} + + +class MergeBounds { + Ogre::AxisAlignedBox *mBounds; + +public: + MergeBounds(Ogre::AxisAlignedBox *bounds) : mBounds(bounds) { } + + void operator()(Ogre::MovableObject *obj) + { + mBounds->merge(obj->getWorldBoundingBox(true)); + } +}; + +Ogre::AxisAlignedBox Animation::getWorldBounds() +{ + Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; + std::for_each(mObjectRoot.mEntities.begin(), mObjectRoot.mEntities.end(), MergeBounds(&bounds)); + return bounds; +} + + bool Animation::isPriorityActive(int priority) const { for (AnimStateMap::const_iterator it = mStates.begin(); it != mStates.end(); ++it) @@ -931,4 +977,65 @@ void Animation::detachObjectFromBone(Ogre::MovableObject *obj) mSkelBase->detachObjectFromBone(obj); } + +ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model, bool isStatic) + : Animation(ptr, ptr.getRefData().getBaseNode()) +{ + setObjectRoot(model, false); + + Ogre::AxisAlignedBox bounds = getWorldBounds(); + + Ogre::Vector3 extents = bounds.getSize(); + extents *= mInsert->getParentSceneNode()->getScale(); + float size = std::max(std::max(extents.x, extents.y), extents.z); + + bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && + Settings::Manager::getBool("limit small object distance", "Viewing distance"); + // do not fade out doors. that will cause holes and look stupid + if(ptr.getTypeName().find("Door") != std::string::npos) + small = false; + + float dist = small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0.0f; + setRenderProperties(mObjectRoot, isStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc, + RQG_Main, RQG_Alpha, dist); +} + +void ObjectAnimation::addLight(const ESM::Light *light) +{ + addExtraLight(mInsert->getCreator(), mObjectRoot, light); +} + + +class FindEntityTransparency { +public: + bool operator()(Ogre::Entity *ent) const + { + unsigned int numsubs = ent->getNumSubEntities(); + for(unsigned int i = 0;i < numsubs;++i) + { + if(ent->getSubEntity(i)->getMaterial()->isTransparent()) + return true; + } + return false; + } +}; + +bool ObjectAnimation::canBatch() const +{ + if(!mObjectRoot.mParticles.empty() || !mObjectRoot.mLights.empty() || !mObjectRoot.mControllers.empty()) + return false; + return std::find_if(mObjectRoot.mEntities.begin(), mObjectRoot.mEntities.end(), + FindEntityTransparency()) == mObjectRoot.mEntities.end(); +} + +void ObjectAnimation::fillBatch(Ogre::StaticGeometry *sg) +{ + std::vector::reverse_iterator iter = mObjectRoot.mEntities.rbegin(); + for(;iter != mObjectRoot.mEntities.rend();++iter) + { + Ogre::Node *node = (*iter)->getParentNode(); + sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); + } +} + } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 42f57d5f1..74e3c503f 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -165,7 +165,7 @@ protected: static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); - static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue); + static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist=0.0f); void clearAnimSources(); @@ -229,6 +229,10 @@ public: virtual void showWeapons(bool showWeapon); + void enableLights(bool enable); + + Ogre::AxisAlignedBox getWorldBounds(); + void setCamera(Camera *cam) { mCamera = cam; } @@ -241,5 +245,15 @@ public: void detachObjectFromBone(Ogre::MovableObject *obj); }; +class ObjectAnimation : public Animation { +public: + ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model, bool isStatic); + + void addLight(const ESM::Light *light); + + bool canBatch() const; + void fillBatch(Ogre::StaticGeometry *sg); +}; + } #endif diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index bede95a26..16d0bedae 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -18,6 +18,7 @@ #include "../mwworld/class.hpp" #include "renderconst.hpp" +#include "animation.hpp" using namespace MWRender; float Objects::lightLinearValue() @@ -124,73 +125,41 @@ void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) mIsStatic = static_; } -void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light) +void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) { Ogre::SceneNode* insert = ptr.getRefData().getBaseNode(); assert(insert); - Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; - NifOgre::ObjectList objects = NifOgre::Loader::createObjects(insert, mesh); - for(size_t i = 0;i < objects.mEntities.size();i++) - bounds.merge(objects.mEntities[i]->getWorldBoundingBox(true)); + std::auto_ptr anim(new ObjectAnimation(ptr, mesh, mIsStatic)); + Ogre::AxisAlignedBox bounds = anim->getWorldBounds(); Ogre::Vector3 extents = bounds.getSize(); extents *= insert->getScale(); float size = std::max(std::max(extents.x, extents.y), extents.z); - bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && Settings::Manager::getBool("limit small object distance", "Viewing distance"); - + bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && + Settings::Manager::getBool("limit small object distance", "Viewing distance"); // do not fade out doors. that will cause holes and look stupid - if (ptr.getTypeName().find("Door") != std::string::npos) + if(ptr.getTypeName().find("Door") != std::string::npos) small = false; if (mBounds.find(ptr.getCell()) == mBounds.end()) mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL; mBounds[ptr.getCell()].merge(bounds); - bool anyTransparency = false; - for(size_t i = 0;!anyTransparency && i < objects.mEntities.size();i++) - { - Ogre::Entity *ent = objects.mEntities[i]; - for(unsigned int i=0;!anyTransparency && i < ent->getNumSubEntities(); ++i) - { - anyTransparency = ent->getSubEntity(i)->getMaterial()->isTransparent(); - } - } + if(ptr.getTypeName() == typeid(ESM::Light).name()) + anim->addLight(ptr.get()->mBase); - if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects") || - anyTransparency || !objects.mParticles.empty() || !objects.mLights.empty()) - { - for(size_t i = 0;i < objects.mEntities.size();i++) - { - Ogre::Entity *ent = objects.mEntities[i]; - for(unsigned int i=0; i < ent->getNumSubEntities(); ++i) - { - Ogre::SubEntity* subEnt = ent->getSubEntity(i); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); - } - ent->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0); - ent->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc); - } - for(size_t i = 0;i < objects.mParticles.size();i++) - { - Ogre::ParticleSystem *part = objects.mParticles[i]; - // TODO: Check the particle system's material for actual transparency - part->setRenderQueueGroup(RQG_Alpha); - part->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0); - part->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc); - } - } - else + if(mIsStatic && Settings::Manager::getBool("use static geometry", "Objects") && anim->canBatch()) { Ogre::StaticGeometry* sg = 0; if (small) { - if( mStaticGeometrySmall.find(ptr.getCell()) == mStaticGeometrySmall.end()) + if(mStaticGeometrySmall.find(ptr.getCell()) == mStaticGeometrySmall.end()) { - uniqueID = uniqueID +1; - sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID)); + uniqueID = uniqueID+1; + sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID)); mStaticGeometrySmall[ptr.getCell()] = sg; sg->setRenderingDistance(Settings::Manager::getInt("small object distance", "Viewing distance")); @@ -200,11 +169,10 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool } else { - if( mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end()) + if(mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end()) { - - uniqueID = uniqueID +1; - sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID)); + uniqueID = uniqueID+1; + sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID)); mStaticGeometry[ptr.getCell()] = sg; } else @@ -225,155 +193,75 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool sg->setRenderQueueGroup(RQG_Main); - std::vector::reverse_iterator iter = objects.mEntities.rbegin(); - while(iter != objects.mEntities.rend()) - { - Ogre::Node *node = (*iter)->getParentNode(); - sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); - - (*iter)->detachFromParent(); - mRenderer.getScene()->destroyEntity(*iter); - ++iter; - } + anim->fillBatch(sg); + /* TODO: We could hold on to this and just detach it from the scene graph, so if the Ptr + * ever needs to modify we can reattach it and rebuild the StaticGeometry object without + * it. Would require associating the Ptr with the StaticGeometry. */ + anim.reset(); } - if (light) - { - insertLight(ptr, objects.mSkelBase, bounds.getCenter() - insert->_getDerivedPosition()); - } + if(anim.get() != NULL) + mObjects.insert(std::make_pair(ptr, anim.release())); } -void Objects::insertLight (const MWWorld::Ptr& ptr, Ogre::Entity* skelBase, Ogre::Vector3 fallbackCenter) +bool Objects::deleteObject (const MWWorld::Ptr& ptr) { - Ogre::SceneNode* insert = mRenderer.getScene()->getSceneNode(ptr.getRefData().getHandle()); - assert(insert); - - MWWorld::LiveCellRef *ref = ptr.get(); - - const int color = ref->mBase->mData.mColor; - const float r = ((color >> 0) & 0xFF) / 255.0f; - const float g = ((color >> 8) & 0xFF) / 255.0f; - const float b = ((color >> 16) & 0xFF) / 255.0f; - const float radius = float (ref->mBase->mData.mRadius); - - Ogre::Light *light = mRenderer.getScene()->createLight(); - light->setDiffuseColour (r, g, b); - - LightInfo info; - info.name = light->getName(); - info.radius = radius; - info.colour = Ogre::ColourValue(r, g, b); - - if (ref->mBase->mData.mFlags & ESM::Light::Negative) - info.colour *= -1; - - info.interior = !ptr.getCell()->mCell->isExterior(); - - if (ref->mBase->mData.mFlags & ESM::Light::Flicker) - info.type = LT_Flicker; - else if (ref->mBase->mData.mFlags & ESM::Light::FlickerSlow) - info.type = LT_FlickerSlow; - else if (ref->mBase->mData.mFlags & ESM::Light::Pulse) - info.type = LT_Pulse; - else if (ref->mBase->mData.mFlags & ESM::Light::PulseSlow) - info.type = LT_PulseSlow; - else - info.type = LT_Normal; + if(!ptr.getRefData().getBaseNode()) + return true; - // randomize lights animations - info.time = Ogre::Math::RangeRandom(-500, +500); - info.phase = Ogre::Math::RangeRandom(-500, +500); - - bool quadratic = lightOutQuadInLin() ? !info.interior : lightQuadratic(); - - // with the standard 1 / (c + d*l + d*d*q) equation the attenuation factor never becomes zero, - // so we ignore lights if their attenuation falls below this factor. - const float threshold = 0.03; - - if (!quadratic) - { - float r = radius * lightLinearRadiusMult(); - float attenuation = lightLinearValue() / r; - float activationRange = 1 / (threshold * attenuation); - light->setAttenuation(activationRange, 0, attenuation, 0); - } - else + PtrAnimationMap::iterator iter = mObjects.find(ptr); + if(iter != mObjects.end()) { - float r = radius * lightQuadraticRadiusMult(); - float attenuation = lightQuadraticValue() / std::pow(r, 2); - float activationRange = std::sqrt(1 / (threshold * attenuation)); - light->setAttenuation(activationRange, 0, 0, attenuation); - } + delete iter->second; + mObjects.erase(iter); - // If there's an AttachLight bone, attach the light to that, otherwise attach it to the base scene node - if (skelBase && skelBase->getSkeleton ()->hasBone ("AttachLight")) - { - skelBase->attachObjectToBone ("AttachLight", light); - } - else - { - Ogre::SceneNode* childNode = insert->createChildSceneNode (fallbackCenter); - childNode->attachObject(light); + mRenderer.getScene()->destroySceneNode(ptr.getRefData().getBaseNode()); + ptr.getRefData().setBaseNode(0); + return true; } - mLights.push_back(info); + return false; } -bool Objects::deleteObject (const MWWorld::Ptr& ptr) -{ - if (Ogre::SceneNode *base = ptr.getRefData().getBaseNode()) - { - Ogre::SceneNode *parent = base->getParentSceneNode(); - - for (std::map::const_iterator iter ( - mCellSceneNodes.begin()); iter!=mCellSceneNodes.end(); ++iter) - if (iter->second==parent) - { - clearSceneNode (base); - base->removeAndDestroyAllChildren(); - mRenderer.getScene()->destroySceneNode (base); - ptr.getRefData().setBaseNode (0); - return true; - } - - return false; - } - - return true; -} void Objects::removeCell(MWWorld::Ptr::CellStore* store) { - if(mCellSceneNodes.find(store) != mCellSceneNodes.end()) + for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();) { - Ogre::SceneNode* base = mCellSceneNodes[store]; - - for (int i=0; inumChildren(); ++i) - clearSceneNode (static_cast (base->getChild (i))); - - base->removeAndDestroyAllChildren(); - mCellSceneNodes.erase(store); - mRenderer.getScene()->destroySceneNode(base); - base = 0; + if(iter->first.getCell() == store) + { + delete iter->second; + mObjects.erase(iter++); + } + else + ++iter; } - if(mStaticGeometry.find(store) != mStaticGeometry.end()) + std::map::iterator geom = mStaticGeometry.find(store); + if(geom != mStaticGeometry.end()) { - Ogre::StaticGeometry* sg = mStaticGeometry[store]; - mStaticGeometry.erase(store); - mRenderer.getScene()->destroyStaticGeometry (sg); - sg = 0; + Ogre::StaticGeometry *sg = geom->second; + mStaticGeometry.erase(geom); + mRenderer.getScene()->destroyStaticGeometry(sg); } - if(mStaticGeometrySmall.find(store) != mStaticGeometrySmall.end()) + + geom = mStaticGeometrySmall.find(store); + if(geom != mStaticGeometrySmall.end()) { - Ogre::StaticGeometry* sg = mStaticGeometrySmall[store]; + Ogre::StaticGeometry *sg = geom->second; mStaticGeometrySmall.erase(store); - mRenderer.getScene()->destroyStaticGeometry (sg); - sg = 0; + mRenderer.getScene()->destroyStaticGeometry(sg); } - if(mBounds.find(store) != mBounds.end()) - mBounds.erase(store); + mBounds.erase(store); + + std::map::iterator cell = mCellSceneNodes.find(store); + if(cell != mCellSceneNodes.end()) + { + cell->second->removeAndDestroyAllChildren(); + mRenderer.getScene()->destroySceneNode(cell->second); + mCellSceneNodes.erase(cell); + } } void Objects::buildStaticGeometry(MWWorld::Ptr::CellStore& cell) @@ -397,146 +285,23 @@ Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell) void Objects::enableLights() { - std::vector::iterator it = mLights.begin(); - while (it != mLights.end()) - { - if (mRootNode->getCreator()->hasLight(it->name)) - { - mRootNode->getCreator()->getLight(it->name)->setVisible(true); - ++it; - } - else - it = mLights.erase(it); - } + PtrAnimationMap::const_iterator it = mObjects.begin(); + for(;it != mObjects.end();it++) + it->second->enableLights(true); } void Objects::disableLights() { - std::vector::iterator it = mLights.begin(); - while (it != mLights.end()) - { - if (mRootNode->getCreator()->hasLight(it->name)) - { - mRootNode->getCreator()->getLight(it->name)->setVisible(false); - ++it; - } - else - it = mLights.erase(it); - } -} - -namespace MWRender -{ - namespace Pulse - { - static float amplitude (float phase) - { - return sin (phase); - } - } - - namespace Flicker - { - static const float fa = 0.785398f; - static const float fb = 1.17024f; - - static const float tdo = 0.94f; - static const float tdm = 2.48f; - - static const float f [3] = { 1.5708f, 4.18774f, 5.19934f }; - static const float o [3] = { 0.804248f, 2.11115f, 3.46832f }; - static const float m [3] = { 1.0f, 0.785f, 0.876f }; - static const float s = 0.394f; - - static const float phase_wavelength = 120.0f * 3.14159265359f / fa; - - static float frequency (float x) - { - return tdo + tdm * sin (fa * x); - } - - static float amplitude (float x) - { - float v = 0.0f; - for (int i = 0; i < 3; ++i) - v += sin (fb*x*f[i] + o[1])*m[i]; - return v * s; - } - } + PtrAnimationMap::const_iterator it = mObjects.begin(); + for(;it != mObjects.end();it++) + it->second->enableLights(false); } void Objects::update(const float dt) { - std::vector::iterator it = mLights.begin(); - while (it != mLights.end()) - { - if (mRootNode->getCreator()->hasLight(it->name)) - { - Ogre::Light* light = mRootNode->getCreator()->getLight(it->name); - - float brightness; - float cycle_time; - float time_distortion; - - if ((it->type == LT_Pulse) && (it->type == LT_PulseSlow)) - { - cycle_time = 2 * Ogre::Math::PI; - time_distortion = 20.0f; - } - else - { - cycle_time = 500.0f; - it->phase = fmod (it->phase + dt, Flicker::phase_wavelength); - time_distortion = Flicker::frequency (it->phase); - } - - it->time += it->dir*dt*time_distortion; - if (it->dir > 0 && it->time > +cycle_time) - { - it->dir = -1.0f; - it->time = +2*cycle_time - it->time; - } - if (it->dir < 0 && it->time < -cycle_time) - { - it->dir = +1.0f; - it->time = -2*cycle_time - it->time; - } - - static const float fast = 4.0f/1.0f; - static const float slow = 1.0f/1.0f; - - // These formulas are just guesswork, but they work pretty well - if (it->type == LT_Normal) - { - // Less than 1/255 light modifier for a constant light: - brightness = (const float)(1.0 + Flicker::amplitude(it->time*slow) / 255.0 ); - } - else if (it->type == LT_Flicker) - { - brightness = (const float)(0.75 + Flicker::amplitude(it->time*fast) * 0.25); - } - else if (it->type == LT_FlickerSlow) - { - brightness = (const float)(0.75 + Flicker::amplitude(it->time*slow) * 0.25); - } - else if (it->type == LT_Pulse) - { - brightness = (const float)(1.0 + Pulse::amplitude (it->time*fast) * 0.25); - } - else if (it->type == LT_PulseSlow) - { - brightness = (const float)(1.0 + Pulse::amplitude (it->time*slow) * 0.25); - } - else - assert(0 && "Invalid light type"); - - light->setDiffuseColour(it->colour * brightness); - - ++it; - } - else - it = mLights.erase(it); - } + PtrAnimationMap::const_iterator it = mObjects.begin(); + for(;it != mObjects.end();it++) + it->second->runAnimation(dt); } void Objects::rebuildStaticGeometry() diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index d428aae96..1e3ce3ddf 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -15,6 +15,8 @@ namespace MWWorld namespace MWRender{ +class ObjectAnimation; + /// information about light needed for rendering enum LightType { @@ -48,15 +50,20 @@ struct LightInfo }; class Objects{ + typedef std::map PtrAnimationMap; + OEngine::Render::OgreRenderer &mRenderer; - std::map mCellSceneNodes; - std::map mStaticGeometry; - std::map mStaticGeometrySmall; - std::map mBounds; - std::vector mLights; + + std::map mCellSceneNodes; + std::map mStaticGeometry; + std::map mStaticGeometrySmall; + std::map mBounds; + PtrAnimationMap mObjects; + Ogre::SceneNode* mRootNode; bool mIsStatic; static int uniqueID; + MWWorld::Fallback* mFallback; float lightLinearValue(); float lightLinearRadiusMult(); @@ -79,8 +86,7 @@ public: {} ~Objects(){} void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_); - void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light=false); - void insertLight (const MWWorld::Ptr& ptr, Ogre::Entity *skelBase=0, Ogre::Vector3 fallbackCenter=Ogre::Vector3(0.0f)); + void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh); void enableLights(); void disableLights(); From 80d271aeb18770f023e94d0f9f155085e0c905e6 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 7 Aug 2013 01:31:01 -0700 Subject: [PATCH 14/15] Remove unused stuff --- apps/openmw/mwrender/objects.cpp | 53 ----------------------- apps/openmw/mwrender/objects.hpp | 51 +--------------------- apps/openmw/mwrender/renderingmanager.cpp | 2 +- 3 files changed, 3 insertions(+), 103 deletions(-) diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 16d0bedae..6cf40300a 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -21,62 +21,9 @@ #include "animation.hpp" using namespace MWRender; -float Objects::lightLinearValue() -{ - return mFallback->getFallbackFloat("LightAttenuation_LinearValue"); -} -float Objects::lightLinearRadiusMult() -{ - return mFallback->getFallbackFloat("LightAttenuation_LinearRadiusMult"); -} -float Objects::lightQuadraticValue() -{ - return mFallback->getFallbackFloat("LightAttenuation_QuadraticValue"); -} -float Objects::lightQuadraticRadiusMult() -{ - return mFallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult"); -} - -bool Objects::lightOutQuadInLin() -{ - return mFallback->getFallbackBool("LightAttenuation_OutQuadInLin"); -} -bool Objects::lightQuadratic() -{ - return mFallback->getFallbackBool("LightAttenuation_UseQuadratic"); -} int Objects::uniqueID = 0; -void Objects::clearSceneNode (Ogre::SceneNode *node) -{ - for (int i=node->numAttachedObjects()-1; i>=0; --i) - { - Ogre::MovableObject *object = node->getAttachedObject (i); - - // for entities, destroy any objects attached to bones - if (object->getTypeFlags () == Ogre::SceneManager::ENTITY_TYPE_MASK) - { - Ogre::Entity* ent = static_cast(object); - Ogre::Entity::ChildObjectListIterator children = ent->getAttachedObjectIterator (); - while (children.hasMoreElements()) - { - mRenderer.getScene ()->destroyMovableObject (children.getNext ()); - } - } - - node->detachObject (object); - mRenderer.getScene()->destroyMovableObject (object); - } - - Ogre::Node::ChildNodeIterator it = node->getChildIterator (); - while (it.hasMoreElements ()) - { - clearSceneNode(static_cast(it.getNext ())); - } -} - void Objects::setRootNode(Ogre::SceneNode* root) { mRootNode = root; diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 1e3ce3ddf..949486732 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -5,7 +5,6 @@ #include #include -#include "../mwworld/fallback.hpp" namespace MWWorld { @@ -17,38 +16,6 @@ namespace MWRender{ class ObjectAnimation; -/// information about light needed for rendering -enum LightType -{ - // These are all mutually exclusive - LT_Normal=0, - LT_Flicker=1, - LT_FlickerSlow=2, - LT_Pulse=3, - LT_PulseSlow=4 -}; - -struct LightInfo -{ - // Constants - std::string name; // ogre handle - Ogre::ColourValue colour; - float radius; - bool interior; // Does this light belong to an interior or exterior cell - LightType type; - - // Runtime variables - float dir; // direction time is running... - float time; // current time - float phase; // current phase - - LightInfo() : - dir(1.0f), time(0.0f), phase (0.0f), - interior(true), type(LT_Normal), radius(1.0) - { - } -}; - class Objects{ typedef std::map PtrAnimationMap; @@ -64,24 +31,10 @@ class Objects{ bool mIsStatic; static int uniqueID; - MWWorld::Fallback* mFallback; - float lightLinearValue(); - float lightLinearRadiusMult(); - - bool lightQuadratic(); - float lightQuadraticValue(); - float lightQuadraticRadiusMult(); - - bool lightOutQuadInLin(); - - void clearSceneNode (Ogre::SceneNode *node); - ///< Remove all movable objects from \a node. - public: - Objects(OEngine::Render::OgreRenderer& renderer, MWWorld::Fallback* fallback) - : mRenderer (renderer) + Objects(OEngine::Render::OgreRenderer &renderer) + : mRenderer(renderer) , mIsStatic(false) - , mFallback(fallback) , mRootNode(NULL) {} ~Objects(){} diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 65fe5014c..bc5d0ef20 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -57,7 +57,7 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b MWWorld::Fallback* fallback) : mRendering(_rend) , mFallback(fallback) - , mObjects(mRendering, mFallback) + , mObjects(mRendering) , mActors(mRendering, this) , mPlayerAnimation(NULL) , mAmbientMode(0) From b4b095ca723ca69d765e9e9957e5741bdc2877d2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 7 Aug 2013 01:32:36 -0700 Subject: [PATCH 15/15] Don't try to get the center of a null bounding box --- apps/openmw/mwrender/animation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 7e1a0aebe..cac19a9ee 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -355,7 +355,8 @@ void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList bounds.merge(ent->getBoundingBox()); } - Ogre::SceneNode *node = mInsert->createChildSceneNode(bounds.getCenter()); + Ogre::SceneNode *node = bounds.isFinite() ? mInsert->createChildSceneNode(bounds.getCenter()) + : mInsert->createChildSceneNode(); node->attachObject(olight); } }