Merge pull request #301 from TES3MP/master

Add master commits up to 28 Sep 2017
new-script-api
David Cernat 7 years ago committed by GitHub
commit 841a4f90c1

@ -40,6 +40,7 @@ Programmers
Chris Robinson (KittyCat) Chris Robinson (KittyCat)
Cory F. Cohen (cfcohen) Cory F. Cohen (cfcohen)
Cris Mihalache (Mirceam) Cris Mihalache (Mirceam)
crussell187
darkf darkf
devnexen devnexen
Dieho Dieho
@ -105,6 +106,7 @@ Programmers
Michael Papageorgiou (werdanith) Michael Papageorgiou (werdanith)
Michał Bień (Glorf) Michał Bień (Glorf)
Michał Moroz (dragonee) Michał Moroz (dragonee)
Miloslav Číž (drummyfish)
Miroslav Puda (pakanek) Miroslav Puda (pakanek)
MiroslavR MiroslavR
Mitchell Schwitzer (schwitzerm) Mitchell Schwitzer (schwitzerm)

@ -17,6 +17,7 @@ set(ESSIMPORTER_FILES
importscri.cpp importscri.cpp
importscpt.cpp importscpt.cpp
importproj.cpp importproj.cpp
importsplm.cpp
importercontext.cpp importercontext.cpp
converter.cpp converter.cpp
convertacdt.cpp convertacdt.cpp

@ -1,12 +1,12 @@
#include "converter.hpp" #include "converter.hpp"
#include <stdexcept> #include <stdexcept>
#include <algorithm>
#include <osgDB/WriteFile> #include <osgDB/WriteFile>
#include <components/esm/creaturestate.hpp> #include <components/esm/creaturestate.hpp>
#include <components/esm/containerstate.hpp> #include <components/esm/containerstate.hpp>
#include <components/esm/projectilestate.hpp>
#include "convertcrec.hpp" #include "convertcrec.hpp"
#include "convertcntc.hpp" #include "convertcntc.hpp"
@ -461,11 +461,7 @@ namespace ESSImport
if (!pnam.isMagic()) if (!pnam.isMagic())
{ {
ESM::ProjectileState out; ESM::ProjectileState out;
out.mId = pnam.mArrowId.toString(); convertBaseState(out, pnam);
out.mPosition = pnam.mPosition;
out.mOrientation.mValues[0] = out.mOrientation.mValues[1] = out.mOrientation.mValues[2] = 0.0f;
out.mOrientation.mValues[3] = 1.0f;
out.mActorId = convertActorId(pnam.mActorId.toString(), *mContext);
out.mBowId = pnam.mBowId.toString(); out.mBowId = pnam.mBowId.toString();
out.mVelocity = pnam.mVelocity; out.mVelocity = pnam.mVelocity;
@ -477,16 +473,49 @@ namespace ESSImport
} }
else else
{ {
// TODO: Implement magic projectile conversion. ESM::MagicBoltState out;
convertBaseState(out, pnam);
/*esm.startRecord(ESM::REC_MPRJ); auto it = std::find_if(mContext->mActiveSpells.begin(), mContext->mActiveSpells.end(),
out.save(esm); [&pnam](const SPLM::ActiveSpell& spell) -> bool { return spell.mIndex == pnam.mSplmIndex; });
esm.endRecord(ESM::REC_MPRJ);*/
std::cerr << "Warning: Skipped conversion for magic projectile \"" << pnam.mArrowId.toString() << "\" (not implemented)" << std::endl; if (it == mContext->mActiveSpells.end())
continue; {
std::cerr << "Warning: Skipped conversion for magic projectile \"" << pnam.mArrowId.toString() << "\" (invalid spell link)" << std::endl;
continue;
}
out.mSpellId = it->mSPDT.mId.toString();
out.mSpeed = pnam.mSpeed * 0.001f; // not sure where this factor comes from
esm.startRecord(ESM::REC_MPRJ);
out.save(esm);
esm.endRecord(ESM::REC_MPRJ);
} }
} }
} }
void ConvertPROJ::convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam)
{
base.mId = pnam.mArrowId.toString();
base.mPosition = pnam.mPosition;
osg::Quat orient;
orient.makeRotate(osg::Vec3f(0,1,0), pnam.mVelocity);
base.mOrientation = orient;
base.mActorId = convertActorId(pnam.mActorId.toString(), *mContext);
}
void ConvertSPLM::read(ESM::ESMReader& esm)
{
mSPLM.load(esm);
mContext->mActiveSpells = mSPLM.mActiveSpells;
}
void ConvertSPLM::write(ESM::ESMWriter& esm)
{
std::cerr << "Warning: Skipped active spell conversion (not implemented)" << std::endl;
}
} }

@ -22,6 +22,7 @@
#include <components/esm/globalscript.hpp> #include <components/esm/globalscript.hpp>
#include <components/esm/queststate.hpp> #include <components/esm/queststate.hpp>
#include <components/esm/stolenitems.hpp> #include <components/esm/stolenitems.hpp>
#include <components/esm/projectilestate.hpp>
#include "importcrec.hpp" #include "importcrec.hpp"
#include "importcntc.hpp" #include "importcntc.hpp"
@ -36,6 +37,7 @@
#include "importjour.hpp" #include "importjour.hpp"
#include "importscpt.hpp" #include "importscpt.hpp"
#include "importproj.h" #include "importproj.h"
#include "importsplm.h"
#include "convertacdt.hpp" #include "convertacdt.hpp"
#include "convertnpcc.hpp" #include "convertnpcc.hpp"
@ -602,9 +604,19 @@ public:
virtual void read(ESM::ESMReader& esm) override; virtual void read(ESM::ESMReader& esm) override;
virtual void write(ESM::ESMWriter& esm) override; virtual void write(ESM::ESMWriter& esm) override;
private: private:
void convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam);
PROJ mProj; PROJ mProj;
}; };
class ConvertSPLM : public Converter
{
public:
virtual void read(ESM::ESMReader& esm) override;
virtual void write(ESM::ESMWriter& esm) override;
private:
SPLM mSPLM;
};
} }
#endif #endif

@ -271,6 +271,7 @@ namespace ESSImport
const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value; const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value;
const unsigned int recGAME = ESM::FourCC<'G','A','M','E'>::value; const unsigned int recGAME = ESM::FourCC<'G','A','M','E'>::value;
const unsigned int recJOUR = ESM::FourCC<'J','O','U','R'>::value; const unsigned int recJOUR = ESM::FourCC<'J','O','U','R'>::value;
const unsigned int recSPLM = ESM::FourCC<'S','P','L','M'>::value;
std::map<unsigned int, std::shared_ptr<Converter> > converters; std::map<unsigned int, std::shared_ptr<Converter> > converters;
converters[ESM::REC_GLOB] = std::shared_ptr<Converter>(new ConvertGlobal()); converters[ESM::REC_GLOB] = std::shared_ptr<Converter>(new ConvertGlobal());
@ -304,12 +305,12 @@ namespace ESSImport
converters[recJOUR ] = std::shared_ptr<Converter>(new ConvertJOUR()); converters[recJOUR ] = std::shared_ptr<Converter>(new ConvertJOUR());
converters[ESM::REC_SCPT] = std::shared_ptr<Converter>(new ConvertSCPT()); converters[ESM::REC_SCPT] = std::shared_ptr<Converter>(new ConvertSCPT());
converters[ESM::REC_PROJ] = std::shared_ptr<Converter>(new ConvertPROJ()); converters[ESM::REC_PROJ] = std::shared_ptr<Converter>(new ConvertPROJ());
converters[recSPLM] = std::shared_ptr<Converter>(new ConvertSPLM());
// TODO: // TODO:
// - REGN (weather in certain regions?) // - REGN (weather in certain regions?)
// - VFXM // - VFXM
// - SPLM (active spell effects) // - SPLM (active spell effects)
// - PROJ (magic projectiles in air)
std::set<unsigned int> unknownRecords; std::set<unsigned int> unknownRecords;

@ -15,7 +15,7 @@
#include "importcrec.hpp" #include "importcrec.hpp"
#include "importcntc.hpp" #include "importcntc.hpp"
#include "importplayer.hpp" #include "importplayer.hpp"
#include "importsplm.h"
@ -54,6 +54,8 @@ namespace ESSImport
std::map<std::string, ESM::Creature> mCreatures; std::map<std::string, ESM::Creature> mCreatures;
std::map<std::string, ESM::NPC> mNpcs; std::map<std::string, ESM::NPC> mNpcs;
std::vector<SPLM::ActiveSpell> mActiveSpells;
Context() Context()
: mDay(0) : mDay(0)
, mMonth(0) , mMonth(0)

