forked from teamnwah/openmw-tes3coop
Merge remote-tracking branch 'zini/master' into material-fix
Conflicts: components/nifogre/ogrenifloader.cppactorid
commit
4e1e0eaf62
@ -0,0 +1,293 @@
|
||||
#include "spellicons.hpp"
|
||||
|
||||
#include <MyGUI_Widget.h>
|
||||
#include <MyGUI_Gui.h>
|
||||
#include <MyGUI_ImageBox.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
#include "../mwmechanics/activespells.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "tooltips.hpp"
|
||||
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
void SpellIcons::updateWidgets(MyGUI::Widget *parent, bool adjustSize)
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
|
||||
|
||||
std::map <int, std::vector<MagicEffectInfo> > effects;
|
||||
|
||||
// add permanent item enchantments
|
||||
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
|
||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
||||
{
|
||||
MWWorld::ContainerStoreIterator it = store.getSlot(slot);
|
||||
if (it == store.end())
|
||||
continue;
|
||||
std::string enchantment = MWWorld::Class::get(*it).getEnchantment(*it);
|
||||
if (enchantment.empty())
|
||||
continue;
|
||||
const ESM::Enchantment* enchant = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(enchantment);
|
||||
if (enchant->mData.mType != ESM::Enchantment::ConstantEffect)
|
||||
continue;
|
||||
|
||||
const ESM::EffectList& list = enchant->mEffects;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
||||
effectIt != list.mList.end(); ++effectIt)
|
||||
{
|
||||
const ESM::MagicEffect* magicEffect =
|
||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
||||
|
||||
MagicEffectInfo effectInfo;
|
||||
effectInfo.mSource = MWWorld::Class::get(*it).getName(*it);
|
||||
effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID);
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
effectInfo.mKey.mArg = effectIt->mSkill;
|
||||
else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
||||
effectInfo.mKey.mArg = effectIt->mAttribute;
|
||||
// just using the min magnitude here, permanent enchantments with a random magnitude just wouldn't make any sense
|
||||
effectInfo.mMagnitude = effectIt->mMagnMin;
|
||||
effectInfo.mPermanent = true;
|
||||
effects[effectIt->mEffectID].push_back (effectInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// add permanent spells
|
||||
const MWMechanics::Spells& spells = stats.getSpells();
|
||||
for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
||||
{
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(it->first);
|
||||
|
||||
// these are the spell types that are permanently in effect
|
||||
if (!(spell->mData.mType == ESM::Spell::ST_Ability)
|
||||
&& !(spell->mData.mType == ESM::Spell::ST_Disease)
|
||||
&& !(spell->mData.mType == ESM::Spell::ST_Curse)
|
||||
&& !(spell->mData.mType == ESM::Spell::ST_Blight))
|
||||
continue;
|
||||
const ESM::EffectList& list = spell->mEffects;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
||||
effectIt != list.mList.end(); ++effectIt)
|
||||
{
|
||||
const ESM::MagicEffect* magicEffect =
|
||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
||||
MagicEffectInfo effectInfo;
|
||||
effectInfo.mSource = getSpellDisplayName (it->first);
|
||||
effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID);
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
effectInfo.mKey.mArg = effectIt->mSkill;
|
||||
else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
||||
effectInfo.mKey.mArg = effectIt->mAttribute;
|
||||
// just using the min magnitude here, permanent spells with a random magnitude just wouldn't make any sense
|
||||
effectInfo.mMagnitude = effectIt->mMagnMin;
|
||||
effectInfo.mPermanent = true;
|
||||
|
||||
effects[effectIt->mEffectID].push_back (effectInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// add lasting effect spells/potions etc
|
||||
const MWMechanics::ActiveSpells::TContainer& activeSpells = stats.getActiveSpells().getActiveSpells();
|
||||
for (MWMechanics::ActiveSpells::TContainer::const_iterator it = activeSpells.begin();
|
||||
it != activeSpells.end(); ++it)
|
||||
{
|
||||
const ESM::EffectList& list = getSpellEffectList(it->first);
|
||||
|
||||
float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
|
||||
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
||||
effectIt != list.mList.end(); ++effectIt)
|
||||
{
|
||||
const ESM::MagicEffect* magicEffect =
|
||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
||||
|
||||
MagicEffectInfo effectInfo;
|
||||
effectInfo.mSource = getSpellDisplayName (it->first);
|
||||
effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID);
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
effectInfo.mKey.mArg = effectIt->mSkill;
|
||||
else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
||||
effectInfo.mKey.mArg = effectIt->mAttribute;
|
||||
effectInfo.mMagnitude = effectIt->mMagnMin + (effectIt->mMagnMax-effectIt->mMagnMin) * it->second.second;
|
||||
effectInfo.mRemainingTime = effectIt->mDuration +
|
||||
(it->second.first - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
|
||||
|
||||
// ingredients need special casing for their magnitude / duration
|
||||
/// \todo duplicated from ActiveSpells, helper function?
|
||||
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>().search (it->first))
|
||||
{
|
||||
effectInfo.mRemainingTime = effectIt->mDuration * it->second.second +
|
||||
(it->second.first - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
|
||||
|
||||
effectInfo.mMagnitude = static_cast<int> (0.05*it->second.second / (0.1 * magicEffect->mData.mBaseCost));
|
||||
}
|
||||
|
||||
|
||||
effects[effectIt->mEffectID].push_back (effectInfo);
|
||||
}
|
||||
}
|
||||
|
||||
parent->setVisible(effects.size() != 0);
|
||||
|
||||
int w=2;
|
||||
if (adjustSize)
|
||||
{
|
||||
int s = effects.size() * 16+4;
|
||||
int diff = parent->getWidth() - s;
|
||||
parent->setSize(s, parent->getHeight());
|
||||
parent->setPosition(parent->getLeft()+diff, parent->getTop());
|
||||
}
|
||||
|
||||
|
||||
for (std::map <int, std::vector<MagicEffectInfo> >::const_iterator it = effects.begin(); it != effects.end(); ++it)
|
||||
{
|
||||
MyGUI::ImageBox* image;
|
||||
if (mWidgetMap.find(it->first) == mWidgetMap.end())
|
||||
image = parent->createWidget<MyGUI::ImageBox>
|
||||
("ImageBox", MyGUI::IntCoord(w,2,16,16), MyGUI::Align::Default);
|
||||
else
|
||||
image = mWidgetMap[it->first];
|
||||
mWidgetMap[it->first] = image;
|
||||
image->setPosition(w,2);
|
||||
image->setVisible(true);
|
||||
|
||||
const ESM::MagicEffect* effect =
|
||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(it->first);
|
||||
|
||||
std::string icon = effect->mIcon;
|
||||
icon[icon.size()-3] = 'd';
|
||||
icon[icon.size()-2] = 'd';
|
||||
icon[icon.size()-1] = 's';
|
||||
icon = "icons\\" + icon;
|
||||
|
||||
image->setImageTexture(icon);
|
||||
w += 16;
|
||||
|
||||
float remainingDuration = 0;
|
||||
|
||||
std::string sourcesDescription;
|
||||
|
||||
const float fadeTime = 5.f;
|
||||
|
||||
for (std::vector<MagicEffectInfo>::const_iterator effectIt = it->second.begin();
|
||||
effectIt != it->second.end(); ++effectIt)
|
||||
{
|
||||
if (effectIt != it->second.begin())
|
||||
sourcesDescription += "\n";
|
||||
|
||||
// if at least one of the effect sources is permanent, the effect will never wear off
|
||||
if (effectIt->mPermanent)
|
||||
remainingDuration = fadeTime;
|
||||
else
|
||||
remainingDuration = std::max(remainingDuration, effectIt->mRemainingTime);
|
||||
|
||||
sourcesDescription += effectIt->mSource;
|
||||
|
||||
if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
sourcesDescription += " (" +
|
||||
MWBase::Environment::get().getWindowManager()->getGameSettingString(
|
||||
ESM::Skill::sSkillNameIds[effectIt->mKey.mArg], "") + ")";
|
||||
if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
||||
sourcesDescription += " (" +
|
||||
MWBase::Environment::get().getWindowManager()->getGameSettingString(
|
||||
ESM::Attribute::sGmstAttributeIds[effectIt->mKey.mArg], "") + ")";
|
||||
|
||||
if (!(effect->mData.mFlags & ESM::MagicEffect::NoMagnitude))
|
||||
{
|
||||
std::string pt = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", "");
|
||||
std::string pts = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoints", "");
|
||||
|
||||
sourcesDescription += ": " + boost::lexical_cast<std::string>(effectIt->mMagnitude);
|
||||
sourcesDescription += " " + ((effectIt->mMagnitude > 1) ? pts : pt);
|
||||
}
|
||||
}
|
||||
|
||||
std::string name = ESM::MagicEffect::effectIdToString (it->first);
|
||||
|
||||
ToolTipInfo tooltipInfo;
|
||||
tooltipInfo.caption = "#{" + name + "}";
|
||||
tooltipInfo.icon = effect->mIcon;
|
||||
tooltipInfo.text = sourcesDescription;
|
||||
tooltipInfo.imageSize = 16;
|
||||
tooltipInfo.wordWrap = false;
|
||||
|
||||
image->setUserData(tooltipInfo);
|
||||
image->setUserString("ToolTipType", "ToolTipInfo");
|
||||
|
||||
// Fade out during the last 5 seconds
|
||||
image->setAlpha(std::min(remainingDuration/fadeTime, 1.f));
|
||||
}
|
||||
|
||||
// hide inactive effects
|
||||
for (std::map<int, MyGUI::ImageBox*>::iterator it = mWidgetMap.begin(); it != mWidgetMap.end(); ++it)
|
||||
{
|
||||
if (effects.find(it->first) == effects.end())
|
||||
it->second->setVisible(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::string SpellIcons::getSpellDisplayName (const std::string& id)
|
||||
{
|
||||
if (const ESM::Spell *spell =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (id))
|
||||
return spell->mName;
|
||||
|
||||
if (const ESM::Potion *potion =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Potion>().search (id))
|
||||
return potion->mName;
|
||||
|
||||
if (const ESM::Ingredient *ingredient =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>().search (id))
|
||||
return ingredient->mName;
|
||||
|
||||
throw std::runtime_error ("ID " + id + " has no display name");
|
||||
}
|
||||
|
||||
ESM::EffectList SpellIcons::getSpellEffectList (const std::string& id)
|
||||
{
|
||||
if (const ESM::Spell *spell =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (id))
|
||||
return spell->mEffects;
|
||||
|
||||
if (const ESM::Potion *potion =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Potion>().search (id))
|
||||
return potion->mEffects;
|
||||
|
||||
if (const ESM::Ingredient *ingredient =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>().search (id))
|
||||
{
|
||||
const ESM::MagicEffect *magicEffect =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||
ingredient->mData.mEffectID[0]);
|
||||
|
||||
ESM::ENAMstruct effect;
|
||||
effect.mEffectID = ingredient->mData.mEffectID[0];
|
||||
effect.mSkill = ingredient->mData.mSkills[0];
|
||||
effect.mAttribute = ingredient->mData.mAttributes[0];
|
||||
effect.mRange = 0;
|
||||
effect.mArea = 0;
|
||||
effect.mDuration = magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration ? 0 : 1;
|
||||
effect.mMagnMin = 1;
|
||||
effect.mMagnMax = 1;
|
||||
ESM::EffectList result;
|
||||
result.mList.push_back (effect);
|
||||
return result;
|
||||
}
|
||||
throw std::runtime_error("ID " + id + " does not have effects");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
#ifndef MWGUI_SPELLICONS_H
|
||||
#define MWGUI_SPELLICONS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../mwmechanics/magiceffects.hpp"
|
||||
|
||||
namespace MyGUI
|
||||
{
|
||||
class Widget;
|
||||
class ImageBox;
|
||||
}
|
||||
namespace ESM
|
||||
{
|
||||
struct ENAMstruct;
|
||||
struct EffectList;
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
// information about a single magic effect source as required for display in the tooltip
|
||||
struct MagicEffectInfo
|
||||
{
|
||||
MagicEffectInfo() : mPermanent(false) {}
|
||||
std::string mSource; // display name for effect source (e.g. potion name)
|
||||
MWMechanics::EffectKey mKey;
|
||||
int mMagnitude;
|
||||
float mRemainingTime;
|
||||
bool mPermanent; // the effect is permanent
|
||||
};
|
||||
|
||||
class SpellIcons
|
||||
{
|
||||
public:
|
||||
void updateWidgets(MyGUI::Widget* parent, bool adjustSize);
|
||||
|
||||
private:
|
||||
std::string getSpellDisplayName (const std::string& id);
|
||||
ESM::EffectList getSpellEffectList (const std::string& id);
|
||||
|
||||
std::map<int, MyGUI::ImageBox*> mWidgetMap;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,107 @@
|
||||
#include "refraction.hpp"
|
||||
|
||||
#include <OgreCamera.h>
|
||||
#include <OgreTextureManager.h>
|
||||
#include <OgreSceneManager.h>
|
||||
#include <OgreHardwarePixelBuffer.h>
|
||||
#include <OgreRenderTarget.h>
|
||||
#include <OgreViewport.h>
|
||||
#include <OgreRoot.h>
|
||||
|
||||
#include <extern/shiny/Main/Factory.hpp>
|
||||
|
||||
#include "renderconst.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
Refraction::Refraction(Ogre::Camera *parentCamera)
|
||||
: mParentCamera(parentCamera)
|
||||
, mRenderActive(false)
|
||||
, mIsUnderwater(false)
|
||||
{
|
||||
mCamera = mParentCamera->getSceneManager()->createCamera("RefractionCamera");
|
||||
|
||||
mParentCamera->getSceneManager()->addRenderQueueListener(this);
|
||||
|
||||
Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual("WaterRefraction",
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, 512, 512, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
|
||||
|
||||
mRenderTarget = texture->getBuffer()->getRenderTarget();
|
||||
Ogre::Viewport* vp = mRenderTarget->addViewport(mCamera);
|
||||
vp->setOverlaysEnabled(false);
|
||||
vp->setShadowsEnabled(false);
|
||||
vp->setVisibilityMask(RV_Actors + RV_Misc + RV_Statics + RV_StaticsSmall + RV_Terrain + RV_Sky);
|
||||
vp->setMaterialScheme("water_refraction");
|
||||
vp->setBackgroundColour (Ogre::ColourValue(0.18039, 0.23137, 0.25490));
|
||||
mRenderTarget->setAutoUpdated(true);
|
||||
mRenderTarget->addListener(this);
|
||||
}
|
||||
|
||||
Refraction::~Refraction()
|
||||
{
|
||||
mRenderTarget->removeListener(this);
|
||||
Ogre::TextureManager::getSingleton().remove("WaterRefraction");
|
||||
mParentCamera->getSceneManager()->destroyCamera(mCamera);
|
||||
mParentCamera->getSceneManager()->removeRenderQueueListener(this);
|
||||
}
|
||||
|
||||
void Refraction::preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
|
||||
{
|
||||
mParentCamera->getParentSceneNode ()->needUpdate ();
|
||||
mCamera->setOrientation(mParentCamera->getDerivedOrientation());
|
||||
mCamera->setPosition(mParentCamera->getDerivedPosition());
|
||||
mCamera->setNearClipDistance(mParentCamera->getNearClipDistance());
|
||||
mCamera->setFarClipDistance(mParentCamera->getFarClipDistance());
|
||||
mCamera->setAspectRatio(mParentCamera->getAspectRatio());
|
||||
mCamera->setFOVy(mParentCamera->getFOVy());
|
||||
|
||||
// for depth calculation, we want the original viewproj matrix _without_ the custom near clip plane.
|
||||
// since all we are interested in is depth, we only need the third row of the matrix.
|
||||
Ogre::Matrix4 projMatrix = mCamera->getProjectionMatrixWithRSDepth () * mCamera->getViewMatrix ();
|
||||
sh::Vector4* row3 = new sh::Vector4(projMatrix[2][0], projMatrix[2][1], projMatrix[2][2], projMatrix[2][3]);
|
||||
sh::Factory::getInstance ().setSharedParameter ("vpRow2Fix", sh::makeProperty<sh::Vector4> (row3));
|
||||
|
||||
// enable clip plane here to take advantage of CPU culling for overwater or underwater objects
|
||||
mCamera->enableCustomNearClipPlane(mIsUnderwater ? mNearClipPlaneUnderwater : mNearClipPlane);
|
||||
|
||||
mRenderActive = true;
|
||||
}
|
||||
|
||||
void Refraction::postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt)
|
||||
{
|
||||
mCamera->disableCustomNearClipPlane ();
|
||||
mRenderActive = false;
|
||||
}
|
||||
|
||||
void Refraction::setHeight(float height)
|
||||
{
|
||||
mNearClipPlane = Ogre::Plane( -Ogre::Vector3(0,0,1), -(height + 5));
|
||||
mNearClipPlaneUnderwater = Ogre::Plane( Ogre::Vector3(0,0,1), height - 5);
|
||||
}
|
||||
|
||||
void Refraction::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation)
|
||||
{
|
||||
// We don't want the sky to get clipped by custom near clip plane (the water plane)
|
||||
if (queueGroupId < 20 && mRenderActive)
|
||||
{
|
||||
mCamera->disableCustomNearClipPlane();
|
||||
Ogre::Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS());
|
||||
}
|
||||
}
|
||||
|
||||
void Refraction::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation)
|
||||
{
|
||||
if (queueGroupId < 20 && mRenderActive)
|
||||
{
|
||||
mCamera->enableCustomNearClipPlane(mIsUnderwater ? mNearClipPlaneUnderwater : mNearClipPlane);
|
||||
Ogre::Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS());
|
||||
}
|
||||
}
|
||||
|
||||
void Refraction::setActive(bool active)
|
||||
{
|
||||
mRenderTarget->setActive(active);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
#ifndef MWRENDER_REFRACTION_H
|
||||
#define MWRENDER_REFRACTION_H
|
||||
|
||||
#include <OgrePlane.h>
|
||||
#include <OgreRenderTargetListener.h>
|
||||
#include <OgreRenderQueueListener.h>
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class Camera;
|
||||
class RenderTarget;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
class Refraction : public Ogre::RenderTargetListener, public Ogre::RenderQueueListener
|
||||
{
|
||||
|
||||
public:
|
||||
Refraction(Ogre::Camera* parentCamera);
|
||||
~Refraction();
|
||||
|
||||
void setHeight (float height);
|
||||
void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);
|
||||
void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);
|
||||
void setUnderwater(bool underwater) {mIsUnderwater = underwater;}
|
||||
void setActive (bool active);
|
||||
|
||||
void renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation);
|
||||
void renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation);
|
||||
|
||||
private:
|
||||
Ogre::Camera* mParentCamera;
|
||||
Ogre::Camera* mCamera;
|
||||
Ogre::RenderTarget* mRenderTarget;
|
||||
Ogre::Plane mNearClipPlane;
|
||||
Ogre::Plane mNearClipPlaneUnderwater;
|
||||
bool mRenderActive;
|
||||
bool mIsUnderwater;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,263 @@
|
||||
#include "ripplesimulation.hpp"
|
||||
|
||||
#include <OgreTextureManager.h>
|
||||
#include <OgreStringConverter.h>
|
||||
#include <OgreHardwarePixelBuffer.h>
|
||||
#include <OgreRoot.h>
|
||||
|
||||
#include <extern/shiny/Main/Factory.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
|
||||
RippleSimulation::RippleSimulation(Ogre::SceneManager* mainSceneManager)
|
||||
: mMainSceneMgr(mainSceneManager),
|
||||
mTime(0),
|
||||
mCurrentFrameOffset(0,0),
|
||||
mPreviousFrameOffset(0,0),
|
||||
mRippleCenter(0,0),
|
||||
mTextureSize(512),
|
||||
mRippleAreaLength(1000),
|
||||
mImpulseSize(20),
|
||||
mTexelOffset(0,0),
|
||||
mFirstUpdate(true)
|
||||
{
|
||||
Ogre::AxisAlignedBox aabInf;
|
||||
aabInf.setInfinite();
|
||||
|
||||
mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC);
|
||||
|
||||
mCamera = mSceneMgr->createCamera("RippleCamera");
|
||||
|
||||
mRectangle = new Ogre::Rectangle2D(true);
|
||||
mRectangle->setBoundingBox(aabInf);
|
||||
mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0, false);
|
||||
Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
|
||||
node->attachObject(mRectangle);
|
||||
|
||||
mImpulse = new Ogre::Rectangle2D(true);
|
||||
mImpulse->setCorners(-0.1, 0.1, 0.1, -0.1, false);
|
||||
mImpulse->setBoundingBox(aabInf);
|
||||
mImpulse->setMaterial("AddImpulse");
|
||||
Ogre::SceneNode* impulseNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
|
||||
impulseNode->attachObject(mImpulse);
|
||||
|
||||
//float w=0.05;
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
Ogre::TexturePtr texture;
|
||||
if (i != 3)
|
||||
texture = Ogre::TextureManager::getSingleton().createManual("RippleHeight" + Ogre::StringConverter::toString(i),
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mTextureSize, mTextureSize, 1, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
|
||||
else
|
||||
texture = Ogre::TextureManager::getSingleton().createManual("RippleNormal",
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mTextureSize, mTextureSize, 1, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
|
||||
|
||||
|
||||
Ogre::RenderTexture* rt = texture->getBuffer()->getRenderTarget();
|
||||
rt->removeAllViewports();
|
||||
rt->addViewport(mCamera);
|
||||
rt->setAutoUpdated(false);
|
||||
rt->getViewport(0)->setClearEveryFrame(false);
|
||||
|
||||
// debug overlay
|
||||
/*
|
||||
Ogre::Rectangle2D* debugOverlay = new Ogre::Rectangle2D(true);
|
||||
debugOverlay->setCorners(w*2-1, 0.9, (w+0.18)*2-1, 0.4, false);
|
||||
w += 0.2;
|
||||
debugOverlay->setBoundingBox(aabInf);
|
||||
Ogre::SceneNode* debugNode = mMainSceneMgr->getRootSceneNode()->createChildSceneNode();
|
||||
debugNode->attachObject(debugOverlay);
|
||||
|
||||
Ogre::MaterialPtr debugMaterial = Ogre::MaterialManager::getSingleton().create("RippleDebug" + Ogre::StringConverter::toString(i),
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
|
||||
if (i != 3)
|
||||
debugMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("RippleHeight" + Ogre::StringConverter::toString(i));
|
||||
else
|
||||
debugMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("RippleNormal");
|
||||
debugMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false);
|
||||
|
||||
debugOverlay->setMaterial("RippleDebug" + Ogre::StringConverter::toString(i));
|
||||
*/
|
||||
|
||||
mRenderTargets[i] = rt;
|
||||
mTextures[i] = texture;
|
||||
}
|
||||
|
||||
sh::Factory::getInstance().setSharedParameter("rippleTextureSize", sh::makeProperty<sh::Vector4>(
|
||||
new sh::Vector4(1.0/512, 1.0/512, 512, 512)));
|
||||
sh::Factory::getInstance().setSharedParameter("rippleCenter", sh::makeProperty<sh::Vector3>(
|
||||
new sh::Vector3(0, 0, 0)));
|
||||
sh::Factory::getInstance().setSharedParameter("rippleAreaLength", sh::makeProperty<sh::FloatValue>(
|
||||
new sh::FloatValue(mRippleAreaLength)));
|
||||
|
||||
}
|
||||
|
||||
RippleSimulation::~RippleSimulation()
|
||||
{
|
||||
delete mRectangle;
|
||||
|
||||
Ogre::Root::getSingleton().destroySceneManager(mSceneMgr);
|
||||
}
|
||||
|
||||
void RippleSimulation::update(float dt, Ogre::Vector2 position)
|
||||
{
|
||||
// try to keep 20 fps
|
||||
mTime += dt;
|
||||
|
||||
while (mTime >= 1/20.0 || mFirstUpdate)
|
||||
{
|
||||
mPreviousFrameOffset = mCurrentFrameOffset;
|
||||
|
||||
mCurrentFrameOffset = position - mRippleCenter;
|
||||
// add texel offsets from previous frame.
|
||||
mCurrentFrameOffset += mTexelOffset;
|
||||
|
||||
mTexelOffset = Ogre::Vector2(std::fmod(mCurrentFrameOffset.x, 1.0f/mTextureSize),
|
||||
std::fmod(mCurrentFrameOffset.y, 1.0f/mTextureSize));
|
||||
|
||||
// now subtract new offset in order to snap to texels
|
||||
mCurrentFrameOffset -= mTexelOffset;
|
||||
|
||||
// texture coordinate space
|
||||
mCurrentFrameOffset /= mRippleAreaLength;
|
||||
|
||||
mRippleCenter = position;
|
||||
|
||||
addImpulses();
|
||||
waterSimulation();
|
||||
heightMapToNormalMap();
|
||||
|
||||
swapHeightMaps();
|
||||
if (!mFirstUpdate)
|
||||
mTime -= 1/20.0;
|
||||
else
|
||||
mFirstUpdate = false;
|
||||
}
|
||||
|
||||
sh::Factory::getInstance().setSharedParameter("rippleCenter", sh::makeProperty<sh::Vector3>(
|
||||
new sh::Vector3(mRippleCenter.x + mTexelOffset.x, mRippleCenter.y + mTexelOffset.y, 0)));
|
||||
}
|
||||
|
||||
void RippleSimulation::addImpulses()
|
||||
{
|
||||
mRectangle->setVisible(false);
|
||||
mImpulse->setVisible(true);
|
||||
|
||||
/// \todo it should be more efficient to render all emitters at once
|
||||
for (std::vector<Emitter>::iterator it=mEmitters.begin(); it !=mEmitters.end(); ++it)
|
||||
{
|
||||
if (it->mPtr == MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ())
|
||||
{
|
||||
// fetch a new ptr (to handle cell change etc)
|
||||
// for non-player actors this is done in updateObjectCell
|
||||
it->mPtr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ();
|
||||
}
|
||||
float* _currentPos = it->mPtr.getRefData().getPosition().pos;
|
||||
Ogre::Vector3 currentPos (_currentPos[0], _currentPos[1], _currentPos[2]);
|
||||
|
||||
if ( (currentPos - it->mLastEmitPosition).length() > 2
|
||||
&& MWBase::Environment::get().getWorld ()->isUnderwater (it->mPtr.getCell(), currentPos))
|
||||
{
|
||||
it->mLastEmitPosition = currentPos;
|
||||
|
||||
Ogre::Vector2 pos (currentPos.x, currentPos.y);
|
||||
pos -= mRippleCenter;
|
||||
pos /= mRippleAreaLength;
|
||||
float size = mImpulseSize / mRippleAreaLength;
|
||||
mImpulse->setCorners(pos.x-size, pos.y+size, pos.x+size, pos.y-size, false);
|
||||
|
||||
// don't render if we are offscreen
|
||||
if (pos.x - size >= 1.0 || pos.y+size <= -1.0 || pos.x+size <= -1.0 || pos.y-size >= 1.0)
|
||||
continue;
|
||||
mRenderTargets[1]->update();
|
||||
}
|
||||
}
|
||||
|
||||
mImpulse->setVisible(false);
|
||||
mRectangle->setVisible(true);
|
||||
}
|
||||
|
||||
void RippleSimulation::waterSimulation()
|
||||
{
|
||||
mRectangle->setMaterial("HeightmapSimulation");
|
||||
|
||||
sh::Factory::getInstance().setTextureAlias("Heightmap0", mTextures[0]->getName());
|
||||
sh::Factory::getInstance().setTextureAlias("Heightmap1", mTextures[1]->getName());
|
||||
|
||||
sh::Factory::getInstance().setSharedParameter("currentFrameOffset", sh::makeProperty<sh::Vector3>(
|
||||
new sh::Vector3(mCurrentFrameOffset.x, mCurrentFrameOffset.y, 0)));
|
||||
sh::Factory::getInstance().setSharedParameter("previousFrameOffset", sh::makeProperty<sh::Vector3>(
|
||||
new sh::Vector3(mPreviousFrameOffset.x, mPreviousFrameOffset.y, 0)));
|
||||
|
||||
mRenderTargets[2]->update();
|
||||
}
|
||||
|
||||
void RippleSimulation::heightMapToNormalMap()
|
||||
{
|
||||
mRectangle->setMaterial("HeightToNormalMap");
|
||||
|
||||
sh::Factory::getInstance().setTextureAlias("Heightmap2", mTextures[2]->getName());
|
||||
|
||||
mRenderTargets[TEX_NORMAL]->update();
|
||||
}
|
||||
|
||||
void RippleSimulation::swapHeightMaps()
|
||||
{
|
||||
// 0 -> 1 -> 2 to 2 -> 0 ->1
|
||||
Ogre::RenderTexture* tmp = mRenderTargets[0];
|
||||
Ogre::TexturePtr tmp2 = mTextures[0];
|
||||
|
||||
mRenderTargets[0] = mRenderTargets[1];
|
||||
mTextures[0] = mTextures[1];
|
||||
|
||||
mRenderTargets[1] = mRenderTargets[2];
|
||||
mTextures[1] = mTextures[2];
|
||||
|
||||
mRenderTargets[2] = tmp;
|
||||
mTextures[2] = tmp2;
|
||||
}
|
||||
|
||||
void RippleSimulation::addEmitter(const MWWorld::Ptr& ptr, float scale, float force)
|
||||
{
|
||||
Emitter newEmitter;
|
||||
newEmitter.mPtr = ptr;
|
||||
newEmitter.mScale = scale;
|
||||
newEmitter.mForce = force;
|
||||
newEmitter.mLastEmitPosition = Ogre::Vector3(0,0,0);
|
||||
mEmitters.push_back (newEmitter);
|
||||
}
|
||||
|
||||
void RippleSimulation::removeEmitter (const MWWorld::Ptr& ptr)
|
||||
{
|
||||
for (std::vector<Emitter>::iterator it = mEmitters.begin(); it != mEmitters.end(); ++it)
|
||||
{
|
||||
if (it->mPtr == ptr)
|
||||
{
|
||||
mEmitters.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RippleSimulation::updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr)
|
||||
{
|
||||
for (std::vector<Emitter>::iterator it = mEmitters.begin(); it != mEmitters.end(); ++it)
|
||||
{
|
||||
if (it->mPtr == old)
|
||||
{
|
||||
it->mPtr = ptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
#ifndef RIPPLE_SIMULATION_H
|
||||
#define RIPPLE_SIMULATION_H
|
||||
|
||||
#include <OgreTexture.h>
|
||||
#include <OgreMaterial.h>
|
||||
#include <OgreVector2.h>
|
||||
#include <OgreVector3.h>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class RenderTexture;
|
||||
class Camera;
|
||||
class SceneManager;
|
||||
class Rectangle2D;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
struct Emitter
|
||||
{
|
||||
MWWorld::Ptr mPtr;
|
||||
Ogre::Vector3 mLastEmitPosition;
|
||||
float mScale;
|
||||
float mForce;
|
||||
};
|
||||
|
||||
class RippleSimulation
|
||||
{
|
||||
public:
|
||||
RippleSimulation(Ogre::SceneManager* mainSceneManager);
|
||||
~RippleSimulation();
|
||||
|
||||
void update(float dt, Ogre::Vector2 position);
|
||||
|
||||
/// adds an emitter, position will be tracked automatically
|
||||
void addEmitter (const MWWorld::Ptr& ptr, float scale = 1.f, float force = 1.f);
|
||||
void removeEmitter (const MWWorld::Ptr& ptr);
|
||||
void updateEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr);
|
||||
|
||||
private:
|
||||
std::vector<Emitter> mEmitters;
|
||||
|
||||
Ogre::RenderTexture* mRenderTargets[4];
|
||||
Ogre::TexturePtr mTextures[4];
|
||||
|
||||
int mTextureSize;
|
||||
float mRippleAreaLength;
|
||||
float mImpulseSize;
|
||||
|
||||
bool mFirstUpdate;
|
||||
|
||||
Ogre::Camera* mCamera;
|
||||
|
||||
// own scenemanager to render our simulation
|
||||
Ogre::SceneManager* mSceneMgr;
|
||||
Ogre::Rectangle2D* mRectangle;
|
||||
|
||||
// scenemanager to create the debug overlays on
|
||||
Ogre::SceneManager* mMainSceneMgr;
|
||||
|
||||
static const int TEX_NORMAL = 3;
|
||||
|
||||
Ogre::Rectangle2D* mImpulse;
|
||||
|
||||
void addImpulses();
|
||||
void heightMapToNormalMap();
|
||||
void waterSimulation();
|
||||
void swapHeightMaps();
|
||||
|
||||
float mTime;
|
||||
|
||||
Ogre::Vector2 mRippleCenter;
|
||||
|
||||
Ogre::Vector2 mTexelOffset;
|
||||
|
||||
Ogre::Vector2 mCurrentFrameOffset;
|
||||
Ogre::Vector2 mPreviousFrameOffset;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,175 @@
|
||||
#ifndef OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP
|
||||
#define OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
|
||||
class NIFFile;
|
||||
|
||||
class NIFStream {
|
||||
|
||||
/// Input stream
|
||||
Ogre::DataStreamPtr inp;
|
||||
|
||||
uint8_t read_byte()
|
||||
{
|
||||
uint8_t byte;
|
||||
if(inp->read(&byte, 1) != 1) return 0;
|
||||
return byte;
|
||||
}
|
||||
uint16_t read_le16()
|
||||
{
|
||||
uint8_t buffer[2];
|
||||
if(inp->read(buffer, 2) != 2) return 0;
|
||||
return buffer[0] | (buffer[1]<<8);
|
||||
}
|
||||
uint32_t read_le32()
|
||||
{
|
||||
uint8_t buffer[4];
|
||||
if(inp->read(buffer, 4) != 4) return 0;
|
||||
return buffer[0] | (buffer[1]<<8) | (buffer[2]<<16) | (buffer[3]<<24);
|
||||
}
|
||||
float read_le32f()
|
||||
{
|
||||
union {
|
||||
uint32_t i;
|
||||
float f;
|
||||
} u = { read_le32() };
|
||||
return u.f;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
NIFFile * const file;
|
||||
|
||||
NIFStream (NIFFile * file, Ogre::DataStreamPtr inp): file (file), inp (inp) {}
|
||||
|
||||
/*************************************************
|
||||
Parser functions
|
||||
****************************************************/
|
||||
|
||||
template <typename T>
|
||||
struct GetHandler
|
||||
{
|
||||
typedef T (NIFStream::*fn_t)();
|
||||
|
||||
static const fn_t sValue; // this is specialized per supported type in the .cpp file
|
||||
|
||||
static T read (NIFStream* nif)
|
||||
{
|
||||
return (nif->*sValue) ();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void read (NIFStream* nif, T & Value)
|
||||
{
|
||||
Value = GetHandler <T>::read (nif);
|
||||
}
|
||||
|
||||
void skip(size_t size) { inp->skip(size); }
|
||||
void read (void * data, size_t size) { inp->read (data, size); }
|
||||
|
||||
char getChar() { return read_byte(); }
|
||||
short getShort() { return read_le16(); }
|
||||
unsigned short getUShort() { return read_le16(); }
|
||||
int getInt() { return read_le32(); }
|
||||
int getUInt() { return read_le32(); }
|
||||
float getFloat() { return read_le32f(); }
|
||||
Ogre::Vector2 getVector2()
|
||||
{
|
||||
float a[2];
|
||||
for(size_t i = 0;i < 2;i++)
|
||||
a[i] = getFloat();
|
||||
return Ogre::Vector2(a);
|
||||
}
|
||||
Ogre::Vector3 getVector3()
|
||||
{
|
||||
float a[3];
|
||||
for(size_t i = 0;i < 3;i++)
|
||||
a[i] = getFloat();
|
||||
return Ogre::Vector3(a);
|
||||
}
|
||||
Ogre::Vector4 getVector4()
|
||||
{
|
||||
float a[4];
|
||||
for(size_t i = 0;i < 4;i++)
|
||||
a[i] = getFloat();
|
||||
return Ogre::Vector4(a);
|
||||
}
|
||||
Ogre::Matrix3 getMatrix3()
|
||||
{
|
||||
Ogre::Real a[3][3];
|
||||
for(size_t i = 0;i < 3;i++)
|
||||
{
|
||||
for(size_t j = 0;j < 3;j++)
|
||||
a[i][j] = Ogre::Real(getFloat());
|
||||
}
|
||||
return Ogre::Matrix3(a);
|
||||
}
|
||||
Ogre::Quaternion getQuaternion()
|
||||
{
|
||||
float a[4];
|
||||
for(size_t i = 0;i < 4;i++)
|
||||
a[i] = getFloat();
|
||||
return Ogre::Quaternion(a);
|
||||
}
|
||||
Transformation getTrafo()
|
||||
{
|
||||
Transformation t;
|
||||
t.pos = getVector3();
|
||||
t.rotation = getMatrix3();
|
||||
t.scale = getFloat();
|
||||
return t;
|
||||
}
|
||||
|
||||
std::string getString(size_t length)
|
||||
{
|
||||
std::vector<char> str (length+1, 0);
|
||||
|
||||
if(inp->read(&str[0], length) != length)
|
||||
throw std::runtime_error ("string length in NIF file does not match");
|
||||
|
||||
return &str[0];
|
||||
}
|
||||
std::string getString()
|
||||
{
|
||||
size_t size = read_le32();
|
||||
return getString(size);
|
||||
}
|
||||
|
||||
void getShorts(std::vector<short> &vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
for(size_t i = 0;i < vec.size();i++)
|
||||
vec[i] = getShort();
|
||||
}
|
||||
void getFloats(std::vector<float> &vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
for(size_t i = 0;i < vec.size();i++)
|
||||
vec[i] = getFloat();
|
||||
}
|
||||
void getVector2s(std::vector<Ogre::Vector2> &vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
for(size_t i = 0;i < vec.size();i++)
|
||||
vec[i] = getVector2();
|
||||
}
|
||||
void getVector3s(std::vector<Ogre::Vector3> &vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
for(size_t i = 0;i < vec.size();i++)
|
||||
vec[i] = getVector3();
|
||||
}
|
||||
void getVector4s(std::vector<Ogre::Vector4> &vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
for(size_t i = 0;i < vec.size();i++)
|
||||
vec[i] = getVector4();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue