Merge remote-tracking branch 'scrawl/master'

deque
Marc Zinnschlag 11 years ago
commit 85965bfd47

@ -228,6 +228,7 @@ namespace MWBase
virtual void showCrosshair(bool show) = 0; virtual void showCrosshair(bool show) = 0;
virtual bool getSubtitlesEnabled() = 0; virtual bool getSubtitlesEnabled() = 0;
virtual void toggleHud() = 0; virtual void toggleHud() = 0;
virtual bool toggleGui() = 0;
virtual void disallowMouse() = 0; virtual void disallowMouse() = 0;
virtual void allowMouse() = 0; virtual void allowMouse() = 0;

@ -521,6 +521,12 @@ namespace MWBase
const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0; const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0;
virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0; virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0;
/// @see MWWorld::WeatherManager::isInStorm
virtual bool isInStorm() const = 0;
/// @see MWWorld::WeatherManager::getStormDirection
virtual Ogre::Vector3 getStormDirection() const = 0;
}; };
} }

@ -537,8 +537,8 @@ namespace MWClass
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
float runSpeed = walkSpeed*6; // The Run speed difference for creatures comes from the animation speed difference (see runStateToWalkState in character.cpp)
runSpeed = std::min(gmst.fMaxWalkSpeedCreature->getFloat(), runSpeed); // flame atronach runs way too fast without this float runSpeed = walkSpeed;
float moveSpeed; float moveSpeed;
if(normalizedEncumbrance >= 1.0f) if(normalizedEncumbrance >= 1.0f)

@ -431,6 +431,9 @@ namespace MWGui
{ {
mGlobalMapRender = new MWRender::GlobalMap(""); mGlobalMapRender = new MWRender::GlobalMap("");
mGlobalMapRender->render(loadingListener); mGlobalMapRender->render(loadingListener);
mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
mGlobalMapImage->setImageTexture("GlobalMap.png"); mGlobalMapImage->setImageTexture("GlobalMap.png");
mGlobalMapOverlay->setImageTexture("GlobalMapOverlay"); mGlobalMapOverlay->setImageTexture("GlobalMapOverlay");
} }
@ -512,7 +515,6 @@ namespace MWGui
else else
mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff ); mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff );
mLastDragPos = MyGUI::IntPoint(_left, _top); mLastDragPos = MyGUI::IntPoint(_left, _top);
} }
@ -536,9 +538,6 @@ namespace MWGui
void MapWindow::open() void MapWindow::open()
{ {
mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
// force markers to foreground // force markers to foreground
for (unsigned int i=0; i<mGlobalMapOverlay->getChildCount (); ++i) for (unsigned int i=0; i<mGlobalMapOverlay->getChildCount (); ++i)
{ {

@ -118,6 +118,7 @@ namespace MWGui
, mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD"))
, mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI"))
, mHudEnabled(true) , mHudEnabled(true)
, mGuiEnabled(true)
, mCursorVisible(true) , mCursorVisible(true)
, mPlayerName() , mPlayerName()
, mPlayerRaceId() , mPlayerRaceId()
@ -420,7 +421,7 @@ namespace MWGui
mRecharge->setVisible(false); mRecharge->setVisible(false);
mVideoBackground->setVisible(false); mVideoBackground->setVisible(false);
mHud->setVisible(mHudEnabled); mHud->setVisible(mHudEnabled && mGuiEnabled);
bool gameMode = !isGuiMode(); bool gameMode = !isGuiMode();
@ -430,6 +431,13 @@ namespace MWGui
if (gameMode) if (gameMode)
setKeyFocusWidget (NULL); setKeyFocusWidget (NULL);
if (!mGuiEnabled)
{
if (containsMode(GM_Console))
mConsole->setVisible(true);
return;
}
// Icons of forced hidden windows are displayed // Icons of forced hidden windows are displayed
setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map))); setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map)));
setWeaponVisibility((mAllowed & GW_Inventory) && (!mInventoryWindow->pinned() || (mForceHidden & GW_Inventory))); setWeaponVisibility((mAllowed & GW_Inventory) && (!mInventoryWindow->pinned() || (mForceHidden & GW_Inventory)));
@ -1345,6 +1353,13 @@ namespace MWGui
mHud->setVisible (mHudEnabled); mHud->setVisible (mHudEnabled);
} }
bool WindowManager::toggleGui()
{
mGuiEnabled = !mGuiEnabled;
updateVisible();
return mGuiEnabled;
}
bool WindowManager::getRestEnabled() bool WindowManager::getRestEnabled()
{ {
//Enable rest dialogue if character creation finished //Enable rest dialogue if character creation finished

@ -223,6 +223,9 @@ namespace MWGui
virtual bool getSubtitlesEnabled(); virtual bool getSubtitlesEnabled();
virtual void toggleHud(); virtual void toggleHud();
/// Turn visibility of *all* GUI elements on or off (HUD and all windows, except the console)
virtual bool toggleGui();
virtual void disallowMouse(); virtual void disallowMouse();
virtual void allowMouse(); virtual void allowMouse();
virtual void notifyInputActionBound(); virtual void notifyInputActionBound();
@ -381,6 +384,7 @@ namespace MWGui
bool mCrosshairEnabled; bool mCrosshairEnabled;
bool mSubtitlesEnabled; bool mSubtitlesEnabled;
bool mHudEnabled; bool mHudEnabled;
bool mGuiEnabled;
bool mCursorVisible; bool mCursorVisible;
void setCursorVisible(bool visible); void setCursorVisible(bool visible);

@ -277,7 +277,7 @@ namespace MWInput
showQuickKeysMenu(); showQuickKeysMenu();
break; break;
case A_ToggleHUD: case A_ToggleHUD:
MWBase::Environment::get().getWindowManager()->toggleHud(); MWBase::Environment::get().getWindowManager()->toggleGui();
break; break;
case A_QuickSave: case A_QuickSave:
quickSave(); quickSave();

@ -530,8 +530,12 @@ namespace MWMechanics
itemGmst)->getString(); itemGmst)->getString();
if (it->first == ESM::MagicEffect::BoundGloves) if (it->first == ESM::MagicEffect::BoundGloves)
{ {
adjustBoundItem("sMagicBoundLeftGauntletID", magnitude > 0, ptr); item = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
adjustBoundItem("sMagicBoundRightGauntletID", magnitude > 0, ptr); "sMagicBoundLeftGauntletID")->getString();
adjustBoundItem(item, magnitude > 0, ptr);
item = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"sMagicBoundRightGauntletID")->getString();
adjustBoundItem(item, magnitude > 0, ptr);
} }
else else
adjustBoundItem(item, magnitude > 0, ptr); adjustBoundItem(item, magnitude > 0, ptr);

