Merge branch 'master' of https://github.com/zinnschlag/openmw into compiler-reorg

pull/51/head
Alex 12 years ago
commit a0931b01fe

@ -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

@ -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;

@ -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;

@ -25,9 +25,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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -30,9 +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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -23,9 +23,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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -27,9 +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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -61,9 +61,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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -29,9 +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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -36,9 +36,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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -26,19 +26,10 @@ namespace MWClass
{
void Light::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{
MWWorld::LiveCellRef<ESM::Light> *ref =
ptr.get<ESM::Light>();
assert (ref->mBase != NULL);
const std::string &model = ref->mBase->mModel;
MWRender::Objects& objects = renderingInterface.getObjects();
objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false);
if (!model.empty())
objects.insertMesh(ptr, "meshes\\" + model, true);
else
objects.insertLight(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

@ -26,9 +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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -42,9 +42,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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -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;
}

@ -28,9 +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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -26,9 +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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -25,9 +25,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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -15,9 +15,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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -33,9 +33,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.insertMesh(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model);
}
}

@ -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 ())

@ -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;

@ -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);

@ -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

@ -268,10 +268,6 @@ namespace MWInput
case A_ToggleHUD:
mWindows.toggleHud();
break;
case A_Use:
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
mPlayer.use();
break;
}
}
}

@ -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)

@ -44,6 +44,7 @@ namespace MWMechanics
void calculateRestoration (const MWWorld::Ptr& ptr, float duration);
void updateDrowning (const MWWorld::Ptr& ptr, float duration);
public:

@ -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)

@ -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;
}

@ -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
{

@ -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

@ -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<ESM::Attribute::Length; ++i)
@ -382,3 +384,23 @@ void MWMechanics::NpcStats::modifyProfit(int diff)
{
mProfit += diff;
}
float MWMechanics::NpcStats::getTimeToStartDrowning()
{
return mTimeToStartDrowning;
}
void MWMechanics::NpcStats::setTimeToStartDrowning(float time)
{
assert(time>=0 && time<=20);
mTimeToStartDrowning=time;
}
float MWMechanics::NpcStats::getLastDrowningHitTime()
{
return mLastDrowningHit;
}
void MWMechanics::NpcStats::setLastDrowningHitTime(float time)
{
mLastDrowningHit=time;
}

@ -62,6 +62,11 @@ namespace MWMechanics
std::set<std::string> 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);
};
}

@ -12,16 +12,16 @@ ActivatorAnimation::~ActivatorAnimation()
}
ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr)
: Animation(ptr)
: Animation(ptr, ptr.getRefData().getBaseNode())
{
MWWorld::LiveCellRef<ESM::Activator> *ref = mPtr.get<ESM::Activator>();
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);

@ -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);