@ -0,0 +1,43 @@
#include "importsplm.h"
#include <components/esm/esmreader.hpp>
namespace ESSImport
{
void SPLM::load(ESM::ESMReader& esm)
{
while (esm.isNextSub("NAME"))
{
ActiveSpell spell;
esm.getHT(spell.mIndex);
esm.getHNT(spell.mSPDT, "SPDT");
spell.mTarget = esm.getHNOString("TNAM");
while (esm.isNextSub("NPDT"))
{
ActiveEffect effect;
esm.getHT(effect.mNPDT);
// Effect-specific subrecords can follow:
// - INAM for disintegration and bound effects
// - CNAM for summoning and command effects
// - VNAM for vampirism
// NOTE: There can be multiple INAMs per effect.
// TODO: Needs more research.
esm.skipHSubUntil("NAM0"); // sentinel
esm.getSubName();
esm.skipHSub();
spell.mActiveEffects.push_back(effect);
}
unsigned char xnam; // sentinel
esm.getHNT(xnam, "XNAM");
mActiveSpells.push_back(spell);
}
}
}

@ -0,0 +1,81 @@
#ifndef OPENMW_ESSIMPORT_IMPORTSPLM_H
#define OPENMW_ESSIMPORT_IMPORTSPLM_H
#include <vector>
#include <components/esm/esmcommon.hpp>
#include <components/esm/util.hpp>
namespace ESM
{
class ESMReader;
}
namespace ESSImport
{
struct SPLM
{
#pragma pack(push)
#pragma pack(1)
struct SPDT // 160 bytes
{
int mType; // 1 = spell, 2 = enchantment, 3 = potion
ESM::NAME32 mId; // base ID of a spell/enchantment/potion
unsigned char mUnknown[4*4];
ESM::NAME32 mCasterId;
ESM::NAME32 mSourceId; // empty for spells
unsigned char mUnknown2[4*11];
};
struct NPDT // 56 bytes
{
ESM::NAME32 mAffectedActorId;
unsigned char mUnknown[4*2];
int mMagnitude;
float mSecondsActive;
unsigned char mUnknown2[4*2];
};
struct INAM // 40 bytes
{
int mUnknown;
unsigned char mUnknown2;
ESM::FIXED_STRING<35> mItemId; // disintegrated item / bound item / item to re-equip after expiration
};
struct CNAM // 36 bytes
{
int mUnknown; // seems to always be 0
ESM::NAME32 mSummonedOrCommandedActor[32];
};
struct VNAM // 4 bytes
{
int mUnknown;
};
#pragma pack(pop)
struct ActiveEffect
{
NPDT mNPDT;
};
struct ActiveSpell
{
int mIndex;
SPDT mSPDT;
std::string mTarget;
std::vector<ActiveEffect> mActiveEffects;
};
std::vector<ActiveSpell> mActiveSpells;
void load(ESM::ESMReader& esm);
};
}
#endif

@ -522,8 +522,7 @@ namespace MWBase
virtual void castSpell (const MWWorld::Ptr& actor) = 0; virtual void castSpell (const MWWorld::Ptr& actor) = 0;
virtual void launchMagicBolt (const std::string& spellId, bool stack, const ESM::EffectList& effects, virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0;
const MWWorld::Ptr& caster, const std::string& sourceName, const osg::Vec3f& fallbackDirection) = 0;
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0; const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0;

