Merge branch 'collision' into next

Conflicts:
	apps/openmw/mwclass/npc.cpp
actorid
Marc Zinnschlag 14 years ago
commit 3012822ea1

@ -35,24 +35,24 @@ cmake_minimum_required(VERSION 2.6)
IF(EXISTS "${CMAKE_SOURCE_DIR}/prebuilt/vc100-mt-gd/ogre_1_7_1")
set(PREBUILT_DIR "${CMAKE_SOURCE_DIR}/prebuilt/vc100-mt-gd")
message (STATUS "OpenMW pre-built binaries found at ${PREBUILT_DIR}.")
SET(ENV{OGRE_HOME} "${PREBUILT_DIR}/ogre_1_7_1")
SET(ENV{BOOST_ROOT} "${PREBUILT_DIR}/boost_1_42_0")
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
set(ENV{BOOST_INCLUDEDIR} "${BOOST_ROOT}/include")
set(ENV{BOOST_LIBRARYDIR} "${BOOST_ROOT}/lib")
set(ENV{FREETYPE_DIR} "${PREBUILT_DIR}/freetype-2.3.5-1")
set(USE_MPG123 OFF)
set(USE_AUDIERE ON)
set(USE_AUDIERE ON)
set(AUDIERE_INCLUDE_DIR "${PREBUILT_DIR}/audiere-1.9.4/include")
set(AUDIERE_LIBRARY "${PREBUILT_DIR}/audiere-1.9.4/lib/audiere.lib")
set(ENV{OPENALDIR} "${PREBUILT_DIR}/OpenAL 1.1 SDK")
ELSE()
message (STATUS "OpenMW pre-built binaries not found. Using standard locations.")
ENDIF()
@ -93,6 +93,12 @@ set(NIFOGRE_HEADER
${COMP_DIR}/nifogre/ogre_nif_loader.hpp)
source_group(components\\nifogre FILES ${NIFOGRE} ${NIFOGRE_HEADER})
set(NIFBULLET
${COMP_DIR}/nifbullet/bullet_nif_loader.cpp)
set(NIFBULLET_HEADER
${COMP_DIR}/nifbullet/bullet_nif_loader.hpp)
source_group(components\\nifbullet FILES ${NIFBULLET} ${NIFBULLET_HEADER})
set(TO_UTF8
${COMP_DIR}/to_utf8/to_utf8.cpp)
set(TO_UTF8_HEADER
@ -142,10 +148,10 @@ file(GLOB INTERPRETER_HEADER ${COMP_DIR}/interpreter/*.hpp)
source_group(components\\interpreter FILES ${INTERPRETER} ${INTERPRETER_HEADER})
set(COMPONENTS ${BSA} ${NIF} ${NIFOGRE} ${ESM_STORE} ${MISC} ${TO_UTF8}
${COMPILER} ${INTERPRETER} ${ESM} ${FILE_FINDER})
${COMPILER} ${INTERPRETER} ${ESM} ${FILE_FINDER} ${NIFBULLET})
set(COMPONENTS_HEADER ${BSA_HEADER} ${NIF_HEADER} ${NIFOGRE_HEADER} ${ESM_STORE_HEADER}
${ESM_HEADER} ${MISC_HEADER} ${COMPILER_HEADER} ${TO_UTF8_HEADER}
${INTERPRETER_HEADER} ${FILE_FINDER_HEADER})
${INTERPRETER_HEADER} ${FILE_FINDER_HEADER} ${NIFBULLET_HEADER})
# source directory: libs
@ -165,6 +171,21 @@ set(OENGINE_GUI
${LIBDIR}/openengine/gui/manager.cpp
)
set(OENGINE_BULLET
${LIBDIR}/openengine/bullet/btKinematicCharacterController.cpp
${LIBDIR}/openengine/bullet/btKinematicCharacterController.h
${LIBDIR}/openengine/bullet/BtOgre.cpp
${LIBDIR}/openengine/bullet/BtOgreExtras.h
${LIBDIR}/openengine/bullet/BtOgreGP.h
${LIBDIR}/openengine/bullet/BtOgrePG.h
${LIBDIR}/openengine/bullet/CMotionState.cpp
${LIBDIR}/openengine/bullet/CMotionState.h
${LIBDIR}/openengine/bullet/physic.cpp
${LIBDIR}/openengine/bullet/physic.hpp
${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp
${LIBDIR}/openengine/bullet/BulletShapeLoader.h
)
# Sound setup
if (USE_AUDIERE)
set(MANGLE_SOUND_OUTPUT
@ -205,7 +226,7 @@ set(OENGINE_SOUND
${LIBDIR}/mangle/sound/outputs/openal_out.cpp
${MANGLE_SOUND_OUTPUT}
)
set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_SOUND})
set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_SOUND} ${OENGINE_BULLET})
source_group(libs\\openengine FILES ${OENGINE_ALL})
set(OPENMW_LIBS ${MANGLE_ALL} ${OENGINE_ALL})
@ -235,6 +256,7 @@ find_package(OGRE REQUIRED)
find_package(Boost REQUIRED COMPONENTS system filesystem program_options thread)
find_package(OIS REQUIRED)
find_package(OpenAL REQUIRED)
find_package(Bullet REQUIRED)
include_directories("."
${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE
${OIS_INCLUDE_DIR} ${Boost_INCLUDE_DIR}
@ -383,7 +405,7 @@ if (WIN32)
set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS")
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS")
endif()
# Release builds use the debug console
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
@ -405,7 +427,7 @@ if (WIN32)
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "-Wl,-subsystem,console")
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
endif(MINGW)
# TODO: At some point release builds should not use the console but rather write to a log file
#set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
#set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")

@ -127,6 +127,7 @@ set(GAMEWORLD
mwworld/actiontake.cpp
mwworld/containerutil.cpp
mwworld/player.cpp
mwworld/doingphysics.cpp
)
set(GAMEWORLD_HEADER
mwworld/refdata.hpp
@ -145,6 +146,8 @@ set(GAMEWORLD_HEADER
mwworld/manualref.hpp
mwworld/containerutil.hpp
mwworld/player.hpp
mwworld/doingphysics.hpp
mwworld/cellfunctors.hpp
)
source_group(apps\\openmw\\mwworld FILES ${GAMEWORLD} ${GAMEWORLD_HEADER})
@ -206,6 +209,7 @@ set(GAMEMECHANICS_HEADER
mwmechanics/stat.hpp
mwmechanics/creaturestats.hpp
mwmechanics/magiceffects.hpp
mwmechanics/movement.hpp
)
source_group(apps\\openmw\\mwmechanics FILES ${GAMEMECHANICS} ${GAMEMECHANICS_HEADER})
@ -227,7 +231,7 @@ add_executable(openmw
# Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING
# when we change the backend.
include_directories(${SOUND_INPUT_INCLUDES})
include_directories(${SOUND_INPUT_INCLUDES} ${BULLET_INCLUDE_DIRS})
add_definitions(${SOUND_DEFINE})
target_link_libraries(openmw
@ -236,6 +240,7 @@ target_link_libraries(openmw
${Boost_LIBRARIES}
${OPENAL_LIBRARY}
${SOUND_INPUT_LIBRARY}
${BULLET_LIBRARIES}
caelum
MyGUIEngine
MyGUIOgrePlatform

@ -6,6 +6,8 @@
#include <iostream>
#include <utility>
#include <OgreVector3.h>
#include "components/esm/records.hpp"
#include <components/esm_store/cell_store.hpp>
#include <components/misc/fileops.hpp>
@ -47,6 +49,7 @@
#include "mwgui/class.hpp"
#include "path.hpp"
#include "components/nifbullet/bullet_nif_loader.hpp"
//using namespace ESM;
@ -167,7 +170,8 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
mEnvironment.mWorld->markCellAsUnchanged();
// update actors
mEnvironment.mMechanicsManager->update();
std::vector<std::pair<std::string, Ogre::Vector3> > movement;
mEnvironment.mMechanicsManager->update (movement);
if (focusFrameCounter++ == focusUpdateFrame)
{
@ -188,6 +192,9 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
focusFrameCounter = 0;
}
if (mEnvironment.mWindowManager->getMode()==MWGui::GM_Game)
mEnvironment.mWorld->doPhysics (movement, mEnvironment.mFrameDuration);
}
catch (const std::exception& e)
{
@ -208,6 +215,7 @@ OMW::Engine::Engine()
, mScriptManager (0)
, mScriptContext (0)
, mGuiManager (0)
, mPhysicEngine (0)
{
MWClass::registerClasses();
}
@ -222,6 +230,7 @@ OMW::Engine::~Engine()
delete mEnvironment.mDialogueManager;
delete mScriptManager;
delete mScriptContext;
delete mPhysicEngine;
}
// Load all BSA files in data directory.
@ -343,8 +352,13 @@ void OMW::Engine::go()
loadBSA();
// Create physics. shapeLoader is deleted by the physic engine
ManualBulletShapeLoader* shapeLoader = new ManualBulletShapeLoader();
mPhysicEngine = new OEngine::Physic::PhysicEngine(shapeLoader);
// Create the world
mEnvironment.mWorld = new MWWorld::World (mOgre, mDataDir, mMaster, mResDir, mNewGame, mEnvironment);
mEnvironment.mWorld = new MWWorld::World (mOgre, mPhysicEngine, mDataDir, mMaster, mResDir, mNewGame, mEnvironment);
// Set up the GUI system
mGuiManager = new OEngine::GUI::MyGUIManager(mOgre.getWindow(), mOgre.getScene(), false, cfgDir);
@ -398,7 +412,7 @@ void OMW::Engine::go()
else
{
pos.pos[0] = pos.pos[1] = 0;
mEnvironment.mWorld->changeCell (mCellName, pos);
mEnvironment.mWorld->changeToInteriorCell (mCellName, pos);
}
// Sets up the input system

@ -8,6 +8,7 @@
#include <OgreFrameListener.h>
#include <openengine/ogre/renderer.hpp>
#include <openengine/bullet/physic.hpp>
#include <components/compiler/extensions.hpp>
#include "mwworld/environment.hpp"
@ -59,6 +60,7 @@ namespace OMW
boost::filesystem::path mDataDir;
boost::filesystem::path mResDir;
OEngine::Render::OgreRenderer mOgre;
OEngine::Physic::PhysicEngine* mPhysicEngine;
std::string mCellName;
std::string mMaster;
bool mShowFPS;

@ -23,6 +23,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -26,6 +26,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -26,6 +26,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -26,6 +26,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -26,6 +26,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -23,6 +23,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -35,6 +35,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertActorPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -30,6 +30,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -26,6 +26,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -31,6 +31,7 @@ namespace MWClass
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
// Extract the color and convert to floating point
const int color = ref->base->data.color;

@ -26,6 +26,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -26,6 +26,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -261,8 +261,9 @@ namespace MWClass
cellRender.insertMesh (headModel, Ogre::Vector3( 0, 0, 5), axis, Ogre::Radian(0), npcName + "head", neckandup, neckNumbers);
neckandup[neckNumbers++] = npcName + "head";
cellRender.insertMesh (hairModel, Ogre::Vector3( 0, -1, 0), axis, Ogre::Radian(0), npcName + "hair", neckandup, neckNumbers);
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
cellRender.insertActorPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
void Npc::enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const
@ -316,7 +317,6 @@ namespace MWClass
{
if (!ptr.getRefData().getNpcStats().get())
{
// xxx
boost::shared_ptr<MWMechanics::NpcStats> stats (
new MWMechanics::NpcStats);
@ -368,6 +368,113 @@ namespace MWClass
return ref->base->script;
}
void Npc::setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const
{
MWMechanics::NpcStats& stats = getNpcStats (ptr);
switch (stance)
{
case Run:
stats.mForceRun = force;
break;
case Sneak:
stats.mForceSneak = force;
break;
case Combat:
throw std::runtime_error ("combat stance not enforcable for NPCs");
}
}
void Npc::setStance (const MWWorld::Ptr& ptr, Stance stance, bool set) const
{
MWMechanics::NpcStats& stats = getNpcStats (ptr);
switch (stance)
{
case Run:
stats.mRun = set;
break;
case Sneak:
stats.mSneak = set;
break;
case Combat:
stats.mCombat = set;
break;
}
}
bool Npc::getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce) const
{
MWMechanics::NpcStats& stats = getNpcStats (ptr);
switch (stance)
{
case Run:
if (!ignoreForce && stats.mForceRun)
return true;
return stats.mRun;
case Sneak:
if (!ignoreForce && stats.mForceSneak)
return true;
return stats.mSneak;
case Combat:
return stats.mCombat;
}
return false;
}
float Npc::getSpeed (const MWWorld::Ptr& ptr) const
{
return getStance (ptr, Run) ? 600 : 300; // TODO calculate these values from stats
}
MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const
{
if (!ptr.getRefData().getMovement().get())
{
boost::shared_ptr<MWMechanics::Movement> movement (
new MWMechanics::Movement);
ptr.getRefData().getMovement() = movement;
}
return *ptr.getRefData().getMovement();
}
Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const
{
Ogre::Vector3 vector (0, 0, 0);
if (ptr.getRefData().getMovement().get())
{
vector.x = - ptr.getRefData().getMovement()->mLeftRight * 200;
vector.y = ptr.getRefData().getMovement()->mForwardBackward * 200;
if (getStance (ptr, Run, false))
vector *= 2;
}
return vector;
}
void Npc::registerSelf()
{
boost::shared_ptr<Class> instance (new Npc);

@ -8,7 +8,7 @@ namespace MWClass
class Npc : public MWWorld::Class
{
public:
virtual std::string getId (const MWWorld::Ptr& ptr) const;
///< Return ID of \a ptr
@ -43,6 +43,26 @@ namespace MWClass
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
///< Return name of the script attached to ptr
virtual void setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const;
///< Force or unforce a stance.
virtual void setStance (const MWWorld::Ptr& ptr, Stance stance, bool set) const;
///< Set or unset a stance.
virtual bool getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce = false)
const;
////< Check if a stance is active or not.
virtual float getSpeed (const MWWorld::Ptr& ptr) const;
///< Return movement speed.
virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const;
///< Return desired movement.
virtual Ogre::Vector3 getMovementVector (const MWWorld::Ptr& ptr) const;
///< Return desired movement vector (determined based on movement settings,
/// stance and stats).
static void registerSelf();
};
}

@ -26,6 +26,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -26,6 +26,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -26,6 +26,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -21,6 +21,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -26,6 +26,7 @@ namespace MWClass
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}
}

@ -8,6 +8,9 @@
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#undef min
#undef max
using namespace MWGui;
/* GenerateClassResultDialog */

@ -7,6 +7,9 @@
#include <algorithm>
#include <iterator>
#undef min
#undef max
using namespace MWGui;

@ -8,6 +8,9 @@
#include <cmath>
#undef min
#undef max
using namespace MWGui;
using namespace Widgets;

@ -4,6 +4,9 @@
#include <boost/lexical_cast.hpp>
#undef min
#undef max
using namespace MWGui;
using namespace MWGui::Widgets;

@ -41,13 +41,31 @@ namespace MWInput
A_MoveLeft, // Move player left / right
A_MoveRight,
A_MoveUp, // Move up / down
A_MoveDown,
A_MoveForward, // Forward / Backward
A_MoveBackward,
A_Activate,
A_Use, //Use weapon, spell, etc.
A_Jump,
A_AutoMove, //Toggle Auto-move forward
A_Rest, //Rest
A_Journal, //Journal
A_Weapon, //Draw/Sheath weapon
A_Spell, //Ready/Unready Casting
A_AlwaysRun, //Toggle Always Run
A_CycleSpellLeft, //cycling through spells
A_CycleSpellRight,
A_CycleWeaponLeft,//Cycling through weapons
A_CycleWeaponRight,
A_ToggleSneak, //Toggles Sneak, add Push-Sneak later
A_ToggleWalk, //Toggle Walking/Running
A_QuickSave,
A_QuickLoad,
A_QuickMenu,
A_GameMenu,
A_LAST // Marker for the last item
};
@ -68,11 +86,14 @@ namespace MWInput
// Count screenshots.
int shotCount;
/* InputImpl Methods */
// Write screenshot to file.
void screenshot()
{
// Find the first unused filename.
//
// Find the first unused filename with a do-while
char buf[50];
do
{
@ -82,8 +103,8 @@ namespace MWInput
ogre.screenshot(buf);
}
// Called when the user presses the button to toggle the inventory
// screen.
/* toggleInventory() is called when the user presses the button to toggle the inventory screen. */
void toggleInventory()
{
using namespace MWGui;
@ -118,11 +139,21 @@ namespace MWInput
mEngine.activate();
}
void toggleAutoMove()
{
player.setAutoMove (!player.getAutoMove());
}
void toggleWalking()
{
player.toggleRunning();
}
// Exit program now button (which is disabled in GUI mode)
void exitNow()
{
if(!windows.isGuiMode())
exit.exitNow();
if(!windows.isGuiMode())
exit.exitNow();
}
public:
@ -159,7 +190,10 @@ namespace MWInput
"Toggle console");
disp->funcs.bind(A_Activate, boost::bind(&InputImpl::activate, this),
"Activate");
disp->funcs.bind(A_AutoMove, boost::bind(&InputImpl::toggleAutoMove, this),
"Auto Move");
disp->funcs.bind(A_ToggleWalk, boost::bind(&InputImpl::toggleWalking, this),
"Toggle Walk/Run");
// Add the exit listener
ogre.getRoot()->addFrameListener(&exit);
@ -195,6 +229,7 @@ namespace MWInput
**********************************/
// Key bindings for keypress events
// NOTE: These keys do not require constant polling - use in conjuction with variables in loops.
disp->bind(A_Quit, KC_Q);
disp->bind(A_Quit, KC_ESCAPE);
@ -202,8 +237,12 @@ namespace MWInput
disp->bind(A_Inventory, KC_I);
disp->bind(A_Console, KC_F1);
disp->bind(A_Activate, KC_SPACE);
disp->bind(A_AutoMove, KC_Z);
disp->bind(A_ToggleSneak, KC_X);
disp->bind(A_ToggleWalk, KC_C);
// Key bindings for polled keys
// NOTE: These keys are constantly being polled. Only add keys that must be checked each frame.
// Arrow keys
poller.bind(A_MoveLeft, KC_LEFT);
@ -216,47 +255,54 @@ namespace MWInput
poller.bind(A_MoveRight, KC_D);
poller.bind(A_MoveForward, KC_W);
poller.bind(A_MoveBackward, KC_S);
// Use shift and ctrl for up and down
poller.bind(A_MoveUp, KC_LSHIFT);
poller.bind(A_MoveDown, KC_LCONTROL);
}
// Used to check for movement keys
//NOTE: Used to check for movement keys
bool frameStarted(const Ogre::FrameEvent &evt)
{
// Tell OIS to handle all input events
input.capture();
// Update windows/gui as a result of input events
// For instance this could mean opening a new window/dialog,
// by doing this after the input events are handled we
// ensure that window/gui changes appear quickly while
// avoiding that window/gui changes does not happen in
// event callbacks (which may crash)
windows.update();
// Disable movement in Gui mode
if(windows.isGuiMode()) return true;
float speed = 300 * evt.timeSinceLastFrame;
float moveX = 0, moveY = 0, moveZ = 0;
if(poller.isDown(A_MoveLeft)) moveX -= speed;
if(poller.isDown(A_MoveRight)) moveX += speed;
if(poller.isDown(A_MoveForward)) moveZ -= speed;
if(poller.isDown(A_MoveBackward)) moveZ += speed;
// TODO: These should be enabled for floating modes (like
// swimming and levitation) and disabled for everything else.
if(poller.isDown(A_MoveUp)) moveY += speed;
if(poller.isDown(A_MoveDown)) moveY -= speed;
if(moveX != 0 || moveY != 0 || moveZ != 0)
player.moveRel(moveX, moveY, moveZ);
// Tell OIS to handle all input events
input.capture();
// Update windows/gui as a result of input events
// For instance this could mean opening a new window/dialog,
// by doing this after the input events are handled we
// ensure that window/gui changes appear quickly while
// avoiding that window/gui changes does not happen in
// event callbacks (which may crash)
windows.update();
// Disable movement in Gui mode
if (windows.isGuiMode()) return true;
// Configure player movement according to keyboard input. Actual movement will
// be done in the physics system.
if (poller.isDown(A_MoveLeft))
{
player.setAutoMove (false);
player.setLeftRight (1);
}
else if (poller.isDown(A_MoveRight))
{
player.setAutoMove (false);
player.setLeftRight (-1);
}
else
player.setLeftRight (0);
if (poller.isDown(A_MoveForward))
{
player.setAutoMove (false);
player.setForwardBackward (1);
}
else if (poller.isDown(A_MoveBackward))
{
player.setAutoMove (false);
player.setForwardBackward (-1);
}
else
player.setForwardBackward (0);
return true;
return true;
}
// Switch between gui modes. Besides controlling the Gui windows
@ -288,6 +334,7 @@ namespace MWInput
}
};
/***CONSTRUCTOR***/
MWInputManager::MWInputManager(OEngine::Render::OgreRenderer &ogre,
MWWorld::Player &player,
MWGui::WindowManager &windows,
@ -297,6 +344,7 @@ namespace MWInput
impl = new InputImpl(ogre,player,windows,debug, engine);
}
/***DESTRUCTOR***/
MWInputManager::~MWInputManager()
{
delete impl;

@ -234,11 +234,17 @@ namespace MWMechanics
void MechanicsManager::removeActor (const MWWorld::Ptr& ptr)
{
if (ptr==mWatched)
mWatched = MWWorld::Ptr();
mActors.erase (ptr);
}
void MechanicsManager::dropActors (const MWWorld::Ptr::CellStore *cellStore)
{
if (!mWatched.isEmpty() && mWatched.getCell()==cellStore)
mWatched = MWWorld::Ptr();
std::set<MWWorld::Ptr>::iterator iter = mActors.begin();
while (iter!=mActors.end())
@ -255,7 +261,7 @@ namespace MWMechanics
mWatched = ptr;
}
void MechanicsManager::update()
void MechanicsManager::update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement)
{
if (!mWatched.isEmpty())
{
@ -337,6 +343,15 @@ namespace MWMechanics
mEnvironment.mWindowManager->configureSkills (majorSkills, minorSkills);
}
for (std::set<MWWorld::Ptr>::iterator iter (mActors.begin()); iter!=mActors.end();
++iter)
{
Ogre::Vector3 vector = MWWorld::Class::get (*iter).getMovementVector (*iter);
if (vector!=Ogre::Vector3::ZERO)
movement.push_back (std::make_pair (iter->getRefData().getHandle(), vector));
}
}
void MechanicsManager::setPlayerName (const std::string& name)

