Merge branch 'master' into saveOnClose

This commit is contained in:
graffy76 2013-03-02 09:24:42 -06:00
commit 9193ddc9d3
161 changed files with 4224 additions and 5385 deletions

View file

@ -74,7 +74,6 @@ set(LIBDIR ${CMAKE_SOURCE_DIR}/libs)
set(OENGINE_OGRE
${LIBDIR}/openengine/ogre/renderer.cpp
${LIBDIR}/openengine/ogre/fader.cpp
${LIBDIR}/openengine/ogre/imagerotate.cpp
${LIBDIR}/openengine/ogre/selectionbuffer.cpp
)
set(OENGINE_GUI
@ -94,8 +93,6 @@ set(OENGINE_BULLET
${LIBDIR}/openengine/bullet/physic.hpp
${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp
${LIBDIR}/openengine/bullet/BulletShapeLoader.h
${LIBDIR}/openengine/bullet/pmove.cpp
${LIBDIR}/openengine/bullet/pmove.h
${LIBDIR}/openengine/bullet/trace.cpp
${LIBDIR}/openengine/bullet/trace.h
@ -299,7 +296,7 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${OpenMW_BINARY_DIR}/openmw.cfg.install")
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
if (NOT WIN32 AND NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
"${OpenMW_BINARY_DIR}/openmw.desktop")
endif()
@ -380,7 +377,7 @@ if(WIN32)
"${OpenMW_SOURCE_DIR}/readme.txt"
"${OpenMW_SOURCE_DIR}/GPL3.txt"
"${OpenMW_SOURCE_DIR}/OFL.txt"
"${OpenMW_SOURCE_DIR}/Bitstream Vera License.txt"
"${OpenMW_SOURCE_DIR}/DejaVu Font License.txt"
"${OpenMW_SOURCE_DIR}/Daedric Font License.txt"
"${OpenMW_BINARY_DIR}/launcher.qss"
"${OpenMW_BINARY_DIR}/settings-default.cfg"
@ -391,7 +388,7 @@ if(WIN32)
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".")
SET(CPACK_GENERATOR "NSIS")
SET(CPACK_PACKAGE_NAME "OpenMW ${OPENMW_VERSION}")
SET(CPACK_PACKAGE_NAME "OpenMW")
SET(CPACK_PACKAGE_VENDOR "OpenMW.org")
SET(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR})
@ -406,7 +403,7 @@ if(WIN32)
SET(CPACK_RESOURCE_FILE_README "${OpenMW_SOURCE_DIR}/readme.txt")
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt")
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
SET(CPACK_NSIS_DISPLAY_NAME "OpenMW")
SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}")
SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_INSTALLED_ICON_NAME "omwlauncher.exe")
@ -529,6 +526,8 @@ if (WIN32)
set(WARNINGS "${WARNINGS} /wd${d}")
endforeach(d)
set_target_properties(shiny PROPERTIES COMPILE_FLAGS ${WARNINGS})
set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS ${WARNINGS})
set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS})
if (BUILD_LAUNCHER)
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS})
@ -657,10 +656,18 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)
# Install binaries
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" )
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" )
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" )
IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" )
ENDIF(BUILD_LAUNCHER)
IF(BUILD_ESMTOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_ESMTOOL)
IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" )
ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" )
ENDIF(BUILD_OPENCS)
# Install icon and .desktop
INSTALL(FILES "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.png" DESTINATION "${ICONDIR}")
@ -674,5 +681,7 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)
# Install resources
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" )
INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${DATADIR}/resources" )
IF(BUILD_LAUNCHER)
INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${DATADIR}/resources" )
ENDIF(BUILD_LAUNCHER)
endif(NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)

View file

@ -20,6 +20,7 @@ void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_i
getData().loadFile (*end2, false);
addOptionalGmsts();
addOptionalGlobals();
}
void CSMDoc::Document::addOptionalGmsts()
@ -139,6 +140,26 @@ void CSMDoc::Document::addOptionalGmsts()
}
}
void CSMDoc::Document::addOptionalGlobals()
{
static const char *sGlobals[] =
{
"dayspassed",
"pcwerewolf",
"pcyear",
0
};
for (int i=0; sGlobals[i]; ++i)
{
ESM::Global global;
global.mId = sGlobals[i];
global.mType = ESM::VT_Int;
global.mValue = 0;
addOptionalGlobal (global);
}
}
void CSMDoc::Document::addOptionalGmst (const ESM::GameSetting& gmst)
{
if (getData().getGmsts().searchId (gmst.mId)==-1)
@ -150,6 +171,17 @@ void CSMDoc::Document::addOptionalGmst (const ESM::GameSetting& gmst)
}
}
void CSMDoc::Document::addOptionalGlobal (const ESM::Global& global)
{
if (getData().getGlobals().searchId (global.mId)==-1)
{
CSMWorld::Record<ESM::Global> record;
record.mBase = global;
record.mState = CSMWorld::RecordBase::State_BaseOnly;
getData().getGlobals().appendRecord (record);
}
}
void CSMDoc::Document::createBase()
{
static const char *sGlobals[] =

View file

@ -20,6 +20,7 @@ class QAbstractItemModel;
namespace ESM
{
struct GameSetting;
struct Global;
}
namespace CSMDoc
@ -53,8 +54,12 @@ namespace CSMDoc
void addOptionalGmsts();
void addOptionalGlobals();
void addOptionalGmst (const ESM::GameSetting& gmst);
void addOptionalGlobal (const ESM::Global& global);
public:
Document (const std::vector<boost::filesystem::path>& files, bool new_);

View file

@ -171,7 +171,7 @@ namespace CSMWorld
record2.mModified = record;
mRecords.push_back (record2);
mIndex.insert (std::make_pair (id, mRecords.size()-1));
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1));
}
else
{
@ -306,7 +306,7 @@ namespace CSMWorld
void IdCollection<ESXRecordT>::appendRecord (const RecordBase& record)
{
mRecords.push_back (dynamic_cast<const Record<ESXRecordT>&> (record));
mIndex.insert (std::make_pair (getId (record), mRecords.size()-1));
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (getId (record)), mRecords.size()-1));
}
template<typename ESXRecordT>

View file

@ -16,7 +16,7 @@ CSVDoc::Operations::Operations()
widgetContainer->setLayout (mLayout);
setWidget (widgetContainer);
setVisible (false);
setFixedHeight (widgetContainer->height());
setTitleBarWidget (new QWidget (this));
}
@ -42,6 +42,8 @@ void CSVDoc::Operations::setProgress (int current, int max, int type, int thread
if ( oldCount > 0)
setFixedHeight (height()/oldCount * newCount);
setVisible (true);
}
void CSVDoc::Operations::quitOperation (int type)
@ -59,6 +61,8 @@ void CSVDoc::Operations::quitOperation (int type)
if (oldCount > 1)
setFixedHeight (height() / oldCount * newCount);
else
setVisible (false);
break;
}

View file

@ -242,7 +242,6 @@ void CSVDoc::View::addGmstsSubView()
void CSVDoc::View::abortOperation (int type)
{
mDocument->abortOperation (type);
mOperations->quitOperation (type);
updateActions();
}

View file

@ -14,8 +14,8 @@ set(GAME_HEADER
source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects
renderinginterface localmap occlusionquery terrain terrainmaterial water shadows
renderingmanager debugging sky player animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows
compositors characterpreview externalrendering globalmap videoplayer
)
@ -26,11 +26,11 @@ add_openmw_dir (mwinput
add_openmw_dir (mwgui
text_input widgets race class birth review windowmanagerimp console dialogue
dialogue_history window_base stats_window messagebox journalwindow charactercreation
map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list
map_window window_pinnable_base tooltips scrollwindow bookwindow list
formatting inventorywindow container hud countdialog tradewindow settingswindow
confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu
itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog
enchantingdialog trainingwindow travelwindow imagebutton exposedwindow
enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor
)
add_openmw_dir (mwdialogue
@ -62,8 +62,9 @@ add_openmw_dir (mwclass
)
add_openmw_dir (mwmechanics
mechanicsmanagerimp stat creaturestats magiceffects movement actors drawstate spells
activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate
mechanicsmanagerimp stat character creaturestats magiceffects movement actors activators
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
aiescort aiactivate
)
add_openmw_dir (mwbase

View file

@ -9,6 +9,7 @@
#include <components/bsa/bsa_archive.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/translation/translation.hpp>
#include <components/nif/nif_file.hpp>
#include <components/nifoverrides/nifoverrides.hpp>
#include <components/nifbullet/bullet_nif_loader.hpp>
@ -17,7 +18,6 @@
#include "mwinput/inputmanagerimp.hpp"
#include "mwgui/windowmanagerimp.hpp"
#include "mwgui/cursorreplace.hpp"
#include "mwscript/scriptmanagerimp.hpp"
#include "mwscript/extensions.hpp"
@ -66,14 +66,15 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
{
try
{
mEnvironment.setFrameDuration (evt.timeSinceLastFrame);
float frametime = std::min(evt.timeSinceLastFrame, 0.2f);
mEnvironment.setFrameDuration(frametime);
// update input
MWBase::Environment::get().getInputManager()->update(evt.timeSinceLastFrame, false);
MWBase::Environment::get().getInputManager()->update(frametime, false);
// sound
if (mUseSound)
MWBase::Environment::get().getSoundManager()->update (evt.timeSinceLastFrame);
MWBase::Environment::get().getSoundManager()->update(frametime);
// global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
@ -87,23 +88,19 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
// passing of time
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->advanceTime (
mEnvironment.getFrameDuration()*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600);
MWBase::Environment::get().getWorld()->advanceTime(
frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600);
if (changed) // keep change flag for another frame, if cell changed happend in local script
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
// update actors
std::vector<std::pair<std::string, Ogre::Vector3> > movement;
MWBase::Environment::get().getMechanicsManager()->update (movement, mEnvironment.getFrameDuration(),
MWBase::Environment::get().getMechanicsManager()->update(frametime,
MWBase::Environment::get().getWindowManager()->isGuiMode());
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->doPhysics (movement, mEnvironment.getFrameDuration());
// update world
MWBase::Environment::get().getWorld()->update (evt.timeSinceLastFrame, MWBase::Environment::get().getWindowManager()->isGuiMode());
MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode());
// update GUI
Ogre::RenderWindow* window = mOgre->getWindow();
@ -111,7 +108,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
MWBase::Environment::get().getWorld()->getTriangleBatchCount(tri, batch);
MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch);
MWBase::Environment::get().getWindowManager()->onFrame(evt.timeSinceLastFrame);
MWBase::Environment::get().getWindowManager()->onFrame(frametime);
}
catch (const std::exception& e)
{
@ -335,9 +332,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
loadBSA();
// cursor replacer (converts the cursor from the bsa so they can be used by mygui)
MWGui::CursorReplace replacer;
// Create the world
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins,
mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap,

View file

@ -37,24 +37,24 @@ namespace MWBase
virtual ~MechanicsManager() {}
virtual void addActor (const MWWorld::Ptr& ptr) = 0;
///< Register an actor for stats management
///
/// \note Dead actors are ignored.
virtual void add (const MWWorld::Ptr& ptr) = 0;
///< Register an object for management
virtual void removeActor (const MWWorld::Ptr& ptr) = 0;
///< Deregister an actor for stats management
virtual void remove (const MWWorld::Ptr& ptr) = 0;
///< Deregister an object for management
virtual void dropActors (const MWWorld::CellStore *cellStore) = 0;
///< Deregister all actors in the given cell.
virtual void updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) = 0;
///< Moves an object to a new cell
virtual void drop (const MWWorld::CellStore *cellStore) = 0;
///< Deregister all objects in the given cell.
virtual void watchActor (const MWWorld::Ptr& ptr) = 0;
///< On each update look for changes in a previously registered actor and update the
/// GUI accordingly.
virtual void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement,
float duration, bool paused) = 0;
///< Update actor stats and store desired velocity vectors in \a movement
virtual void update (float duration, bool paused) = 0;
///< Update objects
///
/// \param paused In game type does not currently advance (this usually means some GUI
/// component is up).
@ -98,6 +98,17 @@ namespace MWBase
virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type,
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0;
///< Perform a persuasion action on NPC
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.
///
/// \param mode 0 normal, 1 immediate start, 2 immediate loop
/// \param count How many times the animation should be run
virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the scene should be ignored.
};
}

View file

@ -198,7 +198,7 @@ namespace MWBase
virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0;
///< Hides dialog and schedules dialog to be deleted.
virtual void messageBox (const std::string& message, const std::vector<std::string>& buttons) = 0;
virtual void messageBox (const std::string& message, const std::vector<std::string>& buttons = std::vector<std::string>()) = 0;
virtual void enterPressed () = 0;
virtual int readPressedButton() = 0;
///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)

View file

@ -6,6 +6,7 @@
#include <components/settings/settings.hpp>
#include "../mwworld/globals.hpp"
#include "../mwworld/ptr.hpp"
namespace Ogre
{
@ -19,6 +20,11 @@ namespace OEngine
{
class Fader;
}
namespace Physic
{
class PhysicEngine;
}
}
namespace ESM
@ -35,6 +41,7 @@ namespace ESM
namespace MWRender
{
class ExternalRendering;
class Animation;
}
namespace MWWorld
@ -42,10 +49,11 @@ namespace MWWorld
class CellStore;
class Player;
class LocalScripts;
class Ptr;
class TimeStamp;
class ESMStore;
class RefData;
typedef std::vector<std::pair<MWWorld::Ptr,Ogre::Vector3> > PtrMovementList;
}
namespace MWBase
@ -227,8 +235,7 @@ namespace MWBase
virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const = 0;
///< Convert position to cell numbers
virtual void doPhysics (const std::vector<std::pair<std::string, Ogre::Vector3> >& actors,
float duration) = 0;
virtual void doPhysics (const MWWorld::PtrMovementList &actors, float duration) = 0;
///< Run physics simulation and modify \a world accordingly.
virtual bool toggleCollisionMode() = 0;
@ -263,18 +270,6 @@ namespace MWBase
///< Create a new recrod (of type npc) in the ESM store.
/// \return pointer to created record
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 rendered scene should be ignored.
///
/// \param mode: 0 normal, 1 immediate start, 2 immediate loop
/// \param number How offen the animation should be run
virtual void skipAnimation (const MWWorld::Ptr& ptr) = 0;
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the rendered scene should be ignored.
virtual void update (float duration, bool paused) = 0;
virtual bool placeObject(const MWWorld::Ptr& object, float cursorX, float cursorY) = 0;
@ -291,8 +286,10 @@ namespace MWBase
virtual void processChangedSettings (const Settings::CategorySettingVector& settings) = 0;
virtual bool isSwimming(const MWWorld::Ptr &object) = 0;
virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0;
virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0;
virtual bool isSwimming(const MWWorld::Ptr &object) const = 0;
virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const = 0;
virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0;
virtual void togglePOV() = 0;
virtual void togglePreviewMode(bool enable) = 0;
@ -311,6 +308,8 @@ namespace MWBase
/// 2 - player is underwater \n
/// 3 - enemies are nearby (not implemented)
/// \todo Probably shouldn't be here
virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0;
/// \todo this does not belong here
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;

View file

@ -5,12 +5,13 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld//cellstore.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/physicssystem.hpp"
#include "../mwrender/objects.hpp"
#include "../mwrender/actors.hpp"
#include "../mwrender/renderinginterface.hpp"
#include "../mwgui/tooltips.hpp"
@ -21,9 +22,8 @@ namespace MWClass
{
const std::string model = getModel(ptr);
if (!model.empty()) {
MWRender::Objects& objects = renderingInterface.getObjects();
objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false);
objects.insertMesh(ptr, model);
MWRender::Actors& actors = renderingInterface.getActors();
actors.insertActivator(ptr);
}
}
@ -32,6 +32,7 @@ namespace MWClass
const std::string model = getModel(ptr);
if(!model.empty())
physics.addObject(ptr);
MWBase::Environment::get().getMechanicsManager()->add(ptr);
}
std::string Activator::getModel(const MWWorld::Ptr &ptr) const

View file

@ -18,6 +18,7 @@
#include "../mwworld/physicssystem.hpp"
#include "../mwrender/renderinginterface.hpp"
#include "../mwrender/actors.hpp"
#include "../mwgui/tooltips.hpp"
@ -96,7 +97,7 @@ namespace MWClass
const std::string model = getModel(ptr);
if(!model.empty())
physics.addActor(ptr);
MWBase::Environment::get().getMechanicsManager()->addActor (ptr);
MWBase::Environment::get().getMechanicsManager()->add(ptr);
}
std::string Creature::getModel(const MWWorld::Ptr &ptr) const

View file

@ -1,9 +1,7 @@
#ifndef GAME_MWCLASS_CREATURE_H
#define GAME_MWCLASS_CREATURE_H
#include "../mwrender/renderinginterface.hpp"
#include "../mwrender/actors.hpp"
#include "../mwworld/class.hpp"
namespace MWClass
{

View file

@ -36,14 +36,9 @@ namespace MWClass
objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false);
if (!model.empty())
objects.insertMesh(ptr, "meshes\\" + model);
const int color = ref->mBase->mData.mColor;
const float r = ((color >> 0) & 0xFF) / 255.0f;
const float g = ((color >> 8) & 0xFF) / 255.0f;
const float b = ((color >> 16) & 0xFF) / 255.0f;
const float radius = float (ref->mBase->mData.mRadius);
objects.insertLight (ptr, r, g, b, radius);
objects.insertMesh(ptr, "meshes\\" + model, true);
else
objects.insertLight(ptr);
}
void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const

View file

@ -55,9 +55,35 @@ namespace MWClass
{
void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const
{
static bool inited = false;
if(!inited)
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
fMinWalkSpeed = gmst.find("fMinWalkSpeed");
fMaxWalkSpeed = gmst.find("fMaxWalkSpeed");
fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect");
fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier");
fAthleticsRunBonus = gmst.find("fAthleticsRunBonus");
fBaseRunMultiplier = gmst.find("fBaseRunMultiplier");
fMinFlySpeed = gmst.find("fMinFlySpeed");
fMaxFlySpeed = gmst.find("fMaxFlySpeed");
fSwimRunBase = gmst.find("fSwimRunBase");
fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult");
fJumpEncumbranceBase = gmst.find("fJumpEncumbranceBase");
fJumpEncumbranceMultiplier = gmst.find("fJumpEncumbranceMultiplier");
fJumpAcrobaticsBase = gmst.find("fJumpAcrobaticsBase");
fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier");
fJumpRunMultiplier = gmst.find("fJumpRunMultiplier");
// Added in Tribunal/Bloodmoon, may not exist
fWereWolfRunMult = gmst.search("fWereWolfRunMult");
inited = true;
}
if (!ptr.getRefData().getCustomData())
{
std::auto_ptr<CustomData> data (new CustomData);
std::auto_ptr<CustomData> data(new CustomData);
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
@ -142,7 +168,7 @@ namespace MWClass
void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const
{
physics.addActor(ptr);
MWBase::Environment::get().getMechanicsManager()->addActor(ptr);
MWBase::Environment::get().getMechanicsManager()->add(ptr);
}
std::string Npc::getModel(const MWWorld::Ptr &ptr) const
@ -297,9 +323,88 @@ namespace MWClass
return false;
}
float Npc::getSpeed (const MWWorld::Ptr& ptr) const
float Npc::getSpeed(const MWWorld::Ptr& ptr) const
{
return getStance (ptr, Run) ? 600 : 300; // TODO calculate these values from stats
const MWBase::World *world = MWBase::Environment::get().getWorld();
const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData());
const MWMechanics::MagicEffects &mageffects = npcdata->mCreatureStats.getMagicEffects();
const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr);
float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mCreatureStats.getAttribute(ESM::Attribute::Speed).getModified()*
(fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat());
walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance;
walkSpeed = std::max(0.0f, walkSpeed);
if(Npc::getStance(ptr, Sneak, false))
walkSpeed *= fSneakSpeedMultiplier->getFloat();
float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() *
fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat());
if(npcdata->mNpcStats.isWerewolf())
runSpeed *= fWereWolfRunMult->getFloat();
float moveSpeed;
if(normalizedEncumbrance >= 1.0f)
moveSpeed = 0.0f;
else if(mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0)
{
float flySpeed = 0.01f*(npcdata->mCreatureStats.getAttribute(ESM::Attribute::Speed).getModified() +
mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude);
flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat());
flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
flySpeed = std::max(0.0f, flySpeed);
moveSpeed = flySpeed;
}
else if(world->isSwimming(ptr))
{
float swimSpeed = walkSpeed;
if(Npc::getStance(ptr, Run, false))
swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(MWMechanics::EffectKey(1/*swift swim*/)).mMagnitude;
swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
fSwimRunAthleticsMult->getFloat();
moveSpeed = swimSpeed;
}
else if(Npc::getStance(ptr, Run, false))
moveSpeed = runSpeed;
else
moveSpeed = walkSpeed;
if(getMovementSettings(ptr).mLeftRight != 0 && getMovementSettings(ptr).mForwardBackward == 0)
moveSpeed *= 0.75f;
return moveSpeed;
}
float Npc::getJump(const MWWorld::Ptr &ptr) const
{
const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData());
const MWMechanics::MagicEffects &mageffects = npcdata->mCreatureStats.getMagicEffects();
const float encumbranceTerm = fJumpEncumbranceBase->getFloat() +
fJumpEncumbranceMultiplier->getFloat() *
(1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr));
float a = npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified();
float b = 0.0f;
if(a > 50.0f)
{
b = a - 50.0f;
a = 50.0f;
}
float x = fJumpAcrobaticsBase->getFloat() +
std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat());
x += 3 * b * fJumpAcroMultiplier->getFloat();
x += mageffects.get(MWMechanics::EffectKey(9/*jump*/)).mMagnitude * 64;
x *= encumbranceTerm;
if(Npc::getStance(ptr, Run, false))
x *= fJumpRunMultiplier->getFloat();
x *= 1.25f;//fatigueTerm;
x -= -627.2/*gravity constant*/;
x /= 3;
return x;
}
MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const
@ -311,14 +416,10 @@ namespace MWClass
Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const
{
Ogre::Vector3 vector (0, 0, 0);
vector.x = getMovementSettings (ptr).mLeftRight * 127;
vector.y = getMovementSettings (ptr).mForwardBackward * 127;
vector.z = getMovementSettings(ptr).mUpDown * 127;
//if (getStance (ptr, Run, false))
// vector *= 2;
Ogre::Vector3 vector;
vector.x = getMovementSettings(ptr).mLeftRight;
vector.y = getMovementSettings(ptr).mForwardBackward;
vector.z = getMovementSettings(ptr).mUpDown;
return vector;
}
@ -420,4 +521,21 @@ namespace MWClass
return MWWorld::Ptr(&cell.mNpcs.insert(*ref), &cell);
}
const ESM::GameSetting *Npc::fMinWalkSpeed;
const ESM::GameSetting *Npc::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect;
const ESM::GameSetting *Npc::fSneakSpeedMultiplier;
const ESM::GameSetting *Npc::fAthleticsRunBonus;
const ESM::GameSetting *Npc::fBaseRunMultiplier;
const ESM::GameSetting *Npc::fMinFlySpeed;
const ESM::GameSetting *Npc::fMaxFlySpeed;
const ESM::GameSetting *Npc::fSwimRunBase;
const ESM::GameSetting *Npc::fSwimRunAthleticsMult;
const ESM::GameSetting *Npc::fJumpEncumbranceBase;
const ESM::GameSetting *Npc::fJumpEncumbranceMultiplier;
const ESM::GameSetting *Npc::fJumpAcrobaticsBase;
const ESM::GameSetting *Npc::fJumpAcroMultiplier;
const ESM::GameSetting *Npc::fJumpRunMultiplier;
const ESM::GameSetting *Npc::fWereWolfRunMult;
}

View file

@ -3,6 +3,11 @@
#include "../mwworld/class.hpp"
namespace ESM
{
class GameSetting;
}
namespace MWClass
{
class Npc : public MWWorld::Class
@ -12,6 +17,23 @@ namespace MWClass
virtual MWWorld::Ptr
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
static const ESM::GameSetting *fMinWalkSpeed;
static const ESM::GameSetting *fMaxWalkSpeed;
static const ESM::GameSetting *fEncumberedMoveEffect;
static const ESM::GameSetting *fSneakSpeedMultiplier;
static const ESM::GameSetting *fAthleticsRunBonus;
static const ESM::GameSetting *fBaseRunMultiplier;
static const ESM::GameSetting *fMinFlySpeed;
static const ESM::GameSetting *fMaxFlySpeed;
static const ESM::GameSetting *fSwimRunBase;
static const ESM::GameSetting *fSwimRunAthleticsMult;
static const ESM::GameSetting *fJumpEncumbranceBase;
static const ESM::GameSetting *fJumpEncumbranceMultiplier;
static const ESM::GameSetting *fJumpAcrobaticsBase;
static const ESM::GameSetting *fJumpAcroMultiplier;
static const ESM::GameSetting *fJumpRunMultiplier;
static const ESM::GameSetting *fWereWolfRunMult;
public:
virtual std::string getId (const MWWorld::Ptr& ptr) const;
@ -64,6 +86,9 @@ namespace MWClass
virtual float getSpeed (const MWWorld::Ptr& ptr) const;
///< Return movement speed.
virtual float getJump(const MWWorld::Ptr &ptr) const;
///< Return jump velocity (not accounting for movement)
virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const;
///< Return desired movement.

View file

@ -2,6 +2,7 @@
#include "dialoguemanagerimp.hpp"
#include <cctype>
#include <cstdlib>
#include <algorithm>
#include <iterator>
@ -251,8 +252,12 @@ namespace MWDialogue
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
if (const ESM::DialInfo *info = filter.search (dialogue, true))
std::vector<const ESM::DialInfo *> infos = filter.list (dialogue, true, true);
if (!infos.empty())
{
const ESM::DialInfo* info = infos[std::rand() % infos.size()];
parseText (info->mResponse);
if (dialogue.mType==ESM::Dialogue::Persuasion)

View file

@ -289,7 +289,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
case SelectWrapper::Function_PcGender:
return player.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Female ? 0 : 1;
return player.get<ESM::NPC>()->mBase->isMale() ? 0 : 1;
case SelectWrapper::Function_PcClothingModifier:
{
@ -559,8 +559,21 @@ MWDialogue::Filter::Filter (const MWWorld::Ptr& actor, int choice, bool talkedTo
: mActor (actor), mChoice (choice), mTalkedToPlayer (talkedToPlayer)
{}
const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const
const ESM::DialInfo* MWDialogue::Filter::search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const
{
std::vector<const ESM::DialInfo *> suitableInfos = list (dialogue, fallbackToInfoRefusal, false);
if (suitableInfos.empty())
return NULL;
else
return suitableInfos[0];
}
std::vector<const ESM::DialInfo *> MWDialogue::Filter::list (const ESM::Dialogue& dialogue,
bool fallbackToInfoRefusal, bool searchAll) const
{
std::vector<const ESM::DialInfo *> infos;
bool infoRefusal = false;
// Iterate over topic responses to find a matching one
@ -569,14 +582,17 @@ const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue,
{
if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter))
{
if (testDisposition (*iter))
return &*iter;
if (testDisposition (*iter)) {
infos.push_back(&*iter);
if (!searchAll)
break;
}
else
infoRefusal = true;
}
}
if (infoRefusal && fallbackToInfoRefusal)
if (infos.empty() && infoRefusal && fallbackToInfoRefusal)
{
// No response is valid because of low NPC disposition,
// search a response in the topic "Info Refusal"
@ -588,11 +604,14 @@ const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue,
for (std::vector<ESM::DialInfo>::const_iterator iter = infoRefusalDialogue.mInfo.begin();
iter!=infoRefusalDialogue.mInfo.end(); ++iter)
if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter))
return &*iter;
if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter)) {
infos.push_back(&*iter);
if (!searchAll)
break;
}
}
return 0;
return infos;
}
bool MWDialogue::Filter::responseAvailable (const ESM::Dialogue& dialogue) const

