mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 19:19:56 +00:00
Merge remote-tracking branch 'scrawl/master'
This commit is contained in:
commit
eb6e1576be
32 changed files with 419 additions and 229 deletions
|
@ -969,6 +969,9 @@ namespace MWClass
|
|||
|
||||
float Npc::getJump(const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
if(getEncumbrance(ptr) > getCapacity(ptr))
|
||||
return 0.f;
|
||||
|
||||
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
||||
const GMST& gmst = getGmst();
|
||||
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
|
||||
|
|
|
@ -533,9 +533,11 @@ namespace MWGui
|
|||
|
||||
if (mGoodbye)
|
||||
{
|
||||
Goodbye* link = new Goodbye();
|
||||
mLinks.push_back(link);
|
||||
std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString();
|
||||
BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, linkNormal, linkHot, linkActive,
|
||||
TypesetBook::InteractiveId(mLinks.back()));
|
||||
TypesetBook::InteractiveId(link));
|
||||
typesetter->lineBreak();
|
||||
typesetter->write(questionStyle, to_utf8_span(goodbye.c_str()));
|
||||
}
|
||||
|
@ -654,7 +656,6 @@ namespace MWGui
|
|||
|
||||
void DialogueWindow::goodbye()
|
||||
{
|
||||
mLinks.push_back(new Goodbye());
|
||||
mGoodbye = true;
|
||||
mEnabled = false;
|
||||
updateHistory();
|
||||
|
|
|
@ -128,11 +128,17 @@ namespace MWGui
|
|||
setRaceId(proto.mRace);
|
||||
recountParts();
|
||||
|
||||
std::string index = proto.mHead.substr(proto.mHead.size() - 2, 2);
|
||||
mFaceIndex = boost::lexical_cast<int>(index) - 1;
|
||||
for (unsigned int i=0; i<mAvailableHeads.size(); ++i)
|
||||
{
|
||||
if (mAvailableHeads[i] == proto.mHead)
|
||||
mFaceIndex = i;
|
||||
}
|
||||
|
||||
index = proto.mHair.substr(proto.mHair.size() - 2, 2);
|
||||
mHairIndex = boost::lexical_cast<int>(index) - 1;
|
||||
for (unsigned int i=0; i<mAvailableHairs.size(); ++i)
|
||||
{
|
||||
if (mAvailableHairs[i] == proto.mHair)
|
||||
mHairIndex = i;
|
||||
}
|
||||
|
||||
mPreviewImage->setImageTexture (textureName);
|
||||
|
||||
|
@ -307,7 +313,15 @@ namespace MWGui
|
|||
record.mHead = mAvailableHeads[mFaceIndex];
|
||||
record.mHair = mAvailableHairs[mHairIndex];
|
||||
|
||||
try
|
||||
{
|
||||
mPreview->setPrototype(record);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Error creating preview: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
mPreviewDirty = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -371,6 +371,7 @@ namespace MWInput
|
|||
{
|
||||
mPlayer->setUpDown (1);
|
||||
triedToMove = true;
|
||||
mOverencumberedMessageDelay = 0.f;
|
||||
}
|
||||
|
||||
if (mAlwaysRunActive)
|
||||
|
@ -623,6 +624,8 @@ namespace MWInput
|
|||
if (arg.zrel && mControlSwitch["playerviewswitch"] && mControlSwitch["playercontrols"]) //Check to make sure you are allowed to zoomout and there is a change
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->changeVanityModeScale(arg.zrel);
|
||||
|
||||
if (Settings::Manager::getBool("allow third person zoom", "Input"))
|
||||
MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "../mwworld/actionequip.hpp"
|
||||
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
#include "../mwmechanics/spellcasting.hpp"
|
||||
|
||||
#include <components/esm/loadench.hpp>
|
||||
#include <components/esm/loadmgef.hpp>
|
||||
|
@ -166,6 +167,9 @@ namespace MWMechanics
|
|||
{
|
||||
const CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
|
||||
if (MWMechanics::getSpellSuccessChance(spell, actor) == 0)
|
||||
return 0.f;
|
||||
|
||||
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
||||
return 0.f;
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ namespace MWMechanics
|
|||
/** \return If the actor has arrived at his destination **/
|
||||
bool pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration);
|
||||
|
||||
// TODO: all this does not belong here, move into temporary storage
|
||||
PathFinder mPathFinder;
|
||||
ObstacleCheck mObstacleCheck;
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ namespace MWMechanics
|
|||
|
||||
unsigned short mPlayedIdle;
|
||||
|
||||
PathFinder mPathFinder;
|
||||
|
||||
AiWanderStorage():
|
||||
mTargetAngle(0),
|
||||
mRotate(false),
|
||||
|
@ -211,9 +213,9 @@ namespace MWMechanics
|
|||
// Are we there yet?
|
||||
bool& chooseAction = storage.mChooseAction;
|
||||
if(walking &&
|
||||
mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
|
||||
storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
|
||||
{
|
||||
stopWalking(actor);
|
||||
stopWalking(actor, storage);
|
||||
moveNow = false;
|
||||
walking = false;
|
||||
chooseAction = true;
|
||||
|
@ -225,7 +227,7 @@ namespace MWMechanics
|
|||
if(walking) // have not yet reached the destination
|
||||
{
|
||||
// turn towards the next point in mPath
|
||||
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
|
||||
zTurn(actor, Ogre::Degree(storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
||||
|
||||
// Returns true if evasive action needs to be taken
|
||||
|
@ -236,9 +238,9 @@ namespace MWMechanics
|
|||
{
|
||||
// remove allowed points then select another random destination
|
||||
mTrimCurrentNode = true;
|
||||
trimAllowedNodes(mAllowedNodes, mPathFinder);
|
||||
trimAllowedNodes(mAllowedNodes, storage.mPathFinder);
|
||||
mObstacleCheck.clear();
|
||||
mPathFinder.clearPath();
|
||||
storage.mPathFinder.clearPath();
|
||||
walking = false;
|
||||
moveNow = true;
|
||||
}
|
||||
|
@ -249,7 +251,7 @@ namespace MWMechanics
|
|||
actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f;
|
||||
// change the angle a bit, too
|
||||
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
|
||||
zTurn(actor, Ogre::Degree(storage.mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
|
||||
}
|
||||
mStuckCount++; // TODO: maybe no longer needed
|
||||
}
|
||||
|
@ -260,7 +262,7 @@ namespace MWMechanics
|
|||
//std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl;
|
||||
mObstacleCheck.clear();
|
||||
|
||||
stopWalking(actor);
|
||||
stopWalking(actor, storage);
|
||||
moveNow = false;
|
||||
walking = false;
|
||||
chooseAction = true;
|
||||
|
@ -280,6 +282,58 @@ namespace MWMechanics
|
|||
rotate = false;
|
||||
}
|
||||
|
||||
// Check if idle animation finished
|
||||
short unsigned& playedIdle = storage.mPlayedIdle;
|
||||
GreetingState& greetingState = storage.mSaidGreeting;
|
||||
if(idleNow && !checkIdle(actor, playedIdle) && (greetingState == Greet_Done || greetingState == Greet_None))
|
||||
{
|
||||
playedIdle = 0;
|
||||
idleNow = false;
|
||||
chooseAction = true;
|
||||
}
|
||||
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
|
||||
if(chooseAction)
|
||||
{
|
||||
playedIdle = 0;
|
||||
getRandomIdle(playedIdle); // NOTE: sets mPlayedIdle with a random selection
|
||||
|
||||
if(!playedIdle && mDistance)
|
||||
{
|
||||
chooseAction = false;
|
||||
moveNow = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander:
|
||||
MWWorld::TimeStamp currentTime = world->getTimeStamp();
|
||||
mStartTime = currentTime;
|
||||
playIdle(actor, playedIdle);
|
||||
chooseAction = false;
|
||||
idleNow = true;
|
||||
|
||||
// Play idle voiced dialogue entries randomly
|
||||
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
|
||||
if (hello > 0)
|
||||
{
|
||||
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
||||
// Don't bother if the player is out of hearing range
|
||||
static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore()
|
||||
.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat();
|
||||
|
||||
// Only say Idle voices when player is in LOS
|
||||
// A bit counterintuitive, likely vanilla did this to reduce the appearance of
|
||||
// voices going through walls?
|
||||
if (roll < fVoiceIdleOdds && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500
|
||||
&& MWBase::Environment::get().getWorld()->getLOS(player, actor))
|
||||
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float& lastReaction = storage.mReaction;
|
||||
lastReaction += duration;
|
||||
if(lastReaction < REACTION_INTERVAL)
|
||||
|
@ -291,7 +345,6 @@ namespace MWMechanics
|
|||
|
||||
// NOTE: everything below get updated every REACTION_INTERVAL seconds
|
||||
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
if(mDuration)
|
||||
{
|
||||
// End package if duration is complete or mid-night hits:
|
||||
|
@ -300,7 +353,7 @@ namespace MWMechanics
|
|||
{
|
||||
if(!mRepeat)
|
||||
{
|
||||
stopWalking(actor);
|
||||
stopWalking(actor, storage);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -310,7 +363,7 @@ namespace MWMechanics
|
|||
{
|
||||
if(!mRepeat)
|
||||
{
|
||||
stopWalking(actor);
|
||||
stopWalking(actor, storage);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -411,7 +464,7 @@ namespace MWMechanics
|
|||
chooseAction = false;
|
||||
idleNow = false;
|
||||
|
||||
if (!mPathFinder.isPathConstructed())
|
||||
if (!storage.mPathFinder.isPathConstructed())
|
||||
{
|
||||
Ogre::Vector3 destNodePos = mReturnPosition;
|
||||
|
||||
|
@ -427,9 +480,9 @@ namespace MWMechanics
|
|||
start.mZ = pos.pos[2];
|
||||
|
||||
// don't take shortcuts for wandering
|
||||
mPathFinder.buildPath(start, dest, actor.getCell(), false);
|
||||
storage.mPathFinder.buildPath(start, dest, actor.getCell(), false);
|
||||
|
||||
if(mPathFinder.isPathConstructed())
|
||||
if(storage.mPathFinder.isPathConstructed())
|
||||
{
|
||||
moveNow = false;
|
||||
walking = true;
|
||||
|
@ -437,48 +490,6 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
AiWander::GreetingState& greetingState = storage.mSaidGreeting;
|
||||
short unsigned& playedIdle = storage.mPlayedIdle;
|
||||
if(chooseAction)
|
||||
{
|
||||
playedIdle = 0;
|
||||
getRandomIdle(playedIdle); // NOTE: sets mPlayedIdle with a random selection
|
||||
|
||||
if(!playedIdle && mDistance)
|
||||
{
|
||||
chooseAction = false;
|
||||
moveNow = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander:
|
||||
MWWorld::TimeStamp currentTime = world->getTimeStamp();
|
||||
mStartTime = currentTime;
|
||||
playIdle(actor, playedIdle);
|
||||
chooseAction = false;
|
||||
idleNow = true;
|
||||
|
||||
// Play idle voiced dialogue entries randomly
|
||||
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
|
||||
if (hello > 0)
|
||||
{
|
||||
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
||||
// Don't bother if the player is out of hearing range
|
||||
static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore()
|
||||
.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat();
|
||||
|
||||
// Only say Idle voices when player is in LOS
|
||||
// A bit counterintuitive, likely vanilla did this to reduce the appearance of
|
||||
// voices going through walls?
|
||||
if (roll < fVoiceIdleOdds && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500
|
||||
&& MWBase::Environment::get().getWorld()->getLOS(player, actor))
|
||||
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow interrupting a walking actor to trigger a greeting
|
||||
if(idleNow || walking)
|
||||
{
|
||||
|
@ -517,7 +528,7 @@ namespace MWMechanics
|
|||
|
||||
if(walking)
|
||||
{
|
||||
stopWalking(actor);
|
||||
stopWalking(actor, storage);
|
||||
moveNow = false;
|
||||
walking = false;
|
||||
mObstacleCheck.clear();
|
||||
|
@ -554,20 +565,12 @@ namespace MWMechanics
|
|||
if (playerDistSqr >= fGreetDistanceReset*fGreetDistanceReset)
|
||||
greetingState = Greet_None;
|
||||
}
|
||||
|
||||
// Check if idle animation finished
|
||||
if(!checkIdle(actor, playedIdle) && (playerDistSqr > helloDistance*helloDistance || greetingState == MWMechanics::AiWander::Greet_Done))
|
||||
{
|
||||
playedIdle = 0;
|
||||
idleNow = false;
|
||||
chooseAction = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(moveNow && mDistance)
|
||||
{
|
||||
// Construct a new path if there isn't one
|
||||
if(!mPathFinder.isPathConstructed())
|
||||
if(!storage.mPathFinder.isPathConstructed())
|
||||
{
|
||||
assert(mAllowedNodes.size());
|
||||
unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size());
|
||||
|
@ -589,16 +592,16 @@ namespace MWMechanics
|
|||
start.mZ = pos.pos[2];
|
||||
|
||||
// don't take shortcuts for wandering
|
||||
mPathFinder.buildPath(start, dest, actor.getCell(), false);
|
||||
storage.mPathFinder.buildPath(start, dest, actor.getCell(), false);
|
||||
|
||||
if(mPathFinder.isPathConstructed())
|
||||
if(storage.mPathFinder.isPathConstructed())
|
||||
{
|
||||
// buildPath inserts dest in case it is not a pathgraph point
|
||||
// index which is a duplicate for AiWander. However below code
|
||||
// does not work since getPath() returns a copy of path not a
|
||||
// reference
|
||||
//if(mPathFinder.getPathSize() > 1)
|
||||
//mPathFinder.getPath().pop_back();
|
||||
//if(storage.mPathFinder.getPathSize() > 1)
|
||||
//storage.mPathFinder.getPath().pop_back();
|
||||
|
||||
// Remove this node as an option and add back the previously used node (stops NPC from picking the same node):
|
||||
ESM::Pathgrid::Point temp = mAllowedNodes[randNode];
|
||||
|
@ -653,9 +656,9 @@ namespace MWMechanics
|
|||
return TypeIdWander;
|
||||
}
|
||||
|
||||
void AiWander::stopWalking(const MWWorld::Ptr& actor)
|
||||
void AiWander::stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
||||
{
|
||||
mPathFinder.clearPath();
|
||||
storage.mPathFinder.clearPath();
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace MWMechanics
|
|||
{
|
||||
|
||||
|
||||
struct AiWanderStorage;
|
||||
|
||||
/// \brief Causes the Actor to wander within a specified range
|
||||
class AiWander : public AiPackage
|
||||
{
|
||||
|
@ -65,7 +67,7 @@ namespace MWMechanics
|
|||
// NOTE: mDistance and mDuration must be set already
|
||||
void init();
|
||||
|
||||
void stopWalking(const MWWorld::Ptr& actor);
|
||||
void stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
||||
void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
||||
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
||||
void getRandomIdle(unsigned short& playedIdle);
|
||||
|
@ -101,7 +103,6 @@ namespace MWMechanics
|
|||
void trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes,
|
||||
const PathFinder& pathfinder);
|
||||
|
||||
// PathFinder mPathFinder;
|
||||
|
||||
// ObstacleCheck mObstacleCheck;
|
||||
float mDoorCheckDuration;
|
||||
|
|
|
@ -1414,6 +1414,8 @@ void CharacterController::update(float duration)
|
|||
{
|
||||
// Started a jump.
|
||||
float z = cls.getJump(mPtr);
|
||||
if (z > 0)
|
||||
{
|
||||
if(vec.x == 0 && vec.y == 0)
|
||||
vec = Ogre::Vector3(0.0f, 0.0f, z);
|
||||
else
|
||||
|
@ -1438,6 +1440,7 @@ void CharacterController::update(float duration)
|
|||
fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);
|
||||
cls.getCreatureStats(mPtr).setFatigue(fatigue);
|
||||
}
|
||||
}
|
||||
else if(mJumpState == JumpState_InAir)
|
||||
{
|
||||
forcestateupdate = true;
|
||||
|
|
|
@ -147,6 +147,11 @@ namespace MWMechanics
|
|||
|
||||
if (value != currentValue)
|
||||
{
|
||||
if(!mIsWerewolf)
|
||||
mAttributes[index] = value;
|
||||
else
|
||||
mWerewolfAttributes[index] = value;
|
||||
|
||||
if (index == ESM::Attribute::Intelligence)
|
||||
mRecalcMagicka = true;
|
||||
else if (index == ESM::Attribute::Strength ||
|
||||
|
@ -164,11 +169,6 @@ namespace MWMechanics
|
|||
setFatigue(fatigue);
|
||||
}
|
||||
}
|
||||
|
||||
if(!mIsWerewolf)
|
||||
mAttributes[index] = value;
|
||||
else
|
||||
mWerewolfAttributes[index] = value;
|
||||
}
|
||||
|
||||
void CreatureStats::setHealth(const DynamicStat<float> &value)
|
||||
|
|
|
@ -930,20 +930,6 @@ namespace MWMechanics
|
|||
|
||||
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||
|
||||
// What amount of alarm did this crime generate?
|
||||
int alarm = 0;
|
||||
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmTresspass")->getInt();
|
||||
else if (type == OT_Pickpocket)
|
||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmPickPocket")->getInt();
|
||||
else if (type == OT_Assault)
|
||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmAttack")->getInt();
|
||||
else if (type == OT_Murder)
|
||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmKilling")->getInt();
|
||||
else if (type == OT_Theft)
|
||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmStealing")->getInt();
|
||||
|
||||
bool reported = false;
|
||||
|
||||
// Find all the actors within the alarm radius
|
||||
std::vector<MWWorld::Ptr> neighbors;
|
||||
|
@ -960,6 +946,8 @@ namespace MWMechanics
|
|||
bool victimAware = false;
|
||||
|
||||
// Find actors who directly witnessed the crime
|
||||
bool crimeSeen = false;
|
||||
bool reported = false;
|
||||
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
|
||||
{
|
||||
if (*it == player)
|
||||
|
@ -987,15 +975,17 @@ namespace MWMechanics
|
|||
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
|
||||
continue;
|
||||
|
||||
crimeSeen = true;
|
||||
}
|
||||
|
||||
// Will the witness report the crime?
|
||||
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm)
|
||||
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100)
|
||||
{
|
||||
reported = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reported)
|
||||
if (crimeSeen && reported)
|
||||
reportCrime(player, victim, type, arg);
|
||||
else if (victimAware && !victim.isEmpty() && type == OT_Assault)
|
||||
startCombat(victim, player);
|
||||
|
|
|
@ -574,7 +574,8 @@ float Animation::getVelocity(const std::string &groupname) const
|
|||
|
||||
static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone)
|
||||
{
|
||||
if(skelsrc->hasBone(bone->getName()))
|
||||
if(bone->getName() != " " // really should be != "", but see workaround in skeleton.cpp for empty node names
|
||||
&& skelsrc->hasBone(bone->getName()))
|
||||
{
|
||||
Ogre::Bone *srcbone = skelsrc->getBone(bone->getName());
|
||||
if(!srcbone->getParent() || !bone->getParent())
|
||||
|
|
|
@ -115,8 +115,8 @@ namespace MWRender
|
|||
|
||||
void CharacterPreview::rebuild()
|
||||
{
|
||||
assert(mAnimation);
|
||||
delete mAnimation;
|
||||
mAnimation = NULL;
|
||||
mAnimation = new NpcAnimation(mCharacter, mNode,
|
||||
0, true, true, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal));
|
||||
|
||||
|
@ -187,6 +187,9 @@ namespace MWRender
|
|||
|
||||
void InventoryPreview::update()
|
||||
{
|
||||
if (!mAnimation)
|
||||
return;
|
||||
|
||||
mAnimation->updateParts();
|
||||
|
||||
MWWorld::InventoryStore &inv = mCharacter.getClass().getInventoryStore(mCharacter);
|
||||
|
|
|
@ -60,6 +60,21 @@ std::string getVampireHead(const std::string& race, bool female)
|
|||
return "meshes\\" + sVampireMapping[thisCombination]->mModel;
|
||||
}
|
||||
|
||||
bool isSkinned (NifOgre::ObjectScenePtr scene)
|
||||
{
|
||||
if (scene->mSkelBase == NULL)
|
||||
return false;
|
||||
for(size_t j = 0; j < scene->mEntities.size(); j++)
|
||||
{
|
||||
Ogre::Entity *ent = scene->mEntities[j];
|
||||
if(scene->mSkelBase != ent && ent->hasSkeleton())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -611,10 +626,11 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
|
|||
for(;ctrl != mObjectParts[i]->mControllers.end();++ctrl)
|
||||
ctrl->update();
|
||||
|
||||
Ogre::Entity *ent = mObjectParts[i]->mSkelBase;
|
||||
if(!ent) continue;
|
||||
updateSkeletonInstance(baseinst, ent->getSkeleton());
|
||||
ent->getAllAnimationStates()->_notifyDirty();
|
||||
if (!isSkinned(mObjectParts[i]))
|
||||
continue;
|
||||
|
||||
updateSkeletonInstance(baseinst, mObjectParts[i]->mSkelBase->getSkeleton());
|
||||
mObjectParts[i]->mSkelBase->getAllAnimationStates()->_notifyDirty();
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -660,7 +676,15 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g
|
|||
removeIndividualPart(type);
|
||||
mPartslots[type] = group;
|
||||
mPartPriorities[type] = priority;
|
||||
try
|
||||
{
|
||||
mObjectParts[type] = insertBoundedPart(mesh, group, sPartList.at(type), enchantedGlow, glowColor);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Error adding NPC part: " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mSoundsDisabled)
|
||||
{
|
||||
|
@ -697,6 +721,7 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g
|
|||
}
|
||||
}
|
||||
|
||||
if (isSkinned(mObjectParts[type]))
|
||||
updateSkeletonInstance(mSkelBase->getSkeleton(), skel);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,41 @@
|
|||
using namespace MWRender;
|
||||
using namespace Ogre;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void setAlpha (NifOgre::ObjectScenePtr scene, Ogre::MovableObject* movable, float alpha)
|
||||
{
|
||||
Ogre::MaterialPtr mat = scene->mMaterialControllerMgr.getWritableMaterial(movable);
|
||||
Ogre::Material::TechniqueIterator techs = mat->getTechniqueIterator();
|
||||
while(techs.hasMoreElements())
|
||||
{
|
||||
Ogre::Technique *tech = techs.getNext();
|
||||
Ogre::Technique::PassIterator passes = tech->getPassIterator();
|
||||
while(passes.hasMoreElements())
|
||||
{
|
||||
Ogre::Pass *pass = passes.getNext();
|
||||
Ogre::ColourValue diffuse = pass->getDiffuse();
|
||||
diffuse.a = alpha;
|
||||
pass->setDiffuse(diffuse);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void setAlpha (NifOgre::ObjectScenePtr scene, float alpha)
|
||||
{
|
||||
for(size_t i = 0; i < scene->mParticles.size(); ++i)
|
||||
setAlpha(scene, scene->mParticles[i], alpha);
|
||||
for(size_t i = 0; i < scene->mEntities.size(); ++i)
|
||||
{
|
||||
if (scene->mEntities[i] != scene->mSkelBase)
|
||||
setAlpha(scene, scene->mEntities[i], alpha);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BillboardObject::BillboardObject( const String& textureName,
|
||||
const float initialSize,
|
||||
const Vector3& position,
|
||||
|
@ -660,6 +695,11 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
|
|||
mSun->setVisibility(weather.mGlareView * strength);
|
||||
|
||||
mAtmosphereNight->setVisible(weather.mNight && mEnabled);
|
||||
|
||||
if (mParticle.get())
|
||||
setAlpha(mParticle, weather.mEffectFade);
|
||||
for (std::map<Ogre::SceneNode*, NifOgre::ObjectScenePtr>::iterator it = mRainModels.begin(); it != mRainModels.end(); ++it)
|
||||
setAlpha(it->second, weather.mEffectFade);
|
||||
}
|
||||
|
||||
void SkyManager::setGlare(const float glare)
|
||||
|
|
|
@ -210,6 +210,12 @@ namespace MWScript
|
|||
{
|
||||
bool state = MWBase::Environment::get().getWindowManager()->toggleGui();
|
||||
runtime.getContext().report(state ? "GUI -> On" : "GUI -> Off");
|
||||
|
||||
if (!state)
|
||||
{
|
||||
while (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_None) // don't use isGuiMode, or we get an infinite loop for modal message boxes!
|
||||
MWBase::Environment::get().getWindowManager()->popGuiMode();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -319,12 +319,11 @@ namespace MWScript
|
|||
ptr = MWWorld::Ptr(ptr.getBase(), store);
|
||||
float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees();
|
||||
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
|
||||
if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity
|
||||
{
|
||||
ax = ax/60.;
|
||||
ay = ay/60.;
|
||||
// Note that you must specify ZRot in minutes (1 degree = 60 minutes; north = 0, east = 5400, south = 10800, west = 16200)
|
||||
// except for when you position the player, then degrees must be used.
|
||||
// See "Morrowind Scripting for Dummies (9th Edition)" pages 50 and 54 for reference.
|
||||
if(ptr != MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
zRot = zRot/60.;
|
||||
}
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot);
|
||||
|
||||
ptr.getClass().adjustPosition(ptr, false);
|
||||
|
@ -378,12 +377,11 @@ namespace MWScript
|
|||
|
||||
float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees();
|
||||
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
|
||||
if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity
|
||||
{
|
||||
ax = ax/60.;
|
||||
ay = ay/60.;
|
||||
// Note that you must specify ZRot in minutes (1 degree = 60 minutes; north = 0, east = 5400, south = 10800, west = 16200)
|
||||
// except for when you position the player, then degrees must be used.
|
||||
// See "Morrowind Scripting for Dummies (9th Edition)" pages 50 and 54 for reference.
|
||||
if(ptr != MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
zRot = zRot/60.;
|
||||
}
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot);
|
||||
ptr.getClass().adjustPosition(ptr, false);
|
||||
}
|
||||
|
|
|
@ -876,8 +876,36 @@ namespace MWWorld
|
|||
public:
|
||||
|
||||
void load(ESM::ESMReader &esm, const std::string &id) {
|
||||
mStatic.push_back(ESM::Pathgrid());
|
||||
mStatic.back().load(esm);
|
||||
|
||||
ESM::Pathgrid pathgrid;
|
||||
pathgrid.load(esm);
|
||||
|
||||
// Try to overwrite existing record
|
||||
// Can't use search() because we aren't sorted yet
|
||||
if (!pathgrid.mCell.empty())
|
||||
{
|
||||
for (std::vector<ESM::Pathgrid>::iterator it = mStatic.begin(); it != mStatic.end(); ++it)
|
||||
{
|
||||
if ((*it).mCell == pathgrid.mCell)
|
||||
{
|
||||
(*it) = pathgrid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (std::vector<ESM::Pathgrid>::iterator it = mStatic.begin(); it != mStatic.end(); ++it)
|
||||
{
|
||||
if ((*it).mData.mX == pathgrid.mData.mX && (*it).mData.mY == pathgrid.mData.mY)
|
||||
{
|
||||
(*it) = pathgrid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mStatic.push_back(pathgrid);
|
||||
}
|
||||
|
||||
size_t getSize() const {
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
||||
#include "../mwsound/sound.hpp"
|
||||
|
||||
#include "../mwrender/renderingmanager.hpp"
|
||||
|
||||
#include "player.hpp"
|
||||
|
@ -152,12 +154,12 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa
|
|||
setFallbackWeather(foggy,"foggy");
|
||||
|
||||
Weather thunderstorm;
|
||||
thunderstorm.mRainLoopSoundID = "rain heavy";
|
||||
thunderstorm.mAmbientLoopSoundID = "rain heavy";
|
||||
thunderstorm.mRainEffect = "meshes\\raindrop.nif";
|
||||
setFallbackWeather(thunderstorm,"thunderstorm");
|
||||
|
||||
Weather rain;
|
||||
rain.mRainLoopSoundID = "rain";
|
||||
rain.mAmbientLoopSoundID = "rain";
|
||||
rain.mRainEffect = "meshes\\raindrop.nif";
|
||||
setFallbackWeather(rain,"rain");
|
||||
|
||||
|
@ -186,7 +188,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa
|
|||
|
||||
WeatherManager::~WeatherManager()
|
||||
{
|
||||
stopSounds(true);
|
||||
stopSounds();
|
||||
}
|
||||
|
||||
void WeatherManager::setWeather(const String& weather, bool instant)
|
||||
|
@ -228,6 +230,8 @@ void WeatherManager::setResult(const String& weatherType)
|
|||
mResult.mCloudSpeed = current.mCloudSpeed;
|
||||
mResult.mGlareView = current.mGlareView;
|
||||
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
|
||||
mResult.mAmbientSoundVolume = 1.f;
|
||||
mResult.mEffectFade = 1.f;
|
||||
mResult.mSunColor = current.mSunDiscSunsetColor;
|
||||
|
||||
mResult.mIsStorm = current.mIsStorm;
|
||||
|
@ -341,11 +345,30 @@ void WeatherManager::transition(float factor)
|
|||
|
||||
mResult.mNight = current.mNight;
|
||||
|
||||
if (factor < 0.5)
|
||||
{
|
||||
mResult.mIsStorm = current.mIsStorm;
|
||||
mResult.mParticleEffect = current.mParticleEffect;
|
||||
mResult.mRainEffect = current.mRainEffect;
|
||||
mResult.mParticleEffect = current.mParticleEffect;
|
||||
mResult.mRainSpeed = current.mRainSpeed;
|
||||
mResult.mRainFrequency = current.mRainFrequency;
|
||||
mResult.mAmbientSoundVolume = 1-(factor*2);
|
||||
mResult.mEffectFade = mResult.mAmbientSoundVolume;
|
||||
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
|
||||
}
|
||||
else
|
||||
{
|
||||
mResult.mIsStorm = other.mIsStorm;
|
||||
mResult.mParticleEffect = other.mParticleEffect;
|
||||
mResult.mRainEffect = other.mRainEffect;
|
||||
mResult.mParticleEffect = other.mParticleEffect;
|
||||
mResult.mRainSpeed = other.mRainSpeed;
|
||||
mResult.mRainFrequency = other.mRainFrequency;
|
||||
mResult.mAmbientSoundVolume = 2*(factor-0.5);
|
||||
mResult.mEffectFade = mResult.mAmbientSoundVolume;
|
||||
mResult.mAmbientLoopSoundID = other.mAmbientLoopSoundID;
|
||||
}
|
||||
}
|
||||
|
||||
void WeatherManager::update(float duration, bool paused)
|
||||
|
@ -361,7 +384,7 @@ void WeatherManager::update(float duration, bool paused)
|
|||
{
|
||||
mRendering->skyDisable();
|
||||
mRendering->getSkyManager()->setLightningStrength(0.f);
|
||||
stopSounds(true);
|
||||
stopSounds();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -541,40 +564,25 @@ void WeatherManager::update(float duration, bool paused)
|
|||
mRendering->getSkyManager()->setWeather(mResult);
|
||||
|
||||
// Play sounds
|
||||
if (mNextWeather == "")
|
||||
if (mPlayingSoundID != mResult.mAmbientLoopSoundID)
|
||||
{
|
||||
std::string ambientSnd = mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID;
|
||||
if (!ambientSnd.empty() && std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), ambientSnd) == mSoundsPlaying.end())
|
||||
{
|
||||
mSoundsPlaying.push_back(ambientSnd);
|
||||
MWBase::Environment::get().getSoundManager()->playSound(ambientSnd, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
|
||||
}
|
||||
stopSounds();
|
||||
if (!mResult.mAmbientLoopSoundID.empty())
|
||||
mAmbientSound = MWBase::Environment::get().getSoundManager()->playSound(mResult.mAmbientLoopSoundID, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
|
||||
|
||||
std::string rainSnd = mWeatherSettings[mCurrentWeather].mRainLoopSoundID;
|
||||
if (!rainSnd.empty() && std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), rainSnd) == mSoundsPlaying.end())
|
||||
{
|
||||
mSoundsPlaying.push_back(rainSnd);
|
||||
MWBase::Environment::get().getSoundManager()->playSound(rainSnd, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
|
||||
mPlayingSoundID = mResult.mAmbientLoopSoundID;
|
||||
}
|
||||
}
|
||||
|
||||
stopSounds(false);
|
||||
if (mAmbientSound.get())
|
||||
mAmbientSound->setVolume(mResult.mAmbientSoundVolume);
|
||||
}
|
||||
|
||||
void WeatherManager::stopSounds(bool stopAll)
|
||||
void WeatherManager::stopSounds()
|
||||
{
|
||||
std::vector<std::string>::iterator it = mSoundsPlaying.begin();
|
||||
while (it!=mSoundsPlaying.end())
|
||||
if (mAmbientSound.get())
|
||||
{
|
||||
if (stopAll ||
|
||||
!((*it == mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID) ||
|
||||
(*it == mWeatherSettings[mCurrentWeather].mRainLoopSoundID)))
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->stopSound(*it);
|
||||
it = mSoundsPlaying.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
MWBase::Environment::get().getSoundManager()->stopSound(mAmbientSound);
|
||||
mAmbientSound.reset();
|
||||
mPlayingSoundID.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -764,7 +772,7 @@ bool WeatherManager::readRecord(ESM::ESMReader& reader, int32_t type)
|
|||
state.load(reader);
|
||||
|
||||
// reset other temporary state, now that we loaded successfully
|
||||
stopSounds(true); // let's hope this never throws
|
||||
stopSounds(); // let's hope this never throws
|
||||
mRegionOverrides.clear();
|
||||
mRegionMods.clear();
|
||||
mThunderFlash = 0.0;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <OgreColourValue.h>
|
||||
#include <OgreVector3.h>
|
||||
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct Region;
|
||||
|
@ -61,10 +63,12 @@ namespace MWWorld
|
|||
bool mIsStorm;
|
||||
|
||||
std::string mAmbientLoopSoundID;
|
||||
float mAmbientSoundVolume;
|
||||
|
||||
std::string mParticleEffect;
|
||||
|
||||
std::string mRainEffect;
|
||||
float mEffectFade;
|
||||
|
||||
float mRainSpeed;
|
||||
float mRainFrequency;
|
||||
};
|
||||
|
@ -125,9 +129,6 @@ namespace MWWorld
|
|||
// This is used for Blight, Ashstorm and Blizzard (Bloodmoon)
|
||||
std::string mAmbientLoopSoundID;
|
||||
|
||||
// Rain sound effect
|
||||
std::string mRainLoopSoundID;
|
||||
|
||||
// Is this an ash storm / blight storm? If so, the following will happen:
|
||||
// - The particles and clouds will be oriented so they appear to come from the Red Mountain.
|
||||
// - Characters will animate their hand to protect eyes from the storm when looking in its direction (idlestorm animation)
|
||||
|
@ -173,7 +174,7 @@ namespace MWWorld
|
|||
*/
|
||||
void update(float duration, bool paused = false);
|
||||
|
||||
void stopSounds(bool stopAll);
|
||||
void stopSounds();
|
||||
|
||||
void setHour(const float hour);
|
||||
|
||||
|
@ -206,6 +207,9 @@ namespace MWWorld
|
|||
bool mIsStorm;
|
||||
Ogre::Vector3 mStormDirection;
|
||||
|
||||
MWBase::SoundPtr mAmbientSound;
|
||||
std::string mPlayingSoundID;
|
||||
|
||||
MWWorld::Fallback* mFallback;
|
||||
void setFallbackWeather(Weather& weather,const std::string& name);
|
||||
MWRender::RenderingManager* mRendering;
|
||||
|
@ -214,8 +218,6 @@ namespace MWWorld
|
|||
|
||||
std::map<std::string, std::string> mRegionOverrides;
|
||||
|
||||
std::vector<std::string> mSoundsPlaying;
|
||||
|
||||
std::string mCurrentWeather;
|
||||
std::string mNextWeather;
|
||||
|
||||
|
|
|
@ -272,7 +272,7 @@ class NiSkinData : public Record
|
|||
public:
|
||||
struct BoneTrafo
|
||||
{
|
||||
Ogre::Matrix3 rotationScale; // Rotation offset from bone, non-uniform scale
|
||||
Ogre::Matrix3 rotation; // Rotation offset from bone?
|
||||
Ogre::Vector3 trans; // Translation
|
||||
float scale; // Probably scale (always 1)
|
||||
};
|
||||
|
@ -295,7 +295,7 @@ public:
|
|||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
trafo.rotationScale = nif->getMatrix3();
|
||||
trafo.rotation = nif->getMatrix3();
|
||||
trafo.trans = nif->getVector3();
|
||||
trafo.scale = nif->getFloat();
|
||||
|
||||
|
@ -307,7 +307,7 @@ public:
|
|||
{
|
||||
BoneInfo &bi = bones[i];
|
||||
|
||||
bi.trafo.rotationScale = nif->getMatrix3();
|
||||
bi.trafo.rotation = nif->getMatrix3();
|
||||
bi.trafo.trans = nif->getVector3();
|
||||
bi.trafo.scale = nif->getFloat();
|
||||
bi.unknown = nif->getVector4();
|
||||
|
|
|
@ -76,7 +76,7 @@ Transformation NIFStream::getTrafo()
|
|||
{
|
||||
Transformation t;
|
||||
t.pos = getVector3();
|
||||
t.rotationScale = getMatrix3();
|
||||
t.rotation = getMatrix3();
|
||||
t.scale = getFloat();
|
||||
return t;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace Nif
|
|||
struct Transformation
|
||||
{
|
||||
Ogre::Vector3 pos;
|
||||
Ogre::Matrix3 rotationScale;
|
||||
Ogre::Matrix3 rotation;
|
||||
float scale;
|
||||
|
||||
static const Transformation& getIdentity()
|
||||
|
|
|
@ -9,10 +9,11 @@ void Node::getProperties(const Nif::NiTexturingProperty *&texprop,
|
|||
const Nif::NiVertexColorProperty *&vertprop,
|
||||
const Nif::NiZBufferProperty *&zprop,
|
||||
const Nif::NiSpecularProperty *&specprop,
|
||||
const Nif::NiWireframeProperty *&wireprop) const
|
||||
const Nif::NiWireframeProperty *&wireprop,
|
||||
const Nif::NiStencilProperty *&stencilprop) const
|
||||
{
|
||||
if(parent)
|
||||
parent->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
|
||||
parent->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop, stencilprop);
|
||||
|
||||
for(size_t i = 0;i < props.length();i++)
|
||||
{
|
||||
|
@ -35,6 +36,8 @@ void Node::getProperties(const Nif::NiTexturingProperty *&texprop,
|
|||
specprop = static_cast<const Nif::NiSpecularProperty*>(pr);
|
||||
else if(pr->recType == Nif::RC_NiWireframeProperty)
|
||||
wireprop = static_cast<const Nif::NiWireframeProperty*>(pr);
|
||||
else if (pr->recType == Nif::RC_NiStencilProperty)
|
||||
stencilprop = static_cast<const Nif::NiStencilProperty*>(pr);
|
||||
else
|
||||
std::cerr<< "Unhandled property type: "<<pr->recName <<std::endl;
|
||||
}
|
||||
|
@ -42,9 +45,8 @@ void Node::getProperties(const Nif::NiTexturingProperty *&texprop,
|
|||
|
||||
Ogre::Matrix4 Node::getLocalTransform() const
|
||||
{
|
||||
Ogre::Matrix4 mat4 = Ogre::Matrix4(trafo.rotationScale);
|
||||
mat4.setTrans(trafo.pos);
|
||||
mat4.setScale(Ogre::Vector3(trafo.rotationScale[0][0], trafo.rotationScale[1][1], trafo.rotationScale[2][2]) * trafo.scale);
|
||||
Ogre::Matrix4 mat4 = Ogre::Matrix4(Ogre::Matrix4::IDENTITY);
|
||||
mat4.makeTransform(trafo.pos, Ogre::Vector3(trafo.scale), Ogre::Quaternion(trafo.rotation));
|
||||
return mat4;
|
||||
}
|
||||
|
||||
|
|
|
@ -98,7 +98,8 @@ public:
|
|||
const Nif::NiVertexColorProperty *&vertprop,
|
||||
const Nif::NiZBufferProperty *&zprop,
|
||||
const Nif::NiSpecularProperty *&specprop,
|
||||
const Nif::NiWireframeProperty *&wireprop) const;
|
||||
const Nif::NiWireframeProperty *&wireprop,
|
||||
const Nif::NiStencilProperty *&stencilprop) const;
|
||||
|
||||
Ogre::Matrix4 getLocalTransform() const;
|
||||
Ogre::Matrix4 getWorldTransform() const;
|
||||
|
|
|
@ -54,6 +54,28 @@ static const char *getTestMode(int mode)
|
|||
return "less_equal";
|
||||
}
|
||||
|
||||
static void setTextureProperties(sh::MaterialInstance* material, const std::string& textureSlotName, const Nif::NiTexturingProperty::Texture& tex)
|
||||
{
|
||||
material->setProperty(textureSlotName + "UVSet", sh::makeProperty(new sh::IntValue(tex.uvSet)));
|
||||
const std::string clampMode = textureSlotName + "ClampMode";
|
||||
switch (tex.clamp)
|
||||
{
|
||||
case 0:
|
||||
material->setProperty(clampMode, sh::makeProperty(new sh::StringValue("clamp clamp")));
|
||||
break;
|
||||
case 1:
|
||||
material->setProperty(clampMode, sh::makeProperty(new sh::StringValue("clamp wrap")));
|
||||
break;
|
||||
case 2:
|
||||
material->setProperty(clampMode, sh::makeProperty(new sh::StringValue("wrap clamp")));
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
material->setProperty(clampMode, sh::makeProperty(new sh::StringValue("wrap wrap")));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
||||
const Ogre::String &name, const Ogre::String &group,
|
||||
const Nif::NiTexturingProperty *texprop,
|
||||
|
@ -63,6 +85,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||
const Nif::NiZBufferProperty *zprop,
|
||||
const Nif::NiSpecularProperty *specprop,
|
||||
const Nif::NiWireframeProperty *wireprop,
|
||||
const Nif::NiStencilProperty *stencilprop,
|
||||
bool &needTangents, bool particleMaterial)
|
||||
{
|
||||
Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton();
|
||||
|
@ -84,6 +107,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||
// Default should be 1, but Bloodmoon's models are broken
|
||||
int specFlags = 0;
|
||||
int wireFlags = 0;
|
||||
int drawMode = 1;
|
||||
Ogre::String texName[7];
|
||||
|
||||
bool vertexColour = (shapedata->colors.size() != 0);
|
||||
|
@ -183,6 +207,20 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||
}
|
||||
}
|
||||
|
||||
if(stencilprop)
|
||||
{
|
||||
drawMode = stencilprop->data.drawMode;
|
||||
if (stencilprop->data.enabled)
|
||||
warn("Unhandled stencil test in "+name);
|
||||
|
||||
Nif::ControllerPtr ctrls = stencilprop->controller;
|
||||
while(!ctrls.empty())
|
||||
{
|
||||
warn("Unhandled stencil controller "+ctrls->recName+" in "+name);
|
||||
ctrls = ctrls->next;
|
||||
}
|
||||
}
|
||||
|
||||
// Material
|
||||
if(matprop)
|
||||
{
|
||||
|
@ -227,8 +265,13 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||
for(int i = 0;i < 7;i++)
|
||||
{
|
||||
if(!texName[i].empty())
|
||||
{
|
||||
boost::hash_combine(h, texName[i]);
|
||||
boost::hash_combine(h, texprop->textures[i].clamp);
|
||||
boost::hash_combine(h, texprop->textures[i].uvSet);
|
||||
}
|
||||
}
|
||||
boost::hash_combine(h, drawMode);
|
||||
boost::hash_combine(h, vertexColour);
|
||||
boost::hash_combine(h, alphaFlags);
|
||||
boost::hash_combine(h, alphaTest);
|
||||
|
@ -286,6 +329,13 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||
instance->setProperty("polygon_mode", sh::makeProperty(new sh::StringValue("wireframe")));
|
||||
}
|
||||
|
||||
if (drawMode == 1)
|
||||
instance->setProperty("cullmode", sh::makeProperty(new sh::StringValue("clockwise")));
|
||||
else if (drawMode == 2)
|
||||
instance->setProperty("cullmode", sh::makeProperty(new sh::StringValue("anticlockwise")));
|
||||
else if (drawMode == 3)
|
||||
instance->setProperty("cullmode", sh::makeProperty(new sh::StringValue("none")));
|
||||
|
||||
instance->setProperty("diffuseMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BaseTexture]));
|
||||
instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture]));
|
||||
instance->setProperty("detailMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DetailTexture]));
|
||||
|
@ -294,22 +344,22 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||
if (!texName[Nif::NiTexturingProperty::BaseTexture].empty())
|
||||
{
|
||||
instance->setProperty("use_diffuse_map", sh::makeProperty(new sh::BooleanValue(true)));
|
||||
instance->setProperty("diffuseMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::BaseTexture].uvSet)));
|
||||
setTextureProperties(instance, "diffuseMap", texprop->textures[Nif::NiTexturingProperty::BaseTexture]);
|
||||
}
|
||||
if (!texName[Nif::NiTexturingProperty::GlowTexture].empty())
|
||||
{
|
||||
instance->setProperty("use_emissive_map", sh::makeProperty(new sh::BooleanValue(true)));
|
||||
instance->setProperty("emissiveMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::GlowTexture].uvSet)));
|
||||
setTextureProperties(instance, "emissiveMap", texprop->textures[Nif::NiTexturingProperty::GlowTexture]);
|
||||
}
|
||||
if (!texName[Nif::NiTexturingProperty::DetailTexture].empty())
|
||||
{
|
||||
instance->setProperty("use_detail_map", sh::makeProperty(new sh::BooleanValue(true)));
|
||||
instance->setProperty("detailMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DetailTexture].uvSet)));
|
||||
setTextureProperties(instance, "detailMap", texprop->textures[Nif::NiTexturingProperty::DetailTexture]);
|
||||
}
|
||||
if (!texName[Nif::NiTexturingProperty::DarkTexture].empty())
|
||||
{
|
||||
instance->setProperty("use_dark_map", sh::makeProperty(new sh::BooleanValue(true)));
|
||||
instance->setProperty("darkMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DarkTexture].uvSet)));
|
||||
setTextureProperties(instance, "darkMap", texprop->textures[Nif::NiTexturingProperty::DarkTexture]);
|
||||
}
|
||||
|
||||
bool useParallax = !texName[Nif::NiTexturingProperty::BumpTexture].empty()
|
||||
|
@ -332,7 +382,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||
instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true)));
|
||||
|
||||
// Override alpha flags based on our override list (transparency-overrides.cfg)
|
||||
if (!texName[0].empty())
|
||||
if ((alphaFlags&1) && !texName[0].empty())
|
||||
{
|
||||
NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]);
|
||||
if (result.first)
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace Nif
|
|||
class NiZBufferProperty;
|
||||
class NiSpecularProperty;
|
||||
class NiWireframeProperty;
|
||||
class NiStencilProperty;
|
||||
}
|
||||
|
||||
namespace NifOgre
|
||||
|
@ -41,6 +42,7 @@ public:
|
|||
const Nif::NiZBufferProperty *zprop,
|
||||
const Nif::NiSpecularProperty *specprop,
|
||||
const Nif::NiWireframeProperty *wireprop,
|
||||
const Nif::NiStencilProperty *stencilprop,
|
||||
bool &needTangents, bool particleMaterial=false);
|
||||
};
|
||||
|
||||
|
|
|
@ -138,10 +138,9 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
|
|||
const Nif::NodeList &bones = skin->bones;
|
||||
for(size_t b = 0;b < bones.length();b++)
|
||||
{
|
||||
const Ogre::Matrix3& rotationScale = data->bones[b].trafo.rotationScale;
|
||||
Ogre::Matrix4 mat (rotationScale);
|
||||
mat.setTrans(data->bones[b].trafo.trans);
|
||||
mat.setScale(Ogre::Vector3(rotationScale[0][0], rotationScale[1][1], rotationScale[2][2]) * data->bones[b].trafo.scale);
|
||||
Ogre::Matrix4 mat;
|
||||
mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale),
|
||||
Ogre::Quaternion(data->bones[b].trafo.rotation));
|
||||
mat = bones[b]->getWorldTransform() * mat;
|
||||
|
||||
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights;
|
||||
|
@ -321,13 +320,14 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
|
|||
const Nif::NiZBufferProperty *zprop = NULL;
|
||||
const Nif::NiSpecularProperty *specprop = NULL;
|
||||
const Nif::NiWireframeProperty *wireprop = NULL;
|
||||
const Nif::NiStencilProperty *stencilprop = NULL;
|
||||
bool needTangents = false;
|
||||
|
||||
shape->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
|
||||
shape->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop, stencilprop);
|
||||
std::string matname = NIFMaterialLoader::getMaterial(data, mesh->getName(), mGroup,
|
||||
texprop, matprop, alphaprop,
|
||||
vertprop, zprop, specprop,
|
||||
wireprop, needTangents);
|
||||
wireprop, stencilprop, needTangents);
|
||||
if(matname.length() > 0)
|
||||
sub->setMaterialName(matname);
|
||||
|
||||
|
|
|
@ -732,7 +732,8 @@ class NIFObjectLoader
|
|||
const Nif::NiZBufferProperty *zprop = NULL;
|
||||
const Nif::NiSpecularProperty *specprop = NULL;
|
||||
const Nif::NiWireframeProperty *wireprop = NULL;
|
||||
node->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
|
||||
const Nif::NiStencilProperty *stencilprop = NULL;
|
||||
node->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop, stencilprop);
|
||||
|
||||
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
|
||||
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
|
||||
|
@ -889,13 +890,14 @@ class NIFObjectLoader
|
|||
const Nif::NiZBufferProperty *zprop = NULL;
|
||||
const Nif::NiSpecularProperty *specprop = NULL;
|
||||
const Nif::NiWireframeProperty *wireprop = NULL;
|
||||
const Nif::NiStencilProperty *stencilprop = NULL;
|
||||
bool needTangents = false;
|
||||
|
||||
partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
|
||||
partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop, stencilprop);
|
||||
partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group,
|
||||
texprop, matprop, alphaprop,
|
||||
vertprop, zprop, specprop,
|
||||
wireprop, needTangents,
|
||||
wireprop, stencilprop, needTangents,
|
||||
// MW doesn't light particles, but the MaterialProperty
|
||||
// used still has lighting, so that must be ignored.
|
||||
true));
|
||||
|
|
|
@ -36,17 +36,9 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
|
|||
if(parent) parent->addChild(bone);
|
||||
mNifToOgreHandleMap[node->recIndex] = bone->getHandle();
|
||||
|
||||
// decompose the local transform into position, scale and orientation.
|
||||
// this is required for cases where the rotationScale matrix includes scaling, which the NIF format allows :(
|
||||
// the code would look a bit nicer if Ogre allowed setting the transform matrix of a Bone directly, but we can't do that.
|
||||
Ogre::Matrix4 mat(node->getLocalTransform());
|
||||
Ogre::Vector3 position, scale;
|
||||
Ogre::Quaternion orientation;
|
||||
mat.decomposition(position, scale, orientation);
|
||||
bone->setOrientation(orientation);
|
||||
bone->setPosition(position);
|
||||
bone->setScale(scale);
|
||||
|
||||
bone->setOrientation(node->trafo.rotation);
|
||||
bone->setPosition(node->trafo.pos);
|
||||
bone->setScale(Ogre::Vector3(node->trafo.scale));
|
||||
bone->setBindingPose();
|
||||
|
||||
if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */
|
||||
|
|
|
@ -67,12 +67,14 @@ material openmw_objects_base
|
|||
depth_check $depth_check
|
||||
transparent_sorting $transparent_sorting
|
||||
polygon_mode $polygon_mode
|
||||
cull_hardware $cullmode
|
||||
|
||||
texture_unit diffuseMap
|
||||
{
|
||||
direct_texture $diffuseMap
|
||||
create_in_ffp $use_diffuse_map
|
||||
tex_coord_set $diffuseMapUVSet
|
||||
tex_address_mode $diffuseMapClampMode
|
||||
}
|
||||
|
||||
texture_unit normalMap
|
||||
|
@ -89,6 +91,7 @@ material openmw_objects_base
|
|||
alpha_op_ex modulate src_current src_texture
|
||||
direct_texture $darkMap
|
||||
tex_coord_set $darkMapUVSet
|
||||
tex_address_mode $darkMapClampMode
|
||||
}
|
||||
|
||||
texture_unit detailMap
|
||||
|
@ -97,6 +100,7 @@ material openmw_objects_base
|
|||
colour_op_ex modulate_x2 src_current src_texture
|
||||
direct_texture $detailMap
|
||||
tex_coord_set $detailMapUVSet
|
||||
tex_address_mode $detailMapClampMode
|
||||
}
|
||||
|
||||
texture_unit emissiveMap
|
||||
|
@ -105,6 +109,7 @@ material openmw_objects_base
|
|||
colour_op add
|
||||
direct_texture $emissiveMap
|
||||
tex_coord_set $emissiveMapUVSet
|
||||
tex_address_mode $emissiveMapClampMode
|
||||
}
|
||||
|
||||
texture_unit envMap
|
||||
|
|
|
@ -179,6 +179,8 @@ camera y multiplier = 1.0
|
|||
|
||||
always run = false
|
||||
|
||||
allow third person zoom = false
|
||||
|
||||
[Game]
|
||||
# Always use the most powerful attack when striking with a weapon (chop, slash or thrust)
|
||||
best attack = false
|
||||
|
|
Loading…
Reference in a new issue