@ -2,12 +2,19 @@
#define GAME_MWMECHANICS_MECHANICSMANAGER_H
#include <set>
#include <vector>
#include <string>
#include "../mwworld/ptr.hpp"
#include "creaturestats.hpp"
#include "npcstats.hpp"
namespace Ogre
{
class Vector3;
}
namespace MWWorld
{
class Environment;
@ -53,8 +60,8 @@ namespace MWMechanics
///< On each update look for changes in a previously registered actor and update the
/// GUI accordingly.
void update();
///< Update actor stats
void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement);
///< Update actor stats and store desired velocity vectors in \a movement
void setPlayerName (const std::string& name);
///< Set player name.

@ -0,0 +1,16 @@
#ifndef GAME_MWMECHANICS_MOVEMENT_H
#define GAME_MWMECHANICS_MOVEMENT_H
namespace MWMechanics
{
/// Desired movement for an actor
struct Movement
{
signed char mLeftRight; // 1: wants to move left, -1: wants to move right
signed char mForwardBackward; // 1:wants to move forward, -1: wants to move backward
Movement() : mLeftRight (0), mForwardBackward (0) {}
};
}
#endif

@ -18,6 +18,15 @@ namespace MWMechanics
std::map<std::string, int> mFactionRank;
Stat<float> mSkill[27];
bool mForceRun;
bool mForceSneak;
bool mRun;
bool mSneak;
bool mCombat;
NpcStats() : mForceRun (false), mForceSneak (false), mRun (false), mSneak (false),
mCombat (false) {}
};
}