View file

@ -1,6 +1,8 @@
#ifndef GAME_MWDIALOGUE_FILTER_H
#define GAME_MWDIALOGUE_FILTER_H
#include <vector>
#include "../mwworld/ptr.hpp"
namespace ESM
@ -51,7 +53,10 @@ namespace MWDialogue
Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer);
const ESM::DialInfo *search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const;
std::vector<const ESM::DialInfo *> list (const ESM::Dialogue& dialogue,
bool fallbackToInfoRefusal, bool searchAll) const;
const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const;
///< Get a matching response for the requested dialogue.
/// Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition.

View file

@ -25,7 +25,7 @@ bool sortBirthSigns(const std::pair<std::string, const ESM::BirthSign*>& left, c
}
BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_chargen_birth.layout", parWindowManager)
: WindowModal("openmw_chargen_birth.layout", parWindowManager)
{
// Centre dialog
center();
@ -66,7 +66,7 @@ void BirthDialog::setNextButtonShow(bool shown)
void BirthDialog::open()
{
WindowBase::open();
WindowModal::open();
updateBirths();
updateSpells();
}

View file

@ -10,7 +10,7 @@
namespace MWGui
{
class BirthDialog : public WindowBase
class BirthDialog : public WindowModal
{
public:
BirthDialog(MWBase::WindowManager& parWindowManager);

View file

@ -21,7 +21,7 @@ using namespace MWGui;
/* GenerateClassResultDialog */
GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_chargen_generate_class_result.layout", parWindowManager)
: WindowModal("openmw_chargen_generate_class_result.layout", parWindowManager)
{
// Centre dialog
center();
@ -68,7 +68,7 @@ void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender)
/* PickClassDialog */
PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_chargen_class.layout", parWindowManager)
: WindowModal("openmw_chargen_class.layout", parWindowManager)
{
// Centre dialog
center();
@ -122,6 +122,7 @@ void PickClassDialog::setNextButtonShow(bool shown)
void PickClassDialog::open()
{
WindowModal::open ();
updateClasses();
updateStats();
}
@ -276,7 +277,7 @@ void InfoBoxDialog::layoutVertically(MyGUI::WidgetPtr widget, int margin)
}
InfoBoxDialog::InfoBoxDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_infobox.layout", parWindowManager)
: WindowModal("openmw_infobox.layout", parWindowManager)
, mCurrentButton(-1)
{
getWidget(mTextBox, "TextBox");
@ -327,6 +328,7 @@ void InfoBoxDialog::setButtons(ButtonList &buttons)
void InfoBoxDialog::open()
{
WindowModal::open();
// Fix layout
layoutVertically(mTextBox, 4);
layoutVertically(mButtonBar, 6);
@ -373,7 +375,7 @@ ClassChoiceDialog::ClassChoiceDialog(MWBase::WindowManager& parWindowManager)
/* CreateClassDialog */
CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_chargen_create_class.layout", parWindowManager)
: WindowModal("openmw_chargen_create_class.layout", parWindowManager)
, mSpecDialog(nullptr)
, mAttribDialog(nullptr)
, mSkillDialog(nullptr)

View file

@ -12,7 +12,7 @@
namespace MWGui
{
class InfoBoxDialog : public WindowBase
class InfoBoxDialog : public WindowModal
{
public:
InfoBoxDialog(MWBase::WindowManager& parWindowManager);
@ -63,7 +63,7 @@ namespace MWGui
ClassChoiceDialog(MWBase::WindowManager& parWindowManager);
};
class GenerateClassResultDialog : public WindowBase
class GenerateClassResultDialog : public WindowModal
{
public:
GenerateClassResultDialog(MWBase::WindowManager& parWindowManager);
@ -90,7 +90,7 @@ namespace MWGui
std::string mCurrentClassId;
};
class PickClassDialog : public WindowBase
class PickClassDialog : public WindowModal
{
public:
PickClassDialog(MWBase::WindowManager& parWindowManager);
@ -238,7 +238,7 @@ namespace MWGui
MyGUI::EditPtr mTextEdit;
};
class CreateClassDialog : public WindowBase
class CreateClassDialog : public WindowModal
{
public:
CreateClassDialog(MWBase::WindowManager& parWindowManager);

View file

@ -0,0 +1,130 @@
#include "cursor.hpp"
#include <MyGUI_PointerManager.h>
#include <MyGUI_InputManager.h>
#include <MyGUI_RenderManager.h>
#include <MyGUI_RotatingSkin.h>
#include <MyGUI_Gui.h>
#include <OgreMath.h>
namespace MWGui
{
ResourceImageSetPointerFix::ResourceImageSetPointerFix() :
mImageSet(nullptr)
{
}
ResourceImageSetPointerFix::~ResourceImageSetPointerFix()
{
}
void ResourceImageSetPointerFix::deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version)
{
Base::deserialization(_node, _version);
MyGUI::xml::ElementEnumerator info = _node->getElementEnumerator();
while (info.next("Property"))
{
const std::string& key = info->findAttribute("key");
const std::string& value = info->findAttribute("value");
if (key == "Point")
mPoint = MyGUI::IntPoint::parse(value);
else if (key == "Size")
mSize = MyGUI::IntSize::parse(value);
else if (key == "Rotation")
mRotation = MyGUI::utility::parseInt(value);
else if (key == "Resource")
mImageSet = MyGUI::ResourceManager::getInstance().getByName(value)->castType<MyGUI::ResourceImageSet>();
}
}
int ResourceImageSetPointerFix::getRotation()
{
return mRotation;
}
void ResourceImageSetPointerFix::setImage(MyGUI::ImageBox* _image)
{
if (mImageSet != nullptr)
_image->setItemResourceInfo(mImageSet->getIndexInfo(0, 0));
}
void ResourceImageSetPointerFix::setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point)
{
_image->setCoord(_point.left - mPoint.left, _point.top - mPoint.top, mSize.width, mSize.height);
}
MyGUI::ResourceImageSetPtr ResourceImageSetPointerFix:: getImageSet()
{
return mImageSet;
}
MyGUI::IntPoint ResourceImageSetPointerFix::getHotSpot()
{
return mPoint;
}
MyGUI::IntSize ResourceImageSetPointerFix::getSize()
{
return mSize;
}
// ----------------------------------------------------------------------------------------
Cursor::Cursor()
{
// hide mygui's pointer since we're rendering it ourselves (because mygui's pointer doesn't support rotation)
MyGUI::PointerManager::getInstance().setVisible(false);
MyGUI::PointerManager::getInstance().eventChangeMousePointer += MyGUI::newDelegate(this, &Cursor::onCursorChange);
mWidget = MyGUI::Gui::getInstance().createWidget<MyGUI::ImageBox>("RotatingSkin",0,0,0,0,MyGUI::Align::Default,"Pointer","");
onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer());
}
Cursor::~Cursor()
{
}
void Cursor::onCursorChange(const std::string &name)
{
ResourceImageSetPointerFix* imgSetPtr = dynamic_cast<ResourceImageSetPointerFix*>(
MyGUI::PointerManager::getInstance().getByName(name));
assert(imgSetPtr != NULL);
MyGUI::ResourceImageSet* imgSet = imgSetPtr->getImageSet();
std::string texture = imgSet->getIndexInfo(0,0).texture;
mSize = imgSetPtr->getSize();
mHotSpot = imgSetPtr->getHotSpot();
int rotation = imgSetPtr->getRotation();
mWidget->setImageTexture(texture);
MyGUI::ISubWidget* main = mWidget->getSubWidgetMain();
MyGUI::RotatingSkin* rotatingSubskin = main->castType<MyGUI::RotatingSkin>();
rotatingSubskin->setCenter(MyGUI::IntPoint(mSize.width/2,mSize.height/2));
rotatingSubskin->setAngle(Ogre::Degree(rotation).valueRadians());
}
void Cursor::update()
{
MyGUI::IntPoint position = MyGUI::InputManager::getInstance().getMousePosition();
mWidget->setPosition(position - mHotSpot);
mWidget->setSize(mSize);
}
void Cursor::setVisible(bool visible)
{
mWidget->setVisible(visible);
}
}

View file

@ -0,0 +1,62 @@
#ifndef MWGUI_CURSOR_H
#define MWGUI_CURSOR_H
#include <MyGUI_IPointer.h>
#include <MyGUI_ResourceImageSet.h>
#include <MyGUI_RTTI.h>
namespace MWGui
{
/// \brief Allows us to get the members of
/// ResourceImageSetPointer that we need.
/// \example MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
/// MyGUI::ResourceManager::getInstance().load("core.xml");
class ResourceImageSetPointerFix :
public MyGUI::IPointer
{
MYGUI_RTTI_DERIVED( ResourceImageSetPointerFix )
public:
ResourceImageSetPointerFix();
virtual ~ResourceImageSetPointerFix();
virtual void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version);
virtual void setImage(MyGUI::ImageBox* _image);
virtual void setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point);
//and now for the whole point of this class, allow us to get
//the hot spot, the image and the size of the cursor.
virtual MyGUI::ResourceImageSetPtr getImageSet();
virtual MyGUI::IntPoint getHotSpot();
virtual MyGUI::IntSize getSize();
virtual int getRotation();
private:
MyGUI::IntPoint mPoint;
MyGUI::IntSize mSize;
MyGUI::ResourceImageSetPtr mImageSet;
int mRotation; // rotation in degrees
};
class Cursor
{
public:
Cursor();
~Cursor();
void update ();
void setVisible (bool visible);
void onCursorChange (const std::string& name);
private:
MyGUI::ImageBox* mWidget;
MyGUI::IntSize mSize;
MyGUI::IntPoint mHotSpot;
};
}
#endif

View file

@ -1,16 +0,0 @@
#include "cursorreplace.hpp"
#include <boost/filesystem.hpp>
#include <openengine/ogre/imagerotate.hpp>
#include <OgreResourceGroupManager.h>
#include <OgreRoot.h>
using namespace MWGui;
CursorReplace::CursorReplace()
{
OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_vresize.png", 90);
OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize1.png", -45);
OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize2.png", 45);
}

View file

@ -1,16 +0,0 @@
#ifndef GAME_CURSORREPLACE_H
#define GAME_CURSORREPLACE_H
#include <string>
namespace MWGui
{
/// \brief MyGUI does not support rotating cursors, so we have to do it manually
class CursorReplace
{
public:
CursorReplace();
};
}
#endif

View file

@ -106,7 +106,7 @@ namespace MWGui
float progress = (float(mCurrentCellLoading)+refProgress) / float(mTotalCellsLoading);
assert(progress <= 1 && progress >= 0);
mLoadingText->setCaption(stage + "... ");
mLoadingText->setCaption(stage);
mProgressBar->setProgressPosition (static_cast<size_t>(progress * 1000));
static float loadingScreenFps = 30.f;

View file

@ -5,6 +5,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
namespace MWGui
{
@ -65,6 +66,7 @@ namespace MWGui
void MainMenu::onButtonClicked(MyGUI::Widget *sender)
{
MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f);
if (sender == mButtons["return"])
MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu);
else if (sender == mButtons["options"])

View file

@ -6,6 +6,8 @@
#include <OgreTextureManager.h>
#include <OgreSceneNode.h>
#include <MyGUI_Gui.h>
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
@ -13,6 +15,8 @@
#include "../mwrender/globalmap.hpp"
#include "widgets.hpp"
using namespace MWGui;
LocalMapBase::LocalMapBase()
@ -88,7 +92,7 @@ void LocalMapBase::applyFogOfWar()
+ boost::lexical_cast<std::string>(my);
std::string image = mPrefix+"_"+ boost::lexical_cast<std::string>(mCurX + (mx-1)) + "_"
+ boost::lexical_cast<std::string>(mCurY + (mInterior ? (my-1) : -1*(my-1)));
+ boost::lexical_cast<std::string>(mCurY + (-1*(my-1)));
MyGUI::ImageBox* fog = mFogWidgets[my + 3*mx];
fog->setImageTexture(mFogOfWar ?
((MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) ? image+"_fog"
@ -96,6 +100,7 @@ void LocalMapBase::applyFogOfWar()
: "");
}
}
notifyMapChanged ();
}
void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2)
@ -127,7 +132,7 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior)
{
// map
std::string image = mPrefix+"_"+ boost::lexical_cast<std::string>(x + (mx-1)) + "_"
+ boost::lexical_cast<std::string>(y + (interior ? (my-1) : -1*(my-1)));
+ boost::lexical_cast<std::string>(y + (-1*(my-1)));
std::string name = "Map_" + boost::lexical_cast<std::string>(mx) + "_"
+ boost::lexical_cast<std::string>(my);
@ -173,7 +178,7 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior)
}
else
{
Ogre::Vector2 position (marker.x, -marker.y);
Ogre::Vector2 position (marker.x, marker.y);
MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy);
widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + (1+cellDx-x) * 512, nY * 512 - 4 + (1+cellDy-y) * 512, 8, 8);
@ -394,10 +399,10 @@ void MapWindow::globalMapUpdatePlayer ()
{
Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition ();
Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation ();
Ogre::Vector2 dir (orient.yAxis ().x, -orient.yAxis().z);
Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y);
float worldX, worldY;
mGlobalMapRender->worldPosToImageSpace (pos.x, pos.z, worldX, worldY);
mGlobalMapRender->worldPosToImageSpace (pos.x, pos.y, worldX, worldY);
worldX *= mGlobalMapRender->getWidth();
worldY *= mGlobalMapRender->getHeight();
@ -425,3 +430,17 @@ void MapWindow::notifyPlayerUpdate ()
{
globalMapUpdatePlayer ();
}
void MapWindow::notifyMapChanged ()
{
// workaround to prevent the map from drawing on top of the button
MyGUI::IntCoord oldCoord = mButton->getCoord ();
MyGUI::Gui::getInstance().destroyWidget (mButton);
mButton = mMainWidget->createWidget<MWGui::Widgets::AutoSizedButton>("MW_Button",
oldCoord, MyGUI::Align::Bottom | MyGUI::Align::Right);
mButton->setProperty ("ExpandDirection", "Left");
mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked);
mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" :
"#{sWorld}");
}

View file

@ -50,6 +50,7 @@ namespace MWGui
void onMarkerUnfocused(MyGUI::Widget* w1, MyGUI::Widget* w2);
virtual void notifyPlayerUpdate() {}
virtual void notifyMapChanged() {}
OEngine::GUI::Layout* mLayout;
@ -99,6 +100,8 @@ namespace MWGui
virtual void onPinToggled();
virtual void notifyPlayerUpdate();
virtual void notifyMapChanged();
};
}
#endif

View file

@ -1,6 +1,8 @@
#include <components/misc/stringops.hpp>
#include "messagebox.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp"
using namespace MWGui;
@ -375,6 +377,7 @@ void InteractiveMessageBox::enterPressed()
if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok)
{
buttonActivated(*button);
MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f);
break;
}
}

View file

@ -20,7 +20,7 @@ using namespace MWGui;
using namespace Widgets;
RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_chargen_race.layout", parWindowManager)
: WindowModal("openmw_chargen_race.layout", parWindowManager)
, mGenderIndex(0)
, mFaceIndex(0)
, mHairIndex(0)
@ -61,7 +61,7 @@ RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager)
prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair);
nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair);
setText("RaceT", mWindowManager.getGameSettingString("sRaceMenu4", "Race"));
setText("RaceT", mWindowManager.getGameSettingString("sRaceMenu5", "Race"));
getWidget(mRaceList, "RaceList");
mRaceList->setScrollVisible(true);
mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
@ -100,6 +100,8 @@ void RaceDialog::setNextButtonShow(bool shown)
void RaceDialog::open()
{
WindowModal::open();
updateRaces();
updateSkills();
updateSpellPowers();

View file

@ -23,7 +23,7 @@ namespace MWGui
namespace MWGui
{
class RaceDialog : public WindowBase
class RaceDialog : public WindowModal
{
public:
RaceDialog(MWBase::WindowManager& parWindowManager);

View file

@ -23,7 +23,7 @@ using namespace Widgets;
const int ReviewDialog::sLineHeight = 18;
ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_chargen_review.layout", parWindowManager)
: WindowModal("openmw_chargen_review.layout", parWindowManager)
, mLastPos(0)
{
// Centre dialog
@ -97,6 +97,7 @@ ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager)
void ReviewDialog::open()
{
WindowModal::open();
updateSkillArea();
}

View file

@ -17,7 +17,7 @@ Layout is defined by resources/mygui/openmw_chargen_review.layout.
namespace MWGui
{
class ReviewDialog : public WindowBase
class ReviewDialog : public WindowModal
{
public:
enum Dialogs {

View file

@ -5,7 +5,7 @@
using namespace MWGui;
TextInputDialog::TextInputDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_text_input.layout", parWindowManager)
: WindowModal("openmw_text_input.layout", parWindowManager)
{
// Centre dialog
center();
@ -39,6 +39,7 @@ void TextInputDialog::setTextLabel(const std::string &label)
void TextInputDialog::open()
{
WindowModal::open();
// Make sure the edit box has focus
MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit);
}

View file

@ -13,7 +13,7 @@ namespace MWGui
namespace MWGui
{
class TextInputDialog : public WindowBase
class TextInputDialog : public WindowModal
{
public:
TextInputDialog(MWBase::WindowManager& parWindowManager);

View file

@ -19,11 +19,11 @@
#include "inventorywindow.hpp"
static const float BALANCE_CHANGE_INITIAL_PAUSE = 0.5; // in seconds
static const float BALANCE_CHANGE_INTERVAL = 0.1; // in seconds
namespace MWGui
{
const float TradeWindow::sBalanceChangeInitialPause = 0.5;
const float TradeWindow::sBalanceChangeInterval = 0.1;
TradeWindow::TradeWindow(MWBase::WindowManager& parWindowManager) :
WindowBase("openmw_trade_window.layout", parWindowManager)
, ContainerBase(NULL) // no drag&drop
@ -157,7 +157,7 @@ namespace MWGui
mBalanceChangePause -= frameDuration;
if (mBalanceChangePause < 0.0) {
mBalanceChangePause += BALANCE_CHANGE_INTERVAL;
mBalanceChangePause += sBalanceChangeInterval;
if (mBalanceButtonsState == BBS_Increase)
onIncreaseButtonTriggered();
else if (mBalanceButtonsState == BBS_Decrease)
@ -296,14 +296,14 @@ namespace MWGui
void TradeWindow::onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
{
mBalanceButtonsState = BBS_Increase;
mBalanceChangePause = BALANCE_CHANGE_INITIAL_PAUSE;
mBalanceChangePause = sBalanceChangeInitialPause;
onIncreaseButtonTriggered();
}
void TradeWindow::onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
{
mBalanceButtonsState = BBS_Decrease;
mBalanceChangePause = BALANCE_CHANGE_INITIAL_PAUSE;
mBalanceChangePause = sBalanceChangeInitialPause;
onDecreaseButtonTriggered();
}

View file

@ -37,6 +37,9 @@ namespace MWGui
void onFrame(float frameDuration);
protected:
static const float sBalanceChangeInitialPause; // in seconds
static const float sBalanceChangeInterval; // in seconds
MyGUI::Button* mFilterAll;
MyGUI::Button* mFilterWeapon;
MyGUI::Button* mFilterApparel;

View file

@ -95,31 +95,46 @@ namespace MWGui
// http://www.uesp.net/wiki/Lore:Calendar
std::string month;
int m = MWBase::Environment::get().getWorld ()->getMonth ();
if (m == 0)
month = "#{sMonthMorningstar}";
else if (m == 1)
month = "#{sMonthSunsdawn}";
else if (m == 2)
month = "#{sMonthFirstseed}";
else if (m == 3)
month = "#{sMonthRainshand}";
else if (m == 4)
month = "#{sMonthSecondseed}";
else if (m == 5)
month = "#{sMonthMidyear}";
else if (m == 6)
month = "#{sMonthSunsheight}";
else if (m == 7)
month = "#{sMonthLastseed}";
else if (m == 8)
month = "#{sMonthHeartfire}";
else if (m == 9)
month = "#{sMonthFrostfall}";
else if (m == 10)
month = "#{sMonthSunsdusk}";
else if (m == 11)
month = "#{sMonthEveningstar}";
switch (m) {
case 0:
month = "#{sMonthMorningstar}";
break;
case 1:
month = "#{sMonthSunsdawn}";
break;
case 2:
month = "#{sMonthFirstseed}";
break;
case 3:
month = "#{sMonthRainshand}";
break;
case 4:
month = "#{sMonthSecondseed}";
break;
case 5:
month = "#{sMonthMidyear}";
break;
case 6:
month = "#{sMonthSunsheight}";
break;
case 7:
month = "#{sMonthLastseed}";
break;
case 8:
month = "#{sMonthHeartfire}";
break;
case 9:
month = "#{sMonthFrostfall}";
break;
case 10:
month = "#{sMonthSunsdusk}";
break;
case 11:
month = "#{sMonthEveningstar}";
break;
default:
break;
}
int hour = MWBase::Environment::get().getWorld ()->getTimeStamp ().getHour ();
bool pm = hour >= 12;
if (hour >= 13) hour -= 12;

View file

@ -53,6 +53,7 @@
#include "trainingwindow.hpp"
#include "imagebutton.hpp"
#include "exposedwindow.hpp"
#include "cursor.hpp"
using namespace MWGui;
@ -130,6 +131,9 @@ WindowManager::WindowManager(
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::ImageButton>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::ExposedWindow>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
MyGUI::ResourceManager::getInstance().load("core.xml");
MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag);
// Get size info from the Gui object
@ -178,6 +182,8 @@ WindowManager::WindowManager(
mInputBlocker = mGui->createWidget<MyGUI::Widget>("",0,0,w,h,MyGUI::Align::Default,"Windows","");
mCursor = new Cursor();
// The HUD is always on
mHud->setVisible(true);
@ -236,6 +242,7 @@ WindowManager::~WindowManager()
delete mTrainingWindow;
delete mCountDialog;
delete mQuickKeysMenu;
delete mCursor;
cleanupGarbage();
@ -262,6 +269,8 @@ void WindowManager::update()
mHud->setFPS(mFPS);
mHud->setTriangleCount(mTriangleCount);
mHud->setBatchCount(mBatchCount);
mCursor->update();
}
void WindowManager::updateVisible()
@ -293,7 +302,7 @@ void WindowManager::updateVisible()
mHud->setVisible(true);
// Mouse is visible whenever we're not in game mode
MyGUI::PointerManager::getInstance().setVisible(isGuiMode());
mCursor->setVisible(isGuiMode());
bool gameMode = !isGuiMode();
@ -421,13 +430,19 @@ void WindowManager::updateVisible()
break;
case GM_LoadingWallpaper:
mHud->setVisible(false);
MyGUI::PointerManager::getInstance().setVisible(false);
mCursor->setVisible(false);
break;
case GM_Loading:
MyGUI::PointerManager::getInstance().setVisible(false);
// Show the pinned windows
mMap->setVisible(mMap->pinned());
mStatsWindow->setVisible(mStatsWindow->pinned());
mInventoryWindow->setVisible(mInventoryWindow->pinned());
mSpellWindow->setVisible(mSpellWindow->pinned());
mCursor->setVisible(false);
break;
case GM_Video:
MyGUI::PointerManager::getInstance().setVisible(false);
mCursor->setVisible(false);
mHud->setVisible(false);
break;
default:
@ -749,7 +764,7 @@ void WindowManager::setSpellVisibility(bool visible)
void WindowManager::setMouseVisible(bool visible)
{
MyGUI::PointerManager::getInstance().setVisible(visible);
mCursor->setVisible(visible);
}
void WindowManager::setDragDrop(bool dragDrop)

View file

@ -72,6 +72,7 @@ namespace MWGui
class SpellCreationDialog;
class EnchantingDialog;
class TrainingWindow;
class Cursor;
class WindowManager : public MWBase::WindowManager
{
@ -188,7 +189,7 @@ namespace MWGui
virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted.
virtual void messageBox (const std::string& message, const std::vector<std::string>& buttons);
virtual void messageBox (const std::string& message, const std::vector<std::string>& buttons = std::vector<std::string>());
virtual void enterPressed ();
virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)
@ -260,6 +261,7 @@ namespace MWGui
EnchantingDialog* mEnchantingDialog;
TrainingWindow* mTrainingWindow;
Translation::Storage& mTranslationDataStorage;
Cursor* mCursor;
CharacterCreation* mCharGen;

View file

@ -21,6 +21,7 @@
#include "../engine.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
@ -51,6 +52,7 @@ namespace MWInput
, mUIYMultiplier (Settings::Manager::getFloat("ui y multiplier", "Input"))
, mPreviewPOVDelay(0.f)
, mTimeIdle(0.f)
, mOverencumberedMessageDelay(0.f)
{
Ogre::RenderWindow* window = ogre.getWindow ();
size_t windowHnd;
@ -85,10 +87,12 @@ namespace MWInput
std::string("false")));
pl.insert(std::make_pair(std::string("x11_keyboard_grab"),
std::string("false")));
pl.insert(std::make_pair(std::string("XAutoRepeatOn"),
std::string("true")));
#endif
}
#if defined OIS_LINUX_PLATFORM
pl.insert(std::make_pair(std::string("XAutoRepeatOn"),
std::string("true")));
#endif
#if defined(__APPLE__) && !defined(__LP64__)
// Give the application window focus to receive input events
@ -175,6 +179,11 @@ namespace MWInput
case A_Activate:
resetIdleTime();
activate();
if( MWBase::Environment::get().getWindowManager()->isGuiMode()
&& MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_InterMessageBox ) {
// Pressing the activation key when a messagebox is prompting for "ok" will activate the ok button
MWBase::Environment::get().getWindowManager()->enterPressed();
}
break;
case A_Journal:
toggleJournal ();
@ -268,26 +277,29 @@ namespace MWInput
// be done in the physics system.
if (mControlSwitch["playercontrols"])
{
bool triedToMove = false;
if (actionIsActive(A_MoveLeft))
{
mPlayer.setAutoMove (false);
mPlayer.setLeftRight (1);
triedToMove = true;
mPlayer.setLeftRight (-1);
}
else if (actionIsActive(A_MoveRight))
{
mPlayer.setAutoMove (false);
mPlayer.setLeftRight (-1);
triedToMove = true;
mPlayer.setLeftRight (1);
}
else
mPlayer.setLeftRight (0);
if (actionIsActive(A_MoveForward))
{
triedToMove = true;
mPlayer.setAutoMove (false);
mPlayer.setForwardBackward (1);
}
else if (actionIsActive(A_MoveBackward))
{
triedToMove = true;
mPlayer.setAutoMove (false);
mPlayer.setForwardBackward (-1);
}
@ -295,12 +307,35 @@ namespace MWInput
mPlayer.setForwardBackward (0);
if (actionIsActive(A_Jump) && mControlSwitch["playerjumping"])
{
mPlayer.setUpDown (1);
triedToMove = true;
}
else if (actionIsActive(A_Crouch))
mPlayer.setUpDown (-1);
else
mPlayer.setUpDown (0);
if(actionIsActive(A_Run))
mPlayer.setRunState(true);
else
mPlayer.setRunState(false);
// if player tried to start moving, but can't (due to being overencumbered), display a notification.
if (triedToMove)
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ();
mOverencumberedMessageDelay -= dt;
if (MWWorld::Class::get(player).getEncumbrance(player) >= MWWorld::Class::get(player).getCapacity(player))
{
if (mOverencumberedMessageDelay <= 0)
{
MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage59}");
mOverencumberedMessageDelay = 1.0;
}
}
}
if (mControlSwitch["playerviewswitch"]) {
// work around preview mode toggle when pressing Alt+Tab
@ -521,6 +556,9 @@ namespace MWInput
void InputManager::toggleMainMenu()
{
if (MyGUI::InputManager::getInstance ().isModalAny())
return;
if (mWindows.isGuiMode () && (mWindows.getMode () == MWGui::GM_MainMenu || mWindows.getMode () == MWGui::GM_Settings))
mWindows.popGuiMode();
else if (mWindows.isGuiMode () && mWindows.getMode () == MWGui::GM_Video)
@ -599,6 +637,9 @@ namespace MWInput
void InputManager::toggleConsole()
{
if (MyGUI::InputManager::getInstance ().isModalAny())
return;
bool gameMode = !mWindows.isGuiMode();
// Switch to console mode no matter what mode we are currently
@ -705,6 +746,7 @@ namespace MWInput
defaultKeyBindings[A_ToggleSpell] = OIS::KC_R;
defaultKeyBindings[A_QuickKeysMenu] = OIS::KC_F1;
defaultKeyBindings[A_Console] = OIS::KC_F2;
defaultKeyBindings[A_Run] = OIS::KC_LSHIFT;
defaultKeyBindings[A_Crouch] = OIS::KC_LCONTROL;
defaultKeyBindings[A_AutoMove] = OIS::KC_Q;
defaultKeyBindings[A_Jump] = OIS::KC_E;
@ -771,6 +813,7 @@ namespace MWInput
descriptions[A_ToggleWeapon] = "sReady_Weapon";
descriptions[A_ToggleSpell] = "sReady_Magic";
descriptions[A_Console] = "sConsoleTitle";
descriptions[A_Run] = "sRun";
descriptions[A_Crouch] = "sCrouch_Sneak";
descriptions[A_AutoMove] = "sAuto_Run";
descriptions[A_Jump] = "sJump";
@ -819,6 +862,7 @@ namespace MWInput
ret.push_back(A_MoveLeft);
ret.push_back(A_MoveRight);
ret.push_back(A_TogglePOV);
ret.push_back(A_Run);
ret.push_back(A_Crouch);
ret.push_back(A_Activate);
ret.push_back(A_ToggleWeapon);

View file

@ -145,6 +145,8 @@ namespace MWInput
bool mMouseLookEnabled;
bool mGuiCursorEnabled;
float mOverencumberedMessageDelay;
float mMouseX;
float mMouseY;
int mMouseWheel;
@ -207,7 +209,7 @@ namespace MWInput
A_Journal, //Journal
A_Weapon, //Draw/Sheath weapon
A_Spell, //Ready/Unready Casting
A_AlwaysRun, //Toggle Always Run
A_Run, //Run when held
A_CycleSpellLeft, //cycling through spells
A_CycleSpellRight,
A_CycleWeaponLeft,//Cycling through weapons

View file

@ -0,0 +1,76 @@
#include "activators.hpp"
#include <OgreVector3.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
namespace MWMechanics
{
Activators::Activators()
{
}
void Activators::addActivator(const MWWorld::Ptr& ptr)
{
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
if(anim != NULL)
mActivators.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle, true)));
}
void Activators::removeActivator (const MWWorld::Ptr& ptr)
{
PtrControllerMap::iterator iter = mActivators.find(ptr);
if(iter != mActivators.end())
mActivators.erase(iter);
}
void Activators::updateActivator(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)
{
PtrControllerMap::iterator iter = mActivators.find(old);
if(iter != mActivators.end())
{
CharacterController ctrl = iter->second;
mActivators.erase(iter);
ctrl.updatePtr(ptr);
mActivators.insert(std::make_pair(ptr, ctrl));
}
}
void Activators::dropActivators (const MWWorld::Ptr::CellStore *cellStore)
{
PtrControllerMap::iterator iter = mActivators.begin();
while(iter != mActivators.end())
{
if(iter->first.getCell()==cellStore)
mActivators.erase(iter++);
else
++iter;
}
}
void Activators::update(float duration, bool paused)
{
if(!paused)
{
for(PtrControllerMap::iterator iter(mActivators.begin());iter != mActivators.end();++iter)
iter->second.update(duration);
}
}
void Activators::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
{
PtrControllerMap::iterator iter = mActivators.find(ptr);
if(iter != mActivators.end())
iter->second.playGroup(groupName, mode, number);
}
void Activators::skipAnimation(const MWWorld::Ptr& ptr)
{
PtrControllerMap::iterator iter = mActivators.find(ptr);
if(iter != mActivators.end())
iter->second.skipAnim();
}
}

View file

@ -0,0 +1,45 @@
#ifndef GAME_MWMECHANICS_ACTIVATORS_H
#define GAME_MWMECHANICS_ACTOVATRS_H
#include <string>
#include <map>
#include "character.hpp"
namespace MWWorld
{
class Ptr;
class CellStore;
}
namespace MWMechanics
{
class Activators
{
typedef std::map<MWWorld::Ptr,CharacterController> PtrControllerMap;
PtrControllerMap mActivators;
public:
Activators();
void addActivator (const MWWorld::Ptr& ptr);
///< Register an animated activator
void removeActivator (const MWWorld::Ptr& ptr);
///< Deregister an activator
void updateActivator(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr);
///< Updates an activator with a new Ptr
void dropActivators (const MWWorld::CellStore *cellStore);
///< Deregister all activators in the given cell.
void update (float duration, bool paused);
///< Update activator animations
void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
void skipAnimation(const MWWorld::Ptr& ptr);
};
}
#endif

View file

@ -165,35 +165,46 @@ namespace MWMechanics
void Actors::addActor (const MWWorld::Ptr& ptr)
{
if (!MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead())
mActors.insert (ptr);
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
if(!MWWorld::Class::get(ptr).getCreatureStats(ptr).isDead())
mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle, true)));
else
MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, "death1", 2);
mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Death1, false)));
}
void Actors::removeActor (const MWWorld::Ptr& ptr)
{
std::set<MWWorld::Ptr>::iterator iter = mActors.find (ptr);
PtrControllerMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
mActors.erase(iter);
}
if (iter!=mActors.end())
mActors.erase (iter);
void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)
{
PtrControllerMap::iterator iter = mActors.find(old);
if(iter != mActors.end())
{
CharacterController ctrl = iter->second;
mActors.erase(iter);
ctrl.updatePtr(ptr);
mActors.insert(std::make_pair(ptr, ctrl));
}
}
void Actors::dropActors (const MWWorld::Ptr::CellStore *cellStore)
{
std::set<MWWorld::Ptr>::iterator iter = mActors.begin();
while (iter!=mActors.end())
if (iter->getCell()==cellStore)
{
mActors.erase (iter++);
}
PtrControllerMap::iterator iter = mActors.begin();
while(iter != mActors.end())
{
if(iter->first.getCell()==cellStore)
mActors.erase(iter++);
else
++iter;
}
}
void Actors::update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement, float duration,
bool paused)
void Actors::update (float duration, bool paused)
{
mDuration += duration;
@ -201,79 +212,91 @@ namespace MWMechanics
{
float totalDuration = mDuration;
mDuration = 0;
std::set<MWWorld::Ptr>::iterator iter (mActors.begin());
while (iter!=mActors.end())
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++)
{
if (!MWWorld::Class::get (*iter).getCreatureStats (*iter).isDead())
if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead())
{
updateActor (*iter, totalDuration);
if(iter->second.getState() >= CharState_Death1)
iter->second.setState(CharState_Idle, true);
if (iter->getTypeName()==typeid (ESM::NPC).name())
updateNpc (*iter, totalDuration, paused);
updateActor(iter->first, totalDuration);
if(iter->first.getTypeName() == typeid(ESM::NPC).name())
updateNpc(iter->first, totalDuration, paused);
if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead())
continue;
}
if (MWWorld::Class::get (*iter).getCreatureStats (*iter).isDead())
// workaround: always keep player alive for now
// \todo remove workaround, once player death can be handled
if(iter->first.getRefData().getHandle()=="player")
{
// workaround: always keep player alive for now
// \todo remove workaround, once player death can be handled
if (iter->getRefData().getHandle()=="player")
{
MWMechanics::DynamicStat<float> stat (
MWWorld::Class::get (*iter).getCreatureStats (*iter).getHealth());
if (stat.getModified()<1)
{
stat.setModified (1, 0);
MWWorld::Class::get (*iter).getCreatureStats (*iter).setHealth (stat);
}
MWMechanics::DynamicStat<float> stat (
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).getHealth());
MWWorld::Class::get (*iter).getCreatureStats (*iter).resurrect();
++iter;
continue;
if (stat.getModified()<1)
{
stat.setModified (1, 0);
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).setHealth(stat);
}
++mDeathCount[MWWorld::Class::get (*iter).getId (*iter)];
MWBase::Environment::get().getWorld()->playAnimationGroup (*iter, "death1", 0);
if (MWWorld::Class::get (*iter).isEssential (*iter))
MWBase::Environment::get().getWindowManager()->messageBox (
"#{sKilledEssential}", std::vector<std::string>());
mActors.erase (iter++);
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).resurrect();
continue;
}
else
++iter;
if(iter->second.getState() >= CharState_Death1)
continue;
iter->second.setState(CharState_Death1, false);
++mDeathCount[MWWorld::Class::get(iter->first).getId(iter->first)];
if(MWWorld::Class::get(iter->first).isEssential(iter->first))
MWBase::Environment::get().getWindowManager()->messageBox(
"#{sKilledEssential}", std::vector<std::string>());
}
}
for (std::set<MWWorld::Ptr>::iterator iter (mActors.begin()); iter!=mActors.end();
++iter)
if(!paused)
{
Ogre::Vector3 vector = MWWorld::Class::get (*iter).getMovementVector (*iter);
mMovement.reserve(mActors.size());
if (vector!=Ogre::Vector3::ZERO)
movement.push_back (std::make_pair (iter->getRefData().getHandle(), vector));
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
{
Ogre::Vector3 movement = iter->second.update(duration);
mMovement.push_back(std::make_pair(iter->first, movement));
}
MWBase::Environment::get().getWorld()->doPhysics(mMovement, duration);
mMovement.clear();
}
}
void Actors::restoreDynamicStats()
{
for (std::set<MWWorld::Ptr>::iterator iter (mActors.begin()); iter!=mActors.end(); ++iter)
{
calculateRestoration (*iter, 3600);
}
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
calculateRestoration(iter->first, 3600);
}
int Actors::countDeaths (const std::string& id) const
{
std::map<std::string, int>::const_iterator iter = mDeathCount.find (id);
if (iter!=mDeathCount.end())
std::map<std::string, int>::const_iterator iter = mDeathCount.find(id);
if(iter != mDeathCount.end())
return iter->second;
return 0;
}
void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
{
PtrControllerMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
iter->second.playGroup(groupName, mode, number);
}
void Actors::skipAnimation(const MWWorld::Ptr& ptr)
{
PtrControllerMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
iter->second.skipAnim();
}
}

