forked from mirror/openmw-tes3mp
Merge pull request #301 from TES3MP/master
Add master commits up to 28 Sep 2017
This commit is contained in:
commit
841a4f90c1
30 changed files with 654 additions and 287 deletions
|
@ -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(),
|
||||||
|
[&pnam](const SPLM::ActiveSpell& spell) -> bool { return spell.mIndex == pnam.mSplmIndex; });
|
||||||
|
|
||||||
|
if (it == mContext->mActiveSpells.end())
|
||||||
|
{
|
||||||
|
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);
|
out.save(esm);
|
||||||
esm.endRecord(ESM::REC_MPRJ);*/
|
esm.endRecord(ESM::REC_MPRJ);
|
||||||
|
|
||||||
std::cerr << "Warning: Skipped conversion for magic projectile \"" << pnam.mArrowId.toString() << "\" (not implemented)" << std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
43
apps/essimporter/importsplm.cpp
Normal file
43
apps/essimporter/importsplm.cpp
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
81
apps/essimporter/importsplm.h
Normal file
81
apps/essimporter/importsplm.h
Normal file
|
@ -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
|
//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)
|
|
||||||
{
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
unsigned short getUShort()
|
||||||
|
{
|
||||||
|
return readLittleEndianType<unsigned short,unsigned short>(inp);
|
||||||
|
}
|
||||||
|
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 getVector2() {
|
||||||
osg::Vec3f getVector3();
|
osg::Vec2f vec;
|
||||||
osg::Vec4f getVector4();
|
readLittleEndianBufferOfType<2,float,uint32_t>(inp, (float*)&vec._v[0]);
|
||||||
Matrix3 getMatrix3();
|
return vec;
|
||||||
osg::Quat getQuaternion();
|
}
|
||||||
Transformation getTrafo();
|
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) {
|
||||||
///Read in a string of the length specified in the file
|
std::vector<char> str(length + 1, 0);
|
||||||
std::string getString();
|
|
||||||
///This is special since the version string doesn't start with a number, and ends with "\n"
|
|
||||||
std::string getVersionString();
|
|
||||||
|
|
||||||
void getUShorts(std::vector<unsigned short> &vec, size_t size);
|
inp->read(&str[0], length);
|
||||||
void getFloats(std::vector<float> &vec, size_t size);
|
|
||||||
void getVector2s(std::vector<osg::Vec2f> &vec, size_t size);
|
return &str[0];
|
||||||
void getVector3s(std::vector<osg::Vec3f> &vec, size_t size);
|
}
|
||||||
void getVector4s(std::vector<osg::Vec4f> &vec, size_t size);
|
///Read in a string of the length specified in the file
|
||||||
void getQuaternions(std::vector<osg::Quat> &quat, size_t size);
|
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"
|
||||||
|
std::string getVersionString() {
|
||||||
|
std::string result;
|
||||||
|
std::getline(*inp, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getUShorts(std::vector<unsigned short> &vec, 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
|
#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 (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…
Reference in a new issue