@ -20,6 +20,7 @@
#include "character.hpp" #include "character.hpp"
#include <OgreStringConverter.h> #include <OgreStringConverter.h>
#include <OgreSceneNode.h>
#include "movement.hpp" #include "movement.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
@ -54,6 +55,43 @@ std::string getBestAttack (const ESM::Weapon* weapon)
return "thrust"; return "thrust";
} }
// Converts a movement Run state to its equivalent Walk state.
MWMechanics::CharacterState runStateToWalkState (MWMechanics::CharacterState state)
{
using namespace MWMechanics;
CharacterState ret = state;
switch (state)
{
case CharState_RunForward:
ret = CharState_WalkForward;
break;
case CharState_RunBack:
ret = CharState_WalkBack;
break;
case CharState_RunLeft:
ret = CharState_WalkLeft;
break;
case CharState_RunRight:
ret = CharState_WalkRight;
break;
case CharState_SwimRunForward:
ret = CharState_SwimWalkForward;
break;
case CharState_SwimRunBack:
ret = CharState_SwimWalkBack;
break;
case CharState_SwimRunLeft:
ret = CharState_SwimWalkLeft;
break;
case CharState_SwimRunRight:
ret = CharState_SwimWalkRight;
break;
default:
break;
}
return ret;
}
} }
namespace MWMechanics namespace MWMechanics
@ -234,6 +272,8 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
1.0f, "start", "stop", 0.0f, ~0ul); 1.0f, "start", "stop", 0.0f, ~0ul);
} }
updateIdleStormState();
if(force && mJumpState != JumpState_None) if(force && mJumpState != JumpState_None)
{ {
std::string jump; std::string jump;
@ -320,7 +360,18 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run); bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run);
if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) // For non-flying creatures, MW uses the Walk animation to calculate the animation velocity
// even if we are running. This must be replicated, otherwise the observed speed would differ drastically.
std::string anim = mCurrentMovement;
if (mPtr.getClass().getTypeName() == typeid(ESM::Creature).name()
&& !(mPtr.get<ESM::Creature>()->mBase->mFlags & ESM::Creature::Flies))
{
CharacterState walkState = runStateToWalkState(mMovementState);
const StateInfo *stateinfo = std::find_if(sMovementList, sMovementListEnd, FindCharState(walkState));
anim = stateinfo->groupname;
}
if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(anim)) > 1.0f)
{ {
mMovementAnimVelocity = vel; mMovementAnimVelocity = vel;
speedmult = mMovementSpeed / vel; speedmult = mMovementSpeed / vel;
@ -550,6 +601,45 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
mPtr = ptr; mPtr = ptr;
} }
void CharacterController::updateIdleStormState()
{
bool inStormDirection = false;
if (MWBase::Environment::get().getWorld()->isInStorm())
{
Ogre::Vector3 stormDirection = MWBase::Environment::get().getWorld()->getStormDirection();
Ogre::Vector3 characterDirection = mPtr.getRefData().getBaseNode()->getOrientation().yAxis();
inStormDirection = stormDirection.angleBetween(characterDirection) < Ogre::Degree(40);
}
if (inStormDirection && mUpperBodyState == UpperCharState_Nothing && mAnimation->hasAnimation("idlestorm"))
{
float complete = 0;
mAnimation->getInfo("idlestorm", &complete);
if (complete == 0)
mAnimation->play("idlestorm", Priority_Torch, MWRender::Animation::Group_RightArm, false,
1.0f, "start", "loop start", 0.0f, 0);
else if (complete == 1)
mAnimation->play("idlestorm", Priority_Torch, MWRender::Animation::Group_RightArm, false,
1.0f, "loop start", "loop stop", 0.0f, ~0ul);
}
else
{
if (mUpperBodyState == UpperCharState_Nothing)
{
if (mAnimation->isPlaying("idlestorm"))
{
if (mAnimation->getCurrentTime("idlestorm") < mAnimation->getTextKeyTime("idlestorm: loop stop"))
{
mAnimation->play("idlestorm", Priority_Torch, MWRender::Animation::Group_RightArm, true,
1.0f, "loop stop", "stop", 0.0f, 0);
}
}
}
else
mAnimation->disable("idlestorm");
}
}
bool CharacterController::updateCreatureState() bool CharacterController::updateCreatureState()
{ {
const MWWorld::Class &cls = mPtr.getClass(); const MWWorld::Class &cls = mPtr.getClass();

@ -178,6 +178,7 @@ class CharacterController
bool updateWeaponState(); bool updateWeaponState();
bool updateCreatureState(); bool updateCreatureState();
void updateIdleStormState();
void updateVisibility(); void updateVisibility();

@ -501,7 +501,8 @@ namespace MWMechanics
{ {
if (!playerStats.getExpelled(npcFaction)) if (!playerStats.getExpelled(npcFaction))
{ {
reaction = playerStats.getFactionReputation(npcFaction); // faction reaction towards itself. yes, that exists
reaction = MWBase::Environment::get().getDialogueManager()->getFactionReaction(npcFaction, npcFaction);
rank = playerStats.getFactionRanks().find(npcFaction)->second; rank = playerStats.getFactionRanks().find(npcFaction)->second;
} }
@ -1101,7 +1102,7 @@ namespace MWMechanics
if (iter->first.getClass().isClass(iter->first, "Guard")) if (iter->first.getClass().isClass(iter->first, "Guard"))
{ {
MWMechanics::AiSequence& aiSeq = iter->first.getClass().getCreatureStats(iter->first).getAiSequence(); MWMechanics::AiSequence& aiSeq = iter->first.getClass().getCreatureStats(iter->first).getAiSequence();
if (aiSeq.getActivePackage()->getTypeId() == MWMechanics::AiPackage::TypeIdPursue) if (aiSeq.getTypeId() == MWMechanics::AiPackage::TypeIdPursue)
{ {
aiSeq.stopPursuit(); aiSeq.stopPursuit();
aiSeq.stack(MWMechanics::AiCombat(target), ptr); aiSeq.stack(MWMechanics::AiCombat(target), ptr);
@ -1112,7 +1113,7 @@ namespace MWMechanics
} }
// Must be done after the target is set up, so that CreatureTargetted dialogue filter works properly // Must be done after the target is set up, so that CreatureTargetted dialogue filter works properly
if (ptr.getClass().isNpc()) if (ptr.getClass().isNpc() && !ptr.getClass().getCreatureStats(ptr).isDead())
MWBase::Environment::get().getDialogueManager()->say(ptr, "attack"); MWBase::Environment::get().getDialogueManager()->say(ptr, "attack");
} }

@ -1027,7 +1027,11 @@ Ogre::Vector3 Animation::runAnimation(float duration)
if(!state.mPlaying && state.mAutoDisable) if(!state.mPlaying && state.mAutoDisable)
{ {
if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName())
mAccumRoot->setPosition(0.f,0.f,0.f);
mStates.erase(stateiter++); mStates.erase(stateiter++);
resetActiveGroups(); resetActiveGroups();
} }
else else
@ -1169,20 +1173,68 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
params.mObjects->mControllers[i].setSource(Ogre::SharedPtr<EffectAnimationTime> (new EffectAnimationTime())); params.mObjects->mControllers[i].setSource(Ogre::SharedPtr<EffectAnimationTime> (new EffectAnimationTime()));
} }
if (!texture.empty())
// Do some manual adjustments on the created entities/particle systems
// It looks like vanilla MW totally ignores lighting settings for effects attached to characters.
// If we don't do this, some effects will look way too dark depending on the environment
// (e.g. magic_cast_dst.nif). They were clearly meant to use emissive lighting.
// We used to have this hack in the NIF material loader, but for effects not attached to characters
// (e.g. ash storms) the lighting settings do seem to be in use. Is there maybe a flag we have missed?
Ogre::ColourValue ambient = Ogre::ColourValue(0.f, 0.f, 0.f);
Ogre::ColourValue diffuse = Ogre::ColourValue(0.f, 0.f, 0.f);
Ogre::ColourValue specular = Ogre::ColourValue(0.f, 0.f, 0.f);
Ogre::ColourValue emissive = Ogre::ColourValue(1.f, 1.f, 1.f);
for(size_t i = 0;i < params.mObjects->mParticles.size(); ++i)
{ {
for(size_t i = 0;i < params.mObjects->mParticles.size(); ++i) Ogre::ParticleSystem* partSys = params.mObjects->mParticles[i];
Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(partSys);
for (int t=0; t<mat->getNumTechniques(); ++t)
{ {
Ogre::ParticleSystem* partSys = params.mObjects->mParticles[i]; Ogre::Technique* tech = mat->getTechnique(t);
for (int p=0; p<tech->getNumPasses(); ++p)
{
Ogre::Pass* pass = tech->getPass(p);
pass->setAmbient(ambient);
pass->setDiffuse(diffuse);
pass->setSpecular(specular);
pass->setEmissive(emissive);
Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(partSys); if (!texture.empty())
{
for (int tex=0; tex<pass->getNumTextureUnitStates(); ++tex)
{
Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex);
tus->setTextureName("textures\\" + texture);
}
}
}
}
}
for(size_t i = 0;i < params.mObjects->mEntities.size(); ++i)
{
Ogre::Entity* ent = params.mObjects->mEntities[i];
if (ent == params.mObjects->mSkelBase)
continue;
Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(ent);
for (int t=0; t<mat->getNumTechniques(); ++t) for (int t=0; t<mat->getNumTechniques(); ++t)
{
Ogre::Technique* tech = mat->getTechnique(t);
for (int p=0; p<tech->getNumPasses(); ++p)
{ {
Ogre::Technique* tech = mat->getTechnique(t); Ogre::Pass* pass = tech->getPass(p);
for (int p=0; p<tech->getNumPasses(); ++p)
pass->setAmbient(ambient);
pass->setDiffuse(diffuse);
pass->setSpecular(specular);
pass->setEmissive(emissive);
if (!texture.empty())
{ {
Ogre::Pass* pass = tech->getPass(p);
for (int tex=0; tex<pass->getNumTextureUnitStates(); ++tex) for (int tex=0; tex<pass->getNumTextureUnitStates(); ++tex)
{ {
Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex); Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex);

@ -62,6 +62,12 @@ namespace MWRender
loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1)); loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1));
loadingListener->setProgress(0); loadingListener->setProgress(0);
const Ogre::ColourValue waterShallowColour(0.15, 0.2, 0.19);
const Ogre::ColourValue waterDeepColour(0.1, 0.14, 0.13);
const Ogre::ColourValue groundColour(0.254, 0.19, 0.13);
const Ogre::ColourValue mountainColour(0.05, 0.05, 0.05);
const Ogre::ColourValue hillColour(0.16, 0.12, 0.08);
//if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png")) //if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png"))
if (1) if (1)
{ {
@ -91,12 +97,6 @@ namespace MWRender
int texelX = (x-mMinX) * cellSize + cellX; int texelX = (x-mMinX) * cellSize + cellX;
int texelY = (mHeight-1) - ((y-mMinY) * cellSize + cellY); int texelY = (mHeight-1) - ((y-mMinY) * cellSize + cellY);
Ogre::ColourValue waterShallowColour(0.15, 0.2, 0.19);
Ogre::ColourValue waterDeepColour(0.1, 0.14, 0.13);
Ogre::ColourValue groundColour(0.254, 0.19, 0.13);
Ogre::ColourValue mountainColour(0.05, 0.05, 0.05);
Ogre::ColourValue hillColour(0.16, 0.12, 0.08);
unsigned char r,g,b; unsigned char r,g,b;
if (land) if (land)

@ -506,10 +506,19 @@ void RenderingManager::configureFog(const float density, const Ogre::ColourValue
mFogColour = colour; mFogColour = colour;
float max = Settings::Manager::getFloat("max viewing distance", "Viewing distance"); float max = Settings::Manager::getFloat("max viewing distance", "Viewing distance");
mFogStart = max / (density) * Settings::Manager::getFloat("fog start factor", "Viewing distance"); if (density == 0)
mFogEnd = max / (density) * Settings::Manager::getFloat("fog end factor", "Viewing distance"); {
mFogStart = 0;
mFogEnd = std::numeric_limits<float>().max();
mRendering.getCamera()->setFarClipDistance (max);
}
else
{
mFogStart = max / (density) * Settings::Manager::getFloat("fog start factor", "Viewing distance");
mFogEnd = max / (density) * Settings::Manager::getFloat("fog end factor", "Viewing distance");
mRendering.getCamera()->setFarClipDistance (max / density);
}
mRendering.getCamera()->setFarClipDistance ( Settings::Manager::getFloat("max viewing distance", "Viewing distance") / density );
} }
void RenderingManager::applyFog (bool underwater) void RenderingManager::applyFog (bool underwater)

@ -8,10 +8,11 @@
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
#include <OgreHardwareVertexBuffer.h> #include <OgreHardwareVertexBuffer.h>
#include <OgreHighLevelGpuProgramManager.h> #include <OgreHighLevelGpuProgramManager.h>
#include <OgreBillboardSet.h> #include <OgreParticleSystem.h>
#include <OgreEntity.h> #include <OgreEntity.h>
#include <OgreSubEntity.h> #include <OgreSubEntity.h>
#include <OgreTechnique.h> #include <OgreTechnique.h>
#include <OgreControllerManager.h>
#include <OgreMeshManager.h> #include <OgreMeshManager.h>
@ -234,6 +235,7 @@ SkyManager::SkyManager(Ogre::SceneNode *root, Ogre::Camera *pCamera)
, mCreated(false) , mCreated(false)
, mCloudAnimationTimer(0.f) , mCloudAnimationTimer(0.f)
, mMoonRed(false) , mMoonRed(false)
, mParticleNode(NULL)
{ {
mSceneMgr = root->getCreator(); mSceneMgr = root->getCreator();
mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
@ -372,6 +374,12 @@ void SkyManager::update(float duration)
if (!mEnabled) return; if (!mEnabled) return;
const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback(); const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback();
if (!mParticle.isNull())
{
for (unsigned int i=0; i<mParticle->mControllers.size(); ++i)
mParticle->mControllers[i].update();
}
// UV Scroll the clouds // UV Scroll the clouds
mCloudAnimationTimer += duration * mCloudSpeed; mCloudAnimationTimer += duration * mCloudSpeed;
sh::Factory::getInstance().setSharedParameter ("cloudAnimationTimer", sh::Factory::getInstance().setSharedParameter ("cloudAnimationTimer",
@ -441,6 +449,37 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
{ {
if (!mCreated) return; if (!mCreated) return;
if (mCurrentParticleEffect != weather.mParticleEffect)
{
mCurrentParticleEffect = weather.mParticleEffect;
if (mCurrentParticleEffect.empty())
{
mParticle.setNull();
}
else
{
if (!mParticleNode)
{
mParticleNode = mCamera->getParentSceneNode()->createChildSceneNode(Ogre::Vector3(0,0,100));
mParticleNode->setInheritOrientation(false);
}
mParticle = NifOgre::Loader::createObjects(mParticleNode, mCurrentParticleEffect);
for(size_t i = 0; i < mParticle->mParticles.size(); ++i)
{
ParticleSystem* particle = mParticle->mParticles[i];
particle->setRenderQueueGroup(RQG_Alpha);
particle->setVisibilityFlags(RV_Sky);
}
for (size_t i = 0; i < mParticle->mControllers.size(); ++i)
{
if (mParticle->mControllers[i].getSource().isNull())
mParticle->mControllers[i].setSource(Ogre::ControllerManager::getSingleton().getFrameTimeSource());
}
}
}
if (mClouds != weather.mCloudTexture) if (mClouds != weather.mCloudTexture)
{ {
sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", "textures\\"+weather.mCloudTexture); sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", "textures\\"+weather.mCloudTexture);

@ -200,6 +200,9 @@ namespace MWRender
std::vector<NifOgre::ObjectScenePtr> mObjects; std::vector<NifOgre::ObjectScenePtr> mObjects;
Ogre::SceneNode* mParticleNode;
NifOgre::ObjectScenePtr mParticle;
// remember some settings so we don't have to apply them again if they didnt change // remember some settings so we don't have to apply them again if they didnt change
Ogre::String mClouds; Ogre::String mClouds;
Ogre::String mNextClouds; Ogre::String mNextClouds;
@ -211,6 +214,8 @@ namespace MWRender
Ogre::ColourValue mSkyColour; Ogre::ColourValue mSkyColour;
Ogre::ColourValue mFogColour; Ogre::ColourValue mFogColour;
std::string mCurrentParticleEffect;
Ogre::Light* mLightning; Ogre::Light* mLightning;
float mRemainingTransitionTime; float mRemainingTransitionTime;

@ -150,9 +150,19 @@ void WeaponAnimation::pitchSkeleton(float xrot, Ogre::SkeletonInstance* skel)
return; return;
float pitch = xrot * mPitchFactor; float pitch = xrot * mPitchFactor;
Ogre::Node *node = skel->getBone("Bip01 Spine2"); Ogre::Node *node;
// In spherearcher.nif, we have spine, not Spine. Not sure if all bone names should be case insensitive?
if (skel->hasBone("Bip01 spine2"))
node = skel->getBone("Bip01 spine2");
else
node = skel->getBone("Bip01 Spine2");
node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD);
node = skel->getBone("Bip01 Spine1");
if (skel->hasBone("Bip01 spine1")) // in spherearcher.nif
node = skel->getBone("Bip01 spine1");
else
node = skel->getBone("Bip01 Spine1");
node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD);
} }

@ -477,6 +477,16 @@ namespace MWScript
} }
}; };
template <class R>
class OpFace : public Interpreter::Opcode0
{
public:
virtual void execute(Interpreter::Runtime& runtime)
{
/// \todo implement
}
};
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
{ {
interpreter.installSegment3 (Compiler::Ai::opcodeAIActivate, new OpAiActivate<ImplicitRef>); interpreter.installSegment3 (Compiler::Ai::opcodeAIActivate, new OpAiActivate<ImplicitRef>);
@ -538,6 +548,9 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Ai::opcodeGetFleeExplicit, new OpGetAiSetting<ExplicitRef>(2)); interpreter.installSegment5 (Compiler::Ai::opcodeGetFleeExplicit, new OpGetAiSetting<ExplicitRef>(2));
interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarm, new OpGetAiSetting<ImplicitRef>(3)); interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarm, new OpGetAiSetting<ImplicitRef>(3));
interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarmExplicit, new OpGetAiSetting<ExplicitRef>(3)); interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarmExplicit, new OpGetAiSetting<ExplicitRef>(3));
interpreter.installSegment5 (Compiler::Ai::opcodeFace, new OpFace<ImplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeFaceExplicit, new OpFace<ExplicitRef>);
} }
} }
} }

@ -227,9 +227,6 @@ namespace MWScript
std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger); std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
// ignore extra garbage argument
runtime.pop();
runtime.push(MWBase::Environment::get().getDialogueManager() runtime.push(MWBase::Environment::get().getDialogueManager()
->getFactionReaction(faction1, faction2)); ->getFactionReaction(faction1, faction2));
} }

@ -400,5 +400,8 @@ op 0x2000247: BetaComment
op 0x2000248: BetaComment, explicit op 0x2000248: BetaComment, explicit
op 0x2000249: OnMurder op 0x2000249: OnMurder
op 0x200024a: OnMurder, explicit op 0x200024a: OnMurder, explicit
op 0x200024b: ToggleMenus
op 0x200024c: Face
op 0x200024d: Face, explicit
opcodes 0x200024b-0x3ffffff unused opcodes 0x200024e-0x3ffffff unused

@ -203,6 +203,15 @@ namespace MWScript
} }
}; };
class OpToggleMenus : public Interpreter::Opcode0
{
public:
virtual void execute(Interpreter::Runtime &runtime)
{
bool state = MWBase::Environment::get().getWindowManager()->toggleGui();
runtime.getContext().report(state ? "GUI -> On" : "GUI -> Off");
}
};
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
{ {
@ -242,6 +251,7 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Gui::opcodeShowMap, new OpShowMap); interpreter.installSegment5 (Compiler::Gui::opcodeShowMap, new OpShowMap);
interpreter.installSegment5 (Compiler::Gui::opcodeFillMap, new OpFillMap); interpreter.installSegment5 (Compiler::Gui::opcodeFillMap, new OpFillMap);
interpreter.installSegment3 (Compiler::Gui::opcodeMenuTest, new OpMenuTest); interpreter.installSegment3 (Compiler::Gui::opcodeMenuTest, new OpMenuTest);
interpreter.installSegment5 (Compiler::Gui::opcodeToggleMenus, new OpToggleMenus);
} }
} }
} }

@ -64,12 +64,12 @@ namespace MWScript
Success = false; Success = false;
} }
if (!Success && mVerbose) if (!Success)
{ {
std::cerr std::cerr
<< "compiling failed: " << name << std::endl << "compiling failed: " << name << std::endl;
<< script->mScriptText if (mVerbose)
<< std::endl << std::endl; std::cerr << script->mScriptText << std::endl << std::endl;
} }
if (Success) if (Success)

