diff --git a/.travis.yml b/.travis.yml index 69879bd78..22f6164c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,8 +40,8 @@ script: - cd ./build - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j2; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi - - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi - - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi + - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && ["${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi + - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && ["${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi notifications: recipients: - corrmage+travis-ci@gmail.com diff --git a/CI/check_tabs.sh b/CI/check_tabs.sh index 91f81e2a1..1e88b57fd 100755 --- a/CI/check_tabs.sh +++ b/CI/check_tabs.sh @@ -1,6 +1,6 @@ #!/bin/bash -OUTPUT=$(grep -nRP '\t' --include=\*.{cpp,hpp,c,h} apps components) +OUTPUT=$(grep -nRP '\t' --include=\*.{cpp,hpp,c,h} --exclude=ui_\* apps components) if [[ $OUTPUT ]] ; then echo "Error: Tab characters found!" diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 36c251053..c9b248984 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -504,8 +504,6 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mAnimation->play(mCurrentIdle, idlePriority, MWRender::Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, ~0ul, true); } - - updateIdleStormState(); } @@ -656,6 +654,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim , mAnimation(anim) , mIdleState(CharState_None) , mMovementState(CharState_None) + , mAdjustMovementAnimSpeed(false) , mHasMovedInXY(false) , mMovementAnimationControlled(true) , mDeathState(CharState_None) @@ -867,7 +866,7 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) mPtr = ptr; } -void CharacterController::updateIdleStormState() +void CharacterController::updateIdleStormState(bool inwater) { bool inStormDirection = false; if (MWBase::Environment::get().getWorld()->isInStorm()) @@ -877,7 +876,7 @@ void CharacterController::updateIdleStormState() inStormDirection = std::acos(stormDirection * characterDirection / (stormDirection.length() * characterDirection.length())) > osg::DegreesToRadians(120.f); } - if (inStormDirection && mUpperBodyState == UpperCharState_Nothing && mAnimation->hasAnimation("idlestorm")) + if (inStormDirection && !inwater && mUpperBodyState == UpperCharState_Nothing && mAnimation->hasAnimation("idlestorm")) { float complete = 0; mAnimation->getInfo("idlestorm", &complete); @@ -1796,6 +1795,7 @@ void CharacterController::update(float duration) forcestateupdate = updateCreatureState() || forcestateupdate; refreshCurrentAnims(idlestate, movestate, jumpstate, forcestateupdate); + updateIdleStormState(inwater); } if (inJump) @@ -2122,7 +2122,7 @@ void CharacterController::updateHeadTracking(float duration) osg::Matrixf mat = mats[0]; osg::Vec3f headPos = mat.getTrans(); - osg::Vec3f targetPos (mHeadTrackTarget.getRefData().getPosition().asVec3()); + osg::Vec3f direction; if (MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mHeadTrackTarget)) { const osg::Node* node = anim->getNode("Head"); @@ -2132,11 +2132,12 @@ void CharacterController::updateHeadTracking(float duration) { osg::MatrixList mats = node->getWorldMatrices(); if (mats.size()) - targetPos = mats[0].getTrans(); + direction = mats[0].getTrans() - headPos; } + else + // no head node to look at, fall back to look at center of collision box + direction = MWBase::Environment::get().getWorld()->aimToTarget(mPtr, mHeadTrackTarget); } - - osg::Vec3f direction = targetPos - headPos; direction.normalize(); if (!mPtr.getRefData().getBaseNode()) diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index fee7b959c..90e285b52 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -196,7 +196,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener bool updateWeaponState(); bool updateCreatureState(); - void updateIdleStormState(); + void updateIdleStormState(bool inwater); void updateHeadTracking(float duration); diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index a681c7945..98ccefe71 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -95,6 +95,11 @@ void Actor::updatePosition() mCollisionObject->setWorldTransform(tr); } +osg::Vec3f Actor::getPosition() const +{ + return toOsg(mCollisionObject->getWorldTransform().getOrigin()); +} + void Actor::updateRotation () { btTransform tr = mCollisionObject->getWorldTransform(); diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index a4afa48a1..bcbc256d0 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -70,6 +70,12 @@ namespace MWPhysics */ osg::Vec3f getHalfExtents() const; + /** + * Returns the position of the collision body + * @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space. + */ + osg::Vec3f getPosition() const; + /** * Returns the half extents of the collision body (scaled according to rendering scale) * @note The reason we need this extra method is because of an inconsistency in MW - NPC race scales aren't applied to the collision shape, diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 80a63bf1b..92994d557 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -240,6 +240,8 @@ namespace MWPhysics const ESM::Position& refpos = ptr.getRefData().getPosition(); osg::Vec3f position(refpos.asVec3()); + float collisionShapeOffset = physicActor->getPosition().z() - position.z(); + // Early-out for totally static creatures // (Not sure if gravity should still apply?) if (!ptr.getClass().isMobile(ptr)) @@ -256,12 +258,11 @@ namespace MWPhysics } btCollisionObject *colobj = physicActor->getCollisionObject(); - osg::Vec3f halfExtents = physicActor->getHalfExtents(); - position.z() += halfExtents.z(); + position.z() += collisionShapeOffset; static const float fSwimHeightScale = MWBase::Environment::get().getWorld()->getStore().get() .find("fSwimHeightScale")->getFloat(); - float swimlevel = waterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale); + float swimlevel = waterlevel + collisionShapeOffset - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale); ActorTracer tracer; osg::Vec3f inertia = physicActor->getInertialForce(); @@ -369,7 +370,7 @@ namespace MWPhysics { // don't let pure water creatures move out of water after stepMove if (ptr.getClass().isPureWaterCreature(ptr) - && newPosition.z() + halfExtents.z() > waterlevel) + && newPosition.z() + physicActor->getHalfExtents().z() > waterlevel) newPosition = oldPosition; } else @@ -450,7 +451,7 @@ namespace MWPhysics } physicActor->setOnGround(isOnGround); - newPosition.z() -= halfExtents.z(); // remove what was added at the beginning + newPosition.z() -= collisionShapeOffset; // remove what was added at the beginning return newPosition; } }; @@ -820,12 +821,8 @@ namespace MWPhysics if (!physactor1 || !physactor2) return false; - osg::Vec3f halfExt1 = physactor1->getHalfExtents(); - osg::Vec3f pos1 (actor1.getRefData().getPosition().asVec3()); - pos1.z() += halfExt1.z()*2*0.9f; // eye level - osg::Vec3f halfExt2 = physactor2->getHalfExtents(); - osg::Vec3f pos2 (actor2.getRefData().getPosition().asVec3()); - pos2.z() += halfExt2.z()*2*0.9f; + osg::Vec3f pos1 (physactor1->getPosition() + osg::Vec3f(0,0,physactor1->getHalfExtents().z() * 0.8)); // eye level + osg::Vec3f pos2 (physactor2->getPosition() + osg::Vec3f(0,0,physactor2->getHalfExtents().z() * 0.8)); RayResult result = castRay(pos1, pos2, MWWorld::Ptr(), CollisionType_World|CollisionType_HeightMap); @@ -869,24 +866,33 @@ namespace MWPhysics } } - osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::Ptr &actor) + osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::Ptr &actor) const { - Actor* physactor = getActor(actor); + const Actor* physactor = getActor(actor); if (physactor) return physactor->getHalfExtents(); else return osg::Vec3f(); } - osg::Vec3f PhysicsSystem::getRenderingHalfExtents(const MWWorld::Ptr &actor) + osg::Vec3f PhysicsSystem::getRenderingHalfExtents(const MWWorld::Ptr &actor) const { - Actor* physactor = getActor(actor); + const Actor* physactor = getActor(actor); if (physactor) return physactor->getRenderingHalfExtents(); else return osg::Vec3f(); } + osg::Vec3f PhysicsSystem::getPosition(const MWWorld::Ptr &actor) const + { + const Actor* physactor = getActor(actor); + if (physactor) + return physactor->getPosition(); + else + return osg::Vec3f(); + } + class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback { public: @@ -1036,6 +1042,14 @@ namespace MWPhysics return NULL; } + const Actor *PhysicsSystem::getActor(const MWWorld::Ptr &ptr) const + { + ActorMap::const_iterator found = mActors.find(ptr); + if (found != mActors.end()) + return found->second; + return NULL; + } + void PhysicsSystem::updateScale(const MWWorld::Ptr &ptr) { ObjectMap::iterator found = mObjects.find(ptr); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 6f38653c8..7c5be0b6e 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -62,6 +62,7 @@ namespace MWPhysics void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated); Actor* getActor(const MWWorld::Ptr& ptr); + const Actor* getActor(const MWWorld::Ptr& ptr) const; // Object or Actor void remove (const MWWorld::Ptr& ptr); @@ -108,10 +109,14 @@ namespace MWPhysics bool isOnGround (const MWWorld::Ptr& actor); /// Get physical half extents (scaled) of the given actor. - osg::Vec3f getHalfExtents(const MWWorld::Ptr& actor); + osg::Vec3f getHalfExtents(const MWWorld::Ptr& actor) const; /// @see MWPhysics::Actor::getRenderingHalfExtents - osg::Vec3f getRenderingHalfExtents(const MWWorld::Ptr& actor); + osg::Vec3f getRenderingHalfExtents(const MWWorld::Ptr& actor) const; + + /// Get the position of the collision shape for the actor. Use together with getHalfExtents() to get the collision bounds in world space. + /// @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space. + osg::Vec3f getPosition(const MWWorld::Ptr& actor) const; /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will /// be overwritten. Valid until the next call to applyQueuedMovement. diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index b2fa87310..06065c566 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1111,25 +1111,6 @@ namespace MWRender attachTo->addChild(lightSource); } - class DisableFreezeOnCullVisitor : public osg::NodeVisitor - { - public: - DisableFreezeOnCullVisitor() - : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) - { - } - - virtual void apply(osg::Geode &geode) - { - for (unsigned int i=0; i(drw)) - partsys->setFreezeOnCull(false); - } - } - }; - void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, std::string texture) { if (!mObjectRoot.get()) @@ -1163,7 +1144,7 @@ namespace MWRender node->accept(findMaxLengthVisitor); // FreezeOnCull doesn't work so well with effect particles, that tend to have moving emitters - DisableFreezeOnCullVisitor disableFreezeOnCullVisitor; + SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor; node->accept(disableFreezeOnCullVisitor); params.mMaxControllerLength = findMaxLengthVisitor.getMaxLength(); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e1c0ea666..7b11c9d85 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -32,6 +32,7 @@ #include #include "../mwworld/fallback.hpp" +#include "../mwworld/cellstore.hpp" #include "sky.hpp" #include "effectmanager.hpp" @@ -130,6 +131,10 @@ namespace MWRender , mRootNode(rootNode) , mResourceSystem(resourceSystem) , mFogDepth(0.f) + , mUnderwaterColor(fallback->getFallbackColour("Water_UnderwaterColor")) + , mUnderwaterWeight(fallback->getFallbackFloat("Water_UnderwaterColorWeight")) + , mUnderwaterFog(0.f) + , mUnderwaterIndoorFog(fallback->getFallbackFloat("Water_UnderwaterIndoorFog")) , mNightEyeFactor(0.f) { osg::ref_ptr lightRoot = new SceneUtil::LightManager; @@ -201,10 +206,6 @@ namespace MWRender updateProjectionMatrix(); mStateUpdater->setFogEnd(mViewDistance); - mUnderwaterColor = fallback->getFallbackColour("Water_UnderwaterColor"); - mUnderwaterWeight = fallback->getFallbackFloat("Water_UnderwaterColorWeight"); - mUnderwaterIndoorFog = fallback->getFallbackFloat("Water_UnderwaterIndoorFog"); - mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip)); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance)); } @@ -449,11 +450,13 @@ namespace MWRender void RenderingManager::setWaterEnabled(bool enabled) { mWater->setEnabled(enabled); + mSky->setWaterEnabled(enabled); } void RenderingManager::setWaterHeight(float height) { mWater->setHeight(height); + mSky->setWaterHeight(height); } class NotifyDrawCompletedCallback : public osg::Camera::DrawCallback diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index b0af85c0b..12708ba48 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -261,8 +262,18 @@ public: META_Node(MWRender, CameraRelativeTransform) - virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor*) const + const osg::Vec3f& getLastEyePoint() const { + return mEyePoint; + } + + virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv) const + { + if (nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) + { + mEyePoint = static_cast(nv)->getEyePoint(); + } + if (_referenceFrame==RELATIVE_RF) { matrix.setTrans(osg::Vec3f(0.f,0.f,0.f)); @@ -321,6 +332,9 @@ public: cv->getCurrentCullingSet().popCurrentMask(); } }; +private: + // eyePoint for the current frame + mutable osg::Vec3f mEyePoint; }; class ModVertexAlphaVisitor : public osg::NodeVisitor @@ -370,6 +384,45 @@ private: int mMeshType; }; +/// @brief Hides the node subgraph if the eye point is below water. +/// @note Must be added as cull callback. +/// @note Meant to be used on a node that is child of a CameraRelativeTransform. +/// The current eye point must be retrieved by the CameraRelativeTransform since we can't get it anymore once we are in camera-relative space. +class UnderwaterSwitchCallback : public osg::NodeCallback +{ +public: + UnderwaterSwitchCallback(CameraRelativeTransform* cameraRelativeTransform) + : mCameraRelativeTransform(cameraRelativeTransform) + , mEnabled(true) + , mWaterLevel(0.f) + { + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + osg::Vec3f eyePoint = mCameraRelativeTransform->getLastEyePoint(); + + if (mEnabled && eyePoint.z() < mWaterLevel) + return; + + traverse(node, nv); + } + + void setEnabled(bool enabled) + { + mEnabled = enabled; + } + void setWaterLevel(float waterLevel) + { + mWaterLevel = waterLevel; + } + +private: + osg::ref_ptr mCameraRelativeTransform; + bool mEnabled; + float mWaterLevel; +}; + /// A base class for the sun and moons. class CelestialBody { @@ -1065,18 +1118,21 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana mRootNode = skyroot; - // By default render before the world is rendered - mRootNode->getOrCreateStateSet()->setRenderBinDetails(RenderBin_Sky, "RenderBin"); - + mEarlyRenderBinRoot = new osg::Group; + // render before the world is rendered + mEarlyRenderBinRoot->getOrCreateStateSet()->setRenderBinDetails(RenderBin_Sky, "RenderBin"); // Prevent unwanted clipping by water reflection camera's clipping plane - mRootNode->getOrCreateStateSet()->setMode(GL_CLIP_PLANE0, osg::StateAttribute::OFF); + mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_CLIP_PLANE0, osg::StateAttribute::OFF); + mRootNode->addChild(mEarlyRenderBinRoot); + + mUnderwaterSwitch = new UnderwaterSwitchCallback(skyroot); } void SkyManager::create() { assert(!mCreated); - mAtmosphereDay = mSceneManager->createInstance("meshes/sky_atmosphere.nif", mRootNode); + mAtmosphereDay = mSceneManager->createInstance("meshes/sky_atmosphere.nif", mEarlyRenderBinRoot); ModVertexAlphaVisitor modAtmosphere(0); mAtmosphereDay->accept(modAtmosphere); @@ -1085,7 +1141,7 @@ void SkyManager::create() mAtmosphereNightNode = new osg::PositionAttitudeTransform; mAtmosphereNightNode->setNodeMask(0); - mRootNode->addChild(mAtmosphereNightNode); + mEarlyRenderBinRoot->addChild(mAtmosphereNightNode); osg::ref_ptr atmosphereNight; if (mSceneManager->getVFS()->exists("meshes/sky_night_02.nif")) @@ -1098,14 +1154,14 @@ void SkyManager::create() mAtmosphereNightUpdater = new AtmosphereNightUpdater(mSceneManager->getTextureManager()); atmosphereNight->addUpdateCallback(mAtmosphereNightUpdater); - mSun.reset(new Sun(mRootNode, *mSceneManager->getTextureManager())); + mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getTextureManager())); const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback(); - mMasser.reset(new Moon(mRootNode, *mSceneManager->getTextureManager(), fallback->getFallbackFloat("Moons_Masser_Size")/125, Moon::Type_Masser)); - mSecunda.reset(new Moon(mRootNode, *mSceneManager->getTextureManager(), fallback->getFallbackFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda)); + mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getTextureManager(), fallback->getFallbackFloat("Moons_Masser_Size")/125, Moon::Type_Masser)); + mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getTextureManager(), fallback->getFallbackFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda)); mCloudNode = new osg::PositionAttitudeTransform; - mRootNode->addChild(mCloudNode); + mEarlyRenderBinRoot->addChild(mCloudNode); mCloudMesh = mSceneManager->createInstance("meshes/sky_clouds_01.nif", mCloudNode); ModVertexAlphaVisitor modClouds(1); mCloudMesh->accept(modClouds); @@ -1122,9 +1178,9 @@ void SkyManager::create() osg::ref_ptr depth = new osg::Depth; depth->setWriteMask(false); - mRootNode->getOrCreateStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON); - mRootNode->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); - mRootNode->getOrCreateStateSet()->setMode(GL_FOG, osg::StateAttribute::OFF); + mEarlyRenderBinRoot->getOrCreateStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON); + mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); + mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_FOG, osg::StateAttribute::OFF); mMoonScriptColor = fallback->getFallbackColour("Moons_Script_Color"); @@ -1196,14 +1252,13 @@ public: mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,mAlpha)); } - // Helper for adding AlphaFader to a subgraph + // Helper for adding AlphaFaders to a subgraph class SetupVisitor : public osg::NodeVisitor { public: SetupVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) { - mAlphaFader = new AlphaFader; } virtual void apply(osg::Node &node) @@ -1225,22 +1280,26 @@ public: callback = callback->getNestedCallback(); } + osg::ref_ptr alphaFader (new AlphaFader); + if (composite) - composite->addController(mAlphaFader); + composite->addController(alphaFader); else - node.addUpdateCallback(mAlphaFader); + node.addUpdateCallback(alphaFader); + + mAlphaFaders.push_back(alphaFader); } } traverse(node); } - osg::ref_ptr getAlphaFader() + std::vector > getAlphaFaders() { - return mAlphaFader; + return mAlphaFaders; } private: - osg::ref_ptr mAlphaFader; + std::vector > mAlphaFaders; }; private: @@ -1278,6 +1337,7 @@ void SkyManager::createRain() stateset->setNestRenderBins(false); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + stateset->setMode(GL_BLEND, osg::StateAttribute::ON); osgParticle::Particle& particleTemplate = mRainParticleSystem->getDefaultParticleTemplate(); particleTemplate.setSizeRange(osgParticle::rangef(5.f, 15.f)); @@ -1313,6 +1373,8 @@ void SkyManager::createRain() mRainFader = new RainFader; mRainNode->addUpdateCallback(mRainFader); + mRainNode->addCullCallback(mUnderwaterSwitch); + mRainNode->setNodeMask(Mask_WeatherParticles); mRootNode->addChild(mRainNode); } @@ -1432,6 +1494,14 @@ void SkyManager::setWeather(const WeatherResult& weather) { mCurrentParticleEffect = weather.mParticleEffect; + // cleanup old particles + if (mParticleEffect) + { + mParticleNode->removeChild(mParticleEffect); + mParticleEffect = NULL; + mParticleFaders.clear(); + } + if (mCurrentParticleEffect.empty()) { if (mParticleNode) @@ -1439,14 +1509,14 @@ void SkyManager::setWeather(const WeatherResult& weather) mRootNode->removeChild(mParticleNode); mParticleNode = NULL; } - mParticleEffect = NULL; - mParticleFader = NULL; } else { if (!mParticleNode) { mParticleNode = new osg::PositionAttitudeTransform; + mParticleNode->addCullCallback(mUnderwaterSwitch); + mParticleNode->setNodeMask(Mask_WeatherParticles); mRootNode->addChild(mParticleNode); } mParticleEffect = mSceneManager->createInstance(mCurrentParticleEffect, mParticleNode); @@ -1456,7 +1526,10 @@ void SkyManager::setWeather(const WeatherResult& weather) AlphaFader::SetupVisitor alphaFaderSetupVisitor; mParticleEffect->accept(alphaFaderSetupVisitor); - mParticleFader = alphaFaderSetupVisitor.getAlphaFader(); + mParticleFaders = alphaFaderSetupVisitor.getAlphaFaders(); + + SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor; + mParticleEffect->accept(disableFreezeOnCullVisitor); } } @@ -1537,8 +1610,8 @@ void SkyManager::setWeather(const WeatherResult& weather) if (mRainFader) mRainFader->setAlpha(weather.mEffectFade * 0.6); // * Rain_Threshold? - if (mParticleFader) - mParticleFader->setAlpha(weather.mEffectFade); + for (std::vector >::const_iterator it = mParticleFaders.begin(); it != mParticleFaders.end(); ++it) + (*it)->setAlpha(weather.mEffectFade); } void SkyManager::sunEnable() @@ -1592,4 +1665,14 @@ void SkyManager::setGlareTimeOfDayFade(float val) mSun->setGlareTimeOfDayFade(val); } +void SkyManager::setWaterHeight(float height) +{ + mUnderwaterSwitch->setWaterLevel(height); +} + +void SkyManager::setWaterEnabled(bool enabled) +{ + mUnderwaterSwitch->setEnabled(enabled); +} + } diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 072083d27..0caadaa07 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -35,6 +35,7 @@ namespace MWRender class RainShooter; class RainFader; class AlphaFader; + class UnderwaterSwitchCallback; struct WeatherResult { @@ -100,6 +101,8 @@ namespace MWRender float mMoonAlpha; }; + ///@brief The SkyManager handles rendering of the sky domes, celestial bodies as well as other objects that need to be rendered + /// relative to the camera (e.g. weather particle effects) class SkyManager { public: @@ -144,6 +147,12 @@ namespace MWRender void setGlareTimeOfDayFade(float val); + /// Enable or disable the water plane (used to remove underwater weather particles) + void setWaterEnabled(bool enabled); + + /// Set height of water plane (used to remove underwater weather particles) + void setWaterHeight(float height); + private: void create(); ///< no need to call this, automatically done on first enable() @@ -155,10 +164,12 @@ namespace MWRender Resource::SceneManager* mSceneManager; osg::ref_ptr mRootNode; + osg::ref_ptr mEarlyRenderBinRoot; osg::ref_ptr mParticleNode; osg::ref_ptr mParticleEffect; - osg::ref_ptr mParticleFader; + std::vector > mParticleFaders; + osg::ref_ptr mUnderwaterSwitch; osg::ref_ptr mCloudNode; diff --git a/apps/openmw/mwrender/vismask.hpp b/apps/openmw/mwrender/vismask.hpp index b1329e958..a26bde1d1 100644 --- a/apps/openmw/mwrender/vismask.hpp +++ b/apps/openmw/mwrender/vismask.hpp @@ -15,21 +15,26 @@ namespace MWRender Mask_Actor = (1<<3), Mask_Player = (1<<4), Mask_Sky = (1<<5), - Mask_Sun = (1<<6), - Mask_Water = (1<<7), - Mask_SimpleWater = (1<<8), - Mask_Terrain = (1<<9), - Mask_FirstPerson = (1<<10), + Mask_Water = (1<<6), + Mask_Terrain = (1<<7), + Mask_FirstPerson = (1<<8), + + // child of Sky + Mask_Sun = (1<<9), + Mask_WeatherParticles = (1<<10), + + // child of Water + Mask_SimpleWater = (1<<11), // top level masks - Mask_Scene = (1<<11), - Mask_GUI = (1<<12), + Mask_Scene = (1<<12), + Mask_GUI = (1<<13), // Set on a Geode - Mask_ParticleSystem = (1<<13), + Mask_ParticleSystem = (1<<14), // Set on cameras within the main scene graph - Mask_RenderToTexture = (1<<14) + Mask_RenderToTexture = (1<<15) // reserved: (1<<16) for SceneUtil::Mask_Lit }; diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index bb6d549e0..80023ecda 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -33,6 +33,9 @@ #include +#include "../mwworld/cellstore.hpp" +#include "../mwworld/fallback.hpp" + #include "vismask.hpp" #include "ripplesimulation.hpp" #include "renderbin.hpp" @@ -79,6 +82,10 @@ namespace waterGeom->setVertexArray(verts); waterGeom->setTexCoordArray(0, texcoords); + osg::ref_ptr normal (new osg::Vec3Array); + normal->push_back(osg::Vec3f(0,0,1)); + waterGeom->setNormalArray(normal, osg::Array::BIND_OVERALL); + waterGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,verts->size())); return waterGeom; } @@ -145,12 +152,9 @@ class ClipCullNode : public osg::Group osg::RefMatrix* modelViewMatrix = new osg::RefMatrix(*cv->getModelViewMatrix()); - // move the plane back along its normal a little bit to prevent bleeding at the water shore - const float clipFudge = -5; - // now apply the height of the plane + // apply the height of the plane // we can't apply this height in the addClipPlane() since the "flip the below graph" function would otherwise flip the height as well - float translate = clipFudge + ((*mCullPlane)[3] * -1); - modelViewMatrix->preMultTranslate(mCullPlane->getNormal() * translate); + modelViewMatrix->preMultTranslate(mCullPlane->getNormal() * ((*mCullPlane)[3] * -1)); // flip the below graph if the eye point is above the plane if (mCullPlane->intersect(osg::BoundingSphere(osg::Vec3d(0,0,eyePoint.z()), 0)) > 0) @@ -158,6 +162,10 @@ class ClipCullNode : public osg::Group modelViewMatrix->preMultScale(osg::Vec3(1,1,-1)); } + // move the plane back along its normal a little bit to prevent bleeding at the water shore + const float clipFudge = -5; + modelViewMatrix->preMultTranslate(mCullPlane->getNormal() * clipFudge); + cv->pushModelViewMatrix(modelViewMatrix, osg::Transform::RELATIVE_RF); traverse(node, nv); cv->popModelViewMatrix(); @@ -451,6 +459,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem : mParent(parent) , mSceneRoot(sceneRoot) , mResourceSystem(resourceSystem) + , mFallback(fallback) , mResourcePath(resourcePath) , mEnabled(true) , mToggled(true) @@ -474,7 +483,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem // simple water fallback for the local map osg::ref_ptr geode2 (osg::clone(mWaterGeode.get(), osg::CopyOp::DEEP_COPY_NODES)); - createSimpleWaterStateSet(geode2); + createSimpleWaterStateSet(geode2, mFallback->getFallbackFloat("Water_Map_Alpha")); geode2->setNodeMask(Mask_SimpleWater); mWaterNode->addChild(geode2); @@ -516,19 +525,19 @@ void Water::updateWaterMaterial() createShaderWaterStateSet(mWaterGeode, mReflection, mRefraction); } else - createSimpleWaterStateSet(mWaterGeode); + createSimpleWaterStateSet(mWaterGeode, mFallback->getFallbackFloat("Water_World_Alpha")); updateVisible(); } -void Water::createSimpleWaterStateSet(osg::Node* node) +void Water::createSimpleWaterStateSet(osg::Node* node, float alpha) { osg::ref_ptr stateset (new osg::StateSet); osg::ref_ptr material (new osg::Material); - material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f)); - material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.7f)); - material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f)); + material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 1.f)); + material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, alpha)); + material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.f, 1.f, 1.f, 1.f)); material->setColorMode(osg::Material::OFF); stateset->setAttributeAndModes(material, osg::StateAttribute::ON); @@ -542,14 +551,18 @@ void Water::createSimpleWaterStateSet(osg::Node* node) stateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin"); std::vector > textures; - for (int i=0; i<32; ++i) + int frameCount = mFallback->getFallbackInt("Water_SurfaceFrameCount"); + std::string texture = mFallback->getFallbackString("Water_SurfaceTexture"); + for (int i=0; igetTextureManager()->getTexture2D(texname.str(), osg::Texture::REPEAT, osg::Texture::REPEAT)); } - osg::ref_ptr controller (new NifOsg::FlipController(0, 2/32.f, textures)); + float fps = mFallback->getFallbackFloat("Water_SurfaceFPS"); + + osg::ref_ptr controller (new NifOsg::FlipController(0, 1.f/fps, textures)); controller->setSource(boost::shared_ptr(new SceneUtil::FrameTimeSource)); node->setUpdateCallback(controller); node->setStateSet(stateset); diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index f77afbfee..551184c11 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -1,12 +1,13 @@ #ifndef OPENMW_MWRENDER_WATER_H #define OPENMW_MWRENDER_WATER_H +#include + #include +#include #include -#include "../mwworld/cellstore.hpp" - namespace osg { class Group; @@ -28,6 +29,8 @@ namespace Resource namespace MWWorld { class Fallback; + class CellStore; + class Ptr; } namespace MWRender @@ -47,6 +50,7 @@ namespace MWRender osg::ref_ptr mWaterNode; osg::ref_ptr mWaterGeode; Resource::ResourceSystem* mResourceSystem; + const MWWorld::Fallback* mFallback; osg::ref_ptr mIncrementalCompileOperation; std::auto_ptr mSimulation; @@ -63,7 +67,7 @@ namespace MWRender osg::Vec3f getSceneNodeCoordinates(int gridX, int gridY); void updateVisible(); - void createSimpleWaterStateSet(osg::Node* node); + void createSimpleWaterStateSet(osg::Node* node, float alpha); /// @param reflection the reflection camera (required) /// @param refraction the refraction camera (optional) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 32d86f55a..5aebbc3a9 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -37,7 +37,7 @@ namespace void addToLevList(ESM::LevelledListBase* list, const std::string& itemId, int level) { - for (std::vector::iterator it = list->mList.begin(); it != list->mList.end();) + for (std::vector::iterator it = list->mList.begin(); it != list->mList.end(); ++it) { if (it->mLevel == level && itemId == it->mId) return; diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 3e9f17278..d5aca17a6 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -68,12 +68,7 @@ namespace MWWorld const ESM::EffectList &effects, const Ptr &caster, const std::string &sourceName, const osg::Vec3f& fallbackDirection) { - float height = 0; - - height += mPhysics->getHalfExtents(caster).z() * 2.f * 0.75f; // Spawn at 0.75 * ActorHeight - - osg::Vec3f pos(caster.getRefData().getPosition().asVec3()); - pos.z() += height; + osg::Vec3f pos = mPhysics->getPosition(caster) + osg::Vec3f(0,0,mPhysics->getHalfExtents(caster).z() * 0.5); // Spawn at 0.75 * ActorHeight if (MWBase::Environment::get().getWorld()->isUnderwater(caster.getCell(), pos)) // Underwater casting not possible return; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 17118e169..0f8c5aa32 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -781,6 +781,9 @@ namespace MWWorld if (reference.getRefData().isEnabled()) { + if (reference == getPlayerPtr()) + throw std::runtime_error("can not disable player object"); + reference.getRefData().disable(); if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) @@ -3256,8 +3259,7 @@ namespace MWWorld osg::Vec3f World::aimToTarget(const Ptr &actor, const MWWorld::Ptr& target) { osg::Vec3f weaponPos = getActorHeadPosition(actor, mRendering); - osg::Vec3f targetPos = target.getRefData().getPosition().asVec3(); - targetPos.z() += mPhysics->getHalfExtents(target).z(); + osg::Vec3f targetPos = mPhysics->getPosition(target); return (targetPos - weaponPos); } } diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index 18d7ddd46..1e1d04cf5 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -280,37 +280,40 @@ namespace SceneUtil // - cull list of lights by the camera frustum // - organize lights in a quad tree - // Don't use Camera::getViewMatrix, that one might be relative to another camera! - const osg::RefMatrix* viewMatrix = cv->getCurrentRenderStage()->getInitialViewMatrix(); - const std::vector& lights = mLightManager->getLightsInViewSpace(cv->getCurrentCamera(), viewMatrix); - - if (lights.size()) + // update light list if necessary + // makes sure we don't update it more than once per frame when rendering with multiple cameras + if (mLastFrameNumber != nv->getFrameStamp()->getFrameNumber()) { + mLastFrameNumber = nv->getFrameStamp()->getFrameNumber(); + + // Don't use Camera::getViewMatrix, that one might be relative to another camera! + const osg::RefMatrix* viewMatrix = cv->getCurrentRenderStage()->getInitialViewMatrix(); + const std::vector& lights = mLightManager->getLightsInViewSpace(cv->getCurrentCamera(), viewMatrix); + // we do the intersections in view space osg::BoundingSphere nodeBound = node->getBound(); osg::Matrixf mat = *cv->getModelViewMatrix(); transformBoundingSphere(mat, nodeBound); - LightManager::LightList lightList; + mLightList.clear(); for (unsigned int i=0; i (8 - mLightManager->getStartLight()); - if (lightList.size() > maxLights) + osg::StateSet* stateset = NULL; + + if (mLightList.size() > maxLights) { // remove lights culled by this camera + LightManager::LightList lightList = mLightList; for (LightManager::LightList::iterator it = lightList.begin(); it != lightList.end() && lightList.size() > maxLights; ) { osg::CullStack::CullingStack& stack = cv->getModelViewCullingStack(); @@ -334,9 +337,11 @@ namespace SceneUtil while (lightList.size() > maxLights) lightList.pop_back(); } + stateset = mLightManager->getLightListStateSet(lightList); } + else + stateset = mLightManager->getLightListStateSet(mLightList); - osg::StateSet* stateset = mLightManager->getLightListStateSet(lightList); cv->pushStateSet(stateset); diff --git a/components/sceneutil/lightmanager.hpp b/components/sceneutil/lightmanager.hpp index c2a1779ed..b652562aa 100644 --- a/components/sceneutil/lightmanager.hpp +++ b/components/sceneutil/lightmanager.hpp @@ -113,14 +113,18 @@ namespace SceneUtil int mStartLight; }; + /// @note Not thread safe for CullThreadPerCamera threading mode. class LightListCallback : public osg::NodeCallback { public: LightListCallback() : mLightManager(NULL) + , mLastFrameNumber(0) {} LightListCallback(const LightListCallback& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) - : osg::Object(copy, copyop), osg::NodeCallback(copy, copyop), mLightManager(copy.mLightManager) + : osg::Object(copy, copyop), osg::NodeCallback(copy, copyop) + , mLightManager(copy.mLightManager) + , mLastFrameNumber(0) {} META_Object(NifOsg, LightListCallback) @@ -129,6 +133,8 @@ namespace SceneUtil private: LightManager* mLightManager; + unsigned int mLastFrameNumber; + LightManager::LightList mLightList; }; /// @brief Configures a light's attenuation according to vanilla Morrowind attenuation settings. diff --git a/components/sceneutil/statesetupdater.hpp b/components/sceneutil/statesetupdater.hpp index 37d08e025..34b8da848 100644 --- a/components/sceneutil/statesetupdater.hpp +++ b/components/sceneutil/statesetupdater.hpp @@ -15,6 +15,7 @@ namespace SceneUtil /// the first StateSet is the one we can write to, the second is the one currently in use by the draw traversal of the last frame. /// After a frame is completed the places are swapped. /// @par Must be set as UpdateCallback on a Node. + /// @note Do not add the same StateSetUpdater to multiple nodes. /// @note Do not add multiple StateSetControllers on the same Node as they will conflict - instead use the CompositeStateSetUpdater. class StateSetUpdater : public osg::NodeCallback { diff --git a/components/sceneutil/visitor.cpp b/components/sceneutil/visitor.cpp new file mode 100644 index 000000000..3738be08d --- /dev/null +++ b/components/sceneutil/visitor.cpp @@ -0,0 +1,32 @@ +#include "visitor.hpp" + +#include + +#include + +#include + +namespace SceneUtil +{ + + void FindByNameVisitor::apply(osg::Group &group) + { + if (Misc::StringUtils::ciEqual(group.getName(), mNameToFind)) + { + mFoundNode = &group; + return; + } + traverse(group); + } + + void DisableFreezeOnCullVisitor::apply(osg::Geode &geode) + { + for (unsigned int i=0; i(drw)) + partsys->setFreezeOnCull(false); + } + } + +} diff --git a/components/sceneutil/visitor.hpp b/components/sceneutil/visitor.hpp index b9342b884..656084940 100644 --- a/components/sceneutil/visitor.hpp +++ b/components/sceneutil/visitor.hpp @@ -3,12 +3,12 @@ #include -#include - // Commonly used scene graph visitors namespace SceneUtil { + // Find a Group by name, case-insensitive + // If not found, mFoundNode will be NULL class FindByNameVisitor : public osg::NodeVisitor { public: @@ -19,20 +19,24 @@ namespace SceneUtil { } - virtual void apply(osg::Group& group) - { - if (Misc::StringUtils::ciEqual(group.getName(), mNameToFind)) - { - mFoundNode = &group; - return; - } - traverse(group); - } + virtual void apply(osg::Group& group); std::string mNameToFind; osg::Group* mFoundNode; }; + // Disable freezeOnCull for all visited particlesystems + class DisableFreezeOnCullVisitor : public osg::NodeVisitor + { + public: + DisableFreezeOnCullVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + { + } + + virtual void apply(osg::Geode &geode); + }; + } #endif