@ -1,6 +1,9 @@
#ifndef GAME_MWMECHANICS_STAT_H
#define GAME_MWMECHANICS_STAT_H
#undef min
#undef max
#include <limits>
namespace MWMechanics

@ -45,6 +45,9 @@ namespace MWRender
virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements) = 0;
virtual void insertObjectPhysics() = 0;
virtual void insertActorPhysics() = 0;
/// insert a light related to the most recent insertBegin call.
virtual void insertLight(float r, float g, float b, float radius) = 0;

@ -58,6 +58,8 @@ void ExteriorCellRender::insertBegin (ESM::CellRef &ref)
// Rotates first around z, then y, then x
mInsert->setOrientation(xr*yr*zr);
mInsertMesh.clear();
}
@ -81,18 +83,18 @@ void ExteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
if(sceneParent[i] != "" && parent->getChild(sceneParent[i]))
parent = dynamic_cast<Ogre::SceneNode*> (parent->getChild(sceneParent[i]));
}
mNpcPart = parent->createChildSceneNode(sceneNodeName);
NIFLoader::load(mesh);
MovableObject *ent = mScene.getMgr()->createEntity(mesh);
mNpcPart->translate(vec);
mNpcPart->rotate(axis, angle);
// mNpcPart->translate(vec);
//mNpcPart->rotate(axis, angle);
mNpcPart->attachObject(ent);
//mNpcPart->
}
*/
void ExteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements)
@ -108,7 +110,7 @@ void ExteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
if(sceneParent[i] != "" && parent->getChild(sceneParent[i]))
parent = dynamic_cast<Ogre::SceneNode*> (parent->getChild(sceneParent[i]));
}
mNpcPart = parent->createChildSceneNode(sceneNodeName);
MeshPtr good2 = NIFLoader::load(mesh);
@ -120,7 +122,7 @@ void ExteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
mNpcPart->rotate(axis, angle);
}
else{
mNpcPart->rotate(axis, angle);
mNpcPart->translate(vec);
}
@ -202,6 +204,22 @@ void ExteriorCellRender::insertMesh(const std::string &mesh)
NIFLoader::load(mesh);
MovableObject *ent = mScene.getMgr()->createEntity(mesh);
mInsert->attachObject(ent);
if (mInsertMesh.empty())
mInsertMesh = mesh;
}
void ExteriorCellRender::insertObjectPhysics()
{
if (!mInsertMesh.empty())
mScene.addObject (mInsert->getName(), mInsertMesh, mInsert->getOrientation(),
mInsert->getScale().x, mInsert->getPosition());
}
void ExteriorCellRender::insertActorPhysics()
{
if (!mInsertMesh.empty())
mScene.addActor (mInsert->getName(), mInsertMesh, mInsert->getPosition());
}
// insert a light related to the most recent insertBegin call.

@ -54,6 +54,7 @@ namespace MWRender
Ogre::SceneNode *mBase;
Ogre::SceneNode *mInsert;
std::string mInsertMesh;
Ogre::SceneNode *mNpcPart;
// 0 normal, 1 more bright, 2 max
@ -67,11 +68,15 @@ namespace MWRender
/// insert a mesh related to the most recent insertBegin call.
virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements);
virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements, bool translateFirst);
virtual void insertMesh(const std::string &mesh);
virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements);
virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements);
virtual void insertObjectPhysics();
virtual void insertActorPhysics();
/// insert a light related to the most recent insertBegin call.
virtual void insertLight(float r, float g, float b, float radius);
@ -85,7 +90,7 @@ namespace MWRender
void configureFog();
void setAmbientMode();
public:

@ -60,6 +60,8 @@ void InteriorCellRender::insertBegin (ESM::CellRef &ref)
// Rotates first around z, then y, then x
insert->setOrientation(xr*yr*zr);
mInsertMesh.clear();
}
// insert a mesh related to the most recent insertBegin call.
@ -100,7 +102,7 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
if(sceneParent[i] != "" && parent->getChild(sceneParent[i]))
parent = dynamic_cast<Ogre::SceneNode*> (parent->getChild(sceneParent[i]));
}
npcPart = parent->createChildSceneNode(sceneNodeName);
//npcPart->showBoundingBox(true);
@ -108,7 +110,7 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
MovableObject *ent = scene.getMgr()->createEntity(mesh);
//ent->extr
// MovableObject *ent2 = scene.getMgr()->createEntity(bounds
// );
//ent->
@ -119,7 +121,7 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
npcPart->rotate(axis, angle);
}
else{
npcPart->rotate(axis, angle);
npcPart->translate(vec);
}
@ -185,6 +187,22 @@ void InteriorCellRender::insertMesh(const std::string &mesh)
NIFLoader::load(mesh);
MovableObject *ent = scene.getMgr()->createEntity(mesh);
insert->attachObject(ent);
if (mInsertMesh.empty())
mInsertMesh = mesh;
}
void InteriorCellRender::insertObjectPhysics()
{
if (!mInsertMesh.empty())
scene.addObject (insert->getName(), mInsertMesh, insert->getOrientation(),
insert->getScale().x, insert->getPosition());
}
void InteriorCellRender::insertActorPhysics()
{
if (!mInsertMesh.empty())
scene.addActor (insert->getName(), mInsertMesh, insert->getPosition());
}
// insert a light related to the most recent insertBegin call.

@ -54,6 +54,7 @@ namespace MWRender
Ogre::SceneNode *base;
Ogre::SceneNode *insert;
std::string mInsertMesh;
Ogre::SceneNode *npcPart;
// 0 normal, 1 more bright, 2 max
@ -69,6 +70,11 @@ namespace MWRender
virtual void insertMesh(const std::string &mesh);
virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements);
virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements, bool translateFirst);
virtual void insertObjectPhysics();
virtual void insertActorPhysics();
/// insert a light related to the most recent insertBegin call.
virtual void insertLight(float r, float g, float b, float radius);