@ -200,6 +200,7 @@ namespace MWMechanics
stopWalking(actor, storage); stopWalking(actor, storage);
currentCell = actor.getCell(); currentCell = actor.getCell();
storage.mPopulateAvailableNodes = true; storage.mPopulateAvailableNodes = true;
mStoredInitialActorPosition = false;
} }
mRemainingDuration -= ((duration*MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600); mRemainingDuration -= ((duration*MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600);

@ -202,6 +202,8 @@ static const StateInfo sMovementList[] = {
{ CharState_TurnLeft, "turnleft" }, { CharState_TurnLeft, "turnleft" },
{ CharState_TurnRight, "turnright" }, { CharState_TurnRight, "turnright" },
{ CharState_SwimTurnLeft, "swimturnleft" },
{ CharState_SwimTurnRight, "swimturnright" },
}; };
static const StateInfo *sMovementListEnd = &sMovementList[sizeof(sMovementList)/sizeof(sMovementList[0])]; static const StateInfo *sMovementListEnd = &sMovementList[sizeof(sMovementList)/sizeof(sMovementList[0])];
@ -261,32 +263,61 @@ void CharacterController::refreshHitRecoilAnims()
bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery(); bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery();
bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown(); bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown();
bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock(); bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock();
bool isSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr);
if(mHitState == CharState_None) if(mHitState == CharState_None)
{ {
if ((mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0 if ((mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0
|| mPtr.getClass().getCreatureStats(mPtr).getFatigue().getBase() == 0) || mPtr.getClass().getCreatureStats(mPtr).getFatigue().getBase() == 0)
&& mAnimation->hasAnimation("knockout")) && mAnimation->hasAnimation("knockout"))
{ {
mHitState = CharState_KnockOut; if (isSwimming && mAnimation->hasAnimation("swimknockout"))
mCurrentHit = "knockout"; {
mHitState = CharState_SwimKnockOut;
mCurrentHit = "swimknockout";
}
else
{
mHitState = CharState_KnockOut;
mCurrentHit = "knockout";
}
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, false, 1, "start", "stop", 0.0f, ~0ul); mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, false, 1, "start", "stop", 0.0f, ~0ul);
mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(true); mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(true);
} }
else if(knockdown && mAnimation->hasAnimation("knockdown")) else if(knockdown && mAnimation->hasAnimation("knockdown"))
{ {
mHitState = CharState_KnockDown; if (isSwimming && mAnimation->hasAnimation("swimknockdown"))
mCurrentHit = "knockdown"; {
mHitState = CharState_SwimKnockDown;
mCurrentHit = "swimknockdown";
}
else
{
mHitState = CharState_KnockDown;
mCurrentHit = "knockdown";
}
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0); mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0);
} }
else if (recovery) else if (recovery)
{ {
std::string anim = chooseRandomGroup("hit"); std::string anim = isSwimming ? chooseRandomGroup("swimhit") : chooseRandomGroup("hit");
if (mAnimation->hasAnimation(anim)) if (isSwimming && mAnimation->hasAnimation(anim))
{ {
mHitState = CharState_Hit; mHitState = CharState_SwimHit;
mCurrentHit = anim; mCurrentHit = anim;
mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0); mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0);
} }
else
{
anim = chooseRandomGroup("hit");
if (mAnimation->hasAnimation(anim))
{
mHitState = CharState_Hit;
mCurrentHit = anim;
mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0);
}
}
} }
else if (block && mAnimation->hasAnimation("shield")) else if (block && mAnimation->hasAnimation("shield"))
{ {
@ -298,7 +329,7 @@ void CharacterController::refreshHitRecoilAnims()
} }
// Cancel upper body animations // Cancel upper body animations
if (mHitState == CharState_KnockDown || mHitState == CharState_KnockOut) if (isKnockedOut() || isKnockedDown())
{ {
if (mUpperBodyState > UpperCharState_WeapEquiped) if (mUpperBodyState > UpperCharState_WeapEquiped)
{ {
@ -323,9 +354,9 @@ void CharacterController::refreshHitRecoilAnims()
mPtr.getClass().getCreatureStats(mPtr).setBlock(false); mPtr.getClass().getCreatureStats(mPtr).setBlock(false);
mHitState = CharState_None; mHitState = CharState_None;
} }
else if (mHitState == CharState_KnockOut && mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() > 0) else if (isKnockedOut() && mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() > 0)
{ {
mHitState = CharState_KnockDown; mHitState = isSwimming ? CharState_SwimKnockDown : CharState_KnockDown;
mAnimation->disable(mCurrentHit); mAnimation->disable(mCurrentHit);
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "loop stop", "stop", 0.0f, 0); mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "loop stop", "stop", 0.0f, 0);
} }
@ -551,7 +582,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
// FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update), // FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update),
// the idle animation should be displayed // the idle animation should be displayed
if ((mUpperBodyState != UpperCharState_Nothing if ((mUpperBodyState != UpperCharState_Nothing
|| (mMovementState != CharState_None && mMovementState != CharState_TurnLeft && mMovementState != CharState_TurnRight) || (mMovementState != CharState_None && !isTurning())
|| mHitState != CharState_None) || mHitState != CharState_None)
&& !mPtr.getClass().isBipedal(mPtr)) && !mPtr.getClass().isBipedal(mPtr))
idle = CharState_None; idle = CharState_None;
@ -637,6 +668,12 @@ void CharacterController::playDeath(float startpoint, CharacterState death)
case CharState_SwimDeath: case CharState_SwimDeath:
mCurrentDeath = "swimdeath"; mCurrentDeath = "swimdeath";
break; break;
case CharState_SwimDeathKnockDown:
mCurrentDeath = "swimdeathknockdown";
break;
case CharState_SwimDeathKnockOut:
mCurrentDeath = "swimdeathknockout";
break;
case CharState_DeathKnockDown: case CharState_DeathKnockDown:
mCurrentDeath = "deathknockdown"; mCurrentDeath = "deathknockdown";
break; break;
@ -704,7 +741,15 @@ void CharacterController::playRandomDeath(float startpoint)
MWBase::Environment::get().getWorld()->useDeathCamera(); MWBase::Environment::get().getWorld()->useDeathCamera();
} }
if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath")) if(mHitState == CharState_SwimKnockDown && mAnimation->hasAnimation("swimdeathknockdown"))
{
mDeathState = CharState_SwimDeathKnockDown;
}
else if(mHitState == CharState_SwimKnockOut && mAnimation->hasAnimation("swimdeathknockout"))
{
mDeathState = CharState_SwimDeathKnockOut;
}
else if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath"))
{ {
mDeathState = CharState_SwimDeath; mDeathState = CharState_SwimDeath;
} }
@ -906,16 +951,17 @@ void CharacterController::handleTextKey(const std::string &groupname, const std:
mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust); mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust);
else if(evt.compare(off, len, "hit") == 0) else if(evt.compare(off, len, "hit") == 0)
{ {
if (groupname == "attack1") if (groupname == "attack1" || groupname == "swimattack1")
mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Chop); mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Chop);
else if (groupname == "attack2") else if (groupname == "attack2" || groupname == "swimattack2")
mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Slash); mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Slash);
else if (groupname == "attack3") else if (groupname == "attack3" || groupname == "swimattack3")
mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust); mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust);
else else
mPtr.getClass().hit(mPtr, mAttackStrength); mPtr.getClass().hit(mPtr, mAttackStrength);
} }
else if (!groupname.empty() && groupname.compare(0, groupname.size()-1, "attack") == 0 else if (!groupname.empty()
&& (groupname.compare(0, groupname.size()-1, "attack") == 0 || groupname.compare(0, groupname.size()-1, "swimattack") == 0)
&& evt.compare(off, len, "start") == 0) && evt.compare(off, len, "start") == 0)
{ {
std::multimap<float, std::string>::const_iterator hitKey = key; std::multimap<float, std::string>::const_iterator hitKey = key;
@ -935,11 +981,11 @@ void CharacterController::handleTextKey(const std::string &groupname, const std:
} }
if (!hasHitKey) if (!hasHitKey)
{ {
if (groupname == "attack1") if (groupname == "attack1" || groupname == "swimattack1")
mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Chop); mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Chop);
else if (groupname == "attack2") else if (groupname == "attack2" || groupname == "swimattack2")
mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Slash); mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Slash);
else if (groupname == "attack3") else if (groupname == "attack3" || groupname == "swimattack3")
mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust); mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust);
} }
} }
@ -1095,13 +1141,14 @@ bool CharacterController::updateCreatureState()
} }
if (weapType != WeapType_Spell || !mAnimation->hasAnimation("spellcast")) // Not all creatures have a dedicated spellcast animation if (weapType != WeapType_Spell || !mAnimation->hasAnimation("spellcast")) // Not all creatures have a dedicated spellcast animation
{ {
bool isSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr);
int roll = Misc::Rng::rollDice(3); // [0, 2] int roll = Misc::Rng::rollDice(3); // [0, 2]
if (roll == 0) if (roll == 0)
mCurrentWeapon = "attack1"; mCurrentWeapon = isSwimming && mAnimation->hasAnimation("swimattack1") ? "swimattack1" : "attack1";
else if (roll == 1) else if (roll == 1)
mCurrentWeapon = "attack2"; mCurrentWeapon = isSwimming && mAnimation->hasAnimation("swimattack2") ? "swimattack2" : "attack2";
else else
mCurrentWeapon = "attack3"; mCurrentWeapon = isSwimming && mAnimation->hasAnimation("swimattack3") ? "swimattack3" : "attack3";
} }
if (!mCurrentWeapon.empty()) if (!mCurrentWeapon.empty())
@ -1181,8 +1228,8 @@ bool CharacterController::updateWeaponState()
bool isStillWeapon = weaptype > WeapType_HandToHand && weaptype < WeapType_Spell && bool isStillWeapon = weaptype > WeapType_HandToHand && weaptype < WeapType_Spell &&
mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell; mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell;
if(weaptype != mWeaponType && mHitState != CharState_KnockDown && mHitState != CharState_KnockOut if(weaptype != mWeaponType && !isKnockedOut() &&
&& mHitState != CharState_Hit) !isKnockedDown() && !isRecovery())
{ {
forcestateupdate = true; forcestateupdate = true;
@ -1444,13 +1491,13 @@ bool CharacterController::updateWeaponState()
} }
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && mHitState != CharState_KnockDown) if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown())
mAttackStrength = complete; mAttackStrength = complete;
} }
else else
{ {
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && mHitState != CharState_KnockDown) if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown())
{ {
float attackStrength = complete; float attackStrength = complete;
if (!mPtr.getClass().isNpc()) if (!mPtr.getClass().isNpc())
@ -1488,7 +1535,7 @@ bool CharacterController::updateWeaponState()
complete = 0.f; complete = 0.f;
mUpperBodyState = UpperCharState_MaxAttackToMinHit; mUpperBodyState = UpperCharState_MaxAttackToMinHit;
} }
else if (mHitState == CharState_KnockDown) else if (isKnockedDown())
{ {
if (mUpperBodyState > UpperCharState_WeapEquiped) if (mUpperBodyState > UpperCharState_WeapEquiped)
mUpperBodyState = UpperCharState_WeapEquiped; mUpperBodyState = UpperCharState_WeapEquiped;
@ -1948,22 +1995,22 @@ void CharacterController::update(float duration)
else if(rot.z() != 0.0f && !inwater && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())) else if(rot.z() != 0.0f && !inwater && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson()))
{ {
if(rot.z() > 0.0f) if(rot.z() > 0.0f)
movestate = CharState_TurnRight; movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight;
else if(rot.z() < 0.0f) else if(rot.z() < 0.0f)
movestate = CharState_TurnLeft; movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft;
} }
} }
mTurnAnimationThreshold -= duration; mTurnAnimationThreshold -= duration;
if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft) if (isTurning())
mTurnAnimationThreshold = 0.05f; mTurnAnimationThreshold = 0.05f;
else if (movestate == CharState_None && (mMovementState == CharState_TurnRight || mMovementState == CharState_TurnLeft) else if (movestate == CharState_None && isTurning()
&& mTurnAnimationThreshold > 0) && mTurnAnimationThreshold > 0)
{ {
movestate = mMovementState; movestate = mMovementState;
} }
if(movestate != CharState_None && movestate != CharState_TurnLeft && movestate != CharState_TurnRight) if(movestate != CharState_None && !isTurning())
clearAnimQueue(); clearAnimQueue();
if(mAnimQueue.empty() || inwater || sneak) if(mAnimQueue.empty() || inwater || sneak)
@ -1989,7 +2036,7 @@ void CharacterController::update(float duration)
if (inJump) if (inJump)
mMovementAnimationControlled = false; mMovementAnimationControlled = false;
if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight) if (isTurning())
{ {
if (duration > 0) if (duration > 0)
mAnimation->adjustSpeedMult(mCurrentMovement, std::min(1.5f, std::abs(rot.z()) / duration / static_cast<float>(osg::PI))); mAnimation->adjustSpeedMult(mCurrentMovement, std::min(1.5f, std::abs(rot.z()) / duration / static_cast<float>(osg::PI)));
@ -2002,7 +2049,7 @@ void CharacterController::update(float duration)
if (!mSkipAnim) if (!mSkipAnim)
{ {
if(mHitState != CharState_KnockDown && mHitState != CharState_KnockOut) if(!isKnockedDown() && !isKnockedOut())
{ {
if (rot != osg::Vec3f()) if (rot != osg::Vec3f())
world->rotateObject(mPtr, rot.x(), rot.y(), rot.z(), true); world->rotateObject(mPtr, rot.x(), rot.y(), rot.z(), true);
@ -2359,9 +2406,30 @@ bool CharacterController::isReadyToBlock() const
return updateCarriedLeftVisible(mWeaponType); return updateCarriedLeftVisible(mWeaponType);
} }
bool CharacterController::isKnockedDown() const
{
return mHitState == CharState_KnockDown ||
mHitState == CharState_SwimKnockDown;
}
bool CharacterController::isKnockedOut() const bool CharacterController::isKnockedOut() const
{ {
return mHitState == CharState_KnockOut; return mHitState == CharState_KnockOut ||
mHitState == CharState_SwimKnockOut;
}
bool CharacterController::isTurning() const
{
return mMovementState == CharState_TurnLeft ||
mMovementState == CharState_TurnRight ||
mMovementState == CharState_SwimTurnLeft ||
mMovementState == CharState_SwimTurnRight;
}
bool CharacterController::isRecovery() const
{
return mHitState == CharState_Hit ||
mHitState == CharState_SwimHit;
} }
bool CharacterController::isAttackingOrSpell() const bool CharacterController::isAttackingOrSpell() const

@ -88,6 +88,8 @@ enum CharacterState {
CharState_TurnLeft, CharState_TurnLeft,
CharState_TurnRight, CharState_TurnRight,
CharState_SwimTurnLeft,
CharState_SwimTurnRight,
CharState_Jump, CharState_Jump,
@ -97,12 +99,17 @@ enum CharacterState {
CharState_Death4, CharState_Death4,
CharState_Death5, CharState_Death5,
CharState_SwimDeath, CharState_SwimDeath,
CharState_SwimDeathKnockDown,
CharState_SwimDeathKnockOut,
CharState_DeathKnockDown, CharState_DeathKnockDown,
CharState_DeathKnockOut, CharState_DeathKnockOut,
CharState_Hit, CharState_Hit,
CharState_SwimHit,
CharState_KnockDown, CharState_KnockDown,
CharState_KnockOut, CharState_KnockOut,
CharState_SwimKnockDown,
CharState_SwimKnockOut,
CharState_Block CharState_Block
}; };
@ -265,9 +272,12 @@ public:
bool isAttackPrepairing() const; bool isAttackPrepairing() const;
bool isReadyToBlock() const; bool isReadyToBlock() const;
bool isKnockedDown() const;
bool isKnockedOut() const; bool isKnockedOut() const;
bool isRecovery() const;
bool isSneaking() const; bool isSneaking() const;
bool isRunning() const; bool isRunning() const;
bool isTurning() const;
bool isAttackingOrSpell() const; bool isAttackingOrSpell() const;
void setAttackingOrSpell(bool attackingOrSpell); void setAttackingOrSpell(bool attackingOrSpell);

@ -1038,14 +1038,17 @@ namespace MWMechanics
MWWorld::Ptr victim; MWWorld::Ptr victim;
bool isAllowed = true;
const MWWorld::CellRef* ownerCellRef = &item.getCellRef(); const MWWorld::CellRef* ownerCellRef = &item.getCellRef();
if (!container.isEmpty()) if (!container.isEmpty())
{ {
// Inherit the owner of the container // Inherit the owner of the container
ownerCellRef = &container.getCellRef(); ownerCellRef = &container.getCellRef();
isAllowed = isAllowedToUse(ptr, container, victim);
} }
else else
{ {
isAllowed = isAllowedToUse(ptr, item, victim);
if (!item.getCellRef().hasContentFile()) if (!item.getCellRef().hasContentFile())
{ {
// this is a manually placed item, which means it was already stolen // this is a manually placed item, which means it was already stolen
@ -1053,7 +1056,7 @@ namespace MWMechanics
} }
} }
if (isAllowedToUse(ptr, item, victim)) if (isAllowed)
return; return;
Owner owner; Owner owner;

@ -345,7 +345,7 @@ namespace MWMechanics
{ {
} }
void CastSpell::launchMagicBolt (const ESM::EffectList& effects) void CastSpell::launchMagicBolt ()
{ {
osg::Vec3f fallbackDirection (0,1,0); osg::Vec3f fallbackDirection (0,1,0);
@ -356,8 +356,7 @@ namespace MWMechanics
osg::Vec3f(mTarget.getRefData().getPosition().asVec3())- osg::Vec3f(mTarget.getRefData().getPosition().asVec3())-
osg::Vec3f(mCaster.getRefData().getPosition().asVec3()); osg::Vec3f(mCaster.getRefData().getPosition().asVec3());
MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, effects, MWBase::Environment::get().getWorld()->launchMagicBolt(mId, mCaster, fallbackDirection);
mCaster, mSourceName, fallbackDirection);
} }
void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster,
@ -865,7 +864,7 @@ namespace MWMechanics
inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch); inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch);
if (launchProjectile) if (launchProjectile)
launchMagicBolt(enchantment->mEffects); launchMagicBolt();
else if (isProjectile || !mTarget.isEmpty()) else if (isProjectile || !mTarget.isEmpty())
inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Target); inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Target);
@ -973,7 +972,7 @@ namespace MWMechanics
if (!mTarget.isEmpty()) if (!mTarget.isEmpty())
inflict(mTarget, mCaster, spell->mEffects, ESM::RT_Touch); inflict(mTarget, mCaster, spell->mEffects, ESM::RT_Touch);
launchMagicBolt(spell->mEffects); launchMagicBolt();
return true; return true;
} }