View file

@ -6,6 +6,9 @@
#include <string>
#include <map>
#include "character.hpp"
#include "../mwbase/world.hpp"
namespace Ogre
{
class Vector3;
@ -21,9 +24,14 @@ namespace MWMechanics
{
class Actors
{
std::set<MWWorld::Ptr> mActors;
float mDuration;
std::map<std::string, int> mDeathCount;
typedef std::map<MWWorld::Ptr,CharacterController> PtrControllerMap;
PtrControllerMap mActors;
MWWorld::PtrMovementList mMovement;
std::map<std::string, int> mDeathCount;
float mDuration;
void updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused);
@ -50,11 +58,13 @@ namespace MWMechanics
///
/// \note Ignored, if \a ptr is not a registered actor.
void updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr);
///< Updates an actor with a new Ptr
void dropActors (const MWWorld::CellStore *cellStore);
///< Deregister all actors in the given cell.
void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement,
float duration, bool paused);
void update (float duration, bool paused);
///< Update actor stats and store desired velocity vectors in \a movement
void updateActor (const MWWorld::Ptr& ptr, float duration);
@ -66,6 +76,9 @@ namespace MWMechanics
int countDeaths (const std::string& id) const;
///< Return the number of deaths for actors with the given ID.
void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
void skipAnimation(const MWWorld::Ptr& ptr);
};
}

View file

@ -0,0 +1,296 @@
/*
* OpenMW - The completely unofficial reimplementation of Morrowind
*
* This file (character.cpp) is part of the OpenMW package.
*
* OpenMW is distributed as free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 3, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 3 along with this program. If not, see
* http://www.gnu.org/licenses/ .
*/
#include "character.hpp"
#include <OgreStringConverter.h>
#include "../mwrender/animation.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/class.hpp"
namespace MWMechanics
{
static const struct {
CharacterState state;
const char groupname[32];
} sStateList[] = {
{ CharState_Idle, "idle" },
{ CharState_Idle2, "idle2" },
{ CharState_Idle3, "idle3" },
{ CharState_Idle4, "idle4" },
{ CharState_Idle5, "idle5" },
{ CharState_Idle6, "idle6" },
{ CharState_Idle7, "idle7" },
{ CharState_Idle8, "idle8" },
{ CharState_Idle9, "idle9" },
{ CharState_IdleSwim, "idleswim" },
{ CharState_WalkForward, "walkforward" },
{ CharState_WalkBack, "walkback" },
{ CharState_WalkLeft, "walkleft" },
{ CharState_WalkRight, "walkright" },
{ CharState_SwimWalkForward, "swimwalkforward" },
{ CharState_SwimWalkBack, "swimwalkback" },
{ CharState_SwimWalkLeft, "swimwalkleft" },
{ CharState_SwimWalkRight, "swimwalkright" },
{ CharState_RunForward, "runforward" },
{ CharState_RunBack, "runback" },
{ CharState_RunLeft, "runleft" },
{ CharState_RunRight, "runright" },
{ CharState_SwimRunForward, "swimrunforward" },
{ CharState_SwimRunBack, "swimrunback" },
{ CharState_SwimRunLeft, "swimrunleft" },
{ CharState_SwimRunRight, "swimrunright" },
{ CharState_Jump, "jump" },
{ CharState_Death1, "death1" },
{ CharState_Death2, "death2" },
{ CharState_Death3, "death3" },
{ CharState_Death4, "death4" },
{ CharState_Death5, "death5" },
};
static const size_t sStateListSize = sizeof(sStateList)/sizeof(sStateList[0]);
static void getStateInfo(CharacterState state, std::string *group)
{
for(size_t i = 0;i < sStateListSize;i++)
{
if(sStateList[i].state == state)
{
*group = sStateList[i].groupname;
return;
}
}
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(state));
}
CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop)
: mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false)
{
if(!mAnimation)
return;
mAnimation->setController(this);
getStateInfo(mState, &mCurrentGroup);
if(ptr.getTypeName() == typeid(ESM::Activator).name())
{
/* Don't accumulate with activators (they don't get moved). */
mAnimation->setAccumulation(Ogre::Vector3::ZERO);
}
else
{
/* Accumulate along X/Y only for now, until we can figure out how we should
* handle knockout and death which moves the character down. */
mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 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);
}
CharacterController::~CharacterController()
{
}
void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
{
mPtr = 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;
}
Ogre::Vector3 CharacterController::update(float duration)
{
Ogre::Vector3 movement(0.0f);
float speed = 0.0f;
if(!(getState() >= CharState_Death1))
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
const Ogre::Vector3 &vec = cls.getMovementVector(mPtr);
bool onground = world->isOnGround(mPtr);
bool inwater = world->isSwimming(mPtr);
bool isrunning = cls.getStance(mPtr, MWWorld::Class::Run);
speed = cls.getSpeed(mPtr);
/* 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)
{
float x = cls.getJump(mPtr);
if(vec.x == 0 && vec.y == 0)
movement.z += x*duration;
else
{
/* FIXME: this would be more correct if we were going into a jumping state,
* rather than normal walking/idle states. */
//Ogre::Vector3 lat = Ogre::Vector3(vec.x, vec.y, 0.0f).normalisedCopy();
//movement += Ogre::Vector3(lat.x, lat.y, 1.0f) * x * 0.707f * duration;
movement.z += x * 0.707f * duration;
}
//decrease fatigue by fFatigueJumpBase + (1 - normalizedEncumbrance) * fFatigueJumpMult;
}
if(std::abs(vec.x/2.0f) > std::abs(vec.y) && speed > 0.0f)
{
if(vec.x > 0.0f)
setState(isrunning ?
(inwater ? CharState_SwimRunRight : CharState_RunRight) :
(inwater ? CharState_SwimWalkRight : CharState_WalkRight), true);
else if(vec.x < 0.0f)
setState(isrunning ?
(inwater ? CharState_SwimRunLeft: CharState_RunLeft) :
(inwater ? CharState_SwimWalkLeft : CharState_WalkLeft), true);
// Apply any forward/backward movement manually
movement.y += vec.y * (speed*duration);
}
else if(vec.y != 0.0f && speed > 0.0f)
{
if(vec.y > 0.0f)
setState(isrunning ?
(inwater ? CharState_SwimRunForward : CharState_RunForward) :
(inwater ? CharState_SwimWalkForward : CharState_WalkForward), true);
else if(vec.y < 0.0f)
setState(isrunning ?
(inwater ? CharState_SwimRunBack : CharState_RunBack) :
(inwater ? CharState_SwimWalkBack : CharState_WalkBack), true);
// Apply any sideways movement manually
movement.x += vec.x * (speed*duration);
}
else if(mAnimQueue.size() == 0)
setState((inwater ? CharState_IdleSwim : CharState_Idle), true);
}
if(mAnimation && !mSkipAnim)
{
mAnimation->setSpeed(speed);
movement += mAnimation->runAnimation(duration);
}
mSkipAnim = false;
return movement;
}
void CharacterController::playGroup(const std::string &groupname, int mode, int count)
{
if(!mAnimation || !mAnimation->hasAnimation(groupname))
std::cerr<< "Animation "<<groupname<<" not found" <<std::endl;
else
{
count = std::max(count, 1);
if(mode != 0 || mAnimQueue.size() == 0)
{
mAnimQueue.clear();
while(count-- > 0)
mAnimQueue.push_back(groupname);
mCurrentGroup = groupname;
mState = CharState_SpecialIdle;
mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", false);
}
else if(mode == 0)
{
mAnimQueue.resize(1);
while(count-- > 0)
mAnimQueue.push_back(groupname);
}
}
}
void CharacterController::skipAnim()
{
mSkipAnim = true;
}
void CharacterController::setState(CharacterState state, bool loop)
{
if(mState == state)
{
if(mAnimation)
mAnimation->setLooping(loop);
return;
}
mState = state;
if(!mAnimation)
return;
mAnimQueue.clear();
std::string anim;
getStateInfo(mState, &anim);
if(mAnimation->hasAnimation(anim))
{
mCurrentGroup = anim;
mAnimation->play(mCurrentGroup, "start", "stop", loop);
}
}
}

View file

@ -0,0 +1,96 @@
#ifndef GAME_MWMECHANICS_CHARACTER_HPP
#define GAME_MWMECHANICS_CHARACTER_HPP
#include <OgreVector3.h>
#include "../mwworld/ptr.hpp"
namespace MWRender
{
class Animation;
}
namespace MWMechanics
{
enum CharacterState {
CharState_SpecialIdle,
CharState_Idle,
CharState_Idle2,
CharState_Idle3,
CharState_Idle4,
CharState_Idle5,
CharState_Idle6,
CharState_Idle7,
CharState_Idle8,
CharState_Idle9,
CharState_IdleSwim,
CharState_WalkForward,
CharState_WalkBack,
CharState_WalkLeft,
CharState_WalkRight,
CharState_SwimWalkForward,
CharState_SwimWalkBack,
CharState_SwimWalkLeft,
CharState_SwimWalkRight,
CharState_RunForward,
CharState_RunBack,
CharState_RunLeft,
CharState_RunRight,
CharState_SwimRunForward,
CharState_SwimRunBack,
CharState_SwimRunLeft,
CharState_SwimRunRight,
CharState_Jump,
/* Death states must be last! */
CharState_Death1,
CharState_Death2,
CharState_Death3,
CharState_Death4,
CharState_Death5
};
class CharacterController
{
MWWorld::Ptr mPtr;
MWRender::Animation *mAnimation;
typedef std::deque<std::string> AnimationQueue;
AnimationQueue mAnimQueue;
std::string mCurrentGroup;
CharacterState mState;
bool mSkipAnim;
protected:
/* Called by the animation whenever a new text key is reached. */
void markerEvent(float time, const std::string &evt);
friend class MWRender::Animation;
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);
Ogre::Vector3 update(float duration);
void playGroup(const std::string &groupname, int mode, int count);
void skipAnim();
void setState(CharacterState state, bool loop);
CharacterState getState() const
{ return mState; }
};
}
#endif /* GAME_MWMECHANICS_CHARACTER_HPP */

View file

@ -175,34 +175,47 @@ namespace MWMechanics
buildPlayer();
}
void MechanicsManager::addActor (const MWWorld::Ptr& ptr)
void MechanicsManager::add(const MWWorld::Ptr& ptr)
{
mActors.addActor (ptr);
if(ptr.getTypeName() == typeid(ESM::Activator).name())
mActivators.addActivator(ptr);
else
mActors.addActor(ptr);
}
void MechanicsManager::removeActor (const MWWorld::Ptr& ptr)
void MechanicsManager::remove(const MWWorld::Ptr& ptr)
{
if (ptr==mWatched)
if(ptr == mWatched)
mWatched = MWWorld::Ptr();
mActors.removeActor(ptr);
mActivators.removeActivator(ptr);
}
void MechanicsManager::updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)
{
if(ptr.getTypeName() == typeid(ESM::Activator).name())
mActivators.updateActivator(old, ptr);
else
mActors.updateActor(old, ptr);
}
void MechanicsManager::drop(const MWWorld::CellStore *cellStore)
{
if(!mWatched.isEmpty() && mWatched.getCell() == cellStore)
mWatched = MWWorld::Ptr();
mActors.removeActor (ptr);
mActors.dropActors(cellStore);
mActivators.dropActivators(cellStore);
}
void MechanicsManager::dropActors (const MWWorld::Ptr::CellStore *cellStore)
{
if (!mWatched.isEmpty() && mWatched.getCell()==cellStore)
mWatched = MWWorld::Ptr();
mActors.dropActors (cellStore);
}
void MechanicsManager::watchActor (const MWWorld::Ptr& ptr)
void MechanicsManager::watchActor(const MWWorld::Ptr& ptr)
{
mWatched = ptr;
}
void MechanicsManager::update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement,
float duration, bool paused)
void MechanicsManager::update(float duration, bool paused)
{
if (!mWatched.isEmpty())
{
@ -296,9 +309,16 @@ namespace MWMechanics
}
winMgr->configureSkills (majorSkills, minorSkills);
// HACK? The player has been changed, so a new Animation object may
// have been made for them. Make sure they're properly updated.
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
mActors.removeActor(ptr);
mActors.addActor(ptr);
}
mActors.update (movement, duration, paused);
mActors.update(duration, paused);
mActivators.update(duration, paused);
}
void MechanicsManager::restoreDynamicStats()
@ -471,8 +491,10 @@ namespace MWMechanics
if(buying) x = buyTerm;
else x = std::min(buyTerm, sellTerm);
int offerPrice;
if (x < 1) offerPrice = int(x * basePrice);
if (x >= 1) offerPrice = basePrice + int((x - 1) * basePrice);
if (x < 1)
offerPrice = int(x * basePrice);
else
offerPrice = basePrice + int((x - 1) * basePrice);
offerPrice = std::max(1, offerPrice);
return offerPrice;
}
@ -535,7 +557,8 @@ namespace MWMechanics
float fPerDieRollMult = gmst.find("fPerDieRollMult")->getFloat();
float fPerTempMult = gmst.find("fPerTempMult")->getFloat();
float x,y;
float x = 0;
float y = 0;
float roll = static_cast<float> (std::rand()) / RAND_MAX * 100;
@ -629,4 +652,20 @@ namespace MWMechanics
permChange = success ? -int(cappedDispositionChange/ fPerTempMult) : y;
}
}
void MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
{
if(ptr.getTypeName() == typeid(ESM::Activator).name())
mActivators.playAnimationGroup(ptr, groupName, mode, number);
else
mActors.playAnimationGroup(ptr, groupName, mode, number);
}
void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr)
{
if(ptr.getTypeName() == typeid(ESM::Activator).name())
mActivators.skipAnimation(ptr);
else
mActors.skipAnimation(ptr);
}
}

View file

@ -7,6 +7,7 @@
#include "creaturestats.hpp"
#include "npcstats.hpp"
#include "activators.hpp"
#include "actors.hpp"
namespace Ogre
@ -29,6 +30,8 @@ namespace MWMechanics
bool mUpdatePlayer;
bool mClassSelected;
bool mRaceSelected;
Activators mActivators;
Actors mActors;
void buildPlayer();
@ -39,24 +42,24 @@ namespace MWMechanics
MechanicsManager();
virtual void addActor (const MWWorld::Ptr& ptr);
///< Register an actor for stats management
///
/// \note Dead actors are ignored.
virtual void add (const MWWorld::Ptr& ptr);
///< Register an object for management
virtual void removeActor (const MWWorld::Ptr& ptr);
///< Deregister an actor for stats management
virtual void remove (const MWWorld::Ptr& ptr);
///< Deregister an object for management
virtual void dropActors (const MWWorld::CellStore *cellStore);
///< Deregister all actors in the given cell.
virtual void updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr);
///< Moves an object to a new cell
virtual void watchActor (const MWWorld::Ptr& ptr);
virtual void drop(const MWWorld::CellStore *cellStore);
///< Deregister all objects in the given cell.
virtual void watchActor(const MWWorld::Ptr& ptr);
///< On each update look for changes in a previously registered actor and update the
/// GUI accordingly.
virtual void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement,
float duration, bool paused);
///< Update actor stats and store desired velocity vectors in \a movement
virtual void update (float duration, bool paused);
///< Update objects
///
/// \param paused In game type does not currently advance (this usually means some GUI
/// component is up).
@ -92,6 +95,9 @@ namespace MWMechanics
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange);
void toLower(std::string npcFaction);
///< Perform a persuasion action on NPC
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
virtual void skipAnimation(const MWWorld::Ptr& ptr);
};
}

View file