@ -11,6 +11,7 @@
#include "../mwworld/world.hpp" // these includes can be removed once the static-hack is gone
#include "../mwworld/ptr.hpp"
#include "../mwworld/doingphysics.hpp"
#include <components/esm/loadstat.hpp>
#include "player.hpp"
@ -18,30 +19,38 @@
using namespace MWRender;
using namespace Ogre;
MWScene::MWScene(OEngine::Render::OgreRenderer &_rend)
: rend(_rend)
MWScene::MWScene(OEngine::Render::OgreRenderer &_rend , OEngine::Physic::PhysicEngine* physEng)
: rend(_rend)
{
rend.createScene("PlayerCam", 55, 5);
eng = physEng;
rend.createScene("PlayerCam", 55, 5);
// Set default mipmap level (NB some APIs ignore this)
TextureManager::getSingleton().setDefaultNumMipmaps(5);
// Set default mipmap level (NB some APIs ignore this)
TextureManager::getSingleton().setDefaultNumMipmaps(5);
// Load resources
ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
// Load resources
ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
// 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 = rend.getScene()->getRootSceneNode();
mwRoot = rt->createChildSceneNode();
mwRoot->pitch(Degree(-90));
// 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 = rend.getScene()->getRootSceneNode();
mwRoot = rt->createChildSceneNode();
mwRoot->pitch(Degree(-90));
//used to obtain ingame information of ogre objects (which are faced or selected)
mRaySceneQuery = rend.getScene()->createRayQuery(Ray());
//used to obtain ingame information of ogre objects (which are faced or selected)
mRaySceneQuery = rend.getScene()->createRayQuery(Ray());
mPlayer = new MWRender::Player (getCamera());
Ogre::SceneNode *playerNode = mwRoot->createChildSceneNode();
playerNode->pitch(Degree(90));
Ogre::SceneNode *cameraYawNode = playerNode->createChildSceneNode();
Ogre::SceneNode *cameraPitchNode = cameraYawNode->createChildSceneNode();
cameraPitchNode->attachObject(getCamera());
mPlayer = new MWRender::Player (getCamera(), playerNode->getName());
}
MWScene::~MWScene()
@ -58,42 +67,120 @@ std::pair<std::string, float> MWScene::getFacedHandle (MWWorld::World& world)
Ray centerRay = getCamera()->getCameraToViewportRay(
getViewport()->getWidth()/2,
getViewport()->getHeight()/2);
//let's avoid the capsule shape of the player.
centerRay.setOrigin(centerRay.getOrigin() + 20*centerRay.getDirection());
btVector3 from(centerRay.getOrigin().x,-centerRay.getOrigin().z,centerRay.getOrigin().y);
btVector3 to(centerRay.getPoint(1000).x,-centerRay.getPoint(1000).z,centerRay.getPoint(1000).y);
return eng->rayTest(from,to);
}
void MWScene::doPhysics (float duration, MWWorld::World& world,
const std::vector<std::pair<std::string, Ogre::Vector3> >& actors)
{
// stop changes to world from being reported back to the physics system
MWWorld::DoingPhysics scopeGuard;
// get all objects touched by the ray
getRaySceneQuery()->setRay (centerRay );
RaySceneQueryResult &result = getRaySceneQuery()->execute();
//set the DebugRenderingMode. To disable it,set it to 0
eng->setDebugRenderingMode(1);
RaySceneQueryResult::iterator nearest = result.end();
//set the walkdirection to 0 (no movement) for every actor)
for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = eng->PhysicActorMap.begin(); it != eng->PhysicActorMap.end();it++)
{
OEngine::Physic::PhysicActor* act = it->second;
act->setWalkDirection(btVector3(0,0,0));
}
for (RaySceneQueryResult::iterator itr = result.begin();
itr != result.end(); itr++ )
for (std::vector<std::pair<std::string, Ogre::Vector3> >::const_iterator iter (actors.begin());
iter!=actors.end(); ++iter)
{
// there seem to be omnipresent objects like the caelum sky dom,
// the distance of these objects is always 0 so this if excludes these
if ( itr->movable && itr->distance >= 0.1)
{
// horrible hack to exclude statics. this must be removed as soon as a replacement for the
// AABB raycasting is implemented (we should not ignore statics)
MWWorld::Ptr ptr = world.getPtrViaHandle (itr->movable->getParentSceneNode()->getName());
if (ptr.getType()==typeid (ESM::Static))
break;
if ( nearest == result.end() ) //if no object is set
{
nearest = itr;
}
else if ( itr->distance < nearest->distance )
{
nearest = itr;
}
}
OEngine::Physic::PhysicActor* act = eng->getCharacter(iter->first);
//dirty stuff to get the camera orientation. Must be changed!
Ogre::SceneNode *sceneNode = rend.getScene()->getSceneNode (iter->first);
Ogre::Quaternion quat = sceneNode->getChildIterator().getNext()->getOrientation();
Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y);
Ogre::Vector3 dir = 0.01*(quat*dir1);
//set the walk direction
act->setWalkDirection(btVector3(dir.x,-dir.z,dir.y));
}
eng->stepSimulation(duration);
if ( nearest != result.end() )
for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = eng->PhysicActorMap.begin(); it != eng->PhysicActorMap.end();it++)
{
handle = nearest->movable->getParentSceneNode()->getName();
distance = nearest->distance;
OEngine::Physic::PhysicActor* act = it->second;
btVector3 newPos = act->getPosition();
MWWorld::Ptr ptr = world.getPtrViaHandle (it->first);
world.moveObject (ptr, newPos.x(), newPos.y(), newPos.z());
}
}
void MWScene::addObject (const std::string& handle, const std::string& mesh,
const Ogre::Quaternion& rotation, float scale, const Ogre::Vector3& position)
{
OEngine::Physic::RigidBody* body = eng->createRigidBody(mesh,handle);
eng->addRigidBody(body);
btTransform tr;
tr.setOrigin(btVector3(position.x,position.y,position.z));
tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w));
body->setWorldTransform(tr);
}
void MWScene::addActor (const std::string& handle, const std::string& mesh,
const Ogre::Vector3& position)
{
//TODO:optimize this. Searching the std::map isn't very efficient i think.
eng->addCharacter(handle);
OEngine::Physic::PhysicActor* act = eng->getCharacter(handle);
act->setPosition(btVector3(position.x,position.y,position.z));
}
return std::pair<std::string, float>(handle, distance);
void MWScene::removeObject (const std::string& handle)
{
//TODO:check if actor???
eng->removeRigidBody(handle);
eng->deleteRigidBody(handle);
}
void MWScene::moveObject (const std::string& handle, const Ogre::Vector3& position, bool updatePhysics)
{
rend.getScene()->getSceneNode(handle)->setPosition(position);
if(updatePhysics)//TODO: is it an actor?
{
OEngine::Physic::RigidBody* body = eng->getRigidBody(handle);
btTransform tr = body->getWorldTransform();
tr.setOrigin(btVector3(position.x,position.y,position.z));
body->setWorldTransform(tr);
}
}
void MWScene::rotateObject (const std::string& handle, const Ogre::Quaternion& rotation)
{
}
void MWScene::scaleObject (const std::string& handle, float scale)
{
}
void MWScene::toggleCollisionMode()
{
for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = eng->PhysicActorMap.begin(); it != eng->PhysicActorMap.end();it++)
{
OEngine::Physic::PhysicActor* act = it->second;
bool cmode = act->getCollisionMode();
if(cmode)
{
act->enableCollisions(false);
act->setGravity(0);
}
else
{
act->enableCollisions(true);
act->setGravity(10);
}
}
}

@ -3,6 +3,10 @@
#include <utility>
#include <openengine/ogre/renderer.hpp>
#include <openengine/bullet/physic.hpp>
#include <vector>
#include <string>
namespace Ogre
{
@ -11,6 +15,8 @@ namespace Ogre
class SceneManager;
class SceneNode;
class RaySceneQuery;
class Quaternion;
class Vector3;
}
namespace MWWorld
@ -34,11 +40,13 @@ namespace MWRender
Ogre::SceneNode *mwRoot;
Ogre::RaySceneQuery *mRaySceneQuery;
OEngine::Physic::PhysicEngine* eng;
MWRender::Player *mPlayer;
public:
MWScene (OEngine::Render::OgreRenderer &_rend);
MWScene (OEngine::Render::OgreRenderer &_rend , OEngine::Physic::PhysicEngine* physEng);
~MWScene();
@ -54,6 +62,34 @@ namespace MWRender
/// name is empty and distance = -1 if there is no object which
/// can be faced
std::pair<std::string, float> getFacedHandle (MWWorld::World& world);
/// Run physics simulation and modify \a world accordingly.
void doPhysics (float duration, MWWorld::World& world,
const std::vector<std::pair<std::string, Ogre::Vector3> >& actors);
/// Add object to physics system.
void addObject (const std::string& handle, const std::string& mesh,
const Ogre::Quaternion& rotation, float scale, const Ogre::Vector3& position);
/// Add actor to physics system.
void addActor (const std::string& handle, const std::string& mesh,
const Ogre::Vector3& position);
/// Remove object from physic systems.
void removeObject (const std::string& handle);
/// Move object.
void moveObject (const std::string& handle, const Ogre::Vector3& position, bool updatePhysics);
/// Change object's orientation.
void rotateObject (const std::string& handle, const Ogre::Quaternion& rotation);
/// Change object's scale.
void scaleObject (const std::string& handle, float scale);
/// Toggle collision mode for player. If disabled player object should ignore
/// collisions and gravity.
void toggleCollisionMode();
};
}

@ -3,6 +3,7 @@
namespace MWRender
{
Player::Player (Ogre::Camera *camera) : mCamera (camera)
Player::Player (Ogre::Camera *camera, const std::string& handle)
: mCamera (camera), mHandle (handle)
{}
}

@ -1,6 +1,8 @@
#ifndef GAME_MWRENDER_PLAYER_H
#define GAME_MWRENDER_PLAYER_H
#include <iostream>
namespace Ogre
{
class Camera;
@ -12,12 +14,15 @@ namespace MWRender
class Player
{
Ogre::Camera *mCamera;
std::string mHandle;
public:
Player (Ogre::Camera *camera);
Player (Ogre::Camera *camera, const std::string& handle);
Ogre::Camera *getCamera() { return mCamera; }
std::string getHandle() const { return mHandle; }
};
}

@ -53,7 +53,7 @@ namespace MWScript
else
{
pos.pos[0] = pos.pos[1] = 0;
context.getWorld().changeCell (cell, pos);
context.getWorld().changeToInteriorCell (cell, pos);
}
}
};

@ -46,7 +46,7 @@ namespace MWScript
InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext());
context.getWorld().getPlayer().toggleCollisionMode();
context.getWorld().toggleCollisionMode();
}
};

@ -16,6 +16,6 @@ namespace MWWorld
if (mCellName.empty())
environment.mWorld->changeToExteriorCell (mPosition);
else
environment.mWorld->changeCell (mCellName, mPosition);
environment.mWorld->changeToInteriorCell (mCellName, mPosition);
}
}

@ -0,0 +1,31 @@
#ifndef GAME_MWWORLD_CELLFUNCTORS_H
#define GAME_MWWORLD_CELLFUNCTORS_H
#include <vector>
#include <string>
#include "refdata.hpp"
namespace ESM
{
class CellRef;
}
namespace MWWorld
{
/// List all (Ogre-)handles.
struct ListHandles
{
std::vector<std::string> mHandles;
bool operator() (ESM::CellRef& ref, RefData& data)
{
std::string handle = data.getHandle();
if (!handle.empty())
mHandles.push_back (handle);
return true;
}
};
}
#endif