@ -106,7 +106,7 @@ namespace MWMechanics
void playSpellCastingEffects(const std::string &spellid); void playSpellCastingEffects(const std::string &spellid);
/// Launch a bolt with the given effects. /// Launch a bolt with the given effects.
void launchMagicBolt (const ESM::EffectList& effects); void launchMagicBolt ();
/// @note \a target can be any type of object, not just actors. /// @note \a target can be any type of object, not just actors.
/// @note \a caster can be any type of object, or even an empty object. /// @note \a caster can be any type of object, or even an empty object.

@ -1,5 +1,6 @@
#include "physicssystem.hpp" #include "physicssystem.hpp"
#include <iostream>
#include <stdexcept> #include <stdexcept>
#include <osg/Group> #include <osg/Group>
@ -683,6 +684,7 @@ namespace MWPhysics
, mWaterHeight(0) , mWaterHeight(0)
, mWaterEnabled(false) , mWaterEnabled(false)
, mParentNode(parentNode) , mParentNode(parentNode)
, mPhysicsDt(1.f / 60.f)
{ {
mResourceSystem->addResourceManager(mShapeManager.get()); mResourceSystem->addResourceManager(mShapeManager.get());
@ -695,6 +697,20 @@ namespace MWPhysics
// Don't update AABBs of all objects every frame. Most objects in MW are static, so we don't need this. // Don't update AABBs of all objects every frame. Most objects in MW are static, so we don't need this.
// Should a "static" object ever be moved, we have to update its AABB manually using DynamicsWorld::updateSingleAabb. // Should a "static" object ever be moved, we have to update its AABB manually using DynamicsWorld::updateSingleAabb.
mCollisionWorld->setForceUpdateAllAabbs(false); mCollisionWorld->setForceUpdateAllAabbs(false);
// Check if a user decided to override a physics system FPS
const char* env = getenv("OPENMW_PHYSICS_FPS");
if (env)
{
std::string str(env);
float physFramerate = std::atof(env);
if (physFramerate > 0)
{
mPhysicsDt = 1.f / physFramerate;
std::cerr << "Warning: physics framerate was overriden (a new value is " << physFramerate << ")." << std::endl;
}
}
} }
PhysicsSystem::~PhysicsSystem() PhysicsSystem::~PhysicsSystem()
@ -1357,13 +1373,12 @@ namespace MWPhysics
mMovementResults.clear(); mMovementResults.clear();
mTimeAccum += dt; mTimeAccum += dt;
const float physicsDt = 1.f/60.0f;
const int maxAllowedSteps = 20; const int maxAllowedSteps = 20;
int numSteps = mTimeAccum / (physicsDt); int numSteps = mTimeAccum / (mPhysicsDt);
numSteps = std::min(numSteps, maxAllowedSteps); numSteps = std::min(numSteps, maxAllowedSteps);
mTimeAccum -= numSteps * physicsDt; mTimeAccum -= numSteps * mPhysicsDt;
if (numSteps) if (numSteps)
{ {
@ -1412,7 +1427,7 @@ namespace MWPhysics
bool positionChanged = false; bool positionChanged = false;
for (int i=0; i<numSteps; ++i) for (int i=0; i<numSteps; ++i)
{ {
position = MovementSolver::move(position, physicActor->getPtr(), physicActor, iter->second, physicsDt, position = MovementSolver::move(position, physicActor->getPtr(), physicActor, iter->second, mPhysicsDt,
flying, waterlevel, slowFall, mCollisionWorld, mStandingCollisions); flying, waterlevel, slowFall, mCollisionWorld, mStandingCollisions);
if (position != physicActor->getPosition()) if (position != physicActor->getPosition())
positionChanged = true; positionChanged = true;
@ -1421,7 +1436,7 @@ namespace MWPhysics
if (positionChanged) if (positionChanged)
mCollisionWorld->updateSingleAabb(physicActor->getCollisionObject()); mCollisionWorld->updateSingleAabb(physicActor->getCollisionObject());
float interpolationFactor = mTimeAccum / physicsDt; float interpolationFactor = mTimeAccum / mPhysicsDt;
osg::Vec3f interpolated = position * interpolationFactor + physicActor->getPreviousPosition() * (1.f - interpolationFactor); osg::Vec3f interpolated = position * interpolationFactor + physicActor->getPreviousPosition() * (1.f - interpolationFactor);
float heightDiff = position.z() - oldHeight; float heightDiff = position.z() - oldHeight;

@ -220,6 +220,8 @@ namespace MWPhysics
osg::ref_ptr<osg::Group> mParentNode; osg::ref_ptr<osg::Group> mParentNode;
float mPhysicsDt;
PhysicsSystem (const PhysicsSystem&); PhysicsSystem (const PhysicsSystem&);
PhysicsSystem& operator= (const PhysicsSystem&); PhysicsSystem& operator= (const PhysicsSystem&);
}; };

@ -279,11 +279,14 @@ namespace MWRender
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
mFieldOfView = Settings::Manager::getFloat("field of view", "Camera"); mFieldOfView = Settings::Manager::getFloat("field of view", "Camera");
mFirstPersonFieldOfView = Settings::Manager::getFloat("first person field of view", "Camera"); mFirstPersonFieldOfView = Settings::Manager::getFloat("first person field of view", "Camera");
updateProjectionMatrix();
mStateUpdater->setFogEnd(mViewDistance); mStateUpdater->setFogEnd(mViewDistance);
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip)); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance)); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
mUniformNear = mRootNode->getOrCreateStateSet()->getUniform("near");
mUniformFar = mRootNode->getOrCreateStateSet()->getUniform("far");
updateProjectionMatrix();
} }
RenderingManager::~RenderingManager() RenderingManager::~RenderingManager()
@ -889,6 +892,9 @@ namespace MWRender
if (mFieldOfViewOverridden) if (mFieldOfViewOverridden)
fov = mFieldOfViewOverride; fov = mFieldOfViewOverride;
mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance); mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance);
mUniformNear->set(mNearClip);
mUniformFar->set(mViewDistance);
} }
void RenderingManager::updateTextureFiltering() void RenderingManager::updateTextureFiltering()