@ -0,0 +1,56 @@
#include "activatoranimation.hpp"
#include <OgreEntity.h>
#include <OgreSceneManager.h>
#include <OgreSubEntity.h>
#include "renderconst.hpp"
#include "../mwbase/world.hpp"
namespace MWRender
{
ActivatorAnimation::~ActivatorAnimation()
{
}
ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr)
: Animation(ptr)
{
MWWorld::LiveCellRef<ESM::Activator> *ref = mPtr.get<ESM::Activator>();
assert (ref->mBase != NULL);
if(!ref->mBase->mModel.empty())
{
std::string mesh = "meshes\\" + ref->mBase->mModel;
createEntityList(mPtr.getRefData().getBaseNode(), mesh);
for(size_t i = 0;i < mEntityList.mEntities.size();i++)
{
Ogre::Entity *ent = mEntityList.mEntities[i];
bool transparent = false;
for(unsigned int j=0;!transparent && j < ent->getNumSubEntities(); ++j)
{
Ogre::MaterialPtr mat = ent->getSubEntity(j)->getMaterial();
Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator();
while(!transparent && techIt.hasMoreElements())
{
Ogre::Technique* tech = techIt.getNext();
Ogre::Technique::PassIterator passIt = tech->getPassIterator();
while(!transparent && passIt.hasMoreElements())
{
Ogre::Pass* pass = passIt.getNext();
transparent = pass->isTransparent();
}
}
}
ent->setVisibilityFlags(RV_Misc);
ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
}
setAnimationSource(mesh);
}
}
}

View file

@ -0,0 +1,21 @@
#ifndef _GAME_RENDER_ACTIVATORANIMATION_H
#define _GAME_RENDER_ACTIVATORANIMATION_H
#include "animation.hpp"
namespace MWWorld
{
class Ptr;
}
namespace MWRender
{
class ActivatorAnimation : public Animation
{
public:
ActivatorAnimation(const MWWorld::Ptr& ptr);
virtual ~ActivatorAnimation();
};
}
#endif

View file

@ -3,14 +3,23 @@
#include <OgreSceneNode.h>
#include <OgreSceneManager.h>
#include "../mwworld/ptr.hpp"
#include "../mwworld/class.hpp"
#include "animation.hpp"
#include "activatoranimation.hpp"
#include "creatureanimation.hpp"
#include "npcanimation.hpp"
#include "renderconst.hpp"
namespace MWRender
{
using namespace Ogre;
Actors::~Actors(){
Actors::~Actors()
{
PtrAnimationMap::iterator it = mAllActors.begin();
for(;it != mAllActors.end();++it)
{
@ -19,18 +28,10 @@ Actors::~Actors(){
}
}
void Actors::setMwRoot(Ogre::SceneNode* root)
{ mMwRoot = root; }
void Actors::setRootNode(Ogre::SceneNode* root)
{ mRootNode = root; }
void Actors::insertNPC(const MWWorld::Ptr &ptr, MWWorld::InventoryStore &inv)
{
insertBegin(ptr, true, true);
NpcAnimation* anim = new MWRender::NpcAnimation(ptr, ptr.getRefData ().getBaseNode (), inv, RV_Actors);
mAllActors[ptr] = anim;
}
void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_)
void Actors::insertBegin(const MWWorld::Ptr &ptr)
{
Ogre::SceneNode* cellnode;
CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(ptr.getCell());
@ -39,7 +40,7 @@ void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_)
else
{
//Create the scenenode and put it in the map
cellnode = mMwRoot->createChildSceneNode();
cellnode = mRootNode->createChildSceneNode();
mCellSceneNodes[ptr.getCell()] = cellnode;
}
@ -62,17 +63,27 @@ void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_)
// Rotates first around z, then y, then x
insert->setOrientation(xr*yr*zr);
if (!enabled)
insert->setVisible (false);
ptr.getRefData().setBaseNode(insert);
}
void Actors::insertCreature (const MWWorld::Ptr& ptr){
insertBegin(ptr, true, true);
CreatureAnimation* anim = new MWRender::CreatureAnimation(ptr);
void Actors::insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv)
{
insertBegin(ptr);
NpcAnimation* anim = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(), inv, RV_Actors);
delete mAllActors[ptr];
mAllActors[ptr] = anim;
}
void Actors::insertCreature (const MWWorld::Ptr& ptr)
{
insertBegin(ptr);
CreatureAnimation* anim = new CreatureAnimation(ptr);
delete mAllActors[ptr];
mAllActors[ptr] = anim;
}
void Actors::insertActivator (const MWWorld::Ptr& ptr)
{
insertBegin(ptr);
ActivatorAnimation* anim = new ActivatorAnimation(ptr);
delete mAllActors[ptr];
mAllActors[ptr] = anim;
}
@ -125,47 +136,41 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store)
}
}
void Actors::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
{
PtrAnimationMap::const_iterator iter = mAllActors.find(ptr);
if(iter != mAllActors.end())
iter->second->playGroup(groupName, mode, number);
}
void Actors::skipAnimation (const MWWorld::Ptr& ptr)
{
PtrAnimationMap::const_iterator iter = mAllActors.find(ptr);
if(iter != mAllActors.end())
iter->second->skipAnim();
}
void Actors::update (float duration)
{
for(PtrAnimationMap::const_iterator iter = mAllActors.begin();iter != mAllActors.end();iter++)
iter->second->runAnimation(duration);
// Nothing to do
}
void Actors::updateObjectCell(const MWWorld::Ptr &ptr)
Animation* Actors::getAnimation(const MWWorld::Ptr &ptr)
{
PtrAnimationMap::const_iterator iter = mAllActors.find(ptr);
if(iter != mAllActors.end())
return iter->second;
return NULL;
}
void Actors::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur)
{
Ogre::SceneNode *node;
MWWorld::CellStore *newCell = ptr.getCell();
MWWorld::CellStore *newCell = cur.getCell();
CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(newCell);
if(celliter != mCellSceneNodes.end())
node = celliter->second;
else
{
node = mMwRoot->createChildSceneNode();
node = mRootNode->createChildSceneNode();
mCellSceneNodes[newCell] = node;
}
node->addChild(ptr.getRefData().getBaseNode());
node->addChild(cur.getRefData().getBaseNode());
PtrAnimationMap::iterator iter = mAllActors.find(ptr);
PtrAnimationMap::iterator iter = mAllActors.find(old);
if(iter != mAllActors.end())
{
/// \note Update key (Ptr's are compared only with refdata so mCell
/// on key is outdated), maybe redundant
Animation *anim = iter->second;
mAllActors.erase(iter);
mAllActors[ptr] = anim;
anim->updatePtr(cur);
mAllActors[cur] = anim;
}
}

View file

@ -1,24 +1,27 @@
#ifndef _GAME_RENDER_ACTORS_H
#define _GAME_RENDER_ACTORS_H
#include "npcanimation.hpp"
#include "creatureanimation.hpp"
#include <openengine/ogre/renderer.hpp>
namespace MWWorld
{
class Ptr;
class CellStore;
class InventoryStore;
}
namespace MWRender
{
class Animation;
class Actors
{
typedef std::map<MWWorld::CellStore*,Ogre::SceneNode*> CellSceneNodeMap;
typedef std::map<MWWorld::Ptr,Animation*> PtrAnimationMap;
OEngine::Render::OgreRenderer &mRend;
Ogre::SceneNode* mMwRoot;
Ogre::SceneNode* mRootNode;
CellSceneNodeMap mCellSceneNodes;
PtrAnimationMap mAllActors;
@ -26,31 +29,22 @@ namespace MWRender
Actors(OEngine::Render::OgreRenderer& _rend): mRend(_rend) {}
~Actors();
void setMwRoot(Ogre::SceneNode* root);
void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_);
void insertCreature (const MWWorld::Ptr& ptr);
void setRootNode(Ogre::SceneNode* root);
void insertBegin (const MWWorld::Ptr& ptr);
void insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv);
void insertCreature (const MWWorld::Ptr& ptr);
void insertActivator (const MWWorld::Ptr& ptr);
bool deleteObject (const MWWorld::Ptr& ptr);
///< \return found?
void removeCell(MWWorld::CellStore* store);
void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode,
int number = 1);
///< Run animation for a MW-reference. Calls to this function for references that are currently not
/// in the rendered scene should be ignored.
///
/// \param mode: 0 normal, 1 immediate start, 2 immediate loop
/// \param number How offen the animation should be run
void skipAnimation (const MWWorld::Ptr& ptr);
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the rendered scene should be ignored.
void update (float duration);
/// Updates containing cell for object rendering data
void updateObjectCell(const MWWorld::Ptr &ptr);
void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur);
Animation* getAnimation(const MWWorld::Ptr &ptr);
};
}
#endif

View file

@ -1,148 +1,453 @@
#include "animation.hpp"
#include <OgreHardwarePixelBuffer.h>
#include <OgreSkeletonManager.h>
#include <OgreSkeletonInstance.h>
#include <OgreEntity.h>
#include <OgreBone.h>
#include <OgreSubMesh.h>
#include <OgreSceneManager.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwmechanics/character.hpp"
namespace MWRender
{
Animation::Animation()
: mInsert(NULL)
, mTime(0.0f)
, mSkipFrame(false)
Animation::Animation(const MWWorld::Ptr &ptr)
: mPtr(ptr)
, mController(NULL)
, mInsert(NULL)
, mAccumRoot(NULL)
, mNonAccumRoot(NULL)
, mAccumulate(Ogre::Vector3::ZERO)
, mLastPosition(0.0f)
, mCurrentKeys(NULL)
, mCurrentAnim(NULL)
, mCurrentTime(0.0f)
, mStopTime(0.0f)
, mPlaying(false)
, mLooping(false)
, mAnimVelocity(0.0f)
, mAnimSpeedMult(1.0f)
{
}
Animation::~Animation()
{
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
for(size_t i = 0;i < mEntityList.mEntities.size();i++)
sceneMgr->destroyEntity(mEntityList.mEntities[i]);
if(mInsert)
{
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
for(size_t i = 0;i < mEntityList.mEntities.size();i++)
sceneMgr->destroyEntity(mEntityList.mEntities[i]);
}
mEntityList.mEntities.clear();
mEntityList.mSkelBase = NULL;
}
struct checklow {
bool operator()(const char &a, const char &b) const
{
return ::tolower(a) == ::tolower(b);
}
};
bool Animation::findGroupTimes(const std::string &groupname, Animation::GroupTimes *times)
void Animation::setAnimationSources(const std::vector<std::string> &names)
{
const std::string &start = groupname+": start";
const std::string &startloop = groupname+": loop start";
const std::string &stop = groupname+": stop";
const std::string &stoploop = groupname+": loop stop";
if(!mEntityList.mSkelBase)
return;
NifOgre::TextKeyMap::const_iterator iter;
for(iter = mTextKeys.begin();iter != mTextKeys.end();iter++)
mCurrentAnim = NULL;
mCurrentKeys = NULL;
mAnimVelocity = 0.0f;
mAccumRoot = NULL;
mNonAccumRoot = NULL;
mSkeletonSources.clear();
std::vector<std::string>::const_iterator nameiter;
for(nameiter = names.begin();nameiter != names.end();nameiter++)
{
if(times->mStart >= 0.0f && times->mLoopStart >= 0.0f && times->mLoopStop >= 0.0f && times->mStop >= 0.0f)
return true;
Ogre::SkeletonPtr skel = NifOgre::Loader::getSkeleton(*nameiter);
if(skel.isNull())
{
std::cerr<< "Failed to get skeleton source "<<*nameiter <<std::endl;
continue;
}
skel->touch();
std::string::const_iterator strpos = iter->second.begin();
std::string::const_iterator strend = iter->second.end();
size_t strlen = strend-strpos;
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 = mEntityList.mSkelBase->getSkeleton()->getBone(bone->getName());
}
mSkeletonSources.push_back(skel);
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);
}
if(start.size() <= strlen && std::mismatch(strpos, strend, start.begin(), checklow()).first == strend)
{
times->mStart = iter->first;
times->mLoopStart = iter->first;
}
else if(startloop.size() <= strlen && std::mismatch(strpos, strend, startloop.begin(), checklow()).first == strend)
{
times->mLoopStart = iter->first;
}
else if(stoploop.size() <= strlen && std::mismatch(strpos, strend, stoploop.begin(), checklow()).first == strend)
{
times->mLoopStop = iter->first;
}
else if(stop.size() <= strlen && std::mismatch(strpos, strend, stop.begin(), checklow()).first == strend)
{
times->mStop = iter->first;
if(times->mLoopStop < 0.0f)
times->mLoopStop = iter->first;
break;
}
}
}
return (times->mStart >= 0.0f && times->mLoopStart >= 0.0f && times->mLoopStop >= 0.0f && times->mStop >= 0.0f);
void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model)
{
mInsert = node->createChildSceneNode();
assert(mInsert);
mEntityList = NifOgre::Loader::createEntities(mInsert, model);
if(mEntityList.mSkelBase)
{
Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates();
Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator();
while(asiter.hasMoreElements())
{
Ogre::AnimationState *state = asiter.getNext();
state->setEnabled(false);
state->setLoop(false);
}
// 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 = mEntityList.mSkelBase->getSkeleton();
Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator();
while(boneiter.hasMoreElements())
boneiter.getNext()->setManuallyControlled(true);
}
}
void Animation::playGroup(std::string groupname, int mode, int loops)
bool Animation::hasAnimation(const std::string &anim)
{
GroupTimes times;
times.mLoops = loops;
if(groupname == "all")
for(std::vector<Ogre::SkeletonPtr>::const_iterator iter(mSkeletonSources.begin());iter != mSkeletonSources.end();iter++)
{
times.mStart = times.mLoopStart = 0.0f;
times.mLoopStop = times.mStop = 0.0f;
NifOgre::TextKeyMap::const_reverse_iterator iter = mTextKeys.rbegin();
if(iter != mTextKeys.rend())
times.mLoopStop = times.mStop = iter->first;
if((*iter)->hasAnimation(anim))
return true;
}
else if(!findGroupTimes(groupname, &times))
throw std::runtime_error("Failed to find animation group "+groupname);
return false;
}
if(mode == 0 && mCurGroup.mLoops > 0)
mNextGroup = times;
void Animation::setController(MWMechanics::CharacterController *controller)
{
mController = controller;
}
void Animation::setAccumulation(const Ogre::Vector3 &accum)
{
mAccumulate = accum;
}
void Animation::setSpeed(float speed)
{
mAnimSpeedMult = 1.0f;
if(mAnimVelocity > 1.0f && speed > 0.0f)
mAnimSpeedMult = speed / mAnimVelocity;
}
void Animation::setLooping(bool loop)
{
mLooping = loop;
}
void Animation::updatePtr(const MWWorld::Ptr &ptr)
{
mPtr = ptr;
}
void Animation::calcAnimVelocity()
{
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 && track->getNumKeyFrames() > 1)
{
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;
break;
}
keyiter++;
}
if(loopstoptime > loopstarttime)
{
Ogre::TransformKeyFrame startkf(0, loopstarttime);
Ogre::TransformKeyFrame endkf(0, loopstoptime);
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstarttime), &startkf);
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstoptime), &endkf);
mAnimVelocity = startkf.getTranslate().distance(endkf.getTranslate()) /
(loopstoptime-loopstarttime);
}
}
}
void Animation::applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel)
{
Ogre::TimeIndex timeindex = anim->_getTimeIndex(time);
Ogre::Animation::NodeTrackIterator tracks = anim->getNodeTrackIterator();
while(tracks.hasMoreElements())
{
Ogre::NodeAnimationTrack *track = tracks.getNext();
const Ogre::String &targetname = track->getAssociatedNode()->getName();
if(!skel->hasBone(targetname))
continue;
Ogre::Bone *bone = skel->getBone(targetname);
bone->setOrientation(Ogre::Quaternion::IDENTITY);
bone->setPosition(Ogre::Vector3::ZERO);
bone->setScale(Ogre::Vector3::UNIT_SCALE);
track->applyToNode(bone, timeindex);
}
// HACK: Dirty the animation state set so that Ogre will apply the
// transformations to entities this skeleton instance is shared with.
mEntityList.mSkelBase->getAllAnimationStates()->_notifyDirty();
}
static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone)
{
if(skelsrc->hasBone(bone->getName()))
{
Ogre::Bone *srcbone = skelsrc->getBone(bone->getName());
if(!srcbone->getParent() || !bone->getParent())
{
bone->setOrientation(srcbone->getOrientation());
bone->setPosition(srcbone->getPosition());
bone->setScale(srcbone->getScale());
}
else
{
bone->_setDerivedOrientation(srcbone->_getDerivedOrientation());
bone->_setDerivedPosition(srcbone->_getDerivedPosition());
bone->setScale(Ogre::Vector3::UNIT_SCALE);
}
}
else
{
mCurGroup = times;
mNextGroup = GroupTimes();
mTime = ((mode==2) ? mCurGroup.mLoopStart : mCurGroup.mStart);
// No matching bone in the source. Make sure it stays properly offset
// from its parent.
bone->resetToInitialState();
}
Ogre::Node::ChildNodeIterator boneiter = bone->getChildIterator();
while(boneiter.hasMoreElements())
updateBoneTree(skelsrc, static_cast<Ogre::Bone*>(boneiter.getNext()));
}
void Animation::skipAnim()
void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel)
{
mSkipFrame = true;
Ogre::Skeleton::BoneIterator boneiter = skel->getRootBoneIterator();
while(boneiter.hasMoreElements())
updateBoneTree(skelsrc, boneiter.getNext());
}
void Animation::runAnimation(float timepassed)
Ogre::Vector3 Animation::updatePosition(float time)
{
if(mCurGroup.mLoops > 0 && !mSkipFrame)
if(mLooping)
mCurrentTime = std::fmod(std::max(time, 0.0f), mCurrentAnim->getLength());
else
mCurrentTime = std::min(mCurrentAnim->getLength(), std::max(time, 0.0f));
applyAnimation(mCurrentAnim, mCurrentTime, mEntityList.mSkelBase->getSkeleton());
Ogre::Vector3 posdiff = Ogre::Vector3::ZERO;
if(mNonAccumRoot)
{
mTime += timepassed;
if(mTime >= mCurGroup.mLoopStop)
/* Get the non-accumulation root's difference from the last update. */
posdiff = (mNonAccumRoot->getPosition() - mLastPosition) * mAccumulate;
/* 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)
{
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;
}
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(mNonAccumRoot)
{
const Ogre::NodeAnimationTrack *track = 0;
Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator();
while(!track && trackiter.hasMoreElements())
{
if(mCurGroup.mLoops > 1)
{
mCurGroup.mLoops--;
mTime = mTime - mCurGroup.mLoopStop + mCurGroup.mLoopStart;
}
else if(mTime >= mCurGroup.mStop)
{
if(mNextGroup.mLoops > 0)
mTime = mTime - mCurGroup.mStop + mNextGroup.mStart;
else
mTime = mCurGroup.mStop;
mCurGroup = mNextGroup;
mNextGroup = GroupTimes();
}
const Ogre::NodeAnimationTrack *cur = trackiter.getNext();
if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName())
track = cur;
}
if(mEntityList.mSkelBase)
if(track)
{
Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates();
Ogre::AnimationStateIterator as = aset->getAnimationStateIterator();
while(as.hasMoreElements())
{
Ogre::AnimationState *state = as.getNext();
state->setTimePosition(mTime);
}
Ogre::TransformKeyFrame kf(0, mCurrentTime);
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf);
mLastPosition = kf.getTranslate() * mAccumulate;
}
}
mSkipFrame = false;
}
bool Animation::handleEvent(float time, const std::string &evt)
{
if(evt == "start" || evt == "loop start")
{
/* Do nothing */
return true;
}
if(evt.compare(0, 7, "sound: ") == 0)
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f);
return true;
}
if(evt.compare(0, 10, "soundgen: ") == 0)
{
// FIXME: Lookup the SoundGen (SNDG) for the specified sound that corresponds
// to this actor type
return true;
}
if(evt == "loop stop")
{
if(mLooping)
{
reset("loop start", "");
if(mCurrentTime >= 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);
return true;
}
void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop)
{
try {
bool found = false;
/* Look in reverse; last-inserted source has priority. */
for(std::vector<Ogre::SkeletonPtr>::const_reverse_iterator iter(mSkeletonSources.rbegin());iter != mSkeletonSources.rend();iter++)
{
if((*iter)->hasAnimation(groupname))
{
mCurrentAnim = (*iter)->getAnimation(groupname);
mCurrentKeys = &mTextKeys[groupname];
mAnimVelocity = 0.0f;
if(mNonAccumRoot)
calcAnimVelocity();
found = true;
break;
}
}
if(!found)
throw std::runtime_error("Failed to find animation "+groupname);
reset(start, stop);
setLooping(loop);
mPlaying = true;
}
catch(std::exception &e) {
std::cerr<< e.what() <<std::endl;
}
}
Ogre::Vector3 Animation::runAnimation(float timepassed)
{
Ogre::Vector3 movement = Ogre::Vector3::ZERO;
timepassed *= mAnimSpeedMult;
while(mCurrentAnim && mPlaying)
{
float targetTime = std::min(mStopTime, mCurrentTime+timepassed);
if(mNextKey == mCurrentKeys->end() || mNextKey->first > targetTime)
{
movement += updatePosition(targetTime);
mPlaying = (mLooping || mStopTime > targetTime);
break;
}
float time = mNextKey->first;
const std::string &evt = mNextKey->second;
mNextKey++;
movement += updatePosition(time);
mPlaying = (mLooping || mStopTime > time);
timepassed = targetTime - time;
if(!handleEvent(time, evt))
break;
}
return movement;
}
}

View file