@ -690,8 +690,11 @@ namespace MWScript
Interpreter::Type_Integer value = runtime[0].mInteger; Interpreter::Type_Integer value = runtime[0].mInteger;
runtime.pop(); runtime.pop();
ptr.getClass().getNpcStats (ptr).setBaseDisposition if (ptr.getClass().isNpc())
(ptr.getClass().getNpcStats (ptr).getBaseDisposition() + value); ptr.getClass().getNpcStats (ptr).setBaseDisposition
(ptr.getClass().getNpcStats (ptr).getBaseDisposition() + value);
// else: must not throw exception (used by an Almalexia dialogue script)
} }
}; };
@ -707,7 +710,8 @@ namespace MWScript
Interpreter::Type_Integer value = runtime[0].mInteger; Interpreter::Type_Integer value = runtime[0].mInteger;
runtime.pop(); runtime.pop();
ptr.getClass().getNpcStats (ptr).setBaseDisposition (value); if (ptr.getClass().isNpc())
ptr.getClass().getNpcStats (ptr).setBaseDisposition (value);
} }
}; };
@ -720,7 +724,10 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
runtime.push (MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr)); if (!ptr.getClass().isNpc())
runtime.push(0);
else
runtime.push (MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr));
} }
}; };

@ -323,7 +323,7 @@ namespace MWScript
} }
else else
{ {
throw std::runtime_error ("unknown cell"); throw std::runtime_error (std::string("unknown cell (") + cellID + ")");
} }
} }
}; };
@ -420,7 +420,7 @@ namespace MWScript
} }
else else
{ {
throw std::runtime_error ("unknown cell"); throw std::runtime_error ( std::string("unknown cell (") + cellID + ")");
} }
} }
}; };

@ -201,6 +201,13 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0
writer.setFormat (ESM::Header::CurrentFormat); writer.setFormat (ESM::Header::CurrentFormat);
// all unused
writer.setVersion(0);
writer.setType(0);
writer.setAuthor("");
writer.setDescription("");
int recordCount = 1 // saved game header int recordCount = 1 // saved game header
+MWBase::Environment::get().getJournal()->countSavedGameRecords() +MWBase::Environment::get().getJournal()->countSavedGameRecords()
+MWBase::Environment::get().getWorld()->countSavedGameRecords() +MWBase::Environment::get().getWorld()->countSavedGameRecords()

@ -12,6 +12,7 @@
#include <openengine/bullet/trace.h> #include <openengine/bullet/trace.h>
#include <openengine/bullet/physic.hpp> #include <openengine/bullet/physic.hpp>
#include <openengine/bullet/BtOgreExtras.h>
#include <openengine/ogre/renderer.hpp> #include <openengine/ogre/renderer.hpp>
#include <components/nifbullet/bulletnifloader.hpp> #include <components/nifbullet/bulletnifloader.hpp>
@ -26,10 +27,49 @@
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../apps/openmw/mwrender/animation.hpp"
#include "../apps/openmw/mwbase/world.hpp"
#include "../apps/openmw/mwbase/environment.hpp"
#include "ptr.hpp" #include "ptr.hpp"
#include "class.hpp" #include "class.hpp"
using namespace Ogre; using namespace Ogre;
namespace
{
void animateCollisionShapes (std::map<OEngine::Physic::RigidBody*, OEngine::Physic::AnimatedShapeInstance>& map)
{
for (std::map<OEngine::Physic::RigidBody*, OEngine::Physic::AnimatedShapeInstance>::iterator it = map.begin();
it != map.end(); ++it)
{
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaHandle(it->first->mName);
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr);
OEngine::Physic::AnimatedShapeInstance& instance = it->second;
std::map<std::string, int>& shapes = instance.mAnimatedShapes;
for (std::map<std::string, int>::iterator shapeIt = shapes.begin();
shapeIt != shapes.end(); ++shapeIt)
{
Ogre::Node* bone = animation->getNode(shapeIt->first);
btCompoundShape* compound = dynamic_cast<btCompoundShape*>(instance.mCompound);
btTransform trans;
trans.setOrigin(BtOgre::Convert::toBullet(bone->_getDerivedPosition()));
trans.setRotation(BtOgre::Convert::toBullet(bone->_getDerivedOrientation()));
compound->getChildShape(shapeIt->second)->setLocalScaling(BtOgre::Convert::toBullet(bone->_getDerivedScale()));
compound->updateChildTransform(shapeIt->second, trans);
}
}
}
}
namespace MWWorld namespace MWWorld
{ {
@ -175,7 +215,7 @@ namespace MWWorld
const int maxHeight = 200.f; const int maxHeight = 200.f;
OEngine::Physic::ActorTracer tracer; OEngine::Physic::ActorTracer tracer;
tracer.findGround(physicActor->getCollisionBody(), position, position-Ogre::Vector3(0,0,maxHeight), engine); tracer.findGround(physicActor, position, position-Ogre::Vector3(0,0,maxHeight), engine);
if(tracer.mFraction >= 1.0f) if(tracer.mFraction >= 1.0f)
{ {
physicActor->setOnGround(false); physicActor->setOnGround(false);
@ -564,11 +604,10 @@ namespace MWWorld
std::string mesh = ptr.getClass().getModel(ptr); std::string mesh = ptr.getClass().getModel(ptr);
Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); Ogre::SceneNode* node = ptr.getRefData().getBaseNode();
handleToMesh[node->getName()] = mesh; handleToMesh[node->getName()] = mesh;
OEngine::Physic::RigidBody* body = mEngine->createAndAdjustRigidBody( mEngine->createAndAdjustRigidBody(
mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, false, placeable); mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, false, placeable);
OEngine::Physic::RigidBody* raycastingBody = mEngine->createAndAdjustRigidBody( mEngine->createAndAdjustRigidBody(
mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, true, placeable); mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, true, placeable);
mEngine->addRigidBody(body, true, raycastingBody);
} }
void PhysicsSystem::addActor (const Ptr& ptr) void PhysicsSystem::addActor (const Ptr& ptr)
@ -607,9 +646,10 @@ namespace MWWorld
Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); Ogre::SceneNode* node = ptr.getRefData().getBaseNode();
const std::string &handle = node->getName(); const std::string &handle = node->getName();
const Ogre::Quaternion &rotation = node->getOrientation(); const Ogre::Quaternion &rotation = node->getOrientation();
// TODO: map to MWWorld::Ptr for faster access
if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle))
{ {
//Needs to be changed
act->setRotation(rotation); act->setRotation(rotation);
} }
if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle))
@ -740,8 +780,10 @@ namespace MWWorld
btCollisionObject object; btCollisionObject object;
object.setCollisionShape(&planeShape); object.setCollisionShape(&planeShape);
// TODO: this seems to have a slight performance impact
if (waterCollision) if (waterCollision)
mEngine->dynamicsWorld->addCollisionObject(&object); mEngine->mDynamicsWorld->addCollisionObject(&object,
0xff, OEngine::Physic::CollisionType_Actor);
// 100 points of slowfall reduce gravity by 90% (this is just a guess) // 100 points of slowfall reduce gravity by 90% (this is just a guess)
float slowFall = 1-std::min(std::max(0.f, (effects.get(ESM::MagicEffect::SlowFall).mMagnitude / 100.f) * 0.9f), 0.9f); float slowFall = 1-std::min(std::max(0.f, (effects.get(ESM::MagicEffect::SlowFall).mMagnitude / 100.f) * 0.9f), 0.9f);
@ -751,7 +793,7 @@ namespace MWWorld
waterlevel, slowFall, mEngine); waterlevel, slowFall, mEngine);
if (waterCollision) if (waterCollision)
mEngine->dynamicsWorld->removeCollisionObject(&object); mEngine->mDynamicsWorld->removeCollisionObject(&object);
float heightDiff = newpos.z - oldHeight; float heightDiff = newpos.z - oldHeight;
@ -767,4 +809,12 @@ namespace MWWorld
return mMovementResults; return mMovementResults;
} }
void PhysicsSystem::stepSimulation(float dt)
{
animateCollisionShapes(mEngine->mAnimatedShapes);
animateCollisionShapes(mEngine->mAnimatedRaycastingShapes);
mEngine->stepSimulation(dt);
}
} }

@ -53,6 +53,8 @@ namespace MWWorld
bool toggleCollisionMode(); bool toggleCollisionMode();
void stepSimulation(float dt);
std::vector<std::string> getCollisions(const MWWorld::Ptr &ptr); ///< get handles this object collides with std::vector<std::string> getCollisions(const MWWorld::Ptr &ptr); ///< get handles this object collides with
Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr); Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr);

@ -78,7 +78,7 @@ namespace MWWorld
state.mSourceName = sourceName; state.mSourceName = sourceName;
state.mId = model; state.mId = model;
state.mSpellId = spellId; state.mSpellId = spellId;
state.mCaster = caster; state.mCasterHandle = caster.getRefData().getHandle();
if (caster.getClass().isActor()) if (caster.getClass().isActor())
state.mActorId = caster.getClass().getCreatureStats(caster).getActorId(); state.mActorId = caster.getClass().getCreatureStats(caster).getActorId();
else else
@ -162,7 +162,7 @@ namespace MWWorld
{ {
MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second); MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second);
MWWorld::Ptr caster = it->mCaster; MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaHandle(it->mCasterHandle);
if (caster.isEmpty()) if (caster.isEmpty())
caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);

@ -67,12 +67,12 @@ namespace MWWorld
int mActorId; int mActorId;
// actorId doesn't work for non-actors, so we also keep track of the Ptr. // actorId doesn't work for non-actors, so we also keep track of the Ogre-handle.
// For non-actors, the caster ptr is mainly needed to prevent the projectile // For non-actors, the caster ptr is mainly needed to prevent the projectile
// from colliding with its caster. // from colliding with its caster.
// TODO: this will break when the game is saved and reloaded, since there is currently // TODO: this will break when the game is saved and reloaded, since there is currently
// no way to write identifiers for non-actors to a savegame. // no way to write identifiers for non-actors to a savegame.
MWWorld::Ptr mCaster; std::string mCasterHandle;
// MW-id of this projectile // MW-id of this projectile
std::string mId; std::string mId;

@ -54,7 +54,7 @@ namespace MWWorld
RefData(); RefData();
/// @param cellRef Used to copy constant data such as position into this class where it can /// @param cellRef Used to copy constant data such as position into this class where it can
/// be altered without effecting the original data. This makes it possible /// be altered without affecting the original data. This makes it possible
/// to reset the position as the orignal data is still held in the CellRef /// to reset the position as the orignal data is still held in the CellRef
RefData (const ESM::CellRef& cellRef); RefData (const ESM::CellRef& cellRef);

@ -58,6 +58,13 @@ void WeatherManager::setFallbackWeather(Weather& weather,const std::string& name
weather.mWindSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Wind_Speed"); weather.mWindSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Wind_Speed");
weather.mCloudSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Cloud_Speed"); weather.mCloudSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Cloud_Speed");
weather.mGlareView = mFallback->getFallbackFloat("Weather_"+upper+"_Glare_View"); weather.mGlareView = mFallback->getFallbackFloat("Weather_"+upper+"_Glare_View");
weather.mCloudTexture = mFallback->getFallbackString("Weather_"+upper+"_Cloud_Texture");
size_t offset = weather.mCloudTexture.find(".tga");
if (offset != std::string::npos)
weather.mCloudTexture.replace(offset, weather.mCloudTexture.length() - offset, ".dds");
weather.mIsStorm = (name == "ashstorm" || name == "blight");
mWeatherSettings[name] = weather; mWeatherSettings[name] = weather;
} }
@ -123,48 +130,42 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa
//Weather //Weather
Weather clear; Weather clear;
clear.mCloudTexture = "tx_sky_clear.dds";
setFallbackWeather(clear,"clear"); setFallbackWeather(clear,"clear");
Weather cloudy; Weather cloudy;
cloudy.mCloudTexture = "tx_sky_cloudy.dds";
setFallbackWeather(cloudy,"cloudy"); setFallbackWeather(cloudy,"cloudy");
Weather foggy; Weather foggy;
foggy.mCloudTexture = "tx_sky_foggy.dds";
setFallbackWeather(foggy,"foggy"); setFallbackWeather(foggy,"foggy");
Weather thunderstorm; Weather thunderstorm;
thunderstorm.mCloudTexture = "tx_sky_thunder.dds";
thunderstorm.mRainLoopSoundID = "rain heavy"; thunderstorm.mRainLoopSoundID = "rain heavy";
setFallbackWeather(thunderstorm,"thunderstorm"); setFallbackWeather(thunderstorm,"thunderstorm");
Weather rain; Weather rain;
rain.mCloudTexture = "tx_sky_rainy.dds";
rain.mRainLoopSoundID = "rain"; rain.mRainLoopSoundID = "rain";
setFallbackWeather(rain,"rain"); setFallbackWeather(rain,"rain");
Weather overcast; Weather overcast;
overcast.mCloudTexture = "tx_sky_overcast.dds";
setFallbackWeather(overcast,"overcast"); setFallbackWeather(overcast,"overcast");
Weather ashstorm; Weather ashstorm;
ashstorm.mCloudTexture = "tx_sky_ashstorm.dds";
ashstorm.mAmbientLoopSoundID = "ashstorm"; ashstorm.mAmbientLoopSoundID = "ashstorm";
ashstorm.mParticleEffect = "meshes\\ashcloud.nif";
setFallbackWeather(ashstorm,"ashstorm"); setFallbackWeather(ashstorm,"ashstorm");
Weather blight; Weather blight;
blight.mCloudTexture = "tx_sky_blight.dds";
blight.mAmbientLoopSoundID = "blight"; blight.mAmbientLoopSoundID = "blight";
blight.mParticleEffect = "meshes\\blightcloud.nif";
setFallbackWeather(blight,"blight"); setFallbackWeather(blight,"blight");
Weather snow; Weather snow;
snow.mCloudTexture = "tx_bm_sky_snow.dds"; snow.mParticleEffect = "meshes\\snow.nif";
setFallbackWeather(snow, "snow"); setFallbackWeather(snow, "snow");
Weather blizzard; Weather blizzard;
blizzard.mCloudTexture = "tx_bm_sky_blizzard.dds";
blizzard.mAmbientLoopSoundID = "BM Blizzard"; blizzard.mAmbientLoopSoundID = "BM Blizzard";
blizzard.mParticleEffect = "meshes\\blizzard.nif";
setFallbackWeather(blizzard,"blizzard"); setFallbackWeather(blizzard,"blizzard");
} }
@ -214,6 +215,10 @@ void WeatherManager::setResult(const String& weatherType)
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID; mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
mResult.mSunColor = current.mSunDiscSunsetColor; mResult.mSunColor = current.mSunDiscSunsetColor;
mResult.mIsStorm = current.mIsStorm;
mResult.mParticleEffect = current.mParticleEffect;
mResult.mNight = (mHour < mSunriseTime || mHour > mNightStart - 1); mResult.mNight = (mHour < mSunriseTime || mHour > mNightStart - 1);
mResult.mFogDepth = mResult.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth; mResult.mFogDepth = mResult.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth;
@ -316,6 +321,9 @@ void WeatherManager::transition(float factor)
mResult.mNightFade = lerp(current.mNightFade, other.mNightFade, factor); mResult.mNightFade = lerp(current.mNightFade, other.mNightFade, factor);
mResult.mNight = current.mNight; mResult.mNight = current.mNight;
mResult.mIsStorm = current.mIsStorm;
mResult.mParticleEffect = current.mParticleEffect;
} }
void WeatherManager::update(float duration) void WeatherManager::update(float duration)
@ -770,3 +778,13 @@ void WeatherManager::switchToNextWeather(bool instantly)
} }
} }
} }
bool WeatherManager::isInStorm() const
{
return mResult.mIsStorm;
}
Ogre::Vector3 WeatherManager::getStormDirection() const
{
return Ogre::Vector3(0,-1,0);
}