@ -83,6 +83,9 @@ namespace MWRender
SceneUtil::UnrefQueue* getUnrefQueue(); SceneUtil::UnrefQueue* getUnrefQueue();
Terrain::World* getTerrain(); Terrain::World* getTerrain();
osg::Uniform* mUniformNear;
osg::Uniform* mUniformFar;
void preloadCommonAssets(); void preloadCommonAssets();
double getReferenceTime() const; double getReferenceTime() const;

@ -336,8 +336,7 @@ public:
void setWaterLevel(float waterLevel) void setWaterLevel(float waterLevel)
{ {
setViewMatrix(osg::Matrix::translate(0,0,-waterLevel) * osg::Matrix::scale(1,1,-1) * osg::Matrix::translate(0,0,waterLevel)); setViewMatrix(osg::Matrix::scale(1,1,-1) * osg::Matrix::translate(0,0,2 * waterLevel));
mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0,0,1), osg::Vec3d(0,0,waterLevel))); mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0,0,1), osg::Vec3d(0,0,waterLevel)));
} }

@ -1,6 +1,7 @@
#include "projectilemanager.hpp" #include "projectilemanager.hpp"
#include <iomanip> #include <iomanip>
#include <iostream>
#include <osg/PositionAttitudeTransform> #include <osg/PositionAttitudeTransform>
@ -41,13 +42,29 @@
namespace namespace
{ {
ESM::EffectList getMagicBoltData(std::vector<std::string>& projectileIDs, std::vector<std::string>& sounds, float& speed, std::string& texture, const ESM::EffectList& effects) ESM::EffectList getMagicBoltData(std::vector<std::string>& projectileIDs, std::vector<std::string>& sounds, float& speed, std::string& texture, std::string& sourceName, const std::string& id)
{ {
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
const ESM::EffectList* effects;
if (const ESM::Spell* spell = esmStore.get<ESM::Spell>().search(id)) // check if it's a spell
{
sourceName = spell->mName;
effects = &spell->mEffects;
}
else // check if it's an enchanted item
{
MWWorld::ManualRef ref(esmStore, id);
MWWorld::Ptr ptr = ref.getPtr();
const ESM::Enchantment* ench = esmStore.get<ESM::Enchantment>().find(ptr.getClass().getEnchantment(ptr));
sourceName = ptr.getClass().getName(ptr);
effects = &ench->mEffects;
}
int count = 0; int count = 0;
speed = 0.0f; speed = 0.0f;
ESM::EffectList projectileEffects; ESM::EffectList projectileEffects;
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin()); for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects->mList.begin());
iter!=effects.mList.end(); ++iter) iter!=effects->mList.end(); ++iter)
{ {
const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find ( const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
iter->mEffectID); iter->mEffectID);
@ -82,14 +99,14 @@ namespace
if (projectileEffects.mList.size() == 1) if (projectileEffects.mList.size() == 1)
{ {
const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find ( const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
effects.mList.begin()->mEffectID); effects->mList.begin()->mEffectID);
texture = magicEffect->mParticle; texture = magicEffect->mParticle;
} }
if (projectileEffects.mList.size() > 1) // insert a VFX_Multiple projectile if there are multiple projectile effects if (projectileEffects.mList.size() > 1) // insert a VFX_Multiple projectile if there are multiple projectile effects
{ {
std::ostringstream ID; std::ostringstream ID;
ID << "VFX_Multiple" << effects.mList.size(); ID << "VFX_Multiple" << effects->mList.size();
std::vector<std::string>::iterator it; std::vector<std::string>::iterator it;
it = projectileIDs.begin(); it = projectileIDs.begin();
it = projectileIDs.insert(it, ID.str()); it = projectileIDs.insert(it, ID.str());
@ -132,6 +149,7 @@ namespace MWWorld
, mResourceSystem(resourceSystem) , mResourceSystem(resourceSystem)
, mRendering(rendering) , mRendering(rendering)
, mPhysics(physics) , mPhysics(physics)
, mCleanupTimer(0.0f)
{ {
} }
@ -235,8 +253,7 @@ namespace MWWorld
state.mEffectAnimationTime->addTime(duration); state.mEffectAnimationTime->addTime(duration);
} }
void ProjectileManager::launchMagicBolt(const std::string &spellId, bool stack, const ESM::EffectList &effects, const Ptr &caster, void ProjectileManager::launchMagicBolt(const std::string &spellId, const Ptr &caster, const osg::Vec3f& fallbackDirection)
const std::string &sourceName, const osg::Vec3f& fallbackDirection)
{ {
osg::Vec3f pos = caster.getRefData().getPosition().asVec3(); osg::Vec3f pos = caster.getRefData().getPosition().asVec3();
if (caster.getClass().isActor()) if (caster.getClass().isActor())
@ -257,18 +274,16 @@ namespace MWWorld
orient.makeRotate(osg::Vec3f(0,1,0), osg::Vec3f(fallbackDirection)); orient.makeRotate(osg::Vec3f(0,1,0), osg::Vec3f(fallbackDirection));
MagicBoltState state; MagicBoltState state;
state.mSourceName = sourceName;
state.mSpellId = spellId; state.mSpellId = spellId;
state.mCasterHandle = caster; state.mCasterHandle = caster;
if (caster.getClass().isActor()) if (caster.getClass().isActor())
state.mActorId = caster.getClass().getCreatureStats(caster).getActorId(); state.mActorId = caster.getClass().getCreatureStats(caster).getActorId();
else else
state.mActorId = -1; state.mActorId = -1;
state.mStack = stack;
std::string texture = ""; std::string texture = "";
state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, texture, effects); state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, texture, state.mSourceName, state.mSpellId);
// Non-projectile should have been removed by getMagicBoltData // Non-projectile should have been removed by getMagicBoltData
if (state.mEffects.mList.empty()) if (state.mEffects.mList.empty())
@ -277,7 +292,7 @@ namespace MWWorld
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0)); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0));
MWWorld::Ptr ptr = ref.getPtr(); MWWorld::Ptr ptr = ref.getPtr();
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(effects); osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, true, lightDiffuseColor, texture); createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, true, lightDiffuseColor, texture);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
@ -312,10 +327,49 @@ namespace MWWorld
void ProjectileManager::update(float dt) void ProjectileManager::update(float dt)
{ {
periodicCleanup(dt);
moveProjectiles(dt); moveProjectiles(dt);
moveMagicBolts(dt); moveMagicBolts(dt);
} }
void ProjectileManager::periodicCleanup(float dt)
{
mCleanupTimer -= dt;
if (mCleanupTimer <= 0.0f)
{
mCleanupTimer = 2.0f;
auto isCleanable = [](const ProjectileManager::State& state) -> bool
{
const float farawayThreshold = 72000.0f;
osg::Vec3 playerPos = MWMechanics::getPlayer().getRefData().getPosition().asVec3();
return (state.mNode->getPosition() - playerPos).length2() >= farawayThreshold*farawayThreshold;
};
for (std::vector<ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end();)
{
if (isCleanable(*it))
{
cleanupProjectile(*it);
it = mProjectiles.erase(it);
}
else
++it;
}
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();)
{
if (isCleanable(*it))
{
cleanupMagicBolt(*it);
it = mMagicBolts.erase(it);
}
else
++it;
}
}
}
void ProjectileManager::moveMagicBolts(float duration) void ProjectileManager::moveMagicBolts(float duration)
{ {
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();) for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();)
@ -364,7 +418,7 @@ namespace MWWorld
cast.mHitPosition = pos; cast.mHitPosition = pos;
cast.mId = it->mSpellId; cast.mId = it->mSpellId;
cast.mSourceName = it->mSourceName; cast.mSourceName = it->mSourceName;
cast.mStack = it->mStack; cast.mStack = false;
cast.inflict(result.mHitObject, caster, it->mEffects, ESM::RT_Target, false, true); cast.inflict(result.mHitObject, caster, it->mEffects, ESM::RT_Target, false, true);
} }
} }
@ -454,20 +508,30 @@ namespace MWWorld
} }
} }
void ProjectileManager::cleanupProjectile(ProjectileManager::ProjectileState& state)
{
mParent->removeChild(state.mNode);
}
void ProjectileManager::cleanupMagicBolt(ProjectileManager::MagicBoltState& state)
{
mParent->removeChild(state.mNode);
for (size_t soundIter = 0; soundIter != state.mSounds.size(); soundIter++)
{
MWBase::Environment::get().getSoundManager()->stopSound(state.mSounds.at(soundIter));
}
}
void ProjectileManager::clear() void ProjectileManager::clear()
{ {
for (std::vector<ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) for (std::vector<ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it)
{ {
mParent->removeChild(it->mNode); cleanupProjectile(*it);
} }
mProjectiles.clear(); mProjectiles.clear();
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it) for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it)
{ {
mParent->removeChild(it->mNode); cleanupMagicBolt(*it);
for (size_t soundIter = 0; soundIter != it->mSounds.size(); soundIter++)
{
MWBase::Environment::get().getSoundManager()->stopSound(it->mSounds.at(soundIter));
}
} }
mMagicBolts.clear(); mMagicBolts.clear();
} }
@ -504,11 +568,7 @@ namespace MWWorld
state.mActorId = it->mActorId; state.mActorId = it->mActorId;
state.mSpellId = it->mSpellId; state.mSpellId = it->mSpellId;
state.mEffects = it->mEffects;
state.mSound = it->mSoundIds.at(0);
state.mSourceName = it->mSourceName;
state.mSpeed = it->mSpeed; state.mSpeed = it->mSpeed;
state.mStack = it->mStack;
state.save(writer); state.save(writer);
@ -553,13 +613,21 @@ namespace MWWorld
esm.load(reader); esm.load(reader);
MagicBoltState state; MagicBoltState state;
state.mSourceName = esm.mSourceName;
state.mIdMagic.push_back(esm.mId); state.mIdMagic.push_back(esm.mId);
state.mSpellId = esm.mSpellId; state.mSpellId = esm.mSpellId;
state.mActorId = esm.mActorId; state.mActorId = esm.mActorId;
state.mStack = esm.mStack;
std::string texture = ""; std::string texture = "";
state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, texture, esm.mEffects);
try
{
state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, texture, state.mSourceName, state.mSpellId);
}
catch(...)
{
std::cerr << "Warning: Failed to recreate magic projectile from saved data (id \"" << state.mSpellId << "\" no longer exists?)" << std::endl;
return true;
}
state.mSpeed = esm.mSpeed; // speed is derived from non-projectile effects as well as state.mSpeed = esm.mSpeed; // speed is derived from non-projectile effects as well as
// projectile effects, so we can't calculate it from the save // projectile effects, so we can't calculate it from the save
// file's effect list, which is already trimmed of non-projectile // file's effect list, which is already trimmed of non-projectile
@ -577,7 +645,7 @@ namespace MWWorld
return true; return true;
} }
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(esm.mEffects); osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true, true, lightDiffuseColor, texture); createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true, true, lightDiffuseColor, texture);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();