@ -3,6 +3,8 @@
#include <stdexcept>
#include <OgreVector3.h>
#include "ptr.hpp"
#include "nullaction.hpp"
@ -92,6 +94,36 @@ namespace MWWorld
return "";
}
void Class::setForceStance (const Ptr& ptr, Stance stance, bool force) const
{
throw std::runtime_error ("stance not supported by class");
}
void Class::setStance (const Ptr& ptr, Stance stance, bool set) const
{
throw std::runtime_error ("stance not supported by class");
}
bool Class::getStance (const Ptr& ptr, Stance stance, bool ignoreForce) const
{
return false;
}
float Class::getSpeed (const Ptr& ptr) const
{
return 0;
}
MWMechanics::Movement& Class::getMovementSettings (const Ptr& ptr) const
{
throw std::runtime_error ("movement settings not supported by class");
}
Ogre::Vector3 Class::getMovementVector (const Ptr& ptr) const
{
return Ogre::Vector3 (0, 0, 0);
}
const Class& Class::get (const std::string& key)
{
std::map<std::string, boost::shared_ptr<Class> >::const_iterator iter = sClasses.find (key);

@ -10,6 +10,11 @@
#include "containerstore.hpp"
#include "refdata.hpp"
namespace Ogre
{
class Vector3;
}
namespace MWRender
{
class CellRenderImp;
@ -19,6 +24,7 @@ namespace MWMechanics
{
struct CreatureStats;
struct NpcStats;
struct Movement;
}
namespace MWWorld
@ -41,6 +47,12 @@ namespace MWWorld
public:
/// NPC-stances.
enum Stance
{
Run, Sneak, Combat
};
virtual ~Class();
virtual std::string getId (const Ptr& ptr) const;
@ -108,6 +120,25 @@ namespace MWWorld
///< Return name of the script attached to ptr (default implementation: return an empty
/// string).
virtual void setForceStance (const Ptr& ptr, Stance stance, bool force) const;
///< Force or unforce a stance.
virtual void setStance (const Ptr& ptr, Stance stance, bool set) const;
///< Set or unset a stance.
virtual bool getStance (const Ptr& ptr, Stance stance, bool ignoreForce = false) const;
////< Check if a stance is active or not.
virtual float getSpeed (const Ptr& ptr) const;
///< Return movement speed.
virtual MWMechanics::Movement& getMovementSettings (const Ptr& ptr) const;
///< Return desired movement.
virtual Ogre::Vector3 getMovementVector (const Ptr& ptr) const;
///< Return desired movement vector (determined based on movement settings,
/// stance and stats).
static const Class& get (const std::string& key);
///< If there is no class for this \a key, an exception is thrown.

@ -0,0 +1,33 @@
#include "doingphysics.hpp"
namespace MWWorld
{
int DoingPhysics::sCounter = 0;
int DoingPhysics::sSuppress = 0;
DoingPhysics::DoingPhysics()
{
++sCounter;
}
DoingPhysics::~DoingPhysics()
{
--sCounter;
}
bool DoingPhysics::isDoingPhysics()
{
return sCounter>0 || sSuppress>0;
}
SuppressDoingPhysics::SuppressDoingPhysics()
{
++DoingPhysics::sSuppress;
}
SuppressDoingPhysics::~SuppressDoingPhysics()
{
--DoingPhysics::sSuppress;
}
}

@ -0,0 +1,46 @@
#ifndef GAME_MWWORLD_DOINGPHYSICS_H
#define GAME_MWWORLD_DOINGPHYSICS_H
namespace MWWorld
{
class SuppressDoingPhysics;
/// Scope guard for blocking physics updates during physics simulation.
class DoingPhysics
{
static int sCounter;
static int sSuppress;
private:
DoingPhysics (const DoingPhysics&);
DoingPhysics& operator= (const DoingPhysics&);
public:
DoingPhysics();
~DoingPhysics();
static bool isDoingPhysics();
friend class SuppressDoingPhysics;
};
/// Scope guard for temporarily lifting the block issues by DoingPhysics
class SuppressDoingPhysics
{
private:
SuppressDoingPhysics (const SuppressDoingPhysics&);
SuppressDoingPhysics& operator= (const SuppressDoingPhysics&);
public:
SuppressDoingPhysics();
~SuppressDoingPhysics();
};
}
#endif

@ -4,17 +4,20 @@
#include "../mwrender/player.hpp"
#include "world.hpp"
#include "class.hpp"
namespace MWWorld
{
Player::Player (MWRender::Player *renderer, const ESM::NPC *player, MWWorld::World& world) :
mCellStore (0), mRenderer (renderer), mWorld (world), mClass (0), mCollisionMode (true)
mCellStore (0), mRenderer (renderer), mWorld (world), mClass (0),
mAutoMove (false), mForwardBackward (0)
{
mPlayer.base = player;
mName = player->name;
mMale = !(player->flags & ESM::NPC::Female);
mRace = player->race;
mPlayer.ref.pos.pos[0] = mPlayer.ref.pos.pos[1] = mPlayer.ref.pos.pos[2] = 0;
mPlayer.mData.setHandle (renderer->getHandle());
mClass = new ESM::Class (*world.getStore().classes.find (player->cls));
}
@ -34,31 +37,52 @@ namespace MWWorld
-mPlayer.ref.pos.pos[1]));
}
void Player::moveRel (float &relX, float &relY, float &relZ)
void Player::setClass (const ESM::Class& class_)
{
ESM::Class *new_class = new ESM::Class (class_);
delete mClass;
mClass = new_class;
}
void Player::setAutoMove (bool enable)
{
// Move camera relative to its own direction
mRenderer->getCamera()->moveRelative (Ogre::Vector3(relX,0,relZ));
MWWorld::Ptr ptr = getPlayer();
// Up/down movement is always done relative the world axis.
mRenderer->getCamera()->move (Ogre::Vector3(0,relY,0));
mAutoMove = enable;
// Get new camera position, converting back to MW coords.
Ogre::Vector3 pos = mRenderer->getCamera()->getPosition();
relX = pos[0];
relY = -pos[2];
relZ = pos[1];
int value = mForwardBackward;
// TODO: Collision detection must be used to find the REAL new
// position, if mCollisionMode==true
if (mAutoMove)
value = 1;
// Set the position
setPos(relX, relY, relZ);
MWWorld::Class::get (ptr).getMovementSettings (ptr).mForwardBackward = value;
}
void Player::setClass (const ESM::Class& class_)
void Player::setLeftRight (int value)
{
ESM::Class *new_class = new ESM::Class (class_);
delete mClass;
mClass = new_class;
MWWorld::Ptr ptr = getPlayer();
MWWorld::Class::get (ptr).getMovementSettings (ptr).mLeftRight = value;
}
void Player::setForwardBackward (int value)
{
MWWorld::Ptr ptr = getPlayer();
mForwardBackward = value;
if (mAutoMove)
value = 1;
MWWorld::Class::get (ptr).getMovementSettings (ptr).mForwardBackward = value;
}
void Player::toggleRunning()
{
MWWorld::Ptr ptr = getPlayer();
bool running = MWWorld::Class::get (ptr).getStance (ptr, MWWorld::Class::Run, true);
MWWorld::Class::get (ptr).setStance (ptr, MWWorld::Class::Run, !running);
}
}

@ -29,7 +29,8 @@ namespace MWWorld
std::string mRace;
std::string mBirthsign;
ESM::Class *mClass;
bool mCollisionMode;
bool mAutoMove;
int mForwardBackward;
public:
@ -45,10 +46,6 @@ namespace MWWorld
mCellStore = cellStore;
}
/// Move the player relative to her own position and
/// orientation. After the call, the new position is returned.
void moveRel (float &relX, float &relY, float &relZ);
MWWorld::Ptr getPlayer()
{
MWWorld::Ptr ptr (&mPlayer, mCellStore);
@ -104,10 +101,18 @@ namespace MWWorld
return *mClass;
}
void toggleCollisionMode()
bool getAutoMove()
{
mCollisionMode = !mCollisionMode;
return mAutoMove;
}
};
void setAutoMove (bool enable);
void setLeftRight (int value);
void setForwardBackward (int value);
void toggleRunning();
};
}
#endif