@ -57,7 +57,11 @@ namespace MWWorld
bool mNight; // use night skybox bool mNight; // use night skybox
float mNightFade; // fading factor for night skybox float mNightFade; // fading factor for night skybox
bool mIsStorm;
std::string mAmbientLoopSoundID; std::string mAmbientLoopSoundID;
std::string mParticleEffect;
}; };
@ -100,7 +104,7 @@ namespace MWWorld
// Duration of weather transition (in days) // Duration of weather transition (in days)
float mTransitionDelta; float mTransitionDelta;
// No idea what this one is used for? // Used by scripts to animate signs, etc based on the wind (GetWindSpeed)
float mWindSpeed; float mWindSpeed;
// Cloud animation speed multiplier // Cloud animation speed multiplier
@ -119,7 +123,16 @@ namespace MWWorld
// Rain sound effect // Rain sound effect
std::string mRainLoopSoundID; std::string mRainLoopSoundID;
/// \todo disease chance // Is this an ash storm / blight storm? This controls two things:
// - The particle node will be oriented so that the particles appear to come from the Red Mountain. (not implemented yet)
// - Characters will animate their hand to protect eyes from the storm when looking in its direction (idlestorm animation)
// Possible effect on movement speed?
bool mIsStorm;
std::string mParticleEffect;
// Note: For Weather Blight, there is a "Disease Chance" (=0.1) setting. But according to MWSFD this feature
// is broken in the vanilla game and was disabled.
}; };
/// ///
@ -151,6 +164,11 @@ namespace MWWorld
float getWindSpeed() const; float getWindSpeed() const;
/// Are we in an ash or blight storm?
bool isInStorm() const;
Ogre::Vector3 getStormDirection() const;
void advanceTime(double hours) void advanceTime(double hours)
{ {
mTimePassed += hours*3600; mTimePassed += hours*3600;

@ -967,7 +967,7 @@ namespace MWWorld
Ogre::Vector3 vec(x, y, z); Ogre::Vector3 vec(x, y, z);
CellStore *currCell = ptr.isInCell() ? ptr.getCell() : NULL; CellStore *currCell = ptr.isInCell() ? ptr.getCell() : NULL; // currCell == NULL should only happen for player, during initial startup
bool isPlayer = ptr == mPlayer->getPlayer(); bool isPlayer = ptr == mPlayer->getPlayer();
bool haveToMove = isPlayer || mWorldScene->isCellActive(*currCell); bool haveToMove = isPlayer || mWorldScene->isCellActive(*currCell);
@ -989,7 +989,9 @@ namespace MWWorld
} }
else else
{ {
if (!mWorldScene->isCellActive(*currCell) && mWorldScene->isCellActive(*newCell)) bool currCellActive = mWorldScene->isCellActive(*currCell);
bool newCellActive = mWorldScene->isCellActive(*newCell);
if (!currCellActive && newCellActive)
{ {
MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *newCell, pos); MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *newCell, pos);
mWorldScene->addObjectToScene(newPtr); mWorldScene->addObjectToScene(newPtr);
@ -1000,7 +1002,7 @@ namespace MWWorld
} }
addContainerScripts(newPtr, newCell); addContainerScripts(newPtr, newCell);
} }
else if (!mWorldScene->isCellActive(*newCell) && mWorldScene->isCellActive(*currCell)) else if (!newCellActive && currCellActive)
{ {
mWorldScene->removeObjectFromScene(ptr); mWorldScene->removeObjectFromScene(ptr);
mLocalScripts.remove(ptr); mLocalScripts.remove(ptr);
@ -1011,7 +1013,9 @@ namespace MWWorld
.copyToCell(ptr, *newCell); .copyToCell(ptr, *newCell);
newPtr.getRefData().setBaseNode(0); newPtr.getRefData().setBaseNode(0);
} }
else else if (!currCellActive && !newCellActive)
ptr.getClass().copyToCell(ptr, *newCell);
else // both cells active
{ {
MWWorld::Ptr copy = MWWorld::Ptr copy =
ptr.getClass().copyToCell(ptr, *newCell, pos); ptr.getClass().copyToCell(ptr, *newCell, pos);
@ -1231,7 +1235,7 @@ namespace MWWorld
if(player != results.end()) if(player != results.end())
moveObjectImp(player->first, player->second.x, player->second.y, player->second.z); moveObjectImp(player->first, player->second.x, player->second.y, player->second.z);
mPhysEngine->stepSimulation(duration); mPhysics->stepSimulation(duration);
} }
bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2)
@ -1471,8 +1475,13 @@ namespace MWWorld
std::vector < std::pair < float, std::string > >::iterator it = results.begin(); std::vector < std::pair < float, std::string > >::iterator it = results.begin();
while (it != results.end()) while (it != results.end())
{ {
if ( (*it).second.find("HeightField") != std::string::npos // not interested in terrain if ((*it).second.find("HeightField") != std::string::npos) // Don't attempt to getPtrViaHandle on terrain
|| getPtrViaHandle((*it).second) == mPlayer->getPlayer() ) // not interested in player (unless you want to talk to yourself) {
++it;
continue;
}
if (getPtrViaHandle((*it).second) == mPlayer->getPlayer() ) // not interested in player (unless you want to talk to yourself)
{ {
it = results.erase(it); it = results.erase(it);
} }
@ -1480,7 +1489,8 @@ namespace MWWorld
++it; ++it;
} }
if (results.empty()) if (results.empty()
|| results.front().second.find("HeightField") != std::string::npos) // Blocked by terrain
{ {
mFacedHandle = ""; mFacedHandle = "";
mFacedDistance = FLT_MAX; mFacedDistance = FLT_MAX;
@ -1847,7 +1857,7 @@ namespace MWWorld
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
OEngine::Physic::ActorTracer tracer; OEngine::Physic::ActorTracer tracer;
// a small distance above collision object is considered "on ground" // a small distance above collision object is considered "on ground"
tracer.findGround(physactor->getCollisionBody(), tracer.findGround(physactor,
pos, pos,
pos - Ogre::Vector3(0, 0, 1.5f), // trace a small amount down pos - Ogre::Vector3(0, 0, 1.5f), // trace a small amount down
mPhysEngine); mPhysEngine);
@ -1988,6 +1998,22 @@ namespace MWWorld
return 0.f; return 0.f;
} }
bool World::isInStorm() const
{
if (isCellExterior() || isCellQuasiExterior())
return mWeatherManager->isInStorm();
else
return false;
}
Ogre::Vector3 World::getStormDirection() const
{
if (isCellExterior() || isCellQuasiExterior())
return mWeatherManager->getStormDirection();
else
return Ogre::Vector3(0,1,0);
}
void World::getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out) void World::getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out)
{ {
const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells();
@ -2303,6 +2329,7 @@ namespace MWWorld
{ {
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
// TODO: this only works for the player
MWWorld::Ptr target = getFacedObject(); MWWorld::Ptr target = getFacedObject();
std::string selectedSpell = stats.getSpells().getSelectedSpell(); std::string selectedSpell = stats.getSpells().getSelectedSpell();

@ -591,6 +591,12 @@ namespace MWWorld
const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName); const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName);
virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor); virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor);
/// @see MWWorld::WeatherManager::isInStorm
virtual bool isInStorm() const;
/// @see MWWorld::WeatherManager::getStormDirection
virtual Ogre::Vector3 getStormDirection() const;
}; };
} }

@ -775,7 +775,7 @@ namespace Compiler
{ {
parser.reset(); parser.reset();
if (optional) if (optional || *iter == 'X')
parser.setOptional (true); parser.setOptional (true);
scanner.scan (parser); scanner.scan (parser);
@ -783,17 +783,20 @@ namespace Compiler
if (optional && parser.isEmpty()) if (optional && parser.isEmpty())
break; break;
std::vector<Interpreter::Type_Code> tmp; if (*iter != 'X')
{
std::vector<Interpreter::Type_Code> tmp;
char type = parser.append (tmp); char type = parser.append (tmp);
if (type!=*iter) if (type!=*iter)
Generator::convert (tmp, type, *iter); Generator::convert (tmp, type, *iter);
stack.push (tmp); stack.push (tmp);
if (optional) if (optional)
++optionalCount; ++optionalCount;
}
} }
} }

@ -20,7 +20,8 @@ namespace Compiler
l - Integer <BR> l - Integer <BR>
s - Short <BR> s - Short <BR>
S - String, case preserved <BR> S - String, case preserved <BR>
x - Optional, ignored argument x - Optional, ignored string argument
X - Optional, ignored integer argument
**/ **/
typedef std::string ScriptArgs; typedef std::string ScriptArgs;

@ -70,6 +70,7 @@ namespace Compiler
extensions.registerFunction ("getlineofsight", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit); extensions.registerFunction ("getlineofsight", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit);
extensions.registerFunction ("getlos", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit); extensions.registerFunction ("getlos", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit);
extensions.registerFunction("gettarget", 'l', "c", opcodeGetTarget, opcodeGetTargetExplicit); extensions.registerFunction("gettarget", 'l', "c", opcodeGetTarget, opcodeGetTargetExplicit);
extensions.registerInstruction("face", "llX", opcodeFace, opcodeFaceExplicit);
} }
} }
@ -179,7 +180,7 @@ namespace Compiler
extensions.registerFunction("samefaction", 'l', "", opcodeSameFaction, extensions.registerFunction("samefaction", 'l', "", opcodeSameFaction,
opcodeSameFactionExplicit); opcodeSameFactionExplicit);
extensions.registerInstruction("modfactionreaction", "ccl", opcodeModFactionReaction); extensions.registerInstruction("modfactionreaction", "ccl", opcodeModFactionReaction);
extensions.registerFunction("getfactionreaction", 'l', "ccl", opcodeGetFactionReaction); extensions.registerFunction("getfactionreaction", 'l', "ccX", opcodeGetFactionReaction);
extensions.registerInstruction("clearinfoactor", "", opcodeClearInfoActor, opcodeClearInfoActorExplicit); extensions.registerInstruction("clearinfoactor", "", opcodeClearInfoActor, opcodeClearInfoActorExplicit);
} }
} }
@ -216,6 +217,8 @@ namespace Compiler
extensions.registerInstruction ("showmap", "S", opcodeShowMap); extensions.registerInstruction ("showmap", "S", opcodeShowMap);
extensions.registerInstruction ("fillmap", "", opcodeFillMap); extensions.registerInstruction ("fillmap", "", opcodeFillMap);
extensions.registerInstruction ("menutest", "/l", opcodeMenuTest); extensions.registerInstruction ("menutest", "/l", opcodeMenuTest);
extensions.registerInstruction ("togglemenus", "", opcodeToggleMenus);
extensions.registerInstruction ("tm", "", opcodeToggleMenus);
} }
} }
@ -403,7 +406,7 @@ namespace Compiler
extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel); extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel);
extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel); extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel);
extensions.registerInstruction ("addspell", "cx", opcodeAddSpell, opcodeAddSpellExplicit); extensions.registerInstruction ("addspell", "cxX", opcodeAddSpell, opcodeAddSpellExplicit);
extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell,
opcodeRemoveSpellExplicit); opcodeRemoveSpellExplicit);
extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects,

@ -59,6 +59,8 @@ namespace Compiler
const int opcodeStartCombatExplicit = 0x200023b; const int opcodeStartCombatExplicit = 0x200023b;
const int opcodeStopCombat = 0x200023c; const int opcodeStopCombat = 0x200023c;
const int opcodeStopCombatExplicit = 0x200023d; const int opcodeStopCombatExplicit = 0x200023d;
const int opcodeFace = 0x200024c;
const int opcodeFaceExplicit = 0x200024d;
} }
namespace Animation namespace Animation
@ -178,6 +180,7 @@ namespace Compiler
const int opcodeShowMap = 0x20001a0; const int opcodeShowMap = 0x20001a0;
const int opcodeFillMap = 0x20001a1; const int opcodeFillMap = 0x20001a1;
const int opcodeMenuTest = 0x2002c; const int opcodeMenuTest = 0x2002c;
const int opcodeToggleMenus = 0x200024b;
} }
namespace Misc namespace Misc