@ -49,8 +49,7 @@ namespace MWWorld
MWRender::RenderingManager* rendering, MWPhysics::PhysicsSystem* physics); MWRender::RenderingManager* rendering, MWPhysics::PhysicsSystem* physics);
/// If caster is an actor, the actor's facing orientation is used. Otherwise fallbackDirection is used. /// If caster is an actor, the actor's facing orientation is used. Otherwise fallbackDirection is used.
void launchMagicBolt (const std::string &spellId, bool stack, const ESM::EffectList& effects, void launchMagicBolt (const std::string &spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection);
const MWWorld::Ptr& caster, const std::string& sourceName, const osg::Vec3f& fallbackDirection);
void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
const osg::Vec3f& pos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength); const osg::Vec3f& pos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength);
@ -69,6 +68,7 @@ namespace MWWorld
Resource::ResourceSystem* mResourceSystem; Resource::ResourceSystem* mResourceSystem;
MWRender::RenderingManager* mRendering; MWRender::RenderingManager* mRendering;
MWPhysics::PhysicsSystem* mPhysics; MWPhysics::PhysicsSystem* mPhysics;
float mCleanupTimer;
struct State struct State
{ {
@ -101,8 +101,6 @@ namespace MWWorld
float mSpeed; float mSpeed;
bool mStack;
std::vector<MWBase::Sound*> mSounds; std::vector<MWBase::Sound*> mSounds;
std::vector<std::string> mSoundIds; std::vector<std::string> mSoundIds;
}; };
@ -119,6 +117,10 @@ namespace MWWorld
std::vector<MagicBoltState> mMagicBolts; std::vector<MagicBoltState> mMagicBolts;
std::vector<ProjectileState> mProjectiles; std::vector<ProjectileState> mProjectiles;
void cleanupProjectile(ProjectileState& state);
void cleanupMagicBolt(MagicBoltState& state);
void periodicCleanup(float dt);
void moveProjectiles(float dt); void moveProjectiles(float dt);
void moveMagicBolts(float dt); void moveMagicBolts(float dt);

@ -2223,6 +2223,9 @@ namespace MWWorld
bool World::isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f &pos) const bool World::isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f &pos) const
{ {
if (!cell)
return false;
if (!(cell->getCell()->hasWater())) { if (!(cell->getCell()->hasWater())) {
return false; return false;
} }
@ -2989,10 +2992,9 @@ namespace MWWorld
mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength); mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength);
} }
void World::launchMagicBolt (const std::string &spellId, bool stack, const ESM::EffectList& effects, void World::launchMagicBolt (const std::string &spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection)
const MWWorld::Ptr& caster, const std::string& sourceName, const osg::Vec3f& fallbackDirection)
{ {
mProjectileManager->launchMagicBolt(spellId, stack, effects, caster, sourceName, fallbackDirection); mProjectileManager->launchMagicBolt(spellId, caster, fallbackDirection);
} }
const std::vector<std::string>& World::getContentFiles() const const std::vector<std::string>& World::getContentFiles() const

@ -635,8 +635,7 @@ namespace MWWorld
*/ */
virtual void castSpell (const MWWorld::Ptr& actor); virtual void castSpell (const MWWorld::Ptr& actor);
virtual void launchMagicBolt (const std::string& spellId, bool stack, const ESM::EffectList& effects, virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) override;
const MWWorld::Ptr& caster, const std::string& sourceName, const osg::Vec3f& fallbackDirection);
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength); const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength);