@ -8,6 +8,10 @@
#include <OgreBone.h>
#include <OgreSubMesh.h>
#include <OgreSceneManager.h>
#include <OgreControllerManager.h>
#include <OgreStaticGeometry.h>
#include <libs/openengine/ogre/lights.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp"
@ -16,6 +20,9 @@
#include "../mwmechanics/character.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/fallback.hpp"
#include "renderconst.hpp"
namespace MWRender
{
@ -35,17 +42,26 @@ 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++)
{
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++)
sceneMgr->destroyEntity(objects.mEntities[i]);
objects.mControllers.clear();
objects.mLights.clear();
objects.mParticles.clear();
objects.mEntities.clear();
objects.mSkelBase = NULL;
}
Animation::Animation(const MWWorld::Ptr &ptr)
Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node)
: mPtr(ptr)
, mCamera(NULL)
, mInsert(NULL)
@ -58,6 +74,7 @@ Animation::Animation(const MWWorld::Ptr &ptr)
{
for(size_t i = 0;i < sNumGroups;i++)
mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this));
mInsert = node->createChildSceneNode();
}
Animation::~Animation()
@ -68,15 +85,21 @@ Animation::~Animation()
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
destroyObjectList(sceneMgr, mObjectRoot);
sceneMgr->destroySceneNode(mInsert);
}
}
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();
mSkelBase = NULL;
destroyObjectList(mInsert->getCreator(), mObjectRoot);
if(model.empty())
return;
std::string mdlname = Misc::StringUtils::lowerCase(model);
std::string::size_type p = mdlname.rfind('\\');
@ -92,9 +115,6 @@ void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, b
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)
@ -140,28 +160,47 @@ void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, b
}
}
void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue)
{
for(size_t i = 0;i < objlist.mEntities.size();i++)
class VisQueueSet {
Ogre::uint32 mVisFlags;
Ogre::uint8 mSolidQueue, mTransQueue;
Ogre::Real mDist;
public:
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
{
Ogre::Entity *ent = objlist.mEntities[i];
if(visflags != 0)
ent->setVisibilityFlags(visflags);
if(mVisFlags != 0)
entity->setVisibilityFlags(mVisFlags);
entity->setRenderingDistance(mDist);
for(unsigned int j = 0;j < ent->getNumSubEntities();++j)
unsigned int numsubs = entity->getNumSubEntities();
for(unsigned int i = 0;i < numsubs;++i)
{
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? transqueue : solidqueue);
Ogre::SubEntity* subEnt = entity->getSubEntity(i);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? mTransQueue : mSolidQueue);
}
}
for(size_t i = 0;i < objlist.mParticles.size();i++)
void operator()(Ogre::ParticleSystem *psys) const
{
Ogre::ParticleSystem *part = objlist.mParticles[i];
if(visflags != 0)
part->setVisibilityFlags(visflags);
if(mVisFlags != 0)
psys->setVisibilityFlags(mVisFlags);
psys->setRenderingDistance(mDist);
// 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, Ogre::Real dist)
{
std::for_each(objlist.mEntities.begin(), objlist.mEntities.end(),
VisQueueSet(visflags, solidqueue, transqueue, dist));
std::for_each(objlist.mParticles.begin(), objlist.mParticles.end(),
VisQueueSet(visflags, solidqueue, transqueue, dist));
}
@ -254,6 +293,76 @@ 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);
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<Ogre::Real>(src, dest, func));
bool interior = !(mPtr.isInCell() && mPtr.getCell()->mCell->isExterior());
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 = bounds.isFinite() ? mInsert->createChildSceneNode(bounds.getCenter())
: mInsert->createChildSceneNode();
node->attachObject(olight);
}
}
Ogre::Node *Animation::getNode(const std::string &name)
{
if(mSkelBase)
@ -805,6 +914,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)
@ -833,4 +979,66 @@ void Animation::detachObjectFromBone(Ogre::MovableObject *obj)
mSkelBase->detachObjectFromBone(obj);
}
ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model)
: 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, (mPtr.getTypeName() == typeid(ESM::Static).name()) ?
(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<Ogre::Entity*>::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());
}
}
}

@ -52,6 +52,7 @@ protected:
virtual void setValue(Ogre::Real value);
};
class NullAnimationValue : public Ogre::ControllerValue<Ogre::Real>
{
public:
@ -61,6 +62,7 @@ protected:
{ }
};
struct AnimSource : public Ogre::AnimationAlloc {
NifOgre::TextKeyMap mTextKeys;
std::vector<Ogre::Controller<Ogre::Real> > mControllers[sNumGroups];
@ -151,21 +153,24 @@ 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
* 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);
static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist=0.0f);
void clearAnimSources();
public:
Animation(const MWWorld::Ptr &ptr);
Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node);
virtual ~Animation();
void updatePtr(const MWWorld::Ptr &ptr);
@ -224,6 +229,10 @@ public:
virtual void showWeapons(bool showWeapon);
void enableLights(bool enable);
Ogre::AxisAlignedBox getWorldBounds();
void setCamera(Camera *cam)
{ mCamera = cam; }
@ -236,5 +245,15 @@ public:
void detachObjectFromBone(Ogre::MovableObject *obj);
};
class ObjectAnimation : public Animation {
public:
ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model);
void addLight(const ESM::Light *light);
bool canBatch() const;
void fillBatch(Ogre::StaticGeometry *sg);
};
}
#endif

@ -12,7 +12,7 @@ CreatureAnimation::~CreatureAnimation()
}
CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
: Animation(ptr)
: Animation(ptr, ptr.getRefData().getBaseNode())
{
MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
@ -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))