@ -1,55 +1,99 @@
#ifndef _GAME_RENDER_ANIMATION_H
#define _GAME_RENDER_ANIMATION_H
#include <vector>
#include <components/nifogre/ogre_nif_loader.hpp>
#include <openengine/ogre/renderer.hpp>
#include "../mwworld/actiontalk.hpp"
#include <components/nif/node.hpp>
#include <openengine/bullet/physic.hpp>
#include "../mwworld/ptr.hpp"
namespace MWMechanics
{
class CharacterController;
}
namespace MWRender
{
namespace MWRender {
class Animation {
struct GroupTimes {
float mStart;
float mStop;
float mLoopStart;
float mLoopStop;
size_t mLoops;
GroupTimes()
: mStart(-1.0f), mStop(-1.0f), mLoopStart(-1.0f), mLoopStop(-1.0f),
mLoops(0)
{ }
};
class Animation
{
protected:
MWWorld::Ptr mPtr;
MWMechanics::CharacterController *mController;
Ogre::SceneNode* mInsert;
float mTime;
GroupTimes mCurGroup;
GroupTimes mNextGroup;
bool mSkipFrame;
NifOgre::EntityList mEntityList;
NifOgre::TextKeyMap mTextKeys;
std::map<std::string,NifOgre::TextKeyMap> mTextKeys;
Ogre::Node *mAccumRoot;
Ogre::Bone *mNonAccumRoot;
Ogre::Vector3 mAccumulate;
Ogre::Vector3 mLastPosition;
bool findGroupTimes(const std::string &groupname, GroupTimes *times);
std::vector<Ogre::SkeletonPtr> mSkeletonSources;
NifOgre::TextKeyMap *mCurrentKeys;
NifOgre::TextKeyMap::const_iterator mNextKey;
Ogre::Animation *mCurrentAnim;
float mCurrentTime;
float mStopTime;
bool mPlaying;
bool mLooping;
float mAnimVelocity;
float mAnimSpeedMult;
void calcAnimVelocity();
/* Applies the given animation to the given skeleton instance, using the specified time. */
void applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel);
/* Updates a skeleton instance so that all bones matching the source skeleton (based on
* bone names) are positioned identically. */
void updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel);
/* Updates the animation to the specified time, and returns the movement
* vector since the last update or reset. */
Ogre::Vector3 updatePosition(float time);
/* 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.
*/
void reset(const std::string &start, const std::string &stop);
bool handleEvent(float time, const std::string &evt);
/* Specifies a list of skeleton names to use as animation sources. */
void setAnimationSources(const std::vector<std::string> &names);
/* 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 createEntityList(Ogre::SceneNode *node, const std::string &model);
public:
Animation();
Animation(const MWWorld::Ptr &ptr);
virtual ~Animation();
void playGroup(std::string groupname, int mode, int loops);
void skipAnim();
virtual void runAnimation(float timepassed);
void setController(MWMechanics::CharacterController *controller);
void updatePtr(const MWWorld::Ptr &ptr);
bool hasAnimation(const std::string &anim);
// Specifies the axis' to accumulate on. Non-accumulated axis will just
// move visually, but not affect the actual movement. Each x/y/z value
// should be on the scale of 0 to 1.
void setAccumulation(const Ogre::Vector3 &accum);
void setSpeed(float speed);
void setLooping(bool loop);
void play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop);
virtual Ogre::Vector3 runAnimation(float timepassed);
};
}

View file

@ -11,6 +11,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
#include "renderconst.hpp"
#include "npcanimation.hpp"
@ -120,7 +121,8 @@ namespace MWRender
void InventoryPreview::update(int sizeX, int sizeY)
{
mAnimation->forceUpdate ();
mAnimation->forceUpdate();
mAnimation->runAnimation(0.0f);
mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024)));
@ -143,8 +145,7 @@ namespace MWRender
{
mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, RV_PlayerPreview);
mAnimation->playGroup ("inventoryhandtohand", 0, 1);
mAnimation->runAnimation (0);
mAnimation->play("inventoryhandtohand", "start", "stop", false);
}
// --------------------------------------------------------------------------------------------------
@ -160,6 +161,7 @@ namespace MWRender
void RaceSelectionPreview::update(float angle)
{
mAnimation->runAnimation(0.0f);
mNode->roll(Ogre::Radian(angle), Ogre::SceneNode::TS_LOCAL);
mNode->setVisible (true);
@ -174,4 +176,9 @@ namespace MWRender
rebuild();
update(0);
}
void RaceSelectionPreview::onSetup ()
{
mAnimation->play("idle", "start", "stop", false);
}
}

View file

@ -85,6 +85,8 @@ namespace MWRender
public:
RaceSelectionPreview();
virtual void onSetup();
void update(float angle);
const ESM::NPC &getPrototype() const {

View file

@ -8,70 +8,54 @@
#include "../mwbase/world.hpp"
using namespace Ogre;
using namespace NifOgre;
namespace MWRender{
namespace MWRender
{
CreatureAnimation::~CreatureAnimation()
{
}
CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr): Animation()
CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
: Animation(ptr)
{
mInsert = ptr.getRefData().getBaseNode();
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
assert (ref->mBase != NULL);
if(!ref->mBase->mModel.empty())
{
std::string mesh = "meshes\\" + ref->mBase->mModel;
std::string model = "meshes\\"+ref->mBase->mModel;
mEntityList = NifOgre::NIFLoader::createEntities(mInsert, &mTextKeys, mesh);
createEntityList(mPtr.getRefData().getBaseNode(), model);
for(size_t i = 0;i < mEntityList.mEntities.size();i++)
{
Ogre::Entity *ent = mEntityList.mEntities[i];
ent->setVisibilityFlags(RV_Actors);
bool transparent = false;
for (unsigned int j=0;j < ent->getNumSubEntities() && !transparent; ++j)
for(unsigned int j=0;!transparent && j < ent->getNumSubEntities(); ++j)
{
Ogre::MaterialPtr mat = ent->getSubEntity(j)->getMaterial();
Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator();
while (techIt.hasMoreElements() && !transparent)
while(!transparent && techIt.hasMoreElements())
{
Ogre::Technique* tech = techIt.getNext();
Ogre::Technique::PassIterator passIt = tech->getPassIterator();
while (passIt.hasMoreElements() && !transparent)
while(!transparent && passIt.hasMoreElements())
{
Ogre::Pass* pass = passIt.getNext();
if (pass->getDepthWriteEnabled() == false)
transparent = true;
transparent = pass->isTransparent();
}
}
}
ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
}
if(mEntityList.mSkelBase)
{
Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates();
Ogre::AnimationStateIterator as = aset->getAnimationStateIterator();
while(as.hasMoreElements())
{
Ogre::AnimationState *state = as.getNext();
state->setEnabled(true);
state->setLoop(false);
}
}
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);
}
}
void CreatureAnimation::runAnimation(float timepassed)
{
// Placeholder
Animation::runAnimation(timepassed);
}
}

View file

@ -3,18 +3,19 @@
#include "animation.hpp"
#include "components/nifogre/ogre_nif_loader.hpp"
namespace MWWorld
{
class Ptr;
}
namespace MWRender{
class CreatureAnimation: public Animation
namespace MWRender
{
class CreatureAnimation : public Animation
{
public:
virtual ~CreatureAnimation();
CreatureAnimation(const MWWorld::Ptr& ptr);
virtual void runAnimation(float timepassed);
virtual ~CreatureAnimation();
};
}
#endif

View file

@ -8,6 +8,8 @@
#include <OgreMaterialManager.h>
#include <OgreManualObject.h>
#include <openengine/bullet/physic.hpp>
#include <components/esm/loadstat.hpp>
#include <components/esm/loadpgrd.hpp>
@ -148,9 +150,9 @@ ManualObject *Debugging::createPathgridPoints(const ESM::Pathgrid *pathgrid)
return result;
}
Debugging::Debugging(SceneNode *mwRoot, OEngine::Physic::PhysicEngine *engine) :
mMwRoot(mwRoot), mEngine(engine),
mSceneMgr(mwRoot->getCreator()),
Debugging::Debugging(SceneNode *root, OEngine::Physic::PhysicEngine *engine) :
mRootNode(root), mEngine(engine),
mSceneMgr(root->getCreator()),
mPathgridEnabled(false),
mInteriorPathgridNode(NULL), mPathGridRoot(NULL),
mGridMatsCreated(false)
@ -206,7 +208,7 @@ void Debugging::togglePathgrid()
createGridMaterials();
// add path grid meshes to already loaded cells
mPathGridRoot = mMwRoot->createChildSceneNode();
mPathGridRoot = mRootNode->createChildSceneNode();
for(CellList::iterator it = mActiveCells.begin(); it != mActiveCells.end(); ++it)
{
enableCellPathgrid(*it);

View file

@ -3,7 +3,6 @@
#include <utility>
#include <openengine/ogre/renderer.hpp>
#include <openengine/bullet/physic.hpp>
#include <vector>
#include <string>
@ -13,6 +12,14 @@ namespace ESM
struct Pathgrid;
}
namespace OEngine
{
namespace Physic
{
class PhysicEngine;
}
}
namespace Ogre
{
class Camera;
@ -47,7 +54,7 @@ namespace MWRender
typedef std::vector<MWWorld::CellStore *> CellList;
CellList mActiveCells;
Ogre::SceneNode *mMwRoot;
Ogre::SceneNode *mRootNode;
Ogre::SceneNode *mPathGridRoot;
@ -71,7 +78,7 @@ namespace MWRender
Ogre::ManualObject *createPathgridLines(const ESM::Pathgrid *pathgrid);
Ogre::ManualObject *createPathgridPoints(const ESM::Pathgrid *pathgrid);
public:
Debugging(Ogre::SceneNode* mwRoot, OEngine::Physic::PhysicEngine *engine);
Debugging(Ogre::SceneNode* root, OEngine::Physic::PhysicEngine *engine);
~Debugging();
bool toggleRenderMode (int mode);

View file

@ -190,7 +190,7 @@ namespace MWRender
{
imageX = float(x / 8192.f - mMinX) / (mMaxX - mMinX + 1);
imageY = 1.f-float(-z / 8192.f - mMinY) / (mMaxY - mMinY + 1);
imageY = 1.f-float(z / 8192.f - mMinY) / (mMaxY - mMinY + 1);
}
void GlobalMap::cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY)

View file

@ -28,9 +28,6 @@ LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWRender::RenderingManag
mCellCamera = mRendering->getScene()->createCamera("CellCamera");
mCellCamera->setProjectionType(PT_ORTHOGRAPHIC);
// look down -y
const float sqrt0pt5 = 0.707106781;
mCellCamera->setOrientation(Quaternion(sqrt0pt5, -sqrt0pt5, 0, 0));
mCameraNode->attachObject(mCellCamera);
}
@ -82,8 +79,8 @@ void LocalMap::saveFogOfWar(MWWorld::Ptr::CellStore* cell)
}
else
{
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z);
Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().z);
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y);
Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().y);
Vector2 length = max-min;
// divide into segments
@ -107,6 +104,7 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell)
mInterior = false;
mCameraRotNode->setOrientation(Quaternion::IDENTITY);
mCellCamera->setOrientation(Quaternion(Ogre::Math::Cos(Ogre::Degree(0)/2.f), 0, 0, -Ogre::Math::Sin(Ogre::Degree(0)/2.f)));
int x = cell->mCell->getGridX();
int y = cell->mCell->getGridY();
@ -115,49 +113,60 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell)
mCameraPosNode->setPosition(Vector3(0,0,0));
render((x+0.5)*sSize, (-y-0.5)*sSize, -10000, 10000, sSize, sSize, name);
render((x+0.5)*sSize, (y+0.5)*sSize, -10000, 10000, sSize, sSize, name);
}
void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell,
AxisAlignedBox bounds)
{
// if we're in an empty cell, don't bother rendering anything
if (bounds.isNull ())
return;
mInterior = true;
mBounds = bounds;
Vector2 z(mBounds.getMaximum().y, mBounds.getMinimum().y);
float zMin = mBounds.getMinimum().z;
float zMax = mBounds.getMaximum().z;
const Vector2& north = MWBase::Environment::get().getWorld()->getNorthVector(cell);
Radian angle(std::atan2(-north.x, -north.y));
Radian angle = Ogre::Math::ATan2 (north.x, north.y);
mAngle = angle.valueRadians();
mCameraRotNode->setOrientation(Quaternion(Math::Cos(angle/2.f), 0, Math::Sin(angle/2.f), 0));
mCellCamera->setOrientation(Quaternion::IDENTITY);
mCameraRotNode->setOrientation(Quaternion(Math::Cos(mAngle/2.f), 0, 0, -Math::Sin(mAngle/2.f)));
// rotate the cell and merge the rotated corners to the bounding box
Vector2 _center(bounds.getCenter().x, bounds.getCenter().z);
Vector3 _c1 = bounds.getCorner(AxisAlignedBox::NEAR_LEFT_BOTTOM);
Vector3 _c2 = bounds.getCorner(AxisAlignedBox::FAR_LEFT_BOTTOM);
Vector3 _c3 = bounds.getCorner(AxisAlignedBox::NEAR_RIGHT_BOTTOM);
Vector3 _c4 = bounds.getCorner(AxisAlignedBox::FAR_RIGHT_BOTTOM);
Vector2 c1(_c1.x, _c1.z);
Vector2 c2(_c2.x, _c2.z);
Vector2 c3(_c3.x, _c3.z);
Vector2 c4(_c4.x, _c4.z);
Vector2 _center(bounds.getCenter().x, bounds.getCenter().y);
Vector3 _c1 = bounds.getCorner(AxisAlignedBox::FAR_LEFT_BOTTOM);
Vector3 _c2 = bounds.getCorner(AxisAlignedBox::FAR_RIGHT_BOTTOM);
Vector3 _c3 = bounds.getCorner(AxisAlignedBox::FAR_LEFT_TOP);
Vector3 _c4 = bounds.getCorner(AxisAlignedBox::FAR_RIGHT_TOP);
Vector2 c1(_c1.x, _c1.y);
Vector2 c2(_c2.x, _c2.y);
Vector2 c3(_c3.x, _c3.y);
Vector2 c4(_c4.x, _c4.y);
c1 = rotatePoint(c1, _center, mAngle);
c2 = rotatePoint(c2, _center, mAngle);
c3 = rotatePoint(c3, _center, mAngle);
c4 = rotatePoint(c4, _center, mAngle);
mBounds.merge(Vector3(c1.x, 0, c1.y));
mBounds.merge(Vector3(c2.x, 0, c2.y));
mBounds.merge(Vector3(c3.x, 0, c3.y));
mBounds.merge(Vector3(c4.x, 0, c4.y));
mBounds.merge(Vector3(c1.x, c1.y, 0));
mBounds.merge(Vector3(c2.x, c2.y, 0));
mBounds.merge(Vector3(c3.x, c3.y, 0));
mBounds.merge(Vector3(c4.x, c4.y, 0));
Vector2 center(mBounds.getCenter().x, mBounds.getCenter().z);
// apply a little padding
mBounds.scale ((mBounds.getSize ()+Ogre::Vector3(1000,1000,0)) / mBounds.getSize ());
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z);
Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().z);
Vector2 center(mBounds.getCenter().x, mBounds.getCenter().y);
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y);
Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().y);
Vector2 length = max-min;
mCameraPosNode->setPosition(Vector3(center.x, 0, center.y));
mCameraPosNode->setPosition(Vector3(center.x, center.y, 0));
// divide into segments
const int segsX = std::ceil( length.x / sSize );
@ -172,7 +181,7 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell,
Vector2 start = min + Vector2(sSize*x,sSize*y);
Vector2 newcenter = start + 4096;
render(newcenter.x - center.x, newcenter.y - center.y, z.y, z.x, sSize, sSize,
render(newcenter.x - center.x, newcenter.y - center.y, zMin, zMax, sSize, sSize,
cell->mCell->mName + "_" + coordStr(x,y));
}
}
@ -193,7 +202,7 @@ void LocalMap::render(const float x, const float y,
mRendering->getScene()->setAmbientLight(ColourValue(1,1,1));
mRenderingManager->disableLights();
mCameraNode->setPosition(Vector3(x, zhigh+100000, y));
mCameraNode->setPosition(Vector3(x, y, zhigh+100000));
//mCellCamera->setFarClipDistance( (zhigh-zlow) * 1.1 );
mCellCamera->setFarClipDistance(0); // infinite
@ -272,15 +281,15 @@ void LocalMap::render(const float x, const float y,
void LocalMap::getInteriorMapPosition (Ogre::Vector2 pos, float& nX, float& nY, int& x, int& y)
{
pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().z), mAngle);
pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().y), mAngle);
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z);
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y);
x = std::ceil((pos.x - min.x)/sSize)-1;
y = std::ceil((pos.y - min.y)/sSize)-1;
nX = (pos.x - min.x - sSize*x)/sSize;
nY = (pos.y - min.y - sSize*y)/sSize;
nY = 1.0-(pos.y - min.y - sSize*y)/sSize;
}
bool LocalMap::isPositionExplored (float nX, float nY, int x, int y, bool interior)
@ -311,19 +320,19 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni
int x,y;
float u,v;
Vector2 pos(position.x, position.z);
Vector2 pos(position.x, position.y);
if (mInterior)
getInteriorMapPosition(pos, u,v, x,y);
Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).zAxis();
Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).yAxis();
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z);
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y);
if (!mInterior)
{
x = std::ceil(pos.x / sSize)-1;
y = std::ceil(-pos.y / sSize)-1;
y = std::ceil(pos.y / sSize)-1;
mCellX = x;
mCellY = y;
}
@ -337,7 +346,7 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni
if (!mInterior)
{
u = std::abs((pos.x - (sSize*x))/sSize);
v = 1-std::abs((pos.y + (sSize*y))/sSize);
v = 1.0-std::abs((pos.y - (sSize*y))/sSize);
texBaseName = "Cell_";
}
else
@ -346,15 +355,13 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni
}
MWBase::Environment::get().getWindowManager()->setPlayerPos(u, v);
MWBase::Environment::get().getWindowManager()->setPlayerDir(playerdirection.x, -playerdirection.z);
MWBase::Environment::get().getWindowManager()->setPlayerDir(playerdirection.x, playerdirection.y);
// explore radius (squared)
const float sqrExploreRadius = (mInterior ? 0.01 : 0.09) * sFogOfWarResolution*sFogOfWarResolution;
const float exploreRadius = (mInterior ? 0.1 : 0.3) * sFogOfWarResolution; // explore radius from 0 to sFogOfWarResolution
const float exploreRadiusUV = exploreRadius / sFogOfWarResolution; // explore radius from 0 to 1 (UV space)
int intExtMult = mInterior ? 1 : -1; // interior and exterior have reversed Y coordinates (interior: top to bottom)
// change the affected fog of war textures (in a 3x3 grid around the player)
for (int mx = -1; mx<2; ++mx)
{
@ -375,7 +382,7 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni
if (!affected)
continue;
std::string texName = texBaseName + coordStr(x+mx,y+my*intExtMult);
std::string texName = texBaseName + coordStr(x+mx,y+my*-1);
TexturePtr tex = TextureManager::getSingleton().getByName(texName+"_fog");
if (!tex.isNull())

View file

@ -5,295 +5,257 @@
#include <OgreSubEntity.h>
#include "../mwworld/esmstore.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "renderconst.hpp"
using namespace Ogre;
using namespace NifOgre;
namespace MWRender{
namespace MWRender
{
const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize] = {
{ ESM::PRT_Head, "Head" },
{ ESM::PRT_Hair, "Head" },
{ ESM::PRT_Neck, "Neck" },
{ ESM::PRT_Cuirass, "Chest" },
{ ESM::PRT_Groin, "Groin" },
{ ESM::PRT_Skirt, "Groin" },
{ ESM::PRT_RHand, "Right Hand" },
{ ESM::PRT_LHand, "Left Hand" },
{ ESM::PRT_RWrist, "Right Wrist" },
{ ESM::PRT_LWrist, "Left Wrist" },
{ ESM::PRT_Shield, "Shield" },
{ ESM::PRT_RForearm, "Right Forearm" },
{ ESM::PRT_LForearm, "Left Forearm" },
{ ESM::PRT_RUpperarm, "Right Upper Arm" },
{ ESM::PRT_LUpperarm, "Left Upper Arm" },
{ ESM::PRT_RFoot, "Right Foot" },
{ ESM::PRT_LFoot, "Left Foot" },
{ ESM::PRT_RAnkle, "Right Ankle" },
{ ESM::PRT_LAnkle, "Left Ankle" },
{ ESM::PRT_RKnee, "Right Knee" },
{ ESM::PRT_LKnee, "Left Knee" },
{ ESM::PRT_RLeg, "Right Upper Leg" },
{ ESM::PRT_LLeg, "Left Upper Leg" },
{ ESM::PRT_RPauldron, "Right Clavicle" },
{ ESM::PRT_LPauldron, "Left Clavicle" },
{ ESM::PRT_Weapon, "Weapon" },
{ ESM::PRT_Tail, "Tail" }
};
NpcAnimation::~NpcAnimation()
{
removeEntities(mHead);
removeEntities(mHair);
removeEntities(mNeck);
removeEntities(mChest);
removeEntities(mGroin);
removeEntities(mSkirt);
removeEntities(mHandL);
removeEntities(mHandR);
removeEntities(mWristL);
removeEntities(mWristR);
removeEntities(mForearmL);
removeEntities(mForearmR);
removeEntities(mUpperArmL);
removeEntities(mUpperArmR);
removeEntities(mFootL);
removeEntities(mFootR);
removeEntities(mAnkleL);
removeEntities(mAnkleR);
removeEntities(mKneeL);
removeEntities(mKneeR);
removeEntities(mUpperLegL);
removeEntities(mUpperLegR);
removeEntities(mClavicleL);
removeEntities(mClavicleR);
removeEntities(mTail);
for(size_t i = 0;i < sPartListSize;i++)
removeEntities(mEntityParts[i]);
}
NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags)
: Animation(),
: Animation(ptr),
mStateID(-1),
mInv(inv),
mTimeToChange(0),
mVisibilityFlags(visibilityFlags),
mRobe(mInv.end()),
mHelmet(mInv.end()),
mShirt(mInv.end()),
mCuirass(mInv.end()),
mGreaves(mInv.end()),
mPauldronL(mInv.end()),
mPauldronR(mInv.end()),
mBoots(mInv.end()),
mPants(mInv.end()),
mGloveL(mInv.end()),
mGloveR(mInv.end()),
mSkirtIter(mInv.end())
mRobe(inv.end()),
mHelmet(inv.end()),
mShirt(inv.end()),
mCuirass(inv.end()),
mGreaves(inv.end()),
mPauldronL(inv.end()),
mPauldronR(inv.end()),
mBoots(inv.end()),
mPants(inv.end()),
mGloveL(inv.end()),
mGloveR(inv.end()),
mSkirtIter(inv.end())
{
mNpc = ptr.get<ESM::NPC>()->mBase;
mNpc = mPtr.get<ESM::NPC>()->mBase;
for (int init = 0; init < 27; init++)
for(size_t i = 0;i < sPartListSize;i++)
{
mPartslots[init] = -1; //each slot is empty
mPartPriorities[init] = 0;
mPartslots[i] = -1; //each slot is empty
mPartPriorities[i] = 0;
}
const MWWorld::ESMStore &store =
MWBase::Environment::get().getWorld()->getStore();
const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace);
float scale = race->mData.mHeight.mMale;
if(!mNpc->isMale())
scale = race->mData.mHeight.mFemale;
node->scale(Ogre::Vector3(scale));
mHeadModel = "meshes\\" + store.get<ESM::BodyPart>().find(mNpc->mHead)->mModel;
mHairModel = "meshes\\" + store.get<ESM::BodyPart>().find(mNpc->mHair)->mModel;
mBodyPrefix = "b_n_" + mNpc->mRace;
Misc::StringUtils::toLower(mBodyPrefix);
mInsert = node;
assert(mInsert);
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
mEntityList = NifOgre::NIFLoader::createEntities(mInsert, &mTextKeys, smodel);
createEntityList(node, smodel);
for(size_t i = 0;i < mEntityList.mEntities.size();i++)
{
Ogre::Entity *base = mEntityList.mEntities[i];
base->getUserObjectBindings ().setUserAny (Ogre::Any(-1));
base->getUserObjectBindings().setUserAny(Ogre::Any(-1));
base->setVisibilityFlags(mVisibilityFlags);
bool transparent = false;
for(unsigned int j=0;j < base->getNumSubEntities();++j)
for(unsigned int j=0;!transparent && j < base->getNumSubEntities();++j)
{
Ogre::MaterialPtr mat = base->getSubEntity(j)->getMaterial();
Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator();
while (techIt.hasMoreElements())
while(!transparent && techIt.hasMoreElements())
{
Ogre::Technique* tech = techIt.getNext();
Ogre::Technique::PassIterator passIt = tech->getPassIterator();
while (passIt.hasMoreElements())
while(!transparent && passIt.hasMoreElements())
{
Ogre::Pass* pass = passIt.getNext();
if (pass->getDepthWriteEnabled() == false)
transparent = true;
transparent = pass->isTransparent();
}
}
}
base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
}
if(mEntityList.mSkelBase)
{
Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates();
Ogre::AnimationStateIterator as = aset->getAnimationStateIterator();
while(as.hasMoreElements())
{
Ogre::AnimationState *state = as.getNext();
state->setEnabled(true);
state->setLoop(false);
}
}
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");
if(mNpc->mModel.length() > 0)
skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel));
setAnimationSources(skelnames);
float scale = race->mData.mHeight.mMale;
if (!mNpc->isMale()) {
scale = race->mData.mHeight.mFemale;
}
mInsert->scale(scale, scale, scale);
updateParts();
updateParts(true);
}
void NpcAnimation::updateParts()
void NpcAnimation::updateParts(bool forceupdate)
{
bool apparelChanged = false;
static const struct {
int numRemoveParts; // Max: 1
ESM::PartReferenceType removeParts[1];
const struct {
MWWorld::ContainerStoreIterator *iter;
MWWorld::ContainerStoreIterator NpcAnimation::*part;
int slot;
int numReserveParts; // Max: 12
ESM::PartReferenceType reserveParts[12];
} slotlist[] = {
{ &mRobe, MWWorld::InventoryStore::Slot_Robe },
{ &mSkirtIter, MWWorld::InventoryStore::Slot_Skirt },
{ &mHelmet, MWWorld::InventoryStore::Slot_Helmet },
{ &mCuirass, MWWorld::InventoryStore::Slot_Cuirass },
{ &mGreaves, MWWorld::InventoryStore::Slot_Greaves },
{ &mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron },
{ &mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron },
{ &mBoots, MWWorld::InventoryStore::Slot_Boots },
{ &mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet },
{ &mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet },
{ &mShirt, MWWorld::InventoryStore::Slot_Shirt },
{ &mPants, MWWorld::InventoryStore::Slot_Pants },
{ 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, { }
},
};
for(size_t i = 0;i < sizeof(slotlist)/sizeof(slotlist[0]);i++)
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 = mInv.getSlot(slotlist[i].slot);
if(*slotlist[i].iter != iter)
MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot);
if(this->*slotlist[i].part != iter)
{
*slotlist[i].iter = iter;
removePartGroup(slotlist[i].slot);
apparelChanged = true;
forceupdate = true;
break;
}
}
if(!forceupdate)
return;
if(apparelChanged)
for(size_t i = 0;i < slotlistsize;i++)
{
if(mRobe != mInv.end())
{
MWWorld::Ptr ptr = *mRobe;
MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot);
const ESM::Clothing *clothes = (ptr.get<ESM::Clothing>())->mBase;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Robe, 5, parts);
reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_Skirt, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_RUpperarm, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_LUpperarm, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_RKnee, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_LKnee, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_RForearm, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_LForearm, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_RPauldron, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_LPauldron, MWWorld::InventoryStore::Slot_Robe, 5);
this->*slotlist[i].part = iter;
removePartGroup(slotlist[i].slot);
if(this->*slotlist[i].part == inv.end())
continue;
for(int rem = 0;rem < slotlist[i].numRemoveParts;rem++)
removeIndividualPart(slotlist[i].removeParts[rem]);
int prio = 1;
MWWorld::ContainerStoreIterator &store = this->*slotlist[i].part;
if(store->getTypeName() == typeid(ESM::Clothing).name())
{
prio = ((slotlist[i].numReserveParts+1)<<1) + 0;
const ESM::Clothing *clothes = store->get<ESM::Clothing>()->mBase;
addPartGroup(slotlist[i].slot, prio, clothes->mParts.mParts);
}
if(mSkirtIter != mInv.end())
else if(store->getTypeName() == typeid(ESM::Armor).name())
{
MWWorld::Ptr ptr = *mSkirtIter;
const ESM::Clothing *clothes = (ptr.get<ESM::Clothing>())->mBase;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Skirt, 4, parts);
reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Skirt, 4);
reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Skirt, 4);
reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Skirt, 4);
prio = ((slotlist[i].numReserveParts+1)<<1) + 1;
const ESM::Armor *armor = store->get<ESM::Armor>()->mBase;
addPartGroup(slotlist[i].slot, prio, armor->mParts.mParts);
}
if(mHelmet != mInv.end())
{
removeIndividualPart(ESM::PRT_Hair);
const ESM::Armor *armor = (mHelmet->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Helmet, 3, parts);
}
if(mCuirass != mInv.end())
{
const ESM::Armor *armor = (mCuirass->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Cuirass, 3, parts);
}
if(mGreaves != mInv.end())
{
const ESM::Armor *armor = (mGreaves->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Greaves, 3, parts);
}
if(mPauldronL != mInv.end())
{
const ESM::Armor *armor = (mPauldronL->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_LeftPauldron, 3, parts);
}
if(mPauldronR != mInv.end())
{
const ESM::Armor *armor = (mPauldronR->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_RightPauldron, 3, parts);
}
if(mBoots != mInv.end())
{
if(mBoots->getTypeName() == typeid(ESM::Clothing).name())
{
const ESM::Clothing *clothes = (mBoots->get<ESM::Clothing>())->mBase;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Boots, 2, parts);
}
else if(mBoots->getTypeName() == typeid(ESM::Armor).name())
{
const ESM::Armor *armor = (mBoots->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Boots, 3, parts);
}
}
if(mGloveL != mInv.end())
{
if(mGloveL->getTypeName() == typeid(ESM::Clothing).name())
{
const ESM::Clothing *clothes = (mGloveL->get<ESM::Clothing>())->mBase;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 2, parts);
}
else
{
const ESM::Armor *armor = (mGloveL->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 3, parts);
}
}
if(mGloveR != mInv.end())
{
if(mGloveR->getTypeName() == typeid(ESM::Clothing).name())
{
const ESM::Clothing *clothes = (mGloveR->get<ESM::Clothing>())->mBase;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 2, parts);
}
else
{
const ESM::Armor *armor = (mGloveR->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 3, parts);
}
}
if(mShirt != mInv.end())
{
const ESM::Clothing *clothes = (mShirt->get<ESM::Clothing>())->mBase;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Shirt, 2, parts);
}
if(mPants != mInv.end())
{
const ESM::Clothing *clothes = (mPants->get<ESM::Clothing>())->mBase;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Pants, 2, parts);
}
for(int res = 0;res < slotlist[i].numReserveParts;res++)
reserveIndividualPart(slotlist[i].reserveParts[res], slotlist[i].slot, prio);
}
if(mPartPriorities[ESM::PRT_Head] < 1)
@ -333,21 +295,18 @@ void NpcAnimation::updateParts()
if(mPartPriorities[PartTypeList[i].type] < 1)
{
const ESM::BodyPart *part = NULL;
const MWWorld::Store<ESM::BodyPart> &partStore =
store.get<ESM::BodyPart>();
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
if (!mNpc->isMale()) {
if(!mNpc->isMale())
{
part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]);
if (part == 0) {
if(part == 0)
part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]);
}
}
if (part == 0) {
if(part == 0)
part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]);
}
if (part == 0) {
if(part == 0)
part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]);
}
if(part)
addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel);
@ -357,27 +316,69 @@ void NpcAnimation::updateParts()
NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int group, const std::string &bonename)
{
NifOgre::EntityList entities = NIFLoader::createEntities(mEntityList.mSkelBase, bonename,
mInsert, mesh);
NifOgre::EntityList entities = NifOgre::Loader::createEntities(mEntityList.mSkelBase, bonename,
mInsert, mesh);
std::vector<Ogre::Entity*> &parts = entities.mEntities;
for(size_t i = 0;i < parts.size();i++)
{
parts[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
parts[i]->setVisibilityFlags(mVisibilityFlags);
parts[i]->getUserObjectBindings ().setUserAny (Ogre::Any(group));
bool transparent = false;
for(unsigned int j=0;!transparent && j < parts[i]->getNumSubEntities();++j)
{
Ogre::MaterialPtr mat = parts[i]->getSubEntity(j)->getMaterial();
Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator();
while(!transparent && techIt.hasMoreElements())
{
Ogre::Technique* tech = techIt.getNext();
Ogre::Technique::PassIterator passIt = tech->getPassIterator();
while(!transparent && passIt.hasMoreElements())
{
Ogre::Pass* pass = passIt.getNext();
transparent = pass->isTransparent();
}
}
}
parts[i]->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
}
if(entities.mSkelBase)
{
Ogre::AnimationStateSet *aset = entities.mSkelBase->getAllAnimationStates();
Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator();
while(asiter.hasMoreElements())
{
Ogre::AnimationState *state = asiter.getNext();
state->setEnabled(false);
state->setLoop(false);
}
Ogre::SkeletonInstance *skelinst = entities.mSkelBase->getSkeleton();
Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator();
while(boneiter.hasMoreElements())
boneiter.getNext()->setManuallyControlled(true);
}
return entities;
}
void NpcAnimation::runAnimation(float timepassed)
Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
{
if(mTimeToChange > .2)
if(mTimeToChange <= 0.0f)
{
mTimeToChange = 0;
mTimeToChange = 0.2f;
updateParts();
}
mTimeToChange += timepassed;
mTimeToChange -= timepassed;
Animation::runAnimation(timepassed);
Ogre::Vector3 ret = Animation::runAnimation(timepassed);
const Ogre::SkeletonInstance *skelsrc = mEntityList.mSkelBase->getSkeleton();
for(size_t i = 0;i < sPartListSize;i++)
{
Ogre::Entity *ent = mEntityParts[i].mSkelBase;
if(!ent) continue;
updateSkeletonInstance(skelsrc, ent->getSkeleton());
ent->getAllAnimationStates()->_notifyDirty();
}
return ret;
}
void NpcAnimation::removeEntities(NifOgre::EntityList &entities)
@ -399,62 +400,14 @@ void NpcAnimation::removeIndividualPart(int type)
mPartPriorities[type] = 0;
mPartslots[type] = -1;
if(type == ESM::PRT_Head) //0
removeEntities(mHead);
else if(type == ESM::PRT_Hair) //1
removeEntities(mHair);
else if(type == ESM::PRT_Neck) //2
removeEntities(mNeck);
else if(type == ESM::PRT_Cuirass)//3
removeEntities(mChest);
else if(type == ESM::PRT_Groin)//4
removeEntities(mGroin);
else if(type == ESM::PRT_Skirt)//5
removeEntities(mSkirt);
else if(type == ESM::PRT_RHand)//6
removeEntities(mHandR);
else if(type == ESM::PRT_LHand)//7
removeEntities(mHandL);
else if(type == ESM::PRT_RWrist)//8
removeEntities(mWristR);
else if(type == ESM::PRT_LWrist) //9
removeEntities(mWristL);
else if(type == ESM::PRT_Shield) //10
for(size_t i = 0;i < sPartListSize;i++)
{
if(type == sPartList[i].type)
{
removeEntities(mEntityParts[i]);
break;
}
}
else if(type == ESM::PRT_RForearm) //11
removeEntities(mForearmR);
else if(type == ESM::PRT_LForearm) //12
removeEntities(mForearmL);
else if(type == ESM::PRT_RUpperarm) //13
removeEntities(mUpperArmR);
else if(type == ESM::PRT_LUpperarm) //14
removeEntities(mUpperArmL);
else if(type == ESM::PRT_RFoot) //15
removeEntities(mFootR);
else if(type == ESM::PRT_LFoot) //16
removeEntities(mFootL);
else if(type == ESM::PRT_RAnkle) //17
removeEntities(mAnkleR);
else if(type == ESM::PRT_LAnkle) //18
removeEntities(mAnkleL);
else if(type == ESM::PRT_RKnee) //19
removeEntities(mKneeR);
else if(type == ESM::PRT_LKnee) //20
removeEntities(mKneeL);
else if(type == ESM::PRT_RLeg) //21
removeEntities(mUpperLegR);
else if(type == ESM::PRT_LLeg) //22
removeEntities(mUpperLegL);
else if(type == ESM::PRT_RPauldron) //23
removeEntities(mClavicleR);
else if(type == ESM::PRT_LPauldron) //24
removeEntities(mClavicleL);
else if(type == ESM::PRT_Weapon) //25
{
}
else if(type == ESM::PRT_Tail) //26
removeEntities(mTail);
}
void NpcAnimation::reserveIndividualPart(int type, int group, int priority)
@ -484,96 +437,23 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority,
removeIndividualPart(type);
mPartslots[type] = group;
mPartPriorities[type] = priority;
switch(type)
for(size_t i = 0;i < sPartListSize;i++)
{
case ESM::PRT_Head: //0
mHead = insertBoundedPart(mesh, group, "Head");
break;
case ESM::PRT_Hair: //1
mHair = insertBoundedPart(mesh, group, "Head");
break;
case ESM::PRT_Neck: //2
mNeck = insertBoundedPart(mesh, group, "Neck");
break;
case ESM::PRT_Cuirass: //3
mChest = insertBoundedPart(mesh, group, "Chest");
break;
case ESM::PRT_Groin: //4
mGroin = insertBoundedPart(mesh, group, "Groin");
break;
case ESM::PRT_Skirt: //5
mSkirt = insertBoundedPart(mesh, group, "Groin");
break;
case ESM::PRT_RHand: //6
mHandR = insertBoundedPart(mesh, group, "Right Hand");
break;
case ESM::PRT_LHand: //7
mHandL = insertBoundedPart(mesh, group, "Left Hand");
break;
case ESM::PRT_RWrist: //8
mWristR = insertBoundedPart(mesh, group, "Right Wrist");
break;
case ESM::PRT_LWrist: //9
mWristL = insertBoundedPart(mesh, group, "Left Wrist");
break;
case ESM::PRT_Shield: //10
break;
case ESM::PRT_RForearm: //11
mForearmR = insertBoundedPart(mesh, group, "Right Forearm");
break;
case ESM::PRT_LForearm: //12
mForearmL = insertBoundedPart(mesh, group, "Left Forearm");
break;
case ESM::PRT_RUpperarm: //13
mUpperArmR = insertBoundedPart(mesh, group, "Right Upper Arm");
break;
case ESM::PRT_LUpperarm: //14
mUpperArmL = insertBoundedPart(mesh, group, "Left Upper Arm");
break;
case ESM::PRT_RFoot: //15
mFootR = insertBoundedPart(mesh, group, "Right Foot");
break;
case ESM::PRT_LFoot: //16
mFootL = insertBoundedPart(mesh, group, "Left Foot");
break;
case ESM::PRT_RAnkle: //17
mAnkleR = insertBoundedPart(mesh, group, "Right Ankle");
break;
case ESM::PRT_LAnkle: //18
mAnkleL = insertBoundedPart(mesh, group, "Left Ankle");
break;
case ESM::PRT_RKnee: //19
mKneeR = insertBoundedPart(mesh, group, "Right Knee");
break;
case ESM::PRT_LKnee: //20
mKneeL = insertBoundedPart(mesh, group, "Left Knee");
break;
case ESM::PRT_RLeg: //21
mUpperLegR = insertBoundedPart(mesh, group, "Right Upper Leg");
break;
case ESM::PRT_LLeg: //22
mUpperLegL = insertBoundedPart(mesh, group, "Left Upper Leg");
break;
case ESM::PRT_RPauldron: //23
mClavicleR = insertBoundedPart(mesh , group, "Right Clavicle");
break;
case ESM::PRT_LPauldron: //24
mClavicleL = insertBoundedPart(mesh, group, "Left Clavicle");
break;
case ESM::PRT_Weapon: //25
break;
case ESM::PRT_Tail: //26
mTail = insertBoundedPart(mesh, group, "Tail");
if(type == sPartList[i].type)
{
mEntityParts[i] = insertBoundedPart(mesh, group, sPartList[i].name);
break;
}
}
return true;
}
void NpcAnimation::addPartGroup(int group, int priority, std::vector<ESM::PartReference> &parts)
void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts)
{
for(std::size_t i = 0; i < parts.size(); i++)
{
ESM::PartReference &part = parts[i];
const ESM::PartReference &part = parts[i];
const MWWorld::Store<ESM::BodyPart> &partStore =
MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();
@ -585,15 +465,10 @@ void NpcAnimation::addPartGroup(int group, int priority, std::vector<ESM::PartRe
bodypart = partStore.search(part.mMale);
if(bodypart)
addOrReplaceIndividualPart(part.mPart, group, priority,"meshes\\" + bodypart->mModel);
addOrReplaceIndividualPart(part.mPart, group, priority, "meshes\\"+bodypart->mModel);
else
reserveIndividualPart(part.mPart, group, priority);
}
}
void NpcAnimation::forceUpdate ()
{
updateParts();
}
}

View file

@ -3,9 +3,6 @@
#include "animation.hpp"
#include "components/nifogre/ogre_nif_loader.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwclass/npc.hpp"
#include "../mwworld/containerstore.hpp"
namespace ESM
@ -13,49 +10,36 @@ namespace ESM
struct NPC;
}
namespace MWRender{
namespace MWWorld
{
class InventoryStore;
}
namespace MWRender
{
class NpcAnimation : public Animation
{
public:
struct PartInfo {
ESM::PartReferenceType type;
const char name[32];
};
class NpcAnimation: public Animation{
private:
MWWorld::InventoryStore& mInv;
static const size_t sPartListSize = 27;
static const PartInfo sPartList[sPartListSize];
int mStateID;
int mPartslots[27]; //Each part slot is taken by clothing, armor, or is empty
int mPartPriorities[27];
//Bounded Parts
NifOgre::EntityList mClavicleL;
NifOgre::EntityList mClavicleR;
NifOgre::EntityList mUpperArmL;
NifOgre::EntityList mUpperArmR;
NifOgre::EntityList mUpperLegL;
NifOgre::EntityList mUpperLegR;
NifOgre::EntityList mForearmL;
NifOgre::EntityList mForearmR;
NifOgre::EntityList mWristL;
NifOgre::EntityList mWristR;
NifOgre::EntityList mKneeR;
NifOgre::EntityList mKneeL;
NifOgre::EntityList mNeck;
NifOgre::EntityList mAnkleL;
NifOgre::EntityList mAnkleR;
NifOgre::EntityList mGroin;
NifOgre::EntityList mSkirt;
NifOgre::EntityList mFootL;
NifOgre::EntityList mFootR;
NifOgre::EntityList mHair;
NifOgre::EntityList mHandL;
NifOgre::EntityList mHandR;
NifOgre::EntityList mHead;
NifOgre::EntityList mChest;
NifOgre::EntityList mTail;
// Bounded Parts
NifOgre::EntityList mEntityParts[sPartListSize];
const ESM::NPC *mNpc;
std::string mHeadModel;
std::string mHairModel;
std::string mBodyPrefix;
float mTimeToChange;
MWWorld::ContainerStoreIterator mRobe;
MWWorld::ContainerStoreIterator mHelmet;
@ -72,23 +56,32 @@ private:
int mVisibilityFlags;
public:
NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node,
MWWorld::InventoryStore& inv, int visibilityFlags);
virtual ~NpcAnimation();
int mPartslots[sPartListSize]; //Each part slot is taken by clothing, armor, or is empty
int mPartPriorities[sPartListSize];
NifOgre::EntityList insertBoundedPart(const std::string &mesh, int group, const std::string &bonename);
virtual void runAnimation(float timepassed);
void updateParts();
void updateParts(bool forceupdate = false);
void removeEntities(NifOgre::EntityList &entities);
void removeIndividualPart(int type);
void reserveIndividualPart(int type, int group, int priority);
bool addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh);
void removePartGroup(int group);
void addPartGroup(int group, int priority, std::vector<ESM::PartReference>& parts);
void addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts);
void forceUpdate();
public:
NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node,
MWWorld::InventoryStore& inv, int visibilityFlags);
virtual ~NpcAnimation();
virtual Ogre::Vector3 runAnimation(float timepassed);
void forceUpdate()
{ updateParts(true); }
};
}
#endif

View file

@ -34,19 +34,37 @@ void Objects::clearSceneNode (Ogre::SceneNode *node)
for (int i=node->numAttachedObjects()-1; i>=0; --i)
{
Ogre::MovableObject *object = node->getAttachedObject (i);
// for entities, destroy any objects attached to bones
if (object->getTypeFlags () == Ogre::SceneManager::ENTITY_TYPE_MASK)
{
Ogre::Entity* ent = static_cast<Ogre::Entity*>(object);
Ogre::Entity::ChildObjectListIterator children = ent->getAttachedObjectIterator ();
while (children.hasMoreElements())
{
mRenderer.getScene ()->destroyMovableObject (children.getNext ());
}
}
node->detachObject (object);
mRenderer.getScene()->destroyMovableObject (object);
}
Ogre::Node::ChildNodeIterator it = node->getChildIterator ();
while (it.hasMoreElements ())
{
clearSceneNode(static_cast<Ogre::SceneNode*>(it.getNext ()));
}
}
void Objects::setMwRoot(Ogre::SceneNode* root)
void Objects::setRootNode(Ogre::SceneNode* root)
{
mMwRoot = root;
mRootNode = root;
}
void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_)
{
Ogre::SceneNode* root = mMwRoot;
Ogre::SceneNode* root = mRootNode;
Ogre::SceneNode* cellnode;
if(mCellSceneNodes.find(ptr.getCell()) == mCellSceneNodes.end())
{
@ -87,20 +105,16 @@ void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_)
mIsStatic = static_;
}
void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light)
{
Ogre::SceneNode* insert = ptr.getRefData().getBaseNode();
assert(insert);
Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL;
NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(insert, NULL, mesh);
NifOgre::EntityList entities = NifOgre::Loader::createEntities(insert, mesh);
for(size_t i = 0;i < entities.mEntities.size();i++)
{
const Ogre::AxisAlignedBox &tmp = entities.mEntities[i]->getBoundingBox();
bounds.merge(Ogre::AxisAlignedBox(insert->_getDerivedPosition() + tmp.getMinimum(),
insert->_getDerivedPosition() + tmp.getMaximum())
);
}
bounds.merge(entities.mEntities[i]->getWorldBoundingBox(true));
Ogre::Vector3 extents = bounds.getSize();
extents *= insert->getScale();
float size = std::max(std::max(extents.x, extents.y), extents.z);
@ -116,23 +130,21 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
mBounds[ptr.getCell()].merge(bounds);
bool transparent = false;
for(size_t i = 0;i < entities.mEntities.size();i++)
for(size_t i = 0;!transparent && i < entities.mEntities.size();i++)
{
Ogre::Entity *ent = entities.mEntities[i];
for (unsigned int i=0; i<ent->getNumSubEntities(); ++i)
for(unsigned int i=0;!transparent && i < ent->getNumSubEntities(); ++i)
{
Ogre::MaterialPtr mat = ent->getSubEntity(i)->getMaterial();
Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator();
while (techIt.hasMoreElements())
while(!transparent && techIt.hasMoreElements())
{
Ogre::Technique* tech = techIt.getNext();
Ogre::Technique::PassIterator passIt = tech->getPassIterator();
while (passIt.hasMoreElements())
while(!transparent && passIt.hasMoreElements())
{
Ogre::Pass* pass = passIt.getNext();
if (pass->getDepthWriteEnabled() == false)
transparent = true;
transparent = pass->isTransparent();
}
}
}
@ -193,26 +205,40 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
sg->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
for(size_t i = 0;i < entities.mEntities.size();i++)
std::vector<Ogre::Entity*>::reverse_iterator iter = entities.mEntities.rbegin();
while(iter != entities.mEntities.rend())
{
Ogre::Entity *ent = entities.mEntities[i];
insert->detachObject(ent);
sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale());
Ogre::Node *node = (*iter)->getParentNode();
sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale());
mRenderer.getScene()->destroyEntity(ent);
(*iter)->detachFromParent();
mRenderer.getScene()->destroyEntity(*iter);
iter++;
}
}
if (light)
{
insertLight(ptr, entities.mSkelBase, bounds.getCenter() - insert->_getDerivedPosition());
}
}
void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius)
void Objects::insertLight (const MWWorld::Ptr& ptr, Ogre::Entity* skelBase, Ogre::Vector3 fallbackCenter)
{
Ogre::SceneNode* insert = mRenderer.getScene()->getSceneNode(ptr.getRefData().getHandle());
assert(insert);
Ogre::Light *light = mRenderer.getScene()->createLight();
light->setDiffuseColour (r, g, b);
MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
const int color = ref->mBase->mData.mColor;
const float r = ((color >> 0) & 0xFF) / 255.0f;
const float g = ((color >> 8) & 0xFF) / 255.0f;
const float b = ((color >> 16) & 0xFF) / 255.0f;
const float radius = float (ref->mBase->mData.mRadius);
Ogre::Light *light = mRenderer.getScene()->createLight();
light->setDiffuseColour (r, g, b);
LightInfo info;
info.name = light->getName();
info.radius = radius;
@ -263,7 +289,17 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, f
light->setAttenuation(r*10, 0, 0, attenuation);
}
insert->attachObject(light);
// If there's an AttachLight bone, attach the light to that, otherwise attach it to the base scene node
if (skelBase && skelBase->getSkeleton ()->hasBone ("AttachLight"))
{
skelBase->attachObjectToBone ("AttachLight", light);
}
else
{
Ogre::SceneNode* childNode = insert->createChildSceneNode (fallbackCenter);
childNode->attachObject(light);
}
mLights.push_back(info);
}
@ -348,9 +384,9 @@ void Objects::enableLights()
std::vector<LightInfo>::iterator it = mLights.begin();
while (it != mLights.end())
{
if (mMwRoot->getCreator()->hasLight(it->name))
if (mRootNode->getCreator()->hasLight(it->name))
{
mMwRoot->getCreator()->getLight(it->name)->setVisible(true);
mRootNode->getCreator()->getLight(it->name)->setVisible(true);
++it;
}
else
@ -363,9 +399,9 @@ void Objects::disableLights()
std::vector<LightInfo>::iterator it = mLights.begin();
while (it != mLights.end())
{
if (mMwRoot->getCreator()->hasLight(it->name))
if (mRootNode->getCreator()->hasLight(it->name))
{
mMwRoot->getCreator()->getLight(it->name)->setVisible(false);
mRootNode->getCreator()->getLight(it->name)->setVisible(false);
++it;
}
else
@ -418,9 +454,9 @@ void Objects::update(const float dt)
std::vector<LightInfo>::iterator it = mLights.begin();
while (it != mLights.end())
{
if (mMwRoot->getCreator()->hasLight(it->name))
if (mRootNode->getCreator()->hasLight(it->name))
{
Ogre::Light* light = mMwRoot->getCreator()->getLight(it->name);
Ogre::Light* light = mRootNode->getCreator()->getLight(it->name);
float brightness;
float cycle_time;
@ -502,21 +538,17 @@ void Objects::rebuildStaticGeometry()
}
}
void Objects::updateObjectCell(const MWWorld::Ptr &ptr)
void Objects::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur)
{
Ogre::SceneNode *node;
MWWorld::CellStore *newCell = ptr.getCell();
MWWorld::CellStore *newCell = cur.getCell();
if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) {
node = mMwRoot->createChildSceneNode();
node = mRootNode->createChildSceneNode();
mCellSceneNodes[newCell] = node;
} else {
node = mCellSceneNodes[newCell];
}
node->addChild(ptr.getRefData().getBaseNode());
/// \note Still unaware how to move aabb and static w/o full rebuild,
/// moving static objects may cause problems
insertMesh(ptr, MWWorld::Class::get(ptr).getModel(ptr));
node->addChild(cur.getRefData().getBaseNode());
}

View file

@ -2,6 +2,7 @@
#define _GAME_RENDER_OBJECTS_H
#include <OgreColourValue.h>
#include <OgreAxisAlignedBox.h>
#include <openengine/ogre/renderer.hpp>
@ -52,7 +53,7 @@ class Objects{
std::map<MWWorld::CellStore *, Ogre::StaticGeometry*> mStaticGeometrySmall;
std::map<MWWorld::CellStore *, Ogre::AxisAlignedBox> mBounds;
std::vector<LightInfo> mLights;
Ogre::SceneNode* mMwRoot;
Ogre::SceneNode* mRootNode;
bool mIsStatic;
static int uniqueID;
@ -72,8 +73,8 @@ public:
Objects(OEngine::Render::OgreRenderer& renderer): mRenderer (renderer), mIsStatic(false) {}
~Objects(){}
void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_);
void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh);
void insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius);
void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light=false);
void insertLight (const MWWorld::Ptr& ptr, Ogre::Entity *skelBase=0, Ogre::Vector3 fallbackCenter=Ogre::Vector3(0.0f));
void enableLights();
void disableLights();
@ -89,12 +90,12 @@ public:
void removeCell(MWWorld::CellStore* store);
void buildStaticGeometry(MWWorld::CellStore &cell);
void setMwRoot(Ogre::SceneNode* root);
void setRootNode(Ogre::SceneNode* root);
void rebuildStaticGeometry();
/// Updates containing cell for object rendering data
void updateObjectCell(const MWWorld::Ptr &ptr);
void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur);
};
}
#endif

View file

@ -113,11 +113,6 @@ namespace MWRender
Ogre::Vector3 dir = mCamera->getRealDirection();
Ogre::Vector3 up = mCamera->getRealUp();
Ogre::Real xch;
xch = pos.y, pos.y = -pos.z, pos.z = xch;
xch = dir.y, dir.y = -dir.z, dir.z = xch;
xch = up.y, up.y = -up.z, up.z = xch;
MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up);
}
@ -129,14 +124,8 @@ namespace MWRender
MWBase::Environment::get().getWindowManager ()->showCrosshair
(!MWBase::Environment::get().getWindowManager ()->isGuiMode () && (mFirstPersonView && !mVanity.enabled && !mPreviewMode));
if (mAnimation) {
mAnimation->runAnimation(duration);
}
mPlayerNode->setVisible(
mVanity.enabled || mPreviewMode || !mFirstPersonView,
false
);
/// \fixme We shouldn't hide the whole model, just certain components of the character (head, chest, feet, etc)
mPlayerNode->setVisible(mVanity.enabled || mPreviewMode || !mFirstPersonView);
if (mFirstPersonView && !mVanity.enabled) {
return;
}
@ -313,10 +302,7 @@ namespace MWRender
delete mAnimation;
mAnimation = anim;
mPlayerNode->setVisible(
mVanity.enabled || mPreviewMode || !mFirstPersonView,
false
);
mPlayerNode->setVisible(mVanity.enabled || mPreviewMode || !mFirstPersonView);
}
void Player::setHeight(float height)
@ -332,10 +318,8 @@ namespace MWRender
bool Player::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera)
{
float xch;
mCamera->getParentSceneNode ()->needUpdate(true);
camera = mCamera->getRealPosition();
xch = camera.z, camera.z = camera.y, camera.y = -xch;
player = mPlayerNode->getPosition();
return mFirstPersonView && !mVanity.enabled && !mPreviewMode;

View file

@ -95,7 +95,9 @@ namespace MWRender
/// Restore default camera distance for current mode.
void setCameraDistance();
void setAnimation(MWRender::NpcAnimation *anim);
void setAnimation(NpcAnimation *anim);
NpcAnimation *getAnimation() const
{ return mAnimation; }
void setHeight(float height);
float getHeight();

View file

@ -18,9 +18,12 @@
#include <extern/shiny/Main/Factory.hpp>
#include <extern/shiny/Platforms/Ogre/OgrePlatform.hpp>
#include <openengine/bullet/physic.hpp>
#include <components/esm/loadstat.hpp>
#include "../mwworld/esmstore.hpp"
#include <components/settings/settings.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone
#include "../mwbase/environment.hpp"
@ -138,26 +141,20 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
applyCompositors();
// Turn the entire scene (represented by the 'root' node) -90
// degrees around the x axis. This makes Z go upwards, and Y go into
// the screen (when x is to the right.) This is the orientation that
// Morrowind uses, and it automagically makes everything work as it
// should.
SceneNode *rt = mRendering.getScene()->getRootSceneNode();
mMwRoot = rt->createChildSceneNode("mwRoot");
mMwRoot->pitch(Degree(-90));
mRootNode = rt;
mObjects.setMwRoot(mMwRoot);
mActors.setMwRoot(mMwRoot);
mObjects.setRootNode(mRootNode);
mActors.setRootNode(mRootNode);
Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player");
Ogre::SceneNode *playerNode = mRootNode->createChildSceneNode ("player");
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
mShadows = new Shadows(&mRendering);
mTerrainManager = new TerrainManager(mRendering.getScene(), this);
mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera());
mSkyManager = new SkyManager(mRootNode, mRendering.getCamera());
mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode());
@ -166,7 +163,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
mSun = 0;
mDebugging = new Debugging(mMwRoot, engine);
mDebugging = new Debugging(mRootNode, engine);
mLocalMap = new MWRender::LocalMap(&mRendering, this);
setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI"));
@ -252,8 +249,7 @@ void RenderingManager::removeObject (const MWWorld::Ptr& ptr)
void RenderingManager::moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position)
{
/// \todo move this to the rendering-subsystems
mRendering.getScene()->getSceneNode (ptr.getRefData().getHandle())->
setPosition (position);
ptr.getRefData().getBaseNode()->setPosition(position);
}
void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale)
@ -300,23 +296,19 @@ bool RenderingManager::rotateObject( const MWWorld::Ptr &ptr, Ogre::Vector3 &rot
}
void
RenderingManager::moveObjectToCell(
const MWWorld::Ptr& ptr,
const Ogre::Vector3& pos,
MWWorld::CellStore *store)
RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur)
{
Ogre::SceneNode *child =
mRendering.getScene()->getSceneNode(ptr.getRefData().getHandle());
mRendering.getScene()->getSceneNode(old.getRefData().getHandle());
Ogre::SceneNode *parent = child->getParentSceneNode();
parent->removeChild(child);
if (MWWorld::Class::get(ptr).isActor()) {
mActors.updateObjectCell(ptr);
if (MWWorld::Class::get(old).isActor()) {
mActors.updateObjectCell(old, cur);
} else {
mObjects.updateObjectCell(ptr);
mObjects.updateObjectCell(old, cur);
}
child->setPosition(pos);
}
void RenderingManager::update (float duration, bool paused)
@ -324,7 +316,7 @@ void RenderingManager::update (float duration, bool paused)
Ogre::Vector3 orig, dest;
mPlayer->setCameraDistance();
if (!mPlayer->getPosition(orig, dest)) {
orig.z += mPlayer->getHeight() * mMwRoot->getScale().z;
orig.z += mPlayer->getHeight() * mRootNode->getScale().z;
btVector3 btOrig(orig.x, orig.y, orig.z);
btVector3 btDest(dest.x, dest.y, dest.z);
@ -368,11 +360,13 @@ void RenderingManager::update (float duration, bool paused)
float *fpos = data.getPosition().pos;
// only for LocalMap::updatePlayer()
Ogre::Vector3 pos(fpos[0], -fpos[2], -fpos[1]);
Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]);
Ogre::SceneNode *node = data.getBaseNode();
//Ogre::Quaternion orient =
//node->convertLocalToWorldOrientation(node->_getDerivedOrientation());
Ogre::Quaternion orient =
node->convertLocalToWorldOrientation(node->_getDerivedOrientation());
node->_getDerivedOrientation();
mLocalMap->updatePlayer(pos, orient);
@ -383,8 +377,8 @@ void RenderingManager::update (float duration, bool paused)
mWater->updateUnderwater(
world->isUnderwater(
*world->getPlayer().getPlayer().getCell()->mCell,
Ogre::Vector3(cam.x, -cam.z, cam.y))
world->getPlayer().getPlayer().getCell(),
cam)
);
mWater->update(duration);
}
@ -580,17 +574,6 @@ void RenderingManager::toggleLight()
setAmbientMode();
}
void RenderingManager::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName,
int mode, int number)
{
mActors.playAnimationGroup(ptr, groupName, mode, number);
}
void RenderingManager::skipAnimation (const MWWorld::Ptr& ptr)
{
mActors.skipAnimation(ptr);
}
void RenderingManager::setSunColour(const Ogre::ColourValue& colour)
{
if (!mSunEnabled) return;
@ -627,8 +610,7 @@ void RenderingManager::sunDisable()
void RenderingManager::setSunDirection(const Ogre::Vector3& direction)
{
// direction * -1 (because 'direction' is camera to sun vector and not sun to camera),
// then convert from MW to ogre coordinates (swap y,z and make y negative)
if (mSun) mSun->setDirection(Vector3(-direction.x, -direction.z, direction.y));
if (mSun) mSun->setDirection(Vector3(-direction.x, -direction.y, -direction.z));
mSkyManager->setSunDirection(direction);
}
@ -929,6 +911,15 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend
rendering.setup (mRendering.getScene());
}
Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr)
{
Animation *anim = mActors.getAnimation(ptr);
if(!anim && ptr.getRefData().getHandle() == "player")
anim = mPlayer->getAnimation();
return anim;
}
void RenderingManager::playVideo(const std::string& name, bool allowSkipping)
{
mVideoPlayer->playVideo ("video/" + name, allowSkipping);

View file

@ -46,6 +46,7 @@ namespace MWRender
class ExternalRendering;
class GlobalMap;
class VideoPlayer;
class Animation;
class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener {
@ -122,9 +123,10 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
void setWaterHeight(const float height);
void toggleWater();
/// Moves object rendering part to proper container
/// \param store Cell the object was in previously (\a ptr has already been updated to the new cell).
void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::CellStore *store);
/// Updates object rendering after cell change
/// \param old Object reference in previous cell
/// \param cur Object reference in new cell
void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur);
void update (float duration, bool paused);
@ -166,18 +168,6 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
/// configure fog manually
void configureFog(const float density, const Ogre::ColourValue& colour);
void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode,
int number = 1);
///< Run animation for a MW-reference. Calls to this function for references that are currently not
/// in the rendered scene should be ignored.
///
/// \param mode: 0 normal, 1 immediate start, 2 immediate loop
/// \param number How offen the animation should be run
void skipAnimation (const MWWorld::Ptr& ptr);
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the rendered scene should be ignored.
Ogre::Vector4 boundingBoxToScreen(Ogre::AxisAlignedBox bounds);
///< transform the specified bounding box (in world coordinates) into screen coordinates.
/// @return packed vector4 (min_x, min_y, max_x, max_y)
@ -196,6 +186,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
void setupExternalRendering (MWRender::ExternalRendering& rendering);
Animation* getAnimation(const MWWorld::Ptr &ptr);
void playVideo(const std::string& name, bool allowSkipping);
void stopVideo();
@ -236,10 +228,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
Ogre::ColourValue mAmbientColor;
Ogre::Light* mSun;
/// Root node for all objects added to the scene. This is rotated so
/// that the OGRE coordinate system matches that used internally in
/// Morrowind.
Ogre::SceneNode *mMwRoot;
Ogre::SceneNode *mRootNode;
OEngine::Physic::PhysicEngine* mPhysicsEngine;

View file

@ -110,7 +110,7 @@ void BillboardObject::setPosition(const Vector3& pPosition)
Vector3 BillboardObject::getPosition() const
{
Vector3 p = mNode->_getDerivedPosition() - mNode->getParentSceneNode()->_getDerivedPosition();
return Vector3(p.x, -p.z, p.y);
return p;
}
void BillboardObject::setVisibilityFlags(int flags)
@ -203,7 +203,7 @@ unsigned int Moon::getPhaseInt() const
return 0;
}
SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera)
SkyManager::SkyManager (SceneNode* root, Camera* pCamera)
: mHour(0.0f)
, mDay(0)
, mMonth(0)
@ -234,9 +234,8 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera)
, mCloudAnimationTimer(0.f)
, mMoonRed(false)
{
mSceneMgr = pMwRoot->getCreator();
mSceneMgr = root->getCreator();
mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
mRootNode->pitch(Degree(-90)); // convert MW to ogre coordinates
mRootNode->setInheritOrientation(false);
}
@ -282,7 +281,7 @@ void SkyManager::create()
// Stars
mAtmosphereNight = mRootNode->createChildSceneNode();
NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(mAtmosphereNight, NULL, "meshes\\sky_night_01.nif");
NifOgre::EntityList entities = NifOgre::Loader::createEntities(mAtmosphereNight, "meshes\\sky_night_01.nif");
for(size_t i = 0, matidx = 0;i < entities.mEntities.size();i++)
{
Entity* night1_ent = entities.mEntities[i];
@ -307,26 +306,28 @@ void SkyManager::create()
// Atmosphere (day)
mAtmosphereDay = mRootNode->createChildSceneNode();
entities = NifOgre::NIFLoader::createEntities(mAtmosphereDay, NULL, "meshes\\sky_atmosphere.nif");
entities = NifOgre::Loader::createEntities(mAtmosphereDay, "meshes\\sky_atmosphere.nif");
for(size_t i = 0;i < entities.mEntities.size();i++)
{
Entity* atmosphere_ent = entities.mEntities[i];
atmosphere_ent->setCastShadows(false);
atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly);
atmosphere_ent->setVisibilityFlags(RV_Sky);
atmosphere_ent->getSubEntity (0)->setMaterialName ("openmw_atmosphere");
for(unsigned int j = 0;j < atmosphere_ent->getNumSubEntities();j++)
atmosphere_ent->getSubEntity (j)->setMaterialName("openmw_atmosphere");
}
// Clouds
SceneNode* clouds_node = mRootNode->createChildSceneNode();
entities = NifOgre::NIFLoader::createEntities(clouds_node, NULL, "meshes\\sky_clouds_01.nif");
entities = NifOgre::Loader::createEntities(clouds_node, "meshes\\sky_clouds_01.nif");
for(size_t i = 0;i < entities.mEntities.size();i++)
{
Entity* clouds_ent = entities.mEntities[i];
clouds_ent->setVisibilityFlags(RV_Sky);
clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5);
clouds_ent->getSubEntity(0)->setMaterialName ("openmw_clouds");
for(unsigned int j = 0;j < clouds_ent->getNumSubEntities();j++)
clouds_ent->getSubEntity(j)->setMaterialName("openmw_clouds");
clouds_ent->setCastShadows(false);
}
@ -389,7 +390,6 @@ void SkyManager::update(float duration)
// increase the strength of the sun glare effect depending
// on how directly the player is looking at the sun
Vector3 sun = mSunGlare->getPosition();
sun = Vector3(sun.x, sun.z, -sun.y);
Vector3 cam = mCamera->getRealDirection();
const Degree angle = sun.angleBetween( cam );
float val = 1- (angle.valueDegrees() / 180.f);

View file

@ -112,7 +112,7 @@ namespace MWRender
class SkyManager
{
public:
SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera);
SkyManager(Ogre::SceneNode* root, Ogre::Camera* pCamera);
~SkyManager();
void update(float duration);

View file

@ -26,7 +26,7 @@ namespace MWRender
//----------------------------------------------------------------------------------------------
TerrainManager::TerrainManager(Ogre::SceneManager* mgr, RenderingManager* rend) :
mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Z, mLandSize, mWorldSize)), mRendering(rend)
mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Y, mLandSize, mWorldSize)), mRendering(rend)
{
mTerrainGlobals = OGRE_NEW TerrainGlobalOptions();
@ -54,8 +54,8 @@ namespace MWRender
mTerrainGlobals->setCompositeMapDistance(mWorldSize*2);
mTerrainGroup.setOrigin(Vector3(mWorldSize/2,
0,
-mWorldSize/2));
mWorldSize/2,
0));
Terrain::ImportData& importSettings = mTerrainGroup.getDefaultImportSettings();

View file

@ -404,6 +404,8 @@ public:
*type = MWSound::SampleType_UInt8;
else if(mAVStream->codec->sample_fmt == AV_SAMPLE_FMT_S16)
*type = MWSound::SampleType_Int16;
else if(mAVStream->codec->sample_fmt == AV_SAMPLE_FMT_FLT)
*type = MWSound::SampleType_Float32;
else
fail(std::string("Unsupported sample format: ")+
av_get_sample_fmt_name(mAVStream->codec->sample_fmt));

View file

@ -42,9 +42,9 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel
mIsUnderwater = false;
mWaterPlane = Plane(Vector3::UNIT_Y, 0);
mWaterPlane = Plane(Vector3::UNIT_Z, 0);
MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, CELL_SIZE*5, CELL_SIZE * 5, 10, 10, true, 1, 3,3, Vector3::UNIT_Z);
MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, CELL_SIZE*5, CELL_SIZE * 5, 10, 10, true, 1, 3,3, Vector3::UNIT_Y);
mWater = mSceneManager->createEntity("water");
mWater->setVisibilityFlags(RV_Water);
@ -168,12 +168,12 @@ void Water::setHeight(const float height)
{
mTop = height;
mWaterPlane = Plane(Vector3::UNIT_Y, height);
mWaterPlane = Plane(Vector3::UNIT_Z, height);
// small error due to reflection texture size & reflection distortion
mErrorPlane = Plane(Vector3::UNIT_Y, height - 5);
mErrorPlane = Plane(Vector3::UNIT_Z, height - 5);
mWaterNode->setPosition(0, height, 0);
mWaterNode->setPosition(0, 0, height);
sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty<sh::FloatValue>(new sh::FloatValue(height)));
}
@ -199,7 +199,7 @@ Water::updateUnderwater(bool underwater)
Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY)
{
return Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), mTop, -gridY * CELL_SIZE - (CELL_SIZE / 2));
return Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), gridY * CELL_SIZE + (CELL_SIZE / 2), mTop);
}
void Water::preRenderTargetUpdate(const RenderTargetEvent& evt)
@ -216,7 +216,7 @@ void Water::preRenderTargetUpdate(const RenderTargetEvent& evt)
mReflectionRenderActive = true;
Vector3 pos = mCamera->getRealPosition();
pos.y = mTop*2 - pos.y;
pos.z = mTop*2 - pos.z;
mSky->setSkyPosition(pos);
mReflectionCamera->enableReflection(mWaterPlane);
}

View file

@ -9,7 +9,7 @@
#include <components/interpreter/runtime.hpp>
#include <components/interpreter/opcodes.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "interpretercontext.hpp"
#include "ref.hpp"
@ -27,7 +27,7 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
MWBase::Environment::get().getWorld()->skipAnimation (ptr);
MWBase::Environment::get().getMechanicsManager()->skipAnimation (ptr);
}
};
@ -54,7 +54,7 @@ namespace MWScript
throw std::runtime_error ("animation mode out of range");
}
MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, group, mode, 1);
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, 1);
}
};
@ -87,7 +87,7 @@ namespace MWScript
throw std::runtime_error ("animation mode out of range");
}
MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, group, mode, loops);
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, loops);
}
};

View file

@ -140,25 +140,42 @@ namespace MWScript
Compiler::Locals& ScriptManager::getLocals (const std::string& name)
{
ScriptCollection::iterator iter = mScripts.find (name);
if (iter==mScripts.end())
{
if (!compile (name))
{
/// \todo Handle case of cyclic member variable access. Currently this could look up
/// the whole application in an endless recursion.
ScriptCollection::iterator iter = mScripts.find (name);
// failed -> ignore script from now on.
std::vector<Interpreter::Type_Code> empty;
mScripts.insert (std::make_pair (name, std::make_pair (empty, Compiler::Locals())));
throw std::runtime_error ("failed to compile script " + name);
}
iter = mScripts.find (name);
if (iter!=mScripts.end())
return iter->second.second;
}
return iter->second.second;
{
std::map<std::string, Compiler::Locals>::iterator iter = mOtherLocals.find (name);
if (iter!=mOtherLocals.end())
return iter->second;
}
Compiler::Locals locals;
if (const ESM::Script *script = mStore.get<ESM::Script>().find (name))
{
int index = 0;
for (int i=0; i<script->mData.mNumShorts; ++i)
locals.declare ('s', script->mVarNames[index++]);
for (int i=0; i<script->mData.mNumLongs; ++i)
locals.declare ('l', script->mVarNames[index++]);
for (int i=0; i<script->mData.mNumFloats; ++i)
locals.declare ('f', script->mVarNames[index++]);
std::map<std::string, Compiler::Locals>::iterator iter =
mOtherLocals.insert (std::make_pair (name, locals)).first;
return iter->second;
}
throw std::logic_error ("script " + name + " does not exist");
}
GlobalScripts& ScriptManager::getGlobalScripts()

View file

@ -47,6 +47,7 @@ namespace MWScript
ScriptCollection mScripts;
GlobalScripts mGlobalScripts;
std::map<std::string, Compiler::Locals> mOtherLocals;
public:

View file

@ -134,6 +134,18 @@ size_t FFmpeg_Decoder::readAVAudioData(void *data, size_t length)
return dec;
}
static AVSampleFormat ffmpegNonPlanarSampleFormat (AVSampleFormat format)
{
switch (format)
{
case AV_SAMPLE_FMT_U8P: return AV_SAMPLE_FMT_U8;
case AV_SAMPLE_FMT_S16P: return AV_SAMPLE_FMT_S16;
case AV_SAMPLE_FMT_S32P: return AV_SAMPLE_FMT_S32;
case AV_SAMPLE_FMT_FLTP: return AV_SAMPLE_FMT_FLT;
case AV_SAMPLE_FMT_DBLP: return AV_SAMPLE_FMT_DBL;
default:return format;
}
}
void FFmpeg_Decoder::open(const std::string &fname)
{
@ -160,7 +172,6 @@ void FFmpeg_Decoder::open(const std::string &fname)
{
if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
mFormatCtx->streams[j]->codec->request_sample_fmt = AV_SAMPLE_FMT_S16;
mStream = &mFormatCtx->streams[j];
break;
}
@ -168,6 +179,8 @@ void FFmpeg_Decoder::open(const std::string &fname)
if(!mStream)
fail("No audio streams in "+fname);
(*mStream)->codec->request_sample_fmt = ffmpegNonPlanarSampleFormat ((*mStream)->codec->sample_fmt);
AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id);
if(!codec)
{
@ -220,6 +233,8 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
*type = SampleType_UInt8;
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16)
*type = SampleType_Int16;
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT)
*type = SampleType_Float32;
else
fail(std::string("Unsupported sample format: ")+
av_get_sample_fmt_name((*mStream)->codec->sample_fmt));

View file

@ -88,6 +88,51 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type)
}
}
}
if(alIsExtensionPresent("AL_EXT_FLOAT32"))
{
static const struct {
char name[32];
ChannelConfig chans;
SampleType type;
} fltfmtlist[] = {
{ "AL_FORMAT_MONO_FLOAT32", ChannelConfig_Mono, SampleType_Float32 },
{ "AL_FORMAT_STEREO_FLOAT32", ChannelConfig_Stereo, SampleType_Float32 },
};
static const size_t fltfmtlistsize = sizeof(fltfmtlist)/sizeof(fltfmtlist[0]);
for(size_t i = 0;i < fltfmtlistsize;i++)
{
if(fltfmtlist[i].chans == chans && fltfmtlist[i].type == type)
{
ALenum format = alGetEnumValue(fltfmtlist[i].name);
if(format != 0 && format != -1)
return format;
}
}
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
static const struct {
char name[32];
ChannelConfig chans;
SampleType type;
} fltmcfmtlist[] = {
{ "AL_FORMAT_QUAD32", ChannelConfig_Quad, SampleType_Float32 },
{ "AL_FORMAT_51CHN32", ChannelConfig_5point1, SampleType_Float32 },
{ "AL_FORMAT_71CHN32", ChannelConfig_7point1, SampleType_Float32 },
};
static const size_t fltmcfmtlistsize = sizeof(fltmcfmtlist)/sizeof(fltmcfmtlist[0]);
for(size_t i = 0;i < fltmcfmtlistsize;i++)
{
if(fltmcfmtlist[i].chans == chans && fltmcfmtlist[i].type == type)
{
ALenum format = alGetEnumValue(fltmcfmtlist[i].name);
if(format != 0 && format != -1)
return format;
}
}
}
}
fail(std::string("Unsupported sound format (")+getChannelConfigName(chans)+", "+getSampleTypeName(type)+")");
return AL_NONE;

View file

@ -9,7 +9,8 @@ namespace MWSound
{
enum SampleType {
SampleType_UInt8,
SampleType_Int16
SampleType_Int16,
SampleType_Float32
};
const char *getSampleTypeName(SampleType type);

View file

@ -607,6 +607,7 @@ namespace MWSound
{
case SampleType_UInt8: return "U8";
case SampleType_Int16: return "S16";
case SampleType_Float32: return "Float32";
}
return "(unknown sample type)";
}
@ -638,6 +639,7 @@ namespace MWSound
{
case SampleType_UInt8: frames *= 1; break;
case SampleType_Int16: frames *= 2; break;
case SampleType_Float32: frames *= 4; break;
}
return frames;
}

View file

@ -86,7 +86,7 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellS
MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader)
: mStore (store), mReader (reader),
mIdCache (20, std::pair<std::string, Ptr::CellStore *> ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable
mIdCache (40, std::pair<std::string, Ptr::CellStore *> ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable
mIdCacheIndex (0)
{}

View file

@ -122,6 +122,11 @@ namespace MWWorld
return 0;
}
float Class::getJump (const Ptr& ptr) const
{
return 0;
}
MWMechanics::Movement& Class::getMovementSettings (const Ptr& ptr) const
{
throw std::runtime_error ("movement settings not supported by class");

View file

@ -140,6 +140,9 @@ namespace MWWorld
virtual float getSpeed (const Ptr& ptr) const;
///< Return movement speed.
virtual float getJump(const MWWorld::Ptr &ptr) const;
///< Return jump velocity (not accounting for movement)
virtual MWMechanics::Movement& getMovementSettings (const Ptr& ptr) const;
///< Return desired movement.

View file

@ -9,6 +9,10 @@
#include <OgreCamera.h>
#include <OgreTextureManager.h>
#include <openengine/bullet/trace.h>
#include <openengine/bullet/physic.hpp>
#include <openengine/ogre/renderer.hpp>
#include <components/nifbullet/bullet_nif_loader.hpp>
//#include "../mwbase/world.hpp" // FIXME
@ -21,23 +25,191 @@ using namespace Ogre;
namespace MWWorld
{
PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) :
mRender(_rend), mEngine(0), mFreeFly (true)
{
static const float sMaxSlope = 60.0f;
static const float sStepSize = 30.0f;
// Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared.
static const int sMaxIterations = 50;
playerphysics = new playerMove;
class MovementSolver
{
private:
static bool stepMove(Ogre::Vector3& position, const Ogre::Vector3 &velocity, float remainingTime,
const Ogre::Vector3 &halfExtents, bool isInterior,
OEngine::Physic::PhysicEngine *engine)
{
traceResults trace; // no initialization needed
newtrace(&trace, position, position+Ogre::Vector3(0.0f,0.0f,sStepSize),
halfExtents, isInterior, engine);
if(trace.fraction == 0.0f)
return false;
newtrace(&trace, trace.endpos, trace.endpos + velocity*remainingTime,
halfExtents, isInterior, engine);
if(trace.fraction == 0.0f || (trace.fraction != 1.0f && getSlope(trace.planenormal) > sMaxSlope))
return false;
newtrace(&trace, trace.endpos, trace.endpos-Ogre::Vector3(0.0f,0.0f,sStepSize), halfExtents, isInterior, engine);
if(getSlope(trace.planenormal) <= sMaxSlope)
{
// only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall.
position = trace.endpos;
return true;
}
return false;
}
static void clipVelocity(Ogre::Vector3& inout, const Ogre::Vector3& normal, float overbounce=1.0f)
{
//Math stuff. Basically just project the velocity vector onto the plane represented by the normal.
//More specifically, it projects velocity onto the normal, takes that result, multiplies it by overbounce and then subtracts it from velocity.
float backoff = inout.dotProduct(normal);
if(backoff < 0.0f)
backoff *= overbounce;
else
backoff /= overbounce;
inout -= normal*backoff;
}
static void projectVelocity(Ogre::Vector3& velocity, const Ogre::Vector3& direction)
{
Ogre::Vector3 normalizedDirection(direction);
normalizedDirection.normalise();
// no divide by normalizedDirection.length necessary because it's normalized
velocity = normalizedDirection * velocity.dotProduct(normalizedDirection);
}
static float getSlope(const Ogre::Vector3 &normal)
{
return normal.angleBetween(Ogre::Vector3(0.0f,0.0f,1.0f)).valueDegrees();
}
public:
static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time,
bool gravity, OEngine::Physic::PhysicEngine *engine)
{
const ESM::Position &refpos = ptr.getRefData().getPosition();
Ogre::Vector3 position(refpos.pos);
/* Anything to collide with? */
OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle());
if(!physicActor || !physicActor->getCollisionMode())
{
// 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)) *
movement;
}
traceResults trace; //no initialization needed
bool onground = false;
float remainingTime = time;
bool isInterior = !ptr.getCell()->isExterior();
Ogre::Vector3 halfExtents = physicActor->getHalfExtents();
Ogre::Vector3 velocity;
if(!gravity)
{
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)) *
movement / time;
}
else
{
if(!(movement.z > 0.0f))
{
newtrace(&trace, position, position-Ogre::Vector3(0,0,4), halfExtents, isInterior, engine);
if(trace.fraction < 1.0f && getSlope(trace.planenormal) <= sMaxSlope)
onground = true;
}
velocity = Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z) *
movement / time;
velocity.z += physicActor->getVerticalForce();
}
Ogre::Vector3 clippedVelocity(velocity);
if(onground)
{
// if we're on the ground, force velocity to track it
clippedVelocity.z = velocity.z = std::max(0.0f, velocity.z);
clipVelocity(clippedVelocity, trace.planenormal);
}
const Ogre::Vector3 up(0.0f, 0.0f, 1.0f);
Ogre::Vector3 newPosition = position;
int iterations = 0;
do {
// trace to where character would go if there were no obstructions
newtrace(&trace, newPosition, newPosition+clippedVelocity*remainingTime, halfExtents, isInterior, engine);
newPosition = trace.endpos;
remainingTime = remainingTime * (1.0f-trace.fraction);
// check for obstructions
if(trace.fraction < 1.0f)
{
//std::cout<<"angle: "<<getSlope(trace.planenormal)<<"\n";
if(getSlope(trace.planenormal) <= sMaxSlope)
{
// We hit a slope we can walk on. Update velocity accordingly.
clipVelocity(clippedVelocity, trace.planenormal);
// We're only on the ground if gravity is affecting us
onground = gravity;
}
else
{
// Can't walk on this. Try to step up onto it.
if((gravity && !onground) ||
!stepMove(newPosition, velocity, remainingTime, halfExtents, isInterior, engine))
{
Ogre::Vector3 resultantDirection = trace.planenormal.crossProduct(up);
resultantDirection.normalise();
clippedVelocity = velocity;
projectVelocity(clippedVelocity, resultantDirection);
// just this isn't enough sometimes. It's the same problem that causes steps to be necessary on even uphill terrain.
clippedVelocity += trace.planenormal*clippedVelocity.length()/50.0f;
}
}
}
iterations++;
} while(iterations < sMaxIterations && remainingTime > 0.0f);
if(onground)
{
newtrace(&trace, newPosition, newPosition-Ogre::Vector3(0,0,sStepSize+4.0f), halfExtents, isInterior, engine);
if(trace.fraction < 1.0f && getSlope(trace.planenormal) <= sMaxSlope)
newPosition.z = trace.endpos.z + 2.0f;
else
onground = false;
}
physicActor->setOnGround(onground);
physicActor->setVerticalForce(!onground ? clippedVelocity.z - time*627.2f : 0.0f);
return newPosition;
}
};
PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) :
mRender(_rend), mEngine(0)
{
// Create physics. shapeLoader is deleted by the physic engine
NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader();
mEngine = new OEngine::Physic::PhysicEngine(shapeLoader);
playerphysics->mEngine = mEngine;
}
PhysicsSystem::~PhysicsSystem()
{
delete mEngine;
delete playerphysics;
}
OEngine::Physic::PhysicEngine* PhysicsSystem::getEngine()
{
return mEngine;
@ -92,9 +264,8 @@ namespace MWWorld
Ogre::Vector3 to = ray.getPoint(queryDistance);
btVector3 _from, _to;
// OGRE to MW coordinates
_from = btVector3(from.x, -from.z, from.y);
_to = btVector3(to.x, -to.z, to.y);
_from = btVector3(from.x, from.y, from.z);
_to = btVector3(to.x, to.y, to.z);
std::vector < std::pair <float, std::string> > results;
/* auto */ results = mEngine->rayTest2(_from,_to);
@ -106,15 +277,7 @@ namespace MWWorld
void PhysicsSystem::setCurrentWater(bool hasWater, int waterHeight)
{
playerphysics->hasWater = hasWater;
if(hasWater){
playerphysics->waterHeight = waterHeight;
}
for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++)
{
it->second->setCurrentWater(hasWater, waterHeight);
}
// TODO: store and use
}
btVector3 PhysicsSystem::getRayPoint(float extent)
@ -123,7 +286,7 @@ namespace MWWorld
Ray centerRay = mRender.getCamera()->getCameraToViewportRay(
mRender.getViewport()->getWidth()/2,
mRender.getViewport()->getHeight()/2);
btVector3 result(centerRay.getPoint(extent).x,-centerRay.getPoint(extent).z,centerRay.getPoint(extent).y);
btVector3 result(centerRay.getPoint(extent).x,centerRay.getPoint(extent).y,centerRay.getPoint(extent).z);
return result;
}
@ -131,7 +294,7 @@ namespace MWWorld
{
//get a ray pointing to the center of the viewport
Ray centerRay = mRender.getCamera()->getCameraToViewportRay(mouseX, mouseY);
btVector3 result(centerRay.getPoint(extent).x,-centerRay.getPoint(extent).z,centerRay.getPoint(extent).y);
btVector3 result(centerRay.getPoint(extent).x,centerRay.getPoint(extent).y,centerRay.getPoint(extent).z);
return result;
}
@ -171,9 +334,8 @@ namespace MWWorld
Ogre::Vector3 to = ray.getPoint(200); /// \todo make this distance (ray length) configurable
btVector3 _from, _to;
// OGRE to MW coordinates
_from = btVector3(from.x, -from.z, from.y);
_to = btVector3(to.x, -to.z, to.y);
_from = btVector3(from.x, from.y, from.z);
_to = btVector3(to.x, to.y, to.z);
std::pair<std::string, float> result = mEngine->rayTest(_from, _to);
@ -185,71 +347,11 @@ namespace MWWorld
}
}
void PhysicsSystem::doPhysics(float dt, const std::vector<std::pair<std::string, Ogre::Vector3> >& actors)
Ogre::Vector3 PhysicsSystem::move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity)
{
//set the DebugRenderingMode. To disable it,set it to 0
//eng->setDebugRenderingMode(1);
//set the movement keys to 0 (no movement) for every actor)
for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++)
{
OEngine::Physic::PhysicActor* act = it->second;
act->setMovement(0,0,0);
}
playerMove::playercmd& pm_ref = playerphysics->cmd;
pm_ref.rightmove = 0;
pm_ref.forwardmove = 0;
pm_ref.upmove = 0;
//playerphysics->ps.move_type = PM_NOCLIP;
for (std::vector<std::pair<std::string, Ogre::Vector3> >::const_iterator iter (actors.begin());
iter!=actors.end(); ++iter)
{
//dirty stuff to get the camera orientation. Must be changed!
if (iter->first == "player") {
playerphysics->ps.viewangles.x =
Ogre::Radian(mPlayerData.pitch).valueDegrees();
playerphysics->ps.viewangles.y =
Ogre::Radian(mPlayerData.yaw).valueDegrees() + 90;
pm_ref.rightmove = iter->second.x;
pm_ref.forwardmove = -iter->second.y;
pm_ref.upmove = iter->second.z;
}
}
mEngine->stepSimulation(dt);
return MovementSolver::move(ptr, movement, time, gravity, mEngine);
}
std::vector< std::pair<std::string, Ogre::Vector3> > PhysicsSystem::doPhysicsFixed (
const std::vector<std::pair<std::string, Ogre::Vector3> >& actors)
{
Pmove(playerphysics);
std::vector< std::pair<std::string, Ogre::Vector3> > response;
for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++)
{
Ogre::Vector3 coord = it->second->getPosition();
if(it->first == "player"){
coord = playerphysics->ps.origin ;
}
response.push_back(std::pair<std::string, Ogre::Vector3>(it->first, coord));
}
return response;
}
void PhysicsSystem::addHeightField (float* heights,
int x, int y, float yoffset,
@ -291,46 +393,20 @@ namespace MWWorld
void PhysicsSystem::moveObject (const Ptr& ptr)
{
Ogre::SceneNode* node = ptr.getRefData().getBaseNode();
std::string handle = node->getName();
Ogre::Vector3 position = node->getPosition();
if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle))
{
// TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow
// start positions others than 0, 0, 0
if(dynamic_cast<btBoxShape*>(body->getCollisionShape()) == NULL){
btTransform tr = body->getWorldTransform();
tr.setOrigin(btVector3(position.x,position.y,position.z));
body->setWorldTransform(tr);
}
else{
//For objects that contain a box shape.
//Do any such objects exist? Perhaps animated objects?
mEngine->boxAdjustExternal(handleToMesh[handle], body, node->getScale().x, position, node->getOrientation());
}
}
if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle))
{
// TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow
// start positions others than 0, 0, 0
if (handle == "player")
{
playerphysics->ps.origin = position;
}
else
{
act->setPosition(position);
}
}
Ogre::SceneNode *node = ptr.getRefData().getBaseNode();
const std::string &handle = node->getName();
const Ogre::Vector3 &position = node->getPosition();
if(OEngine::Physic::RigidBody *body = mEngine->getRigidBody(handle))
body->getWorldTransform().setOrigin(btVector3(position.x,position.y,position.z));
else if(OEngine::Physic::PhysicActor *physact = mEngine->getCharacter(handle))
physact->setPosition(position);
}
void PhysicsSystem::rotateObject (const Ptr& ptr)
{
Ogre::SceneNode* node = ptr.getRefData().getBaseNode();
std::string handle = node->getName();
Ogre::Quaternion rotation = node->getOrientation();
const std::string &handle = node->getName();
const Ogre::Quaternion &rotation = node->getOrientation();
if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle))
{
//Needs to be changed
@ -348,7 +424,7 @@ namespace MWWorld
void PhysicsSystem::scaleObject (const Ptr& ptr)
{
Ogre::SceneNode* node = ptr.getRefData().getBaseNode();
std::string handle = node->getName();
const std::string &handle = node->getName();
if(handleToMesh.find(handle) != handleToMesh.end())
{
removeObject(handle);
@ -361,7 +437,6 @@ namespace MWWorld
bool PhysicsSystem::toggleCollisionMode()
{
playerphysics->ps.move_type = (playerphysics->ps.move_type == PM_NOCLIP ? PM_NORMAL : PM_NOCLIP);
for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++)
{
if (it->first=="player")
@ -372,12 +447,10 @@ namespace MWWorld
if(cmode)
{
act->enableCollisions(false);
mFreeFly = true;
return false;
}
else
{
mFreeFly = false;
act->enableCollisions(true);
return true;
}

View file

@ -1,12 +1,27 @@
#ifndef GAME_MWWORLD_PHYSICSSYSTEM_H
#define GAME_MWWORLD_PHYSICSSYSTEM_H
#include <openengine/ogre/renderer.hpp>
#include "ptr.hpp"
#include <openengine/bullet/pmove.h>
#include <OgreVector3.h>
#include <btBulletCollisionCommon.h>
namespace OEngine
{
namespace Render
{
class OgreRenderer;
}
namespace Physic
{
class PhysicEngine;
}
}
namespace MWWorld
{
class World;
class Ptr;
class PhysicsSystem
{
@ -14,12 +29,6 @@ namespace MWWorld
PhysicsSystem (OEngine::Render::OgreRenderer &_rend);
~PhysicsSystem ();
void doPhysics(float duration, const std::vector<std::pair<std::string, Ogre::Vector3> >& actors);
///< do physics with dt - Usage: first call doPhysics with frame dt, then call doPhysicsFixed as often as time steps have passed
std::vector< std::pair<std::string, Ogre::Vector3> > doPhysicsFixed (const std::vector<std::pair<std::string, Ogre::Vector3> >& actors);
///< do physics with fixed timestep - Usage: first call doPhysics with frame dt, then call doPhysicsFixed as often as time steps have passed
void addObject (const MWWorld::Ptr& ptr);
void addActor (const MWWorld::Ptr& ptr);
@ -41,6 +50,8 @@ namespace MWWorld
bool toggleCollisionMode();
Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity);
std::pair<float, std::string> getFacedHandle (MWWorld::World& world, float queryDistance);
std::vector < std::pair <float, std::string> > getFacedHandles (float queryDistance);
std::vector < std::pair <float, std::string> > getFacedHandles (float mouseX, float mouseY, float queryDistance);
@ -74,8 +85,6 @@ namespace MWWorld
OEngine::Render::OgreRenderer &mRender;
OEngine::Physic::PhysicEngine* mEngine;
bool mFreeFly;
playerMove* playerphysics;
std::map<std::string, std::string> handleToMesh;
PhysicsSystem (const PhysicsSystem&);

View file

@ -71,6 +71,12 @@ namespace MWWorld
MWWorld::Class::get (ptr).getMovementSettings (ptr).mUpDown = value;
}
void Player::setRunState(bool run)
{
MWWorld::Ptr ptr = getPlayer();
MWWorld::Class::get(ptr).setStance(ptr, MWWorld::Class::Run, run);
}
void Player::toggleRunning()
{
MWWorld::Ptr ptr = getPlayer();

View file

@ -65,6 +65,7 @@ namespace MWWorld
void setForwardBackward (int value);
void setUpDown(int value);
void setRunState(bool run);
void toggleRunning();
};
}

View file

@ -1,5 +1,6 @@
#include "scene.hpp"
#include <components/nif/nif_file.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" /// FIXME
@ -7,9 +8,11 @@
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "physicssystem.hpp"
#include "player.hpp"
#include "localscripts.hpp"
#include "esmstore.hpp"
#include "class.hpp"
#include "cellfunctors.hpp"
@ -24,13 +27,10 @@ namespace
{
const MWWorld::Class& class_ =
MWWorld::Class::get (MWWorld::Ptr (&*cellRefList.mList.begin(), &cell));
int numRefs = cellRefList.mList.size();
int current = 0;
for (typename T::List::iterator it = cellRefList.mList.begin();
it != cellRefList.mList.end(); it++)
{
MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 1, current, numRefs);
++current;
if (it->mData.getCount() || it->mData.isEnabled())
@ -52,10 +52,6 @@ namespace
}
}
}
else
{
MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 1, 0, 1);
}
}
}
@ -98,7 +94,7 @@ namespace MWWorld
//mPhysics->removeObject("Unnamed_43");
MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter);
MWBase::Environment::get().getMechanicsManager()->dropActors (*iter);
MWBase::Environment::get().getMechanicsManager()->drop (*iter);
MWBase::Environment::get().getSoundManager()->stopSound (*iter);
mActiveCells.erase(*iter);
}
@ -164,7 +160,7 @@ namespace MWWorld
MWBase::MechanicsManager *mechMgr =
MWBase::Environment::get().getMechanicsManager();
mechMgr->addActor(player);
mechMgr->add(player);
mechMgr->watchActor(player);
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
@ -173,11 +169,17 @@ namespace MWWorld
void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos)
{
Nif::NIFFile::CacheLock cachelock;
const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
mRendering.preCellChange(mCurrentCell);
// remove active
MWBase::Environment::get().getMechanicsManager()->removeActor (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
MWBase::Environment::get().getMechanicsManager()->remove(MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
std::string loadingExteriorText;
loadingExteriorText = gmst.find ("sLoadingMessage3")->getString();
CellStoreCollection::iterator active = mActiveCells.begin();
@ -213,8 +215,6 @@ namespace MWWorld
continue;
}
}
MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Unloading cells", 0, current, numUnload);
unloadCell (active++);
++current;
}
@ -263,7 +263,9 @@ namespace MWWorld
{
CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y);
MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 0, current, numLoad);
//Loading Exterior loading text
MWBase::Environment::get().getWindowManager ()->setLoadingProgress (loadingExteriorText, 0, current, numLoad);
loadCell (cell);
++current;
}
@ -322,6 +324,13 @@ namespace MWWorld
void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position)
{
const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
std::string loadingInteriorText;
loadingInteriorText = gmst.find ("sLoadingMessage2")->getString();
CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName);
bool loadcell = (mCurrentCell == NULL);
if(!loadcell)
@ -357,8 +366,6 @@ namespace MWWorld
active = mActiveCells.begin();
while (active!=mActiveCells.end())
{
MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Unloading cells", 0, current, numUnload);
unloadCell (active++);
++current;
}
@ -366,7 +373,9 @@ namespace MWWorld
// Load cell.
std::cout << "cellName: " << cell->mCell->mName << std::endl;
MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 0, 0, 1);
//Loading Interior loading text
MWBase::Environment::get().getWindowManager ()->setLoadingProgress (loadingInteriorText, 0, 0, 1);
loadCell (cell);
mCurrentCell = cell;
@ -441,7 +450,7 @@ namespace MWWorld
void Scene::removeObjectFromScene (const Ptr& ptr)
{
MWBase::Environment::get().getMechanicsManager()->removeActor (ptr);
MWBase::Environment::get().getMechanicsManager()->remove (ptr);
MWBase::Environment::get().getSoundManager()->stopSound3D (ptr);
mPhysics->removeObject (ptr.getRefData().getHandle());
mRendering.removeObject (ptr);

View file

@ -3,7 +3,7 @@
#include "../mwrender/renderingmanager.hpp"
#include "physicssystem.hpp"
#include "ptr.hpp"
#include "globals.hpp"
namespace Ogre
@ -34,9 +34,9 @@ namespace MWRender
namespace MWWorld
{
class PhysicsSystem;
class Player;
class CellStore;
class Ptr;
class Scene
{

View file

@ -17,9 +17,9 @@ namespace MWWorld
virtual void setUp() {}
virtual void listIdentifier(std::vector<std::string> &list) const {}
virtual int getSize() const = 0;
virtual size_t getSize() const = 0;
virtual void load(ESM::ESMReader &esm, const std::string &id) = 0;
virtual bool eraseStatic(const std::string &id) {return false;}
};
@ -110,7 +110,7 @@ namespace MWWorld
item.mId = Misc::StringUtils::lowerCase(id);
typename std::map<std::string, T>::const_iterator it = mStatic.find(item.mId);
if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) {
return &(it->second);
}
@ -158,7 +158,7 @@ namespace MWWorld
return mShared.end();
}
int getSize() const {
size_t getSize() const {
return mShared.size();
}
@ -188,14 +188,14 @@ namespace MWWorld
item.mId = Misc::StringUtils::lowerCase(id);
typename std::map<std::string, T>::iterator it = mStatic.find(item.mId);
if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) {
mStatic.erase(it);
}
return true;
}
bool erase(const std::string &id) {
std::string key = Misc::StringUtils::lowerCase(id);
typename Dynamic::iterator it = mDynamic.find(key);
@ -220,9 +220,15 @@ namespace MWWorld
template <>
inline void Store<ESM::Dialogue>::load(ESM::ESMReader &esm, const std::string &id) {
std::string idLower = Misc::StringUtils::lowerCase(id);
mStatic[idLower] = ESM::Dialogue();
mStatic[idLower].mId = id; // don't smash case here, as this line is printed... I think
mStatic[idLower].load(esm);
std::map<std::string, ESM::Dialogue>::iterator it = mStatic.find(idLower);
if (it == mStatic.end()) {
it = mStatic.insert( std::make_pair( idLower, ESM::Dialogue() ) ).first;
it->second.mId = id; // don't smash case here, as this line is printed... I think
}
//I am not sure is it need to load the dialog from a plugin if it was already loaded from prevois plugins
it->second.load(esm);
}
template <>
@ -269,11 +275,11 @@ namespace MWWorld
return ptr;
}
int getSize() const {
size_t getSize() const {
return mStatic.size();
}
int getSize(size_t plugin) const {
size_t getSize(size_t plugin) const {
assert(plugin < mStatic.size());
return mStatic[plugin].size();
}
@ -338,7 +344,7 @@ namespace MWWorld
}
int getSize() const {
size_t getSize() const {
return mStatic.size();
}
@ -409,7 +415,7 @@ namespace MWWorld
DynamicInt mDynamicInt;
DynamicExt mDynamicExt;
const ESM::Cell *search(const ESM::Cell &cell) const {
if (cell.isExterior()) {
return search(cell.getGridX(), cell.getGridY());
@ -481,7 +487,7 @@ namespace MWWorld
newCell->mData.mY = y;
mExt[std::make_pair(x, y)] = *newCell;
delete newCell;
return &mExt[std::make_pair(x, y)];
}
@ -528,7 +534,7 @@ namespace MWWorld
// There some nasty three-way cyclic header dependency involved, which I could only fix by moving
// this method.
void load(ESM::ESMReader &esm, const std::string &id);
iterator intBegin() const {
return iterator(mSharedInt.begin());
}
@ -567,7 +573,7 @@ namespace MWWorld
return 0;
}
int getSize() const {
size_t getSize() const {
return mSharedInt.size() + mSharedExt.size();
}
@ -701,7 +707,7 @@ namespace MWWorld
mStatic.back().load(esm);
}
int getSize() const {
size_t getSize() const {
return mStatic.size();
}
@ -930,7 +936,7 @@ namespace MWWorld
}
}
int getSize() const {
size_t getSize() const {
return mStatic.size();
}

View file

@ -1,5 +1,7 @@
#include "worldimp.hpp"
#include <libs/openengine/bullet/physic.hpp>
#include <components/bsa/bsa_archive.hpp>
#include <components/files/collections.hpp>
#include <components/compiler/locals.hpp>
@ -10,6 +12,8 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/scriptmanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwrender/sky.hpp"
#include "../mwrender/player.hpp"
@ -18,6 +22,7 @@
#include "player.hpp"
#include "manualref.hpp"
#include "cellfunctors.hpp"
#include "containerstore.hpp"
using namespace Ogre;
@ -183,6 +188,8 @@ namespace MWWorld
mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine);
mPhysEngine->setSceneManager(renderer.getScene());
mWeatherManager = new MWWorld::WeatherManager(mRendering);
int idx = 0;
@ -732,6 +739,7 @@ namespace MWWorld
removeContainerScripts(ptr);
if (isPlayer)
{
if (!newCell.isExterior())
changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.mCell->mName), pos);
else
@ -740,13 +748,18 @@ namespace MWWorld
int cellY = newCell.mCell->getGridY();
mWorldScene->changeCell(cellX, cellY, pos, false);
}
}
else
{
if (!mWorldScene->isCellActive(*currCell))
copyObjectToCell(ptr, newCell, pos);
else if (!mWorldScene->isCellActive(newCell))
{
MWWorld::Class::get(ptr).copyToCell(ptr, newCell);
MWWorld::Class::get(ptr)
.copyToCell(ptr, newCell)
.getRefData()
.setBaseNode(0);
mWorldScene->removeObjectFromScene(ptr);
mLocalScripts.remove(ptr);
removeContainerScripts (ptr);
@ -757,7 +770,10 @@ namespace MWWorld
MWWorld::Ptr copy =
MWWorld::Class::get(ptr).copyToCell(ptr, newCell);
mRendering->moveObjectToCell(copy, vec, currCell);
mRendering->updateObjectCell(ptr, copy);
MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager();
mechMgr->updateCell(ptr, copy);
std::string script =
MWWorld::Class::get(ptr).getScript(ptr);
@ -768,15 +784,6 @@ namespace MWWorld
mLocalScripts.add(script, copy);
addContainerScripts (copy, &newCell);
}
if (MWWorld::Class::get(ptr).isActor())
{
MWBase::MechanicsManager *mechMgr =
MWBase::Environment::get().getMechanicsManager();
mechMgr->removeActor(ptr);
mechMgr->addActor(copy);
}
}
ptr.getRefData().setCount(0);
}
@ -825,16 +832,16 @@ namespace MWWorld
rot.y = Ogre::Degree(y).valueRadians();
rot.z = Ogre::Degree(z).valueRadians();
float *objRot = ptr.getRefData().getPosition().rot;
if(ptr.getRefData().getBaseNode() == 0 || !mRendering->rotateObject(ptr, rot, adjust))
if (mRendering->rotateObject(ptr, rot, adjust))
{
objRot[0] = (adjust ? objRot[0] + rot.x : rot.x), objRot[1] = (adjust ? objRot[1] + rot.y : rot.y), objRot[2] = (adjust ? objRot[2] + rot.z : rot.z);
return;
}
// rotate physically iff renderer confirm so
float *objRot = ptr.getRefData().getPosition().rot;
objRot[0] = rot.x, objRot[1] = rot.y, objRot[2] = rot.z;
// do this after rendering rotated the object so it gets changed by Class->adjustRotation
objRot[0] = rot.x, objRot[1] = rot.y, objRot[2] = rot.z;
mPhysics->rotateObject(ptr);
if (ptr.getRefData().getBaseNode() != 0) {
mPhysics->rotateObject(ptr);
}
}
}
void World::safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos)
@ -871,53 +878,33 @@ namespace MWWorld
--cellY;
}
void World::doPhysics (const std::vector<std::pair<std::string, Ogre::Vector3> >& actors,
float duration)
void World::doPhysics(const PtrMovementList &actors, float duration)
{
mPhysics->doPhysics(duration, actors);
/* No duration? Shouldn't be any movement, then. */
if(duration <= 0.0f)
return;
const int tick = 16; // 16 ms ^= 60 Hz
// Game clock part of the loop, contains everything that has to be executed in a fixed timestep
long long dt = mTimer.getMilliseconds() - lastTick;
if (dt >= 100)
PtrMovementList::const_iterator player(actors.end());
for(PtrMovementList::const_iterator iter(actors.begin());iter != actors.end();iter++)
{
// throw away wall clock time if necessary to keep the framerate above the minimum of 10 fps
lastTick += (dt - 100);
dt = 100;
if(iter->first.getRefData().getHandle() == "player")
{
/* Handle player last, in case a cell transition occurs */
player = iter;
continue;
}
Ogre::Vector3 vec = mPhysics->move(iter->first, iter->second, duration,
!isSwimming(iter->first) && !isFlying(iter->first));
moveObjectImp(iter->first, vec.x, vec.y, vec.z);
}
while (dt >= tick)
if(player != actors.end())
{
dt -= tick;
lastTick += tick;
std::vector< std::pair<std::string, Ogre::Vector3> > vectors = mPhysics->doPhysicsFixed (actors);
std::vector< std::pair<std::string, Ogre::Vector3> >::iterator player = vectors.end();
for (std::vector< std::pair<std::string, Ogre::Vector3> >::iterator it = vectors.begin();
it!= vectors.end(); ++it)
{
if (it->first=="player")
{
player = it;
}
else
{
MWWorld::Ptr ptr = getPtrViaHandle (it->first);
moveObjectImp (ptr, it->second.x, it->second.y, it->second.z);
}
}
// Make sure player is moved last (otherwise the cell might change in the middle of an update
// loop)
if (player!=vectors.end())
{
if (moveObjectImp (getPtrViaHandle (player->first),
player->second.x, player->second.y, player->second.z) == true)
return; // abort the current loop if the cell has changed
}
Ogre::Vector3 vec = mPhysics->move(player->first, player->second, duration,
!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
mPhysEngine->stepSimulation (duration);
}
bool World::toggleCollisionMode()
@ -985,17 +972,6 @@ namespace MWWorld
return ret;
}
void World::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode,
int number)
{
mRendering->playAnimationGroup (ptr, groupName, mode, number);
}
void World::skipAnimation (const MWWorld::Ptr& ptr)
{
mRendering->skipAnimation (ptr);
}
void World::update (float duration, bool paused)
{
mWorldScene->update (duration, paused);
@ -1048,7 +1024,6 @@ namespace MWWorld
// currently its here because we need to access the physics system
float* p = mPlayer->getPlayer().getRefData().getPosition().pos;
Vector3 sun = mRendering->getSkyManager()->getRealSunPos();
sun = Vector3(sun.x, -sun.z, sun.y);
mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun));
}
@ -1146,7 +1121,7 @@ namespace MWWorld
}
else
p = mPhysics->getRayPoint(results.front().first);
Ogre::Vector3 pos(p.x(), p.z(), -p.y());
Ogre::Vector3 pos(p.x(), p.y(), p.z());
Ogre::SceneNode* node = mFaced1.getRefData().getBaseNode();
//std::cout << "Num facing 1 : " << mFaced1Name << std::endl;
@ -1174,7 +1149,7 @@ namespace MWWorld
}
else
p = mPhysics->getRayPoint(results.at (1).first);
Ogre::Vector3 pos(p.x(), p.z(), -p.y());
Ogre::Vector3 pos(p.x(), p.y(), p.z());
Ogre::SceneNode* node1 = mFaced1.getRefData().getBaseNode();
Ogre::SceneNode* node2 = mFaced2.getRefData().getBaseNode();
@ -1252,8 +1227,8 @@ namespace MWWorld
if (!ref)
return Vector2(0, 1);
Ogre::SceneNode* node = ref->mData.getBaseNode();
Vector3 dir = node->_getDerivedOrientation().yAxis();
Vector2 d = Vector2(dir.x, dir.z);
Vector3 dir = node->_getDerivedOrientation() * Ogre::Vector3(0,1,0);
Vector2 d = Vector2(dir.x, dir.y);
return d;
}
@ -1323,7 +1298,7 @@ namespace MWWorld
if (isCellExterior())
{
int cellX, cellY;
positionToIndex(result.second[0], -result.second[2], cellX, cellY);
positionToIndex(result.second[0], result.second[1], cellX, cellY);
cell = mCells.getExterior(cellX, cellY);
}
else
@ -1331,8 +1306,8 @@ namespace MWWorld
ESM::Position pos = getPlayer().getPlayer().getRefData().getPosition();
pos.pos[0] = result.second[0];
pos.pos[1] = -result.second[2];
pos.pos[2] = result.second[1];
pos.pos[1] = result.second[1];
pos.pos[2] = result.second[2];
Ptr dropped = copyObjectToCell(object, *cell, pos);
PCDropped(dropped);
@ -1416,25 +1391,42 @@ namespace MWWorld
}
bool
World::isSwimming(const MWWorld::Ptr &object)
World::isFlying(const MWWorld::Ptr &ptr) const
{
const MWWorld::Class &cls = MWWorld::Class::get(ptr);
if(cls.isActor() && cls.getCreatureStats(ptr).getMagicEffects().get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0)
return true;
return false;
}
bool
World::isSwimming(const MWWorld::Ptr &object) const
{
/// \todo add check ifActor() - only actors can swim
float *fpos = object.getRefData().getPosition().pos;
Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]);
/// \fixme should rely on object height
pos.z += 30;
/// \fixme 3/4ths submerged?
const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(object.getRefData().getHandle());
if(actor) pos.z += actor->getHalfExtents().z * 1.5;
return isUnderwater(*object.getCell()->mCell, pos);
return isUnderwater(object.getCell(), pos);
}
bool
World::isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos)
World::isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const
{
if (!(cell.mData.mFlags & ESM::Cell::HasWater)) {
if (!(cell->mCell->mData.mFlags & ESM::Cell::HasWater)) {
return false;
}
return pos.z < cell.mWater;
return pos.z < cell->mWaterLevel;
}
bool World::isOnGround(const MWWorld::Ptr &ptr) const
{
RefData &refdata = ptr.getRefData();
const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle());
return physactor && physactor->getOnGround();
}
void World::renderPlayer()
@ -1451,24 +1443,21 @@ namespace MWWorld
{
Ptr::CellStore *currentCell = mWorldScene->getCurrentCell();
Ogre::Vector3 playerPos;
float* pos = mPlayer->getPlayer ().getRefData ().getPosition ().pos;
playerPos.x = pos[0];
playerPos.y = pos[1];
playerPos.z = pos[2];
RefData &refdata = mPlayer->getPlayer().getRefData();
Ogre::Vector3 playerPos(refdata.getPosition().pos);
std::pair<bool, Ogre::Vector3> hit =
mPhysics->castRay(playerPos, Ogre::Vector3(0,0,-1), 50);
bool isOnGround = (hit.first ? (hit.second.distance (playerPos) < 25) : false);
if (!isOnGround || isUnderwater (*currentCell->mCell, playerPos))
const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle());
if(!physactor->getOnGround() || isUnderwater(currentCell, playerPos))
return 2;
if (currentCell->mCell->mData.mFlags & ESM::Cell::NoSleep)
if((currentCell->mCell->mData.mFlags&ESM::Cell::NoSleep))
return 1;
return 0;
}
MWRender::Animation* World::getAnimation(const MWWorld::Ptr &ptr)
{
return mRendering->getAnimation(ptr);
}
void World::playVideo (const std::string &name, bool allowSkipping)

