You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
6.1 KiB
C++
170 lines
6.1 KiB
C++
#include "ripplesimulation.hpp"
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <OgreSceneNode.h>
|
|
#include <OgreSceneManager.h>
|
|
#include <OgreParticleSystem.h>
|
|
#include <OgreParticle.h>
|
|
|
|
#include <extern/shiny/Main/Factory.hpp>
|
|
|
|
#include "../mwbase/environment.hpp"
|
|
#include "../mwbase/world.hpp"
|
|
|
|
#include "../mwworld/fallback.hpp"
|
|
|
|
#include "renderconst.hpp"
|
|
|
|
namespace MWRender
|
|
{
|
|
|
|
RippleSimulation::RippleSimulation(Ogre::SceneManager* mainSceneManager, const MWWorld::Fallback* fallback)
|
|
: mSceneMgr(mainSceneManager)
|
|
, mParticleSystem(NULL)
|
|
, mSceneNode(NULL)
|
|
{
|
|
mRippleLifeTime = fallback->getFallbackFloat("Water_RippleLifetime");
|
|
mRippleRotSpeed = fallback->getFallbackFloat("Water_RippleRotSpeed");
|
|
|
|
// Unknown:
|
|
// fallback=Water_RippleScale,0.15, 6.5
|
|
// fallback=Water_RippleAlphas,0.7, 0.1, 0.01
|
|
|
|
// Instantiate from ripples.particle file
|
|
mParticleSystem = mSceneMgr->createParticleSystem("openmw/Ripples", "openmw/Ripples");
|
|
|
|
mParticleSystem->setRenderQueueGroup(RQG_Ripples);
|
|
mParticleSystem->setVisibilityFlags(RV_Effects);
|
|
|
|
int rippleFrameCount = fallback->getFallbackInt("Water_RippleFrameCount");
|
|
std::string tex = fallback->getFallbackString("Water_RippleTexture");
|
|
|
|
sh::MaterialInstance* mat = sh::Factory::getInstance().getMaterialInstance("openmw/Ripple");
|
|
mat->setProperty("anim_texture2", sh::makeProperty(new sh::StringValue(std::string("textures\\water\\") + tex + ".dds "
|
|
+ Ogre::StringConverter::toString(rippleFrameCount)
|
|
+ " "
|
|
+ Ogre::StringConverter::toString(0.3))));
|
|
|
|
// seems to be required to allocate mFreeParticles. TODO: patch Ogre to handle this better
|
|
mParticleSystem->_update(0.f);
|
|
|
|
mSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
|
|
mSceneNode->attachObject(mParticleSystem);
|
|
}
|
|
|
|
RippleSimulation::~RippleSimulation()
|
|
{
|
|
if (mParticleSystem)
|
|
mSceneMgr->destroyParticleSystem(mParticleSystem);
|
|
mParticleSystem = NULL;
|
|
|
|
if (mSceneNode)
|
|
mSceneMgr->destroySceneNode(mSceneNode);
|
|
mSceneNode = NULL;
|
|
}
|
|
|
|
void RippleSimulation::update(float dt, Ogre::Vector2 position)
|
|
{
|
|
bool newParticle = false;
|
|
for (std::vector<Emitter>::iterator it=mEmitters.begin(); it !=mEmitters.end(); ++it)
|
|
{
|
|
if (it->mPtr == MWBase::Environment::get().getWorld ()->getPlayerPtr())
|
|
{
|
|
// fetch a new ptr (to handle cell change etc)
|
|
// for non-player actors this is done in updateObjectCell
|
|
it->mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr();
|
|
}
|
|
Ogre::Vector3 currentPos (it->mPtr.getRefData().getPosition().pos);
|
|
currentPos.z = 0;
|
|
if ( (currentPos - it->mLastEmitPosition).length() > 10
|
|
// Only emit when close to the water surface, not above it and not too deep in the water
|
|
&& MWBase::Environment::get().getWorld ()->isUnderwater (it->mPtr.getCell(),
|
|
Ogre::Vector3(it->mPtr.getRefData().getPosition().pos))
|
|
&& !MWBase::Environment::get().getWorld()->isSubmerged(it->mPtr))
|
|
{
|
|
it->mLastEmitPosition = currentPos;
|
|
|
|
newParticle = true;
|
|
Ogre::Particle* created = mParticleSystem->createParticle();
|
|
if (!created)
|
|
break; // TODO: cleanup the oldest particle to make room
|
|
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
|
|
Ogre::Vector3& position = created->mPosition;
|
|
Ogre::Vector3& direction = created->mDirection;
|
|
Ogre::ColourValue& colour = created->mColour;
|
|
float& totalTimeToLive = created->mTotalTimeToLive;
|
|
float& timeToLive = created->mTimeToLive;
|
|
Ogre::Radian& rotSpeed = created->mRotationSpeed;
|
|
Ogre::Radian& rotation = created->mRotation;
|
|
#else
|
|
Ogre::Vector3& position = created->position;
|
|
Ogre::Vector3& direction = created->direction;
|
|
Ogre::ColourValue& colour = created->colour;
|
|
float& totalTimeToLive = created->totalTimeToLive;
|
|
float& timeToLive = created->timeToLive;
|
|
Ogre::Radian& rotSpeed = created->rotationSpeed;
|
|
Ogre::Radian& rotation = created->rotation;
|
|
#endif
|
|
timeToLive = totalTimeToLive = mRippleLifeTime;
|
|
colour = Ogre::ColourValue(0.f, 0.f, 0.f, 0.7f); // Water_RippleAlphas.x?
|
|
direction = Ogre::Vector3(0,0,0);
|
|
position = currentPos;
|
|
position.z = 0; // Z is set by the Scene Node
|
|
rotSpeed = mRippleRotSpeed;
|
|
rotation = Ogre::Radian(Ogre::Math::RangeRandom(-Ogre::Math::PI, Ogre::Math::PI));
|
|
created->setDimensions(mParticleSystem->getDefaultWidth(), mParticleSystem->getDefaultHeight());
|
|
}
|
|
}
|
|
|
|
if (newParticle) // now apparently needs another update, otherwise it won't render in the first frame after a particle is created. TODO: patch Ogre to handle this better
|
|
mParticleSystem->_update(0.f);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RippleSimulation::setWaterHeight(float height)
|
|
{
|
|
mSceneNode->setPosition(0,0,height);
|
|
}
|
|
|
|
void RippleSimulation::clear()
|
|
{
|
|
mParticleSystem->clear();
|
|
}
|
|
|
|
|
|
}
|