@ -20,46 +20,50 @@
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]);
}
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),
@ -82,7 +86,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
{
mNpc = mPtr.get<ESM::NPC>()->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;
@ -102,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)
{
@ -143,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)
{
@ -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();
}
@ -267,6 +271,19 @@ 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<ESM::Light>()->mBase;
addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft,
1, "meshes\\"+light->mModel);
addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], light);
}
}
showWeapons(mShowWeapons);
const int Flag_Female = 0x01;
@ -366,23 +383,31 @@ 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);
}
}
}
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)
{
@ -422,7 +447,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<Ogre::Controller<Ogre::Real> >::iterator ctrl(mObjectParts[i].mControllers.begin());
for(;ctrl != mObjectParts[i].mControllers.end();ctrl++)
@ -437,22 +462,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])
{
@ -464,14 +482,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;
@ -480,28 +498,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);
// 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<Ogre::Controller<Ogre::Real> >::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<Ogre::Controller<Ogre::Real> >::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<ESM::PartReference> &parts)
@ -542,9 +568,9 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::
}
if(bodypart)
addOrReplaceIndividualPart(part->mPart, 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);
}
}
@ -557,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);
}
}

@ -21,25 +21,21 @@ namespace MWRender
class NpcAnimation : public Animation
{
public:
struct PartInfo {
ESM::PartReferenceType type;
const char name[32];
};
typedef std::map<ESM::PartReferenceType,std::string> 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<ESM::PartReference> &parts);

@ -18,70 +18,18 @@
#include "../mwworld/class.hpp"
#include "renderconst.hpp"
#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<Ogre::Entity*>(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<Ogre::SceneNode*>(it.getNext ()));
}
}
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;
@ -118,79 +66,45 @@ 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, bool light)
void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh)
{
Ogre::SceneNode* insert = ptr.getRefData().getBaseNode();
assert(insert);
insertBegin(ptr);
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<ObjectAnimation> 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")) && 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<ESM::Light>()->mBase);
if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects") ||
anyTransparency || !objects.mParticles.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(ptr.getTypeName() == typeid(ESM::Static).name() &&
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 +114,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 +138,75 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool
sg->setRenderQueueGroup(RQG_Main);
std::vector<Ogre::Entity*>::reverse_iterator iter = objects.mEntities.rbegin();
while(iter != objects.mEntities.rend())
{
Ogre::Node *node = (*iter)->getParentNode();
sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale());
(*iter)->detachFromParent();
mRenderer.getScene()->destroyEntity(*iter);
++iter;
}
anim->fillBatch(sg);
/* TODO: We could hold on to this and just detach it from the scene graph, so if the Ptr
* ever needs to modify we can reattach it and rebuild the StaticGeometry object without
* it. Would require associating the Ptr with the StaticGeometry. */
anim.reset();
}
if (light)
{
insertLight(ptr, objects.mSkelBase, bounds.getCenter() - insert->_getDerivedPosition());
}
if(anim.get() != NULL)
mObjects.insert(std::make_pair(ptr, anim.release()));
}
void Objects::insertLight (const MWWorld::Ptr& ptr, Ogre::Entity* skelBase, Ogre::Vector3 fallbackCenter)
bool Objects::deleteObject (const MWWorld::Ptr& ptr)
{
Ogre::SceneNode* insert = mRenderer.getScene()->getSceneNode(ptr.getRefData().getHandle());
assert(insert);
MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
const int color = ref->mBase->mData.mColor;
const float r = ((color >> 0) & 0xFF) / 255.0f;
const float g = ((color >> 8) & 0xFF) / 255.0f;
const float b = ((color >> 16) & 0xFF) / 255.0f;
const float radius = float (ref->mBase->mData.mRadius);
Ogre::Light *light = mRenderer.getScene()->createLight();
light->setDiffuseColour (r, g, b);
LightInfo info;
info.name = light->getName();
info.radius = radius;
info.colour = Ogre::ColourValue(r, g, b);
if (ref->mBase->mData.mFlags & ESM::Light::Negative)
info.colour *= -1;
info.interior = !ptr.getCell()->mCell->isExterior();
if (ref->mBase->mData.mFlags & ESM::Light::Flicker)
info.type = LT_Flicker;
else if (ref->mBase->mData.mFlags & ESM::Light::FlickerSlow)
info.type = LT_FlickerSlow;
else if (ref->mBase->mData.mFlags & ESM::Light::Pulse)
info.type = LT_Pulse;
else if (ref->mBase->mData.mFlags & ESM::Light::PulseSlow)
info.type = LT_PulseSlow;
else
info.type = LT_Normal;
if(!ptr.getRefData().getBaseNode())
return true;
// randomize lights animations
info.time = Ogre::Math::RangeRandom(-500, +500);
info.phase = Ogre::Math::RangeRandom(-500, +500);
bool quadratic = lightOutQuadInLin() ? !info.interior : lightQuadratic();
// with the standard 1 / (c + d*l + d*d*q) equation the attenuation factor never becomes zero,
// so we ignore lights if their attenuation falls below this factor.
const float threshold = 0.03;
if (!quadratic)
PtrAnimationMap::iterator iter = mObjects.find(ptr);
if(iter != mObjects.end())
{
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);
}
delete iter->second;
mObjects.erase(iter);
// If there's an AttachLight bone, attach the light to that, otherwise attach it to the base scene node
if (skelBase && skelBase->getSkeleton ()->hasBone ("AttachLight"))
{
skelBase->attachObjectToBone ("AttachLight", light);
}
else
{
Ogre::SceneNode* childNode = insert->createChildSceneNode (fallbackCenter);
childNode->attachObject(light);
mRenderer.getScene()->destroySceneNode(ptr.getRefData().getBaseNode());
ptr.getRefData().setBaseNode(0);
return true;
}
mLights.push_back(info);
return false;
}
bool Objects::deleteObject (const MWWorld::Ptr& ptr)
{
if (Ogre::SceneNode *base = ptr.getRefData().getBaseNode())
{
Ogre::SceneNode *parent = base->getParentSceneNode();
for (std::map<MWWorld::Ptr::CellStore *, Ogre::SceneNode *>::const_iterator iter (
mCellSceneNodes.begin()); iter!=mCellSceneNodes.end(); ++iter)
if (iter->second==parent)
{
clearSceneNode (base);
base->removeAndDestroyAllChildren();
mRenderer.getScene()->destroySceneNode (base);
ptr.getRefData().setBaseNode (0);
return true;
}
return false;
}
return true;
}
void Objects::removeCell(MWWorld::Ptr::CellStore* store)
{
if(mCellSceneNodes.find(store) != mCellSceneNodes.end())
for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();)
{
Ogre::SceneNode* base = mCellSceneNodes[store];
for (int i=0; i<base->numChildren(); ++i)
clearSceneNode (static_cast<Ogre::SceneNode *> (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<MWWorld::CellStore*,Ogre::StaticGeometry*>::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<MWWorld::CellStore*,Ogre::SceneNode*>::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 +230,23 @@ Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell)
void Objects::enableLights()
{
std::vector<LightInfo>::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<LightInfo>::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<LightInfo>::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()

@ -5,7 +5,6 @@
#include <OgreAxisAlignedBox.h>
#include <openengine/ogre/renderer.hpp>
#include "../mwworld/fallback.hpp"
namespace MWWorld
{
@ -15,72 +14,32 @@ namespace MWWorld
namespace MWRender{
/// 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 ObjectAnimation;
class Objects{
typedef std::map<MWWorld::Ptr,ObjectAnimation*> PtrAnimationMap;
OEngine::Render::OgreRenderer &mRenderer;
std::map<MWWorld::CellStore *, Ogre::SceneNode *> mCellSceneNodes;
std::map<MWWorld::CellStore *, Ogre::StaticGeometry*> mStaticGeometry;
std::map<MWWorld::CellStore *, Ogre::StaticGeometry*> mStaticGeometrySmall;
std::map<MWWorld::CellStore *, Ogre::AxisAlignedBox> mBounds;
std::vector<LightInfo> mLights;
Ogre::SceneNode* mRootNode;
bool mIsStatic;
static int uniqueID;
MWWorld::Fallback* mFallback;
float lightLinearValue();
float lightLinearRadiusMult();
bool lightQuadratic();
float lightQuadraticValue();
float lightQuadraticRadiusMult();
std::map<MWWorld::CellStore*,Ogre::SceneNode*> mCellSceneNodes;
std::map<MWWorld::CellStore*,Ogre::StaticGeometry*> mStaticGeometry;
std::map<MWWorld::CellStore*,Ogre::StaticGeometry*> mStaticGeometrySmall;
std::map<MWWorld::CellStore*,Ogre::AxisAlignedBox> mBounds;
PtrAnimationMap mObjects;
bool lightOutQuadInLin();
Ogre::SceneNode* mRootNode;
static int uniqueID;
void clearSceneNode (Ogre::SceneNode *node);
///< Remove all movable objects from \a node.
void insertBegin(const MWWorld::Ptr& ptr);
public:
Objects(OEngine::Render::OgreRenderer& renderer, MWWorld::Fallback* fallback)
: mRenderer (renderer)
, mIsStatic(false)
, mFallback(fallback)
Objects(OEngine::Render::OgreRenderer &renderer)
: mRenderer(renderer)
, mRootNode(NULL)
{}
~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 insertModel(const MWWorld::Ptr& ptr, const std::string &model);
void enableLights();
void disableLights();

@ -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)

@ -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);
@ -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<ESM::LandTexture>().getSize(plugin);
std::set<uint16_t> 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
@ -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<uint16_t, int>::const_iterator iter;
@ -352,9 +351,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 +366,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 +374,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;
if ( splatX >= 0 && splatX < blendMapSize &&
splatY >= 0 && splatY < blendMapSize )
{
const int index = (splatY)*blendMapSize + splatX;
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 splatY = blendMapSize - relY - 1;
const int splatX = relX;
}
}
assert(splatX >= 0 && splatX < blendMapSize);
assert(splatY >= 0 && splatY < blendMapSize);
const int index = (splatY)*blendMapSize + splatX;
pBlend[index] = 1;
}
}

@ -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();

@ -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);