@ -108,13 +108,13 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons
esm.writeHNT("NAM9", mGoldValue); esm.writeHNT("NAM9", mGoldValue);
} }
if (mTeleport && !inInventory) if (!inInventory && mTeleport)
{ {
esm.writeHNT("DODT", mDoorDest); esm.writeHNT("DODT", mDoorDest);
esm.writeHNOCString("DNAM", mDestCell); esm.writeHNOCString("DNAM", mDestCell);
} }
if (mLockLevel != 0 && !inInventory) { if (!inInventory && mLockLevel != 0) {
esm.writeHNT("FLTV", mLockLevel); esm.writeHNT("FLTV", mLockLevel);
} }
@ -127,13 +127,13 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons
if (mReferenceBlocked != -1) if (mReferenceBlocked != -1)
esm.writeHNT("UNAM", mReferenceBlocked); esm.writeHNT("UNAM", mReferenceBlocked);
if (mFltv != 0 && !inInventory) if (!inInventory && mFltv != 0)
esm.writeHNT("FLTV", mFltv); esm.writeHNT("FLTV", mFltv);
if (!inInventory) if (!inInventory)
esm.writeHNT("DATA", mPos, 24); esm.writeHNT("DATA", mPos, 24);
if (mNam0 != 0 && !inInventory) if (!inInventory && mNam0 != 0)
esm.writeHNT("NAM0", mNam0); esm.writeHNT("NAM0", mNam0);
} }
@ -158,6 +158,7 @@ void ESM::CellRef::blank()
mReferenceBlocked = 0; mReferenceBlocked = 0;
mFltv = 0; mFltv = 0;
mNam0 = 0; mNam0 = 0;
mTeleport = false;
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
{ {

@ -18,6 +18,11 @@ namespace ESM
mHeader.mData.version = ver; mHeader.mData.version = ver;
} }
void ESMWriter::setType(int type)
{
mHeader.mData.type = type;
}
void ESMWriter::setAuthor(const std::string& auth) void ESMWriter::setAuthor(const std::string& auth)
{ {
mHeader.mData.author.assign (auth); mHeader.mData.author.assign (auth);

@ -25,10 +25,15 @@ class ESMWriter
ESMWriter(); ESMWriter();
unsigned int getVersion() const; unsigned int getVersion() const;
// Set various header data (ESM::Header::Data). All of the below functions must be called before writing,
// otherwise this data will be left uninitialized.
void setVersion(unsigned int ver = 0x3fa66666); void setVersion(unsigned int ver = 0x3fa66666);
void setType(int type);
void setEncoder(ToUTF8::Utf8Encoder *encoding); void setEncoder(ToUTF8::Utf8Encoder *encoding);
void setAuthor(const std::string& author); void setAuthor(const std::string& author);
void setDescription(const std::string& desc); void setDescription(const std::string& desc);
// Set the record count for writing it in the file header // Set the record count for writing it in the file header
void setRecordCount (int count); void setRecordCount (int count);
// Counts how many records we have actually written. // Counts how many records we have actually written.

@ -12,7 +12,8 @@ namespace ESM
struct StatState struct StatState
{ {
T mBase; T mBase;
T mMod; T mMod; // Note: can either be the modifier, or the modified value.
// A bit inconsistent, but we can't fix this without breaking compatibility.
T mCurrent; T mCurrent;
T mDamage; T mDamage;
float mProgress; float mProgress;
@ -30,7 +31,9 @@ namespace ESM
void StatState<T>::load (ESMReader &esm) void StatState<T>::load (ESMReader &esm)
{ {
esm.getHNT (mBase, "STBA"); esm.getHNT (mBase, "STBA");
esm.getHNT (mMod, "STMO");
mMod = 0;
esm.getHNOT (mMod, "STMO");
mCurrent = 0; mCurrent = 0;
esm.getHNOT (mCurrent, "STCU"); esm.getHNOT (mCurrent, "STCU");
mDamage = 0; mDamage = 0;
@ -43,7 +46,9 @@ namespace ESM
void StatState<T>::save (ESMWriter &esm) const void StatState<T>::save (ESMWriter &esm) const
{ {
esm.writeHNT ("STBA", mBase); esm.writeHNT ("STBA", mBase);
esm.writeHNT ("STMO", mMod);
if (mMod != 0)
esm.writeHNT ("STMO", mMod);
if (mCurrent) if (mCurrent)
esm.writeHNT ("STCU", mCurrent); esm.writeHNT ("STCU", mCurrent);
@ -56,4 +61,4 @@ namespace ESM
} }
} }
#endif #endif

@ -89,7 +89,14 @@ public:
float lifetime; float lifetime;
float lifetimeRandom; float lifetimeRandom;
int emitFlags; // Bit 0: Emit Rate toggle bit (0 = auto adjust, 1 = use Emit Rate value) enum EmitFlags
{
NoAutoAdjust = 0x1 // If this flag is set, we use the emitRate value. Otherwise,
// we calculate an emit rate so that the maximum number of particles
// in the system (numParticles) is never exceeded.
};
int emitFlags;
Ogre::Vector3 offsetRandom; Ogre::Vector3 offsetRandom;
NodePtr emitter; NodePtr emitter;

@ -49,20 +49,6 @@ typedef unsigned char ubyte;
namespace NifBullet namespace NifBullet
{ {
struct TriangleMeshShape : public btBvhTriangleMeshShape
{
TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression)
: btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression)
{
}
virtual ~TriangleMeshShape()
{
delete getTriangleInfoMap();
delete m_meshInterface;
}
};
ManualBulletShapeLoader::~ManualBulletShapeLoader() ManualBulletShapeLoader::~ManualBulletShapeLoader()
{ {
} }
@ -81,9 +67,8 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource)
mBoundingBox = NULL; mBoundingBox = NULL;
mShape->mBoxTranslation = Ogre::Vector3(0,0,0); mShape->mBoxTranslation = Ogre::Vector3(0,0,0);
mShape->mBoxRotation = Ogre::Quaternion::IDENTITY; mShape->mBoxRotation = Ogre::Quaternion::IDENTITY;
mHasShape = false; mCompoundShape = NULL;
mStaticMesh = NULL;
btTriangleMesh* mesh1 = new btTriangleMesh();
// Load the NIF. TODO: Wrap this in a try-catch block once we're out // Load the NIF. TODO: Wrap this in a try-catch block once we're out
// of the early stages of development. Right now we WANT to catch // of the early stages of development. Right now we WANT to catch
@ -111,19 +96,35 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource)
mShape->mHasCollisionNode = hasRootCollisionNode(node); mShape->mHasCollisionNode = hasRootCollisionNode(node);
//do a first pass //do a first pass
handleNode(mesh1, node,0,false,false,false); handleNode(node,0,false,false,false);
if(mBoundingBox != NULL) if(mBoundingBox != NULL)
{ {
mShape->mCollisionShape = mBoundingBox; mShape->mCollisionShape = mBoundingBox;
delete mesh1; delete mStaticMesh;
if (mCompoundShape)
{
int n = mCompoundShape->getNumChildShapes();
for(int i=0; i <n;i++)
delete (mCompoundShape->getChildShape(i));
delete mCompoundShape;
}
} }
else if (mHasShape && mShape->mCollide) else
{ {
mShape->mCollisionShape = new TriangleMeshShape(mesh1,true); if (mCompoundShape)
{
mShape->mCollisionShape = mCompoundShape;
if (mStaticMesh)
{
btTransform trans;
trans.setIdentity();
mCompoundShape->addChildShape(trans, new TriangleMeshShape(mStaticMesh,true));
}
}
else if (mStaticMesh)
mShape->mCollisionShape = new TriangleMeshShape(mStaticMesh,true);
} }
else
delete mesh1;
//second pass which create a shape for raycasting. //second pass which create a shape for raycasting.
mResourceName = mShape->getName(); mResourceName = mShape->getName();
@ -131,23 +132,23 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource)
mBoundingBox = NULL; mBoundingBox = NULL;
mShape->mBoxTranslation = Ogre::Vector3(0,0,0); mShape->mBoxTranslation = Ogre::Vector3(0,0,0);
mShape->mBoxRotation = Ogre::Quaternion::IDENTITY; mShape->mBoxRotation = Ogre::Quaternion::IDENTITY;
mHasShape = false; mStaticMesh = NULL;
mCompoundShape = NULL;
btTriangleMesh* mesh2 = new btTriangleMesh();
handleNode(mesh2, node,0,true,true,false); handleNode(node,0,true,true,false);
if(mBoundingBox != NULL) if (mCompoundShape)
{
mShape->mRaycastingShape = mBoundingBox;
delete mesh2;
}
else if (mHasShape)
{ {
mShape->mRaycastingShape = new TriangleMeshShape(mesh2,true); mShape->mRaycastingShape = mCompoundShape;
if (mStaticMesh)
{
btTransform trans;
trans.setIdentity();
mCompoundShape->addChildShape(trans, new TriangleMeshShape(mStaticMesh,true));
}
} }
else else if (mStaticMesh)
delete mesh2; mShape->mRaycastingShape = new TriangleMeshShape(mStaticMesh,true);
} }
bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node) bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node)
@ -172,14 +173,17 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node)
return false; return false;
} }
void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node *node, int flags, void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags,
bool isCollisionNode, bool isCollisionNode,
bool raycasting, bool isMarker) bool raycasting, bool isMarker, bool isAnimated)
{ {
// Accumulate the flags from all the child nodes. This works for all // Accumulate the flags from all the child nodes. This works for all
// the flags we currently use, at least. // the flags we currently use, at least.
flags |= node->flags; flags |= node->flags;
if (!node->controller.empty() && node->controller->recType == Nif::RC_NiKeyframeController)
isAnimated = true;
if (!raycasting) if (!raycasting)
isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode); isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode);
else else
@ -227,10 +231,12 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node *
if ( (isCollisionNode || (!mShape->mHasCollisionNode && !raycasting)) if ( (isCollisionNode || (!mShape->mHasCollisionNode && !raycasting))
&& (!isMarker || (mShape->mHasCollisionNode && !raycasting))) && (!isMarker || (mShape->mHasCollisionNode && !raycasting)))
{ {
// NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape!
// It must be ignored completely.
// (occurs in tr_ex_imp_wall_arch_04.nif)
if(node->hasBounds) if(node->hasBounds)
{ {
// Checking for BBoxCollision flag causes issues with some actors :/ if (flags & Nif::NiNode::Flag_BBoxCollision && !raycasting)
if (!(node->flags & Nif::NiNode::Flag_Hidden))
{ {
mShape->mBoxTranslation = node->boundPos; mShape->mBoxTranslation = node->boundPos;
mShape->mBoxRotation = node->boundRot; mShape->mBoxRotation = node->boundRot;
@ -240,7 +246,7 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node *
else if(node->recType == Nif::RC_NiTriShape) else if(node->recType == Nif::RC_NiTriShape)
{ {
mShape->mCollide = !(flags&0x800); mShape->mCollide = !(flags&0x800);
handleNiTriShape(mesh, static_cast<const Nif::NiTriShape*>(node), flags, node->getWorldTransform(), raycasting); handleNiTriShape(static_cast<const Nif::NiTriShape*>(node), flags, node->getWorldTransform(), raycasting, isAnimated);
} }
} }
@ -252,13 +258,13 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node *
for(size_t i = 0;i < list.length();i++) for(size_t i = 0;i < list.length();i++)
{ {
if(!list[i].empty()) if(!list[i].empty())
handleNode(mesh, list[i].getPtr(), flags, isCollisionNode, raycasting, isMarker); handleNode(list[i].getPtr(), flags, isCollisionNode, raycasting, isMarker, isAnimated);
} }
} }
} }
void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform,
bool raycasting) bool raycasting, bool isAnimated)
{ {
assert(shape != NULL); assert(shape != NULL);
@ -281,17 +287,64 @@ void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif::
// bother setting it up. // bother setting it up.
return; return;
mHasShape = true; if (!shape->skin.empty())
isAnimated = false;
const Nif::NiTriShapeData *data = shape->data.getPtr(); if (isAnimated)
const std::vector<Ogre::Vector3> &vertices = data->vertices;
const short *triangles = &data->triangles[0];
for(size_t i = 0;i < data->triangles.size();i+=3)
{ {
Ogre::Vector3 b1 = transform*vertices[triangles[i+0]]; if (!mCompoundShape)
Ogre::Vector3 b2 = transform*vertices[triangles[i+1]]; mCompoundShape = new btCompoundShape();
Ogre::Vector3 b3 = transform*vertices[triangles[i+2]];
mesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z)); btTriangleMesh* childMesh = new btTriangleMesh();
const Nif::NiTriShapeData *data = shape->data.getPtr();
childMesh->preallocateVertices(data->vertices.size());
childMesh->preallocateIndices(data->triangles.size());
const std::vector<Ogre::Vector3> &vertices = data->vertices;
const std::vector<short> &triangles = data->triangles;
for(size_t i = 0;i < data->triangles.size();i+=3)
{
Ogre::Vector3 b1 = vertices[triangles[i+0]];
Ogre::Vector3 b2 = vertices[triangles[i+1]];
Ogre::Vector3 b3 = vertices[triangles[i+2]];
childMesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z));
}
TriangleMeshShape* childShape = new TriangleMeshShape(childMesh,true);
childShape->setLocalScaling(btVector3(transform[0][0], transform[1][1], transform[2][2]));
Ogre::Quaternion q = transform.extractQuaternion();
Ogre::Vector3 v = transform.getTrans();
btTransform trans(btQuaternion(q.x, q.y, q.z, q.w), btVector3(v.x, v.y, v.z));
if (raycasting)
mShape->mAnimatedRaycastingShapes.insert(std::make_pair(shape->name, mCompoundShape->getNumChildShapes()));
else
mShape->mAnimatedShapes.insert(std::make_pair(shape->name, mCompoundShape->getNumChildShapes()));
mCompoundShape->addChildShape(trans, childShape);
}
else
{
if (!mStaticMesh)
mStaticMesh = new btTriangleMesh();
// Static shape, just transform all vertices into position
const Nif::NiTriShapeData *data = shape->data.getPtr();
const std::vector<Ogre::Vector3> &vertices = data->vertices;
const std::vector<short> &triangles = data->triangles;
for(size_t i = 0;i < data->triangles.size();i+=3)
{
Ogre::Vector3 b1 = transform*vertices[triangles[i+0]];
Ogre::Vector3 b2 = transform*vertices[triangles[i+1]];
Ogre::Vector3 b3 = transform*vertices[triangles[i+2]];
mStaticMesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z));
}
} }
} }
@ -304,4 +357,53 @@ void ManualBulletShapeLoader::load(const std::string &name,const std::string &gr
OEngine::Physic::BulletShapeManager::getSingleton().create(name,group,true,this); OEngine::Physic::BulletShapeManager::getSingleton().create(name,group,true,this);
} }
bool findBoundingBox (const Nif::Node* node, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation)
{
if(node->hasBounds)
{
if (!(node->flags & Nif::NiNode::Flag_Hidden))
{
translation = node->boundPos;
orientation = node->boundRot;
halfExtents = node->boundXYZ;
return true;
}
}
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
if(ninode)
{
const Nif::NodeList &list = ninode->children;
for(size_t i = 0;i < list.length();i++)
{
if(!list[i].empty())
if (findBoundingBox(list[i].getPtr(), halfExtents, translation, orientation))
return true;
}
}
return false;
}
bool getBoundingBox(const std::string& nifFile, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation)
{
Nif::NIFFile::ptr pnif (Nif::NIFFile::create (nifFile));
Nif::NIFFile & nif = *pnif.get ();
if (nif.numRoots() < 1)
{
return false;
}
Nif::Record *r = nif.getRoot(0);
assert(r != NULL);
Nif::Node *node = dynamic_cast<Nif::Node*>(r);
if (node == NULL)
{
return false;
}
return findBoundingBox(node, halfExtents, translation, orientation);
}
} // namespace NifBullet } // namespace NifBullet