@ -9,6 +9,7 @@
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/movement.hpp"
#include "containerstore.hpp"
@ -36,6 +37,7 @@ namespace MWWorld
// are never copied outside of container operations.
boost::shared_ptr<MWMechanics::CreatureStats> mCreatureStats;
boost::shared_ptr<MWMechanics::NpcStats> mNpcStats;
boost::shared_ptr<MWMechanics::Movement> mMovement;
boost::shared_ptr<ContainerStore<RefData> > mContainerStore;
@ -102,6 +104,11 @@ namespace MWWorld
return mNpcStats;
}
boost::shared_ptr<MWMechanics::Movement>& getMovement()
{
return mMovement;
}
boost::shared_ptr<ContainerStore<RefData> >& getContainerStore()
{
return mContainerStore;

@ -21,6 +21,8 @@
#include "refdata.hpp"
#include "globals.hpp"
#include "doingphysics.hpp"
#include "cellfunctors.hpp"
namespace
{
@ -270,6 +272,15 @@ namespace MWWorld
void World::unloadCell (CellRenderCollection::iterator iter)
{
ListHandles functor;
iter->first->forEach<ListHandles>(functor);
{ // silence annoying g++ warning
for (std::vector<std::string>::const_iterator iter (functor.mHandles.begin());
iter!=functor.mHandles.end(); ++iter)
mScene.removeObject (*iter);
}
removeScripts (iter->first);
mEnvironment.mMechanicsManager->dropActors (iter->first);
iter->second->destroy();
@ -294,9 +305,12 @@ namespace MWWorld
}
}
void World::playerCellChange (Ptr::CellStore *cell, const ESM::Position& position)
void World::playerCellChange (Ptr::CellStore *cell, const ESM::Position& position,
bool adjustPlayerPos)
{
mPlayer->setPos (position.pos[0], position.pos[1], position.pos[2], true);
if (adjustPlayerPos)
mPlayer->setPos (position.pos[0], position.pos[1], position.pos[2], true);
mPlayer->setCell (cell);
// TODO orientation
@ -315,11 +329,91 @@ namespace MWWorld
}
}
World::World (OEngine::Render::OgreRenderer& renderer, const boost::filesystem::path& dataDir,
const std::string& master, const boost::filesystem::path& resDir, bool newGame, Environment& environment)
: mSkyManager (0), mScene (renderer), mPlayer (0), mCurrentCell (0), mGlobalVariables (0),
void World::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos)
{
SuppressDoingPhysics scopeGuard;
// remove active
mEnvironment.mMechanicsManager->removeActor (mPlayer->getPlayer());
CellRenderCollection::iterator active = mActiveCells.begin();
while (active!=mActiveCells.end())
{
if (!(active->first->cell->data.flags & ESM::Cell::Interior))
{
if (std::abs (X-active->first->cell->data.gridX)<=1 &&
std::abs (Y-active->first->cell->data.gridY)<=1)
{
// keep cells within the new 3x3 grid
++active;
continue;
}
}
unloadCell (active++);
}
// Load cells
for (int x=X-1; x<=X+1; ++x)
for (int y=Y-1; y<=Y+1; ++y)
{
CellRenderCollection::iterator iter = mActiveCells.begin();
while (iter!=mActiveCells.end())
{
assert (!(iter->first->cell->data.flags & ESM::Cell::Interior));
if (x==iter->first->cell->data.gridX &&
y==iter->first->cell->data.gridY)
break;
++iter;
}
if (iter==mActiveCells.end())
{
mExteriors[std::make_pair (x, y)].loadExt (x, y, mStore, mEsm);
Ptr::CellStore *cell = &mExteriors[std::make_pair (x, y)];
loadCell (cell, new MWRender::ExteriorCellRender (*cell, mEnvironment, mScene));
}
}
// find current cell
CellRenderCollection::iterator iter = mActiveCells.begin();
while (iter!=mActiveCells.end())
{
assert (!(iter->first->cell->data.flags & ESM::Cell::Interior));
if (X==iter->first->cell->data.gridX &&
Y==iter->first->cell->data.gridY)
break;
++iter;
}
assert (iter!=mActiveCells.end());
mCurrentCell = iter->first;
// adjust player
playerCellChange (&mExteriors[std::make_pair (X, Y)], position, adjustPlayerPos);
// Sky system
adjustSky();
mCellChanged = true;
}
World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, const boost::filesystem::path& dataDir,
const std::string& master, const boost::filesystem::path& resDir,
bool newGame, Environment& environment)
: mSkyManager (0), mScene (renderer,physEng), mPlayer (0), mCurrentCell (0), mGlobalVariables (0),
mSky (false), mCellChanged (false), mEnvironment (environment)
{
mPhysEngine = physEng;
boost::filesystem::path masterPath (dataDir);
masterPath /= master;
@ -330,6 +424,7 @@ namespace MWWorld
mStore.load (mEsm);
mPlayer = new MWWorld::Player (mScene.getPlayer(), mStore.npcs.find ("player"), *this);
mScene.addActor (mPlayer->getPlayer().getRefData().getHandle(), "", Ogre::Vector3 (0, 0, 0));
// global variables
mGlobalVariables = new Globals (mStore);
@ -342,6 +437,8 @@ namespace MWWorld
mSkyManager =
MWRender::SkyManager::create(renderer.getWindow(), mScene.getCamera(), resDir);
mPhysEngine = physEng;
}
World::~World()
@ -357,6 +454,8 @@ namespace MWWorld
delete mPlayer;
delete mSkyManager;
delete mGlobalVariables;
//delete mPhysEngine;
}
MWWorld::Player& World::getPlayer()
@ -417,7 +516,8 @@ namespace MWWorld
Ptr World::getPtrViaHandle (const std::string& handle)
{
// TODO player
if (mPlayer->getPlayer().getRefData().getHandle()==handle)
return mPlayer->getPlayer();
for (CellRenderCollection::iterator iter (mActiveCells.begin());
iter!=mActiveCells.end(); ++iter)
@ -586,8 +686,10 @@ namespace MWWorld
return mGlobalVariables->getInt ("timescale");
}
void World::changeCell (const std::string& cellName, const ESM::Position& position)
void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position)
{
SuppressDoingPhysics scopeGuard;
// remove active
CellRenderCollection::iterator active = mActiveCells.begin();
@ -613,89 +715,14 @@ namespace MWWorld
//currentRegion->name = "";
}
void World::changeCell (int X, int Y, const ESM::Position& position)
{
// remove active
CellRenderCollection::iterator active = mActiveCells.begin();
while (active!=mActiveCells.end())
{
if (!(active->first->cell->data.flags & ESM::Cell::Interior))
{
if (std::abs (X-active->first->cell->data.gridX)<=1 &&
std::abs (Y-active->first->cell->data.gridY)<=1)
{
// keep cells within the new 3x3 grid
++active;
continue;
}
}
unloadCell (active++);
}
// Load cells
for (int x=X-1; x<=X+1; ++x)
for (int y=Y-1; y<=Y+1; ++y)
{
CellRenderCollection::iterator iter = mActiveCells.begin();
while (iter!=mActiveCells.end())
{
assert (!(iter->first->cell->data.flags & ESM::Cell::Interior));
if (x==iter->first->cell->data.gridX &&
y==iter->first->cell->data.gridY)
break;
++iter;
}
if (iter==mActiveCells.end())
{
mExteriors[std::make_pair (x, y)].loadExt (x, y, mStore, mEsm);
Ptr::CellStore *cell = &mExteriors[std::make_pair (x, y)];
loadCell (cell, new MWRender::ExteriorCellRender (*cell, mEnvironment, mScene));
}
}
// find current cell
CellRenderCollection::iterator iter = mActiveCells.begin();
while (iter!=mActiveCells.end())
{
assert (!(iter->first->cell->data.flags & ESM::Cell::Interior));
if (X==iter->first->cell->data.gridX &&
Y==iter->first->cell->data.gridY)
break;
++iter;
}
assert (iter!=mActiveCells.end());
mCurrentCell = iter->first;
// adjust player
playerCellChange (&mExteriors[std::make_pair (X, Y)], position);
// Sky system
adjustSky();
mCellChanged = true;
}
void World::changeToExteriorCell (const ESM::Position& position)
void World::changeToExteriorCell (const ESM::Position& position)
{
int x = 0;
int y = 0;
positionToIndex (position.pos[0], position.pos[1], x, y);
changeCell (x, y, position);
changeCell (x, y, position, true);
}
const ESM::Cell *World::getExterior (const std::string& cellName) const
@ -721,6 +748,7 @@ namespace MWWorld
return 0;
}
void World::markCellAsUnchanged()
{
mCellChanged = false;
@ -745,14 +773,17 @@ namespace MWWorld
if (MWRender::CellRender *render = searchRender (ptr.getCell()))
{
render->deleteObject (ptr.getRefData().getHandle());
ptr.getRefData().setHandle ("");
if (mActiveCells.find (ptr.getCell())!=mActiveCells.end())
{
Class::get (ptr).disable (ptr, mEnvironment);
mEnvironment.mSoundManager->stopSound3D (ptr);
if (!DoingPhysics::isDoingPhysics())
mScene.removeObject (ptr.getRefData().getHandle());
}
render->deleteObject (ptr.getRefData().getHandle());
ptr.getRefData().setHandle ("");
}
}
}
@ -777,12 +808,16 @@ namespace MWWorld
if (mCurrentCell->cell->data.gridX!=cellX || mCurrentCell->cell->data.gridY!=cellY)
{
changeCell (cellX, cellY, mPlayer->getPlayer().getCellRef().pos);
changeCell (cellX, cellY, mPlayer->getPlayer().getCellRef().pos, false);
}
}
}
}
mScene.moveObject (ptr.getRefData().getHandle(), Ogre::Vector3 (x, y, z),
!DoingPhysics::isDoingPhysics());
// TODO cell change for non-player ref
}
@ -814,4 +849,15 @@ namespace MWWorld
if (y<0)
--cellY;
}
void World::doPhysics (const std::vector<std::pair<std::string, Ogre::Vector3> >& actors,
float duration)
{
mScene.doPhysics (duration, *this, actors);
}
void World::toggleCollisionMode()
{
mScene.toggleCollisionMode();
}
}

@ -14,6 +14,13 @@
#include "ptr.hpp"
#include "globals.hpp"
#include <openengine/bullet/physic.hpp>
namespace Ogre
{
class Vector3;
}
namespace ESM
{
struct Position;
@ -63,6 +70,8 @@ namespace MWWorld
bool mCellChanged;
Environment& mEnvironment;
OEngine::Physic::PhysicEngine* mPhysEngine;
// not implemented
World (const World&);
World& operator= (const World&);
@ -83,14 +92,19 @@ namespace MWWorld
void loadCell (Ptr::CellStore *cell, MWRender::CellRender *render);
void playerCellChange (Ptr::CellStore *cell, const ESM::Position& position);
void playerCellChange (Ptr::CellStore *cell, const ESM::Position& position,
bool adjustPlayerPos = true);
void adjustSky();
void changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos);
///< Move from exterior to interior or from interior cell to a different
/// interior cell.
public:
World (OEngine::Render::OgreRenderer& renderer, const boost::filesystem::path& dataDir,
const std::string& master, const boost::filesystem::path& resDir, bool newGame, Environment& environment);
World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, const boost::filesystem::path& dataDir,
const std::string& master, const boost::filesystem::path& resDir, bool newGame,
Environment& environment);
~World();
@ -138,12 +152,11 @@ namespace MWWorld
float getTimeScaleFactor() const;
void changeCell (const std::string& cellName, const ESM::Position& position);
///< works only for interior cells currently.
void changeCell (int X, int Y, const ESM::Position& position);
void changeToInteriorCell (const std::string& cellName, const ESM::Position& position);
///< Move to interior cell.
void changeToExteriorCell (const ESM::Position& position);
///< Move to exterior cell.
const ESM::Cell *getExterior (const std::string& cellName) const;
///< Return a cell matching the given name or a 0-pointer, if there is no such cell.
@ -162,6 +175,14 @@ namespace MWWorld
void positionToIndex (float x, float y, int &cellX, int &cellY) const;
///< Convert position to cell numbers
void doPhysics (const std::vector<std::pair<std::string, Ogre::Vector3> >& actors,
float duration);
///< Run physics simulation and modify \a world accordingly.
void toggleCollisionMode();
///< Toggle collision mode for player. If disabled player object should ignore
/// collisions and gravity.
};
}

@ -0,0 +1,76 @@
# - Try to find the Bullet physics engine
#
# This module defines the following variables
#
# BULLET_FOUND - Was bullet found
# BULLET_INCLUDE_DIRS - the Bullet include directories
# BULLET_LIBRARIES - Link to this, by default it includes
# all bullet components (Dynamics,
# Collision, LinearMath, & SoftBody)
#
# This module accepts the following variables
#
# BULLET_ROOT - Can be set to bullet install path or Windows build path
#
# Copyright (c) 2009, Philip Lowman <philip at yhbt.com>
#
# Redistribution AND use is allowed according to the terms of the New
# BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
macro(_FIND_BULLET_LIBRARY _var)
find_library(${_var}
NAMES
${ARGN}
PATHS
${BULLET_ROOT}
${BULLET_ROOT}/out/release8/libs
${BULLET_ROOT}/out/debug8/libs
PATH_SUFFIXES lib
)
mark_as_advanced(${_var})
endmacro()
macro(_BULLET_APPEND_LIBRARIES _list _release)
set(_debug ${_release}_DEBUG)
if(${_debug})
set(${_list} ${${_list}} optimized ${${_release}} debug ${${_debug}})
else()
set(${_list} ${${_list}} ${${_release}})
endif()
endmacro()
find_path(BULLET_INCLUDE_DIR NAMES btBulletCollisionCommon.h
PATHS
${BULLET_ROOT}/include
${BULLET_ROOT}/src
PATH_SUFFIXES bullet
)
# Find the libraries
_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY BulletDynamics)
_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY_DEBUG BulletDynamics_d)
_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY BulletCollision)
_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY_DEBUG BulletCollision_d)
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY LinearMath BulletMath)
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY_DEBUG LinearMath_d BulletMath_d)
_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY BulletSoftBody)
_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY_DEBUG BulletSoftBody_d)
# handle the QUIETLY and REQUIRED arguments and set BULLET_FOUND to TRUE if
# all listed variables are TRUE
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Bullet DEFAULT_MSG
BULLET_DYNAMICS_LIBRARY BULLET_COLLISION_LIBRARY BULLET_MATH_LIBRARY
BULLET_SOFTBODY_LIBRARY BULLET_INCLUDE_DIR)
set(BULLET_INCLUDE_DIRS ${BULLET_INCLUDE_DIR})
if(BULLET_FOUND)
_BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_DYNAMICS_LIBRARY)
_BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_COLLISION_LIBRARY)
_BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_MATH_LIBRARY)
_BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_SOFTBODY_LIBRARY)
endif()