@ -27,11 +27,7 @@ namespace ESM
BaseProjectileState::save(esm); BaseProjectileState::save(esm);
esm.writeHNString ("SPEL", mSpellId); esm.writeHNString ("SPEL", mSpellId);
esm.writeHNString ("SRCN", mSourceName);
mEffects.save(esm);
esm.writeHNT ("SPED", mSpeed); esm.writeHNT ("SPED", mSpeed);
esm.writeHNT ("STCK", mStack);
esm.writeHNString ("SOUN", mSound);
} }
void MagicBoltState::load(ESMReader &esm) void MagicBoltState::load(ESMReader &esm)
@ -39,11 +35,14 @@ namespace ESM
BaseProjectileState::load(esm); BaseProjectileState::load(esm);
mSpellId = esm.getHNString("SPEL"); mSpellId = esm.getHNString("SPEL");
mSourceName = esm.getHNString ("SRCN"); if (esm.isNextSub("SRCN")) // for backwards compatibility
mEffects.load(esm); esm.skipHSub();
ESM::EffectList().load(esm); // for backwards compatibility
esm.getHNT (mSpeed, "SPED"); esm.getHNT (mSpeed, "SPED");
esm.getHNT (mStack, "STCK"); if (esm.isNextSub("STCK")) // for backwards compatibility
mSound = esm.getHNString ("SOUN"); esm.skipHSub();
if (esm.isNextSub("SOUN")) // for backwards compatibility
esm.skipHSub();
} }
void ProjectileState::save(ESMWriter &esm) const void ProjectileState::save(ESMWriter &esm) const

@ -31,11 +31,7 @@ namespace ESM
struct MagicBoltState : public BaseProjectileState struct MagicBoltState : public BaseProjectileState
{ {
std::string mSpellId; std::string mSpellId;
std::string mSourceName;
ESM::EffectList mEffects;
float mSpeed; float mSpeed;
bool mStack;
std::string mSound;
void load (ESMReader &esm); void load (ESMReader &esm);
void save (ESMWriter &esm) const; void save (ESMWriter &esm) const;

@ -8,7 +8,7 @@
namespace namespace
{ {
// somewhat arbitrary though 64KB buffers didn't seem to improve performance any // somewhat arbitrary though 64KB buffers didn't seem to improve performance any
const size_t sBufferSize = 4096; const size_t sBufferSize = 8192;
} }
namespace Files namespace Files

@ -6,138 +6,8 @@ namespace Nif
{ {
//Private functions //Private functions
uint8_t NIFStream::read_byte()
{
uint8_t byte;
inp->read((char*)&byte, 1);
return byte;
}
uint16_t NIFStream::read_le16()
{
uint8_t buffer[2];
inp->read((char*)buffer, 2);
return buffer[0] | (buffer[1]<<8);
}
uint32_t NIFStream::read_le32()
{
uint8_t buffer[4];
inp->read((char*)buffer, 4);
return buffer[0] | (buffer[1]<<8) | (buffer[2]<<16) | (buffer[3]<<24);
}
float NIFStream::read_le32f()
{
union {
uint32_t i;
float f;
} u = { read_le32() };
return u.f;
}
//Public functions
osg::Vec2f NIFStream::getVector2()
{
osg::Vec2f vec;
for(size_t i = 0;i < 2;i++)
vec._v[i] = getFloat();
return vec;
}
osg::Vec3f NIFStream::getVector3()
{
osg::Vec3f vec;
for(size_t i = 0;i < 3;i++)
vec._v[i] = getFloat();
return vec;
}
osg::Vec4f NIFStream::getVector4()
{
osg::Vec4f vec;
for(size_t i = 0;i < 4;i++)
vec._v[i] = getFloat();
return vec;
}
Matrix3 NIFStream::getMatrix3()
{
Matrix3 mat;
for(size_t i = 0;i < 3;i++)
{
for(size_t j = 0;j < 3;j++)
mat.mValues[i][j] = getFloat();
}
return mat;
}
osg::Quat NIFStream::getQuaternion()
{
osg::Quat quat;
quat.w() = getFloat();
quat.x() = getFloat();
quat.y() = getFloat();
quat.z() = getFloat();
return quat;
}
Transformation NIFStream::getTrafo()
{
Transformation t;
t.pos = getVector3();
t.rotation = getMatrix3();
t.scale = getFloat();
return t;
}
std::string NIFStream::getString(size_t length) //Public functions
{
std::vector<char> str (length+1, 0);
inp->read(&str[0], length);
return &str[0];
}
std::string NIFStream::getString()
{
size_t size = read_le32();
return getString(size);
}
std::string NIFStream::getVersionString()
{
std::string result;
std::getline(*inp, result);
return result;
}
void NIFStream::getUShorts(std::vector<unsigned short> &vec, size_t size)
{
vec.resize(size);
for(size_t i = 0;i < vec.size();i++)
vec[i] = getUShort();
}
void NIFStream::getFloats(std::vector<float> &vec, size_t size)
{
vec.resize(size);
for(size_t i = 0;i < vec.size();i++)
vec[i] = getFloat();
}
void NIFStream::getVector2s(std::vector<osg::Vec2f> &vec, size_t size)
{
vec.resize(size);
for(size_t i = 0;i < vec.size();i++)
vec[i] = getVector2();
}
void NIFStream::getVector3s(std::vector<osg::Vec3f> &vec, size_t size)
{
vec.resize(size);
for(size_t i = 0;i < vec.size();i++)
vec[i] = getVector3();
}
void NIFStream::getVector4s(std::vector<osg::Vec4f> &vec, size_t size)
{
vec.resize(size);
for(size_t i = 0;i < vec.size();i++)
vec[i] = getVector4();
}
void NIFStream::getQuaternions(std::vector<osg::Quat> &quat, size_t size)
{
quat.resize(size);
for(size_t i = 0;i < quat.size();i++)
quat[i] = getQuaternion();
}
} }

@ -21,16 +21,70 @@ namespace Nif
class NIFFile; class NIFFile;
/*
readLittleEndianBufferOfType: This template should only be used with non POD data types
*/
template <uint32_t numInstances, typename T, typename IntegerT> inline void readLittleEndianBufferOfType(Files::IStreamPtr &pIStream, T* dest)
{
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86)
pIStream->read((char*)dest, numInstances * sizeof(T));
#else
uint8_t* destByteBuffer = (uint8_t*)dest;
pIStream->read((char*)dest, numInstances * sizeof(T));
/*
Due to the loop iterations being known at compile time,
this nested loop will most likely be unrolled
For example, for 2 instances of a 4 byte data type, you should get the below result
*/
union {
IntegerT i;
T t;
} u;
for (uint32_t i = 0; i < numInstances; i++)
{
u = { 0 };
for (uint32_t byte = 0; byte < sizeof(T); byte++)
u.i |= (((IntegerT)destByteBuffer[i * sizeof(T) + byte]) << (byte * 8));
dest[i] = u.t;
}
#endif
}
/*
readLittleEndianDynamicBufferOfType: This template should only be used with non POD data types
*/
template <typename T, typename IntegerT> inline void readLittleEndianDynamicBufferOfType(Files::IStreamPtr &pIStream, T* dest, uint32_t numInstances)
{
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86)
pIStream->read((char*)dest, numInstances * sizeof(T));
#else
uint8_t* destByteBuffer = (uint8_t*)dest;
pIStream->read((char*)dest, numInstances * sizeof(T));
union {
IntegerT i;
T t;
} u;
for (uint32_t i = 0; i < numInstances; i++)
{
u.i = 0;
for (uint32_t byte = 0; byte < sizeof(T); byte++)
u.i |= ((IntegerT)destByteBuffer[i * sizeof(T) + byte]) << (byte * 8);
dest[i] = u.t;
}
#endif
}
template<typename type, typename IntegerT> type inline readLittleEndianType(Files::IStreamPtr &pIStream)
{
type val;
readLittleEndianBufferOfType<1,type,IntegerT>(pIStream, (type*)&val);
return val;
}
class NIFStream { class NIFStream {
/// Input stream /// Input stream
Files::IStreamPtr inp; Files::IStreamPtr inp;
uint8_t read_byte();
uint16_t read_le16();
uint32_t read_le32();
float read_le32f();
public: public:
NIFFile * const file; NIFFile * const file;
@ -39,33 +93,117 @@ public:
void skip(size_t size) { inp->ignore(size); } void skip(size_t size) { inp->ignore(size); }
char getChar() { return read_byte(); } char getChar()
short getShort() { return read_le16(); } {
unsigned short getUShort() { return read_le16(); } return readLittleEndianType<char,char>(inp);
int getInt() { return read_le32(); } }
unsigned int getUInt() { return read_le32(); } short getShort()
float getFloat() { return read_le32f(); } {
return readLittleEndianType<short,short>(inp);
osg::Vec2f getVector2(); }
osg::Vec3f getVector3(); unsigned short getUShort()
osg::Vec4f getVector4(); {
Matrix3 getMatrix3(); return readLittleEndianType<unsigned short,unsigned short>(inp);
osg::Quat getQuaternion(); }
Transformation getTrafo(); int getInt()
{
return readLittleEndianType<int,int>(inp);
}
unsigned int getUInt()
{
return readLittleEndianType<unsigned int,unsigned int>(inp);
}
float getFloat()
{
return readLittleEndianType<float,uint32_t>(inp);
}
osg::Vec2f getVector2() {
osg::Vec2f vec;
readLittleEndianBufferOfType<2,float,uint32_t>(inp, (float*)&vec._v[0]);
return vec;
}
osg::Vec3f getVector3() {
osg::Vec3f vec;
readLittleEndianBufferOfType<3, float,uint32_t>(inp, (float*)&vec._v[0]);
return vec;
}
osg::Vec4f getVector4() {
osg::Vec4f vec;
readLittleEndianBufferOfType<4, float,uint32_t>(inp, (float*)&vec._v[0]);
return vec;
}
Matrix3 getMatrix3() {
Matrix3 mat;
readLittleEndianBufferOfType<9, float,uint32_t>(inp, (float*)&mat.mValues);
return mat;
}
osg::Quat getQuaternion() {
float f[4];
readLittleEndianBufferOfType<4, float,uint32_t>(inp, (float*)&f);
osg::Quat quat;
quat.w() = f[0];
quat.x() = f[1];
quat.y() = f[2];
quat.z() = f[3];
return quat;
}
Transformation getTrafo() {
Transformation t;
t.pos = getVector3();
t.rotation = getMatrix3();
t.scale = getFloat();
return t;
}
///Read in a string of the given length ///Read in a string of the given length
std::string getString(size_t length); std::string getString(size_t length) {
std::vector<char> str(length + 1, 0);
inp->read(&str[0], length);
return &str[0];
}
///Read in a string of the length specified in the file ///Read in a string of the length specified in the file
std::string getString(); std::string getString() {
size_t size = readLittleEndianType<uint32_t,uint32_t>(inp);
return getString(size);
}
///This is special since the version string doesn't start with a number, and ends with "\n" ///This is special since the version string doesn't start with a number, and ends with "\n"
std::string getVersionString(); std::string getVersionString() {
std::string result;
void getUShorts(std::vector<unsigned short> &vec, size_t size); std::getline(*inp, result);
void getFloats(std::vector<float> &vec, size_t size); return result;
void getVector2s(std::vector<osg::Vec2f> &vec, size_t size); }
void getVector3s(std::vector<osg::Vec3f> &vec, size_t size);
void getVector4s(std::vector<osg::Vec4f> &vec, size_t size); void getUShorts(std::vector<unsigned short> &vec, size_t size) {
void getQuaternions(std::vector<osg::Quat> &quat, size_t size); vec.resize(size);
readLittleEndianDynamicBufferOfType<unsigned short,unsigned short>(inp, &vec.front(), size);
}
void getFloats(std::vector<float> &vec, size_t size) {
vec.resize(size);
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, &vec.front(), size);
}
void getVector2s(std::vector<osg::Vec2f> &vec, size_t size) {
vec.resize(size);
/* The packed storage of each Vec2f is 2 floats exactly */
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp,(float*) &vec.front(), size*2);
}
void getVector3s(std::vector<osg::Vec3f> &vec, size_t size) {
vec.resize(size);
/* The packed storage of each Vec3f is 3 floats exactly */
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, (float*) &vec.front(), size*3);
}
void getVector4s(std::vector<osg::Vec4f> &vec, size_t size) {
vec.resize(size);
/* The packed storage of each Vec4f is 4 floats exactly */
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, (float*) &vec.front(), size*4);
}
void getQuaternions(std::vector<osg::Quat> &quat, size_t size) {
quat.resize(size);
for (size_t i = 0;i < quat.size();i++)
quat[i] = getQuaternion();
}
}; };
} }

