From 726c93c3651a304c8c3eee06d0871e72272cbc71 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 7 Aug 2013 01:27:16 +0200 Subject: [PATCH 01/22] Terrain fixes to match vanilla better - vertices need to be mapped directly to texels for colormap and blendmap (this also caused seams at cell borders), layer uv scale appears to be 8 not 10 --- apps/openmw/mwrender/terrain.cpp | 40 ++++++++------------------------ files/materials/terrain.shader | 25 ++++++++++++++------ 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index c27dce6cad..139b69c104 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -45,7 +45,7 @@ namespace MWRender // Setting this to 0 seems to cause glitches though. :/ mTerrainGlobals->setMaxPixelError(1); - mTerrainGlobals->setLayerBlendMapSize(32); + mTerrainGlobals->setLayerBlendMapSize(ESM::Land::LAND_TEXTURE_SIZE/2 + 1); //10 (default) didn't seem to be quite enough mTerrainGlobals->setSkirtSize(128); @@ -352,9 +352,9 @@ namespace MWRender } //covert the ltex data into a set of blend maps - for ( int texY = fromY - 1; texY < fromY + size + 1; texY++ ) + for ( int texY = fromY; texY < fromY + size + 1; texY++ ) { - for ( int texX = fromX - 1; texX < fromX + size + 1; texX++ ) + for ( int texX = fromX - 1; texX < fromX + size; texX++ ) // NB we wrap X from the other side because Y is reversed { const uint16_t ltexIndex = getLtexIndexAt(cellX, cellY, texX, texY); @@ -367,7 +367,7 @@ namespace MWRender //while texX is the splat index relative to the entire cell, //relX is relative to the current segment we are splatting - const int relX = texX - fromX; + const int relX = texX - fromX + 1; const int relY = texY - fromY; const int layerIndex = indexes.find(ltexIndex)->second; @@ -375,35 +375,15 @@ namespace MWRender float* const pBlend = terrain->getLayerBlendMap(layerIndex) ->getBlendPointer(); - for ( int y = -1; y < splatSize + 1; y++ ) - { - for ( int x = -1; x < splatSize + 1; x++ ) - { - //Note: Y is reversed - const int splatY = blendMapSize - 1 - relY * splatSize - y; - const int splatX = relX * splatSize + x; + const int splatY = blendMapSize - relY - 1; + const int splatX = relX; - if ( splatX >= 0 && splatX < blendMapSize && - splatY >= 0 && splatY < blendMapSize ) - { - const int index = (splatY)*blendMapSize + splatX; + assert(splatX >= 0 && splatX < blendMapSize); + assert(splatY >= 0 && splatY < blendMapSize); - if ( y >= 0 && y < splatSize && - x >= 0 && x < splatSize ) - { - pBlend[index] = 1; - } - else - { - //this provides a transition shading but also - //rounds off the corners slightly - pBlend[index] = std::min(1.0f, pBlend[index] + 0.5f); - } - } - - } - } + const int index = (splatY)*blendMapSize + splatX; + pBlend[index] = 1; } } diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 58146118e9..1330229570 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -205,7 +205,6 @@ shUniform(float, waterLevel) @shSharedParameter(waterLevel) #endif - SH_START_PROGRAM { @@ -232,35 +231,46 @@ float previousAlpha = 1.f; #endif // Layer calculations +// rescale UV to directly map vertices to texel centers +// TODO: parameterize texel size +float2 blendUV = (UV - 0.5) * (8.0 / (8.0+1.0)) + 0.5; @shForeach(@shPropertyString(num_blendmaps)) - float4 blendValues@shIterator = shSample(blendMap@shIterator, UV); + float4 blendValues@shIterator = shSample(blendMap@shIterator, blendUV); @shEndForeach float3 albedo = float3(0,0,0); + + float2 layerUV = UV * 8; + @shForeach(@shPropertyString(num_layers)) #if IS_FIRST_PASS #if @shIterator == 0 // first layer of first pass is the base layer and doesn't need a blend map - albedo = shSample(diffuseMap0, UV * 10).rgb; + albedo = shSample(diffuseMap0, layerUV).rgb; #else - albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); + albedo = shLerp(albedo, shSample(diffuseMap@shIterator, layerUV).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); #endif #else #if @shIterator == 0 - albedo = shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator); + albedo = shSample(diffuseMap@shIterator, layerUV).rgb, blendValues@shPropertyString(blendmap_component_@shIterator); #else - albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); + albedo = shLerp(albedo, shSample(diffuseMap@shIterator, layerUV).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); #endif previousAlpha *= 1.f-blendValues@shPropertyString(blendmap_component_@shIterator); #endif @shEndForeach shOutputColour(0) = float4(1,1,1,1); + #if COLOUR_MAP - shOutputColour(0).rgb *= shSample(colourMap, UV).rgb; + // Since we're emulating vertex colors here, + // rescale UV to directly map vertices to texel centers. TODO: parameterize texel size + const float colourmapSize = 33.f; + float2 colourUV = (UV - 0.5) * (colourmapSize / (colourmapSize+1.f)) + 0.5; + shOutputColour(0).rgb *= shSample(colourMap, colourUV).rgb; #endif shOutputColour(0).rgb *= albedo; @@ -347,6 +357,7 @@ float previousAlpha = 1.f; #else shOutputColour(0).a = 1.f-previousAlpha; #endif + } #endif From 76e538c22ab89fa409d9e1418652e39b7f1e4e02 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 7 Aug 2013 02:03:23 +0200 Subject: [PATCH 02/22] Unused variable fix --- apps/openmw/mwrender/terrain.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 139b69c104..c3a6b2a73c 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -340,7 +340,6 @@ namespace MWRender assert( (size & (size - 1)) == 0 && "Size must be a power of 2"); const int blendMapSize = terrain->getLayerBlendMapSize(); - const int splatSize = blendMapSize / size; //zero out every map std::map::const_iterator iter; From 3f7daa4884b71dcd35ed8aba9375ac95c115f1ce Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 7 Aug 2013 02:58:03 +0200 Subject: [PATCH 03/22] Forgot to change initTerrainTextures --- apps/openmw/mwrender/terrain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index c3a6b2a73c..1b829a2c25 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -246,9 +246,9 @@ namespace MWRender //cells which may lead to inconsistent results when shading between cells int num = MWBase::Environment::get().getWorld()->getStore().get().getSize(plugin); std::set ltexIndexes; - for ( int y = fromY - 1; y < fromY + size + 1; y++ ) + for ( int y = fromY; y < fromY + size + 1; y++ ) { - for ( int x = fromX - 1; x < fromX + size + 1; x++ ) + for ( int x = fromX - 1; x < fromX + size; x++ ) // NB we wrap X from the other side because Y is reversed { int idx = getLtexIndexAt(cellX, cellY, x, y); // This is a quick hack to prevent the program from trying to fetch textures From eab4e09566b815f615463a1b6fa0d9d6bd3942bd Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 2 Aug 2013 18:21:42 -0700 Subject: [PATCH 04/22] 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 d15e951716..573fe389ca 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 0a547b4d3d..8aa797b0cb 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 0d9399c616..e26c2e2a52 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 4660c6e9a6..d78b1901c4 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 05/22] 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 4dd4f6f365..b9c463432f 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 06/22] 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 93fe495c94..d7b254fee1 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 4e18faf9a3..c7f3ab94a2 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 07/22] 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 30e7b9e1e5..69375b74d2 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 08/22] 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 e63a101602..0232c71c5a 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 09/22] 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 14e2c76a41..2cb92ce2f8 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 10/22] 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 0bb7485723..b3f601270b 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 b92b17ba80..bede95a266 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 45f3cbcd8a..edad13a9a3 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 11/22] 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 b3f601270b..adee771f69 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 0b8f3a02fb..541509fb66 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 0232c71c5a..6611b36947 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 12/22] 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 6611b36947..2d3cea12fc 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 e72fa56ed7..aa51ffbe9d 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 13/22] 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 9fc37b413a..575fecd0c5 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 adee771f69..533a7eab3b 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); + 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()); - - // 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"); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 541509fb66..ceda463ff0 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 0000000000..52aca6a705 --- /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 0000000000..c63f164255 --- /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 14/22] Use for_each and functors to avoid some for loops --- apps/openmw/mwrender/animation.cpp | 46 ++++++++++++++++++--------- apps/openmw/mwrender/npcanimation.cpp | 24 +++++++++----- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 533a7eab3b..737d4da64b 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++) - { - Ogre::Entity *ent = objlist.mEntities[i]; - if(visflags != 0) - ent->setVisibilityFlags(visflags); - for(unsigned int j = 0;j < ent->getNumSubEntities();++j) +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 + { + if(mVisFlags != 0) + entity->setVisibilityFlags(mVisFlags); + + 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 2d3cea12fc..971cf58fee 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 15/22] 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 f3d0ec3ff4..7f4be9a688 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 737d4da64b..a91679dc6d 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 ceda463ff0..42f57d5f1e 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 7817c23c9e..c3ad512ddd 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 971cf58fee..24f77a512a 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 16/22] 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 | 377 ++++++----------------------- apps/openmw/mwrender/objects.hpp | 20 +- 5 files changed, 217 insertions(+), 326 deletions(-) diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index b5ad0fe669..904931ab87 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 a91679dc6d..7e1a0aebe0 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 42f57d5f1e..74e3c503f5 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 bede95a266..16d0bedaea 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()); - } -} - -void Objects::insertLight (const MWWorld::Ptr& ptr, Ogre::Entity* skelBase, Ogre::Vector3 fallbackCenter) -{ - 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; - - // 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 - { - float r = radius * lightQuadraticRadiusMult(); - float attenuation = lightQuadraticValue() / std::pow(r, 2); - float activationRange = std::sqrt(1 / (threshold * attenuation)); - light->setAttenuation(activationRange, 0, 0, attenuation); - } - - // 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); - } - - mLights.push_back(info); + if(anim.get() != NULL) + mObjects.insert(std::make_pair(ptr, anim.release())); } bool Objects::deleteObject (const MWWorld::Ptr& ptr) { - if (Ogre::SceneNode *base = ptr.getRefData().getBaseNode()) + if(!ptr.getRefData().getBaseNode()) + return true; + + PtrAnimationMap::iterator iter = mObjects.find(ptr); + if(iter != mObjects.end()) { - Ogre::SceneNode *parent = base->getParentSceneNode(); + delete iter->second; + mObjects.erase(iter); - 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; + mRenderer.getScene()->destroySceneNode(ptr.getRefData().getBaseNode()); + ptr.getRefData().setBaseNode(0); + return true; } - return true; + return false; } + 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 d428aae96f..1e3ce3ddfc 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 17/22] 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 16d0bedaea..6cf40300a3 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 1e3ce3ddfc..9494867328 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 65fe5014cf..bc5d0ef203 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 18/22] 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 7e1a0aebe0..cac19a9ee3 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); } } From 0458fd553111dedeca0f595d32aa5d8fec496c3f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 7 Aug 2013 03:02:27 -0700 Subject: [PATCH 19/22] Remove unused/unneeded parameters --- apps/openmw/mwclass/apparatus.cpp | 2 +- apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/book.cpp | 2 +- apps/openmw/mwclass/clothing.cpp | 2 +- apps/openmw/mwclass/container.cpp | 2 +- apps/openmw/mwclass/door.cpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/light.cpp | 2 +- apps/openmw/mwclass/lockpick.cpp | 2 +- apps/openmw/mwclass/misc.cpp | 2 +- apps/openmw/mwclass/potion.cpp | 2 +- apps/openmw/mwclass/probe.cpp | 2 +- apps/openmw/mwclass/repair.cpp | 2 +- apps/openmw/mwclass/static.cpp | 2 +- apps/openmw/mwclass/weapon.cpp | 2 +- apps/openmw/mwrender/animation.cpp | 5 +++-- apps/openmw/mwrender/animation.hpp | 2 +- apps/openmw/mwrender/objects.cpp | 11 +++++------ apps/openmw/mwrender/objects.hpp | 5 ++--- 19 files changed, 26 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index b56ed118fa..c45fe9b1cd 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -26,7 +26,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index f8e4dc40af..8dd8bb6d62 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -31,7 +31,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 40ba217338..05a0085f34 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -24,7 +24,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index df05a7910b..884b27aede 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -28,7 +28,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 8180cb6864..db715813d4 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -62,7 +62,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 381ffa2330..ebf2e4bf95 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -30,7 +30,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 2cc607f5fb..6ec11b457b 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -37,7 +37,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 904931ab87..8d9eeae2dd 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -33,7 +33,7 @@ namespace MWClass const std::string &model = ref->mBase->mModel; MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); if(!model.empty()) objects.insertMesh(ptr, "meshes\\" + model); else diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 084490742d..6cca2000c7 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -27,7 +27,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 6b384be992..65d1fda56c 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -43,7 +43,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index c2d2920d0c..87b40ec4b2 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -29,7 +29,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 6b7e432300..1e5c430923 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -27,7 +27,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index e2993029b4..1d3116daa9 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -26,7 +26,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 026eada7b3..ada4736768 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -16,7 +16,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), true); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index db07fb553d..74ed6a8e39 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -34,7 +34,7 @@ namespace MWClass const std::string model = getModel(ptr); if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); + objects.insertBegin(ptr); objects.insertMesh(ptr, model); } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index cac19a9ee3..2baadbed0d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -979,7 +979,7 @@ void Animation::detachObjectFromBone(Ogre::MovableObject *obj) } -ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model, bool isStatic) +ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model) : Animation(ptr, ptr.getRefData().getBaseNode()) { setObjectRoot(model, false); @@ -997,7 +997,8 @@ ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &mod 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, + setRenderProperties(mObjectRoot, (mPtr.getTypeName() == typeid(ESM::Static).name()) ? + (small ? RV_StaticsSmall : RV_Statics) : RV_Misc, RQG_Main, RQG_Alpha, dist); } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 74e3c503f5..b0e4f6dbdf 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -247,7 +247,7 @@ public: class ObjectAnimation : public Animation { public: - ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model, bool isStatic); + ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model); void addLight(const ESM::Light *light); diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 6cf40300a3..6563238d09 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -29,7 +29,7 @@ void Objects::setRootNode(Ogre::SceneNode* root) mRootNode = root; } -void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) +void Objects::insertBegin (const MWWorld::Ptr& ptr) { Ogre::SceneNode* root = mRootNode; Ogre::SceneNode* cellnode; @@ -66,10 +66,7 @@ void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) // Rotates first around z, then y, then x insert->setOrientation(xr*yr*zr); - if (!enabled) - insert->setVisible (false); ptr.getRefData().setBaseNode(insert); - mIsStatic = static_; } void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) @@ -77,7 +74,7 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) Ogre::SceneNode* insert = ptr.getRefData().getBaseNode(); assert(insert); - std::auto_ptr anim(new ObjectAnimation(ptr, mesh, mIsStatic)); + std::auto_ptr anim(new ObjectAnimation(ptr, mesh)); Ogre::AxisAlignedBox bounds = anim->getWorldBounds(); Ogre::Vector3 extents = bounds.getSize(); @@ -97,7 +94,9 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) if(ptr.getTypeName() == typeid(ESM::Light).name()) anim->addLight(ptr.get()->mBase); - if(mIsStatic && Settings::Manager::getBool("use static geometry", "Objects") && anim->canBatch()) + if(ptr.getTypeName() == typeid(ESM::Static).name() && + Settings::Manager::getBool("use static geometry", "Objects") && + anim->canBatch()) { Ogre::StaticGeometry* sg = 0; diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 9494867328..815233a66f 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -28,17 +28,16 @@ class Objects{ PtrAnimationMap mObjects; Ogre::SceneNode* mRootNode; - bool mIsStatic; + static int uniqueID; public: Objects(OEngine::Render::OgreRenderer &renderer) : mRenderer(renderer) - , mIsStatic(false) , mRootNode(NULL) {} ~Objects(){} - void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_); + void insertBegin (const MWWorld::Ptr& ptr); void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh); void enableLights(); From 2b2101958d8927b4d0408f9a64ee418e4d093c99 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 7 Aug 2013 03:30:19 -0700 Subject: [PATCH 20/22] Destroy the scene node we create --- apps/openmw/mwrender/animation.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 2baadbed0d..055d14ecbf 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -74,8 +74,7 @@ Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node) { for(size_t i = 0;i < sNumGroups;i++) mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this)); - mInsert = node ? node->createChildSceneNode() : - mPtr.getRefData().getBaseNode()->createChildSceneNode(); + mInsert = node->createChildSceneNode(); } Animation::~Animation() @@ -86,6 +85,8 @@ Animation::~Animation() Ogre::SceneManager *sceneMgr = mInsert->getCreator(); destroyObjectList(sceneMgr, mObjectRoot); + + sceneMgr->destroySceneNode(mInsert); } } From 076e7d8e164353047f64f7d5129b293a77adff74 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 7 Aug 2013 03:51:57 -0700 Subject: [PATCH 21/22] Make insertBegin internal --- apps/openmw/mwclass/apparatus.cpp | 4 +--- apps/openmw/mwclass/armor.cpp | 4 +--- apps/openmw/mwclass/book.cpp | 4 +--- apps/openmw/mwclass/clothing.cpp | 4 +--- apps/openmw/mwclass/container.cpp | 4 +--- apps/openmw/mwclass/door.cpp | 4 +--- apps/openmw/mwclass/ingredient.cpp | 4 +--- apps/openmw/mwclass/light.cpp | 16 ++++------------ apps/openmw/mwclass/lockpick.cpp | 4 +--- apps/openmw/mwclass/misc.cpp | 4 +--- apps/openmw/mwclass/potion.cpp | 4 +--- apps/openmw/mwclass/probe.cpp | 4 +--- apps/openmw/mwclass/repair.cpp | 4 +--- apps/openmw/mwclass/static.cpp | 4 +--- apps/openmw/mwclass/weapon.cpp | 4 +--- apps/openmw/mwrender/actors.hpp | 4 +++- apps/openmw/mwrender/objects.cpp | 9 ++++----- apps/openmw/mwrender/objects.hpp | 5 +++-- 18 files changed, 28 insertions(+), 62 deletions(-) diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index c45fe9b1cd..b409b4c844 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -25,9 +25,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 8dd8bb6d62..27b3870cbf 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -30,9 +30,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 05a0085f34..05d0ac69f0 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -23,9 +23,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 884b27aede..2485728b12 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -27,9 +27,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index db715813d4..6aad8422ed 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -61,9 +61,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index ebf2e4bf95..cc202865ee 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -29,9 +29,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 6ec11b457b..a53fc5a285 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -36,9 +36,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 8d9eeae2dd..cdd41751f4 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -26,18 +26,10 @@ namespace MWClass { void Light::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - assert (ref->mBase != NULL); - - const std::string &model = ref->mBase->mModel; - - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - if(!model.empty()) - objects.insertMesh(ptr, "meshes\\" + model); - else - objects.insertMesh(ptr, ""); + const std::string model = getModel(ptr); + if(!model.empty()) { + renderingInterface.getObjects().insertModel(ptr, model); + } } void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 6cca2000c7..65af5b52b6 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -26,9 +26,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 65d1fda56c..9345ed9aed 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -42,9 +42,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 87b40ec4b2..51fcd1c13e 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -28,9 +28,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 1e5c430923..c140d9471f 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -26,9 +26,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 1d3116daa9..b5af02efff 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -25,9 +25,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index ada4736768..bd7deca889 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -15,9 +15,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 74ed6a8e39..dce1f288ab 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -33,9 +33,7 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr); - objects.insertMesh(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model); } } diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index af58883983..4547db9ad2 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -27,6 +27,8 @@ namespace MWRender CellSceneNodeMap mCellSceneNodes; PtrAnimationMap mAllActors; + void insertBegin(const MWWorld::Ptr &ptr); + public: Actors(OEngine::Render::OgreRenderer& _rend, MWRender::RenderingManager* rendering) : mRend(_rend) @@ -36,7 +38,7 @@ namespace MWRender ~Actors(); void setRootNode(Ogre::SceneNode* root); - void insertBegin (const MWWorld::Ptr& ptr); + void insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv); void insertCreature (const MWWorld::Ptr& ptr); void insertActivator (const MWWorld::Ptr& ptr); diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 6563238d09..337327dc49 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -29,7 +29,7 @@ void Objects::setRootNode(Ogre::SceneNode* root) mRootNode = root; } -void Objects::insertBegin (const MWWorld::Ptr& ptr) +void Objects::insertBegin(const MWWorld::Ptr& ptr) { Ogre::SceneNode* root = mRootNode; Ogre::SceneNode* cellnode; @@ -69,16 +69,15 @@ void Objects::insertBegin (const MWWorld::Ptr& ptr) ptr.getRefData().setBaseNode(insert); } -void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) +void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh) { - Ogre::SceneNode* insert = ptr.getRefData().getBaseNode(); - assert(insert); + insertBegin(ptr); std::auto_ptr anim(new ObjectAnimation(ptr, mesh)); Ogre::AxisAlignedBox bounds = anim->getWorldBounds(); Ogre::Vector3 extents = bounds.getSize(); - extents *= insert->getScale(); + extents *= ptr.getRefData().getBaseNode()->getScale(); float size = std::max(std::max(extents.x, extents.y), extents.z); bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 815233a66f..22dd1e4f5d 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -31,14 +31,15 @@ class Objects{ static int uniqueID; + void insertBegin(const MWWorld::Ptr& ptr); + public: Objects(OEngine::Render::OgreRenderer &renderer) : mRenderer(renderer) , mRootNode(NULL) {} ~Objects(){} - void insertBegin (const MWWorld::Ptr& ptr); - void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh); + void insertModel(const MWWorld::Ptr& ptr, const std::string &model); void enableLights(); void disableLights(); From 8f4506f5b6e5d63350697157f6229a213fdec21b Mon Sep 17 00:00:00 2001 From: PLkolek Date: Wed, 7 Aug 2013 15:34:11 +0200 Subject: [PATCH 22/22] Implemented drowning. Currently no visual effects on losing health, the breathing sound doesn't change (we don't have one), the breath bar doesn't turn red when no breath left and it doesn't pulse from black to red. --- apps/openmw/mwbase/windowmanager.hpp | 7 ++++ apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwgui/hud.cpp | 17 ++++++++++ apps/openmw/mwgui/hud.hpp | 8 ++++- apps/openmw/mwgui/windowmanagerimp.cpp | 10 ++++++ apps/openmw/mwgui/windowmanagerimp.hpp | 7 ++++ apps/openmw/mwmechanics/actors.cpp | 34 +++++++++++++++++++ apps/openmw/mwmechanics/actors.hpp | 1 + .../mwmechanics/mechanicsmanagerimp.cpp | 14 ++++++++ apps/openmw/mwmechanics/npcstats.cpp | 22 ++++++++++++ apps/openmw/mwmechanics/npcstats.hpp | 15 ++++++++ apps/openmw/mwworld/worldimp.cpp | 11 ++++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ files/mygui/openmw_hud.layout | 16 ++++++++- 14 files changed, 164 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 81ef9ee79d..2d923b63df 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -138,6 +138,10 @@ namespace MWBase virtual void setValue (const std::string& id, const std::string& value) = 0; virtual void setValue (const std::string& id, int value) = 0; + /// Set time left for the player to start drowning (update the drowning bar) + /// @param time value from [0,20] + virtual void setDrowningTimeLeft (float time) =0; + virtual void setPlayerClass (const ESM::Class &class_) = 0; ///< set current class of player @@ -181,6 +185,9 @@ namespace MWBase virtual void setInteriorMapTexture(const int x, const int y) = 0; ///< set the index of the map texture that should be used (for interiors) + /// sets the visibility of the drowning bar + virtual void setDrowningBarVisibility(bool visible) = 0; + /// sets the visibility of the hud health/magicka/stamina bars virtual void setHMSVisibility(bool visible) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 480bcf9cf4..e363749419 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -323,6 +323,8 @@ namespace MWBase virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0; virtual bool isSwimming(const MWWorld::Ptr &object) const = 0; + ///Is the head of the creature underwater? + virtual bool isSubmerged(const MWWorld::Ptr &object) const = 0; virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const = 0; virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0; diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index f5cb12e058..f82fbda2bd 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -25,6 +25,8 @@ namespace MWGui , mHealth(NULL) , mMagicka(NULL) , mStamina(NULL) + , mDrowning(NULL) + , mDrowningFrame(NULL) , mWeapImage(NULL) , mSpellImage(NULL) , mWeapStatus(NULL) @@ -69,6 +71,11 @@ namespace MWGui magickaFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); fatigueFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); + //Drowning bar + getWidget(mDrowningFrame, "DrowningFrame"); + getWidget(mDrowning, "Drowning"); + mDrowning->setProgressRange(200); + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); // Item and spell images and status bars @@ -197,6 +204,16 @@ namespace MWGui } } + void HUD::setDrowningTimeLeft(float time) + { + mDrowning->setProgressPosition(time/20.0*200.0); + } + + void HUD::setDrowningBarVisible(bool visible) + { + mDrowningFrame->setVisible(visible); + } + void HUD::onWorldClicked(MyGUI::Widget* _sender) { if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index a3cab2c93a..c40742a603 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -18,6 +18,11 @@ namespace MWGui void setTriangleCount(unsigned int count); void setBatchCount(unsigned int count); + /// Set time left for the player to start drowning + /// @param time value from [0,20] + void setDrowningTimeLeft(float time); + void setDrowningBarVisible(bool visible); + void setHmsVisible(bool visible); void setWeapVisible(bool visible); void setSpellVisible(bool visible); @@ -50,7 +55,7 @@ namespace MWGui void setEnemy(const MWWorld::Ptr& enemy); private: - MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth; + MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning; MyGUI::Widget* mHealthFrame; MyGUI::Widget *mWeapBox, *mSpellBox, *mSneakBox; MyGUI::ImageBox *mWeapImage, *mSpellImage; @@ -62,6 +67,7 @@ namespace MWGui MyGUI::ImageBox* mCrosshair; MyGUI::TextBox* mCellNameBox; MyGUI::TextBox* mWeaponSpellBox; + MyGUI::Widget* mDrowningFrame; MyGUI::Widget* mDummy; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 7924310387..7cf9eaa643 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -599,6 +599,11 @@ namespace MWGui mStatsWindow->setValue (id, value); } + void WindowManager::setDrowningTimeLeft (float time) + { + mHud->setDrowningTimeLeft(time); + } + void WindowManager::setPlayerClass (const ESM::Class &class_) { mStatsWindow->setValue("class", class_.mName); @@ -787,6 +792,11 @@ namespace MWGui mHud->setPlayerDir(x,y); } + void WindowManager::setDrowningBarVisibility(bool visible) + { + mHud->setDrowningBarVisible(visible); + } + void WindowManager::setHMSVisibility(bool visible) { mHud->setHmsVisible (visible); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index a178dc621b..8188946e10 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -148,6 +148,10 @@ namespace MWGui virtual void setValue (const std::string& id, const std::string& value); virtual void setValue (const std::string& id, int value); + /// Set time left for the player to start drowning (update the drowning bar) + /// @param time value from [0,20] + virtual void setDrowningTimeLeft (float time); + virtual void setPlayerClass (const ESM::Class &class_); ///< set current class of player virtual void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. virtual void setReputation (int reputation); ///< set the current reputation value @@ -173,6 +177,9 @@ namespace MWGui virtual void setInteriorMapTexture(const int x, const int y); ///< set the index of the map texture that should be used (for interiors) + /// sets the visibility of the drowning bar + virtual void setDrowningBarVisibility(bool visible); + // sets the visibility of the hud health/magicka/stamina bars virtual void setHMSVisibility(bool visible); // sets the visibility of the hud minimap diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index fb273c7c13..0d720ecacf 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -16,6 +16,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "npcstats.hpp" #include "creaturestats.hpp" #include "movement.hpp" @@ -40,6 +41,8 @@ namespace MWMechanics { if (!paused && ptr.getRefData().getHandle()!="player") MWWorld::Class::get (ptr).getInventoryStore (ptr).autoEquip (ptr); + if(!paused) + updateDrowning(ptr,duration); } void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) @@ -159,6 +162,37 @@ namespace MWMechanics } } + void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration) + { + Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); + CreatureStats& creatureStats=MWWorld::Class::get(ptr).getCreatureStats(ptr); + NpcStats& stats=MWWorld::Class::get(ptr).getNpcStats(ptr); + bool waterBreathing=creatureStats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).mMagnitude>0; + if(MWBase::Environment::get().getWorld()->isSubmerged(ptr) && !waterBreathing) + { + if(creatureStats.getFatigue().getCurrent()==0) + stats.setTimeToStartDrowning(0); + float timeLeft=stats.getTimeToStartDrowning()-duration; + if(timeLeft<0) + timeLeft=0; + stats.setTimeToStartDrowning(timeLeft); + if(timeLeft==0) + stats.setLastDrowningHitTime(stats.getLastDrowningHitTime()+duration); + } + else + { + stats.setTimeToStartDrowning(20); + stats.setLastDrowningHitTime(0); + } + //if npc is drowning and it's time to hit, then hit + while(stats.getTimeToStartDrowning()==0.0 && stats.getLastDrowningHitTime()>0.33) + { + stats.setLastDrowningHitTime(stats.getLastDrowningHitTime()-0.33); + //fixme: replace it with something different once screen hit effects are implemented (blood on screen) + MWWorld::Class::get(ptr).setActorHealth(ptr, creatureStats.getHealth().getCurrent()-1.0); + } + } + Actors::Actors() : mDuration (0) {} void Actors::addActor (const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 1369d783c6..777fdbb9a4 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -44,6 +44,7 @@ namespace MWMechanics void calculateRestoration (const MWWorld::Ptr& ptr, float duration); + void updateDrowning (const MWWorld::Ptr& ptr, float duration); public: diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7aa347d6ef..cdcede5076 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -254,6 +254,20 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[2], stats.getFatigue()); } + if(npcStats.getTimeToStartDrowning() != mWatchedNpc.getTimeToStartDrowning()) + { + mWatchedNpc.setTimeToStartDrowning(npcStats.getTimeToStartDrowning()); + if(npcStats.getTimeToStartDrowning()>=20.0) + { + MWBase::Environment::get().getWindowManager()->setDrowningBarVisibility(false); + } + else + { + MWBase::Environment::get().getWindowManager()->setDrowningBarVisibility(true); + MWBase::Environment::get().getWindowManager()->setDrowningTimeLeft(npcStats.getTimeToStartDrowning()); + } + } + bool update = false; //Loop over ESM::Skill::SkillEnum diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 50c1278567..69ba1c5cb8 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -32,6 +32,8 @@ MWMechanics::NpcStats::NpcStats() , mWerewolfKills (0) , mProfit(0) , mAttackStrength(0.0f) +, mTimeToStartDrowning(20.0) +, mLastDrowningHit(0) { mSkillIncreases.resize (ESM::Attribute::Length); for (int i=0; i=0 && time<=20); + mTimeToStartDrowning=time; +} + +float MWMechanics::NpcStats::getLastDrowningHitTime() +{ + return mLastDrowningHit; +} + +void MWMechanics::NpcStats::setLastDrowningHitTime(float time) +{ + mLastDrowningHit=time; +} diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 75fca0685a..44ad575e40 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -62,6 +62,11 @@ namespace MWMechanics std::set mUsedIds; + /// Countdown to getting damage while underwater + float mTimeToStartDrowning; + /// time since last hit from drowning + float mLastDrowningHit; + public: NpcStats(); @@ -142,6 +147,16 @@ namespace MWMechanics void setWerewolf (bool set); int getWerewolfKills() const; + + float getTimeToStartDrowning(); + /// Sets time left for the creature to drown if it stays underwater. + /// @param time value from [0,20] + void setTimeToStartDrowning(float time); + + float getLastDrowningHitTime(); + /// Sets time since last hit caused by drowning. + /// @param time value from [0,0.33] + void setLastDrowningHitTime(float time); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f396157a40..7703470b4c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1566,6 +1566,17 @@ namespace MWWorld return false; } + bool World::isSubmerged(const MWWorld::Ptr &object) const + { + float *fpos = object.getRefData().getPosition().pos; + Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); + + const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(object.getRefData().getHandle()); + if(actor) pos.z += 1.85*actor->getHalfExtents().z; + + return isUnderwater(object.getCell(), pos); + } + bool World::isSwimming(const MWWorld::Ptr &object) const { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 6d8caad219..cb2e00e1f5 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -348,6 +348,8 @@ namespace MWWorld virtual void processChangedSettings(const Settings::CategorySettingVector& settings); virtual bool isFlying(const MWWorld::Ptr &ptr) const; + ///Is the head of the creature underwater? + virtual bool isSubmerged(const MWWorld::Ptr &object) const; virtual bool isSwimming(const MWWorld::Ptr &object) const; virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const; virtual bool isOnGround(const MWWorld::Ptr &ptr) const; diff --git a/files/mygui/openmw_hud.layout b/files/mygui/openmw_hud.layout index 16b9f2c205..9faa931c61 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -32,7 +32,21 @@ - + + + + + + + + + + + + + + +