Merge branch 'master' of https://github.com/zinnschlag/openmw.git into DialogueSystem

actorid
gugus 13 years ago
commit 10321ff51a

6
.gitmodules vendored

@ -1,6 +0,0 @@
[submodule "libs/mangle"]
path = libs/mangle
url = git://github.com/zinnschlag/mangle.git
[submodule "libs/openengine"]
path = libs/openengine
url = git://github.com/zinnschlag/OpenEngine

@ -255,6 +255,13 @@ if (APPLE)
"${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY)
# prepare plugins
if (${CMAKE_BUILD_TYPE} MATCHES "Release" OR
${CMAKE_BUILD_TYPE} MATCHES "RelWithDebugInfo")
set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL})
else()
set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG})
endif()
foreach(plugin ${USED_OGRE_PLUGINS})
configure_file("${OGRE_PLUGIN_DIR}/${plugin}.dylib"
"${APP_BUNDLE_DIR}/Contents/Plugins/${plugin}.dylib"
@ -265,8 +272,7 @@ endif (APPLE)
# Compiler settings
if (CMAKE_COMPILER_IS_GNUCC)
#add_definitions (-Wall -Werror)
add_definitions (-Wall)
add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-unused-but-set-parameter -Wno-reorder)
endif (CMAKE_COMPILER_IS_GNUCC)
if(DPKG_PROGRAM)
@ -343,6 +349,9 @@ if(WIN32)
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")
SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.ico")
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.ico")
# SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp")
SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe")
if(EXISTS ${VCREDIST32})
@ -402,6 +411,45 @@ if (WIN32)
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
# Play a bit with the warning levels
set(WARNINGS "/Wall") # Since windows can only disable specific warnings, not enable them
set(WARNINGS_DISABLE
# Warnings that aren't enabled normally and don't need to be enabled
# They're unneeded and sometimes completely retarded warnings that /Wall enables
# Not going to bother commenting them as they tend to warn on every standard library files
4061 4263 4264 4266 4350 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946
# Warnings that are thrown on standard libraries and not OpenMW
4347 # Non-template function with same name and parameter count as template function
4365 # Variable signed/unsigned mismatch
4510 4512 # Unable to generate copy constructor/assignment operator as it's not public in the base
4706 # Assignment in conditional expression
4738 # Storing 32-bit float result in memory, possible loss of performance
4986 # Undocumented warning that occurs in the crtdbg.h file
4996 # Function was declared deprecated
# OpenMW specific warnings
4099 # Type mismatch, declared class or struct is defined with other type
4100 # Unreferenced formal parameter (-Wunused-parameter)
4127 # Conditional expression is constant
4242 # Storing value in a variable of a smaller type, possible loss of data
4244 # Storing value of one type in variable of another (size_t in int, for example)
4305 # Truncating value (double to float, for example)
4309 # Variable overflow, trying to store 128 in a signed char for example
4355 # Using 'this' in member initialization list
4701 # Potentially uninitialized local variable used
)
foreach(d ${WARNINGS_DISABLE})
set(WARNINGS "${WARNINGS} /wd${d}")
endforeach(d)
set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS})
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS})
set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS})
endif(MSVC)
# Same for MinGW

