Merge remote-tracking branch 'scrawl/master'

report
Marc Zinnschlag 10 years ago
commit eb6e1576be

@ -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];
mPreview->setPrototype(record);
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,7 +624,9 @@ 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);
MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true);
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;

@ -57,6 +57,8 @@ namespace MWMechanics
bool mWalking;
unsigned short mPlayedIdle;
PathFinder mPathFinder;
AiWanderStorage():
mTargetAngle(0),
@ -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)
{
@ -494,7 +505,7 @@ namespace MWMechanics
Ogre::Vector3 playerPos(player.getRefData().getPosition().pos);
Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos);
float playerDistSqr = playerPos.squaredDistance(actorPos);
int& greetingTimer = storage.mGreetingTimer;
if (greetingState == Greet_None)
{
@ -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];
@ -616,7 +619,7 @@ namespace MWMechanics
// Choose a different node and delete this one from possible nodes because it is uncreachable:
else
mAllowedNodes.erase(mAllowedNodes.begin() + randNode);
}
}
}
return false; // AiWander package not yet completed
@ -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,29 +1414,32 @@ void CharacterController::update(float duration)
{
// Started a jump.
float z = cls.getJump(mPtr);
if(vec.x == 0 && vec.y == 0)
vec = Ogre::Vector3(0.0f, 0.0f, z);
else
if (z > 0)
{
Ogre::Vector3 lat = Ogre::Vector3(vec.x, vec.y, 0.0f).normalisedCopy();
vec = Ogre::Vector3(lat.x, lat.y, 1.0f) * z * 0.707f;
}
if(vec.x == 0 && vec.y == 0)
vec = Ogre::Vector3(0.0f, 0.0f, z);
else
{
Ogre::Vector3 lat = Ogre::Vector3(vec.x, vec.y, 0.0f).normalisedCopy();
vec = Ogre::Vector3(lat.x, lat.y, 1.0f) * z * 0.707f;
}
// advance acrobatics
if (mPtr.getRefData().getHandle() == "player")
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0);
// decrease fatigue
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->getFloat();
const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->getFloat();
float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr);
if (normalizedEncumbrance > 1)
normalizedEncumbrance = 1;
const int fatigueDecrease = fatigueJumpBase + (1 - normalizedEncumbrance) * fatigueJumpMult;
DynamicStat<float> fatigue = cls.getCreatureStats(mPtr).getFatigue();
fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);
cls.getCreatureStats(mPtr).setFatigue(fatigue);
// advance acrobatics
if (mPtr.getRefData().getHandle() == "player")
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0);
// decrease fatigue
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->getFloat();
const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->getFloat();
float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr);
if (normalizedEncumbrance > 1)
normalizedEncumbrance = 1;
const int fatigueDecrease = fatigueJumpBase + (1 - normalizedEncumbrance) * fatigueJumpMult;
DynamicStat<float> fatigue = cls.getCreatureStats(mPtr).getFatigue();
fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);
cls.getCreatureStats(mPtr).setFatigue(fatigue);
}
}
else if(mJumpState == JumpState_InAir)
{

@ -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;
// Will the witness report the crime?
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm)
{
reported = true;
}
crimeSeen = true;
}
// Will the witness report the crime?
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;
mObjectParts[type] = insertBoundedPart(mesh, group, sPartList.at(type), enchantedGlow, glowColor);
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,7 +721,8 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g
}
}
updateSkeletonInstance(mSkelBase->getSkeleton(), skel);
if (isSkinned(mObjectParts[type]))
updateSkeletonInstance(mSkelBase->getSkeleton(), skel);
}
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(mObjectParts[type]->mControllers.begin());

@ -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;
mResult.mIsStorm = current.mIsStorm;
mResult.mParticleEffect = current.mParticleEffect;
mResult.mRainEffect = current.mRainEffect;
mResult.mRainSpeed = current.mRainSpeed;
mResult.mRainFrequency = current.mRainFrequency;
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…
Cancel
Save