@ -28,6 +28,7 @@
#include <string> #include <string>
#include <BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h> #include <BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h>
#include <BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h> #include <BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h>
#include <BulletCollision/CollisionShapes/btCompoundShape.h>
#include <btBulletDynamicsCommon.h> #include <btBulletDynamicsCommon.h>
#include <openengine/bullet/BulletShapeLoader.h> #include <openengine/bullet/BulletShapeLoader.h>
@ -44,6 +45,22 @@ namespace Nif
namespace NifBullet namespace NifBullet
{ {
// Subclass btBhvTriangleMeshShape to auto-delete the meshInterface
struct TriangleMeshShape : public btBvhTriangleMeshShape
{
TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression)
: btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression)
{
}
virtual ~TriangleMeshShape()
{
delete getTriangleInfoMap();
delete m_meshInterface;
}
};
/** /**
*Load bulletShape from NIF files. *Load bulletShape from NIF files.
*/ */
@ -52,8 +69,9 @@ class ManualBulletShapeLoader : public OEngine::Physic::BulletShapeLoader
public: public:
ManualBulletShapeLoader() ManualBulletShapeLoader()
: mShape(NULL) : mShape(NULL)
, mStaticMesh(NULL)
, mCompoundShape(NULL)
, mBoundingBox(NULL) , mBoundingBox(NULL)
, mHasShape(false)
{ {
} }
@ -88,7 +106,8 @@ private:
/** /**
*Parse a node. *Parse a node.
*/ */
void handleNode(btTriangleMesh* mesh, Nif::Node const *node, int flags, bool isCollisionNode, bool raycasting, bool isMarker); void handleNode(Nif::Node const *node, int flags, bool isCollisionNode,
bool raycasting, bool isMarker, bool isAnimated=false);
/** /**
*Helper function *Helper function
@ -98,16 +117,24 @@ private:
/** /**
*convert a NiTriShape to a bullet trishape. *convert a NiTriShape to a bullet trishape.
*/ */
void handleNiTriShape(btTriangleMesh* mesh, const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, bool raycasting); void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, bool raycasting, bool isAnimated);
std::string mResourceName; std::string mResourceName;
OEngine::Physic::BulletShape* mShape;//current shape OEngine::Physic::BulletShape* mShape;//current shape
btBoxShape *mBoundingBox;
bool mHasShape; btCompoundShape* mCompoundShape;
btTriangleMesh* mStaticMesh;
btBoxShape *mBoundingBox;
}; };
bool getBoundingBox(const std::string& nifFile, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation);
bool findBoundingBox(const Nif::Node* node, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation);
} }
#endif #endif

@ -106,7 +106,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
const Nif::NiZBufferProperty *zprop, const Nif::NiZBufferProperty *zprop,
const Nif::NiSpecularProperty *specprop, const Nif::NiSpecularProperty *specprop,
const Nif::NiWireframeProperty *wireprop, const Nif::NiWireframeProperty *wireprop,
bool &needTangents, bool disableLighting) bool &needTangents, bool particleMaterial)
{ {
Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton(); Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton();
Ogre::MaterialPtr material = matMgr.getByName(name); Ogre::MaterialPtr material = matMgr.getByName(name);
@ -245,12 +245,9 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
} }
} }
if (disableLighting) if (particleMaterial)
{ {
ambient = Ogre::Vector3(0.f); alpha = 1.f; // Apparently ignored, might be overridden by particle vertex colors?
diffuse = Ogre::Vector3(0.f);
specular = Ogre::Vector3(0.f);
emissive = Ogre::Vector3(1.f);
} }
{ {

@ -49,7 +49,7 @@ public:
const Nif::NiZBufferProperty *zprop, const Nif::NiZBufferProperty *zprop,
const Nif::NiSpecularProperty *specprop, const Nif::NiSpecularProperty *specprop,
const Nif::NiWireframeProperty *wireprop, const Nif::NiWireframeProperty *wireprop,
bool &needTangents, bool disableLighting=false); bool &needTangents, bool particleMaterial=false);
}; };
} }

@ -756,7 +756,12 @@ class NIFObjectLoader
Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif"); Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif");
emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f, emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f,
partctrl->velocity + partctrl->velocityRandom*0.5f); partctrl->velocity + partctrl->velocityRandom*0.5f);
emitter->setEmissionRate(partctrl->emitRate);
if (partctrl->emitFlags & Nif::NiParticleSystemController::NoAutoAdjust)
emitter->setEmissionRate(partctrl->emitRate);
else
emitter->setEmissionRate(partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom/2));
emitter->setTimeToLive(partctrl->lifetime, emitter->setTimeToLive(partctrl->lifetime,
partctrl->lifetime + partctrl->lifetimeRandom); partctrl->lifetime + partctrl->lifetimeRandom);
emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x));