View file

@ -37,6 +37,7 @@ namespace MWRender
{
class SkyManager;
class CellRender;
class Animation;
}
namespace MWWorld
@ -264,8 +265,7 @@ namespace MWWorld
virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const;
///< Convert position to cell numbers
virtual void doPhysics (const std::vector<std::pair<std::string, Ogre::Vector3> >& actors,
float duration);
virtual void doPhysics(const PtrMovementList &actors, float duration);
///< Run physics simulation and modify \a world accordingly.
virtual bool toggleCollisionMode();
@ -298,18 +298,6 @@ namespace MWWorld
/// \return pointer to created record
virtual void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName,
int mode, int number = 1);
///< Run animation for a MW-reference. Calls to this function for references that are
/// currently not in the rendered scene should be ignored.
///
/// \param mode: 0 normal, 1 immediate start, 2 immediate loop
/// \param number How offen the animation should be run
virtual void skipAnimation (const MWWorld::Ptr& ptr);
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the rendered scene should be ignored.
virtual void update (float duration, bool paused);
virtual bool placeObject (const Ptr& object, float cursorX, float cursorY);
@ -326,8 +314,10 @@ namespace MWWorld
virtual void processChangedSettings(const Settings::CategorySettingVector& settings);
virtual bool isSwimming(const MWWorld::Ptr &object);
virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos);
virtual bool isFlying(const MWWorld::Ptr &ptr) const;
virtual bool isSwimming(const MWWorld::Ptr &object) const;
virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const;
virtual bool isOnGround(const MWWorld::Ptr &ptr) const;
virtual void togglePOV() {
mRendering->togglePOV();
@ -360,6 +350,9 @@ namespace MWWorld
/// 2 - player is underwater \n
/// 3 - enemies are nearby (not implemented)
/// \todo Probably shouldn't be here
virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr);
/// \todo this does not belong here
virtual void playVideo(const std::string& name, bool allowSkipping);
virtual void stopVideo();

Some files were not shown because too many files have changed in this diff Show more