@ -133,7 +133,48 @@ namespace ESMS
loadRefs(store, esm);
}
/// Call functor (ref) for each reference. functor must return a bool. Returning
/// false will abort the iteration.
/// \return Iteration completed?
template<class Functor>
bool forEach (Functor& functor)
{
return
forEachImp (functor, activators) &&
forEachImp (functor, potions) &&
forEachImp (functor, appas) &&
forEachImp (functor, armors) &&
forEachImp (functor, books) &&
forEachImp (functor, clothes) &&
forEachImp (functor, containers) &&
forEachImp (functor, creatures) &&
forEachImp (functor, doors) &&
forEachImp (functor, ingreds) &&
forEachImp (functor, creatureLists) &&
forEachImp (functor, itemLists) &&
forEachImp (functor, lights) &&
forEachImp (functor, lockpicks) &&
forEachImp (functor, miscItems) &&
forEachImp (functor, npcs) &&
forEachImp (functor, probes) &&
forEachImp (functor, repairs) &&
forEachImp (functor, statics) &&
forEachImp (functor, weapons);
}
private:
template<class Functor, class List>
bool forEachImp (Functor& functor, List& list)
{
for (typename List::List::iterator iter (list.list.begin()); iter!=list.list.end();
++iter)
if (!functor (iter->ref, iter->mData))
return false;
return true;
}
void loadRefs(const ESMStore &store, ESMReader &esm)
{
assert (cell);

@ -83,7 +83,6 @@ void NIFFile::parse()
// NiNodes
if(rec == "NiNode" || rec == "AvoidNode" ||
rec == "RootCollisionNode" ||
rec == "NiBSParticleNode" ||
rec == "NiBSAnimationNode" ||
rec == "NiBillboardNode") { r = new NiNode; r->recType = RC_NiNode; }
@ -93,6 +92,8 @@ void NIFFile::parse()
else if(rec == "NiRotatingParticles") { r = new NiRotatingParticles; r->recType = RC_NiRotatingParticles; }
else if(rec == "NiAutoNormalParticles") { r = new NiAutoNormalParticles; r->recType = RC_NiAutoNormalParticles; }
else if(rec == "NiCamera") { r = new NiCamera; r->recType = RC_NiCamera; }
else if(rec == "RootCollisionNode"){ r = new NiNode; r->recType = RC_RootCollisionNode; }// a root collision node is exactly like a node
//that's why there is no need to create a new type
// Properties
else if(rec == "NiTexturingProperty") { r = new NiTexturingProperty; r->recType = RC_NiTexturingProperty; }

@ -79,7 +79,8 @@ enum RecordType
RC_NiAutoNormalParticlesData,
RC_NiSequenceStreamHelper,
RC_NiSourceTexture,
RC_NiSkinInstance
RC_NiSkinInstance,
RC_RootCollisionNode
};
/// Base class for all records

@ -0,0 +1,289 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (ogre_nif_loader.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 "bullet_nif_loader.hpp"
#include <Ogre.h>
#include <stdio.h>
#include <libs/mangle/vfs/servers/ogre_vfs.hpp>
#include "../nif/nif_file.hpp"
#include "../nif/node.hpp"
#include "../nif/data.hpp"
#include "../nif/property.hpp"
#include "../nif/controller.hpp"
#include "../nif/extra.hpp"
#include <libs/platform/strings.h>
#include <vector>
#include <list>
// For warning messages
#include <iostream>
// float infinity
#include <limits>
typedef unsigned char ubyte;
using namespace std;
using namespace Ogre;
using namespace Nif;
using namespace Mangle::VFS;
//====================================================================================================
Ogre::Matrix3 ManualBulletShapeLoader::getMatrix(Nif::Transformation* tr)
{
Ogre::Matrix3 rot(tr->rotation.v[0].array[0],tr->rotation.v[0].array[1],tr->rotation.v[0].array[2],
tr->rotation.v[1].array[0],tr->rotation.v[1].array[1],tr->rotation.v[1].array[2],
tr->rotation.v[2].array[0],tr->rotation.v[2].array[1],tr->rotation.v[2].array[2]);
return rot;
}
Ogre::Vector3 ManualBulletShapeLoader::getVector(Nif::Transformation* tr)
{
Ogre::Vector3 vect3(tr->pos.array[0],tr->pos.array[1],tr->pos.array[2]);
return vect3;
}
btQuaternion ManualBulletShapeLoader::getbtQuat(Ogre::Matrix3 m)
{
Ogre::Quaternion oquat(m);
btQuaternion quat;
quat.setW(oquat.w);
quat.setX(oquat.x);
quat.setY(oquat.y);
quat.setZ(oquat.z);
return quat;
}
btVector3 ManualBulletShapeLoader::getbtVector(Nif::Vector v)
{
btVector3 a(v.array[0],v.array[1],v.array[2]);
return a;
}
void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource)
{
cShape = static_cast<BulletShape *>(resource);
resourceName = cShape->getName();
cShape->collide = false;
currentShape = new btCompoundShape();
cShape->Shape = currentShape;
if (!vfs) vfs = new OgreVFS(resourceGroup);
if (!vfs->isFile(resourceName))
{
warn("File not found.");
return;
}
// Load the NIF. TODO: Wrap this in a try-catch block once we're out
// of the early stages of development. Right now we WANT to catch
// every error as early and intrusively as possible, as it's most
// likely a sign of incomplete code rather than faulty input.
Nif::NIFFile nif(vfs->open(resourceName), resourceName);
if (nif.numRecords() < 1)
{
warn("Found no records in NIF.");
return;
}
// The first record is assumed to be the root node
Nif::Record *r = nif.getRecord(0);
assert(r != NULL);
Nif::Node *node = dynamic_cast<Nif::Node*>(r);
if (node == NULL)
{
warn("First record in file was not a node, but a " +
r->recName.toString() + ". Skipping file.");
return;
}
//do a first pass
handleNode(node,0,Ogre::Matrix3::IDENTITY,Ogre::Vector3::ZERO,1,false,false);
//if collide = false, then it does a second pass which create a shape for raycasting.
if(cShape->collide == false)
{
handleNode(node,0,Ogre::Matrix3::IDENTITY,Ogre::Vector3::ZERO,1,false,true);
}
}
void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags,
Ogre::Matrix3 parentRot,Ogre::Vector3 parentPos,float parentScale,bool isCollisionNode,bool raycastingOnly)
{
// Accumulate the flags from all the child nodes. This works for all
// the flags we currently use, at least.
flags |= node->flags;
// Check for extra data
Nif::Extra *e = node;
while (!e->extra.empty())
{
// Get the next extra data in the list
e = e->extra.getPtr();
assert(e != NULL);
if (e->recType == Nif::RC_NiStringExtraData)
{
// String markers may contain important information
// affecting the entire subtree of this node
Nif::NiStringExtraData *sd = (Nif::NiStringExtraData*)e;
if (sd->string == "NCO" && !raycastingOnly)
{
// No collision. Use an internal flag setting to mark this.
// We ignor this node!
flags |= 0x800;
return;
}
else if (sd->string == "MRK" && !raycastingOnly)
// Marker objects. These are only visible in the
// editor. Until and unless we add an editor component to
// the engine, just skip this entire node.
return;
}
}
//transfo of parents node + curent node
Ogre::Matrix3 finalRot;
Ogre::Vector3 finalPos;
float finalScale;
Nif::Transformation &final = *((Nif::Transformation*)node->trafo);
Ogre::Vector3 nodePos = getVector(&final);
Ogre::Matrix3 nodeRot = getMatrix(&final);
finalPos = nodePos + parentPos;
finalRot = parentRot*nodeRot;
finalScale = final.scale*parentScale;
// For NiNodes, loop through children
if (node->recType == Nif::RC_NiNode)
{
Nif::NodeList &list = ((Nif::NiNode*)node)->children;
int n = list.length();
for (int i=0; i<n; i++)
{
if (list.has(i))
{
handleNode(&list[i], flags,finalRot,finalPos,finalScale,isCollisionNode,raycastingOnly);
}
}
}
else if (node->recType == Nif::RC_NiTriShape && isCollisionNode)
{
cShape->collide = true;
handleNiTriShape(dynamic_cast<Nif::NiTriShape*>(node), flags,finalRot,finalPos,finalScale,raycastingOnly);
}
else if(node->recType == Nif::RC_RootCollisionNode)
{
Nif::NodeList &list = ((Nif::NiNode*)node)->children;
int n = list.length();
for (int i=0; i<n; i++)
{
if (list.has(i))
handleNode(&list[i], flags,finalRot,finalPos,finalScale,true,raycastingOnly);
}
}
}
void ManualBulletShapeLoader::handleNiTriShape(Nif::NiTriShape *shape, int flags,Ogre::Matrix3 parentRot,Ogre::Vector3 parentPos,float parentScale,
bool raycastingOnly)
{
assert(shape != NULL);
btCollisionShape* NodeShape;
// Interpret flags
bool hidden = (flags & 0x01) != 0; // Not displayed
bool collide = (flags & 0x02) != 0; // Use mesh for collision
bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision
// If the object was marked "NCO" earlier, it shouldn't collide with
// anything. So don't do anything.
if (flags & 0x800 && !raycastingOnly)
{
collide = false;
bbcollide = false;
return;
}
if (!collide && !bbcollide && hidden && !raycastingOnly)
// This mesh apparently isn't being used for anything, so don't
// bother setting it up.
return;
btTransform tr;
tr.setRotation(getbtQuat(parentRot));
tr.setOrigin(btVector3(parentPos.x,parentPos.y,parentPos.z));
// Bounding box collision isn't implemented, always use mesh for now.
/*if (bbcollide)
{
return;
std::cout << "bbcolide?";
//TODO: check whether it's half box or not (is there a /2?)
NodeShape = new btBoxShape(btVector3(shape->boundXYZ->array[0]/2.,shape->boundXYZ->array[1]/2.,shape->boundXYZ->array[2]/2.));
std::cout << "bbcolide12121212121";
currentShape->addChildShape(tr,NodeShape);
std::cout << "aaaaaaaaaaaaa";
return;
collide = true;
bbcollide = false;
}*/
/* Do in-place transformation.the only needed transfo is the scale. (maybe not in fact)
*/
btTriangleMesh *mTriMesh = new btTriangleMesh();
Nif::NiTriShapeData *data = shape->data.getPtr();
float* vertices = (float*)data->vertices.ptr;
unsigned short* triangles = (unsigned short*)data->triangles.ptr;
for(unsigned int i=0; i < data->triangles.length; i = i+3)
{
btVector3 b1(vertices[triangles[i+0]*3]*parentScale,vertices[triangles[i+0]*3+1]*parentScale,vertices[triangles[i+0]*3+2]*parentScale);
btVector3 b2(vertices[triangles[i+1]*3]*parentScale,vertices[triangles[i+1]*3+1]*parentScale,vertices[triangles[i+1]*3+2]*parentScale);
btVector3 b3(vertices[triangles[i+2]*3]*parentScale,vertices[triangles[i+2]*3+1]*parentScale,vertices[triangles[i+2]*3+2]*parentScale);
mTriMesh->addTriangle(b1,b2,b3);
}
NodeShape = new btBvhTriangleMeshShape(mTriMesh,true);
currentShape->addChildShape(tr,NodeShape);
}
void ManualBulletShapeLoader::load(const std::string &name,const std::string &group)
{
// Check if the resource already exists
Ogre::ResourcePtr ptr = BulletShapeManager::getSingleton().getByName(name, group);
if (!ptr.isNull())
return;
BulletShapeManager::getSingleton().create(name,group,true,this);
}