@ -32,9 +32,6 @@ namespace Translation
boost::filesystem::ifstream stream ( boost::filesystem::ifstream stream (
dataFileCollections.getCollection (extension).getPath (fileName)); dataFileCollections.getCollection (extension).getPath (fileName));
// Configure the stream to throw exception upon error
stream.exceptions ( boost::filesystem::ifstream::failbit | boost::filesystem::ifstream::badbit );
if (!stream.is_open()) if (!stream.is_open())
throw std::runtime_error ("failed to open translation file: " + fileName); throw std::runtime_error ("failed to open translation file: " + fileName);
@ -44,9 +41,8 @@ namespace Translation
void Storage::loadDataFromStream(ContainerType& container, std::istream& stream) void Storage::loadDataFromStream(ContainerType& container, std::istream& stream)
{ {
// NOTE: does not handle failbit/badbit. stream must be set up beforehand to throw in these cases.
std::string line; std::string line;
while (!stream.eof()) while (!stream.eof() && !stream.fail())
{ {
std::getline( stream, line ); std::getline( stream, line );
if (!line.empty() && *line.rbegin() == '\r') if (!line.empty() && *line.rbegin() == '\r')

@ -17,8 +17,6 @@
<!-- Global map --> <!-- Global map -->
<Widget type="ScrollView" skin="MW_MapView" position="0 0 284 264" align="Stretch" name="GlobalMap"> <Widget type="ScrollView" skin="MW_MapView" position="0 0 284 264" align="Stretch" name="GlobalMap">
<Property key="CanvasSize" value="1536 1536"/>
<Widget type="ImageBox" skin="ImageBox" position_real="0 0 1 1" align="Stretch" name="GlobalMapImage"> <Widget type="ImageBox" skin="ImageBox" position_real="0 0 1 1" align="Stretch" name="GlobalMapImage">
<Widget type="ImageBox" skin="ImageBox" position_real="0 0 1 1" align="Stretch" name="GlobalMapOverlay"/> <Widget type="ImageBox" skin="ImageBox" position_real="0 0 1 1" align="Stretch" name="GlobalMapOverlay"/>
</Widget> </Widget>

@ -42,7 +42,7 @@ void BulletShape::deleteShape(btCollisionShape* shape)
{ {
if(shape->isCompound()) if(shape->isCompound())
{ {
btCompoundShape* ms = static_cast<btCompoundShape*>(mCollisionShape); btCompoundShape* ms = static_cast<btCompoundShape*>(shape);
int a = ms->getNumChildShapes(); int a = ms->getNumChildShapes();
for(int i=0; i <a;i++) for(int i=0; i <a;i++)
{ {
@ -51,13 +51,14 @@ void BulletShape::deleteShape(btCollisionShape* shape)
} }
delete shape; delete shape;
} }
shape = NULL;
} }
void BulletShape::unloadImpl() void BulletShape::unloadImpl()
{ {
deleteShape(mCollisionShape); deleteShape(mCollisionShape);
deleteShape(mRaycastingShape); deleteShape(mRaycastingShape);
mCollisionShape = NULL;
mRaycastingShape = NULL;
} }
//TODO:change this? //TODO:change this?

@ -15,8 +15,6 @@ namespace Physic
*/ */
class BulletShape : public Ogre::Resource class BulletShape : public Ogre::Resource
{ {
Ogre::String mString;
protected: protected:
void loadImpl(); void loadImpl();
void unloadImpl(); void unloadImpl();
@ -32,6 +30,14 @@ public:
virtual ~BulletShape(); virtual ~BulletShape();
// Stores animated collision shapes. If any collision nodes in the NIF are animated, then mCollisionShape
// will be a btCompoundShape (which consists of one or more child shapes).
// In this map, for each animated collision shape,
// we store the bone name mapped to the child index of the shape in the btCompoundShape.
std::map<std::string, int> mAnimatedShapes;
std::map<std::string, int> mAnimatedRaycastingShapes;
btCollisionShape* mCollisionShape; btCollisionShape* mCollisionShape;
btCollisionShape* mRaycastingShape; btCollisionShape* mRaycastingShape;

@ -11,120 +11,159 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
namespace
{
// Create a copy of the given collision shape (responsibility of user to delete the returned shape).
btCollisionShape *duplicateCollisionShape(btCollisionShape *shape)
{
if(shape->isCompound())
{
btCompoundShape *comp = static_cast<btCompoundShape*>(shape);
btCompoundShape *newShape = new btCompoundShape;
int numShapes = comp->getNumChildShapes();
for(int i = 0;i < numShapes;i++)
{
btCollisionShape *child = duplicateCollisionShape(comp->getChildShape(i));
btTransform trans = comp->getChildTransform(i);
newShape->addChildShape(trans, child);
}
return newShape;
}
if(btBvhTriangleMeshShape *trishape = dynamic_cast<btBvhTriangleMeshShape*>(shape))
{
btTriangleMesh* oldMesh = dynamic_cast<btTriangleMesh*>(trishape->getMeshInterface());
btTriangleMesh* newMesh = new btTriangleMesh(*oldMesh);
NifBullet::TriangleMeshShape *newShape = new NifBullet::TriangleMeshShape(newMesh, true);
return newShape;
}
throw std::logic_error(std::string("Unhandled Bullet shape duplication: ")+shape->getName());
}
void deleteShape(btCollisionShape* shape)
{
if(shape!=NULL)
{
if(shape->isCompound())
{
btCompoundShape* ms = static_cast<btCompoundShape*>(shape);
int a = ms->getNumChildShapes();
for(int i=0; i <a;i++)
{
deleteShape(ms->getChildShape(i));
}
}
delete shape;
}
}
}
namespace OEngine { namespace OEngine {
namespace Physic namespace Physic
{ {
PhysicActor::PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale) PhysicActor::PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale)
: mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0) : mName(name), mEngine(engine), mMesh(mesh)
, mBody(0), mRaycastingBody(0), mOnGround(false), mCollisionMode(true), mBoxRotation(0,0,0,0) , mBody(0), mOnGround(false), mInternalCollisionMode(true)
, mCollisionBody(true) , mExternalCollisionMode(true)
, mForce(0.0f) , mForce(0.0f)
, mScale(scale)
{ {
mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation); if (!NifBullet::getBoundingBox(mMesh, mHalfExtents, mMeshTranslation, mMeshOrientation))
mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, true); {
Ogre::Quaternion inverse = mBoxRotation.Inverse(); mHalfExtents = Ogre::Vector3(0.f);
mBoxRotationInverse = Ogre::Quaternion(inverse.w, inverse.x, inverse.y,inverse.z); mMeshTranslation = Ogre::Vector3(0.f);
mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map mMeshOrientation = Ogre::Quaternion::IDENTITY;
}
// Use capsule shape only if base is square (nonuniform scaling apparently doesn't work on it)
if (std::abs(mHalfExtents.x-mHalfExtents.y)<mHalfExtents.x*0.05 && mHalfExtents.z >= mHalfExtents.x)
{
// Could also be btCapsuleShapeZ, but the movement solver seems to have issues with it (jumping on slopes doesn't work)
mShape.reset(new btCylinderShapeZ(BtOgre::Convert::toBullet(mHalfExtents)));
}
else
mShape.reset(new btBoxShape(BtOgre::Convert::toBullet(mHalfExtents)));
mShape->setLocalScaling(btVector3(scale,scale,scale));
btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo
(0,0, mShape.get());
mBody = new RigidBody(CI, name);
mBody->mPlaceable = false;
setPosition(position);
setRotation(rotation);
mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor,
CollisionType_Actor|CollisionType_World|CollisionType_HeightMap);
} }
PhysicActor::~PhysicActor() PhysicActor::~PhysicActor()
{ {
if(mBody) if(mBody)
{ {
mEngine->dynamicsWorld->removeRigidBody(mBody); mEngine->mDynamicsWorld->removeRigidBody(mBody);
delete mBody; delete mBody;
} }
if(mRaycastingBody)
{
mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody);
delete mRaycastingBody;
}
} }
void PhysicActor::enableCollisionMode(bool collision) void PhysicActor::enableCollisionMode(bool collision)
{ {
mCollisionMode = collision; mInternalCollisionMode = collision;
} }
void PhysicActor::enableCollisionBody(bool collision) void PhysicActor::enableCollisionBody(bool collision)
{ {
assert(mBody); assert(mBody);
if(collision && !mCollisionBody) enableCollisionBody(); if(collision && !mExternalCollisionMode) enableCollisionBody();
if(!collision && mCollisionBody) disableCollisionBody(); if(!collision && mExternalCollisionMode) disableCollisionBody();
mCollisionBody = collision; mExternalCollisionMode = collision;
} }
void PhysicActor::setPosition(const Ogre::Vector3 &pos) const Ogre::Vector3& PhysicActor::getPosition() const
{ {
assert(mBody); return mPosition;
if(pos != getPosition())
{
mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation);
mEngine->adjustRigidBody(mRaycastingBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation);
}
} }
void PhysicActor::setRotation(const Ogre::Quaternion &quat) void PhysicActor::setPosition(const Ogre::Vector3 &position)
{ {
assert(mBody); assert(mBody);
if(!quat.equals(getRotation(), Ogre::Radian(0))){
mEngine->adjustRigidBody(mBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation);
mEngine->adjustRigidBody(mRaycastingBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation);
} mPosition = position;
}
btTransform tr = mBody->getWorldTransform();
Ogre::Quaternion meshrot = mMeshOrientation;
Ogre::Vector3 transrot = meshrot * (mMeshTranslation * mScale);
Ogre::Vector3 newPosition = transrot + position;
Ogre::Vector3 PhysicActor::getPosition() tr.setOrigin(BtOgre::Convert::toBullet(newPosition));
{ mBody->setWorldTransform(tr);
assert(mBody);
btVector3 vec = mBody->getWorldTransform().getOrigin();
Ogre::Quaternion rotation = Ogre::Quaternion(mBody->getWorldTransform().getRotation().getW(), mBody->getWorldTransform().getRotation().getX(),
mBody->getWorldTransform().getRotation().getY(), mBody->getWorldTransform().getRotation().getZ());
Ogre::Vector3 transrot = rotation * mBoxScaledTranslation;
Ogre::Vector3 visualPosition = Ogre::Vector3(vec.getX(), vec.getY(), vec.getZ()) - transrot;
return visualPosition;
} }
Ogre::Quaternion PhysicActor::getRotation() void PhysicActor::setRotation (const Ogre::Quaternion& rotation)
{ {
assert(mBody); btTransform tr = mBody->getWorldTransform();
btQuaternion quat = mBody->getWorldTransform().getRotation(); tr.setRotation(BtOgre::Convert::toBullet(mMeshOrientation * rotation));
return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()) * mBoxRotationInverse; mBody->setWorldTransform(tr);
} }
void PhysicActor::setScale(float scale){ void PhysicActor::setScale(float scale)
//We only need to change the scaled box translation, box rotations remain the same. {
assert(mBody); mScale = scale;
mBoxScaledTranslation = mBoxScaledTranslation / mBody->getCollisionShape()->getLocalScaling().getX(); mShape->setLocalScaling(btVector3(scale,scale,scale));
mBoxScaledTranslation *= scale; setPosition(mPosition);
Ogre::Vector3 pos = getPosition();
Ogre::Quaternion rot = getRotation();
if(mBody){
mEngine->dynamicsWorld->removeRigidBody(mBody);
mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody);
delete mBody;
delete mRaycastingBody;
}
//Create the newly scaled rigid body
mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot);
mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot, 0, 0, true);
mEngine->addRigidBody(mCollisionBody ? mBody : 0, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map
} }
Ogre::Vector3 PhysicActor::getHalfExtents() const Ogre::Vector3 PhysicActor::getHalfExtents() const
{ {
if(mBody) return mHalfExtents * mScale;
{
btBoxShape *box = static_cast<btBoxShape*>(mBody->getCollisionShape());
if(box != NULL)
{
btVector3 size = box->getHalfExtentsWithMargin();
return Ogre::Vector3(size.getX(), size.getY(), size.getZ());
}
}
return Ogre::Vector3(0.0f);
} }
void PhysicActor::setInertialForce(const Ogre::Vector3 &force) void PhysicActor::setInertialForce(const Ogre::Vector3 &force)
@ -139,12 +178,16 @@ namespace Physic
void PhysicActor::disableCollisionBody() void PhysicActor::disableCollisionBody()
{ {
mEngine->dynamicsWorld->removeRigidBody(mBody); mEngine->mDynamicsWorld->removeRigidBody(mBody);
mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor,
CollisionType_Raycasting);
} }
void PhysicActor::enableCollisionBody() void PhysicActor::enableCollisionBody()
{ {
mEngine->dynamicsWorld->addRigidBody(mBody,CollisionType_Actor,CollisionType_World|CollisionType_HeightMap); mEngine->mDynamicsWorld->removeRigidBody(mBody);
mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor,
CollisionType_Actor|CollisionType_World|CollisionType_HeightMap);
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -189,8 +232,8 @@ namespace Physic
broadphase = new btDbvtBroadphase(); broadphase = new btDbvtBroadphase();
// The world. // The world.
dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration); mDynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
dynamicsWorld->setGravity(btVector3(0,0,-10)); mDynamicsWorld->setGravity(btVector3(0,0,-10));
if(BulletShapeManager::getSingletonPtr() == NULL) if(BulletShapeManager::getSingletonPtr() == NULL)
{ {
@ -208,10 +251,10 @@ namespace Physic
if(!isDebugCreated) if(!isDebugCreated)
{ {
Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode(); Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld); mDebugDrawer = new BtOgre::DebugDrawer(node, mDynamicsWorld);
dynamicsWorld->setDebugDrawer(mDebugDrawer); mDynamicsWorld->setDebugDrawer(mDebugDrawer);
isDebugCreated = true; isDebugCreated = true;
dynamicsWorld->debugDrawWorld(); mDynamicsWorld->debugDrawWorld();
} }
} }
@ -238,10 +281,15 @@ namespace Physic
PhysicEngine::~PhysicEngine() PhysicEngine::~PhysicEngine()
{ {
for (std::map<RigidBody*, AnimatedShapeInstance>::iterator it = mAnimatedShapes.begin(); it != mAnimatedShapes.end(); ++it)
deleteShape(it->second.mCompound);
for (std::map<RigidBody*, AnimatedShapeInstance>::iterator it = mAnimatedRaycastingShapes.begin(); it != mAnimatedRaycastingShapes.end(); ++it)
deleteShape(it->second.mCompound);
HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin(); HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin();
for (; hf_it != mHeightFieldMap.end(); ++hf_it) for (; hf_it != mHeightFieldMap.end(); ++hf_it)
{ {
dynamicsWorld->removeRigidBody(hf_it->second.mBody); mDynamicsWorld->removeRigidBody(hf_it->second.mBody);
delete hf_it->second.mShape; delete hf_it->second.mShape;
delete hf_it->second.mBody; delete hf_it->second.mBody;
} }
@ -251,7 +299,7 @@ namespace Physic
{ {
if (rb_it->second != NULL) if (rb_it->second != NULL)
{ {
dynamicsWorld->removeRigidBody(rb_it->second); mDynamicsWorld->removeRigidBody(rb_it->second);
delete rb_it->second; delete rb_it->second;
rb_it->second = NULL; rb_it->second = NULL;
@ -262,7 +310,7 @@ namespace Physic
{ {
if (rb_it->second != NULL) if (rb_it->second != NULL)
{ {
dynamicsWorld->removeRigidBody(rb_it->second); mDynamicsWorld->removeRigidBody(rb_it->second);
delete rb_it->second; delete rb_it->second;
rb_it->second = NULL; rb_it->second = NULL;
@ -281,7 +329,7 @@ namespace Physic
delete mDebugDrawer; delete mDebugDrawer;
delete dynamicsWorld; delete mDynamicsWorld;
delete solver; delete solver;
delete collisionConfiguration; delete collisionConfiguration;
delete dispatcher; delete dispatcher;
@ -331,7 +379,7 @@ namespace Physic
mHeightFieldMap [name] = hf; mHeightFieldMap [name] = hf;
dynamicsWorld->addRigidBody(body,CollisionType_HeightMap|CollisionType_Raycasting, mDynamicsWorld->addRigidBody(body,CollisionType_HeightMap,
CollisionType_World|CollisionType_Actor|CollisionType_Raycasting); CollisionType_World|CollisionType_Actor|CollisionType_Raycasting);
} }
@ -343,7 +391,7 @@ namespace Physic
HeightField hf = mHeightFieldMap [name]; HeightField hf = mHeightFieldMap [name];
dynamicsWorld->removeRigidBody(hf.mBody); mDynamicsWorld->removeRigidBody(hf.mBody);
delete hf.mShape; delete hf.mShape;
delete hf.mBody; delete hf.mBody;
@ -367,7 +415,6 @@ namespace Physic
{ {
std::string sid = (boost::format("%07.3f") % scale).str(); std::string sid = (boost::format("%07.3f") % scale).str();
std::string outputstring = mesh + sid; std::string outputstring = mesh + sid;
//std::cout << "The string" << outputstring << "\n";
//get the shape from the .nif //get the shape from the .nif
mShapeLoader->load(outputstring,"General"); mShapeLoader->load(outputstring,"General");
@ -397,17 +444,38 @@ namespace Physic
if (!shape->mRaycastingShape && raycasting) if (!shape->mRaycastingShape && raycasting)
return NULL; return NULL;
if (!raycasting) btCollisionShape* collisionShape = raycasting ? shape->mRaycastingShape : shape->mCollisionShape;
shape->mCollisionShape->setLocalScaling( btVector3(scale,scale,scale));
else // If this is an animated compound shape, we must duplicate it so we can animate
shape->mRaycastingShape->setLocalScaling( btVector3(scale,scale,scale)); // multiple instances independently.
if (!raycasting && !shape->mAnimatedShapes.empty())
collisionShape = duplicateCollisionShape(collisionShape);
if (raycasting && !shape->mAnimatedRaycastingShapes.empty())
collisionShape = duplicateCollisionShape(collisionShape);
collisionShape->setLocalScaling( btVector3(scale,scale,scale));
//create the real body //create the real body
btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo
(0,0, raycasting ? shape->mRaycastingShape : shape->mCollisionShape); (0,0, collisionShape);
RigidBody* body = new RigidBody(CI,name); RigidBody* body = new RigidBody(CI,name);
body->mPlaceable = placeable; body->mPlaceable = placeable;
if (!raycasting && !shape->mAnimatedShapes.empty())
{
AnimatedShapeInstance instance;
instance.mAnimatedShapes = shape->mAnimatedShapes;
instance.mCompound = collisionShape;
mAnimatedShapes[body] = instance;
}
if (raycasting && !shape->mAnimatedRaycastingShapes.empty())
{
AnimatedShapeInstance instance;
instance.mAnimatedShapes = shape->mAnimatedRaycastingShapes;
instance.mCompound = collisionShape;
mAnimatedRaycastingShapes[body] = instance;
}
if(scaledBoxTranslation != 0) if(scaledBoxTranslation != 0)
*scaledBoxTranslation = shape->mBoxTranslation * scale; *scaledBoxTranslation = shape->mBoxTranslation * scale;
if(boxRotation != 0) if(boxRotation != 0)
@ -415,34 +483,20 @@ namespace Physic
adjustRigidBody(body, position, rotation, shape->mBoxTranslation * scale, shape->mBoxRotation); adjustRigidBody(body, position, rotation, shape->mBoxTranslation * scale, shape->mBoxRotation);
return body; if (!raycasting)
{
} assert (mCollisionObjectMap.find(name) == mCollisionObjectMap.end());
mCollisionObjectMap[name] = body;
void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody,bool actor) mDynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_Actor|CollisionType_HeightMap);
{
if(!body && !raycastingBody)
return; // nothing to do
const std::string& name = (body ? body->mName : raycastingBody->mName);
if (body){
if(actor) dynamicsWorld->addRigidBody(body,CollisionType_Actor,CollisionType_World|CollisionType_HeightMap);
else dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_Actor|CollisionType_HeightMap);
} }
else
if (raycastingBody) {
dynamicsWorld->addRigidBody(raycastingBody,CollisionType_Raycasting,CollisionType_Raycasting|CollisionType_World); assert (mRaycastingObjectMap.find(name) == mRaycastingObjectMap.end());
mRaycastingObjectMap[name] = body;
if(addToMap){ mDynamicsWorld->addRigidBody(body,CollisionType_Raycasting,CollisionType_Raycasting);
removeRigidBody(name);
deleteRigidBody(name);
if (body)
mCollisionObjectMap[name] = body;
if (raycastingBody)
mRaycastingObjectMap[name] = raycastingBody;
} }
return body;
} }
void PhysicEngine::removeRigidBody(const std::string &name) void PhysicEngine::removeRigidBody(const std::string &name)
@ -453,7 +507,7 @@ namespace Physic
RigidBody* body = it->second; RigidBody* body = it->second;
if(body != NULL) if(body != NULL)
{ {
dynamicsWorld->removeRigidBody(body); mDynamicsWorld->removeRigidBody(body);
} }
} }
it = mRaycastingObjectMap.find(name); it = mRaycastingObjectMap.find(name);
@ -462,7 +516,7 @@ namespace Physic
RigidBody* body = it->second; RigidBody* body = it->second;
if(body != NULL) if(body != NULL)
{ {
dynamicsWorld->removeRigidBody(body); mDynamicsWorld->removeRigidBody(body);
} }
} }
} }
@ -476,6 +530,10 @@ namespace Physic
if(body != NULL) if(body != NULL)
{ {
if (mAnimatedShapes.find(body) != mAnimatedShapes.end())
deleteShape(mAnimatedShapes[body].mCompound);
mAnimatedShapes.erase(body);
delete body; delete body;
} }
mCollisionObjectMap.erase(it); mCollisionObjectMap.erase(it);
@ -487,6 +545,10 @@ namespace Physic
if(body != NULL) if(body != NULL)
{ {
if (mAnimatedRaycastingShapes.find(body) != mAnimatedRaycastingShapes.end())
deleteShape(mAnimatedRaycastingShapes[body].mCompound);
mAnimatedRaycastingShapes.erase(body);
delete body; delete body;
} }
mRaycastingObjectMap.erase(it); mRaycastingObjectMap.erase(it);
@ -605,7 +667,7 @@ namespace Physic
if (!body) // fall back to raycasting body if there is no collision body if (!body) // fall back to raycasting body if there is no collision body
body = getRigidBody(name, true); body = getRigidBody(name, true);
ContactTestResultCallback callback; ContactTestResultCallback callback;
dynamicsWorld->contactTest(body, callback); mDynamicsWorld->contactTest(body, callback);
return callback.mResult; return callback.mResult;
} }
@ -615,16 +677,16 @@ namespace Physic
btCollisionObject *object) btCollisionObject *object)
{ {
DeepestNotMeContactTestResultCallback callback(filter, origin); DeepestNotMeContactTestResultCallback callback(filter, origin);
callback.m_collisionFilterGroup = CollisionType_Actor;
callback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor; callback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor;
dynamicsWorld->contactTest(object, callback); mDynamicsWorld->contactTest(object, callback);
return std::make_pair(callback.mObject, callback.mContactPoint); return std::make_pair(callback.mObject, callback.mContactPoint);
} }
void PhysicEngine::stepSimulation(double deltaT) void PhysicEngine::stepSimulation(double deltaT)
{ {
// This seems to be needed for character controller objects // This seems to be needed for character controller objects
dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); mDynamicsWorld->stepSimulation(deltaT,10, 1/60.0);
if(isDebugCreated) if(isDebugCreated)
{ {
mDebugDrawer->step(); mDebugDrawer->step();
@ -640,8 +702,6 @@ namespace Physic
PhysicActor* newActor = new PhysicActor(name, mesh, this, position, rotation, scale); PhysicActor* newActor = new PhysicActor(name, mesh, this, position, rotation, scale);
//dynamicsWorld->addAction( newActor->mCharacter );
mActorMap[name] = newActor; mActorMap[name] = newActor;
} }
@ -653,7 +713,6 @@ namespace Physic
PhysicActor* act = it->second; PhysicActor* act = it->second;
if(act != NULL) if(act != NULL)
{ {
delete act; delete act;
} }
mActorMap.erase(it); mActorMap.erase(it);
@ -684,14 +743,15 @@ namespace Physic
float d = -1; float d = -1;
btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
resultCallback1.m_collisionFilterGroup = 0xff;
if(raycastingObjectOnly) if(raycastingObjectOnly)
resultCallback1.m_collisionFilterMask = CollisionType_Raycasting; resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor;
else else
resultCallback1.m_collisionFilterMask = CollisionType_World; resultCallback1.m_collisionFilterMask = CollisionType_World;
if(!ignoreHeightMap) if(!ignoreHeightMap)
resultCallback1.m_collisionFilterMask = resultCallback1.m_collisionFilterMask | CollisionType_HeightMap; resultCallback1.m_collisionFilterMask = resultCallback1.m_collisionFilterMask | CollisionType_HeightMap;
dynamicsWorld->rayTest(from, to, resultCallback1); mDynamicsWorld->rayTest(from, to, resultCallback1);
if (resultCallback1.hasHit()) if (resultCallback1.hasHit())
{ {
name = static_cast<const RigidBody&>(*resultCallback1.m_collisionObject).mName; name = static_cast<const RigidBody&>(*resultCallback1.m_collisionObject).mName;
@ -724,6 +784,7 @@ namespace Physic
std::pair<bool, float> PhysicEngine::sphereCast (float radius, btVector3& from, btVector3& to) std::pair<bool, float> PhysicEngine::sphereCast (float radius, btVector3& from, btVector3& to)
{ {
OurClosestConvexResultCallback callback(from, to); OurClosestConvexResultCallback callback(from, to);
callback.m_collisionFilterGroup = 0xff;
callback.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_HeightMap; callback.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_HeightMap;
btSphereShape shape(radius); btSphereShape shape(radius);
@ -732,7 +793,7 @@ namespace Physic
btTransform from_ (btrot, from); btTransform from_ (btrot, from);
btTransform to_ (btrot, to); btTransform to_ (btrot, to);
dynamicsWorld->convexSweepTest(&shape, from_, to_, callback); mDynamicsWorld->convexSweepTest(&shape, from_, to_, callback);
if (callback.hasHit()) if (callback.hasHit())
return std::make_pair(true, callback.m_closestHitFraction); return std::make_pair(true, callback.m_closestHitFraction);
@ -743,8 +804,9 @@ namespace Physic
std::vector< std::pair<float, std::string> > PhysicEngine::rayTest2(btVector3& from, btVector3& to) std::vector< std::pair<float, std::string> > PhysicEngine::rayTest2(btVector3& from, btVector3& to)
{ {
MyRayResultCallback resultCallback1; MyRayResultCallback resultCallback1;
resultCallback1.m_collisionFilterMask = CollisionType_Raycasting; resultCallback1.m_collisionFilterGroup = 0xff;
dynamicsWorld->rayTest(from, to, resultCallback1); resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor|CollisionType_HeightMap;
mDynamicsWorld->rayTest(from, to, resultCallback1);
std::vector< std::pair<float, const btCollisionObject*> > results = resultCallback1.results; std::vector< std::pair<float, const btCollisionObject*> > results = resultCallback1.results;
std::vector< std::pair<float, std::string> > results2; std::vector< std::pair<float, std::string> > results2;

@ -76,13 +76,13 @@ namespace Physic
RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name); RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name);
virtual ~RigidBody(); virtual ~RigidBody();
std::string mName; std::string mName;
// Hack: placeable objects (that can be picked up by the player) have different collision behaviour.
// This variable needs to be passed to BulletNifLoader.
bool mPlaceable; bool mPlaceable;
}; };
/**
* A physic actor uses a rigid body based on box shapes.
* Pmove is used to move the physic actor around the dynamic world.
*/
class PhysicActor class PhysicActor
{ {
public: public:
@ -92,13 +92,6 @@ namespace Physic
void setPosition(const Ogre::Vector3 &pos); void setPosition(const Ogre::Vector3 &pos);
/**
* This adjusts the rotation of a PhysicActor
* If we have any problems with this (getting stuck in pmove) we should change it
* from setting the visual orientation to setting the orientation of the rigid body directly.
*/
void setRotation(const Ogre::Quaternion &quat);
/** /**
* Sets the collisionMode for this actor. If disabled, the actor can fly and clip geometry. * Sets the collisionMode for this actor. If disabled, the actor can fly and clip geometry.
*/ */
@ -111,28 +104,20 @@ namespace Physic
bool getCollisionMode() const bool getCollisionMode() const
{ {
return mCollisionMode; return mInternalCollisionMode;
} }
/**
* This returns the visual position of the PhysicActor (used to position a scenenode).
* Note - this is different from the position of the contained mBody.
*/
Ogre::Vector3 getPosition();
/**
* Returns the visual orientation of the PhysicActor
*/
Ogre::Quaternion getRotation();
/** /**
* Sets the scale of the PhysicActor * Sets the scale of the PhysicActor
*/ */
void setScale(float scale); void setScale(float scale);
void setRotation (const Ogre::Quaternion& rotation);
const Ogre::Vector3& getPosition() const;
/** /**
* Returns the half extents for this PhysiActor * Returns the (scaled) half extents
*/ */
Ogre::Vector3 getHalfExtents() const; Ogre::Vector3 getHalfExtents() const;
@ -153,7 +138,7 @@ namespace Physic
bool getOnGround() const bool getOnGround() const
{ {
return mCollisionMode && mOnGround; return mInternalCollisionMode && mOnGround;
} }
btCollisionObject *getCollisionBody() const btCollisionObject *getCollisionBody() const
@ -165,17 +150,21 @@ namespace Physic
void disableCollisionBody(); void disableCollisionBody();
void enableCollisionBody(); void enableCollisionBody();
boost::shared_ptr<btCollisionShape> mShape;
OEngine::Physic::RigidBody* mBody; OEngine::Physic::RigidBody* mBody;
OEngine::Physic::RigidBody* mRaycastingBody;
Ogre::Vector3 mBoxScaledTranslation; Ogre::Quaternion mMeshOrientation;
Ogre::Quaternion mBoxRotation; Ogre::Vector3 mMeshTranslation;
Ogre::Quaternion mBoxRotationInverse; Ogre::Vector3 mHalfExtents;
float mScale;
Ogre::Vector3 mPosition;
Ogre::Vector3 mForce; Ogre::Vector3 mForce;
bool mOnGround; bool mOnGround;
bool mCollisionMode; bool mInternalCollisionMode;
bool mCollisionBody; bool mExternalCollisionMode;
std::string mMesh; std::string mMesh;
std::string mName; std::string mName;
@ -189,6 +178,14 @@ namespace Physic
RigidBody* mBody; RigidBody* mBody;
}; };
struct AnimatedShapeInstance
{
btCollisionShape* mCompound;
// Maps bone name to child index in the compound shape
std::map<std::string, int> mAnimatedShapes;
};
/** /**
* The PhysicEngine class contain everything which is needed for Physic. * The PhysicEngine class contain everything which is needed for Physic.
* It's needed that Ogre Resources are set up before the PhysicEngine is created. * It's needed that Ogre Resources are set up before the PhysicEngine is created.
@ -239,11 +236,6 @@ namespace Physic
*/ */
void removeHeightField(int x, int y); void removeHeightField(int x, int y);
/**
* Add a RigidBody to the simulation
*/
void addRigidBody(RigidBody* body, bool addToMap = true, RigidBody* raycastingBody = NULL,bool actor = false);
/** /**
* Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap. * Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap.
*/ */
@ -335,7 +327,7 @@ namespace Physic
btDefaultCollisionConfiguration* collisionConfiguration; btDefaultCollisionConfiguration* collisionConfiguration;
btSequentialImpulseConstraintSolver* solver; btSequentialImpulseConstraintSolver* solver;
btCollisionDispatcher* dispatcher; btCollisionDispatcher* dispatcher;
btDiscreteDynamicsWorld* dynamicsWorld; btDiscreteDynamicsWorld* mDynamicsWorld;
//the NIF file loader. //the NIF file loader.
BulletShapeLoader* mShapeLoader; BulletShapeLoader* mShapeLoader;
@ -346,8 +338,14 @@ namespace Physic
typedef std::map<std::string,RigidBody*> RigidBodyContainer; typedef std::map<std::string,RigidBody*> RigidBodyContainer;
RigidBodyContainer mCollisionObjectMap; RigidBodyContainer mCollisionObjectMap;
// Compound shapes that must be animated each frame based on bone positions
// the index refers to an element in mCollisionObjectMap
std::map<RigidBody*, AnimatedShapeInstance > mAnimatedShapes;
RigidBodyContainer mRaycastingObjectMap; RigidBodyContainer mRaycastingObjectMap;
std::map<RigidBody*, AnimatedShapeInstance > mAnimatedRaycastingShapes;
typedef std::map<std::string, PhysicActor*> PhysicActorContainer; typedef std::map<std::string, PhysicActor*> PhysicActorContainer;
PhysicActorContainer mActorMap; PhysicActorContainer mActorMap;

@ -65,12 +65,13 @@ void ActorTracer::doTrace(btCollisionObject *actor, const Ogre::Vector3 &start,
to.setOrigin(btend); to.setOrigin(btend);
ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0)); ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0));
newTraceCallback.m_collisionFilterGroup = CollisionType_Actor;
newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap |
CollisionType_Actor; CollisionType_Actor;
btCollisionShape *shape = actor->getCollisionShape(); btCollisionShape *shape = actor->getCollisionShape();
assert(shape->isConvex()); assert(shape->isConvex());
enginePass->dynamicsWorld->convexSweepTest(static_cast<btConvexShape*>(shape), enginePass->mDynamicsWorld->convexSweepTest(static_cast<btConvexShape*>(shape),
from, to, newTraceCallback); from, to, newTraceCallback);
// Copy the hit data over to our trace results struct: // Copy the hit data over to our trace results struct:
@ -89,27 +90,26 @@ void ActorTracer::doTrace(btCollisionObject *actor, const Ogre::Vector3 &start,
} }
} }
void ActorTracer::findGround(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass) void ActorTracer::findGround(const OEngine::Physic::PhysicActor* actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass)
{ {
const btVector3 btstart(start.x, start.y, start.z+1.0f); const btVector3 btstart(start.x, start.y, start.z+1.0f);
const btVector3 btend(end.x, end.y, end.z+1.0f); const btVector3 btend(end.x, end.y, end.z+1.0f);
const btTransform &trans = actor->getWorldTransform(); const btTransform &trans = actor->getCollisionBody()->getWorldTransform();
btTransform from(trans.getBasis(), btstart); btTransform from(trans.getBasis(), btstart);
btTransform to(trans.getBasis(), btend); btTransform to(trans.getBasis(), btend);
ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0)); ClosestNotMeConvexResultCallback newTraceCallback(actor->getCollisionBody(), btstart-btend, btScalar(0.0));
newTraceCallback.m_collisionFilterGroup = CollisionType_Actor;
newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap |
CollisionType_Actor; CollisionType_Actor;
const btBoxShape *shape = dynamic_cast<btBoxShape*>(actor->getCollisionShape()); btVector3 halfExtents(actor->getHalfExtents().x, actor->getHalfExtents().y, actor->getHalfExtents().z);
assert(shape);
btVector3 halfExtents = shape->getHalfExtentsWithMargin();
halfExtents[2] = 1.0f; halfExtents[2] = 1.0f;
btBoxShape box(halfExtents); btCylinderShapeZ base(halfExtents);
enginePass->dynamicsWorld->convexSweepTest(&box, from, to, newTraceCallback); enginePass->mDynamicsWorld->convexSweepTest(&base, from, to, newTraceCallback);
if(newTraceCallback.hasHit()) if(newTraceCallback.hasHit())
{ {
const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld;

@ -12,6 +12,7 @@ namespace OEngine
namespace Physic namespace Physic
{ {
class PhysicEngine; class PhysicEngine;
class PhysicActor;
struct ActorTracer struct ActorTracer
{ {
@ -22,7 +23,7 @@ namespace Physic
void doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, void doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end,
const PhysicEngine *enginePass); const PhysicEngine *enginePass);
void findGround(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, void findGround(const OEngine::Physic::PhysicActor* actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end,
const PhysicEngine *enginePass); const PhysicEngine *enginePass);
}; };
} }

Loading…
Cancel
Save