@ -6,7 +6,7 @@
// tweakables -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // tweakables -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
const float VISIBILITY = 1200.0; // how far you can look through water const float VISIBILITY = 2.5;
const float BIG_WAVES_X = 0.1; // strength of big waves const float BIG_WAVES_X = 0.1; // strength of big waves
const float BIG_WAVES_Y = 0.1; const float BIG_WAVES_Y = 0.1;
@ -31,6 +31,8 @@ const vec3 SUN_EXT = vec3(0.45, 0.55, 0.68); //sunlight extinction
const float SPEC_HARDNESS = 256.0; // specular highlights hardness const float SPEC_HARDNESS = 256.0; // specular highlights hardness
const float BUMP_SUPPRESS_DEPTH = 0.3; // at what water depth bumpmap will be supressed for reflections and refractions (prevents artifacts at shores)
const vec2 WIND_DIR = vec2(0.5f, -0.8f); const vec2 WIND_DIR = vec2(0.5f, -0.8f);
const float WIND_SPEED = 0.2f; const float WIND_SPEED = 0.2f;
@ -56,9 +58,9 @@ float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta)
return result; return result;
} }
varying vec3 screenCoordsPassthrough; varying vec3 screenCoordsPassthrough;
varying vec4 position; varying vec4 position;
varying float depthPassthrough; varying float depthPassthrough;
uniform sampler2D normalMap; uniform sampler2D normalMap;
@ -74,8 +76,18 @@ uniform float near;
uniform float far; uniform float far;
uniform vec3 nodePosition; uniform vec3 nodePosition;
float frustumDepth;
float linearizeDepth(float depth) // takes <0,1> non-linear depth value and returns <0,1> linearized value
{
float z_n = 2.0 * depth - 1.0;
depth = 2.0 * near * far / (far + near - z_n * frustumDepth);
return depth / frustumDepth;
}
void main(void) void main(void)
{ {
frustumDepth = abs(far - near);
vec3 worldPos = position.xyz + nodePosition.xyz; vec3 worldPos = position.xyz + nodePosition.xyz;
vec2 UV = worldPos.xy / (8192.0*5.0) * 3.0; vec2 UV = worldPos.xy / (8192.0*5.0) * 3.0;
UV.y *= -1.0; UV.y *= -1.0;
@ -147,32 +159,37 @@ void main(void)
fresnel = clamp(fresnel, 0.0, 1.0); fresnel = clamp(fresnel, 0.0, 1.0);
#if REFRACTION
float normalization = frustumDepth / 1000;
float depthSample = linearizeDepth(texture2D(refractionDepthMap,screenCoords).x) * normalization;
float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-(normal.xy*REFR_BUMP)).x) * normalization;
float surfaceDepth = linearizeDepth(gl_FragCoord.z) * normalization;
float realWaterDepth = depthSample - surfaceDepth; // undistorted water depth in view direction, independent of frustum
float shore = clamp(realWaterDepth / BUMP_SUPPRESS_DEPTH,0,1);
#else
float shore = 1.0;
#endif
// reflection // reflection
vec3 reflection = texture2D(reflectionMap, screenCoords+(normal.xy*REFL_BUMP)).rgb; vec3 reflection = texture2D(reflectionMap, screenCoords+(normal.xy*REFL_BUMP*shore)).rgb;
// refraction // refraction
#if REFRACTION #if REFRACTION
vec3 refraction = texture2D(refractionMap, screenCoords-(normal.xy*REFR_BUMP)).rgb; vec3 refraction = texture2D(refractionMap, screenCoords-(normal.xy*REFR_BUMP*shore)).rgb;
// brighten up the refraction underwater // brighten up the refraction underwater
refraction = (cameraPos.z < 0.0) ? clamp(refraction * 1.5, 0.0, 1.0) : refraction; refraction = (cameraPos.z < 0.0) ? clamp(refraction * 1.5, 0.0, 1.0) : refraction;
#endif #endif
// specular // specular
vec3 R = reflect(vVec, normal); vec3 R = reflect(vVec, normal);
float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS) * shadow; float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS) * shadow;
vec3 waterColor = WATER_COLOR; vec3 waterColor = WATER_COLOR;
waterColor = waterColor * length(gl_LightModel.ambient.xyz); waterColor = waterColor * length(gl_LightModel.ambient.xyz);
#if REFRACTION
float refractionDepth = texture2D(refractionDepthMap, screenCoords-(normal.xy*REFR_BUMP)).x;
float z_n = 2.0 * refractionDepth - 1.0;
refractionDepth = 2.0 * near * far / (far + near - z_n * (far - near));
float waterDepth = refractionDepth - depthPassthrough;
#if REFRACTION
if (cameraPos.z > 0.0) if (cameraPos.z > 0.0)
refraction = mix(refraction, waterColor, clamp(waterDepth/VISIBILITY, 0.0, 1.0)); refraction = mix(refraction, waterColor, clamp(depthSampleDistorted/VISIBILITY, 0.0, 1.0));
gl_FragData[0].xyz = mix( mix(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * gl_LightSource[0].specular.xyz; gl_FragData[0].xyz = mix( mix(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * gl_LightSource[0].specular.xyz;
#else #else

Loading…
Cancel
Save