#include "ripplesimulation.hpp" #include #include #include #include #include #include #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::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.7); // 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)); } } 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::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::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(); } }