@ -74,7 +74,7 @@ namespace MWWorld
bool isInCell() const
{
return (mContainerStore == 0);
return (mContainerStore == 0) && (mCell != 0);
}
void setContainerStore (ContainerStore *store);

@ -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
{

@ -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;

@ -43,6 +43,7 @@ struct ObjectList {
Ogre::Entity *mSkelBase;
std::vector<Ogre::Entity*> mEntities;
std::vector<Ogre::ParticleSystem*> mParticles;
std::vector<Ogre::Light*> mLights;
std::map<int,TextKeyMap> mTextKeys;

@ -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

@ -32,7 +32,21 @@
<Property key="NeedMouse" value="false"/>
</Widget>
</Widget>
<!-- Drowning bar -->
<Widget type="Widget" skin="HUD_Box" position="0 36 220 56" align="Center Top" name="DrowningFrame">
<Property key="Visible" value="false"/>
<Widget type="TextBox" skin="SandText" position="0 8 220 24" name="DrowningTitle" align="Center Top HStretch">
<Property key="Caption" value="#{sBreath}"/>
<Property key="TextAlign" value="Center"/>
<Property key="TextShadow" value="true"/>
<Property key="TextShadowColour" value="0 0 0"/>
</Widget>
<Widget type="ProgressBar" skin="MW_Progress_Loading" position="12 36 196 8" align="Center Top" name="Drowning">
<Property key="NeedMouse" value="false"/>
</Widget>
</Widget>
<!-- Equipped weapon/selected spell name display for a few seconds after it changes -->
<Widget type="TextBox" skin="SandText" position="13 118 270 24" name="WeaponSpellName" align="Left Bottom HStretch">
<Property key="Visible" value="false"/>

@ -0,0 +1,118 @@
#include "lights.hpp"
#include <OgreLight.h>
#include <OgreMath.h>
namespace OEngine {
namespace Render {
LightFunction::LightFunction(LightType type)
: ControllerFunction<Ogre::Real>(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);
}
}
}

@ -0,0 +1,51 @@
#ifndef OENGINE_OGRE_LIGHTS_H
#define OENGINE_OGRE_LIGHTS_H
#include <OgreController.h>
#include <OgreColourValue.h>
/*
* 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<Ogre::Real>
{
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::Real>
{
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

@ -4,6 +4,7 @@
#include <OgreRenderTexture.h>
#include <OgreSubEntity.h>
#include <OgreEntity.h>
#include <OgreTechnique.h>
#include <stdexcept>
#include <extern/shiny/Main/Factory.hpp>
@ -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;
}

Loading…
Cancel
Save