mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-21 07:53:53 +00:00
Merge branch 'master' into HEAD
This commit is contained in:
commit
2a42556aa5
90 changed files with 2418 additions and 1650 deletions
|
@ -96,15 +96,15 @@ bool GameSettings::readFile(QTextStream &stream)
|
|||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine().simplified();
|
||||
QString line = stream.readLine();
|
||||
|
||||
if (line.isEmpty() || line.startsWith("#"))
|
||||
continue;
|
||||
|
||||
if (keyRe.indexIn(line) != -1) {
|
||||
|
||||
QString key = keyRe.cap(1).simplified();
|
||||
QString value = keyRe.cap(2).simplified();
|
||||
QString key = keyRe.cap(1);
|
||||
QString value = keyRe.cap(2);
|
||||
|
||||
// Don't remove existing data entries
|
||||
if (key != QLatin1String("data"))
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine().simplified();
|
||||
QString line = stream.readLine();
|
||||
|
||||
if (line.isEmpty() || line.startsWith("#"))
|
||||
continue;
|
||||
|
@ -65,8 +65,8 @@ public:
|
|||
|
||||
if (keyRe.indexIn(line) != -1) {
|
||||
|
||||
QString key = keyRe.cap(1).simplified();
|
||||
QString value = keyRe.cap(2).simplified();
|
||||
QString key = keyRe.cap(1);
|
||||
QString value = keyRe.cap(2);
|
||||
|
||||
if (!sectionPrefix.isEmpty())
|
||||
key.prepend(sectionPrefix);
|
||||
|
|
|
@ -14,7 +14,7 @@ set(GAME_HEADER
|
|||
source_group(game FILES ${GAME} ${GAME_HEADER})
|
||||
|
||||
add_openmw_dir (mwrender
|
||||
renderingmanager debugging sky player animation npcanimation creatureanimation activatoranimation
|
||||
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
|
||||
actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows
|
||||
compositors characterpreview externalrendering globalmap videoplayer ripplesimulation refraction
|
||||
)
|
||||
|
@ -54,7 +54,7 @@ add_openmw_dir (mwworld
|
|||
containerstore actiontalk actiontake manualref player cellfunctors failedaction
|
||||
cells localscripts customdata weather inventorystore ptr actionopen actionread
|
||||
actionequip timestamp actionalchemy cellstore actionapply actioneat
|
||||
esmstore store recordcmp fallback actionrepair actionsoulgem livecellref
|
||||
esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
|
||||
)
|
||||
|
||||
add_openmw_dir (mwclass
|
||||
|
|
|
@ -368,7 +368,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins,
|
||||
mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap,
|
||||
mActivationDistanceOverride));
|
||||
MWBase::Environment::get().getWorld()->setupPlayer(mNewGame);
|
||||
MWBase::Environment::get().getWorld()->setupPlayer();
|
||||
|
||||
//Load translation data
|
||||
mTranslationDataStorage.setEncoder(mEncoder);
|
||||
|
|
|
@ -99,6 +99,9 @@ namespace MWBase
|
|||
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0;
|
||||
///< Perform a persuasion action on NPC
|
||||
|
||||
virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;
|
||||
///< Forces an object to refresh its animation state.
|
||||
|
||||
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0;
|
||||
///< Run animation for a MW-reference. Calls to this function for references that are currently not
|
||||
/// in the scene should be ignored.
|
||||
|
|
|
@ -230,6 +230,8 @@ namespace MWBase
|
|||
|
||||
virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0;
|
||||
|
||||
virtual void localRotateObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0;
|
||||
|
||||
virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) = 0;
|
||||
///< place an object in a "safe" location (ie not in the void, etc).
|
||||
|
||||
|
@ -315,15 +317,20 @@ namespace MWBase
|
|||
|
||||
virtual void togglePOV() = 0;
|
||||
virtual void togglePreviewMode(bool enable) = 0;
|
||||
virtual bool toggleVanityMode(bool enable, bool force) = 0;
|
||||
virtual bool toggleVanityMode(bool enable) = 0;
|
||||
virtual void allowVanityMode(bool allow) = 0;
|
||||
virtual void togglePlayerLooking(bool enable) = 0;
|
||||
virtual void changeVanityModeScale(float factor) = 0;
|
||||
virtual bool vanityRotateCamera(float * rot) = 0;
|
||||
|
||||
virtual void setupPlayer(bool newGame) = 0;
|
||||
virtual void setupPlayer() = 0;
|
||||
virtual void renderPlayer() = 0;
|
||||
|
||||
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door) = 0;
|
||||
///< if activated, should this door be opened or closed?
|
||||
virtual void activateDoor(const MWWorld::Ptr& door) = 0;
|
||||
///< activate (open or close) an non-teleport door
|
||||
|
||||
virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0;
|
||||
|
||||
virtual int canRest() = 0;
|
||||
|
|
|
@ -375,12 +375,12 @@ namespace MWClass
|
|||
return MWWorld::Ptr(&cell.mArmors.insert(*ref), &cell);
|
||||
}
|
||||
|
||||
short Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
|
||||
float Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Armor> *ref =
|
||||
ptr.get<ESM::Armor>();
|
||||
|
||||
return ref->mBase->mData.mEnchant;
|
||||
return ref->mBase->mData.mEnchant/10.f;
|
||||
}
|
||||
|
||||
bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const
|
||||
|
|
|
@ -77,7 +77,7 @@ namespace MWClass
|
|||
|
||||
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
|
||||
|
||||
virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
|
||||
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
|
||||
};
|
||||
|
|
|
@ -176,12 +176,12 @@ namespace MWClass
|
|||
return MWWorld::Ptr(&cell.mBooks.insert(*ref), &cell);
|
||||
}
|
||||
|
||||
short Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
|
||||
float Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Book> *ref =
|
||||
ptr.get<ESM::Book>();
|
||||
|
||||
return ref->mBase->mData.mEnchant;
|
||||
return ref->mBase->mData.mEnchant/10.f;
|
||||
}
|
||||
|
||||
bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace MWClass
|
|||
|
||||
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
|
||||
|
||||
virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
|
||||
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
|
||||
};
|
||||
|
|
|
@ -295,12 +295,12 @@ namespace MWClass
|
|||
return MWWorld::Ptr(&cell.mClothes.insert(*ref), &cell);
|
||||
}
|
||||
|
||||
short Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
|
||||
float Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Clothing> *ref =
|
||||
ptr.get<ESM::Clothing>();
|
||||
|
||||
return ref->mBase->mData.mEnchant;
|
||||
return ref->mBase->mData.mEnchant/10.f;
|
||||
}
|
||||
|
||||
bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const
|
||||
|
|
|
@ -71,7 +71,7 @@ namespace MWClass
|
|||
|
||||
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
|
||||
|
||||
virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
|
||||
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "../mwworld/nullaction.hpp"
|
||||
#include "../mwworld/failedaction.hpp"
|
||||
#include "../mwworld/actionteleport.hpp"
|
||||
#include "../mwworld/actiondoor.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
#include "../mwworld/physicssystem.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
@ -71,7 +72,7 @@ namespace MWClass
|
|||
ptr.get<ESM::Door>();
|
||||
|
||||
const std::string &openSound = ref->mBase->mOpenSound;
|
||||
//const std::string &closeSound = ref->mBase->closeSound;
|
||||
const std::string &closeSound = ref->mBase->mCloseSound;
|
||||
const std::string lockedSound = "LockedDoor";
|
||||
const std::string trapActivationSound = "Disarm Trap Fail";
|
||||
|
||||
|
@ -139,12 +140,11 @@ namespace MWClass
|
|||
else
|
||||
{
|
||||
// animated door
|
||||
// TODO return action for rotating the door
|
||||
|
||||
// This is a little pointless, but helps with testing
|
||||
boost::shared_ptr<MWWorld::Action> action(new MWWorld::NullAction);
|
||||
|
||||
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionDoor(ptr));
|
||||
if (MWBase::Environment::get().getWorld()->getOpenOrCloseDoor(ptr))
|
||||
action->setSound(openSound);
|
||||
else
|
||||
action->setSound(closeSound);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
|
|
@ -181,4 +181,12 @@ namespace MWClass
|
|||
{
|
||||
return npcServices & ESM::NPC::Picks;
|
||||
}
|
||||
|
||||
int Lockpick::getItemMaxHealth (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Lockpick> *ref =
|
||||
ptr.get<ESM::Lockpick>();
|
||||
|
||||
return ref->mBase->mData.mUses;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,9 @@ namespace MWClass
|
|||
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
|
||||
|
||||
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
|
||||
|
||||
virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const;
|
||||
///< Return item max health or throw an exception, if class does not have item health
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -180,4 +180,12 @@ namespace MWClass
|
|||
{
|
||||
return npcServices & ESM::NPC::Probes;
|
||||
}
|
||||
|
||||
int Probe::getItemMaxHealth (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Probe> *ref =
|
||||
ptr.get<ESM::Probe>();
|
||||
|
||||
return ref->mBase->mData.mUses;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,9 @@ namespace MWClass
|
|||
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
|
||||
|
||||
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
|
||||
|
||||
virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const;
|
||||
///< Return item max health or throw an exception, if class does not have item health
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -428,12 +428,12 @@ namespace MWClass
|
|||
return MWWorld::Ptr(&cell.mWeapons.insert(*ref), &cell);
|
||||
}
|
||||
|
||||
short Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
|
||||
float Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Weapon> *ref =
|
||||
ptr.get<ESM::Weapon>();
|
||||
|
||||
return ref->mBase->mData.mEnchant;
|
||||
return ref->mBase->mData.mEnchant/10.f;
|
||||
}
|
||||
|
||||
bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const
|
||||
|
|
|
@ -79,7 +79,7 @@ namespace MWClass
|
|||
|
||||
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
|
||||
|
||||
virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
|
||||
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace MWDialogue
|
|||
static std::string idFromIndex (const std::string& topic, int index);
|
||||
};
|
||||
|
||||
/// \biref A quest entry with a timestamp.
|
||||
/// \brief A quest entry with a timestamp.
|
||||
struct StampedJournalEntry : public JournalEntry
|
||||
{
|
||||
int mDay;
|
||||
|
|
|
@ -397,11 +397,24 @@ namespace MWGui
|
|||
|
||||
void Console::setSelectedObject(const MWWorld::Ptr& object)
|
||||
{
|
||||
mPtr = object;
|
||||
if (!mPtr.isEmpty())
|
||||
setTitle("#{sConsoleTitle} (" + mPtr.getCellRef().mRefID + ")");
|
||||
else
|
||||
if (!object.isEmpty())
|
||||
{
|
||||
if (object == mPtr)
|
||||
{
|
||||
setTitle("#{sConsoleTitle}");
|
||||
mPtr=MWWorld::Ptr();
|
||||
}
|
||||
else
|
||||
{
|
||||
setTitle("#{sConsoleTitle} (" + object.getCellRef().mRefID + ")");
|
||||
mPtr = object;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setTitle("#{sConsoleTitle}");
|
||||
mPtr = MWWorld::Ptr();
|
||||
}
|
||||
MyGUI::InputManager::getInstance().setKeyFocusWidget(command);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,8 +57,9 @@ namespace MWGui
|
|||
|
||||
void EnchantingDialog::updateLabels()
|
||||
{
|
||||
mEnchantmentPoints->setCaption(boost::lexical_cast<std::string>(mEnchanting.getEnchantCost())
|
||||
+ " / " + boost::lexical_cast<std::string>(mEnchanting.getMaxEnchantValue()));
|
||||
std::stringstream enchantCost;
|
||||
enchantCost << std::setprecision(1) << std::fixed << mEnchanting.getEnchantCost();
|
||||
mEnchantmentPoints->setCaption(enchantCost.str() + " / " + boost::lexical_cast<std::string>(mEnchanting.getMaxEnchantValue()));
|
||||
|
||||
mCharge->setCaption(boost::lexical_cast<std::string>(mEnchanting.getGemCharge()));
|
||||
|
||||
|
|
|
@ -921,14 +921,16 @@ namespace MWGui
|
|||
const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>()
|
||||
.find(MWWorld::Class::get(item).getEnchantment(item));
|
||||
|
||||
int chargePercent = item.getCellRef().mEnchantmentCharge / static_cast<float>(ench->mData.mCharge) * 100;
|
||||
int chargePercent = (item.getCellRef().mEnchantmentCharge == -1) ? 100
|
||||
: (item.getCellRef().mEnchantmentCharge / static_cast<float>(ench->mData.mCharge) * 100);
|
||||
mHud->setSelectedEnchantItem(item, chargePercent);
|
||||
mSpellWindow->setTitle(MWWorld::Class::get(item).getName(item));
|
||||
}
|
||||
|
||||
void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item)
|
||||
{
|
||||
int durabilityPercent = item.getCellRef().mCharge / static_cast<float>(MWWorld::Class::get(item).getItemMaxHealth(item)) * 100;
|
||||
int durabilityPercent = (item.getCellRef().mCharge == -1) ? 100
|
||||
: (item.getCellRef().mCharge / static_cast<float>(MWWorld::Class::get(item).getItemMaxHealth(item)) * 100);
|
||||
mHud->setSelectedWeapon(item, durabilityPercent);
|
||||
mInventoryWindow->setTitle(MWWorld::Class::get(item).getName(item));
|
||||
}
|
||||
|
@ -1113,6 +1115,13 @@ namespace MWGui
|
|||
{
|
||||
mLoadingScreen->loadingDone ();
|
||||
}
|
||||
bool WindowManager::getRestEnabled()
|
||||
{
|
||||
//Enable rest dialogue if character creation finished
|
||||
if(mRestAllowed==false && MWBase::Environment::get().getWorld()->getGlobalVariable ("chargenstate").mFloat==-1)
|
||||
mRestAllowed=true;
|
||||
return mRestAllowed;
|
||||
}
|
||||
|
||||
bool WindowManager::getPlayerSleeping ()
|
||||
{
|
||||
|
|
|
@ -220,7 +220,7 @@ namespace MWGui
|
|||
virtual void loadingDone();
|
||||
|
||||
virtual void enableRest() { mRestAllowed = true; }
|
||||
virtual bool getRestEnabled() { return mRestAllowed; }
|
||||
virtual bool getRestEnabled();
|
||||
|
||||
virtual bool getPlayerSleeping();
|
||||
virtual void wakeUpPlayer();
|
||||
|
|
|
@ -679,6 +679,7 @@ namespace MWInput
|
|||
|
||||
void InputManager::quickKey (int index)
|
||||
{
|
||||
if (!mWindows.isGuiMode())
|
||||
mWindows.activateQuickKey (index);
|
||||
}
|
||||
|
||||
|
@ -719,19 +720,17 @@ namespace MWInput
|
|||
|
||||
void InputManager::resetIdleTime()
|
||||
{
|
||||
if (mTimeIdle < 0) {
|
||||
MWBase::Environment::get().getWorld()->toggleVanityMode(false, false);
|
||||
}
|
||||
if (mTimeIdle < 0)
|
||||
MWBase::Environment::get().getWorld()->toggleVanityMode(false);
|
||||
mTimeIdle = 0.f;
|
||||
}
|
||||
|
||||
void InputManager::updateIdleTime(float dt)
|
||||
{
|
||||
if (mTimeIdle >= 0.f) {
|
||||
if (mTimeIdle >= 0.f)
|
||||
mTimeIdle += dt;
|
||||
}
|
||||
if (mTimeIdle > 30.f) {
|
||||
MWBase::Environment::get().getWorld()->toggleVanityMode(true, false);
|
||||
MWBase::Environment::get().getWorld()->toggleVanityMode(true);
|
||||
mTimeIdle = -1.f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -290,6 +290,13 @@ namespace MWMechanics
|
|||
return 0;
|
||||
}
|
||||
|
||||
void Actors::forceStateUpdate(const MWWorld::Ptr & ptr)
|
||||
{
|
||||
PtrControllerMap::iterator iter = mActors.find(ptr);
|
||||
if(iter != mActors.end())
|
||||
iter->second.forceStateUpdate();
|
||||
}
|
||||
|
||||
void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
|
||||
{
|
||||
PtrControllerMap::iterator iter = mActors.find(ptr);
|
||||
|
|
|
@ -78,6 +78,8 @@ namespace MWMechanics
|
|||
int countDeaths (const std::string& id) const;
|
||||
///< Return the number of deaths for actors with the given ID.
|
||||
|
||||
void forceStateUpdate(const MWWorld::Ptr &ptr);
|
||||
|
||||
void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
|
||||
void skipAnimation(const MWWorld::Ptr& ptr);
|
||||
};
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
|
||||
|
@ -103,14 +104,13 @@ static void getStateInfo(CharacterState state, std::string *group)
|
|||
|
||||
|
||||
CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop)
|
||||
: mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false)
|
||||
: mPtr(ptr), mAnimation(anim), mCharState(state), mSkipAnim(false), mMovingAnim(false), mSecondsOfRunning(0), mSecondsOfSwimming(0)
|
||||
{
|
||||
if(!mAnimation)
|
||||
return;
|
||||
|
||||
mAnimation->setController(this);
|
||||
|
||||
getStateInfo(mState, &mCurrentGroup);
|
||||
std::string group;
|
||||
getStateInfo(mCharState, &group);
|
||||
if(MWWorld::Class::get(mPtr).isActor())
|
||||
{
|
||||
/* Accumulate along X/Y only for now, until we can figure out how we should
|
||||
|
@ -122,19 +122,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
|||
/* Don't accumulate with non-actors. */
|
||||
mAnimation->setAccumulation(Ogre::Vector3(0.0f));
|
||||
}
|
||||
if(mAnimation->hasAnimation(mCurrentGroup))
|
||||
mAnimation->play(mCurrentGroup, "stop", "stop", loop);
|
||||
}
|
||||
|
||||
CharacterController::CharacterController(const CharacterController &rhs)
|
||||
: mPtr(rhs.mPtr), mAnimation(rhs.mAnimation), mAnimQueue(rhs.mAnimQueue)
|
||||
, mCurrentGroup(rhs.mCurrentGroup), mState(rhs.mState)
|
||||
, mSkipAnim(rhs.mSkipAnim)
|
||||
{
|
||||
if(!mAnimation)
|
||||
return;
|
||||
/* We've been copied. Update the animation with the new controller. */
|
||||
mAnimation->setController(this);
|
||||
if(mAnimation->hasAnimation(group))
|
||||
mMovingAnim = mAnimation->play(group, "start", "stop", 1.0f, loop ? (~(size_t)0) : 0);
|
||||
}
|
||||
|
||||
CharacterController::~CharacterController()
|
||||
|
@ -148,31 +137,6 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
|
|||
}
|
||||
|
||||
|
||||
void CharacterController::markerEvent(float time, const std::string &evt)
|
||||
{
|
||||
if(evt == "stop")
|
||||
{
|
||||
if(mAnimQueue.size() >= 2 && mAnimQueue[0] == mAnimQueue[1])
|
||||
{
|
||||
mAnimQueue.pop_front();
|
||||
mAnimation->play(mCurrentGroup, "loop start", "stop", false);
|
||||
}
|
||||
else if(mAnimQueue.size() > 0)
|
||||
{
|
||||
mAnimQueue.pop_front();
|
||||
if(mAnimQueue.size() > 0)
|
||||
{
|
||||
mCurrentGroup = mAnimQueue.front();
|
||||
mAnimation->play(mCurrentGroup, "start", "stop", false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::cerr<< "Unhandled animation event: "<<evt <<std::endl;
|
||||
}
|
||||
|
||||
|
||||
void CharacterController::update(float duration, Movement &movement)
|
||||
{
|
||||
float speed = 0.0f;
|
||||
|
@ -189,6 +153,29 @@ void CharacterController::update(float duration, Movement &movement)
|
|||
const Ogre::Vector3 &rot = cls.getRotationVector(mPtr);
|
||||
speed = cls.getSpeed(mPtr);
|
||||
|
||||
// advance athletics
|
||||
if (vec.squaredLength() > 0 && mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer())
|
||||
{
|
||||
if (inwater)
|
||||
{
|
||||
mSecondsOfSwimming += duration;
|
||||
while (mSecondsOfSwimming > 1)
|
||||
{
|
||||
MWWorld::Class::get(mPtr).skillUsageSucceeded(mPtr, ESM::Skill::Athletics, 1);
|
||||
mSecondsOfSwimming -= 1;
|
||||
}
|
||||
}
|
||||
else if (isrunning)
|
||||
{
|
||||
mSecondsOfRunning += duration;
|
||||
while (mSecondsOfRunning > 1)
|
||||
{
|
||||
MWWorld::Class::get(mPtr).skillUsageSucceeded(mPtr, ESM::Skill::Athletics, 0);
|
||||
mSecondsOfRunning -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: The state should be set to Jump, and X/Y movement should be disallowed except
|
||||
* for the initial thrust (which would be carried by "physics" until landing). */
|
||||
if(onground && vec.z > 0.0f)
|
||||
|
@ -214,11 +201,13 @@ void CharacterController::update(float duration, Movement &movement)
|
|||
if(vec.x > 0.0f)
|
||||
setState(inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight)
|
||||
: (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight)), true);
|
||||
|
||||
else if(vec.x < 0.0f)
|
||||
setState(inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft)
|
||||
: (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft)), true);
|
||||
|
||||
// If this animation isn't moving us sideways, do it manually
|
||||
if(!mMovingAnim)
|
||||
movement.mPosition[0] += vec.x * (speed*duration);
|
||||
// Apply any forward/backward movement manually
|
||||
movement.mPosition[1] += vec.y * (speed*duration);
|
||||
}
|
||||
|
@ -227,12 +216,15 @@ void CharacterController::update(float duration, Movement &movement)
|
|||
if(vec.y > 0.0f)
|
||||
setState(inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward)
|
||||
: (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward)), true);
|
||||
|
||||
else if(vec.y < 0.0f)
|
||||
setState(inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack)
|
||||
: (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack)), true);
|
||||
|
||||
// Apply any sideways movement manually
|
||||
movement.mPosition[0] += vec.x * (speed*duration);
|
||||
// If this animation isn't moving us forward/backward, do it manually
|
||||
if(!mMovingAnim)
|
||||
movement.mPosition[1] += vec.y * (speed*duration);
|
||||
}
|
||||
else if(rot.z != 0.0f && !inwater && !sneak)
|
||||
{
|
||||
|
@ -241,8 +233,18 @@ void CharacterController::update(float duration, Movement &movement)
|
|||
else if(rot.z < 0.0f)
|
||||
setState(CharState_TurnLeft, true);
|
||||
}
|
||||
else if(mAnimQueue.size() == 0)
|
||||
else if(getState() != CharState_SpecialIdle || !mAnimation->isPlaying(0))
|
||||
{
|
||||
if(mAnimQueue.size() == 0)
|
||||
setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true);
|
||||
else
|
||||
{
|
||||
mMovingAnim = mAnimation->play(mAnimQueue.front().first,
|
||||
"start", "stop", 0.0f,
|
||||
mAnimQueue.front().second);
|
||||
mAnimQueue.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
movement.mRotation[0] += rot.x * duration;
|
||||
movement.mRotation[1] += rot.y * duration;
|
||||
|
@ -268,20 +270,17 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int
|
|||
else
|
||||
{
|
||||
count = std::max(count, 1);
|
||||
if(mode != 0 || mAnimQueue.size() == 0)
|
||||
if(mode != 0 || getState() != CharState_SpecialIdle)
|
||||
{
|
||||
mAnimQueue.clear();
|
||||
while(count-- > 0)
|
||||
mAnimQueue.push_back(groupname);
|
||||
mCurrentGroup = groupname;
|
||||
mState = CharState_SpecialIdle;
|
||||
mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", false);
|
||||
mCharState = CharState_SpecialIdle;
|
||||
mLooping = false;
|
||||
mMovingAnim = mAnimation->play(groupname, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1);
|
||||
}
|
||||
else if(mode == 0)
|
||||
{
|
||||
mAnimQueue.resize(1);
|
||||
while(count-- > 0)
|
||||
mAnimQueue.push_back(groupname);
|
||||
mAnimQueue.clear();
|
||||
mAnimQueue.push_back(std::make_pair(groupname, count-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -294,25 +293,24 @@ void CharacterController::skipAnim()
|
|||
|
||||
void CharacterController::setState(CharacterState state, bool loop)
|
||||
{
|
||||
if(mState == state)
|
||||
{
|
||||
if(mAnimation)
|
||||
mAnimation->setLooping(loop);
|
||||
if(mCharState == state)
|
||||
return;
|
||||
}
|
||||
mState = state;
|
||||
mCharState = state;
|
||||
mLooping = loop;
|
||||
|
||||
forceStateUpdate();
|
||||
}
|
||||
|
||||
void CharacterController::forceStateUpdate()
|
||||
{
|
||||
if(!mAnimation)
|
||||
return;
|
||||
mAnimQueue.clear();
|
||||
|
||||
std::string anim;
|
||||
getStateInfo(mState, &anim);
|
||||
if(mAnimation->hasAnimation(anim))
|
||||
{
|
||||
mCurrentGroup = anim;
|
||||
mAnimation->play(mCurrentGroup, "start", "stop", loop);
|
||||
}
|
||||
getStateInfo(mCharState, &anim);
|
||||
if((mMovingAnim=mAnimation->hasAnimation(anim)) != false)
|
||||
mMovingAnim = mAnimation->play(anim, "start", "stop", 0.0f, mLooping ? (~(size_t)0) : 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -72,22 +72,21 @@ class CharacterController
|
|||
MWWorld::Ptr mPtr;
|
||||
MWRender::Animation *mAnimation;
|
||||
|
||||
typedef std::deque<std::string> AnimationQueue;
|
||||
typedef std::deque<std::pair<std::string,size_t> > AnimationQueue;
|
||||
AnimationQueue mAnimQueue;
|
||||
|
||||
std::string mCurrentGroup;
|
||||
CharacterState mState;
|
||||
CharacterState mCharState;
|
||||
bool mLooping;
|
||||
bool mSkipAnim;
|
||||
|
||||
protected:
|
||||
/* Called by the animation whenever a new text key is reached. */
|
||||
void markerEvent(float time, const std::string &evt);
|
||||
// counted for skill increase
|
||||
float mSecondsOfSwimming;
|
||||
float mSecondsOfRunning;
|
||||
|
||||
friend class MWRender::Animation;
|
||||
bool mMovingAnim;
|
||||
|
||||
public:
|
||||
CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop);
|
||||
CharacterController(const CharacterController &rhs);
|
||||
virtual ~CharacterController();
|
||||
|
||||
void updatePtr(const MWWorld::Ptr &ptr);
|
||||
|
@ -99,7 +98,9 @@ public:
|
|||
|
||||
void setState(CharacterState state, bool loop);
|
||||
CharacterState getState() const
|
||||
{ return mState; }
|
||||
{ return mCharState; }
|
||||
|
||||
void forceStateUpdate();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
int Enchanting::getEnchantCost() const
|
||||
float Enchanting::getEnchantCost() const
|
||||
{
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
float cost = 0;
|
||||
|
@ -193,7 +193,7 @@ namespace MWMechanics
|
|||
return soul->mData.mSoul;
|
||||
}
|
||||
|
||||
int Enchanting::getMaxEnchantValue() const
|
||||
float Enchanting::getMaxEnchantValue() const
|
||||
{
|
||||
if (itemEmpty())
|
||||
return 0;
|
||||
|
|
|
@ -35,9 +35,9 @@ namespace MWMechanics
|
|||
bool create(); //Return true if created, false if failed.
|
||||
void nextEnchantType(); //Set enchant type to next possible type (for mOldItemPtr object)
|
||||
int getEnchantType() const;
|
||||
int getEnchantCost() const;
|
||||
float getEnchantCost() const;
|
||||
int getEnchantPrice() const;
|
||||
int getMaxEnchantValue() const;
|
||||
float getMaxEnchantValue() const;
|
||||
int getGemCharge() const;
|
||||
float getEnchantChance() const;
|
||||
bool soulEmpty() const; //Return true if empty
|
||||
|
|
|
@ -654,6 +654,12 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
void MechanicsManager::forceStateUpdate(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
if(MWWorld::Class::get(ptr).isActor())
|
||||
mActors.forceStateUpdate(ptr);
|
||||
}
|
||||
|
||||
void MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
|
||||
{
|
||||
if(MWWorld::Class::get(ptr).isActor())
|
||||
|
|
|
@ -96,6 +96,8 @@ namespace MWMechanics
|
|||
void toLower(std::string npcFaction);
|
||||
///< Perform a persuasion action on NPC
|
||||
|
||||
virtual void forceStateUpdate(const MWWorld::Ptr &ptr);
|
||||
|
||||
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
|
||||
virtual void skipAnimation(const MWWorld::Ptr& ptr);
|
||||
};
|
||||
|
|
|
@ -169,8 +169,7 @@ float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& cla
|
|||
if (specialisationFactor<=0)
|
||||
throw std::runtime_error ("invalid skill specialisation factor");
|
||||
}
|
||||
|
||||
return 1.0 / (level +1) * (1.0 / (skillFactor)) * typeFactor * specialisationFactor;
|
||||
return 1.0 / ((level+1) * (1.0/skillFactor) * typeFactor * specialisationFactor);
|
||||
}
|
||||
|
||||
void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, int usageType)
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
#include "activatoranimation.hpp"
|
||||
|
||||
#include <OgreEntity.h>
|
||||
#include <OgreParticleSystem.h>
|
||||
#include <OgreSceneManager.h>
|
||||
#include <OgreSubEntity.h>
|
||||
|
||||
#include "renderconst.hpp"
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -24,28 +19,10 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr)
|
|||
assert (ref->mBase != NULL);
|
||||
if(!ref->mBase->mModel.empty())
|
||||
{
|
||||
std::string mesh = "meshes\\" + ref->mBase->mModel;
|
||||
const std::string name = "meshes\\"+ref->mBase->mModel;
|
||||
|
||||
createObjectList(mPtr.getRefData().getBaseNode(), mesh);
|
||||
for(size_t i = 0;i < mObjectList.mEntities.size();i++)
|
||||
{
|
||||
Ogre::Entity *ent = mObjectList.mEntities[i];
|
||||
ent->setVisibilityFlags(RV_Misc);
|
||||
|
||||
for(unsigned int j=0; j < ent->getNumSubEntities(); ++j)
|
||||
{
|
||||
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
|
||||
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < mObjectList.mParticles.size();i++)
|
||||
{
|
||||
Ogre::ParticleSystem *part = mObjectList.mParticles[i];
|
||||
part->setVisibilityFlags(RV_Misc);
|
||||
|
||||
part->setRenderQueueGroup(RQG_Alpha);
|
||||
}
|
||||
setAnimationSource(mesh);
|
||||
addObjectList(mPtr.getRefData().getBaseNode(), name, false);
|
||||
setRenderProperties(mObjects.back().mObjectList, RV_Misc, RQG_Main, RQG_Alpha);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include <OgreSkeletonManager.h>
|
||||
#include <OgreSkeletonInstance.h>
|
||||
#include <OgreEntity.h>
|
||||
#include <OgreSubEntity.h>
|
||||
#include <OgreParticleSystem.h>
|
||||
#include <OgreBone.h>
|
||||
#include <OgreSubMesh.h>
|
||||
#include <OgreSceneManager.h>
|
||||
|
@ -16,6 +18,32 @@
|
|||
namespace MWRender
|
||||
{
|
||||
|
||||
Animation::AnimLayer::AnimLayer()
|
||||
: mControllers(NULL)
|
||||
, mTextKeys(NULL)
|
||||
, mTime(0.0f)
|
||||
, mPlaying(false)
|
||||
, mLoopCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Ogre::Real Animation::AnimationValue::getValue() const
|
||||
{
|
||||
size_t idx = mIndex;
|
||||
while(idx > 0 && mAnimation->mLayer[idx].mGroupName.empty())
|
||||
idx--;
|
||||
if(!mAnimation->mLayer[idx].mGroupName.empty())
|
||||
return mAnimation->mLayer[idx].mTime;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void Animation::AnimationValue::setValue(Ogre::Real value)
|
||||
{
|
||||
mAnimation->mLayer[mIndex].mTime = value;
|
||||
}
|
||||
|
||||
|
||||
void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects)
|
||||
{
|
||||
for(size_t i = 0;i < objects.mParticles.size();i++)
|
||||
|
@ -31,22 +59,22 @@ void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectL
|
|||
|
||||
Animation::Animation(const MWWorld::Ptr &ptr)
|
||||
: mPtr(ptr)
|
||||
, mController(NULL)
|
||||
, mInsert(NULL)
|
||||
, mSkelBase(NULL)
|
||||
, mAccumRoot(NULL)
|
||||
, mNonAccumRoot(NULL)
|
||||
, mAccumulate(Ogre::Vector3::ZERO)
|
||||
, mNonAccumCtrl(NULL)
|
||||
, mAccumulate(0.0f)
|
||||
, mLastPosition(0.0f)
|
||||
, mCurrentControllers(NULL)
|
||||
, mCurrentKeys(NULL)
|
||||
, mCurrentAnim(NULL)
|
||||
, mCurrentTime(0.0f)
|
||||
, mStopTime(0.0f)
|
||||
, mPlaying(false)
|
||||
, mLooping(false)
|
||||
, mAnimVelocity(0.0f)
|
||||
, mAnimSpeedMult(1.0f)
|
||||
{
|
||||
for(size_t i = 0;i < sMaxLayers;i++)
|
||||
mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this, i));
|
||||
|
||||
/* As long as we remain under 128 active controllers, we can avoid
|
||||
* reallocations. */
|
||||
mActiveCtrls.reserve(128);
|
||||
}
|
||||
|
||||
Animation::~Animation()
|
||||
|
@ -54,105 +82,34 @@ Animation::~Animation()
|
|||
if(mInsert)
|
||||
{
|
||||
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
|
||||
destroyObjectList(sceneMgr, mObjectList);
|
||||
|
||||
for(size_t i = 0;i < mAnimationSources.size();i++)
|
||||
destroyObjectList(sceneMgr, mAnimationSources[i]);
|
||||
mAnimationSources.clear();
|
||||
for(size_t i = 0;i < mObjects.size();i++)
|
||||
destroyObjectList(sceneMgr, mObjects[i].mObjectList);
|
||||
mObjects.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Animation::setAnimationSources(const std::vector<std::string> &names)
|
||||
void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly)
|
||||
{
|
||||
if(!mObjectList.mSkelBase)
|
||||
return;
|
||||
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
|
||||
|
||||
mCurrentControllers = &mObjectList.mControllers;
|
||||
mCurrentAnim = NULL;
|
||||
mCurrentKeys = NULL;
|
||||
mAnimVelocity = 0.0f;
|
||||
mAccumRoot = NULL;
|
||||
mNonAccumRoot = NULL;
|
||||
mTextKeys.clear();
|
||||
for(size_t i = 0;i < mAnimationSources.size();i++)
|
||||
destroyObjectList(sceneMgr, mAnimationSources[i]);
|
||||
mAnimationSources.clear();
|
||||
|
||||
Ogre::SharedPtr<Ogre::ControllerValue<Ogre::Real> > ctrlval(OGRE_NEW AnimationValue(this));
|
||||
Ogre::SkeletonInstance *skelinst = mObjectList.mSkelBase->getSkeleton();
|
||||
std::vector<std::string>::const_iterator nameiter;
|
||||
for(nameiter = names.begin();nameiter != names.end();nameiter++)
|
||||
if(!mInsert)
|
||||
{
|
||||
mAnimationSources.push_back(NifOgre::Loader::createObjectBase(sceneMgr, *nameiter));
|
||||
if(!mAnimationSources.back().mSkelBase)
|
||||
{
|
||||
std::cerr<< "Failed to get skeleton source "<<*nameiter <<std::endl;
|
||||
destroyObjectList(sceneMgr, mAnimationSources.back());
|
||||
mAnimationSources.pop_back();
|
||||
continue;
|
||||
}
|
||||
NifOgre::ObjectList &objects = mAnimationSources.back();
|
||||
|
||||
for(size_t i = 0;i < objects.mControllers.size();i++)
|
||||
{
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objects.mControllers[i].getDestination().getPointer());
|
||||
if(!dstval) continue;
|
||||
|
||||
const Ogre::String &trgtname = dstval->getNode()->getName();
|
||||
if(!skelinst->hasBone(trgtname)) continue;
|
||||
|
||||
Ogre::Bone *bone = skelinst->getBone(trgtname);
|
||||
dstval->setNode(bone);
|
||||
}
|
||||
|
||||
for(size_t i = 0;i < objects.mControllers.size();i++)
|
||||
{
|
||||
if(objects.mControllers[i].getSource().isNull())
|
||||
objects.mControllers[i].setSource(ctrlval);
|
||||
}
|
||||
|
||||
Ogre::Entity *ent = objects.mSkelBase;
|
||||
Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(ent->getSkeleton()->getName());
|
||||
Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator();
|
||||
while(boneiter.hasMoreElements())
|
||||
{
|
||||
Ogre::Bone *bone = boneiter.getNext();
|
||||
Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings();
|
||||
const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID);
|
||||
if(data.isEmpty() || !Ogre::any_cast<bool>(data))
|
||||
continue;
|
||||
|
||||
if(!mNonAccumRoot)
|
||||
{
|
||||
mAccumRoot = mInsert;
|
||||
mNonAccumRoot = mObjectList.mSkelBase->getSkeleton()->getBone(bone->getName());
|
||||
}
|
||||
|
||||
for(int i = 0;i < skel->getNumAnimations();i++)
|
||||
{
|
||||
Ogre::Animation *anim = skel->getAnimation(i);
|
||||
const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+
|
||||
"@"+anim->getName());
|
||||
if(!groupdata.isEmpty())
|
||||
mTextKeys[anim->getName()] = Ogre::any_cast<NifOgre::TextKeyMap>(groupdata);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model)
|
||||
{
|
||||
mInsert = node->createChildSceneNode();
|
||||
assert(mInsert);
|
||||
}
|
||||
|
||||
mObjectList = NifOgre::Loader::createObjects(mInsert, model);
|
||||
if(mObjectList.mSkelBase)
|
||||
mObjects.push_back(ObjectInfo());
|
||||
ObjectInfo &obj = mObjects.back();
|
||||
obj.mActiveLayers = 0;
|
||||
obj.mObjectList = (!baseonly ? NifOgre::Loader::createObjects(mInsert, model) :
|
||||
NifOgre::Loader::createObjectBase(mInsert, model));
|
||||
|
||||
NifOgre::ObjectList &objlist = obj.mObjectList;
|
||||
if(objlist.mSkelBase)
|
||||
{
|
||||
Ogre::AnimationStateSet *aset = mObjectList.mSkelBase->getAllAnimationStates();
|
||||
if(mObjects.size() == 1)
|
||||
mSkelBase = objlist.mSkelBase;
|
||||
|
||||
Ogre::AnimationStateSet *aset = objlist.mSkelBase->getAllAnimationStates();
|
||||
Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator();
|
||||
while(asiter.hasMoreElements())
|
||||
{
|
||||
|
@ -164,27 +121,184 @@ void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model
|
|||
// Set the bones as manually controlled since we're applying the
|
||||
// transformations manually (needed if we want to apply an animation
|
||||
// from one skeleton onto another).
|
||||
Ogre::SkeletonInstance *skelinst = mObjectList.mSkelBase->getSkeleton();
|
||||
Ogre::SkeletonInstance *skelinst = objlist.mSkelBase->getSkeleton();
|
||||
Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator();
|
||||
while(boneiter.hasMoreElements())
|
||||
boneiter.getNext()->setManuallyControlled(true);
|
||||
}
|
||||
|
||||
Ogre::SharedPtr<Ogre::ControllerValue<Ogre::Real> > ctrlval(OGRE_NEW AnimationValue(this));
|
||||
for(size_t i = 0;i < mObjectList.mControllers.size();i++)
|
||||
if(objlist.mSkelBase && mSkelBase)
|
||||
{
|
||||
if(mObjectList.mControllers[i].getSource().isNull())
|
||||
mObjectList.mControllers[i].setSource(ctrlval);
|
||||
Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
|
||||
if(mSkelBase == objlist.mSkelBase)
|
||||
{
|
||||
if(objlist.mTextKeys.size() > 0)
|
||||
{
|
||||
mAccumRoot = mInsert;
|
||||
mNonAccumRoot = baseinst->getBone(objlist.mTextKeys.begin()->first);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(size_t i = 0;i < objlist.mControllers.size();i++)
|
||||
{
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
|
||||
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objlist.mControllers[i].getDestination().getPointer());
|
||||
if(!dstval) continue;
|
||||
|
||||
const Ogre::String &trgtname = dstval->getNode()->getName();
|
||||
if(!baseinst->hasBone(trgtname)) continue;
|
||||
|
||||
Ogre::Bone *bone = baseinst->getBone(trgtname);
|
||||
dstval->setNode(bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < objlist.mControllers.size();i++)
|
||||
{
|
||||
if(objlist.mControllers[i].getSource().isNull())
|
||||
objlist.mControllers[i].setSource(mAnimationValuePtr[0]);
|
||||
}
|
||||
|
||||
mActiveCtrls.insert(mActiveCtrls.end(), objlist.mControllers.begin(), objlist.mControllers.end());
|
||||
}
|
||||
|
||||
void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue)
|
||||
{
|
||||
for(size_t i = 0;i < objlist.mEntities.size();i++)
|
||||
{
|
||||
Ogre::Entity *ent = objlist.mEntities[i];
|
||||
if(visflags != 0)
|
||||
ent->setVisibilityFlags(visflags);
|
||||
|
||||
for(unsigned int j = 0;j < ent->getNumSubEntities();++j)
|
||||
{
|
||||
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
|
||||
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? transqueue : solidqueue);
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < objlist.mParticles.size();i++)
|
||||
{
|
||||
Ogre::ParticleSystem *part = objlist.mParticles[i];
|
||||
if(visflags != 0)
|
||||
part->setVisibilityFlags(visflags);
|
||||
// TODO: Check particle material for actual transparency
|
||||
part->setRenderQueueGroup(transqueue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Animation::clearExtraSources()
|
||||
{
|
||||
for(size_t layer = 0;layer < sMaxLayers;layer++)
|
||||
{
|
||||
mLayer[layer].mGroupName.clear();
|
||||
mLayer[layer].mTextKeys = NULL;
|
||||
mLayer[layer].mControllers = NULL;
|
||||
mLayer[layer].mTime = 0.0f;
|
||||
mLayer[layer].mLoopCount = 0;
|
||||
mLayer[layer].mPlaying = false;
|
||||
}
|
||||
mNonAccumCtrl = NULL;
|
||||
mAnimVelocity = 0.0f;
|
||||
|
||||
mLastPosition = Ogre::Vector3(0.0f);
|
||||
if(mAccumRoot)
|
||||
mAccumRoot->setPosition(mLastPosition);
|
||||
|
||||
if(mObjects.size() > 1)
|
||||
{
|
||||
mObjects.resize(1);
|
||||
mObjects[0].mActiveLayers = 0;
|
||||
|
||||
NifOgre::ObjectList &objlist = mObjects[0].mObjectList;
|
||||
mActiveCtrls.clear();
|
||||
mActiveCtrls.insert(mActiveCtrls.end(), objlist.mControllers.begin(), objlist.mControllers.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Animation::updateActiveControllers()
|
||||
{
|
||||
mActiveCtrls.clear();
|
||||
|
||||
/* First, get all controllers that don't target a node, or that target
|
||||
* nodes that don't belong to any particular layer.
|
||||
*/
|
||||
std::vector<ObjectInfo>::iterator obj(mObjects.begin());
|
||||
for(;obj != mObjects.end();obj++)
|
||||
{
|
||||
std::vector<Ogre::Controller<Ogre::Real> >::const_iterator ctrl(obj->mObjectList.mControllers.begin());
|
||||
for(;ctrl != obj->mObjectList.mControllers.end();ctrl++)
|
||||
{
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
|
||||
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(ctrl->getDestination().getPointer());
|
||||
if(dstval)
|
||||
{
|
||||
/*if(getLayerByName(dstval->getNode()->getName()) >= 0)*/
|
||||
continue;
|
||||
}
|
||||
mActiveCtrls.insert(mActiveCtrls.end(), *ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Ogre::Controller<Ogre::Real> > *ctrls = NULL;
|
||||
size_t layer = 0;
|
||||
while(layer < sMaxLayers)
|
||||
{
|
||||
/* Now get controllers that target nodes that belong to this layer from
|
||||
* whatever objectlist is active on this layer.
|
||||
*/
|
||||
std::vector<ObjectInfo>::iterator obj(mObjects.begin());
|
||||
for(;obj != mObjects.end();obj++)
|
||||
{
|
||||
if((obj->mActiveLayers&(1<<layer)))
|
||||
{
|
||||
ctrls = &obj->mObjectList.mControllers;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(ctrls == NULL)
|
||||
{
|
||||
layer++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if any objectlists are active on subsequent layers. Include
|
||||
* those layers if not.
|
||||
*/
|
||||
size_t nextlayer = layer+1;
|
||||
for(;nextlayer < sMaxLayers;nextlayer++)
|
||||
{
|
||||
for(obj = mObjects.begin();obj != mObjects.end();obj++)
|
||||
{
|
||||
if((obj->mActiveLayers&(1<<nextlayer)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Ogre::Controller<Ogre::Real> >::const_iterator ctrl(ctrls->begin());
|
||||
for(;ctrl != ctrls->end();ctrl++)
|
||||
{
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
|
||||
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(ctrl->getDestination().getPointer());
|
||||
if(dstval)
|
||||
{
|
||||
/*ssize_t idx = getLayerByName(dstval->getNode()->getName());
|
||||
if(idx >= (ssize_t)layer && idx < (ssize_t)nextlayer)*/
|
||||
mActiveCtrls.insert(mActiveCtrls.end(), *ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
layer = nextlayer;
|
||||
}
|
||||
mCurrentControllers = &mObjectList.mControllers;
|
||||
}
|
||||
|
||||
|
||||
Ogre::Node *Animation::getNode(const std::string &name)
|
||||
{
|
||||
if(mObjectList.mSkelBase)
|
||||
if(mSkelBase)
|
||||
{
|
||||
Ogre::SkeletonInstance *skel = mObjectList.mSkelBase->getSkeleton();
|
||||
Ogre::SkeletonInstance *skel = mSkelBase->getSkeleton();
|
||||
if(skel->hasBone(name))
|
||||
return skel->getBone(name);
|
||||
}
|
||||
|
@ -192,20 +306,34 @@ Ogre::Node *Animation::getNode(const std::string &name)
|
|||
}
|
||||
|
||||
|
||||
bool Animation::hasAnimation(const std::string &anim)
|
||||
NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname)
|
||||
{
|
||||
for(std::vector<NifOgre::ObjectList>::const_iterator iter(mAnimationSources.begin());iter != mAnimationSources.end();iter++)
|
||||
NifOgre::TextKeyMap::const_iterator iter(keys.begin());
|
||||
for(;iter != keys.end();iter++)
|
||||
{
|
||||
if(iter->mSkelBase->hasAnimationState(anim))
|
||||
return true;
|
||||
if(iter->second.compare(0, groupname.size(), groupname) == 0 &&
|
||||
iter->second.compare(groupname.size(), 2, ": ") == 0)
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
return iter;
|
||||
}
|
||||
|
||||
|
||||
void Animation::setController(MWMechanics::CharacterController *controller)
|
||||
bool Animation::hasAnimation(const std::string &anim)
|
||||
{
|
||||
mController = controller;
|
||||
if(!mSkelBase)
|
||||
return false;
|
||||
|
||||
for(std::vector<ObjectInfo>::const_iterator iter(mObjects.begin());iter != mObjects.end();iter++)
|
||||
{
|
||||
if(iter->mObjectList.mTextKeys.size() == 0)
|
||||
continue;
|
||||
|
||||
const NifOgre::TextKeyMap &keys = iter->mObjectList.mTextKeys.begin()->second;
|
||||
if(findGroupStart(keys, anim) != keys.end())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -221,10 +349,6 @@ void Animation::setSpeed(float speed)
|
|||
mAnimSpeedMult = speed / mAnimVelocity;
|
||||
}
|
||||
|
||||
void Animation::setLooping(bool loop)
|
||||
{
|
||||
mLooping = loop;
|
||||
}
|
||||
|
||||
void Animation::updatePtr(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
|
@ -232,47 +356,36 @@ void Animation::updatePtr(const MWWorld::Ptr &ptr)
|
|||
}
|
||||
|
||||
|
||||
void Animation::calcAnimVelocity()
|
||||
float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl, const Ogre::Vector3 &accum, const std::string &groupname)
|
||||
{
|
||||
const Ogre::NodeAnimationTrack *track = 0;
|
||||
|
||||
Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator();
|
||||
while(!track && trackiter.hasMoreElements())
|
||||
const std::string start = groupname+": start";
|
||||
const std::string loopstart = groupname+": loop start";
|
||||
const std::string loopstop = groupname+": loop stop";
|
||||
const std::string stop = groupname+": stop";
|
||||
float starttime = std::numeric_limits<float>::max();
|
||||
float stoptime = 0.0f;
|
||||
NifOgre::TextKeyMap::const_iterator keyiter(keys.begin());
|
||||
while(keyiter != keys.end())
|
||||
{
|
||||
const Ogre::NodeAnimationTrack *cur = trackiter.getNext();
|
||||
if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName())
|
||||
track = cur;
|
||||
}
|
||||
|
||||
if(track && track->getNumKeyFrames() > 1)
|
||||
if(keyiter->second == start || keyiter->second == loopstart)
|
||||
starttime = keyiter->first;
|
||||
else if(keyiter->second == loopstop || keyiter->second == stop)
|
||||
{
|
||||
float loopstarttime = 0.0f;
|
||||
float loopstoptime = mCurrentAnim->getLength();
|
||||
NifOgre::TextKeyMap::const_iterator keyiter = mCurrentKeys->begin();
|
||||
while(keyiter != mCurrentKeys->end())
|
||||
{
|
||||
if(keyiter->second == "loop start")
|
||||
loopstarttime = keyiter->first;
|
||||
else if(keyiter->second == "loop stop")
|
||||
{
|
||||
loopstoptime = keyiter->first;
|
||||
stoptime = keyiter->first;
|
||||
break;
|
||||
}
|
||||
keyiter++;
|
||||
}
|
||||
|
||||
if(loopstoptime > loopstarttime)
|
||||
if(stoptime > starttime)
|
||||
{
|
||||
Ogre::TransformKeyFrame startkf(0, loopstarttime);
|
||||
Ogre::TransformKeyFrame endkf(0, loopstoptime);
|
||||
Ogre::Vector3 startpos = nonaccumctrl->getTranslation(starttime) * accum;
|
||||
Ogre::Vector3 endpos = nonaccumctrl->getTranslation(stoptime) * accum;
|
||||
|
||||
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstarttime), &startkf);
|
||||
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstoptime), &endkf);
|
||||
return startpos.distance(endpos) / (stoptime-starttime);
|
||||
}
|
||||
|
||||
mAnimVelocity = startkf.getTranslate().distance(endkf.getTranslate()) /
|
||||
(loopstoptime-loopstarttime);
|
||||
}
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone)
|
||||
|
@ -313,93 +426,90 @@ void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Og
|
|||
}
|
||||
|
||||
|
||||
Ogre::Vector3 Animation::updatePosition()
|
||||
void Animation::updatePosition(Ogre::Vector3 &position)
|
||||
{
|
||||
Ogre::Vector3 posdiff;
|
||||
|
||||
Ogre::TransformKeyFrame kf(0, mCurrentTime);
|
||||
Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator();
|
||||
while(trackiter.hasMoreElements())
|
||||
{
|
||||
const Ogre::NodeAnimationTrack *track = trackiter.getNext();
|
||||
if(track->getAssociatedNode()->getName() == mNonAccumRoot->getName())
|
||||
{
|
||||
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the non-accumulation root's difference from the last update. */
|
||||
posdiff = (kf.getTranslate() - mLastPosition) * mAccumulate;
|
||||
/* Get the non-accumulation root's difference from the last update, and move the position
|
||||
* accordingly.
|
||||
*/
|
||||
posdiff = (mNonAccumCtrl->getTranslation(mLayer[0].mTime) - mLastPosition) * mAccumulate;
|
||||
position += posdiff;
|
||||
|
||||
/* Translate the accumulation root back to compensate for the move. */
|
||||
mLastPosition += posdiff;
|
||||
mAccumRoot->setPosition(-mLastPosition);
|
||||
|
||||
return posdiff;
|
||||
}
|
||||
|
||||
void Animation::reset(const std::string &start, const std::string &stop)
|
||||
bool Animation::reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint)
|
||||
{
|
||||
mNextKey = mCurrentKeys->begin();
|
||||
|
||||
while(mNextKey != mCurrentKeys->end() && mNextKey->second != start)
|
||||
mNextKey++;
|
||||
if(mNextKey != mCurrentKeys->end())
|
||||
mCurrentTime = mNextKey->first;
|
||||
else
|
||||
std::string tag = groupname+": "+start;
|
||||
NifOgre::TextKeyMap::const_iterator startkey(keys.begin());
|
||||
while(startkey != keys.end() && startkey->second != tag)
|
||||
startkey++;
|
||||
if(startkey == keys.end() && start == "loop start")
|
||||
{
|
||||
mNextKey = mCurrentKeys->begin();
|
||||
while(mNextKey != mCurrentKeys->end() && mNextKey->second != "start")
|
||||
mNextKey++;
|
||||
if(mNextKey != mCurrentKeys->end())
|
||||
mCurrentTime = mNextKey->first;
|
||||
else
|
||||
{
|
||||
mNextKey = mCurrentKeys->begin();
|
||||
mCurrentTime = 0.0f;
|
||||
tag = groupname+": start";
|
||||
startkey = keys.begin();
|
||||
while(startkey != keys.end() && startkey->second != tag)
|
||||
startkey++;
|
||||
}
|
||||
if(startkey == keys.end())
|
||||
return false;
|
||||
|
||||
tag = groupname+": "+stop;
|
||||
NifOgre::TextKeyMap::const_iterator stopkey(startkey);
|
||||
while(stopkey != keys.end() && stopkey->second != tag)
|
||||
stopkey++;
|
||||
if(stopkey == keys.end())
|
||||
return false;
|
||||
|
||||
if(startkey == stopkey)
|
||||
return false;
|
||||
|
||||
mLayer[layeridx].mStartKey = startkey;
|
||||
mLayer[layeridx].mLoopStartKey = startkey;
|
||||
mLayer[layeridx].mStopKey = stopkey;
|
||||
mLayer[layeridx].mNextKey = startkey;
|
||||
|
||||
mLayer[layeridx].mTime = mLayer[layeridx].mStartKey->first + ((mLayer[layeridx].mStopKey->first-
|
||||
mLayer[layeridx].mStartKey->first) * startpoint);
|
||||
|
||||
tag = groupname+": loop start";
|
||||
while(mLayer[layeridx].mNextKey->first <= mLayer[layeridx].mTime && mLayer[layeridx].mNextKey != mLayer[layeridx].mStopKey)
|
||||
{
|
||||
if(mLayer[layeridx].mNextKey->second == tag)
|
||||
mLayer[layeridx].mLoopStartKey = mLayer[layeridx].mNextKey;
|
||||
mLayer[layeridx].mNextKey++;
|
||||
}
|
||||
|
||||
if(stop.length() > 0)
|
||||
{
|
||||
NifOgre::TextKeyMap::const_iterator stopKey = mNextKey;
|
||||
while(stopKey != mCurrentKeys->end() && stopKey->second != stop)
|
||||
stopKey++;
|
||||
if(stopKey != mCurrentKeys->end())
|
||||
mStopTime = stopKey->first;
|
||||
else
|
||||
mStopTime = mCurrentAnim->getLength();
|
||||
}
|
||||
if(layeridx == 0 && nonaccumctrl)
|
||||
mLastPosition = nonaccumctrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate;
|
||||
|
||||
if(mNonAccumRoot)
|
||||
{
|
||||
const Ogre::NodeAnimationTrack *track = 0;
|
||||
Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator();
|
||||
while(!track && trackiter.hasMoreElements())
|
||||
{
|
||||
const Ogre::NodeAnimationTrack *cur = trackiter.getNext();
|
||||
if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName())
|
||||
track = cur;
|
||||
}
|
||||
|
||||
if(track)
|
||||
{
|
||||
Ogre::TransformKeyFrame kf(0, mCurrentTime);
|
||||
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf);
|
||||
mLastPosition = kf.getTranslate() * mAccumulate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Animation::handleEvent(float time, const std::string &evt)
|
||||
{
|
||||
if(evt == "start" || evt == "loop start")
|
||||
{
|
||||
/* Do nothing */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Animation::doLoop(size_t layeridx)
|
||||
{
|
||||
if(mLayer[layeridx].mLoopCount == 0)
|
||||
return false;
|
||||
mLayer[layeridx].mLoopCount--;
|
||||
|
||||
mLayer[layeridx].mTime = mLayer[layeridx].mLoopStartKey->first;
|
||||
mLayer[layeridx].mNextKey = mLayer[layeridx].mLoopStartKey;
|
||||
mLayer[layeridx].mNextKey++;
|
||||
mLayer[layeridx].mPlaying = true;
|
||||
if(layeridx == 0 && mNonAccumCtrl)
|
||||
mLastPosition = mNonAccumCtrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Animation::handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key)
|
||||
{
|
||||
float time = key->first;
|
||||
const std::string &evt = key->second;
|
||||
|
||||
if(evt.compare(0, 7, "sound: ") == 0)
|
||||
{
|
||||
|
@ -414,108 +524,219 @@ bool Animation::handleEvent(float time, const std::string &evt)
|
|||
return true;
|
||||
}
|
||||
|
||||
if(evt == "loop stop")
|
||||
if(evt.compare(0, mLayer[layeridx].mGroupName.size(), mLayer[layeridx].mGroupName) != 0 ||
|
||||
evt.compare(mLayer[layeridx].mGroupName.size(), 2, ": ") != 0)
|
||||
{
|
||||
if(mLooping)
|
||||
// Not ours, skip it
|
||||
return true;
|
||||
}
|
||||
size_t off = mLayer[layeridx].mGroupName.size()+2;
|
||||
size_t len = evt.size() - off;
|
||||
|
||||
if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0)
|
||||
{
|
||||
reset("loop start", "");
|
||||
if(mCurrentTime >= time)
|
||||
mLayer[layeridx].mLoopStartKey = key;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0)
|
||||
{
|
||||
if(doLoop(layeridx))
|
||||
{
|
||||
if(mLayer[layeridx].mTime >= time)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if(evt == "stop")
|
||||
{
|
||||
if(mLooping)
|
||||
{
|
||||
reset("loop start", "");
|
||||
if(mCurrentTime >= time)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
// fall-through
|
||||
}
|
||||
if(mController)
|
||||
mController->markerEvent(time, evt);
|
||||
|
||||
std::cerr<< "Unhandled animation textkey: "<<evt <<std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop)
|
||||
bool Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops)
|
||||
{
|
||||
try {
|
||||
bool found = false;
|
||||
// TODO: parameterize this
|
||||
size_t layeridx = 0;
|
||||
|
||||
if(!mSkelBase)
|
||||
return false;
|
||||
|
||||
for(std::vector<ObjectInfo>::iterator iter(mObjects.begin());iter != mObjects.end();iter++)
|
||||
iter->mActiveLayers &= ~(1<<layeridx);
|
||||
|
||||
mLayer[layeridx].mGroupName.clear();
|
||||
mLayer[layeridx].mTextKeys = NULL;
|
||||
mLayer[layeridx].mControllers = NULL;
|
||||
mLayer[layeridx].mTime = 0.0f;
|
||||
mLayer[layeridx].mLoopCount = 0;
|
||||
mLayer[layeridx].mPlaying = false;
|
||||
|
||||
bool movinganim = false;
|
||||
bool foundanim = false;
|
||||
|
||||
/* Look in reverse; last-inserted source has priority. */
|
||||
for(std::vector<NifOgre::ObjectList>::reverse_iterator iter(mAnimationSources.rbegin());iter != mAnimationSources.rend();iter++)
|
||||
for(std::vector<ObjectInfo>::reverse_iterator iter(mObjects.rbegin());iter != mObjects.rend();iter++)
|
||||
{
|
||||
if(iter->mSkelBase->hasAnimationState(groupname))
|
||||
NifOgre::ObjectList &objlist = iter->mObjectList;
|
||||
if(objlist.mTextKeys.size() == 0)
|
||||
continue;
|
||||
|
||||
const NifOgre::TextKeyMap &keys = objlist.mTextKeys.begin()->second;
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl = NULL;
|
||||
if(layeridx == 0 && mNonAccumRoot)
|
||||
{
|
||||
mCurrentAnim = iter->mSkelBase->getSkeleton()->getAnimation(groupname);
|
||||
mCurrentKeys = &mTextKeys[groupname];
|
||||
mCurrentControllers = &iter->mControllers;
|
||||
mAnimVelocity = 0.0f;
|
||||
|
||||
if(mNonAccumRoot)
|
||||
calcAnimVelocity();
|
||||
|
||||
found = true;
|
||||
for(size_t i = 0;i < objlist.mControllers.size();i++)
|
||||
{
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
|
||||
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objlist.mControllers[i].getDestination().getPointer());
|
||||
if(dstval && dstval->getNode() == mNonAccumRoot)
|
||||
{
|
||||
nonaccumctrl = dstval;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
throw std::runtime_error("Failed to find animation "+groupname);
|
||||
}
|
||||
|
||||
reset(start, stop);
|
||||
setLooping(loop);
|
||||
mPlaying = true;
|
||||
if(!foundanim)
|
||||
{
|
||||
if(!reset(layeridx, keys, nonaccumctrl, groupname, start, stop, startpoint))
|
||||
continue;
|
||||
|
||||
mLayer[layeridx].mGroupName = groupname;
|
||||
mLayer[layeridx].mTextKeys = &keys;
|
||||
mLayer[layeridx].mControllers = &objlist.mControllers;
|
||||
mLayer[layeridx].mLoopCount = loops;
|
||||
mLayer[layeridx].mPlaying = true;
|
||||
|
||||
if(layeridx == 0)
|
||||
{
|
||||
mNonAccumCtrl = nonaccumctrl;
|
||||
mAnimVelocity = 0.0f;
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
std::cerr<< e.what() <<std::endl;
|
||||
|
||||
iter->mActiveLayers |= (1<<layeridx);
|
||||
foundanim = true;
|
||||
|
||||
if(mAccumulate == Ogre::Vector3(0.0f))
|
||||
break;
|
||||
}
|
||||
|
||||
if(!nonaccumctrl)
|
||||
break;
|
||||
|
||||
mAnimVelocity = calcAnimVelocity(keys, nonaccumctrl, mAccumulate, groupname);
|
||||
if(mAnimVelocity > 1.0f)
|
||||
{
|
||||
movinganim = (nonaccumctrl==mNonAccumCtrl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!foundanim)
|
||||
std::cerr<< "Failed to find animation "<<groupname <<std::endl;
|
||||
|
||||
updateActiveControllers();
|
||||
|
||||
return movinganim;
|
||||
}
|
||||
|
||||
Ogre::Vector3 Animation::runAnimation(float timepassed)
|
||||
void Animation::disable(size_t layeridx)
|
||||
{
|
||||
if(mLayer[layeridx].mGroupName.empty())
|
||||
return;
|
||||
|
||||
for(std::vector<ObjectInfo>::iterator iter(mObjects.begin());iter != mObjects.end();iter++)
|
||||
iter->mActiveLayers &= ~(1<<layeridx);
|
||||
|
||||
mLayer[layeridx].mGroupName.clear();
|
||||
mLayer[layeridx].mTextKeys = NULL;
|
||||
mLayer[layeridx].mControllers = NULL;
|
||||
mLayer[layeridx].mTime = 0.0f;
|
||||
mLayer[layeridx].mLoopCount = 0;
|
||||
mLayer[layeridx].mPlaying = false;
|
||||
|
||||
updateActiveControllers();
|
||||
}
|
||||
|
||||
bool Animation::getInfo(size_t layeridx, float *complete, std::string *groupname, std::string *start, std::string *stop) const
|
||||
{
|
||||
if(mLayer[layeridx].mGroupName.empty())
|
||||
{
|
||||
if(complete) *complete = 0.0f;
|
||||
if(groupname) *groupname = "";
|
||||
if(start) *start = "";
|
||||
if(stop) *stop = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(complete) *complete = (mLayer[layeridx].mTime - mLayer[layeridx].mStartKey->first) /
|
||||
(mLayer[layeridx].mStopKey->first - mLayer[layeridx].mStartKey->first);
|
||||
if(groupname) *groupname = mLayer[layeridx].mGroupName;
|
||||
if(start) *start = mLayer[layeridx].mStartKey->second.substr(mLayer[layeridx].mGroupName.size()+2);
|
||||
if(stop) *stop = mLayer[layeridx].mStopKey->second.substr(mLayer[layeridx].mGroupName.size()+2);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Ogre::Vector3 Animation::runAnimation(float duration)
|
||||
{
|
||||
Ogre::Vector3 movement(0.0f);
|
||||
|
||||
timepassed *= mAnimSpeedMult;
|
||||
while(mCurrentAnim && mPlaying)
|
||||
duration *= mAnimSpeedMult;
|
||||
for(size_t layeridx = 0;layeridx < sMaxLayers;layeridx++)
|
||||
{
|
||||
float targetTime = mCurrentTime + timepassed;
|
||||
if(mNextKey == mCurrentKeys->end() || mNextKey->first > targetTime)
|
||||
if(mLayer[layeridx].mGroupName.empty())
|
||||
continue;
|
||||
|
||||
float timepassed = duration;
|
||||
while(mLayer[layeridx].mPlaying)
|
||||
{
|
||||
mCurrentTime = std::min(mStopTime, targetTime);
|
||||
if(mNonAccumRoot)
|
||||
movement += updatePosition();
|
||||
mPlaying = (mLooping || mStopTime > mCurrentTime);
|
||||
timepassed = targetTime - mCurrentTime;
|
||||
float targetTime = mLayer[layeridx].mTime + timepassed;
|
||||
if(mLayer[layeridx].mNextKey->first > targetTime)
|
||||
{
|
||||
mLayer[layeridx].mTime = targetTime;
|
||||
if(layeridx == 0 && mNonAccumCtrl)
|
||||
updatePosition(movement);
|
||||
break;
|
||||
}
|
||||
|
||||
float time = mNextKey->first;
|
||||
const std::string &evt = mNextKey->second;
|
||||
mNextKey++;
|
||||
NifOgre::TextKeyMap::const_iterator key(mLayer[layeridx].mNextKey++);
|
||||
mLayer[layeridx].mTime = key->first;
|
||||
if(layeridx == 0 && mNonAccumCtrl)
|
||||
updatePosition(movement);
|
||||
|
||||
mCurrentTime = time;
|
||||
if(mNonAccumRoot)
|
||||
movement += updatePosition();
|
||||
mPlaying = (mLooping || mStopTime > mCurrentTime);
|
||||
timepassed = targetTime - mCurrentTime;
|
||||
mLayer[layeridx].mPlaying = (key != mLayer[layeridx].mStopKey);
|
||||
timepassed = targetTime - mLayer[layeridx].mTime;
|
||||
|
||||
if(!handleEvent(time, evt))
|
||||
if(!handleTextKey(layeridx, key))
|
||||
break;
|
||||
}
|
||||
for(size_t i = 0;i < mCurrentControllers->size();i++)
|
||||
(*mCurrentControllers)[i].update();
|
||||
}
|
||||
|
||||
if(mObjectList.mSkelBase)
|
||||
for(size_t i = 0;i < mActiveCtrls.size();i++)
|
||||
mActiveCtrls[i].update();
|
||||
|
||||
if(mSkelBase)
|
||||
{
|
||||
const Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
|
||||
for(std::vector<ObjectInfo>::iterator iter(mObjects.begin());iter != mObjects.end();iter++)
|
||||
{
|
||||
Ogre::Entity *ent = iter->mObjectList.mSkelBase;
|
||||
if(!ent) continue;
|
||||
|
||||
Ogre::SkeletonInstance *inst = ent->getSkeleton();
|
||||
if(baseinst != inst)
|
||||
updateSkeletonInstance(baseinst, inst);
|
||||
|
||||
// HACK: Dirty the animation state set so that Ogre will apply the
|
||||
// transformations to entities this skeleton instance is shared with.
|
||||
mObjectList.mSkelBase->getAllAnimationStates()->_notifyDirty();
|
||||
ent->getAllAnimationStates()->_notifyDirty();
|
||||
}
|
||||
}
|
||||
|
||||
return movement;
|
||||
}
|
||||
|
||||
void Animation::showWeapons(bool showWeapon){}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,10 +8,6 @@
|
|||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
class CharacterController;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
@ -23,48 +19,67 @@ protected:
|
|||
{
|
||||
private:
|
||||
Animation *mAnimation;
|
||||
size_t mIndex;
|
||||
|
||||
public:
|
||||
AnimationValue(Animation *anim) : mAnimation(anim)
|
||||
AnimationValue(Animation *anim, size_t layeridx)
|
||||
: mAnimation(anim), mIndex(layeridx)
|
||||
{ }
|
||||
|
||||
virtual Ogre::Real getValue() const
|
||||
{
|
||||
return mAnimation->mCurrentTime;
|
||||
}
|
||||
virtual Ogre::Real getValue() const;
|
||||
virtual void setValue(Ogre::Real value);
|
||||
};
|
||||
|
||||
virtual void setValue(Ogre::Real value)
|
||||
{
|
||||
mAnimation->mCurrentTime = value;
|
||||
}
|
||||
struct ObjectInfo {
|
||||
NifOgre::ObjectList mObjectList;
|
||||
/* Bit-field specifying which animation layers this object list is
|
||||
* explicitly animating on (1 = layer 0, 2 = layer 1, 4 = layer 2.
|
||||
* etc).
|
||||
*/
|
||||
int mActiveLayers;
|
||||
};
|
||||
|
||||
struct AnimLayer {
|
||||
std::string mGroupName;
|
||||
std::vector<Ogre::Controller<Ogre::Real> > *mControllers;
|
||||
const NifOgre::TextKeyMap *mTextKeys;
|
||||
NifOgre::TextKeyMap::const_iterator mStartKey;
|
||||
NifOgre::TextKeyMap::const_iterator mLoopStartKey;
|
||||
NifOgre::TextKeyMap::const_iterator mStopKey;
|
||||
NifOgre::TextKeyMap::const_iterator mNextKey;
|
||||
|
||||
float mTime;
|
||||
|
||||
bool mPlaying;
|
||||
size_t mLoopCount;
|
||||
|
||||
AnimLayer();
|
||||
};
|
||||
|
||||
MWWorld::Ptr mPtr;
|
||||
MWMechanics::CharacterController *mController;
|
||||
|
||||
Ogre::SceneNode* mInsert;
|
||||
NifOgre::ObjectList mObjectList;
|
||||
std::map<std::string,NifOgre::TextKeyMap> mTextKeys;
|
||||
Ogre::SceneNode *mInsert;
|
||||
Ogre::Entity *mSkelBase;
|
||||
std::vector<ObjectInfo> mObjects;
|
||||
Ogre::Node *mAccumRoot;
|
||||
Ogre::Bone *mNonAccumRoot;
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *mNonAccumCtrl;
|
||||
Ogre::Vector3 mAccumulate;
|
||||
Ogre::Vector3 mLastPosition;
|
||||
|
||||
std::vector<NifOgre::ObjectList> mAnimationSources;
|
||||
|
||||
std::vector<Ogre::Controller<Ogre::Real> > *mCurrentControllers;
|
||||
NifOgre::TextKeyMap *mCurrentKeys;
|
||||
NifOgre::TextKeyMap::const_iterator mNextKey;
|
||||
Ogre::Animation *mCurrentAnim;
|
||||
float mCurrentTime;
|
||||
float mStopTime;
|
||||
bool mPlaying;
|
||||
bool mLooping;
|
||||
std::vector<Ogre::Controller<Ogre::Real> > mActiveCtrls;
|
||||
|
||||
float mAnimVelocity;
|
||||
float mAnimSpeedMult;
|
||||
|
||||
void calcAnimVelocity();
|
||||
static const size_t sMaxLayers = 1;
|
||||
AnimLayer mLayer[sMaxLayers];
|
||||
Ogre::SharedPtr<Ogre::ControllerValue<Ogre::Real> > mAnimationValuePtr[sMaxLayers];
|
||||
|
||||
static float calcAnimVelocity(const NifOgre::TextKeyMap &keys,
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl,
|
||||
const Ogre::Vector3 &accum,
|
||||
const std::string &groupname);
|
||||
|
||||
/* Updates a skeleton instance so that all bones matching the source skeleton (based on
|
||||
* bone names) are positioned identically. */
|
||||
|
@ -72,34 +87,39 @@ protected:
|
|||
|
||||
/* Updates the position of the accum root node for the current time, and
|
||||
* returns the wanted movement vector from the previous update. */
|
||||
Ogre::Vector3 updatePosition();
|
||||
void updatePosition(Ogre::Vector3 &position);
|
||||
|
||||
static NifOgre::TextKeyMap::const_iterator findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname);
|
||||
|
||||
/* Resets the animation to the time of the specified start marker, without
|
||||
* moving anything, and set the end time to the specified stop marker. If
|
||||
* the marker is not found, it resets to the beginning or end respectively.
|
||||
* the marker is not found, or if the markers are the same, it returns
|
||||
* false.
|
||||
*/
|
||||
void reset(const std::string &start, const std::string &stop);
|
||||
bool reset(size_t layeridx, const NifOgre::TextKeyMap &keys,
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl,
|
||||
const std::string &groupname, const std::string &start, const std::string &stop,
|
||||
float startpoint);
|
||||
|
||||
bool handleEvent(float time, const std::string &evt);
|
||||
bool doLoop(size_t layeridx);
|
||||
|
||||
/* Specifies a list of skeleton names to use as animation sources. */
|
||||
void setAnimationSources(const std::vector<std::string> &names);
|
||||
bool handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key);
|
||||
|
||||
/* Specifies a single skeleton name to use as an animation source. */
|
||||
void setAnimationSource(const std::string &name)
|
||||
{
|
||||
std::vector<std::string> names(1, name);
|
||||
setAnimationSources(names);
|
||||
}
|
||||
|
||||
void createObjectList(Ogre::SceneNode *node, const std::string &model);
|
||||
void addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly);
|
||||
static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects);
|
||||
|
||||
static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue);
|
||||
|
||||
void updateActiveControllers();
|
||||
|
||||
public:
|
||||
Animation(const MWWorld::Ptr &ptr);
|
||||
virtual ~Animation();
|
||||
|
||||
void setController(MWMechanics::CharacterController *controller);
|
||||
/** Clears all ObjectLists except the first one. As a consequence, any
|
||||
* playing animations are stopped.
|
||||
*/
|
||||
void clearExtraSources();
|
||||
|
||||
void updatePtr(const MWWorld::Ptr &ptr);
|
||||
|
||||
|
@ -112,10 +132,40 @@ public:
|
|||
|
||||
void setSpeed(float speed);
|
||||
|
||||
void setLooping(bool loop);
|
||||
/** Plays an animation.
|
||||
* \param groupname Name of the animation group to play.
|
||||
* \param start Key marker from which to start.
|
||||
* \param stop Key marker to stop at.
|
||||
* \param startpoint How far in between the two markers to start. 0 starts
|
||||
* at the start marker, 1 starts at the stop marker.
|
||||
* \param loops How many times to loop the animation. This will use the
|
||||
* "loop start" and "loop stop" markers if they exist,
|
||||
* otherwise it will use "start" and "stop".
|
||||
* \return Boolean specifying whether the animation will return movement
|
||||
* for the character at all.
|
||||
*/
|
||||
bool play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops);
|
||||
|
||||
void play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop);
|
||||
virtual Ogre::Vector3 runAnimation(float timepassed);
|
||||
/** Stops and removes the animation from the given layer. */
|
||||
void disable(size_t layeridx);
|
||||
|
||||
/** Gets info about the given animation layer.
|
||||
* \param layeridx Layer index to get info about.
|
||||
* \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc.
|
||||
* \param groupname Stores animation group being played.
|
||||
* \param start Stores the start key
|
||||
* \param stop Stores the stop key
|
||||
* \return True if an animation is active on the layer, false otherwise.
|
||||
*/
|
||||
bool getInfo(size_t layeridx, float *complete=NULL, std::string *groupname=NULL, std::string *start=NULL, std::string *stop=NULL) const;
|
||||
|
||||
virtual Ogre::Vector3 runAnimation(float duration);
|
||||
|
||||
virtual void showWeapons(bool showWeapon);
|
||||
|
||||
/* Returns if there's an animation playing on the given layer. */
|
||||
bool isPlaying(size_t layeridx) const
|
||||
{ return mLayer[layeridx].mPlaying; }
|
||||
|
||||
Ogre::Node *getNode(const std::string &name);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#include "player.hpp"
|
||||
#include "camera.hpp"
|
||||
|
||||
#include <OgreSceneNode.h>
|
||||
#include <OgreCamera.h>
|
||||
#include <OgreSceneManager.h>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
@ -14,10 +15,9 @@
|
|||
|
||||
namespace MWRender
|
||||
{
|
||||
Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node)
|
||||
Camera::Camera (Ogre::Camera *camera)
|
||||
: mCamera(camera),
|
||||
mPlayerNode(node),
|
||||
mCameraNode(mPlayerNode->createChildSceneNode()),
|
||||
mCameraNode(NULL),
|
||||
mFirstPersonView(true),
|
||||
mPreviewMode(false),
|
||||
mFreeLook(true),
|
||||
|
@ -28,51 +28,16 @@ namespace MWRender
|
|||
{
|
||||
mVanity.enabled = false;
|
||||
mVanity.allowed = true;
|
||||
mVanity.forced = false;
|
||||
|
||||
mCameraNode->attachObject(mCamera);
|
||||
mCameraNode->setPosition(0.f, 0.f, mHeight);
|
||||
|
||||
mPreviewCam.yaw = 0.f;
|
||||
mPreviewCam.offset = 400.f;
|
||||
}
|
||||
|
||||
Player::~Player()
|
||||
Camera::~Camera()
|
||||
{
|
||||
delete mAnimation;
|
||||
}
|
||||
|
||||
bool Player::rotate(const Ogre::Vector3 &rot, bool adjust)
|
||||
{
|
||||
if (mVanity.enabled) {
|
||||
toggleVanityMode(false);
|
||||
}
|
||||
|
||||
Ogre::Vector3 trueRot = rot;
|
||||
|
||||
/// \note rotate player on forced vanity
|
||||
if (mVanity.forced) {
|
||||
if (mFreeLook) {
|
||||
float diff = (adjust) ? rot.z : mMainCam.yaw - rot.z;
|
||||
|
||||
mVanity.enabled = false;
|
||||
rotateCamera(rot, adjust);
|
||||
mVanity.enabled = true;
|
||||
|
||||
compensateYaw(diff);
|
||||
}
|
||||
trueRot.z = 0.f;
|
||||
}
|
||||
|
||||
if (mFreeLook || mVanity.enabled || mPreviewMode) {
|
||||
rotateCamera(trueRot, adjust);
|
||||
}
|
||||
|
||||
/// \note if vanity mode is forced by TVM then rotate player
|
||||
return (!mVanity.enabled && !mPreviewMode) || mVanity.forced;
|
||||
}
|
||||
|
||||
void Player::rotateCamera(const Ogre::Vector3 &rot, bool adjust)
|
||||
void Camera::rotateCamera(const Ogre::Vector3 &rot, bool adjust)
|
||||
{
|
||||
if (adjust) {
|
||||
setYaw(getYaw() + rot.z);
|
||||
|
@ -81,33 +46,37 @@ namespace MWRender
|
|||
setYaw(rot.z);
|
||||
setPitch(rot.x);
|
||||
}
|
||||
Ogre::Quaternion xr(
|
||||
Ogre::Radian(getPitch() + Ogre::Math::HALF_PI),
|
||||
Ogre::Vector3::UNIT_X
|
||||
);
|
||||
Ogre::Quaternion zr(
|
||||
Ogre::Radian(getYaw()),
|
||||
Ogre::Vector3::NEGATIVE_UNIT_Z
|
||||
);
|
||||
|
||||
Ogre::Quaternion xr(Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X);
|
||||
if (!mVanity.enabled && !mPreviewMode) {
|
||||
mPlayerNode->setOrientation(zr);
|
||||
mCameraNode->setOrientation(xr);
|
||||
} else {
|
||||
Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z);
|
||||
mCameraNode->setOrientation(zr * xr);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Player::getHandle() const
|
||||
const std::string &Camera::getHandle() const
|
||||
{
|
||||
return mPlayerNode->getName();
|
||||
return mTrackingPtr.getRefData().getHandle();
|
||||
}
|
||||
|
||||
void Player::attachTo(const MWWorld::Ptr &ptr)
|
||||
void Camera::attachTo(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
ptr.getRefData().setBaseNode(mPlayerNode);
|
||||
mTrackingPtr = ptr;
|
||||
Ogre::SceneNode *node = mTrackingPtr.getRefData().getBaseNode()->createChildSceneNode(Ogre::Vector3(0.0f, 0.0f, mHeight));
|
||||
if(mCameraNode)
|
||||
{
|
||||
node->setOrientation(mCameraNode->getOrientation());
|
||||
node->setPosition(mCameraNode->getPosition());
|
||||
node->setScale(mCameraNode->getScale());
|
||||
mCameraNode->getCreator()->destroySceneNode(mCameraNode);
|
||||
}
|
||||
mCameraNode = node;
|
||||
mCameraNode->attachObject(mCamera);
|
||||
}
|
||||
|
||||
void Player::updateListener()
|
||||
void Camera::updateListener()
|
||||
{
|
||||
Ogre::Vector3 pos = mCamera->getRealPosition();
|
||||
Ogre::Vector3 dir = mCamera->getRealDirection();
|
||||
|
@ -116,29 +85,27 @@ namespace MWRender
|
|||
MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up);
|
||||
}
|
||||
|
||||
void Player::update(float duration)
|
||||
void Camera::update(float duration)
|
||||
{
|
||||
updateListener();
|
||||
|
||||
// only show the crosshair in game mode and in first person mode.
|
||||
MWBase::Environment::get().getWindowManager ()->showCrosshair
|
||||
(!MWBase::Environment::get().getWindowManager ()->isGuiMode () && (mFirstPersonView && !mVanity.enabled && !mPreviewMode));
|
||||
MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();
|
||||
wm->showCrosshair(!wm->isGuiMode() && (mFirstPersonView && !mVanity.enabled && !mPreviewMode));
|
||||
|
||||
if (mFirstPersonView && !mVanity.enabled) {
|
||||
return;
|
||||
}
|
||||
if (mVanity.enabled) {
|
||||
if(mVanity.enabled)
|
||||
{
|
||||
Ogre::Vector3 rot(0.f, 0.f, 0.f);
|
||||
rot.z = Ogre::Degree(3.f * duration).valueRadians();
|
||||
rotateCamera(rot, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Player::toggleViewMode()
|
||||
void Camera::toggleViewMode()
|
||||
{
|
||||
mFirstPersonView = !mFirstPersonView;
|
||||
mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ?
|
||||
NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson);
|
||||
mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson :
|
||||
NpcAnimation::VM_Normal);
|
||||
if (mFirstPersonView) {
|
||||
mCamera->setPosition(0.f, 0.f, 0.f);
|
||||
setLowHeight(false);
|
||||
|
@ -148,28 +115,24 @@ namespace MWRender
|
|||
}
|
||||
}
|
||||
|
||||
void Player::allowVanityMode(bool allow)
|
||||
void Camera::allowVanityMode(bool allow)
|
||||
{
|
||||
if (!allow && mVanity.enabled && !mVanity.forced) {
|
||||
if (!allow && mVanity.enabled)
|
||||
toggleVanityMode(false);
|
||||
}
|
||||
mVanity.allowed = allow;
|
||||
}
|
||||
|
||||
bool Player::toggleVanityMode(bool enable, bool force)
|
||||
{
|
||||
if ((mVanity.forced && !force) ||
|
||||
(!mVanity.allowed && (force || enable)))
|
||||
bool Camera::toggleVanityMode(bool enable)
|
||||
{
|
||||
if(!mVanity.allowed && enable)
|
||||
return false;
|
||||
} else if (mVanity.enabled == enable) {
|
||||
return true;
|
||||
}
|
||||
mVanity.enabled = enable;
|
||||
mVanity.forced = force && enable;
|
||||
|
||||
mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ?
|
||||
NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson);
|
||||
if(mVanity.enabled == enable)
|
||||
return true;
|
||||
mVanity.enabled = enable;
|
||||
|
||||
mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson :
|
||||
NpcAnimation::VM_Normal);
|
||||
|
||||
float offset = mPreviewCam.offset;
|
||||
Ogre::Vector3 rot(0.f, 0.f, 0.f);
|
||||
|
@ -185,20 +148,22 @@ namespace MWRender
|
|||
setLowHeight(!mFirstPersonView);
|
||||
}
|
||||
rot.z = getYaw();
|
||||
|
||||
mCamera->setPosition(0.f, 0.f, offset);
|
||||
rotateCamera(rot, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Player::togglePreviewMode(bool enable)
|
||||
void Camera::togglePreviewMode(bool enable)
|
||||
{
|
||||
if (mPreviewMode == enable) {
|
||||
if(mPreviewMode == enable)
|
||||
return;
|
||||
}
|
||||
|
||||
mPreviewMode = enable;
|
||||
mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ?
|
||||
NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson);
|
||||
mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson :
|
||||
NpcAnimation::VM_Normal);
|
||||
|
||||
float offset = mCamera->getPosition().z;
|
||||
if (mPreviewMode) {
|
||||
mMainCam.offset = offset;
|
||||
|
@ -211,19 +176,19 @@ namespace MWRender
|
|||
|
||||
setLowHeight(!mFirstPersonView);
|
||||
}
|
||||
|
||||
mCamera->setPosition(0.f, 0.f, offset);
|
||||
rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false);
|
||||
}
|
||||
|
||||
float Player::getYaw()
|
||||
float Camera::getYaw()
|
||||
{
|
||||
if (mVanity.enabled || mPreviewMode) {
|
||||
if(mVanity.enabled || mPreviewMode)
|
||||
return mPreviewCam.yaw;
|
||||
}
|
||||
return mMainCam.yaw;
|
||||
}
|
||||
|
||||
void Player::setYaw(float angle)
|
||||
void Camera::setYaw(float angle)
|
||||
{
|
||||
if (angle > Ogre::Math::PI) {
|
||||
angle -= Ogre::Math::TWO_PI;
|
||||
|
@ -237,7 +202,7 @@ namespace MWRender
|
|||
}
|
||||
}
|
||||
|
||||
float Player::getPitch()
|
||||
float Camera::getPitch()
|
||||
{
|
||||
if (mVanity.enabled || mPreviewMode) {
|
||||
return mPreviewCam.pitch;
|
||||
|
@ -245,18 +210,18 @@ namespace MWRender
|
|||
return mMainCam.pitch;
|
||||
}
|
||||
|
||||
void Player::setPitch(float angle)
|
||||
void Camera::setPitch(float angle)
|
||||
{
|
||||
const float epsilon = 0.000001;
|
||||
const float epsilon = 0.000001f;
|
||||
float limit = Ogre::Math::HALF_PI - epsilon;
|
||||
if (mVanity.forced || mPreviewMode) {
|
||||
if(mPreviewMode)
|
||||
limit /= 2;
|
||||
}
|
||||
if (angle > limit) {
|
||||
|
||||
if(angle > limit)
|
||||
angle = limit;
|
||||
} else if (angle < -limit) {
|
||||
else if(angle < -limit)
|
||||
angle = -limit;
|
||||
}
|
||||
|
||||
if (mVanity.enabled || mPreviewMode) {
|
||||
mPreviewCam.pitch = angle;
|
||||
} else {
|
||||
|
@ -264,11 +229,11 @@ namespace MWRender
|
|||
}
|
||||
}
|
||||
|
||||
void Player::setCameraDistance(float dist, bool adjust, bool override)
|
||||
void Camera::setCameraDistance(float dist, bool adjust, bool override)
|
||||
{
|
||||
if (mFirstPersonView && !mPreviewMode && !mVanity.enabled) {
|
||||
if(mFirstPersonView && !mPreviewMode && !mVanity.enabled)
|
||||
return;
|
||||
}
|
||||
|
||||
Ogre::Vector3 v(0.f, 0.f, dist);
|
||||
if (adjust) {
|
||||
v += mCamera->getPosition();
|
||||
|
@ -293,7 +258,7 @@ namespace MWRender
|
|||
}
|
||||
}
|
||||
|
||||
void Player::setCameraDistance()
|
||||
void Camera::setCameraDistance()
|
||||
{
|
||||
if (mDistanceAdjusted) {
|
||||
if (mVanity.enabled || mPreviewMode) {
|
||||
|
@ -305,65 +270,54 @@ namespace MWRender
|
|||
mDistanceAdjusted = false;
|
||||
}
|
||||
|
||||
void Player::setAnimation(NpcAnimation *anim)
|
||||
void Camera::setAnimation(NpcAnimation *anim)
|
||||
{
|
||||
delete mAnimation;
|
||||
// If we're switching to a new NpcAnimation, ensure the old one is
|
||||
// using a normal view mode
|
||||
if(mAnimation && mAnimation != anim)
|
||||
mAnimation->setViewMode(NpcAnimation::VM_Normal);
|
||||
mAnimation = anim;
|
||||
|
||||
mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ?
|
||||
NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson);
|
||||
mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson :
|
||||
NpcAnimation::VM_Normal);
|
||||
}
|
||||
|
||||
void Player::setHeight(float height)
|
||||
void Camera::setHeight(float height)
|
||||
{
|
||||
mHeight = height;
|
||||
mCameraNode->setPosition(0.f, 0.f, mHeight);
|
||||
}
|
||||
|
||||
float Player::getHeight()
|
||||
float Camera::getHeight()
|
||||
{
|
||||
return mHeight * mPlayerNode->getScale().z;
|
||||
return mHeight * mTrackingPtr.getRefData().getBaseNode()->getScale().z;
|
||||
}
|
||||
|
||||
bool Player::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera)
|
||||
bool Camera::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera)
|
||||
{
|
||||
mCamera->getParentSceneNode ()->needUpdate(true);
|
||||
camera = mCamera->getRealPosition();
|
||||
player = mPlayerNode->getPosition();
|
||||
player = mTrackingPtr.getRefData().getBaseNode()->getPosition();
|
||||
|
||||
return mFirstPersonView && !mVanity.enabled && !mPreviewMode;
|
||||
}
|
||||
|
||||
Ogre::Vector3 Player::getPosition()
|
||||
Ogre::Vector3 Camera::getPosition()
|
||||
{
|
||||
return mPlayerNode->getPosition();
|
||||
return mTrackingPtr.getRefData().getBaseNode()->getPosition();
|
||||
}
|
||||
|
||||
void Player::getSightAngles(float &pitch, float &yaw)
|
||||
void Camera::getSightAngles(float &pitch, float &yaw)
|
||||
{
|
||||
pitch = mMainCam.pitch;
|
||||
yaw = mMainCam.yaw;
|
||||
}
|
||||
|
||||
void Player::compensateYaw(float diff)
|
||||
{
|
||||
mPreviewCam.yaw -= diff;
|
||||
Ogre::Quaternion zr(
|
||||
Ogre::Radian(mPreviewCam.yaw),
|
||||
Ogre::Vector3::NEGATIVE_UNIT_Z
|
||||
);
|
||||
Ogre::Quaternion xr(
|
||||
Ogre::Radian(mPreviewCam.pitch),
|
||||
Ogre::Vector3::UNIT_X);
|
||||
mCameraNode->setOrientation(zr * xr);
|
||||
}
|
||||
|
||||
void Player::togglePlayerLooking(bool enable)
|
||||
void Camera::togglePlayerLooking(bool enable)
|
||||
{
|
||||
mFreeLook = enable;
|
||||
}
|
||||
|
||||
void Player::setLowHeight(bool low)
|
||||
void Camera::setLowHeight(bool low)
|
||||
{
|
||||
if (low) {
|
||||
mCameraNode->setPosition(0.f, 0.f, mHeight * 0.85);
|
||||
|
@ -372,7 +326,7 @@ namespace MWRender
|
|||
}
|
||||
}
|
||||
|
||||
bool Player::isVanityOrPreviewModeEnabled()
|
||||
bool Camera::isVanityOrPreviewModeEnabled()
|
||||
{
|
||||
return mPreviewMode || mVanity.enabled;
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
#ifndef GAME_MWRENDER_PLAYER_H
|
||||
#define GAME_MWRENDER_PLAYER_H
|
||||
#ifndef GAME_MWRENDER_CAMERA_H
|
||||
#define GAME_MWRENDER_CAMERA_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class Vector3;
|
||||
|
@ -10,24 +12,20 @@ namespace Ogre
|
|||
class SceneNode;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Ptr;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class NpcAnimation;
|
||||
/// \brief Player character rendering and camera control
|
||||
class Player
|
||||
|
||||
/// \brief Camera control
|
||||
class Camera
|
||||
{
|
||||
struct CamData {
|
||||
float pitch, yaw, offset;
|
||||
};
|
||||
|
||||
Ogre::Camera *mCamera;
|
||||
MWWorld::Ptr mTrackingPtr;
|
||||
|
||||
Ogre::SceneNode *mPlayerNode;
|
||||
Ogre::Camera *mCamera;
|
||||
Ogre::SceneNode *mCameraNode;
|
||||
|
||||
NpcAnimation *mAnimation;
|
||||
|
@ -37,7 +35,7 @@ namespace MWRender
|
|||
bool mFreeLook;
|
||||
|
||||
struct {
|
||||
bool enabled, allowed, forced;
|
||||
bool enabled, allowed;
|
||||
} mVanity;
|
||||
|
||||
float mHeight, mCameraDistance;
|
||||
|
@ -51,15 +49,11 @@ namespace MWRender
|
|||
void setLowHeight(bool low = true);
|
||||
|
||||
public:
|
||||
Camera(Ogre::Camera *camera);
|
||||
~Camera();
|
||||
|
||||
Player (Ogre::Camera *camera, Ogre::SceneNode* mNode);
|
||||
~Player();
|
||||
|
||||
/// Set where the player is looking at. Uses Morrowind (euler) angles
|
||||
/// Set where the camera is looking at. Uses Morrowind (euler) angles
|
||||
/// \param rot Rotation angles in radians
|
||||
/// \return true if player object needs to bo rotated physically
|
||||
bool rotate(const Ogre::Vector3 &rot, bool adjust);
|
||||
|
||||
void rotateCamera(const Ogre::Vector3 &rot, bool adjust);
|
||||
|
||||
float getYaw();
|
||||
|
@ -68,22 +62,21 @@ namespace MWRender
|
|||
float getPitch();
|
||||
void setPitch(float angle);
|
||||
|
||||
void compensateYaw(float diff);
|
||||
|
||||
std::string getHandle() const;
|
||||
const std::string &getHandle() const;
|
||||
|
||||
/// Attach camera to object
|
||||
/// \note there is no protection from attaching the same camera to
|
||||
/// several different objects
|
||||
void attachTo(const MWWorld::Ptr &);
|
||||
|
||||
void toggleViewMode();
|
||||
|
||||
bool toggleVanityMode(bool enable, bool force = false);
|
||||
bool toggleVanityMode(bool enable);
|
||||
void allowVanityMode(bool allow);
|
||||
|
||||
void togglePreviewMode(bool enable);
|
||||
|
||||
bool isFirstPerson() const
|
||||
{ return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); }
|
||||
|
||||
void update(float duration);
|
||||
|
||||
/// Set camera distance for current mode. Don't work on 1st person view.
|
||||
|
@ -96,8 +89,6 @@ namespace MWRender
|
|||
void setCameraDistance();
|
||||
|
||||
void setAnimation(NpcAnimation *anim);
|
||||
NpcAnimation *getAnimation() const
|
||||
{ return mAnimation; }
|
||||
|
||||
void setHeight(float height);
|
||||
float getHeight();
|
|
@ -12,6 +12,7 @@
|
|||
#include "../mwbase/world.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
#include "renderconst.hpp"
|
||||
#include "npcanimation.hpp"
|
||||
|
@ -134,6 +135,46 @@ namespace MWRender
|
|||
|
||||
void InventoryPreview::update(int sizeX, int sizeY)
|
||||
{
|
||||
MWWorld::InventoryStore &inv = MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter);
|
||||
MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
std::string groupname;
|
||||
if(iter == inv.end())
|
||||
groupname = "inventoryhandtohand";
|
||||
else
|
||||
{
|
||||
const std::string &type = iter->getTypeName();
|
||||
if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())
|
||||
groupname = "inventoryweapononehand";
|
||||
else if(type == typeid(ESM::Weapon).name())
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Weapon> *ref = iter->get<ESM::Weapon>();
|
||||
|
||||
int type = ref->mBase->mData.mType;
|
||||
if(type == ESM::Weapon::ShortBladeOneHand ||
|
||||
type == ESM::Weapon::LongBladeOneHand ||
|
||||
type == ESM::Weapon::BluntOneHand ||
|
||||
type == ESM::Weapon::AxeOneHand)
|
||||
groupname = "inventoryweapononehand";
|
||||
else if(type == ESM::Weapon::LongBladeTwoHand ||
|
||||
type == ESM::Weapon::BluntTwoClose ||
|
||||
type == ESM::Weapon::AxeTwoHand)
|
||||
groupname = "inventoryweapontwohand";
|
||||
else if(type == ESM::Weapon::BluntTwoWide ||
|
||||
type == ESM::Weapon::SpearTwoWide)
|
||||
groupname = "inventoryweapontwowide";
|
||||
else
|
||||
groupname = "inventoryhandtohand";
|
||||
}
|
||||
else
|
||||
groupname = "inventoryhandtohand";
|
||||
}
|
||||
|
||||
if(groupname != mCurrentAnimGroup)
|
||||
{
|
||||
mCurrentAnimGroup = groupname;
|
||||
mAnimation->play(mCurrentAnimGroup, "start", "stop", 0.0f, 0);
|
||||
}
|
||||
|
||||
mAnimation->forceUpdate();
|
||||
mAnimation->runAnimation(0.0f);
|
||||
|
||||
|
@ -155,7 +196,10 @@ namespace MWRender
|
|||
if (!mSelectionBuffer)
|
||||
mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0);
|
||||
|
||||
mAnimation->play("inventoryhandtohand", "start", "stop", false);
|
||||
mAnimation->showWeapons(true);
|
||||
|
||||
mCurrentAnimGroup = "inventoryhandtohand";
|
||||
mAnimation->play(mCurrentAnimGroup, "start", "stop", 0.0f, 0);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------------
|
||||
|
@ -189,7 +233,7 @@ namespace MWRender
|
|||
|
||||
void RaceSelectionPreview::onSetup ()
|
||||
{
|
||||
mAnimation->play("idle", "start", "stop", false);
|
||||
mAnimation->play("idle", "start", "stop", 0.0f, 0);
|
||||
|
||||
updateCamera();
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace MWRender
|
|||
MWWorld::Ptr mCharacter;
|
||||
|
||||
MWRender::NpcAnimation* mAnimation;
|
||||
std::string mCurrentAnimGroup;
|
||||
|
||||
std::string mName;
|
||||
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
#include "creatureanimation.hpp"
|
||||
|
||||
#include <OgreEntity.h>
|
||||
#include <OgreParticleSystem.h>
|
||||
#include <OgreSceneManager.h>
|
||||
#include <OgreSubEntity.h>
|
||||
|
||||
#include "renderconst.hpp"
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -26,31 +21,11 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
|
|||
{
|
||||
std::string model = "meshes\\"+ref->mBase->mModel;
|
||||
|
||||
createObjectList(mPtr.getRefData().getBaseNode(), model);
|
||||
for(size_t i = 0;i < mObjectList.mEntities.size();i++)
|
||||
{
|
||||
Ogre::Entity *ent = mObjectList.mEntities[i];
|
||||
ent->setVisibilityFlags(RV_Actors);
|
||||
|
||||
for(unsigned int j=0; j < ent->getNumSubEntities(); ++j)
|
||||
{
|
||||
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
|
||||
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < mObjectList.mParticles.size();i++)
|
||||
{
|
||||
Ogre::ParticleSystem *part = mObjectList.mParticles[i];
|
||||
part->setVisibilityFlags(RV_Actors);
|
||||
|
||||
part->setRenderQueueGroup(RQG_Alpha);
|
||||
}
|
||||
|
||||
std::vector<std::string> names;
|
||||
if((ref->mBase->mFlags&ESM::Creature::Biped))
|
||||
names.push_back("meshes\\base_anim.nif");
|
||||
names.push_back(model);
|
||||
setAnimationSources(names);
|
||||
addObjectList(mPtr.getRefData().getBaseNode(), "meshes\\base_anim.nif", true);
|
||||
|
||||
addObjectList(mPtr.getRefData().getBaseNode(), model, false);
|
||||
setRenderProperties(mObjects.back().mObjectList, RV_Actors, RQG_Main, RQG_Alpha);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
#include "player.hpp"
|
||||
#include "renderconst.hpp"
|
||||
|
||||
using namespace Ogre;
|
||||
|
|
|
@ -39,8 +39,6 @@ namespace MWWorld
|
|||
|
||||
namespace MWRender
|
||||
{
|
||||
class Player;
|
||||
|
||||
class Debugging
|
||||
{
|
||||
OEngine::Physic::PhysicEngine* mEngine;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
||||
#include "renderconst.hpp"
|
||||
|
||||
|
@ -29,7 +30,7 @@ const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize
|
|||
{ ESM::PRT_LHand, "Left Hand" },
|
||||
{ ESM::PRT_RWrist, "Right Wrist" },
|
||||
{ ESM::PRT_LWrist, "Left Wrist" },
|
||||
{ ESM::PRT_Shield, "Shield" },
|
||||
{ ESM::PRT_Shield, "Shield Bone" },
|
||||
{ ESM::PRT_RForearm, "Right Forearm" },
|
||||
{ ESM::PRT_LForearm, "Left Forearm" },
|
||||
{ ESM::PRT_RUpperarm, "Right Upper Arm" },
|
||||
|
@ -44,7 +45,7 @@ const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize
|
|||
{ ESM::PRT_LLeg, "Left Upper Leg" },
|
||||
{ ESM::PRT_RPauldron, "Right Clavicle" },
|
||||
{ ESM::PRT_LPauldron, "Left Clavicle" },
|
||||
{ ESM::PRT_Weapon, "Weapon" },
|
||||
{ ESM::PRT_Weapon, "Weapon Bone" },
|
||||
{ ESM::PRT_Tail, "Tail" }
|
||||
};
|
||||
|
||||
|
@ -73,7 +74,10 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
|
|||
mGloveL(inv.end()),
|
||||
mGloveR(inv.end()),
|
||||
mSkirtIter(inv.end()),
|
||||
mViewMode(viewMode)
|
||||
mWeapon(inv.end()),
|
||||
mShield(inv.end()),
|
||||
mViewMode(viewMode),
|
||||
mShowWeapons(false)
|
||||
{
|
||||
mNpc = mPtr.get<ESM::NPC>()->mBase;
|
||||
|
||||
|
@ -96,39 +100,24 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
|
|||
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
|
||||
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
|
||||
|
||||
createObjectList(node, smodel);
|
||||
for(size_t i = 0;i < mObjectList.mEntities.size();i++)
|
||||
{
|
||||
Ogre::Entity *base = mObjectList.mEntities[i];
|
||||
|
||||
base->getUserObjectBindings().setUserAny(Ogre::Any(-1));
|
||||
if (mVisibilityFlags != 0)
|
||||
base->setVisibilityFlags(mVisibilityFlags);
|
||||
|
||||
for(unsigned int j=0; j < base->getNumSubEntities(); ++j)
|
||||
{
|
||||
Ogre::SubEntity* subEnt = base->getSubEntity(j);
|
||||
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < mObjectList.mParticles.size();i++)
|
||||
{
|
||||
Ogre::ParticleSystem *part = mObjectList.mParticles[i];
|
||||
|
||||
part->getUserObjectBindings().setUserAny(Ogre::Any(-1));
|
||||
if(mVisibilityFlags != 0)
|
||||
part->setVisibilityFlags(mVisibilityFlags);
|
||||
part->setRenderQueueGroup(RQG_Alpha);
|
||||
}
|
||||
|
||||
std::vector<std::string> skelnames(1, smodel);
|
||||
if(!mNpc->isMale() && !isBeast)
|
||||
skelnames.push_back("meshes\\base_anim_female.nif");
|
||||
else if(mBodyPrefix.find("argonian") != std::string::npos)
|
||||
skelnames.push_back("meshes\\argonian_swimkna.nif");
|
||||
addObjectList(node, smodel, true);
|
||||
if(mBodyPrefix.find("argonian") != std::string::npos)
|
||||
addObjectList(node, "meshes\\argonian_swimkna.nif", true);
|
||||
else if(!mNpc->isMale() && !isBeast)
|
||||
addObjectList(node, "meshes\\base_anim_female.nif", true);
|
||||
if(mNpc->mModel.length() > 0)
|
||||
skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel));
|
||||
setAnimationSources(skelnames);
|
||||
addObjectList(node, "meshes\\"+mNpc->mModel, true);
|
||||
if(mViewMode == VM_FirstPerson)
|
||||
{
|
||||
/* A bit counter-intuitive, but unlike third-person anims, it seems
|
||||
* beast races get both base_anim.1st.nif and base_animkna.1st.nif.
|
||||
*/
|
||||
addObjectList(node, "meshes\\base_anim.1st.nif", true);
|
||||
if(isBeast)
|
||||
addObjectList(node, "meshes\\base_animkna.1st.nif", true);
|
||||
if(!mNpc->isMale() && !isBeast)
|
||||
addObjectList(node, "meshes\\base_anim_female.1st.nif", true);
|
||||
}
|
||||
|
||||
forceUpdate();
|
||||
}
|
||||
|
@ -138,28 +127,31 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
|
|||
assert(viewMode != VM_HeadOnly);
|
||||
mViewMode = viewMode;
|
||||
|
||||
/* FIXME: Enable this once first-person animations work. */
|
||||
#if 0
|
||||
Ogre::SceneNode *node = mInsert->getParentSceneNode();
|
||||
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace);
|
||||
|
||||
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
|
||||
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
|
||||
|
||||
std::vector<std::string> skelnames(1, smodel);
|
||||
if(!mNpc->isMale() && !isBeast)
|
||||
skelnames.push_back("meshes\\base_anim_female.nif");
|
||||
else if(mBodyPrefix.find("argonian") != std::string::npos)
|
||||
skelnames.push_back("meshes\\argonian_swimkna.nif");
|
||||
clearExtraSources();
|
||||
if(mBodyPrefix.find("argonian") != std::string::npos)
|
||||
addObjectList(node, "meshes\\argonian_swimkna.nif", true);
|
||||
else if(!mNpc->isMale() && !isBeast)
|
||||
addObjectList(node, "meshes\\base_anim_female.nif", true);
|
||||
if(mNpc->mModel.length() > 0)
|
||||
skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel));
|
||||
addObjectList(node, "meshes\\"+mNpc->mModel, true);
|
||||
if(mViewMode == VM_FirstPerson)
|
||||
{
|
||||
smodel = (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif");
|
||||
skelnames.push_back(smodel);
|
||||
/* A bit counter-intuitive, but unlike third-person anims, it seems
|
||||
* beast races get both base_anim.1st.nif and base_animkna.1st.nif.
|
||||
*/
|
||||
addObjectList(node, "meshes\\base_anim.1st.nif", true);
|
||||
if(isBeast)
|
||||
addObjectList(node, "meshes\\base_animkna.1st.nif", true);
|
||||
if(!mNpc->isMale() && !isBeast)
|
||||
addObjectList(node, "meshes\\base_anim_female.1st.nif", true);
|
||||
}
|
||||
setAnimationSources(skelnames);
|
||||
#endif
|
||||
MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr);
|
||||
|
||||
for(size_t i = 0;i < sPartListSize;i++)
|
||||
removeIndividualPart(i);
|
||||
|
@ -169,84 +161,33 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
|
|||
void NpcAnimation::updateParts(bool forceupdate)
|
||||
{
|
||||
static const struct {
|
||||
int numRemoveParts; // Max: 1
|
||||
ESM::PartReferenceType removeParts[1];
|
||||
|
||||
MWWorld::ContainerStoreIterator NpcAnimation::*part;
|
||||
int slot;
|
||||
|
||||
int numReserveParts; // Max: 12
|
||||
ESM::PartReferenceType reserveParts[12];
|
||||
MWWorld::ContainerStoreIterator NpcAnimation::*mPart;
|
||||
int mSlot;
|
||||
int mBasePriority;
|
||||
} slotlist[] = {
|
||||
{ 0, { },
|
||||
&NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe,
|
||||
12, { ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg,
|
||||
ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee,
|
||||
ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_RPauldron, ESM::PRT_LPauldron }
|
||||
},
|
||||
|
||||
{ 0, { },
|
||||
&NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt,
|
||||
3, { ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg }
|
||||
},
|
||||
|
||||
{ 1, { ESM::PRT_Hair },
|
||||
&NpcAnimation::mHelmet, MWWorld::InventoryStore::Slot_Helmet,
|
||||
0, { }
|
||||
},
|
||||
|
||||
{ 0, { },
|
||||
&NpcAnimation::mCuirass, MWWorld::InventoryStore::Slot_Cuirass,
|
||||
0, { }
|
||||
},
|
||||
|
||||
{ 0, { },
|
||||
&NpcAnimation::mGreaves, MWWorld::InventoryStore::Slot_Greaves,
|
||||
0, { }
|
||||
},
|
||||
|
||||
{ 0, { },
|
||||
&NpcAnimation::mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron,
|
||||
0, { }
|
||||
},
|
||||
|
||||
{ 0, { },
|
||||
&NpcAnimation::mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron,
|
||||
0, { }
|
||||
},
|
||||
|
||||
{ 0, { },
|
||||
&NpcAnimation::mBoots, MWWorld::InventoryStore::Slot_Boots,
|
||||
0, { }
|
||||
},
|
||||
|
||||
{ 0, { },
|
||||
&NpcAnimation::mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet,
|
||||
0, { }
|
||||
},
|
||||
|
||||
{ 0, { },
|
||||
&NpcAnimation::mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet,
|
||||
0, { }
|
||||
},
|
||||
|
||||
{ 0, { },
|
||||
&NpcAnimation::mShirt, MWWorld::InventoryStore::Slot_Shirt,
|
||||
0, { }
|
||||
},
|
||||
|
||||
{ 0, { },
|
||||
&NpcAnimation::mPants, MWWorld::InventoryStore::Slot_Pants,
|
||||
0, { }
|
||||
},
|
||||
// FIXME: Priority is based on the number of reserved slots. There should be a better way.
|
||||
{ &NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe, 12 },
|
||||
{ &NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt, 3 },
|
||||
{ &NpcAnimation::mHelmet, MWWorld::InventoryStore::Slot_Helmet, 0 },
|
||||
{ &NpcAnimation::mCuirass, MWWorld::InventoryStore::Slot_Cuirass, 0 },
|
||||
{ &NpcAnimation::mGreaves, MWWorld::InventoryStore::Slot_Greaves, 0 },
|
||||
{ &NpcAnimation::mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron, 0 },
|
||||
{ &NpcAnimation::mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron, 0 },
|
||||
{ &NpcAnimation::mBoots, MWWorld::InventoryStore::Slot_Boots, 0 },
|
||||
{ &NpcAnimation::mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet, 0 },
|
||||
{ &NpcAnimation::mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet, 0 },
|
||||
{ &NpcAnimation::mShirt, MWWorld::InventoryStore::Slot_Shirt, 0 },
|
||||
{ &NpcAnimation::mPants, MWWorld::InventoryStore::Slot_Pants, 0 },
|
||||
{ &NpcAnimation::mShield, MWWorld::InventoryStore::Slot_CarriedLeft, 0 },
|
||||
{ &NpcAnimation::mWeapon, MWWorld::InventoryStore::Slot_CarriedRight, 0 }
|
||||
};
|
||||
static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]);
|
||||
|
||||
MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr);
|
||||
for(size_t i = 0;!forceupdate && i < slotlistsize;i++)
|
||||
{
|
||||
MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot);
|
||||
if(this->*slotlist[i].part != iter)
|
||||
MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].mSlot);
|
||||
if(this->*slotlist[i].mPart != iter)
|
||||
{
|
||||
forceupdate = true;
|
||||
break;
|
||||
|
@ -259,40 +200,55 @@ void NpcAnimation::updateParts(bool forceupdate)
|
|||
if(mViewMode == VM_FirstPerson)
|
||||
{
|
||||
for(size_t i = 0;i < slotlistsize;i++)
|
||||
this->*slotlist[i].part = inv.getSlot(slotlist[i].slot);
|
||||
this->*slotlist[i].mPart = inv.getSlot(slotlist[i].mSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++)
|
||||
{
|
||||
MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot);
|
||||
MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].mSlot);
|
||||
|
||||
this->*slotlist[i].part = iter;
|
||||
removePartGroup(slotlist[i].slot);
|
||||
this->*slotlist[i].mPart = iter;
|
||||
removePartGroup(slotlist[i].mSlot);
|
||||
|
||||
if(this->*slotlist[i].part == inv.end())
|
||||
if(this->*slotlist[i].mPart == inv.end())
|
||||
continue;
|
||||
|
||||
for(int rem = 0;rem < slotlist[i].numRemoveParts;rem++)
|
||||
removeIndividualPart(slotlist[i].removeParts[rem]);
|
||||
if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Helmet)
|
||||
removeIndividualPart(ESM::PRT_Hair);
|
||||
|
||||
int prio = 1;
|
||||
MWWorld::ContainerStoreIterator &store = this->*slotlist[i].part;
|
||||
MWWorld::ContainerStoreIterator &store = this->*slotlist[i].mPart;
|
||||
if(store->getTypeName() == typeid(ESM::Clothing).name())
|
||||
{
|
||||
prio = ((slotlist[i].numReserveParts+1)<<1) + 0;
|
||||
prio = ((slotlist[i].mBasePriority+1)<<1) + 0;
|
||||
const ESM::Clothing *clothes = store->get<ESM::Clothing>()->mBase;
|
||||
addPartGroup(slotlist[i].slot, prio, clothes->mParts.mParts);
|
||||
addPartGroup(slotlist[i].mSlot, prio, clothes->mParts.mParts);
|
||||
}
|
||||
else if(store->getTypeName() == typeid(ESM::Armor).name())
|
||||
{
|
||||
prio = ((slotlist[i].numReserveParts+1)<<1) + 1;
|
||||
prio = ((slotlist[i].mBasePriority+1)<<1) + 1;
|
||||
const ESM::Armor *armor = store->get<ESM::Armor>()->mBase;
|
||||
addPartGroup(slotlist[i].slot, prio, armor->mParts.mParts);
|
||||
addPartGroup(slotlist[i].mSlot, prio, armor->mParts.mParts);
|
||||
}
|
||||
|
||||
for(int res = 0;res < slotlist[i].numReserveParts;res++)
|
||||
reserveIndividualPart(slotlist[i].reserveParts[res], slotlist[i].slot, prio);
|
||||
if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Robe)
|
||||
{
|
||||
ESM::PartReferenceType parts[] = {
|
||||
ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg,
|
||||
ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee,
|
||||
ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_RPauldron, ESM::PRT_LPauldron
|
||||
};
|
||||
size_t parts_size = sizeof(parts)/sizeof(parts[0]);
|
||||
for(size_t p = 0;p < parts_size;++p)
|
||||
reserveIndividualPart(parts[p], slotlist[i].mSlot, prio);
|
||||
}
|
||||
else if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Skirt)
|
||||
{
|
||||
reserveIndividualPart(ESM::PRT_Groin, slotlist[i].mSlot, prio);
|
||||
reserveIndividualPart(ESM::PRT_RLeg, slotlist[i].mSlot, prio);
|
||||
reserveIndividualPart(ESM::PRT_LLeg, slotlist[i].mSlot, prio);
|
||||
}
|
||||
}
|
||||
|
||||
if(mViewMode != VM_FirstPerson)
|
||||
|
@ -305,7 +261,26 @@ void NpcAnimation::updateParts(bool forceupdate)
|
|||
if(mViewMode == VM_HeadOnly)
|
||||
return;
|
||||
|
||||
std::map<int, int> bodypartMap;
|
||||
showWeapons(mShowWeapons);
|
||||
|
||||
const int Flag_Female = 0x01;
|
||||
const int Flag_FirstPerson = 0x02;
|
||||
|
||||
int flags = 0;
|
||||
if (!mNpc->isMale())
|
||||
flags |= Flag_Female;
|
||||
if (mViewMode == VM_FirstPerson)
|
||||
flags |= Flag_FirstPerson;
|
||||
|
||||
// Remember body parts so we only have to search through the store once for each race/gender/viewmode combination
|
||||
static std::map< std::pair<std::string, int> , std::vector<const ESM::BodyPart*> > sRaceMapping;
|
||||
std::string race = Misc::StringUtils::lowerCase(mNpc->mRace);
|
||||
std::pair<std::string, int> thisCombination = std::make_pair(race, flags);
|
||||
if (sRaceMapping.find(thisCombination) == sRaceMapping.end())
|
||||
{
|
||||
static std::map<int, int> bodypartMap;
|
||||
if(bodypartMap.size() == 0)
|
||||
{
|
||||
bodypartMap[ESM::PRT_Neck] = ESM::BodyPart::MP_Neck;
|
||||
bodypartMap[ESM::PRT_Cuirass] = ESM::BodyPart::MP_Chest;
|
||||
bodypartMap[ESM::PRT_Groin] = ESM::BodyPart::MP_Groin;
|
||||
|
@ -326,39 +301,20 @@ void NpcAnimation::updateParts(bool forceupdate)
|
|||
bodypartMap[ESM::PRT_RLeg] = ESM::BodyPart::MP_Upperleg;
|
||||
bodypartMap[ESM::PRT_LLeg] = ESM::BodyPart::MP_Upperleg;
|
||||
bodypartMap[ESM::PRT_Tail] = ESM::BodyPart::MP_Tail;
|
||||
}
|
||||
|
||||
sRaceMapping[thisCombination].resize(ESM::PRT_Count, NULL);
|
||||
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
|
||||
const int Flag_Female = 0x01;
|
||||
const int Flag_FirstPerson = 0x02;
|
||||
|
||||
int flags = 0;
|
||||
if (!mNpc->isMale())
|
||||
flags |= Flag_Female;
|
||||
if (mViewMode == VM_FirstPerson)
|
||||
flags |= Flag_FirstPerson;
|
||||
|
||||
// Remember body parts so we only have to search through the store once for each race/gender/viewmode combination
|
||||
static std::map< std::pair<std::string, int> , std::vector<const ESM::BodyPart*> > sRaceMapping;
|
||||
std::string race = Misc::StringUtils::lowerCase(mNpc->mRace);
|
||||
std::pair<std::string, int> thisCombination = std::make_pair(race, flags);
|
||||
if (sRaceMapping.find(thisCombination) == sRaceMapping.end())
|
||||
{
|
||||
sRaceMapping[thisCombination].resize(ESM::PRT_Count);
|
||||
for (int i=0; i<ESM::PRT_Count; ++i)
|
||||
sRaceMapping[thisCombination][i] = NULL;
|
||||
|
||||
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
|
||||
|
||||
for (MWWorld::Store<ESM::BodyPart>::iterator it = partStore.begin(); it != partStore.end(); ++it)
|
||||
for(MWWorld::Store<ESM::BodyPart>::iterator it = partStore.begin(); it != partStore.end(); ++it)
|
||||
{
|
||||
const ESM::BodyPart& bodypart = *it;
|
||||
if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)
|
||||
continue;
|
||||
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))
|
||||
continue;
|
||||
if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace))
|
||||
|
@ -382,31 +338,20 @@ void NpcAnimation::updateParts(bool forceupdate)
|
|||
if (mPartPriorities[part] < 1 && bodypart)
|
||||
addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel);
|
||||
}
|
||||
|
||||
showWeapons(mShowWeapons);
|
||||
}
|
||||
|
||||
NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename)
|
||||
{
|
||||
NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mObjectList.mSkelBase, bonename,
|
||||
mInsert, model);
|
||||
for(size_t i = 0;i < objects.mEntities.size();i++)
|
||||
{
|
||||
objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
|
||||
if(mVisibilityFlags != 0)
|
||||
objects.mEntities[i]->setVisibilityFlags(mVisibilityFlags);
|
||||
NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model);
|
||||
setRenderProperties(objects, mVisibilityFlags, RQG_Main, RQG_Alpha);
|
||||
|
||||
for(unsigned int j=0; j < objects.mEntities[i]->getNumSubEntities(); ++j)
|
||||
{
|
||||
Ogre::SubEntity* subEnt = objects.mEntities[i]->getSubEntity(j);
|
||||
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < objects.mEntities.size();i++)
|
||||
objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
|
||||
for(size_t i = 0;i < objects.mParticles.size();i++)
|
||||
{
|
||||
objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
|
||||
if(mVisibilityFlags != 0)
|
||||
objects.mParticles[i]->setVisibilityFlags(mVisibilityFlags);
|
||||
objects.mParticles[i]->setRenderQueueGroup(RQG_Alpha);
|
||||
}
|
||||
|
||||
if(objects.mSkelBase)
|
||||
{
|
||||
Ogre::AnimationStateSet *aset = objects.mSkelBase->getAllAnimationStates();
|
||||
|
@ -422,6 +367,7 @@ NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, in
|
|||
while(boneiter.hasMoreElements())
|
||||
boneiter.getNext()->setManuallyControlled(true);
|
||||
}
|
||||
|
||||
return objects;
|
||||
}
|
||||
|
||||
|
@ -435,14 +381,16 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
|
|||
mTimeToChange -= timepassed;
|
||||
|
||||
Ogre::Vector3 ret = Animation::runAnimation(timepassed);
|
||||
const Ogre::SkeletonInstance *skelsrc = mObjectList.mSkelBase->getSkeleton();
|
||||
|
||||
Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
|
||||
for(size_t i = 0;i < sPartListSize;i++)
|
||||
{
|
||||
Ogre::Entity *ent = mObjectParts[i].mSkelBase;
|
||||
if(!ent) continue;
|
||||
updateSkeletonInstance(skelsrc, ent->getSkeleton());
|
||||
updateSkeletonInstance(baseinst, ent->getSkeleton());
|
||||
ent->getAllAnimationStates()->_notifyDirty();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -523,4 +471,23 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::
|
|||
}
|
||||
}
|
||||
|
||||
void NpcAnimation::showWeapons(bool showWeapon)
|
||||
{
|
||||
mShowWeapons = showWeapon;
|
||||
if(showWeapon)
|
||||
{
|
||||
MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr);
|
||||
mWeapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
if(mWeapon != inv.end()) // special case for weapons
|
||||
{
|
||||
std::string mesh = MWWorld::Class::get(*mWeapon).getModel(*mWeapon);
|
||||
addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
removeIndividualPart(ESM::PRT_Weapon);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ private:
|
|||
std::string mHairModel;
|
||||
std::string mBodyPrefix;
|
||||
ViewMode mViewMode;
|
||||
bool mShowWeapons;
|
||||
|
||||
float mTimeToChange;
|
||||
MWWorld::ContainerStoreIterator mRobe;
|
||||
|
@ -60,6 +61,8 @@ private:
|
|||
MWWorld::ContainerStoreIterator mGloveL;
|
||||
MWWorld::ContainerStoreIterator mGloveR;
|
||||
MWWorld::ContainerStoreIterator mSkirtIter;
|
||||
MWWorld::ContainerStoreIterator mWeapon;
|
||||
MWWorld::ContainerStoreIterator mShield;
|
||||
|
||||
int mVisibilityFlags;
|
||||
|
||||
|
@ -85,6 +88,8 @@ public:
|
|||
|
||||
virtual Ogre::Vector3 runAnimation(float timepassed);
|
||||
|
||||
virtual void showWeapons(bool showWeapon);
|
||||
|
||||
void setViewMode(ViewMode viewMode);
|
||||
|
||||
void forceUpdate()
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "../mwbase/inputmanager.hpp" // FIXME
|
||||
#include "../mwbase/windowmanager.hpp" // FIXME
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
|
@ -47,12 +49,14 @@ using namespace Ogre;
|
|||
|
||||
namespace MWRender {
|
||||
|
||||
RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir,
|
||||
const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine,MWWorld::Fallback* fallback)
|
||||
RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir,
|
||||
const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine,
|
||||
MWWorld::Fallback* fallback)
|
||||
: mRendering(_rend)
|
||||
, mFallback(fallback)
|
||||
, mObjects(mRendering,mFallback)
|
||||
, mObjects(mRendering, mFallback)
|
||||
, mActors(mRendering, this)
|
||||
, mPlayerAnimation(NULL)
|
||||
, mAmbientMode(0)
|
||||
, mSunEnabled(0)
|
||||
, mPhysicsEngine(engine)
|
||||
|
@ -146,14 +150,13 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
|
|||
|
||||
applyCompositors();
|
||||
|
||||
SceneNode *rt = mRendering.getScene()->getRootSceneNode();
|
||||
mRootNode = rt;
|
||||
mRootNode = mRendering.getScene()->getRootSceneNode();
|
||||
mRootNode->createChildSceneNode("player");
|
||||
|
||||
mObjects.setRootNode(mRootNode);
|
||||
mActors.setRootNode(mRootNode);
|
||||
|
||||
Ogre::SceneNode *playerNode = mRootNode->createChildSceneNode ("player");
|
||||
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
|
||||
mCamera = new MWRender::Camera(mRendering.getCamera());
|
||||
|
||||
mShadows = new Shadows(&mRendering);
|
||||
|
||||
|
@ -181,7 +184,8 @@ RenderingManager::~RenderingManager ()
|
|||
mRendering.getWindow()->removeListener(this);
|
||||
mRendering.removeWindowEventListener(this);
|
||||
|
||||
delete mPlayer;
|
||||
delete mPlayerAnimation;
|
||||
delete mCamera;
|
||||
delete mSkyManager;
|
||||
delete mDebugging;
|
||||
delete mShadows;
|
||||
|
@ -262,37 +266,20 @@ void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3
|
|||
ptr.getRefData().getBaseNode()->setScale(scale);
|
||||
}
|
||||
|
||||
bool RenderingManager::rotateObject(const MWWorld::Ptr &ptr, Ogre::Vector3 &rot, bool adjust)
|
||||
void RenderingManager::rotateObject(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
bool isActive = ptr.getRefData().getBaseNode() != 0;
|
||||
bool isPlayer = isActive && ptr.getRefData().getHandle() == "player";
|
||||
bool force = true;
|
||||
Ogre::Vector3 rot(ptr.getRefData().getPosition().rot);
|
||||
|
||||
if (isPlayer)
|
||||
force = mPlayer->rotate(rot, adjust);
|
||||
if(ptr.getRefData().getHandle() == mCamera->getHandle() &&
|
||||
!mCamera->isVanityOrPreviewModeEnabled())
|
||||
mCamera->rotateCamera(rot, false);
|
||||
|
||||
MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z);
|
||||
if (!isPlayer && isActive)
|
||||
{
|
||||
if(adjust)
|
||||
{
|
||||
const float *objRot = ptr.getRefData().getPosition().rot;
|
||||
rot.x += objRot[0];
|
||||
rot.y += objRot[1];
|
||||
rot.z += objRot[2];
|
||||
}
|
||||
Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z);
|
||||
if(!MWWorld::Class::get(ptr).isActor())
|
||||
newo = Ogre::Quaternion(Ogre::Radian(-rot.x), Ogre::Vector3::UNIT_X) *
|
||||
Ogre::Quaternion(Ogre::Radian(-rot.y), Ogre::Vector3::UNIT_Y) * newo;
|
||||
|
||||
Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(-rot.x), Ogre::Vector3::UNIT_X) *
|
||||
Ogre::Quaternion(Ogre::Radian(-rot.y), Ogre::Vector3::UNIT_Y) *
|
||||
Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z);
|
||||
ptr.getRefData().getBaseNode()->setOrientation(newo);
|
||||
}
|
||||
else if(isPlayer)
|
||||
{
|
||||
rot.x = -mPlayer->getPitch();
|
||||
rot.z = mPlayer->getYaw();
|
||||
}
|
||||
return force;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -315,27 +302,29 @@ void RenderingManager::update (float duration, bool paused)
|
|||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
|
||||
MWWorld::Ptr player = world->getPlayer().getPlayer();
|
||||
|
||||
int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude;
|
||||
mRendering.getFader()->setFactor(1.f-(blind / 100.f));
|
||||
|
||||
setAmbientMode();
|
||||
|
||||
// player position
|
||||
MWWorld::RefData &data =
|
||||
MWBase::Environment::get()
|
||||
.getWorld()
|
||||
->getPlayer()
|
||||
.getPlayer()
|
||||
.getRefData();
|
||||
MWWorld::RefData &data = player.getRefData();
|
||||
float *_playerPos = data.getPosition().pos;
|
||||
Ogre::Vector3 playerPos(_playerPos[0], _playerPos[1], _playerPos[2]);
|
||||
|
||||
Ogre::Vector3 orig, dest;
|
||||
mPlayer->setCameraDistance();
|
||||
if (!mPlayer->getPosition(orig, dest)) {
|
||||
orig.z += mPlayer->getHeight() * mRootNode->getScale().z;
|
||||
mCamera->setCameraDistance();
|
||||
if(!mCamera->getPosition(orig, dest))
|
||||
{
|
||||
orig.z += mCamera->getHeight() * mRootNode->getScale().z;
|
||||
|
||||
btVector3 btOrig(orig.x, orig.y, orig.z);
|
||||
btVector3 btDest(dest.x, dest.y, dest.z);
|
||||
std::pair<std::string, float> test =
|
||||
mPhysicsEngine->rayTest(btOrig, btDest);
|
||||
std::pair<std::string,float> test = mPhysicsEngine->rayTest(btOrig, btDest);
|
||||
if (!test.first.empty()) {
|
||||
mPlayer->setCameraDistance(test.second * orig.distance(dest), false, false);
|
||||
mCamera->setCameraDistance(test.second * orig.distance(dest), false, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -349,14 +338,12 @@ void RenderingManager::update (float duration, bool paused)
|
|||
|
||||
Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition();
|
||||
|
||||
applyFog(world->isUnderwater (world->getPlayer().getPlayer().getCell(), cam));
|
||||
applyFog(world->isUnderwater(player.getCell(), cam));
|
||||
|
||||
if(paused)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mPlayer->update(duration);
|
||||
mCamera->update(duration);
|
||||
|
||||
mActors.update (duration);
|
||||
mObjects.update (duration);
|
||||
|
@ -367,18 +354,11 @@ void RenderingManager::update (float duration, bool paused)
|
|||
mSkyManager->setGlare(mOcclusionQuery->getSunVisibility());
|
||||
|
||||
Ogre::SceneNode *node = data.getBaseNode();
|
||||
//Ogre::Quaternion orient =
|
||||
//node->convertLocalToWorldOrientation(node->_getDerivedOrientation());
|
||||
Ogre::Quaternion orient =
|
||||
node->_getDerivedOrientation();
|
||||
Ogre::Quaternion orient = node->_getDerivedOrientation();
|
||||
|
||||
mLocalMap->updatePlayer(playerPos, orient);
|
||||
|
||||
mWater->updateUnderwater(
|
||||
world->isUnderwater(
|
||||
world->getPlayer().getPlayer().getCell(),
|
||||
cam)
|
||||
);
|
||||
mWater->updateUnderwater(world->isUnderwater(player.getCell(), cam));
|
||||
|
||||
mWater->update(duration, playerPos);
|
||||
}
|
||||
|
@ -597,8 +577,15 @@ void RenderingManager::setSunColour(const Ogre::ColourValue& colour)
|
|||
|
||||
void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour)
|
||||
{
|
||||
mRendering.getScene()->setAmbientLight(colour);
|
||||
mTerrainManager->setAmbient(colour);
|
||||
mAmbientColor = colour;
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
int nightEye = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::NightEye)).mMagnitude;
|
||||
Ogre::ColourValue final = colour;
|
||||
final += Ogre::ColourValue(0.7,0.7,0.7,0) * std::min(1.f, (nightEye/100.f));
|
||||
|
||||
mRendering.getScene()->setAmbientLight(final);
|
||||
mTerrainManager->setAmbient(final);
|
||||
}
|
||||
|
||||
void RenderingManager::sunEnable(bool real)
|
||||
|
@ -865,39 +852,49 @@ void RenderingManager::getTriangleBatchCount(unsigned int &triangles, unsigned i
|
|||
}
|
||||
}
|
||||
|
||||
void RenderingManager::attachCameraTo(const MWWorld::Ptr &ptr)
|
||||
void RenderingManager::setupPlayer(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
mPlayer->attachTo(ptr);
|
||||
ptr.getRefData().setBaseNode(mRendering.getScene()->getSceneNode("player"));
|
||||
mCamera->attachTo(ptr);
|
||||
}
|
||||
|
||||
void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
MWRender::NpcAnimation *anim =
|
||||
new MWRender::NpcAnimation(
|
||||
ptr, ptr.getRefData ().getBaseNode (),
|
||||
MWWorld::Class::get(ptr).getInventoryStore(ptr), RV_Actors
|
||||
);
|
||||
mPlayer->setAnimation(anim);
|
||||
mWater->removeEmitter (ptr);
|
||||
mWater->addEmitter (ptr);
|
||||
if(!mPlayerAnimation)
|
||||
{
|
||||
mPlayerAnimation = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(),
|
||||
MWWorld::Class::get(ptr).getInventoryStore(ptr),
|
||||
RV_Actors);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reconstruct the NpcAnimation in-place
|
||||
mPlayerAnimation->~NpcAnimation();
|
||||
new(mPlayerAnimation) NpcAnimation(ptr, ptr.getRefData().getBaseNode(),
|
||||
MWWorld::Class::get(ptr).getInventoryStore(ptr),
|
||||
RV_Actors);
|
||||
}
|
||||
mCamera->setAnimation(mPlayerAnimation);
|
||||
mWater->removeEmitter(ptr);
|
||||
mWater->addEmitter(ptr);
|
||||
// apply race height
|
||||
MWBase::Environment::get().getWorld()->scaleObject(ptr, 1.f);
|
||||
}
|
||||
|
||||
void RenderingManager::getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw)
|
||||
void RenderingManager::getCameraData(Ogre::Vector3 &eyepos, float &pitch, float &yaw)
|
||||
{
|
||||
eyepos = mPlayer->getPosition();
|
||||
eyepos.z += mPlayer->getHeight();
|
||||
mPlayer->getSightAngles(pitch, yaw);
|
||||
eyepos = mCamera->getPosition();
|
||||
eyepos.z += mCamera->getHeight();
|
||||
mCamera->getSightAngles(pitch, yaw);
|
||||
}
|
||||
|
||||
bool RenderingManager::vanityRotateCamera(float* rot)
|
||||
bool RenderingManager::vanityRotateCamera(const float *rot)
|
||||
{
|
||||
if(!mPlayer->isVanityOrPreviewModeEnabled())
|
||||
if(!mCamera->isVanityOrPreviewModeEnabled())
|
||||
return false;
|
||||
|
||||
Ogre::Vector3 vRot(rot);
|
||||
mPlayer->rotateCamera(vRot, true);
|
||||
mCamera->rotateCamera(vRot, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -920,7 +917,7 @@ Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr)
|
|||
{
|
||||
Animation *anim = mActors.getAnimation(ptr);
|
||||
if(!anim && ptr.getRefData().getHandle() == "player")
|
||||
anim = mPlayer->getAnimation();
|
||||
anim = mPlayerAnimation;
|
||||
return anim;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include "objects.hpp"
|
||||
#include "actors.hpp"
|
||||
#include "player.hpp"
|
||||
#include "camera.hpp"
|
||||
#include "occlusionquery.hpp"
|
||||
|
||||
namespace Ogre
|
||||
|
@ -50,49 +50,44 @@ namespace MWRender
|
|||
class VideoPlayer;
|
||||
class Animation;
|
||||
|
||||
class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener {
|
||||
|
||||
private:
|
||||
|
||||
|
||||
class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener
|
||||
{
|
||||
private:
|
||||
virtual MWRender::Objects& getObjects();
|
||||
virtual MWRender::Actors& getActors();
|
||||
|
||||
public:
|
||||
public:
|
||||
RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir,
|
||||
const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine,MWWorld::Fallback* fallback);
|
||||
const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine,
|
||||
MWWorld::Fallback* fallback);
|
||||
virtual ~RenderingManager();
|
||||
|
||||
void togglePOV() {
|
||||
mPlayer->toggleViewMode();
|
||||
void togglePOV()
|
||||
{ mCamera->toggleViewMode(); }
|
||||
|
||||
void togglePreviewMode(bool enable)
|
||||
{ mCamera->togglePreviewMode(enable); }
|
||||
|
||||
bool toggleVanityMode(bool enable)
|
||||
{ return mCamera->toggleVanityMode(enable); }
|
||||
|
||||
void allowVanityMode(bool allow)
|
||||
{ mCamera->allowVanityMode(allow); }
|
||||
|
||||
void togglePlayerLooking(bool enable)
|
||||
{ mCamera->togglePlayerLooking(enable); }
|
||||
|
||||
void changeVanityModeScale(float factor)
|
||||
{
|
||||
if(mCamera->isVanityOrPreviewModeEnabled())
|
||||
mCamera->setCameraDistance(-factor/120.f*10, true, true);
|
||||
}
|
||||
|
||||
void togglePreviewMode(bool enable) {
|
||||
mPlayer->togglePreviewMode(enable);
|
||||
}
|
||||
bool vanityRotateCamera(const float *rot);
|
||||
|
||||
bool toggleVanityMode(bool enable, bool force) {
|
||||
return mPlayer->toggleVanityMode(enable, force);
|
||||
}
|
||||
void getCameraData(Ogre::Vector3 &eyepos, float &pitch, float &yaw);
|
||||
|
||||
void allowVanityMode(bool allow) {
|
||||
mPlayer->allowVanityMode(allow);
|
||||
}
|
||||
|
||||
void togglePlayerLooking(bool enable) {
|
||||
mPlayer->togglePlayerLooking(enable);
|
||||
}
|
||||
|
||||
void changeVanityModeScale(float factor) {
|
||||
if (mPlayer->isVanityOrPreviewModeEnabled())
|
||||
mPlayer->setCameraDistance(-factor/120.f*10, true, true);
|
||||
}
|
||||
|
||||
bool vanityRotateCamera(float* rot);
|
||||
|
||||
void getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw);
|
||||
|
||||
void attachCameraTo(const MWWorld::Ptr &ptr);
|
||||
void setupPlayer(const MWWorld::Ptr &ptr);
|
||||
void renderPlayer(const MWWorld::Ptr &ptr);
|
||||
|
||||
SkyManager* getSkyManager();
|
||||
|
@ -121,11 +116,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
|
|||
void moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position);
|
||||
void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale);
|
||||
|
||||
/// Rotates object accordingly to its type
|
||||
/// \param rot euler angles in radians
|
||||
/// \param adjust indicates should rotation be set or adjusted
|
||||
/// \return true if object needs to be rotated physically
|
||||
bool rotateObject (const MWWorld::Ptr& ptr, Ogre::Vector3 &rot, bool adjust = false);
|
||||
/// Updates an object's rotation
|
||||
void rotateObject (const MWWorld::Ptr& ptr);
|
||||
|
||||
void setWaterHeight(const float height);
|
||||
void toggleWater();
|
||||
|
@ -207,12 +199,11 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
|
|||
void stopVideo();
|
||||
void frameStarted(float dt);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
virtual void windowResized(Ogre::RenderWindow* rw);
|
||||
virtual void windowClosed(Ogre::RenderWindow* rw);
|
||||
|
||||
private:
|
||||
|
||||
private:
|
||||
sh::Factory* mFactory;
|
||||
|
||||
void setAmbientMode();
|
||||
|
@ -241,6 +232,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
|
|||
MWRender::Objects mObjects;
|
||||
MWRender::Actors mActors;
|
||||
|
||||
MWRender::NpcAnimation *mPlayerAnimation;
|
||||
|
||||
// 0 normal, 1 more bright, 2 max
|
||||
int mAmbientMode;
|
||||
|
||||
|
@ -255,7 +248,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
|
|||
|
||||
OEngine::Physic::PhysicEngine* mPhysicsEngine;
|
||||
|
||||
MWRender::Player *mPlayer;
|
||||
MWRender::Camera *mCamera;
|
||||
|
||||
MWRender::Debugging *mDebugging;
|
||||
|
||||
|
|
|
@ -205,7 +205,7 @@ unsigned int Moon::getPhaseInt() const
|
|||
return 0;
|
||||
}
|
||||
|
||||
SkyManager::SkyManager (SceneNode* root, Camera* pCamera)
|
||||
SkyManager::SkyManager(Ogre::SceneNode *root, Ogre::Camera *pCamera)
|
||||
: mHour(0.0f)
|
||||
, mDay(0)
|
||||
, mMonth(0)
|
||||
|
|
|
@ -54,9 +54,10 @@ namespace MWRender
|
|||
|
||||
TerrainMaterial::Profile::~Profile()
|
||||
{
|
||||
if (mMaterial)
|
||||
sh::Factory::getInstance().destroyMaterialInstance(mMaterial->getName());
|
||||
}
|
||||
|
||||
|
||||
Ogre::MaterialPtr TerrainMaterial::Profile::generate(const Ogre::Terrain* terrain)
|
||||
{
|
||||
const Ogre::String& matName = terrain->getMaterialName();
|
||||
|
|
|
@ -318,5 +318,18 @@ op 0x20001fb: DropSoulGem, explicit reference
|
|||
op 0x20001fc: OnDeath
|
||||
op 0x20001fd: IsWerewolf
|
||||
op 0x20001fe: IsWerewolf, explicit reference
|
||||
op 0x20001ff: Rotate
|
||||
op 0x2000200: Rotate, explicit reference
|
||||
op 0x2000201: RotateWorld
|
||||
op 0x2000202: RotateWorld, explicit reference
|
||||
op 0x2000203: SetAtStart
|
||||
op 0x2000204: SetAtStart, explicit
|
||||
op 0x2000205: OnDeath, explicit
|
||||
op 0x2000206: Move
|
||||
op 0x2000207: Move, explicit
|
||||
op 0x2000208: MoveWorld
|
||||
op 0x2000209: MoveWorld, explicit
|
||||
op 0x200020a: Fall
|
||||
op 0x200020b: Fall, explicit
|
||||
|
||||
opcodes 0x20001ff-0x3ffffff unused
|
||||
opcodes 0x200020c-0x3ffffff unused
|
||||
|
|
|
@ -282,10 +282,8 @@ namespace MWScript
|
|||
MWBase::World *world =
|
||||
MWBase::Environment::get().getWorld();
|
||||
|
||||
if (world->toggleVanityMode(sActivate, true)) {
|
||||
context.report(
|
||||
(sActivate) ? "Vanity Mode -> On" : "Vanity Mode -> Off"
|
||||
);
|
||||
if (world->toggleVanityMode(sActivate)) {
|
||||
context.report(sActivate ? "Vanity Mode -> On" : "Vanity Mode -> Off");
|
||||
sActivate = !sActivate;
|
||||
} else {
|
||||
context.report("Vanity Mode -> No");
|
||||
|
@ -557,6 +555,16 @@ namespace MWScript
|
|||
}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
class OpFall : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
const int opcodeXBox = 0x200000c;
|
||||
const int opcodeOnActivate = 0x200000d;
|
||||
const int opcodeActivate = 0x2000075;
|
||||
|
@ -598,6 +606,8 @@ namespace MWScript
|
|||
const int opcodeSetDelete = 0x20001e5;
|
||||
const int opcodeSetDeleteExplicit = 0x20001e6;
|
||||
const int opcodeGetSquareRoot = 0x20001e7;
|
||||
const int opcodeFall = 0x200020a;
|
||||
const int opcodeFallExplicit = 0x200020b;
|
||||
|
||||
const int opcodePlayBink = 0x20001f7;
|
||||
|
||||
|
@ -639,6 +649,7 @@ namespace MWScript
|
|||
extensions.registerFunction ("getcurrenttime", 'f', "", opcodeGetCurrentTime);
|
||||
extensions.registerInstruction ("setdelete", "l", opcodeSetDelete, opcodeSetDeleteExplicit);
|
||||
extensions.registerFunction ("getsquareroot", 'f', "f", opcodeGetSquareRoot);
|
||||
extensions.registerInstruction ("fall", "", opcodeFall, opcodeFallExplicit);
|
||||
}
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||
|
@ -685,6 +696,9 @@ namespace MWScript
|
|||
interpreter.installSegment5 (opcodeSetDelete, new OpSetDelete<ImplicitRef>);
|
||||
interpreter.installSegment5 (opcodeSetDeleteExplicit, new OpSetDelete<ExplicitRef>);
|
||||
interpreter.installSegment5 (opcodeGetSquareRoot, new OpGetSquareRoot);
|
||||
interpreter.installSegment5 (opcodeFall, new OpFall<ImplicitRef>);
|
||||
interpreter.installSegment5 (opcodeFallExplicit, new OpFall<ExplicitRef>);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1025,16 +1025,14 @@ namespace MWScript
|
|||
}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
class OpOnDeath : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWScript::InterpreterContext& context
|
||||
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||
|
||||
MWWorld::Ptr ptr = context.getReference();
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
Interpreter::Type_Integer value =
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).hasDied();
|
||||
|
@ -1146,9 +1144,8 @@ namespace MWScript
|
|||
const int opcodeRaiseRankExplicit = 0x20001e9;
|
||||
const int opcodeLowerRank = 0x20001ea;
|
||||
const int opcodeLowerRankExplicit = 0x20001eb;
|
||||
|
||||
const int opcodeOnDeath = 0x20001fc;
|
||||
|
||||
const int opcodeOnDeathExplicit = 0x2000205;
|
||||
const int opcodeIsWerewolf = 0x20001fd;
|
||||
const int opcodeIsWerewolfExplicit = 0x20001fe;
|
||||
|
||||
|
@ -1266,7 +1263,7 @@ namespace MWScript
|
|||
extensions.registerInstruction ("raiserank", "", opcodeRaiseRank, opcodeRaiseRankExplicit);
|
||||
extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit);
|
||||
|
||||
extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath);
|
||||
extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit);
|
||||
|
||||
extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit);
|
||||
}
|
||||
|
@ -1384,7 +1381,8 @@ namespace MWScript
|
|||
interpreter.installSegment5 (opcodeLowerRank, new OpLowerRank<ImplicitRef>);
|
||||
interpreter.installSegment5 (opcodeLowerRankExplicit, new OpLowerRank<ExplicitRef>);
|
||||
|
||||
interpreter.installSegment5 (opcodeOnDeath, new OpOnDeath);
|
||||
interpreter.installSegment5 (opcodeOnDeath, new OpOnDeath<ImplicitRef>);
|
||||
interpreter.installSegment5 (opcodeOnDeathExplicit, new OpOnDeath<ExplicitRef>);
|
||||
|
||||
interpreter.installSegment5 (opcodeIsWerewolf, new OpIsWerewolf<ImplicitRef>);
|
||||
interpreter.installSegment5 (opcodeIsWerewolfExplicit, new OpIsWerewolf<ExplicitRef>);
|
||||
|
|
|
@ -84,21 +84,27 @@ namespace MWScript
|
|||
Interpreter::Type_Float angle = runtime[0].mFloat;
|
||||
runtime.pop();
|
||||
|
||||
float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees();
|
||||
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
|
||||
float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees();
|
||||
float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees();
|
||||
float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees();
|
||||
float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees();
|
||||
|
||||
float *objRot = ptr.getRefData().getPosition().rot;
|
||||
|
||||
float lx = Ogre::Radian(objRot[0]).valueDegrees();
|
||||
float ly = Ogre::Radian(objRot[1]).valueDegrees();
|
||||
float lz = Ogre::Radian(objRot[2]).valueDegrees();
|
||||
|
||||
if (axis == "x")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az);
|
||||
MWBase::Environment::get().getWorld()->localRotateObject(ptr,angle-lx,ay,az);
|
||||
}
|
||||
else if (axis == "y")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az);
|
||||
MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,angle-ly,az);
|
||||
}
|
||||
else if (axis == "z")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle);
|
||||
MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay,angle-lz);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error ("invalid ration axis: " + axis);
|
||||
|
@ -148,15 +154,15 @@ namespace MWScript
|
|||
|
||||
if (axis=="x")
|
||||
{
|
||||
runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees());
|
||||
runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[0]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees());
|
||||
}
|
||||
else if (axis=="y")
|
||||
{
|
||||
runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees());
|
||||
runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[1]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees());
|
||||
}
|
||||
else if (axis=="z")
|
||||
{
|
||||
runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees());
|
||||
runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[2]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees());
|
||||
}
|
||||
else
|
||||
throw std::runtime_error ("invalid ration axis: " + axis);
|
||||
|
@ -542,6 +548,165 @@ namespace MWScript
|
|||
}
|
||||
};
|
||||
|
||||
template<class R>
|
||||
class OpRotate : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
const MWWorld::Ptr& ptr = R()(runtime);
|
||||
|
||||
std::string axis = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
Interpreter::Type_Float rotation = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());
|
||||
runtime.pop();
|
||||
|
||||
float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees();
|
||||
float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees();
|
||||
float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees();
|
||||
|
||||
if (axis == "x")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax+rotation,ay,az);
|
||||
}
|
||||
else if (axis == "y")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay+rotation,az);
|
||||
}
|
||||
else if (axis == "z")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay,az+rotation);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error ("invalid rotation axis: " + axis);
|
||||
}
|
||||
};
|
||||
|
||||
template<class R>
|
||||
class OpRotateWorld : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
std::string axis = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
Interpreter::Type_Float rotation = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());
|
||||
runtime.pop();
|
||||
|
||||
float *objRot = ptr.getRefData().getPosition().rot;
|
||||
|
||||
float ax = Ogre::Radian(objRot[0]).valueDegrees();
|
||||
float ay = Ogre::Radian(objRot[1]).valueDegrees();
|
||||
float az = Ogre::Radian(objRot[2]).valueDegrees();
|
||||
|
||||
if (axis == "x")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax+rotation,ay,az);
|
||||
}
|
||||
else if (axis == "y")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay+rotation,az);
|
||||
}
|
||||
else if (axis == "z")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,az+rotation);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error ("invalid rotation axis: " + axis);
|
||||
}
|
||||
};
|
||||
|
||||
template<class R>
|
||||
class OpSetAtStart : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
ptr.getRefData().getLocalRotation().rot[0] = 0;
|
||||
ptr.getRefData().getLocalRotation().rot[1] = 0;
|
||||
ptr.getRefData().getLocalRotation().rot[2] = 0;
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr, 0,0,0,true);
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().mPos.pos[0],
|
||||
ptr.getCellRef().mPos.pos[1], ptr.getCellRef().mPos.pos[2]);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
template<class R>
|
||||
class OpMove : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
const MWWorld::Ptr& ptr = R()(runtime);
|
||||
|
||||
std::string axis = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());
|
||||
runtime.pop();
|
||||
|
||||
Ogre::Vector3 posChange;
|
||||
if (axis == "x")
|
||||
{
|
||||
posChange=Ogre::Vector3(movement, 0, 0);
|
||||
}
|
||||
else if (axis == "y")
|
||||
{
|
||||
posChange=Ogre::Vector3(0, movement, 0);
|
||||
}
|
||||
else if (axis == "z")
|
||||
{
|
||||
posChange=Ogre::Vector3(0, 0, movement);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error ("invalid movement axis: " + axis);
|
||||
|
||||
Ogre::Vector3 worldPos = ptr.getRefData().getBaseNode()->convertLocalToWorldPosition(posChange);
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x, worldPos.y, worldPos.z);
|
||||
}
|
||||
};
|
||||
|
||||
template<class R>
|
||||
class OpMoveWorld : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
std::string axis = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());
|
||||
runtime.pop();
|
||||
|
||||
float *objPos = ptr.getRefData().getPosition().pos;
|
||||
|
||||
if (axis == "x")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+movement, objPos[1], objPos[2]);
|
||||
}
|
||||
else if (axis == "y")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1]+movement, objPos[2]);
|
||||
}
|
||||
else if (axis == "z")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1], objPos[2]+movement);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error ("invalid movement axis: " + axis);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const int opcodeSetScale = 0x2000164;
|
||||
const int opcodeSetScaleExplicit = 0x2000165;
|
||||
const int opcodeSetAngle = 0x2000166;
|
||||
|
@ -568,6 +733,16 @@ namespace MWScript
|
|||
const int opcodePlaceAtMeExplicit = 0x200019e;
|
||||
const int opcodeModScale = 0x20001e3;
|
||||
const int opcodeModScaleExplicit = 0x20001e4;
|
||||
const int opcodeRotate = 0x20001ff;
|
||||
const int opcodeRotateExplicit = 0x2000200;
|
||||
const int opcodeRotateWorld = 0x2000201;
|
||||
const int opcodeRotateWorldExplicit = 0x2000202;
|
||||
const int opcodeSetAtStart = 0x2000203;
|
||||
const int opcodeSetAtStartExplicit = 0x2000204;
|
||||
const int opcodeMove = 0x2000206;
|
||||
const int opcodeMoveExplicit = 0x2000207;
|
||||
const int opcodeMoveWorld = 0x2000208;
|
||||
const int opcodeMoveWorldExplicit = 0x2000209;
|
||||
|
||||
void registerExtensions (Compiler::Extensions& extensions)
|
||||
{
|
||||
|
@ -585,6 +760,11 @@ namespace MWScript
|
|||
extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc);
|
||||
extensions.registerInstruction("placeatme","clfl",opcodePlaceAtMe,opcodePlaceAtMeExplicit);
|
||||
extensions.registerInstruction("modscale","f",opcodeModScale,opcodeModScaleExplicit);
|
||||
extensions.registerInstruction("rotate","cf",opcodeRotate,opcodeRotateExplicit);
|
||||
extensions.registerInstruction("rotateworld","cf",opcodeRotateWorld,opcodeRotateWorldExplicit);
|
||||
extensions.registerInstruction("setatstart","",opcodeSetAtStart,opcodeSetAtStartExplicit);
|
||||
extensions.registerInstruction("move","cf",opcodeMove,opcodeMoveExplicit);
|
||||
extensions.registerInstruction("moveworld","cf",opcodeMoveWorld,opcodeMoveWorldExplicit);
|
||||
}
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||
|
@ -614,6 +794,16 @@ namespace MWScript
|
|||
interpreter.installSegment5(opcodePlaceAtMeExplicit,new OpPlaceAtMe<ExplicitRef>);
|
||||
interpreter.installSegment5(opcodeModScale,new OpModScale<ImplicitRef>);
|
||||
interpreter.installSegment5(opcodeModScaleExplicit,new OpModScale<ExplicitRef>);
|
||||
interpreter.installSegment5(opcodeRotate,new OpRotate<ImplicitRef>);
|
||||
interpreter.installSegment5(opcodeRotateExplicit,new OpRotate<ExplicitRef>);
|
||||
interpreter.installSegment5(opcodeRotateWorld,new OpRotateWorld<ImplicitRef>);
|
||||
interpreter.installSegment5(opcodeRotateWorldExplicit,new OpRotateWorld<ExplicitRef>);
|
||||
interpreter.installSegment5(opcodeSetAtStart,new OpSetAtStart<ImplicitRef>);
|
||||
interpreter.installSegment5(opcodeSetAtStartExplicit,new OpSetAtStart<ExplicitRef>);
|
||||
interpreter.installSegment5(opcodeMove,new OpMove<ImplicitRef>);
|
||||
interpreter.installSegment5(opcodeMoveExplicit,new OpMove<ExplicitRef>);
|
||||
interpreter.installSegment5(opcodeMoveWorld,new OpMoveWorld<ImplicitRef>);
|
||||
interpreter.installSegment5(opcodeMoveWorldExplicit,new OpMoveWorld<ExplicitRef>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
16
apps/openmw/mwworld/actiondoor.cpp
Normal file
16
apps/openmw/mwworld/actiondoor.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "actiondoor.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
ActionDoor::ActionDoor (const MWWorld::Ptr& object) : Action (false, object)
|
||||
{
|
||||
}
|
||||
|
||||
void ActionDoor::executeImp (const MWWorld::Ptr& actor)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->activateDoor(getTarget());
|
||||
}
|
||||
}
|
18
apps/openmw/mwworld/actiondoor.hpp
Normal file
18
apps/openmw/mwworld/actiondoor.hpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef GAME_MWWORLD_ACTIONDOOR_H
|
||||
#define GAME_MWWORLD_ACTIONDOOR_H
|
||||
|
||||
#include "action.hpp"
|
||||
#include "ptr.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class ActionDoor : public Action
|
||||
{
|
||||
virtual void executeImp (const MWWorld::Ptr& actor);
|
||||
|
||||
public:
|
||||
ActionDoor (const Ptr& object);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -132,7 +132,7 @@ namespace MWWorld
|
|||
return 0;
|
||||
}
|
||||
|
||||
short Class::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
|
||||
float Class::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
throw std::runtime_error ("class does not support enchanting");
|
||||
}
|
||||
|
|
|
@ -231,7 +231,7 @@ namespace MWWorld
|
|||
///< @return the enchantment ID if the object is enchanted, otherwise an empty string
|
||||
/// (default implementation: return empty string)
|
||||
|
||||
virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
|
||||
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
|
||||
///< @return the number of enchantment points available for possible enchanting
|
||||
|
||||
virtual void adjustScale(const MWWorld::Ptr& ptr,float& scale) const;
|
||||
|
|
|
@ -193,6 +193,8 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
|
|||
{
|
||||
count = std::abs(count); /// \todo implement item restocking (indicated by negative count)
|
||||
|
||||
try
|
||||
{
|
||||
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id);
|
||||
|
||||
if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
|
||||
|
@ -247,6 +249,13 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
|
|||
ref.getPtr().getCellRef().mOwner = owner;
|
||||
addImp (ref.getPtr());
|
||||
}
|
||||
}
|
||||
catch (std::logic_error& e)
|
||||
{
|
||||
// Vanilla doesn't fail on nonexistent items in levelled lists
|
||||
std::cerr << "Warning: ignoring nonexistent item '" << id << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MWWorld::ContainerStore::clear()
|
||||
|
|
|
@ -131,7 +131,6 @@ namespace MWWorld
|
|||
return position;
|
||||
}
|
||||
|
||||
|
||||
static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time,
|
||||
bool gravity, OEngine::Physic::PhysicEngine *engine)
|
||||
{
|
||||
|
@ -145,7 +144,7 @@ namespace MWWorld
|
|||
// FIXME: This works, but it's inconcsistent with how the rotations are applied elsewhere. Why?
|
||||
return position + (Ogre::Quaternion(Ogre::Radian( -refpos.rot[2]), Ogre::Vector3::UNIT_Z)*
|
||||
Ogre::Quaternion(Ogre::Radian( -refpos.rot[1]), Ogre::Vector3::UNIT_Y)*
|
||||
Ogre::Quaternion(Ogre::Radian( -refpos.rot[0]), Ogre::Vector3::UNIT_X)) *
|
||||
Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) *
|
||||
movement;
|
||||
}
|
||||
|
||||
|
@ -161,7 +160,7 @@ namespace MWWorld
|
|||
{
|
||||
velocity = (Ogre::Quaternion(Ogre::Radian( -refpos.rot[2]), Ogre::Vector3::UNIT_Z)*
|
||||
Ogre::Quaternion(Ogre::Radian( -refpos.rot[1]), Ogre::Vector3::UNIT_Y)*
|
||||
Ogre::Quaternion(Ogre::Radian( -refpos.rot[0]), Ogre::Vector3::UNIT_X)) *
|
||||
Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) *
|
||||
movement / time;
|
||||
}
|
||||
else
|
||||
|
@ -261,14 +260,13 @@ namespace MWWorld
|
|||
std::pair<float, std::string> PhysicsSystem::getFacedHandle (MWWorld::World& world, float queryDistance)
|
||||
{
|
||||
btVector3 dir(0, 1, 0);
|
||||
dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch);
|
||||
dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw);
|
||||
dir = dir.rotate(btVector3(1, 0, 0), mCameraData.pitch);
|
||||
dir = dir.rotate(btVector3(0, 0, 1), mCameraData.yaw);
|
||||
dir.setX(-dir.x());
|
||||
|
||||
btVector3 origin(
|
||||
mPlayerData.eyepos.x,
|
||||
mPlayerData.eyepos.y,
|
||||
mPlayerData.eyepos.z);
|
||||
btVector3 origin(mCameraData.eyepos.x,
|
||||
mCameraData.eyepos.y,
|
||||
mCameraData.eyepos.z);
|
||||
origin += dir * 5;
|
||||
|
||||
btVector3 dest = origin + dir * queryDistance;
|
||||
|
@ -281,14 +279,13 @@ namespace MWWorld
|
|||
std::vector < std::pair <float, std::string> > PhysicsSystem::getFacedHandles (float queryDistance)
|
||||
{
|
||||
btVector3 dir(0, 1, 0);
|
||||
dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch);
|
||||
dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw);
|
||||
dir = dir.rotate(btVector3(1, 0, 0), mCameraData.pitch);
|
||||
dir = dir.rotate(btVector3(0, 0, 1), mCameraData.yaw);
|
||||
dir.setX(-dir.x());
|
||||
|
||||
btVector3 origin(
|
||||
mPlayerData.eyepos.x,
|
||||
mPlayerData.eyepos.y,
|
||||
mPlayerData.eyepos.z);
|
||||
btVector3 origin(mCameraData.eyepos.x,
|
||||
mCameraData.eyepos.y,
|
||||
mCameraData.eyepos.z);
|
||||
origin += dir * 5;
|
||||
|
||||
btVector3 dest = origin + dir * queryDistance;
|
||||
|
@ -390,6 +387,11 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> PhysicsSystem::getCollisions(const Ptr &ptr)
|
||||
{
|
||||
return mEngine->getCollisions(ptr.getRefData().getBaseNode()->getName());
|
||||
}
|
||||
|
||||
Ogre::Vector3 PhysicsSystem::move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity)
|
||||
{
|
||||
return MovementSolver::move(ptr, movement, time, gravity, mEngine);
|
||||
|
@ -548,10 +550,10 @@ namespace MWWorld
|
|||
return true;
|
||||
}
|
||||
|
||||
void PhysicsSystem::updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw)
|
||||
void PhysicsSystem::updateCameraData(const Ogre::Vector3 &eyepos, float pitch, float yaw)
|
||||
{
|
||||
mPlayerData.eyepos = eyepos;
|
||||
mPlayerData.pitch = pitch;
|
||||
mPlayerData.yaw = yaw;
|
||||
mCameraData.eyepos = eyepos;
|
||||
mCameraData.pitch = pitch;
|
||||
mCameraData.yaw = yaw;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ namespace MWWorld
|
|||
bool toggleCollisionMode();
|
||||
|
||||
Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity);
|
||||
std::vector<std::string> getCollisions(const MWWorld::Ptr &ptr); ///< get handles this object collides with
|
||||
Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr);
|
||||
|
||||
std::pair<float, std::string> getFacedHandle (MWWorld::World& world, float queryDistance);
|
||||
|
@ -76,13 +77,13 @@ namespace MWWorld
|
|||
|
||||
bool getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max);
|
||||
|
||||
void updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw);
|
||||
void updateCameraData(const Ogre::Vector3 &eyepos, float pitch, float yaw);
|
||||
|
||||
private:
|
||||
struct {
|
||||
Ogre::Vector3 eyepos;
|
||||
float pitch, yaw;
|
||||
} mPlayerData;
|
||||
} mCameraData;
|
||||
|
||||
OEngine::Render::OgreRenderer &mRender;
|
||||
OEngine::Physic::PhysicEngine* mEngine;
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace MWWorld
|
|||
mEnabled = refData.mEnabled;
|
||||
mCount = refData.mCount;
|
||||
mPosition = refData.mPosition;
|
||||
mLocalRotation = refData.mLocalRotation;
|
||||
|
||||
mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0;
|
||||
}
|
||||
|
@ -34,7 +35,11 @@ namespace MWWorld
|
|||
RefData::RefData (const ESM::CellRef& cellRef)
|
||||
: mBaseNode(0), mHasLocals (false), mEnabled (true), mCount (1), mPosition (cellRef.mPos),
|
||||
mCustomData (0)
|
||||
{}
|
||||
{
|
||||
mLocalRotation.rot[0]=0;
|
||||
mLocalRotation.rot[1]=0;
|
||||
mLocalRotation.rot[2]=0;
|
||||
}
|
||||
|
||||
RefData::RefData (const RefData& refData)
|
||||
: mBaseNode(0), mCustomData (0)
|
||||
|
@ -76,10 +81,13 @@ namespace MWWorld
|
|||
{}
|
||||
}
|
||||
|
||||
std::string RefData::getHandle()
|
||||
const std::string &RefData::getHandle()
|
||||
{
|
||||
if (!mBaseNode)
|
||||
return "";
|
||||
if(!mBaseNode)
|
||||
{
|
||||
static const std::string empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
return mBaseNode->getName();
|
||||
}
|
||||
|
@ -141,6 +149,11 @@ namespace MWWorld
|
|||
return mPosition;
|
||||
}
|
||||
|
||||
LocalRotation& RefData::getLocalRotation()
|
||||
{
|
||||
return mLocalRotation;
|
||||
}
|
||||
|
||||
void RefData::setCustomData (CustomData *data)
|
||||
{
|
||||
delete mCustomData;
|
||||
|
|
|
@ -18,6 +18,10 @@ namespace ESM
|
|||
|
||||
namespace MWWorld
|
||||
{
|
||||
struct LocalRotation{
|
||||
float rot[3];
|
||||
};
|
||||
|
||||
class CustomData;
|
||||
|
||||
class RefData
|
||||
|
@ -34,6 +38,8 @@ namespace MWWorld
|
|||
|
||||
ESM::Position mPosition;
|
||||
|
||||
LocalRotation mLocalRotation;
|
||||
|
||||
CustomData *mCustomData;
|
||||
|
||||
void copy (const RefData& refData);
|
||||
|
@ -54,7 +60,7 @@ namespace MWWorld
|
|||
RefData& operator= (const RefData& refData);
|
||||
|
||||
/// Return OGRE handle (may be empty).
|
||||
std::string getHandle();
|
||||
const std::string &getHandle();
|
||||
|
||||
/// Return OGRE base node (can be a null pointer).
|
||||
Ogre::SceneNode* getBaseNode();
|
||||
|
@ -78,6 +84,8 @@ namespace MWWorld
|
|||
|
||||
ESM::Position& getPosition();
|
||||
|
||||
LocalRotation& getLocalRotation();
|
||||
|
||||
void setCustomData (CustomData *data);
|
||||
///< Set custom data (potentially replacing old custom data). The ownership of \æ data is
|
||||
/// transferred to this.
|
||||
|
|
|
@ -49,7 +49,12 @@ namespace
|
|||
{
|
||||
rendering.addObject(ptr);
|
||||
class_.insertObject(ptr, physics);
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr, 0, 0, 0, true);
|
||||
|
||||
float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees();
|
||||
float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees();
|
||||
float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees();
|
||||
MWBase::Environment::get().getWorld()->localRotateObject(ptr, ax, ay, az);
|
||||
|
||||
MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().mScale);
|
||||
class_.adjustPosition(ptr);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "store.hpp"
|
||||
#include "esmstore.hpp"
|
||||
|
||||
namespace MWWorld {
|
||||
|
||||
|
@ -15,8 +16,39 @@ void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
|
|||
ESM::Cell *cell = new ESM::Cell;
|
||||
cell->mName = id;
|
||||
|
||||
// The cell itself takes care of some of the hairy details
|
||||
cell->load(esm, *mEsmStore);
|
||||
//First part of cell loading
|
||||
cell->preLoad(esm);
|
||||
|
||||
//Handling MovedCellRefs, there is no way to do it inside loadcell
|
||||
while (esm.isNextSub("MVRF")) {
|
||||
ESM::CellRef ref;
|
||||
ESM::MovedCellRef cMRef;
|
||||
cell->getNextMVRF(esm, cMRef);
|
||||
|
||||
MWWorld::Store<ESM::Cell> &cStore = const_cast<MWWorld::Store<ESM::Cell>&>(mEsmStore->get<ESM::Cell>());
|
||||
ESM::Cell *cellAlt = const_cast<ESM::Cell*>(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1]));
|
||||
|
||||
// Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following
|
||||
// implementation when the oher implementation works as well.
|
||||
cell->getNextRef(esm, ref);
|
||||
std::string lowerCase;
|
||||
|
||||
std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase),
|
||||
(int(*)(int)) std::tolower);
|
||||
|
||||
// Add data required to make reference appear in the correct cell.
|
||||
// We should not need to test for duplicates, as this part of the code is pre-cell merge.
|
||||
cell->mMovedRefs.push_back(cMRef);
|
||||
// But there may be duplicates here!
|
||||
ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefnum);
|
||||
if (iter == cellAlt->mLeasedRefs.end())
|
||||
cellAlt->mLeasedRefs.push_back(ref);
|
||||
else
|
||||
*iter = ref;
|
||||
}
|
||||
|
||||
//Second part of cell loading
|
||||
cell->postLoad(esm);
|
||||
|
||||
if(cell->mData.mFlags & ESM::Cell::Interior)
|
||||
{
|
||||
|
|
|
@ -872,7 +872,28 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
void setUp() {
|
||||
std::sort(mStatic.begin(), mStatic.end(), Compare());
|
||||
/// \note This method sorts indexed values for further
|
||||
/// searches. Every loaded item is present in storage, but
|
||||
/// latest loaded shadows any previous while searching.
|
||||
/// If memory cost will be too high, it is possible to remove
|
||||
/// unused values.
|
||||
|
||||
Compare cmp;
|
||||
|
||||
std::stable_sort(mStatic.begin(), mStatic.end(), cmp);
|
||||
|
||||
typename std::vector<T>::iterator first, next;
|
||||
next = first = mStatic.begin();
|
||||
|
||||
while (first != mStatic.end() && ++next != mStatic.end()) {
|
||||
while (next != mStatic.end() && !cmp(*first, *next)) {
|
||||
++next;
|
||||
}
|
||||
if (first != --next) {
|
||||
std::swap(*first, *next);
|
||||
}
|
||||
first = ++next;
|
||||
}
|
||||
}
|
||||
|
||||
const T *search(int index) const {
|
||||
|
|
|
@ -289,8 +289,7 @@ void WeatherManager::update(float duration)
|
|||
|
||||
if (exterior)
|
||||
{
|
||||
std::string regionstr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion;
|
||||
Misc::StringUtils::toLower(regionstr);
|
||||
std::string regionstr = Misc::StringUtils::lowerCase(MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion);
|
||||
|
||||
if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion)
|
||||
{
|
||||
|
@ -621,6 +620,9 @@ unsigned int WeatherManager::getWeatherID() const
|
|||
|
||||
void WeatherManager::changeWeather(const std::string& region, const unsigned int id)
|
||||
{
|
||||
// make sure this region exists
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Region>().find(region);
|
||||
|
||||
std::string weather;
|
||||
if (id==0)
|
||||
weather = "clear";
|
||||
|
@ -645,5 +647,9 @@ void WeatherManager::changeWeather(const std::string& region, const unsigned int
|
|||
else
|
||||
weather = "clear";
|
||||
|
||||
mRegionOverrides[region] = weather;
|
||||
mRegionOverrides[Misc::StringUtils::lowerCase(region)] = weather;
|
||||
|
||||
std::string playerRegion = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion;
|
||||
if (Misc::StringUtils::ciEqual(region, playerRegion))
|
||||
setWeather(weather);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include <components/files/collections.hpp>
|
||||
#include <components/compiler/locals.hpp>
|
||||
|
||||
#include <boost/math/special_functions/sign.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
@ -16,7 +18,6 @@
|
|||
#include "../mwmechanics/movement.hpp"
|
||||
|
||||
#include "../mwrender/sky.hpp"
|
||||
#include "../mwrender/player.hpp"
|
||||
|
||||
#include "../mwclass/door.hpp"
|
||||
|
||||
|
@ -163,7 +164,8 @@ namespace MWWorld
|
|||
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride)
|
||||
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
|
||||
mSky (true), mCells (mStore, mEsm),
|
||||
mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride),mFallback(fallbackMap)
|
||||
mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride),
|
||||
mFallback(fallbackMap), mNewGame(newGame)
|
||||
{
|
||||
mPhysics = new PhysicsSystem(renderer);
|
||||
mPhysEngine = mPhysics->getEngine();
|
||||
|
@ -214,7 +216,7 @@ namespace MWWorld
|
|||
// global variables
|
||||
mGlobalVariables = new Globals (mStore);
|
||||
|
||||
if (newGame)
|
||||
if (mNewGame)
|
||||
{
|
||||
// set new game mark
|
||||
mGlobalVariables->setInt ("chargenstate", 1);
|
||||
|
@ -810,18 +812,90 @@ namespace MWWorld
|
|||
|
||||
void World::rotateObjectImp (const Ptr& ptr, Ogre::Vector3 rot, bool adjust)
|
||||
{
|
||||
if (mRendering->rotateObject(ptr, rot, adjust))
|
||||
{
|
||||
// rotate physically iff renderer confirm so
|
||||
const float two_pi = Ogre::Math::TWO_PI;
|
||||
const float pi = Ogre::Math::PI;
|
||||
|
||||
float *objRot = ptr.getRefData().getPosition().rot;
|
||||
if(adjust)
|
||||
{
|
||||
objRot[0] += rot.x;
|
||||
objRot[1] += rot.y;
|
||||
objRot[2] += rot.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
objRot[0] = rot.x;
|
||||
objRot[1] = rot.y;
|
||||
objRot[2] = rot.z;
|
||||
}
|
||||
|
||||
if (ptr.getRefData().getBaseNode() != 0) {
|
||||
if(Class::get(ptr).isActor())
|
||||
{
|
||||
/* HACK? Actors shouldn't really be rotating around X (or Y), but
|
||||
* currently it's done so for rotating the camera, which needs
|
||||
* clamping.
|
||||
*/
|
||||
const float half_pi = Ogre::Math::HALF_PI;
|
||||
|
||||
if(objRot[0] < -half_pi) objRot[0] = -half_pi;
|
||||
else if(objRot[0] > half_pi) objRot[0] = half_pi;
|
||||
}
|
||||
else
|
||||
{
|
||||
while(objRot[0] < -pi) objRot[0] += two_pi;
|
||||
while(objRot[0] > pi) objRot[0] -= two_pi;
|
||||
}
|
||||
|
||||
while(objRot[1] < -pi) objRot[1] += two_pi;
|
||||
while(objRot[1] > pi) objRot[1] -= two_pi;
|
||||
|
||||
while(objRot[2] < -pi) objRot[2] += two_pi;
|
||||
while(objRot[2] > pi) objRot[2] -= two_pi;
|
||||
|
||||
if(ptr.getRefData().getBaseNode() != 0)
|
||||
{
|
||||
mRendering->rotateObject(ptr);
|
||||
mPhysics->rotateObject(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void World::localRotateObject (const Ptr& ptr, float x, float y, float z)
|
||||
{
|
||||
if (ptr.getRefData().getBaseNode() != 0) {
|
||||
|
||||
ptr.getRefData().getLocalRotation().rot[0]=Ogre::Degree(x).valueRadians();
|
||||
ptr.getRefData().getLocalRotation().rot[1]=Ogre::Degree(y).valueRadians();
|
||||
ptr.getRefData().getLocalRotation().rot[2]=Ogre::Degree(z).valueRadians();
|
||||
|
||||
float fullRotateRad=Ogre::Degree(360).valueRadians();
|
||||
|
||||
while(ptr.getRefData().getLocalRotation().rot[0]>=fullRotateRad)
|
||||
ptr.getRefData().getLocalRotation().rot[0]-=fullRotateRad;
|
||||
while(ptr.getRefData().getLocalRotation().rot[1]>=fullRotateRad)
|
||||
ptr.getRefData().getLocalRotation().rot[1]-=fullRotateRad;
|
||||
while(ptr.getRefData().getLocalRotation().rot[2]>=fullRotateRad)
|
||||
ptr.getRefData().getLocalRotation().rot[2]-=fullRotateRad;
|
||||
|
||||
while(ptr.getRefData().getLocalRotation().rot[0]<=-fullRotateRad)
|
||||
ptr.getRefData().getLocalRotation().rot[0]+=fullRotateRad;
|
||||
while(ptr.getRefData().getLocalRotation().rot[1]<=-fullRotateRad)
|
||||
ptr.getRefData().getLocalRotation().rot[1]+=fullRotateRad;
|
||||
while(ptr.getRefData().getLocalRotation().rot[2]<=-fullRotateRad)
|
||||
ptr.getRefData().getLocalRotation().rot[2]+=fullRotateRad;
|
||||
|
||||
float *worldRot = ptr.getRefData().getPosition().rot;
|
||||
|
||||
Ogre::Quaternion worldRotQuat(Ogre::Quaternion(Ogre::Radian(-worldRot[0]), Ogre::Vector3::UNIT_X)*
|
||||
Ogre::Quaternion(Ogre::Radian(-worldRot[1]), Ogre::Vector3::UNIT_Y)*
|
||||
Ogre::Quaternion(Ogre::Radian(-worldRot[2]), Ogre::Vector3::UNIT_Z));
|
||||
|
||||
Ogre::Quaternion rot(Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-x).valueRadians()), Ogre::Vector3::UNIT_X)*
|
||||
Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-y).valueRadians()), Ogre::Vector3::UNIT_Y)*
|
||||
Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-z).valueRadians()), Ogre::Vector3::UNIT_Z));
|
||||
|
||||
ptr.getRefData().getBaseNode()->setOrientation(worldRotQuat*rot);
|
||||
mPhysics->rotateObject(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void World::adjustPosition(const Ptr &ptr)
|
||||
|
@ -923,10 +997,66 @@ namespace MWWorld
|
|||
!isSwimming(player->first) && !isFlying(player->first));
|
||||
moveObjectImp(player->first, vec.x, vec.y, vec.z);
|
||||
}
|
||||
// the only purpose this has currently is to update the debug drawer
|
||||
|
||||
processDoors(duration);
|
||||
|
||||
mPhysEngine->stepSimulation (duration);
|
||||
}
|
||||
|
||||
void World::processDoors(float duration)
|
||||
{
|
||||
std::map<MWWorld::Ptr, int>::iterator it = mDoorStates.begin();
|
||||
while (it != mDoorStates.end())
|
||||
{
|
||||
if (!mWorldScene->isCellActive(*it->first.getCell()))
|
||||
mDoorStates.erase(it++);
|
||||
else
|
||||
{
|
||||
float oldRot = Ogre::Radian(it->first.getRefData().getLocalRotation().rot[2]).valueDegrees();
|
||||
float diff = duration * 90;
|
||||
float targetRot = std::min(std::max(0.f, oldRot + diff * (it->second ? 1 : -1)), 90.f);
|
||||
localRotateObject(it->first, 0, 0, targetRot);
|
||||
|
||||
// AABB of the door
|
||||
Ogre::Vector3 min,max;
|
||||
mPhysics->getObjectAABB(it->first, min, max);
|
||||
Ogre::Vector3 dimensions = max-min;
|
||||
|
||||
std::vector<std::string> collisions = mPhysics->getCollisions(it->first);
|
||||
for (std::vector<std::string>::iterator cit = collisions.begin(); cit != collisions.end(); ++cit)
|
||||
{
|
||||
MWWorld::Ptr ptr = getPtrViaHandle(*cit);
|
||||
if (MWWorld::Class::get(ptr).isActor())
|
||||
{
|
||||
// we collided with an actor, we need to undo the rotation and push the door away from the actor
|
||||
|
||||
// figure out on which side of the door the actor we collided with is
|
||||
Ogre::Vector3 relativePos = it->first.getRefData().getBaseNode()->
|
||||
convertWorldToLocalPosition(ptr.getRefData().getBaseNode()->_getDerivedPosition());
|
||||
|
||||
float axisToCheck;
|
||||
if (dimensions.x > dimensions.y)
|
||||
axisToCheck = relativePos.y * boost::math::sign((min+max).y);
|
||||
else
|
||||
axisToCheck = relativePos.x * boost::math::sign((min+max).x);
|
||||
if (axisToCheck >= 0)
|
||||
targetRot = std::min(std::max(0.f, oldRot + diff*0.5f), 90.f);
|
||||
else
|
||||
targetRot = std::min(std::max(0.f, oldRot - diff*0.5f), 90.f);
|
||||
|
||||
localRotateObject(it->first, 0, 0, targetRot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((targetRot == 90.f && it->second) || targetRot == 0.f)
|
||||
mDoorStates.erase(it++);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool World::toggleCollisionMode()
|
||||
{
|
||||
return mPhysics->toggleCollisionMode();;
|
||||
|
@ -1022,8 +1152,8 @@ namespace MWWorld
|
|||
|
||||
float pitch, yaw;
|
||||
Ogre::Vector3 eyepos;
|
||||
mRendering->getPlayerData(eyepos, pitch, yaw);
|
||||
mPhysics->updatePlayerData(eyepos, pitch, yaw);
|
||||
mRendering->getCameraData(eyepos, pitch, yaw);
|
||||
mPhysics->updateCameraData(eyepos, pitch, yaw);
|
||||
|
||||
performUpdateSceneQueries ();
|
||||
|
||||
|
@ -1367,15 +1497,17 @@ namespace MWWorld
|
|||
return mRendering->vanityRotateCamera(rot);
|
||||
}
|
||||
|
||||
void World::setupPlayer(bool newGame)
|
||||
void World::setupPlayer()
|
||||
{
|
||||
const ESM::NPC* player = mStore.get<ESM::NPC>().find ("player");
|
||||
mPlayer = new MWWorld::Player (player, *this);
|
||||
mRendering->attachCameraTo(mPlayer->getPlayer());
|
||||
if (newGame)
|
||||
const ESM::NPC *player = mStore.get<ESM::NPC>().find("player");
|
||||
mPlayer = new MWWorld::Player(player, *this);
|
||||
|
||||
Ptr ptr = mPlayer->getPlayer();
|
||||
mRendering->setupPlayer(ptr);
|
||||
if (mNewGame)
|
||||
{
|
||||
MWWorld::Class::get(mPlayer->getPlayer()).getContainerStore(mPlayer->getPlayer()).fill(player->mInventory, "", mStore);
|
||||
MWWorld::Class::get(mPlayer->getPlayer()).getInventoryStore(mPlayer->getPlayer()).autoEquip (mPlayer->getPlayer());
|
||||
MWWorld::Class::get(ptr).getContainerStore(ptr).fill(player->mInventory, "", mStore);
|
||||
MWWorld::Class::get(ptr).getInventoryStore(ptr).autoEquip(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1383,6 +1515,8 @@ namespace MWWorld
|
|||
{
|
||||
mRendering->renderPlayer(mPlayer->getPlayer());
|
||||
mPhysics->addActor(mPlayer->getPlayer());
|
||||
if (mNewGame)
|
||||
toggleCollisionMode();
|
||||
}
|
||||
|
||||
void World::setupExternalRendering (MWRender::ExternalRendering& rendering)
|
||||
|
@ -1398,7 +1532,7 @@ namespace MWWorld
|
|||
Ogre::Vector3 playerPos(refdata.getPosition().pos);
|
||||
|
||||
const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle());
|
||||
if(!physactor->getOnGround() || isUnderwater(currentCell, playerPos))
|
||||
if((!physactor->getOnGround()&&physactor->getCollisionMode()) || isUnderwater(currentCell, playerPos))
|
||||
return 2;
|
||||
if((currentCell->mCell->mData.mFlags&ESM::Cell::NoSleep))
|
||||
return 1;
|
||||
|
@ -1425,4 +1559,27 @@ namespace MWWorld
|
|||
{
|
||||
mRendering->frameStarted(dt);
|
||||
}
|
||||
|
||||
void World::activateDoor(const MWWorld::Ptr& door)
|
||||
{
|
||||
if (mDoorStates.find(door) != mDoorStates.end())
|
||||
{
|
||||
// if currently opening, then close, if closing, then open
|
||||
mDoorStates[door] = !mDoorStates[door];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (door.getRefData().getLocalRotation().rot[2] == 0)
|
||||
mDoorStates[door] = 1; // open
|
||||
else
|
||||
mDoorStates[door] = 0; // close
|
||||
}
|
||||
}
|
||||
|
||||
bool World::getOpenOrCloseDoor(const Ptr &door)
|
||||
{
|
||||
if (mDoorStates.find(door) != mDoorStates.end())
|
||||
return !mDoorStates[door]; // if currently opening or closing, then do the opposite
|
||||
return door.getRefData().getLocalRotation().rot[2] == 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,6 +85,11 @@ namespace MWWorld
|
|||
float mFaced2Distance;
|
||||
int mNumFacing;
|
||||
|
||||
bool mNewGame;
|
||||
|
||||
std::map<MWWorld::Ptr, int> mDoorStates;
|
||||
///< only holds doors that are currently moving. 0 means closing, 1 opening
|
||||
|
||||
unsigned long lastTick;
|
||||
Ogre::Timer mTimer;
|
||||
|
||||
|
@ -110,6 +115,9 @@ namespace MWWorld
|
|||
void addContainerScripts(const Ptr& reference, Ptr::CellStore* cell);
|
||||
void PCDropped (const Ptr& item);
|
||||
|
||||
virtual void processDoors(float duration);
|
||||
///< Run physics simulation and modify \a world accordingly.
|
||||
|
||||
public:
|
||||
|
||||
World (OEngine::Render::OgreRenderer& renderer,
|
||||
|
@ -254,6 +262,8 @@ namespace MWWorld
|
|||
/// \param adjust indicates rotation should be set or adjusted
|
||||
virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false);
|
||||
|
||||
virtual void localRotateObject (const Ptr& ptr, float x, float y, float z);
|
||||
|
||||
virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos);
|
||||
///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr.
|
||||
|
||||
|
@ -345,8 +355,8 @@ namespace MWWorld
|
|||
mRendering->togglePreviewMode(enable);
|
||||
}
|
||||
|
||||
virtual bool toggleVanityMode(bool enable, bool force) {
|
||||
return mRendering->toggleVanityMode(enable, force);
|
||||
virtual bool toggleVanityMode(bool enable) {
|
||||
return mRendering->toggleVanityMode(enable);
|
||||
}
|
||||
|
||||
virtual void allowVanityMode(bool allow) {
|
||||
|
@ -363,9 +373,14 @@ namespace MWWorld
|
|||
|
||||
virtual bool vanityRotateCamera(float * rot);
|
||||
|
||||
virtual void setupPlayer(bool newGame);
|
||||
virtual void setupPlayer();
|
||||
virtual void renderPlayer();
|
||||
|
||||
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door);
|
||||
///< if activated, should this door be opened or closed?
|
||||
virtual void activateDoor(const MWWorld::Ptr& door);
|
||||
///< activate (open or close) an non-teleport door
|
||||
|
||||
virtual void setupExternalRendering (MWRender::ExternalRendering& rendering);
|
||||
|
||||
virtual int canRest();
|
||||
|
|
|
@ -313,7 +313,7 @@ public:
|
|||
void destroyInstance( Archive* arch) { delete arch; }
|
||||
};
|
||||
|
||||
class DirArchiveFactory : public FileSystemArchiveFactory
|
||||
class DirArchiveFactory : public ArchiveFactory
|
||||
{
|
||||
public:
|
||||
const String& getType() const
|
||||
|
@ -327,6 +327,11 @@ public:
|
|||
return new DirArchive(name);
|
||||
}
|
||||
|
||||
virtual Archive* createInstance(const String& name, bool readOnly)
|
||||
{
|
||||
return new DirArchive(name);
|
||||
}
|
||||
|
||||
void destroyInstance( Archive* arch) { delete arch; }
|
||||
};
|
||||
|
||||
|
|
|
@ -71,6 +71,19 @@ namespace Compiler
|
|||
return true;
|
||||
}
|
||||
|
||||
if (mState==EndNameState)
|
||||
{
|
||||
// optional repeated name after end statement
|
||||
if (mName!=loc.mLiteral)
|
||||
reportWarning ("Names for script " + mName + " do not match", loc);
|
||||
|
||||
mState = EndCompleteState;
|
||||
return false; // we are stopping here, because there might be more garbage on the end line,
|
||||
// that we must ignore.
|
||||
//
|
||||
/// \todo allow this workaround to be disabled for newer scripts
|
||||
}
|
||||
|
||||
return Parser::parseKeyword (keyword, loc, scanner);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,6 @@
|
|||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
#include <apps/openmw/mwworld/store.hpp>
|
||||
#include <apps/openmw/mwworld/cellstore.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
|
@ -132,38 +129,13 @@ void Cell::load(ESMReader &esm, bool saveContext)
|
|||
}
|
||||
}
|
||||
|
||||
void Cell::load(ESMReader &esm, MWWorld::ESMStore &store)
|
||||
void Cell::preLoad(ESMReader &esm) //Can't be "load" because it conflicts with function in esmtool
|
||||
{
|
||||
this->load(esm, false);
|
||||
}
|
||||
|
||||
// preload moved references
|
||||
while (esm.isNextSub("MVRF")) {
|
||||
CellRef ref;
|
||||
MovedCellRef cMRef;
|
||||
getNextMVRF(esm, cMRef);
|
||||
|
||||
MWWorld::Store<ESM::Cell> &cStore = const_cast<MWWorld::Store<ESM::Cell>&>(store.get<ESM::Cell>());
|
||||
ESM::Cell *cellAlt = const_cast<ESM::Cell*>(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1]));
|
||||
|
||||
// Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following
|
||||
// implementation when the oher implementation works as well.
|
||||
getNextRef(esm, ref);
|
||||
std::string lowerCase;
|
||||
|
||||
std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase),
|
||||
(int(*)(int)) std::tolower);
|
||||
|
||||
// Add data required to make reference appear in the correct cell.
|
||||
// We should not need to test for duplicates, as this part of the code is pre-cell merge.
|
||||
mMovedRefs.push_back(cMRef);
|
||||
// But there may be duplicates here!
|
||||
ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefnum);
|
||||
if (iter == cellAlt->mLeasedRefs.end())
|
||||
cellAlt->mLeasedRefs.push_back(ref);
|
||||
else
|
||||
*iter = ref;
|
||||
}
|
||||
|
||||
void Cell::postLoad(ESMReader &esm)
|
||||
{
|
||||
// Save position of the cell references and move on
|
||||
mContextList.push_back(esm.getContext());
|
||||
esm.skipRecord();
|
||||
|
|
|
@ -96,7 +96,8 @@ struct Cell
|
|||
CellRefTracker mLeasedRefs;
|
||||
MovedCellRefTracker mMovedRefs;
|
||||
|
||||
void load(ESMReader &esm, MWWorld::ESMStore &store);
|
||||
void preLoad(ESMReader &esm);
|
||||
void postLoad(ESMReader &esm);
|
||||
|
||||
// This method is left in for compatibility with esmtool. Parsing moved references currently requires
|
||||
// passing ESMStore, bit it does not know about this parameter, so we do it this way.
|
||||
|
|
|
@ -48,7 +48,7 @@ struct Weapon
|
|||
short mType;
|
||||
short mHealth;
|
||||
float mSpeed, mReach;
|
||||
short mEnchant; // Enchantment points
|
||||
short mEnchant; // Enchantment points. The real value is mEnchant/10.f
|
||||
unsigned char mChop[2], mSlash[2], mThrust[2]; // Min and max
|
||||
int mFlags;
|
||||
}; // 32 bytes
|
||||
|
|
|
@ -303,5 +303,28 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class NiFlipController : public Controller
|
||||
{
|
||||
public:
|
||||
int mTexSlot;
|
||||
float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources
|
||||
NiSourceTextureList mSources;
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
Controller::read(nif);
|
||||
mTexSlot = nif->getUInt();
|
||||
/*unknown=*/nif->getUInt();/*0?*/
|
||||
mDelta = nif->getFloat();
|
||||
mSources.read(nif);
|
||||
}
|
||||
|
||||
void post(NIFFile *nif)
|
||||
{
|
||||
Controller::post(nif);
|
||||
mSources.post(nif);
|
||||
}
|
||||
};
|
||||
|
||||
} // Namespace
|
||||
#endif
|
||||
|
|
|
@ -207,8 +207,8 @@ struct RecordFactoryEntry {
|
|||
static const RecordFactoryEntry recordFactories [] = {
|
||||
|
||||
{ "NiNode", &construct <NiNode >, RC_NiNode },
|
||||
{ "AvoidNode", &construct <NiNode >, RC_NiNode },
|
||||
{ "NiBSParticleNode", &construct <NiNode >, RC_NiNode },
|
||||
{ "AvoidNode", &construct <NiNode >, RC_AvoidNode },
|
||||
{ "NiBSParticleNode", &construct <NiNode >, RC_NiBSParticleNode },
|
||||
{ "NiBSAnimationNode", &construct <NiNode >, RC_NiBSAnimationNode },
|
||||
{ "NiBillboardNode", &construct <NiNode >, RC_NiNode },
|
||||
{ "NiTriShape", &construct <NiTriShape >, RC_NiTriShape },
|
||||
|
@ -235,6 +235,7 @@ static const RecordFactoryEntry recordFactories [] = {
|
|||
{ "NiMaterialColorController", &construct <NiMaterialColorController >, RC_NiMaterialColorController },
|
||||
{ "NiBSPArrayController", &construct <NiBSPArrayController >, RC_NiBSPArrayController },
|
||||
{ "NiParticleSystemController", &construct <NiParticleSystemController >, RC_NiParticleSystemController },
|
||||
{ "NiFlipController", &construct <NiFlipController >, RC_NiFlipController },
|
||||
{ "NiAmbientLight", &construct <NiLight >, RC_NiLight },
|
||||
{ "NiDirectionalLight", &construct <NiLight >, RC_NiLight },
|
||||
{ "NiTextureEffect", &construct <NiTextureEffect >, RC_NiTextureEffect },
|
||||
|
|
|
@ -128,13 +128,17 @@ struct NiNode : Node
|
|||
NodeList children;
|
||||
NodeList effects;
|
||||
|
||||
/* Known NiNode flags:
|
||||
0x01 hidden
|
||||
0x02 use mesh for collision
|
||||
0x04 use bounding box for collision (?)
|
||||
0x08 unknown, but common
|
||||
0x20, 0x40, 0x80 unknown
|
||||
*/
|
||||
enum Flags {
|
||||
Flag_Hidden = 0x0001,
|
||||
Flag_MeshCollision = 0x0002,
|
||||
Flag_BBoxCollision = 0x0004
|
||||
};
|
||||
enum BSAnimFlags {
|
||||
AnimFlag_AutoPlay = 0x0020
|
||||
};
|
||||
enum BSParticleFlags {
|
||||
ParticleFlag_AutoPlay = 0x0020
|
||||
};
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
|
|
|
@ -36,9 +36,11 @@ enum RecordType
|
|||
{
|
||||
RC_MISSING = 0,
|
||||
RC_NiNode,
|
||||
RC_AvoidNode,
|
||||
RC_NiTriShape,
|
||||
RC_NiRotatingParticles,
|
||||
RC_NiAutoNormalParticles,
|
||||
RC_NiBSParticleNode,
|
||||
RC_NiCamera,
|
||||
RC_NiTexturingProperty,
|
||||
RC_NiMaterialProperty,
|
||||
|
@ -59,6 +61,7 @@ enum RecordType
|
|||
RC_NiMaterialColorController,
|
||||
RC_NiBSPArrayController,
|
||||
RC_NiParticleSystemController,
|
||||
RC_NiFlipController,
|
||||
RC_NiBSAnimationNode,
|
||||
RC_NiLight,
|
||||
RC_NiTextureEffect,
|
||||
|
|
|
@ -163,6 +163,7 @@ typedef RecordPtrT<NiAutoNormalParticlesData> NiAutoNormalParticlesDataPtr;
|
|||
|
||||
typedef RecordListT<Node> NodeList;
|
||||
typedef RecordListT<Property> PropertyList;
|
||||
typedef RecordListT<NiSourceTexture> NiSourceTextureList;
|
||||
|
||||
} // Namespace
|
||||
#endif
|
||||
|
|
|
@ -185,6 +185,10 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node *
|
|||
else
|
||||
isCollisionNode = isCollisionNode && (node->recType != Nif::RC_RootCollisionNode);
|
||||
|
||||
// Don't collide with AvoidNode shapes
|
||||
if(node->recType == Nif::RC_AvoidNode)
|
||||
flags |= 0x800;
|
||||
|
||||
// Marker objects
|
||||
/// \todo don't do this in the editor
|
||||
std::string nodename = node->name;
|
||||
|
@ -255,9 +259,9 @@ void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif::
|
|||
assert(shape != NULL);
|
||||
|
||||
// Interpret flags
|
||||
bool hidden = (flags & 0x01) != 0; // Not displayed
|
||||
bool collide = (flags & 0x02) != 0; // Use mesh for collision
|
||||
bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision
|
||||
bool hidden = (flags&Nif::NiNode::Flag_Hidden) != 0;
|
||||
bool collide = (flags&Nif::NiNode::Flag_MeshCollision) != 0;
|
||||
bool bbcollide = (flags&Nif::NiNode::Flag_BBoxCollision) != 0;
|
||||
|
||||
// If the object was marked "NCO" earlier, it shouldn't collide with
|
||||
// anything. So don't do anything.
|
||||
|
|
|
@ -109,13 +109,26 @@ NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders;
|
|||
|
||||
void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape)
|
||||
{
|
||||
Ogre::SkeletonPtr skel;
|
||||
const Nif::NiTriShapeData *data = shape->data.getPtr();
|
||||
const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr());
|
||||
std::vector<Ogre::Vector3> srcVerts = data->vertices;
|
||||
std::vector<Ogre::Vector3> srcNorms = data->normals;
|
||||
Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC;
|
||||
bool vertShadowBuffer = false;
|
||||
|
||||
if(!shape->controller.empty())
|
||||
{
|
||||
Nif::ControllerPtr ctrl = shape->controller;
|
||||
do {
|
||||
if(ctrl->recType == Nif::RC_NiGeomMorpherController)
|
||||
{
|
||||
vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY;
|
||||
vertShadowBuffer = true;
|
||||
break;
|
||||
}
|
||||
} while(!(ctrl=ctrl->next).empty());
|
||||
}
|
||||
|
||||
if(skin != NULL)
|
||||
{
|
||||
vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY;
|
||||
|
@ -125,10 +138,6 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
|
|||
// explicitly attached later.
|
||||
mesh->setSkeletonName(mName);
|
||||
|
||||
// Get the skeleton resource, so vertices can be transformed into the bones' initial state.
|
||||
Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
|
||||
skel = skelMgr->getByName(mName);
|
||||
|
||||
// Convert vertices and normals to bone space from bind position. It would be
|
||||
// better to transform the bones into bind position, but there doesn't seem to
|
||||
// be a reliable way to do that.
|
||||
|
@ -139,11 +148,10 @@ 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++)
|
||||
{
|
||||
Ogre::Bone *bone = skel->getBone(bones[b]->name);
|
||||
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 = bone->_getFullTransform() * mat;
|
||||
mat = bones[b]->getWorldTransform() * mat;
|
||||
|
||||
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights;
|
||||
for(size_t i = 0;i < weights.size();i++)
|
||||
|
@ -296,6 +304,8 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
|
|||
// Assign bone weights for this TriShape
|
||||
if(skin != NULL)
|
||||
{
|
||||
Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(mName);
|
||||
|
||||
const Nif::NiSkinData *data = skin->data.getPtr();
|
||||
const Nif::NodeList &bones = skin->bones;
|
||||
for(size_t i = 0;i < bones.length();i++)
|
||||
|
|
|
@ -44,15 +44,6 @@
|
|||
#include "material.hpp"
|
||||
#include "mesh.hpp"
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
// TODO: Do something useful
|
||||
ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&)
|
||||
{ return o; }
|
||||
|
||||
}
|
||||
|
||||
namespace NifOgre
|
||||
{
|
||||
|
||||
|
@ -74,11 +65,7 @@ public:
|
|||
, mStopTime(ctrl->timeStop)
|
||||
{
|
||||
if(mDeltaInput)
|
||||
{
|
||||
mDeltaCount = mPhase;
|
||||
while(mDeltaCount < mStartTime)
|
||||
mDeltaCount += (mStopTime-mStartTime);
|
||||
}
|
||||
}
|
||||
|
||||
virtual Ogre::Real calculate(Ogre::Real value)
|
||||
|
@ -86,6 +73,9 @@ public:
|
|||
if(mDeltaInput)
|
||||
{
|
||||
mDeltaCount += value*mFrequency;
|
||||
if(mDeltaCount < mStartTime)
|
||||
mDeltaCount = mStopTime - std::fmod(mStartTime - mDeltaCount,
|
||||
mStopTime - mStartTime);
|
||||
mDeltaCount = std::fmod(mDeltaCount - mStartTime,
|
||||
mStopTime - mStartTime) + mStartTime;
|
||||
return mDeltaCount;
|
||||
|
@ -104,7 +94,7 @@ public:
|
|||
private:
|
||||
std::vector<Nif::NiVisData::VisData> mData;
|
||||
|
||||
virtual bool calculate(Ogre::Real time)
|
||||
bool calculate(Ogre::Real time) const
|
||||
{
|
||||
if(mData.size() == 0)
|
||||
return true;
|
||||
|
@ -144,10 +134,19 @@ public:
|
|||
, mData(data->mVis)
|
||||
{ }
|
||||
|
||||
virtual Ogre::Quaternion getRotation(float time) const
|
||||
{ return Ogre::Quaternion(); }
|
||||
|
||||
virtual Ogre::Vector3 getTranslation(float time) const
|
||||
{ return Ogre::Vector3(0.0f); }
|
||||
|
||||
virtual Ogre::Vector3 getScale(float time) const
|
||||
{ return Ogre::Vector3(1.0f); }
|
||||
|
||||
virtual Ogre::Real getValue() const
|
||||
{
|
||||
// Should not be called
|
||||
return 1.0f;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
virtual void setValue(Ogre::Real time)
|
||||
|
@ -170,6 +169,60 @@ public:
|
|||
Nif::Vector3KeyList mTranslations;
|
||||
Nif::FloatKeyList mScales;
|
||||
|
||||
static float interpKey(const Nif::FloatKeyList::VecType &keys, float time)
|
||||
{
|
||||
if(time <= keys.front().mTime)
|
||||
return keys.front().mValue;
|
||||
|
||||
Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1);
|
||||
for(;iter != keys.end();iter++)
|
||||
{
|
||||
if(iter->mTime < time)
|
||||
continue;
|
||||
|
||||
Nif::FloatKeyList::VecType::const_iterator last(iter-1);
|
||||
float a = (time-last->mTime) / (iter->mTime-last->mTime);
|
||||
return last->mValue + ((iter->mValue - last->mValue)*a);
|
||||
}
|
||||
return keys.back().mValue;
|
||||
}
|
||||
|
||||
static Ogre::Vector3 interpKey(const Nif::Vector3KeyList::VecType &keys, float time)
|
||||
{
|
||||
if(time <= keys.front().mTime)
|
||||
return keys.front().mValue;
|
||||
|
||||
Nif::Vector3KeyList::VecType::const_iterator iter(keys.begin()+1);
|
||||
for(;iter != keys.end();iter++)
|
||||
{
|
||||
if(iter->mTime < time)
|
||||
continue;
|
||||
|
||||
Nif::Vector3KeyList::VecType::const_iterator last(iter-1);
|
||||
float a = (time-last->mTime) / (iter->mTime-last->mTime);
|
||||
return last->mValue + ((iter->mValue - last->mValue)*a);
|
||||
}
|
||||
return keys.back().mValue;
|
||||
}
|
||||
|
||||
static Ogre::Quaternion interpKey(const Nif::QuaternionKeyList::VecType &keys, float time)
|
||||
{
|
||||
if(time <= keys.front().mTime)
|
||||
return keys.front().mValue;
|
||||
|
||||
Nif::QuaternionKeyList::VecType::const_iterator iter(keys.begin()+1);
|
||||
for(;iter != keys.end();iter++)
|
||||
{
|
||||
if(iter->mTime < time)
|
||||
continue;
|
||||
|
||||
Nif::QuaternionKeyList::VecType::const_iterator last(iter-1);
|
||||
float a = (time-last->mTime) / (iter->mTime-last->mTime);
|
||||
return Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue);
|
||||
}
|
||||
return keys.back().mValue;
|
||||
}
|
||||
|
||||
public:
|
||||
Value(Ogre::Node *target, const Nif::NiKeyframeData *data)
|
||||
: NodeTargetValue<Ogre::Real>(target)
|
||||
|
@ -178,6 +231,27 @@ public:
|
|||
, mScales(data->mScales)
|
||||
{ }
|
||||
|
||||
virtual Ogre::Quaternion getRotation(float time) const
|
||||
{
|
||||
if(mRotations.mKeys.size() > 0)
|
||||
return interpKey(mRotations.mKeys, time);
|
||||
return mNode->getOrientation();
|
||||
}
|
||||
|
||||
virtual Ogre::Vector3 getTranslation(float time) const
|
||||
{
|
||||
if(mTranslations.mKeys.size() > 0)
|
||||
return interpKey(mTranslations.mKeys, time);
|
||||
return mNode->getPosition();
|
||||
}
|
||||
|
||||
virtual Ogre::Vector3 getScale(float time) const
|
||||
{
|
||||
if(mScales.mKeys.size() > 0)
|
||||
return Ogre::Vector3(interpKey(mScales.mKeys, time));
|
||||
return mNode->getScale();
|
||||
}
|
||||
|
||||
virtual Ogre::Real getValue() const
|
||||
{
|
||||
// Should not be called
|
||||
|
@ -187,68 +261,11 @@ public:
|
|||
virtual void setValue(Ogre::Real time)
|
||||
{
|
||||
if(mRotations.mKeys.size() > 0)
|
||||
{
|
||||
if(time <= mRotations.mKeys.front().mTime)
|
||||
mNode->setOrientation(mRotations.mKeys.front().mValue);
|
||||
else if(time >= mRotations.mKeys.back().mTime)
|
||||
mNode->setOrientation(mRotations.mKeys.back().mValue);
|
||||
else
|
||||
{
|
||||
Nif::QuaternionKeyList::VecType::const_iterator iter(mRotations.mKeys.begin()+1);
|
||||
for(;iter != mRotations.mKeys.end();iter++)
|
||||
{
|
||||
if(iter->mTime < time)
|
||||
continue;
|
||||
|
||||
Nif::QuaternionKeyList::VecType::const_iterator last(iter-1);
|
||||
float a = (time-last->mTime) / (iter->mTime-last->mTime);
|
||||
mNode->setOrientation(Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mNode->setOrientation(interpKey(mRotations.mKeys, time));
|
||||
if(mTranslations.mKeys.size() > 0)
|
||||
{
|
||||
if(time <= mTranslations.mKeys.front().mTime)
|
||||
mNode->setPosition(mTranslations.mKeys.front().mValue);
|
||||
else if(time >= mTranslations.mKeys.back().mTime)
|
||||
mNode->setPosition(mTranslations.mKeys.back().mValue);
|
||||
else
|
||||
{
|
||||
Nif::Vector3KeyList::VecType::const_iterator iter(mTranslations.mKeys.begin()+1);
|
||||
for(;iter != mTranslations.mKeys.end();iter++)
|
||||
{
|
||||
if(iter->mTime < time)
|
||||
continue;
|
||||
|
||||
Nif::Vector3KeyList::VecType::const_iterator last(iter-1);
|
||||
float a = (time-last->mTime) / (iter->mTime-last->mTime);
|
||||
mNode->setPosition(last->mValue + ((iter->mValue - last->mValue)*a));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mNode->setPosition(interpKey(mTranslations.mKeys, time));
|
||||
if(mScales.mKeys.size() > 0)
|
||||
{
|
||||
if(time <= mScales.mKeys.front().mTime)
|
||||
mNode->setScale(Ogre::Vector3(mScales.mKeys.front().mValue));
|
||||
else if(time >= mScales.mKeys.back().mTime)
|
||||
mNode->setScale(Ogre::Vector3(mScales.mKeys.back().mValue));
|
||||
else
|
||||
{
|
||||
Nif::FloatKeyList::VecType::const_iterator iter(mScales.mKeys.begin()+1);
|
||||
for(;iter != mScales.mKeys.end();iter++)
|
||||
{
|
||||
if(iter->mTime < time)
|
||||
continue;
|
||||
|
||||
Nif::FloatKeyList::VecType::const_iterator last(iter-1);
|
||||
float a = (time-last->mTime) / (iter->mTime-last->mTime);
|
||||
mNode->setScale(Ogre::Vector3(last->mValue + ((iter->mValue - last->mValue)*a)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mNode->setScale(Ogre::Vector3(interpKey(mScales.mKeys, time)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -289,7 +306,7 @@ public:
|
|||
}
|
||||
|
||||
public:
|
||||
Value(const Ogre::MaterialPtr &material, Nif::NiUVData *data)
|
||||
Value(const Ogre::MaterialPtr &material, const Nif::NiUVData *data)
|
||||
: mMaterial(material)
|
||||
, mUTrans(data->mKeyList[0])
|
||||
, mVTrans(data->mKeyList[1])
|
||||
|
@ -329,11 +346,70 @@ public:
|
|||
typedef DefaultFunction Function;
|
||||
};
|
||||
|
||||
class ParticleSystemController
|
||||
{
|
||||
public:
|
||||
class Value : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
Ogre::ParticleSystem *mParticleSys;
|
||||
float mEmitStart;
|
||||
float mEmitStop;
|
||||
|
||||
public:
|
||||
Value(Ogre::ParticleSystem *psys, const Nif::NiParticleSystemController *pctrl)
|
||||
: mParticleSys(psys)
|
||||
, mEmitStart(pctrl->startTime)
|
||||
, mEmitStop(pctrl->stopTime)
|
||||
{
|
||||
}
|
||||
|
||||
Ogre::Real getValue() const
|
||||
{ return 0.0f; }
|
||||
|
||||
void setValue(Ogre::Real value)
|
||||
{
|
||||
mParticleSys->setEmitting(value >= mEmitStart && value < mEmitStop);
|
||||
}
|
||||
};
|
||||
|
||||
typedef DefaultFunction Function;
|
||||
};
|
||||
|
||||
class GeomMorpherController
|
||||
{
|
||||
public:
|
||||
class Value : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
Ogre::SubEntity *mSubEntity;
|
||||
std::vector<Nif::NiMorphData::MorphData> mMorphs;
|
||||
|
||||
public:
|
||||
Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data)
|
||||
: mSubEntity(subent)
|
||||
, mMorphs(data->mMorphs)
|
||||
{ }
|
||||
|
||||
virtual Ogre::Real getValue() const
|
||||
{
|
||||
// Should not be called
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
virtual void setValue(Ogre::Real value)
|
||||
{
|
||||
// TODO: Implement
|
||||
}
|
||||
};
|
||||
|
||||
typedef DefaultFunction Function;
|
||||
};
|
||||
|
||||
|
||||
/** Manual resource loader for NIF objects (meshes, particle systems, etc).
|
||||
* This is the main class responsible for translating the internal NIF
|
||||
* structures into something Ogre can use.
|
||||
/** Object creator for NIFs. This is the main class responsible for creating
|
||||
* "live" Ogre objects (entities, particle systems, controllers, etc) from
|
||||
* their NIF equivalents.
|
||||
*/
|
||||
class NIFObjectLoader
|
||||
{
|
||||
|
@ -349,14 +425,79 @@ class NIFObjectLoader
|
|||
}
|
||||
|
||||
|
||||
static void createEntity(const std::string &name, const std::string &group,
|
||||
Ogre::SceneManager *sceneMgr, ObjectList &objectlist,
|
||||
const Nif::Node *node, int flags, int animflags)
|
||||
{
|
||||
const Nif::NiTriShape *shape = static_cast<const Nif::NiTriShape*>(node);
|
||||
|
||||
std::string fullname = name+"@index="+Ogre::StringConverter::toString(shape->recIndex);
|
||||
if(shape->name.length() > 0)
|
||||
fullname += "@shape="+shape->name;
|
||||
Misc::StringUtils::toLower(fullname);
|
||||
|
||||
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
|
||||
if(meshMgr.getByName(fullname).isNull())
|
||||
NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex);
|
||||
|
||||
Ogre::Entity *entity = sceneMgr->createEntity(fullname);
|
||||
entity->setVisible(!(flags&Nif::NiNode::Flag_Hidden));
|
||||
|
||||
objectlist.mEntities.push_back(entity);
|
||||
if(objectlist.mSkelBase)
|
||||
{
|
||||
if(entity->hasSkeleton())
|
||||
entity->shareSkeletonInstanceWith(objectlist.mSkelBase);
|
||||
else
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity);
|
||||
}
|
||||
}
|
||||
|
||||
Nif::ControllerPtr ctrl = node->controller;
|
||||
while(!ctrl.empty())
|
||||
{
|
||||
if(ctrl->recType == Nif::RC_NiUVController)
|
||||
{
|
||||
const Nif::NiUVController *uv = static_cast<const Nif::NiUVController*>(ctrl.getPtr());
|
||||
|
||||
const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial();
|
||||
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
|
||||
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
|
||||
Ogre::ControllerValueRealPtr());
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
else if(ctrl->recType == Nif::RC_NiGeomMorpherController)
|
||||
{
|
||||
const Nif::NiGeomMorpherController *geom = static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr());
|
||||
|
||||
Ogre::SubEntity *subent = entity->getSubEntity(0);
|
||||
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
|
||||
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
|
||||
Ogre::ControllerValueRealPtr());
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(subent, geom->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl)
|
||||
{
|
||||
Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif");
|
||||
emitter->setParticleVelocity(partctrl->velocity-partctrl->velocityRandom,
|
||||
partctrl->velocity+partctrl->velocityRandom);
|
||||
emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f,
|
||||
partctrl->velocity + partctrl->velocityRandom*0.5f);
|
||||
emitter->setEmissionRate(partctrl->emitRate);
|
||||
emitter->setTimeToLive(partctrl->lifetime-partctrl->lifetimeRandom,
|
||||
partctrl->lifetime+partctrl->lifetimeRandom);
|
||||
emitter->setTimeToLive(partctrl->lifetime - partctrl->lifetimeRandom*0.5f,
|
||||
partctrl->lifetime + partctrl->lifetimeRandom*0.5f);
|
||||
emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x));
|
||||
emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y));
|
||||
emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z));
|
||||
|
@ -416,9 +557,9 @@ class NIFObjectLoader
|
|||
}
|
||||
}
|
||||
|
||||
static Ogre::ParticleSystem *createParticleSystem(const std::string &name, const std::string &group,
|
||||
Ogre::SceneManager *sceneMgr, Ogre::Entity *entitybase,
|
||||
const Nif::Node *partnode)
|
||||
static void createParticleSystem(const std::string &name, const std::string &group,
|
||||
Ogre::SceneManager *sceneMgr, ObjectList &objectlist,
|
||||
const Nif::Node *partnode, int flags, int partflags)
|
||||
{
|
||||
const Nif::NiAutoNormalParticlesData *particledata = NULL;
|
||||
if(partnode->recType == Nif::RC_NiAutoNormalParticles)
|
||||
|
@ -426,13 +567,13 @@ class NIFObjectLoader
|
|||
else if(partnode->recType == Nif::RC_NiRotatingParticles)
|
||||
particledata = static_cast<const Nif::NiRotatingParticles*>(partnode)->data.getPtr();
|
||||
|
||||
Ogre::ParticleSystem *partsys = sceneMgr->createParticleSystem();
|
||||
try {
|
||||
std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex);
|
||||
if(partnode->name.length() > 0)
|
||||
fullname += "@type="+partnode->name;
|
||||
Misc::StringUtils::toLower(fullname);
|
||||
|
||||
Ogre::ParticleSystem *partsys = sceneMgr->createParticleSystem();
|
||||
|
||||
const Nif::NiTexturingProperty *texprop = NULL;
|
||||
const Nif::NiMaterialProperty *matprop = NULL;
|
||||
const Nif::NiAlphaProperty *alphaprop = NULL;
|
||||
|
@ -467,9 +608,17 @@ class NIFObjectLoader
|
|||
if(!partctrl->emitter.empty() && !partsys->isAttached())
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex);
|
||||
Ogre::Bone *trgtbone = entitybase->getSkeleton()->getBone(trgtid);
|
||||
entitybase->attachObjectToBone(trgtbone->getName(), partsys);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys);
|
||||
}
|
||||
|
||||
Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ?
|
||||
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
|
||||
Ogre::ControllerValueRealPtr());
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW ParticleSystemController::Value(partsys, partctrl));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&Nif::NiNode::ParticleFlag_AutoPlay)));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
|
@ -477,22 +626,80 @@ class NIFObjectLoader
|
|||
if(!partsys->isAttached())
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex);
|
||||
Ogre::Bone *trgtbone = entitybase->getSkeleton()->getBone(trgtid);
|
||||
entitybase->attachObjectToBone(trgtbone->getName(), partsys);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys);
|
||||
}
|
||||
|
||||
partsys->setVisible(!(flags&Nif::NiNode::Flag_Hidden));
|
||||
objectlist.mParticles.push_back(partsys);
|
||||
}
|
||||
|
||||
|
||||
static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectList &objectlist, int animflags)
|
||||
{
|
||||
do {
|
||||
if(ctrl->recType == Nif::RC_NiVisController)
|
||||
{
|
||||
const Nif::NiVisController *vis = static_cast<const Nif::NiVisController*>(ctrl.getPtr());
|
||||
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
|
||||
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
|
||||
Ogre::ControllerValueRealPtr());
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
else if(ctrl->recType == Nif::RC_NiKeyframeController)
|
||||
{
|
||||
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
|
||||
if(!key->data.empty())
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
|
||||
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
|
||||
Ogre::ControllerValueRealPtr());
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
}
|
||||
ctrl = ctrl->next;
|
||||
} while(!ctrl.empty());
|
||||
}
|
||||
|
||||
|
||||
static void extractTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap &textkeys)
|
||||
{
|
||||
for(size_t i = 0;i < tk->list.size();i++)
|
||||
{
|
||||
const std::string &str = tk->list[i].text;
|
||||
std::string::size_type pos = 0;
|
||||
while(pos < str.length())
|
||||
{
|
||||
if(::isspace(str[pos]))
|
||||
{
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos));
|
||||
std::string result = str.substr(pos, nextpos-pos);
|
||||
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
|
||||
|
||||
pos = nextpos;
|
||||
}
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
std::cerr<< "Particles exception: "<<e.what() <<std::endl;
|
||||
sceneMgr->destroyParticleSystem(partsys);
|
||||
partsys = NULL;
|
||||
};
|
||||
return partsys;
|
||||
}
|
||||
|
||||
|
||||
static void createObjects(const std::string &name, const std::string &group,
|
||||
Ogre::SceneManager *sceneMgr, const Nif::Node *node,
|
||||
ObjectList &objectlist, int flags)
|
||||
ObjectList &objectlist, int flags, int animflags, int partflags)
|
||||
{
|
||||
// Do not create objects for the collision shape (includes all children)
|
||||
if(node->recType == Nif::RC_RootCollisionNode)
|
||||
|
@ -503,12 +710,24 @@ class NIFObjectLoader
|
|||
if (node->name.find("marker") != std::string::npos)
|
||||
return;
|
||||
|
||||
if(node->recType == Nif::RC_NiBSAnimationNode)
|
||||
animflags |= node->flags;
|
||||
else if(node->recType == Nif::RC_NiBSParticleNode)
|
||||
partflags |= node->flags;
|
||||
else
|
||||
flags |= node->flags;
|
||||
|
||||
Nif::ExtraPtr e = node->extra;
|
||||
while(!e.empty())
|
||||
{
|
||||
if(e->recType == Nif::RC_NiStringExtraData)
|
||||
if(e->recType == Nif::RC_NiTextKeyExtraData)
|
||||
{
|
||||
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
|
||||
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex);
|
||||
extractTextKeys(tk, objectlist.mTextKeys[trgtid]);
|
||||
}
|
||||
else if(e->recType == Nif::RC_NiStringExtraData)
|
||||
{
|
||||
const Nif::NiStringExtraData *sd = static_cast<const Nif::NiStringExtraData*>(e.getPtr());
|
||||
// String markers may contain important information
|
||||
|
@ -520,9 +739,13 @@ class NIFObjectLoader
|
|||
flags |= 0x80000000;
|
||||
}
|
||||
}
|
||||
|
||||
e = e->extra;
|
||||
}
|
||||
|
||||
if(!node->controller.empty())
|
||||
createNodeControllers(name, node->controller, objectlist, animflags);
|
||||
|
||||
if(node->recType == Nif::RC_NiCamera)
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex);
|
||||
|
@ -530,94 +753,15 @@ class NIFObjectLoader
|
|||
objectlist.mCameras.push_back(trgtbone);
|
||||
}
|
||||
|
||||
Nif::ControllerPtr ctrl = node->controller;
|
||||
while(!ctrl.empty())
|
||||
{
|
||||
if(ctrl->recType == Nif::RC_NiVisController)
|
||||
{
|
||||
const Nif::NiVisController *vis = static_cast<const Nif::NiVisController*>(ctrl.getPtr());
|
||||
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
Ogre::ControllerValueRealPtr srcval; /* Filled in later */
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, false));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
else if(ctrl->recType == Nif::RC_NiKeyframeController)
|
||||
{
|
||||
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
|
||||
if(!key->data.empty())
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
Ogre::ControllerValueRealPtr srcval; /* Filled in later */
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
}
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
|
||||
if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000))
|
||||
{
|
||||
const Nif::NiTriShape *shape = static_cast<const Nif::NiTriShape*>(node);
|
||||
|
||||
std::string fullname = name+"@index="+Ogre::StringConverter::toString(shape->recIndex);
|
||||
if(shape->name.length() > 0)
|
||||
fullname += "@shape="+shape->name;
|
||||
Misc::StringUtils::toLower(fullname);
|
||||
|
||||
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
|
||||
if(meshMgr.getByName(fullname).isNull())
|
||||
NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex);
|
||||
|
||||
Ogre::Entity *entity = sceneMgr->createEntity(fullname);
|
||||
entity->setVisible(!(flags&0x01));
|
||||
|
||||
objectlist.mEntities.push_back(entity);
|
||||
if(objectlist.mSkelBase)
|
||||
{
|
||||
if(entity->hasSkeleton())
|
||||
entity->shareSkeletonInstanceWith(objectlist.mSkelBase);
|
||||
else
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity);
|
||||
}
|
||||
}
|
||||
|
||||
Nif::ControllerPtr ctrl = node->controller;
|
||||
while(!ctrl.empty())
|
||||
{
|
||||
if(ctrl->recType == Nif::RC_NiUVController)
|
||||
{
|
||||
const Nif::NiUVController *uv = static_cast<const Nif::NiUVController*>(ctrl.getPtr());
|
||||
|
||||
const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial();
|
||||
Ogre::ControllerValueRealPtr srcval(Ogre::ControllerManager::getSingleton().getFrameTimeSource());
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, true));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
createEntity(name, group, sceneMgr, objectlist, node, flags, animflags);
|
||||
}
|
||||
|
||||
if((node->recType == Nif::RC_NiAutoNormalParticles ||
|
||||
node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000))
|
||||
{
|
||||
Ogre::ParticleSystem *partsys = createParticleSystem(name, group, sceneMgr, objectlist.mSkelBase, node);
|
||||
if(partsys != NULL)
|
||||
{
|
||||
partsys->setVisible(!(flags&0x01));
|
||||
objectlist.mParticles.push_back(partsys);
|
||||
}
|
||||
createParticleSystem(name, group, sceneMgr, objectlist, node, flags, partflags);
|
||||
}
|
||||
|
||||
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
|
||||
|
@ -627,7 +771,7 @@ class NIFObjectLoader
|
|||
for(size_t i = 0;i < children.length();i++)
|
||||
{
|
||||
if(!children[i].empty())
|
||||
createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags);
|
||||
createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags, animflags, partflags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -674,7 +818,7 @@ public:
|
|||
// Create a base skeleton entity if this NIF needs one
|
||||
createSkelBase(name, group, sceneMgr, node, objectlist);
|
||||
}
|
||||
createObjects(name, group, sceneMgr, node, objectlist, flags);
|
||||
createObjects(name, group, sceneMgr, node, objectlist, flags, 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -757,12 +901,15 @@ ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonena
|
|||
}
|
||||
|
||||
|
||||
ObjectList Loader::createObjectBase(Ogre::SceneManager *sceneMgr, std::string name, const std::string &group)
|
||||
ObjectList Loader::createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group)
|
||||
{
|
||||
ObjectList objectlist;
|
||||
|
||||
Misc::StringUtils::toLower(name);
|
||||
NIFObjectLoader::load(sceneMgr, objectlist, name, group, 0xC0000000);
|
||||
NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group, 0xC0000000);
|
||||
|
||||
if(objectlist.mSkelBase)
|
||||
parentNode->attachObject(objectlist.mSkelBase);
|
||||
|
||||
return objectlist;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ struct ObjectList {
|
|||
// bones in the mSkelBase which are NiCamera nodes.
|
||||
std::vector<Ogre::Bone*> mCameras;
|
||||
|
||||
std::map<int,TextKeyMap> mTextKeys;
|
||||
|
||||
std::vector<Ogre::Controller<Ogre::Real> > mControllers;
|
||||
|
||||
ObjectList() : mSkelBase(0)
|
||||
|
@ -69,7 +71,7 @@ public:
|
|||
std::string name,
|
||||
const std::string &group="General");
|
||||
|
||||
static ObjectList createObjectBase(Ogre::SceneManager *sceneMgr,
|
||||
static ObjectList createObjectBase(Ogre::SceneNode *parentNode,
|
||||
std::string name,
|
||||
const std::string &group="General");
|
||||
};
|
||||
|
@ -85,6 +87,10 @@ public:
|
|||
NodeTargetValue(Ogre::Node *target) : mNode(target)
|
||||
{ }
|
||||
|
||||
virtual Ogre::Quaternion getRotation(T value) const = 0;
|
||||
virtual Ogre::Vector3 getTranslation(T value) const = 0;
|
||||
virtual Ogre::Vector3 getScale(T value) const = 0;
|
||||
|
||||
void setNode(Ogre::Node *target)
|
||||
{ mNode = target; }
|
||||
Ogre::Node *getNode() const
|
||||
|
@ -94,14 +100,4 @@ typedef Ogre::SharedPtr<NodeTargetValue<Ogre::Real> > NodeTargetValueRealPtr;
|
|||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
// These operators allow extra data types to be stored in an Ogre::Any
|
||||
// object, which can then be stored in user object bindings on the nodes
|
||||
|
||||
ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,142 +11,7 @@
|
|||
namespace NifOgre
|
||||
{
|
||||
|
||||
void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector<const Nif::NiKeyframeController*> &ctrls, const std::vector<std::string> &targets, float startTime, float stopTime)
|
||||
{
|
||||
Ogre::Animation *anim = skel->createAnimation(name, stopTime);
|
||||
|
||||
for(size_t i = 0;i < ctrls.size();i++)
|
||||
{
|
||||
const Nif::NiKeyframeController *kfc = ctrls[i];
|
||||
if(kfc->data.empty())
|
||||
continue;
|
||||
const Nif::NiKeyframeData *kf = kfc->data.getPtr();
|
||||
|
||||
/* Get the keyframes and make sure they're sorted first to last */
|
||||
const Nif::QuaternionKeyList &quatkeys = kf->mRotations;
|
||||
const Nif::Vector3KeyList &trankeys = kf->mTranslations;
|
||||
const Nif::FloatKeyList &scalekeys = kf->mScales;
|
||||
|
||||
Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin();
|
||||
Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin();
|
||||
Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin();
|
||||
|
||||
Ogre::Bone *bone = skel->getBone(targets[i]);
|
||||
// NOTE: For some reason, Ogre doesn't like the node track ID being different from
|
||||
// the bone ID
|
||||
Ogre::NodeAnimationTrack *nodetrack = anim->hasNodeTrack(bone->getHandle()) ?
|
||||
anim->getNodeTrack(bone->getHandle()) :
|
||||
anim->createNodeTrack(bone->getHandle(), bone);
|
||||
|
||||
Ogre::Quaternion lastquat, curquat;
|
||||
Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f);
|
||||
Ogre::Vector3 lastscale(1.0f), curscale(1.0f);
|
||||
if(quatiter != quatkeys.mKeys.end())
|
||||
lastquat = curquat = quatiter->mValue;
|
||||
if(traniter != trankeys.mKeys.end())
|
||||
lasttrans = curtrans = traniter->mValue;
|
||||
if(scaleiter != scalekeys.mKeys.end())
|
||||
lastscale = curscale = Ogre::Vector3(scaleiter->mValue);
|
||||
|
||||
bool didlast = false;
|
||||
while(!didlast)
|
||||
{
|
||||
float curtime = std::numeric_limits<float>::max();
|
||||
|
||||
//Get latest time
|
||||
if(quatiter != quatkeys.mKeys.end())
|
||||
curtime = std::min(curtime, quatiter->mTime);
|
||||
if(traniter != trankeys.mKeys.end())
|
||||
curtime = std::min(curtime, traniter->mTime);
|
||||
if(scaleiter != scalekeys.mKeys.end())
|
||||
curtime = std::min(curtime, scaleiter->mTime);
|
||||
|
||||
curtime = std::max(curtime, startTime);
|
||||
if(curtime >= stopTime)
|
||||
{
|
||||
didlast = true;
|
||||
curtime = stopTime;
|
||||
}
|
||||
|
||||
// Get the latest quaternions, translations, and scales for the
|
||||
// current time
|
||||
while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime)
|
||||
{
|
||||
lastquat = curquat;
|
||||
if(++quatiter != quatkeys.mKeys.end())
|
||||
curquat = quatiter->mValue;
|
||||
}
|
||||
while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime)
|
||||
{
|
||||
lasttrans = curtrans;
|
||||
if(++traniter != trankeys.mKeys.end())
|
||||
curtrans = traniter->mValue;
|
||||
}
|
||||
while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime)
|
||||
{
|
||||
lastscale = curscale;
|
||||
if(++scaleiter != scalekeys.mKeys.end())
|
||||
curscale = Ogre::Vector3(scaleiter->mValue);
|
||||
}
|
||||
|
||||
Ogre::TransformKeyFrame *kframe;
|
||||
kframe = nodetrack->createNodeKeyFrame(curtime);
|
||||
if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin())
|
||||
kframe->setRotation(curquat);
|
||||
else
|
||||
{
|
||||
Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1;
|
||||
float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime);
|
||||
kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat));
|
||||
}
|
||||
if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin())
|
||||
kframe->setTranslate(curtrans);
|
||||
else
|
||||
{
|
||||
Nif::Vector3KeyList::VecType::const_iterator last = traniter-1;
|
||||
float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime);
|
||||
kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff));
|
||||
}
|
||||
if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin())
|
||||
kframe->setScale(curscale);
|
||||
else
|
||||
{
|
||||
Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1;
|
||||
float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime);
|
||||
kframe->setScale(lastscale + ((curscale-lastscale)*diff));
|
||||
}
|
||||
}
|
||||
}
|
||||
anim->optimise();
|
||||
}
|
||||
|
||||
|
||||
TextKeyMap NIFSkeletonLoader::extractTextKeys(const Nif::NiTextKeyExtraData *tk)
|
||||
{
|
||||
TextKeyMap textkeys;
|
||||
for(size_t i = 0;i < tk->list.size();i++)
|
||||
{
|
||||
const std::string &str = tk->list[i].text;
|
||||
std::string::size_type pos = 0;
|
||||
while(pos < str.length())
|
||||
{
|
||||
if(::isspace(str[pos]))
|
||||
{
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos));
|
||||
std::string result = str.substr(pos, nextpos-pos);
|
||||
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
|
||||
|
||||
pos = nextpos;
|
||||
}
|
||||
}
|
||||
return textkeys;
|
||||
}
|
||||
|
||||
void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent)
|
||||
void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent)
|
||||
{
|
||||
Ogre::Bone *bone;
|
||||
if(!skel->hasBone(node->name))
|
||||
|
@ -164,6 +29,8 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
|
|||
if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */
|
||||
node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */
|
||||
node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */
|
||||
node->recType == Nif::RC_NiBSAnimationNode || /* Handled in the object loader */
|
||||
node->recType == Nif::RC_NiBSParticleNode ||
|
||||
node->recType == Nif::RC_NiCamera ||
|
||||
node->recType == Nif::RC_NiAutoNormalParticles ||
|
||||
node->recType == Nif::RC_NiRotatingParticles
|
||||
|
@ -173,28 +40,16 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
|
|||
Nif::ControllerPtr ctrl = node->controller;
|
||||
while(!ctrl.empty())
|
||||
{
|
||||
if(ctrl->recType == Nif::RC_NiKeyframeController)
|
||||
ctrls.push_back(static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr()));
|
||||
else if(!(ctrl->recType == Nif::RC_NiParticleSystemController ||
|
||||
if(!(ctrl->recType == Nif::RC_NiParticleSystemController ||
|
||||
ctrl->recType == Nif::RC_NiVisController ||
|
||||
ctrl->recType == Nif::RC_NiUVController
|
||||
ctrl->recType == Nif::RC_NiUVController ||
|
||||
ctrl->recType == Nif::RC_NiKeyframeController ||
|
||||
ctrl->recType == Nif::RC_NiGeomMorpherController
|
||||
))
|
||||
warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName());
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
|
||||
Nif::ExtraPtr e = node->extra;
|
||||
while(!e.empty())
|
||||
{
|
||||
if(e->recType == Nif::RC_NiTextKeyExtraData && !animroot)
|
||||
{
|
||||
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
|
||||
textkeys = extractTextKeys(tk);
|
||||
animroot = bone;
|
||||
}
|
||||
e = e->extra;
|
||||
}
|
||||
|
||||
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
|
||||
if(ninode)
|
||||
{
|
||||
|
@ -202,7 +57,7 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
|
|||
for(size_t i = 0;i < children.length();i++)
|
||||
{
|
||||
if(!children[i].empty())
|
||||
buildBones(skel, children[i].getPtr(), animroot, textkeys, ctrls, bone);
|
||||
buildBones(skel, children[i].getPtr(), bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,84 +70,14 @@ void NIFSkeletonLoader::loadResource(Ogre::Resource *resource)
|
|||
Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName()));
|
||||
const Nif::Node *node = static_cast<const Nif::Node*>(nif->getRoot(0));
|
||||
|
||||
std::vector<const Nif::NiKeyframeController*> ctrls;
|
||||
Ogre::Bone *animroot = NULL;
|
||||
TextKeyMap textkeys;
|
||||
try {
|
||||
buildBones(skel, node, animroot, textkeys, ctrls);
|
||||
buildBones(skel, node);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
std::cerr<< "Exception while loading "<<skel->getName() <<std::endl;
|
||||
std::cerr<< e.what() <<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Animations without textkeys don't get Ogre::Animation objects. */
|
||||
if(!animroot)
|
||||
return;
|
||||
|
||||
std::vector<std::string> targets;
|
||||
// TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file
|
||||
if(ctrls.size() == 0) // No animations? Then we're done.
|
||||
return;
|
||||
|
||||
float maxtime = 0.0f;
|
||||
for(size_t i = 0;i < ctrls.size();i++)
|
||||
{
|
||||
const Nif::NiKeyframeController *ctrl = ctrls[i];
|
||||
maxtime = std::max(maxtime, ctrl->timeStop);
|
||||
Nif::Named *target = dynamic_cast<Nif::Named*>(ctrl->target.getPtr());
|
||||
if(target != NULL)
|
||||
targets.push_back(target->name);
|
||||
}
|
||||
|
||||
if(targets.size() != ctrls.size())
|
||||
{
|
||||
warn("Target size mismatch ("+Ogre::StringConverter::toString(targets.size())+" targets, "+
|
||||
Ogre::StringConverter::toString(ctrls.size())+" controllers)");
|
||||
return;
|
||||
}
|
||||
|
||||
Ogre::UserObjectBindings &bindings = animroot->getUserObjectBindings();
|
||||
bindings.setUserAny(sTextKeyExtraDataID, Ogre::Any(true));
|
||||
|
||||
std::string currentgroup;
|
||||
TextKeyMap::const_iterator keyiter = textkeys.begin();
|
||||
for(keyiter = textkeys.begin();keyiter != textkeys.end();keyiter++)
|
||||
{
|
||||
std::string::size_type sep = keyiter->second.find(':');
|
||||
if((sep == currentgroup.length() && keyiter->second.compare(0, sep, currentgroup) == 0) ||
|
||||
(sep == sizeof("soundgen")-1 && keyiter->second.compare(0, sep, "soundgen") == 0) ||
|
||||
(sep == sizeof("sound")-1 && keyiter->second.compare(0, sep, "sound") == 0))
|
||||
continue;
|
||||
currentgroup = keyiter->second.substr(0, sep);
|
||||
|
||||
if(skel->hasAnimation(currentgroup))
|
||||
continue;
|
||||
|
||||
TextKeyMap::const_iterator lastkeyiter = textkeys.end();
|
||||
while((--lastkeyiter)->first > keyiter->first)
|
||||
{
|
||||
if(lastkeyiter->second.find(':') == currentgroup.length() &&
|
||||
lastkeyiter->second.compare(0, currentgroup.length(), currentgroup) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
buildAnimation(skel, currentgroup, ctrls, targets, keyiter->first, lastkeyiter->first);
|
||||
|
||||
TextKeyMap::const_iterator insiter(keyiter);
|
||||
TextKeyMap groupkeys;
|
||||
do {
|
||||
sep = insiter->second.find(':');
|
||||
if(sep == currentgroup.length() && insiter->second.compare(0, sep, currentgroup) == 0)
|
||||
groupkeys.insert(std::make_pair(insiter->first, insiter->second.substr(sep+2)));
|
||||
else if((sep == sizeof("soundgen")-1 && insiter->second.compare(0, sep, "soundgen") == 0) ||
|
||||
(sep == sizeof("sound")-1 && insiter->second.compare(0, sep, "sound") == 0))
|
||||
groupkeys.insert(std::make_pair(insiter->first, insiter->second));
|
||||
} while(insiter++ != lastkeyiter);
|
||||
|
||||
bindings.setUserAny(std::string(sTextKeyExtraDataID)+"@"+currentgroup, Ogre::Any(groupkeys));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -300,16 +85,16 @@ Ogre::SkeletonPtr NIFSkeletonLoader::createSkeleton(const std::string &name, con
|
|||
{
|
||||
/* We need to be a little aggressive here, since some NIFs have a crap-ton
|
||||
* of nodes and Ogre only supports 256 bones. We will skip a skeleton if:
|
||||
* There are no bones used for skinning, there are no controllers on non-
|
||||
* NiTriShape nodes, there are no nodes named "AttachLight", and the tree
|
||||
* consists of NiNode, NiTriShape, and RootCollisionNode types only.
|
||||
* There are no bones used for skinning, there are no controllers, there
|
||||
* are no nodes named "AttachLight", and the tree consists of NiNode,
|
||||
* NiTriShape, and RootCollisionNode types only.
|
||||
*/
|
||||
if(!node->boneTrafo)
|
||||
{
|
||||
if(node->recType == Nif::RC_NiTriShape)
|
||||
return Ogre::SkeletonPtr();
|
||||
if(node->controller.empty() && node->name != "AttachLight")
|
||||
{
|
||||
if(node->recType == Nif::RC_NiTriShape)
|
||||
return Ogre::SkeletonPtr();
|
||||
if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode)
|
||||
{
|
||||
const Nif::NiNode *ninode = static_cast<const Nif::NiNode*>(node);
|
||||
|
|
|
@ -36,10 +36,7 @@ class NIFSkeletonLoader : public Ogre::ManualResourceLoader
|
|||
abort();
|
||||
}
|
||||
|
||||
static void buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector<const Nif::NiKeyframeController*> &ctrls, const std::vector<std::string> &targets, float startTime, float stopTime);
|
||||
|
||||
static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk);
|
||||
void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent=NULL);
|
||||
void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent=NULL);
|
||||
|
||||
// Lookup to retrieve an Ogre bone handle for a given Nif record index
|
||||
std::map<int,int> mNifToOgreHandleMap;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<Property key="OffsetHeight" value="0"/>
|
||||
<Codes>
|
||||
<Code range="33 126"/>
|
||||
<Code range="160"/> <!-- Non-breaking space -->
|
||||
<Code range="192 382"/> <!-- Central and Eastern European languages glyphs -->
|
||||
<Code range="1025 1105"/>
|
||||
<Code range="2026"/> <!-- Ellipsis -->
|
||||
|
|
|
@ -509,6 +509,43 @@ namespace Physic
|
|||
}
|
||||
}
|
||||
|
||||
class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback
|
||||
{
|
||||
public:
|
||||
std::vector<std::string> mResult;
|
||||
|
||||
// added in bullet 2.81
|
||||
// this is just a quick hack, as there does not seem to be a BULLET_VERSION macro?
|
||||
#if defined(BT_COLLISION_OBJECT_WRAPPER_H)
|
||||
virtual btScalar addSingleResult(btManifoldPoint& cp,
|
||||
const btCollisionObjectWrapper* colObj0Wrap,int partId0,int index0,
|
||||
const btCollisionObjectWrapper* colObj1Wrap,int partId1,int index1)
|
||||
{
|
||||
const RigidBody* body = dynamic_cast<const RigidBody*>(colObj0Wrap->m_collisionObject);
|
||||
if (body)
|
||||
mResult.push_back(body->mName);
|
||||
return 0.f;
|
||||
}
|
||||
#else
|
||||
virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* col0, int partId0, int index0,
|
||||
const btCollisionObject* col1, int partId1, int index1)
|
||||
{
|
||||
const RigidBody* body = dynamic_cast<const RigidBody*>(col0);
|
||||
if (body)
|
||||
mResult.push_back(body->mName);
|
||||
return 0.f;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
std::vector<std::string> PhysicEngine::getCollisions(const std::string& name)
|
||||
{
|
||||
RigidBody* body = getRigidBody(name);
|
||||
ContactTestResultCallback callback;
|
||||
dynamicsWorld->contactTest(body, callback);
|
||||
return callback.mResult;
|
||||
}
|
||||
|
||||
void PhysicEngine::stepSimulation(double deltaT)
|
||||
{
|
||||
// This seems to be needed for character controller objects
|
||||
|
|
|
@ -298,6 +298,8 @@ namespace Physic
|
|||
*/
|
||||
std::vector< std::pair<float, std::string> > rayTest2(btVector3& from, btVector3& to);
|
||||
|
||||
std::vector<std::string> getCollisions(const std::string& name);
|
||||
|
||||
//event list of non player object
|
||||
std::list<PhysicEvent> NPEventList;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ Fader::Fader(Ogre::SceneManager* sceneMgr)
|
|||
, mTargetAlpha(0.f)
|
||||
, mCurrentAlpha(0.f)
|
||||
, mStartAlpha(0.f)
|
||||
, mFactor(1.f)
|
||||
{
|
||||
// Create the fading material
|
||||
MaterialPtr material = MaterialManager::getSingleton().create("FadeInOutMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME );
|
||||
|
@ -63,18 +64,19 @@ void Fader::update(float dt)
|
|||
if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha;
|
||||
}
|
||||
|
||||
applyAlpha();
|
||||
|
||||
mRemainingTime -= dt;
|
||||
}
|
||||
|
||||
if (mCurrentAlpha == 0.f) mRectangle->setVisible(false);
|
||||
if (1.f-((1.f-mCurrentAlpha) * mFactor) == 0.f)
|
||||
mRectangle->setVisible(false);
|
||||
else
|
||||
applyAlpha();
|
||||
}
|
||||
|
||||
void Fader::applyAlpha()
|
||||
{
|
||||
mRectangle->setVisible(true);
|
||||
mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, mCurrentAlpha);
|
||||
mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, 1.f-((1.f-mCurrentAlpha) * mFactor));
|
||||
}
|
||||
|
||||
void Fader::fadeIn(float time)
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace Render
|
|||
void fadeOut(const float time);
|
||||
void fadeTo(const int percent, const float time);
|
||||
|
||||
void setFactor (float factor) { mFactor = factor; }
|
||||
|
||||
private:
|
||||
enum FadingMode
|
||||
{
|
||||
|
@ -49,6 +51,8 @@ namespace Render
|
|||
float mCurrentAlpha;
|
||||
float mStartAlpha;
|
||||
|
||||
float mFactor;
|
||||
|
||||
Ogre::SceneManager* mSceneMgr;
|
||||
};
|
||||
}}
|
||||
|
|
Loading…
Reference in a new issue