@ -1,80 +1,79 @@
NOTE: This README is for ardekantur's Mac branch of OpenMW. A README
for the main branch has yet to be written. If you want to submit one,
please send me a message!
#Getting OpenMW Working on OS X
OpenMW
======
## Initial setup
First of all, clone OpenMW repo.
From the [official website][]:
$ git clone github.com/zinnschlag/openmw
> OpenMW is an attempt to reimplement the popular role playing game
Morrowind. It aims to be a fully playable, open source
implementation of the game. You must own Morrowind to use OpenMW.
Or use your github url if you forked.
About dependencies: I prefer not to install them globally (i. e. in /usr/local/), so I'm installing them in directory in my home directory. If OpenMW sources is in $HOME/path/openmw, I'm using $HOME/path/libs/root as prefix for boost and other libs.
About This Project
------------------
It's useful to create env var for lib install prefix:
$ export OMW_LIB_PREFIX=$HOME/path/libs/root`
This specific repository is a branch of OpenMW intended to keep pace
with development of the project in order to provide a Mac build for
interested parties to contribute. This is not an official, sanctioned
branch of the OpenMW project. I will only be able to answer specific
questions about getting this project running on Mac OS X, **no other
platform**. I will not even be able to guarantee my changes maintain
backwards compatibility against builds in other operating systems. You
have been warned.
Most of libs can be installed from [Homebrew][homebrew]. Only mpg123 needs to be installed from source (due to lack of universal compilation support). I think that some of libs can be installed from MacPorts or Fink too.
As OpenMW currently only supports i386 architecture on OS X, denendencies also should support it. Set some env vars in current terminal:
Getting OpenMW Working
----------------------
1. Clone this repository.
2. Note about libs: I prefer not to install them globally (i. e. in /usr/local/), so I installing them in directory in my home directory. If OpenMW sources is in $HOME/path/openmw, I'm using $HOME/path/libs/root as prefix for boost and other libs.
It's useful to create env var for lib install prefix:
$ export OMW_LIB_PREFIX=$HOME/path/libs/root
3. First of all, set for current terminal some env vars:
$ export CFLAGS="-arch i386"
$ export CXXFLAGS="-arch i386"
$ export LDFLAGS="-arch i386"
All libs will build with correct architecture.
If you close your terminal, you should set env vars again before pcoceeding to next steps!
4. Download [boost][] (tested with 1.45) and install it with the following command:
If you close your terminal, you should set env vars again before pcoceeding to next steps!
## Boost
Download [boost][boost] and install it with the following command:
$ cd /path/to/boost/source
$ ./bootstrap.sh --prefix=$OMW_LIB_PREFIX
$ ./bjam --build-dir=build --layout=versioned \
--toolset=darwin architecture=x86 address-model=32 \
--link-shared,static --prefix=$OMW_LIB_PREFIX install
Alternatively you can install boost with homebrew:
$ brew install boost --universal
5. Download [Ogre][] SDK (tested with 1.7.2), unpack it and move
`lib/Release/Ogre.framework` into `Library/Frameworks`.
I think MacPorts also should support universal build for boost.
6. Download [OIS][] and use the XCode project provided in
`ois/Mac/XCode-2.2`. Be sure to set your build architecture to
`i386` and your SDK platform to either 10.5 or 10.6. Once it
builds, move `ois/Mac/XCode-2.2/build/Debug/OIS.framework` to
`/Library/Frameworks`.
## Ogre
Download [Ogre][] SDK (tested with 1.7.3), unpack it somewhere and move
`lib/Release/Ogre.framework` into `/Library/Frameworks`.
## OIS
Download patched [OIS][] and use the XCode project provided. Be sure to set your build architecture to
`i386`. Once it built, locate built OIS.framework with Xcode and move it to `/Library/Frameworks`.
## mpg123
Download [MPG 123][mpg123] and build it:
7. Download [mpg123][] and build it:
$ cd /path/to/mpg123/source
$ ./configure --prefix=$OMW_LIB_PREFIX --disable-debug \
--disable-dependency-tracking \
--with-optimization=4 \
--with-audio=coreaudio \
--with-default-audio=coreaudio \
--with-audio=dummy \
--with-default-audio=dummy \
--with-cpu=sse_alone \
$ make install
8. Download [libsndfile][] and build it:
## libsndfile
Download [libsndfile][] and build it:
$ cd /path/to/libsndfile/source
$ ./configure --prefix=$OMW_LIB_PREFIX \
--disable-dependency-tracking
$ make install
9. Download [Bullet][] and build it:
or install with homebrew:
$ brew install libsndfile --universal
## Bullet
Download [Bullet][] and build it:
$ cd /path/to/bullet/source
$ mkdir build
$ cd build
@ -87,12 +86,25 @@ Getting OpenMW Working
-G"Unix Makefiles" ../
$ make install
10. Generate the Makefile for OpenMW as follows and build OpenMW:
or install with homebrew:
$ brew install bullet --HEAD --universal
I prefer head because 2.79 has some issue which causes OpenMW to lag. Also you can edit formula and install 2.77, which is stable and haven't mentioned issue.
## Qt
Install [Qt][qt]. Qt SDK distributed by Nokia is not an option because it's 64 bit only, and OpenMW currently doesn't build for 64 bit on OS X. I'm installing it from Homebrew:
$ brew install qt --universal
## Run CMake
Generate the Makefile for OpenMW as follows and build OpenMW:
$ mkdir /path/to/openmw/build/dir
$ cd /path/to/open/build/dir
$ cmake \
-D CMAKE_OSX_ARCHITECTURES=i386 \
-D OGRESDK=/path/to/ogre/sdk \
-D OGRE_SDK=/path/to/ogre/sdk \
-D BOOST_INCLUDEDIR=$OMW_LIB_PREFIX/include/boost-1_45 \
-D BOOST_LIBRARYDIR=$OMW_LIB_PREFIX/lib \
-D SNDFILE_INCLUDE_DIR=$OMW_LIB_PREFIX/include \
@ -106,27 +118,43 @@ Getting OpenMW Working
-D BULLET_INCLUDE_DIR=$OMW_LIB_PREFIX/include/bullet/ \
-G "Unix Makefiles" /path/to/openmw/source/dir
$ make
You can use -G"Xcode" if you prefer Xcode, or -G"Eclipse CDT4 - Unix Makefiles"
if you prefer Eclipse. You also can specify -D CMAKE_BUILD_TYPE=Debug for debug
build.
You can use `-G"Xcode"` if you prefer Xcode, or -G"Eclipse CDT4 - Unix Makefiles"
if you prefer Eclipse. You also can specify `-D CMAKE_BUILD_TYPE=Debug` for debug
build. As for CMake 2.8.7 and Xcode 4.3, Xcode generator is broken. Sadly Eclipse CDT also cannot import generated project at least on my machine.
If all libs installed via homebrew (excluding mpg123), then command would be even simplier:
$ cmake \
-D CMAKE_OSX_ARCHITECTURES="i386" \
-D OGRE_SDK=/path/to/ogre/sdk \
-D MPG123_LIBRARY=$OMW_LIB_PREFIX/lib/libmpg123.a \
-D MPG123_INCLUDE_DIR=$OMW_LIB_PREFIX/include \
-G "Unix Makefiles" /path/to/openmw/source/dir
$ make
Note for users with recent Xcode versions: you must explicitly specify what set of compilers do you use! If not, gcc will be used for C and Clang for C++. Just add this two -D's to command: `-D CMAKE_C_COMPILER=/usr/bin/clang` and `-D CMAKE_CXX_COMPILER=/usr/bin/clang`
Note for Xcode 4.3 users: you should specify full path to used SDK, because current CMake (2.8.7) couldn't find SDKs inside Xcode app bundle:
-D CMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk"
11. Copy your Morrowind `Data Files` directory into the OpenMW build dir
with the name `data` or create symlink:
$ ln -s /path/to/morrowind/data/files /path/to/openmw/build/dir/data
# Run
From your build directory run:
12. From your build directory run:
$ OpenMW.app/Contents/MacOS/openmw
or:
$ open OpenMW.app
Enjoy!
or:
$ open OpenMW.app
Enjoy!
[homebrew]: https://github.com/mxcl/homebrew
[boost]: http://www.boost.org
[Ogre]: http://www.ogre3d.org
[Bullet]: http://bulletphysics.org
[OIS]: http://wgois.sf.net
[OIS]: https://github.com/corristo/ois-fork
[mpg123]: http://www.mpg123.de
[libsndfile]: http://www.mega-nerd.com/libsndfile
[official website]: http://openmw.com
[Will Thimbleby's Ogre Framework]: http://www.thimbleby.net/ogre/
[qt]: http://qt.nokia.com/

@ -41,10 +41,11 @@ source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER} ${LAUNCHER_HEADER_MOC
find_package(Qt4 REQUIRED)
set(QT_USE_QTGUI 1)
if (NOT APPLE) # this dependency can be completely removed, but now it only tested on OS X
find_package(PNG REQUIRED)
include_directories(${PNG_INCLUDE_DIR})
endif()
# Set some platform specific settings
if(WIN32)
set(GUI_TYPE WIN32)
set(QT_USE_QTMAIN TRUE)
endif(WIN32)
QT4_ADD_RESOURCES(RCC_SRCS resources.qrc)
QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
@ -53,6 +54,7 @@ include(${QT_USE_FILE})
# Main executable
add_executable(omwlauncher
${GUI_TYPE}
${LAUNCHER}
${RCC_SRCS}
${MOC_SRCS}
@ -62,7 +64,6 @@ target_link_libraries(omwlauncher
${Boost_LIBRARIES}
${OGRE_LIBRARIES}
${QT_LIBRARIES}
${PNG_LIBRARY}
components
)

@ -9,6 +9,23 @@
#include "pluginsmodel.hpp"
#include "pluginsview.hpp"
#include <boost/version.hpp>
/**
* Workaround for problems with whitespaces in paths in older versions of Boost library
*/
#if (BOOST_VERSION <= 104600)
namespace boost
{
template<>
inline boost::filesystem::path lexical_cast<boost::filesystem::path, std::string>(const std::string& arg)
{
return boost::filesystem::path(arg);
}
} /* namespace boost */
#endif /* (BOOST_VERSION <= 104600) */
using namespace ESM;
using namespace std;

@ -44,7 +44,7 @@ add_openmw_dir (mwsound
add_openmw_dir (mwworld
refdata world physicssystem scene environment globals class action nullaction actionteleport
containerstore actiontalk actiontake manualref player cellfunctors
cells localscripts customdata weather
cells localscripts customdata weather inventorystore
)
add_openmw_dir (mwclass

@ -118,8 +118,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
// sound
if (mUseSound)
{
if (!mEnvironment.mSoundManager->isMusicPlaying())
mEnvironment.mSoundManager->startRandomTitle();
mEnvironment.mSoundManager->playPlaylist();
mEnvironment.mSoundManager->update (evt.timeSinceLastFrame);
}
@ -340,7 +339,6 @@ void OMW::Engine::go()
// Create sound system
mEnvironment.mSoundManager = new MWSound::SoundManager(mOgre->getRoot(),
mOgre->getCamera(),
mEnvironment.mWorld->getStore(),
mDataDirs,
mUseSound, mFSStrict, mEnvironment);
@ -389,7 +387,7 @@ void OMW::Engine::go()
mOgre->getRoot()->addFrameListener (this);
// Play some good 'ol tunes
mEnvironment.mSoundManager->startRandomTitle();
mEnvironment.mSoundManager->playPlaylist(std::string("Explore"));
// scripts
if (mCompileAll)

@ -7,9 +7,12 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/environment.hpp"
#include "../mwrender/objects.hpp"
#include "../mwsound/soundmanager.hpp"
namespace MWClass
{
void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -53,6 +56,8 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Apparatus::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, false, true);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -71,4 +76,14 @@ namespace MWClass
registerClass (typeid (ESM::Apparatus).name(), instance);
}
std::string Apparatus::getUpSoundId (const MWWorld::Ptr& ptr) const
{
return std::string("Item Apparatus Up");
}
std::string Apparatus::getDownSoundId (const MWWorld::Ptr& ptr) const
{
return std::string("Item Apparatus Down");
}
}

@ -26,6 +26,12 @@ namespace MWClass
///< Return name of the script attached to ptr
static void registerSelf();
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const;
///< Return the pick up sound Id
virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const;
///< Return the put down sound Id
};
}

@ -2,14 +2,22 @@
#include "armor.hpp"
#include <components/esm/loadarmo.hpp>
#include <components/esm/loadskil.hpp>
#include <components/esm/loadgmst.hpp>
#include <components/esm_store/cell_store.hpp>
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/environment.hpp"
#include "../mwworld/world.hpp"
#include "../mwrender/objects.hpp"
#include "../mwsound/soundmanager.hpp"
namespace MWClass
{
void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -52,6 +60,8 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Armor::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -77,10 +87,105 @@ namespace MWClass
return ref->base->script;
}
std::pair<std::vector<int>, bool> Armor::getEquipmentSlots (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Armor, MWWorld::RefData> *ref =
ptr.get<ESM::Armor>();
std::vector<int> slots;
const int size = 11;
static const int sMapping[size][2] =
{
{ ESM::Armor::Helmet, MWWorld::InventoryStore::Slot_Helmet },
{ ESM::Armor::Cuirass, MWWorld::InventoryStore::Slot_Cuirass },
{ ESM::Armor::LPauldron, MWWorld::InventoryStore::Slot_LeftPauldron },
{ ESM::Armor::RPauldron, MWWorld::InventoryStore::Slot_RightPauldron },
{ ESM::Armor::Greaves, MWWorld::InventoryStore::Slot_Greaves },
{ ESM::Armor::Boots, MWWorld::InventoryStore::Slot_Boots },
{ ESM::Armor::LGauntlet, MWWorld::InventoryStore::Slot_LeftGauntlet },
{ ESM::Armor::RGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet },
{ ESM::Armor::Shield, MWWorld::InventoryStore::Slot_CarriedLeft },
{ ESM::Armor::LBracer, MWWorld::InventoryStore::Slot_LeftGauntlet },
{ ESM::Armor::RBracer, MWWorld::InventoryStore::Slot_RightGauntlet }
};
for (int i=0; i<size; ++i)
if (sMapping[i][0]==ref->base->data.type)
{
slots.push_back (int (sMapping[i][1]));
break;
}
return std::make_pair (slots, false);
}
int Armor::getEquipmentSkill (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Armor, MWWorld::RefData> *ref =
ptr.get<ESM::Armor>();
std::string typeGmst;
switch (ref->base->data.type)
{
case ESM::Armor::Helmet: typeGmst = "iHelmWeight"; break;
case ESM::Armor::Cuirass: typeGmst = "iCuirassWeight"; break;
case ESM::Armor::LPauldron:
case ESM::Armor::RPauldron: typeGmst = "iPauldronWeight"; break;
case ESM::Armor::Greaves: typeGmst = "iGreavesWeight"; break;
case ESM::Armor::Boots: typeGmst = "iBootsWeight"; break;
case ESM::Armor::LGauntlet:
case ESM::Armor::RGauntlet: typeGmst = "iGauntletWeight"; break;
/// \todo how to determine if shield light, medium or heavy?
// case ESM::Armor::Shield:
case ESM::Armor::LBracer:
case ESM::Armor::RBracer: typeGmst = "iGauntletWeight"; break;
}
if (typeGmst.empty())
return -1;
float iWeight = environment.mWorld->getStore().gameSettings.find (typeGmst)->i;
if (iWeight * environment.mWorld->getStore().gameSettings.find ("fLightMaxMod")->f>=
ref->base->data.weight)
return ESM::Skill::LightArmor;
if (iWeight * environment.mWorld->getStore().gameSettings.find ("fMedMaxMod")->f>=
ref->base->data.weight)
return ESM::Skill::MediumArmor;
return ESM::Skill::HeavyArmor;
}
void Armor::registerSelf()
{
boost::shared_ptr<Class> instance (new Armor);
registerClass (typeid (ESM::Armor).name(), instance);
}
std::string Armor::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
int es = getEquipmentSkill(ptr, environment);
if (es == ESM::Skill::LightArmor)
return std::string("Item Armor Light Up");
else if (es == ESM::Skill::MediumArmor)
return std::string("Item Armor Medium Up");
else
return std::string("Item Armor Heavy Up");
}
std::string Armor::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
int es = getEquipmentSkill(ptr, environment);
if (es == ESM::Skill::LightArmor)
return std::string("Item Armor Light Down");
else if (es == ESM::Skill::MediumArmor)
return std::string("Item Armor Medium Down");
else
return std::string("Item Armor Heavy Down");
}
}

@ -31,7 +31,22 @@ namespace MWClass
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
///< Return name of the script attached to ptr
virtual std::pair<std::vector<int>, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const;
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
/// stay stacked when equipped?
virtual int getEquipmentSkill (const MWWorld::Ptr& ptr,
const MWWorld::Environment& environment) const;
/// Return the index of the skill this item corresponds to when equiopped or -1, if there is
/// no such skill.
static void registerSelf();
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the pick up sound Id
virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the put down sound Id
};
}

@ -7,9 +7,12 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/environment.hpp"
#include "../mwrender/objects.hpp"
#include "../mwsound/soundmanager.hpp"
namespace MWClass
{
void Book::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -55,6 +58,8 @@ namespace MWClass
{
// TODO implement reading
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -73,4 +78,14 @@ namespace MWClass
registerClass (typeid (ESM::Book).name(), instance);
}
std::string Book::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Book Up");
}
std::string Book::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Book Down");
}
}

@ -26,6 +26,12 @@ namespace MWClass
///< Return name of the script attached to ptr
static void registerSelf();
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the pick up sound Id
virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the put down sound Id
};
}

@ -7,9 +7,13 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/environment.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwrender/objects.hpp"
#include "../mwsound/soundmanager.hpp"
namespace MWClass
{
void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -53,6 +57,8 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Clothing::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -65,10 +71,86 @@ namespace MWClass
return ref->base->script;
}
std::pair<std::vector<int>, bool> Clothing::getEquipmentSlots (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Clothing, MWWorld::RefData> *ref =
ptr.get<ESM::Clothing>();
std::vector<int> slots;
if (ref->base->data.type==ESM::Clothing::Ring)
{
slots.push_back (int (MWWorld::InventoryStore::Slot_LeftRing));
slots.push_back (int (MWWorld::InventoryStore::Slot_RightRing));
}
else
{
const int size = 9;
static const int sMapping[size][2] =
{
{ ESM::Clothing::Shirt, MWWorld::InventoryStore::Slot_Cuirass },
{ ESM::Clothing::Belt, MWWorld::InventoryStore::Slot_Belt },
{ ESM::Clothing::Robe, MWWorld::InventoryStore::Slot_Robe },
{ ESM::Clothing::Pants, MWWorld::InventoryStore::Slot_Pants },
{ ESM::Clothing::Shoes, MWWorld::InventoryStore::Slot_Boots },
{ ESM::Clothing::LGlove, MWWorld::InventoryStore::Slot_LeftGauntlet },
{ ESM::Clothing::RGlove, MWWorld::InventoryStore::Slot_RightGauntlet },
{ ESM::Clothing::Skirt, MWWorld::InventoryStore::Slot_Skirt },
{ ESM::Clothing::Amulet, MWWorld::InventoryStore::Slot_Amulet }
};
for (int i=0; i<size; ++i)
if (sMapping[i][0]==ref->base->data.type)
{
slots.push_back (int (sMapping[i][1]));
break;
}
}
return std::make_pair (slots, false);
}
int Clothing::getEquipmentSkill (const MWWorld::Ptr& ptr,
const MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Clothing, MWWorld::RefData> *ref =
ptr.get<ESM::Clothing>();
if (ref->base->data.type==ESM::Clothing::Shoes)
return ESM::Skill::Unarmored;
return -1;
}
void Clothing::registerSelf()
{
boost::shared_ptr<Class> instance (new Clothing);
registerClass (typeid (ESM::Clothing).name(), instance);
}
std::string Clothing::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Clothing, MWWorld::RefData> *ref =
ptr.get<ESM::Clothing>();
if (ref->base->data.type == 8)
{
return std::string("Item Ring Up");
}
return std::string("Item Clothes Up");
}
std::string Clothing::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Clothing, MWWorld::RefData> *ref =
ptr.get<ESM::Clothing>();
if (ref->base->data.type == 8)
{
return std::string("Item Ring Down");
}
return std::string("Item Clothes Down");
}
}

@ -25,7 +25,22 @@ namespace MWClass
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
///< Return name of the script attached to ptr
virtual std::pair<std::vector<int>, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const;
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
/// stay stacked when equipped?
virtual int getEquipmentSkill (const MWWorld::Ptr& ptr,
const MWWorld::Environment& environment) const;
/// Return the index of the skill this item corresponds to when equiopped or -1, if there is
/// no such skill.
static void registerSelf();
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the pick up sound Id
virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the put down sound Id
};
}

@ -85,7 +85,7 @@ namespace MWClass
{
// TODO check for key
std::cout << "Locked container" << std::endl;
environment.mSoundManager->playSound(lockedSound, 1.0, 1.0);
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false);
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}
else
@ -100,7 +100,7 @@ namespace MWClass
{
// Trap activation goes here
std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl;
environment.mSoundManager->playSound(trapActivationSound, 1.0, 1.0);
environment.mSoundManager->playSound3D (ptr, trapActivationSound, 1.0, 1.0, false);
ptr.getCellRef().trap = "";
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}

@ -73,7 +73,7 @@ namespace MWClass
// TODO check for key
// TODO report failure to player (message, sound?). Look up behaviour of original MW.
std::cout << "Locked!" << std::endl;
environment.mSoundManager->playSound(lockedSound, 1.0, 1.0);
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false);
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}
@ -81,7 +81,7 @@ namespace MWClass
{
// Trap activation
std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl;
environment.mSoundManager->playSound(trapActivationSound, 1.0, 1.0);
environment.mSoundManager->playSound3D(ptr, trapActivationSound, 1.0, 1.0, false);
ptr.getCellRef().trap = "";
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}
@ -92,6 +92,7 @@ namespace MWClass
if (environment.mWorld->getPlayer().getPlayer()==actor)
{
// the player is using the door
// The reason this is not 3D is that it would get interrupted when you teleport
environment.mSoundManager->playSound(openSound, 1.0, 1.0);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTeleportPlayer (ref->ref.destCell, ref->ref.doorDest));
@ -109,7 +110,7 @@ namespace MWClass
// TODO return action for rotating the door
// This is a little pointless, but helps with testing
environment.mSoundManager->playSound(openSound, 1.0, 1.0);
environment.mSoundManager->playSound3D (ptr, openSound, 1.0, 1.0, false);
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}
}

@ -7,9 +7,12 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/environment.hpp"
#include "../mwrender/objects.hpp"
#include "../mwsound/soundmanager.hpp"
namespace MWClass
{
void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -51,6 +54,8 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Ingredient::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -69,4 +74,14 @@ namespace MWClass
registerClass (typeid (ESM::Ingredient).name(), instance);
}
std::string Ingredient::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Ingredient Up");
}
std::string Ingredient::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Ingredient Down");
}
}

@ -26,6 +26,12 @@ namespace MWClass
///< Return name of the script attached to ptr
static void registerSelf();
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the pick up sound Id
virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the put down sound Id
};
}

@ -9,6 +9,7 @@
#include "../mwworld/actiontake.hpp"
#include "../mwworld/nullaction.hpp"
#include "../mwworld/environment.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwsound/soundmanager.hpp"
@ -82,6 +83,8 @@ namespace MWClass
if (!(ref->base->data.flags & ESM::Light::Carry))
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -94,10 +97,33 @@ namespace MWClass
return ref->base->script;
}
std::pair<std::vector<int>, bool> Light::getEquipmentSlots (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Light, MWWorld::RefData> *ref =
ptr.get<ESM::Light>();
std::vector<int> slots;
if (ref->base->data.flags & ESM::Light::Carry)
slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedLeft));
return std::make_pair (slots, false);
}
void Light::registerSelf()
{
boost::shared_ptr<Class> instance (new Light);
registerClass (typeid (ESM::Light).name(), instance);
}
std::string Light::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Misc Up");
}
std::string Light::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Misc Down");
}
}

@ -30,7 +30,17 @@ namespace MWClass
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
///< Return name of the script attached to ptr
virtual std::pair<std::vector<int>, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const;
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
/// stay stacked when equipped?
static void registerSelf();
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the pick up sound Id
virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the put down sound Id
};
}

@ -7,9 +7,13 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/environment.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwrender/objects.hpp"
#include "../mwsound/soundmanager.hpp"
namespace MWClass
{
void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -54,6 +58,8 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Lockpick::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -66,10 +72,29 @@ namespace MWClass
return ref->base->script;
}
std::pair<std::vector<int>, bool> Lockpick::getEquipmentSlots (const MWWorld::Ptr& ptr) const
{
std::vector<int> slots;
slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight));
return std::make_pair (slots, false);
}
void Lockpick::registerSelf()
{
boost::shared_ptr<Class> instance (new Lockpick);
registerClass (typeid (ESM::Tool).name(), instance);
}
std::string Lockpick::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Lockpick Up");
}
std::string Lockpick::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Lockpick Down");
}
}

@ -25,7 +25,17 @@ namespace MWClass
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
///< Return name of the script attached to ptr
virtual std::pair<std::vector<int>, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const;
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
/// stay stacked when equipped?
static void registerSelf();
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the pick up sound Id
virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the put down sound Id
};
}

@ -7,9 +7,12 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/environment.hpp"
#include "../mwrender/objects.hpp"
#include "../mwsound/soundmanager.hpp"
namespace MWClass
{
void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -53,6 +56,8 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Miscellaneous::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -71,4 +76,28 @@ namespace MWClass
registerClass (typeid (ESM::Miscellaneous).name(), instance);
}
std::string Miscellaneous::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Miscellaneous, MWWorld::RefData> *ref =
ptr.get<ESM::Miscellaneous>();
if (ref->base->name =="Gold")
{
return std::string("Item Gold Up");
}
return std::string("Item Misc Up");
}
std::string Miscellaneous::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Miscellaneous, MWWorld::RefData> *ref =
ptr.get<ESM::Miscellaneous>();
if (ref->base->name =="Gold")
{
return std::string("Item Gold Down");
}
return std::string("Item Misc Down");
}
}

@ -26,6 +26,12 @@ namespace MWClass
///< Return name of the script attached to ptr
static void registerSelf();
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the pick up sound Id
virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the put down sound Id
};
}

@ -16,7 +16,7 @@
#include "../mwworld/actiontalk.hpp"
#include "../mwworld/environment.hpp"
#include "../mwworld/world.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/customdata.hpp"
namespace
@ -29,7 +29,7 @@ namespace
MWMechanics::NpcStats mNpcStats;
MWMechanics::CreatureStats mCreatureStats;
MWMechanics::Movement mMovement;
MWWorld::ContainerStore mContainerStore;
MWWorld::InventoryStore mInventoryStore;
virtual MWWorld::CustomData *clone() const;
};
@ -161,7 +161,15 @@ namespace MWClass
{
ensureCustomData (ptr);
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore;
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore;
}
MWWorld::InventoryStore& Npc::getInventoryStore (const MWWorld::Ptr& ptr)
const
{
ensureCustomData (ptr);
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore;
}
std::string Npc::getScript (const MWWorld::Ptr& ptr) const

@ -38,6 +38,9 @@ namespace MWClass
virtual MWWorld::ContainerStore& getContainerStore (const MWWorld::Ptr& ptr) const;
///< Return container store
virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const;
///< Return inventory store
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const;
///< Generate action for activation

@ -7,9 +7,12 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/environment.hpp"
#include "../mwrender/objects.hpp"
#include "../mwsound/soundmanager.hpp"
namespace MWClass
{
void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -53,6 +56,8 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Potion::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -71,4 +76,14 @@ namespace MWClass
registerClass (typeid (ESM::Potion).name(), instance);
}
std::string Potion::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Potion Up");
}
std::string Potion::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Potion Down");
}
}

@ -26,6 +26,12 @@ namespace MWClass
///< Return name of the script attached to ptr
static void registerSelf();
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the pick up sound Id
virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the put down sound Id
};
}

@ -7,9 +7,13 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/environment.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwrender/objects.hpp"
#include "../mwsound/soundmanager.hpp"
namespace MWClass
{
void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -53,6 +57,8 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Probe::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -65,10 +71,29 @@ namespace MWClass
return ref->base->script;
}
std::pair<std::vector<int>, bool> Probe::getEquipmentSlots (const MWWorld::Ptr& ptr) const
{
std::vector<int> slots;
slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight));
return std::make_pair (slots, false);
}
void Probe::registerSelf()
{
boost::shared_ptr<Class> instance (new Probe);
registerClass (typeid (ESM::Probe).name(), instance);
}
std::string Probe::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Probe Up");
}
std::string Probe::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Probe Down");
}
}

@ -25,7 +25,17 @@ namespace MWClass
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
///< Return name of the script attached to ptr
virtual std::pair<std::vector<int>, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const;
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
/// stay stacked when equipped?
static void registerSelf();
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the pick up sound Id
virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the put down sound Id
};
}

@ -7,9 +7,12 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/environment.hpp"
#include "../mwrender/objects.hpp"
#include "../mwsound/soundmanager.hpp"
namespace MWClass
{
void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -53,6 +56,8 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Repair::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -71,4 +76,14 @@ namespace MWClass
registerClass (typeid (ESM::Repair).name(), instance);
}
std::string Repair::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Repair Up");
}
std::string Repair::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
return std::string("Item Repair Down");
}
}

@ -26,6 +26,12 @@ namespace MWClass
///< Return name of the script attached to ptr
static void registerSelf();
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the pick up sound Id
virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the put down sound Id
};
}

@ -7,9 +7,13 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/environment.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwrender/objects.hpp"
#include "../mwsound/soundmanager.hpp"
namespace MWClass
{
void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
@ -53,6 +57,8 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Weapon::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
@ -78,10 +84,157 @@ namespace MWClass
return ref->base->script;
}
std::pair<std::vector<int>, bool> Weapon::getEquipmentSlots (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Weapon, MWWorld::RefData> *ref =
ptr.get<ESM::Weapon>();
std::vector<int> slots;
bool stack = false;
if (ref->base->data.type==ESM::Weapon::Arrow || ref->base->data.type==ESM::Weapon::Bolt)
{
slots.push_back (int (MWWorld::InventoryStore::Slot_Ammunition));
stack = true;
}
else if (ref->base->data.type==ESM::Weapon::MarksmanThrown)
{
slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight));
stack = true;
}
else
slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight));
return std::make_pair (slots, stack);
}
int Weapon::getEquipmentSkill (const MWWorld::Ptr& ptr,
const MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Weapon, MWWorld::RefData> *ref =
ptr.get<ESM::Weapon>();
const int size = 12;
static const int sMapping[size][2] =
{
{ ESM::Weapon::ShortBladeOneHand, ESM::Skill::ShortBlade },
{ ESM::Weapon::LongBladeOneHand, ESM::Skill::LongBlade },
{ ESM::Weapon::LongBladeTwoHand, ESM::Skill::LongBlade },
{ ESM::Weapon::BluntOneHand, ESM::Skill::BluntWeapon },
{ ESM::Weapon::BluntTwoClose, ESM::Skill::BluntWeapon },
{ ESM::Weapon::BluntTwoWide, ESM::Skill::BluntWeapon },
{ ESM::Weapon::SpearTwoWide, ESM::Skill::Spear },
{ ESM::Weapon::AxeOneHand, ESM::Skill::Axe },
{ ESM::Weapon::AxeTwoHand, ESM::Skill::Axe },
{ ESM::Weapon::MarksmanBow, ESM::Skill::Marksman },
{ ESM::Weapon::MarksmanCrossbow, ESM::Skill::Marksman },
{ ESM::Weapon::MarksmanThrown, ESM::Skill::Marksman }
};
for (int i=0; i<size; ++i)
if (sMapping[i][0]==ref->base->data.type)
return sMapping[i][1];
return -1;
}
void Weapon::registerSelf()
{
boost::shared_ptr<Class> instance (new Weapon);
registerClass (typeid (ESM::Weapon).name(), instance);
}
std::string Weapon::getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Weapon, MWWorld::RefData> *ref =
ptr.get<ESM::Weapon>();
int type = ref->base->data.type;
// Ammo
if (type == 12 || type == 13)
{
return std::string("Item Ammo Up");
}
// Bow
if (type == 9)
{
return std::string("Item Weapon Bow Up");
}
// Crossbow
if (type == 10)
{
return std::string("Item Weapon Crossbow Up");
}
// Longblades, One hand and Two
if (type == 1 || type == 2)
{
return std::string("Item Weapon Longblade Up");
}
// Shortblade and thrown weapons
// thrown weapons may not be entirely correct
if (type == 0 || type == 11)
{
return std::string("Item Weapon Shortblade Up");
}
// Spear
if (type == 6)
{
return std::string("Item Weapon Spear Up");
}
// Blunts and Axes
if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8)
{
return std::string("Item Weapon Blunt Up");
}
return std::string("Item Misc Up");
}
std::string Weapon::getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Weapon, MWWorld::RefData> *ref =
ptr.get<ESM::Weapon>();
int type = ref->base->data.type;
// Ammo
if (type == 12 || type == 13)
{
return std::string("Item Ammo Down");
}
// Bow
if (type == 9)
{
return std::string("Item Weapon Bow Down");
}
// Crossbow
if (type == 10)
{
return std::string("Item Weapon Crossbow Down");
}
// Longblades, One hand and Two
if (type == 1 || type == 2)
{
return std::string("Item Weapon Longblade Down");
}
// Shortblade and thrown weapons
// thrown weapons may not be entirely correct
if (type == 0 || type == 11)
{
return std::string("Item Weapon Shortblade Down");
}
// Spear
if (type == 6)
{
return std::string("Item Weapon Spear Down");
}
// Blunts and Axes
if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8)
{
return std::string("Item Weapon Blunt Down");
}
return std::string("Item Misc Down");
}
}

@ -31,7 +31,22 @@ namespace MWClass
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
///< Return name of the script attached to ptr
virtual std::pair<std::vector<int>, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const;
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
/// stay stacked when equipped?
virtual int getEquipmentSkill (const MWWorld::Ptr& ptr,
const MWWorld::Environment& environment) const;
/// Return the index of the skill this item corresponds to when equiopped or -1, if there is
/// no such skill.
static void registerSelf();
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the pick up sound Id
virtual std::string getDownSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the put down sound Id
};
}

@ -50,7 +50,7 @@ namespace MWGui
return isGood();
}
catch (const Compiler::SourceException& error)
catch (const Compiler::SourceException&)
{
// error has already been reported via error handler
}
@ -342,7 +342,7 @@ namespace MWGui
if( ( matches.front().find(' ') != string::npos ) ) {
if( !has_front_quote )
output.append(string("\""));
return output.append(matches.front() + string("\" "));
return output.append(matches.front() + string("\" "));
}
else if( has_front_quote ) {
return output.append(matches.front() + string("\" "));
@ -361,7 +361,7 @@ namespace MWGui
/* Append the longest match to the end of the output string*/
output.append(matches.front().substr( 0, i));
return output;
}
}
}
}

@ -111,7 +111,6 @@ namespace MWRender{
Nif::NiTriShapeCopy& copy = *allshapesiter;
std::vector<Ogre::Vector3>* allvertices = &copy.vertices;
std::vector<Ogre::Vector3>* allnormals = &copy.normals;
@ -182,7 +181,6 @@ namespace MWRender{
std::vector<Nif::NiSkinData::IndividualWeight> inds = iter->second;
int verIndex = iter->first;
Ogre::Vector3 currentVertex = (*allvertices)[verIndex];
Ogre::Vector3 currentNormal = (*allnormals)[verIndex];
Nif::NiSkinData::BoneInfoCopy* boneinfocopy = &(allshapesiter->boneinfo[inds[0].boneinfocopyindex]);
Ogre::Bone *bonePtr = 0;
@ -276,6 +274,7 @@ namespace MWRender{
rotmult = bonePtr->getOrientation();
scale = bonePtr->getScale().x;
boneSequenceIter++;
for(; boneSequenceIter != boneSequence.end(); boneSequenceIter++)
{
if(creaturemodel->getSkeleton()->hasBone(*boneSequenceIter)){
@ -330,7 +329,7 @@ namespace MWRender{
}
}
bool Animation::timeIndex( float time, std::vector<float> times, int & i, int & j, float & x ){
bool Animation::timeIndex( float time, const std::vector<float> & times, int & i, int & j, float & x ){
int count;
if ( (count = times.size()) > 0 )
{
@ -388,6 +387,8 @@ namespace MWRender{
}
void Animation::handleAnimationTransforms(){
Ogre::SkeletonInstance* skel = base->getSkeleton();
@ -404,10 +405,10 @@ namespace MWRender{
for(unsigned int i = 0; i < entityparts.size(); i++){
//Ogre::SkeletonInstance* skel = entityparts[i]->getSkeleton();
Ogre::Bone* b = skel->getRootBone();
b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3));//This is a trick
//Ogre::Bone* b = skel->getRootBone();
//b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3));//This is a trick
entityparts[i]->getAllAnimationStates()->_notifyDirty();
//entityparts[i]->getAllAnimationStates()->_notifyDirty();
}
@ -424,18 +425,19 @@ namespace MWRender{
float x;
float x2;
std::vector<Ogre::Quaternion> quats = iter->getQuat();
const std::vector<Ogre::Quaternion> & quats = iter->getQuat();
const std::vector<float> & ttime = iter->gettTime();
std::vector<float> ttime = iter->gettTime();
std::vector<float>::iterator ttimeiter = ttime.begin();
const std::vector<float> & rtime = iter->getrTime();
int rindexJ = rindexI[slot];
std::vector<float> rtime = iter->getrTime();
int rindexJ = 0;
timeIndex(time, rtime, rindexI[slot], rindexJ, x2);
int tindexJ = 0;
int tindexJ = tindexI[slot];
std::vector<Ogre::Vector3> translist1 = iter->getTranslist1();
const std::vector<Ogre::Vector3> & translist1 = iter->getTranslist1();
timeIndex(time, ttime, tindexI[slot], tindexJ, x);
@ -443,34 +445,35 @@ namespace MWRender{
Ogre::Quaternion r;
bool bTrans = translist1.size() > 0;
if(bTrans){
Ogre::Vector3 v1 = translist1[tindexI[slot]];
Ogre::Vector3 v2 = translist1[tindexJ];
t = (v1 + (v2 - v1) * x);
}
bool bQuats = quats.size() > 0;
if(bQuats){
r = Ogre::Quaternion::Slerp(x2, quats[rindexI[slot]], quats[rindexJ], true);
}
skel = base->getSkeleton();
if(skel->hasBone(iter->getBonename())){
Ogre::Bone* bone = skel->getBone(iter->getBonename());
if(bTrans)
if(bTrans){
Ogre::Vector3 v1 = translist1[tindexI[slot]];
Ogre::Vector3 v2 = translist1[tindexJ];
t = (v1 + (v2 - v1) * x);
bone->setPosition(t);
if(bQuats)
}
if(bQuats){
r = Ogre::Quaternion::Slerp(x2, quats[rindexI[slot]], quats[rindexJ], true);
bone->setOrientation(r);
}
skel->_updateTransforms();
base->getAllAnimationStates()->_notifyDirty();
}
slot++;
}
skel->_updateTransforms();
base->getAllAnimationStates()->_notifyDirty();
}
}

@ -28,6 +28,7 @@ class Animation{
MWWorld::Environment& mEnvironment;
std::map<Nif::NiSkinData::BoneInfoCopy*, PosAndRot> vecRotPos;
static std::map<std::string, int> mUniqueIDs;
std::vector<std::vector<Nif::NiTriShapeCopy>* > shapeparts; //All the NiTriShape data that we need for animating an npc
@ -55,7 +56,7 @@ class Animation{
Ogre::Entity* base;
void handleShapes(std::vector<Nif::NiTriShapeCopy>* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel);
void handleAnimationTransforms();
bool timeIndex( float time, std::vector<float> times, int & i, int & j, float & x );
bool timeIndex( float time, const std::vector<float> & times, int & i, int & j, float & x );
std::string getUniqueID(std::string mesh);
public:
@ -65,7 +66,7 @@ class Animation{
void stopScript();
~Animation();
virtual ~Animation();
};
}

@ -16,7 +16,7 @@ namespace MWRender{
class CreatureAnimation: public Animation{
public:
~CreatureAnimation();
virtual ~CreatureAnimation();
CreatureAnimation(const MWWorld::Ptr& ptr, MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend);
virtual void runAnimation(float timepassed);

@ -42,6 +42,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, MWWorld::Environment& _env,O
std::string bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4);
char secondtolast = bodyRaceID.at(bodyRaceID.length() - 2);
bool female = tolower(secondtolast) == 'f';
std::transform(bodyRaceID.begin(), bodyRaceID.end(), bodyRaceID.begin(), ::tolower);
bool beast = bodyRaceID == "b_n_khajiit_m_" || bodyRaceID == "b_n_khajiit_f_" || bodyRaceID == "b_n_argonian_m_" || bodyRaceID == "b_n_argonian_f_";
/*std::cout << "Race: " << ref->base->race ;
@ -276,6 +277,7 @@ void NpcAnimation::runAnimation(float timepassed){
shapepartsiter++;
entitypartsiter++;
}
}
}

@ -20,7 +20,7 @@ class NpcAnimation: public Animation{
public:
NpcAnimation(const MWWorld::Ptr& ptr, MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend);
~NpcAnimation();
virtual ~NpcAnimation();
Ogre::Entity* insertBoundedPart(const std::string &mesh, std::string bonename);
void insertFreePart(const std::string &mesh, const std::string suffix, Ogre::SceneNode* insert);
virtual void runAnimation(float timepassed);

@ -210,9 +210,14 @@ void RenderingManager::configureFog(ESMS::CellStore<MWWorld::RefData> &mCell)
void RenderingManager::configureFog(const float density, const Ogre::ColourValue& colour)
{
/// \todo make the viewing distance and fog start/end configurable
float low = 3000 / density;
float high = 6200 / density;
// right now we load 3x3 cells, so the maximum viewing distance we
// can allow (to prevent objects suddenly popping up) equals:
// 8192 * 0.69
// ^ cell size ^ minimum density value used (clear weather)
float low = 5652.48 / density / 2.f;
float high = 5652.48 / density;
mRendering.getScene()->setFog (FOG_LINEAR, colour, 0, low, high);
mRendering.getCamera()->setFarClipDistance ( high );

@ -59,7 +59,8 @@ void BillboardObject::setPosition(const Vector3& pPosition)
Vector3 BillboardObject::getPosition() const
{
return mNode->getPosition();
Vector3 p = mNode->_getDerivedPosition() - mNode->getParentSceneNode()->_getDerivedPosition();
return Vector3(p.x, -p.z, p.y);
}
void BillboardObject::setColour(const ColourValue& pColour)
@ -161,14 +162,20 @@ Moon::Moon( const String& textureName,
" in float2 uv : TEXCOORD0, \n"
" out float4 oColor : COLOR, \n"
" uniform sampler2D texture : TEXUNIT0, \n"
" uniform float4 skyColour, \n"
" uniform float4 diffuse, \n"
" uniform float4 emissive \n"
") \n"
"{ \n"
" float4 tex = tex2D(texture, uv); \n"
" oColor = float4(emissive.xyz,1) * tex2D(texture, uv) * float4(1,1,1,diffuse.a); \n"
" float bump = pow((1-diffuse.a),4); \n"
" oColor.rgb += float3(bump, bump, bump)*0.5; \n"
" oColor = float4(emissive.xyz,1) * tex; \n"
// use a circle for the alpha (compute UV distance to center)
// looks a bit bad because its not filtered on the edges,
// but it's cheaper than a seperate alpha texture.
" float sqrUVdist = pow(uv.x-0.5,2) + pow(uv.y-0.5, 2); \n"
" oColor.a = diffuse.a * (sqrUVdist >= 0.24 ? 0 : 1); \n"
" oColor.rgb += (1-tex.a) * oColor.a * skyColour.rgb; \n"//fill dark side of moon with skycolour
" oColor.rgb += (1-diffuse.a) * skyColour.rgb; \n"//fade bump
"}";
fshader->setSource(outStream2.str());
fshader->load();
@ -186,15 +193,19 @@ void Moon::setType(const Moon::Type& type)
mType = type;
}
void Moon::setSkyColour(const Ogre::ColourValue& colour)
{
mMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("skyColour", colour);
}
/// \todo the moon phase rendering is not correct - the dark part of the moon does not occlude the stars
void Moon::setPhase(const Moon::Phase& phase)
{
// Colour texture
Ogre::String textureName = "textures\\tx_";
if (mType == Moon::Type_Secunda) textureName += "secunda_";
else textureName += "masser_";
if (phase == Moon::Phase_New) textureName += "new";
else if (phase == Moon::Phase_WaxingCrescent) textureName += "one_wax";
else if (phase == Moon::Phase_WaxingHalf) textureName += "half_wax";
@ -203,9 +214,9 @@ void Moon::setPhase(const Moon::Phase& phase)
else if (phase == Moon::Phase_WaningHalf) textureName += "half_wan";
else if (phase == Moon::Phase_WaningGibbous) textureName += "three_wan";
else if (phase == Moon::Phase_Full) textureName += "full";
textureName += ".dds";
mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(textureName);
mPhase = phase;
@ -338,24 +349,11 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) :
mAtmosphereNight = mRootNode->createChildSceneNode();
mAtmosphereNight->attachObject(night1_ent);
for (unsigned int i=0; i<night1_ent->getNumSubEntities(); ++i)
{
MaterialPtr mp = night1_ent->getSubEntity(i)->getMaterial();
mp->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0);
mp->getTechnique(0)->getPass(0)->setAmbient(0.0, 0.0, 0.0);
mp->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, 1.0);
mp->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false);
mp->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false);
mp->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA);
mStarsMaterials[i] = mp;
}
// Stars vertex shader
HighLevelGpuProgramPtr vshader3 = mgr.createProgram("Stars_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
HighLevelGpuProgramPtr stars_vp = mgr.createProgram("Stars_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
"cg", GPT_VERTEX_PROGRAM);
vshader3->setParameter("profiles", "vs_2_x arbvp1");
vshader3->setParameter("entry_point", "main_vp");
stars_vp->setParameter("profiles", "vs_2_x arbvp1");
stars_vp->setParameter("entry_point", "main_vp");
StringUtil::StrStreamType outStream4;
outStream4 <<
"void main_vp( \n"
@ -371,10 +369,9 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) :
" oFade = (position.z > 50) ? 1.f : 0.f; \n"
" oPosition = mul( worldViewProj, position ); \n"
"}";
vshader3->setSource(outStream4.str());
vshader3->load();
vshader3->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
night1_ent->getSubEntity(3)->getMaterial()->getTechnique(0)->getPass(0)->setVertexProgram(vshader3->getName());
stars_vp->setSource(outStream4.str());
stars_vp->load();
stars_vp->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
// Stars fragment shader
HighLevelGpuProgramPtr stars_fp = mgr.createProgram("Stars_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
@ -399,7 +396,20 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) :
stars_fp->load();
stars_fp->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR);
stars_fp->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR);
night1_ent->getSubEntity(3)->getMaterial()->getTechnique(0)->getPass(0)->setFragmentProgram(stars_fp->getName());
for (unsigned int i=0; i<night1_ent->getNumSubEntities(); ++i)
{
MaterialPtr mp = night1_ent->getSubEntity(i)->getMaterial();
mp->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0);
mp->getTechnique(0)->getPass(0)->setAmbient(0.0, 0.0, 0.0);
mp->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, 1.0);
mp->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false);
mp->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false);
mp->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA);
mp->getTechnique(0)->getPass(0)->setVertexProgram(stars_vp->getName());
mp->getTechnique(0)->getPass(0)->setFragmentProgram(stars_fp->getName());
mStarsMaterials[i] = mp;
}
// Atmosphere (day)
mesh = NifOgre::NIFLoader::load("meshes\\sky_atmosphere.nif");
@ -583,6 +593,9 @@ void SkyManager::update(float duration)
mSun->setVisible(mSunEnabled);
mMasser->setVisible(mMasserEnabled);
mSecunda->setVisible(mSecundaEnabled);
// rotate the whole sky by 360 degrees every 4 days
mRootNode->roll(Degree(mHourDiff*360/96.f));
}
void SkyManager::enable()
@ -647,6 +660,8 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
if (mSkyColour != weather.mSkyColor)
{
mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(weather.mSkyColor);
mMasser->setSkyColour(weather.mSkyColor);
mSecunda->setSkyColour(weather.mSkyColor);
mSkyColour = weather.mSkyColor;
}
@ -760,6 +775,9 @@ void SkyManager::setSecundaFade(const float fade)
void SkyManager::setHour(double hour)
{
mHourDiff = mHour - hour;
if (mHourDiff > 0) mHourDiff -= 24;
mHour = hour;
}

@ -34,6 +34,8 @@ namespace MWRender
Ogre::SceneNode* rootNode
);
BillboardObject();
virtual ~BillboardObject() {}
void setColour(const Ogre::ColourValue& pColour);
void setPosition(const Ogre::Vector3& pPosition);
@ -69,6 +71,8 @@ namespace MWRender
const Ogre::Vector3& position,
Ogre::SceneNode* rootNode
);
virtual ~Moon() {}
enum Phase
{
@ -90,6 +94,7 @@ namespace MWRender
void setPhase(const Phase& phase);
void setType(const Type& type);
void setSkyColour(const Ogre::ColourValue& colour);
Phase getPhase() const;
unsigned int getPhaseInt() const;
@ -162,7 +167,9 @@ namespace MWRender
float mHour;
int mDay;
int mMonth;
float mHourDiff;
BillboardObject* mSun;
BillboardObject* mSunGlare;
Moon* mMasser;

@ -10,10 +10,8 @@
#include <mangle/sound/clients/ogre_listener_mover.hpp>
#include <mangle/sound/clients/ogre_output_updater.hpp>
#include <components/file_finder/file_finder.hpp>
#include <components/esm_store/store.hpp>
#include "../mwworld/environment.hpp"
#include "../mwworld/world.hpp"
#include "../mwworld/player.hpp"
@ -45,7 +43,6 @@
using namespace Mangle::Sound;
typedef OEngine::Sound::SoundManager OEManager;
typedef OEngine::Sound::SoundManagerPtr OEManagerPtr;
// Set the position on a sound based on a Ptr.
static void setPos(SoundPtr &snd, const MWWorld::Ptr ref)
@ -60,175 +57,65 @@ static void setPos(SoundPtr &snd, const MWWorld::Ptr ref)
namespace MWSound
{
struct SoundManager::SoundImpl
{
/* This is the sound manager. It loades, stores and deletes
sounds based on the sound factory it is given.
*/
OEManagerPtr mgr;
SoundPtr music;
/* This class calls update() on the sound manager each frame
using and Ogre::FrameListener
*/
Mangle::Sound::OgreOutputUpdater updater;
/* This class tracks the movement of an Ogre::Camera and moves
a sound listener automatically to follow it.
*/
Mangle::Sound::OgreListenerMover cameraTracker;
const ESMS::ESMStore &store;
typedef std::map<std::string,Mangle::Sound::WSoundPtr> IDMap;
typedef std::map<MWWorld::Ptr,IDMap> PtrMap;
PtrMap sounds;
// This is used for case insensitive and slash-type agnostic file
// finding. It takes DOS paths (any case, \\ slashes or / slashes)
// relative to the sound dir, and translates them into full paths
// of existing files in the filesystem, if they exist.
bool FSstrict;
FileFinder::LessTreeFileFinder files;
FileFinder::StrictTreeFileFinder strict;
FileFinder::LessTreeFileFinder musicpath;
FileFinder::StrictTreeFileFinder musicpathStrict;
SoundImpl(Ogre::Root *root, Ogre::Camera *camera, const ESMS::ESMStore &str,
const Files::PathContainer& soundDir,
const Files::PathContainer& musicDir,
bool fsstrict)
: mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY)))
, updater(mgr)
, cameraTracker(mgr)
, store(str)
, FSstrict(fsstrict)
, files(soundDir)
, strict(soundDir)
, musicpath(musicDir)
, musicpathStrict(musicDir)
{
std::cout << "Sound output: " << SOUND_OUT << std::endl;
std::cout << "Sound decoder: " << SOUND_IN << std::endl;
// Attach the camera to the camera tracker
cameraTracker.followCamera(camera);
// Tell Ogre to update the sound system each frame
root->addFrameListener(&updater);
}
~SoundImpl()
{
Ogre::Root::getSingleton().removeFrameListener(&updater);
cameraTracker.unfollowCamera();
}
static std::string toMp3(std::string str)
{
std::string::size_type i = str.rfind('.');
if(str.find('/', i) == std::string::npos &&
str.find('\\', i) == std::string::npos)
str = str.substr(0, i) + ".mp3";
else
str += ".mp3";
return str;
}
bool hasFile(const std::string &str, bool music = false)
SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera,
const Files::PathContainer& dataDirs,
bool useSound, bool fsstrict, MWWorld::Environment& environment)
: mFSStrict(fsstrict)
, mEnvironment(environment)
, mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY)))
, updater(mgr)
, cameraTracker(mgr)
, mCurrentPlaylist(NULL)
{
bool found = false;
if(!FSstrict)
if(useSound)
{
if(music)
{
found = musicpath.has(str);
// Not found? Try with .mp3
if (!found)
{
found = musicpath.has(toMp3(str));
}
}
else
// The music library will accept these filetypes
// If none is given then it will accept all filetypes
std::vector<std::string> acceptableExtensions;
acceptableExtensions.push_back(".mp3");
acceptableExtensions.push_back(".wav");
acceptableExtensions.push_back(".ogg");
acceptableExtensions.push_back(".flac");
// Makes a list of all sound files, searches in reverse for priority reasons
for (Files::PathContainer::const_reverse_iterator it = dataDirs.rbegin(); it != dataDirs.rend(); ++it)
{
found = files.has(str);
if (!found)
{
found = files.has(toMp3(str));
}
Files::FileLister(*it / std::string("Sound"), mSoundFiles, true);
}
}
else
{
if(music)
{
found = musicpathStrict.has(str);
// Not found? Try with .mp3
if (!found)
{
found = musicpathStrict.has(toMp3(str));
}
}
else
// Makes a FileLibrary of all music files, searches in reverse for priority reasons
for (Files::PathContainer::const_reverse_iterator it = dataDirs.rbegin(); it != dataDirs.rend(); ++it)
{
found = strict.has(str);
if (!found)
{
found = strict.has(toMp3(str));
}
mMusicLibrary.add(*it / std::string("Music"), true, mFSStrict, acceptableExtensions);
}
}
return found;
}
std::string anything = "anything"; // anything is better that a segfault
mCurrentPlaylist = mMusicLibrary.section(anything, mFSStrict); // now points to an empty path
// Convert a Morrowind sound path (eg. Fx\funny.wav) to full path
// with proper slash conversion (eg. datadir/Sound/Fx/funny.wav)
std::string convertPath(const std::string &str, bool music = false)
{
if(FSstrict == false)
{
// Search and return
if(music && musicpath.has(str))
return musicpath.lookup(str);
else if(files.has(str))
return files.lookup(str);
// Try mp3 if the wav wasn't found
std::string mp3 = toMp3(str);
if(music && musicpath.has(mp3))
return musicpath.lookup(mp3);
else if(files.has(mp3))
return files.lookup(mp3);
}
else
{
if(music && musicpathStrict.has(str))
return musicpathStrict.lookup(str);
else if(strict.has(str))
return strict.lookup(str);
// Try mp3 if the wav wasn't found
std::string mp3 = toMp3(str);
if(music && musicpathStrict.has(mp3))
return musicpathStrict.lookup(mp3);
else if(strict.has(str))
return strict.lookup(mp3);
std::cout << "Sound output: " << SOUND_OUT << std::endl;
std::cout << "Sound decoder: " << SOUND_IN << std::endl;
// Attach the camera to the camera tracker
cameraTracker.followCamera(camera);
// Tell Ogre to update the sound system each frame
root->addFrameListener(&updater);
}
}
// Give up
return "";
SoundManager::~SoundManager()
{
Ogre::Root::getSingleton().removeFrameListener(&updater);
cameraTracker.unfollowCamera();
}
// Convert a soundId to file name, and modify the volume
// according to the sounds local volume setting, minRange and
// maxRange.
std::string lookup(const std::string &soundId,
std::string SoundManager::lookup(const std::string &soundId,
float &volume, float &min, float &max)
{
const ESM::Sound *snd = store.sounds.search(soundId);
const ESM::Sound *snd = mEnvironment.mWorld->getStore().sounds.search(soundId);
if(snd == NULL) return "";
if(snd->data.volume == 0)
@ -249,16 +136,16 @@ namespace MWSound
max = std::max(min, max);
}
return convertPath(snd->sound);
return Files::FileListLocator(mSoundFiles, snd->sound, mFSStrict, false);
}
// Add a sound to the list and play it
void add(const std::string &file,
void SoundManager::add(const std::string &file,
MWWorld::Ptr ptr,
const std::string &id,
float volume, float pitch,
float min, float max,
bool loop)
bool loop, bool untracked)
{
try
{
@ -270,7 +157,10 @@ namespace MWSound
setPos(snd, ptr);
snd->play();
sounds[ptr][id] = WSoundPtr(snd);
if (!untracked)
{
sounds[ptr][id] = WSoundPtr(snd);
}
}
catch(...)
{
@ -280,7 +170,7 @@ namespace MWSound
// Clears all the sub-elements of a given iterator, and then
// removes it from 'sounds'.
void clearAll(PtrMap::iterator& it)
void SoundManager::clearAll(PtrMap::iterator& it)
{
IDMap::iterator sit = it->second.begin();
@ -301,7 +191,7 @@ namespace MWSound
// Stop a sound and remove it from the list. If id="" then
// remove the entire object and stop all its sounds.
void remove(MWWorld::Ptr ptr, const std::string &id = "")
void SoundManager::remove(MWWorld::Ptr ptr, const std::string &id)
{
PtrMap::iterator it = sounds.find(ptr);
if(it != sounds.end())
@ -324,7 +214,7 @@ namespace MWSound
}
}
bool isPlaying(MWWorld::Ptr ptr, const std::string &id) const
bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const
{
PtrMap::const_iterator it = sounds.find(ptr);
if(it != sounds.end())
@ -348,7 +238,7 @@ namespace MWSound
}
// Remove all references to objects belonging to a given cell
void removeCell(const MWWorld::Ptr::CellStore *cell)
void SoundManager::removeCell(const MWWorld::Ptr::CellStore *cell)
{
PtrMap::iterator it2, it = sounds.begin();
while(it != sounds.end())
@ -360,7 +250,7 @@ namespace MWSound
}
}
void updatePositions(MWWorld::Ptr ptr)
void SoundManager::updatePositions(MWWorld::Ptr ptr)
{
// Find the reference (if any)
PtrMap::iterator it = sounds.find(ptr);
@ -378,85 +268,45 @@ namespace MWSound
}
}
}
}; /* SoundImpl */
void SoundManager::stopMusic()
{
if (music)
music->stop();
setPlaylist();
}
void SoundManager::streamMusicFull(const std::string& filename)
{
if(!mData) return;
// Play the sound and tell it to stream, if possible. TODO:
// Store the reference, the jukebox will need to check status,
// control volume etc.
if (mData->music)
mData->music->stop();
mData->music = mData->mgr->load(filename);
mData->music->setStreaming(true);
mData->music->setVolume(0.4);
mData->music->play();
}
SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera,
const ESMS::ESMStore &store, const Files::PathContainer& dataDirs,
bool useSound, bool fsstrict, MWWorld::Environment& environment)
: mData(NULL)
, fsStrict(fsstrict)
, mEnvironment(environment)
{
for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
{
MP3Lookup((*it) / "Music/Explore/");
}
if(useSound)
{
Files::PathContainer soundDirs;;
for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
{
soundDirs.push_back( *it / std::string("Sound"));
}
mData = new SoundImpl(root, camera, store, soundDirs /* Sound */, dataDirs /* Music */, fsstrict);
}
test.name = "";
total = 0;
}
if (music)
music->stop();
music = mgr->load(filename);
music->setStreaming(true);
music->setVolume(0.4);
music->play();
SoundManager::~SoundManager()
{
if(mData)
delete mData;
}
void SoundManager::streamMusic(const std::string& filename)
{
if(mData->hasFile(filename, true))
{
streamMusicFull(mData->convertPath(filename, true));
}
}
void SoundManager::MP3Lookup(const boost::filesystem::path& dir)
{
boost::filesystem::directory_iterator dir_iter(dir), dir_end;
std::string mp3extension = ".mp3";
for(;dir_iter != dir_end; dir_iter++)
{
if(boost::filesystem::extension(*dir_iter) == mp3extension)
std::string filePath = mMusicLibrary.locate(filename, mFSStrict, true).string();
if(!filePath.empty())
{
files.push_back(*dir_iter);
streamMusicFull(filePath);
}
}
}
void SoundManager::startRandomTitle()
{
if(!files.empty())
if(mCurrentPlaylist && !mCurrentPlaylist->empty())
{
Files::PathContainer::iterator fileIter = files.begin();
Files::PathContainer::const_iterator fileIter = mCurrentPlaylist->begin();
srand( time(NULL) );
int r = rand() % files.size() + 1; //old random code
int r = rand() % mCurrentPlaylist->size() + 1; //old random code
std::advance(fileIter, r - 1);
std::string music = fileIter->string();
@ -476,105 +326,158 @@ namespace MWSound
bool SoundManager::isMusicPlaying()
{
bool test = false;
if(mData && mData->music)
if(music)
{
test = mData->music->isPlaying();
test = music->isPlaying();
}
return test;
}
SoundManager::SoundImpl SoundManager::getMData()
{
// bool test = mData->music->isPlaying();
return *mData;
}
bool SoundManager::setPlaylist(std::string playlist)
{
const Files::PathContainer* previousPlaylist;
previousPlaylist = mCurrentPlaylist;
if (playlist == "")
{
mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict);
}
else if(mMusicLibrary.containsSection(playlist, mFSStrict))
{
mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict);
}
else
{
std::cout << "Warning: playlist named " << playlist << " does not exist.\n";
}
return previousPlaylist == mCurrentPlaylist;
}
void SoundManager::playPlaylist(std::string playlist)
{
if (playlist == "")
{
if(!isMusicPlaying())
{
startRandomTitle();
}
return;
}
if(!setPlaylist(playlist))
{
startRandomTitle();
}
else if (!isMusicPlaying())
{
startRandomTitle();
}
}
void SoundManager::say (MWWorld::Ptr ptr, const std::string& filename)
{
// The range values are not tested
if(!mData) return;
if(mData->hasFile(filename))
mData->add(mData->convertPath(filename), ptr, "_say_sound", 1, 1, 100, 20000, false);
std::string filePath = Files::FileListLocator(mSoundFiles, filename, mFSStrict, true);
if(!filePath.empty())
add(filePath, ptr, "_say_sound", 1, 1, 100, 20000, false);
else
std::cout << "Sound file " << filename << " not found, skipping.\n";
}
bool SoundManager::sayDone (MWWorld::Ptr ptr) const
{
if(!mData) return false;
return !mData->isPlaying(ptr, "_say_sound");
return !isPlaying(ptr, "_say_sound");
}
void SoundManager::playSound(const std::string& soundId, float volume, float pitch)
void SoundManager::playSound(const std::string& soundId, float volume, float pitch, bool loop)
{
if(!mData) return;
// Play and forget
float min, max;
const std::string &file = mData->lookup(soundId, volume, min, max);
const std::string &file = lookup(soundId, volume, min, max);
if (file != "")
{
SoundPtr snd = mData->mgr->load(file);
SoundPtr snd = mgr->load(file);
snd->setRepeat(loop);
snd->setVolume(volume);
snd->setRange(min,max);
snd->setPitch(pitch);
snd->setRelative(true);
snd->play();
if (loop)
{
// Only add the looping sound once
IDMap::iterator it = mLoopedSounds.find(soundId);
if(it == mLoopedSounds.end())
{
mLoopedSounds[soundId] = WSoundPtr(snd);
}
}
}
}
void SoundManager::playSound3D (MWWorld::Ptr ptr, const std::string& soundId,
float volume, float pitch, bool loop)
float volume, float pitch, bool loop, bool untracked)
{
if(!mData) return;
// Look up the sound in the ESM data
float min, max;
const std::string &file = mData->lookup(soundId, volume, min, max);
const std::string &file = lookup(soundId, volume, min, max);
if (file != "")
mData->add(file, ptr, soundId, volume, pitch, min, max, loop);
add(file, ptr, soundId, volume, pitch, min, max, loop, untracked);
}
void SoundManager::stopSound3D (MWWorld::Ptr ptr, const std::string& soundId)
{
if(!mData) return;
mData->remove(ptr, soundId);
remove(ptr, soundId);
}
void SoundManager::stopSound (MWWorld::Ptr::CellStore *cell)
{
if(!mData) return;
mData->removeCell(cell);
removeCell(cell);
}
void SoundManager::stopSound(const std::string& soundId)
{
IDMap::iterator it = mLoopedSounds.find(soundId);
if(it != mLoopedSounds.end())
{
SoundPtr snd = it->second.lock();
if(snd) snd->stop();
mLoopedSounds.erase(it);
}
}
bool SoundManager::getSoundPlaying (MWWorld::Ptr ptr, const std::string& soundId) const
{
// Mark all sounds as playing, otherwise the scripts will just
// keep trying to play them every frame.
if(!mData) return true;
return mData->isPlaying(ptr, soundId);
return isPlaying(ptr, soundId);
}
void SoundManager::updateObject(MWWorld::Ptr ptr)
{
if (mData != NULL)
{
mData->updatePositions(ptr);
}
updatePositions(ptr);
}
void SoundManager::update (float duration)
{
MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell();
static int total = 0;
static std::string regionName = "";
static float timePassed = 0.0;
timePassed += duration;
//If the region has changed
if(!(current->cell->data.flags & current->cell->Interior) && timer.elapsed() >= 10)
if(!(current->cell->data.flags & current->cell->Interior) && timePassed >= 10)
{
timer.restart();
if (test.name != current->cell->region)
ESM::Region test = (ESM::Region) *(mEnvironment.mWorld->getStore().regions.find(current->cell->region));
timePassed = 0;
if (regionName != current->cell->region)
{
regionName = current->cell->region;
total = 0;
test = (ESM::Region) *(mEnvironment.mWorld->getStore().regions.find(current->cell->region));
}
if(test.soundList.size() > 0)
@ -598,15 +501,15 @@ namespace MWSound
soundIter = test.soundList.begin();
while (soundIter != test.soundList.end())
{
const ESM::NAME32 go = soundIter->sound;
const std::string go = soundIter->sound.toString();
int chance = (int) soundIter->chance;
//std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n";
soundIter++;
if( r - pos < chance)
{
//play sound
std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n";
mEnvironment.mSoundManager->playSound(go.name, 20.0, 1.0);
std::cout << "Sound: " << go <<" Chance:" << chance << "\n";
mEnvironment.mSoundManager->playSound(go, 20.0, 1.0);
break;
}
@ -616,7 +519,7 @@ namespace MWSound
}
else if(current->cell->data.flags & current->cell->Interior)
{
test.name = "";
regionName = "";
}
}

@ -3,13 +3,14 @@
#include <string>
#include <boost/filesystem.hpp>
#include <boost/timer.hpp>
#include <mangle/sound/clients/ogre_output_updater.hpp>
#include <mangle/sound/clients/ogre_listener_mover.hpp>
#include "../mwworld/ptr.hpp"
#include <openengine/sound/sndmanager.hpp>
#include <components/files/multidircollection.hpp>
#include <components/files/filelibrary.hpp>
#include "../mwworld/ptr.hpp"
namespace Ogre
@ -18,11 +19,16 @@ namespace Ogre
class Camera;
}
namespace ESMS
namespace Mangle
{
struct ESMStore;
namespace Sound
{
typedef boost::shared_ptr<Sound> SoundPtr;
}
}
typedef OEngine::Sound::SoundManagerPtr OEManagerPtr;
namespace MWWorld
{
struct Environment;
@ -30,43 +36,94 @@ namespace MWWorld
namespace MWSound
{
//SoundPtr *music;
class SoundManager
{
// Hide implementation details - engine.cpp is compiling
// enough as it is.
struct SoundImpl;
SoundImpl *mData;
Files::PathContainer files;
bool fsStrict;
MWWorld::Environment& mEnvironment;
// This is used for case insensitive and slash-type agnostic file
// finding. It takes DOS paths (any case, \\ slashes or / slashes)
// relative to the sound dir, and translates them into full paths
// of existing files in the filesystem, if they exist.
bool mFSStrict;
int total;
ESM::Region test;
boost::timer timer;
MWWorld::Environment& mEnvironment;
void streamMusicFull (const std::string& filename);
///< Play a soundifle
/// \param absolute filename
/* This is the sound manager. It loades, stores and deletes
sounds based on the sound factory it is given.
*/
OEManagerPtr mgr;
Mangle::Sound::SoundPtr music;
/* This class calls update() on the sound manager each frame
using and Ogre::FrameListener
*/
Mangle::Sound::OgreOutputUpdater updater;
/* This class tracks the movement of an Ogre::Camera and moves
a sound listener automatically to follow it.
*/
Mangle::Sound::OgreListenerMover cameraTracker;
typedef std::map<std::string,Mangle::Sound::WSoundPtr> IDMap;
typedef std::map<MWWorld::Ptr,IDMap> PtrMap;
PtrMap sounds;
// A list of all sound files used to lookup paths
Files::PathContainer mSoundFiles;
// A library of all Music file paths stored by the folder they are contained in
Files::FileLibrary mMusicLibrary;
// Points to the current playlist of music files stored in the music library
const Files::PathContainer* mCurrentPlaylist;
IDMap mLoopedSounds;
std::string lookup(const std::string &soundId,
float &volume, float &min, float &max);
void add(const std::string &file,
MWWorld::Ptr ptr, const std::string &id,
float volume, float pitch, float min, float max,
bool loop, bool untracked=false);
void clearAll(PtrMap::iterator& it);
void remove(MWWorld::Ptr ptr, const std::string &id = "");
bool isPlaying(MWWorld::Ptr ptr, const std::string &id) const;
void removeCell(const MWWorld::Ptr::CellStore *cell);
void updatePositions(MWWorld::Ptr ptr);
public:
SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store,
SoundManager(Ogre::Root*, Ogre::Camera*,
const Files::PathContainer& dataDir, bool useSound, bool fsstrict,
MWWorld::Environment& environment);
~SoundManager();
void stopMusic();
///< Stops music if it's playing
void streamMusic(const std::string& filename);
///< Play a soundifle
/// \param filename name of a sound file in "Music/" in the data directory.
void startRandomTitle();
void MP3Lookup(const boost::filesystem::path& dir);
///< Starts a random track from the current playlist
bool isMusicPlaying();
///< Returns true if music is playing
SoundImpl getMData();
bool setPlaylist(std::string playlist="");
///< Set the playlist to an existing folder
/// \param name of the folder that contains the playlist
/// if none is set then it is set to an empty playlist
/// \return Return true if the previous playlist was the same
void playPlaylist(std::string playlist="");
///< Start playing music from the selected folder
/// \param name of the folder that contains the playlist
/// if none is set then it plays from the current playlist
void say (MWWorld::Ptr reference, const std::string& filename);
///< Make an actor say some text.
@ -75,11 +132,11 @@ namespace MWSound
bool sayDone (MWWorld::Ptr reference) const;
///< Is actor not speaking?
void playSound (const std::string& soundId, float volume, float pitch);
void playSound (const std::string& soundId, float volume, float pitch, bool loop=false);
///< Play a sound, independently of 3D-position
void playSound3D (MWWorld::Ptr reference, const std::string& soundId,
float volume, float pitch, bool loop);
float volume, float pitch, bool loop, bool untracked=false);
///< Play a sound from an object
void stopSound3D (MWWorld::Ptr reference, const std::string& soundId = "");
@ -89,6 +146,9 @@ namespace MWSound
void stopSound (MWWorld::Ptr::CellStore *cell);
///< Stop all sounds for the given cell.
void stopSound(const std::string& soundId);
///< Stop a non-3d looping sound
bool getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId) const;
///< Is the given sound currently playing on the given object?

@ -5,6 +5,8 @@
#include <algorithm>
#include "world.hpp"
#include "class.hpp"
#include "containerstore.hpp"
MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell)
{
@ -35,6 +37,39 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell)
}
}
void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore)
{
for (ESMS::CellRefList<ESM::Container, RefData>::List::iterator iter (
cellStore.containers.list.begin());
iter!=cellStore.containers.list.end(); ++iter)
{
Ptr container (&*iter, &cellStore);
Class::get (container).getContainerStore (container).fill (
iter->base->inventory, mStore);
}
for (ESMS::CellRefList<ESM::Creature, RefData>::List::iterator iter (
cellStore.creatures.list.begin());
iter!=cellStore.creatures.list.end(); ++iter)
{
Ptr container (&*iter, &cellStore);
Class::get (container).getContainerStore (container).fill (
iter->base->inventory, mStore);
}
for (ESMS::CellRefList<ESM::NPC, RefData>::List::iterator iter (
cellStore.npcs.list.begin());
iter!=cellStore.npcs.list.end(); ++iter)
{
Ptr container (&*iter, &cellStore);
Class::get (container).getContainerStore (container).fill (
iter->base->inventory, mStore);
}
}
MWWorld::Cells::Cells (const ESMS::ESMStore& store, ESM::ESMReader& reader, MWWorld::World& world)
: mStore (store), mReader (reader), mWorld (world) {}
@ -43,6 +78,8 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y)
std::map<std::pair<int, int>, Ptr::CellStore>::iterator result =
mExteriors.find (std::make_pair (x, y));
bool fill = false;
if (result==mExteriors.end())
{
const ESM::Cell *cell = mStore.cells.searchExt (x, y);
@ -63,11 +100,16 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y)
result = mExteriors.insert (std::make_pair (
std::make_pair (x, y), Ptr::CellStore (cell))).first;
fill = true;
}
if (result->second.mState!=Ptr::CellStore::State_Loaded)
result->second.load (mStore, mReader);
if (fill)
fillContainers (result->second);
return &result->second;
}
@ -75,16 +117,23 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getInterior (const std::string& name)
{
std::map<std::string, Ptr::CellStore>::iterator result = mInteriors.find (name);
bool fill = false;
if (result==mInteriors.end())
{
const ESM::Cell *cell = mStore.cells.findInt (name);
result = mInteriors.insert (std::make_pair (name, Ptr::CellStore (cell))).first;
fill = true;
}
if (result->second.mState!=Ptr::CellStore::State_Loaded)
result->second.load (mStore, mReader);
if (fill)
fillContainers (result->second);
return &result->second;
}

@ -34,6 +34,8 @@ namespace MWWorld
Ptr::CellStore *getCellStore (const ESM::Cell *cell);
void fillContainers (Ptr::CellStore& cellStore);
public:
Cells (const ESMS::ESMStore& store, ESM::ESMReader& reader, MWWorld::World& world);

@ -77,6 +77,11 @@ namespace MWWorld
throw std::runtime_error ("class does not have a container store");
}
InventoryStore& Class::getInventoryStore (const Ptr& ptr) const
{
throw std::runtime_error ("class does not have an inventory store");
}
void Class::lock (const Ptr& ptr, int lockLevel) const
{
throw std::runtime_error ("class does not support locking");
@ -122,6 +127,16 @@ namespace MWWorld
return Ogre::Vector3 (0, 0, 0);
}
std::pair<std::vector<int>, bool> Class::getEquipmentSlots (const Ptr& ptr) const
{
return std::make_pair (std::vector<int>(), false);
}
int Class::getEquipmentSkill (const Ptr& ptr, const Environment& environment) const
{
return -1;
}
const Class& Class::get (const std::string& key)
{
std::map<std::string, boost::shared_ptr<Class> >::const_iterator iter = sClasses.find (key);
@ -141,4 +156,14 @@ namespace MWWorld
{
sClasses.insert (std::make_pair (key, instance));
}
std::string Class::getUpSoundId (const Ptr& ptr, const MWWorld::Environment& environment) const
{
throw std::runtime_error ("class does not have an up sound");
}
std::string Class::getDownSoundId (const Ptr& ptr, const MWWorld::Environment& environment) const
{
throw std::runtime_error ("class does not have an down sound");
}
}

@ -3,6 +3,7 @@
#include <map>
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>
@ -34,6 +35,7 @@ namespace MWWorld
class Ptr;
class Environment;
class ContainerStore;
class InventoryStore;
/// \brief Base class for referenceable esm records
class Class
@ -108,6 +110,10 @@ namespace MWWorld
///< Return container store or throw an exception, if class does not have a
/// container store (default implementation: throw an exceoption)
virtual InventoryStore& getInventoryStore (const Ptr& ptr) const;
///< Return inventory store or throw an exception, if class does not have a
/// inventory store (default implementation: throw an exceoption)
virtual void lock (const Ptr& ptr, int lockLevel) const;
///< Lock object (default implementation: throw an exception)
@ -137,6 +143,18 @@ namespace MWWorld
///< Return desired movement vector (determined based on movement settings,
/// stance and stats).
virtual std::pair<std::vector<int>, bool> getEquipmentSlots (const Ptr& ptr) const;
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
/// stay stacked when equipped?
///
/// Default implementation: return (empty vector, false).
virtual int getEquipmentSkill (const Ptr& ptr, const Environment& environment)
const;
/// Return the index of the skill this item corresponds to when equiopped or -1, if there is
/// no such skill.
/// (default implementation: return -1)
static const Class& get (const std::string& key);
///< If there is no class for this \a key, an exception is thrown.
@ -144,6 +162,14 @@ namespace MWWorld
///< If there is no class for this pointer, an exception is thrown.
static void registerClass (const std::string& key, boost::shared_ptr<Class> instance);
virtual std::string getUpSoundId (const Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the up sound ID of \a ptr or throw an exception, if class does not support ID retrieval
/// (default implementation: throw an exception)
virtual std::string getDownSoundId (const Ptr& ptr, const MWWorld::Environment& environment) const;
///< Return the down sound ID of \a ptr or throw an exception, if class does not support ID retrieval
/// (default implementation: throw an exception)
};
}

@ -5,6 +5,12 @@
#include <typeinfo>
#include <stdexcept>
#include <components/esm/loadcont.hpp>
#include "manualref.hpp"
MWWorld::ContainerStore::~ContainerStore() {}
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::begin (int mask)
{
return ContainerStoreIterator (mask, this);
@ -17,7 +23,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end()
void MWWorld::ContainerStore::add (const Ptr& ptr)
{
/// \todo implement item stocking
/// \todo implement item stacking
switch (getType (ptr))
{
@ -36,6 +42,40 @@ void MWWorld::ContainerStore::add (const Ptr& ptr)
}
}
void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const ESMS::ESMStore& store)
{
for (std::vector<ESM::ContItem>::const_iterator iter (items.list.begin()); iter!=items.list.end();
++iter)
{
ManualRef ref (store, iter->item.toString());
if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
{
/// \todo implement leveled item lists
continue;
}
ref.getPtr().getRefData().setCount (iter->count);
add (ref.getPtr());
}
}
void MWWorld::ContainerStore::clear()
{
potions.list.clear();
appas.list.clear();
armors.list.clear();
books.list.clear();
clothes.list.clear();
ingreds.list.clear();
lights.list.clear();
lockpicks.list.clear();
miscItems.list.clear();
probes.list.clear();
repairs.list.clear();
weapons.list.clear();
}
int MWWorld::ContainerStore::getType (const Ptr& ptr)
{
if (ptr.isEmpty())
@ -331,6 +371,11 @@ int MWWorld::ContainerStoreIterator::getType() const
return mType;
}
const MWWorld::ContainerStore *MWWorld::ContainerStoreIterator::getContainerStore() const
{
return mContainer;
}
bool MWWorld::operator== (const ContainerStoreIterator& left, const ContainerStoreIterator& right)
{
return left.isEqual (right);

@ -1,11 +1,18 @@
#ifndef GAME_MWWORLD_CONTAINERSTORE_H
#define GAME_MWWORLD_CONTAINERSTORE_H
#include <iterator>
#include <components/esm_store/cell_store.hpp>
#include "refdata.hpp"
#include "ptr.hpp"
namespace ESM
{
struct InventoryList;
}
namespace MWWorld
{
class ContainerStoreIterator;
@ -48,6 +55,8 @@ namespace MWWorld
public:
virtual ~ContainerStore();
ContainerStoreIterator begin (int mask = Type_All);
ContainerStoreIterator end();
@ -60,6 +69,12 @@ namespace MWWorld
/// \attention Do not add items to an existing stack by increasing the count instead of
/// calling this function!
void fill (const ESM::InventoryList& items, const ESMS::ESMStore& store);
///< Insert items into *this.
void clear();
///< Empty container.
static int getType (const Ptr& ptr);
///< This function throws an exception, if ptr does not point to an object, that can be
/// put into a container.
@ -71,6 +86,7 @@ namespace MWWorld
///
/// \note The iterator will automatically skip over deleted objects.
class ContainerStoreIterator
: public std::iterator<std::forward_iterator_tag, Ptr, std::ptrdiff_t, Ptr *, Ptr&>
{
int mType;
int mMask;
@ -126,6 +142,8 @@ namespace MWWorld
int getType() const;
const ContainerStore *getContainerStore() const;
friend class ContainerStore;
};

@ -0,0 +1,86 @@
#include "inventorystore.hpp"
#include <iterator>
#include <algorithm>
#include "class.hpp"
void MWWorld::InventoryStore::copySlots (const InventoryStore& store)
{
// some const-trickery, required because of a flaw in the handling of MW-references and the
// resulting workarounds
for (std::vector<ContainerStoreIterator>::const_iterator iter (
const_cast<InventoryStore&> (store).mSlots.begin());
iter!=const_cast<InventoryStore&> (store).mSlots.end(); ++iter)
{
std::size_t distance = std::distance (const_cast<InventoryStore&> (store).begin(), *iter);
ContainerStoreIterator slot = begin();
std::advance (slot, distance);
mSlots.push_back (slot);
}
}
MWWorld::InventoryStore::InventoryStore()
{
for (int i=0; i<Slots; ++i)
mSlots.push_back (end());
}
MWWorld::InventoryStore::InventoryStore (const InventoryStore& store)
: ContainerStore (store)
{
copySlots (store);
}
MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStore& store)
{
ContainerStore::operator= (store);
mSlots.clear();
copySlots (store);
return *this;
}
void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& iterator)
{
if (slot<0 || slot>=static_cast<int> (mSlots.size()))
throw std::runtime_error ("slot number out of range");
if (iterator.getContainerStore()!=this)
throw std::runtime_error ("attempt to equip an item that is not in the inventory");
if (iterator!=end())
{
std::pair<std::vector<int>, bool> slots = Class::get (*iterator).getEquipmentSlots (*iterator);
if (std::find (slots.first.begin(), slots.first.end(), slot)==slots.first.end())
throw std::runtime_error ("invalid slot");
}
/// \todo restack item previously in this slot (if required)
/// \todo unstack item pointed to by iterator if required)
mSlots[slot] = iterator;
}
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot)
{
if (slot<0 || slot>=static_cast<int> (mSlots.size()))
throw std::runtime_error ("slot number out of range");
if (mSlots[slot]==end())
return end();
if (mSlots[slot]->getRefData().getCount()<1)
{
// object has been deleted
mSlots[slot] = end();
return end();
}
return mSlots[slot];
}

@ -0,0 +1,58 @@
#ifndef GAME_MWWORLD_INVENTORYSTORE_H
#define GAME_MWWORLD_INVENTORYSTORE_H
#include "containerstore.hpp"
namespace MWWorld
{
///< \brief Variant of the ContainerStore for NPCs
class InventoryStore : public ContainerStore
{
public:
static const int Slot_Helmet = 0;
static const int Slot_Cuirass = 1;
static const int Slot_Greaves = 2;
static const int Slot_LeftPauldron = 3;
static const int Slot_RightPauldron = 4;
static const int Slot_LeftGauntlet = 5;
static const int Slot_RightGauntlet = 6;
static const int Slot_Boots = 7;
static const int Slot_Shirt = 8;
static const int Slot_Pants = 9;
static const int Slot_Skirt = 10;
static const int Slot_Robe = 11;
static const int Slot_LeftRing = 12;
static const int Slot_RightRing = 13;
static const int Slot_Amulet = 14;
static const int Slot_Belt = 15;
static const int Slot_CarriedRight = 16;
static const int Slot_CarriedLeft = 17;
static const int Slot_Ammunition = 18;
static const int Slots = 19;
static const int Slot_NoSlot = -1;
private:
mutable std::vector<ContainerStoreIterator> mSlots;
void copySlots (const InventoryStore& store);
public:
InventoryStore();
InventoryStore (const InventoryStore& store);
InventoryStore& operator= (const InventoryStore& store);
void equip (int slot, const ContainerStoreIterator& iterator);
///< \note \a iteartor can be an end-iterator
ContainerStoreIterator getSlot (int slot);
};
}
#endif

@ -496,7 +496,10 @@ WeatherResult WeatherManager::transition(float factor)
void WeatherManager::update(float duration)
{
mWeatherUpdateTime -= duration;
if (mEnvironment->mWorld->isCellExterior() || mEnvironment->mWorld->isCellQuasiExterior())
bool exterior = (mEnvironment->mWorld->isCellExterior() || mEnvironment->mWorld->isCellQuasiExterior());
if (exterior)
{
std::string regionstr = mEnvironment->mWorld->getPlayer().getPlayer().getCell()->cell->region;
boost::algorithm::to_lower(regionstr);
@ -671,7 +674,7 @@ void WeatherManager::update(float duration)
mRendering->getSkyManager()->secundaDisable();
}
if (mCurrentWeather == "thunderstorm" && mNextWeather == "")
if (mCurrentWeather == "thunderstorm" && mNextWeather == "" && exterior)
{
if (mThunderFlash > 0)
{
@ -730,6 +733,42 @@ void WeatherManager::update(float duration)
mRendering->skyDisable();
mRendering->getSkyManager()->setThunder(0.f);
}
// play sounds
std::string ambientSnd = (mNextWeather == "" ? mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID : "");
if (!exterior) ambientSnd = "";
if (ambientSnd != "")
{
if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), ambientSnd) == mSoundsPlaying.end())
{
mSoundsPlaying.push_back(ambientSnd);
mEnvironment->mSoundManager->playSound(ambientSnd, 1.0, 1.0, true);
}
}
std::string rainSnd = (mNextWeather == "" ? mWeatherSettings[mCurrentWeather].mRainLoopSoundID : "");
if (!exterior) rainSnd = "";
if (rainSnd != "")
{
if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), rainSnd) == mSoundsPlaying.end())
{
mSoundsPlaying.push_back(rainSnd);
mEnvironment->mSoundManager->playSound(rainSnd, 1.0, 1.0, true);
}
}
// stop sounds
std::vector<std::string>::iterator it=mSoundsPlaying.begin();
while (it!=mSoundsPlaying.end())
{
if ( *it != ambientSnd && *it != rainSnd)
{
mEnvironment->mSoundManager->stopSound(*it);
it = mSoundsPlaying.erase(it);
}
else
++it;
}
}
void WeatherManager::setHour(const float hour)

@ -243,8 +243,10 @@ namespace MWWorld
MWWorld::Environment* mEnvironment;
std::map<Ogre::String, Weather> mWeatherSettings;
std::map<std::string, std::string> mRegionOverrides;
std::vector<std::string> mSoundsPlaying;
Ogre::String mCurrentWeather;
Ogre::String mNextWeather;

@ -161,9 +161,9 @@ namespace MWWorld
{
mPhysics = new PhysicsSystem(renderer);
mPhysEngine = mPhysics->getEngine();
mRendering = new MWRender::RenderingManager(renderer, resDir, mPhysEngine, environment);
mWeatherManager = new MWWorld::WeatherManager(mRendering, &environment);
boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master));
@ -381,7 +381,7 @@ namespace MWWorld
mGlobalVariables->setFloat ("gamehour", hour);
mRendering->skySetHour (hour);
mWeatherManager->setHour (hour);
if (days>0)
@ -418,10 +418,10 @@ namespace MWWorld
mGlobalVariables->setInt ("month", month);
mRendering->skySetDate (day, month);
mWeatherManager->setDate (day, month);
}
void World::setMonth (int month)
@ -702,9 +702,9 @@ namespace MWWorld
void World::update (float duration)
{
mWorldScene->update (duration);
mWeatherManager->update (duration);
// cast a ray from player to sun to detect if the sun is visible
// this is temporary until we find a better place to put this code
// currently its here because we need to access the physics system
@ -713,7 +713,7 @@ namespace MWWorld
sun = Vector3(sun.x, -sun.z, sun.y);
mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun));
}
bool World::isCellExterior() const
{
Ptr::CellStore *currentCell = mWorldScene->getCurrentCell();
@ -726,7 +726,7 @@ namespace MWWorld
}
return false;
}
bool World::isCellQuasiExterior() const
{
Ptr::CellStore *currentCell = mWorldScene->getCurrentCell();
@ -739,17 +739,17 @@ namespace MWWorld
}
return false;
}
int World::getCurrentWeather() const
{
return mWeatherManager->getWeatherID();
}
void World::changeWeather(const std::string& region, const unsigned int id)
{
mWeatherManager->changeWeather(region, id);
}
OEngine::Render::Fader* World::getFader()
{
return mRendering->getFader();

@ -44,6 +44,7 @@ add_component_dir (misc
add_component_dir (files
linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager
filelibrary
)
add_component_dir (compiler

@ -95,8 +95,6 @@ namespace Compiler
return true;
}
return false;
}
bool ControlParser::parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner)
@ -108,7 +106,7 @@ namespace Compiler
Codes expr;
mExprParser.append (expr);
Generator::jump (loop, -mCodeBlock.size()-expr.size());
Generator::jump (loop, -static_cast<int> (mCodeBlock.size()-expr.size()));
std::copy (expr.begin(), expr.end(), std::back_inserter (mCode));
@ -122,7 +120,7 @@ namespace Compiler
Codes loop2;
Generator::jump (loop2, -mCodeBlock.size()-expr.size()-skip.size());
Generator::jump (loop2, -static_cast<int> (mCodeBlock.size()-expr.size()-skip.size()));
if (loop.size()!=loop2.size())
throw std::logic_error (
@ -153,8 +151,6 @@ namespace Compiler
return true;
}
return false;
}
ControlParser::ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals,

@ -3,32 +3,68 @@
namespace ESM
{
void PathGrid::load(ESMReader &esm)
void Pathgrid::load(ESMReader &esm)
{
esm.getHNT(data, "DATA", 12);
cell = esm.getHNString("NAME");
// Remember this file position
context = esm.getContext();
// keep track of total connections so we can reserve edge vector size
int edgeCount = 0;
// Check that the sizes match up. Size = 16 * s2 (path points?)
if (esm.isNextSub("PGRP"))
{
esm.skipHSub();
esm.getSubHeader();
int size = esm.getSubSize();
if (size != 16 * data.s2)
esm.fail("Path grid table size mismatch");
// Check that the sizes match up. Size = 16 * s2 (path points)
if (size != static_cast<int> (sizeof(Point) * data.s2))
esm.fail("Path point subrecord size mismatch");
else
{
int pointCount = data.s2;
points.reserve(pointCount);
for (int i = 0; i < pointCount; ++i)
{
Point p;
esm.getExact(&p, sizeof(Point));
points.push_back(p);
edgeCount += p.connectionNum;
}
}
}
// Size varies. Path grid chances? Connections? Multiples of 4
// suggest either int or two shorts, or perhaps a float. Study
// it later.
if (esm.isNextSub("PGRC"))
{
esm.skipHSub();
esm.getSubHeader();
int size = esm.getSubSize();
if (size % 4 != 0)
if (size % sizeof(int) != 0)
esm.fail("PGRC size not a multiple of 4");
else
{
int rawConnNum = size / sizeof(int);
std::vector<int> rawConnections;
rawConnections.reserve(rawConnNum);
for (int i = 0; i < rawConnNum; ++i)
{
int currentValue;
esm.getT(currentValue);
rawConnections.push_back(currentValue);
}
std::vector<int>::const_iterator rawIt = rawConnections.begin();
int pointIndex = 0;
edges.reserve(edgeCount);
for(PointList::const_iterator it = points.begin(); it != points.end(); it++, pointIndex++)
{
unsigned char connectionNum = (*it).connectionNum;
for (int i = 0; i < connectionNum; ++i) {
Edge edge;
edge.v0 = pointIndex;
edge.v1 = *rawIt;
rawIt++;
edges.push_back(edge);
}
}
}
}
}

@ -9,20 +9,37 @@ namespace ESM
/*
* Path grid.
*/
struct PathGrid
struct Pathgrid
{
struct DATAstruct
{
int x, y; // Grid location, matches cell for exterior cells
short s1; // ?? Usually but not always a power of 2. Doesn't seem
// to have any relation to the size of PGRC.
short s2; // Number of path points? Size of PGRP block is always 16 * s2;
short s2; // Number of path points.
}; // 12 bytes
struct Point // path grid point
{
int x, y, z; // Location of point
unsigned char autogenerated; // autogenerated vs. user coloring flag?
unsigned char connectionNum; // number of connections for this point
short unknown;
}; // 16 bytes
struct Edge // path grid edge
{
int v0, v1; // index of points connected with this edge
}; // 8 bytes
std::string cell; // Cell name
DATAstruct data;
ESM_Context context; // Context so we can return here later and
// finish the job
typedef std::vector<Point> PointList;
PointList points;
typedef std::vector<Edge> EdgeList;
EdgeList edges;
void load(ESMReader &esm);
};

@ -22,6 +22,8 @@ namespace ESMS
struct RecList
{
virtual ~RecList() {}
virtual void load(ESMReader &esm, const std::string &id) = 0;
virtual int getSize() = 0;
virtual void listIdentifier (std::vector<std::string>& identifier) const = 0;
@ -42,6 +44,8 @@ namespace ESMS
template <typename X>
struct RecListT : RecList
{
virtual ~RecListT() {}
typedef std::map<std::string,X> MapType;
MapType list;
@ -90,6 +94,8 @@ namespace ESMS
template <typename X>
struct RecListWithIDT : RecList
{
virtual ~RecListWithIDT() {}
typedef std::map<std::string,X> MapType;
MapType list;
@ -139,6 +145,8 @@ namespace ESMS
template <typename X>
struct RecIDListT : RecList
{
virtual ~RecIDListT() {}
typedef std::map<std::string,X> MapType;
MapType list;
@ -189,6 +197,8 @@ namespace ESMS
*/
struct LTexList : RecList
{
virtual ~LTexList() {}
// TODO: For multiple ESM/ESP files we need one list per file.
std::vector<LandTexture> ltex;
int count;
@ -223,6 +233,8 @@ namespace ESMS
*/
struct LandList : RecList
{
virtual ~LandList() {}
// Map containing all landscapes
typedef std::map<int, Land*> LandsCol;
typedef std::map<int, LandsCol> Lands;
@ -296,7 +308,7 @@ namespace ESMS
identifier.push_back (iter->first);
}
~CellList()
virtual ~CellList()
{
for (IntCells::iterator it = intCells.begin(); it!=intCells.end(); ++it)
delete it->second;
@ -390,9 +402,100 @@ namespace ESMS
}
};
struct PathgridList : RecList
{
int count;
// List of grids for interior cells. Indexed by cell name.
typedef std::map<std::string,ESM::Pathgrid*, ciLessBoost> IntGrids;
IntGrids intGrids;
// List of grids for exterior cells. Indexed as extCells[gridX][gridY].
typedef std::map<std::pair<int, int>, ESM::Pathgrid*> ExtGrids;
ExtGrids extGrids;
PathgridList() : count(0) {}
virtual ~PathgridList()
{
for (IntGrids::iterator it = intGrids.begin(); it!=intGrids.end(); ++it)
delete it->second;
for (ExtGrids::iterator it = extGrids.begin(); it!=extGrids.end(); ++it)
delete it->second;
}
int getSize() { return count; }
virtual void listIdentifier (std::vector<std::string>& identifier) const
{
// do nothing
}
void load(ESMReader &esm, const std::string &id)
{
count++;
ESM::Pathgrid *grid = new ESM::Pathgrid;
grid->load(esm);
if (grid->data.x == 0 && grid->data.y == 0)
{
intGrids[grid->cell] = grid;
}
else
{
extGrids[std::make_pair(grid->data.x, grid->data.y)] = grid;
}
}
Pathgrid *find(int cellX, int cellY, std::string cellName) const
{
Pathgrid *result = search(cellX, cellY, cellName);
if (!result)
{
throw std::runtime_error("no pathgrid found for cell " + cellName);
}
return result;
}
Pathgrid *search(int cellX, int cellY, std::string cellName) const
{
Pathgrid *result = NULL;
if (cellX == 0 && cellY == 0) // possibly interior
{
IntGrids::const_iterator it = intGrids.find(cellName);
if (it != intGrids.end())
result = it->second;
}
else
{
ExtGrids::const_iterator it = extGrids.find(std::make_pair(cellX, cellY));
if (it != extGrids.end())
result = it->second;
}
return result;
}
Pathgrid *search(const ESM::Cell &cell) const
{
int cellX, cellY;
if (cell.data.flags & ESM::Cell::Interior)
{
cellX = cellY = 0;
}
else
{
cellX = cell.data.gridX;
cellY = cell.data.gridY;
}
return search(cellX, cellY, cell.name);
}
};
template <typename X>
struct ScriptListT : RecList
{
virtual ~ScriptListT() {}
typedef std::map<std::string,X> MapType;
MapType list;
@ -444,6 +547,8 @@ namespace ESMS
template <typename X>
struct IndexListT
{
virtual ~IndexListT() {}
typedef std::map<int, X> MapType;
MapType list;

@ -74,7 +74,8 @@ namespace ESMS
ScriptListT<Script> scripts;
IndexListT<MagicEffect> magicEffects;
IndexListT<Skill> skills;
//RecListT<PathGrid> pathgrids;
//RecListT<Pathgrid> pathgrids;
PathgridList pathgrids;
// Special entry which is hardcoded and not loaded from an ESM
IndexListT<Attribute> attributes;
@ -124,7 +125,7 @@ namespace ESMS
recLists[REC_MISC] = &miscItems;
recLists[REC_NPC_] = &npcs;
recLists[REC_NPCC] = &npcChange;
//recLists[REC_PGRD] = &pathgrids;
recLists[REC_PGRD] = &pathgrids;
recLists[REC_PROB] = &probes;
recLists[REC_RACE] = &races;
recLists[REC_REGN] = &regions;

@ -0,0 +1,120 @@
#include "filelibrary.hpp"
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
namespace Files
{
// Looks for a string in a vector of strings
bool containsVectorString(const StringVector& list, const std::string& str)
{
for (StringVector::const_iterator iter = list.begin();
iter != list.end(); iter++)
{
if (*iter == str)
return true;
}
return false;
}
// Searches a path and adds the results to the library
void FileLibrary::add(const boost::filesystem::path &root, bool recursive, bool strict,
const StringVector &acceptableExtensions)
{
if (!boost::filesystem::exists(root))
{
std::cout << "Warning " << root.string() << " does not exist.\n";
return;
}
std::string fileExtension;
std::string type;
// remember the last location of the priority list when listing new items
int length = mPriorityList.size();
// First makes a list of all candidate files
FileLister(root, mPriorityList, recursive);
// Then sort these files into sections according to the folder they belong to
for (PathContainer::iterator listIter = mPriorityList.begin() + length;
listIter != mPriorityList.end(); ++listIter)
{
if( !acceptableExtensions.empty() )
{
fileExtension = boost::filesystem::path (listIter->extension()).string();
boost::algorithm::to_lower(fileExtension);
if(!containsVectorString(acceptableExtensions, fileExtension))
continue;
}
type = boost::filesystem::path (listIter->parent_path().leaf()).string();
if (!strict)
boost::algorithm::to_lower(type);
mMap[type].push_back(*listIter);
// std::cout << "Added path: " << listIter->string() << " in section "<< type <<std::endl;
}
}
// Returns true if the named section exists
bool FileLibrary::containsSection(std::string sectionName, bool strict)
{
if (!strict)
boost::algorithm::to_lower(sectionName);
StringPathContMap::const_iterator mapIter = mMap.find(sectionName);
if (mapIter == mMap.end())
return false;
else
return true;
}
// Returns a pointer to const for a section of the library
const PathContainer* FileLibrary::section(std::string sectionName, bool strict)
{
if (!strict)
boost::algorithm::to_lower(sectionName);
StringPathContMap::const_iterator mapIter = mMap.find(sectionName);
if (mapIter == mMap.end())
{
//std::cout << "Empty\n";
return &mEmptyPath;
}
else
{
return &(mapIter->second);
}
}
// Searches the library for an item and returns a boost path to it
boost::filesystem::path FileLibrary::locate(std::string item, bool strict, bool ignoreExtensions, std::string sectionName)
{
boost::filesystem::path result("");
if (sectionName == "")
{
return FileListLocator(mPriorityList, boost::filesystem::path(item), strict, ignoreExtensions);
}
else
{
if (!containsSection(sectionName, strict))
{
std::cout << "Warning: There is no section named " << sectionName << "\n";
return result;
}
result = FileListLocator(mMap[sectionName], boost::filesystem::path(item), strict, ignoreExtensions);
}
return result;
}
// Prints all the available sections, used for debugging
void FileLibrary::printSections()
{
for(StringPathContMap::const_iterator mapIter = mMap.begin();
mapIter != mMap.end(); mapIter++)
{
std::cout << mapIter->first <<std::endl;
}
}
}

@ -0,0 +1,49 @@
#ifndef COMPONENTS_FILES_FILELIBRARY_HPP
#define COMPONENTS_FILES_FILELIBRARY_HPP
#include <components/files/fileops.hpp>
namespace Files
{
typedef std::map<std::string, PathContainer> StringPathContMap;
typedef std::vector<std::string> StringVector;
/// Looks for a string in a vector of strings
bool containsVectorString(const StringVector& list, const std::string& str);
/// \brief Searches directories and makes lists of files according to folder name
class FileLibrary
{
private:
StringPathContMap mMap;
PathContainer mEmptyPath;
PathContainer mPriorityList;
public:
/// Searches a path and adds the results to the library
/// Recursive search and fs strict options are available
/// Takes a vector of acceptable files extensions, if none is given it lists everything.
void add(const boost::filesystem::path &root, bool recursive, bool strict,
const StringVector &acceptableExtensions);
/// Returns true if the named section exists
/// You can run this check before running section()
bool containsSection(std::string sectionName, bool strict);
/// Returns a pointer to const for a section of the library
/// which is essentially a PathContainer.
/// If the section does not exists it returns a pointer to an empty path.
const PathContainer* section(std::string sectionName, bool strict);
/// Searches the library for an item and returns a boost path to it
/// Optionally you can provide a specific section
/// The result is the first that comes up according to alphabetical
/// section naming
boost::filesystem::path locate(std::string item, bool strict, bool ignoreExtensions, std::string sectionName="");
/// Prints all the available sections, used for debugging
void printSections();
};
}
#endif

@ -1,5 +1,9 @@
#include "fileops.hpp"
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
namespace Files
{
@ -9,4 +13,107 @@ bool isFile(const char *name)
return boost::filesystem::exists(boost::filesystem::path(name));
}
// Returns true if the last part of the superset matches the subset
bool endingMatches(const std::string& superset, const std::string& subset)
{
if (subset.length() > superset.length())
return false;
return superset.substr(superset.length() - subset.length()) == subset;
}
// Makes a list of files from a directory
void FileLister( boost::filesystem::path currentPath, Files::PathContainer& list, bool recursive)
{
if (!boost::filesystem::exists(currentPath))
{
std::cout << "WARNING: " << currentPath.string() << " does not exist.\n";
return ;
}
if (recursive)
{
for ( boost::filesystem::recursive_directory_iterator end, itr(currentPath.string());
itr != end; ++itr )
{
if ( boost::filesystem::is_regular_file(*itr))
list.push_back(itr->path());
}
}
else
{
for ( boost::filesystem::directory_iterator end, itr(currentPath.string());
itr != end; ++itr )
{
if ( boost::filesystem::is_regular_file(*itr))
list.push_back(itr->path());
}
}
}
// Locates path in path container
boost::filesystem::path FileListLocator (const Files::PathContainer& list, const boost::filesystem::path& toFind,
bool strict, bool ignoreExtensions)
{
boost::filesystem::path result("");
if (list.empty())
return result;
std::string toFindStr;
if (ignoreExtensions)
toFindStr = boost::filesystem::basename(toFind);
else
toFindStr = toFind.string();
std::string fullPath;
// The filesystems slash sets the default slash
std::string slash;
std::string wrongslash;
if(list[0].string().find("\\") != std::string::npos)
{
slash = "\\";
wrongslash = "/";
}
else
{
slash = "/";
wrongslash = "\\";
}
// The file being looked for is converted to the new slash
if(toFindStr.find(wrongslash) != std::string::npos )
{
boost::replace_all(toFindStr, wrongslash, slash);
}
if (!strict)
{
boost::algorithm::to_lower(toFindStr);
}
for (Files::PathContainer::const_iterator it = list.begin(); it != list.end(); ++it)
{
fullPath = it->string();
if (ignoreExtensions)
fullPath.erase(fullPath.length() -
boost::filesystem::path (it->extension()).string().length());
if (!strict)
{
boost::algorithm::to_lower(fullPath);
}
if(endingMatches(fullPath, toFindStr))
{
result = *it;
break;
}
}
return result;
}
// Overloaded form of the locator that takes a string and returns a string
std::string FileListLocator (const Files::PathContainer& list,const std::string& toFind, bool strict, bool ignoreExtensions)
{
return FileListLocator(list, boost::filesystem::path(toFind), strict, ignoreExtensions).string();
}
}

@ -1,6 +1,12 @@
#ifndef COMPONENTS_FILES_FILEOPS_HPP
#define COMPONENTS_FILES_FILEOPS_HPP
#include <map>
#include <vector>
#include <string>
#include <boost/filesystem/path.hpp>
namespace Files
{
@ -8,6 +14,25 @@ namespace Files
///\param [in] name - filename
bool isFile(const char *name);
/// A vector of Boost Paths, very handy
typedef std::vector<boost::filesystem::path> PathContainer;
/// Makes a list of files from a directory by taking a boost
/// path and a Path Container and adds to the Path container
/// all files in the path. It has a recursive option.
void FileLister( boost::filesystem::path currentPath, Files::PathContainer& list, bool recursive);
/// Locates boost path in path container
/// returns the path from the container
/// that contains the searched path.
/// If it's not found it returns and empty path
/// Takes care of slashes, backslashes and it has a strict option.
boost::filesystem::path FileListLocator (const Files::PathContainer& list, const boost::filesystem::path& toFind,
bool strict, bool ignoreExtensions);
/// Overloaded form of the locator that takes a string and returns a string
std::string FileListLocator (const Files::PathContainer& list,const std::string& toFind, bool strict, bool ignoreExtensions);
}
#endif /* COMPONENTS_FILES_FILEOPS_HPP */

@ -177,6 +177,8 @@ void NIFFile::parse()
records[i]->post(this);
}
/// \todo move to the write cpp file
void NiSkinInstance::post(NIFFile *nif)
{
int bnum = bones.length();

@ -100,6 +100,8 @@ struct Record
/// Does post-processing, after the entire tree is loaded
virtual void post(NIFFile *nif) {}
virtual ~Record() {}
/*
Use these later if you want custom allocation of all NIF objects

@ -1330,7 +1330,10 @@ void NIFLoader::loadResource(Resource *resource)
(*iter)->addBoneAssignment(vba);
}
mesh->_notifySkeleton(mSkel);
//Don't link on npc parts to eliminate redundant skeletons
//Will have to be changed later slightly for robes/skirts
if(triname == "")
mesh->_notifySkeleton(mSkel);
}
}

@ -1 +0,0 @@
Subproject commit 14b2851e72f610ae81dd296598867e6fb0babd2a

@ -0,0 +1,3 @@
upload_docs.sh
docs
*~

File diff suppressed because it is too large Load Diff

@ -0,0 +1,26 @@
Minimal Abstraction Game Layer (Mangle) is licensed under the
'zlib/libpng' license:
----
Copyright (c) 2009 Nicolay Korslund
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.

@ -0,0 +1,129 @@
Welcome to Mangle v0.1
----------------------
Written by: Nicolay Korslund (korslund@gmail.com)
License: zlib/png (see LICENSE.txt)
WWW: http://asm-soft.com/mangle/
Documentation: http://asm-soft.com/mangle/docs
Mangle is the project name for a small set of generic interfaces for
various game middleware libraries, such as sound, input, graphics, and
so on. You can imagine that it stands for "Minimal Abstraction Game
Layer", if you like. It will consist of several more or less
independent modules, one for each of these areas. These may be used
together to build an entire game engine, or they can be used
individually as separate libraries.
However, Mangle does NOT actually implement a game engine, or any new
fundamental functionality. More on that below.
Currently there's modules for sound and streams / archives (virtual
file systems.) More will come in the future (including input, 2D/3D
graphics, GUI, physics, and more.)
Main idea
---------
The idea behind Mangle is to provide a uniform, consistent interface
to other game libraries. The library does not provide ANY
functionality on its own. Instead it connects to a backend
implementation of your choice (or of your making.)
The Sound module, for example, currently has backends for OpenAL
(output only), FFmpeg (input only) and for Audiere. Hopefully we'll
add IrrKlang, FMod, DirectSound, Miles and more in the future. It can
combine libraries to get more complete functionality (like using
OpenAL for output and FFmpeg to decode sound files), and it's also
easy to write your own backend if you're using a different (or
home-brewed) sound system.
Regardless of what backend you use, the front-end interfaces (found
eg. in sound/output.h) is identical, and as a library user you
shouldn't notice much difference at all if you swap one backend for
another at a later point. It should Just Work.
The interfaces themselves are also quite simple. Setting up a sound
stream from FFmpeg or other decoder into OpenAL can be quite hairy -
but with Mangle the hairy parts have already been written for you. You
just plug the parts together.
The goal in the long run is to support a wide variety of game-related
libraries, and as many backend libraries (free and commercial) as
possible, so that you the user will have to write as little code as
possible.
What is it good for
-------------------
The main point of Mangle, as we said above, is that it connects to any
library of your choice "behind the scenes" but provides the same,
super-simple interface front-end for all of them. There can benefit
you in many ways:
- If you want to use a new library that Mangle support. You don't have
to scour the net for tutorials and usage examples, since much of the
common usage code is already included in the implementation classes.
- If you don't want to pollute your code with library-specific code.
The Mangle interfaces can help you keep your code clean, and its
user interface is often simpler than the exteral library one.
- If you want to quickly connect different libraries together, it
really helps if they speak a common language. The Mangle interfaces
are exactly that - a common language between libraries. Do you need
Audiere to load sounds from a weird archive format only implemented
for PhysFS, all channeled through the OGRE resource system? No
problem!
- If you are creating a library that depends on a specific feature
(such as sound), but you don't want to lock your users into any
specific sound library. Mangle works as an abstraction that lets
your users select their own implementation.
- If you want to support multiple backends for your game/app, or want
to make it possible to easily switch backends later. You can select
backends at compile time or even at runtime. For example you might
want to switch to to a commercial sound library at a later stage in
development, or you may want to use a different input library on
console platforms than on PC.
The Mangle implementations are extremely light-weight - often just one
or two cpp/h pairs per module. You can plug them directly into your
program, there's no separate library building step required.
Since the library aims to be very modularly put together, you can
also, in many cases, just copy-and-paste the parts you need and ignore
the rest. Or modify stuff without fearing that the whole 'system' will
come crashing down, because there is no big 'system' to speak of.
Past and future
---------------
Mangle started out as (and still is) a spin-off from OpenMW, another
project I am personally working on ( http://openmw.com/ ). OpenMW is
an attempt to recreate the engine behind the commercial game
Morrowind, using only open source software.
The projects are still tightly interlinked, and they will continue to
be until OpenMW is finished. Most near-future work on Mangle will be
focused chiefly on OpenMW at the moment. However I will gladly include
external contributions and suggestions that are not OpenMW-related if
someone sends them to me.
Conclusion
----------
As you might have guessed, Mangle is more a concept in development
than a finished library right now.
All feedback, ideas, concepts, questions and code are very
welcome. Send them to: korslund@gmail.com
I will put up a forum later as well if there's enough interest.

@ -0,0 +1,29 @@
#ifndef MANGLE_INPUT_OGREINPUTFRAME_H
#define MANGLE_INPUT_OGREINPUTFRAME_H
/*
This Ogre FrameListener calls capture() on an input driver every frame.
*/
#include <OgreFrameListener.h>
#include "../driver.hpp"
namespace Mangle {
namespace Input {
struct OgreInputCapture : Ogre::FrameListener
{
Mangle::Input::Driver &driver;
OgreInputCapture(Mangle::Input::Driver &drv)
: driver(drv) {}
bool frameStarted(const Ogre::FrameEvent &evt)
{
driver.capture();
return true;
}
};
}}
#endif

@ -0,0 +1,69 @@
#ifndef MANGLE_INPUT_DRIVER_H
#define MANGLE_INPUT_DRIVER_H
#include "event.hpp"
namespace Mangle
{
namespace Input
{
/** Input::Driver is the main interface to any input system that
handles keyboard and/or mouse input, along with any other
input source like joysticks.
It is really a generalized event system, and could also be
used for non-input related events. The definition of the event
codes and structures are entirely dependent on the
implementation.
A system-independent key code list will be found in keys.hpp,
and input drivers should privide optional translations to/from
this list for full compatibility.
*/
struct Driver
{
Driver() {}
virtual ~Driver() {}
/** Captures input and produces the relevant events from it. An
event callback must be set with setEvent(), or all events
will be ignored.
*/
virtual void capture() = 0;
/** Check the state of a given key or button. The key/button
definitions depends on the driver.
*/
virtual bool isDown(int index) = 0;
/** Show or hide system mouse cursor
*/
virtual void showMouse(bool show) = 0;
/** Set the event handler for input events. The evt->event()
function is called for each event. The meaning of the index
and *p parameters will be specific to each driver and to
each input system.
*/
void setEvent(EventPtr evt)
{ event = evt; }
/** Instigate an event. Is used internally for all events, but
can also be called from the outside to "fake" events from
this driver.
*/
void makeEvent(Event::Type type, int index, const void *p=NULL)
{
if(event)
event->event(type,index,p);
}
private:
/// Holds the event callback set byt setEvent()
EventPtr event;
};
typedef boost::shared_ptr<Driver> DriverPtr;
}
}
#endif

@ -0,0 +1,46 @@
#ifndef MANGLE_INPUT_EVENT_H
#define MANGLE_INPUT_EVENT_H
#include "../tools/shared_ptr.hpp"
namespace Mangle
{
namespace Input
{
/** Generic callback for input events. The meaning of the
parameters depend on the system producing the events.
*/
struct Event
{
/// Event types
enum Type
{
EV_Unknown = 1, // Unknown event type
EV_KeyDown = 2, // Keyboard button was pressed
EV_KeyUp = 4, // Keyboard button was released
EV_Keyboard = 6, // All keyboard events
EV_MouseMove = 8, // Mouse movement
EV_MouseDown = 16, // Mouse button pressed
EV_MouseUp = 32, // Mouse button released
EV_Mouse = 56, // All mouse events
EV_ALL = 63 // All events
};
/**
Called upon all events. The first parameter give the event
type, the second gives additional data (usually the local
keysym or button index as defined by the driver), and the
pointer points to the full custom event structure provided by
the driver (the type may vary depending on the EventType,
this is defined in the Driver documentation.)
*/
virtual void event(Type type, int index, const void *p) = 0;
virtual ~Event() {}
};
typedef boost::shared_ptr<Event> EventPtr;
}
}
#endif

@ -0,0 +1,47 @@
#ifndef MANGLE_INPUT_EVENTLIST_H
#define MANGLE_INPUT_EVENTLIST_H
#include "../event.hpp"
#include <vector>
namespace Mangle
{
namespace Input
{
/** And Event handler that distributes each event to a list of
other handlers. Supports filtering events by their Type
parameter.
*/
struct EventList : Event
{
struct Filter
{
EventPtr evt;
int flags;
};
std::vector<Filter> list;
void add(EventPtr e, int flags = EV_ALL)
{
Filter f;
f.evt = e;
f.flags = flags;
list.push_back(f);
}
virtual void event(Type type, int index, const void *p)
{
std::vector<Filter>::iterator it;
for(it=list.begin(); it!=list.end(); it++)
{
if(type & it->flags)
it->evt->event(type,index,p);
}
}
};
typedef boost::shared_ptr<EventList> EventListPtr;
}
}
#endif

@ -0,0 +1,148 @@
#include "ois_driver.hpp"
#include <assert.h>
#include <sstream>
#include <OgreRenderWindow.h>
#include <OIS/OIS.h>
#ifdef __APPLE_CC__
#include <Carbon/Carbon.h>
#endif
using namespace Mangle::Input;
using namespace OIS;
struct Mangle::Input::OISListener : OIS::KeyListener, OIS::MouseListener
{
OISDriver &drv;
OISListener(OISDriver &driver)
: drv(driver) {}
bool keyPressed( const OIS::KeyEvent &arg )
{
drv.makeEvent(Event::EV_KeyDown, arg.key, &arg);
return true;
}
bool keyReleased( const OIS::KeyEvent &arg )
{
drv.makeEvent(Event::EV_KeyUp, arg.key, &arg);
return true;
}
bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
// Mouse button events are handled as key events
// TODO: Translate mouse buttons into pseudo-keysyms
drv.makeEvent(Event::EV_MouseDown, id, &arg);
return true;
}
bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
// TODO: ditto
drv.makeEvent(Event::EV_MouseUp, id, &arg);
return true;
}
bool mouseMoved( const OIS::MouseEvent &arg )
{
drv.makeEvent(Event::EV_MouseMove, -1, &arg);
return true;
}
};
OISDriver::OISDriver(Ogre::RenderWindow *window, bool exclusive)
{
assert(window);
size_t windowHnd;
window->getCustomAttribute("WINDOW", &windowHnd);
std::ostringstream windowHndStr;
ParamList pl;
windowHndStr << windowHnd;
pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
// Set non-exclusive mouse and keyboard input if the user requested
// it.
if(!exclusive)
{
#if defined OIS_WIN32_PLATFORM
pl.insert(std::make_pair(std::string("w32_mouse"),
std::string("DISCL_FOREGROUND" )));
pl.insert(std::make_pair(std::string("w32_mouse"),
std::string("DISCL_NONEXCLUSIVE")));
pl.insert(std::make_pair(std::string("w32_keyboard"),
std::string("DISCL_FOREGROUND")));
pl.insert(std::make_pair(std::string("w32_keyboard"),
std::string("DISCL_NONEXCLUSIVE")));
#elif defined OIS_LINUX_PLATFORM
pl.insert(std::make_pair(std::string("x11_mouse_grab"),
std::string("false")));
pl.insert(std::make_pair(std::string("x11_mouse_hide"),
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
}
#ifdef __APPLE_CC__
// Give the application window focus to receive input events
ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
SetFrontProcess(&psn);
#endif
inputMgr = InputManager::createInputSystem( pl );
// Create all devices
keyboard = static_cast<Keyboard*>(inputMgr->createInputObject
( OISKeyboard, true ));
mouse = static_cast<Mouse*>(inputMgr->createInputObject
( OISMouse, true ));
// Set mouse region
const MouseState &ms = mouse->getMouseState();
ms.width = window->getWidth();
ms.height = window->getHeight();
// Set up the input listener
listener = new OISListener(*this);
keyboard-> setEventCallback(listener);
mouse-> setEventCallback(listener);
}
OISDriver::~OISDriver()
{
// Delete the listener object
if(listener)
delete listener;
if(inputMgr == NULL) return;
// Kill the input systems. This will reset input options such as key
// repeat rate.
inputMgr->destroyInputObject(keyboard);
inputMgr->destroyInputObject(mouse);
InputManager::destroyInputSystem(inputMgr);
inputMgr = NULL;
}
void OISDriver::capture()
{
// Capture keyboard and mouse events
keyboard->capture();
mouse->capture();
}
bool OISDriver::isDown(int index)
{
// TODO: Extend to mouse buttons as well
return keyboard->isKeyDown((OIS::KeyCode)index);
}

@ -0,0 +1,48 @@
#ifndef MANGLE_INPUT_OIS_DRIVER_H
#define MANGLE_INPUT_OIS_DRIVER_H
#include "../driver.hpp"
namespace OIS
{
class InputManager;
class Mouse;
class Keyboard;
}
namespace Ogre
{
class RenderWindow;
}
namespace Mangle
{
namespace Input
{
struct OISListener;
/** Input driver for OIS, the input manager typically used with
Ogre.
*/
struct OISDriver : Driver
{
/// If exclusive=true, then we capture mouse and keyboard from
/// the OS.
OISDriver(Ogre::RenderWindow *window, bool exclusive=true);
~OISDriver();
void capture();
bool isDown(int index);
/// Not currently supported.
void showMouse(bool) {}
private:
OIS::InputManager *inputMgr;
OIS::Mouse *mouse;
OIS::Keyboard *keyboard;
OISListener *listener;
};
}
}
#endif

@ -0,0 +1,54 @@
#include "sdl_driver.hpp"
#include <SDL.h>
using namespace Mangle::Input;
void SDLDriver::capture()
{
// Poll for events
SDL_Event evt;
while(SDL_PollEvent(&evt))
{
Event::Type type = Event::EV_Unknown;
int index = -1;
switch(evt.type)
{
// For key events, send the keysym as the index.
case SDL_KEYDOWN:
type = Event::EV_KeyDown;
index = evt.key.keysym.sym;
break;
case SDL_KEYUP:
type = Event::EV_KeyUp;
index = evt.key.keysym.sym;
break;
case SDL_MOUSEMOTION:
type = Event::EV_MouseMove;
break;
// Add more event types later
}
// Pass the event along, using -1 as index for unidentified
// event types.
makeEvent(type, index, &evt);
}
}
bool SDLDriver::isDown(int index)
{
int num;
Uint8 *keys = SDL_GetKeyState(&num);
assert(index >= 0 && index < num);
// The returned array from GetKeyState is indexed by the
// SDLK_KEYNAME enums and is just a list of bools. If the indexed
// value is true, the button is down.
return keys[index];
}
void SDLDriver::showMouse(bool show)
{
SDL_ShowCursor(show?SDL_ENABLE:SDL_DISABLE);
}

@ -0,0 +1,27 @@
#ifndef MANGLE_INPUT_SDL_DRIVER_H
#define MANGLE_INPUT_SDL_DRIVER_H
#include "../driver.hpp"
namespace Mangle
{
namespace Input
{
/** Input driver for SDL. As the input system of SDL is seldomly
used alone (most often along with the video system), it is
assumed that you do your own initialization and cleanup of SDL
before and after using this driver.
The Event.event() calls will be given the proper EV_ type, the
key index (for key up/down events), and a pointer to the full
SDL_Event structure.
*/
struct SDLDriver : Driver
{
void capture();
bool isDown(int index);
void showMouse(bool);
};
}
}
#endif

@ -0,0 +1,2 @@
*_test
ogre.cfg

@ -0,0 +1,15 @@
GCC=g++ -Wall
all: sdl_driver_test ois_driver_test evtlist_test
sdl_driver_test: sdl_driver_test.cpp
$(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL
ois_driver_test: ois_driver_test.cpp
$(GCC) $< ../servers/ois_driver.cpp -o $@ -I/usr/local/include/OGRE/ -lOgreMain -lOIS -lboost_filesystem
evtlist_test: evtlist_test.cpp ../filters/eventlist.hpp ../event.hpp
$(GCC) $< -o $@
clean:
rm *_test

@ -0,0 +1,35 @@
#include <iostream>
#include "../driver.hpp"
#include <unistd.h>
using namespace std;
using namespace Mangle::Input;
Driver *input;
struct MyCB : Event
{
void event(Event::Type type, int i, const void *p)
{
cout << "got event: type=" << type << " index=" << i << endl;
}
};
void mainLoop(int argc, int quitKey)
{
cout << "Hold the Q key to quit:\n";
input->setEvent(EventPtr(new MyCB));
while(!input->isDown(quitKey))
{
input->capture();
usleep(20000);
if(argc == 1)
{
cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n";
break;
}
}
delete input;
cout << "\nBye bye!\n";
}

@ -0,0 +1,45 @@
#include <iostream>
#include "../filters/eventlist.hpp"
using namespace std;
using namespace Mangle::Input;
struct MyEvent : Event
{
int ii;
MyEvent(int i) : ii(i) {}
void event(Event::Type type, int i, const void *p)
{
cout << " #" << ii << " got event: type=" << type << " index=" << i << endl;
}
};
EventList lst;
int iii=1;
void make(int flags)
{
lst.add(EventPtr(new MyEvent(iii++)), flags);
}
void send(Event::Type type)
{
cout << "Sending type " << type << endl;
lst.event(type,0,NULL);
}
int main()
{
make(Event::EV_ALL);
make(Event::EV_KeyDown);
make(Event::EV_KeyUp | Event::EV_MouseDown);
send(Event::EV_Unknown);
send(Event::EV_KeyDown);
send(Event::EV_KeyUp);
send(Event::EV_MouseDown);
cout << "Enough of that\n";
return 0;
}

@ -0,0 +1,51 @@
#include "common.cpp"
#include "../servers/ois_driver.hpp"
#include <Ogre.h>
#include <OIS/OIS.h>
#include <boost/filesystem.hpp>
bool isFile(const char *name)
{
boost::filesystem::path cfg_file_path(name);
return boost::filesystem::exists(cfg_file_path);
}
using namespace Ogre;
using namespace OIS;
Root *root;
RenderWindow *window;
void setupOgre()
{
// Disable logging
new LogManager;
Log *log = LogManager::getSingleton().createLog("");
log->setDebugOutputEnabled(false);
bool useConfig = isFile("ogre.cfg");
// Set up Root
root = new Root("plugins.cfg", "ogre.cfg", "");
// Configure
if(!useConfig)
root->showConfigDialog();
else
root->restoreConfig();
// Initialize OGRE window
window = root->initialise(true, "test", "");
}
int main(int argc, char** argv)
{
setupOgre();
input = new OISDriver(window);
mainLoop(argc, KC_Q);
delete root;
return 0;
}

@ -0,0 +1,12 @@
Sending type 1
#1 got event: type=1 index=0
Sending type 2
#1 got event: type=2 index=0
#2 got event: type=2 index=0
Sending type 4
#1 got event: type=4 index=0
#3 got event: type=4 index=0
Sending type 16
#1 got event: type=16 index=0
#3 got event: type=16 index=0
Enough of that

@ -0,0 +1,5 @@
Hold the Q key to quit:
got event: type=8 index=-1
You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly
Bye bye!

@ -0,0 +1,5 @@
Hold the Q key to quit:
got event: type=1 index=-1
You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly
Bye bye!

@ -0,0 +1,12 @@
# Defines plugins to load
# Define plugin folder
PluginFolder=/usr/local/lib/OGRE/
# Define plugins
Plugin=RenderSystem_GL
Plugin=Plugin_ParticleFX
Plugin=Plugin_OctreeSceneManager
# Plugin=Plugin_CgProgramManager

@ -0,0 +1,16 @@
#include "common.cpp"
#include "../servers/sdl_driver.hpp"
#include <SDL.h>
int main(int argc, char** argv)
{
SDL_Init(SDL_INIT_VIDEO);
SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
input = new SDLDriver();
mainLoop(argc, SDLK_q);
SDL_Quit();
return 0;
}

@ -0,0 +1,18 @@
#!/bin/bash
make || exit
mkdir -p output
PROGS=*_test
for a in $PROGS; do
if [ -f "output/$a.out" ]; then
echo "Running $a:"
./$a | diff output/$a.out -
else
echo "Creating $a.out"
./$a > "output/$a.out"
git add "output/$a.out"
fi
done

@ -0,0 +1,63 @@
#ifndef MANGLE_REND2D_DRIVER_H
#define MANGLE_REND2D_DRIVER_H
#include <string>
#include "sprite.hpp"
namespace Mangle
{
namespace Rend2D
{
/**
The driver is the connection to the backend system that powers
2D sprite rendering. For example the backend could be SDL or
any other 2D-capable graphics library.
*/
struct Driver
{
/// Get the screen sprite
virtual Sprite *getScreen() = 0;
/// Sets the video mode.
virtual void setVideoMode(int width, int height, int bpp=32, bool fullscreen=false) = 0;
/** Update the screen. Until this function is called, none of
the changes written to the screen sprite will be visible.
*/
virtual void update() = 0;
/// Set the window title, as well as the title of the window
/// when "iconified"
virtual void setWindowTitle(const std::string &title,
const std::string &icon) = 0;
/// Set the window title
void setWindowTitle(const std::string &title) { setWindowTitle(title,title); }
/// Load sprite from an image file. Thows an exception on
/// failure.
virtual Sprite* loadImage(const std::string &file) = 0;
/// Load a sprite from an image file stored in memory. Throws
/// exception on failure.
virtual Sprite* loadImage(const void* data, size_t size) = 0;
/** @brief Set gamma value for all colors.
Note: Setting this in windowed mode will affect the ENTIRE
SCREEN!
*/
virtual void setGamma(float gamma) = 0;
/// Set gamma individually for red, green, blue
virtual void setGamma(float red, float green, float blue) = 0;
/// Get screen width
virtual int width() = 0;
/// Get screen height
virtual int height() = 0;
};
}
}
#endif

@ -0,0 +1,259 @@
#include "sdl_driver.hpp"
#include <SDL.h>
#include <SDL_image.h>
#include <stdexcept>
#include <assert.h>
using namespace Mangle::Rend2D;
const SpriteData *SDL_Sprite::lock()
{
// Make sure we aren't already locked
assert(!data.pixels);
// Lock the surface and set up the data structure
SDL_LockSurface(surface);
data.pixels = surface->pixels;
data.w = surface->w;
data.h = surface->h;
data.pitch = surface->pitch;
data.bypp = surface->format->BytesPerPixel;
return &data;
}
void SDL_Sprite::unlock()
{
if(data.pixels)
{
SDL_UnlockSurface(surface);
data.pixels = NULL;
}
}
// This is a really crappy and slow implementation, only intended for
// testing purposes. Use lock/unlock for faster pixel drawing.
void SDL_Sprite::pixel(int x, int y, int color)
{
SDL_LockSurface(surface);
int bpp = surface->format->BytesPerPixel;
char *p = (char*)surface->pixels + y*surface->pitch + x*bpp;
switch(bpp)
{
case 1: *p = color; break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
{
p[0] = (color >> 16) & 0xff;
p[1] = (color >> 8) & 0xff;
p[2] = color & 0xff;
}
else
{
p[0] = color & 0xff;
p[1] = (color >> 8) & 0xff;
p[2] = (color >> 16) & 0xff;
}
break;
case 4:
*(int*)p = color;
break;
}
SDL_UnlockSurface(surface);
}
void SDL_Sprite::draw(Sprite *s, // Must be SDL_Sprite
int x, int y, // Destination position
int sx, int sy, // Source position
int w, int h // Amount to draw. -1 means remainder.
)
{
// Get source surface
SDL_Sprite *other = dynamic_cast<SDL_Sprite*>(s);
assert(other != NULL);
SDL_Surface *img = other->getSurface();
// Check coordinate validity
assert(sx <= img->w && sy <= img->h);
assert(x <= surface->w && y <= surface->h);
assert(sx >= 0 && sy >= 0);
// Compute width and height if necessary
if(w == -1) w = img->w - sx;
if(h == -1) h = img->h - sy;
// Check them if they're valid
assert(w >= 0 && w <= img->w);
assert(h >= 0 && h <= img->h);
SDL_Rect dest;
dest.x = x;
dest.y = y;
dest.w = w;
dest.h = h;
SDL_Rect src;
src.x = sx;
src.y = sy;
src.w = w;
src.h = h;
// Do the Blitman
SDL_BlitSurface(img, &src, surface, &dest);
}
SDL_Sprite::SDL_Sprite(SDL_Surface *s, bool autoDelete)
: surface(s), autoDel(autoDelete)
{
assert(surface != NULL);
data.pixels = NULL;
}
SDL_Sprite::~SDL_Sprite()
{
if(autoDel)
SDL_FreeSurface(surface);
}
void SDL_Sprite::fill(int value)
{
SDL_FillRect(surface, NULL, value);
}
int SDL_Sprite::width() { return surface->w; }
int SDL_Sprite::height() { return surface->h; }
SDLDriver::SDLDriver() : display(NULL), realDisp(NULL), softDouble(false)
{
if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1)
throw std::runtime_error("Error initializing SDL video");
}
SDLDriver::~SDLDriver()
{
if(display) delete display;
SDL_Quit();
}
void SDLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen)
{
unsigned int flags;
if(display) delete display;
if (fullscreen)
// Assume fullscreen mode allows a double-bufferd hardware
// mode. We need more test code for this to be safe though.
flags = SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF;
else
flags = SDL_SWSURFACE;
// Create the surface and check it
realDisp = SDL_SetVideoMode(width, height, bpp, flags);
if(realDisp == NULL)
throw std::runtime_error("Failed setting SDL video mode");
// Code for software double buffering. I haven't found this to be
// any speed advantage at all in windowed mode (it's slower, as one
// would expect.) Not properly tested in fullscreen mode with
// hardware buffers, but it will probably only be an improvement if
// we do excessive writing (ie. write each pixel on average more
// than once) or try to read from the display buffer.
if(softDouble)
{
// Make a new surface with the same attributes as the real
// display surface.
SDL_Surface *back = SDL_DisplayFormat(realDisp);
assert(back != NULL);
// Create a sprite representing the double buffer
display = new SDL_Sprite(back);
}
else
{
// Create a sprite directly representing the display surface.
// The 'false' parameter means do not autodelete the screen
// surface upon exit (since SDL manages it)
display = new SDL_Sprite(realDisp, false);
}
}
/// Update the screen
void SDLDriver::update()
{
// Blit the soft double buffer onto the real display buffer
if(softDouble)
SDL_BlitSurface(display->getSurface(), NULL, realDisp, NULL );
if(realDisp)
SDL_Flip(realDisp);
}
/// Set the window title, as well as the title of the window when
/// "iconified"
void SDLDriver::setWindowTitle(const std::string &title,
const std::string &icon)
{
SDL_WM_SetCaption( title.c_str(), icon.c_str() );
}
// Convert the given surface to display format.
static SDL_Surface* convertImage(SDL_Surface* surf)
{
if(surf != NULL)
{
// Convert the image to the display buffer format, for faster
// blitting
SDL_Surface *surf2 = SDL_DisplayFormat(surf);
SDL_FreeSurface(surf);
surf = surf2;
}
return surf;
}
/// Load sprite from an image file, using SDL_image.
Sprite* SDLDriver::loadImage(const std::string &file)
{
SDL_Surface *surf = IMG_Load(file.c_str());
surf = convertImage(surf);
if(surf == NULL)
throw std::runtime_error("SDL failed to load image file '" + file + "'");
return spriteFromSDL(surf);
}
/// Load sprite from an SDL_RWops structure. autoFree determines
/// whether the RWops struct should be closed/freed after use.
Sprite* SDLDriver::loadImage(SDL_RWops *src, bool autoFree)
{
SDL_Surface *surf = IMG_Load_RW(src, autoFree);
surf = convertImage(surf);
if(surf == NULL)
throw std::runtime_error("SDL failed to load image");
return spriteFromSDL(surf);
}
/// Load a sprite from an image file stored in memory. Uses
/// SDL_image.
Sprite* SDLDriver::loadImage(const void* data, size_t size)
{
SDL_RWops *rw = SDL_RWFromConstMem(data, size);
return loadImage(rw, true);
}
void SDLDriver::setGamma(float red, float green, float blue)
{
SDL_SetGamma(red,green,blue);
}
/// Convert an existing SDL surface into a sprite
Sprite* SDLDriver::spriteFromSDL(SDL_Surface *surf, bool autoFree)
{
assert(surf);
return new SDL_Sprite(surf, autoFree);
}
void SDLDriver::sleep(int ms) { SDL_Delay(ms); }
unsigned int SDLDriver::ticks() { return SDL_GetTicks(); }

@ -0,0 +1,125 @@
#ifndef MANGLE_DRAW2D_SDL_H
#define MANGLE_DRAW2D_SDL_H
#include "../driver.hpp"
// Predeclarations keep the streets safe at night
struct SDL_Surface;
struct SDL_RWops;
namespace Mangle
{
namespace Rend2D
{
/// SDL-implementation of Sprite
struct SDL_Sprite : Sprite
{
/** Draw a sprite in the given position. Can only draw other SDL
sprites.
*/
void draw(Sprite *s, // Must be SDL_Sprite
int x, int y, // Destination position
int sx=0, int sy=0, // Source position
int w=-1, int h=-1 // Amount to draw. -1 means remainder.
);
SDL_Sprite(SDL_Surface *s, bool autoDelete=true);
~SDL_Sprite();
// Information retrieval
int width();
int height();
SDL_Surface *getSurface() { return surface; }
// Fill with a given pixel value
void fill(int value);
// Set one pixel
void pixel(int x, int y, int value);
const SpriteData *lock();
void unlock();
private:
// The SDL surface
SDL_Surface* surface;
// Used for locking
SpriteData data;
// If true, delete this surface when the canvas is destructed
bool autoDel;
};
class SDLDriver : public Driver
{
// The main display surface
SDL_Sprite *display;
// The actual display surface. May or may not be the same
// surface pointed to by 'display' above, depending on the
// softDouble flag.
SDL_Surface *realDisp;
// If true, we do software double buffering.
bool softDouble;
public:
SDLDriver();
~SDLDriver();
/// Sets the video mode. Will create the window if it is not
/// already set up. Note that for SDL, bpp=0 means use current
/// bpp.
void setVideoMode(int width, int height, int bpp=0, bool fullscreen=false);
/// Update the screen
void update();
/// Set the window title, as well as the title of the window
/// when "iconified"
void setWindowTitle(const std::string &title,
const std::string &icon);
// Include overloads from our Glorious parent
using Driver::setWindowTitle;
/// Load sprite from an image file, using SDL_image.
Sprite* loadImage(const std::string &file);
/// Load sprite from an SDL_RWops structure. autoFree determines
/// whether the RWops struct should be closed/freed after use.
Sprite* loadImage(SDL_RWops *src, bool autoFree=false);
/// Load a sprite from an image file stored in memory. Uses
/// SDL_image.
Sprite* loadImage(const void* data, size_t size);
/// Set gamma value
void setGamma(float gamma) { setGamma(gamma,gamma,gamma); }
/// Set gamma individually for red, green, blue
void setGamma(float red, float green, float blue);
/// Convert an existing SDL surface into a sprite
Sprite* spriteFromSDL(SDL_Surface *surf, bool autoFree = true);
// Get width and height
int width() { return display ? display->width() : 0; }
int height() { return display ? display->height() : 0; }
/// Get the screen sprite
Sprite *getScreen() { return display; }
/// Not really a graphic-related function, but very
/// handly. Sleeps the given number of milliseconds using
/// SDL_Delay().
void sleep(int ms);
/// Get the number of ticks since SDL initialization, using
/// SDL_GetTicks().
unsigned int ticks();
};
}
}
#endif

@ -0,0 +1,311 @@
#include "sdl_gl_driver.hpp"
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_opengl.h>
#include <stdexcept>
#include <assert.h>
using namespace Mangle::Rend2D;
void SDLGL_Sprite::draw(Sprite *s, // Must be SDLGL_Sprite
int x, int y, // Destination position
int sx, int sy, // Source position
int w, int h // Amount to draw. -1 means remainder.
)
{
// Get source surface
SDLGL_Sprite *other = dynamic_cast<SDLGL_Sprite*>(s);
assert(other != NULL);
SDL_Surface *img = other->getSurface();
// Check coordinate validity
assert(sx <= img->w && sy <= img->h);
assert(x <= surface->w && y <= surface->h);
assert(sx >= 0 && sy >= 0);
// Compute width and height if necessary
if(w == -1) w = img->w - sx;
if(h == -1) h = img->h - sy;
// Check them if they're valid
assert(w >= 0 && w <= img->w);
assert(h >= 0 && h <= img->h);
SDL_Rect dest;
dest.x = x;
dest.y = y;
dest.w = w;
dest.h = h;
SDL_Rect src;
src.x = sx;
src.y = sy;
src.w = w;
src.h = h;
// Do the Blitman
SDL_BlitSurface(img, &src, surface, &dest);
}
SDLGL_Sprite::SDLGL_Sprite(SDL_Surface *s, bool autoDelete)
: surface(s), autoDel(autoDelete)
{
assert(surface != NULL);
}
SDLGL_Sprite::~SDLGL_Sprite()
{
if(autoDel)
SDL_FreeSurface(surface);
}
void SDLGL_Sprite::fill(int value)
{
SDL_FillRect(surface, NULL, value);
}
int SDLGL_Sprite::width() { return surface->w; }
int SDLGL_Sprite::height() { return surface->h; }
SDLGLDriver::SDLGLDriver() : display(NULL), realDisp(NULL)
{
if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1)
throw std::runtime_error("Error initializing SDL video");
}
SDLGLDriver::~SDLGLDriver()
{
if(display) delete display;
SDL_Quit();
}
// Surface used for the screen. Since OpenGL surfaces must have sizes
// that are powers of 2, we have to "fake" the returned display size
// to match the screen, not the surface itself. If we don't use this,
// the client program will get confused about the actual size of our
// screen, thinking it is bigger than it is.
struct FakeSizeSprite : SDLGL_Sprite
{
int fakeW, fakeH;
FakeSizeSprite(SDL_Surface *s, int fw, int fh)
: SDLGL_Sprite(s), fakeW(fw), fakeH(fh)
{}
int width() { return fakeW; }
int height() { return fakeH; }
};
static int makePow2(int num)
{
assert(num);
if((num & (num-1)) != 0)
{
int cnt = 0;
while(num)
{
num >>= 1;
cnt++;
}
num = 1 << cnt;
}
return num;
}
void SDLGLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen)
{
unsigned int flags;
if(display) delete display;
flags = SDL_OPENGL;
if (fullscreen)
flags |= SDL_FULLSCREEN;
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1 );
// Create the surface and check it
screen = SDL_SetVideoMode(width, height, bpp, flags);
if(screen == NULL)
throw std::runtime_error("Failed setting SDL video mode");
// Expand width and height to be powers of 2
int width2 = makePow2(width);
int height2 = makePow2(height);
// Create a new SDL surface of this size
const SDL_PixelFormat& fmt = *(screen->format);
realDisp = SDL_CreateRGBSurface(SDL_SWSURFACE,width2,height2,
fmt.BitsPerPixel,
fmt.Rmask,fmt.Gmask,fmt.Bmask,fmt.Amask);
// Create a sprite directly representing the display surface. This
// allows the user to blit to it directly.
display = new FakeSizeSprite(realDisp, width, height);
// Set up the OpenGL format
nOfColors = fmt.BytesPerPixel;
if(nOfColors == 4)
{
if (fmt.Rmask == 0x000000ff)
texture_format = GL_RGBA;
else
texture_format = GL_BGRA;
}
else if(nOfColors == 3)
{
if (fmt.Rmask == 0x000000ff)
texture_format = GL_RGB;
else
texture_format = GL_BGR;
}
else
assert(0 && "unsupported screen format");
glEnable(GL_TEXTURE_2D);
// Have OpenGL generate a texture object handle for us
glGenTextures( 1, &texture );
// Bind the texture object
glBindTexture( GL_TEXTURE_2D, texture );
// Set the texture's stretching properties
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
}
void SDLGLDriver::updateNoSwap()
{
if(!realDisp) return;
// Fist, set up the screen texture:
// Bind the texture object
glBindTexture( GL_TEXTURE_2D, texture );
// Edit the texture object's image data
glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, realDisp->w, realDisp->h, 0,
texture_format, GL_UNSIGNED_BYTE, realDisp->pixels );
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// OpenGL barf. Set up the projection to match our screen
int vPort[4];
glGetIntegerv(GL_VIEWPORT, vPort);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, vPort[2], 0, vPort[3], -1, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glBegin( GL_QUADS );
// Needed to move the screen into the right place
int diff = screen->h - realDisp->h;
// Bottom-left vertex (corner)
glTexCoord2i( 0, 1 );
glVertex3f(0,diff,0);
// Bottom-right vertex (corner)
glTexCoord2i( 1, 1 );
glVertex3f( realDisp->w, diff, 0.f );
// Top-right vertex (corner)
glTexCoord2i( 1, 0 );
glVertex3f( realDisp->w, screen->h, 0.f );
// Top-left vertex (corner)
glTexCoord2i( 0, 0 );
glVertex3f( 0, screen->h, 0.f );
glEnd();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
void SDLGLDriver::swap()
{
SDL_GL_SwapBuffers();
}
void SDLGLDriver::update()
{
updateNoSwap();
swap();
}
/// Set the window title, as well as the title of the window when
/// "iconified"
void SDLGLDriver::setWindowTitle(const std::string &title,
const std::string &icon)
{
SDL_WM_SetCaption( title.c_str(), icon.c_str() );
}
// Convert the given surface to display format.
static SDL_Surface* convertImage(SDL_Surface* surf)
{
if(surf != NULL)
{
// Convert the image to the display buffer format, for faster
// blitting
SDL_Surface *surf2 = SDL_DisplayFormat(surf);
SDL_FreeSurface(surf);
surf = surf2;
}
return surf;
}
/// Load sprite from an image file, using SDL_image.
Sprite* SDLGLDriver::loadImage(const std::string &file)
{
SDL_Surface *surf = IMG_Load(file.c_str());
surf = convertImage(surf);
if(surf == NULL)
throw std::runtime_error("SDL failed to load image file '" + file + "'");
return spriteFromSDL(surf);
}
/// Load sprite from an SDL_RWops structure. autoFree determines
/// whether the RWops struct should be closed/freed after use.
Sprite* SDLGLDriver::loadImage(SDL_RWops *src, bool autoFree)
{
SDL_Surface *surf = IMG_Load_RW(src, autoFree);
surf = convertImage(surf);
if(surf == NULL)
throw std::runtime_error("SDL failed to load image");
return spriteFromSDL(surf);
}
/// Load a sprite from an image file stored in memory. Uses
/// SDL_image.
Sprite* SDLGLDriver::loadImage(const void* data, size_t size)
{
SDL_RWops *rw = SDL_RWFromConstMem(data, size);
return loadImage(rw, true);
}
void SDLGLDriver::setGamma(float red, float green, float blue)
{
SDL_SetGamma(red,green,blue);
}
/// Convert an existing SDL surface into a sprite
Sprite* SDLGLDriver::spriteFromSDL(SDL_Surface *surf, bool autoFree)
{
assert(surf);
return new SDLGL_Sprite(surf, autoFree);
}
void SDLGLDriver::sleep(int ms) { SDL_Delay(ms); }
unsigned int SDLGLDriver::ticks() { return SDL_GetTicks(); }

@ -0,0 +1,132 @@
#ifndef MANGLE_DRAW2D_SDLGL_H
#define MANGLE_DRAW2D_SDLGL_H
/** This driver is similar to SDLDriver, except that it uses SDL on
top of OpenGL.
I've decided to make it a separate file instead of just adding
optional OpenGL support to the original, so that pure SDL users
don't have to add OpenGL as a dependency.
*/
#include "../driver.hpp"
// Predeclarations keep the streets safe at night
struct SDL_Surface;
struct SDL_RWops;
namespace Mangle
{
namespace Rend2D
{
/// SDL-implementation of Sprite
struct SDLGL_Sprite : Sprite
{
/** Draw a sprite in the given position. Can only draw other SDL
sprites.
*/
void draw(Sprite *s, // Must be SDLGL_Sprite
int x, int y, // Destination position
int sx=0, int sy=0, // Source position
int w=-1, int h=-1 // Amount to draw. -1 means remainder.
);
SDLGL_Sprite(SDL_Surface *s, bool autoDelete=true);
~SDLGL_Sprite();
// Information retrieval
virtual int width();
virtual int height();
SDL_Surface *getSurface() { return surface; }
// Fill with a given pixel value
void fill(int value);
private:
// The SDL surface
SDL_Surface* surface;
// If true, delete this surface when the canvas is destructed
bool autoDel;
};
class SDLGLDriver : public Driver
{
// The main display surface
SDLGL_Sprite *display;
// The screen surface. This is completely unused.
SDL_Surface *screen;
// The display surface and main GL texture. These are used when
// drawing the entire screen as one surface, as a drop-in
// replacement for SDLDriver.
SDL_Surface *realDisp;
unsigned int texture;
int nOfColors, texture_format;
public:
SDLGLDriver();
~SDLGLDriver();
/// Sets the video mode. Will create the window if it is not
/// already set up. Note that for SDL, bpp=0 means use current
/// bpp.
void setVideoMode(int width, int height, int bpp=0, bool fullscreen=false);
/// Update the screen
void update();
/// Calls SDL_GL_SwapBuffers
void swap();
/// Draw surface to screen but do not call SDL_GL_SwapBuffers()
void updateNoSwap();
/// Set the window title, as well as the title of the window
/// when "iconified"
void setWindowTitle(const std::string &title,
const std::string &icon);
// Include overloads from our Glorious parent
using Driver::setWindowTitle;
/// Load sprite from an image file, using SDL_image.
Sprite* loadImage(const std::string &file);
/// Load sprite from an SDL_RWops structure. autoFree determines
/// whether the RWops struct should be closed/freed after use.
Sprite* loadImage(SDL_RWops *src, bool autoFree=false);
/// Load a sprite from an image file stored in memory. Uses
/// SDL_image.
Sprite* loadImage(const void* data, size_t size);
/// Set gamma value
void setGamma(float gamma) { setGamma(gamma,gamma,gamma); }
/// Set gamma individually for red, green, blue
void setGamma(float red, float green, float blue);
/// Convert an existing SDL surface into a sprite
Sprite* spriteFromSDL(SDL_Surface *surf, bool autoFree = true);
// Get width and height
int width() { return display ? display->width() : 0; }
int height() { return display ? display->height() : 0; }
/// Get the screen sprite
Sprite *getScreen() { return display; }
/// Not really a graphic-related function, but very
/// handly. Sleeps the given number of milliseconds using
/// SDL_Delay().
void sleep(int ms);
/// Get the number of ticks since SDL initialization, using
/// SDL_GetTicks().
unsigned int ticks();
};
}
}
#endif

@ -0,0 +1,57 @@
#ifndef MANGLE_REND2D_SPRITE_H
#define MANGLE_REND2D_SPRITE_H
namespace Mangle
{
namespace Rend2D
{
/**
A pointer to sprite data for direct drawing. Only to be used
while the corresponding sprite is locked.
*/
struct SpriteData
{
void *pixels; // Pixel data
int w, h; // Width and height
int pitch, bypp; // Pitch (bytes) and bytes per pixel
};
/**
A Sprite is either a bitmap to be drawn or an output of area
for blitting other bitmaps, or both. They are created by the
Driver.
*/
struct Sprite
{
/// Draw a sprite in the given position
virtual void draw(Sprite *s, // The sprite to draw
int x, int y, // Destination position
int sx=0, int sy=0, // Source position
int w=-1, int h=-1 // Amount to draw. -1 means remainder.
) = 0;
virtual ~Sprite() {}
// Information retrieval
virtual int width() = 0;
virtual int height() = 0;
/// Fill the sprite with the given pixel value. The pixel format
/// depends on the format of the sprite.
virtual void fill(int value) = 0;
/// Set one pixel value. The pixel format depends on the sprite
/// format. This is not expected to be fast, and in some
/// implementations may not work at all.
virtual void pixel(int x, int y, int value) {}
/// Lock sprite for direct drawing, and return a struct
/// containing the necessary pointer. When finished, unlock the
/// sprite with unlock(). May return NULL, if so then direct
/// drawing is not possible.
virtual const SpriteData *lock() { return NULL; }
virtual void unlock() {}
};
}
}
#endif

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

Loading…
Cancel
Save