@ -0,0 +1,124 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (ogre_nif_loader.h) 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/ .
*/
#ifndef _BULLET_NIF_LOADER_H_
#define _BULLET_NIF_LOADER_H_
#include <OgreMesh.h>
#include <assert.h>
#include <string>
#include <BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h>
#include <BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h>
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include "openengine/bullet/BulletShapeLoader.h"
#include <vector>
#include <list>
// For warning messages
#include <iostream>
// float infinity
#include <limits>
namespace Nif
{
class Node;
class Transformation;
class NiTriShape;
class Vector;
class Matrix;
}
namespace Mangle
{
namespace VFS
{
class OgreVFS;
}
}
/**
*Load bulletShape from NIF files.
*/
class ManualBulletShapeLoader : public BulletShapeLoader
{
public:
ManualBulletShapeLoader():resourceGroup("General"){vfs = 0;}
virtual ~ManualBulletShapeLoader() {}
void warn(std::string msg)
{
std::cerr << "NIFLoader: Warn:" << msg << "\n";
}
void fail(std::string msg)
{
std::cerr << "NIFLoader: Fail: "<< msg << std::endl;
assert(1);
}
/**
*This function should not be called manualy. Use load instead. (this is called by the BulletShapeManager when you use load).
*/
void loadResource(Ogre::Resource *resource);
/**
*This function load a new bulletShape from a NIF file into the BulletShapeManager.
*When the file is loaded, you can then use BulletShapeManager::getByName() to retrive the bulletShape.
*Warning: this function will just crash if the resourceGroup doesn't exist!
*/
void load(const std::string &name,const std::string &group);
private:
Ogre::Matrix3 getMatrix(Nif::Transformation* tr);
Ogre::Vector3 getVector(Nif::Transformation* tr);
btQuaternion getbtQuat(Ogre::Matrix3 m);
btVector3 getbtVector(Nif::Vector v);
/**
*Parse a node.
*/
void handleNode(Nif::Node *node, int flags,
Ogre::Matrix3 parentRot,Ogre::Vector3 parentPos,float parentScale,bool isCollisionNode,bool raycastingOnly);
/**
*convert a NiTriShape to a bullet trishape.
*/
void handleNiTriShape(Nif::NiTriShape *shape, int flags,Ogre::Matrix3 parentRot,Ogre::Vector3 parentPos,float parentScales,bool raycastingOnly);
Mangle::VFS::OgreVFS *vfs;
std::string resourceName;
std::string resourceGroup;
BulletShape* cShape;//current shape
btCompoundShape* currentShape;//the shape curently under construction
};
#endif

@ -0,0 +1,209 @@
#include "bullet_nif_loader.hpp"
#include "..\nifogre\ogre_nif_loader.hpp"
#include "..\bsa\bsa_archive.hpp"
#include "..\nifogre\ogre_nif_loader.hpp"
#include <Ogre.h>
#include <OIS.h>
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include "BtOgrePG.h"
#include "BtOgreGP.h"
#include "BtOgreExtras.h"
const char* mesh = "meshes\\x\\ex_hlaalu_b_24.nif";
class MyMotionState : public btMotionState {
public:
MyMotionState(const btTransform &initialpos, Ogre::SceneNode *node) {
mVisibleobj = node;
mPos1 = initialpos;
node->setPosition(initialpos.getOrigin().x(),initialpos.getOrigin().y(),initialpos.getOrigin().z());
}
virtual ~MyMotionState() {
}
void setNode(Ogre::SceneNode *node) {
mVisibleobj = node;
}
virtual void getWorldTransform(btTransform &worldTrans) const {
worldTrans = mPos1;
}
virtual void setWorldTransform(const btTransform &worldTrans) {
if(NULL == mVisibleobj) return; // silently return before we set a node
btQuaternion rot = worldTrans.getRotation();
mVisibleobj->setOrientation(rot.w(), rot.x(), rot.y(), rot.z());
btVector3 pos = worldTrans.getOrigin();
mVisibleobj->setPosition(pos.x(), pos.y(), pos.z());
}
protected:
Ogre::SceneNode *mVisibleobj;
btTransform mPos1;
};
int main()
{
try
{
//Ogre stuff
Ogre::Root* pRoot = new Ogre::Root();
pRoot->showConfigDialog();
BulletShapeManager* manag = new BulletShapeManager();
Ogre::RenderWindow* win = pRoot->initialise(true,"test");
Ogre::SceneManager* scmg = pRoot->createSceneManager(Ogre::ST_GENERIC,"MonGestionnaireDeScene");
Ogre::Camera* pCamera = scmg->createCamera("test");
Ogre::Viewport* pViewport = win->addViewport(pCamera);
pCamera->setPosition(-50,0,0);
pCamera->setFarClipDistance(10000);
pCamera->setNearClipDistance(1.);
pCamera->lookAt(0,0,0);
//Ogre::ResourceGroupManager::getSingleton().addResourceLocation("C++/OgreSK/media/models","FileSystem","General");
Ogre::ResourceGroupManager::getSingleton().addResourceLocation("","FileSystem","General");
/*Ogre::ResourceGroupManager::getSingleton().addResourceLocation("C++/OgreSK/media/materials/scripts","FileSystem","General");
Ogre::ResourceGroupManager::getSingleton().addResourceLocation("C++/OgreSK/media/materials/textures","FileSystem","General");
Ogre::ResourceGroupManager::getSingleton().addResourceLocation("C++/OgreSK/media/materials/programs","FileSystem","General");*/
//OIS stuff
OIS::ParamList pl;
size_t windowHnd = 0;
std::ostringstream windowHndStr;
win->getCustomAttribute("WINDOW", &windowHnd);
windowHndStr << windowHnd;
pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
OIS::InputManager *pInputManager = OIS::InputManager::createInputSystem( pl );
OIS::Mouse *pMouse = static_cast<OIS::Mouse*>(pInputManager->createInputObject(OIS::OISMouse, false));
OIS::Keyboard* pKeyboard = static_cast<OIS::Keyboard*>(pInputManager->createInputObject(OIS::OISKeyboard, false));
unsigned int width, height, depth;
int top, left;
win->getMetrics(width, height, depth, left, top);
const OIS::MouseState &ms = pMouse->getMouseState();
ms.width = width;
ms.height = height;
//Ressources stuff
addBSA("Morrowind.bsa");
//Ogre::ResourceGroupManager::getSingleton().createResourceGroup("general");
Ogre::ResourcePtr ptr = BulletShapeManager::getSingleton().getByName(mesh,"General");
ManualBulletShapeLoader* ShapeLoader = new ManualBulletShapeLoader();
ShapeLoader->load(mesh,"General");
//BulletShapeManager::getSingleton().unload(mesh);
//ShapeLoader->load(mesh,"General");
NIFLoader::load(mesh);
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
//BulletShapeManager::getSingleton().
BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(mesh,"General");
BulletShapeManager::getSingleton().load(mesh,"General");
BulletShapeManager::getSingleton().unload(mesh);
BulletShapeManager::getSingleton().load(mesh,"General");
BulletShapeManager::getSingleton().load(mesh,"General");
//shape->load();
//shape->unload();
//shape->load();
//Bullet init
btBroadphaseInterface* broadphase = new btDbvtBroadphase();
// Set up the collision configuration and dispatcher
btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration();
btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);
// The actual physics solver
btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
// The world.
btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
dynamicsWorld->setGravity(btVector3(0,-10,0));
//le sol?
Ogre::SceneNode *node = scmg->getRootSceneNode()->createChildSceneNode("node");
Ogre::Entity *ent = scmg->createEntity("Mesh1",mesh);
node->attachObject(ent);
MyMotionState* mst = new MyMotionState(btTransform::getIdentity(),node);
btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI(0,mst,shape->Shape,btVector3(0,0,0));
btRigidBody* groundRigidBody = new btRigidBody(groundRigidBodyCI);
dynamicsWorld->addRigidBody(groundRigidBody);
//une balle:
Ogre::SceneNode *node2 = scmg->getRootSceneNode()->createChildSceneNode("node2");
Ogre::Entity *ent2 = scmg->createEntity("Mesh2","ogrehead.mesh");
node2->attachObject(ent2);
node2->setPosition(0,500,0);
btTransform iT;
iT.setIdentity();
iT.setOrigin(btVector3(0,5000,0));
MyMotionState* mst2 = new MyMotionState(btTransform::getIdentity(),node2);
btSphereShape* sphereshape = new btSphereShape(10);
btRigidBody::btRigidBodyConstructionInfo sphereCI(10,mst2,sphereshape,btVector3(0,0,0));
btRigidBody* sphere = new btRigidBody(sphereCI);
dynamicsWorld->addRigidBody(sphere);
//btOgre!
BtOgre::DebugDrawer* mDebugDrawer = new BtOgre::DebugDrawer(scmg->getRootSceneNode(), dynamicsWorld);
dynamicsWorld->setDebugDrawer(mDebugDrawer);
Ogre::Timer timer;
timer.reset();
bool cont = true;
while(cont)
{
if(timer.getMilliseconds()>30)
{
pMouse->capture();
pKeyboard->capture();
Ogre::Vector3 a(0,0,0);
if(pKeyboard->isKeyDown(OIS::KC_UP))
{
a = a + Ogre::Vector3(0,0,-20);
}
if(pKeyboard->isKeyDown(OIS::KC_DOWN))
{
a = a + Ogre::Vector3(0,0,20);
}
if(pKeyboard->isKeyDown(OIS::KC_ESCAPE))
{
cont = false;
}
OIS::MouseState MS = pMouse->getMouseState();
pCamera->yaw(-Ogre::Degree(MS.X.rel));
pCamera->pitch(-Ogre::Degree(MS.Y.rel));
pCamera->moveRelative(a);
pRoot->renderOneFrame();
mDebugDrawer->step();
timer.reset();
dynamicsWorld->stepSimulation(0.03);
}
}
std::cout << "cool";
delete manag;
delete pRoot;
char a;
std::cin >> a;
}
catch(Ogre::Exception& e)
{
std::cout << e.getFullDescription();
char a;
std::cin >> a;
}
}

@ -1 +1 @@
Subproject commit c60a48b397b2077e9a81fdbf3fe31c83276f6ffa
Subproject commit 25b3cf935967beb5011419b6d3911fca05863743
Loading…
Cancel
Save