Merge remote-tracking branch 'origin/master'

openmw-37
Marc Zinnschlag 9 years ago
commit e081eb2c34

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

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

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

@ -196,7 +196,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener
bool updateWeaponState();
bool updateCreatureState();
void updateIdleStormState();
void updateIdleStormState(bool inwater);
void updateHeadTracking(float duration);

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

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

@ -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<ESM::GameSetting>()
.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);

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

@ -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<geode.getNumDrawables(); ++i)
{
osg::Drawable* drw = geode.getDrawable(i);
if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(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();

@ -32,6 +32,7 @@
#include <components/esm/loadcell.hpp>
#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<SceneUtil::LightManager> 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

@ -35,6 +35,7 @@
#include <components/sceneutil/util.hpp>
#include <components/sceneutil/statesetupdater.hpp>
#include <components/sceneutil/controller.hpp>
#include <components/sceneutil/visitor.hpp>
#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<osgUtil::CullVisitor*>(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<CameraRelativeTransform> 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<osg::Node> 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<osg::Depth> 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> 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<AlphaFader> getAlphaFader()
std::vector<osg::ref_ptr<AlphaFader> > getAlphaFaders()
{
return mAlphaFader;
return mAlphaFaders;
}
private:
osg::ref_ptr<AlphaFader> mAlphaFader;
std::vector<osg::ref_ptr<AlphaFader> > 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<osg::ref_ptr<AlphaFader> >::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);
}
}

@ -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<osg::Group> mRootNode;
osg::ref_ptr<osg::Group> mEarlyRenderBinRoot;
osg::ref_ptr<osg::PositionAttitudeTransform> mParticleNode;
osg::ref_ptr<osg::Node> mParticleEffect;
osg::ref_ptr<AlphaFader> mParticleFader;
std::vector<osg::ref_ptr<AlphaFader> > mParticleFaders;
osg::ref_ptr<UnderwaterSwitchCallback> mUnderwaterSwitch;
osg::ref_ptr<osg::PositionAttitudeTransform> mCloudNode;

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

@ -33,6 +33,9 @@
#include <components/esm/loadcell.hpp>
#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<osg::Vec3Array> 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<osg::Geode> 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<osg::StateSet> stateset (new osg::StateSet);
osg::ref_ptr<osg::Material> 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<osg::ref_ptr<osg::Texture2D> > 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; i<frameCount; ++i)
{
std::ostringstream texname;
texname << "textures/water/water" << std::setw(2) << std::setfill('0') << i << ".dds";
texname << "textures/water/" << texture << std::setw(2) << std::setfill('0') << i << ".dds";
textures.push_back(mResourceSystem->getTextureManager()->getTexture2D(texname.str(), osg::Texture::REPEAT, osg::Texture::REPEAT));
}
osg::ref_ptr<NifOsg::FlipController> controller (new NifOsg::FlipController(0, 2/32.f, textures));
float fps = mFallback->getFallbackFloat("Water_SurfaceFPS");
osg::ref_ptr<NifOsg::FlipController> controller (new NifOsg::FlipController(0, 1.f/fps, textures));
controller->setSource(boost::shared_ptr<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource));
node->setUpdateCallback(controller);
node->setStateSet(stateset);

@ -1,12 +1,13 @@
#ifndef OPENMW_MWRENDER_WATER_H
#define OPENMW_MWRENDER_WATER_H
#include <memory>
#include <osg/ref_ptr>
#include <osg/Vec3f>
#include <components/settings/settings.hpp>
#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<osg::PositionAttitudeTransform> mWaterNode;
osg::ref_ptr<osg::Geode> mWaterGeode;
Resource::ResourceSystem* mResourceSystem;
const MWWorld::Fallback* mFallback;
osg::ref_ptr<osgUtil::IncrementalCompileOperation> mIncrementalCompileOperation;
std::auto_ptr<RippleSimulation> 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)

@ -37,7 +37,7 @@ namespace
void addToLevList(ESM::LevelledListBase* list, const std::string& itemId, int level)
{
for (std::vector<ESM::LevelledListBase::LevelItem>::iterator it = list->mList.begin(); it != list->mList.end();)
for (std::vector<ESM::LevelledListBase::LevelItem>::iterator it = list->mList.begin(); it != list->mList.end(); ++it)
{
if (it->mLevel == level && itemId == it->mId)
return;

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

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

@ -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<LightManager::LightSourceViewBound>& 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<LightManager::LightSourceViewBound>& 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<lights.size(); ++i)
{
const LightManager::LightSourceViewBound& l = lights[i];
if (l.mViewBound.intersects(nodeBound))
lightList.push_back(&l);
}
if (lightList.empty())
{
traverse(node, nv);
return;
mLightList.push_back(&l);
}
}
if (mLightList.size())
{
unsigned int maxLights = static_cast<unsigned int> (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);

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

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

@ -0,0 +1,32 @@
#include "visitor.hpp"
#include <osg/Geode>
#include <osgParticle/ParticleSystem>
#include <components/misc/stringops.hpp>
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<geode.getNumDrawables(); ++i)
{
osg::Drawable* drw = geode.getDrawable(i);
if (osgParticle::ParticleSystem* partsys = dynamic_cast<osgParticle::ParticleSystem*>(drw))
partsys->setFreezeOnCull(false);
}
}
}

@ -3,12 +3,12 @@
#include <osg/NodeVisitor>
#include <components/misc/stringops.hpp>
// 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

Loading…
Cancel
Save