diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 1ce6250b3a..0000000000
--- a/.gitmodules
+++ /dev/null
@@ -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
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7564fab90e..8ba9a5f0b4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,7 +18,7 @@ include (OpenMWMacros)
 # Version
 
 set (OPENMW_VERSION_MAJOR 0)
-set (OPENMW_VERSION_MINOR 12)
+set (OPENMW_VERSION_MINOR 13)
 set (OPENMW_VERSION_RELEASE 0)
 
 set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
@@ -27,6 +27,8 @@ set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VE
 
 configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp")
 
+option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE)
+
 # Sound source selection
 option(USE_AUDIERE "use Audiere for sound" OFF)
 option(USE_FFMPEG "use ffmpeg for sound" OFF)
@@ -116,6 +118,10 @@ set(OENGINE_BULLET
     ${LIBDIR}/openengine/bullet/physic.hpp
     ${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp
     ${LIBDIR}/openengine/bullet/BulletShapeLoader.h
+	${LIBDIR}/openengine/bullet/pmove.h
+	${LIBDIR}/openengine/bullet/pmove.cpp
+	${LIBDIR}/openengine/bullet/trace.h
+	${LIBDIR}/openengine/bullet/trace.cpp
 )
 
 # Sound setup
@@ -189,8 +195,16 @@ find_package(Boost REQUIRED COMPONENTS system filesystem program_options thread)
 find_package(OIS REQUIRED)
 find_package(OpenAL REQUIRED)
 find_package(Bullet REQUIRED)
+IF(OGRE_STATIC)
+find_package(Cg REQUIRED)
+IF(WIN32)
+set(OGRE_PLUGIN_INCLUDE_DIRS ${OGRE_Plugin_CgProgramManager_INCLUDE_DIRS} ${OGRE_Plugin_OctreeSceneManager_INCLUDE_DIRS} ${OGRE_Plugin_ParticleFX_INCLUDE_DIRS} ${OGRE_RenderSystem_Direct3D9_INCLUDE_DIRS} ${OGRE_RenderSystem_GL_INCLUDE_DIRS})
+ELSE(WIN32)
+set(OGRE_PLUGIN_INCLUDE_DIRS ${OGRE_Plugin_CgProgramManager_INCLUDE_DIRS} ${OGRE_Plugin_OctreeSceneManager_INCLUDE_DIRS} ${OGRE_Plugin_ParticleFX_INCLUDE_DIRS} ${OGRE_RenderSystem_GL_INCLUDE_DIRS})
+ENDIF(WIN32)
+ENDIF(OGRE_STATIC)
 include_directories("."
-    ${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE
+    ${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE ${OGRE_PLUGIN_INCLUDE_DIRS}
     ${OIS_INCLUDE_DIRS} ${Boost_INCLUDE_DIR}
     ${PLATFORM_INCLUDE_DIR}
     ${CMAKE_HOME_DIRECTORY}/extern/mygui_3.0.1/MyGUIEngine/include
@@ -255,6 +269,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 +286,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)
@@ -405,6 +425,47 @@ 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})
+    if (BUILD_LAUNCHER)
+		set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS})
+	endif (BUILD_LAUNCHER)
+    set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS})
   endif(MSVC)
 
   # Same for MinGW
diff --git a/README_Mac.md b/README_Mac.md
index 39382e0c9d..dc39183680 100644
--- a/README_Mac.md
+++ b/README_Mac.md
@@ -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.
 
-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
+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"
+
+# Run
+From your build directory run:
 
-12. From your build directory run:
         $ OpenMW.app/Contents/MacOS/openmw
-    or:
+or:
+
         $ open OpenMW.app    
-  Enjoy!
-
-   
+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/
\ No newline at end of file
diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt
index f0e5eba948..fd736e0112 100644
--- a/apps/launcher/CMakeLists.txt
+++ b/apps/launcher/CMakeLists.txt
@@ -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})
@@ -52,7 +53,17 @@ QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
 include(${QT_USE_FILE})
 
 # Main executable
+IF(OGRE_STATIC)
+IF(WIN32)
+ADD_DEFINITIONS(-DENABLE_PLUGIN_Direct3D9 -DENABLE_PLUGIN_GL)
+set(OGRE_STATIC_PLUGINS ${OGRE_RenderSystem_Direct3D9_LIBRARIES} ${OGRE_RenderSystem_GL_LIBRARIES})
+ELSE(WIN32)
+ADD_DEFINITIONS(-DENABLE_PLUGIN_GL)
+set(OGRE_STATIC_PLUGINS ${OGRE_RenderSystem_GL_LIBRARIES})
+ENDIF(WIN32)
+ENDIF(OGRE_STATIC)
 add_executable(omwlauncher
+    ${GUI_TYPE}
     ${LAUNCHER}
     ${RCC_SRCS}
     ${MOC_SRCS}
@@ -61,8 +72,8 @@ add_executable(omwlauncher
 target_link_libraries(omwlauncher
     ${Boost_LIBRARIES}
     ${OGRE_LIBRARIES}
+	${OGRE_STATIC_PLUGINS}
     ${QT_LIBRARIES}
-    ${PNG_LIBRARY}
     components
 )
 
diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp
index c954468080..054cbf1414 100644
--- a/apps/launcher/datafilespage.cpp
+++ b/apps/launcher/datafilespage.cpp
@@ -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;
 
diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp
index b7d397f067..95b38d53ea 100644
--- a/apps/launcher/graphicspage.cpp
+++ b/apps/launcher/graphicspage.cpp
@@ -186,7 +186,11 @@ void GraphicsPage::setupOgre()
 
     try
     {
+    #if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9)
+        mOgre = new Ogre::Root("", file.fileName().toStdString(), "./launcherOgre.log");
+    #else
         mOgre = new Ogre::Root(pluginCfg.toStdString(), file.fileName().toStdString(), "./launcherOgre.log");
+    #endif
     }
     catch(Ogre::Exception &ex)
     {
@@ -207,6 +211,15 @@ void GraphicsPage::setupOgre()
         return;
     }
 
+	#ifdef ENABLE_PLUGIN_GL
+	mGLPlugin = new Ogre::GLPlugin();
+	mOgre->installPlugin(mGLPlugin);
+	#endif
+	#ifdef ENABLE_PLUGIN_Direct3D9
+	mD3D9Plugin = new Ogre::D3D9Plugin();
+	mOgre->installPlugin(mD3D9Plugin);
+	#endif
+
     // Get the available renderers and put them in the combobox
     const Ogre::RenderSystemList &renderers = mOgre->getAvailableRenderers();
 
diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp
index bdfd4f038d..6a91a0628e 100644
--- a/apps/launcher/graphicspage.hpp
+++ b/apps/launcher/graphicspage.hpp
@@ -8,6 +8,14 @@
 #include <OgreConfigFile.h>
 #include <OgreConfigDialog.h>
 
+// Static plugin headers
+#ifdef ENABLE_PLUGIN_GL
+# include "OgreGLPlugin.h"
+#endif
+#ifdef ENABLE_PLUGIN_Direct3D9
+# include "OgreD3D9Plugin.h"
+#endif
+
 class QComboBox;
 class QCheckBox;
 class QStackedWidget;
@@ -32,6 +40,12 @@ private:
     Ogre::RenderSystem *mSelectedRenderSystem;
     Ogre::RenderSystem *mOpenGLRenderSystem;
     Ogre::RenderSystem *mDirect3DRenderSystem;
+ 	#ifdef ENABLE_PLUGIN_GL
+ 	Ogre::GLPlugin* mGLPlugin;
+ 	#endif
+	#ifdef ENABLE_PLUGIN_Direct3D9
+ 	Ogre::D3D9Plugin* mD3D9Plugin;
+ 	#endif
 
     QComboBox *mRendererComboBox;
 
diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt
index 39cd99cf6c..6d33298dfe 100644
--- a/apps/openmw/CMakeLists.txt
+++ b/apps/openmw/CMakeLists.txt
@@ -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
@@ -57,6 +57,15 @@ add_openmw_dir (mwmechanics
     )
 
 # Main executable
+IF(OGRE_STATIC)
+IF(WIN32)
+ADD_DEFINITIONS(-DENABLE_PLUGIN_CgProgramManager -DENABLE_PLUGIN_OctreeSceneManager -DENABLE_PLUGIN_ParticleFX -DENABLE_PLUGIN_-DENABLE_PLUGIN_Direct3D9 -DENABLE_PLUGIN_GL)
+set(OGRE_STATIC_PLUGINS ${OGRE_Plugin_CgProgramManager_LIBRARIES} ${OGRE_Plugin_OctreeSceneManager_LIBRARIES} ${OGRE_Plugin_ParticleFX_LIBRARIES} ${OGRE_RenderSystem_Direct3D9_LIBRARIES} ${OGRE_RenderSystem_GL_LIBRARIES})
+ELSE(WIN32)
+ADD_DEFINITIONS(-DENABLE_PLUGIN_CgProgramManager -DENABLE_PLUGIN_OctreeSceneManager -DENABLE_PLUGIN_ParticleFX -DENABLE_PLUGIN_GL)
+set(OGRE_STATIC_PLUGINS ${OGRE_Plugin_CgProgramManager_LIBRARIES} ${Cg_LIBRARIES} ${OGRE_Plugin_OctreeSceneManager_LIBRARIES} ${OGRE_Plugin_ParticleFX_LIBRARIES} ${OGRE_RenderSystem_GL_LIBRARIES})
+ENDIF(WIN32)
+ENDIF(OGRE_STATIC)
 add_executable(openmw
     ${OPENMW_LIBS} ${OPENMW_LIBS_HEADER}
     ${COMPONENT_FILES}
@@ -72,6 +81,7 @@ add_definitions(${SOUND_DEFINE})
 
 target_link_libraries(openmw
     ${OGRE_LIBRARIES}
+	${OGRE_STATIC_PLUGINS}
     ${OIS_LIBRARIES}
     ${Boost_LIBRARIES}
     ${OPENAL_LIBRARY}
diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp
index 0e21893070..89068ce533 100644
--- a/apps/openmw/engine.cpp
+++ b/apps/openmw/engine.cpp
@@ -60,7 +60,7 @@ void OMW::Engine::executeLocalScripts()
 
         MWScript::InterpreterContext interpreterContext (mEnvironment,
             &script.second.getRefData().getLocals(), script.second);
-        mScriptManager->run (script.first, interpreterContext);
+        mEnvironment.mScriptManager->run (script.first, interpreterContext);
 
         if (mEnvironment.mWorld->hasCellChanged())
             break;
@@ -182,7 +182,6 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
   , mCompileAll (false)
   , mReportFocus (false)
   , mFocusTDiff (0)
-  , mScriptManager (0)
   , mScriptContext (0)
   , mFSStrict (false)
   , mCfgMgr(configurationManager)
@@ -199,7 +198,7 @@ OMW::Engine::~Engine()
     delete mEnvironment.mMechanicsManager;
     delete mEnvironment.mDialogueManager;
     delete mEnvironment.mJournal;
-    delete mScriptManager;
+    delete mEnvironment.mScriptManager;
     delete mScriptContext;
     delete mOgre;
 }
@@ -348,18 +347,18 @@ void OMW::Engine::go()
         mEnvironment);
     mScriptContext->setExtensions (&mExtensions);
 
-    mScriptManager = new MWScript::ScriptManager (mEnvironment.mWorld->getStore(), mVerboseScripts,
-        *mScriptContext);
+    mEnvironment.mScriptManager = new MWScript::ScriptManager (mEnvironment.mWorld->getStore(),
+        mVerboseScripts, *mScriptContext);
 
     mEnvironment.mGlobalScripts = new MWScript::GlobalScripts (mEnvironment.mWorld->getStore(),
-        *mScriptManager);
+        *mEnvironment.mScriptManager);
 
     // Create game mechanics system
     mEnvironment.mMechanicsManager = new MWMechanics::MechanicsManager (mEnvironment);
 
     // Create dialog system
     mEnvironment.mJournal = new MWDialogue::Journal (mEnvironment);
-    mEnvironment.mDialogueManager = new MWDialogue::DialogueManager (mEnvironment);
+    mEnvironment.mDialogueManager = new MWDialogue::DialogueManager (mEnvironment,mExtensions);
 
     // load cell
     ESM::Position pos;
@@ -393,7 +392,7 @@ void OMW::Engine::go()
     // scripts
     if (mCompileAll)
     {
-        std::pair<int, int> result = mScriptManager->compileAll();
+        std::pair<int, int> result = mEnvironment.mScriptManager->compileAll();
 
         if (result.first)
             std::cout
@@ -411,6 +410,9 @@ void OMW::Engine::go()
 
 void OMW::Engine::activate()
 {
+    if (mEnvironment.mWindowManager->getMode()!=MWGui::GM_Game)
+        return;
+
     std::string handle = mEnvironment.mWorld->getFacedHandle();
 
     if (handle.empty())
@@ -435,7 +437,7 @@ void OMW::Engine::activate()
     if (!script.empty())
     {
         mEnvironment.mWorld->getLocalScripts().setIgnore (ptr);
-        mScriptManager->run (script, interpreterContext);
+        mEnvironment.mScriptManager->run (script, interpreterContext);
     }
 
     if (!interpreterContext.hasActivationBeenHandled())
diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp
index 5c5cdc018f..690430784b 100644
--- a/apps/openmw/engine.hpp
+++ b/apps/openmw/engine.hpp
@@ -78,10 +78,9 @@ namespace OMW
             std::string mFocusName;
 
             MWWorld::Environment mEnvironment;
-            MWScript::ScriptManager *mScriptManager;
             Compiler::Extensions mExtensions;
             Compiler::Context *mScriptContext;
-            
+
 
             Files::Collections mFileCollections;
             bool mFSStrict;
diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp
index da7ff86964..e95fb572f3 100644
--- a/apps/openmw/mwclass/apparatus.cpp
+++ b/apps/openmw/mwclass/apparatus.cpp
@@ -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");
+    }
 }
diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp
index 86223cf604..c0849e1fe2 100644
--- a/apps/openmw/mwclass/apparatus.hpp
+++ b/apps/openmw/mwclass/apparatus.hpp
@@ -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
     };
 }
 
diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp
index 3cdf631197..e1c2734f0e 100644
--- a/apps/openmw/mwclass/armor.cpp
+++ b/apps/openmw/mwclass/armor.cpp
@@ -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");
+    }
 }
diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp
index 060bc364e8..2b66ff8280 100644
--- a/apps/openmw/mwclass/armor.hpp
+++ b/apps/openmw/mwclass/armor.hpp
@@ -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
     };
 }
 
diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp
index 576e521eec..0a81ebafb7 100644
--- a/apps/openmw/mwclass/book.cpp
+++ b/apps/openmw/mwclass/book.cpp
@@ -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");
+    }
 }
diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp
index 12dc27bb2e..ccbbfb4b2d 100644
--- a/apps/openmw/mwclass/book.hpp
+++ b/apps/openmw/mwclass/book.hpp
@@ -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
     };
 }
 
diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp
index 88c43d82cb..4fe19ada40 100644
--- a/apps/openmw/mwclass/clothing.cpp
+++ b/apps/openmw/mwclass/clothing.cpp
@@ -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");
+    }
 }
diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp
index 606aba9e02..171b062461 100644
--- a/apps/openmw/mwclass/clothing.hpp
+++ b/apps/openmw/mwclass/clothing.hpp
@@ -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
     };
 }
 
diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp
index c586069962..c58a25c03e 100644
--- a/apps/openmw/mwclass/container.cpp
+++ b/apps/openmw/mwclass/container.cpp
@@ -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);
             }
diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp
index bd7af9597a..5654dff698 100644
--- a/apps/openmw/mwclass/door.cpp
+++ b/apps/openmw/mwclass/door.cpp
@@ -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);
         }
     }
diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp
index d00e4592da..1a7edf6325 100644
--- a/apps/openmw/mwclass/ingredient.cpp
+++ b/apps/openmw/mwclass/ingredient.cpp
@@ -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");
+    }
 }
diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp
index 92d2c4eefe..9463dcf8d7 100644
--- a/apps/openmw/mwclass/ingredient.hpp
+++ b/apps/openmw/mwclass/ingredient.hpp
@@ -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
     };
 }
 
diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp
index f9ec1c9569..e2e63a89bb 100644
--- a/apps/openmw/mwclass/light.cpp
+++ b/apps/openmw/mwclass/light.cpp
@@ -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");
+    }
 }
diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp
index c9940d0a59..46a4d60ba4 100644
--- a/apps/openmw/mwclass/light.hpp
+++ b/apps/openmw/mwclass/light.hpp
@@ -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
     };
 }
 
diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp
index 98c05a1b3f..3dda2f4af0 100644
--- a/apps/openmw/mwclass/lockpick.cpp
+++ b/apps/openmw/mwclass/lockpick.cpp
@@ -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");
+    }
 }
diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp
index 9cbfa0d238..0c9189c548 100644
--- a/apps/openmw/mwclass/lockpick.hpp
+++ b/apps/openmw/mwclass/lockpick.hpp
@@ -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
     };
 }
 
diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp
index 8dde84be9b..864fc1e382 100644
--- a/apps/openmw/mwclass/misc.cpp
+++ b/apps/openmw/mwclass/misc.cpp
@@ -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");
+    }
 }
diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp
index de01a64f4b..b07964f990 100644
--- a/apps/openmw/mwclass/misc.hpp
+++ b/apps/openmw/mwclass/misc.hpp
@@ -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
     };
 }
 
diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp
index cc7daa83eb..83a94d27d1 100644
--- a/apps/openmw/mwclass/npc.cpp
+++ b/apps/openmw/mwclass/npc.cpp
@@ -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
diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp
index bef417332d..f210eda5f0 100644
--- a/apps/openmw/mwclass/npc.hpp
+++ b/apps/openmw/mwclass/npc.hpp
@@ -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
diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp
index c57c18fd1f..4ab3745900 100644
--- a/apps/openmw/mwclass/potion.cpp
+++ b/apps/openmw/mwclass/potion.cpp
@@ -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");
+    }
 }
diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp
index fd78bba539..be9e713fba 100644
--- a/apps/openmw/mwclass/potion.hpp
+++ b/apps/openmw/mwclass/potion.hpp
@@ -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
     };
 }
 
diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp
index de024e4303..4b4d79a73e 100644
--- a/apps/openmw/mwclass/probe.cpp
+++ b/apps/openmw/mwclass/probe.cpp
@@ -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");
+    }
 }
diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp
index 3f2bfed5be..1507d65aab 100644
--- a/apps/openmw/mwclass/probe.hpp
+++ b/apps/openmw/mwclass/probe.hpp
@@ -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
     };
 }
 
diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp
index f831b6b503..758bf40797 100644
--- a/apps/openmw/mwclass/repair.cpp
+++ b/apps/openmw/mwclass/repair.cpp
@@ -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");
+    }
 }
diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp
index a5864ab35a..17b606f4cb 100644
--- a/apps/openmw/mwclass/repair.hpp
+++ b/apps/openmw/mwclass/repair.hpp
@@ -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
     };
 }
 
diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp
index 90fd3e33bb..20db0cf38f 100644
--- a/apps/openmw/mwclass/weapon.cpp
+++ b/apps/openmw/mwclass/weapon.cpp
@@ -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");
+    }
 }
diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp
index b056249b93..f863c0bfe2 100644
--- a/apps/openmw/mwclass/weapon.hpp
+++ b/apps/openmw/mwclass/weapon.hpp
@@ -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
     };
 }
 
diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp
index f78160cd78..dd57dfda07 100644
--- a/apps/openmw/mwdialogue/dialoguemanager.cpp
+++ b/apps/openmw/mwdialogue/dialoguemanager.cpp
@@ -9,16 +9,36 @@
 
 #include <components/esm_store/store.hpp>
 
+
 #include "../mwworld/class.hpp"
 #include "../mwworld/environment.hpp"
 #include "../mwworld/world.hpp"
 #include "../mwworld/refdata.hpp"
 #include "../mwworld/player.hpp"
+#include "../mwworld/containerstore.hpp"
 
 #include "../mwinput/inputmanager.hpp"
+#include "../mwgui/dialogue.hpp"
+#include "../mwgui/window_manager.hpp"
+
+#include "journal.hpp"
 
 #include <iostream>
 
+#include "../mwscript/extensions.hpp"
+#include "../mwscript/scriptmanager.hpp"
+
+#include <components/compiler/exception.hpp>
+#include <components/compiler/errorhandler.hpp>
+#include <components/compiler/scanner.hpp>
+#include <components/compiler/locals.hpp>
+#include <components/compiler/output.hpp>
+#include <components/interpreter/interpreter.hpp>
+
+#include "../mwscript/compilercontext.hpp"
+#include "../mwscript/interpretercontext.hpp"
+#include <components/compiler/scriptparser.hpp>
+
 namespace
 {
     std::string toLower (const std::string& name)
@@ -31,17 +51,18 @@ namespace
         return lowerCase;
     }
 
+
     template<typename T1, typename T2>
     bool selectCompare (char comp, T1 value1, T2 value2)
     {
         switch (comp)
         {
-            case '0': return value1==value2;
-            case '1': return value1!=value2;
-            case '2': return value1>value2;
-            case '3': return value1>=value2;
-            case '4': return value1<value2;
-            case '5': return value1<=value2;
+        case '0': return value1==value2;
+        case '1': return value1!=value2;
+        case '2': return value1>value2;
+        case '3': return value1>=value2;
+        case '4': return value1<value2;
+        case '5': return value1<=value2;
         }
 
         throw std::runtime_error ("unknown compare type in dialogue info select");
@@ -87,26 +108,26 @@ namespace
     {
         switch (world.getGlobalVariableType (name))
         {
-            case 's':
+        case 's':
 
-                return selectCompare (comp, value, world.getGlobalVariable (name).mShort);
+            return selectCompare (comp, value, world.getGlobalVariable (name).mShort);
 
-            case 'l':
+        case 'l':
 
-                return selectCompare (comp, value, world.getGlobalVariable (name).mLong);
+            return selectCompare (comp, value, world.getGlobalVariable (name).mLong);
 
-            case 'f':
+        case 'f':
 
-                return selectCompare (comp, value, world.getGlobalVariable (name).mFloat);
+            return selectCompare (comp, value, world.getGlobalVariable (name).mFloat);
 
-            case ' ':
+        case ' ':
 
-                world.getGlobalVariable (name); // trigger exception
-                break;
+            world.getGlobalVariable (name); // trigger exception
+            break;
 
-            default:
+        default:
 
-                throw std::runtime_error ("unsupported gobal variable type");
+            throw std::runtime_error ("unsupported gobal variable type");
         }
 
         return false;
@@ -115,6 +136,128 @@ namespace
 
 namespace MWDialogue
 {
+
+    //helper function
+    std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos)
+    {
+        return toLower(str).find(toLower(substr),pos);
+    }
+
+    bool DialogueManager::functionFilter(const MWWorld::Ptr& actor, const ESM::DialInfo& info,bool choice)
+    {
+        bool isAChoice = false;//is there any choice in the filters?
+        bool isFunction = false;
+        for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator iter (info.selects.begin());
+            iter != info.selects.end(); ++iter)
+        {
+            ESM::DialInfo::SelectStruct select = *iter;
+            char type = select.selectRule[1];
+            if(type == '1')
+            {
+                isFunction = true;
+                char comp = select.selectRule[4];
+                std::string name = select.selectRule.substr (5);
+                std::string function = select.selectRule.substr(2,2);
+
+                int ifunction;
+                std::istringstream iss(function);
+                iss >> ifunction;
+                switch(ifunction)
+                {
+                case 39://PC Expelled
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 40://PC Common Disease
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 41://PC Blight Disease
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 43://PC Crime level
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 46://Same faction
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 48://Detected
+                    if(!selectCompare<int,int>(comp,1,select.i)) return false;
+                    break;
+
+                case 49://Alarmed
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 50://choice
+                    isAChoice = true;
+                    if(choice)
+                    {
+                        if(!selectCompare<int,int>(comp,mChoice,select.i)) return false;
+                    }
+                    break;
+
+                case 60://PC Vampire
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 61://Level
+                    if(!selectCompare<int,int>(comp,1,select.i)) return false;
+                    break;
+
+                case 62://Attacked
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 63://Talked to PC
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 64://PC Health
+                    if(!selectCompare<int,int>(comp,50,select.i)) return false;
+                    break;
+
+                case 65://Creature target
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 66://Friend hit
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 67://Fight
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 68://Hello????
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 69://Alarm
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 70://Flee
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                case 71://Should Attack
+                    if(!selectCompare<int,int>(comp,0,select.i)) return false;
+                    break;
+
+                default:
+                    break;
+
+                }
+            }
+        }
+
+        return true;
+    }
+
     bool DialogueManager::isMatching (const MWWorld::Ptr& actor,
         const ESM::DialInfo::SelectStruct& select) const
     {
@@ -124,58 +267,173 @@ namespace MWDialogue
         {
             char comp = select.selectRule[4];
             std::string name = select.selectRule.substr (5);
-
-            // TODO types 4, 5, 6, 7, 8, 9, A, B, C
+            std::string function = select.selectRule.substr(1,2);
 
             switch (type)
             {
-                case '1': // function
+            case '1': // function
 
-                    return false; // TODO implement functions
+                return true; // TODO implement functions
 
-                case '2': // global
+            case '2': // global
 
-                    if (select.type==ESM::VT_Short || select.type==ESM::VT_Int ||
-                        select.type==ESM::VT_Long)
-                    {
-                        if (!checkGlobal (comp, toLower (name), select.i, *mEnvironment.mWorld))
-                            return false;
-                    }
-                    else if (select.type==ESM::VT_Float)
-                    {
-                        if (!checkGlobal (comp, toLower (name), select.f, *mEnvironment.mWorld))
-                            return false;
-                    }
-                    else
-                        throw std::runtime_error (
-                            "unsupported variable type in dialogue info select");
+                if (select.type==ESM::VT_Short || select.type==ESM::VT_Int ||
+                    select.type==ESM::VT_Long)
+                {
+                    if (!checkGlobal (comp, toLower (name), select.i, *mEnvironment.mWorld))
+                        return false;
+                }
+                else if (select.type==ESM::VT_Float)
+                {
+                    if (!checkGlobal (comp, toLower (name), select.f, *mEnvironment.mWorld))
+                        return false;
+                }
+                else
+                    throw std::runtime_error (
+                    "unsupported variable type in dialogue info select");
 
-                    return true;
+                return true;
 
-                case '3': // local
+            case '3': // local
 
-                    if (select.type==ESM::VT_Short || select.type==ESM::VT_Int ||
-                        select.type==ESM::VT_Long)
-                    {
-                        if (!checkLocal (comp, toLower (name), select.i, actor,
-                            mEnvironment.mWorld->getStore()))
-                            return false;
-                    }
-                    else if (select.type==ESM::VT_Float)
-                    {
-                        if (!checkLocal (comp, toLower (name), select.f, actor,
-                            mEnvironment.mWorld->getStore()))
-                            return false;
-                    }
-                    else
-                        throw std::runtime_error (
-                            "unsupported variable type in dialogue info select");
+                if (select.type==ESM::VT_Short || select.type==ESM::VT_Int ||
+                    select.type==ESM::VT_Long)
+                {
+                    if (!checkLocal (comp, toLower (name), select.i, actor,
+                        mEnvironment.mWorld->getStore()))
+                        return false;
+                }
+                else if (select.type==ESM::VT_Float)
+                {
+                    if (!checkLocal (comp, toLower (name), select.f, actor,
+                        mEnvironment.mWorld->getStore()))
+                        return false;
+                }
+                else
+                    throw std::runtime_error (
+                    "unsupported variable type in dialogue info select");
 
-                    return true;
+                return true;
 
-                default:
+            case '4'://journal
+                if(select.type==ESM::VT_Int)
+                {
+                    if(!selectCompare<int,int>(comp,mEnvironment.mJournal->getJournalIndex(toLower(name)),select.i)) return false;
+                }
+                else
+                    throw std::runtime_error (
+                    "unsupported variable type in dialogue info select");
 
-                    std::cout << "unchecked select: " << type << " " << comp << " " << name << std::endl;
+                return true;
+
+            case '5'://item
+                {
+                MWWorld::Ptr player = mEnvironment.mWorld->getPlayer().getPlayer();
+                MWWorld::ContainerStore& store = MWWorld::Class::get (player).getContainerStore (player);
+
+                int sum = 0;
+
+                for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter)
+                    if (iter->getCellRef().refID==name)
+                        sum += iter->getRefData().getCount();
+                if(!selectCompare<int,int>(comp,sum,select.i)) return false;
+                }
+
+                return true;
+
+
+            case '6'://dead
+                if(!selectCompare<int,int>(comp,0,select.i)) return false;
+
+            case '7':// not ID
+                if(select.type==ESM::VT_String ||select.type==ESM::VT_Int)//bug in morrowind here? it's not a short, it's a string
+                {
+                    int isID = int(toLower(name)==toLower(MWWorld::Class::get (actor).getId (actor)));
+                    if (selectCompare<int,int>(comp,!isID,select.i)) return false;
+                }
+                else
+                    throw std::runtime_error (
+                    "unsupported variable type in dialogue info select");
+
+                return true;
+
+            case '8':// not faction
+                if(select.type==ESM::VT_Int)
+                {
+                    ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData>* npc = actor.get<ESM::NPC>();
+                    int isFaction = int(toLower(npc->base->faction) == toLower(name));
+                    if(selectCompare<int,int>(comp,!isFaction,select.i))
+                        return false;
+                }
+                else
+                    throw std::runtime_error (
+                    "unsupported variable type in dialogue info select");
+
+                return true;
+
+            case '9':// not class
+                if(select.type==ESM::VT_Int)
+                {
+                    ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData>* npc = actor.get<ESM::NPC>();
+                    int isClass = int(toLower(npc->base->cls) == toLower(name));
+                    if(selectCompare<int,int>(comp,!isClass,select.i))
+                        return false;
+                }
+                else
+                    throw std::runtime_error (
+                    "unsupported variable type in dialogue info select");
+
+                return true;
+
+            case 'A'://not Race
+                if(select.type==ESM::VT_Int)
+                {
+                    ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData>* npc = actor.get<ESM::NPC>();
+                    int isRace = int(toLower(npc->base->race) == toLower(name));
+                    if(selectCompare<int,int>(comp,!isRace,select.i))
+                        return false;
+                }
+                else
+                    throw std::runtime_error (
+                    "unsupported variable type in dialogue info select");
+
+                return true;
+
+            case 'B'://not Cell
+                if(select.type==ESM::VT_Int)
+                {
+                    int isCell = int(toLower(actor.getCell()->cell->name) == toLower(name));
+                    if(selectCompare<int,int>(comp,!isCell,select.i))
+                        return false;
+                }
+                else
+                    throw std::runtime_error (
+                    "unsupported variable type in dialogue info select");
+                return true;
+
+            case 'C'://not local
+                if (select.type==ESM::VT_Short || select.type==ESM::VT_Int ||
+                    select.type==ESM::VT_Long)
+                {
+                    if (checkLocal (comp, toLower (name), select.i, actor,
+                        mEnvironment.mWorld->getStore()))
+                        return false;
+                }
+                else if (select.type==ESM::VT_Float)
+                {
+                    if (checkLocal (comp, toLower (name), select.f, actor,
+                        mEnvironment.mWorld->getStore()))
+                        return false;
+                }
+                else
+                    throw std::runtime_error (
+                    "unsupported variable type in dialogue info select");
+                return true;
+
+
+            default:
+
+                std::cout << "unchecked select: " << type << " " << comp << " " << name << std::endl;
             }
         }
 
@@ -189,6 +447,10 @@ namespace MWDialogue
             if (toLower (info.actor)!=MWWorld::Class::get (actor).getId (actor))
                 return false;
 
+        //PC Faction
+        if(!info.pcFaction.empty()) return false;
+
+        //NPC race
         if (!info.race.empty())
         {
             ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *cellRef = actor.get<ESM::NPC>();
@@ -200,6 +462,7 @@ namespace MWDialogue
                 return false;
         }
 
+        //NPC class
         if (!info.clas.empty())
         {
             ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *cellRef = actor.get<ESM::NPC>();
@@ -211,6 +474,7 @@ namespace MWDialogue
                 return false;
         }
 
+        //NPC faction
         if (!info.npcFaction.empty())
         {
             ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *cellRef = actor.get<ESM::NPC>();
@@ -220,10 +484,32 @@ namespace MWDialogue
 
             if (toLower (info.npcFaction)!=toLower (cellRef->base->faction))
                 return false;
+
+            //check NPC rank
+            if(cellRef->base->npdt52.gold != -10)
+            {
+                if(cellRef->base->npdt52.rank < info.data.rank) return false;
+            }
+            else
+            {
+                if(cellRef->base->npdt12.rank < info.data.rank) return false;
+            }
         }
 
         // TODO check player faction
 
+        //check gender
+        ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData>* npc = actor.get<ESM::NPC>();
+        if(npc->base->flags&npc->base->Female)
+        {
+            if(static_cast<int> (info.data.gender)==0)  return false;
+        }
+        else
+        {
+            if(static_cast<int> (info.data.gender)==1)  return false;
+        }
+
+
         // check cell
         if (!info.cell.empty())
             if (mEnvironment.mWorld->getPlayer().getPlayer().getCell()->cell->name != info.cell)
@@ -236,50 +522,281 @@ namespace MWDialogue
             if (!isMatching (actor, *iter))
                 return false;
 
-        std::cout
-            << "unchecked entries:" << std::endl
-            << "    player faction: " << info.pcFaction << std::endl
-            << "    disposition: " << info.data.disposition << std::endl
-            << "    NPC rank: " << static_cast<int> (info.data.rank) << std::endl
-            << "    gender: " << static_cast<int> (info.data.gender) << std::endl
-            << "    PC rank: " << static_cast<int> (info.data.PCrank) << std::endl;
-
         return true;
     }
 
-    DialogueManager::DialogueManager (MWWorld::Environment& environment) : mEnvironment (environment) {}
+    DialogueManager::DialogueManager (MWWorld::Environment& environment,const Compiler::Extensions& extensions) :
+    mEnvironment (environment),mCompilerContext (MWScript::CompilerContext::Type_Dialgoue, environment),
+        mErrorStream(std::cout.rdbuf()),mErrorHandler(mErrorStream)
+    {
+        mChoice = -1;
+        mIsInChoice = false;
+        mCompilerContext.setExtensions (&extensions);
+    }
+
+    void DialogueManager::addTopic(std::string topic)
+    {
+        knownTopics[toLower(topic)] = true;
+    }
+
+    void DialogueManager::parseText(std::string text)
+    {
+        std::list<std::string>::iterator it;
+        for(it = actorKnownTopics.begin();it != actorKnownTopics.end();it++)
+        {
+            size_t pos = find_str_ci(text,*it,0);
+            if(pos !=std::string::npos)
+            {
+                if(pos==0)
+                {
+                    knownTopics[*it] = true;
+                }
+                else if(text.substr(pos -1,1) == " ")
+                {
+                    knownTopics[*it] = true;
+                }
+            }
+        }
+        updateTopics();
+    }
 
     void DialogueManager::startDialogue (const MWWorld::Ptr& actor)
     {
-        std::cout << "talking with " << MWWorld::Class::get (actor).getName (actor) << std::endl;
+        mChoice = -1;
+        mIsInChoice = false;
 
-        const ESM::Dialogue *dialogue = mEnvironment.mWorld->getStore().dialogs.find ("hello");
+        mActor = actor;
 
-        for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
-            iter!=dialogue->mInfo.end(); ++iter)
+        mDialogueMap.clear();
+        actorKnownTopics.clear();
+        ESMS::RecListT<ESM::Dialogue>::MapType dialogueList = mEnvironment.mWorld->getStore().dialogs.list;
+        for(ESMS::RecListT<ESM::Dialogue>::MapType::iterator it = dialogueList.begin(); it!=dialogueList.end();it++)
         {
-            if (isMatching (actor, *iter))
+            mDialogueMap[it->first] = it->second;
+        }
+
+        //initialise the GUI
+        mEnvironment.mInputManager->setGuiMode(MWGui::GM_Dialogue);
+        MWGui::DialogueWindow* win = mEnvironment.mWindowManager->getDialogueWindow();
+        win->startDialogue(MWWorld::Class::get (actor).getName (actor));
+
+        //setup the list of topics known by the actor. Topics who are also on the knownTopics list will be added to the GUI
+        updateTopics();
+
+        //greeting
+        bool greetingFound = false;
+        //ESMS::RecListT<ESM::Dialogue>::MapType dialogueList = mEnvironment.mWorld->getStore().dialogs.list;
+        for(ESMS::RecListT<ESM::Dialogue>::MapType::iterator it = dialogueList.begin(); it!=dialogueList.end();it++)
+        {
+            ESM::Dialogue ndialogue = it->second;
+            if(ndialogue.type == ESM::Dialogue::Greeting)
             {
-                // start dialogue
-                std::cout << "found matching info record" << std::endl;
-
-                std::cout << "response: " << iter->response << std::endl;
-
-                if (!iter->sound.empty())
+                if (greetingFound) break;
+                for (std::vector<ESM::DialInfo>::const_iterator iter (it->second.mInfo.begin());
+                    iter!=it->second.mInfo.end(); ++iter)
                 {
-                    // TODO play sound
-                }
+                    if (isMatching (actor, *iter) && functionFilter(mActor,*iter,true))
+                    {
+                        if (!iter->sound.empty())
+                        {
+                            // TODO play sound
+                        }
 
-                if (!iter->resultScript.empty())
-                {
-                    std::cout << "script: " << iter->resultScript << std::endl;
-                    // TODO execute script
+                        std::string text = iter->response;
+                        parseText(text);
+                        win->addText(iter->response);
+                        executeScript(iter->resultScript);
+                        greetingFound = true;
+                        mLastTopic = it->first;
+                        mLastDialogue = *iter;
+                        break;
+                    }
                 }
-
-                mEnvironment.mInputManager->setGuiMode(MWGui::GM_Dialogue);
-                break;
             }
         }
     }
 
+    bool DialogueManager::compile (const std::string& cmd,std::vector<Interpreter::Type_Code>& code)
+    {
+        try
+        {
+            mErrorHandler.reset();
+
+            std::istringstream input (cmd + "\n");
+
+            Compiler::Scanner scanner (mErrorHandler, input, mCompilerContext.getExtensions());
+
+            Compiler::Locals locals;
+
+            std::string actorScript = MWWorld::Class::get (mActor).getScript (mActor);
+
+            if (!actorScript.empty())
+            {
+                // grab local variables from actor's script, if available.
+                locals = mEnvironment.mScriptManager->getLocals (actorScript);
+            }
+
+            Compiler::ScriptParser parser(mErrorHandler,mCompilerContext, locals, false);
+
+            scanner.scan (parser);
+            if(mErrorHandler.isGood())
+            {
+                parser.getCode(code);
+                return true;
+            }
+            return false;
+        }
+        catch (const Compiler::SourceException& error)
+        {
+            // error has already been reported via error handler
+        }
+        catch (const std::exception& error)
+        {
+            printError (std::string ("An exception has been thrown: ") + error.what());
+        }
+
+        return false;
+    }
+
+    void DialogueManager::executeScript(std::string script)
+    {
+        std::vector<Interpreter::Type_Code> code;
+        if(compile(script,code))
+        {
+            try
+            {
+                MWScript::InterpreterContext interpreterContext(mEnvironment,&mActor.getRefData().getLocals(),mActor);
+                Interpreter::Interpreter interpreter;
+                MWScript::installOpcodes (interpreter);
+                interpreter.run (&code[0], code.size(), interpreterContext);
+            }
+            catch (const std::exception& error)
+            {
+                printError (std::string ("An exception has been thrown: ") + error.what());
+            }
+        }
+    }
+
+    void DialogueManager::updateTopics()
+    {
+        std::list<std::string> keywordList;
+
+        actorKnownTopics.clear();
+        MWGui::DialogueWindow* win = mEnvironment.mWindowManager->getDialogueWindow();
+        ESMS::RecListT<ESM::Dialogue>::MapType dialogueList = mEnvironment.mWorld->getStore().dialogs.list;
+        for(ESMS::RecListT<ESM::Dialogue>::MapType::iterator it = dialogueList.begin(); it!=dialogueList.end();it++)
+        {
+            ESM::Dialogue ndialogue = it->second;
+            if(ndialogue.type == ESM::Dialogue::Topic)
+            {
+                for (std::vector<ESM::DialInfo>::const_iterator iter (it->second.mInfo.begin());
+                    iter!=it->second.mInfo.end(); ++iter)
+                {
+                    if (isMatching (mActor, *iter) && functionFilter(mActor,*iter,false))
+                    {
+                         actorKnownTopics.push_back(it->first);
+                        //does the player know the topic?
+                        if(knownTopics.find(toLower(it->first)) != knownTopics.end())
+                        {
+                            keywordList.push_back(it->first);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        win->setKeywords(keywordList);
+    }
+
+    void DialogueManager::keywordSelected(std::string keyword)
+    {
+        if(!mIsInChoice)
+        {
+            if(mDialogueMap.find(keyword) != mDialogueMap.end())
+            {
+                ESM::Dialogue ndialogue = mDialogueMap[keyword];
+                std::vector<ESM::DialInfo>::const_iterator iter;
+                if(ndialogue.type == ESM::Dialogue::Topic)
+                {
+                    for (iter  = ndialogue.mInfo.begin();
+                        iter!=ndialogue.mInfo.end(); ++iter)
+                    {
+                        if (isMatching (mActor, *iter) && functionFilter(mActor,*iter,true))
+                        {
+                            std::string text = iter->response;
+                            std::string script = iter->resultScript;
+
+                            parseText(text);
+
+                            MWGui::DialogueWindow* win = mEnvironment.mWindowManager->getDialogueWindow();
+                            win->addTitle(keyword);
+                            win->addText(iter->response);
+
+                            executeScript(script);
+
+                            mLastTopic = keyword;
+                            mLastDialogue = *iter;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        updateTopics();
+    }
+
+    void DialogueManager::goodbyeSelected()
+    {
+        mEnvironment.mInputManager->setGuiMode(MWGui::GM_Game);
+    }
+
+    void DialogueManager::questionAnswered(std::string answere)
+    {
+        if(mChoiceMap.find(answere) != mChoiceMap.end())
+        {
+            mChoice = mChoiceMap[answere];
+
+            std::vector<ESM::DialInfo>::const_iterator iter;
+            if(mDialogueMap.find(mLastTopic) != mDialogueMap.end())
+            {
+                ESM::Dialogue ndialogue = mDialogueMap[mLastTopic];
+                if(ndialogue.type == ESM::Dialogue::Topic)
+                {
+                    for (std::vector<ESM::DialInfo>::const_iterator iter = ndialogue.mInfo.begin();
+                        iter!=ndialogue.mInfo.end(); ++iter)
+                    {
+                        if (isMatching (mActor, *iter) && functionFilter(mActor,*iter,true))
+                        {
+                            mChoiceMap.clear();
+                            mChoice = -1;
+                            mIsInChoice = false;
+                            MWGui::DialogueWindow* win = mEnvironment.mWindowManager->getDialogueWindow();
+                            std::string text = iter->response;
+                            parseText(text);
+                            win->addText(text);
+                            executeScript(iter->resultScript);
+                            mLastTopic = mLastTopic;
+                            mLastDialogue = *iter;
+                            break;
+                        }
+                    }
+                }
+            }
+            updateTopics();
+        }
+    }
+
+    void DialogueManager::printError(std::string error)
+    {
+        MWGui::DialogueWindow* win = mEnvironment.mWindowManager->getDialogueWindow();
+        win->addText(error);
+    }
+
+    void DialogueManager::askQuestion(std::string question, int choice)
+    {
+        MWGui::DialogueWindow* win = mEnvironment.mWindowManager->getDialogueWindow();
+        win->askQuestion(question);
+        mChoiceMap[question] = choice;
+        mIsInChoice = true;
+    }
 }
diff --git a/apps/openmw/mwdialogue/dialoguemanager.hpp b/apps/openmw/mwdialogue/dialoguemanager.hpp
index 5b6b262404..260d8e3394 100644
--- a/apps/openmw/mwdialogue/dialoguemanager.hpp
+++ b/apps/openmw/mwdialogue/dialoguemanager.hpp
@@ -3,7 +3,13 @@
 
 #include <components/esm/loadinfo.hpp>
 
+#include <components/compiler/streamerrorhandler.hpp>
+#include "../mwscript/compilercontext.hpp"
+#include "../mwscript/interpretercontext.hpp"
+#include <components/compiler/output.hpp>
+
 #include "../mwworld/ptr.hpp"
+#include <map>
 
 namespace MWWorld
 {
@@ -20,12 +26,48 @@ namespace MWDialogue
 
             bool isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo& info) const;
 
+            bool functionFilter(const MWWorld::Ptr& actor, const ESM::DialInfo& info,bool choice);
+
+            void parseText(std::string text);
+
+            void updateTopics();
+
+            std::map<std::string,ESM::Dialogue> mDialogueMap;
+            std::map<std::string,bool> knownTopics;// Those are the topics the player knows.
+            std::list<std::string> actorKnownTopics;
+
+            MWScript::CompilerContext mCompilerContext;
+            std::ostream mErrorStream;
+            Compiler::StreamErrorHandler mErrorHandler;
+            
+
+            bool compile (const std::string& cmd,std::vector<Interpreter::Type_Code>& code);
+            void executeScript(std::string script);
+            MWWorld::Ptr mActor;
+
+            void printError(std::string error);
+
+            int mChoice;
+            std::map<std::string,int> mChoiceMap;
+            std::string mLastTopic;
+            ESM::DialInfo mLastDialogue;
+            bool mIsInChoice;
+
         public:
 
-            DialogueManager (MWWorld::Environment& environment);
+            DialogueManager (MWWorld::Environment& environment,const Compiler::Extensions& extensions);
 
             void startDialogue (const MWWorld::Ptr& actor);
 
+            void addTopic(std::string topic);
+
+            void askQuestion(std::string question,int choice);
+
+            //calbacks for the GUI
+            void keywordSelected(std::string keyword);
+            void goodbyeSelected();
+            void questionAnswered(std::string answere);
+
     };
 }
 
diff --git a/apps/openmw/mwdialogue/journal.cpp b/apps/openmw/mwdialogue/journal.cpp
index 42cce5cf55..0715214ebd 100644
--- a/apps/openmw/mwdialogue/journal.cpp
+++ b/apps/openmw/mwdialogue/journal.cpp
@@ -3,6 +3,9 @@
 
 #include "../mwworld/environment.hpp"
 
+#include "../mwgui/window_manager.hpp"
+#include "../mwgui/messagebox.hpp"
+
 namespace MWDialogue
 {
     Quest& Journal::getQuest (const std::string& id)
@@ -34,6 +37,10 @@ namespace MWDialogue
         Quest& quest = getQuest (id);
 
         quest.addEntry (entry, *mEnvironment.mWorld); // we are doing slicing on purpose here
+        
+        std::vector<std::string> empty;
+        std::string notification = "Your Journal has been updated.";
+        mEnvironment.mWindowManager->messageBox (notification, empty);
     }
 
     void Journal::setJournalIndex (const std::string& id, int index)
@@ -60,7 +67,12 @@ namespace MWDialogue
 
     int Journal::getJournalIndex (const std::string& id) const
     {
-        return 0;
+        TQuestContainer::const_iterator iter = mQuests.find (id);
+
+        if (iter==mQuests.end())
+            return 0;
+
+        return iter->second.getIndex();
     }
 
     Journal::TEntryIter Journal::begin() const
diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp
index 0e2e692d3f..3fd6e7892e 100644
--- a/apps/openmw/mwgui/console.cpp
+++ b/apps/openmw/mwgui/console.cpp
@@ -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;
-                }  
+                }
             }
         }
 
diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp
index e48c142aaf..cef47d074e 100644
--- a/apps/openmw/mwgui/dialogue.cpp
+++ b/apps/openmw/mwgui/dialogue.cpp
@@ -3,6 +3,8 @@
 #include "window_manager.hpp"
 #include "widgets.hpp"
 #include "components/esm_store/store.hpp"
+#include "../mwworld/environment.hpp"
+#include "../mwdialogue/dialoguemanager.hpp"
 
 #include <assert.h>
 #include <iostream>
@@ -14,32 +16,54 @@
 using namespace MWGui;
 using namespace Widgets;
 
-DialogueWindow::DialogueWindow(WindowManager& parWindowManager)
-  : WindowBase("openmw_dialogue_window_layout.xml", parWindowManager)
+/**
+*Copied from the internet.
+*/
+
+std::string lower_string(const std::string& str)
+{
+        std::string lowerCase;
+
+        std::transform (str.begin(), str.end(), std::back_inserter (lowerCase),
+            (int(*)(int)) std::tolower);
+
+        return lowerCase;
+}
+
+std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos)
+{
+    return lower_string(str).find(lower_string(substr),pos);
+}
+
+
+DialogueWindow::DialogueWindow(WindowManager& parWindowManager,MWWorld::Environment& environment)
+    : WindowBase("openmw_dialogue_window_layout.xml", parWindowManager),
+    mEnvironment(environment)
 {
     // Centre dialog
     center();
 
     //WindowManager *wm = environment.mWindowManager;
     setText("NpcName", "Name of character");
-  
+
     //History view
     getWidget(history, "History");
     history->setOverflowToTheLeft(true);
     history->getClient()->eventMouseButtonClick = MyGUI::newDelegate(this, &DialogueWindow::onHistoryClicked);
-   
-    //Topics list 
+    history->setMaxTextLength(1000000);
+    //Topics list
     getWidget(topicsList, "TopicsList");
     topicsList->setScrollVisible(true);
-    topicsList->eventListSelectAccept      = MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
+    //topicsList->eventListSelectAccept      = MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
     topicsList->eventListMouseItemActivate = MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
-    topicsList->eventListChangePosition    = MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
+    //topicsList->eventListChangePosition    = MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
 
     MyGUI::ButtonPtr byeButton;
     getWidget(byeButton, "ByeButton");
     byeButton->eventMouseButtonClick = MyGUI::newDelegate(this, &DialogueWindow::onByeClicked);
 
-    updateOptions();
+    getWidget(pDispositionBar, "Disposition");
+    getWidget(pDispositionText,"DispositionText");
 }
 
 void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender)
@@ -51,70 +75,126 @@ void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender)
     const IntPoint& lastPressed = InputManager::getInstance().getLastLeftPressed();
 
     size_t cursorPosition = t->getCursorPosition(lastPressed);
-    if(history->getColorAtPos(cursorPosition) != "#FFFFFF")
+    MyGUI::UString color = history->getColorAtPos(cursorPosition);
+    if(color != "#B29154")
     {
         UString key = history->getColorTextAt(cursorPosition);
-        std::cout << "Clicked on key: " << key << std::endl;
-        //eventTopicSelected(key);
+        if(color == "#686EBA") mEnvironment.mDialogueManager->keywordSelected(lower_string(key));
+
+        if(color == "#572D21") mEnvironment.mDialogueManager->questionAnswered(key);
     }
 }
 
 void DialogueWindow::open()
 {
+    topicsList->removeAllItems();
+    pTopicsText.clear();
+    history->eraseText(0,history->getTextLength());
     updateOptions();
     setVisible(true);
 }
 
 void DialogueWindow::onByeClicked(MyGUI::Widget* _sender)
 {
-    eventBye();
+    mEnvironment.mDialogueManager->goodbyeSelected();
 }
 
 void DialogueWindow::onSelectTopic(MyGUI::List* _sender, size_t _index)
 {
     if (_index == MyGUI::ITEM_NONE)
         return;
-
-    //const std::string* theTopic  = topicsList->getItemDataAt<std::string>(_index);
-    //std::cout << "Selected: "<< theTopic << std::endl;
-    //eventTopicSelected(key);
+    std::string topic =  _sender->getItemNameAt(_index);
+    mEnvironment.mDialogueManager->keywordSelected(lower_string(topic));
 }
 
+void DialogueWindow::startDialogue(std::string npcName)
+{
+    setText("NpcName", npcName);
+}
+
+void DialogueWindow::setKeywords(std::list<std::string> keyWords)
+{
+    topicsList->removeAllItems();
+    for(std::list<std::string>::iterator it = keyWords.begin(); it != keyWords.end(); it++)
+    {
+        topicsList->addItem(*it);
+    }
+}
+
+void DialogueWindow::removeKeyword(std::string keyWord)
+{
+    if(topicsList->findItemIndexWith(keyWord) != MyGUI::ITEM_NONE)
+    {
+        topicsList->removeItemAt(topicsList->findItemIndexWith(keyWord));
+        pTopicsText.erase(keyWord);
+    }
+}
+
+void addColorInString(std::string& str, const std::string& keyword,std::string color1, std::string color2)
+{
+    size_t pos = 0;
+    while((pos = find_str_ci(str,keyword, pos)) != std::string::npos)
+    {
+        if(pos==0)
+        {
+            str.insert(pos,color1);
+            pos += color1.length();
+            pos += keyword.length();
+            str.insert(pos,color2);
+            pos+= color2.length();
+        }
+        else
+        {
+            if(str.substr(pos -1,1) == " ")
+            {
+                str.insert(pos,color1);
+                pos += color1.length();
+                pos += keyword.length();
+                str.insert(pos,color2);
+                pos+= color2.length();
+            }
+            else
+            {
+                pos += keyword.length();
+            }
+        }
+    }
+}
+
+std::string DialogueWindow::parseText(std::string text)
+{
+    for(unsigned int i = 0;i<topicsList->getItemCount();i++)
+    {
+        std::string keyWord = topicsList->getItemNameAt(i);
+        addColorInString(text,keyWord,"#686EBA","#B29154");
+    }
+    return text;
+}
+
+void DialogueWindow::addText(std::string text)
+{
+    history->addDialogText("#B29154"+parseText(text)+"#B29154");
+}
+
+void DialogueWindow::addTitle(std::string text)
+{
+    history->addDialogHeading(text);
+}
+
+void DialogueWindow::askQuestion(std::string question)
+{
+    history->addDialogText("#572D21"+question+"#B29154"+" ");
+}
 
 void DialogueWindow::updateOptions()
 {
-    //FIXME Add this properly
-    history->addDialogText("Through the translucent surface of the orb, you see shifting images of distant locations...");
-    for(int z = 0; z < 10; z++)
-    {
-        history->addDialogHeading("Fort Frostmoth");
-        history->addDialogText("The image in the orb flickers, and you see.... The cold courtyard of #FF0000Fort Frostmoth#FFFFFF, battered bu werewolf attack, but still standing, still projecting Imperial might even to this distant and cold corner of the world.");
-    }
-
     //Clear the list of topics
     topicsList->removeAllItems();
-    int i = 0;
-    topicsList->addItem("Ald'ruhn", i++);
-    topicsList->addItem("Balmora", i++);
-    topicsList->addItem("Sadrith Mora", i++);
-    topicsList->addItem("Vivec", i++);
-    topicsList->addItem("Ald Velothi", i++);
-    topicsList->addItem("Caldera", i++);
-    topicsList->addItem("Dagon Fel ", i++);
-    topicsList->addItem("Gnaar Mok", i++);
-    topicsList->addItem("Gnisis", i++);
-    topicsList->addItem("Hla Oad", i++);
-    topicsList->addItem("Khuul", i++);
-    topicsList->addItem("Maar Gan", i++);
-    topicsList->addItem("Molag Mar", i++);
-    topicsList->addItem("Pelagiad", i++);
-    topicsList->addItem("Seyda Neen", i++);
-    topicsList->addItem("Suran", i++);
-    topicsList->addItem("Tel Aruhn", i++);
-    topicsList->addItem("Tel Branora", i++);
-    topicsList->addItem("Tel Fyr", i++);
-    topicsList->addItem("Tel Mora", i++);
-    topicsList->addItem("Tel Vos", i++);
-    topicsList->addItem("Vos", i++);
-}
+    pTopicsText.clear();
+    history->eraseText(0,history->getTextLength());
 
+    pDispositionBar->setProgressRange(100);
+    pDispositionBar->setProgressPosition(40);
+    pDispositionText->eraseText(0,pDispositionText->getTextLength());
+    pDispositionText->addText("#B29154"+std::string("40/100")+"#B29154");
+}
diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp
index ddb6f8a4c8..7dfd7bb7f4 100644
--- a/apps/openmw/mwgui/dialogue.hpp
+++ b/apps/openmw/mwgui/dialogue.hpp
@@ -9,6 +9,11 @@ namespace MWGui
     class WindowManager;
 }
 
+namespace MWWorld
+{
+    class Environment;
+}
+
 /*
   This file contains the dialouge window
   Layout is defined by resources/mygui/openmw_dialogue_window_layout.xml.
@@ -23,7 +28,7 @@ namespace MWGui
     class DialogueWindow: public WindowBase
     {
     public:
-        DialogueWindow(WindowManager& parWindowManager);
+        DialogueWindow(WindowManager& parWindowManager,MWWorld::Environment& environment);
 
         void open();
 
@@ -35,6 +40,14 @@ namespace MWGui
         */
         EventHandle_Void eventBye;
 
+        void startDialogue(std::string npcName);
+        void stopDialogue();
+        void setKeywords(std::list<std::string> keyWord);
+        void removeKeyword(std::string keyWord);
+        void addText(std::string text);
+        void addTitle(std::string text);
+        void askQuestion(std::string question);
+
     protected:
         void onSelectTopic(MyGUI::List* _sender, size_t _index);
         void onByeClicked(MyGUI::Widget* _sender);
@@ -42,9 +55,18 @@ namespace MWGui
 
     private:
         void updateOptions();
+        /**
+        *Helper function that add topic keyword in blue in a text.
+        */
+        std::string parseText(std::string text);
 
         DialogeHistory*     history;
         MyGUI::ListPtr      topicsList;
+        MyGUI::ProgressPtr pDispositionBar;
+        MyGUI::EditPtr pDispositionText;
+        std::map<std::string,std::string> pTopicsText;// this map links keyword and "real" text.
+
+        MWWorld::Environment& mEnvironment;
     };
 }
 #endif
diff --git a/apps/openmw/mwgui/dialogue_history.cpp b/apps/openmw/mwgui/dialogue_history.cpp
index aaa559d243..ceb9045281 100644
--- a/apps/openmw/mwgui/dialogue_history.cpp
+++ b/apps/openmw/mwgui/dialogue_history.cpp
@@ -61,9 +61,9 @@ UString DialogeHistory::getColorTextAt(size_t _pos)
 
 void DialogeHistory::addDialogHeading(const UString& parText)
 {
-    UString head("\n#00FF00");
+    UString head("\n#D8C09A");
     head.append(parText);
-    head.append("#FFFFFF\n");
+    head.append("#B29154\n");
     addText(head);
 }
 
diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp
index 3f3c89f39e..5c9ef1f9b2 100644
--- a/apps/openmw/mwgui/journalwindow.cpp
+++ b/apps/openmw/mwgui/journalwindow.cpp
@@ -4,6 +4,8 @@
 #include "../mwworld/environment.hpp"
 #include "../mwworld/world.hpp"
 
+#include "../mwsound/soundmanager.hpp"
+
 namespace
 {
     struct book
@@ -115,6 +117,8 @@ MWGui::JournalWindow::JournalWindow (WindowManager& parWindowManager)
 void MWGui::JournalWindow::open()
 {
     mPageNumber = 0;
+    std::string journalOpenSound = "book open";
+    mWindowManager.getEnvironment().mSoundManager->playSound (journalOpenSound, 1.0, 1.0);
     if(mWindowManager.getEnvironment().mJournal->begin()!=mWindowManager.getEnvironment().mJournal->end())
     {
         book journal;
@@ -176,6 +180,8 @@ void MWGui::JournalWindow::notifyNextPage(MyGUI::WidgetPtr _sender)
 {
     if(mPageNumber < int(leftPages.size())-1)
     {
+        std::string nextSound = "book page2";
+        mWindowManager.getEnvironment().mSoundManager->playSound (nextSound, 1.0, 1.0);
         mPageNumber = mPageNumber + 1;
         displayLeftText(leftPages[mPageNumber]);
         displayRightText(rightPages[mPageNumber]);
@@ -186,6 +192,8 @@ void MWGui::JournalWindow::notifyPrevPage(MyGUI::WidgetPtr _sender)
 {
     if(mPageNumber > 0)
     {
+        std::string prevSound = "book page";
+        mWindowManager.getEnvironment().mSoundManager->playSound (prevSound, 1.0, 1.0);
         mPageNumber = mPageNumber - 1;
         displayLeftText(leftPages[mPageNumber]);
         displayRightText(rightPages[mPageNumber]);
diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp
index aca9fbd9a8..baf8548143 100644
--- a/apps/openmw/mwgui/window_manager.cpp
+++ b/apps/openmw/mwgui/window_manager.cpp
@@ -51,6 +51,7 @@ WindowManager::WindowManager(MWWorld::Environment& environment,
     console = new Console(w,h, environment, extensions);
     mJournal = new JournalWindow(*this);
     mMessageBoxManager = new MessageBoxManager(this);
+    dialogueWindow = new DialogueWindow(*this,environment);
 
     // The HUD is always on
     hud->setVisible(true);
@@ -149,6 +150,7 @@ void WindowManager::updateVisible()
     stats->setVisible(false);
     console->disable();
     mJournal->setVisible(false);
+    dialogueWindow->setVisible(false);
 
     // Mouse is visible whenever we're not in game mode
     gui->setVisiblePointer(isGuiMode());
@@ -195,11 +197,6 @@ void WindowManager::updateVisible()
 
     if (mode == GM_Dialogue)
     {
-        if (!dialogueWindow)
-        {
-            dialogueWindow = new DialogueWindow(*this);
-            dialogueWindow->eventBye = MyGUI::newDelegate(this, &WindowManager::onDialogueWindowBye);
-        }
         dialogueWindow->open();
         return;
     }
@@ -349,6 +346,7 @@ void WindowManager::updateSkillArea()
 
 void WindowManager::removeDialog(OEngine::GUI::Layout*dialog)
 {
+    std::cout << "dialogue a la poubelle";
     assert(dialog);
     if (!dialog)
         return;
@@ -387,7 +385,8 @@ void WindowManager::onDialogueWindowBye()
     if (dialogueWindow)
     {
         //FIXME set some state and stuff?
-        removeDialog(dialogueWindow);
+        //removeDialog(dialogueWindow);
+        dialogueWindow->setVisible(false);
     }
     setGuiMode(GM_Game);
 }
diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp
index 89ff4b9bb9..0124c92392 100644
--- a/apps/openmw/mwgui/window_manager.hpp
+++ b/apps/openmw/mwgui/window_manager.hpp
@@ -124,6 +124,8 @@ namespace MWGui
       updateVisible();
     }
 
+    MWGui::DialogueWindow* getDialogueWindow() {return dialogueWindow;}
+
     MyGUI::Gui* getGui() const { return gui; }
 
     void wmUpdateFps(float fps, size_t triangleCount, size_t batchCount)
diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp
index 9df987dc18..7b0d7015c0 100644
--- a/apps/openmw/mwrender/animation.cpp
+++ b/apps/openmw/mwrender/animation.cpp
@@ -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,7 +274,7 @@ namespace MWRender{
 						rotmult = bonePtr->getOrientation();
 						scale = bonePtr->getScale().x;
 						boneSequenceIter++;
-						
+
 					    for(; boneSequenceIter != boneSequence.end(); boneSequenceIter++)
 					    {
 							if(creaturemodel->getSkeleton()->hasBone(*boneSequenceIter)){
@@ -390,7 +388,7 @@ namespace MWRender{
 
  void Animation::handleAnimationTransforms(){
 
-	 
+
     Ogre::SkeletonInstance* skel = base->getSkeleton();
 
 
@@ -430,11 +428,11 @@ namespace MWRender{
 	    const std::vector<Ogre::Quaternion> & quats = iter->getQuat();
 
         const std::vector<float> & ttime = iter->gettTime();
-        
+
 
         const std::vector<float> & rtime = iter->getrTime();
         int rindexJ = rindexI[slot];
-		 
+
 	    timeIndex(time, rtime, rindexI[slot], rindexJ, x2);
 	    int tindexJ = tindexI[slot];
 
@@ -447,10 +445,10 @@ namespace MWRender{
         Ogre::Quaternion r;
 
         bool bTrans = translist1.size() > 0;
-	   
+
 
         bool bQuats = quats.size() > 0;
-	   
+
     if(skel->hasBone(iter->getBonename())){
         Ogre::Bone* bone = skel->getBone(iter->getBonename());
         if(bTrans){
@@ -467,10 +465,10 @@ namespace MWRender{
 
 
 
-        
+
 
 	}
-	
+
 
     slot++;
     }
diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp
index e08b86e8de..d1e8071f06 100644
--- a/apps/openmw/mwrender/animation.hpp
+++ b/apps/openmw/mwrender/animation.hpp
@@ -66,7 +66,7 @@ class Animation{
      void stopScript();
     
     
-     ~Animation();
+     virtual ~Animation();
  
 };
 }
diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp
index 179991442c..2229eeec98 100644
--- a/apps/openmw/mwrender/creatureanimation.hpp
+++ b/apps/openmw/mwrender/creatureanimation.hpp
@@ -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);
 	
diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp
index e8ce735f79..e2071957c0 100644
--- a/apps/openmw/mwrender/npcanimation.hpp
+++ b/apps/openmw/mwrender/npcanimation.hpp
@@ -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);
diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp
index 7b58a80d76..4f84d90a95 100644
--- a/apps/openmw/mwrender/renderingmanager.cpp
+++ b/apps/openmw/mwrender/renderingmanager.cpp
@@ -51,7 +51,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
     cameraPitchNode->attachObject(mRendering.getCamera());
     
     //mSkyManager = 0;
-    mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera());
+    mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment);
 
     mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
     mSun = 0;
@@ -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 );
diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp
index 1b324459a5..a747b9be04 100644
--- a/apps/openmw/mwrender/sky.cpp
+++ b/apps/openmw/mwrender/sky.cpp
@@ -10,16 +10,12 @@
 
 #include <components/nifogre/ogre_nif_loader.hpp>
 
+#include "../mwworld/environment.hpp"
+#include "../mwworld/world.hpp"
+
 using namespace MWRender;
 using namespace Ogre;
 
-// the speed at which the clouds are animated
-#define CLOUD_SPEED 0.001
-
-// this distance has to be set accordingly so that the
-// celestial bodies are behind the clouds, but in front of the atmosphere
-#define CELESTIAL_BODY_DISTANCE 1000.f
-
 BillboardObject::BillboardObject( const String& textureName,
                     const float initialSize,
                     const Vector3& position,
@@ -50,7 +46,7 @@ void BillboardObject::setVisibility(const float visibility)
 void BillboardObject::setPosition(const Vector3& pPosition)
 {
     Vector3 normalised = pPosition.normalisedCopy();
-    Vector3 finalPosition = normalised * CELESTIAL_BODY_DISTANCE;
+    Vector3 finalPosition = normalised * 1000.f;
 
     mBBSet->setCommonDirection( -normalised );
 
@@ -59,7 +55,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)
@@ -84,7 +81,7 @@ void BillboardObject::init(const String& textureName,
 {
     SceneManager* sceneMgr = rootNode->getCreator();
 
-    Vector3 finalPosition = position.normalisedCopy() * CELESTIAL_BODY_DISTANCE;
+    Vector3 finalPosition = position.normalisedCopy() * 1000.f;
 
     static unsigned int bodyCount=0;
 
@@ -161,14 +158,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 +189,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 +210,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;
@@ -285,9 +292,10 @@ void SkyManager::ModVertexAlpha(Entity* ent, unsigned int meshType)
     ent->getMesh()->getSubMesh(0)->vertexData->vertexBufferBinding->getBuffer(ves_diffuse->getSource())->unlock();
 }
 
-SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) :
+SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environment* env) :
     mGlareFade(0), mGlareEnabled(false)
 {
+    mEnvironment = env;
     mViewport = pCamera->getViewport();
     mSceneMgr = pMwRoot->getCreator();
     mRootNode = pCamera->getParentSceneNode()->createChildSceneNode();
@@ -301,7 +309,7 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) :
     Pass* pass = material->getTechnique(0)->getPass(0);
     pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
     mThunderTextureUnit = pass->createTextureUnitState();
-    mThunderTextureUnit->setColourOperationEx(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, ColourValue(1.f, 1.f, 1.f)); // always black colour
+    mThunderTextureUnit->setColourOperationEx(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, ColourValue(1.f, 1.f, 1.f));
     mThunderTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, 0.5f);
     OverlayManager& ovm = OverlayManager::getSingleton();
     mThunderOverlay = ovm.create( "ThunderOverlay" );
@@ -338,24 +346,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 +366,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 +393,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");
@@ -494,7 +501,7 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) :
     "   uniform float4 emissive \n"
     ")	\n"
     "{	\n"
-    "   uv += float2(1,1) * time * speed * "<<CLOUD_SPEED<<"; \n" // Scroll in x,y direction
+    "   uv += float2(1,0) * time * speed * 0.003; \n" // Scroll in x direction
     "   float4 tex = lerp(tex2D(texture, uv), tex2D(secondTexture, uv), transitionFactor); \n"
     "   oColor = color * float4(emissive.xyz,1) * tex * float4(1,1,1,opacity); \n"
     "}";
@@ -548,7 +555,7 @@ void SkyManager::update(float duration)
     if (!mEnabled) return;
 
     // UV Scroll the clouds
-    mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstantFromTime("time", 1);
+    mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstantFromTime("time", mEnvironment->mWorld->getTimeScaleFactor()/30.f);
 
     /// \todo improve this
     mMasser->setPhase( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
@@ -583,6 +590,9 @@ void SkyManager::update(float duration)
     mSun->setVisible(mSunEnabled);
     mMasser->setVisible(mMasserEnabled);
     mSecunda->setVisible(mSecundaEnabled);
+
+    // rotate the stars by 360 degrees every 4 days
+    mAtmosphereNight->roll(Degree(mEnvironment->mWorld->getTimeScaleFactor()*duration*360 / (3600*96.f)));
 }
 
 void SkyManager::enable()
@@ -647,6 +657,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;
     }
 
@@ -677,6 +689,7 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
         strength = 1.f;
 
     mSunGlare->setVisibility(weather.mGlareView * strength);
+    mSun->setVisibility(strength);
 
     mAtmosphereNight->setVisible(weather.mNight && mEnabled);
 }
diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp
index 2678165e39..bf52afd8dd 100644
--- a/apps/openmw/mwrender/sky.hpp
+++ b/apps/openmw/mwrender/sky.hpp
@@ -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;
@@ -102,7 +107,7 @@ namespace MWRender
     class SkyManager
     {
     public:
-        SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera);
+        SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera, MWWorld::Environment* env);
         ~SkyManager();
         
         void update(float duration);
@@ -159,10 +164,11 @@ namespace MWRender
         Ogre::Vector3 getRealSunPos();
         
     private:
+        MWWorld::Environment* mEnvironment;
         float mHour;
         int mDay;
         int mMonth;
-    
+
         BillboardObject* mSun;
         BillboardObject* mSunGlare;
         Moon* mMasser;
diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp
index c2ff9ed8bd..0cca028e29 100644
--- a/apps/openmw/mwscript/dialogueextensions.cpp
+++ b/apps/openmw/mwscript/dialogueextensions.cpp
@@ -8,6 +8,7 @@
 #include <components/interpreter/opcodes.hpp>
 
 #include "../mwdialogue/journal.hpp"
+#include "../mwdialogue/dialoguemanager.hpp"
 
 #include "interpretercontext.hpp"
 
@@ -72,15 +73,62 @@ namespace MWScript
                 }
         };
 
+        class OpAddTopic : public Interpreter::Opcode0
+        {
+            public:
+
+                virtual void execute (Interpreter::Runtime& runtime)
+                {
+                    MWScript::InterpreterContext& context
+                        = static_cast<MWScript::InterpreterContext&> (runtime.getContext());
+
+                    std::string topic = runtime.getStringLiteral (runtime[0].mInteger);
+                    runtime.pop();
+
+                    context.getEnvironment().mDialogueManager->addTopic(topic);
+                }
+        };
+
+        class OpChoice : public Interpreter::Opcode1
+        {
+            public:
+
+                virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
+                {
+                    MWScript::InterpreterContext& context
+                        = static_cast<MWScript::InterpreterContext&> (runtime.getContext());
+                    MWDialogue::DialogueManager* dialogue = context.getEnvironment().mDialogueManager;
+                    while(arg0>0)
+                    {
+                        std::string question = runtime.getStringLiteral (runtime[0].mInteger);
+                        runtime.pop();
+                        arg0 = arg0 -1;
+                        Interpreter::Type_Integer choice = 1;
+                        if(arg0>0)
+                        {
+                            choice = runtime[0].mInteger;
+                            runtime.pop();
+                            arg0 = arg0 -1;
+                        }
+                        dialogue->askQuestion(question,choice);
+                    }
+                }
+        };
+
+
         const int opcodeJournal = 0x2000133;
         const int opcodeSetJournalIndex = 0x2000134;
         const int opcodeGetJournalIndex = 0x2000135;
+        const int opcodeAddTopic = 0x200013a;
+        const int opcodeChoice = 0x2000a;
 
         void registerExtensions (Compiler::Extensions& extensions)
         {
             extensions.registerInstruction ("journal", "cl", opcodeJournal);
             extensions.registerInstruction ("setjournalindex", "cl", opcodeSetJournalIndex);
             extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex);
+            extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic);
+            extensions.registerInstruction ("choice", "/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice);
         }
 
         void installOpcodes (Interpreter::Interpreter& interpreter)
@@ -88,6 +136,8 @@ namespace MWScript
             interpreter.installSegment5 (opcodeJournal, new OpJournal);
             interpreter.installSegment5 (opcodeSetJournalIndex, new OpSetJournalIndex);
             interpreter.installSegment5 (opcodeGetJournalIndex, new OpGetJournalIndex);
+            interpreter.installSegment5 (opcodeAddTopic, new OpAddTopic);
+            interpreter.installSegment3 (opcodeChoice,new OpChoice);
         }
     }
 
diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt
index 09b0c04825..eab5bf846f 100644
--- a/apps/openmw/mwscript/docs/vmformat.txt
+++ b/apps/openmw/mwscript/docs/vmformat.txt
@@ -23,7 +23,8 @@ op 0x20006: PlayAnim
 op 0x20007: PlayAnim, explicit reference
 op 0x20008: LoopAnim
 op 0x20009: LoopAnim, explicit reference
-opcodes 0x2000a-0x3ffff unused
+op 0x2000a: Choice
+opcodes 0x2000b-0x3ffff unused
 
 Segment 4:
 (not implemented yet)
@@ -115,6 +116,7 @@ op 0x2000136: GetPCCell
 op 0x2000137: GetButtonPressed
 op 0x2000138: SkipAnim
 op 0x2000139: SkipAnim, expplicit reference
+op 0x200013a: AddTopic
 op 0x200013b: twf
 op 0x200013c: FadeIn
 op 0x200013d: FadeOut
diff --git a/apps/openmw/mwscript/scriptmanager.cpp b/apps/openmw/mwscript/scriptmanager.cpp
index 5fcfcc6050..e93f2deeca 100644
--- a/apps/openmw/mwscript/scriptmanager.cpp
+++ b/apps/openmw/mwscript/scriptmanager.cpp
@@ -63,7 +63,7 @@ namespace MWScript
             {
                 std::vector<Interpreter::Type_Code> code;
                 mParser.getCode (code);
-                mScripts.insert (std::make_pair (name, code));
+                mScripts.insert (std::make_pair (name, std::make_pair (code, mParser.getLocals())));
 
                 // TODO sanity check on generated locals
 
@@ -77,8 +77,7 @@ namespace MWScript
     void ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext)
     {
         // compile script
-        std::map<std::string, std::vector<Interpreter::Type_Code> >::iterator iter =
-            mScripts.find (name);
+        ScriptCollection::iterator iter = mScripts.find (name);
 
         if (iter==mScripts.end())
         {
@@ -86,7 +85,7 @@ namespace MWScript
             {
                 // failed -> ignore script from now on.
                 std::vector<Interpreter::Type_Code> empty;
-                mScripts.insert (std::make_pair (name, empty));
+                mScripts.insert (std::make_pair (name, std::make_pair (empty, Compiler::Locals())));
                 return;
             }
 
@@ -95,7 +94,7 @@ namespace MWScript
         }
 
         // execute script
-        if (!iter->second.empty())
+        if (!iter->second.first.empty())
             try
             {
                 if (!mOpcodesInstalled)
@@ -104,7 +103,7 @@ namespace MWScript
                     mOpcodesInstalled = true;
                 }
 
-                mInterpreter.run (&iter->second[0], iter->second.size(), interpreterContext);
+                mInterpreter.run (&iter->second.first[0], iter->second.first.size(), interpreterContext);
             }
             catch (const std::exception& e)
             {
@@ -113,7 +112,7 @@ namespace MWScript
                 if (mVerbose)
                     std::cerr << "(" << e.what() << ")" << std::endl;
 
-                iter->second.clear(); // don't execute again.
+                iter->second.first.clear(); // don't execute again.
             }
     }
 
@@ -132,4 +131,24 @@ namespace MWScript
 
         return std::make_pair (count, success);
     }
+
+    Compiler::Locals& ScriptManager::getLocals (const std::string& name)
+    {
+        ScriptCollection::iterator iter = mScripts.find (name);
+
+        if (iter==mScripts.end())
+        {
+            if (!compile (name))
+            {
+                // failed -> ignore script from now on.
+                std::vector<Interpreter::Type_Code> empty;
+                mScripts.insert (std::make_pair (name, std::make_pair (empty, Compiler::Locals())));
+                throw std::runtime_error ("failed to compile script " + name);
+            }
+
+            iter = mScripts.find (name);
+        }
+
+        return iter->second.second;
+    }
 }
diff --git a/apps/openmw/mwscript/scriptmanager.hpp b/apps/openmw/mwscript/scriptmanager.hpp
index 74511f456e..35cbc0d1e8 100644
--- a/apps/openmw/mwscript/scriptmanager.hpp
+++ b/apps/openmw/mwscript/scriptmanager.hpp
@@ -39,7 +39,11 @@ namespace MWScript
             Interpreter::Interpreter mInterpreter;
             bool mOpcodesInstalled;
 
-            std::map<std::string, std::vector<Interpreter::Type_Code> > mScripts;
+            typedef std::pair<std::vector<Interpreter::Type_Code>, Compiler::Locals> CompiledScript;
+            typedef std::map<std::string, CompiledScript> ScriptCollection;
+
+
+            ScriptCollection mScripts;
 
         public:
 
@@ -56,6 +60,9 @@ namespace MWScript
             std::pair<int, int> compileAll();
             ///< Compile all scripts
             /// \return count, success
+
+            Compiler::Locals& getLocals (const std::string& name);
+            ///< Return locals for script \a name.
     };
 };
 
diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp
index 2440eda238..226796603e 100644
--- a/apps/openmw/mwsound/soundmanager.cpp
+++ b/apps/openmw/mwsound/soundmanager.cpp
@@ -67,6 +67,7 @@ namespace MWSound
         , updater(mgr)
         , cameraTracker(mgr)
         , mCurrentPlaylist(NULL)
+        , mUsingSound(useSound)
     {
         if(useSound)
         {
@@ -105,8 +106,11 @@ namespace MWSound
 
     SoundManager::~SoundManager()
     {
-        Ogre::Root::getSingleton().removeFrameListener(&updater);
-        cameraTracker.unfollowCamera();
+        if(mUsingSound)
+        {
+            Ogre::Root::getSingleton().removeFrameListener(&updater);
+            cameraTracker.unfollowCamera();
+        }
     }
 
     // Convert a soundId to file name, and modify the volume
@@ -136,7 +140,7 @@ namespace MWSound
         max = std::max(min, max);
       }
 
-      return Files::FileListLocator(mSoundFiles, snd->sound, mFSStrict);
+      return Files::FileListLocator(mSoundFiles, snd->sound, mFSStrict, false);
     }
 
     // Add a sound to the list and play it
@@ -145,7 +149,7 @@ namespace MWSound
              const std::string &id,
              float volume, float pitch,
              float min, float max,
-             bool loop)
+             bool loop, bool untracked)
     {
       try
         {
@@ -157,7 +161,10 @@ namespace MWSound
           setPos(snd, ptr);
           snd->play();
 
-          sounds[ptr][id] = WSoundPtr(snd);
+          if (!untracked)
+          {
+            sounds[ptr][id] = WSoundPtr(snd);
+          }
         }
       catch(...)
         {
@@ -290,7 +297,7 @@ namespace MWSound
 
     void SoundManager::streamMusic(const std::string& filename)
     {
-        std::string filePath = mMusicLibrary.locate(filename, mFSStrict).string();
+        std::string filePath = mMusicLibrary.locate(filename, mFSStrict, true).string();
         if(!filePath.empty())
         {
             streamMusicFull(filePath);
@@ -351,6 +358,9 @@ namespace MWSound
 
     void SoundManager::playPlaylist(std::string playlist)
     {
+        if (!mUsingSound)
+            return;
+
         if (playlist == "")
         {
             if(!isMusicPlaying())
@@ -372,8 +382,11 @@ namespace MWSound
 
   void SoundManager::say (MWWorld::Ptr ptr, const std::string& filename)
   {
+    if (!mUsingSound)
+      return;
+
     // The range values are not tested
-    std::string filePath = Files::FileListLocator(mSoundFiles, filename, mFSStrict);
+    std::string filePath = Files::FileListLocator(mSoundFiles, filename, mFSStrict, true);
     if(!filePath.empty())
       add(filePath, ptr, "_say_sound", 1, 1, 100, 20000, false);
     else
@@ -397,6 +410,7 @@ namespace MWSound
         snd->setVolume(volume);
         snd->setRange(min,max);
         snd->setPitch(pitch);
+        snd->setRelative(true);
         snd->play();
 
         if (loop)
@@ -412,13 +426,13 @@ namespace MWSound
   }
 
   void SoundManager::playSound3D (MWWorld::Ptr ptr, const std::string& soundId,
-                                  float volume, float pitch, bool loop)
+                                  float volume, float pitch, bool loop, bool untracked)
   {
     // Look up the sound in the ESM data
     float min, max;
     const std::string &file = lookup(soundId, volume, min, max);
     if (file != "")
-      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)
diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp
index 03c19ce774..dcf64b90ca 100644
--- a/apps/openmw/mwsound/soundmanager.hpp
+++ b/apps/openmw/mwsound/soundmanager.hpp
@@ -82,12 +82,14 @@ namespace MWSound
 
             IDMap mLoopedSounds;
 
+            bool mUsingSound;
+
             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 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;
@@ -136,7 +138,7 @@ namespace MWSound
             ///< 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 = "");
diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp
index 079c888aa6..8c657a5c83 100644
--- a/apps/openmw/mwworld/cells.cpp
+++ b/apps/openmw/mwworld/cells.cpp
@@ -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;
 }
 
diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp
index 0ecbd02d37..42aa1050c0 100644
--- a/apps/openmw/mwworld/cells.hpp
+++ b/apps/openmw/mwworld/cells.hpp
@@ -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);
diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp
index 641da73e1e..9d766909f7 100644
--- a/apps/openmw/mwworld/class.cpp
+++ b/apps/openmw/mwworld/class.cpp
@@ -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");
+    }
 }
diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp
index 9b6acb3ce3..67320b3e07 100644
--- a/apps/openmw/mwworld/class.hpp
+++ b/apps/openmw/mwworld/class.hpp
@@ -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)
     };
 }
 
diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp
index 54908deec1..c498cabced 100644
--- a/apps/openmw/mwworld/containerstore.cpp
+++ b/apps/openmw/mwworld/containerstore.cpp
@@ -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);
diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp
index 7263245f36..69e5ba4466 100644
--- a/apps/openmw/mwworld/containerstore.hpp
+++ b/apps/openmw/mwworld/containerstore.hpp
@@ -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;
     };
 
diff --git a/apps/openmw/mwworld/environment.hpp b/apps/openmw/mwworld/environment.hpp
index a403ee1657..3a83f886fd 100644
--- a/apps/openmw/mwworld/environment.hpp
+++ b/apps/openmw/mwworld/environment.hpp
@@ -9,6 +9,7 @@ namespace MWSound
 namespace MWScript
 {
     class GlobalScripts;
+    class ScriptManager;
 }
 
 namespace MWGui
@@ -41,7 +42,7 @@ namespace MWWorld
     {
     public:
         Environment()
-        : mWorld (0), mSoundManager (0), mGlobalScripts (0), mWindowManager (0),
+        : mWorld (0), mSoundManager (0), mGlobalScripts (0), mScriptManager (0), mWindowManager (0),
           mMechanicsManager (0), mDialogueManager (0), mJournal (0), mFrameDuration (0),
           mInputManager (0)
         {}
@@ -49,6 +50,7 @@ namespace MWWorld
         World *mWorld;
         MWSound::SoundManager *mSoundManager;
         MWScript::GlobalScripts *mGlobalScripts;
+        MWScript::ScriptManager *mScriptManager;
         MWGui::WindowManager *mWindowManager;
         MWMechanics::MechanicsManager *mMechanicsManager;
         MWDialogue::DialogueManager *mDialogueManager;
diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp
new file mode 100644
index 0000000000..264be7bb33
--- /dev/null
+++ b/apps/openmw/mwworld/inventorystore.cpp
@@ -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];
+}
diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp
new file mode 100644
index 0000000000..c41f9e8e37
--- /dev/null
+++ b/apps/openmw/mwworld/inventorystore.hpp
@@ -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
diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp
index 90afc4e78b..2bd3825aa5 100644
--- a/apps/openmw/mwworld/weather.cpp
+++ b/apps/openmw/mwworld/weather.cpp
@@ -26,6 +26,7 @@ const float WeatherGlobals::mSunsetTime = 18;
 const float WeatherGlobals::mSunriseDuration = 2;
 const float WeatherGlobals::mSunsetDuration = 2;
 const float WeatherGlobals::mWeatherUpdateTime = 20.f;
+
 const float WeatherGlobals::mThunderFrequency = .4;
 const float WeatherGlobals::mThunderThreshold = 0.6;
 const float WeatherGlobals::mThunderSoundDelay = 0.25;
@@ -328,6 +329,9 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering, Environmen
 
 void WeatherManager::setWeather(const String& weather, bool instant)
 {
+    if (weather == mCurrentWeather && mNextWeather == "") 
+        return;
+
     if (instant || mFirstUpdate)
     {
         mNextWeather = "";
@@ -339,12 +343,12 @@ void WeatherManager::setWeather(const String& weather, bool instant)
         if (mNextWeather != "")
         {
             // transition more than 50% finished?
-            if (mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*60) <= 0.5)
+            if (mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600) <= 0.5)
                 mCurrentWeather = mNextWeather;
         }
-            
+
         mNextWeather = weather;
-        mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*60;
+        mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600;
     }
 }
 
@@ -352,7 +356,7 @@ WeatherResult WeatherManager::getResult(const String& weather)
 {
     const Weather& current = mWeatherSettings[weather];
     WeatherResult result;
-    
+
     result.mCloudTexture = current.mCloudTexture;
     result.mCloudBlendFactor = 0;
     result.mCloudOpacity = current.mCloudsMaximumPercent;
@@ -361,16 +365,13 @@ WeatherResult WeatherManager::getResult(const String& weather)
     result.mGlareView = current.mGlareView;
     result.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
     result.mSunColor = current.mSunDiscSunsetColor;
-    
-    const float fade_duration = current.mTransitionDelta * 24.f;
-    
-    result.mNight = (mHour < 6.f+fade_duration || mHour > 20.f-fade_duration);
-    
+
+    result.mNight = (mHour < 6 || mHour > 19);
+
     result.mFogDepth = result.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth;
-    
+
     // night
-    if (mHour <= (WeatherGlobals::mSunriseTime-WeatherGlobals::mSunriseDuration)
-        || mHour >= (WeatherGlobals::mSunsetTime+WeatherGlobals::mSunsetDuration))
+    if (mHour <= 5.5f || mHour >= 21)
     {
         result.mFogColor = current.mFogNightColor;
         result.mAmbientColor = current.mAmbientNightColor;
@@ -378,82 +379,68 @@ WeatherResult WeatherManager::getResult(const String& weather)
         result.mSkyColor = current.mSkyNightColor;
         result.mNightFade = 1.f;
     }
-    
+
     // sunrise
-    else if (mHour >= (WeatherGlobals::mSunriseTime-WeatherGlobals::mSunriseDuration) && mHour <= WeatherGlobals::mSunriseTime)
+    else if (mHour >= 5.5f && mHour <= 9)
     {
-        if (mHour <= (WeatherGlobals::mSunriseTime-WeatherGlobals::mSunriseDuration+fade_duration))
+        if (mHour <= 6)
         {
             // fade in
-            float advance = (WeatherGlobals::mSunriseTime-WeatherGlobals::mSunriseDuration+fade_duration)-mHour;
-            float factor = (advance / fade_duration);
+            float advance = 6-mHour;
+            float factor = advance / 0.5f;
             result.mFogColor = lerp(current.mFogSunriseColor, current.mFogNightColor);
             result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientNightColor);
             result.mSunColor = lerp(current.mSunSunriseColor, current.mSunNightColor);
             result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyNightColor);
             result.mNightFade = factor;
         }
-        else if (mHour >= (WeatherGlobals::mSunriseTime-fade_duration))
+        else //if (mHour >= 6)
         {
             // fade out
-            float advance = mHour-(WeatherGlobals::mSunriseTime-fade_duration);
-            float factor = advance / fade_duration;
+            float advance = mHour-6;
+            float factor = advance / 3.f;
             result.mFogColor = lerp(current.mFogSunriseColor, current.mFogDayColor);
             result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientDayColor);
             result.mSunColor = lerp(current.mSunSunriseColor, current.mSunDayColor);
             result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyDayColor);
         }
-        else 
-        {
-            result.mFogColor = current.mFogSunriseColor;
-            result.mAmbientColor = current.mAmbientSunriseColor;
-            result.mSunColor = current.mSunSunriseColor;
-            result.mSkyColor = current.mSkySunriseColor;
-        }
     }
-    
+
     // day
-    else if (mHour >= (WeatherGlobals::mSunriseTime) && mHour <= (WeatherGlobals::mSunsetTime))
+    else if (mHour >= 9 && mHour <= 17)
     {
         result.mFogColor = current.mFogDayColor;
         result.mAmbientColor = current.mAmbientDayColor;
         result.mSunColor = current.mSunDayColor;
         result.mSkyColor = current.mSkyDayColor;
     }
-    
+
     // sunset
-    else if (mHour >= (WeatherGlobals::mSunsetTime) && mHour <= (WeatherGlobals::mSunsetTime+WeatherGlobals::mSunsetDuration))
+    else if (mHour >= 17 && mHour <= 21)
     {
-        if (mHour <= (WeatherGlobals::mSunsetTime+fade_duration))
+        if (mHour <= 19)
         {
             // fade in
-            float advance = (WeatherGlobals::mSunsetTime+fade_duration)-mHour;
-            float factor = (advance / fade_duration);
+            float advance = 19-mHour;
+            float factor = (advance / 2);
             result.mFogColor = lerp(current.mFogSunsetColor, current.mFogDayColor);
             result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientDayColor);
             result.mSunColor = lerp(current.mSunSunsetColor, current.mSunDayColor);
             result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyDayColor);
         }
-        else if (mHour >= (WeatherGlobals::mSunsetTime+WeatherGlobals::mSunsetDuration-fade_duration))
+        else //if (mHour >= 19)
         {
             // fade out
-            float advance = mHour-(WeatherGlobals::mSunsetTime+WeatherGlobals::mSunsetDuration-fade_duration);
-            float factor = advance / fade_duration;
+            float advance = mHour-19;
+            float factor = advance / 2.f;
             result.mFogColor = lerp(current.mFogSunsetColor, current.mFogNightColor);
             result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientNightColor);
             result.mSunColor = lerp(current.mSunSunsetColor, current.mSunNightColor);
             result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyNightColor);
             result.mNightFade = factor;
         }
-        else 
-        {
-            result.mFogColor = current.mFogSunsetColor;
-            result.mAmbientColor = current.mAmbientSunsetColor;
-            result.mSunColor = current.mSunSunsetColor;
-            result.mSkyColor = current.mSkySunsetColor;
-        }
     }
-    
+
     return result;
 }
 
@@ -462,41 +449,37 @@ WeatherResult WeatherManager::transition(float factor)
     const WeatherResult& current = getResult(mCurrentWeather);
     const WeatherResult& other = getResult(mNextWeather);
     WeatherResult result;
-    
+
     result.mCloudTexture = current.mCloudTexture;
     result.mNextCloudTexture = other.mCloudTexture;
     result.mCloudBlendFactor = factor;
-    
+
     result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity);
     result.mFogColor = lerp(current.mFogColor, other.mFogColor);
     result.mSunColor = lerp(current.mSunColor, other.mSunColor);
     result.mSkyColor = lerp(current.mSkyColor, other.mSkyColor);
-    
+
     result.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor);
     result.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor);
     result.mFogDepth = lerp(current.mFogDepth, other.mFogDepth);
     result.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed);
-    result.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed);
+    //result.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed);
+    result.mCloudSpeed = current.mCloudSpeed;
     result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity);
     result.mGlareView = lerp(current.mGlareView, other.mGlareView);
-    
+
     result.mNight = current.mNight;
-    
-    // sound change behaviour:
-    // if 'other' has a new sound, switch to it after 1/2 of the transition length
-    if (other.mAmbientLoopSoundID != "")
-        result.mAmbientLoopSoundID = factor>0.5 ? other.mAmbientLoopSoundID : current.mAmbientLoopSoundID;
-    // if 'current' has a sound and 'other' does not have a sound, turn off the sound immediately
-    else if (current.mAmbientLoopSoundID != "")
-        result.mAmbientLoopSoundID = "";
-        
+
     return result;
 }
 
 void WeatherManager::update(float duration)
 {
-    mWeatherUpdateTime -= duration;
-    if (mEnvironment->mWorld->isCellExterior() || mEnvironment->mWorld->isCellQuasiExterior())
+    mWeatherUpdateTime -= duration * mEnvironment->mWorld->getTimeScaleFactor();
+
+    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);
@@ -504,17 +487,17 @@ void WeatherManager::update(float duration)
         if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion)
         {
             mCurrentRegion = regionstr;
-            mWeatherUpdateTime = WeatherGlobals::mWeatherUpdateTime*60.f;
-            
+            mWeatherUpdateTime = WeatherGlobals::mWeatherUpdateTime*3600;
+
             std::string weather;
-            
+
             if (mRegionOverrides.find(regionstr) != mRegionOverrides.end())
                 weather = mRegionOverrides[regionstr];
             else
             {
                 // get weather probabilities for the current region
                 const ESM::Region *region = mEnvironment->mWorld->getStore().regions.find (regionstr);
-                
+
                 float clear = region->data.clear/255.f;
                 float cloudy = region->data.cloudy/255.f;
                 float foggy = region->data.foggy/255.f;
@@ -525,13 +508,13 @@ void WeatherManager::update(float duration)
                 float blight = region->data.blight/255.f;
                 float snow = region->data.a/255.f;
                 float blizzard = region->data.b/255.f;
-                
+
                 // re-scale to 100 percent
                 const float total = clear+cloudy+foggy+overcast+rain+thunder+ash+blight+snow+blizzard;
-                            
+
                 srand(time(NULL));
                 float random = ((rand()%100)/100.f) * total;
-                                
+
                 if (random >= snow+blight+ash+thunder+rain+overcast+foggy+cloudy+clear)
                     weather = "blizzard";
                 else if (random >= blight+ash+thunder+rain+overcast+foggy+cloudy+clear)
@@ -553,56 +536,54 @@ void WeatherManager::update(float duration)
                 else
                     weather = "clear";
             }
-            
+
             setWeather(weather, false);
-            /*
-            std::cout << "roll result: " << random << std::endl;
-            
-            std::cout << regionstr << " weather probabilities: " << clear << " " << cloudy << " " << foggy << " " 
-                << overcast << " " << rain << " " << thunder << " " << ash << " " << blight << " " << snow << " " 
-                << blizzard << std::endl;
-            
-            std::cout << "New weather : " << weather << std::endl;
-            */
         }
-                
+
         WeatherResult result;
-        
+
         if (mNextWeather != "")
         {
-            mRemainingTransitionTime -= duration;
+            mRemainingTransitionTime -= duration * mEnvironment->mWorld->getTimeScaleFactor();
             if (mRemainingTransitionTime < 0)
             {
                 mCurrentWeather = mNextWeather;
                 mNextWeather = "";
             }
         }
-        
+
         if (mNextWeather != "")
-            result = transition(1-(mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*60)));
+            result = transition(1-(mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600)));
         else
             result = getResult(mCurrentWeather);
-        
+
         mRendering->configureFog(result.mFogDepth, result.mFogColor);
-        
+
         // disable sun during night
-        if (mHour >= WeatherGlobals::mSunsetTime+WeatherGlobals::mSunsetDuration
-            || mHour <= WeatherGlobals::mSunriseTime-WeatherGlobals::mSunriseDuration)
+        if (mHour >= 20 || mHour <= 6.f)
             mRendering->getSkyManager()->sunDisable();
         else
-        {
-            // during day, calculate sun angle
-            float height = 1-std::abs(((mHour-13)/7.f));
-            int facing = mHour > 13.f ? 1 : -1;
-            Vector3 final(
-                (1-height)*facing, 
-                (1-height)*facing, 
-                height);
-            mRendering->setSunDirection(final);
-            
             mRendering->getSkyManager()->sunEnable();
-        }
-        
+
+        // sun angle
+        float height;
+
+        // rise at 6, set at 20
+        if (mHour >= 6 && mHour <= 20)
+            height = 1-std::abs(((mHour-13)/7.f));
+        else if (mHour > 20)
+            height = (mHour-20.f)/4.f;
+        else //if (mHour > 0 && mHour < 6)
+            height = 1-(mHour/6.f);
+
+        int facing = (mHour > 13.f) ? 1 : -1;
+
+        Vector3 final(
+            (1-height)*facing, 
+            (1-height)*facing, 
+            height);
+        mRendering->setSunDirection(final);
+
         // moon calculations
         float night;
         if (mHour >= 14)
@@ -611,9 +592,9 @@ void WeatherManager::update(float duration)
             night = mHour+10;
         else
             night = 0;
-            
+
         night /= 20.f;
-        
+
         if (night != 0)
         {
             float moonHeight = 1-std::abs((night-0.5)*2);
@@ -622,17 +603,17 @@ void WeatherManager::update(float duration)
                 (1-moonHeight)*facing, 
                 (1-moonHeight)*facing, 
                 moonHeight);
-            
+
             Vector3 secunda(
                 (1-moonHeight)*facing*0.8, 
                 (1-moonHeight)*facing*1.25, 
                 moonHeight);
-            
+
             mRendering->getSkyManager()->setMasserDirection(masser);
             mRendering->getSkyManager()->setSecundaDirection(secunda);
             mRendering->getSkyManager()->masserEnable();
             mRendering->getSkyManager()->secundaEnable();
-            
+
             float hour_fade;
             if (mHour >= 7.f && mHour <= 14.f)
                 hour_fade = 1-(mHour-7)/3.f;
@@ -640,28 +621,28 @@ void WeatherManager::update(float duration)
                 hour_fade = mHour-14;
             else
                 hour_fade = 1;
-            
+
             float secunda_angle_fade;
             float masser_angle_fade;
             float angle = moonHeight*90.f;
-            
+
             if (angle >= 30 && angle <= 50)
                 secunda_angle_fade = (angle-30)/20.f;
             else if (angle <30)
                 secunda_angle_fade = 0.f;
             else
                 secunda_angle_fade = 1.f;
-            
+
             if (angle >= 40 && angle <= 50)
                 masser_angle_fade = (angle-40)/10.f;
             else if (angle <40)
                 masser_angle_fade = 0.f;
             else
                 masser_angle_fade = 1.f;
-                
+
             masser_angle_fade *= hour_fade;
             secunda_angle_fade *= hour_fade;
-            
+
             mRendering->getSkyManager()->setMasserFade(masser_angle_fade);
             mRendering->getSkyManager()->setSecundaFade(secunda_angle_fade);
         }
@@ -670,8 +651,8 @@ void WeatherManager::update(float duration)
             mRendering->getSkyManager()->masserDisable();
             mRendering->getSkyManager()->secundaDisable();
         }
-        
-        if (mCurrentWeather == "thunderstorm" && mNextWeather == "")
+
+        if (mCurrentWeather == "thunderstorm" && mNextWeather == "" && exterior)
         {
             if (mThunderFlash > 0)
             {
@@ -689,7 +670,7 @@ void WeatherManager::update(float duration)
                     mEnvironment->mSoundManager->playSound(soundname, 1.0, 1.0);
                     mThunderSoundDelay = 1000;
                 }
-                
+
                 mThunderFlash -= duration;
                 if (mThunderFlash > 0)
                     mRendering->getSkyManager()->setThunder( mThunderFlash / WeatherGlobals::mThunderThreshold );
@@ -708,20 +689,20 @@ void WeatherManager::update(float duration)
                 if (mThunderChance >= mThunderChanceNeeded)
                 {
                     mThunderFlash = WeatherGlobals::mThunderThreshold;
-                    
+
                     mRendering->getSkyManager()->setThunder( mThunderFlash / WeatherGlobals::mThunderThreshold );
-                    
+
                     mThunderSoundDelay = WeatherGlobals::mThunderSoundDelay;
                 }
             }
         }
         else
             mRendering->getSkyManager()->setThunder(0.f);
-        
+
         mRendering->setAmbientColour(result.mAmbientColor);
         mRendering->sunEnable();
         mRendering->setSunColour(result.mSunColor);
-        
+
         mRendering->getSkyManager()->setWeather(result);
     }
     else
@@ -730,19 +711,46 @@ 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)
 {
-    // accelerate a bit for testing
-    /*
-    mHour += 0.005;
-    
-    if (mHour >= 24.f) mHour = 0.f;
-    
-    std::cout << "hour " << mHour << std::endl;
-    */
-    
     mHour = hour;
 }
 
diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp
index 9353f7cd1d..b9b40e6fad 100644
--- a/apps/openmw/mwworld/weather.hpp
+++ b/apps/openmw/mwworld/weather.hpp
@@ -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;
diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp
index 1a69f4d7b4..a636ce2887 100644
--- a/apps/openmw/mwworld/world.cpp
+++ b/apps/openmw/mwworld/world.cpp
@@ -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)
@@ -478,7 +478,7 @@ namespace MWWorld
 
     float World::getTimeScaleFactor() const
     {
-        return mGlobalVariables->getInt ("timescale");
+        return mGlobalVariables->getFloat ("timescale");
     }
 
     void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position)
@@ -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();
diff --git a/cmake/FindCg.cmake b/cmake/FindCg.cmake
new file mode 100644
index 0000000000..4bd348c46f
--- /dev/null
+++ b/cmake/FindCg.cmake
@@ -0,0 +1,53 @@
+#-------------------------------------------------------------------
+# This file is part of the CMake build system for OGRE
+#     (Object-oriented Graphics Rendering Engine)
+# For the latest info, see http://www.ogre3d.org/
+#
+# The contents of this file are placed in the public domain. Feel
+# free to make use of it in any way you like.
+#-------------------------------------------------------------------
+
+# - Try to find Cg
+# Once done, this will define
+#
+#  Cg_FOUND - system has Cg
+#  Cg_INCLUDE_DIRS - the Cg include directories 
+#  Cg_LIBRARIES - link these to use Cg
+
+include(FindPkgMacros)
+findpkg_begin(Cg)
+
+# Get path, convert backslashes as ${ENV_${var}}
+getenv_path(Cg_HOME)
+getenv_path(OGRE_SOURCE)
+getenv_path(OGRE_HOME)
+
+# construct search paths
+set(Cg_PREFIX_PATH ${Cg_HOME} ${ENV_Cg_HOME}
+  ${OGRE_SOURCE}/Dependencies
+  ${ENV_OGRE_SOURCE}/Dependencies
+  ${OGRE_HOME} ${ENV_OGRE_HOME}
+  /opt/nvidia-cg-toolkit)
+create_search_paths(Cg)
+# redo search if prefix path changed
+clear_if_changed(Cg_PREFIX_PATH
+  Cg_LIBRARY_FWK
+  Cg_LIBRARY_REL
+  Cg_LIBRARY_DBG
+  Cg_INCLUDE_DIR
+)
+
+set(Cg_LIBRARY_NAMES Cg)
+get_debug_names(Cg_LIBRARY_NAMES)
+
+use_pkgconfig(Cg_PKGC Cg)
+
+findpkg_framework(Cg)
+
+find_path(Cg_INCLUDE_DIR NAMES cg.h HINTS ${Cg_FRAMEWORK_INCLUDES} ${Cg_INC_SEARCH_PATH} ${Cg_PKGC_INCLUDE_DIRS} PATH_SUFFIXES Cg)
+find_library(Cg_LIBRARY_REL NAMES ${Cg_LIBRARY_NAMES} HINTS ${Cg_LIB_SEARCH_PATH} ${Cg_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" release relwithdebinfo minsizerel)
+find_library(Cg_LIBRARY_DBG NAMES ${Cg_LIBRARY_NAMES_DBG} HINTS ${Cg_LIB_SEARCH_PATH} ${Cg_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" debug)
+make_library_set(Cg_LIBRARY)
+
+findpkg_finish(Cg)
+add_parent_dir(Cg_INCLUDE_DIRS Cg_INCLUDE_DIR)
diff --git a/cmake/FindFreeImage.cmake b/cmake/FindFreeImage.cmake
new file mode 100644
index 0000000000..3b21a17d64
--- /dev/null
+++ b/cmake/FindFreeImage.cmake
@@ -0,0 +1,47 @@
+#-------------------------------------------------------------------
+# This file is part of the CMake build system for OGRE
+#     (Object-oriented Graphics Rendering Engine)
+# For the latest info, see http://www.ogre3d.org/
+#
+# The contents of this file are placed in the public domain. Feel
+# free to make use of it in any way you like.
+#-------------------------------------------------------------------
+
+# - Try to find FreeImage
+# Once done, this will define
+#
+#  FreeImage_FOUND - system has FreeImage
+#  FreeImage_INCLUDE_DIRS - the FreeImage include directories 
+#  FreeImage_LIBRARIES - link these to use FreeImage
+
+include(FindPkgMacros)
+findpkg_begin(FreeImage)
+
+# Get path, convert backslashes as ${ENV_${var}}
+getenv_path(FREEIMAGE_HOME)
+
+# construct search paths
+set(FreeImage_PREFIX_PATH ${FREEIMAGE_HOME} ${ENV_FREEIMAGE_HOME})
+create_search_paths(FreeImage)
+# redo search if prefix path changed
+clear_if_changed(FreeImage_PREFIX_PATH
+  FreeImage_LIBRARY_FWK
+  FreeImage_LIBRARY_REL
+  FreeImage_LIBRARY_DBG
+  FreeImage_INCLUDE_DIR
+)
+
+set(FreeImage_LIBRARY_NAMES freeimage)
+get_debug_names(FreeImage_LIBRARY_NAMES)
+
+use_pkgconfig(FreeImage_PKGC freeimage)
+
+findpkg_framework(FreeImage)
+
+find_path(FreeImage_INCLUDE_DIR NAMES FreeImage.h HINTS ${FreeImage_INC_SEARCH_PATH} ${FreeImage_PKGC_INCLUDE_DIRS})
+find_library(FreeImage_LIBRARY_REL NAMES ${FreeImage_LIBRARY_NAMES} HINTS ${FreeImage_LIB_SEARCH_PATH} ${FreeImage_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" release relwithdebinfo minsizerel)
+find_library(FreeImage_LIBRARY_DBG NAMES ${FreeImage_LIBRARY_NAMES_DBG} HINTS ${FreeImage_LIB_SEARCH_PATH} ${FreeImage_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" debug)
+make_library_set(FreeImage_LIBRARY)
+
+findpkg_finish(FreeImage)
+
diff --git a/cmake/FindOGRE.cmake b/cmake/FindOGRE.cmake
index 917a8653ef..fb4a090c6e 100644
--- a/cmake/FindOGRE.cmake
+++ b/cmake/FindOGRE.cmake
@@ -18,7 +18,7 @@
 # Once done, this will define
 #
 #  OGRE_FOUND - system has OGRE
-#  OGRE_INCLUDE_DIRS - the OGRE include directories
+#  OGRE_INCLUDE_DIRS - the OGRE include directories 
 #  OGRE_LIBRARIES - link these to use the OGRE core
 #  OGRE_BINARY_REL - location of the main Ogre binary (win32 non-static only, release)
 #  OGRE_BINARY_DBG - location of the main Ogre binaries (win32 non-static only, debug)
@@ -28,15 +28,14 @@
 #  Plugin_BSPSceneManager, Plugin_CgProgramManager,
 #  Plugin_OctreeSceneManager, Plugin_OctreeZone,
 #  Plugin_ParticleFX, Plugin_PCZSceneManager,
-#  RenderSystem_GL, RenderSystem_Direct3D9,
+#  RenderSystem_GL, RenderSystem_Direct3D9, RenderSystem_Direct3D10,
 #  Paging, Terrain
 #
 # For each of these components, the following variables are defined:
 #
-
 #  OGRE_${COMPONENT}_FOUND - ${COMPONENT} is available
 #  OGRE_${COMPONENT}_INCLUDE_DIRS - additional include directories for ${COMPONENT}
-#  OGRE_${COMPONENT}_LIBRARIES - link these to use ${COMPONENT}
+#  OGRE_${COMPONENT}_LIBRARIES - link these to use ${COMPONENT} 
 #  OGRE_${COMPONENT}_BINARY_REL - location of the component binary (win32 non-static only, release)
 #  OGRE_${COMPONENT}_BINARY_DBG - location of the component binary (win32 non-static only, debug)
 #
@@ -113,7 +112,7 @@ if (OGRE_PREFIX_SOURCE AND OGRE_PREFIX_BUILD)
     set(OGRE_BIN_SEARCH_PATH ${dir}/bin ${OGRE_BIN_SEARCH_PATH})
 	set(OGRE_BIN_SEARCH_PATH ${dir}/Samples/Common/bin ${OGRE_BIN_SEARCH_PATH})
   endforeach(dir)
-
+  
   if (OGRE_PREFIX_DEPENDENCIES_DIR)
     set(OGRE_INC_SEARCH_PATH ${OGRE_PREFIX_DEPENDENCIES_DIR}/include ${OGRE_INC_SEARCH_PATH})
     set(OGRE_LIB_SEARCH_PATH ${OGRE_PREFIX_DEPENDENCIES_DIR}/lib ${OGRE_LIB_SEARCH_PATH})
@@ -125,12 +124,12 @@ else()
 endif ()
 
 # redo search if any of the environmental hints changed
-set(OGRE_COMPONENTS Paging Terrain
+set(OGRE_COMPONENTS Paging Terrain 
   Plugin_BSPSceneManager Plugin_CgProgramManager Plugin_OctreeSceneManager
   Plugin_OctreeZone Plugin_PCZSceneManager Plugin_ParticleFX
-  RenderSystem_Direct3D11 RenderSystem_Direct3D9 RenderSystem_GL RenderSystem_GLES RenderSystem_GLES2)
-set(OGRE_RESET_VARS
-  OGRE_CONFIG_INCLUDE_DIR OGRE_INCLUDE_DIR
+  RenderSystem_Direct3D10 RenderSystem_Direct3D9 RenderSystem_GL RenderSystem_GLES)
+set(OGRE_RESET_VARS 
+  OGRE_CONFIG_INCLUDE_DIR OGRE_INCLUDE_DIR 
   OGRE_LIBRARY_FWK OGRE_LIBRARY_REL OGRE_LIBRARY_DBG
   OGRE_PLUGIN_DIR_DBG OGRE_PLUGIN_DIR_REL OGRE_MEDIA_DIR)
 foreach (comp ${OGRE_COMPONENTS})
@@ -145,7 +144,7 @@ clear_if_changed(OGRE_PREFIX_WATCH ${OGRE_RESET_VARS})
 # try to locate Ogre via pkg-config
 use_pkgconfig(OGRE_PKGC "OGRE${OGRE_LIB_SUFFIX}")
 
-if(NOT OGRE_BUILD_PLATFORM_APPLE_IOS)
+if(NOT OGRE_BUILD_PLATFORM_IPHONE AND APPLE)
   # try to find framework on OSX
   findpkg_framework(OGRE)
 else()
@@ -236,7 +235,6 @@ if (OGRE_STATIC)
   find_package(Freetype QUIET)
   find_package(OpenGL QUIET)
   find_package(OpenGLES QUIET)
-  find_package(OpenGLES2 QUIET)
   find_package(ZLIB QUIET)
   find_package(ZZip QUIET)
   if (UNIX AND NOT APPLE)
@@ -246,26 +244,24 @@ if (OGRE_STATIC)
       set(X11_FOUND FALSE)
     endif ()
   endif ()
-  if (APPLE AND NOT OGRE_BUILD_PLATFORM_APPLE_IOS)
+  if (APPLE AND NOT OGRE_BUILD_PLATFORM_IPHONE)
     find_package(Cocoa QUIET)
     find_package(Carbon QUIET)
-    find_package(CoreVideo QUIET)
-    if (NOT Cocoa_FOUND OR NOT Carbon_FOUND OR NOT CoreVideo_FOUND)
+    if (NOT Cocoa_FOUND OR NOT Carbon_FOUND)
       set(OGRE_DEPS_FOUND FALSE)
     endif ()
   endif ()
-  if (APPLE AND OGRE_BUILD_PLATFORM_APPLE_IOS)
+  if (APPLE AND OGRE_BUILD_PLATFORM_IPHONE)
     find_package(iPhoneSDK QUIET)
     if (NOT iPhoneSDK_FOUND)
       set(OGRE_DEPS_FOUND FALSE)
     endif ()
   endif ()
 
-  set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${ZZip_LIBRARIES} ${ZLIB_LIBRARIES} ${FreeImage_LIBRARIES} ${FREETYPE_LIBRARIES} )
-
-  if (APPLE AND NOT OGRE_BUILD_PLATFORM_APPLE_IOS)
-    set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${X11_LIBRARIES} ${X11_Xt_LIBRARIES} ${XAW_LIBRARY} ${X11_Xrandr_LIB} ${Carbon_LIBRARIES} ${Cocoa_LIBRARIES})
-  endif()
+  set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${OGRE_LIBRARY_FWK} ${ZZip_LIBRARIES} ${ZLIB_LIBRARIES} 
+    ${FreeImage_LIBRARIES} ${FREETYPE_LIBRARIES} 
+    ${X11_LIBRARIES} ${X11_Xt_LIBRARIES} ${XAW_LIBRARY} ${X11_Xrandr_LIB}
+    ${Cocoa_LIBRARIES} ${Carbon_LIBRARIES})
 
   if (NOT ZLIB_FOUND OR NOT ZZip_FOUND)
     set(OGRE_DEPS_FOUND FALSE)
@@ -309,7 +305,7 @@ if (OGRE_STATIC)
       endif ()
     endif ()
   endif ()
-
+  
   if (NOT OGRE_DEPS_FOUND)
     pkg_message(OGRE "Could not find all required dependencies for the Ogre package.")
     set(OGRE_FOUND FALSE)
@@ -341,7 +337,7 @@ endif()
 # Find Ogre components
 #########################################################
 
-set(OGRE_COMPONENT_SEARCH_PATH_REL
+set(OGRE_COMPONENT_SEARCH_PATH_REL 
   ${OGRE_LIBRARY_DIR_REL}/..
   ${OGRE_LIBRARY_DIR_REL}/../..
   ${OGRE_BIN_SEARCH_PATH}
@@ -393,26 +389,38 @@ macro(ogre_find_plugin PLUGIN HEADER)
     set(TMP_CMAKE_LIB_PREFIX ${CMAKE_FIND_LIBRARY_PREFIXES})
     set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} "")
   endif()
-
+  
   # strip RenderSystem_ or Plugin_ prefix from plugin name
   string(REPLACE "RenderSystem_" "" PLUGIN_TEMP ${PLUGIN})
   string(REPLACE "Plugin_" "" PLUGIN_NAME ${PLUGIN_TEMP})
-
+  
   # header files for plugins are not usually needed, but find them anyway if they are present
   set(OGRE_PLUGIN_PATH_SUFFIXES
-    PlugIns PlugIns/${PLUGIN_NAME} Plugins Plugins/${PLUGIN_NAME} ${PLUGIN}
+    PlugIns PlugIns/${PLUGIN_NAME} Plugins Plugins/${PLUGIN_NAME} ${PLUGIN} 
     RenderSystems RenderSystems/${PLUGIN_NAME} ${ARGN})
-  find_path(OGRE_${PLUGIN}_INCLUDE_DIR NAMES ${HEADER}
-    HINTS ${OGRE_INCLUDE_DIRS} ${OGRE_PREFIX_SOURCE}
+  find_path(OGRE_${PLUGIN}_INCLUDE_DIR NAMES ${HEADER} 
+    HINTS ${OGRE_INCLUDE_DIRS} ${OGRE_PREFIX_SOURCE}  
     PATH_SUFFIXES ${OGRE_PLUGIN_PATH_SUFFIXES})
   # find link libraries for plugins
   set(OGRE_${PLUGIN}_LIBRARY_NAMES "${PLUGIN}${OGRE_LIB_SUFFIX}")
   get_debug_names(OGRE_${PLUGIN}_LIBRARY_NAMES)
   set(OGRE_${PLUGIN}_LIBRARY_FWK ${OGRE_LIBRARY_FWK})
+  # Search for release plugins in OGRE dir with version suffix
   find_library(OGRE_${PLUGIN}_LIBRARY_REL NAMES ${OGRE_${PLUGIN}_LIBRARY_NAMES}
-    HINTS ${OGRE_LIBRARY_DIRS} PATH_SUFFIXES "" OGRE opt release release/opt relwithdebinfo relwithdebinfo/opt minsizerel minsizerel/opt)
+    HINTS ${OGRE_LIBRARY_DIRS} PATH_SUFFIXES "" OGRE-${OGRE_VERSION} opt release release/opt relwithdebinfo relwithdebinfo/opt minsizerel minsizerel/opt)
+  if(NOT EXISTS "${OGRE_${PLUGIN}_LIBRARY_REL}")
+    # Search for release plugins in OGRE dir without version suffix
+    find_library(OGRE_${PLUGIN}_LIBRARY_REL NAMES ${OGRE_${PLUGIN}_LIBRARY_NAMES}
+      HINTS ${OGRE_LIBRARY_DIRS} PATH_SUFFIXES "" OGRE opt release release/opt relwithdebinfo relwithdebinfo/opt minsizerel minsizerel/opt)
+  endif()
+  # Search for debug plugins in OGRE dir with version suffix
   find_library(OGRE_${PLUGIN}_LIBRARY_DBG NAMES ${OGRE_${PLUGIN}_LIBRARY_NAMES_DBG}
-    HINTS ${OGRE_LIBRARY_DIRS} PATH_SUFFIXES "" OGRE opt debug debug/opt)
+    HINTS ${OGRE_LIBRARY_DIRS} PATH_SUFFIXES "" OGRE-${OGRE_VERSION} opt debug debug/opt)
+  if(NOT EXISTS "${OGRE_${PLUGIN}_LIBRARY_DBG}")
+    # Search for debug plugins in OGRE dir without version suffix
+    find_library(OGRE_${PLUGIN}_LIBRARY_DBG NAMES ${OGRE_${PLUGIN}_LIBRARY_NAMES_DBG}
+      HINTS ${OGRE_LIBRARY_DIRS} PATH_SUFFIXES "" OGRE opt debug debug/opt)
+  endif()
   make_library_set(OGRE_${PLUGIN}_LIBRARY)
 
   if (OGRE_${PLUGIN}_LIBRARY OR OGRE_${PLUGIN}_INCLUDE_DIR)
@@ -429,7 +437,7 @@ macro(ogre_find_plugin PLUGIN HEADER)
   if (OGRE_${PLUGIN}_FOUND)
     if (NOT OGRE_PLUGIN_DIR_REL OR NOT OGRE_PLUGIN_DIR_DBG)
       if (WIN32)
-        set(OGRE_PLUGIN_SEARCH_PATH_REL
+        set(OGRE_PLUGIN_SEARCH_PATH_REL 
           ${OGRE_LIBRARY_DIR_REL}/..
           ${OGRE_LIBRARY_DIR_REL}/../..
 		  ${OGRE_BIN_SEARCH_PATH}
@@ -445,12 +453,16 @@ macro(ogre_find_plugin PLUGIN HEADER)
           PATH_SUFFIXES "" bin bin/debug debug)
       elseif (UNIX)
         get_filename_component(OGRE_PLUGIN_DIR_TMP ${OGRE_${PLUGIN}_LIBRARY_REL} PATH)
-        set(OGRE_PLUGIN_DIR_REL ${OGRE_PLUGIN_DIR_TMP} CACHE STRING "Ogre plugin dir (release)" FORCE)
+        # For some reason this fails
+        #set(OGRE_PLUGIN_DIR_REL ${OGRE_PLUGIN_DIR_TMP} CACHE STRING "Ogre plugin dir (release)")
+        set(OGRE_PLUGIN_DIR_REL ${OGRE_PLUGIN_DIR_TMP})
         get_filename_component(OGRE_PLUGIN_DIR_TMP ${OGRE_${PLUGIN}_LIBRARY_DBG} PATH)
-        set(OGRE_PLUGIN_DIR_DBG ${OGRE_PLUGIN_DIR_TMP} CACHE STRING "Ogre plugin dir (debug)" FORCE)
+        # Same here
+        #set(OGRE_PLUGIN_DIR_DBG ${OGRE_PLUGIN_DIR_TMP} CACHE STRING "Ogre plugin dir (debug)")
+        set(OGRE_PLUGIN_DIR_DBG ${OGRE_PLUGIN_DIR_TMP})
       endif ()
     endif ()
-
+	
 	# find binaries
 	if (NOT OGRE_STATIC)
 		if (WIN32)
@@ -459,7 +471,7 @@ macro(ogre_find_plugin PLUGIN HEADER)
 		endif()
 		mark_as_advanced(OGRE_${PLUGIN}_REL OGRE_${PLUGIN}_DBG)
 	endif()
-
+	
   endif ()
 
   if (TMP_CMAKE_LIB_PREFIX)
@@ -475,8 +487,8 @@ ogre_find_plugin(Plugin_OctreeSceneManager OgreOctreeSceneManager.h PlugIns/Octr
 ogre_find_plugin(Plugin_ParticleFX OgreParticleFXPrerequisites.h PlugIns/ParticleFX/include)
 ogre_find_plugin(RenderSystem_GL OgreGLRenderSystem.h RenderSystems/GL/include)
 ogre_find_plugin(RenderSystem_GLES OgreGLESRenderSystem.h RenderSystems/GLES/include)
-ogre_find_plugin(RenderSystem_GLES2 OgreGLES2RenderSystem.h RenderSystems/GLES2/include)
 ogre_find_plugin(RenderSystem_Direct3D9 OgreD3D9RenderSystem.h RenderSystems/Direct3D9/include)
+ogre_find_plugin(RenderSystem_Direct3D10 OgreD3D10RenderSystem.h RenderSystems/Direct3D10/include)
 ogre_find_plugin(RenderSystem_Direct3D11 OgreD3D11RenderSystem.h RenderSystems/Direct3D11/include)
 
 if (OGRE_STATIC)
@@ -484,26 +496,28 @@ if (OGRE_STATIC)
   if (NOT DirectX_FOUND)
     set(OGRE_RenderSystem_Direct3D9_FOUND FALSE)
   endif ()
+  if (NOT DirectX_D3D10_FOUND)
+    set(OGRE_RenderSystem_Direct3D10_FOUND FALSE)
+  endif ()
   if (NOT DirectX_D3D11_FOUND)
     set(OGRE_RenderSystem_Direct3D11_FOUND FALSE)
   endif ()
   if (NOT OPENGL_FOUND)
     set(OGRE_RenderSystem_GL_FOUND FALSE)
   endif ()
-  if (NOT OPENGLES_FOUND)
+  if (NOT OPENGLES_FOUND AND NOT OPENGLES2_FOUND)
     set(OGRE_RenderSystem_GLES_FOUND FALSE)
   endif ()
-  if (NOT OPENGLES2_FOUND)
-    set(OGRE_RenderSystem_GLES2_FOUND FALSE)
-  endif ()
   if (NOT Cg_FOUND)
     set(OGRE_Plugin_CgProgramManager_FOUND FALSE)
   endif ()
-
+  
   set(OGRE_RenderSystem_Direct3D9_LIBRARIES ${OGRE_RenderSystem_Direct3D9_LIBRARIES}
     ${DirectX_LIBRARIES}
   )
-
+  set(OGRE_RenderSystem_Direct3D10_LIBRARIES ${OGRE_RenderSystem_Direct3D10_LIBRARIES}
+    ${DirectX_D3D10_LIBRARIES}
+  )
   set(OGRE_RenderSystem_Direct3D11_LIBRARIES ${OGRE_RenderSystem_Direct3D11_LIBRARIES}
     ${DirectX_D3D11_LIBRARIES}
   )
@@ -513,9 +527,6 @@ if (OGRE_STATIC)
   set(OGRE_RenderSystem_GLES_LIBRARIES ${OGRE_RenderSystem_GLES_LIBRARIES}
     ${OPENGLES_LIBRARIES}
   )
-  set(OGRE_RenderSystem_GLES2_LIBRARIES ${OGRE_RenderSystem_GLES2_LIBRARIES}
-    ${OPENGLES2_LIBRARIES}
-  )
   set(OGRE_Plugin_CgProgramManager_LIBRARIES ${OGRE_Plugin_CgProgramManager_LIBRARIES}
     ${Cg_LIBRARIES}
   )
@@ -540,3 +551,4 @@ set(OGRE_MEDIA_SEARCH_SUFFIX
 clear_if_changed(OGRE_PREFIX_WATCH OGRE_MEDIA_DIR)
 find_path(OGRE_MEDIA_DIR NAMES packs/cubemapsJS.zip HINTS ${OGRE_MEDIA_SEARCH_PATH}
   PATHS ${OGRE_PREFIX_PATH} PATH_SUFFIXES ${OGRE_MEDIA_SEARCH_SUFFIX})
+
diff --git a/cmake/FindZZip.cmake b/cmake/FindZZip.cmake
new file mode 100644
index 0000000000..68fe043f36
--- /dev/null
+++ b/cmake/FindZZip.cmake
@@ -0,0 +1,48 @@
+#-------------------------------------------------------------------
+# This file is part of the CMake build system for OGRE
+#     (Object-oriented Graphics Rendering Engine)
+# For the latest info, see http://www.ogre3d.org/
+#
+# The contents of this file are placed in the public domain. Feel
+# free to make use of it in any way you like.
+#-------------------------------------------------------------------
+
+# - Try to find zziplib
+# Once done, this will define
+#
+#  ZZip_FOUND - system has ZZip
+#  ZZip_INCLUDE_DIRS - the ZZip include directories 
+#  ZZip_LIBRARIES - link these to use ZZip
+
+include(FindPkgMacros)
+findpkg_begin(ZZip)
+
+# Get path, convert backslashes as ${ENV_${var}}
+getenv_path(ZZIP_HOME)
+
+
+# construct search paths
+set(ZZip_PREFIX_PATH ${ZZIP_HOME} ${ENV_ZZIP_HOME})
+create_search_paths(ZZip)
+# redo search if prefix path changed
+clear_if_changed(ZZip_PREFIX_PATH
+  ZZip_LIBRARY_FWK
+  ZZip_LIBRARY_REL
+  ZZip_LIBRARY_DBG
+  ZZip_INCLUDE_DIR
+)
+
+set(ZZip_LIBRARY_NAMES zzip zziplib)
+get_debug_names(ZZip_LIBRARY_NAMES)
+
+use_pkgconfig(ZZip_PKGC zziplib)
+
+findpkg_framework(ZZip)
+
+find_path(ZZip_INCLUDE_DIR NAMES zzip/zzip.h HINTS ${ZZip_INC_SEARCH_PATH} ${ZZip_PKGC_INCLUDE_DIRS})
+find_library(ZZip_LIBRARY_REL NAMES ${ZZip_LIBRARY_NAMES} HINTS ${ZZip_LIB_SEARCH_PATH} ${ZZip_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" release relwithdebinfo minsizerel)
+find_library(ZZip_LIBRARY_DBG NAMES ${ZZip_LIBRARY_NAMES_DBG} HINTS ${ZZip_LIB_SEARCH_PATH} ${ZZip_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" debug)
+make_library_set(ZZip_LIBRARY)
+
+findpkg_finish(ZZip)
+
diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp
index c255154b5a..5d74ee9d42 100644
--- a/components/compiler/controlparser.cpp
+++ b/components/compiler/controlparser.cpp
@@ -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,
diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp
index 06c3207e2d..95480c0239 100644
--- a/components/compiler/exprparser.cpp
+++ b/components/compiler/exprparser.cpp
@@ -639,7 +639,7 @@ namespace Compiler
         std::vector<Interpreter::Type_Code>& code, bool invert)
     {
         bool optional = false;
-        bool optionalCount = 0;
+        int optionalCount = 0;
 
         ExprParser parser (getErrorHandler(), getContext(), mLocals, mLiterals, true);
         StringParser stringParser (getErrorHandler(), getContext(), mLiterals);
diff --git a/components/compiler/scriptparser.cpp b/components/compiler/scriptparser.cpp
index ce142847f5..ac0ee63f1c 100644
--- a/components/compiler/scriptparser.cpp
+++ b/components/compiler/scriptparser.cpp
@@ -24,7 +24,7 @@ namespace Compiler
         mLineParser.reset();
         if (mLineParser.parseName (name, loc, scanner))
             scanner.scan (mLineParser);
-        
+
         return true;
     }
 
@@ -34,22 +34,22 @@ namespace Compiler
         {
             mControlParser.reset();
             if (mControlParser.parseKeyword (keyword, loc, scanner))
-                scanner.scan (mControlParser);   
-        
+                scanner.scan (mControlParser);
+
             mControlParser.appendCode (mOutput.getCode());
-        
+
             return true;
         }
-    
+
         if (keyword==Scanner::K_end && mEnd)
         {
             return false;
         }
-    
+
         mLineParser.reset();
         if (mLineParser.parseKeyword (keyword, loc, scanner))
             scanner.scan (mLineParser);
-            
+
         return true;
     }
 
@@ -57,11 +57,11 @@ namespace Compiler
     {
         if (code==Scanner::S_newline) // empty line
             return true;
-            
+
         mLineParser.reset();
         if (mLineParser.parseSpecial (code, loc, scanner))
             scanner.scan (mLineParser);
-            
+
         return true;
     }
 
@@ -77,4 +77,3 @@ namespace Compiler
         mOutput.clear();
     }
 }
-
diff --git a/components/compiler/stringparser.cpp b/components/compiler/stringparser.cpp
index 396a88c786..fe7bd30b9e 100644
--- a/components/compiler/stringparser.cpp
+++ b/components/compiler/stringparser.cpp
@@ -39,6 +39,11 @@ namespace Compiler
             mState = CommaState;
             return true;
         }
+        else if (code==Scanner::S_newline && mState==StartState)
+        {
+            scanner.putbackSpecial (code, loc);
+            return false;
+        }
 
         return Parser::parseSpecial (code, loc, scanner);
     }
diff --git a/components/esm/loadpgrd.cpp b/components/esm/loadpgrd.cpp
index b0727b0c01..dc63ce335e 100644
--- a/components/esm/loadpgrd.cpp
+++ b/components/esm/loadpgrd.cpp
@@ -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);
+                }
+            }
+        }
     }
 }
 
diff --git a/components/esm/loadpgrd.hpp b/components/esm/loadpgrd.hpp
index 8c030d3141..6e2c6e134f 100644
--- a/components/esm/loadpgrd.hpp
+++ b/components/esm/loadpgrd.hpp
@@ -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);
 };
diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp
index 20a2e8ff95..678f794c81 100644
--- a/components/esm_store/reclists.hpp
+++ b/components/esm_store/reclists.hpp
@@ -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;
diff --git a/components/esm_store/store.hpp b/components/esm_store/store.hpp
index e3bbf9e82c..fab04d3e97 100644
--- a/components/esm_store/store.hpp
+++ b/components/esm_store/store.hpp
@@ -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;
diff --git a/components/files/filelibrary.cpp b/components/files/filelibrary.cpp
index f1467166e3..c114159291 100644
--- a/components/files/filelibrary.cpp
+++ b/components/files/filelibrary.cpp
@@ -89,12 +89,12 @@ namespace Files
     }
 
     // Searches the library for an item and returns a boost path to it
-    boost::filesystem::path FileLibrary::locate(std::string item, bool strict, std::string sectionName)
+    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);
+            return FileListLocator(mPriorityList, boost::filesystem::path(item), strict, ignoreExtensions);
         }
         else
         {
@@ -103,7 +103,7 @@ namespace Files
                 std::cout << "Warning: There is no section named " << sectionName << "\n";
                 return result;
             }
-            result = FileListLocator(mMap[sectionName], boost::filesystem::path(item), strict);
+            result = FileListLocator(mMap[sectionName], boost::filesystem::path(item), strict, ignoreExtensions);
         }
         return result;
     }
diff --git a/components/files/filelibrary.hpp b/components/files/filelibrary.hpp
index 55978084f9..6abe972690 100644
--- a/components/files/filelibrary.hpp
+++ b/components/files/filelibrary.hpp
@@ -39,7 +39,7 @@ namespace Files
             /// 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, std::string sectionName="");
+            boost::filesystem::path locate(std::string item, bool strict, bool ignoreExtensions, std::string sectionName="");
 
             /// Prints all the available sections, used for debugging
             void printSections();
diff --git a/components/files/fileops.cpp b/components/files/fileops.cpp
index f57eaa5462..4bb71f8661 100644
--- a/components/files/fileops.cpp
+++ b/components/files/fileops.cpp
@@ -13,6 +13,14 @@ 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)
     {
@@ -42,13 +50,18 @@ bool isFile(const char *name)
     }
 
     // Locates path in path container
-    boost::filesystem::path FileListLocator (const Files::PathContainer& list, const boost::filesystem::path& toFind, bool strict)
+    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 = toFind.string();
+        std::string toFindStr;
+        if (ignoreExtensions)
+            toFindStr = boost::filesystem::basename(toFind);
+        else
+            toFindStr = toFind.string();
 
         std::string fullPath;
 
@@ -80,11 +93,15 @@ bool isFile(const char *name)
         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(fullPath.find(toFindStr) != std::string::npos)
+            if(endingMatches(fullPath, toFindStr))
             {
                 result = *it;
                 break;
@@ -94,9 +111,9 @@ bool isFile(const char *name)
     }
 
     // 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)
+    std::string FileListLocator (const Files::PathContainer& list,const std::string& toFind, bool strict, bool ignoreExtensions)
     {
-        return FileListLocator(list, boost::filesystem::path(toFind), strict).string();
+        return FileListLocator(list, boost::filesystem::path(toFind), strict, ignoreExtensions).string();
     }
 
 }
diff --git a/components/files/fileops.hpp b/components/files/fileops.hpp
index 49ef7b9472..bf1c51485f 100644
--- a/components/files/fileops.hpp
+++ b/components/files/fileops.hpp
@@ -27,10 +27,11 @@ bool isFile(const char *name);
     /// 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);
+    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);
+    std::string FileListLocator (const Files::PathContainer& list,const std::string& toFind, bool strict, bool ignoreExtensions);
 
 }
 
diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp
index 361e5f6e9e..52a37ba5c1 100644
--- a/components/nif/nif_file.cpp
+++ b/components/nif/nif_file.cpp
@@ -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();
diff --git a/components/nif/record.hpp b/components/nif/record.hpp
index e94e3d0d34..40f91f84f8 100644
--- a/components/nif/record.hpp
+++ b/components/nif/record.hpp
@@ -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
 
diff --git a/extern/mygui_3.0.1/CMakeLists.txt b/extern/mygui_3.0.1/CMakeLists.txt
index 2cbe8aabe6..6a96bd3674 100644
--- a/extern/mygui_3.0.1/CMakeLists.txt
+++ b/extern/mygui_3.0.1/CMakeLists.txt
@@ -54,6 +54,7 @@ configure_file("${SDIR}/openmw_chargen_class_description_layout.xml" "${DDIR}/op
 configure_file("${SDIR}/openmw_chargen_birth_layout.xml" "${DDIR}/openmw_chargen_birth_layout.xml" COPYONLY)
 configure_file("${SDIR}/openmw_chargen_review_layout.xml" "${DDIR}/openmw_chargen_review_layout.xml" COPYONLY)
 configure_file("${SDIR}/openmw_dialogue_window_layout.xml" "${DDIR}/openmw_dialogue_window_layout.xml" COPYONLY)
+configure_file("${SDIR}/openmw_dialogue_window_skin.xml" "${DDIR}/openmw_dialogue_window_skin.xml" COPYONLY)
 configure_file("${SDIR}/openmw_inventory_window_layout.xml" "${DDIR}/openmw_inventory_window_layout.xml" COPYONLY)
 configure_file("${SDIR}/openmw_layers.xml" "${DDIR}/openmw_layers.xml" COPYONLY)
 configure_file("${SDIR}/openmw_mainmenu_layout.xml" "${DDIR}/openmw_mainmenu_layout.xml" COPYONLY)
diff --git a/extern/mygui_3.0.1/openmw_resources/core.xml b/extern/mygui_3.0.1/openmw_resources/core.xml
index 31d409a35a..e98b20d3ad 100644
--- a/extern/mygui_3.0.1/openmw_resources/core.xml
+++ b/extern/mygui_3.0.1/openmw_resources/core.xml
@@ -20,6 +20,7 @@
             <List file="openmw_mainmenu_skin.xml" group="General"/>
             <List file="openmw_console.skin.xml" group="General"/>
             <List file="openmw_journal_skin.xml" group="General"/>
+			<List file="openmw_dialogue_window_skin.xml" group="General"/>
     </MyGUI>
 
 </MyGUI>
diff --git a/extern/mygui_3.0.1/openmw_resources/openmw_dialogue_window_layout.xml b/extern/mygui_3.0.1/openmw_resources/openmw_dialogue_window_layout.xml
index 2987e82be0..44642167b2 100644
--- a/extern/mygui_3.0.1/openmw_resources/openmw_dialogue_window_layout.xml
+++ b/extern/mygui_3.0.1/openmw_resources/openmw_dialogue_window_layout.xml
@@ -17,8 +17,13 @@
             <Property key="Edit_VisibleVScroll" value="1" />
         </Widget>
 
+		<!-- The disposition bar-->
+        <Widget type="Progress" skin="MW_EnergyBar_Blue" position="432 39 132 18"
+            align="Right Top" name="Disposition">		
+			<Widget type="Edit" skin="MW_DispositionEdit" position_real = "0.25 0 0.5 1" name = "DispositionText"/>	
+		</Widget>
         <!-- The list of topics -->
-        <Widget type="List" skin="MW_List" position="432 39 132 341" name="TopicsList">
+        <Widget type="List" skin="MW_List" position="432 62 132 318" name="TopicsList">
         </Widget>
 
         <!-- The Goodbye button -->
diff --git a/extern/mygui_3.0.1/openmw_resources/openmw_dialogue_window_skin.xml b/extern/mygui_3.0.1/openmw_resources/openmw_dialogue_window_skin.xml
new file mode 100644
index 0000000000..25e0b86592
--- /dev/null
+++ b/extern/mygui_3.0.1/openmw_resources/openmw_dialogue_window_skin.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<MyGUI type="Skin">
+
+    <Skin name = "MW_DispEdit" size = "10 10">
+        <Property key="FontName" value = "MonoFont" />
+        <Property key="AlignText" value = "Left Top" />
+        <Property key="Colour" value = "0000FF" />
+        <!--Property key="Pointer" value = "beam" /-->
+        <BasisSkin type="EditText" offset = "0 0 10 10" align = "Stretch"/>
+    </Skin>
+	
+    <Skin name="MW_DispositionEdit" size="0 0 50 50">
+        <Property key="WordWrap" value = "true" />
+        <Child type="Widget" skin="MW_DispEdit" offset="0 0 35 10" align = "ALIGN_STRETCH" name = "Client"/>
+        <!--Child type="VScroll" skin="VScroll" offset = "35 0 15 50" align = "Right VStretch" name = "VScroll"/-->
+    </Skin>
+</MyGUI>
\ No newline at end of file
diff --git a/libs/mangle b/libs/mangle
deleted file mode 160000
index 14b2851e72..0000000000
--- a/libs/mangle
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 14b2851e72f610ae81dd296598867e6fb0babd2a
diff --git a/libs/mangle/.gitignore b/libs/mangle/.gitignore
new file mode 100644
index 0000000000..cd24d78972
--- /dev/null
+++ b/libs/mangle/.gitignore
@@ -0,0 +1,3 @@
+upload_docs.sh
+docs
+*~
diff --git a/libs/mangle/Doxyfile b/libs/mangle/Doxyfile
new file mode 100644
index 0000000000..f3e0180029
--- /dev/null
+++ b/libs/mangle/Doxyfile
@@ -0,0 +1,1510 @@
+# Doxyfile 1.5.8
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file 
+# that follow. The default is UTF-8 which is also the encoding used for all 
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the 
+# iconv built into libc) for the transcoding. See 
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = Mangle
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         =  1
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of 
+# source files, where putting all generated files in the same directory would 
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, 
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), 
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, 
+# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, 
+# Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is 
+# used as the annotated text. Otherwise, the brief description is used as-is. 
+# If left blank, the following values are used ("$name" is automatically 
+# replaced with the name of the entity): "The $name class" "The $name widget" 
+# "The $name file" "is" "provides" "specifies" "contains" 
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
+# inherited members of a class in the documentation of that class as if those 
+# members were ordinary class members. Constructors, destructors and assignment 
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = 
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like regular Qt-style comments 
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
+# interpret the first line (until the first dot) of a Qt-style 
+# comment as the brief description. If set to NO, the comments 
+# will behave just like regular Qt-style comments (thus requiring 
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
+# a new page for each member. If set to NO, the documentation of a member will 
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
+# sources only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Java. For instance, namespaces will be presented as packages, qualified 
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
+# sources. Doxygen will then generate output that is tailored for 
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses. 
+# With this tag you can assign which parser to use for a given extension. 
+# Doxygen has a built-in mapping, but you can override or extend it using this tag. 
+# The format is ext=language, where ext is a file extension, and language is one of 
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, 
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat 
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), 
+# use: inc=Fortran f=C
+
+EXTENSION_MAPPING      = 
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
+# to include (a tag file for) the STL sources as input, then you should 
+# set this tag to YES in order to let doxygen match functions declarations and 
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
+# func(std::string) {}). This also make the inheritance and collaboration 
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = YES
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to 
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
+# Doxygen will parse them like normal C++ but will assume all classes use public 
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter 
+# and setter methods for a property. Setting this option to YES (the default) 
+# will make doxygen to replace the get and set methods by a property in the 
+# documentation. This will only work if the methods are indeed getting or 
+# setting a simple type. If this is not the case, or you want to show the 
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
+# is documented as struct, union, or enum with the name of the typedef. So 
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
+# with name TypeT. When disabled the typedef will appear as a member of a file, 
+# namespace, or class. And the struct will be named TypeS. This can typically 
+# be useful for C code in case the coding convention dictates that all compound 
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to 
+# determine which symbols to keep in memory and which to flush to disk. 
+# When the cache is full, less often used symbols will be written to disk. 
+# For small to medium size projects (<1000 input files) the default value is 
+# probably good enough. For larger projects a too small cache size can cause 
+# doxygen to be busy swapping symbols to and from disk most of the time 
+# causing a significant performance penality. 
+# If the system has enough physical memory increasing the cache will improve the 
+# performance by keeping more symbols in memory. Note that the value works on 
+# a logarithmic scale so increasing the size by one will rougly double the 
+# memory usage. The cache size is given by this formula: 
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be 
+# extracted and appear in the documentation as a namespace called 
+# 'anonymous_namespace{file}', where file will be replaced with the base 
+# name of the file that contains the anonymous namespace. By default 
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
+# hierarchy of group names into alphabetical order. If set to NO (the default) 
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. 
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. 
+# This will remove the Files entry from the Quick Index and from the 
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
+# Namespaces page. 
+# This will remove the Namespaces entry from the Quick Index 
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from 
+# the version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the program writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    = 
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by 
+# doxygen. The layout file controls the global structure of the generated output files 
+# in an output format independent way. The create the layout file that represents 
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a 
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name 
+# of the layout file.
+
+LAYOUT_FILE            = 
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for 
+# functions that are documented, but have no documentation for their parameters 
+# or return value. If set to NO (the default) doxygen will only warn about 
+# wrong or incomplete parameter documentation, but not about the absence of 
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text. Optionally the format may contain 
+# $version, which will be replaced by the version of the file (if it could 
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = sound stream vfs input
+
+# This tag can be used to specify the character encoding of the source files 
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
+# also the default input encoding. Doxygen uses libiconv (or the iconv built 
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          = *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
+# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories. Note that the wildcards are matched 
+# against the file with absolute path, so to exclude all test directories 
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = */tests/*
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
+# (namespaces, classes, functions, etc.) that should be excluded from the 
+# output. The symbol name can be a fully qualified name, a word, or if the 
+# wildcard * is used, a substring. Examples: ANamespace, AClass, 
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output. 
+# If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis. 
+# Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match. 
+# The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) 
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from 
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will 
+# link to the source code. 
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code 
+# will point to the HTML generated by the htags(1) tool instead of doxygen 
+# built-in source browser. The htags tool is part of GNU's global source 
+# tagging system (see http://www.gnu.org/software/global/global.html). You 
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = docs
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
+# documentation will contain sections that can be hidden and shown after the 
+# page has loaded. For this to work a browser that supports 
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files 
+# will be generated that can be used as input for Apple's Xcode 3 
+# integrated development environment, introduced with OSX 10.5 (Leopard). 
+# To create a documentation set, doxygen will generate a Makefile in the 
+# HTML output directory. Running make will produce the docset in that 
+# directory and running "make install" will install the docset in 
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 
+# it at startup. 
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
+# feed. A documentation feed provides an umbrella under which multiple 
+# documentation sets from a single provider (such as a company or product suite) 
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
+# should uniquely identify the documentation set bundle. This should be a 
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING 
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file 
+# content.
+
+CHM_INDEX_ENCODING     = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER 
+# are set, an additional index file will be generated that can be used as input for 
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated 
+# HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can 
+# be used to specify the file name of the resulting .qch file. 
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               = 
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating 
+# Qt Help Project output. For more information please see 
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = 
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 
+# Qt Help Project output. For more information please see 
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. 
+# For more information please see 
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   = 
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see 
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  = 
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's 
+# filter section matches. 
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  = 
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 
+# be used to specify the location of Qt's qhelpgenerator. 
+# If non-empty doxygen will try to run qhelpgenerator on the generated 
+# .qhp file.
+
+QHG_LOCATION           = 
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index 
+# structure should be generated to display hierarchical information. 
+# If the tag value is set to FRAME, a side panel will be generated 
+# containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature. Other possible values 
+# for this tag are: HIERARCHIES, which will generate the Groups, Directories, 
+# and Class Hierarchy pages using a tree view instead of an ordered list; 
+# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which 
+# disables this behavior completely. For backwards compatibility with previous 
+# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE 
+# respectively.
+
+GENERATE_TREEVIEW      = NONE
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# Use this tag to change the font size of Latex formulas included 
+# as images in the HTML documentation. The default is 10. Note that 
+# when you change the font size after a successful doxygen run you need 
+# to manually remove any form_*.png images from the HTML output directory 
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader. 
+# This is useful 
+# if you want to understand what is going on. 
+# On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse 
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#  
+# TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#  
+# TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links. 
+# Note that each tag file must have a unique name 
+# (where the name does NOT include the path) 
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
+# or super classes. Setting the tag to NO turns the diagrams off. Note that 
+# this option is superseded by the HAVE_DOT option below. This is only a 
+# fallback. It is recommended to install and use dot, since it yields more 
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc 
+# command. Doxygen will then run the mscgen tool (see 
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where 
+# the mscgen tool resides. If left empty the tool is assumed to be found in the 
+# default search path.
+
+MSCGEN_PATH            = 
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output 
+# directory and reference it in all dot files that doxygen generates. This 
+# font does not include all possible unicode characters however, so when you need 
+# these (or just want a differently looking font) you can specify the font name 
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font, 
+# which can be done by putting it in a standard location or by setting the 
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory 
+# containing the font.
+
+DOT_FONTNAME           = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. 
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the 
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a 
+# different font using DOT_FONTNAME you can set the path where dot 
+# can find it using this tag.
+
+DOT_FONTPATH           = 
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
+# doxygen will generate a call dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable call graphs 
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
+# doxygen will generate a caller dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable caller 
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# then doxygen will show the dependencies a directory has on other directories 
+# in a graphical way. The dependency relations are determined by the #include 
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif 
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
+# nodes that will be shown in the graph. If the number of nodes in a graph 
+# becomes larger than this value, doxygen will truncate the graph, which is 
+# visualized by representing a node as a red box. Note that doxygen if the 
+# number of direct children of the root node in a graph is already larger than 
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes 
+# that lay further from the root node will be omitted. Note that setting this 
+# option to 1 or 2 may greatly reduce the computation time needed for large 
+# code bases. Also note that the size of a graph can be further restricted by 
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is disabled by default, because dot on Windows does not 
+# seem to support this out of the box. Warning: Depending on the platform used, 
+# enabling this option may lead to badly anti-aliased labels on the edges of 
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
+# files in one run (i.e. multiple -o and -T options on the command line). This 
+# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Options related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
diff --git a/libs/mangle/LICENSE.txt b/libs/mangle/LICENSE.txt
new file mode 100644
index 0000000000..ccfcc9f220
--- /dev/null
+++ b/libs/mangle/LICENSE.txt
@@ -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.
+
diff --git a/libs/mangle/README.txt b/libs/mangle/README.txt
new file mode 100644
index 0000000000..f4849bebda
--- /dev/null
+++ b/libs/mangle/README.txt
@@ -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.
diff --git a/libs/mangle/input/clients/ogre_input_capture.hpp b/libs/mangle/input/clients/ogre_input_capture.hpp
new file mode 100644
index 0000000000..2e77dc10b1
--- /dev/null
+++ b/libs/mangle/input/clients/ogre_input_capture.hpp
@@ -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
diff --git a/libs/mangle/input/driver.hpp b/libs/mangle/input/driver.hpp
new file mode 100644
index 0000000000..f4ba159c52
--- /dev/null
+++ b/libs/mangle/input/driver.hpp
@@ -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
diff --git a/libs/mangle/input/event.hpp b/libs/mangle/input/event.hpp
new file mode 100644
index 0000000000..dc7b470887
--- /dev/null
+++ b/libs/mangle/input/event.hpp
@@ -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
diff --git a/libs/mangle/input/filters/eventlist.hpp b/libs/mangle/input/filters/eventlist.hpp
new file mode 100644
index 0000000000..b3e2ff8f24
--- /dev/null
+++ b/libs/mangle/input/filters/eventlist.hpp
@@ -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
diff --git a/libs/mangle/input/servers/ois_driver.cpp b/libs/mangle/input/servers/ois_driver.cpp
new file mode 100644
index 0000000000..2071b91ea6
--- /dev/null
+++ b/libs/mangle/input/servers/ois_driver.cpp
@@ -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);
+}
diff --git a/libs/mangle/input/servers/ois_driver.hpp b/libs/mangle/input/servers/ois_driver.hpp
new file mode 100644
index 0000000000..ba780c39e6
--- /dev/null
+++ b/libs/mangle/input/servers/ois_driver.hpp
@@ -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
diff --git a/libs/mangle/input/servers/sdl_driver.cpp b/libs/mangle/input/servers/sdl_driver.cpp
new file mode 100644
index 0000000000..93884a6e6c
--- /dev/null
+++ b/libs/mangle/input/servers/sdl_driver.cpp
@@ -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);
+}
diff --git a/libs/mangle/input/servers/sdl_driver.hpp b/libs/mangle/input/servers/sdl_driver.hpp
new file mode 100644
index 0000000000..b71346cba1
--- /dev/null
+++ b/libs/mangle/input/servers/sdl_driver.hpp
@@ -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
diff --git a/libs/mangle/input/tests/.gitignore b/libs/mangle/input/tests/.gitignore
new file mode 100644
index 0000000000..460c76f00b
--- /dev/null
+++ b/libs/mangle/input/tests/.gitignore
@@ -0,0 +1,2 @@
+*_test
+ogre.cfg
diff --git a/libs/mangle/input/tests/Makefile b/libs/mangle/input/tests/Makefile
new file mode 100644
index 0000000000..8760adfe73
--- /dev/null
+++ b/libs/mangle/input/tests/Makefile
@@ -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
diff --git a/libs/mangle/input/tests/common.cpp b/libs/mangle/input/tests/common.cpp
new file mode 100644
index 0000000000..0c7c76466b
--- /dev/null
+++ b/libs/mangle/input/tests/common.cpp
@@ -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";
+}
diff --git a/libs/mangle/input/tests/evtlist_test.cpp b/libs/mangle/input/tests/evtlist_test.cpp
new file mode 100644
index 0000000000..fbd980cbd9
--- /dev/null
+++ b/libs/mangle/input/tests/evtlist_test.cpp
@@ -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;
+}
diff --git a/libs/mangle/input/tests/ois_driver_test.cpp b/libs/mangle/input/tests/ois_driver_test.cpp
new file mode 100644
index 0000000000..386f24055e
--- /dev/null
+++ b/libs/mangle/input/tests/ois_driver_test.cpp
@@ -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;
+}
diff --git a/libs/mangle/input/tests/output/evtlist_test.out b/libs/mangle/input/tests/output/evtlist_test.out
new file mode 100644
index 0000000000..180dcc58a8
--- /dev/null
+++ b/libs/mangle/input/tests/output/evtlist_test.out
@@ -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
diff --git a/libs/mangle/input/tests/output/ois_driver_test.out b/libs/mangle/input/tests/output/ois_driver_test.out
new file mode 100644
index 0000000000..7d273fd46d
--- /dev/null
+++ b/libs/mangle/input/tests/output/ois_driver_test.out
@@ -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!
diff --git a/libs/mangle/input/tests/output/sdl_driver_test.out b/libs/mangle/input/tests/output/sdl_driver_test.out
new file mode 100644
index 0000000000..2df2e4014e
--- /dev/null
+++ b/libs/mangle/input/tests/output/sdl_driver_test.out
@@ -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!
diff --git a/libs/mangle/input/tests/plugins.cfg b/libs/mangle/input/tests/plugins.cfg
new file mode 100644
index 0000000000..57ec54e1a0
--- /dev/null
+++ b/libs/mangle/input/tests/plugins.cfg
@@ -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
+
+
diff --git a/libs/mangle/input/tests/sdl_driver_test.cpp b/libs/mangle/input/tests/sdl_driver_test.cpp
new file mode 100644
index 0000000000..5db6dbba8f
--- /dev/null
+++ b/libs/mangle/input/tests/sdl_driver_test.cpp
@@ -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;
+}
diff --git a/libs/mangle/input/tests/test.sh b/libs/mangle/input/tests/test.sh
new file mode 100755
index 0000000000..2d07708adc
--- /dev/null
+++ b/libs/mangle/input/tests/test.sh
@@ -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
diff --git a/libs/mangle/rend2d/driver.hpp b/libs/mangle/rend2d/driver.hpp
new file mode 100644
index 0000000000..08a15b0aeb
--- /dev/null
+++ b/libs/mangle/rend2d/driver.hpp
@@ -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
diff --git a/libs/mangle/rend2d/servers/sdl_driver.cpp b/libs/mangle/rend2d/servers/sdl_driver.cpp
new file mode 100644
index 0000000000..aa1ff6c6dc
--- /dev/null
+++ b/libs/mangle/rend2d/servers/sdl_driver.cpp
@@ -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(); }
diff --git a/libs/mangle/rend2d/servers/sdl_driver.hpp b/libs/mangle/rend2d/servers/sdl_driver.hpp
new file mode 100644
index 0000000000..0f205ba34c
--- /dev/null
+++ b/libs/mangle/rend2d/servers/sdl_driver.hpp
@@ -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
diff --git a/libs/mangle/rend2d/servers/sdl_gl_driver.cpp b/libs/mangle/rend2d/servers/sdl_gl_driver.cpp
new file mode 100644
index 0000000000..2bcb1d6777
--- /dev/null
+++ b/libs/mangle/rend2d/servers/sdl_gl_driver.cpp
@@ -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(); }
diff --git a/libs/mangle/rend2d/servers/sdl_gl_driver.hpp b/libs/mangle/rend2d/servers/sdl_gl_driver.hpp
new file mode 100644
index 0000000000..d116e3659b
--- /dev/null
+++ b/libs/mangle/rend2d/servers/sdl_gl_driver.hpp
@@ -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
diff --git a/libs/mangle/rend2d/sprite.hpp b/libs/mangle/rend2d/sprite.hpp
new file mode 100644
index 0000000000..f49da6cb6d
--- /dev/null
+++ b/libs/mangle/rend2d/sprite.hpp
@@ -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
diff --git a/libs/mangle/rend2d/tests/.gitignore b/libs/mangle/rend2d/tests/.gitignore
new file mode 100644
index 0000000000..8144904045
--- /dev/null
+++ b/libs/mangle/rend2d/tests/.gitignore
@@ -0,0 +1 @@
+*_test
diff --git a/libs/mangle/rend2d/tests/Makefile b/libs/mangle/rend2d/tests/Makefile
new file mode 100644
index 0000000000..d430f60a93
--- /dev/null
+++ b/libs/mangle/rend2d/tests/Makefile
@@ -0,0 +1,15 @@
+GCC=g++ -Wall -Werror
+
+all: sdl_test sdl_move_test sdlgl_move_test
+
+sdl_test: sdl_test.cpp
+	$(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image
+
+sdl_move_test: sdl_move_test.cpp ../servers/sdl_driver.cpp
+	$(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image
+
+sdlgl_move_test: sdlgl_move_test.cpp ../servers/sdl_gl_driver.cpp
+	$(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image -lGL
+
+clean:
+	rm *_test
diff --git a/libs/mangle/rend2d/tests/output/sdl_move_test.out b/libs/mangle/rend2d/tests/output/sdl_move_test.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/libs/mangle/rend2d/tests/output/sdl_test.out b/libs/mangle/rend2d/tests/output/sdl_test.out
new file mode 100644
index 0000000000..4528e1a98a
--- /dev/null
+++ b/libs/mangle/rend2d/tests/output/sdl_test.out
@@ -0,0 +1,11 @@
+Loading SDL driver.
+Creating window.
+Current mode: 640x480
+Setting fancy title, cause we like fancy titles.
+Loading tile1-blue.png from file.
+Loading tile1-yellow.png from memory.
+Going bananas.
+Taking a breather.
+WOW DID YOU SEE THAT!?
+Mucking about with the gamma settings
+Done.
diff --git a/libs/mangle/rend2d/tests/output/sdlgl_move_test.out b/libs/mangle/rend2d/tests/output/sdlgl_move_test.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/libs/mangle/rend2d/tests/sdl_move_test.cpp b/libs/mangle/rend2d/tests/sdl_move_test.cpp
new file mode 100644
index 0000000000..bfbca98fa7
--- /dev/null
+++ b/libs/mangle/rend2d/tests/sdl_move_test.cpp
@@ -0,0 +1,30 @@
+#include <iostream>
+#include <fstream>
+
+using namespace std;
+
+#include "../servers/sdl_driver.hpp"
+
+using namespace Mangle::Rend2D;
+
+int main()
+{
+  SDLDriver sdl;
+
+  sdl.setVideoMode(640,480,0,false);
+  sdl.setWindowTitle("Testing 123");
+  Sprite *screen = sdl.getScreen();
+  const char* imgName = "tile1-blue.png";
+  Sprite *image = sdl.loadImage(imgName);
+
+  for(int frames=0; frames<170; frames++)
+    {
+      screen->fill(0);
+      for(int j=0; j<10; j++)
+        for(int i=0; i<25; i++)
+          screen->draw(image, 2*frames+30*j, 20*i);
+      sdl.update();
+      sdl.sleep(10);
+    }
+  return 0;
+}
diff --git a/libs/mangle/rend2d/tests/sdl_test.cpp b/libs/mangle/rend2d/tests/sdl_test.cpp
new file mode 100644
index 0000000000..0355112e61
--- /dev/null
+++ b/libs/mangle/rend2d/tests/sdl_test.cpp
@@ -0,0 +1,65 @@
+#include <iostream>
+#include <fstream>
+
+using namespace std;
+
+#include "../servers/sdl_driver.hpp"
+
+using namespace Mangle::Rend2D;
+
+int main()
+{
+  cout << "Loading SDL driver.\n";
+  SDLDriver sdl;
+
+  cout << "Creating window.\n";
+  sdl.setVideoMode(640,480);
+  cout << "Current mode: " << sdl.width() << "x" << sdl.height() << endl;
+
+  cout << "Setting fancy title, cause we like fancy titles.\n";
+  sdl.setWindowTitle("Chief executive window");
+
+  // Display surface
+  Sprite *screen = sdl.getScreen();
+
+  const char* imgName = "tile1-blue.png";
+  cout << "Loading " << imgName << " from file.\n";
+  Sprite *image = sdl.loadImage(imgName);
+
+  const char* imgName2 = "tile1-yellow.png";
+  cout << "Loading " << imgName2 << " from memory.\n";
+  Sprite *image2;
+  {
+    // This is hard-coded for file sizes below 500 bytes, so obviously
+    // you shouldn't mess with the image files.
+    ifstream file(imgName2, ios::binary);
+    char buf[500];
+    file.read(buf, 500);
+    int size = file.gcount();
+    image2 = sdl.loadImage(buf, size);
+  }
+
+  cout << "Going bananas.\n";
+  for(int i=1; i<20; i++)
+    screen->draw(image, 30*i, 20*i);
+
+  cout << "Taking a breather.\n";
+  sdl.update();
+  for(int i=1; i<20; i++)
+    screen->draw(image2, 30*(20-i), 20*i);
+  sdl.sleep(800);
+  sdl.update();
+  cout << "WOW DID YOU SEE THAT!?\n";
+  sdl.sleep(800);
+
+  cout << "Mucking about with the gamma settings\n";
+  sdl.setGamma(2.0, 0.1, 0.8);
+  sdl.sleep(100);
+  sdl.setGamma(0.6, 2.1, 2.1);
+  sdl.sleep(100);
+  sdl.setGamma(1.6);
+  sdl.sleep(100);
+
+  cout << "Done.\n";
+  return 0;
+}
diff --git a/libs/mangle/rend2d/tests/sdlgl_move_test.cpp b/libs/mangle/rend2d/tests/sdlgl_move_test.cpp
new file mode 100644
index 0000000000..b769ee837d
--- /dev/null
+++ b/libs/mangle/rend2d/tests/sdlgl_move_test.cpp
@@ -0,0 +1,31 @@
+#include <iostream>
+#include <fstream>
+
+using namespace std;
+
+#include "../servers/sdl_gl_driver.hpp"
+
+using namespace Mangle::Rend2D;
+
+int main()
+{
+  SDLGLDriver sdl;
+
+  sdl.setVideoMode(640,480,0,false);
+  sdl.setWindowTitle("Testing 123");
+  Sprite *screen = sdl.getScreen();
+  const char* imgName = "tile1-blue.png";
+  Sprite *image = sdl.loadImage(imgName);
+
+  for(int frames=0; frames<170; frames++)
+    {
+      screen->fill(0);
+      for(int j=0; j<10; j++)
+        for(int i=0; i<25; i++)
+          screen->draw(image, 2*frames+30*j, 20*i);
+      sdl.update();
+      sdl.sleep(5);
+    }
+
+  return 0;
+}
diff --git a/libs/mangle/rend2d/tests/test.sh b/libs/mangle/rend2d/tests/test.sh
new file mode 100755
index 0000000000..2d07708adc
--- /dev/null
+++ b/libs/mangle/rend2d/tests/test.sh
@@ -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
diff --git a/libs/mangle/rend2d/tests/tile1-blue.png b/libs/mangle/rend2d/tests/tile1-blue.png
new file mode 100644
index 0000000000..066e6f8eb9
Binary files /dev/null and b/libs/mangle/rend2d/tests/tile1-blue.png differ
diff --git a/libs/mangle/rend2d/tests/tile1-yellow.png b/libs/mangle/rend2d/tests/tile1-yellow.png
new file mode 100644
index 0000000000..2aaf9015d3
Binary files /dev/null and b/libs/mangle/rend2d/tests/tile1-yellow.png differ
diff --git a/libs/mangle/sound/.gitignore b/libs/mangle/sound/.gitignore
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/libs/mangle/sound/.gitignore
@@ -0,0 +1 @@
+
diff --git a/libs/mangle/sound/clients/ogre_listener_mover.hpp b/libs/mangle/sound/clients/ogre_listener_mover.hpp
new file mode 100644
index 0000000000..74c21db32a
--- /dev/null
+++ b/libs/mangle/sound/clients/ogre_listener_mover.hpp
@@ -0,0 +1,79 @@
+#ifndef MANGLE_SOUND_OGRELISTENERMOVER_H
+#define MANGLE_SOUND_OGRELISTENERMOVER_H
+
+#include <OgreCamera.h>
+#include <assert.h>
+#include "../output.hpp"
+
+namespace Mangle {
+namespace Sound {
+
+  /** This class lets a sound listener (ie. the SoundFactory) track a
+      given camera in Ogre3D. The position and orientation of the
+      listener will be updated to match the camera whenever the camera
+      is moved.
+   */
+  struct OgreListenerMover : Ogre::Camera::Listener
+  {
+    OgreListenerMover(Mangle::Sound::SoundFactoryPtr snd)
+      : soundFact(snd), camera(NULL)
+    {}
+
+    /// Follow a camera. WARNING: This will OVERRIDE any other
+    /// MovableObject::Listener you may have attached to the camera.
+    void followCamera(Ogre::Camera *cam)
+    {
+      camera = cam;
+      camera->addListener(this);
+    }
+
+    void unfollowCamera()
+    {
+        // If the camera is null, this object wasn't following a camera.
+        // It doesn't make sense to call unfollow
+        assert(camera != NULL);
+
+        camera->removeListener(this);
+        camera = NULL;
+    }
+
+  private:
+    Mangle::Sound::SoundFactoryPtr soundFact;
+    Ogre::Camera *camera;
+    Ogre::Vector3 pos, dir, up;
+
+    /// From Camera::Listener. This is called once per
+    /// frame. Unfortunately, Ogre doesn't allow us to be notified
+    /// only when the camera itself has moved, so we must poll every
+    /// frame.
+    void cameraPreRenderScene(Ogre::Camera *cam)
+    {
+      assert(cam == camera);
+
+      Ogre::Vector3 nPos, nDir, nUp;
+
+      nPos = camera->getRealPosition();
+      nDir = camera->getRealDirection();
+      nUp  = camera->getRealUp();
+
+      // Don't bother the sound system needlessly
+      if(nDir != dir || nPos != pos || nUp != up)
+        {
+          pos = nPos;
+          dir = nDir;
+          up  = nUp;
+
+          soundFact->setListenerPos(pos.x, pos.y, pos.z,
+                                    dir.x, dir.y, dir.z,
+                                    up.x, up.y, up.z);
+        }
+    }
+
+    void cameraDestroyed(Ogre::Camera *cam)
+    {
+      assert(cam == camera);
+      camera = NULL;
+    }
+  };
+}}
+#endif
diff --git a/libs/mangle/sound/clients/ogre_output_updater.hpp b/libs/mangle/sound/clients/ogre_output_updater.hpp
new file mode 100644
index 0000000000..b73168c759
--- /dev/null
+++ b/libs/mangle/sound/clients/ogre_output_updater.hpp
@@ -0,0 +1,31 @@
+#ifndef MANGLE_SOUND_OGREUPDATER_H
+#define MANGLE_SOUND_OGREUPDATER_H
+
+/*
+  This Ogre FrameListener calls update on a SoundFactory
+ */
+
+#include <OgreFrameListener.h>
+#include "../output.hpp"
+#include <assert.h>
+
+namespace Mangle {
+namespace Sound {
+
+  struct OgreOutputUpdater : Ogre::FrameListener
+  {
+    Mangle::Sound::SoundFactoryPtr driver;
+
+    OgreOutputUpdater(Mangle::Sound::SoundFactoryPtr drv)
+      : driver(drv)
+    { assert(drv->needsUpdate); }
+
+    bool frameStarted(const Ogre::FrameEvent &evt)
+    {
+      driver->update();
+      return true;
+    }
+  };
+}}
+
+#endif
diff --git a/libs/mangle/sound/filters/input_filter.hpp b/libs/mangle/sound/filters/input_filter.hpp
new file mode 100644
index 0000000000..00ee187660
--- /dev/null
+++ b/libs/mangle/sound/filters/input_filter.hpp
@@ -0,0 +1,68 @@
+#ifndef MANGLE_INPUT_FILTER_H
+#define MANGLE_INPUT_FILTER_H
+
+#include "../output.hpp"
+
+#include <assert.h>
+
+namespace Mangle {
+namespace Sound {
+
+/**
+   @brief This filter class adds file loading capabilities to a
+   Sound::SoundFactory class, by associating a SampleSourceLoader with
+   it.
+
+   The class takes an existing SoundFactory able to load streams, and
+   associates a SampleSourceLoader with it. The combined class is able
+   to load files directly.  */
+class InputFilter : public SoundFactory
+{
+ protected:
+  SoundFactoryPtr snd;
+  SampleSourceLoaderPtr inp;
+
+ public:
+  /// Empty constructor
+  InputFilter() {}
+
+  /// Assign an input manager and a sound manager to this object
+  InputFilter(SoundFactoryPtr _snd, SampleSourceLoaderPtr _inp)
+    { set(_snd, _inp); }
+
+  /// Assign an input manager and a sound manager to this object
+  void set(SoundFactoryPtr _snd, SampleSourceLoaderPtr _inp)
+  {
+    inp = _inp;
+    snd = _snd;
+
+    // Set capabilities
+    needsUpdate = snd->needsUpdate;
+    has3D = snd->has3D;
+    canLoadStream = inp->canLoadStream;
+
+    // Both these should be true, or the use of this class is pretty
+    // pointless
+    canLoadSource = snd->canLoadSource;
+    canLoadFile = inp->canLoadFile;
+    assert(canLoadSource && canLoadFile);
+  }
+
+  virtual SoundPtr load(const std::string &file)
+  { return loadRaw(inp->load(file)); }
+
+  virtual SoundPtr load(Stream::StreamPtr input)
+  { return loadRaw(inp->load(input)); }
+
+  virtual SoundPtr loadRaw(SampleSourcePtr input)
+  { return snd->loadRaw(input); }
+
+  virtual void update() { snd->update(); }
+  virtual void setListenerPos(float x, float y, float z,
+                              float fx, float fy, float fz,
+                              float ux, float uy, float uz)
+    { snd->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz); }
+};
+
+}}
+#endif
diff --git a/libs/mangle/sound/filters/openal_audiere.hpp b/libs/mangle/sound/filters/openal_audiere.hpp
new file mode 100644
index 0000000000..5b9b518249
--- /dev/null
+++ b/libs/mangle/sound/filters/openal_audiere.hpp
@@ -0,0 +1,24 @@
+#ifndef MANGLE_AUDIERE_OPENAL_H
+#define MANGLE_AUDIERE_OPENAL_H
+
+#include "input_filter.hpp"
+#include "../sources/audiere_source.hpp"
+#include "../outputs/openal_out.hpp"
+
+namespace Mangle {
+namespace Sound {
+
+/// A InputFilter that adds audiere decoding to OpenAL. Audiere has
+/// it's own output, but OpenAL sports 3D and other advanced features.
+class OpenAL_Audiere_Factory : public InputFilter
+{
+ public:
+  OpenAL_Audiere_Factory()
+    {
+      set(SoundFactoryPtr(new OpenAL_Factory),
+          SampleSourceLoaderPtr(new AudiereLoader));
+    }
+};
+
+}}
+#endif
diff --git a/libs/mangle/sound/filters/openal_ffmpeg.hpp b/libs/mangle/sound/filters/openal_ffmpeg.hpp
new file mode 100644
index 0000000000..42c76af0cd
--- /dev/null
+++ b/libs/mangle/sound/filters/openal_ffmpeg.hpp
@@ -0,0 +1,23 @@
+#ifndef MANGLE_FFMPEG_OPENAL_H
+#define MANGLE_FFMPEG_OPENAL_H
+
+#include "input_filter.hpp"
+#include "../sources/ffmpeg_source.hpp"
+#include "../outputs/openal_out.hpp"
+
+namespace Mangle {
+namespace Sound {
+
+/// A InputFilter that adds ffmpeg decoding to OpenAL.
+class OpenAL_FFMpeg_Factory : public InputFilter
+{
+ public:
+  OpenAL_FFMpeg_Factory()
+    {
+      set(SoundFactoryPtr(new OpenAL_Factory),
+          SampleSourceLoaderPtr(new FFMpegLoader));
+    }
+};
+
+}}
+#endif
diff --git a/libs/mangle/sound/filters/openal_mpg123.hpp b/libs/mangle/sound/filters/openal_mpg123.hpp
new file mode 100644
index 0000000000..bfd926c0bb
--- /dev/null
+++ b/libs/mangle/sound/filters/openal_mpg123.hpp
@@ -0,0 +1,24 @@
+#ifndef MANGLE_MPG123_OPENAL_H
+#define MANGLE_MPG123_OPENAL_H
+
+#include "input_filter.hpp"
+#include "../sources/mpg123_source.hpp"
+#include "../outputs/openal_out.hpp"
+
+namespace Mangle {
+namespace Sound {
+
+/// A InputFilter that adds mpg123 decoding to OpenAL. Only supports
+/// MP3 files.
+class OpenAL_Mpg123_Factory : public InputFilter
+{
+ public:
+  OpenAL_Mpg123_Factory()
+    {
+      set(SoundFactoryPtr(new OpenAL_Factory),
+          SampleSourceLoaderPtr(new Mpg123Loader));
+    }
+};
+
+}}
+#endif
diff --git a/libs/mangle/sound/filters/openal_sndfile.hpp b/libs/mangle/sound/filters/openal_sndfile.hpp
new file mode 100644
index 0000000000..fd7e780259
--- /dev/null
+++ b/libs/mangle/sound/filters/openal_sndfile.hpp
@@ -0,0 +1,24 @@
+#ifndef MANGLE_SNDFILE_OPENAL_H
+#define MANGLE_SNDFILE_OPENAL_H
+
+#include "input_filter.hpp"
+#include "../sources/libsndfile.hpp"
+#include "../outputs/openal_out.hpp"
+
+namespace Mangle {
+namespace Sound {
+
+/// A InputFilter that adds libsnd decoding to OpenAL. libsndfile
+/// supports most formats except MP3.
+class OpenAL_SndFile_Factory : public InputFilter
+{
+ public:
+  OpenAL_SndFile_Factory()
+    {
+      set(SoundFactoryPtr(new OpenAL_Factory),
+          SampleSourceLoaderPtr(new SndFileLoader));
+    }
+};
+
+}}
+#endif
diff --git a/libs/mangle/sound/filters/openal_sndfile_mpg123.hpp b/libs/mangle/sound/filters/openal_sndfile_mpg123.hpp
new file mode 100644
index 0000000000..6e5db4d0e1
--- /dev/null
+++ b/libs/mangle/sound/filters/openal_sndfile_mpg123.hpp
@@ -0,0 +1,33 @@
+#ifndef MANGLE_SNDFILE_MPG123_OPENAL_H
+#define MANGLE_SNDFILE_MPG123_OPENAL_H
+
+#include "input_filter.hpp"
+#include "source_splicer.hpp"
+#include "../sources/mpg123_source.hpp"
+#include "../sources/libsndfile.hpp"
+#include "../outputs/openal_out.hpp"
+
+namespace Mangle {
+namespace Sound {
+
+/// A InputFilter that uses OpenAL for output, and mpg123 (for MP3) +
+/// libsndfile (for everything else) to decode files. Can only load
+/// from the file system, and uses the file name to differentiate
+/// between mp3 and non-mp3 types.
+class OpenAL_SndFile_Mpg123_Factory : public InputFilter
+{
+ public:
+  OpenAL_SndFile_Mpg123_Factory()
+    {
+      SourceSplicer *splice = new SourceSplicer;
+
+      splice->add("mp3", SampleSourceLoaderPtr(new Mpg123Loader));
+      splice->setDefault(SampleSourceLoaderPtr(new SndFileLoader));
+
+      set(SoundFactoryPtr(new OpenAL_Factory),
+          SampleSourceLoaderPtr(splice));
+    }
+};
+
+}}
+#endif
diff --git a/libs/mangle/sound/filters/openal_various.hpp b/libs/mangle/sound/filters/openal_various.hpp
new file mode 100644
index 0000000000..945b3dabda
--- /dev/null
+++ b/libs/mangle/sound/filters/openal_various.hpp
@@ -0,0 +1,39 @@
+#ifndef MANGLE_VARIOUS_OPENAL_H
+#define MANGLE_VARIOUS_OPENAL_H
+
+#include "input_filter.hpp"
+#include "source_splicer.hpp"
+#include "../sources/mpg123_source.hpp"
+#include "../sources/wav_source.hpp"
+#include "../outputs/openal_out.hpp"
+
+namespace Mangle {
+namespace Sound {
+
+/** A InputFilter that uses OpenAL for output, and load input from
+    various individual sources, depending on file extension. Currently
+    supports:
+
+      MP3: mpg123
+      WAV: custom wav loader (PCM only)
+
+    This could be an alternative to using eg. 3rd party decoder
+    libraries like libsndfile.
+ */
+class OpenAL_Various_Factory : public InputFilter
+{
+ public:
+  OpenAL_Various_Factory()
+    {
+      SourceSplicer *splice = new SourceSplicer;
+
+      splice->add("mp3", SampleSourceLoaderPtr(new Mpg123Loader));
+      splice->add("wav", SampleSourceLoaderPtr(new WavLoader));
+
+      set(SoundFactoryPtr(new OpenAL_Factory),
+          SampleSourceLoaderPtr(splice));
+    }
+};
+
+}}
+#endif
diff --git a/libs/mangle/sound/filters/pure_filter.hpp b/libs/mangle/sound/filters/pure_filter.hpp
new file mode 100644
index 0000000000..fc5e625744
--- /dev/null
+++ b/libs/mangle/sound/filters/pure_filter.hpp
@@ -0,0 +1,73 @@
+#ifndef MANGLE_SOUND_OUTPUT_PUREFILTER_H
+#define MANGLE_SOUND_OUTPUT_PUREFILTER_H
+
+#include "../output.hpp"
+
+namespace Mangle
+{
+  namespace Sound
+  {
+    // For use in writing other filters
+    class SoundFilter : public Sound
+    {
+    protected:
+      SoundPtr client;
+
+    public:
+      SoundFilter(SoundPtr c) : client(c) {}
+      void play() { client->play(); }
+      void stop() { client->stop(); }
+      void pause() { client->pause(); }
+      bool isPlaying() const { return client->isPlaying(); }
+      void setVolume(float f) { client->setVolume(f); }
+      void setPan(float f) { client->setPan(f); }
+      void setPos(float x, float y, float z)
+      { client->setPos(x,y,z); }
+      void setPitch(float p) { client->setPitch(p); }
+      void setRepeat(bool b) { client->setRepeat(b); }
+      void setRange(float a, float b=0, float c=0)
+      { client->setRange(a,b,c); }
+      void setStreaming(bool b) { client->setStreaming(b); }
+      void setRelative(bool b) { client->setRelative(b); }
+
+      // The clone() function is not implemented here, as you will
+      // almost certainly want to override it yourself
+    };
+
+    class FactoryFilter : public SoundFactory
+    {
+    protected:
+      SoundFactoryPtr client;
+
+    public:
+      FactoryFilter(SoundFactoryPtr c) : client(c)
+      {
+        needsUpdate = client->needsUpdate;
+        has3D = client->has3D;
+        canLoadFile = client->canLoadFile;
+        canLoadStream = client->canLoadStream;
+        canLoadSource = client->canLoadSource;
+      }
+
+      SoundPtr loadRaw(SampleSourcePtr input)
+      { return client->loadRaw(input); }
+
+      SoundPtr load(Stream::StreamPtr input)
+      { return client->load(input); }
+
+      SoundPtr load(const std::string &file)
+      { return client->load(file); }
+
+      void update()
+      { client->update(); }
+
+      void setListenerPos(float x, float y, float z,
+                          float fx, float fy, float fz,
+                          float ux, float uy, float uz)
+      {
+        client->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz);
+      }
+    };
+  }
+}
+#endif
diff --git a/libs/mangle/sound/filters/source_splicer.hpp b/libs/mangle/sound/filters/source_splicer.hpp
new file mode 100644
index 0000000000..9c76230865
--- /dev/null
+++ b/libs/mangle/sound/filters/source_splicer.hpp
@@ -0,0 +1,90 @@
+#ifndef MANGLE_SOUND_SOURCE_SPLICE_H
+#define MANGLE_SOUND_SOURCE_SPLICE_H
+
+#include "../source.hpp"
+#include <stdexcept>
+#include <string>
+#include <list>
+#include <assert.h>
+
+namespace Mangle
+{
+  namespace Sound
+  {
+    class SourceSplicer : public SampleSourceLoader
+    {
+      struct SourceType
+      {
+        std::string type;
+        SampleSourceLoaderPtr loader;
+      };
+
+      typedef std::list<SourceType> TypeList;
+      TypeList list;
+      SampleSourceLoaderPtr catchAll;
+
+      static bool isMatch(char a, char b)
+      {
+        if(a >= 'A' && a <= 'Z')
+          a += 'a' - 'A';
+        if(b >= 'A' && b <= 'Z')
+          b += 'a' - 'A';
+        return a == b;
+      }
+
+    public:
+      SourceSplicer()
+      {
+        canLoadStream = false;
+        canLoadFile = true;
+      }
+
+      void add(const std::string &type, SampleSourceLoaderPtr fact)
+      {
+        SourceType tp;
+        tp.type = type;
+        tp.loader = fact;
+        list.push_back(tp);
+      }
+
+      void setDefault(SampleSourceLoaderPtr def)
+      {
+        catchAll = def;
+      }
+
+      SampleSourcePtr load(const std::string &file)
+      {
+        // Search the list for this file type.
+        for(TypeList::iterator it = list.begin();
+            it != list.end(); it++)
+          {
+            const std::string &t = it->type;
+
+            int diff = file.size() - t.size();
+            if(diff < 0) continue;
+
+            bool match = true;
+            for(unsigned i=0; i<t.size(); i++)
+              if(!isMatch(t[i], file[i+diff]))
+                {
+                  match = false;
+                  break;
+                }
+
+            // Got something! We're done.
+            if(match)
+              return it->loader->load(file);
+          }
+        // If not found, use the catch-all
+        if(catchAll)
+          return catchAll->load(file);
+
+        throw std::runtime_error("No handler for sound file " + file);
+      }
+
+      SampleSourcePtr load(Stream::StreamPtr input) { assert(0); }
+    };
+  }
+}
+
+#endif
diff --git a/libs/mangle/sound/output.hpp b/libs/mangle/sound/output.hpp
new file mode 100644
index 0000000000..e30bf21e27
--- /dev/null
+++ b/libs/mangle/sound/output.hpp
@@ -0,0 +1,183 @@
+#ifndef MANGLE_SOUND_OUTPUT_H
+#define MANGLE_SOUND_OUTPUT_H
+
+#include <string>
+#include <assert.h>
+
+#include "source.hpp"
+#include "../stream/stream.hpp"
+
+namespace Mangle {
+namespace Sound {
+
+/// Abstract interface for a single playable sound
+/** This class represents one sound outlet, which may be played,
+    stopped, paused and so on.
+
+    Sound instances are created from the SoundFactory class. Sounds
+    may be connected to a SampleSource or read directly from a file,
+    and they may support 3d sounds, looping and other features
+    depending on the capabilities of the backend system.
+
+    To create multiple instances of one sound, it is recommended to
+    'clone' an existing instance instead of reloading it from
+    file. Cloned sounds will often (depending on the back-end) use
+    less memory due to shared buffers.
+*/
+class Sound;
+typedef boost::shared_ptr<Sound> SoundPtr;
+typedef boost::weak_ptr  <Sound> WSoundPtr;
+
+class Sound
+{
+ public:
+  /// Play or resume the sound
+  virtual void play() = 0;
+
+  /// Stop the sound
+  virtual void stop() = 0;
+
+  /// Pause the sound, may be resumed later
+  virtual void pause() = 0;
+
+  /// Check if the sound is still playing
+  virtual bool isPlaying() const = 0;
+
+  /// Set the volume. The parameter must be between 0.0 and 1.0.
+  virtual void setVolume(float) = 0;
+
+  /// Set left/right pan. -1.0 is left, 0.0 is center and 1.0 is right.
+  virtual void setPan(float) = 0;
+
+  /// Set pitch (1.0 is normal speed)
+  virtual void setPitch(float) = 0;
+
+  /// Set range factors for 3D sounds. The meaning of the fields
+  /// depend on implementation.
+  virtual void setRange(float a, float b=0.0, float c=0.0) = 0;
+
+  /// Set the position. May not work with all backends.
+  virtual void setPos(float x, float y, float z) = 0;
+
+  /// Set loop mode
+  virtual void setRepeat(bool) = 0;
+
+  /// If set to true the sound will not be affected by player movement
+  virtual void setRelative(bool) = 0;
+
+  /// Set streaming mode.
+  /** This may be used by implementations to optimize for very large
+      files. If streaming mode is off (default), most implementations
+      will load the entire file into memory before starting playback.
+   */
+  virtual void setStreaming(bool) = 0;
+
+  /// Create a new instance of this sound.
+  /** Playback status is not cloned, only the sound data
+      itself. Back-ends can use this as a means of sharing data and
+      saving memory. */
+  virtual SoundPtr clone() = 0;
+
+  /// Virtual destructor
+  virtual ~Sound() {}
+};
+
+/// Factory interface for creating Sound objects
+/** The SoundFactory is the main entry point to a given sound output
+    system. It is used to create Sound objects, which may be connected
+    to a sound file or stream, and which may be individually played,
+    paused, and so on.
+
+    The class also contains a set of public bools which describe the
+    capabilities the particular system. These should be set by
+    implementations (base classes) in their respective constructors.
+ */
+class SoundFactory
+{
+ public:
+  /// Virtual destructor
+  virtual ~SoundFactory() {}
+
+  /** @brief If set to true, you should call update() regularly (every frame
+      or so) on this sound manager. If false, update() should not be
+      called.
+  */
+  bool needsUpdate;
+
+  /** @brief true if 3D functions are available. If false, all use of
+      3D sounds and calls to setPos / setListenerPos will result in
+      undefined behavior.
+  */
+  bool has3D;
+
+  /// true if we can load sounds directly from file (containing encoded data)
+  bool canLoadFile;
+
+  /// If true, we can lound sound files from a Stream (containing encoded data)
+  bool canLoadStream;
+
+  /// true if we can load sounds from a SampleSource (containing raw data)
+  bool canLoadSource;
+
+  /**
+     @brief Load a sound from a sample source. Only valid if
+     canLoadSource is true.
+
+     This function loads a sound from a given stream as defined by
+     SampleSource.
+
+     @param input the input source
+     @param stream true if the file should be streamed.
+            Implementations may use this for optimizing playback of
+            large files, but they are not required to.
+     @return a new Sound object
+  */
+  virtual SoundPtr loadRaw(SampleSourcePtr input) = 0;
+
+  /**
+     @brief Load a sound file from stream. Only valid if canLoadStream
+     is true.
+
+     @param input audio file stream
+     @param stream true if the file should be streamed
+     @see load(InputSource*,bool)
+  */
+  virtual SoundPtr load(Stream::StreamPtr input) = 0;
+
+  /**
+     @brief Load a sound directly from file. Only valid if canLoadFile
+     is true.
+
+     @param file filename
+     @param stream true if the file should be streamed
+     @see load(InputSource*,bool)
+  */
+  virtual SoundPtr load(const std::string &file) = 0;
+
+  /// Call this every frame if needsUpdate is true
+  /**
+     This should be called regularly (about every frame in a normal
+     game setting.) Implementions may use this for filling streaming
+     buffers and similar tasks. Implementations that do not need this
+     should set needsUpdate to false.
+  */
+  virtual void update() { assert(0); }
+
+  /// Set listener position (coordinates, front and up vectors)
+  /**
+     Only valid if has3D is true.
+
+     @param x,y,z listener position
+     @param fx,fy,fz listener's looking direction
+     @param ux,uy,uz listener's up direction
+   */
+  virtual void setListenerPos(float x, float y, float z,
+                              float fx, float fy, float fz,
+                              float ux, float uy, float uz) = 0;
+};
+
+typedef boost::shared_ptr<SoundFactory> SoundFactoryPtr;
+
+}} // Namespaces
+
+#endif
diff --git a/libs/mangle/sound/outputs/openal_out.cpp b/libs/mangle/sound/outputs/openal_out.cpp
new file mode 100644
index 0000000000..2056b4f602
--- /dev/null
+++ b/libs/mangle/sound/outputs/openal_out.cpp
@@ -0,0 +1,500 @@
+#include "openal_out.hpp"
+#include <assert.h>
+#include <stdexcept>
+
+#include "../../stream/filters/buffer_stream.hpp"
+
+#ifdef _WIN32
+#include <al.h>
+#include <alc.h>
+#elif defined(__APPLE__)
+#include <OpenAL/alc.h>
+#include <OpenAL/al.h>
+#else
+#include <AL/al.h>
+#include <AL/alc.h>
+#endif
+
+using namespace Mangle::Sound;
+
+// ---- Helper functions and classes ----
+
+// Static buffer used to shuffle sound data from the input into
+// OpenAL. The data is only stored temporarily and then immediately
+// shuffled off to the library. This is not thread safe, but it works
+// fine with multiple sounds in one thread. It could be made thread
+// safe simply by using thread local storage.
+const size_t BSIZE = 32*1024;
+static char tmp_buffer[BSIZE];
+
+// Number of buffers used (per sound) for streaming sounds. Each
+// buffer is of size BSIZE. Increasing this will make streaming sounds
+// more fault tolerant against temporary lapses in call to update(),
+// but will also increase memory usage.
+// This was changed from 4 to 150 for an estimated 30 seconds tolerance.
+// At some point we should replace it with a more multithreading-ish
+// solution.
+const int STREAM_BUF_NUM = 150;
+
+static void fail(const std::string &msg)
+{ throw std::runtime_error("OpenAL exception: " + msg); }
+
+/*
+  Check for AL error. Since we're always calling this with string
+  literals, and it only makes sense to optimize for the non-error
+  case, the parameter is const char* rather than std::string.
+
+  This way we don't force the compiler to create a string object each
+  time we're called (since the string is never used unless there's an
+  error), although a good compiler might have optimized that away in
+  any case.
+ */
+static void checkALError(const char *where)
+{
+  ALenum err = alGetError();
+  if(err != AL_NO_ERROR)
+    {
+      std::string msg = where;
+
+      const ALchar* errmsg = alGetString(err);
+      if(errmsg)
+        fail("\"" + std::string(alGetString(err)) + "\" while " + msg);
+      else
+        fail("non-specified error while " + msg + " (did you forget to initialize OpenAL?)");
+    }
+}
+
+static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate)
+{
+  boost::int32_t rate_, ch, bits;
+  inp->getInfo(&rate_, &ch, &bits);
+  rate = rate_;
+
+  fmt = 0;
+
+  if(bits == 8)
+    {
+      if(ch == 1) fmt = AL_FORMAT_MONO8;
+      if(ch == 2) fmt = AL_FORMAT_STEREO8;
+      if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
+        {
+          if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD8");
+          if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN8");
+        }
+    }
+  if(bits == 16)
+    {
+      if(ch == 1) fmt = AL_FORMAT_MONO16;
+      if(ch == 2) fmt = AL_FORMAT_STEREO16;
+      if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16");
+      if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
+        {
+          if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16");
+          if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN16");
+        }
+    }
+
+  if(fmt == 0)
+    fail("Unsupported input format");
+}
+
+/// OpenAL sound output
+class Mangle::Sound::OpenAL_Sound : public Sound
+{
+  ALuint inst;
+
+  // Buffers. Only the first is used for non-streaming sounds.
+  ALuint bufferID[STREAM_BUF_NUM];
+
+  // Number of buffers used
+  int bufNum;
+
+  // Parameters used for filling buffers
+  int fmt, rate;
+
+  // Poor mans reference counting. Might improve this later. When
+  // NULL, the buffer has not been set up yet.
+  int *refCnt;
+
+  bool streaming;
+
+  // Input stream
+  SampleSourcePtr input;
+
+  OpenAL_Factory *owner;
+  bool ownerAlive;
+
+  // Used for streamed sound list
+  OpenAL_Sound *next, *prev;
+
+  void setupBuffer();
+
+  // Fill data into the given buffer and queue it, if there is any
+  // data left to queue. Assumes the buffer is already unqueued, if
+  // necessary.
+  void queueBuffer(ALuint buf)
+  {
+    // If there is no more data, do nothing
+    if(!input) return;
+    if(input->eof())
+      {
+        input.reset();
+        return;
+      }
+
+    // Get some new data
+    size_t bytes = input->read(tmp_buffer, BSIZE);
+    if(bytes == 0)
+      {
+        input.reset();
+        return;
+      }
+
+    // Move data into the OpenAL buffer
+    alBufferData(buf, fmt, tmp_buffer, bytes, rate);
+    // Queue it
+    alSourceQueueBuffers(inst, 1, &buf);
+    checkALError("Queueing buffer data");
+  }
+
+ public:
+  /// Read samples from the given input buffer
+  OpenAL_Sound(SampleSourcePtr input, OpenAL_Factory *fact);
+
+  /// Play an existing buffer, with a given ref counter. Used
+  /// internally for cloning.
+  OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact);
+
+  ~OpenAL_Sound();
+
+  // Must be called regularly on streamed sounds
+  void update()
+  {
+    if(!streaming) return;
+    if(!input) return;
+
+    // Get the number of processed buffers
+    ALint count;
+    alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count);
+    checkALError("getting number of unprocessed buffers");
+
+    for(int i=0; i<count; i++)
+      {
+        ALuint buf;
+        // Unqueue one of the processed buffer
+        alSourceUnqueueBuffers(inst, 1, &buf);
+
+        // Then reload it with data (if any) and queue it up
+        queueBuffer(buf);
+      }
+  }
+
+  void play();
+  void stop();
+  void pause();
+  bool isPlaying() const;
+  void setVolume(float);
+  void setPos(float x, float y, float z);
+  void setPitch(float);
+  void setRepeat(bool);
+  void setRelative(bool);
+
+  void notifyOwnerDeath()
+  { ownerAlive = false; }
+
+  // We can enable streaming, but never disable it.
+  void setStreaming(bool s)
+  { if(s) streaming = true; }
+
+  SoundPtr clone();
+
+  // a = AL_REFERENCE_DISTANCE
+  // b = AL_MAX_DISTANCE
+  // c = ignored
+  void setRange(float a, float b=0.0, float c=0.0);
+
+  /// Not implemented
+  void setPan(float) {}
+};
+
+// ---- OpenAL_Factory ----
+
+SoundPtr OpenAL_Factory::loadRaw(SampleSourcePtr input)
+{
+  return SoundPtr(new OpenAL_Sound(input, this));
+}
+
+void OpenAL_Factory::setListenerPos(float x, float y, float z,
+                                    float fx, float fy, float fz,
+                                    float ux, float uy, float uz)
+{
+  ALfloat orient[6];
+  orient[0] = fx;
+  orient[1] = fy;
+  orient[2] = fz;
+  orient[3] = ux;
+  orient[4] = uy;
+  orient[5] = uz;
+  alListener3f(AL_POSITION, x, y, z);
+  alListenerfv(AL_ORIENTATION, orient);
+}
+
+OpenAL_Factory::OpenAL_Factory(bool doSetup)
+  : device(NULL), context(NULL), didSetup(doSetup)
+{
+  needsUpdate = true;
+  has3D = true;
+  canLoadFile = false;
+  canLoadStream = false;
+  canLoadSource = true;
+
+  ALCdevice *Device;
+  ALCcontext *Context;
+
+  if(doSetup)
+    {
+      // Set up sound system
+      Device = alcOpenDevice(NULL);
+      Context = alcCreateContext(Device, NULL);
+
+      if(!Device || !Context)
+        fail("Failed to initialize context or device");
+
+      alcMakeContextCurrent(Context);
+
+      device = Device;
+      context = Context;
+      alDistanceModel(AL_LINEAR_DISTANCE);
+    }
+}
+
+void OpenAL_Factory::update()
+{
+  // Loop through all streaming sounds and update them
+  StreamList::iterator it = streaming.begin();
+  for(;it != streaming.end(); it++)
+    (*it)->update();
+}
+
+void OpenAL_Factory::notifyStreaming(OpenAL_Sound *snd)
+{
+  // Add the sound to the streaming list
+  streaming.push_back(snd);
+}
+
+void OpenAL_Factory::notifyDelete(OpenAL_Sound *snd)
+{
+  // Remove the sound from the stream list
+  streaming.remove(snd);
+}
+
+OpenAL_Factory::~OpenAL_Factory()
+{
+  // Notify remaining streamed sounds that we're dying
+  StreamList::iterator it = streaming.begin();
+  for(;it != streaming.end(); it++)
+    (*it)->notifyOwnerDeath();
+
+  // Deinitialize sound system
+  if(didSetup)
+    {
+      alcMakeContextCurrent(NULL);
+      if(context) alcDestroyContext((ALCcontext*)context);
+      if(device) alcCloseDevice((ALCdevice*)device);
+    }
+}
+
+// ---- OpenAL_Sound ----
+
+void OpenAL_Sound::play()
+{
+  setupBuffer();
+  alSourcePlay(inst);
+  checkALError("starting playback");
+}
+
+void OpenAL_Sound::stop()
+{
+  alSourceStop(inst);
+  checkALError("stopping");
+}
+
+void OpenAL_Sound::pause()
+{
+  alSourcePause(inst);
+  checkALError("pausing");
+}
+
+bool OpenAL_Sound::isPlaying() const
+{
+  ALint state;
+  alGetSourcei(inst, AL_SOURCE_STATE, &state);
+
+  return state == AL_PLAYING;
+}
+
+void OpenAL_Sound::setVolume(float volume)
+{
+  if(volume > 1.0) volume = 1.0;
+  if(volume < 0.0) volume = 0.0;
+  alSourcef(inst, AL_GAIN, volume);
+  checkALError("setting volume");
+}
+
+void OpenAL_Sound::setRange(float a, float b, float)
+{
+  alSourcef(inst, AL_REFERENCE_DISTANCE, a);
+  alSourcef(inst, AL_MAX_DISTANCE, b);
+  checkALError("setting sound ranges");
+}
+
+void OpenAL_Sound::setPos(float x, float y, float z)
+{
+  alSource3f(inst, AL_POSITION, x, y, z);
+  checkALError("setting position");
+}
+
+void OpenAL_Sound::setPitch(float pitch)
+{
+  alSourcef(inst, AL_PITCH, pitch);
+  checkALError("setting pitch");
+}
+
+void OpenAL_Sound::setRepeat(bool rep)
+{
+  alSourcei(inst, AL_LOOPING, rep?AL_TRUE:AL_FALSE);
+}
+
+void OpenAL_Sound::setRelative(bool rel)
+{
+  alSourcei(inst, AL_SOURCE_RELATIVE, rel?AL_TRUE:AL_FALSE);
+  checkALError("setting relative");
+}
+
+SoundPtr OpenAL_Sound::clone()
+{
+  setupBuffer();
+  assert(!streaming && "cloning streamed sounds not supported");
+  return SoundPtr(new OpenAL_Sound(bufferID[0], refCnt, owner));
+}
+
+// Constructor used for cloned sounds
+OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact)
+  : refCnt(ref), streaming(false), owner(fact), ownerAlive(false)
+{
+  // Increase the reference count
+  assert(ref != NULL);
+  (*refCnt)++;
+
+  // Set up buffer
+  bufferID[0] = buf;
+  bufNum = 1;
+
+  // Create a source
+  alGenSources(1, &inst);
+  checkALError("creating instance (clone)");
+  alSourcei(inst, AL_BUFFER, bufferID[0]);
+  checkALError("assigning buffer (clone)");
+}
+
+// Constructor used for original (non-cloned) sounds
+OpenAL_Sound::OpenAL_Sound(SampleSourcePtr _input, OpenAL_Factory *fact)
+  : refCnt(NULL), streaming(false), input(_input), owner(fact), ownerAlive(false)
+{
+  // Create a source
+  alGenSources(1, &inst);
+  checkALError("creating source");
+
+  // By default, the sound starts out in a buffer-less mode. We don't
+  // create a buffer until the sound is played. This gives the user
+  // the chance to call setStreaming(true) first.
+}
+
+void OpenAL_Sound::setupBuffer()
+{
+  if(refCnt != NULL) return;
+
+  assert(input);
+
+  // Get the format
+  getALFormat(input, fmt, rate);
+
+  // Create a cheap reference counter for the buffer
+  refCnt = new int;
+  *refCnt = 1;
+
+  if(streaming) bufNum = STREAM_BUF_NUM;
+  else bufNum = 1;
+
+  // Set up the OpenAL buffer(s)
+  alGenBuffers(bufNum, bufferID);
+  checkALError("generating buffer(s)");
+  assert(bufferID[0] != 0);
+
+  // STREAMING.
+  if(streaming)
+    {
+      // Just queue all the buffers with data and exit. queueBuffer()
+      // will work correctly also in the case where there is not
+      // enough data to fill all the buffers.
+      for(int i=0; i<bufNum; i++)
+        queueBuffer(bufferID[i]);
+
+      // Notify the manager what we're doing
+      owner->notifyStreaming(this);
+      ownerAlive = true;
+
+      return;
+    }
+
+  // NON-STREAMING. We have to load all the data and shove it into the
+  // buffer.
+
+  // Does the stream support pointer operations?
+  if(input->hasPtr)
+    {
+      // If so, we can read the data directly from the stream
+      alBufferData(bufferID[0], fmt, input->getPtr(), input->size(), rate);
+    }
+  else
+    {
+      // Read the entire stream into a temporary buffer first
+      Mangle::Stream::BufferStream buf(input, 128*1024);
+
+      // Then copy that into OpenAL
+      alBufferData(bufferID[0], fmt, buf.getPtr(), buf.size(), rate);
+    }
+  checkALError("loading sound data");
+
+  // We're done with the input stream, release the pointer
+  input.reset();
+
+  alSourcei(inst, AL_BUFFER, bufferID[0]);
+  checkALError("assigning buffer");
+}
+
+OpenAL_Sound::~OpenAL_Sound()
+{
+  // Stop
+  alSourceStop(inst);
+
+  // Return sound
+  alDeleteSources(1, &inst);
+
+  // Notify the factory that we quit. You will hear from our union
+  // rep. The bool check is to handle cases where the manager goes out
+  // of scope before the sounds do. In that case, don't try to contact
+  // the factory.
+  if(ownerAlive)
+    owner->notifyDelete(this);
+
+  // Decrease the reference counter
+  if((-- (*refCnt)) == 0)
+    {
+      // We're the last owner. Delete the buffer(s) and the counter
+      // itself.
+      alDeleteBuffers(bufNum, bufferID);
+      checkALError("deleting buffer");
+      delete refCnt;
+    }
+}
diff --git a/libs/mangle/sound/outputs/openal_out.hpp b/libs/mangle/sound/outputs/openal_out.hpp
new file mode 100644
index 0000000000..44d03ecf81
--- /dev/null
+++ b/libs/mangle/sound/outputs/openal_out.hpp
@@ -0,0 +1,44 @@
+#ifndef MANGLE_SOUND_OPENAL_OUT_H
+#define MANGLE_SOUND_OPENAL_OUT_H
+
+#include "../output.hpp"
+#include <list>
+
+namespace Mangle {
+namespace Sound {
+
+class OpenAL_Sound;
+
+class OpenAL_Factory : public SoundFactory
+{
+  void *device;
+  void *context;
+  bool didSetup;
+
+  // List of streaming sounds that need to be updated every frame.
+  typedef std::list<OpenAL_Sound*> StreamList;
+  StreamList streaming;
+
+  friend class OpenAL_Sound;
+  void notifyStreaming(OpenAL_Sound*);
+  void notifyDelete(OpenAL_Sound*);
+
+ public:
+  /// Initialize object. Pass true (default) if you want the
+  /// constructor to set up the current ALCdevice and ALCcontext for
+  /// you.
+  OpenAL_Factory(bool doSetup = true);
+  ~OpenAL_Factory();
+
+  SoundPtr load(const std::string &file) { assert(0); return SoundPtr(); }
+  SoundPtr load(Stream::StreamPtr input) { assert(0); return SoundPtr(); }
+  SoundPtr loadRaw(SampleSourcePtr input);
+
+  void update();
+  void setListenerPos(float x, float y, float z,
+                      float fx, float fy, float fz,
+                      float ux, float uy, float uz);
+};
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/sound/source.hpp b/libs/mangle/sound/source.hpp
new file mode 100644
index 0000000000..fbe7cf958b
--- /dev/null
+++ b/libs/mangle/sound/source.hpp
@@ -0,0 +1,62 @@
+#ifndef MANGLE_SOUND_SOURCE_H
+#define MANGLE_SOUND_SOURCE_H
+
+#include <string>
+#include <boost/cstdint.hpp>
+#include <assert.h>
+
+#include "../stream/stream.hpp"
+
+namespace Mangle {
+namespace Sound {
+
+typedef boost::int32_t int32_t;
+
+/// A stream containing raw sound data and information about the format
+class SampleSource : public Stream::Stream
+{
+ protected:
+  bool isEof;
+
+ public:
+  SampleSource() : isEof(false) {}
+
+  /// Get the sample rate, number of channels, and bits per
+  /// sample. NULL parameters are ignored.
+  virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) = 0;
+
+  bool eof() const { return isEof; }
+
+  // Disabled functions by default. You can still override them in
+  // subclasses.
+  void seek(size_t pos) { assert(0); }
+  size_t tell() const { assert(0); return 0; }
+  size_t size() const { assert(0); return 0; }
+};
+
+typedef boost::shared_ptr<SampleSource> SampleSourcePtr;
+
+/// A factory interface for loading SampleSources from file or stream
+class SampleSourceLoader
+{
+ public:
+  /// If true, the stream version of load() works
+  bool canLoadStream;
+
+  /// If true, the file version of load() works
+  bool canLoadFile;
+
+  /// Load a sound input source from file (if canLoadFile is true)
+  virtual SampleSourcePtr load(const std::string &file) = 0;
+
+  /// Load a sound input source from stream (if canLoadStream is true)
+  virtual SampleSourcePtr load(Stream::StreamPtr input) = 0;
+
+  /// Virtual destructor
+  virtual ~SampleSourceLoader() {}
+};
+
+typedef boost::shared_ptr<SampleSourceLoader> SampleSourceLoaderPtr;
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/sound/sources/audiere_source.cpp b/libs/mangle/sound/sources/audiere_source.cpp
new file mode 100644
index 0000000000..faaa3c8c5b
--- /dev/null
+++ b/libs/mangle/sound/sources/audiere_source.cpp
@@ -0,0 +1,77 @@
+#include "audiere_source.hpp"
+
+#include "../../stream/clients/audiere_file.hpp"
+
+#include <stdexcept>
+
+using namespace Mangle::Stream;
+
+static void fail(const std::string &msg)
+{ throw std::runtime_error("Audiere exception: " + msg); }
+
+using namespace audiere;
+using namespace Mangle::Sound;
+
+// --- SampleSource ---
+
+void AudiereSource::getInfo(Mangle::Sound::int32_t *rate,
+    Mangle::Sound::int32_t *channels, Mangle::Sound::int32_t *bits)
+{
+  SampleFormat fmt;
+  int channels_, rate_;
+  sample->getFormat(channels_, rate_, fmt);
+  *channels = channels_;
+  *rate = rate_;
+  if(bits)
+    {
+      if(fmt == SF_U8)
+        *bits = 8;
+      else if(fmt == SF_S16)
+        *bits = 16;
+      else assert(0);
+    }
+}
+
+// --- Constructors ---
+
+AudiereSource::AudiereSource(const std::string &file)
+{
+  sample = OpenSampleSource(file.c_str());
+
+  if(!sample)
+    fail("Couldn't load file " + file);
+
+  doSetup();
+}
+
+AudiereSource::AudiereSource(StreamPtr input)
+{
+  // Use our Stream::AudiereFile implementation to convert a Mangle
+  // 'Stream' to an Audiere 'File'
+  sample = OpenSampleSource(new AudiereFile(input));
+  if(!sample)
+    fail("Couldn't load stream");
+
+  doSetup();
+}
+
+AudiereSource::AudiereSource(audiere::SampleSourcePtr src)
+  : sample(src)
+{ assert(sample); doSetup(); }
+
+// Common function called from all constructors
+void AudiereSource::doSetup()
+{
+  assert(sample);
+
+  SampleFormat fmt;
+  int channels, rate;
+  sample->getFormat(channels, rate, fmt);
+
+  // Calculate the size of one frame, and pass it to SampleReader.
+  setup(GetSampleSize(fmt) * channels);
+
+  isSeekable = sample->isSeekable();
+  hasPosition = true;
+  hasSize = true;
+}
diff --git a/libs/mangle/sound/sources/audiere_source.hpp b/libs/mangle/sound/sources/audiere_source.hpp
new file mode 100644
index 0000000000..d797c55c86
--- /dev/null
+++ b/libs/mangle/sound/sources/audiere_source.hpp
@@ -0,0 +1,48 @@
+#ifndef MANGLE_SOUND_AUDIERE_SOURCE_H
+#define MANGLE_SOUND_AUDIERE_SOURCE_H
+
+#include "sample_reader.hpp"
+
+// audiere.h from 1.9.4 (latest) release uses
+// cstring routines like strchr() and strlen() without
+// including cstring itself.
+#include <cstring>
+#include <audiere.h>
+
+namespace Mangle {
+namespace Sound {
+
+/// A sample source that decodes files using Audiere
+class AudiereSource : public SampleReader
+{
+  audiere::SampleSourcePtr sample;
+
+  size_t readSamples(void *data, size_t length)
+  { return sample->read(length, data); }
+
+  void doSetup();
+
+ public:
+  /// Decode the given sound file
+  AudiereSource(const std::string &file);
+
+  /// Decode the given sound stream
+  AudiereSource(Mangle::Stream::StreamPtr src);
+
+  /// Read directly from an existing audiere::SampleSource
+  AudiereSource(audiere::SampleSourcePtr src);
+
+  void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
+
+  void seek(size_t pos) { sample->setPosition(pos/frameSize); }
+  size_t tell() const { return sample->getPosition()*frameSize; }
+  size_t size() const { return sample->getLength()*frameSize; }
+};
+
+#include "loadertemplate.hpp"
+
+/// A factory that loads AudiereSources from file and stream
+typedef SSL_Template<AudiereSource,true,true> AudiereLoader;
+
+}} // Namespace
+#endif
diff --git a/libs/mangle/sound/sources/ffmpeg_source.cpp b/libs/mangle/sound/sources/ffmpeg_source.cpp
new file mode 100644
index 0000000000..6349be6913
--- /dev/null
+++ b/libs/mangle/sound/sources/ffmpeg_source.cpp
@@ -0,0 +1,189 @@
+#include "ffmpeg_source.hpp"
+
+#include <stdexcept>
+
+using namespace Mangle::Sound;
+
+// Static output buffer. Not thread safe, but supports multiple
+// streams operated from the same thread.
+static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE];
+
+static void fail(const std::string &msg)
+{ throw std::runtime_error("FFMpeg exception: " + msg); }
+
+// --- Loader ---
+
+static bool init = false;
+
+FFMpegLoader::FFMpegLoader(bool setup)
+{
+  if(setup && !init)
+    {
+      av_register_all();
+      av_log_set_level(AV_LOG_ERROR);
+      init = true;
+    }
+}
+
+// --- Source ---
+
+FFMpegSource::FFMpegSource(const std::string &file)
+{
+  std::string msg;
+  AVCodec *codec;
+
+  if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0)
+    fail("Error loading audio file " + file);
+
+  if(av_find_stream_info(FmtCtx) < 0)
+    {
+      msg = "Error in file stream " + file;
+      goto err;
+    }
+
+  // Pick the first audio stream, if any
+  for(StreamNum = 0; StreamNum < FmtCtx->nb_streams; StreamNum++)
+    {
+      // Pick the first audio stream
+      if(FmtCtx->streams[StreamNum]->codec->codec_type == CODEC_TYPE_AUDIO)
+        break;
+    }
+
+  if(StreamNum == FmtCtx->nb_streams)
+    fail("File '" + file + "' didn't contain any audio streams");
+
+  // Open the decoder
+  CodecCtx = FmtCtx->streams[StreamNum]->codec;
+  codec = avcodec_find_decoder(CodecCtx->codec_id);
+
+  if(!codec || avcodec_open(CodecCtx, codec) < 0)
+    {
+      msg = "Error loading '" + file + "': ";
+      if(codec)
+        msg += "coded error";
+      else
+        msg += "no codec found";
+      goto err;
+    }
+
+  // No errors, we're done
+  return;
+
+  // Handle errors
+ err:
+  av_close_input_file(FmtCtx);
+  fail(msg);
+}
+
+FFMpegSource::~FFMpegSource()
+{
+  avcodec_close(CodecCtx);
+  av_close_input_file(FmtCtx);
+}
+
+void FFMpegSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits)
+{
+  if(rate) *rate = CodecCtx->sample_rate;
+  if(channels) *channels = CodecCtx->channels;
+  if(bits) *bits = 16;
+}
+
+size_t FFMpegSource::read(void *data, size_t length)
+{
+  if(isEof) return 0;
+
+  size_t left = length;
+  uint8_t *outPtr = (uint8_t*)data;
+
+  // First, copy over any stored data we might be sitting on
+  {
+    size_t s = storage.size();
+    size_t copy = s;
+    if(s)
+      {
+        // Make sure there's room
+        if(copy > left)
+          copy = left;
+
+        // Copy
+        memcpy(outPtr, &storage[0], copy);
+        outPtr += copy;
+        left -= copy;
+
+        // Is there anything left in the storage?
+        assert(s>= copy);
+        s -= copy;
+        if(s)
+          {
+            assert(left == 0);
+
+            // Move it to the start and resize
+            memmove(&storage[0], &storage[copy], s);
+            storage.resize(s);
+          }
+      }
+  }
+
+  // Next, get more input data from stream, and decode it
+  while(left)
+    {
+      AVPacket packet;
+
+      // Get the next packet, if any
+      if(av_read_frame(FmtCtx, &packet) < 0)
+        break;
+
+      // We only allow one stream per file at the moment
+      assert((int)StreamNum == packet.stream_index);
+
+      // Decode the packet
+      int len = AVCODEC_MAX_AUDIO_FRAME_SIZE;
+      int tmp = avcodec_decode_audio2(CodecCtx, (int16_t*)outBuf,
+                                      &len, packet.data, packet.size);
+      assert(tmp < 0 || tmp == packet.size);
+
+      // We don't need the input packet any longer
+      av_free_packet(&packet);
+
+      if(tmp < 0)
+        fail("Error decoding audio stream");
+
+      // Copy whatever data we got, and advance the pointer
+      if(len > 0)
+        {
+          // copy = how many bytes do we copy now
+          size_t copy = len;
+          if(copy > left)
+            copy = left;
+
+          // len = how many bytes are left uncopied
+          len -= copy;
+
+          // copy data
+          memcpy(outPtr, outBuf, copy);
+
+          // left = how much space is left in the caller output
+          // buffer. This loop repeats as long left is > 0
+          left -= copy;
+          outPtr += copy;
+          assert(left >= 0);
+
+          if(len > 0)
+            {
+              // There were uncopied bytes. Store them for later.
+              assert(left == 0);
+              storage.resize(len);
+              memcpy(&storage[0], outBuf, len);
+            }
+        }
+    }
+
+  // End of loop. Return the number of bytes copied.
+  assert(left <= length);
+
+  // If we're returning less than asked for, then we're done
+  if(left > 0)
+    isEof = true;
+
+  return length - left;
+}
diff --git a/libs/mangle/sound/sources/ffmpeg_source.hpp b/libs/mangle/sound/sources/ffmpeg_source.hpp
new file mode 100644
index 0000000000..d422b98090
--- /dev/null
+++ b/libs/mangle/sound/sources/ffmpeg_source.hpp
@@ -0,0 +1,52 @@
+#ifndef MANGLE_SOUND_FFMPEG_H
+#define MANGLE_SOUND_FFMPEG_H
+
+#include "../source.hpp"
+#include <vector>
+#include <assert.h>
+
+extern "C"
+{
+#include <avcodec.h>
+#include <avformat.h>
+}
+
+namespace Mangle {
+namespace Sound {
+
+class FFMpegSource : public SampleSource
+{
+  AVFormatContext *FmtCtx;
+  AVCodecContext *CodecCtx;
+  unsigned int StreamNum;
+
+  std::vector<uint8_t> storage;
+
+ public:
+  /// Decode the given sound file
+  FFMpegSource(const std::string &file);
+
+  /// Decode the given sound stream (not supported by FFmpeg)
+  FFMpegSource(Mangle::Stream::StreamPtr src) { assert(0); }
+
+  ~FFMpegSource();
+
+  // Overrides
+  void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
+  size_t read(void *data, size_t length);
+};
+
+#include "loadertemplate.hpp"
+
+/// A factory that loads FFMpegSources from file
+class FFMpegLoader : public SSL_Template<FFMpegSource,false,true>
+{
+ public:
+
+  /// Sets up the libavcodec library. If you want to do your own
+  /// setup, send a setup=false parameter.
+  FFMpegLoader(bool setup=true);
+};
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/sound/sources/libsndfile.cpp b/libs/mangle/sound/sources/libsndfile.cpp
new file mode 100644
index 0000000000..b69a2d4368
--- /dev/null
+++ b/libs/mangle/sound/sources/libsndfile.cpp
@@ -0,0 +1,48 @@
+#include "libsndfile.hpp"
+
+#include <stdexcept>
+#include <sndfile.h>
+
+using namespace Mangle::Stream;
+
+static void fail(const std::string &msg)
+{ throw std::runtime_error("Mangle::libsndfile: " + msg); }
+
+using namespace Mangle::Sound;
+
+void SndFileSource::getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits)
+{
+  *_rate = rate;
+  *_channels = channels;
+  *_bits = bits;
+}
+
+size_t SndFileSource::readSamples(void *data, size_t length)
+{
+  // readf_* reads entire frames, including channels
+  return sf_readf_short((SNDFILE*)handle, (short*)data, length);
+}
+
+SndFileSource::SndFileSource(const std::string &file)
+{
+  SF_INFO info;
+  info.format = 0;
+  handle = sf_open(file.c_str(), SFM_READ, &info);
+  if(handle == NULL)
+    fail("Failed to open " + file);
+
+  // I THINK that using sf_read_short forces the library to convert to
+  // 16 bits no matter what, but the libsndfile docs aren't exactly
+  // very clear on this point.
+  channels = info.channels;
+  rate = info.samplerate;
+  bits = 16;
+
+  // 16 bits per sample times number of channels
+  setup(2*channels);
+}
+
+SndFileSource::~SndFileSource()
+{
+  sf_close((SNDFILE*)handle);
+}
diff --git a/libs/mangle/sound/sources/libsndfile.hpp b/libs/mangle/sound/sources/libsndfile.hpp
new file mode 100644
index 0000000000..7286cf0fe4
--- /dev/null
+++ b/libs/mangle/sound/sources/libsndfile.hpp
@@ -0,0 +1,36 @@
+#ifndef MANGLE_SOUND_SNDFILE_SOURCE_H
+#define MANGLE_SOUND_SNDFILE_SOURCE_H
+
+#include "sample_reader.hpp"
+
+namespace Mangle {
+namespace Sound {
+
+/// A sample source that decodes files using libsndfile. Supports most
+/// formats except mp3.
+class SndFileSource : public SampleReader
+{
+  void *handle;
+  int channels, rate, bits;
+
+  size_t readSamples(void *data, size_t length);
+
+ public:
+  /// Decode the given sound file
+  SndFileSource(const std::string &file);
+
+  /// Decode the given sound stream (not supported)
+  SndFileSource(Mangle::Stream::StreamPtr src) { assert(0); }
+
+  ~SndFileSource();
+
+  void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
+};
+
+#include "loadertemplate.hpp"
+
+/// A factory that loads SndFileSources from file and stream
+typedef SSL_Template<SndFileSource,false,true> SndFileLoader;
+
+}} // Namespace
+#endif
diff --git a/libs/mangle/sound/sources/loadertemplate.hpp b/libs/mangle/sound/sources/loadertemplate.hpp
new file mode 100644
index 0000000000..a27a77d106
--- /dev/null
+++ b/libs/mangle/sound/sources/loadertemplate.hpp
@@ -0,0 +1,28 @@
+#ifndef SSL_TEMPL_H
+#define SSL_TEMPL_H
+
+template <class SourceT, bool stream, bool file>
+class SSL_Template : public SampleSourceLoader
+{
+ public:
+
+  SSL_Template()
+    {
+      canLoadStream = stream;
+      canLoadFile = file;
+    }
+
+  SampleSourcePtr load(const std::string &filename)
+  {
+    assert(canLoadFile);
+    return SampleSourcePtr(new SourceT(filename));
+  }
+
+  SampleSourcePtr load(Stream::StreamPtr input)
+  {
+    assert(canLoadStream);
+    return SampleSourcePtr(new SourceT(input));
+  }
+};
+
+#endif
diff --git a/libs/mangle/sound/sources/mpg123_source.cpp b/libs/mangle/sound/sources/mpg123_source.cpp
new file mode 100644
index 0000000000..24d6ecce1c
--- /dev/null
+++ b/libs/mangle/sound/sources/mpg123_source.cpp
@@ -0,0 +1,115 @@
+#include "mpg123_source.hpp"
+
+#include <stdexcept>
+
+#include <mpg123.h>
+
+using namespace Mangle::Stream;
+
+/*
+  TODOs:
+
+  - mpg123 impressively enough supports custom stream reading. Which
+    means we could (and SHOULD!) support reading from Mangle::Streams
+    as well. But I'll save it til I need it.
+
+    An alternative way to do this is through feeding (which they also
+    support), but that's more messy.
+
+  - the library also supports output, via various other sources,
+    including ALSA, OSS, PortAudio, PulseAudio and SDL. Using this
+    library as a pure output library (if that is possible) would be a
+    nice shortcut over using those libraries - OTOH it's another
+    dependency.
+
+  - we could implement seek(), tell() and size(), but they aren't
+    really necessary. Furthermore, since the returned size is only a
+    guess, it is not safe to rely on it.
+ */
+
+static void fail(const std::string &msg)
+{ throw std::runtime_error("Mangle::Mpg123 exception: " + msg); }
+
+static void checkError(int err, void *mh = NULL)
+{
+  if(err != MPG123_OK)
+    {
+      std::string msg;
+      if(mh) msg = mpg123_strerror((mpg123_handle*)mh);
+      else msg = mpg123_plain_strerror(err);
+      fail(msg);
+    }
+}
+
+using namespace Mangle::Sound;
+
+void Mpg123Source::getInfo(int32_t *pRate, int32_t *pChannels, int32_t *pBits)
+{
+  // Use the values we found in the constructor
+  *pRate = rate;
+  *pChannels = channels;
+  *pBits = bits;
+}
+
+size_t Mpg123Source::read(void *data, size_t length)
+{
+  size_t done;
+  // This is extraordinarily nice. I like this library.
+  int err = mpg123_read((mpg123_handle*)mh, (unsigned char*)data, length, &done);
+  assert(done <= length);
+  if(err == MPG123_DONE)
+    isEof = true;
+  else
+    checkError(err, mh);
+  return done;
+}
+
+Mpg123Loader::Mpg123Loader(bool setup)
+{
+  // Do as we're told
+  if(setup)
+    {
+      int err = mpg123_init();
+      checkError(err);
+    }
+  didSetup = setup;
+}
+
+Mpg123Loader::~Mpg123Loader()
+{
+  // Deinitialize the library on exit
+  if(didSetup)
+    mpg123_exit();
+}
+
+Mpg123Source::Mpg123Source(const std::string &file)
+{
+  int err;
+
+  // Create a new handle
+  mh = mpg123_new(NULL, &err);
+  if(mh == NULL)
+    checkError(err, mh);
+
+  mpg123_handle *mhh = (mpg123_handle*)mh;
+
+  // Open the file (hack around constness)
+  err = mpg123_open(mhh, (char*)file.c_str());
+  checkError(err, mh);
+
+  // Get the format
+  int encoding;
+  err = mpg123_getformat(mhh, &rate, &channels, &encoding);
+  checkError(err, mh);
+  if(encoding != MPG123_ENC_SIGNED_16)
+    fail("Unsupported encoding in " + file);
+
+  // This is the only bit size we support.
+  bits = 16;
+}
+
+Mpg123Source::~Mpg123Source()
+{
+  mpg123_close((mpg123_handle*)mh);
+  mpg123_delete((mpg123_handle*)mh);
+}
diff --git a/libs/mangle/sound/sources/mpg123_source.hpp b/libs/mangle/sound/sources/mpg123_source.hpp
new file mode 100644
index 0000000000..1ac16b5306
--- /dev/null
+++ b/libs/mangle/sound/sources/mpg123_source.hpp
@@ -0,0 +1,47 @@
+#ifndef MANGLE_SOUND_MPG123_SOURCE_H
+#define MANGLE_SOUND_MPG123_SOURCE_H
+
+#include "../source.hpp"
+#include <assert.h>
+
+namespace Mangle {
+namespace Sound {
+
+/// A sample source that decodes files using libmpg123. Only supports
+/// MP3 files.
+class Mpg123Source : public SampleSource
+{
+  void *mh;
+  long int rate;
+  int channels, bits;
+
+ public:
+  /// Decode the given sound file
+  Mpg123Source(const std::string &file);
+
+  /// Needed by SSL_Template but not yet supported
+  Mpg123Source(Mangle::Stream::StreamPtr data)
+  { assert(0); }
+
+  ~Mpg123Source();
+
+  void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
+  size_t read(void *data, size_t length);
+};
+
+#include "loadertemplate.hpp"
+
+/// A factory that loads Mpg123Sources from file and stream
+struct Mpg123Loader : SSL_Template<Mpg123Source,false,true>
+{
+  /** Sets up libmpg123 for you, and closes it on destruction. If you
+      want to do this yourself, send setup=false.
+   */
+  Mpg123Loader(bool setup=true);
+  ~Mpg123Loader();
+private:
+  bool didSetup;
+};
+
+}} // Namespace
+#endif
diff --git a/libs/mangle/sound/sources/sample_reader.cpp b/libs/mangle/sound/sources/sample_reader.cpp
new file mode 100644
index 0000000000..c30de654a5
--- /dev/null
+++ b/libs/mangle/sound/sources/sample_reader.cpp
@@ -0,0 +1,99 @@
+#include "sample_reader.hpp"
+
+#include <string.h>
+
+using namespace Mangle::Sound;
+
+void SampleReader::setup(int size)
+{
+  pullSize = 0;
+  frameSize = size;
+  pullOver = new char[size];
+}
+
+SampleReader::~SampleReader()
+{
+  if(pullOver)
+    delete[] pullOver;
+}
+
+size_t SampleReader::read(void *_data, size_t length)
+{
+  if(isEof) return 0;
+  char *data = (char*)_data;
+
+  // Pullsize holds the number of bytes that were copied "extra" at
+  // the end of LAST round. If non-zero, it also means there is data
+  // left in the pullOver buffer.
+  if(pullSize)
+    {
+      // Amount of data left
+      size_t doRead = frameSize - pullSize;
+      assert(doRead > 0);
+
+      // Make sure we don't read more than we're supposed to
+      if(doRead > length) doRead = length;
+
+      memcpy(data, pullOver+pullSize, doRead);
+
+      // Update the number of bytes now copied
+      pullSize += doRead;
+      assert(pullSize <= frameSize);
+
+      if(pullSize < frameSize)
+        {
+          // There is STILL data left in the pull buffer, and we've
+          // done everything we were supposed to. Leave it and return.
+          assert(doRead == length);
+          return doRead;
+        }
+
+      // Set up variables for further reading below. No need to update
+      // pullSize, it is overwritten anyway.
+      length -= doRead;
+      data += doRead;
+    }
+
+  // Number of whole frames
+  size_t frames = length / frameSize;
+
+  // Read the data
+  size_t res = readSamples(data, frames);
+  assert(res <= frames);
+
+  // Total bytes read
+  size_t num = res*frameSize;
+  data += num;
+
+  if(res < frames)
+    {
+      // End of stream.
+      isEof = true;
+      // Determine how much we read
+      return data-(char*)_data;
+    }
+
+  // Determine the overshoot
+  pullSize = length - num;
+  assert(pullSize < frameSize && pullSize >= 0);
+
+  // Are we missing data?
+  if(pullSize)
+    {
+      // Fill in one sample
+      res = readSamples(pullOver,1);
+      assert(res == 1 || res == 0);
+      if(res)
+        {
+          // Move as much as we can into the output buffer
+          memcpy(data, pullOver, pullSize);
+          data += pullSize;
+        }
+      else
+        // Failed reading, we're out of data
+        isEof = true;
+    }
+
+  // Return the total number of bytes stored
+  return data-(char*)_data;
+}
diff --git a/libs/mangle/sound/sources/sample_reader.hpp b/libs/mangle/sound/sources/sample_reader.hpp
new file mode 100644
index 0000000000..89ddf1f652
--- /dev/null
+++ b/libs/mangle/sound/sources/sample_reader.hpp
@@ -0,0 +1,48 @@
+#ifndef MANGLE_SOUND_SAMPLE_READER_H
+#define MANGLE_SOUND_SAMPLE_READER_H
+
+#include "../source.hpp"
+
+namespace Mangle {
+namespace Sound {
+
+  /* This is a helper base class for other SampleSource
+     implementations. Certain sources (like Audiere and libsndfile)
+     insist on reading whole samples rather than bytes. This class
+     compensates for that, and allows you to read bytes rather than
+     samples.
+
+     There are two ways for subclasses to use this class. EITHER call
+     setup() with the size of frameSize. This will allocate a buffer,
+     which the destructor frees. OR set frameSize manually and
+     manipulate the pullOver pointer yourself. In that case you MUST
+     reset it to NULL if you don't want the destructor to call
+     delete[] on it.
+   */
+class SampleReader : public SampleSource
+{
+  // How much of the above buffer is in use.
+  int pullSize;
+
+protected:
+  // Pullover buffer
+  char* pullOver;
+
+  // Size of one frame, in bytes. This is also the size of the
+  // pullOver buffer.
+  int frameSize;
+
+  // The parameter gives the size of one sample/frame, in bytes.
+  void setup(int);
+
+  // Read the given number of samples, in multiples of frameSize. Does
+  // not have to set or respect isEof.
+  virtual size_t readSamples(void *data, size_t num) = 0;
+
+ public:
+  SampleReader() : pullSize(0), pullOver(NULL) {}
+  ~SampleReader();
+  size_t read(void *data, size_t length);
+};
+}} // Namespace
+#endif
diff --git a/libs/mangle/sound/sources/stream_source.hpp b/libs/mangle/sound/sources/stream_source.hpp
new file mode 100644
index 0000000000..43c605a004
--- /dev/null
+++ b/libs/mangle/sound/sources/stream_source.hpp
@@ -0,0 +1,47 @@
+#ifndef MANGLE_SOUND_STREAMSOURCE_H
+#define MANGLE_SOUND_STREAMSOURCE_H
+
+#include "../source.hpp"
+
+namespace Mangle {
+namespace Sound {
+
+/// A class for reading raw samples directly from a stream.
+class Stream2Samples : public SampleSource
+{
+  Mangle::Stream::StreamPtr inp;
+  int32_t rate, channels, bits;
+
+ public:
+  Stream2Samples(Mangle::Stream::StreamPtr _inp, int32_t _rate, int32_t _channels, int32_t _bits)
+   : inp(_inp), rate(_rate), channels(_channels), bits(_bits)
+    {
+      isSeekable = inp->isSeekable;
+      hasPosition = inp->hasPosition;
+      hasSize = inp->hasSize;
+      hasPtr = inp->hasPtr;
+    }
+
+  /// Get the sample rate, number of channels, and bits per
+  /// sample. NULL parameters are ignored.
+  void getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits)
+  {
+    if(_rate) *_rate = rate;
+    if(_channels) *_channels = channels;
+    if(_bits) *_bits = bits;
+  }
+
+  size_t read(void *out, size_t count)
+    { return inp->read(out, count); }
+
+  void seek(size_t pos) { inp->seek(pos); }
+  size_t tell() const { return inp->tell(); }
+  size_t size() const { return inp->size(); }
+  bool eof() const { return inp->eof(); }
+  const void *getPtr() { return inp->getPtr(); }
+  const void *getPtr(size_t size) { return inp->getPtr(size); }
+  const void *getPtr(size_t pos, size_t size) { return inp->getPtr(pos, size); }
+};
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/sound/sources/wav_source.cpp b/libs/mangle/sound/sources/wav_source.cpp
new file mode 100644
index 0000000000..a46b3d27ec
--- /dev/null
+++ b/libs/mangle/sound/sources/wav_source.cpp
@@ -0,0 +1,99 @@
+#include "wav_source.hpp"
+
+#include "../../stream/servers/file_stream.hpp"
+
+#include <stdexcept>
+
+using namespace Mangle::Stream;
+using namespace Mangle::Sound;
+
+static void fail(const std::string &msg)
+{ throw std::runtime_error("Mangle::Wav exception: " + msg); }
+
+void WavSource::getInfo(int32_t *pRate, int32_t *pChannels, int32_t *pBits)
+{
+  // Use the values we found in the constructor
+  *pRate = rate;
+  *pChannels = channels;
+  *pBits = bits;
+}
+
+void WavSource::seek(size_t pos)
+{
+  // Seek the stream and set 'left'
+  assert(isSeekable);
+  if(pos > total) pos = total;
+  input->seek(dataOffset + pos);
+  left = total-pos;
+}
+
+size_t WavSource::read(void *data, size_t length)
+{
+  if(length > left)
+    length = left;
+  size_t read = input->read(data, length);
+  if(read < length)
+    // Something went wrong
+    fail("WAV read error");
+  return length;
+}
+
+void WavSource::open(Mangle::Stream::StreamPtr data)
+{
+  input = data;
+
+  hasPosition = true;
+  hasSize = true;
+  // If we can check position and seek in the input stream, then we
+  // can seek the wav data too.
+  isSeekable = input->isSeekable && input->hasPosition;
+
+  // Read header
+  unsigned int val;
+
+  input->read(&val,4);  // header
+  if(val != 0x46464952) // "RIFF"
+    fail("Not a WAV file");
+
+  input->read(&val,4);  // size (ignored)
+  input->read(&val,4);  // file format
+  if(val != 0x45564157) // "WAVE"
+    fail("Not a valid WAV file");
+
+  input->read(&val,4);  // "fmt "
+  input->read(&val,4);  // chunk size (must be 16)
+  if(val != 16)
+    fail("Unsupported WAV format");
+
+  input->read(&val,2);
+  if(val != 1)
+    fail("Non-PCM (compressed) WAV files not supported");
+
+  // Sound data specification
+  channels = 0;
+  input->read(&channels,2);
+  input->read(&rate, 4);
+
+  // Skip next 6 bytes
+  input->read(&val, 4);
+  input->read(&val, 2);
+
+  // Bits per sample
+  bits = 0;
+  input->read(&bits,2);
+
+  input->read(&val,4);  // Data header
+  if(val != 0x61746164) // "data"
+    fail("Expected data block");
+
+  // Finally, read the data size
+  input->read(&total,4);
+  left = total;
+
+  // Store the beginning of the data block for later
+  if(input->hasPosition)
+    dataOffset = input->tell();
+}
+
+WavSource::WavSource(const std::string &file)
+{ open(StreamPtr(new FileStream(file))); }
diff --git a/libs/mangle/sound/sources/wav_source.hpp b/libs/mangle/sound/sources/wav_source.hpp
new file mode 100644
index 0000000000..227f4da733
--- /dev/null
+++ b/libs/mangle/sound/sources/wav_source.hpp
@@ -0,0 +1,49 @@
+#ifndef MANGLE_SOUND_WAV_SOURCE_H
+#define MANGLE_SOUND_WAV_SOURCE_H
+
+#include "../source.hpp"
+#include <assert.h>
+
+namespace Mangle {
+namespace Sound {
+
+/// WAV file decoder. Has no external library dependencies.
+class WavSource : public SampleSource
+{
+  // Sound info
+  uint32_t rate, channels, bits;
+
+  // Total size (of output) and bytes left
+  uint32_t total, left;
+
+  // Offset in input of the beginning of the data block
+  size_t dataOffset;
+
+  Mangle::Stream::StreamPtr input;
+
+  void open(Mangle::Stream::StreamPtr);
+
+ public:
+  /// Decode the given sound file
+  WavSource(const std::string&);
+
+  /// Decode from stream
+  WavSource(Mangle::Stream::StreamPtr s)
+  { open(s); }
+
+  void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
+  size_t read(void *data, size_t length);
+
+  void seek(size_t);
+  size_t tell() const { return total-left; }
+  size_t size() const { return total; }
+  bool eof() const { return left > 0; }
+};
+
+#include "loadertemplate.hpp"
+
+/// A factory that loads WavSources from file and stream
+typedef SSL_Template<WavSource,true,true> WavLoader;
+
+}} // Namespace
+#endif
diff --git a/libs/mangle/sound/tests/.gitignore b/libs/mangle/sound/tests/.gitignore
new file mode 100644
index 0000000000..8144904045
--- /dev/null
+++ b/libs/mangle/sound/tests/.gitignore
@@ -0,0 +1 @@
+*_test
diff --git a/libs/mangle/sound/tests/Makefile b/libs/mangle/sound/tests/Makefile
new file mode 100644
index 0000000000..6fcac72da7
--- /dev/null
+++ b/libs/mangle/sound/tests/Makefile
@@ -0,0 +1,38 @@
+GCC=g++ -I../ -Wall
+
+all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test openal_sndfile_test wav_source_test openal_various_test
+
+L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat)
+I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat
+L_OPENAL=$(shell pkg-config --libs openal)
+L_AUDIERE=-laudiere
+
+wav_source_test: wav_source_test.cpp ../sources/wav_source.cpp
+	$(GCC) $^ -o $@
+
+openal_various_test: openal_various_test.cpp ../sources/mpg123_source.cpp ../sources/wav_source.cpp ../outputs/openal_out.cpp
+	$(GCC) $^ -o $@ -lmpg123 ${L_OPENAL}
+
+openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../sources/sample_reader.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp
+	$(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL)
+
+openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outputs/openal_out.cpp
+	$(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) $(I_FFMPEG)
+
+openal_mpg123_test: openal_mpg123_test.cpp ../sources/mpg123_source.cpp ../outputs/openal_out.cpp
+	$(GCC) $^ -o $@ -lmpg123 ${L_OPENAL}
+
+openal_sndfile_test: openal_sndfile_test.cpp ../sources/libsndfile.cpp ../sources/sample_reader.cpp ../outputs/openal_out.cpp
+	$(GCC) $^ -o $@ -lsndfile ${L_OPENAL}
+
+openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp
+	$(GCC) $^ -o $@ $(L_OPENAL)
+
+audiere_source_test: audiere_source_test.cpp ../sources/audiere_source.cpp ../../stream/clients/audiere_file.cpp ../sources/sample_reader.cpp
+	$(GCC) $^ -o $@ $(L_AUDIERE)
+
+ffmpeg_source_test: ffmpeg_source_test.cpp ../sources/ffmpeg_source.cpp
+	$(GCC) $^ -o $@ $(L_FFMPEG) $(I_FFMPEG)
+
+clean:
+	rm *_test
diff --git a/libs/mangle/sound/tests/audiere_source_test.cpp b/libs/mangle/sound/tests/audiere_source_test.cpp
new file mode 100644
index 0000000000..637d743b21
--- /dev/null
+++ b/libs/mangle/sound/tests/audiere_source_test.cpp
@@ -0,0 +1,68 @@
+#include <iostream>
+
+#include "../../stream/servers/file_stream.hpp"
+#include "../sources/audiere_source.hpp"
+
+#include <assert.h>
+#include <string.h>
+
+using namespace std;
+using namespace Mangle::Stream;
+using namespace Mangle::Sound;
+
+// Contents and size of cow.raw
+void *orig;
+size_t orig_size;
+
+void run(SampleSourcePtr &src)
+{
+  size_t ss = src->size();
+  assert(ss == orig_size);
+
+  cout << "Source size: " << ss << endl;
+  int rate, channels, bits;
+  src->getInfo(&rate, &channels, &bits);
+  cout << "rate=" << rate << "\nchannels=" << channels
+       << "\nbits=" << bits << endl;
+
+  cout << "Reading entire buffer into memory\n";
+  void *buf = malloc(ss);
+  src->read(buf, ss);
+
+  cout << "Comparing...\n";
+  if(memcmp(buf, orig, ss) != 0)
+    {
+      cout << "Oops!\n";
+      assert(0);
+    }
+
+  cout << "Done\n";
+}
+
+int main()
+{
+  {
+    cout << "Reading cow.raw first\n";
+    FileStream tmp("cow.raw");
+    orig_size = tmp.size();
+    cout << "Size: " << orig_size << endl;
+    orig = malloc(orig_size);
+    tmp.read(orig, orig_size);
+    cout << "Done\n";
+  }
+
+  {
+    cout << "\nLoading cow.wav by filename:\n";
+    SampleSourcePtr cow_file( new AudiereSource("cow.wav") );
+    run(cow_file);
+  }
+
+  {
+    cout << "\nLoading cow.wav by stream:\n";
+    StreamPtr inp( new FileStream("cow.wav") );
+    SampleSourcePtr cow_stream( new AudiereSource(inp) );
+    run(cow_stream);
+  }
+
+  return 0;
+}
diff --git a/libs/mangle/sound/tests/cow.raw b/libs/mangle/sound/tests/cow.raw
new file mode 100644
index 0000000000..c4d155bbfb
Binary files /dev/null and b/libs/mangle/sound/tests/cow.raw differ
diff --git a/libs/mangle/sound/tests/cow.wav b/libs/mangle/sound/tests/cow.wav
new file mode 100644
index 0000000000..494e6c4ac1
Binary files /dev/null and b/libs/mangle/sound/tests/cow.wav differ
diff --git a/libs/mangle/sound/tests/ffmpeg_source_test.cpp b/libs/mangle/sound/tests/ffmpeg_source_test.cpp
new file mode 100644
index 0000000000..f03b15b996
--- /dev/null
+++ b/libs/mangle/sound/tests/ffmpeg_source_test.cpp
@@ -0,0 +1,62 @@
+#include <iostream>
+
+#include "../../stream/servers/file_stream.hpp"
+#include "../sources/ffmpeg_source.hpp"
+
+#include <assert.h>
+#include <string.h>
+
+using namespace std;
+using namespace Mangle::Stream;
+using namespace Mangle::Sound;
+
+// Contents and size of cow.raw
+void *orig;
+size_t orig_size;
+
+void run(SampleSourcePtr &src)
+{
+  int rate, channels, bits;
+  src->getInfo(&rate, &channels, &bits);
+  cout << "rate=" << rate << "\nchannels=" << channels
+       << "\nbits=" << bits << endl;
+
+  cout << "Reading entire buffer into memory\n";
+  void *buf = malloc(orig_size);
+  size_t ss = src->read(buf, orig_size);
+  cout << "Actually read: " << ss << endl;
+  assert(ss == orig_size);
+
+  cout << "Comparing...\n";
+  if(memcmp(buf, orig, ss) != 0)
+    {
+      cout << "Oops!\n";
+      assert(0);
+    }
+
+  cout << "Done\n";
+}
+
+int main()
+{
+  {
+    cout << "Reading cow.raw first\n";
+    FileStream tmp("cow.raw");
+    orig_size = tmp.size();
+    cout << "Size: " << orig_size << endl;
+    orig = malloc(orig_size);
+    tmp.read(orig, orig_size);
+    cout << "Done\n";
+  }
+
+  // Initializes the library, not used for anything else.
+  FFMpegLoader fm;
+
+  {
+    cout << "\nLoading cow.wav by filename:\n";
+    SampleSourcePtr cow_file( new FFMpegSource("cow.wav") );
+    run(cow_file);
+  }
+
+  return 0;
+}
diff --git a/libs/mangle/sound/tests/openal_audiere_test.cpp b/libs/mangle/sound/tests/openal_audiere_test.cpp
new file mode 100644
index 0000000000..ced7fe5d23
--- /dev/null
+++ b/libs/mangle/sound/tests/openal_audiere_test.cpp
@@ -0,0 +1,52 @@
+#include <iostream>
+#include <exception>
+
+#include "../../stream/servers/file_stream.hpp"
+#include "../filters/openal_audiere.hpp"
+
+using namespace std;
+using namespace Mangle::Stream;
+using namespace Mangle::Sound;
+
+OpenAL_Audiere_Factory mg;
+
+void play(const char* name, bool stream=false)
+{
+  // Only load streams if the backend supports it
+  if(stream && !mg.canLoadStream)
+    return;
+
+  cout << "Playing " << name;
+  if(stream) cout << " (from stream)";
+  cout << "\n";
+
+  SoundPtr snd;
+
+  try
+    {
+      if(stream)
+        snd = mg.load(StreamPtr(new FileStream(name)));
+      else
+        snd = mg.load(name);
+
+      snd->play();
+
+      while(snd->isPlaying())
+        {
+          usleep(10000);
+          if(mg.needsUpdate) mg.update();
+        }
+    }
+  catch(exception &e)
+    {
+      cout << "  ERROR: " << e.what() << "\n";
+    }
+}
+
+int main()
+{
+  play("cow.wav");
+  play("owl.ogg");
+  play("cow.wav", true);
+  return 0;
+}
diff --git a/libs/mangle/sound/tests/openal_ffmpeg_test.cpp b/libs/mangle/sound/tests/openal_ffmpeg_test.cpp
new file mode 100644
index 0000000000..d4b8e93003
--- /dev/null
+++ b/libs/mangle/sound/tests/openal_ffmpeg_test.cpp
@@ -0,0 +1,52 @@
+#include <iostream>
+#include <exception>
+
+#include "../../stream/servers/file_stream.hpp"
+#include "../filters/openal_ffmpeg.hpp"
+
+using namespace std;
+using namespace Mangle::Stream;
+using namespace Mangle::Sound;
+
+OpenAL_FFMpeg_Factory mg;
+
+void play(const char* name, bool stream=false)
+{
+  // Only load streams if the backend supports it
+  if(stream && !mg.canLoadStream)
+    return;
+
+  cout << "Playing " << name;
+  if(stream) cout << " (from stream)";
+  cout << "\n";
+
+  SoundPtr snd;
+
+  try
+    {
+      if(stream)
+        snd = mg.load(StreamPtr(new FileStream(name)));
+      else
+        snd = mg.load(name);
+
+      snd->play();
+
+      while(snd->isPlaying())
+        {
+          usleep(10000);
+          if(mg.needsUpdate) mg.update();
+        }
+    }
+  catch(exception &e)
+    {
+      cout << "  ERROR: " << e.what() << "\n";
+    }
+}
+
+int main()
+{
+  play("cow.wav");
+  play("owl.ogg");
+  play("cow.wav", true);
+  return 0;
+}
diff --git a/libs/mangle/sound/tests/openal_mpg123_test.cpp b/libs/mangle/sound/tests/openal_mpg123_test.cpp
new file mode 100644
index 0000000000..fef1a5605a
--- /dev/null
+++ b/libs/mangle/sound/tests/openal_mpg123_test.cpp
@@ -0,0 +1,54 @@
+#include <iostream>
+#include <exception>
+
+#include "../../stream/servers/file_stream.hpp"
+#include "../filters/openal_mpg123.hpp"
+
+using namespace std;
+using namespace Mangle::Stream;
+using namespace Mangle::Sound;
+
+OpenAL_Mpg123_Factory mg;
+
+void play(const char* name, bool stream=false)
+{
+  // Only load streams if the backend supports it
+  if(stream && !mg.canLoadStream)
+    return;
+
+  cout << "Playing " << name;
+  if(stream) cout << " (from stream)";
+  cout << "\n";
+
+  SoundPtr snd;
+
+  try
+    {
+      if(stream)
+        snd = mg.load(StreamPtr(new FileStream(name)));
+      else
+        snd = mg.load(name);
+
+      snd->setStreaming(true);
+      snd->play();
+
+      while(snd->isPlaying())
+        {
+          usleep(10000);
+          if(mg.needsUpdate) mg.update();
+        }
+    }
+  catch(exception &e)
+    {
+      cout << "  ERROR: " << e.what() << "\n";
+    }
+}
+
+int main(int argc, char**argv)
+{
+  if(argc != 2)
+    cout << "Please specify an MP3 file\n";
+  else
+    play(argv[1]);
+  return 0;
+}
diff --git a/libs/mangle/sound/tests/openal_output_test.cpp b/libs/mangle/sound/tests/openal_output_test.cpp
new file mode 100644
index 0000000000..a8059ec652
--- /dev/null
+++ b/libs/mangle/sound/tests/openal_output_test.cpp
@@ -0,0 +1,59 @@
+#include <iostream>
+#include <exception>
+
+#include "../../stream/servers/file_stream.hpp"
+#include "../sources/stream_source.hpp"
+#include "../outputs/openal_out.hpp"
+
+using namespace std;
+using namespace Mangle::Stream;
+using namespace Mangle::Sound;
+
+int main()
+{
+  cout << "Loading cow.raw\n";
+
+  int rate = 11025;
+  int chan = 1;
+  int bits = 16;
+
+  cout << "  rate=" << rate << "\n  channels=" << chan
+       << "\n  bits=" << bits << endl;
+
+  StreamPtr file( new FileStream("cow.raw") );
+  SampleSourcePtr source( new Stream2Samples( file, rate, chan, bits));
+
+  cout << "Playing\n";
+
+  OpenAL_Factory mg;
+
+  SoundPtr snd = mg.loadRaw(source);
+
+  try
+    {
+      // Try setting all kinds of stuff before playing. OpenAL_Sound
+      // uses delayed buffer loading, but these should still work
+      // without a buffer.
+      snd->stop();
+      snd->pause();
+      snd->setVolume(0.8);
+      snd->setPitch(0.9);
+
+      // Also test streaming, since all the other examples test
+      // non-streaming sounds.
+      snd->setStreaming(true);
+
+      snd->play();
+
+      while(snd->isPlaying())
+        {
+          usleep(10000);
+          mg.update();
+        }
+    }
+  catch(exception &e)
+    {
+      cout << "  ERROR: " << e.what() << "\n";
+    }
+  return 0;
+}
diff --git a/libs/mangle/sound/tests/openal_sndfile_test.cpp b/libs/mangle/sound/tests/openal_sndfile_test.cpp
new file mode 100644
index 0000000000..bd5f117a59
--- /dev/null
+++ b/libs/mangle/sound/tests/openal_sndfile_test.cpp
@@ -0,0 +1,52 @@
+#include <iostream>
+#include <exception>
+
+#include "../../stream/servers/file_stream.hpp"
+#include "../filters/openal_sndfile.hpp"
+
+using namespace std;
+using namespace Mangle::Stream;
+using namespace Mangle::Sound;
+
+OpenAL_SndFile_Factory mg;
+
+void play(const char* name, bool stream=false)
+{
+  // Only load streams if the backend supports it
+  if(stream && !mg.canLoadStream)
+    return;
+
+  cout << "Playing " << name;
+  if(stream) cout << " (from stream)";
+  cout << "\n";
+
+  SoundPtr snd;
+
+  try
+    {
+      if(stream)
+        snd = mg.load(StreamPtr(new FileStream(name)));
+      else
+        snd = mg.load(name);
+
+      snd->play();
+
+      while(snd->isPlaying())
+        {
+          usleep(10000);
+          if(mg.needsUpdate) mg.update();
+        }
+    }
+  catch(exception &e)
+    {
+      cout << "  ERROR: " << e.what() << "\n";
+    }
+}
+
+int main()
+{
+  play("cow.wav");
+  play("owl.ogg");
+  play("cow.wav", true);
+  return 0;
+}
diff --git a/libs/mangle/sound/tests/openal_various_test.cpp b/libs/mangle/sound/tests/openal_various_test.cpp
new file mode 100644
index 0000000000..9426a672ec
--- /dev/null
+++ b/libs/mangle/sound/tests/openal_various_test.cpp
@@ -0,0 +1,51 @@
+#include <iostream>
+#include <exception>
+
+#include "../../stream/servers/file_stream.hpp"
+#include "../filters/openal_various.hpp"
+
+using namespace std;
+using namespace Mangle::Stream;
+using namespace Mangle::Sound;
+
+OpenAL_Various_Factory mg;
+
+void play(const char* name, bool stream=false)
+{
+  // Only load streams if the backend supports it
+  if(stream && !mg.canLoadStream)
+    return;
+
+  cout << "Playing " << name;
+  if(stream) cout << " (from stream)";
+  cout << "\n";
+
+  SoundPtr snd;
+
+  try
+    {
+      if(stream)
+        snd = mg.load(StreamPtr(new FileStream(name)));
+      else
+        snd = mg.load(name);
+
+      snd->play();
+
+      while(snd->isPlaying())
+        {
+          usleep(10000);
+          if(mg.needsUpdate) mg.update();
+        }
+    }
+  catch(exception &e)
+    {
+      cout << "  ERROR: " << e.what() << "\n";
+    }
+}
+
+int main()
+{
+  play("cow.wav");
+  play("cow.wav", true);
+  return 0;
+}
diff --git a/libs/mangle/sound/tests/output/audiere_source_test.out b/libs/mangle/sound/tests/output/audiere_source_test.out
new file mode 100644
index 0000000000..47a5a9e418
--- /dev/null
+++ b/libs/mangle/sound/tests/output/audiere_source_test.out
@@ -0,0 +1,21 @@
+Reading cow.raw first
+Size: 37502
+Done
+
+Loading cow.wav by filename:
+Source size: 37502
+rate=11025
+channels=1
+bits=16
+Reading entire buffer into memory
+Comparing...
+Done
+
+Loading cow.wav by stream:
+Source size: 37502
+rate=11025
+channels=1
+bits=16
+Reading entire buffer into memory
+Comparing...
+Done
diff --git a/libs/mangle/sound/tests/output/ffmpeg_source_test.out b/libs/mangle/sound/tests/output/ffmpeg_source_test.out
new file mode 100644
index 0000000000..1c7d491139
--- /dev/null
+++ b/libs/mangle/sound/tests/output/ffmpeg_source_test.out
@@ -0,0 +1,12 @@
+Reading cow.raw first
+Size: 37502
+Done
+
+Loading cow.wav by filename:
+rate=11025
+channels=1
+bits=16
+Reading entire buffer into memory
+Actually read: 37502
+Comparing...
+Done
diff --git a/libs/mangle/sound/tests/output/openal_audiere_test.out b/libs/mangle/sound/tests/output/openal_audiere_test.out
new file mode 100644
index 0000000000..4fe01eac2b
--- /dev/null
+++ b/libs/mangle/sound/tests/output/openal_audiere_test.out
@@ -0,0 +1,3 @@
+Playing cow.wav
+Playing owl.ogg
+Playing cow.wav (from stream)
diff --git a/libs/mangle/sound/tests/output/openal_ffmpeg_test.out b/libs/mangle/sound/tests/output/openal_ffmpeg_test.out
new file mode 100644
index 0000000000..96e1db0f9a
--- /dev/null
+++ b/libs/mangle/sound/tests/output/openal_ffmpeg_test.out
@@ -0,0 +1,2 @@
+Playing cow.wav
+Playing owl.ogg
diff --git a/libs/mangle/sound/tests/output/openal_mpg123_test.out b/libs/mangle/sound/tests/output/openal_mpg123_test.out
new file mode 100644
index 0000000000..e55dabbb1d
--- /dev/null
+++ b/libs/mangle/sound/tests/output/openal_mpg123_test.out
@@ -0,0 +1 @@
+Please specify an MP3 file
diff --git a/libs/mangle/sound/tests/output/openal_output_test.out b/libs/mangle/sound/tests/output/openal_output_test.out
new file mode 100644
index 0000000000..04392a72e8
--- /dev/null
+++ b/libs/mangle/sound/tests/output/openal_output_test.out
@@ -0,0 +1,5 @@
+Loading cow.raw
+  rate=11025
+  channels=1
+  bits=16
+Playing
diff --git a/libs/mangle/sound/tests/output/openal_sndfile_test.out b/libs/mangle/sound/tests/output/openal_sndfile_test.out
new file mode 100644
index 0000000000..96e1db0f9a
--- /dev/null
+++ b/libs/mangle/sound/tests/output/openal_sndfile_test.out
@@ -0,0 +1,2 @@
+Playing cow.wav
+Playing owl.ogg
diff --git a/libs/mangle/sound/tests/output/openal_various_test.out b/libs/mangle/sound/tests/output/openal_various_test.out
new file mode 100644
index 0000000000..f25a555138
--- /dev/null
+++ b/libs/mangle/sound/tests/output/openal_various_test.out
@@ -0,0 +1 @@
+Playing cow.wav
diff --git a/libs/mangle/sound/tests/output/wav_source_test.out b/libs/mangle/sound/tests/output/wav_source_test.out
new file mode 100644
index 0000000000..b6fc8e6fc8
--- /dev/null
+++ b/libs/mangle/sound/tests/output/wav_source_test.out
@@ -0,0 +1,12 @@
+Source size: 37502
+rate=11025
+channels=1
+bits=16
+Reading entire buffer into memory
+
+Reading cow.raw
+Size: 37502
+
+Comparing...
+
+Done
diff --git a/libs/mangle/sound/tests/owl.ogg b/libs/mangle/sound/tests/owl.ogg
new file mode 100644
index 0000000000..e992f24d48
Binary files /dev/null and b/libs/mangle/sound/tests/owl.ogg differ
diff --git a/libs/mangle/sound/tests/test.sh b/libs/mangle/sound/tests/test.sh
new file mode 100755
index 0000000000..2d07708adc
--- /dev/null
+++ b/libs/mangle/sound/tests/test.sh
@@ -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
diff --git a/libs/mangle/sound/tests/wav_source_test.cpp b/libs/mangle/sound/tests/wav_source_test.cpp
new file mode 100644
index 0000000000..749af18496
--- /dev/null
+++ b/libs/mangle/sound/tests/wav_source_test.cpp
@@ -0,0 +1,48 @@
+#include <iostream>
+
+#include "../sources/wav_source.hpp"
+#include "../../stream/servers/file_stream.hpp"
+
+#include <assert.h>
+#include <string.h>
+
+using namespace std;
+using namespace Mangle::Sound;
+using namespace Mangle::Stream;
+
+int main()
+{
+  WavSource wav("cow.wav");
+
+  cout << "Source size: " << wav.size() << endl;
+  int rate, channels, bits;
+  wav.getInfo(&rate, &channels, &bits);
+  cout << "rate=" << rate << "\nchannels=" << channels
+       << "\nbits=" << bits << endl;
+
+  cout << "Reading entire buffer into memory\n";
+  void *buf = malloc(wav.size());
+  wav.read(buf, wav.size());
+
+  cout << "\nReading cow.raw\n";
+  FileStream tmp("cow.raw");
+  cout << "Size: " << tmp.size() << endl;
+  void *buf2 = malloc(tmp.size());
+  tmp.read(buf2, tmp.size());
+
+  cout << "\nComparing...\n";
+  if(tmp.size() != wav.size())
+    {
+      cout << "SIZE MISMATCH!\n";
+      assert(0);
+    }
+
+  if(memcmp(buf, buf2, wav.size()) != 0)
+    {
+      cout << "CONTENT MISMATCH!\n";
+      assert(0);
+    }
+
+  cout << "\nDone\n";
+  return 0;
+}
diff --git a/libs/mangle/stream/clients/audiere_file.cpp b/libs/mangle/stream/clients/audiere_file.cpp
new file mode 100644
index 0000000000..16bc7891aa
--- /dev/null
+++ b/libs/mangle/stream/clients/audiere_file.cpp
@@ -0,0 +1,32 @@
+#include "audiere_file.hpp"
+
+using namespace audiere;
+using namespace Mangle::Stream;
+
+bool AudiereFile::seek(int pos, SeekMode mode)
+{
+  assert(inp->isSeekable);
+  assert(inp->hasPosition);
+
+  size_t newPos;
+
+  switch(mode)
+    {
+    case BEGIN: newPos = pos; break;
+    case CURRENT: newPos = pos+tell(); break;
+    case END:
+      // Seeking from the end. This requires that we're able to get
+      // the entire size of the stream. The pos also has to be
+      // non-positive.
+      assert(inp->hasSize);
+      assert(pos <= 0);
+      newPos = inp->size() + pos;
+      break;
+    default:
+      assert(0 && "invalid seek mode");
+    }
+
+  inp->seek(newPos);
+  return inp->tell() == newPos;
+
+}
diff --git a/libs/mangle/stream/clients/audiere_file.hpp b/libs/mangle/stream/clients/audiere_file.hpp
new file mode 100644
index 0000000000..61e26f21b2
--- /dev/null
+++ b/libs/mangle/stream/clients/audiere_file.hpp
@@ -0,0 +1,38 @@
+#ifndef MANGLE_STREAM_AUDIERECLIENT_H
+#define MANGLE_STREAM_AUDIERECLIENT_H
+
+#include <audiere.h>
+#include <assert.h>
+
+#include "../stream.hpp"
+
+namespace Mangle {
+namespace Stream {
+
+/** @brief An Audiere::File that wraps a Mangle::Stream input.
+
+    This lets Audiere read sound files from any generic archive or
+    file manager that supports Mangle streams.
+ */
+class AudiereFile : public audiere::RefImplementation<audiere::File>
+{
+  StreamPtr inp;
+
+ public:
+  AudiereFile(StreamPtr _inp)
+    : inp(_inp) {}
+
+  /// Read 'count' bytes, return bytes successfully read
+  int ADR_CALL read(void *buf, int count)
+    { return inp->read(buf,count); }
+
+  /// Seek, relative to specified seek mode. Returns true if successful.
+  bool ADR_CALL seek(int pos, audiere::File::SeekMode mode);
+
+  /// Get current position
+  int ADR_CALL tell()
+    { assert(inp->hasPosition); return inp->tell(); }
+};
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/clients/io_stream.cpp b/libs/mangle/stream/clients/io_stream.cpp
new file mode 100644
index 0000000000..5f1edc2217
--- /dev/null
+++ b/libs/mangle/stream/clients/io_stream.cpp
@@ -0,0 +1,221 @@
+#include "io_stream.hpp"
+
+// This seems to work
+#ifndef EOF
+#define EOF -1
+#endif
+
+using namespace Mangle::Stream;
+
+#define BSIZE 1024
+
+// Streambuf for normal stream reading
+class _istreambuf : public std::streambuf
+{
+  StreamPtr client;
+  char buf[BSIZE];
+
+public:
+  _istreambuf(StreamPtr strm) : client(strm)
+  {
+    // Make sure we picked the right class
+    assert(client->isReadable);
+    assert(!client->hasPtr);
+
+    // Tell streambuf to delegate reading operations to underflow()
+    setg(NULL,NULL,NULL);
+
+    // Disallow writing
+    setp(NULL,NULL);
+  }
+
+  /* Underflow is called when there is no more info to read in the
+     input buffer. We need to refill buf with new data (if any), and
+     set up the internal pointers with setg() to reflect the new
+     state.
+  */
+  int underflow()
+  {
+    // Read some more data
+    size_t read = client->read(buf, BSIZE);
+    assert(read <= BSIZE);
+
+    // If we're out of data, then EOF
+    if(read == 0)
+      return EOF;
+
+    // Otherwise, set up input buffer
+    setg(buf, buf, buf+read);
+
+    // Return the first char
+    return *((unsigned char*)buf);
+  }
+
+  // Seek stream, if the source supports it. Ignores the second
+  // parameter as Mangle doesn't separate input and output pointers.
+  std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::in)
+  {
+    // Does this stream know how to seek?
+    if(!client->isSeekable || !client->hasPosition)
+      // If not, signal an error.
+      return -1;
+
+    // Set stream position and reset the buffer.
+    client->seek(pos);
+    setg(NULL,NULL,NULL);
+
+    return client->tell();
+  }
+};
+
+// Streambuf optimized for pointer-based input streams
+class _ptrstreambuf : public std::streambuf
+{
+  StreamPtr client;
+
+public:
+  _ptrstreambuf(StreamPtr strm) : client(strm)
+  {
+    // Make sure we picked the right class
+    assert(client->isReadable);
+    assert(client->hasPtr);
+
+    // seekpos() does all the work
+    seekpos(0);
+  }
+
+  // Underflow is only called when we're at the end of the file
+  int underflow() { return EOF; }
+
+  // Seek to a new position within the memory stream. This bypasses
+  // client->seek() entirely so isSeekable doesn't have to be set.
+  std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::in)
+  {
+    // All pointer streams need a size
+    assert(client->hasSize);
+
+    // Figure out how much will be left of the stream after seeking
+    size_t size = client->size() - pos;
+
+    // Get a pointer
+    char* ptr = (char*)client->getPtr(pos,size);
+
+    // And use it
+    setg(ptr,ptr,ptr+size);
+
+    return pos;
+  }
+};
+
+// Streambuf for stream writing
+class _ostreambuf : public std::streambuf
+{
+  StreamPtr client;
+  char buf[BSIZE];
+
+public:
+  _ostreambuf(StreamPtr strm) : client(strm)
+  {
+    // Make sure we picked the right class
+    assert(client->isWritable);
+
+    // Inform streambuf about our nice buffer
+    setp(buf, buf+BSIZE);
+
+    // Disallow reading
+    setg(NULL,NULL,NULL);
+  }
+
+  /* Sync means to flush (write) all current data to the output
+     stream. It will also set up the entire output buffer to be usable
+     again.
+  */
+  int sync()
+  {
+    // Get the number of bytes that streambuf wants us to write
+    int num = pptr() - pbase();
+    assert(num >= 0);
+
+    // Is there any work to do?
+    if(num == 0) return 0;
+
+    if((int)client->write(pbase(), num) != num)
+      // Inform caller that writing failed
+      return -1;
+
+    // Reset output buffer pointers
+    setp(buf, buf+BSIZE);
+
+    // No error
+    return 0;
+  }
+
+  /* Called whenever the output buffer is full.
+   */
+  int overflow(int c)
+  {
+    // First, write all existing data
+    if(sync()) return EOF;
+
+    // Put the requested character in the next round of output
+    if(c != EOF)
+      {
+        *pptr() = c;
+        pbump(1);
+      }
+
+    // No error
+    return 0;
+  }
+
+  // Seek stream, if the source supports it.
+  std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::out)
+  {
+    if(!client->isSeekable || !client->hasPosition)
+      return -1;
+
+    // Flush data and reset buffers
+    sync();
+
+    // Set stream position
+    client->seek(pos);
+
+    return client->tell();
+  }
+};
+
+MangleIStream::MangleIStream(StreamPtr inp)
+  : std::istream(NULL)
+{
+  assert(inp->isReadable);
+
+  // Pick the right streambuf implementation based on whether the
+  // input supports pointers or not.
+  if(inp->hasPtr)
+    buf = new _ptrstreambuf(inp);
+  else
+    buf = new _istreambuf(inp);
+
+  rdbuf(buf);
+}
+
+MangleIStream::~MangleIStream()
+{
+  delete buf;
+}
+
+MangleOStream::MangleOStream(StreamPtr out)
+  : std::ostream(NULL)
+{
+  assert(out->isWritable);
+  buf = new _ostreambuf(out);
+
+  rdbuf(buf);
+}
+
+MangleOStream::~MangleOStream()
+{
+  // Make sure we don't have lingering data on exit
+  flush();
+  delete buf;
+}
diff --git a/libs/mangle/stream/clients/io_stream.hpp b/libs/mangle/stream/clients/io_stream.hpp
new file mode 100644
index 0000000000..98c6252ed2
--- /dev/null
+++ b/libs/mangle/stream/clients/io_stream.hpp
@@ -0,0 +1,43 @@
+#ifndef MANGLE_STREAM_IOSTREAM_H
+#define MANGLE_STREAM_IOSTREAM_H
+
+#include <assert.h>
+#include "../stream.hpp"
+#include <iostream>
+
+namespace Mangle {
+namespace Stream {
+
+  /** This file contains classes for wrapping an std::istream or
+      std::ostream around a Mangle::Stream.
+
+      This allows you to use Mangle streams in places that require std
+      streams.
+
+      This is much easier than trying to make your own custom streams
+      into iostreams. The std::iostream interface is horrible and NOT
+      designed for easy subclassing. Create a Mangle::Stream instead,
+      and use this wrapper.
+  */
+
+  // An istream wrapping a readable Mangle::Stream. Has extra
+  // optimizations for pointer-based streams.
+  class MangleIStream : public std::istream
+  {
+    std::streambuf *buf;
+  public:
+    MangleIStream(StreamPtr inp);
+    ~MangleIStream();
+  };
+
+  // An ostream wrapping a writable Mangle::Stream.
+  class MangleOStream : public std::ostream
+  {
+    std::streambuf *buf;
+  public:
+    MangleOStream(StreamPtr inp);
+    ~MangleOStream();
+  };
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/clients/ogre_datastream.hpp b/libs/mangle/stream/clients/ogre_datastream.hpp
new file mode 100644
index 0000000000..76a6f20cfb
--- /dev/null
+++ b/libs/mangle/stream/clients/ogre_datastream.hpp
@@ -0,0 +1,68 @@
+#ifndef MANGLE_STREAM_OGRECLIENT_H
+#define MANGLE_STREAM_OGRECLIENT_H
+
+#include <OgreDataStream.h>
+#include <assert.h>
+#include "../stream.hpp"
+
+namespace Mangle {
+namespace Stream {
+
+/** An OGRE DataStream that wraps a Mangle::Stream input.
+
+    This has been built and tested against OGRE 1.6.2. You might have
+    to make your own modifications if you're working with newer (or
+    older) versions.
+ */
+class Mangle2OgreStream : public Ogre::DataStream
+{
+  StreamPtr inp;
+
+  void init()
+    {
+      // Get the size, if possible
+      if(inp->hasSize)
+        mSize = inp->size();
+
+      // Allow writing if inp supports it
+      if(inp->isWritable)
+        mAccess |= Ogre::DataStream::WRITE;
+    }
+
+ public:
+  /// Constructor without name
+  Mangle2OgreStream(StreamPtr _inp)
+    : inp(_inp) { init(); }
+
+  /// Constructor for a named data stream
+  Mangle2OgreStream(const Ogre::String &name, StreamPtr _inp)
+    : Ogre::DataStream(name), inp(_inp) { init(); }
+
+  // Only implement the DataStream functions we have to implement
+
+  size_t read(void *buf, size_t count)
+  { return inp->read(buf,count); }
+
+  size_t write(const void *buf, size_t count)
+  { assert(inp->isWritable); return inp->write(buf,count); }
+
+  void skip(long count)
+    {
+      assert(inp->isSeekable && inp->hasPosition);
+      inp->seek(inp->tell() + count);
+    }
+
+  void seek(size_t pos)
+    { assert(inp->isSeekable); inp->seek(pos); }
+
+  size_t tell() const
+    { assert(inp->hasPosition); return inp->tell(); }
+
+  bool eof() const { return inp->eof(); }
+
+  /// Does nothing
+  void close() {}
+};
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/filters/buffer_stream.hpp b/libs/mangle/stream/filters/buffer_stream.hpp
new file mode 100644
index 0000000000..f037212a38
--- /dev/null
+++ b/libs/mangle/stream/filters/buffer_stream.hpp
@@ -0,0 +1,74 @@
+#ifndef MANGLE_STREAM_BUFFER_H
+#define MANGLE_STREAM_BUFFER_H
+
+#include "../servers/memory_stream.hpp"
+#include <vector>
+
+namespace Mangle {
+namespace Stream {
+
+/** A Stream that reads another Stream into a buffer, and serves it as
+    a MemoryStream. Might be expanded with other capabilities later.
+ */
+
+class BufferStream : public MemoryStream
+{
+  std::vector<char> buffer;
+
+ public:
+  /*
+    input = stream to copy
+    ADD = each read increment (for streams without size())
+   */
+  BufferStream(StreamPtr input, size_t ADD = 32*1024)
+    {
+      assert(input);
+
+      // Allocate memory, read the stream into it. Then call set()
+      if(input->hasSize)
+        {
+          // We assume that we can get the position as well
+          assert(input->hasPosition);
+
+          // Calculate how much is left of the stream
+          size_t left = input->size() - input->tell();
+
+          // Allocate the buffer and fill it
+          buffer.resize(left);
+          input->read(&buffer[0], left);
+        }
+      else
+        {
+          // We DON'T know how big the stream is. We'll have to read
+          // it in increments.
+          size_t len=0, newlen;
+
+          while(!input->eof())
+            {
+              // Read one block
+              newlen = len + ADD;
+              buffer.resize(newlen);
+              size_t read = input->read(&buffer[len], ADD);
+
+              // Increase the total length
+              len += read;
+
+              // If we read less than expected, we should be at the
+              // end of the stream
+              assert(read == ADD || (read < ADD && input->eof()));
+            }
+
+          // Downsize to match the real length
+          buffer.resize(len);
+        }
+
+      // After the buffer has been filled, set up our MemoryStream
+      // ancestor to reference it.
+      set(&buffer[0], buffer.size());
+    }
+};
+
+typedef boost::shared_ptr<BufferStream> BufferStreamPtr;
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/filters/pure_filter.hpp b/libs/mangle/stream/filters/pure_filter.hpp
new file mode 100644
index 0000000000..f0ce91f876
--- /dev/null
+++ b/libs/mangle/stream/filters/pure_filter.hpp
@@ -0,0 +1,46 @@
+#ifndef MANGLE_STREAM_FILTER_H
+#define MANGLE_STREAM_FILTER_H
+
+#include "../stream.hpp"
+
+namespace Mangle {
+namespace Stream {
+
+/** A stream that filters another stream with no changes. Intended as
+    a base class for other filters.
+ */
+class PureFilter : public Stream
+{
+ protected:
+  StreamPtr src;
+
+ public:
+  PureFilter() {}
+  PureFilter(StreamPtr _src)
+  { setStream(_src); }
+
+  void setStream(StreamPtr _src)
+    {
+      src = _src;
+      isSeekable = src->isSeekable;
+      isWritable = src->isWritable;
+      hasPosition = src->hasPosition;
+      hasSize = src->hasSize;
+      hasPtr = src->hasPtr;
+    }
+
+  size_t read(void *buf, size_t count) { return src->read(buf, count); }
+  size_t write(const void *buf, size_t count) { return src->write(buf,count); }
+  void flush() { src->flush(); }
+  void seek(size_t pos) { src->seek(pos); }
+  size_t tell() const { return src->tell(); }
+  size_t size() const { return src->size(); }
+  bool eof() const { return src->eof(); }
+  const void *getPtr() { return src->getPtr(); }
+  const void *getPtr(size_t size) { return src->getPtr(size); }
+  const void *getPtr(size_t pos, size_t size)
+    { return src->getPtr(pos, size); }
+};
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/filters/slice_stream.hpp b/libs/mangle/stream/filters/slice_stream.hpp
new file mode 100644
index 0000000000..6337b9d57d
--- /dev/null
+++ b/libs/mangle/stream/filters/slice_stream.hpp
@@ -0,0 +1,101 @@
+#ifndef MANGLE_STREAM_SLICE_H
+#define MANGLE_STREAM_SLICE_H
+
+#include "../stream.hpp"
+
+namespace Mangle {
+namespace Stream {
+
+/** A Stream that represents a subset (called a slice) of another stream.
+ */
+class SliceStream : public Stream
+{
+  StreamPtr src;
+  size_t offset, length, pos;
+
+ public:
+  SliceStream(StreamPtr _src, size_t _offset, size_t _length)
+    : src(_src), offset(_offset), length(_length), pos(0)
+    {
+      assert(src->hasSize);
+      assert(src->isSeekable);
+
+      // Make sure we can actually fit inside the source stream
+      assert(src->size() >= offset+length);
+
+      isSeekable = true;
+      hasPosition = true;
+      hasSize = true;
+      hasPtr = src->hasPtr;
+      isWritable = src->isWritable;
+    }
+
+  size_t read(void *buf, size_t count)
+  {
+    // Check that we're not reading past our slice
+    if(count > length-pos)
+      count = length-pos;
+
+    // Seek into place and start reading
+    src->seek(offset+pos);
+    count = src->read(buf, count);
+
+    pos += count;
+    assert(pos <= length);
+    return count;
+  }
+
+  // Note that writing to a slice does not allow you to append data,
+  // you may only overwrite existing data.
+  size_t write(const void *buf, size_t count)
+  {
+    assert(isWritable);
+    // Check that we're not reading past our slice
+    if(count > length-pos)
+      count = length-pos;
+
+    // Seek into place and action
+    src->seek(offset+pos);
+    count = src->write(buf, count);
+
+    pos += count;
+    assert(pos <= length);
+    return count;
+  }
+
+  void seek(size_t _pos)
+  {
+    pos = _pos;
+    if(pos > length) pos = length;
+  }
+
+  bool eof() const { return pos == length; }
+  size_t tell() const { return pos; }
+  size_t size() const { return length; }
+  void flush() { src->flush(); }
+
+  const void *getPtr() { return getPtr(0, length); }
+  const void *getPtr(size_t size)
+    {
+      const void *ptr = getPtr(pos, size);
+      seek(pos+size);
+      return ptr;
+    }
+  const void *getPtr(size_t pos, size_t size)
+    {
+      // Boundry checks on pos and size. Bounding the size is
+      // important even when getting pointers, as the source stream
+      // may use the size parameter for something (such as memory
+      // mapping or buffering.)
+      if(pos > length) pos = length;
+      if(pos+size > length) size = length-pos;
+
+      // Ask the source to kindly give us a pointer
+      return src->getPtr(offset+pos, size);
+    }
+};
+
+typedef boost::shared_ptr<SliceStream> SliceStreamPtr;
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/servers/file_stream.hpp b/libs/mangle/stream/servers/file_stream.hpp
new file mode 100644
index 0000000000..314a49642b
--- /dev/null
+++ b/libs/mangle/stream/servers/file_stream.hpp
@@ -0,0 +1,32 @@
+#ifndef MANGLE_STREAM_FILESERVER_H
+#define MANGLE_STREAM_FILESERVER_H
+
+#include "std_stream.hpp"
+#include <fstream>
+#include <stdexcept>
+
+namespace Mangle {
+namespace Stream {
+
+/** Very simple file input stream, based on std::ifstream
+ */
+class FileStream : public StdStream
+{
+  std::ifstream file;
+
+ public:
+  FileStream(const std::string &name)
+    : StdStream(&file)
+    {
+      file.open(name.c_str(), std::ios::binary);
+
+      if(file.fail())
+        throw std::runtime_error("FileStream: failed to open file " + name);
+    }
+  ~FileStream() { file.close(); }
+};
+
+typedef boost::shared_ptr<FileStream> FileStreamPtr;
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/servers/memory_stream.hpp b/libs/mangle/stream/servers/memory_stream.hpp
new file mode 100644
index 0000000000..0849e4a3cd
--- /dev/null
+++ b/libs/mangle/stream/servers/memory_stream.hpp
@@ -0,0 +1,116 @@
+#ifndef MANGLE_STREAM_MEMSERVER_H
+#define MANGLE_STREAM_MEMSERVER_H
+
+#include <assert.h>
+#include "../stream.hpp"
+#include <string.h>
+
+namespace Mangle {
+namespace Stream {
+
+// Do this before the class declaration, since the class itself
+// uses it.
+class MemoryStream;
+typedef boost::shared_ptr<MemoryStream> MemoryStreamPtr;
+
+/** A Stream wrapping a memory buffer
+
+    This will create a fully seekable stream out of any pointer/length
+    pair you give it.
+ */
+class MemoryStream : public Stream
+{
+  const void *data;
+  size_t length, pos;
+
+ public:
+ MemoryStream(const void *ptr, size_t len)
+   : data(ptr), length(len), pos(0)
+  {
+    isSeekable = true;
+    hasPosition = true;
+    hasSize = true;
+    hasPtr = true;
+  }
+
+  MemoryStream()
+    : data(NULL), length(0), pos(0)
+  {
+    isSeekable = true;
+    hasPosition = true;
+    hasSize = true;
+    hasPtr = true;
+  }
+
+  size_t read(void *buf, size_t count)
+  {
+    assert(data != NULL);
+    assert(pos <= length);
+
+    // Don't read more than we have
+    if(count > (length - pos))
+      count = length - pos;
+
+    // Copy data
+    if(count)
+      memcpy(buf, ((char*)data)+pos, count);
+
+    // aaand remember to increase the count
+    pos += count;
+
+    return count;
+  }
+
+  void seek(size_t _pos)
+  {
+    pos = _pos;
+    if(pos > length)
+      pos = length;
+  }
+
+  size_t tell() const { return pos; }
+  size_t size() const { return length; }
+  bool eof() const { return pos == length; }
+
+  const void *getPtr() { return data; }
+  const void *getPtr(size_t size)
+    {
+      // This variant of getPtr must move the position pointer
+      size_t opos = pos;
+      pos += size;
+      if(pos > length) pos = length;
+      return ((char*)data)+opos;
+    }
+  const void *getPtr(size_t pos, size_t size)
+    {
+      if(pos > length) pos = length;
+      return ((char*)data)+pos;
+    }
+
+  // New members in MemoryStream:
+
+  /// Set a new buffer and length. This will rewind the position to zero.
+  void set(const void* _data, size_t _length)
+  {
+    data = _data;
+    length = _length;
+    pos = 0;
+  }
+
+  /// Clone this memory stream
+  /** Make a new stream of the same buffer. If setPos is true, we also
+      set the clone's position to be the same as ours.
+
+      No memory is copied during this operation, the new stream is
+      just another 'view' into the same shared memory buffer.
+  */
+  MemoryStreamPtr clone(bool setPos=false) const
+  {
+    MemoryStreamPtr res(new MemoryStream(data, length));
+    if(setPos) res->seek(pos);
+    return res;
+  }
+};
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/servers/ogre_datastream.hpp b/libs/mangle/stream/servers/ogre_datastream.hpp
new file mode 100644
index 0000000000..a5be98c84a
--- /dev/null
+++ b/libs/mangle/stream/servers/ogre_datastream.hpp
@@ -0,0 +1,37 @@
+#ifndef MANGLE_STREAM_OGRESERVER_H
+#define MANGLE_STREAM_OGRESERVER_H
+
+#include <OgreDataStream.h>
+
+namespace Mangle {
+namespace Stream {
+
+/** A Stream wrapping an OGRE DataStream.
+
+    This has been built and tested against OGRE 1.6.2. You might have
+    to make your own modifications if you're working with newer (or
+    older) versions.
+ */
+class OgreStream : public Stream
+{
+  Ogre::DataStreamPtr inp;
+
+ public:
+ OgreStream(Ogre::DataStreamPtr _inp) : inp(_inp)
+  {
+    isSeekable = true;
+    hasPosition = true;
+    hasSize = true;
+  }
+
+  size_t read(void *buf, size_t count) { return inp->read(buf,count); }
+  void seek(size_t pos) { inp->seek(pos); }
+  size_t tell() const { return inp->tell(); }
+  size_t size() const { return inp->size(); }
+  bool eof() const { return inp->eof(); }
+};
+
+typedef boost::shared_ptr<OgreStream> OgreStreamPtr;
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/servers/outfile_stream.hpp b/libs/mangle/stream/servers/outfile_stream.hpp
new file mode 100644
index 0000000000..8d953d9041
--- /dev/null
+++ b/libs/mangle/stream/servers/outfile_stream.hpp
@@ -0,0 +1,41 @@
+#ifndef MANGLE_OSTREAM_FILESERVER_H
+#define MANGLE_OSTREAM_FILESERVER_H
+
+#include "std_ostream.hpp"
+#include <fstream>
+
+namespace Mangle {
+namespace Stream {
+
+/** File stream based on std::ofstream, only supports writing.
+ */
+class OutFileStream : public StdOStream
+{
+  std::ofstream file;
+
+ public:
+  /**
+     By default we overwrite the file. If append=true, then we will
+     open an existing file and seek to the end instead.
+   */
+  OutFileStream(const std::string &name, bool append=false)
+    : StdOStream(&file)
+    {
+      std::ios::openmode mode = std::ios::binary;
+      if(append)
+        mode |= std::ios::app;
+      else
+        mode |= std::ios::trunc;
+
+      file.open(name.c_str(), mode);
+
+      if(file.fail())
+        throw std::runtime_error("OutFileStream: failed to open file " + name);
+    }
+  ~OutFileStream() { file.close(); }
+};
+
+typedef boost::shared_ptr<OutFileStream> OutFileStreamPtr;
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/servers/phys_stream.hpp b/libs/mangle/stream/servers/phys_stream.hpp
new file mode 100644
index 0000000000..4312ac0416
--- /dev/null
+++ b/libs/mangle/stream/servers/phys_stream.hpp
@@ -0,0 +1,36 @@
+#ifndef MANGLE_STREAM_OGRESERVER_H
+#define MANGLE_STREAM_OGRESERVER_H
+
+#include <physfs.h>
+
+namespace Mangle {
+namespace Stream {
+
+/// A Stream wrapping a PHYSFS_file stream from the PhysFS library.
+class PhysFile : public Stream
+{
+  PHYSFS_file *file;
+
+ public:
+  PhysFile(PHYSFS_file *inp) : file(inp)
+    {
+      isSeekable = true;
+      hasPosition = true;
+      hasSize = true;
+    }
+
+  ~PhysFile() { PHYSFS_close(file); }
+
+  size_t read(void *buf, size_t count)
+    { return PHYSFS_read(file, buf, 1, count); }
+
+  void seek(size_t pos) { PHYSFS_seek(file, pos); }
+  size_t tell() const { return PHYSFS_tell(file); }
+  size_t size() const { return PHYSFS_fileLength(file); }
+  bool eof() const { return PHYSFS_eof(file); }
+};
+
+typedef boost::shared_ptr<PhysFile> PhysFilePtr;
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/servers/std_ostream.hpp b/libs/mangle/stream/servers/std_ostream.hpp
new file mode 100644
index 0000000000..f406e1a934
--- /dev/null
+++ b/libs/mangle/stream/servers/std_ostream.hpp
@@ -0,0 +1,78 @@
+#ifndef MANGLE_OSTREAM_STDIOSERVER_H
+#define MANGLE_OSTREAM_STDIOSERVER_H
+
+#include "../stream.hpp"
+#include <iostream>
+#include <stdexcept>
+
+namespace Mangle {
+namespace Stream {
+
+/** Simple wrapper for std::ostream, only supports output.
+ */
+class StdOStream : public Stream
+{
+  std::ostream *inf;
+
+  static void fail(const std::string &msg)
+  { throw std::runtime_error("StdOStream: " + msg); }
+
+ public:
+ StdOStream(std::ostream *_inf)
+   : inf(_inf)
+  {
+    isSeekable = true;
+    hasPosition = true;
+    hasSize = true;
+    isWritable = true;
+    isReadable = false;
+  }
+
+  size_t write(const void* buf, size_t len)
+  {
+    inf->write((const char*)buf, len);
+    if(inf->fail())
+      fail("error writing to stream");
+    // Just return len, but that is ok. The only cases where we would
+    // return less than len is when an error occured.
+    return len;
+  }
+
+  void flush()
+  {
+    inf->flush();
+  }
+
+  void seek(size_t pos)
+  {
+    inf->seekp(pos);
+    if(inf->fail())
+      fail("seek error");
+  }
+
+  size_t tell() const
+  // Hack around the fact that ifstream->tellp() isn't const
+  { return ((StdOStream*)this)->inf->tellp(); }
+
+  size_t size() const
+  {
+    // Use the standard iostream size hack, terrible as it is.
+    std::streampos pos = inf->tellp();
+    inf->seekp(0, std::ios::end);
+    size_t res = inf->tellp();
+    inf->seekp(pos);
+
+    if(inf->fail())
+      fail("could not get stream size");
+
+    return res;
+  }
+
+  bool eof() const
+  { return inf->eof(); }
+};
+
+typedef boost::shared_ptr<StdOStream> StdOStreamPtr;
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/servers/std_stream.hpp b/libs/mangle/stream/servers/std_stream.hpp
new file mode 100644
index 0000000000..163f023f60
--- /dev/null
+++ b/libs/mangle/stream/servers/std_stream.hpp
@@ -0,0 +1,70 @@
+#ifndef MANGLE_STREAM_STDIOSERVER_H
+#define MANGLE_STREAM_STDIOSERVER_H
+
+#include "../stream.hpp"
+#include <iostream>
+#include <stdexcept>
+
+namespace Mangle {
+namespace Stream {
+
+/** Simple wrapper for std::istream.
+ */
+class StdStream : public Stream
+{
+  std::istream *inf;
+
+  static void fail(const std::string &msg)
+  { throw std::runtime_error("StdStream: " + msg); }
+
+ public:
+ StdStream(std::istream *_inf)
+   : inf(_inf)
+  {
+    isSeekable = true;
+    hasPosition = true;
+    hasSize = true;
+  }
+
+  size_t read(void* buf, size_t len)
+  {
+    inf->read((char*)buf, len);
+    if(inf->bad())
+      fail("error reading from stream");
+    return inf->gcount();
+  }
+
+  void seek(size_t pos)
+  {
+    inf->clear();
+    inf->seekg(pos);
+    if(inf->fail())
+      fail("seek error");
+  }
+
+  size_t tell() const
+  // Hack around the fact that ifstream->tellg() isn't const
+  { return ((StdStream*)this)->inf->tellg(); }
+
+  size_t size() const
+  {
+    // Use the standard iostream size hack, terrible as it is.
+    std::streampos pos = inf->tellg();
+    inf->seekg(0, std::ios::end);
+    size_t res = inf->tellg();
+    inf->seekg(pos);
+
+    if(inf->fail())
+      fail("could not get stream size");
+
+    return res;
+  }
+
+  bool eof() const
+  { return inf->eof(); }
+};
+
+typedef boost::shared_ptr<StdStream> StdStreamPtr;
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/stream.hpp b/libs/mangle/stream/stream.hpp
new file mode 100644
index 0000000000..2ee4fcbd8d
--- /dev/null
+++ b/libs/mangle/stream/stream.hpp
@@ -0,0 +1,103 @@
+#ifndef MANGLE_STREAM_INPUT_H
+#define MANGLE_STREAM_INPUT_H
+
+#include <stdlib.h>
+#include "../tools/shared_ptr.hpp"
+#include <assert.h>
+
+namespace Mangle {
+namespace Stream {
+
+/// An abstract interface for a stream data.
+class Stream
+{
+ public:
+  // Feature options. These should be set in the constructor.
+
+  /// If true, seek() works
+  bool isSeekable;
+
+  /// If true, tell() works
+  bool hasPosition;
+
+  /// If true, size() works
+  bool hasSize;
+
+  /// If true, write() works. Writing through pointer operations is
+  /// not (yet) supported.
+  bool isWritable;
+
+  /// If true, read() and eof() works.
+  bool isReadable;
+
+  /// If true, the getPtr() functions work
+  bool hasPtr;
+
+  /// Initialize all bools to false by default, except isReadable.
+  Stream() :
+    isSeekable(false), hasPosition(false), hasSize(false),
+    isWritable(false), isReadable(true), hasPtr(false) {}
+
+  /// Virtual destructor
+  virtual ~Stream() {}
+
+  /** Read a given number of bytes from the stream. Returns the actual
+      number read. If the return value is less than count, then the
+      stream is empty or an error occured. Only required for readable
+      streams.
+  */
+  virtual size_t read(void* buf, size_t count) { assert(0); return 0; }
+
+  /** Write a given number of bytes from the stream. Semantics is
+      similar to read(). Only valid if isWritable is true.
+
+      The returned value is the number of bytes written. However in
+      most cases, unlike for read(), a write-count less than requested
+      usually indicates an error. The implementation should throw such
+      errors as exceptions rather than expect the caller to handle
+      them.
+
+      Since most implementations do NOT support writing we default to
+      an assert(0) here.
+  */
+  virtual size_t write(const void *buf, size_t count) { assert(0); return 0; }
+
+  /// Flush an output stream. Does nothing for non-writing streams.
+  virtual void flush() {}
+
+  /// Seek to an absolute position in this stream. Not all streams are
+  /// seekable.
+  virtual void seek(size_t pos) { assert(0); }
+
+  /// Get the current position in the stream. Non-seekable streams are
+  /// not required to keep track of this.
+  virtual size_t tell() const { assert(0); return 0; }
+
+  /// Return the total size of the stream. For streams hasSize is
+  /// false, size() should fail in some way, since it is an error to
+  /// call it in those cases.
+  virtual size_t size() const { assert(0); return 0; }
+
+  /// Returns true if the stream is empty. Required for readable
+  /// streams.
+  virtual bool eof() const { assert(0); return 0; }
+
+  /// Return a pointer to the entire stream. This function (and the
+  /// other getPtr() variants below) should only be implemented for
+  /// memory-based streams where using them would be an optimization.
+  virtual const void *getPtr() { assert(0); return NULL; }
+
+  /// Get a pointer to a memory region of 'size' bytes starting from
+  /// position 'pos'
+  virtual const void *getPtr(size_t pos, size_t size) { assert(0); return NULL; }
+
+  /// Get a pointer to a memory region of 'size' bytes from the
+  /// current position. Unlike the two other getPtr variants, this
+  /// will advance the position past the returned area.
+  virtual const void *getPtr(size_t size) { assert(0); return NULL; }
+};
+
+typedef boost::shared_ptr<Stream> StreamPtr;
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/stream/tests/.gitignore b/libs/mangle/stream/tests/.gitignore
new file mode 100644
index 0000000000..9dfd618e2c
--- /dev/null
+++ b/libs/mangle/stream/tests/.gitignore
@@ -0,0 +1,2 @@
+*_test
+test.file
diff --git a/libs/mangle/stream/tests/Makefile b/libs/mangle/stream/tests/Makefile
new file mode 100644
index 0000000000..5f43978190
--- /dev/null
+++ b/libs/mangle/stream/tests/Makefile
@@ -0,0 +1,34 @@
+GCC=g++ -I../ -Wall -Werror
+
+all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test iostream_test
+
+I_OGRE=$(shell pkg-config --cflags OGRE)
+L_OGRE=$(shell pkg-config --libs OGRE)
+L_AUDIERE=-laudiere
+
+ogre_client_test: ogre_client_test.cpp ../stream.hpp ../clients/ogre_datastream.hpp
+	$(GCC) $< -o $@ $(I_OGRE) $(L_OGRE)
+
+audiere_client_test: audiere_client_test.cpp ../stream.hpp ../clients/audiere_file.hpp ../clients/audiere_file.cpp
+	$(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE)
+
+iostream_test: iostream_test.cpp ../clients/io_stream.cpp
+	$(GCC) $^ -o $@
+
+file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp
+	$(GCC) $< -o $@
+
+file_write_test: file_write_test.cpp ../stream.hpp ../servers/outfile_stream.hpp ../servers/std_ostream.hpp
+	$(GCC) $< -o $@
+
+memory_server_test: memory_server_test.cpp ../stream.hpp ../servers/memory_stream.hpp
+	$(GCC) $< -o $@
+
+buffer_filter_test: buffer_filter_test.cpp ../stream.hpp ../servers/memory_stream.hpp ../filters/buffer_stream.hpp
+	$(GCC) $< -o $@
+
+slice_filter_test: slice_filter_test.cpp ../stream.hpp ../servers/memory_stream.hpp ../filters/slice_stream.hpp
+	$(GCC) $< -o $@
+
+clean:
+	rm *_test
diff --git a/libs/mangle/stream/tests/audiere_client_test.cpp b/libs/mangle/stream/tests/audiere_client_test.cpp
new file mode 100644
index 0000000000..82be569cc3
--- /dev/null
+++ b/libs/mangle/stream/tests/audiere_client_test.cpp
@@ -0,0 +1,34 @@
+#include "../servers/memory_stream.hpp"
+#include "../clients/audiere_file.hpp"
+#include <audiere.h>
+#include <iostream>
+
+using namespace Mangle::Stream;
+using namespace audiere;
+using namespace std;
+
+int main()
+{
+  char str[12];
+  memset(str, 0, 12);
+  StreamPtr inp(new MemoryStream("hello world", 11));
+  FilePtr p(new AudiereFile(inp));
+  cout << "pos=" << p->tell() << endl;
+  p->read(str, 2);
+  cout << "2 bytes: " << str << endl;
+  cout << "pos=" << p->tell() << endl;
+  p->seek(4, File::BEGIN);
+  cout << "pos=" << p->tell() << endl;
+  p->read(str, 3);
+  cout << "3 bytes: " << str << endl;
+  p->seek(-1, File::CURRENT);
+  cout << "pos=" << p->tell() << endl;
+  p->seek(-4, File::END);
+  cout << "pos=" << p->tell() << endl;
+  p->read(str, 4);
+  cout << "last 4 bytes: " << str << endl;
+  p->seek(0, File::BEGIN);
+  p->read(str, 11);
+  cout << "entire stream: " << str << endl;
+  return 0;
+}
diff --git a/libs/mangle/stream/tests/buffer_filter_test.cpp b/libs/mangle/stream/tests/buffer_filter_test.cpp
new file mode 100644
index 0000000000..e53bda6510
--- /dev/null
+++ b/libs/mangle/stream/tests/buffer_filter_test.cpp
@@ -0,0 +1,41 @@
+#include <iostream>
+#include <string.h>
+
+#include "../filters/buffer_stream.hpp"
+
+using namespace Mangle::Stream;
+using namespace std;
+
+int main()
+{
+  StreamPtr orig (new MemoryStream("hello world", 11));
+  StreamPtr inp (new BufferStream(orig));
+
+  cout << "Size: " << inp->size() << endl;
+  cout << "Pos: " << inp->tell() << "\nSeeking...\n";
+  inp->seek(3);
+  cout << "Pos: " << inp->tell() << endl;
+  char data[12];
+  memset(data, 0, 12);
+  cout << "Reading: " << inp->read(data, 4) << endl;
+  cout << "Four bytes: " << data << endl;
+  cout << "Eof: " << inp->eof() << endl;
+  cout << "Pos: " << inp->tell() << "\nSeeking again...\n";
+  inp->seek(33);
+  cout << "Pos: " << inp->tell() << endl;
+  cout << "Eof: " << inp->eof() << "\nSeek to 6\n";
+  inp->seek(6);
+  cout << "Eof: " << inp->eof() << endl;
+  cout << "Pos: " << inp->tell() << endl;
+  cout << "Over-reading: " << inp->read(data, 200) << endl;
+  cout << "Result: " << data << endl;
+  cout << "Eof: " << inp->eof() << endl;
+  cout << "Pos: " << inp->tell() << endl;
+  inp->seek(0);
+  cout << "Finally, reading the entire string: " << inp->read(data,11) << endl;
+  cout << "Result: " << data << endl;
+  cout << "Eof: " << inp->eof() << endl;
+  cout << "Pos: " << inp->tell() << endl;
+
+  return 0;
+}
diff --git a/libs/mangle/stream/tests/file_server_test.cpp b/libs/mangle/stream/tests/file_server_test.cpp
new file mode 100644
index 0000000000..3e1e3cfa57
--- /dev/null
+++ b/libs/mangle/stream/tests/file_server_test.cpp
@@ -0,0 +1,18 @@
+#include "../servers/file_stream.hpp"
+#include <iostream>
+
+using namespace Mangle::Stream;
+using namespace std;
+
+int main()
+{
+  StreamPtr inp(new FileStream("file_server_test.cpp"));
+
+  char buf[21];
+  buf[20] = 0;
+  cout << "pos=" << inp->tell() << " eof=" << inp->eof() << endl;
+  inp->read(buf, 20);
+  cout << "First 20 bytes: " << buf << endl;
+  cout << "pos=" << inp->tell() << " eof=" << inp->eof() << endl;
+  return 0;
+}
diff --git a/libs/mangle/stream/tests/file_write_test.cpp b/libs/mangle/stream/tests/file_write_test.cpp
new file mode 100644
index 0000000000..c6c61fccae
--- /dev/null
+++ b/libs/mangle/stream/tests/file_write_test.cpp
@@ -0,0 +1,41 @@
+#include "../servers/outfile_stream.hpp"
+#include <iostream>
+
+using namespace Mangle::Stream;
+using namespace std;
+
+void print(Stream &str)
+{
+  cout << "size=" << str.size()
+       << " pos=" << str.tell()
+       << " eof=" << str.eof()
+       << endl;
+}
+
+int main()
+{
+  {
+    cout << "\nCreating file\n";
+    OutFileStream out("test.file");
+    print(out);
+    out.write("hello",5);
+    print(out);
+  }
+
+  {
+    cout << "\nAppending to file\n";
+    OutFileStream out("test.file", true);
+    print(out);
+    out.write(" again\n",7);
+    print(out);
+  }
+
+  {
+    cout << "\nOverwriting file\n";
+    OutFileStream out("test.file");
+    print(out);
+    out.write("overwrite!\n",11);
+    print(out);
+  }
+  return 0;
+}
diff --git a/libs/mangle/stream/tests/iostream_test.cpp b/libs/mangle/stream/tests/iostream_test.cpp
new file mode 100644
index 0000000000..60648c6c5f
--- /dev/null
+++ b/libs/mangle/stream/tests/iostream_test.cpp
@@ -0,0 +1,176 @@
+#include <iostream>
+#include "../clients/io_stream.hpp"
+#include "../servers/memory_stream.hpp"
+
+using namespace Mangle::Stream;
+using namespace std;
+
+void test1()
+{
+  cout << "Testing ASCII reading from memory:\n";
+  StreamPtr input(new MemoryStream("hello you world you", 19));
+  MangleIStream inp(input);
+
+  string str;
+  while(!inp.eof())
+    {
+      inp >> str;
+      cout << "Got: " << str << endl;
+    }
+}
+
+class Dummy : public Stream
+{
+  int count;
+
+public:
+
+  Dummy() : count(0)
+  {
+  }
+
+  size_t read(void *ptr, size_t num)
+  {
+    char *p = (char*)ptr;
+    char *start = p;
+    for(; (count < 2560) && (p-start < (int)num); count++)
+      {
+        *p = count / 10;
+        p++;
+      }
+    return p-start;
+  }
+
+  bool eof() const { return count == 2560; }
+};
+
+void test2()
+{
+  cout << "\nTesting binary reading from non-memory:\n";
+
+  StreamPtr input(new Dummy);
+  MangleIStream inp(input);
+
+  int x = 0;
+  while(!inp.eof())
+    {
+      unsigned char buf[5];
+      inp.read((char*)buf,5);
+
+      // istream doesn't set eof() until we read _beyond_ the end of
+      // the stream, so we need an extra check.
+      if(inp.gcount() == 0) break;
+
+      /*
+      for(int i=0;i<5;i++)
+        cout << (int)buf[i] << " ";
+      cout << endl;
+      */
+
+      assert(buf[4] == buf[0]);
+      assert(buf[0] == x/2);
+      x++;
+    }
+  cout << "  Done\n";
+}
+
+struct Dummy2 : Stream
+{
+  Dummy2()
+  {
+    isWritable = true;
+    isReadable = false;
+  }
+
+  size_t write(const void *ptr, size_t num)
+  {
+    const char *p = (const char*)ptr;
+    cout << "    Got: ";
+    for(unsigned i=0;i<num;i++)
+      cout << *(p++) << " ";
+    cout << endl;
+    return num;
+  }
+};
+
+void test3()
+{
+  cout << "\nWriting to dummy stream:\n";
+
+  cout << "  Pure dummy test:\n";
+  StreamPtr output(new Dummy2);
+  output->write("testing", 7);
+
+  cout << "  Running through MangleOStream:\n";
+  MangleOStream out(output);
+  out << "hello";
+  out << " - are you ok?";
+  cout << "  Flushing:\n";
+  out.flush();
+
+  cout << "  Writing a hell of a lot of characters:\n";
+  for(int i=0; i<127; i++)
+    out << "xxxxxxxx"; // 127 * 8 = 1016
+  out << "fffffff"; // +7 = 1023
+  cout << "  Just one more:\n";
+  out << "y";
+  cout << "  And oooone more:\n";
+  out << "z";
+
+  cout << "  Flushing again:\n";
+  out.flush();
+  cout << "  Writing some more and exiting:\n";
+  out << "blah bleh blob";
+}
+
+struct Dummy3 : Stream
+{
+  int pos;
+
+  Dummy3() : pos(0)
+  {
+    hasPosition = true;
+    isSeekable = true;
+  }
+
+  size_t read(void*, size_t num)
+  {
+    cout << "    Reading " << num << " bytes from " << pos << endl;
+    pos += num;
+    return num;
+  }
+
+  void seek(size_t npos) { pos = npos; }
+  size_t tell() const { return pos; }
+};
+
+void test4()
+{
+  cout << "\nTesting seeking;\n";
+  StreamPtr input(new Dummy3);
+
+  cout << "  Direct reading:\n";
+  input->read(0,10);
+  input->read(0,5);
+
+  MangleIStream inp(input);
+
+  cout << "  Reading from istream:\n";
+  char buf[20];
+  inp.read(buf, 20);
+  inp.read(buf, 20);
+  inp.read(buf, 20);
+
+  cout << "  Seeking to 30 and reading again:\n";
+  inp.seekg(30);
+  inp.read(buf, 20);
+}
+
+int main()
+{
+  test1();
+  test2();
+  test3();
+  test4();
+  return 0;
+}
diff --git a/libs/mangle/stream/tests/memory_server_test.cpp b/libs/mangle/stream/tests/memory_server_test.cpp
new file mode 100644
index 0000000000..24d3bb17e0
--- /dev/null
+++ b/libs/mangle/stream/tests/memory_server_test.cpp
@@ -0,0 +1,42 @@
+#include <iostream>
+#include <string.h>
+
+#include "../servers/memory_stream.hpp"
+
+using namespace Mangle::Stream;
+using namespace std;
+
+int main()
+{
+  Stream* inp = new MemoryStream("hello world\0", 12);
+
+  cout << "Size: " << inp->size() << endl;
+  cout << "Pos: " << inp->tell() << "\nSeeking...\n";
+  inp->seek(3);
+  cout << "Pos: " << inp->tell() << endl;
+  char data[12];
+  memset(data, 0, 12);
+  cout << "Reading: " << inp->read(data, 4) << endl;
+  cout << "Four bytes: " << data << endl;
+  cout << "Eof: " << inp->eof() << endl;
+  cout << "Pos: " << inp->tell() << "\nSeeking again...\n";
+  inp->seek(33);
+  cout << "Pos: " << inp->tell() << endl;
+  cout << "Eof: " << inp->eof() << "\nSeek to 6\n";
+  inp->seek(6);
+  cout << "Eof: " << inp->eof() << endl;
+  cout << "Pos: " << inp->tell() << endl;
+  cout << "Over-reading: " << inp->read(data, 200) << endl;
+  cout << "Result: " << data << endl;
+  cout << "Eof: " << inp->eof() << endl;
+  cout << "Pos: " << inp->tell() << endl;
+  inp->seek(0);
+  cout << "Finally, reading the entire string: " << inp->read(data,11) << endl;
+  cout << "Result: " << data << endl;
+  cout << "Eof: " << inp->eof() << endl;
+  cout << "Pos: " << inp->tell() << endl;
+
+  cout << "Entire stream from pointer: " << (char*)inp->getPtr() << endl;
+
+  return 0;
+}
diff --git a/libs/mangle/stream/tests/ogre_client_test.cpp b/libs/mangle/stream/tests/ogre_client_test.cpp
new file mode 100644
index 0000000000..c8d0442c01
--- /dev/null
+++ b/libs/mangle/stream/tests/ogre_client_test.cpp
@@ -0,0 +1,22 @@
+#include "../servers/memory_stream.hpp"
+#include "../clients/ogre_datastream.hpp"
+#include <iostream>
+
+using namespace Mangle::Stream;
+using namespace Ogre;
+using namespace std;
+
+int main()
+{
+  StreamPtr inp(new MemoryStream("hello world", 11));
+  DataStreamPtr p(new Mangle2OgreStream("hello", inp));
+  cout << "Name: " << p->getName() << endl;
+  cout << "As string: " << p->getAsString() << endl;
+  cout << "pos=" << p->tell() << " eof=" << p->eof() << endl;
+  p->seek(0);
+  cout << "pos=" << p->tell() << " eof=" << p->eof() << endl;
+  p->skip(5);
+  p->skip(-2);
+  cout << "pos=" << p->tell() << " eof=" << p->eof() << endl;
+  return 0;
+}
diff --git a/libs/mangle/stream/tests/output/audiere_client_test.out b/libs/mangle/stream/tests/output/audiere_client_test.out
new file mode 100644
index 0000000000..2130db9fd0
--- /dev/null
+++ b/libs/mangle/stream/tests/output/audiere_client_test.out
@@ -0,0 +1,9 @@
+pos=0
+2 bytes: he
+pos=2
+pos=4
+3 bytes: o w
+pos=6
+pos=7
+last 4 bytes: orld
+entire stream: hello world
diff --git a/libs/mangle/stream/tests/output/buffer_filter_test.out b/libs/mangle/stream/tests/output/buffer_filter_test.out
new file mode 100644
index 0000000000..0ca252f25e
--- /dev/null
+++ b/libs/mangle/stream/tests/output/buffer_filter_test.out
@@ -0,0 +1,22 @@
+Size: 11
+Pos: 0
+Seeking...
+Pos: 3
+Reading: 4
+Four bytes: lo w
+Eof: 0
+Pos: 7
+Seeking again...
+Pos: 11
+Eof: 1
+Seek to 6
+Eof: 0
+Pos: 6
+Over-reading: 5
+Result: world
+Eof: 1
+Pos: 11
+Finally, reading the entire string: 11
+Result: hello world
+Eof: 1
+Pos: 11
diff --git a/libs/mangle/stream/tests/output/file_server_test.out b/libs/mangle/stream/tests/output/file_server_test.out
new file mode 100644
index 0000000000..240f066a31
--- /dev/null
+++ b/libs/mangle/stream/tests/output/file_server_test.out
@@ -0,0 +1,3 @@
+pos=0 eof=0
+First 20 bytes: #include "../servers
+pos=20 eof=0
diff --git a/libs/mangle/stream/tests/output/file_write_test.out b/libs/mangle/stream/tests/output/file_write_test.out
new file mode 100644
index 0000000000..34b07c49f6
--- /dev/null
+++ b/libs/mangle/stream/tests/output/file_write_test.out
@@ -0,0 +1,12 @@
+
+Creating file
+size=0 pos=0 eof=0
+size=5 pos=5 eof=0
+
+Appending to file
+size=5 pos=5 eof=0
+size=12 pos=12 eof=0
+
+Overwriting file
+size=0 pos=0 eof=0
+size=11 pos=11 eof=0
diff --git a/libs/mangle/stream/tests/output/iostream_test.out b/libs/mangle/stream/tests/output/iostream_test.out
new file mode 100644
index 0000000000..b6da80c80d
--- /dev/null
+++ b/libs/mangle/stream/tests/output/iostream_test.out
@@ -0,0 +1,32 @@
+Testing ASCII reading from memory:
+Got: hello
+Got: you
+Got: world
+Got: you
+
+Testing binary reading from non-memory:
+  Done
+
+Writing to dummy stream:
+  Pure dummy test:
+    Got: t e s t i n g 
+  Running through MangleOStream:
+  Flushing:
+    Got: h e l l o   -   a r e   y o u   o k ? 
+  Writing a hell of a lot of characters:
+  Just one more:
+  And oooone more:
+    Got: x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x f f f f f f f y 
+  Flushing again:
+    Got: z 
+  Writing some more and exiting:
+    Got: b l a h   b l e h   b l o b 
+
+Testing seeking;
+  Direct reading:
+    Reading 10 bytes from 0
+    Reading 5 bytes from 10
+  Reading from istream:
+    Reading 1024 bytes from 15
+  Seeking to 30 and reading again:
+    Reading 1024 bytes from 30
diff --git a/libs/mangle/stream/tests/output/memory_server_test.out b/libs/mangle/stream/tests/output/memory_server_test.out
new file mode 100644
index 0000000000..7cd9533e78
--- /dev/null
+++ b/libs/mangle/stream/tests/output/memory_server_test.out
@@ -0,0 +1,23 @@
+Size: 12
+Pos: 0
+Seeking...
+Pos: 3
+Reading: 4
+Four bytes: lo w
+Eof: 0
+Pos: 7
+Seeking again...
+Pos: 12
+Eof: 1
+Seek to 6
+Eof: 0
+Pos: 6
+Over-reading: 6
+Result: world
+Eof: 1
+Pos: 12
+Finally, reading the entire string: 11
+Result: hello world
+Eof: 0
+Pos: 11
+Entire stream from pointer: hello world
diff --git a/libs/mangle/stream/tests/output/ogre_client_test.out b/libs/mangle/stream/tests/output/ogre_client_test.out
new file mode 100644
index 0000000000..62cd14604c
--- /dev/null
+++ b/libs/mangle/stream/tests/output/ogre_client_test.out
@@ -0,0 +1,5 @@
+Name: hello
+As string: hello world
+pos=11 eof=1
+pos=0 eof=0
+pos=3 eof=0
diff --git a/libs/mangle/stream/tests/output/slice_filter_test.out b/libs/mangle/stream/tests/output/slice_filter_test.out
new file mode 100644
index 0000000000..6d84704a77
--- /dev/null
+++ b/libs/mangle/stream/tests/output/slice_filter_test.out
@@ -0,0 +1,36 @@
+
+Slice 1:
+--------
+Size: 6
+Pos: 0
+Seeking...
+Reading 6 bytes
+Result: hello
+Pos: 6
+Eof: 1
+Seeking:
+Pos: 2
+Eof: 0
+Reading 4 bytes
+Result: llo
+Pos: 6
+Eof: 1
+Entire stream as pointer: hello
+
+Slice 2:
+--------
+Size: 6
+Pos: 0
+Seeking...
+Reading 6 bytes
+Result: world
+Pos: 6
+Eof: 1
+Seeking:
+Pos: 2
+Eof: 0
+Reading 4 bytes
+Result: rld
+Pos: 6
+Eof: 1
+Entire stream as pointer: world
diff --git a/libs/mangle/stream/tests/slice_filter_test.cpp b/libs/mangle/stream/tests/slice_filter_test.cpp
new file mode 100644
index 0000000000..da90e24ad0
--- /dev/null
+++ b/libs/mangle/stream/tests/slice_filter_test.cpp
@@ -0,0 +1,42 @@
+#include <iostream>
+#include <string.h>
+
+#include "../filters/slice_stream.hpp"
+#include "../servers/memory_stream.hpp"
+
+using namespace Mangle::Stream;
+using namespace std;
+
+void test(StreamPtr inp)
+{
+  cout << "Size: " << inp->size() << endl;
+  cout << "Pos: " << inp->tell() << "\nSeeking...\n";
+  char data[6];
+  memset(data, 0, 6);
+  cout << "Reading " << inp->read(data, 6) << " bytes\n";
+  cout << "Result: " << data << endl;
+  cout << "Pos: " << inp->tell() << endl;
+  cout << "Eof: " << inp->eof() << endl;
+  inp->seek(2);
+  cout << "Seeking:\nPos: " << inp->tell() << endl;
+  cout << "Eof: " << inp->eof() << endl;
+  cout << "Reading " << inp->read(data, 6) << " bytes\n";
+  cout << "Result: " << data << endl;
+  cout << "Pos: " << inp->tell() << endl;
+  cout << "Eof: " << inp->eof() << endl;
+  cout << "Entire stream as pointer: " << (char*)inp->getPtr() << endl;
+}
+
+int main()
+{
+  StreamPtr orig (new MemoryStream("hello\0world\0", 12));
+  StreamPtr slice1 (new SliceStream(orig,0,6));
+  StreamPtr slice2 (new SliceStream(orig,6,6));
+
+  cout << "\nSlice 1:\n--------\n";
+  test(slice1);
+  cout << "\nSlice 2:\n--------\n";
+  test(slice2);
+
+  return 0;
+}
diff --git a/libs/mangle/stream/tests/test.sh b/libs/mangle/stream/tests/test.sh
new file mode 100755
index 0000000000..2d07708adc
--- /dev/null
+++ b/libs/mangle/stream/tests/test.sh
@@ -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
diff --git a/libs/mangle/testall.sh b/libs/mangle/testall.sh
new file mode 100755
index 0000000000..b93fee2159
--- /dev/null
+++ b/libs/mangle/testall.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+function run()
+{
+  echo "TESTING $1"
+  cd "$1/tests/"
+  ./test.sh
+  cd ../../
+}
+
+run stream
+run vfs
+run sound
+run input
+run rend2d
+run .
diff --git a/libs/mangle/tests/.gitignore b/libs/mangle/tests/.gitignore
new file mode 100644
index 0000000000..8144904045
--- /dev/null
+++ b/libs/mangle/tests/.gitignore
@@ -0,0 +1 @@
+*_test
diff --git a/libs/mangle/tests/Makefile b/libs/mangle/tests/Makefile
new file mode 100644
index 0000000000..d912c0784b
--- /dev/null
+++ b/libs/mangle/tests/Makefile
@@ -0,0 +1,14 @@
+GCC=g++ -I../
+
+all: ogrevfs_audiere_openal_test
+
+I_OGRE=$(shell pkg-config --cflags OGRE)
+L_OGRE=$(shell pkg-config --libs OGRE)
+L_OPENAL=$(shell pkg-config --libs openal)
+L_AUDIERE=-laudiere
+
+ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/servers/ogre_vfs.cpp ../sound/sources/audiere_source.cpp ../sound/outputs/openal_out.cpp ../stream/clients/audiere_file.cpp
+	$(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) $(L_OPENAL) $(L_AUDIERE)
+
+clean:
+	rm *_test
diff --git a/libs/mangle/tests/ogrevfs_audiere_openal_test.cpp b/libs/mangle/tests/ogrevfs_audiere_openal_test.cpp
new file mode 100644
index 0000000000..4936538c55
--- /dev/null
+++ b/libs/mangle/tests/ogrevfs_audiere_openal_test.cpp
@@ -0,0 +1,49 @@
+/*
+  This example combines:
+
+  - the OGRE VFS system (to read from zip)
+  - Audiere (for decoding sound data)
+  - OpenAL (for sound playback)
+
+ */
+
+#include "sound/filters/openal_audiere.hpp"
+#include "vfs/servers/ogre_vfs.hpp"
+#include <Ogre.h>
+#include <iostream>
+
+using namespace Ogre;
+using namespace Mangle;
+using namespace std;
+
+int main()
+{
+  // Disable Ogre logging
+  new LogManager;
+  Log *log = LogManager::getSingleton().createLog("");
+  log->setDebugOutputEnabled(false);
+
+  // Set up Root
+  Root *root = new Root("","","");
+
+  // Add zip file with a sound in it
+  root->addResourceLocation("sound.zip", "Zip", "General");
+
+  // Ogre file system
+  VFS::OgreVFS vfs;
+
+  // The main sound system
+  Sound::OpenAL_Audiere_Factory mg;
+  Sound::SoundPtr snd = mg.load(vfs.open("owl.ogg"));
+
+  cout << "Playing 'owl.ogg' from 'sound.zip'\n";
+  snd->play();
+
+  while(snd->isPlaying())
+    {
+      usleep(10000);
+      if(mg.needsUpdate) mg.update();
+    }
+
+  return 0;
+}
diff --git a/libs/mangle/tests/output/ogrevfs_audiere_openal_test.out b/libs/mangle/tests/output/ogrevfs_audiere_openal_test.out
new file mode 100644
index 0000000000..28ea8a71b7
--- /dev/null
+++ b/libs/mangle/tests/output/ogrevfs_audiere_openal_test.out
@@ -0,0 +1 @@
+Playing 'owl.ogg' from 'sound.zip'
diff --git a/libs/mangle/tests/sound.zip b/libs/mangle/tests/sound.zip
new file mode 100644
index 0000000000..fd32b35299
Binary files /dev/null and b/libs/mangle/tests/sound.zip differ
diff --git a/libs/mangle/tests/test.sh b/libs/mangle/tests/test.sh
new file mode 100755
index 0000000000..2d07708adc
--- /dev/null
+++ b/libs/mangle/tests/test.sh
@@ -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
diff --git a/libs/mangle/tools/shared_ptr.hpp b/libs/mangle/tools/shared_ptr.hpp
new file mode 100644
index 0000000000..3d073fc24f
--- /dev/null
+++ b/libs/mangle/tools/shared_ptr.hpp
@@ -0,0 +1,3 @@
+// This file should include whatever it needs to define the boost/tr1
+// shared_ptr<> and weak_ptr<> templates.
+#include <boost/smart_ptr.hpp>
diff --git a/libs/mangle/vfs/clients/ogre_archive.cpp b/libs/mangle/vfs/clients/ogre_archive.cpp
new file mode 100644
index 0000000000..2d3f7c5204
--- /dev/null
+++ b/libs/mangle/vfs/clients/ogre_archive.cpp
@@ -0,0 +1,85 @@
+#include "ogre_archive.hpp"
+
+#include "../../stream/clients/ogre_datastream.hpp"
+
+using namespace Mangle::VFS;
+using namespace Mangle::Stream;
+
+Ogre::DataStreamPtr MangleArchive::open(const Ogre::String& filename) const
+{
+  return Ogre::DataStreamPtr(new Mangle2OgreStream
+                             (filename, vfs->open(filename)));
+}
+
+static void fill(Ogre::FileInfoList &out, FileInfoList &in)
+{
+  int size = in.size();
+  out.resize(size);
+
+  for(int i=0; i<size; i++)
+    {
+      out[i].filename = in[i].name;
+      out[i].basename = in[i].basename;
+      out[i].path = ""; // FIXME
+      out[i].uncompressedSize = in[i].size;
+      out[i].compressedSize = in[i].size;
+    }
+}
+
+static void fill(Ogre::StringVector &out, FileInfoList &in)
+{
+  int size = in.size();
+  out.resize(size);
+
+  for(int i=0; i<size; i++)
+    out[i] = in[i].name;
+}
+
+Ogre::StringVectorPtr MangleArchive::list(bool recursive, bool dirs)
+{
+  assert(vfs->hasList);
+  FileInfoListPtr lst = vfs->list("", recursive, dirs);
+  Ogre::StringVector *res = new Ogre::StringVector;
+
+  fill(*res, *lst);
+
+  return Ogre::StringVectorPtr(res);
+}
+
+Ogre::FileInfoListPtr MangleArchive::listFileInfo(bool recursive, bool dirs)
+{
+  assert(vfs->hasList);
+  FileInfoListPtr lst = vfs->list("", recursive, dirs);
+  Ogre::FileInfoList *res = new Ogre::FileInfoList;
+
+  fill(*res, *lst);
+
+  return Ogre::FileInfoListPtr(res);
+}
+
+// Find functions will only work if vfs->hasFind is set.
+Ogre::StringVectorPtr MangleArchive::find(const Ogre::String& pattern,
+                                bool recursive,
+                                bool dirs)
+{
+  assert(vfs->hasFind);
+  FileInfoListPtr lst = vfs->find(pattern, recursive, dirs);
+  Ogre::StringVector *res = new Ogre::StringVector;
+
+  fill(*res, *lst);
+
+  return Ogre::StringVectorPtr(res);
+}
+
+Ogre::FileInfoListPtr MangleArchive::findFileInfo(const Ogre::String& pattern,
+                                        bool recursive,
+                                        bool dirs)
+{
+  assert(vfs->hasFind);
+  FileInfoListPtr lst = vfs->find(pattern, recursive, dirs);
+  Ogre::FileInfoList *res = new Ogre::FileInfoList;
+
+  fill(*res, *lst);
+
+  return Ogre::FileInfoListPtr(res);
+}
diff --git a/libs/mangle/vfs/clients/ogre_archive.hpp b/libs/mangle/vfs/clients/ogre_archive.hpp
new file mode 100644
index 0000000000..7b371c6efe
--- /dev/null
+++ b/libs/mangle/vfs/clients/ogre_archive.hpp
@@ -0,0 +1,59 @@
+#ifndef MANGLE_VFS_OGRECLIENT_H
+#define MANGLE_VFS_OGRECLIENT_H
+
+#include <OgreArchive.h>
+#include <assert.h>
+#include "../vfs.hpp"
+
+namespace Mangle {
+namespace VFS {
+
+/** An OGRE Archive implementation that wraps a Mangle::VFS
+    filesystem.
+
+    This has been built and tested against OGRE 1.6.2, and has been
+    extended for OGRE 1.7. You might have to make your own
+    modifications if you're working with newer (or older) versions.
+ */
+class MangleArchive : public Ogre::Archive
+{
+  VFSPtr vfs;
+
+ public:
+  MangleArchive(VFSPtr _vfs, const std::string &name,
+                const std::string &archType = "Mangle")
+    : Ogre::Archive(name, archType)
+    , vfs(_vfs)
+  {}
+
+  bool isCaseSensitive() const { return vfs->isCaseSensitive; }
+
+  // These do nothing. You have to load / unload the archive in the
+  // constructor/destructor.
+  void load() {}
+  void unload() {}
+
+  bool exists(const Ogre::String& filename)
+    { return vfs->isFile(filename); }
+
+  time_t getModifiedTime(const Ogre::String& filename)
+    { return vfs->stat(filename)->time; }
+
+  // With both these we support both OGRE 1.6 and 1.7
+  Ogre::DataStreamPtr open(const Ogre::String& filename) const;
+  Ogre::DataStreamPtr open(const Ogre::String& filename, bool readOnly) const
+  { return open(filename); }
+
+  Ogre::StringVectorPtr list(bool recursive = true, bool dirs = false);
+  Ogre::FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false);
+
+  // Find functions will only work if vfs->hasFind is set.
+  Ogre::StringVectorPtr find(const Ogre::String& pattern, bool recursive = true,
+                             bool dirs = false);
+  Ogre::FileInfoListPtr findFileInfo(const Ogre::String& pattern,
+                                     bool recursive = true,
+                                     bool dirs = false);
+};
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/vfs/servers/ogre_vfs.cpp b/libs/mangle/vfs/servers/ogre_vfs.cpp
new file mode 100644
index 0000000000..7f728c1b0f
--- /dev/null
+++ b/libs/mangle/vfs/servers/ogre_vfs.cpp
@@ -0,0 +1,61 @@
+#include "ogre_vfs.hpp"
+#include "../../stream/servers/ogre_datastream.hpp"
+
+using namespace Mangle::VFS;
+using namespace Mangle::Stream;
+
+OgreVFS::OgreVFS(const std::string &_group)
+  : group(_group)
+{
+  hasList = true;
+  hasFind = true;
+  isCaseSensitive = true;
+
+  // Get the group manager once
+  gm = Ogre::ResourceGroupManager::getSingletonPtr();
+
+  // Use the default group if none was specified
+  if(group.empty())
+    group = gm->getWorldResourceGroupName();
+}
+
+StreamPtr OgreVFS::open(const std::string &name)
+{
+  Ogre::DataStreamPtr data = gm->openResource(name, group);
+  return StreamPtr(new OgreStream(data));
+}
+
+static void fill(FileInfoList &out, Ogre::FileInfoList &in, bool dirs)
+{
+  int size = in.size();
+  out.resize(size);
+
+  for(int i=0; i<size; i++)
+    {
+      out[i].name = in[i].filename;
+      out[i].basename = in[i].basename;
+      out[i].size = in[i].uncompressedSize;
+      out[i].isDir = dirs;
+      out[i].time = 0;
+    }
+}
+
+FileInfoListPtr OgreVFS::list(const std::string& dir,
+                            bool recurse,
+                            bool dirs) const
+{
+  Ogre::FileInfoListPtr olist = gm->listResourceFileInfo(group, dirs);
+  FileInfoListPtr res(new FileInfoList);
+  fill(*res, *olist, dirs);
+  return res;
+}
+
+FileInfoListPtr OgreVFS::find(const std::string& pattern,
+                            bool recursive,
+                            bool dirs) const
+{
+  Ogre::FileInfoListPtr olist = gm->findResourceFileInfo(group, pattern, dirs);
+  FileInfoListPtr res(new FileInfoList);
+  fill(*res, *olist, dirs);
+  return res;
+}
diff --git a/libs/mangle/vfs/servers/ogre_vfs.hpp b/libs/mangle/vfs/servers/ogre_vfs.hpp
new file mode 100644
index 0000000000..7ab64169df
--- /dev/null
+++ b/libs/mangle/vfs/servers/ogre_vfs.hpp
@@ -0,0 +1,70 @@
+#ifndef MANGLE_VFS_OGRESERVER_H
+#define MANGLE_VFS_OGRESERVER_H
+
+#include "../vfs.hpp"
+#include <OgreResourceGroupManager.h>
+
+namespace Mangle {
+namespace VFS {
+
+/** @brief An interface into the OGRE VFS system.
+
+    This class does NOT wrap a single Ogre::Archive, but rather an
+    entire resource group in Ogre. You can use this class to tap into
+    all paths, Zip files, custom archives on so on that have been
+    inserted into Ogre as resource locations.
+
+    This has been built and tested against OGRE 1.6.2. You might have
+    to make your own modifications if you're working with newer (or
+    older) versions.
+ */
+class OgreVFS : public VFS
+{
+  std::string group;
+  Ogre::ResourceGroupManager *gm;
+
+ public:
+  /** @brief Constructor
+
+      OGRE must be initialized (ie. you must have created an
+      Ogre::Root object somewhere) before calling this.
+
+      @param group Optional resource group name. If none is given,
+      OGRE's default (or 'World') resource group is used.
+   */
+  OgreVFS(const std::string &_group = "");
+
+  /// Open a new data stream. Deleting the object should be enough to
+  /// close it.
+  virtual Stream::StreamPtr open(const std::string &name);
+
+  /// Check for the existence of a file
+  virtual bool isFile(const std::string &name) const
+    { return gm->resourceExists(group, name); }
+
+  /// This doesn't work, always returns false.
+  virtual bool isDir(const std::string &name) const
+    { return false; }
+
+  /// This doesn't work.
+  virtual FileInfoPtr stat(const std::string &name) const
+    { return FileInfoPtr(); }
+
+  /// List all entries in a given directory. A blank dir should be
+  /// interpreted as a the root/current directory of the archive. If
+  /// dirs is true, list directories instead of files. OGRE note: The
+  /// ogre resource systemd does not support recursive listing of
+  /// files. We might make a separate filter for this later.
+  virtual FileInfoListPtr list(const std::string& dir = "",
+                               bool recurse=true,
+                               bool dirs=false) const;
+
+  /// Find files after a given pattern. Wildcards (*) are
+  /// supported.
+  virtual FileInfoListPtr find(const std::string& pattern,
+                               bool recursive=true,
+                               bool dirs=false) const;
+};
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/vfs/servers/physfs_vfs.hpp b/libs/mangle/vfs/servers/physfs_vfs.hpp
new file mode 100644
index 0000000000..8535088e06
--- /dev/null
+++ b/libs/mangle/vfs/servers/physfs_vfs.hpp
@@ -0,0 +1,71 @@
+#ifndef MANGLE_VFS_PHYSFS_SERVER_H
+#define MANGLE_VFS_PHYSFS_SERVER_H
+
+#include "../vfs.hpp"
+#include "../../stream/servers/phys_stream.hpp"
+
+#include <physfs.h>
+#include <assert.h>
+
+namespace Mangle {
+namespace VFS {
+
+/** @brief An interface into the PhysFS virtual file system library
+
+    You have to set up PhysFS on your own before using this class.
+ */
+class PhysVFS : public VFS
+{
+ public:
+  PhysVFS()
+    {
+      hasList = true;
+      hasFind = false;
+      isCaseSensitive = true;
+    }
+
+  /// Open a new data stream. Deleting the object should be enough to
+  /// close it.
+  virtual Stream::StreamPtr open(const std::string &name)
+    { return Stream::StreamPtr(new Stream::PhysFile(PHYSFS_openRead(name.c_str()))); }
+
+  /// Check for the existence of a file
+  virtual bool isFile(const std::string &name) const
+    { return PHYSFS_exists(name.c_str()); }
+
+  /// Checks for a directory
+  virtual bool isDir(const std::string &name) const
+    { return PHYSFS_isDirectory(name.c_str()); }
+
+  /// This doesn't work
+  virtual FileInfoPtr stat(const std::string &name) const
+    { assert(0); return FileInfoPtr(); }
+
+  virtual FileInfoListPtr list(const std::string& dir = "",
+                            bool recurse=true,
+                            bool dirs=false) const
+    {
+      char **files = PHYSFS_enumerateFiles(dir.c_str());
+      FileInfoListPtr lst(new FileInfoList);
+
+      // Add all teh files
+      int i = 0;
+      while(files[i] != NULL)
+        {
+          FileInfo fi;
+          fi.name = files[i];
+          fi.isDir = false;
+
+          lst->push_back(fi);
+        }
+      return lst;
+    }
+
+  virtual FileInfoListPtr find(const std::string& pattern,
+                               bool recursive=true,
+                               bool dirs=false) const
+    { assert(0); }
+};
+
+}} // namespaces
+#endif
diff --git a/libs/mangle/vfs/tests/.gitignore b/libs/mangle/vfs/tests/.gitignore
new file mode 100644
index 0000000000..8144904045
--- /dev/null
+++ b/libs/mangle/vfs/tests/.gitignore
@@ -0,0 +1 @@
+*_test
diff --git a/libs/mangle/vfs/tests/Makefile b/libs/mangle/vfs/tests/Makefile
new file mode 100644
index 0000000000..a5d1d17120
--- /dev/null
+++ b/libs/mangle/vfs/tests/Makefile
@@ -0,0 +1,25 @@
+GCC=g++ -I../ -Wall
+
+all: dummy_test ogre_client_test ogre_resource_test ogre_server_test physfs_server_test
+
+I_OGRE=$(shell pkg-config --cflags OGRE)
+L_OGRE=$(shell pkg-config --libs OGRE)
+L_PHYSFS=-lphysfs
+
+ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.hpp ../clients/ogre_archive.hpp ../clients/ogre_archive.cpp
+	$(GCC) $< ../clients/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE)
+
+ogre_resource_test: ogre_resource_test.cpp
+	$(GCC) $< -o $@ $(I_OGRE) $(L_OGRE)
+
+ogre_server_test: ogre_server_test.cpp ../vfs.hpp ../servers/ogre_vfs.hpp ../servers/ogre_vfs.cpp
+	$(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) ../servers/ogre_vfs.cpp
+
+physfs_server_test: physfs_server_test.cpp ../vfs.hpp ../servers/physfs_vfs.hpp
+	$(GCC) $< -o $@ $(L_PHYSFS)
+
+dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.hpp
+	$(GCC) $< -o $@
+
+clean:
+	rm *_test
diff --git a/libs/mangle/vfs/tests/dummy_test.cpp b/libs/mangle/vfs/tests/dummy_test.cpp
new file mode 100644
index 0000000000..541010c6a3
--- /dev/null
+++ b/libs/mangle/vfs/tests/dummy_test.cpp
@@ -0,0 +1,42 @@
+#include "dummy_vfs.cpp"
+
+#include <iostream>
+#include <string.h>
+
+using namespace std;
+
+void print(FileInfo &inf)
+{
+  cout << "name: " << inf.name << endl;
+  cout << "basename: " << inf.basename << endl;
+  cout << "isDir: " << inf.isDir << endl;
+  cout << "size: " << inf.size << endl;
+  cout << "time: " << inf.time << endl;
+}
+void print(FileInfoPtr inf) { print(*inf); }
+
+void print(FileInfoList &lst)
+{
+  for(unsigned i=0; i<lst.size(); i++)
+    print(lst[i]);
+}
+void print(FileInfoListPtr lst) { print(*lst); }
+
+int main()
+{
+  DummyVFS vfs;
+
+  cout << "Listing all files:\n";
+  print(vfs.list());
+  cout << "\nStat for single files:\n";
+  print(vfs.stat("file1"));
+  cout << endl;
+  print(vfs.stat("dir/file2"));
+  cout << endl;
+  print(vfs.stat("dir"));
+
+  StreamPtr inp = vfs.open("file1");
+  cout << "filesize: " << inp->size() << endl;
+
+  return 0;
+}
diff --git a/libs/mangle/vfs/tests/dummy_vfs.cpp b/libs/mangle/vfs/tests/dummy_vfs.cpp
new file mode 100644
index 0000000000..0448e96a55
--- /dev/null
+++ b/libs/mangle/vfs/tests/dummy_vfs.cpp
@@ -0,0 +1,117 @@
+// This file is shared between several test programs
+#include "vfs.hpp"
+#include <assert.h>
+#include <string.h>
+
+#include "../../stream/servers/memory_stream.hpp"
+
+using namespace Mangle::VFS;
+using namespace Mangle::Stream;
+
+class DummyVFS : public VFS
+{
+public:
+  DummyVFS()
+  {
+    hasFind = false;
+    hasList = true;
+    isCaseSensitive = true;
+  }
+
+  // We only support opening 'file1' at the moment.
+  StreamPtr open(const std::string &name)
+  {
+    assert(name == "file1");
+    return StreamPtr(new MemoryStream("hello world", 11));
+  }
+
+  bool isFile(const std::string &name) const
+  {
+    return (name == "file1" ||
+            name == "dir/file2");
+  }
+
+  bool isDir(const std::string &name) const
+  {
+    return name == "dir";
+  }
+
+  /// Get info about a single file
+  FileInfoPtr stat(const std::string &name) const
+  {
+    FileInfoPtr fi(new FileInfo);
+    fi->name = name;
+    fi->time = 0;
+
+    if(isFile(name))
+      {
+        if(name == "dir/file2")
+          {
+            fi->basename = "file2";
+            fi->size = 2;
+          }
+        else
+          {
+            fi->basename = "file1";
+            fi->size = 1;
+          }
+        fi->isDir = false;
+      }
+    else if(isDir(name))
+      {
+        fi->basename = "dir";
+        fi->isDir = true;
+        fi->size = 0;
+      }
+    else assert(0);
+
+    return fi;
+  }
+
+  /// List all entries in a given directory. A blank dir should be
+  /// interpreted as a the root/current directory of the archive. If
+  /// dirs is true, list directories instead of files.
+  virtual FileInfoListPtr list(const std::string& dir = "",
+                               bool recurse=true,
+                               bool dirs=false) const
+  {
+    assert(dir == "");
+
+    FileInfoListPtr fl(new FileInfoList);
+
+    FileInfo fi;
+
+    if(!dirs)
+      {
+        fi.name = "file1";
+        fi.basename = "file1";
+        fi.isDir = false;
+        fi.size = 1;
+        fi.time = 0;
+        fl->push_back(fi);
+
+        if(recurse)
+          {
+            fi.name = "dir/file2";
+            fi.basename = "file2";
+            fi.size = 2;
+            fl->push_back(fi);
+          }
+      }
+    else
+      {
+        fi.name = "dir";
+        fi.basename = "dir";
+        fi.isDir = true;
+        fi.size = 0;
+        fi.time = 0;
+        fl->push_back(fi);
+      }
+    return fl;
+  }
+
+  FileInfoListPtr find(const std::string& pattern,
+                    bool recursive=true,
+                    bool dirs=false) const
+  { assert(0); }
+};
diff --git a/libs/mangle/vfs/tests/ogre_client_test.cpp b/libs/mangle/vfs/tests/ogre_client_test.cpp
new file mode 100644
index 0000000000..8e1269b56f
--- /dev/null
+++ b/libs/mangle/vfs/tests/ogre_client_test.cpp
@@ -0,0 +1,39 @@
+#include "dummy_vfs.cpp"
+#include "../clients/ogre_archive.hpp"
+#include <iostream>
+
+using namespace Ogre;
+using namespace std;
+
+void print(StringVectorPtr lst)
+{
+  int s = lst->size();
+
+  for(int i=0; i<s; i++)
+    {
+      cout << "  " << (*lst)[i] << endl;
+    }
+}
+
+int main()
+{
+  VFSPtr vfs(new DummyVFS());
+  MangleArchive arc(vfs, "dummy");
+
+  cout << "Case: " << arc.isCaseSensitive() << endl;
+  cout << "Name: " << arc.getName() << endl;
+  cout << "Type: " << arc.getType() << endl;
+  cout << "All files:\n";
+  print(arc.list());
+  cout << "Non-recursive:\n";
+  print(arc.list(false, false));
+  cout << "Dirs:\n";
+  print(arc.list(false, true));
+
+  DataStreamPtr file = arc.open("file1");
+
+  cout << "filesize: " << file->size() << endl;
+  cout << "contents: " << file->getAsString() << endl;
+
+  return 0;
+}
diff --git a/libs/mangle/vfs/tests/ogre_resource_test.cpp b/libs/mangle/vfs/tests/ogre_resource_test.cpp
new file mode 100644
index 0000000000..8965adaa1e
--- /dev/null
+++ b/libs/mangle/vfs/tests/ogre_resource_test.cpp
@@ -0,0 +1,61 @@
+#include <iostream>
+#include <OgreRoot.h>
+#include <OgreResourceGroupManager.h>
+
+/*
+  This isn't really a test of our implementation, but a test of using
+  the Ogre resource system to find files. If the Ogre interface
+  changes and you have to change this test, you will have to change
+  the servers/ogre_vfs.cpp implementation equivalently.
+
+ */
+
+using namespace std;
+using namespace Ogre;
+
+ResourceGroupManager *gm;
+String group;
+
+void find(const std::string &fileName)
+{
+  cout << "\nFile: " << fileName << endl;
+
+  if(!gm->resourceExists(group, fileName))
+    {
+      cout << "Does not exist\n";
+      return;
+    }
+
+  DataStreamPtr data = gm->openResource(fileName, group);
+
+  cout << "Size: " << data->size() << endl;
+  cout << "First line: " << data->getLine() << "\n";
+}
+
+int main()
+{
+  // Disable logging
+  new LogManager;
+  Log *log = LogManager::getSingleton().createLog("");
+  log->setDebugOutputEnabled(false);
+
+  // Set up Ogre
+  Root root("","","");
+
+  root.addResourceLocation("./", "FileSystem", "General");
+
+  gm = ResourceGroupManager::getSingletonPtr();
+  group = gm->getWorldResourceGroupName();
+
+  find("Makefile");
+  find("ogre_resource_test.cpp");
+  find("bleh");
+
+  cout << "\nAll source files:\n";
+  FileInfoListPtr list = gm->findResourceFileInfo(group, "*.cpp");
+  FileInfoList::iterator it, end;
+  it = list->begin();
+  end = list->end();
+  for(; it != end; it++)
+    cout << "  " << it->filename << endl;
+}
diff --git a/libs/mangle/vfs/tests/ogre_server_test.cpp b/libs/mangle/vfs/tests/ogre_server_test.cpp
new file mode 100644
index 0000000000..b846eef96e
--- /dev/null
+++ b/libs/mangle/vfs/tests/ogre_server_test.cpp
@@ -0,0 +1,38 @@
+#include "../servers/ogre_vfs.hpp"
+
+#include <Ogre.h>
+
+#include "server_common.cpp"
+
+Ogre::Root *root;
+
+void setupOgre()
+{
+  using namespace Ogre;
+
+  // Disable logging
+  new LogManager;
+  Log *log = LogManager::getSingleton().createLog("");
+  log->setDebugOutputEnabled(false);
+
+  // Set up Root
+  root = new Root("","","");
+
+  // Add a zip file and the current directory
+  root->addResourceLocation("test.zip", "Zip", "General");
+  root->addResourceLocation("./", "FileSystem", "General");
+}
+
+int main()
+{
+  // Set up the engine
+  setupOgre();
+
+  // This is our entry point into the resource file system
+  OgreVFS vfs("General");
+
+  // Run the test
+  testAll(vfs);
+
+  return 0;
+}
diff --git a/libs/mangle/vfs/tests/output/dummy_test.out b/libs/mangle/vfs/tests/output/dummy_test.out
new file mode 100644
index 0000000000..61f1fda80a
--- /dev/null
+++ b/libs/mangle/vfs/tests/output/dummy_test.out
@@ -0,0 +1,31 @@
+Listing all files:
+name: file1
+basename: file1
+isDir: 0
+size: 1
+time: 0
+name: dir/file2
+basename: file2
+isDir: 0
+size: 2
+time: 0
+
+Stat for single files:
+name: file1
+basename: file1
+isDir: 0
+size: 1
+time: 0
+
+name: dir/file2
+basename: file2
+isDir: 0
+size: 2
+time: 0
+
+name: dir
+basename: dir
+isDir: 1
+size: 0
+time: 0
+filesize: 11
diff --git a/libs/mangle/vfs/tests/output/ogre_client_test.out b/libs/mangle/vfs/tests/output/ogre_client_test.out
new file mode 100644
index 0000000000..bc10b1e5cc
--- /dev/null
+++ b/libs/mangle/vfs/tests/output/ogre_client_test.out
@@ -0,0 +1,12 @@
+Case: 1
+Name: dummy
+Type: Mangle
+All files:
+  file1
+  dir/file2
+Non-recursive:
+  file1
+Dirs:
+  dir
+filesize: 11
+contents: hello world
diff --git a/libs/mangle/vfs/tests/output/ogre_resource_test.out b/libs/mangle/vfs/tests/output/ogre_resource_test.out
new file mode 100644
index 0000000000..9bfc4ff5b6
--- /dev/null
+++ b/libs/mangle/vfs/tests/output/ogre_resource_test.out
@@ -0,0 +1,20 @@
+
+File: Makefile
+Size: 828
+First line: GCC=g++ -I../
+
+File: ogre_resource_test.cpp
+Size: 1437
+First line: #include <iostream>
+
+File: bleh
+Does not exist
+
+All source files:
+  physfs_server_test.cpp
+  dummy_test.cpp
+  ogre_resource_test.cpp
+  server_common.cpp
+  dummy_vfs.cpp
+  ogre_client_test.cpp
+  ogre_server_test.cpp
diff --git a/libs/mangle/vfs/tests/output/ogre_server_test.out b/libs/mangle/vfs/tests/output/ogre_server_test.out
new file mode 100644
index 0000000000..74f3508444
--- /dev/null
+++ b/libs/mangle/vfs/tests/output/ogre_server_test.out
@@ -0,0 +1,11 @@
+
+File: Makefile
+Size: 828
+First 12 bytes: GCC=g++ -I..
+
+File: testfile.txt
+Size: 13
+First 12 bytes: hello world!
+
+File: blah_bleh
+File doesn't exist
diff --git a/libs/mangle/vfs/tests/output/physfs_server_test.out b/libs/mangle/vfs/tests/output/physfs_server_test.out
new file mode 100644
index 0000000000..74f3508444
--- /dev/null
+++ b/libs/mangle/vfs/tests/output/physfs_server_test.out
@@ -0,0 +1,11 @@
+
+File: Makefile
+Size: 828
+First 12 bytes: GCC=g++ -I..
+
+File: testfile.txt
+Size: 13
+First 12 bytes: hello world!
+
+File: blah_bleh
+File doesn't exist
diff --git a/libs/mangle/vfs/tests/physfs_server_test.cpp b/libs/mangle/vfs/tests/physfs_server_test.cpp
new file mode 100644
index 0000000000..9e9d088cdf
--- /dev/null
+++ b/libs/mangle/vfs/tests/physfs_server_test.cpp
@@ -0,0 +1,21 @@
+#include "../servers/physfs_vfs.hpp"
+
+#include "server_common.cpp"
+
+#include <physfs.h>
+
+int main()
+{
+  // Set up the library and paths
+  PHYSFS_init("blah");
+  PHYSFS_addToSearchPath("test.zip", 1);
+  PHYSFS_addToSearchPath("./", 1);
+
+  // Create our interface
+  PhysVFS vfs;
+
+  // Run the test
+  testAll(vfs);
+
+  return 0;
+}
diff --git a/libs/mangle/vfs/tests/server_common.cpp b/libs/mangle/vfs/tests/server_common.cpp
new file mode 100644
index 0000000000..1834bc25a7
--- /dev/null
+++ b/libs/mangle/vfs/tests/server_common.cpp
@@ -0,0 +1,33 @@
+#include <iostream>
+
+using namespace Mangle::VFS;
+using namespace Mangle::Stream;
+using namespace std;
+
+void find(VFS &vfs, const std::string &file)
+{
+  cout << "\nFile: " << file << endl;
+
+  if(!vfs.isFile(file))
+    {
+      cout << "File doesn't exist\n";
+      return;
+    }
+
+  StreamPtr data = vfs.open(file);
+
+  cout << "Size: " << data->size() << endl;
+
+  char buf[13];
+  buf[12] = 0;
+  data->read(buf, 12);
+
+  cout << "First 12 bytes: " << buf << "\n";
+}
+
+void testAll(VFS &vfs)
+{
+  find(vfs, "Makefile");     // From the file system
+  find(vfs, "testfile.txt"); // From the zip
+  find(vfs, "blah_bleh");    // Doesn't exist
+}
diff --git a/libs/mangle/vfs/tests/test.sh b/libs/mangle/vfs/tests/test.sh
new file mode 100755
index 0000000000..2d07708adc
--- /dev/null
+++ b/libs/mangle/vfs/tests/test.sh
@@ -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
diff --git a/libs/mangle/vfs/tests/test.zip b/libs/mangle/vfs/tests/test.zip
new file mode 100644
index 0000000000..ec82f8bc6b
Binary files /dev/null and b/libs/mangle/vfs/tests/test.zip differ
diff --git a/libs/mangle/vfs/vfs.hpp b/libs/mangle/vfs/vfs.hpp
new file mode 100644
index 0000000000..258a6b0a0e
--- /dev/null
+++ b/libs/mangle/vfs/vfs.hpp
@@ -0,0 +1,87 @@
+#ifndef MANGLE_VFS_H
+#define MANGLE_VFS_H
+
+#include "../stream/stream.hpp"
+#include <string>
+#include <vector>
+
+namespace Mangle {
+namespace VFS {
+
+/// Generic file info structure
+struct FileInfo
+{
+  /// Full name, including path
+  std::string name;
+
+  /// Base name, not including path
+  std::string basename;
+
+  /// Is this a directory?
+  bool isDir;
+
+  /// File size
+  size_t size;
+
+  /// Last modification date
+  time_t time;
+};
+
+typedef std::vector<FileInfo> FileInfoList;
+
+typedef boost::shared_ptr<FileInfo> FileInfoPtr;
+typedef boost::shared_ptr<FileInfoList> FileInfoListPtr;
+
+/** An interface to any file system or other provider of named data
+    streams
+*/
+class VFS
+{
+ public:
+  // Feature options. These should be set in the constructor.
+
+  /// If true, the list() function work
+  bool hasList;
+
+  /// If true, the find() function work
+  bool hasFind;
+
+  /// If true, the file system is case sensitive
+  bool isCaseSensitive;
+
+  /// Virtual destructor
+  virtual ~VFS() {}
+
+  /// Open a new data stream. Deleting the object (letting all the
+  /// pointers to it go out of scope) should be enough to close it.
+  virtual Stream::StreamPtr open(const std::string &name) = 0;
+
+  /// Check for the existence of a file
+  virtual bool isFile(const std::string &name) const = 0;
+
+  /// Check for the existence of a directory
+  virtual bool isDir(const std::string &name) const = 0;
+
+  /// Get info about a single file
+  virtual FileInfoPtr stat(const std::string &name) const = 0;
+
+  /// List all entries in a given directory. A blank dir should be
+  /// interpreted as a the root/current directory of the archive. If
+  /// dirs is true, list directories instead of files.
+  virtual FileInfoListPtr list(const std::string& dir = "",
+                               bool recurse=true,
+                               bool dirs=false) const = 0;
+
+  /// Find files after a given pattern. Wildcards (*) are
+  /// supported. Only valid if 'hasFind' is true. Don't implement your
+  /// own pattern matching here if the backend doesn't support it
+  /// natively; use a filter instead.
+  virtual FileInfoListPtr find(const std::string& pattern,
+                               bool recursive=true,
+                               bool dirs=false) const = 0;
+};
+
+typedef boost::shared_ptr<VFS> VFSPtr;
+
+}} // namespaces
+#endif
diff --git a/libs/openengine b/libs/openengine
deleted file mode 160000
index 8f98718315..0000000000
--- a/libs/openengine
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 8f98718315fe11af359740c4a025fd1ca52a9157
diff --git a/libs/openengine/.gitignore b/libs/openengine/.gitignore
new file mode 100644
index 0000000000..23a5e931b8
--- /dev/null
+++ b/libs/openengine/.gitignore
@@ -0,0 +1,3 @@
+*~
+*.o
+*_test
diff --git a/libs/openengine/README b/libs/openengine/README
new file mode 100644
index 0000000000..621fe8d60c
--- /dev/null
+++ b/libs/openengine/README
@@ -0,0 +1,12 @@
+OpenEngine README
+=================
+
+OpenEngine is a bunch of stand-alone game engine modules collected from the OpenMW project (see http://github.com/korslund/openmw or http://openmw.com ) and from certain other projects.
+
+It is currently a very early work in progress, and development will follow OpenMW closely for a while forward.
+
+OpenEngine will depend heavily on Mangle ( http://github.com/korslund/mangle/ ) and will thus aim to be backend agnostic. When finished it should work with a variety for free and commercial middleware libraries as backends for graphics, sound, physics, input and so on.
+
+All questions can be directed to Nicolay Korslund at korslund@gmail.com
+
+- Nicolay
diff --git a/libs/openengine/bullet/BtOgre.cpp b/libs/openengine/bullet/BtOgre.cpp
new file mode 100644
index 0000000000..6187390832
--- /dev/null
+++ b/libs/openengine/bullet/BtOgre.cpp
@@ -0,0 +1,1090 @@
+/*
+ * =============================================================================================
+ *
+ *       Filename:  BtOgre.cpp
+ *
+ *    Description:  BtOgre implementation.
+ *
+ *        Version:  1.0
+ *        Created:  27/12/2008 01:47:56 PM
+ *
+ *         Author:  Nikhilesh (nikki)
+ *
+ * =============================================================================================
+ */
+
+#include "BtOgrePG.h"
+#include "BtOgreGP.h"
+#include "BtOgreExtras.h"
+
+using namespace Ogre;
+
+namespace BtOgre {
+
+/*
+ * =============================================================================================
+ * BtOgre::VertexIndexToShape
+ * =============================================================================================
+ */
+
+    void VertexIndexToShape::addStaticVertexData(const VertexData *vertex_data)
+    {
+        if (!vertex_data)
+            return;
+
+        const VertexData *data = vertex_data;
+
+        const unsigned int prev_size = mVertexCount;
+        mVertexCount += (unsigned int)data->vertexCount;
+
+        Ogre::Vector3* tmp_vert = new Ogre::Vector3[mVertexCount];
+        if (mVertexBuffer)
+        {
+            memcpy(tmp_vert, mVertexBuffer, sizeof(Vector3) * prev_size);
+            delete[] mVertexBuffer;
+        }
+        mVertexBuffer = tmp_vert;
+
+        // Get the positional buffer element
+        {
+            const Ogre::VertexElement* posElem = data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
+            Ogre::HardwareVertexBufferSharedPtr vbuf = data->vertexBufferBinding->getBuffer(posElem->getSource());
+            const unsigned int vSize = (unsigned int)vbuf->getVertexSize();
+
+            unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
+            float* pReal;
+            Ogre::Vector3 * curVertices = &mVertexBuffer[prev_size];
+            const unsigned int vertexCount = (unsigned int)data->vertexCount;
+            for(unsigned int j = 0; j < vertexCount; ++j)
+            {
+                posElem->baseVertexPointerToElement(vertex, &pReal);
+                vertex += vSize;
+
+                curVertices->x = (*pReal++);
+                curVertices->y = (*pReal++);
+                curVertices->z = (*pReal++);
+
+                *curVertices = mTransform * (*curVertices);
+
+                curVertices++;
+            }
+            vbuf->unlock();
+        }
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void VertexIndexToShape::addAnimatedVertexData(const Ogre::VertexData *vertex_data,
+                                                   const Ogre::VertexData *blend_data,
+                                                   const Ogre::Mesh::IndexMap *indexMap)
+    {
+        // Get the bone index element
+        assert(vertex_data);
+
+        const VertexData *data = blend_data;
+        const unsigned int prev_size = mVertexCount;
+        mVertexCount += (unsigned int)data->vertexCount;
+        Ogre::Vector3* tmp_vert = new Ogre::Vector3[mVertexCount];
+        if (mVertexBuffer)
+        {
+            memcpy(tmp_vert, mVertexBuffer, sizeof(Vector3) * prev_size);
+            delete[] mVertexBuffer;
+        }
+        mVertexBuffer = tmp_vert;
+
+        // Get the positional buffer element
+        {
+            const Ogre::VertexElement* posElem = data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
+            assert (posElem);
+            Ogre::HardwareVertexBufferSharedPtr vbuf = data->vertexBufferBinding->getBuffer(posElem->getSource());
+            const unsigned int vSize = (unsigned int)vbuf->getVertexSize();
+
+            unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
+            float* pReal;
+            Ogre::Vector3 * curVertices = &mVertexBuffer[prev_size];
+            const unsigned int vertexCount = (unsigned int)data->vertexCount;
+            for(unsigned int j = 0; j < vertexCount; ++j)
+            {
+                posElem->baseVertexPointerToElement(vertex, &pReal);
+                vertex += vSize;
+
+                curVertices->x = (*pReal++);
+                curVertices->y = (*pReal++);
+                curVertices->z = (*pReal++);
+
+                *curVertices = mTransform * (*curVertices);
+
+                curVertices++;
+            }
+            vbuf->unlock();
+        }
+
+        {
+            const Ogre::VertexElement* bneElem = vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_BLEND_INDICES);
+            assert (bneElem);
+
+            Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(bneElem->getSource());
+            const unsigned int vSize = (unsigned int)vbuf->getVertexSize();
+            unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
+
+            unsigned char* pBone;
+
+            if (!mBoneIndex)
+                mBoneIndex = new BoneIndex();
+            BoneIndex::iterator i;
+
+            Ogre::Vector3 * curVertices = &mVertexBuffer[prev_size];
+
+            const unsigned int vertexCount = (unsigned int)vertex_data->vertexCount;
+            for(unsigned int j = 0; j < vertexCount; ++j)
+            {
+                bneElem->baseVertexPointerToElement(vertex, &pBone);
+                vertex += vSize;
+
+                const unsigned char currBone = (indexMap) ? (*indexMap)[*pBone] : *pBone;
+                i = mBoneIndex->find (currBone);
+                Vector3Array* l = 0;
+                if (i == mBoneIndex->end())
+                {
+                    l = new Vector3Array;
+                    mBoneIndex->insert(BoneKeyIndex(currBone, l));
+                }
+                else
+                {
+                    l = i->second;
+                }
+
+                l->push_back(*curVertices);
+
+                curVertices++;
+            }
+            vbuf->unlock();
+        }
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void VertexIndexToShape::addIndexData(IndexData *data, const unsigned int offset)
+    {
+        const unsigned int prev_size = mIndexCount;
+        mIndexCount += (unsigned int)data->indexCount;
+
+        unsigned int* tmp_ind = new unsigned int[mIndexCount];
+        if (mIndexBuffer)
+        {
+            memcpy (tmp_ind, mIndexBuffer, sizeof(unsigned int) * prev_size);
+            delete[] mIndexBuffer;
+        }
+        mIndexBuffer = tmp_ind;
+
+        const unsigned int numTris = (unsigned int) data->indexCount / 3;
+        HardwareIndexBufferSharedPtr ibuf = data->indexBuffer;
+        const bool use32bitindexes = (ibuf->getType() == HardwareIndexBuffer::IT_32BIT);
+        unsigned int index_offset = prev_size;
+
+        if (use32bitindexes)
+        {
+            const unsigned int* pInt = static_cast<unsigned int*>(ibuf->lock(HardwareBuffer::HBL_READ_ONLY));
+            for(unsigned int k = 0; k < numTris; ++k)
+            {
+                mIndexBuffer[index_offset ++] = offset + *pInt++;
+                mIndexBuffer[index_offset ++] = offset + *pInt++;
+                mIndexBuffer[index_offset ++] = offset + *pInt++;
+            }
+            ibuf->unlock();
+        }
+        else
+        {
+            const unsigned short* pShort = static_cast<unsigned short*>(ibuf->lock(HardwareBuffer::HBL_READ_ONLY));
+            for(unsigned int k = 0; k < numTris; ++k)
+            {
+                mIndexBuffer[index_offset ++] = offset + static_cast<unsigned int> (*pShort++);
+                mIndexBuffer[index_offset ++] = offset + static_cast<unsigned int> (*pShort++);
+                mIndexBuffer[index_offset ++] = offset + static_cast<unsigned int> (*pShort++);
+            }
+            ibuf->unlock();
+        }
+
+    }
+
+    //------------------------------------------------------------------------------------------------
+    Real VertexIndexToShape::getRadius()
+    {
+        if (mBoundRadius == (-1))
+        {
+            getSize();
+            mBoundRadius = (std::max(mBounds.x,std::max(mBounds.y,mBounds.z)) * 0.5);
+        }
+        return mBoundRadius;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    Vector3 VertexIndexToShape::getSize()
+    {
+        const unsigned int vCount = getVertexCount();
+        if (mBounds == Ogre::Vector3(-1,-1,-1) && vCount > 0)
+        {
+
+            const Ogre::Vector3 * const v = getVertices();
+
+            Ogre::Vector3 vmin(v[0]);
+            Ogre::Vector3 vmax(v[0]);
+
+            for(unsigned int j = 1; j < vCount; j++)
+            {
+                vmin.x = std::min(vmin.x, v[j].x);
+                vmin.y = std::min(vmin.y, v[j].y);
+                vmin.z = std::min(vmin.z, v[j].z);
+
+                vmax.x = std::max(vmax.x, v[j].x);
+                vmax.y = std::max(vmax.y, v[j].y);
+                vmax.z = std::max(vmax.z, v[j].z);
+            }
+
+            mBounds.x = vmax.x - vmin.x;
+            mBounds.y = vmax.y - vmin.y;
+            mBounds.z = vmax.z - vmin.z;
+        }
+
+        return mBounds;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    const Ogre::Vector3* VertexIndexToShape::getVertices()
+    {
+        return mVertexBuffer;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    unsigned int VertexIndexToShape::getVertexCount()
+    {
+        return mVertexCount;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    const unsigned int* VertexIndexToShape::getIndices()
+    {
+        return mIndexBuffer;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    unsigned int VertexIndexToShape::getIndexCount()
+    {
+        return mIndexCount;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    btSphereShape* VertexIndexToShape::createSphere()
+    {
+        const Ogre::Real rad = getRadius();
+        assert((rad > 0.0) &&
+                ("Sphere radius must be greater than zero"));
+        btSphereShape* shape = new btSphereShape(rad);
+
+        shape->setLocalScaling(Convert::toBullet(mScale));
+
+        return shape;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    btBoxShape* VertexIndexToShape::createBox()
+    {
+        const Ogre::Vector3 sz = getSize();
+
+        assert((sz.x > 0.0) && (sz.y > 0.0) && (sz.y > 0.0) &&
+                ("Size of box must be greater than zero on all axes"));
+
+        btBoxShape* shape = new btBoxShape(Convert::toBullet(sz * 0.5));
+
+        shape->setLocalScaling(Convert::toBullet(mScale));
+
+        return shape;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    btCylinderShape* VertexIndexToShape::createCylinder()
+    {
+        const Ogre::Vector3 sz = getSize();
+
+        assert((sz.x > 0.0) && (sz.y > 0.0) && (sz.y > 0.0) &&
+                ("Size of Cylinder must be greater than zero on all axes"));
+
+        btCylinderShape* shape = new btCylinderShapeX(Convert::toBullet(sz * 0.5));
+
+        shape->setLocalScaling(Convert::toBullet(mScale));
+
+        return shape;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    btConvexHullShape* VertexIndexToShape::createConvex()
+    {
+        assert(mVertexCount && (mIndexCount >= 6) &&
+                ("Mesh must have some vertices and at least 6 indices (2 triangles)"));
+
+        return new btConvexHullShape((btScalar*) &mVertexBuffer[0].x, mVertexCount, sizeof(Vector3));
+    }
+
+    //------------------------------------------------------------------------------------------------
+    btBvhTriangleMeshShape* VertexIndexToShape::createTrimesh()
+    {
+        assert(mVertexCount && (mIndexCount >= 6) &&
+                ("Mesh must have some vertices and at least 6 indices (2 triangles)"));
+
+        unsigned int numFaces = mIndexCount / 3;
+
+        btTriangleMesh *trimesh = new btTriangleMesh();
+        unsigned int *indices = mIndexBuffer;
+        Vector3 *vertices = mVertexBuffer;
+
+        btVector3    vertexPos[3];
+        for (unsigned int n = 0; n < numFaces; ++n)
+        {
+            {
+                const Vector3 &vec = vertices[*indices];
+                vertexPos[0][0] = vec.x;
+                vertexPos[0][1] = vec.y;
+                vertexPos[0][2] = vec.z;
+            }
+            {
+                const Vector3 &vec = vertices[*(indices + 1)];
+                vertexPos[1][0] = vec.x;
+                vertexPos[1][1] = vec.y;
+                vertexPos[1][2] = vec.z;
+            }
+            {
+                const Vector3 &vec = vertices[*(indices + 2)];
+                vertexPos[2][0] = vec.x;
+                vertexPos[2][1] = vec.y;
+                vertexPos[2][2] = vec.z;
+            }
+
+            indices += 3;
+
+            trimesh->addTriangle(vertexPos[0], vertexPos[1], vertexPos[2]);
+        }
+
+        const bool useQuantizedAABB = true;
+        btBvhTriangleMeshShape *shape = new btBvhTriangleMeshShape(trimesh, useQuantizedAABB);
+
+        shape->setLocalScaling(Convert::toBullet(mScale));
+
+        return shape;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    VertexIndexToShape::~VertexIndexToShape()
+    {
+        delete[] mVertexBuffer;
+        delete[] mIndexBuffer;
+
+        if (mBoneIndex)
+        {
+            for(BoneIndex::iterator i = mBoneIndex->begin();
+                    i != mBoneIndex->end();
+                    ++i)
+            {
+                delete i->second;
+            }
+            delete mBoneIndex;
+        }
+    }
+
+    //------------------------------------------------------------------------------------------------
+    VertexIndexToShape::VertexIndexToShape(const Matrix4 &transform) :
+        mVertexBuffer (0),
+        mIndexBuffer (0),
+        mVertexCount (0),
+        mIndexCount (0),
+        mTransform (transform),
+        mBoundRadius (-1),
+        mBounds (Vector3(-1,-1,-1)),
+        mBoneIndex (0),
+        mScale(1)
+    {
+    }
+
+/*
+ * =============================================================================================
+ * BtOgre::StaticMeshToShapeConverter
+ * =============================================================================================
+ */
+
+    StaticMeshToShapeConverter::StaticMeshToShapeConverter() :
+        VertexIndexToShape(),
+        mEntity (0),
+        mNode (0)
+    {
+    }
+
+    //------------------------------------------------------------------------------------------------
+    StaticMeshToShapeConverter::~StaticMeshToShapeConverter()
+    {
+    }
+
+    //------------------------------------------------------------------------------------------------
+    StaticMeshToShapeConverter::StaticMeshToShapeConverter(Entity *entity,  const Matrix4 &transform) :
+        VertexIndexToShape(transform),
+        mEntity (0),
+        mNode (0)
+    {
+        addEntity(entity, transform);
+    }
+
+    //------------------------------------------------------------------------------------------------
+    StaticMeshToShapeConverter::StaticMeshToShapeConverter(Renderable *rend, const Matrix4 &transform) :
+        VertexIndexToShape(transform),
+        mEntity (0),
+        mNode (0)
+    {
+        RenderOperation op;
+        rend->getRenderOperation(op);
+        VertexIndexToShape::addStaticVertexData(op.vertexData);
+        if(op.useIndexes)
+            VertexIndexToShape::addIndexData(op.indexData);
+
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void StaticMeshToShapeConverter::addEntity(Entity *entity,const Matrix4 &transform)
+    {
+        // Each entity added need to reset size and radius
+        // next time getRadius and getSize are asked, they're computed.
+        mBounds  = Ogre::Vector3(-1,-1,-1);
+        mBoundRadius = -1;
+
+        mEntity = entity;
+        mNode = (SceneNode*)(mEntity->getParentNode());
+        mTransform = transform;
+        mScale = mNode->getScale();
+
+        if (mEntity->getMesh()->sharedVertexData)
+        {
+            VertexIndexToShape::addStaticVertexData (mEntity->getMesh()->sharedVertexData);
+        }
+
+        for (unsigned int i = 0;i < mEntity->getNumSubEntities();++i)
+        {
+            SubMesh *sub_mesh = mEntity->getSubEntity(i)->getSubMesh();
+
+            if (!sub_mesh->useSharedVertices)
+            {
+                VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount);
+                VertexIndexToShape::addStaticVertexData (sub_mesh->vertexData);
+            }
+            else
+            {
+                VertexIndexToShape::addIndexData (sub_mesh->indexData);
+            }
+
+        }
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void StaticMeshToShapeConverter::addMesh(const MeshPtr &mesh, const Matrix4 &transform)
+    {
+        // Each entity added need to reset size and radius
+        // next time getRadius and getSize are asked, they're computed.
+        mBounds  = Ogre::Vector3(-1,-1,-1);
+        mBoundRadius = -1;
+
+        //_entity = entity;
+        //_node = (SceneNode*)(_entity->getParentNode());
+        mTransform = transform;
+
+        if (mesh->hasSkeleton ())
+            Ogre::LogManager::getSingleton().logMessage("MeshToShapeConverter::addMesh : Mesh " + mesh->getName () + " as skeleton but added to trimesh non animated");
+
+        if (mesh->sharedVertexData)
+        {
+            VertexIndexToShape::addStaticVertexData (mesh->sharedVertexData);
+        }
+
+        for(unsigned int i = 0;i < mesh->getNumSubMeshes();++i)
+        {
+            SubMesh *sub_mesh = mesh->getSubMesh(i);
+
+            if (!sub_mesh->useSharedVertices)
+            {
+                VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount);
+                VertexIndexToShape::addStaticVertexData (sub_mesh->vertexData);
+            }
+            else
+            {
+                VertexIndexToShape::addIndexData (sub_mesh->indexData);
+            }
+
+        }
+    }
+
+/*
+ * =============================================================================================
+ * BtOgre::AnimatedMeshToShapeConverter
+ * =============================================================================================
+ */
+
+    AnimatedMeshToShapeConverter::AnimatedMeshToShapeConverter(Entity *entity,const Matrix4 &transform) :
+        VertexIndexToShape(transform),
+        mEntity (0),
+        mNode (0),
+        mTransformedVerticesTemp(0),
+        mTransformedVerticesTempSize(0)
+    {
+        addEntity(entity, transform);
+    }
+
+    //------------------------------------------------------------------------------------------------
+    AnimatedMeshToShapeConverter::AnimatedMeshToShapeConverter() :
+        VertexIndexToShape(),
+        mEntity (0),
+        mNode (0),
+        mTransformedVerticesTemp(0),
+        mTransformedVerticesTempSize(0)
+    {
+    }
+
+    //------------------------------------------------------------------------------------------------
+    AnimatedMeshToShapeConverter::~AnimatedMeshToShapeConverter()
+    {
+        delete[] mTransformedVerticesTemp;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void AnimatedMeshToShapeConverter::addEntity(Entity *entity,const Matrix4 &transform)
+    {
+        // Each entity added need to reset size and radius
+        // next time getRadius and getSize are asked, they're computed.
+        mBounds  = Ogre::Vector3(-1,-1,-1);
+        mBoundRadius = -1;
+
+        mEntity = entity;
+        mNode = (SceneNode*)(mEntity->getParentNode());
+        mTransform = transform;
+
+        assert (entity->getMesh()->hasSkeleton ());
+
+        mEntity->addSoftwareAnimationRequest(false);
+        mEntity->_updateAnimation();
+
+        if (mEntity->getMesh()->sharedVertexData)
+        {
+            VertexIndexToShape::addAnimatedVertexData (mEntity->getMesh()->sharedVertexData,
+                    mEntity->_getSkelAnimVertexData(),
+                    &mEntity->getMesh()->sharedBlendIndexToBoneIndexMap);
+        }
+
+        for (unsigned int i = 0;i < mEntity->getNumSubEntities();++i)
+        {
+            SubMesh *sub_mesh = mEntity->getSubEntity(i)->getSubMesh();
+
+            if (!sub_mesh->useSharedVertices)
+            {
+                VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount);
+
+                VertexIndexToShape::addAnimatedVertexData (sub_mesh->vertexData,
+                        mEntity->getSubEntity(i)->_getSkelAnimVertexData(),
+                        &sub_mesh->blendIndexToBoneIndexMap);
+            }
+            else
+            {
+                VertexIndexToShape::addIndexData (sub_mesh->indexData);
+            }
+
+        }
+
+        mEntity->removeSoftwareAnimationRequest(false);
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void AnimatedMeshToShapeConverter::addMesh(const MeshPtr &mesh, const Matrix4 &transform)
+    {
+        // Each entity added need to reset size and radius
+        // next time getRadius and getSize are asked, they're computed.
+        mBounds  = Ogre::Vector3(-1,-1,-1);
+        mBoundRadius = -1;
+
+        //_entity = entity;
+        //_node = (SceneNode*)(_entity->getParentNode());
+        mTransform = transform;
+
+        assert (mesh->hasSkeleton ());
+
+        if (mesh->sharedVertexData)
+        {
+            VertexIndexToShape::addAnimatedVertexData (mesh->sharedVertexData,
+                    0,
+                    &mesh->sharedBlendIndexToBoneIndexMap);
+        }
+
+        for(unsigned int i = 0;i < mesh->getNumSubMeshes();++i)
+        {
+            SubMesh *sub_mesh = mesh->getSubMesh(i);
+
+            if (!sub_mesh->useSharedVertices)
+            {
+                VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount);
+
+                VertexIndexToShape::addAnimatedVertexData (sub_mesh->vertexData,
+                        0,
+                        &sub_mesh->blendIndexToBoneIndexMap);
+            }
+            else
+            {
+                VertexIndexToShape::addIndexData (sub_mesh->indexData);
+            }
+
+        }
+    }
+
+    //------------------------------------------------------------------------------------------------
+    bool AnimatedMeshToShapeConverter::getBoneVertices(unsigned char bone,
+                                                       unsigned int &vertex_count,
+                                                       Ogre::Vector3* &vertices,
+                                                       const Vector3 &bonePosition)
+    {
+        BoneIndex::iterator i = mBoneIndex->find(bone);
+
+        if (i == mBoneIndex->end())
+            return false;
+
+        if (i->second->empty())
+            return false;
+
+        vertex_count = (unsigned int) i->second->size() + 1;
+        if (vertex_count > mTransformedVerticesTempSize)
+        {
+            if (mTransformedVerticesTemp)
+                delete[] mTransformedVerticesTemp;
+
+            mTransformedVerticesTemp = new Ogre::Vector3[vertex_count];
+
+        }
+
+        vertices = mTransformedVerticesTemp;
+        vertices[0] = bonePosition;
+        //mEntity->_getParentNodeFullTransform() *
+        //mEntity->getSkeleton()->getBone(bone)->_getDerivedPosition();
+
+        //mEntity->getSkeleton()->getBone(bone)->_getDerivedOrientation()
+        unsigned int currBoneVertex = 1;
+        Vector3Array::iterator j = i->second->begin();
+        while(j != i->second->end())
+        {
+            vertices[currBoneVertex] = (*j);
+            ++j;
+            ++currBoneVertex;
+        }
+        return true;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    btBoxShape* AnimatedMeshToShapeConverter::createAlignedBox(unsigned char bone,
+                                                               const Vector3 &bonePosition,
+                                                               const Quaternion &boneOrientation)
+    {
+        unsigned int vertex_count;
+        Vector3* vertices;
+
+        if (!getBoneVertices(bone, vertex_count, vertices, bonePosition))
+            return 0;
+
+        Vector3 min_vec(vertices[0]);
+        Vector3 max_vec(vertices[0]);
+
+        for(unsigned int j = 1; j < vertex_count ;j++)
+        {
+            min_vec.x = std::min(min_vec.x,vertices[j].x);
+            min_vec.y = std::min(min_vec.y,vertices[j].y);
+            min_vec.z = std::min(min_vec.z,vertices[j].z);
+
+            max_vec.x = std::max(max_vec.x,vertices[j].x);
+            max_vec.y = std::max(max_vec.y,vertices[j].y);
+            max_vec.z = std::max(max_vec.z,vertices[j].z);
+        }
+        const Ogre::Vector3 maxMinusMin(max_vec - min_vec);
+        btBoxShape* box = new btBoxShape(Convert::toBullet(maxMinusMin));
+
+        /*const Ogre::Vector3 pos
+          (min_vec.x + (maxMinusMin.x * 0.5),
+          min_vec.y + (maxMinusMin.y * 0.5),
+          min_vec.z + (maxMinusMin.z * 0.5));*/
+
+        //box->setPosition(pos);
+
+        return box;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    bool AnimatedMeshToShapeConverter::getOrientedBox(unsigned char bone,
+                                                      const Vector3 &bonePosition,
+                                                      const Quaternion &boneOrientation,
+                                                      Vector3 &box_afExtent,
+                                                      Vector3 *box_akAxis,
+                                                      Vector3 &box_kCenter)
+    {
+        unsigned int vertex_count;
+        Vector3* vertices;
+
+        if (!getBoneVertices(bone, vertex_count, vertices, bonePosition))
+            return false;
+
+        box_kCenter = Vector3::ZERO;
+
+        {
+            for(unsigned int c = 0 ;c < vertex_count;c++)
+            {
+                box_kCenter += vertices[c];
+            }
+            const Ogre::Real invVertexCount = 1.0 / vertex_count;
+            box_kCenter *= invVertexCount;
+        }
+        Quaternion orient = boneOrientation;
+        orient.ToAxes(box_akAxis);
+
+        // Let C be the box center and let U0, U1, and U2 be the box axes.  Each
+        // input point is of the form X = C + y0*U0 + y1*U1 + y2*U2.  The
+        // following code computes min(y0), max(y0), min(y1), max(y1), min(y2),
+        // and max(y2).  The box center is then adjusted to be
+        //   C' = C + 0.5*(min(y0)+max(y0))*U0 + 0.5*(min(y1)+max(y1))*U1 +
+        //        0.5*(min(y2)+max(y2))*U2
+
+        Ogre::Vector3 kDiff (vertices[1] - box_kCenter);
+        Ogre::Real fY0Min = kDiff.dotProduct(box_akAxis[0]), fY0Max = fY0Min;
+        Ogre::Real fY1Min = kDiff.dotProduct(box_akAxis[1]), fY1Max = fY1Min;
+        Ogre::Real fY2Min = kDiff.dotProduct(box_akAxis[2]), fY2Max = fY2Min;
+
+        for (unsigned int i = 2; i < vertex_count; i++)
+        {
+            kDiff = vertices[i] - box_kCenter;
+
+            const Ogre::Real fY0 = kDiff.dotProduct(box_akAxis[0]);
+            if ( fY0 < fY0Min )
+                fY0Min = fY0;
+            else if ( fY0 > fY0Max )
+                fY0Max = fY0;
+
+            const Ogre::Real fY1 = kDiff.dotProduct(box_akAxis[1]);
+            if ( fY1 < fY1Min )
+                fY1Min = fY1;
+            else if ( fY1 > fY1Max )
+                fY1Max = fY1;
+
+            const Ogre::Real fY2 = kDiff.dotProduct(box_akAxis[2]);
+            if ( fY2 < fY2Min )
+                fY2Min = fY2;
+            else if ( fY2 > fY2Max )
+                fY2Max = fY2;
+        }
+
+        box_afExtent.x = ((Real)0.5)*(fY0Max - fY0Min);
+        box_afExtent.y = ((Real)0.5)*(fY1Max - fY1Min);
+        box_afExtent.z = ((Real)0.5)*(fY2Max - fY2Min);
+
+        box_kCenter += (0.5*(fY0Max+fY0Min))*box_akAxis[0] +
+            (0.5*(fY1Max+fY1Min))*box_akAxis[1] +
+            (0.5*(fY2Max+fY2Min))*box_akAxis[2];
+
+        box_afExtent *= 2.0;
+
+        return true;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    btBoxShape *AnimatedMeshToShapeConverter::createOrientedBox(unsigned char bone,
+                                                                const Vector3 &bonePosition,
+                                                                const Quaternion &boneOrientation)
+    {
+        Ogre::Vector3 box_akAxis[3];
+        Ogre::Vector3 box_afExtent;
+        Ogre::Vector3 box_afCenter;
+
+        if (!getOrientedBox(bone, bonePosition, boneOrientation,
+                    box_afExtent,
+                    box_akAxis,
+                    box_afCenter))
+            return 0;
+
+        btBoxShape *geom = new btBoxShape(Convert::toBullet(box_afExtent));
+        //geom->setOrientation(Quaternion(box_akAxis[0],box_akAxis[1],box_akAxis[2]));
+        //geom->setPosition(box_afCenter);
+        return geom;
+    }
+
+/*
+ * =============================================================================================
+ * BtOgre::DynamicRenderable
+ * =============================================================================================
+ */
+
+    DynamicRenderable::DynamicRenderable()
+    {
+    }
+
+    //------------------------------------------------------------------------------------------------
+    DynamicRenderable::~DynamicRenderable()
+    {
+        delete mRenderOp.vertexData;
+        delete mRenderOp.indexData;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void DynamicRenderable::initialize(RenderOperation::OperationType operationType,
+                                       bool useIndices)
+    {
+        // Initialize render operation
+        mRenderOp.operationType = operationType;
+        mRenderOp.useIndexes = useIndices;
+        mRenderOp.vertexData = new VertexData;
+        if (mRenderOp.useIndexes)
+            mRenderOp.indexData = new IndexData;
+
+        // Reset buffer capacities
+        mVertexBufferCapacity = 0;
+        mIndexBufferCapacity = 0;
+
+        // Create vertex declaration
+        createVertexDeclaration();
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void DynamicRenderable::prepareHardwareBuffers(size_t vertexCount,
+                                                   size_t indexCount)
+    {
+        // Prepare vertex buffer
+        size_t newVertCapacity = mVertexBufferCapacity;
+        if ((vertexCount > mVertexBufferCapacity) ||
+                (!mVertexBufferCapacity))
+        {
+            // vertexCount exceeds current capacity!
+            // It is necessary to reallocate the buffer.
+
+            // Check if this is the first call
+            if (!newVertCapacity)
+                newVertCapacity = 1;
+
+            // Make capacity the next power of two
+            while (newVertCapacity < vertexCount)
+                newVertCapacity <<= 1;
+        }
+        else if (vertexCount < mVertexBufferCapacity>>1) {
+            // Make capacity the previous power of two
+            while (vertexCount < newVertCapacity>>1)
+                newVertCapacity >>= 1;
+        }
+        if (newVertCapacity != mVertexBufferCapacity)
+        {
+            mVertexBufferCapacity = newVertCapacity;
+            // Create new vertex buffer
+            HardwareVertexBufferSharedPtr vbuf =
+                HardwareBufferManager::getSingleton().createVertexBuffer(
+                        mRenderOp.vertexData->vertexDeclaration->getVertexSize(0),
+                        mVertexBufferCapacity,
+                        HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); // TODO: Custom HBU_?
+
+            // Bind buffer
+            mRenderOp.vertexData->vertexBufferBinding->setBinding(0, vbuf);
+        }
+        // Update vertex count in the render operation
+        mRenderOp.vertexData->vertexCount = vertexCount;
+
+        if (mRenderOp.useIndexes)
+        {
+            OgreAssert(indexCount <= std::numeric_limits<unsigned short>::max(), "indexCount exceeds 16 bit");
+
+            size_t newIndexCapacity = mIndexBufferCapacity;
+            // Prepare index buffer
+            if ((indexCount > newIndexCapacity) ||
+                    (!newIndexCapacity))
+            {
+                // indexCount exceeds current capacity!
+                // It is necessary to reallocate the buffer.
+
+                // Check if this is the first call
+                if (!newIndexCapacity)
+                    newIndexCapacity = 1;
+
+                // Make capacity the next power of two
+                while (newIndexCapacity < indexCount)
+                    newIndexCapacity <<= 1;
+
+            }
+            else if (indexCount < newIndexCapacity>>1)
+            {
+                // Make capacity the previous power of two
+                while (indexCount < newIndexCapacity>>1)
+                    newIndexCapacity >>= 1;
+            }
+
+            if (newIndexCapacity != mIndexBufferCapacity)
+            {
+                mIndexBufferCapacity = newIndexCapacity;
+                // Create new index buffer
+                mRenderOp.indexData->indexBuffer =
+                    HardwareBufferManager::getSingleton().createIndexBuffer(
+                            HardwareIndexBuffer::IT_16BIT,
+                            mIndexBufferCapacity,
+                            HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); // TODO: Custom HBU_?
+            }
+
+            // Update index count in the render operation
+            mRenderOp.indexData->indexCount = indexCount;
+        }
+    }
+
+    //------------------------------------------------------------------------------------------------
+    Real DynamicRenderable::getBoundingRadius(void) const
+    {
+        return Math::Sqrt(std::max(mBox.getMaximum().squaredLength(), mBox.getMinimum().squaredLength()));
+    }
+
+    //------------------------------------------------------------------------------------------------
+    Real DynamicRenderable::getSquaredViewDepth(const Camera* cam) const
+    {
+        Vector3 vMin, vMax, vMid, vDist;
+        vMin = mBox.getMinimum();
+        vMax = mBox.getMaximum();
+        vMid = ((vMax - vMin) * 0.5) + vMin;
+        vDist = cam->getDerivedPosition() - vMid;
+
+        return vDist.squaredLength();
+    }
+
+/*
+ * =============================================================================================
+ * BtOgre::DynamicLines
+ * =============================================================================================
+ */
+
+    enum {
+        POSITION_BINDING,
+        TEXCOORD_BINDING
+    };
+
+    //------------------------------------------------------------------------------------------------
+    DynamicLines::DynamicLines(OperationType opType)
+    {
+        initialize(opType,false);
+        setMaterial("BaseWhiteNoLighting");
+        mDirty = true;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    DynamicLines::~DynamicLines()
+    {
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void DynamicLines::setOperationType(OperationType opType)
+    {
+        mRenderOp.operationType = opType;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    RenderOperation::OperationType DynamicLines::getOperationType() const
+    {
+        return mRenderOp.operationType;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void DynamicLines::addPoint(const Vector3 &p)
+    {
+        mPoints.push_back(p);
+        mDirty = true;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void DynamicLines::addPoint(Real x, Real y, Real z)
+    {
+        mPoints.push_back(Vector3(x,y,z));
+        mDirty = true;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    const Vector3& DynamicLines::getPoint(unsigned short index) const
+    {
+        assert(index < mPoints.size() && "Point index is out of bounds!!");
+        return mPoints[index];
+    }
+
+    //------------------------------------------------------------------------------------------------
+    unsigned short DynamicLines::getNumPoints(void) const
+    {
+        return (unsigned short)mPoints.size();
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void DynamicLines::setPoint(unsigned short index, const Vector3 &value)
+    {
+        assert(index < mPoints.size() && "Point index is out of bounds!!");
+
+        mPoints[index] = value;
+        mDirty = true;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void DynamicLines::clear()
+    {
+        mPoints.clear();
+        mDirty = true;
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void DynamicLines::update()
+    {
+        if (mDirty) fillHardwareBuffers();
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void DynamicLines::createVertexDeclaration()
+    {
+        VertexDeclaration *decl = mRenderOp.vertexData->vertexDeclaration;
+        decl->addElement(POSITION_BINDING, 0, VET_FLOAT3, VES_POSITION);
+    }
+
+    //------------------------------------------------------------------------------------------------
+    void DynamicLines::fillHardwareBuffers()
+    {
+        int size = mPoints.size();
+
+        prepareHardwareBuffers(size,0);
+
+        if (!size) {
+            mBox.setExtents(Vector3::ZERO,Vector3::ZERO);
+            mDirty=false;
+            return;
+        }
+
+        Vector3 vaabMin = mPoints[0];
+        Vector3 vaabMax = mPoints[0];
+
+        HardwareVertexBufferSharedPtr vbuf =
+            mRenderOp.vertexData->vertexBufferBinding->getBuffer(0);
+
+        Real *prPos = static_cast<Real*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));
+        {
+            for(int i = 0; i < size; i++)
+            {
+                *prPos++ = mPoints[i].x;
+                *prPos++ = mPoints[i].y;
+                *prPos++ = mPoints[i].z;
+
+                if(mPoints[i].x < vaabMin.x)
+                    vaabMin.x = mPoints[i].x;
+                if(mPoints[i].y < vaabMin.y)
+                    vaabMin.y = mPoints[i].y;
+                if(mPoints[i].z < vaabMin.z)
+                    vaabMin.z = mPoints[i].z;
+
+                if(mPoints[i].x > vaabMax.x)
+                    vaabMax.x = mPoints[i].x;
+                if(mPoints[i].y > vaabMax.y)
+                    vaabMax.y = mPoints[i].y;
+                if(mPoints[i].z > vaabMax.z)
+                    vaabMax.z = mPoints[i].z;
+            }
+        }
+        vbuf->unlock();
+
+        mBox.setExtents(vaabMin, vaabMax);
+
+        mDirty = false;
+    }
+}
diff --git a/libs/openengine/bullet/BtOgreExtras.h b/libs/openengine/bullet/BtOgreExtras.h
new file mode 100644
index 0000000000..f3e1aa87ab
--- /dev/null
+++ b/libs/openengine/bullet/BtOgreExtras.h
@@ -0,0 +1,280 @@
+/*
+ * =====================================================================================
+ *
+ *       Filename:  BtOgreExtras.h
+ *
+ *    Description:  Contains the Ogre Mesh to Bullet Shape converters.
+ *
+ *        Version:  1.0
+ *        Created:  27/12/2008 01:45:56 PM
+ *
+ *         Author:  Nikhilesh (nikki)
+ *
+ * =====================================================================================
+ */
+
+#ifndef _BtOgreShapes_H_
+#define _BtOgreShapes_H_
+
+#include "btBulletDynamicsCommon.h"
+#include "OgreSimpleRenderable.h"
+#include "OgreCamera.h"
+#include "OgreHardwareBufferManager.h"
+#include "OgreMaterialManager.h"
+#include "OgreTechnique.h"
+#include "OgrePass.h"
+
+#include "OgreLogManager.h"
+
+namespace BtOgre
+{
+
+typedef std::vector<Ogre::Vector3> Vector3Array;
+
+//Converts from and to Bullet and Ogre stuff. Pretty self-explanatory.
+class Convert
+{
+public:
+    Convert() {};
+    ~Convert() {};
+
+    static btQuaternion toBullet(const Ogre::Quaternion &q)
+    {
+        return btQuaternion(q.x, q.y, q.z, q.w);
+    }
+    static btVector3 toBullet(const Ogre::Vector3 &v)
+    {
+        return btVector3(v.x, v.y, v.z);
+    }
+
+    static Ogre::Quaternion toOgre(const btQuaternion &q)
+    {
+        return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z());
+    }
+    static Ogre::Vector3 toOgre(const btVector3 &v)
+    {
+        return Ogre::Vector3(v.x(), v.y(), v.z());
+    }
+};
+
+//From here on its debug-drawing stuff. ------------------------------------------------------------------
+
+class DynamicRenderable : public Ogre::SimpleRenderable
+{
+public:
+  /// Constructor
+  DynamicRenderable();
+  /// Virtual destructor
+  virtual ~DynamicRenderable();
+
+  /** Initializes the dynamic renderable.
+   @remarks
+      This function should only be called once. It initializes the
+      render operation, and calls the abstract function
+      createVertexDeclaration().
+   @param operationType The type of render operation to perform.
+   @param useIndices Specifies whether to use indices to determine the
+          vertices to use as input. */
+  void initialize(Ogre::RenderOperation::OperationType operationType,
+                  bool useIndices);
+
+  /// Implementation of Ogre::SimpleRenderable
+  virtual Ogre::Real getBoundingRadius(void) const;
+  /// Implementation of Ogre::SimpleRenderable
+  virtual Ogre::Real getSquaredViewDepth(const Ogre::Camera* cam) const;
+
+protected:
+  /// Maximum capacity of the currently allocated vertex buffer.
+  size_t mVertexBufferCapacity;
+  /// Maximum capacity of the currently allocated index buffer.
+  size_t mIndexBufferCapacity;
+
+  /** Creates the vertex declaration.
+   @remarks
+      Override and set mRenderOp.vertexData->vertexDeclaration here.
+      mRenderOp.vertexData will be created for you before this method
+      is called. */
+  virtual void createVertexDeclaration() = 0;
+
+  /** Prepares the hardware buffers for the requested vertex and index counts.
+   @remarks
+      This function must be called before locking the buffers in
+      fillHardwareBuffers(). It guarantees that the hardware buffers
+      are large enough to hold at least the requested number of
+      vertices and indices (if using indices). The buffers are
+      possibly reallocated to achieve this.
+   @par
+      The vertex and index count in the render operation are set to
+      the values of vertexCount and indexCount respectively.
+   @param vertexCount The number of vertices the buffer must hold.
+
+   @param indexCount The number of indices the buffer must hold. This
+          parameter is ignored if not using indices. */
+  void prepareHardwareBuffers(size_t vertexCount, size_t indexCount);
+
+  /** Fills the hardware vertex and index buffers with data.
+   @remarks
+      This function must call prepareHardwareBuffers() before locking
+      the buffers to ensure the they are large enough for the data to
+      be written. Afterwards the vertex and index buffers (if using
+      indices) can be locked, and data can be written to them. */
+  virtual void fillHardwareBuffers() = 0;
+};
+
+class DynamicLines : public DynamicRenderable
+{
+  typedef Ogre::Vector3 Vector3;
+  typedef Ogre::Quaternion Quaternion;
+  typedef Ogre::Camera Camera;
+  typedef Ogre::Real Real;
+  typedef Ogre::RenderOperation::OperationType OperationType;
+
+public:
+  /// Constructor - see setOperationType() for description of argument.
+  DynamicLines(OperationType opType=Ogre::RenderOperation::OT_LINE_STRIP);
+  virtual ~DynamicLines();
+
+  /// Add a point to the point list
+  void addPoint(const Ogre::Vector3 &p);
+  /// Add a point to the point list
+  void addPoint(Real x, Real y, Real z);
+
+  /// Change the location of an existing point in the point list
+  void setPoint(unsigned short index, const Vector3 &value);
+
+  /// Return the location of an existing point in the point list
+  const Vector3& getPoint(unsigned short index) const;
+
+  /// Return the total number of points in the point list
+  unsigned short getNumPoints(void) const;
+
+  /// Remove all points from the point list
+  void clear();
+
+  /// Call this to update the hardware buffer after making changes.
+  void update();
+
+  /** Set the type of operation to draw with.
+   * @param opType Can be one of
+   *    - RenderOperation::OT_LINE_STRIP
+   *    - RenderOperation::OT_LINE_LIST
+   *    - RenderOperation::OT_POINT_LIST
+   *    - RenderOperation::OT_TRIANGLE_LIST
+   *    - RenderOperation::OT_TRIANGLE_STRIP
+   *    - RenderOperation::OT_TRIANGLE_FAN
+   *    The default is OT_LINE_STRIP.
+   */
+  void setOperationType(OperationType opType);
+  OperationType getOperationType() const;
+
+protected:
+  /// Implementation DynamicRenderable, creates a simple vertex-only decl
+  virtual void createVertexDeclaration();
+  /// Implementation DynamicRenderable, pushes point list out to hardware memory
+  virtual void fillHardwareBuffers();
+
+private:
+  std::vector<Vector3> mPoints;
+  bool mDirty;
+};
+
+class DebugDrawer : public btIDebugDraw
+{
+protected:
+    Ogre::SceneNode *mNode;
+    btDynamicsWorld *mWorld;
+    DynamicLines *mLineDrawer;
+    bool mDebugOn;
+
+public:
+
+    DebugDrawer(Ogre::SceneNode *node, btDynamicsWorld *world)
+        : mNode(node),
+          mWorld(world),
+          mDebugOn(true)
+    {
+        mLineDrawer = new DynamicLines(Ogre::RenderOperation::OT_LINE_LIST);
+        mNode->attachObject(mLineDrawer);
+
+                if (!Ogre::ResourceGroupManager::getSingleton().resourceGroupExists("BtOgre"))
+                    Ogre::ResourceGroupManager::getSingleton().createResourceGroup("BtOgre");
+                if (!Ogre::MaterialManager::getSingleton().resourceExists("BtOgre/DebugLines"))
+                {
+                    Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create("BtOgre/DebugLines", "BtOgre");
+                    mat->setReceiveShadows(false);
+                    mat->setSelfIllumination(1,1,1);
+                }
+
+        mLineDrawer->setMaterial("BtOgre/DebugLines");
+    }
+
+    ~DebugDrawer()
+    {
+                Ogre::MaterialManager::getSingleton().remove("BtOgre/DebugLines");
+                Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup("BtOgre");
+        delete mLineDrawer;
+    }
+
+    void step()
+    {
+        if (mDebugOn)
+        {
+            mWorld->debugDrawWorld();
+            mLineDrawer->update();
+            mNode->needUpdate();
+            mLineDrawer->clear();
+        }
+        else
+        {
+            mLineDrawer->clear();
+            mLineDrawer->update();
+            mNode->needUpdate();
+        }
+    }
+
+    void drawLine(const btVector3& from,const btVector3& to,const btVector3& color)
+    {
+        mLineDrawer->addPoint(Convert::toOgre(from));
+        mLineDrawer->addPoint(Convert::toOgre(to));
+    }
+
+    void drawContactPoint(const btVector3& PointOnB,const btVector3& normalOnB,btScalar distance,int lifeTime,const btVector3& color)
+    {
+        mLineDrawer->addPoint(Convert::toOgre(PointOnB));
+        mLineDrawer->addPoint(Convert::toOgre(PointOnB) + (Convert::toOgre(normalOnB) * distance * 20));
+    }
+
+    void reportErrorWarning(const char* warningString)
+    {
+        Ogre::LogManager::getSingleton().logMessage(warningString);
+    }
+
+    void draw3dText(const btVector3& location,const char* textString)
+    {
+    }
+
+    //0 for off, anything else for on.
+    void setDebugMode(int isOn)
+    {
+        mDebugOn = (isOn == 0) ? false : true;
+
+        if (!mDebugOn)
+            mLineDrawer->clear();
+    }
+
+    //0 for off, anything else for on.
+    int getDebugMode() const
+    {
+        return mDebugOn;
+    }
+
+};
+
+}
+
+#endif
+
+
+
+
+
diff --git a/libs/openengine/bullet/BtOgreGP.h b/libs/openengine/bullet/BtOgreGP.h
new file mode 100644
index 0000000000..f0534de4bb
--- /dev/null
+++ b/libs/openengine/bullet/BtOgreGP.h
@@ -0,0 +1,142 @@
+/*
+ * =====================================================================================
+ *
+ *       Filename:  BtOgreGP.h
+ *
+ *    Description:  The part of BtOgre that handles information transfer from Ogre to
+ *                  Bullet (like mesh data for making trimeshes).
+ *
+ *        Version:  1.0
+ *        Created:  27/12/2008 03:29:56 AM
+ *
+ *         Author:  Nikhilesh (nikki)
+ *
+ * =====================================================================================
+ */
+
+#ifndef _BtOgrePG_H_
+#define _BtOgrePG_H_
+
+#include "btBulletDynamicsCommon.h"
+#include "BtOgreExtras.h"
+#include "Ogre.h"
+
+namespace BtOgre {
+
+typedef std::map<unsigned char, Vector3Array*> BoneIndex;
+typedef std::pair<unsigned short, Vector3Array*> BoneKeyIndex;
+
+class VertexIndexToShape
+{
+public:
+    VertexIndexToShape(const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
+    ~VertexIndexToShape();
+
+    Ogre::Real getRadius();
+    Ogre::Vector3 getSize();
+
+
+    btSphereShape* createSphere();
+    btBoxShape* createBox();
+    btBvhTriangleMeshShape* createTrimesh();
+    btCylinderShape* createCylinder();
+    btConvexHullShape* createConvex();
+
+    const Ogre::Vector3* getVertices();
+    unsigned int getVertexCount();
+    const unsigned int* getIndices();
+    unsigned int getIndexCount();
+
+protected:
+
+    void addStaticVertexData(const Ogre::VertexData *vertex_data);
+
+    void addAnimatedVertexData(const Ogre::VertexData *vertex_data,
+                               const Ogre::VertexData *blended_data,
+                               const Ogre::Mesh::IndexMap *indexMap);
+
+    void addIndexData(Ogre::IndexData *data, const unsigned int offset = 0);
+
+
+protected:
+    Ogre::Vector3*      mVertexBuffer;
+    unsigned int*       mIndexBuffer;
+    unsigned int        mVertexCount;
+    unsigned int        mIndexCount;
+
+    Ogre::Matrix4       mTransform;
+
+    Ogre::Real          mBoundRadius;
+    Ogre::Vector3       mBounds;
+
+    BoneIndex           *mBoneIndex;
+
+    Ogre::Vector3       mScale;
+};
+
+//For static (non-animated) meshes.
+class StaticMeshToShapeConverter : public VertexIndexToShape
+{
+public:
+
+    StaticMeshToShapeConverter(Ogre::Renderable *rend, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
+    StaticMeshToShapeConverter(Ogre::Entity *entity,   const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
+    StaticMeshToShapeConverter();
+
+    ~StaticMeshToShapeConverter();
+
+    void addEntity(Ogre::Entity *entity,const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
+
+    void addMesh(const Ogre::MeshPtr &mesh, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
+
+
+protected:
+
+    Ogre::Entity*       mEntity;
+    Ogre::SceneNode*    mNode;
+};
+
+//For animated meshes.
+class AnimatedMeshToShapeConverter : public VertexIndexToShape
+{
+public:
+
+    AnimatedMeshToShapeConverter(Ogre::Entity *entity, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
+    AnimatedMeshToShapeConverter();
+    ~AnimatedMeshToShapeConverter();
+
+    void addEntity(Ogre::Entity *entity,const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY);
+    void addMesh(const Ogre::MeshPtr &mesh, const Ogre::Matrix4 &transform);
+
+    btBoxShape* createAlignedBox(unsigned char bone,
+            const Ogre::Vector3 &bonePosition,
+            const Ogre::Quaternion &boneOrientation);
+
+    btBoxShape* createOrientedBox(unsigned char bone,
+            const Ogre::Vector3 &bonePosition,
+            const Ogre::Quaternion &boneOrientation);
+
+protected:
+
+    bool getBoneVertices(unsigned char bone,
+            unsigned int &vertex_count,
+            Ogre::Vector3* &vertices,
+            const Ogre::Vector3 &bonePosition);
+
+    bool getOrientedBox(unsigned char bone,
+            const Ogre::Vector3 &bonePosition,
+            const Ogre::Quaternion &boneOrientation,
+            Ogre::Vector3 &extents,
+            Ogre::Vector3 *axis,
+            Ogre::Vector3 &center);
+
+    Ogre::Entity*       mEntity;
+    Ogre::SceneNode*    mNode;
+
+    Ogre::Vector3       *mTransformedVerticesTemp;
+    size_t               mTransformedVerticesTempSize;
+};
+
+}
+
+#endif
diff --git a/libs/openengine/bullet/BtOgrePG.h b/libs/openengine/bullet/BtOgrePG.h
new file mode 100644
index 0000000000..9ff069a8f9
--- /dev/null
+++ b/libs/openengine/bullet/BtOgrePG.h
@@ -0,0 +1,81 @@
+/*
+ * =====================================================================================
+ *
+ *       Filename:  BtOgrePG.h
+ *
+ *    Description:  The part of BtOgre that handles information transfer from Bullet to
+ *                  Ogre (like updating graphics object positions).
+ *
+ *        Version:  1.0
+ *        Created:  27/12/2008 03:40:56 AM
+ *
+ *         Author:  Nikhilesh (nikki)
+ *
+ * =====================================================================================
+ */
+
+#ifndef _BtOgreGP_H_
+#define _BtOgreGP_H_
+
+#include "btBulletDynamicsCommon.h"
+#include "OgreSceneNode.h"
+#include "BtOgreExtras.h"
+
+namespace BtOgre {
+
+//A MotionState is Bullet's way of informing you about updates to an object.
+//Pass this MotionState to a btRigidBody to have your SceneNode updated automaticaly.
+class RigidBodyState : public btMotionState
+{
+    protected:
+        btTransform mTransform;
+        btTransform mCenterOfMassOffset;
+
+        Ogre::SceneNode *mNode;
+
+    public:
+        RigidBodyState(Ogre::SceneNode *node, const btTransform &transform, const btTransform &offset = btTransform::getIdentity())
+            : mTransform(transform),
+              mCenterOfMassOffset(offset),
+              mNode(node)
+        {
+        }
+
+        RigidBodyState(Ogre::SceneNode *node)
+            : mTransform(((node != NULL) ? BtOgre::Convert::toBullet(node->getOrientation()) : btQuaternion(0,0,0,1)),
+                         ((node != NULL) ? BtOgre::Convert::toBullet(node->getPosition())    : btVector3(0,0,0))),
+              mCenterOfMassOffset(btTransform::getIdentity()),
+              mNode(node)
+        {
+        }
+
+        virtual void getWorldTransform(btTransform &ret) const
+        {
+            ret = mCenterOfMassOffset.inverse() * mTransform;
+        }
+
+        virtual void setWorldTransform(const btTransform &in)
+        {
+            if (mNode == NULL)
+                return;
+
+            mTransform = in;
+            btTransform transform = in * mCenterOfMassOffset;
+
+            btQuaternion rot = transform.getRotation();
+            btVector3 pos = transform.getOrigin();
+            mNode->setOrientation(rot.w(), rot.x(), rot.y(), rot.z());
+            mNode->setPosition(pos.x(), pos.y(), pos.z());
+        }
+
+        void setNode(Ogre::SceneNode *node)
+        {
+            mNode = node;
+        }
+};
+
+//Softbody-Ogre connection goes here!
+
+}
+
+#endif
diff --git a/libs/openengine/bullet/BulletShapeLoader.cpp b/libs/openengine/bullet/BulletShapeLoader.cpp
new file mode 100644
index 0000000000..48b87e1e8c
--- /dev/null
+++ b/libs/openengine/bullet/BulletShapeLoader.cpp
@@ -0,0 +1,124 @@
+#include "BulletShapeLoader.h"
+
+
+
+BulletShape::BulletShape(Ogre::ResourceManager* creator, const Ogre::String &name,
+    Ogre::ResourceHandle handle, const Ogre::String &group, bool isManual,
+    Ogre::ManualResourceLoader *loader) :
+Ogre::Resource(creator, name, handle, group, isManual, loader)
+{
+    /* If you were storing a pointer to an object, then you would set that pointer to NULL here.
+    */
+
+    /* For consistency with StringInterface, but we don't add any parameters here
+    That's because the Resource implementation of StringInterface is to
+    list all the options that need to be set before loading, of which
+    we have none as such. Full details can be set through scripts.
+    */
+    Shape = NULL;
+    collide = true;
+    createParamDictionary("BulletShape");
+}
+
+BulletShape::~BulletShape()
+{
+}
+
+// farm out to BulletShapeLoader
+void BulletShape::loadImpl()
+{
+    mLoader->loadResource(this);
+}
+
+void BulletShape::deleteShape(btCollisionShape* mShape)
+{
+    if(mShape!=NULL)
+    {
+        if(mShape->isCompound())
+        {
+            btCompoundShape* ms = static_cast<btCompoundShape*>(Shape);
+            int a = ms->getNumChildShapes();
+            for(int i=0; i <a;i++)
+            {
+                deleteShape(ms->getChildShape(i));
+            }
+        }
+        delete mShape;
+    }
+    mShape = NULL;
+}
+
+void BulletShape::unloadImpl()
+{
+    deleteShape(Shape);
+}
+
+//TODO:change this?
+size_t BulletShape::calculateSize() const
+{
+    return 1;
+}
+
+
+
+//=============================================================================================================
+template<> BulletShapeManager *Ogre::Singleton<BulletShapeManager>::ms_Singleton = 0;
+
+BulletShapeManager *BulletShapeManager::getSingletonPtr()
+{
+    return ms_Singleton;
+}
+
+BulletShapeManager &BulletShapeManager::getSingleton()
+{
+    assert(ms_Singleton);
+    return(*ms_Singleton);
+}
+
+BulletShapeManager::BulletShapeManager()
+{
+    mResourceType = "BulletShape";
+
+    // low, because it will likely reference other resources
+    mLoadOrder = 30.0f;
+
+    // this is how we register the ResourceManager with OGRE
+    Ogre::ResourceGroupManager::getSingleton()._registerResourceManager(mResourceType, this);
+}
+
+BulletShapeManager::~BulletShapeManager()
+{
+    // and this is how we unregister it
+    Ogre::ResourceGroupManager::getSingleton()._unregisterResourceManager(mResourceType);
+}
+
+BulletShapePtr BulletShapeManager::load(const Ogre::String &name, const Ogre::String &group)
+{
+    BulletShapePtr textf = getByName(name);
+
+    if (textf.isNull())
+        textf = create(name, group);
+
+    textf->load();
+    return textf;
+}
+
+Ogre::Resource *BulletShapeManager::createImpl(const Ogre::String &name, Ogre::ResourceHandle handle,
+    const Ogre::String &group, bool isManual, Ogre::ManualResourceLoader *loader,
+    const Ogre::NameValuePairList *createParams)
+{
+    BulletShape* res = new BulletShape(this, name, handle, group, isManual, loader);
+    //if(isManual)
+    //{
+    //loader->loadResource(res);
+    //}
+    return res;
+}
+
+
+//====================================================================
+void BulletShapeLoader::loadResource(Ogre::Resource *resource)
+{}
+
+void BulletShapeLoader::load(const std::string &name,const std::string &group)
+{}
diff --git a/libs/openengine/bullet/BulletShapeLoader.h b/libs/openengine/bullet/BulletShapeLoader.h
new file mode 100644
index 0000000000..316ee523c2
--- /dev/null
+++ b/libs/openengine/bullet/BulletShapeLoader.h
@@ -0,0 +1,138 @@
+#ifndef _BULLET_SHAPE_LOADER_H_
+#define _BULLET_SHAPE_LOADER_H_
+
+#include <OgreResource.h>
+#include <OgreResourceManager.h>
+#include <btBulletCollisionCommon.h>
+
+//For some reason, Ogre Singleton  cannot be used in another namespace, that's why there is no namespace here.
+//But the risk of name collision seems pretty low here.
+
+/**
+*Define a new resource which describe a Shape usable by bullet.See BulletShapeManager for how to get/use them.
+*/
+class BulletShape : public Ogre::Resource
+{
+    Ogre::String mString;
+
+protected:
+    void loadImpl();
+    void unloadImpl();
+    size_t calculateSize() const;
+
+    void deleteShape(btCollisionShape* mShape);
+
+public:
+
+    BulletShape(Ogre::ResourceManager *creator, const Ogre::String &name,
+        Ogre::ResourceHandle handle, const Ogre::String &group, bool isManual = false,
+        Ogre::ManualResourceLoader *loader = 0);
+
+    virtual ~BulletShape();
+
+    btCollisionShape* Shape;
+    //this flag indicate if the shape is used for collision or if it's for raycasting only.
+    bool collide;
+};
+
+/**
+*
+*/
+class BulletShapePtr : public Ogre::SharedPtr<BulletShape>
+{
+public:
+    BulletShapePtr() : Ogre::SharedPtr<BulletShape>() {}
+    explicit BulletShapePtr(BulletShape *rep) : Ogre::SharedPtr<BulletShape>(rep) {}
+    BulletShapePtr(const BulletShapePtr &r) : Ogre::SharedPtr<BulletShape>(r) {}
+    BulletShapePtr(const Ogre::ResourcePtr &r) : Ogre::SharedPtr<BulletShape>()
+    {
+        if( r.isNull() )
+            return;
+        // lock & copy other mutex pointer
+        OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME)
+            OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME)
+            pRep = static_cast<BulletShape*>(r.getPointer());
+        pUseCount = r.useCountPointer();
+        useFreeMethod = r.freeMethod();
+        if (pUseCount)
+        {
+            ++(*pUseCount);
+        }
+    }
+
+    /// Operator used to convert a ResourcePtr to a BulletShapePtr
+    BulletShapePtr& operator=(const Ogre::ResourcePtr& r)
+    {
+        if(pRep == static_cast<BulletShape*>(r.getPointer()))
+            return *this;
+        release();
+        if( r.isNull() )
+            return *this; // resource ptr is null, so the call to release above has done all we need to do.
+        // lock & copy other mutex pointer
+        OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME)
+            OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME)
+            pRep = static_cast<BulletShape*>(r.getPointer());
+        pUseCount = r.useCountPointer();
+        useFreeMethod = r.freeMethod();
+        if (pUseCount)
+        {
+            ++(*pUseCount);
+        }
+        return *this;
+    }
+};
+
+
+
+
+/**
+*Hold any BulletShape that was created by the ManualBulletShapeLoader.
+*
+*To get a bulletShape, you must load it first.
+*First, create a manualBulletShapeLoader. Then call ManualBulletShapeManager->load(). This create an "empty" resource.
+*Then use BulletShapeManager->load(). This will fill the resource with the required info.
+*To get the resource,use BulletShapeManager::getByName.
+*When you use the resource no more, just use BulletShapeManager->unload(). It won't completly delete the resource, but it will
+*"empty" it.This allow a better management of memory: when you are leaving a cell, just unload every useless shape.
+*
+*Alternatively, you can call BulletShape->load() in order to actually load the resource.
+*When you are finished with it, just call BulletShape->unload().
+*
+*IMO: prefere the first methode, i am not completly sure about the 2nd.
+*
+*Important Note: i have no idea of what happen if you try to load two time the same resource without unloading.
+*It won't crash, but it might lead to memory leaks(I don't know how Ogre handle this). So don't do it!
+*/
+class BulletShapeManager : public Ogre::ResourceManager, public Ogre::Singleton<BulletShapeManager>
+{
+protected:
+
+    // must implement this from ResourceManager's interface
+    Ogre::Resource *createImpl(const Ogre::String &name, Ogre::ResourceHandle handle,
+        const Ogre::String &group, bool isManual, Ogre::ManualResourceLoader *loader,
+        const Ogre::NameValuePairList *createParams);
+
+public:
+
+    BulletShapeManager();
+    virtual ~BulletShapeManager();
+
+    virtual BulletShapePtr load(const Ogre::String &name, const Ogre::String &group);
+
+    static BulletShapeManager &getSingleton();
+    static BulletShapeManager *getSingletonPtr();
+};
+
+class BulletShapeLoader : public Ogre::ManualResourceLoader
+{
+public:
+
+    BulletShapeLoader(){};
+    virtual ~BulletShapeLoader() {}
+
+    virtual void loadResource(Ogre::Resource *resource);
+
+    virtual void load(const std::string &name,const std::string &group);
+};
+
+#endif
diff --git a/libs/openengine/bullet/CMotionState.cpp b/libs/openengine/bullet/CMotionState.cpp
new file mode 100644
index 0000000000..5ddef51752
--- /dev/null
+++ b/libs/openengine/bullet/CMotionState.cpp
@@ -0,0 +1,45 @@
+#include "CMotionState.h"
+#include "physic.hpp"
+
+#include <btBulletDynamicsCommon.h>
+#include <btBulletCollisionCommon.h>
+#include <components/nifbullet/bullet_nif_loader.hpp>
+//#include <apps\openmw\mwworld\world.hpp>
+
+namespace OEngine {
+namespace Physic
+{
+
+    CMotionState::CMotionState(PhysicEngine* eng,std::string name)
+    {
+        pEng = eng;
+        tr.setIdentity();
+        pName = name;
+    };
+
+    void CMotionState::getWorldTransform(btTransform &worldTrans) const
+    {
+        worldTrans = tr;
+    }
+
+    void CMotionState::setWorldTransform(const btTransform &worldTrans)
+    {
+        tr = worldTrans;
+
+        PhysicEvent evt;
+        evt.isNPC = isNPC;
+        evt.isPC = isPC;
+        evt.newTransform = tr;
+        evt.RigidBodyName = pName;
+
+        if(isPC)
+        {
+            pEng->PEventList.push_back(evt);
+        }
+        else
+        {
+            pEng->NPEventList.push_back(evt);
+        }
+    }
+
+}}
diff --git a/libs/openengine/bullet/CMotionState.h b/libs/openengine/bullet/CMotionState.h
new file mode 100644
index 0000000000..3508ab4ef1
--- /dev/null
+++ b/libs/openengine/bullet/CMotionState.h
@@ -0,0 +1,52 @@
+#ifndef OENGINE_CMOTIONSTATE_H
+#define OENGINE_CMOTIONSTATE_H
+
+#include <BulletDynamics/Dynamics/btRigidBody.h>
+#include <string>
+
+namespace OEngine {
+namespace Physic
+{
+    class PhysicEngine;
+
+    /**
+     * A CMotionState is associated with a single RigidBody.
+     * When the RigidBody is moved by bullet, bullet will call the function setWorldTransform.
+     * for more info, see the bullet Wiki at btMotionState.
+     */
+    class CMotionState:public btMotionState
+    {
+    public:
+
+        CMotionState(PhysicEngine* eng,std::string name);
+
+        /**
+         * Return the position of the RigidBody.
+         */
+        virtual void getWorldTransform(btTransform &worldTrans) const;
+
+        /**
+         * Function called by bullet when the RigidBody is moved.
+         * It add an event to the EventList of the PhysicEngine class.
+         */
+        virtual void setWorldTransform(const btTransform &worldTrans);
+
+    protected:
+        PhysicEngine* pEng;
+        btTransform tr;
+        bool isNPC;
+        bool isPC;
+
+        std::string pName;
+    };
+
+    struct PhysicEvent
+    {
+        bool isNPC;
+        bool isPC;
+        btTransform newTransform;
+        std::string RigidBodyName;
+    };
+
+}}
+#endif
diff --git a/libs/openengine/bullet/btKinematicCharacterController.cpp b/libs/openengine/bullet/btKinematicCharacterController.cpp
new file mode 100644
index 0000000000..fc4f3278f4
--- /dev/null
+++ b/libs/openengine/bullet/btKinematicCharacterController.cpp
@@ -0,0 +1,643 @@
+/*
+Bullet Continuous Collision Detection and Physics Library
+Copyright (c) 2003-2008 Erwin Coumans  http://bulletphysics.com
+
+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.
+*/
+
+#include "LinearMath/btIDebugDraw.h"
+#include "BulletCollision/CollisionDispatch/btGhostObject.h"
+#include "BulletCollision/CollisionShapes/btMultiSphereShape.h"
+#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
+#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
+#include "BulletCollision/CollisionDispatch/btCollisionWorld.h"
+#include "LinearMath/btDefaultMotionState.h"
+#include "btKinematicCharacterController.h"
+
+///@todo Interact with dynamic objects,
+///Ride kinematicly animated platforms properly
+///Support ducking
+class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
+{
+public:
+    btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
+    {
+        m_me[0] = me;
+        count = 1;
+    }
+
+    btKinematicClosestNotMeRayResultCallback (btCollisionObject* me[], int count_) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0))
+    {
+        count = count_;
+
+        for(int i = 0; i < count; i++)
+            m_me[i] = me[i];
+    }
+
+    virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace)
+    {
+        for(int i = 0; i < count; i++)
+            if (rayResult.m_collisionObject == m_me[i])
+                return 1.0;
+
+        return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace);
+    }
+protected:
+    btCollisionObject* m_me[10];
+    int count;
+};
+
+class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
+{
+public:
+    btKinematicClosestNotMeConvexResultCallback( btCollisionObject* me, const btVector3& up, btScalar minSlopeDot )
+        : btCollisionWorld::ClosestConvexResultCallback( btVector3( 0.0, 0.0, 0.0 ), btVector3( 0.0, 0.0, 0.0 ) ),
+        m_me( me ), m_up( up ), m_minSlopeDot( minSlopeDot )
+    {
+    }
+
+    virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)
+    {
+        if( convexResult.m_hitCollisionObject == m_me )
+            return btScalar( 1 );
+
+        btVector3 hitNormalWorld;
+        if( normalInWorldSpace )
+        {
+            hitNormalWorld = convexResult.m_hitNormalLocal;
+        }
+        else
+        {
+            ///need to transform normal into worldspace
+            hitNormalWorld = m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal;
+        }
+
+        // NOTE : m_hitNormalLocal is not always vertical on the ground with a capsule or a box...
+
+        btScalar dotUp = m_up.dot(hitNormalWorld);
+        if( dotUp < m_minSlopeDot )
+            return btScalar( 1 );
+
+        return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace);
+    }
+
+protected:
+    btCollisionObject* m_me;
+    const btVector3 m_up;
+    btScalar m_minSlopeDot;
+};
+
+
+
+btKinematicCharacterController::btKinematicCharacterController( btPairCachingGhostObject* externalGhostObject_,
+    btPairCachingGhostObject* internalGhostObject_,
+    btScalar stepHeight,
+    btScalar constantScale,
+    btScalar gravity,
+    btScalar fallVelocity,
+    btScalar jumpVelocity,
+    btScalar recoveringFactor )
+{
+    m_upAxis = btKinematicCharacterController::Y_AXIS;
+
+    m_walkDirection.setValue( btScalar( 0 ), btScalar( 0 ), btScalar( 0 ) );
+
+    m_useGhostObjectSweepTest = true;
+
+    externalGhostObject = externalGhostObject_;
+    internalGhostObject = internalGhostObject_;
+
+    m_recoveringFactor = recoveringFactor;
+
+    m_stepHeight = stepHeight;
+
+    m_useWalkDirection = true;  // use walk direction by default, legacy behavior
+    m_velocityTimeInterval = btScalar( 0 );
+    m_verticalVelocity = btScalar( 0 );
+    m_verticalOffset = btScalar( 0 );
+
+    m_gravity = constantScale * gravity;
+    m_fallSpeed = constantScale * fallVelocity; // Terminal velocity of a sky diver in m/s.
+
+    m_jumpSpeed = constantScale * jumpVelocity; // ?
+    m_wasJumping = false;
+
+    setMaxSlope( btRadians( 45.0 ) );
+
+    mCollision = true;
+}
+
+
+btKinematicCharacterController::~btKinematicCharacterController ()
+{
+}
+
+void btKinematicCharacterController::setVerticalVelocity(float z)
+{
+    m_verticalVelocity = z;
+}
+
+bool btKinematicCharacterController::recoverFromPenetration( btCollisionWorld* collisionWorld )
+{
+    bool penetration = false;
+
+    if(!mCollision) return penetration;
+
+    collisionWorld->getDispatcher()->dispatchAllCollisionPairs( internalGhostObject->getOverlappingPairCache(),
+        collisionWorld->getDispatchInfo(),
+        collisionWorld->getDispatcher() );
+
+    btVector3 currentPosition = internalGhostObject->getWorldTransform().getOrigin();
+
+    btScalar maxPen = btScalar( 0 );
+
+    for( int i = 0; i < internalGhostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++ )
+    {
+        m_manifoldArray.resize(0);
+
+        btBroadphasePair* collisionPair = &internalGhostObject->getOverlappingPairCache()->getOverlappingPairArray()[i];
+
+        if( collisionPair->m_algorithm )
+            collisionPair->m_algorithm->getAllContactManifolds( m_manifoldArray );
+
+
+        for( int j = 0; j < m_manifoldArray.size(); j++ )
+        {
+            btPersistentManifold* manifold = m_manifoldArray[j];
+
+            btScalar directionSign = manifold->getBody0() == internalGhostObject ? btScalar( -1.0 ) : btScalar( 1.0 );
+
+            for( int p = 0; p < manifold->getNumContacts(); p++ )
+            {
+                const btManifoldPoint&pt = manifold->getContactPoint( p );
+                if( (manifold->getBody1() == externalGhostObject && manifold->getBody0() == internalGhostObject)
+                    ||(manifold->getBody0() == externalGhostObject && manifold->getBody1() == internalGhostObject) )
+                {
+                }
+                else
+                {
+                    btScalar dist = pt.getDistance();
+
+                    if( dist < 0.0 )
+                    {
+                        if( dist < maxPen )
+                            maxPen = dist;
+
+                        // NOTE : btScalar affects the stairs but the parkinson...
+                        // 0.0 , the capsule can break the walls...
+                        currentPosition += pt.m_normalWorldOnB * directionSign * dist * m_recoveringFactor;
+
+                        penetration = true;
+                    }
+                }
+            }
+
+            // ???
+            //manifold->clearManifold();
+        }
+    }
+
+    btTransform transform = internalGhostObject->getWorldTransform();
+
+    transform.setOrigin( currentPosition );
+
+    internalGhostObject->setWorldTransform( transform );
+    externalGhostObject->setWorldTransform( transform );
+
+    return penetration;
+}
+
+
+btVector3 btKinematicCharacterController::stepUp( btCollisionWorld* world, const btVector3& currentPosition, btScalar& currentStepOffset )
+{
+    btVector3 targetPosition = currentPosition + getUpAxisDirections()[ m_upAxis ] * ( m_stepHeight + ( m_verticalOffset > btScalar( 0.0 ) ? m_verticalOffset : 0.0 ) );
+
+    //if the no collisions mode is on, no need to go any further
+    if(!mCollision)
+    {
+        currentStepOffset = m_stepHeight;
+        return targetPosition;
+    }
+
+    // Retrieve the collision shape
+    //
+    btCollisionShape* collisionShape = externalGhostObject->getCollisionShape();
+    btAssert( collisionShape->isConvex() );
+
+    btConvexShape* convexShape = ( btConvexShape* )collisionShape;
+
+    // FIXME: Handle penetration properly
+    //
+    btTransform start;
+    start.setIdentity();
+    start.setOrigin( currentPosition + getUpAxisDirections()[ m_upAxis ] * ( convexShape->getMargin() ) );
+
+    btTransform end;
+    end.setIdentity();
+    end.setOrigin( targetPosition );
+
+    btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, -getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine );
+    callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup;
+    callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask;
+
+    // Sweep test
+    //
+    if( m_useGhostObjectSweepTest )
+        externalGhostObject->convexSweepTest( convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration );
+
+    else
+        world->convexSweepTest( convexShape, start, end, callback );
+
+    if( callback.hasHit() )
+    {
+        // Only modify the position if the hit was a slope and not a wall or ceiling.
+        //
+        if( callback.m_hitNormalWorld.dot(getUpAxisDirections()[m_upAxis]) > btScalar( 0.0 ) )
+        {
+            // We moved up only a fraction of the step height
+            //
+            currentStepOffset = m_stepHeight * callback.m_closestHitFraction;
+
+            return currentPosition.lerp( targetPosition, callback.m_closestHitFraction );
+        }
+
+        m_verticalVelocity = btScalar( 0.0 );
+        m_verticalOffset = btScalar( 0.0 );
+
+        return currentPosition;
+    }
+    else
+    {
+        currentStepOffset = m_stepHeight;
+        return targetPosition;
+    }
+}
+
+
+///Reflect the vector d around the vector r
+inline btVector3 reflect( const btVector3& d, const btVector3& r )
+{
+    return d - ( btScalar( 2.0 ) * d.dot( r ) ) * r;
+}
+
+
+///Project a vector u on another vector v
+inline btVector3 project( const btVector3& u, const btVector3& v )
+{
+    return v * u.dot( v );
+}
+
+
+///Helper for computing the character sliding
+inline btVector3 slide( const btVector3& direction, const btVector3& planeNormal )
+{
+    return direction - project( direction, planeNormal );
+}
+
+
+
+btVector3 slideOnCollision( const btVector3& fromPosition, const btVector3& toPosition, const btVector3& hitNormal )
+{
+    btVector3 moveDirection = toPosition - fromPosition;
+    btScalar moveLength = moveDirection.length();
+
+    if( moveLength <= btScalar( SIMD_EPSILON ) )
+        return toPosition;
+
+    moveDirection.normalize();
+
+    btVector3 reflectDir = reflect( moveDirection, hitNormal );
+    reflectDir.normalize();
+
+    return fromPosition + slide( reflectDir, hitNormal ) * moveLength;
+}
+
+
+btVector3 btKinematicCharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& currentPosition, const btVector3& walkMove )
+{
+    // We go to !
+    //
+    btVector3 targetPosition = currentPosition + walkMove;
+
+    //if the no collisions mode is on, no need to go any further
+    if(!mCollision) return targetPosition;
+
+    // Retrieve the collision shape
+    //
+    btCollisionShape* collisionShape = externalGhostObject->getCollisionShape();
+    btAssert( collisionShape->isConvex() );
+
+    btConvexShape* convexShape = ( btConvexShape* )collisionShape;
+
+    btTransform start;
+    start.setIdentity();
+
+    btTransform end;
+    end.setIdentity();
+
+    btScalar fraction = btScalar( 1.0 );
+
+    // This optimization scheme suffers in the corners.
+    // It basically jumps from a wall to another, then fails to find a new
+    // position (after 4 iterations here) and finally don't move at all.
+    //
+    // The stepping algorithm adds some problems with stairs. It seems
+    // the treads create some fake corner using capsules for collisions.
+    //
+    for( int i = 0; i < 4 && fraction > btScalar( 0.01 ); i++ )
+    {
+        start.setOrigin( currentPosition );
+        end.setOrigin( targetPosition );
+
+        btVector3 sweepDirNegative = currentPosition - targetPosition;
+
+        btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, sweepDirNegative, btScalar( 0.0 ) );
+        callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup;
+        callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask;
+
+        if( m_useGhostObjectSweepTest )
+            externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration );
+
+        else
+            collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration );
+
+        if( callback.hasHit() )
+        {
+            // Try another target position
+            //
+            targetPosition = slideOnCollision( currentPosition, targetPosition, callback.m_hitNormalWorld );
+            fraction = callback.m_closestHitFraction;
+        }
+        else
+
+            // Move to the valid target position
+            //
+            return targetPosition;
+    }
+
+    // Don't move if you can't find a valid target position...
+    // It prevents some flickering.
+    //
+    return currentPosition;
+}
+
+
+///Handle the gravity
+btScalar btKinematicCharacterController::addFallOffset( bool wasOnGround, btScalar currentStepOffset, btScalar dt )
+{
+    btScalar downVelocity = ( m_verticalVelocity < 0.0 ? -m_verticalVelocity : btScalar( 0.0 ) ) * dt;
+
+    if( downVelocity > btScalar( 0.0 ) && downVelocity < m_stepHeight && ( wasOnGround || !m_wasJumping ) )
+        downVelocity = m_stepHeight;
+
+    return currentStepOffset + downVelocity;
+}
+
+
+btVector3 btKinematicCharacterController::stepDown( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar currentStepOffset )
+{
+    btVector3 stepDrop = getUpAxisDirections()[ m_upAxis ] * currentStepOffset;
+
+    // Be sure we are falling from the last m_currentPosition
+    // It prevents some flickering
+    //
+    btVector3 targetPosition = currentPosition - stepDrop;
+
+    //if the no collisions mode is on, no need to go any further
+    if(!mCollision) return targetPosition;
+
+    btTransform start;
+    start.setIdentity();
+    start.setOrigin( currentPosition );
+
+    btTransform end;
+    end.setIdentity();
+    end.setOrigin( targetPosition );
+
+    btKinematicClosestNotMeConvexResultCallback callback( internalGhostObject, getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine );
+    callback.m_collisionFilterGroup = internalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup;
+    callback.m_collisionFilterMask = internalGhostObject->getBroadphaseHandle()->m_collisionFilterMask;
+
+    // Retrieve the collision shape
+    //
+    btCollisionShape* collisionShape = internalGhostObject->getCollisionShape();
+    btAssert( collisionShape->isConvex() );
+    btConvexShape* convexShape = ( btConvexShape* )collisionShape;
+
+    if( m_useGhostObjectSweepTest )
+        externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration );
+
+    else
+        collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration );
+
+    if( callback.hasHit() )
+    {
+        m_verticalVelocity = btScalar( 0.0 );
+        m_verticalOffset = btScalar( 0.0 );
+        m_wasJumping = false;
+
+        // We dropped a fraction of the height -> hit floor
+        //
+        return currentPosition.lerp( targetPosition, callback.m_closestHitFraction );
+    }
+    else
+
+        // We dropped the full height
+        //
+        return targetPosition;
+}
+
+
+
+void btKinematicCharacterController::setWalkDirection( const btVector3& walkDirection )
+{
+    m_useWalkDirection = true;
+    m_walkDirection = walkDirection;
+}
+
+
+void btKinematicCharacterController::setVelocityForTimeInterval( const btVector3& velocity, btScalar timeInterval )
+{
+    m_useWalkDirection = false;
+    m_walkDirection = velocity;
+    m_velocityTimeInterval = timeInterval;
+}
+
+
+void btKinematicCharacterController::reset()
+{
+}
+
+
+void btKinematicCharacterController::warp( const btVector3& origin )
+{
+    btTransform transform;
+    transform.setIdentity();
+    transform.setOrigin( -origin );
+
+    externalGhostObject->setWorldTransform( transform );
+    internalGhostObject->setWorldTransform( transform );
+}
+
+
+void btKinematicCharacterController::preStep( btCollisionWorld* collisionWorld )
+{
+    BT_PROFILE( "preStep" );
+
+    for( int i = 0; i < 4 && recoverFromPenetration ( collisionWorld ); i++ );
+}
+
+
+void btKinematicCharacterController::playerStep( btCollisionWorld* collisionWorld, btScalar dt )
+{
+    BT_PROFILE( "playerStep" );
+
+    if( !m_useWalkDirection && m_velocityTimeInterval <= btScalar( 0.0 ) )
+        return;
+
+    bool wasOnGround = onGround();
+
+    // Handle the gravity
+    //
+    m_verticalVelocity -= m_gravity * dt;
+
+    if( m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed )
+        m_verticalVelocity = m_jumpSpeed;
+
+    if( m_verticalVelocity < 0.0 && btFabs( m_verticalVelocity ) > btFabs( m_fallSpeed ) )
+        m_verticalVelocity = -btFabs( m_fallSpeed );
+
+    m_verticalOffset = m_verticalVelocity * dt;
+
+    // This forced stepping up can cause problems when the character
+    // walks (jump in fact...) under too low ceilings.
+    //
+    btVector3 currentPosition = externalGhostObject->getWorldTransform().getOrigin();
+    btScalar currentStepOffset;
+
+    currentPosition = stepUp( collisionWorld, currentPosition, currentStepOffset );
+
+    // Move in the air and slide against the walls ignoring the stair steps.
+    //
+    if( m_useWalkDirection )
+        currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, m_walkDirection );
+
+    else
+    {
+        btScalar dtMoving = ( dt < m_velocityTimeInterval ) ? dt : m_velocityTimeInterval;
+        m_velocityTimeInterval -= dt;
+
+        // How far will we move while we are moving ?
+        //
+        btVector3 moveDirection = m_walkDirection * dtMoving;
+
+        currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, moveDirection );
+    }
+
+    // Finally find the ground.
+    //
+    currentStepOffset = addFallOffset( wasOnGround, currentStepOffset, dt );
+
+    currentPosition = stepDown( collisionWorld, currentPosition, currentStepOffset );
+
+    // Apply the new position to the collision objects.
+    //
+    btTransform tranform;
+    tranform = externalGhostObject->getWorldTransform();
+    tranform.setOrigin( currentPosition );
+
+    externalGhostObject->setWorldTransform( tranform );
+    internalGhostObject->setWorldTransform( tranform );
+}
+
+
+void btKinematicCharacterController::setFallSpeed( btScalar fallSpeed )
+{
+    m_fallSpeed = fallSpeed;
+}
+
+
+void btKinematicCharacterController::setJumpSpeed( btScalar jumpSpeed )
+{
+    m_jumpSpeed = jumpSpeed;
+}
+
+
+void btKinematicCharacterController::setMaxJumpHeight( btScalar maxJumpHeight )
+{
+    m_maxJumpHeight = maxJumpHeight;
+}
+
+
+bool btKinematicCharacterController::canJump() const
+{
+    return onGround();
+}
+
+
+void btKinematicCharacterController::jump()
+{
+    if( !canJump() )
+        return;
+
+    m_verticalVelocity = m_jumpSpeed;
+    m_wasJumping = true;
+}
+
+
+void btKinematicCharacterController::setGravity( btScalar gravity )
+{
+    m_gravity = gravity;
+}
+
+
+btScalar btKinematicCharacterController::getGravity() const
+{
+    return m_gravity;
+}
+
+
+void btKinematicCharacterController::setMaxSlope( btScalar slopeRadians )
+{
+    m_maxSlopeRadians = slopeRadians;
+    m_maxSlopeCosine = btCos( slopeRadians );
+}
+
+
+btScalar btKinematicCharacterController::getMaxSlope() const
+{
+    return m_maxSlopeRadians;
+}
+
+
+bool btKinematicCharacterController::onGround() const
+{
+    return btFabs( m_verticalVelocity ) < btScalar( SIMD_EPSILON ) &&
+        btFabs( m_verticalOffset ) < btScalar( SIMD_EPSILON );
+}
+
+
+btVector3* btKinematicCharacterController::getUpAxisDirections()
+{
+    static btVector3 sUpAxisDirection[] =
+    {
+        btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 0.0 ) ),
+        btVector3( btScalar( 0.0 ), btScalar( 1.0 ), btScalar( 0.0 ) ),
+        btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 1.0 ) )
+    };
+
+    return sUpAxisDirection;
+}
+
+
+void btKinematicCharacterController::debugDraw( btIDebugDraw* debugDrawer )
+{
+}
diff --git a/libs/openengine/bullet/btKinematicCharacterController.h b/libs/openengine/bullet/btKinematicCharacterController.h
new file mode 100644
index 0000000000..d24cd97222
--- /dev/null
+++ b/libs/openengine/bullet/btKinematicCharacterController.h
@@ -0,0 +1,168 @@
+/*
+Bullet Continuous Collision Detection and Physics Library
+Copyright (c) 2003-2008 Erwin Coumans  http://bulletphysics.com
+
+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.
+*/
+
+#ifndef KINEMATIC_CHARACTER_CONTROLLER_H
+#define KINEMATIC_CHARACTER_CONTROLLER_H
+
+#include "LinearMath/btVector3.h"
+#include "LinearMath/btQuickprof.h"
+
+#include "BulletDynamics/Character/btCharacterControllerInterface.h"
+
+#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
+
+
+class btCollisionShape;
+class btRigidBody;
+class btCollisionWorld;
+class btCollisionDispatcher;
+class btPairCachingGhostObject;
+
+///btKinematicCharacterController is an object that supports a sliding motion in a world.
+///It uses a ghost object and convex sweep test to test for upcoming collisions. This is combined with discrete collision detection to recover from penetrations.
+///Interaction between btKinematicCharacterController and dynamic rigid bodies needs to be explicity implemented by the user.
+class btKinematicCharacterController : public btCharacterControllerInterface
+{
+public:
+  enum UpAxis
+  {
+    X_AXIS = 0,
+    Y_AXIS = 1,
+    Z_AXIS = 2
+  };
+
+private:
+    btPairCachingGhostObject* externalGhostObject;  // use this for querying collisions for sliding and move
+    btPairCachingGhostObject* internalGhostObject;  // and this for recoreving from penetrations
+
+    btScalar m_verticalVelocity;
+    btScalar m_verticalOffset;
+    btScalar m_fallSpeed;
+    btScalar m_jumpSpeed;
+    btScalar m_maxJumpHeight;
+    btScalar m_maxSlopeRadians; // Slope angle that is set (used for returning the exact value)
+    btScalar m_maxSlopeCosine;  // Cosine equivalent of m_maxSlopeRadians (calculated once when set, for optimization)
+    btScalar m_gravity;
+    btScalar m_recoveringFactor;
+
+    btScalar m_stepHeight;
+
+    ///this is the desired walk direction, set by the user
+    btVector3   m_walkDirection;
+
+    ///keep track of the contact manifolds
+    btManifoldArray m_manifoldArray;
+
+  ///Gravity attributes
+    bool  m_wasJumping;
+
+    bool    m_useGhostObjectSweepTest;
+    bool    m_useWalkDirection;
+    btScalar    m_velocityTimeInterval;
+
+    UpAxis m_upAxis;
+
+    static btVector3* getUpAxisDirections();
+
+    bool recoverFromPenetration ( btCollisionWorld* collisionWorld );
+
+    btVector3 stepUp( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar& currentStepOffset );
+  btVector3 stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& currentPosition, const btVector3& walkMove );
+  btScalar addFallOffset( bool wasJumping, btScalar currentStepOffset, btScalar dt );
+  btVector3 stepDown( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar currentStepOffset );
+
+public:
+  /// externalGhostObject is used for querying the collisions for sliding along the wall,
+  /// and internalGhostObject is used for querying the collisions for recovering from large penetrations.
+  /// These parameters can point on the same object.
+  /// Using a smaller internalGhostObject can help for removing some flickering but create some
+  /// stopping artefacts when sliding along stairs or small walls.
+  /// Don't forget to scale gravity and fallSpeed if you scale the world.
+    btKinematicCharacterController( btPairCachingGhostObject* externalGhostObject,
+                                  btPairCachingGhostObject* internalGhostObject,
+                                  btScalar stepHeight,
+                                  btScalar constantScale = btScalar( 1.0 ),
+                                  btScalar gravity = btScalar( 9.8 ),
+                                  btScalar fallVelocity = btScalar( 55.0 ),
+                                  btScalar jumpVelocity = btScalar( 9.8 ),
+                                  btScalar recoveringFactor = btScalar( 0.2 ) );
+
+    ~btKinematicCharacterController ();
+
+    void setVerticalVelocity(float z);
+
+    ///btActionInterface interface
+    virtual void updateAction( btCollisionWorld* collisionWorld, btScalar deltaTime )
+    {
+    preStep( collisionWorld );
+        playerStep( collisionWorld, deltaTime );
+    }
+
+    ///btActionInterface interface
+    void debugDraw( btIDebugDraw* debugDrawer );
+
+  void setUpAxis( UpAxis axis )
+    {
+        m_upAxis = axis;
+    }
+
+    /// This should probably be called setPositionIncrementPerSimulatorStep.
+    /// This is neither a direction nor a velocity, but the amount to
+    /// increment the position each simulation iteration, regardless
+    /// of dt.
+    /// This call will reset any velocity set by setVelocityForTimeInterval().
+    virtual void    setWalkDirection(const btVector3& walkDirection);
+
+    /// Caller provides a velocity with which the character should move for
+    /// the given time period.  After the time period, velocity is reset
+    /// to zero.
+    /// This call will reset any walk direction set by setWalkDirection().
+    /// Negative time intervals will result in no motion.
+    virtual void setVelocityForTimeInterval(const btVector3& velocity,
+                btScalar timeInterval);
+
+    void reset();
+    void warp( const btVector3& origin );
+
+    void preStep( btCollisionWorld* collisionWorld );
+    void playerStep( btCollisionWorld* collisionWorld, btScalar dt );
+
+    void setFallSpeed( btScalar fallSpeed );
+    void setJumpSpeed( btScalar jumpSpeed );
+    void setMaxJumpHeight( btScalar maxJumpHeight );
+    bool canJump() const;
+
+    void jump();
+
+    void setGravity( btScalar gravity );
+    btScalar getGravity() const;
+
+    /// The max slope determines the maximum angle that the controller can walk up.
+    /// The slope angle is measured in radians.
+    void setMaxSlope( btScalar slopeRadians );
+    btScalar getMaxSlope() const;
+
+    void setUseGhostSweepTest( bool useGhostObjectSweepTest )
+    {
+        m_useGhostObjectSweepTest = useGhostObjectSweepTest;
+    }
+
+    bool onGround() const;
+
+    //if set to false, there will be no collision.
+    bool mCollision;
+};
+
+#endif // KINEMATIC_CHARACTER_CONTROLLER_H
diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp
new file mode 100644
index 0000000000..07bad30535
--- /dev/null
+++ b/libs/openengine/bullet/physic.cpp
@@ -0,0 +1,375 @@
+#include "physic.hpp"
+#include <btBulletDynamicsCommon.h>
+#include <btBulletCollisionCommon.h>
+#include <components/nifbullet/bullet_nif_loader.hpp>
+//#include <apps\openmw\mwworld\world.hpp>
+#include "CMotionState.h"
+#include "OgreRoot.h"
+#include "btKinematicCharacterController.h"
+#include "BtOgrePG.h"
+#include "BtOgreGP.h"
+#include "BtOgreExtras.h"
+
+#define BIT(x) (1<<(x))
+
+namespace OEngine {
+namespace Physic
+{
+    enum collisiontypes {
+        COL_NOTHING = 0, //<Collide with nothing
+        COL_WORLD = BIT(0), //<Collide with world objects
+        COL_ACTOR_INTERNAL = BIT(1), //<Collide internal capsule
+        COL_ACTOR_EXTERNAL = BIT(2) //<collide with external capsule
+    };
+
+    PhysicActor::PhysicActor(std::string name)
+    {
+        mName = name;
+
+        // The capsule is at the origin
+        btTransform transform;
+        transform.setIdentity();
+
+        // External capsule
+        externalGhostObject = new PairCachingGhostObject(name);
+        externalGhostObject->setWorldTransform( transform );
+
+        btScalar externalCapsuleHeight = 120;
+        btScalar externalCapsuleWidth = 19;
+
+        externalCollisionShape = new btCapsuleShapeZ( externalCapsuleWidth,  externalCapsuleHeight );
+        externalCollisionShape->setMargin( 0.1 );
+
+        externalGhostObject->setCollisionShape( externalCollisionShape );
+        externalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT );
+
+        // Internal capsule
+        internalGhostObject = new PairCachingGhostObject(name);
+        internalGhostObject->setWorldTransform( transform );
+        //internalGhostObject->getBroadphaseHandle()->s
+        btScalar internalCapsuleHeight =  110;
+        btScalar internalCapsuleWidth =  17;
+
+        internalCollisionShape = new btCapsuleShapeZ( internalCapsuleWidth, internalCapsuleHeight );
+        internalCollisionShape->setMargin( 0.1 );
+
+        internalGhostObject->setCollisionShape( internalCollisionShape );
+        internalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT );
+
+        mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 40 ),1,4,20,9.8,0.2 );
+        mCharacter->setUpAxis(btKinematicCharacterController::Z_AXIS);
+        mCharacter->setUseGhostSweepTest(false);
+
+        mCharacter->mCollision = false;
+        setGravity(0);
+
+        mTranslation = btVector3(0,0,70);
+    }
+
+    PhysicActor::~PhysicActor()
+    {
+        delete mCharacter;
+        delete internalGhostObject;
+        delete internalCollisionShape;
+        delete externalGhostObject;
+        delete externalCollisionShape;
+    }
+
+    void PhysicActor::setGravity(float gravity)
+    {
+        mCharacter->setGravity(gravity);
+        //mCharacter->
+    }
+
+    void PhysicActor::enableCollisions(bool collision)
+    {
+        mCharacter->mCollision = collision;
+    }
+
+    void PhysicActor::setVerticalVelocity(float z)
+    {
+        mCharacter->setVerticalVelocity(z);
+    }
+
+    bool PhysicActor::getCollisionMode()
+    {
+        return mCharacter->mCollision;
+    }
+
+    void PhysicActor::setWalkDirection(const btVector3& mvt)
+    {
+        mCharacter->setWalkDirection( mvt );
+    }
+
+    void PhysicActor::Rotate(const btQuaternion& quat)
+    {
+        externalGhostObject->getWorldTransform().setRotation( externalGhostObject->getWorldTransform().getRotation() * quat );
+        internalGhostObject->getWorldTransform().setRotation( internalGhostObject->getWorldTransform().getRotation() * quat );
+    }
+
+    void PhysicActor::setRotation(const btQuaternion& quat)
+    {
+        externalGhostObject->getWorldTransform().setRotation( quat );
+        internalGhostObject->getWorldTransform().setRotation( quat );
+    }
+
+    btVector3 PhysicActor::getPosition(void)
+    {
+        return internalGhostObject->getWorldTransform().getOrigin() -mTranslation;
+    }
+
+    btQuaternion PhysicActor::getRotation(void)
+    {
+        return internalGhostObject->getWorldTransform().getRotation();
+    }
+
+    void PhysicActor::setPosition(const btVector3& pos)
+    {
+        internalGhostObject->getWorldTransform().setOrigin(pos+mTranslation);
+        externalGhostObject->getWorldTransform().setOrigin(pos+mTranslation);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+    RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name)
+        :btRigidBody(CI),mName(name)
+    {
+
+    };
+
+
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+
+    PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader)
+    {
+        // Set up the collision configuration and dispatcher
+        collisionConfiguration = new btDefaultCollisionConfiguration();
+        dispatcher = new btCollisionDispatcher(collisionConfiguration);
+
+        // The actual physics solver
+        solver = new btSequentialImpulseConstraintSolver;
+
+        //TODO: memory leak?
+        btOverlappingPairCache* pairCache = new btSortedOverlappingPairCache();
+        //pairCache->setInternalGhostPairCallback( new btGhostPairCallback() );
+
+        broadphase = new btDbvtBroadphase(pairCache);
+
+        // The world.
+        dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration);
+        dynamicsWorld->setGravity(btVector3(0,0,-10));
+
+        if(BulletShapeManager::getSingletonPtr() == NULL)
+        {
+            new BulletShapeManager();
+        }
+        //TODO:singleton?
+        mShapeLoader = shapeLoader;
+
+        isDebugCreated = false;
+    }
+
+    void PhysicEngine::createDebugRendering()
+    {
+        if(!isDebugCreated)
+        {
+            Ogre::SceneManagerEnumerator::SceneManagerIterator iter = Ogre::Root::getSingleton().getSceneManagerIterator();
+            iter.begin();
+            Ogre::SceneManager* scn = iter.getNext();
+            Ogre::SceneNode* node = scn->getRootSceneNode()->createChildSceneNode();
+            node->pitch(Ogre::Degree(-90));
+            mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld);
+            dynamicsWorld->setDebugDrawer(mDebugDrawer);
+            isDebugCreated = true;
+            dynamicsWorld->debugDrawWorld();
+        }
+    }
+
+    void PhysicEngine::setDebugRenderingMode(int mode)
+    {
+        if(!isDebugCreated)
+        {
+            createDebugRendering();
+        }
+        mDebugDrawer->setDebugMode(mode);
+    }
+
+    PhysicEngine::~PhysicEngine()
+    {
+        delete dynamicsWorld;
+        delete solver;
+        delete collisionConfiguration;
+        delete dispatcher;
+        delete broadphase;
+        delete mShapeLoader;
+    }
+
+    RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name,float scale)
+    {
+        //get the shape from the .nif
+        mShapeLoader->load(mesh,"General");
+        BulletShapeManager::getSingletonPtr()->load(mesh,"General");
+        BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(mesh,"General");
+        shape->Shape->setLocalScaling(btVector3(scale,scale,scale));
+
+        //create the motionState
+        CMotionState* newMotionState = new CMotionState(this,name);
+
+        //create the real body
+        btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,shape->Shape);
+        RigidBody* body = new RigidBody(CI,name);
+        body->collide = shape->collide;
+        return body;
+    }
+
+    void PhysicEngine::addRigidBody(RigidBody* body)
+    {
+        if(body->collide)
+        {
+            dynamicsWorld->addRigidBody(body,COL_WORLD,COL_WORLD|COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL);
+        }
+        else
+        {
+            dynamicsWorld->addRigidBody(body,COL_WORLD,COL_NOTHING);
+        }
+        body->setActivationState(DISABLE_DEACTIVATION);
+        RigidBodyMap[body->mName] = body;
+    }
+
+    void PhysicEngine::removeRigidBody(std::string name)
+    {
+        std::map<std::string,RigidBody*>::iterator it = RigidBodyMap.find(name);
+        if (it != RigidBodyMap.end() )
+        {
+            RigidBody* body = it->second;
+            if(body != NULL)
+            {
+                // broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher);
+                /*std::map<std::string,PhysicActor*>::iterator it2 = PhysicActorMap.begin();
+                  for(;it2!=PhysicActorMap.end();it++)
+                  {
+                  it2->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher);
+                  it2->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher);
+                  }*/
+                dynamicsWorld->removeRigidBody(RigidBodyMap[name]);
+            }
+        }
+    }
+
+    void PhysicEngine::deleteRigidBody(std::string name)
+    {
+        std::map<std::string,RigidBody*>::iterator it = RigidBodyMap.find(name);
+        if (it != RigidBodyMap.end() )
+        {
+            RigidBody* body = it->second;
+            if(body != NULL)
+            {
+                delete body;
+            }
+            RigidBodyMap.erase(it);
+        }
+    }
+
+    RigidBody* PhysicEngine::getRigidBody(std::string name)
+    {
+        RigidBody* body = RigidBodyMap[name];
+        return body;
+    }
+
+    void PhysicEngine::stepSimulation(double deltaT)
+    {
+        dynamicsWorld->stepSimulation(deltaT,1,1/50.);
+        if(isDebugCreated)
+        {
+            mDebugDrawer->step();
+        }
+    }
+
+    void PhysicEngine::addCharacter(std::string name)
+    {
+        PhysicActor* newActor = new PhysicActor(name);
+        dynamicsWorld->addCollisionObject( newActor->externalGhostObject, COL_ACTOR_EXTERNAL, COL_WORLD |COL_ACTOR_EXTERNAL );
+        dynamicsWorld->addCollisionObject( newActor->internalGhostObject, COL_ACTOR_INTERNAL, COL_WORLD |COL_ACTOR_INTERNAL );
+        dynamicsWorld->addAction( newActor->mCharacter );
+        PhysicActorMap[name] = newActor;
+    }
+
+    void PhysicEngine::removeCharacter(std::string name)
+    {
+        //std::cout << "remove";
+        std::map<std::string,PhysicActor*>::iterator it = PhysicActorMap.find(name);
+        if (it != PhysicActorMap.end() )
+        {
+            PhysicActor* act = it->second;
+            if(act != NULL)
+            {
+                /*broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher);
+                  broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher);
+                  std::map<std::string,PhysicActor*>::iterator it2 = PhysicActorMap.begin();
+                  for(;it2!=PhysicActorMap.end();it++)
+                  {
+                  it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher);
+                  it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher);
+                  it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher);
+                  it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher);
+                  }*/
+                //act->externalGhostObject->
+                dynamicsWorld->removeCollisionObject(act->externalGhostObject);
+                dynamicsWorld->removeCollisionObject(act->internalGhostObject);
+                dynamicsWorld->removeAction(act->mCharacter);
+                delete act;
+            }
+            PhysicActorMap.erase(it);
+        }
+        //std::cout << "ok";
+    }
+
+    PhysicActor* PhysicEngine::getCharacter(std::string name)
+    {
+        PhysicActor* act = PhysicActorMap[name];
+        return act;
+    }
+
+    void PhysicEngine::emptyEventLists(void)
+    {
+    }
+
+    std::pair<std::string,float> PhysicEngine::rayTest(btVector3& from,btVector3& to)
+    {
+        std::string name = "";
+        float d = -1;
+
+        float d1 = 10000.;
+        btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
+        resultCallback1.m_collisionFilterMask = COL_WORLD;
+        dynamicsWorld->rayTest(from, to, resultCallback1);
+        if (resultCallback1.hasHit())
+        {
+            name = static_cast<RigidBody&>(*resultCallback1.m_collisionObject).mName;
+            d1 = resultCallback1.m_closestHitFraction;
+            d = d1;
+        }
+
+        btCollisionWorld::ClosestRayResultCallback resultCallback2(from, to);
+        resultCallback2.m_collisionFilterMask = COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL;
+        dynamicsWorld->rayTest(from, to, resultCallback2);
+        float d2 = 10000.;
+        if (resultCallback2.hasHit())
+        {
+            d2 = resultCallback1.m_closestHitFraction;
+            if(d2<=d1)
+            {
+                name = static_cast<PairCachingGhostObject&>(*resultCallback2.m_collisionObject).mName;
+                d = d2;
+            }
+        }
+
+        return std::pair<std::string,float>(name,d);
+    }
+}};
diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp
new file mode 100644
index 0000000000..88e3699aee
--- /dev/null
+++ b/libs/openengine/bullet/physic.hpp
@@ -0,0 +1,230 @@
+#ifndef OENGINE_BULLET_PHYSIC_H
+#define OENGINE_BULLET_PHYSIC_H
+
+#include <BulletDynamics/Dynamics/btRigidBody.h>
+#include "BulletCollision/CollisionDispatch/btGhostObject.h"
+#include <string>
+#include <list>
+#include <map>
+#include "BulletShapeLoader.h"
+
+class btRigidBody;
+class btBroadphaseInterface;
+class btDefaultCollisionConfiguration;
+class btSequentialImpulseConstraintSolver;
+class btCollisionDispatcher;
+class btDiscreteDynamicsWorld;
+class btKinematicCharacterController;
+
+namespace BtOgre
+{
+    class DebugDrawer;
+}
+
+namespace MWWorld
+{
+    class World;
+}
+
+namespace OEngine {
+namespace Physic
+{
+    class CMotionState;
+    struct PhysicEvent;
+
+    /**
+    *This is just used to be able to name objects.
+    */
+    class PairCachingGhostObject : public btPairCachingGhostObject
+    {
+    public:
+        PairCachingGhostObject(std::string name)
+            :btPairCachingGhostObject(),mName(name)
+        {
+        }
+        std::string mName;
+    };
+
+    /**
+     * A physic Actor use a modifed KinematicCharacterController taken in the bullet forum.
+     */
+    class PhysicActor
+    {
+    public:
+        PhysicActor(std::string name);
+
+        ~PhysicActor();
+
+        /**
+         * This function set the walkDirection. This is not relative to the actor orientation.
+         * I think it's also needed to take time into account. A typical call should look like this:
+         * setWalkDirection( mvt * orientation * dt)
+         */
+        void setWalkDirection(const btVector3& mvt);
+
+        void Rotate(const btQuaternion& quat);
+
+        void setRotation(const btQuaternion& quat);
+
+        void setGravity(float gravity);
+
+        void setVerticalVelocity(float z);
+
+        void enableCollisions(bool collision);
+
+        bool getCollisionMode();
+
+        btVector3 getPosition(void);
+
+        btQuaternion getRotation(void);
+
+        void setPosition(const btVector3& pos);
+
+        btKinematicCharacterController* mCharacter;
+
+        PairCachingGhostObject* internalGhostObject;
+        btCollisionShape* internalCollisionShape;
+
+        PairCachingGhostObject* externalGhostObject;
+        btCollisionShape* externalCollisionShape;
+
+        std::string mName;
+
+        /**
+        *NPC scenenode is located on there feet, and you can't simply translate a btShape, so this vector is used
+        *each time get/setposition is called.
+        */
+        btVector3 mTranslation;
+    };
+
+    /**
+     *This class is just an extension of normal btRigidBody in order to add extra info.
+     *When bullet give back a btRigidBody, you can just do a static_cast to RigidBody,
+     *so one never should use btRigidBody directly!
+     */
+    class RigidBody: public btRigidBody
+    {
+    public:
+        RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name);
+        std::string mName;
+
+        //is this body used for raycasting only?
+        bool collide;
+    };
+
+    /**
+     * The PhysicEngine class contain everything which is needed for Physic.
+     * It's needed that Ogre Resources are set up before the PhysicEngine is created.
+     * Note:deleting it WILL NOT delete the RigidBody!
+     * TODO:unload unused resources?
+     */
+    class PhysicEngine
+    {
+    public:
+        /**
+         * Note that the shapeLoader IS destroyed by the phyic Engine!!
+         */
+        PhysicEngine(BulletShapeLoader* shapeLoader);
+
+        /**
+         * It DOES destroy the shape loader!
+         */
+        ~PhysicEngine();
+
+        /**
+         * Create a RigidBody.It does not add it to the simulation, but it does add it to the rigidBody Map,
+         * so you can get it with the getRigidBody function.
+         */
+        RigidBody* createRigidBody(std::string mesh,std::string name,float scale);
+
+        /**
+         * Add a RigidBody to the simulation
+         */
+        void addRigidBody(RigidBody* body);
+
+        /**
+         * Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap.
+         */
+        void removeRigidBody(std::string name);
+
+        /**
+         * Delete a RigidBody, and remove it from RigidBodyMap.
+         */
+        void deleteRigidBody(std::string name);
+
+        /**
+         * Return a pointer to a given rigid body.
+         * TODO:check if exist
+         */
+        RigidBody* getRigidBody(std::string name);
+
+        /**
+         * Create and add a character to the scene, and add it to the ActorMap.
+         */
+        void addCharacter(std::string name);
+
+        /**
+         * Remove a character from the scene. TODO:delete it! for now, a small memory leak^^ done?
+         */
+        void removeCharacter(std::string name);
+
+        /**
+         * Return a pointer to a character
+         * TODO:check if the actor exist...
+         */
+        PhysicActor* getCharacter(std::string name);
+
+        /**
+         * This step the simulation of a given time.
+         */
+        void stepSimulation(double deltaT);
+
+        /**
+         * Empty events lists
+         */
+        void emptyEventLists(void);
+
+        /**
+         * Create a debug rendering. It is called by setDebgRenderingMode if it's not created yet.
+         * Important Note: this will crash if the Render is not yet initialise!
+         */
+        void createDebugRendering();
+
+        /**
+         * Set the debug rendering mode. 0 to turn it off.
+         * Important Note: this will crash if the Render is not yet initialise!
+         */
+        void setDebugRenderingMode(int mode);
+
+        /**
+         * Return the closest object hit by a ray. If there are no objects, it will return ("",-1).
+         */
+        std::pair<std::string,float> rayTest(btVector3& from,btVector3& to);
+
+        //event list of non player object
+        std::list<PhysicEvent> NPEventList;
+
+        //event list affecting the player
+        std::list<PhysicEvent> PEventList;
+
+        //Bullet Stuff
+        btBroadphaseInterface* broadphase;
+        btDefaultCollisionConfiguration* collisionConfiguration;
+        btSequentialImpulseConstraintSolver* solver;
+        btCollisionDispatcher* dispatcher;
+        btDiscreteDynamicsWorld* dynamicsWorld;
+
+        //the NIF file loader.
+        BulletShapeLoader* mShapeLoader;
+
+        std::map<std::string,RigidBody*> RigidBodyMap;
+        std::map<std::string,PhysicActor*> PhysicActorMap;
+
+        //debug rendering
+        BtOgre::DebugDrawer* mDebugDrawer;
+        bool isDebugCreated;
+    };
+
+}}
+
+#endif
diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp
new file mode 100644
index 0000000000..45fe84f4fc
--- /dev/null
+++ b/libs/openengine/bullet/pmove.cpp
@@ -0,0 +1,2042 @@
+/*
+This source file is a *modified* version of bg_pmove.c from the Quake 3 Arena source code,
+which was released under the GNU GPL (v2) in 2005.
+Quake 3 Arena is copyright (C) 1999-2005 Id Software, Inc.
+*/
+
+
+#include "pmove.h"
+
+
+
+//#include "bprintf.h"
+
+//#include "..\..\ESMParser\ESMParser\CELL.h"
+
+//#include "GameTime.h"
+
+//#include "Object.h"
+
+//#include "Sound.h"
+
+//#include "..\..\ESMParser\ESMParser\SNDG.h"
+//#include "..\..\ESMParser\ESMParser\SOUN.h"
+
+#include <map>
+
+//SceneInstance* global_lastscene = NULL;
+
+// Forward declaration:
+void PM_AirMove();
+
+static playerMove* pm = NULL;
+
+//extern std::map<CellCoords, CELL* const> ExtCellLookup;
+
+static struct playermoveLocal
+{
+	playermoveLocal() : frametime(1.0f / 20.0f), groundPlane(true), walking(true), msec(50)
+	{
+		forward = Ogre::Vector3(0.0f, 0.0f, 0.0f);
+		right = Ogre::Vector3(0.0f, 0.0f, 0.0f);
+		up = Ogre::Vector3(0.0f, 0.0f, 0.0f);
+
+		previous_origin = Ogre::Vector3(0.0f, 0.0f, 0.0f);
+		previous_velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f);
+	}
+
+	traceResults groundTrace;
+
+	//SceneInstance* scene;
+
+	float frametime; // in seconds (usually something like 0.01f)
+	float impactSpeed;
+
+	Ogre::Vector3 forward;
+	Ogre::Vector3 right;
+	Ogre::Vector3 up;
+
+	int msec;
+
+	Ogre::Vector3 previous_origin, previous_velocity;
+
+	int previous_waterlevel; // the waterlevel before this pmove
+
+	bool groundPlane; // if we're standing on a groundplane this frame
+
+	bool walking;
+	int waterHeight;
+	bool hasWater;
+	bool isInterior;
+	//Object* traceObj;
+
+} pml;
+
+static inline void PM_ClipVelocity(const Ogre::Vector3& in, const Ogre::Vector3& normal, Ogre::Vector3& out, const float overbounce)
+{
+	float	backoff;
+	//float	change;
+	//int		i;
+	
+	// backoff = in dot normal
+	//backoff = DotProduct (in, normal);
+	backoff = in.dotProduct(normal);
+	
+	if ( backoff < 0 )
+		backoff *= overbounce;
+	else
+		backoff /= overbounce;
+
+	// change = normal * backoff
+	// out = in - change
+	/*for ( i=0 ; i<3 ; i++ ) 
+	{
+		change = normal[i]*backoff;
+		out[i] = in[i] - change;
+	}*/
+	float changex = normal.x * backoff;
+	out.x = in.x - changex;
+	float changey = normal.y * backoff;
+	out.y = in.y - changey;
+	float changez = normal.z * backoff;
+	out.z = in.z - changez;
+}
+
+float VectorNormalize2( const Ogre::Vector3& v, Ogre::Vector3& out) 
+{
+	float	length, ilength;
+
+	length = v.x * v.x+ v.y * v.y + v.z * v.z;
+	length = sqrt(length);
+
+	if (length)
+	{
+#ifndef Q3_VM // bk0101022 - FPE related
+//	  assert( ((Q_fabs(v[0])!=0.0f) || (Q_fabs(v[1])!=0.0f) || (Q_fabs(v[2])!=0.0f)) );
+#endif
+		ilength = 1 / length;
+		out.x= v.x * ilength;
+		out.y = v.y * ilength;
+		out.z = v.z * ilength;
+	} else 
+	{
+#ifndef Q3_VM // bk0101022 - FPE related
+//	  assert( ((Q_fabs(v[0])==0.0f) && (Q_fabs(v[1])==0.0f) && (Q_fabs(v[2])==0.0f)) );
+#endif
+		//VectorClear( out );
+		out.x = 0; out.y = 0; out.z = 0;
+	}
+		
+	return length;
+
+}
+
+
+float VectorNormalize(Ogre::Vector3& out) 
+{
+	float	length, ilength;
+
+	length = out.x * out.x + out.y * out.y + out.z * out.z;
+	length = sqrt(length);
+
+	if (length)
+	{
+#ifndef Q3_VM // bk0101022 - FPE related
+//	  assert( ((Q_fabs(v[0])!=0.0f) || (Q_fabs(v[1])!=0.0f) || (Q_fabs(v[2])!=0.0f)) );
+#endif
+		ilength = 1 / length;
+		out.x = out.x * ilength;
+		out.y = out.y * ilength;
+		out.z = out.z * ilength;
+	} 
+		
+	return length;
+
+}
+
+/*
+==================
+PM_SlideMove
+
+Returns qtrue if the velocity was clipped in some way
+==================
+*/
+
+bool	PM_SlideMove( bool gravity ) 
+{
+	int			bumpcount, numbumps;
+	Ogre::Vector3		dir;
+	float		d;
+	int			numplanes;
+	Ogre::Vector3		planes[MAX_CLIP_PLANES];
+	Ogre::Vector3		primal_velocity;
+	Ogre::Vector3		clipVelocity;
+	int			i, j, k;
+	struct traceResults	trace;
+	Ogre::Vector3		end;
+	float		time_left;
+	float		into;
+	Ogre::Vector3		endVelocity;
+	Ogre::Vector3		endClipVelocity;
+	
+	numbumps = 4;
+
+	// primal_velocity = pm->ps->velocity
+	//VectorCopy (pm->ps->velocity, primal_velocity);
+	primal_velocity = pm->ps.velocity;
+
+	if ( gravity ) 
+	{
+		// endVelocity = pm->ps->velocity - vec3(0, 0, pm->ps->gravity * pml.frametime)
+		//VectorCopy( pm->ps->velocity, endVelocity );
+		endVelocity = pm->ps.velocity;
+		//endVelocity[2] -= pm->ps->gravity * pml.frametime;
+		endVelocity.y -= pm->ps.gravity * pml.frametime;
+
+		// pm->ps->velocity = avg(pm->ps->velocity.z, endVelocity.z)
+		//pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5;
+		pm->ps.velocity.y= (pm->ps.velocity.y + endVelocity.y) * 0.5f;
+
+		//primal_velocity[2] = endVelocity[2];
+		primal_velocity.y = endVelocity.y;
+
+		if ( pml.groundPlane ) 
+			// slide along the ground plane
+			//PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP );
+			PM_ClipVelocity(pm->ps.velocity, pml.groundTrace.planenormal, pm->ps.velocity, OVERCLIP);
+	}
+
+	time_left = pml.frametime;
+
+	// never turn against the ground plane
+	if ( pml.groundPlane ) 
+	{
+		numplanes = 1;
+
+		// planes[0] = pml.groundTrace.plane.normal
+		//VectorCopy( pml.groundTrace.plane.normal, planes[0] );
+		planes[0] = pml.groundTrace.planenormal;
+	} else 
+		numplanes = 0;
+
+	// never turn against original velocity
+	VectorNormalize2( pm->ps.velocity, planes[numplanes] );
+	numplanes++;
+
+	for ( bumpcount = 0; bumpcount < numbumps; bumpcount++ ) 
+	{
+
+		// calculate position we are trying to move to
+		//VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end );
+		end = pm->ps.origin + pm->ps.velocity * time_left;
+
+		// see if we can make it there
+		//pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask);
+		//tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&(end), *(const D3DXVECTOR3* const)&(pm->ps.velocity), 0, pml.traceObj);
+		newtrace(&trace, pm->ps.origin, end, halfExtents, Ogre::Math::DegreesToRadians (pm->ps.viewangles.y), pm->isInterior, pm->mEngine);
+
+		if (trace.allsolid) 
+		{
+			// entity is completely trapped in another solid
+			//pm->ps->velocity[2] = 0;	// don't build up falling damage, but allow sideways acceleration
+			pm->ps.velocity.y = 0;
+			return true;
+		}
+
+		if (trace.fraction > 0) 
+			// actually covered some distance
+			//VectorCopy (trace.endpos, pm->ps->origin);
+			pm->ps.origin = trace.endpos;
+
+		if (trace.fraction == 1)
+			 break;		// moved the entire distance
+
+		// save entity for contact8
+		//PM_AddTouchEnt( trace.entityNum );
+
+		time_left -= time_left * trace.fraction;
+
+		if (numplanes >= MAX_CLIP_PLANES) 
+		{
+			// this shouldn't really happen
+			//VectorClear( pm->ps->velocity );
+			pm->ps.velocity = Ogre::Vector3(0,0,0);
+			return true;
+		}
+
+		//
+		// if this is the same plane we hit before, nudge velocity
+		// out along it, which fixes some epsilon issues with
+		// non-axial planes
+		//
+		for ( i = 0 ; i < numplanes ; i++ ) 
+		{
+			if (trace.planenormal.dotProduct(planes[i]) > 0.99)     //OGRE::VECTOR3  ?
+			//if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) 
+			{
+				// pm->ps->velocity += (trace.plane.normal + pm->ps->velocity)
+				//VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity );
+				pm->ps.velocity = (trace.planenormal + pm->ps.velocity);
+				break;
+			}
+		}
+
+		if ( i < numplanes )
+			continue;
+
+		//VectorCopy (trace.plane.normal, planes[numplanes]);
+		planes[numplanes] = trace.planenormal;
+		numplanes++;
+
+		//
+		// modify velocity so it parallels all of the clip planes
+		//
+
+		// find a plane that it enters
+		for ( i = 0 ; i < numplanes ; i++ ) 
+		{
+			//into = DotProduct( pm->ps->velocity, planes[i] );
+			into = pm->ps.velocity.dotProduct(planes[i]);
+			if ( into >= 0.1 )
+				continue;		// move doesn't interact with the plane
+
+			// see how hard we are hitting things
+			if ( -into > pml.impactSpeed )
+				pml.impactSpeed = -into;
+
+			// slide along the plane
+			//PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP );
+			PM_ClipVelocity(pm->ps.velocity, planes[i], clipVelocity, OVERCLIP);
+
+			// slide along the plane
+			PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP );
+
+			// see if there is a second plane that the new move enters
+			for ( j = 0 ; j < numplanes ; j++ ) 
+			{
+				if ( j == i )
+					continue;
+
+				if (clipVelocity.dotProduct(planes[j]) >= 0.1)
+				//if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 )
+					continue;		// move doesn't interact with the plane
+
+				// try clipping the move to the plane
+				PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP );
+				PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP );
+
+				// see if it goes back into the first clip plane
+				if (clipVelocity.dotProduct(planes[i]) >= 0)
+				//if ( DotProduct( clipVelocity, planes[i] ) >= 0 )
+					continue;
+
+
+				// slide the original velocity along the crease
+				//dProduct (planes[i], planes[j], dir);
+				dir = planes[i].crossProduct(planes[j]) ;
+
+				//VectorNormalize( dir );
+				//D3DXVec3Normalize( (D3DXVECTOR3* const)&dir, (const D3DXVECTOR3* const)&dir);
+				VectorNormalize(dir);
+
+				//d = DotProduct( dir, pm->ps->velocity );
+				d = dir.dotProduct(pm->ps.velocity);
+
+				//VectorScale( dir, d, clipVelocity );
+				clipVelocity = dir * d;
+
+				//CrossProduct (planes[i], planes[j], dir);
+				dir = planes[i].crossProduct(planes[j]) ;
+		
+
+				//VectorNormalize( dir );
+				//D3DXVec3Normalize( (D3DXVECTOR3* const)&dir, (const D3DXVECTOR3* const)&dir);
+				VectorNormalize(dir);
+
+				//d = DotProduct( dir, endVelocity );
+				d = dir.dotProduct(endVelocity);
+
+				//VectorScale( dir, d, endClipVelocity );
+				endClipVelocity = dir * d;
+
+				// see if there is a third plane the the new move enters
+				for ( k = 0 ; k < numplanes ; k++ ) 
+				{
+					if ( k == i || k == j )
+						continue;
+
+					if (clipVelocity.dotProduct(planes[k]) >= 0.1)
+					//if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 )
+						continue;		// move doesn't interact with the plane
+
+					// stop dead at a tripple plane interaction
+					//VectorClear( pm->ps->velocity );
+					printf("Stop dead at a triple plane interaction\n");
+					pm->ps.velocity = Ogre::Vector3(0,0,0);
+					return true;
+				}
+			}
+
+			// if we have fixed all interactions, try another move
+			//VectorCopy( clipVelocity, pm->ps->velocity );
+			pm->ps.velocity = clipVelocity;
+
+			//VectorCopy( endClipVelocity, endVelocity );
+			endVelocity = endClipVelocity;
+			break;
+		}
+	}
+
+	if ( gravity )
+		//VectorCopy( endVelocity, pm->ps->velocity );
+		pm->ps.velocity = endVelocity;
+
+	// don't change velocity if in a timer (FIXME: is this correct?)
+	if ( pm->ps.pm_time )
+		//VectorCopy( primal_velocity, pm->ps->velocity );
+		pm->ps.velocity = primal_velocity;
+
+	//return ( (qboolean)(bumpcount != 0) );
+	return bumpcount != 0;
+}
+
+/*
+==================
+PM_StepSlideMove
+
+==================
+*/
+int PM_StepSlideMove( bool gravity ) 
+{
+	Ogre::Vector3		start_o, start_v;
+	Ogre::Vector3		down_o, down_v;
+	traceResults		trace;
+//	float		down_dist, up_dist;
+//	vec3_t		delta, delta2;
+	Ogre::Vector3		up, down;
+	float		stepSize;
+
+	// start_o = pm->ps->origin
+	//VectorCopy (pm->ps->origin, start_o);
+	start_o = pm->ps.origin;
+
+	// start_v = pm->ps->velocity
+	//VectorCopy (pm->ps->velocity, start_v);
+	start_v = pm->ps.velocity;
+
+	if ( PM_SlideMove( gravity ) == false )
+		return 1;		// we got exactly where we wanted to go first try	
+
+	// down = start_o - vec3(0, 0, STEPSIZE)
+	//VectorCopy(start_o, down);
+	down = start_o;
+	down.y -= STEPSIZE;
+
+	//pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask);
+	//tracefunc(&trace, start_o, down, , 0, pml.scene);
+	//tracefunc(&trace, *(const D3DXVECTOR3* const)&start_o, *(const D3DXVECTOR3* const)&down, D3DXVECTOR3(0.0f, -STEPSIZE, 0.0f), 0, pml.traceObj);
+	newtrace(&trace, start_o, down, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine);
+	
+	// up = vec3(0, 0, 1)
+	//VectorSet(up, 0, 0, 1);
+	up = Ogre::Vector3(0.0f, 1.0f, 0.0f);
+
+	// never step up when you still have up velocity
+	//if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 || DotProduct(trace.plane.normal, up) < 0.7)) 
+	if (pm->ps.velocity.y > 0 && (
+		trace.fraction == 1.0 || trace.planenormal.dotProduct(up) < 0.7
+		) )
+		return 2;
+
+	// down_o = pm->ps->origin
+	//VectorCopy (pm->ps->origin, down_o);
+	down_o = pm->ps.origin;
+
+	// down_v = pm->ps->velocity
+	//VectorCopy (pm->ps->velocity, down_v);
+	down_v = pm->ps.velocity;
+
+	// up = start_o + vec3(0, 0, STEPSIZE)
+	//VectorCopy (start_o, up);
+	up = start_o;
+	//up[2] += STEPSIZE;
+	up.y += STEPSIZE;
+
+	// test the player position if they were a stepheight higher
+	//pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask);
+	//tracefunc(&trace, *(const D3DXVECTOR3* const)&start_o, *(const D3DXVECTOR3* const)&up, D3DXVECTOR3(0.0f, STEPSIZE, 0.0f), 0, pml.traceObj);
+	newtrace(&trace, start_o, up, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine);
+	if ( trace.allsolid ) 
+	{
+		//if ( pm->debugLevel ) 
+			//Com_Printf("%i:bend can't step\n", c_pmove);
+		//bprintf("bend can't step\n");
+		return 3;		// can't step up
+	}
+
+	//stepSize = trace.endpos[2] - start_o[2];
+	stepSize = trace.endpos.y - start_o.y;
+
+	// try slidemove from this position
+	//VectorCopy (trace.endpos, pm->ps->origin); // pm->ps->origin = trace.endpos
+	pm->ps.origin = trace.endpos;
+	//VectorCopy (start_v, pm->ps->velocity); // pm->ps->velocity = start_v
+	pm->ps.velocity = start_v;
+
+	PM_SlideMove( gravity );
+
+	// push down the final amount
+
+	// down = pm->ps->origin - vec3(0, 0, stepSize)
+	//VectorCopy (pm->ps->origin, down);
+	down = pm->ps.origin;
+	//down[2] -= stepSize;
+	down.y -= stepSize;
+
+
+	//pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask);
+	//tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&down, D3DXVECTOR3(0.0f, -STEPSIZE, 0.0f), 0, pml.traceObj);
+	newtrace(&trace, pm->ps.origin, down, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine);
+	if ( !trace.allsolid )
+		//VectorCopy (trace.endpos, pm->ps->origin);
+		pm->ps.origin = trace.endpos;
+
+	if ( trace.fraction < 1.0 )
+		//PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP );
+		PM_ClipVelocity(pm->ps.velocity, trace.planenormal, pm->ps.velocity, OVERCLIP);
+
+	{
+		// use the step move
+		float	delta;
+
+		//delta = pm->ps->origin[2] - start_o[2];
+		delta = pm->ps.origin.y - start_o.y;
+		if ( delta > 2 ) 
+		{
+			if (gravity)
+				printf("g on: %f ", delta);
+			else
+				printf("g off: %f ",  delta);
+
+			if ( delta < 7 ) 
+				printf("stepped 3 < x < 7\n");
+				//PM_AddEvent( EV_STEP_4 );
+			else if ( delta < 11 ) 
+				printf("stepped 7 < x < 11\n");
+				//PM_AddEvent( EV_STEP_8 );
+			else if ( delta < 15 ) 
+				printf("stepped 11 < x < 15\n");
+				//PM_AddEvent( EV_STEP_12 );
+			else 
+				printf("stepped 15+\n");
+				//PM_AddEvent( EV_STEP_16 );
+				
+		}
+		/*if ( pm->debugLevel )
+			Com_Printf("%i:stepped\n", c_pmove);*/
+	}
+
+	return 4;
+}
+
+void PM_Friction(void)
+{
+	Ogre::Vector3	vec;
+	float* vel;
+	float	speed, newspeed, control;
+	float	drop;
+	
+	vel = &(pm->ps.velocity.x);
+	
+	// vec = vel
+	//VectorCopy( vel, vec );
+	vec = pm->ps.velocity;
+
+	if ( pml.walking )
+		//vec[2] = 0;	// ignore slope movement
+		vec.y = 0;
+
+	//speed = VectorLength(vec);
+	speed = vec.length();
+	if (speed < 1) 
+	{
+		vel[0] = 0;
+		vel[2] = 0;		// allow sinking underwater
+		// FIXME: still have z friction underwater?
+		//bprintf("Static friction (vec = [%f, %f, %f]) (vec.length = %f)\n", vec.x, vec.y, vec.z, speed);
+		return;
+	}
+
+	drop = 0;
+
+	// apply ground friction
+	if ( pm->ps.waterlevel <= 1 ) 
+	{
+		if ( pml.walking )//&& !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) 
+		{
+			// if getting knocked back, no friction
+			//if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) 
+			{
+				control = (speed < pm_stopspeed) ? pm_stopspeed : speed;
+				drop += control * pm_friction * pml.frametime;
+			}
+		}
+	}
+
+	// apply water friction even if just wading
+	if ( pm->ps.waterlevel ) 
+		drop += speed * pm_waterfriction * pm->ps.waterlevel * pml.frametime;
+
+	// apply flying friction
+	/*if ( pm->ps->powerups[PW_FLIGHT])
+		drop += speed * pm_flightfriction * pml.frametime;
+
+	if ( pm->ps->pm_type == PM_SPECTATOR)
+		drop += speed * pm_spectatorfriction * pml.frametime;*/
+	if (pm->ps.move_type == PM_SPECTATOR)
+		drop += speed * pm_flightfriction * pml.frametime;
+
+	// scale the velocity
+	newspeed = speed - drop;
+	if (newspeed < 0)
+		newspeed = 0;
+
+	newspeed /= speed;
+
+	// vel *= newspeed
+	vel[0] = vel[0] * newspeed;
+	vel[1] = vel[1] * newspeed;
+	vel[2] = vel[2] * newspeed;
+}
+
+float PM_CmdScale(playerMove::playercmd* const cmd)
+{
+	int		max;
+	float	total;
+	float	scale;
+
+	max = abs( cmd->forwardmove );
+	if ( abs( cmd->rightmove ) > max )
+		max = abs( cmd->rightmove );
+
+	if ( abs( cmd->upmove ) > max )
+		max = abs( cmd->upmove );
+
+	if ( !max )
+		return 0;
+
+	total = sqrtf( (const float)(cmd->forwardmove * cmd->forwardmove
+		+ cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove) );
+	scale = (float)pm->ps.speed * max / ( 127.0f * total );
+
+	return scale;
+}
+
+static void PM_Accelerate( Ogre::Vector3& wishdir, float wishspeed, float accel )
+{
+//	int			i;
+	float		addspeed, accelspeed, currentspeed;
+
+	// currentspeed = pm->ps->velocity dot wishdir
+	//currentspeed = DotProduct (pm->ps->velocity, wishdir);
+	currentspeed = pm->ps.velocity.dotProduct(wishdir);
+
+	addspeed = wishspeed - currentspeed;
+	if (addspeed <= 0) 
+		return;
+
+	accelspeed = accel * pml.frametime * wishspeed;
+
+	// Clamp accelspeed at addspeed
+	if (accelspeed > addspeed)
+		accelspeed = addspeed;
+	
+	// pm->ps->velocity += accelspeed * wishdir
+	//for (i=0 ; i<3 ; i++)
+		//pm->ps->velocity[i] += accelspeed * wishdir[i];	
+	pm->ps.velocity += (wishdir * accelspeed);
+}
+
+static bool PM_CheckJump(void)
+{
+	//if ( pm->ps->pm_flags & PMF_RESPAWNED )
+		//return qfalse;		// don't allow jump until all buttons are up
+
+	if ( pm->cmd.upmove < 10 )
+		// not holding jump
+		return false;
+
+	pm->cmd.upmove = 0;
+
+	// must wait for jump to be released
+	/*if ( pm->ps->pm_flags & PMF_JUMP_HELD ) 
+	{
+		// clear upmove so cmdscale doesn't lower running speed
+		pm->cmd.upmove = 0;
+		return false;
+	}*/
+
+	pml.groundPlane = false;		// jumping away
+	pml.walking = false;
+	//pm->ps->pm_flags |= PMF_JUMP_HELD;
+
+	pm->ps.groundEntityNum = ENTITYNUM_NONE;
+	pm->ps.velocity.y = JUMP_VELOCITY;
+	//PM_AddEvent( EV_JUMP );
+
+	/*if ( pm->cmd.forwardmove >= 0 ) 
+	{
+		PM_ForceLegsAnim( LEGS_JUMP );
+		pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
+	} 
+	else 
+	{
+		PM_ForceLegsAnim( LEGS_JUMPB );
+		pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
+	}*/
+
+	return true;
+}
+
+static void PM_WaterMove( playerMove* const pm ) 
+{
+	//int		i;
+	//vec3_t	wishvel;
+	Ogre::Vector3 wishvel;
+	float	wishspeed;
+	//vec3_t	wishdir;
+	Ogre::Vector3 wishdir;
+	float	scale;
+	float	vel;
+
+	/*if ( PM_CheckWaterJump() ) 
+	{
+		PM_WaterJumpMove();
+		return;
+	}*/
+#if 0
+	// jump = head for surface
+	if ( pm->cmd.upmove >= 10 ) {
+		if (pm->ps->velocity[2] > -300) {
+			if ( pm->watertype == CONTENTS_WATER ) {
+				pm->ps->velocity[2] = 100;
+			} else if (pm->watertype == CONTENTS_SLIME) {
+				pm->ps->velocity[2] = 80;
+			} else {
+				pm->ps->velocity[2] = 50;
+			}
+		}
+	}
+#endif
+	PM_Friction ();
+
+	if (pm->cmd.forwardmove || pm->cmd.rightmove)
+	{
+		//NEEDS TO BE REWRITTEN FOR OGRE TIME---------------------------------------------------
+		/*
+		static const TimeTicks footstep_duration = GetTimeFreq(); // make each splash last 1.0s
+		static TimeTicks lastStepTime = 0;
+		const TimeTicks thisStepTime = GetTimeQPC();
+		static bool lastWasLeft = false;
+		if (thisStepTime > lastStepTime)
+		{
+			if (pm->cmd.ducking)
+				lastStepTime = thisStepTime + footstep_duration * 2; // splashes while ducking are twice as slow
+			else
+				lastStepTime = thisStepTime + footstep_duration;
+
+			lastWasLeft = !lastWasLeft;
+		*/
+		//-----------------jhooks1
+
+			/*
+			namestruct defaultCreature;
+			const SNDG* const sndg = SNDG::GetFromMap(defaultCreature, lastWasLeft ? SNDG::r_swim : SNDG::l_swim);
+			if (sndg)
+			{
+				const namestruct& SOUNID = sndg->soundID;
+				const SOUN* const soun = SOUN::GetSound(SOUNID);
+				if (soun)
+				{
+					PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() );
+				}
+			}*/
+			//Sound, ignore for now -- jhooks1
+		//}
+	}
+
+	scale = PM_CmdScale( &pm->cmd );
+	//
+	// user intentions
+	//
+	if ( !scale ) 
+	{
+		/*wishvel[0] = 0;
+		wishvel[1] = 0;
+		wishvel[2] = -60;		// sink towards bottom
+		*/
+		wishvel.x = 0;
+		wishvel.y = -60;
+		wishvel.z = 0;
+	} 
+	else 
+	{
+		/*for (i=0 ; i<3 ; i++)
+			wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;*/
+		wishvel = pml.forward * scale * pm->cmd.forwardmove + pml.right * scale * pm->cmd.rightmove;
+
+		//wishvel[2] += scale * pm->cmd.upmove;
+		wishvel.y += pm->cmd.upmove * scale;
+	}
+
+	//VectorCopy (wishvel, wishdir);
+	wishdir = wishvel;
+	wishspeed = VectorNormalize(wishdir);
+
+	if ( wishspeed > pm->ps.speed * pm_swimScale )
+		wishspeed = pm->ps.speed * pm_swimScale;
+
+	PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate);
+
+	// make sure we can go up slopes easily under water
+	//if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) 
+	if (pml.groundPlane && pm->ps.velocity.dotProduct(pml.groundTrace.planenormal) < 0.0f)
+	{
+		//vel = VectorLength(pm->ps->velocity);
+		vel = pm->ps.velocity.length();
+
+		// slide along the ground plane
+		//PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP );
+		PM_ClipVelocity(pm->ps.velocity, pml.groundTrace.planenormal, pm->ps.velocity, OVERCLIP);
+
+		VectorNormalize(pm->ps.velocity);
+		//VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
+		pm->ps.velocity = pm->ps.velocity * vel;
+	}
+
+	PM_SlideMove( false );
+}
+
+/*
+===================
+PM_WalkMove
+
+===================
+*/
+static void PM_WalkMove( playerMove* const pmove ) 
+{
+//	int			i;
+	Ogre::Vector3		wishvel;
+	float		fmove, smove;
+	Ogre::Vector3		wishdir;
+	float		wishspeed;
+	float		scale;
+	playerMove::playercmd cmd;
+	float		accelerate;
+	float		vel;
+
+	if ( pm->ps.waterlevel > 2 && //DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) 
+		pml.forward.dotProduct(pml.groundTrace.planenormal) > 0.0f)
+	{
+		// begin swimming
+		PM_WaterMove(pmove);
+		return;
+	}
+
+
+	if ( PM_CheckJump () ) 
+	{
+		// jumped away
+		if ( pm->ps.waterlevel > 1 ) 
+			PM_WaterMove(pmove);
+		else
+			PM_AirMove();
+		printf("Jumped away\n");
+		return;
+	}
+
+	// Footsteps time
+	if (pmove->cmd.forwardmove || pmove->cmd.rightmove)
+	{
+		bool step_underwater = false;
+		//if (pmove->traceObj)
+		//{
+
+
+			//jhooks1 - Water handling, deal with later
+
+
+
+			if (pmove->hasWater)
+			{
+				if (pmove->hasWater )
+				{
+					const float waterHeight = pmove->waterHeight;
+					const float waterSoundStepHeight = waterHeight + halfExtents.y;
+					if (pmove->ps.origin.y < waterSoundStepHeight)
+						step_underwater = true;
+				}
+			}
+		//}
+
+		/*
+		static const TimeTicks footstep_duration = GetTimeFreq() / 2; // make each footstep last 500ms
+		static TimeTicks lastStepTime = 0;
+		const TimeTicks thisStepTime = GetTimeQPC();
+		static bool lastWasLeft = false;
+		if (thisStepTime > lastStepTime)
+		{
+			if (pmove->cmd.ducking)
+				lastStepTime = thisStepTime + footstep_duration * 2; // footsteps while ducking are twice as slow
+			else
+				lastStepTime = thisStepTime + footstep_duration;
+
+			lastWasLeft = !lastWasLeft;
+			*/
+
+			if (step_underwater)
+			{
+				/*
+				const namestruct ns(lastWasLeft ? "FootWaterRight" : "FootWaterLeft");
+				const SOUN* const soun = SOUN::GetSound(ns);
+				if (soun)
+				{
+					PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() );
+				}*/
+			}
+			else
+			{
+				/*
+				namestruct defaultCreature;
+				const SNDG* const sndg = SNDG::GetFromMap(defaultCreature, lastWasLeft ? SNDG::r_foot : SNDG::l_foot);
+				if (sndg)
+				{
+					const namestruct& SOUNID = sndg->soundID;
+					const SOUN* const soun = SOUN::GetSound(SOUNID);
+					if (soun)
+					{
+						PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() );
+					}
+				}*/
+			}
+		}
+
+
+	PM_Friction ();
+
+	//bprintf("vel: (%f, %f, %f)\n", pm->ps.velocity.x, pm->ps.velocity.y, pm->ps.velocity.z);
+
+	fmove = pm->cmd.forwardmove;
+	smove = pm->cmd.rightmove;
+
+	cmd = pm->cmd;
+	scale = PM_CmdScale( &cmd );
+
+	// set the movementDir so clients can rotate the legs for strafing
+	//PM_SetMovementDir();
+
+	// project moves down to flat plane
+	//pml.forward[2] = 0;
+	pml.forward.y = 0;
+
+	//pml.right[2] = 0;
+	pml.right.y = 0;
+
+	// project the forward and right directions onto the ground plane
+	PM_ClipVelocity (pml.forward, pml.groundTrace.planenormal, pml.forward, OVERCLIP );
+	PM_ClipVelocity (pml.right, pml.groundTrace.planenormal, pml.right, OVERCLIP );
+	//
+	//VectorNormalize (pml.forward);
+	pml.forward = pml.forward.normalise();
+	pml.right = pml.right.normalise();
+	
+
+	// wishvel = (pml.forward * fmove) + (pml.right * smove);
+	//for ( i = 0 ; i < 3 ; i++ ) 
+		//wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove;
+	wishvel = pml.forward * fmove + pml.right * smove;
+	//bprintf("f: (%f, %f, %f), s: (%f, %f, %f)\n", fmove, smove);
+
+
+	// when going up or down slopes the wish velocity should Not be zero
+//	wishvel[2] = 0;
+
+	// wishdir = wishvel
+	//VectorCopy (wishvel, wishdir);
+	//wishvel = wishdir;
+	wishdir = wishvel;
+
+	wishspeed = VectorNormalize(wishdir);
+	wishspeed *= scale;
+
+	// clamp the speed lower if ducking
+	if ( pm->cmd.ducking ) 
+		if ( wishspeed > pm->ps.speed * pm_duckScale )
+			wishspeed = pm->ps.speed * pm_duckScale;
+
+	// clamp the speed lower if wading or walking on the bottom
+	if ( pm->ps.waterlevel ) 
+	{
+		float	waterScale;
+
+		waterScale = pm->ps.waterlevel / 3.0f;
+		waterScale = 1.0f - ( 1.0f - pm_swimScale ) * waterScale;
+		if ( wishspeed > pm->ps.speed * waterScale )
+			wishspeed = pm->ps.speed * waterScale;
+	}
+
+	// when a player gets hit, they temporarily lose
+	// full control, which allows them to be moved a bit
+	//if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK )
+		//accelerate = pm_airaccelerate;
+	//else
+		accelerate = pm_accelerate;
+
+
+	PM_Accelerate (wishdir, wishspeed, accelerate);
+
+	//Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
+	//Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity));
+
+	//if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK )
+		//pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
+	//else 
+	//{
+		// don't reset the z velocity for slopes
+//		pm->ps->velocity[2] = 0;
+	//}
+
+	//vel = VectorLength(pm->ps->velocity);
+	vel = pm->ps.velocity.length();
+
+	// slide along the ground plane
+	PM_ClipVelocity (pm->ps.velocity, pml.groundTrace.planenormal, 
+		pm->ps.velocity, OVERCLIP );
+
+	// don't decrease velocity when going up or down a slope
+	//VectorNormalize(pm->ps->velocity);
+	pm->ps.velocity = pm->ps.velocity.normalise();
+	
+	//VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
+	pm->ps.velocity = pm->ps.velocity * vel;
+
+	// don't do anything if standing still
+	//if (!pm->ps->velocity[0] && !pm->ps->velocity[1])
+	if (!pm->ps.velocity.x && !pm->ps.velocity.z)
+		return;
+
+	PM_StepSlideMove( false );
+
+	//Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity));
+
+}
+
+void PM_UpdateViewAngles( playerMove::playerStruct* const ps, playerMove::playercmd* const cmd ) 
+{
+	short		temp;
+	int		i;
+
+	//while(1);
+
+	//if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) 
+		//return;		// no view changes at all
+
+	//if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 )
+		//return;		// no view changes at all
+
+	// circularly clamp the angles with deltas
+	//bprintf("View angles: %i, %i, %i\n", cmd->angles[0], cmd->angles[1], cmd->angles[2]);
+	for (i = 0 ; i < 3 ; i++) 
+	{
+		temp = cmd->angles[i];// + ps->delta_angles[i];
+		//if ( i == PITCH ) 
+		{
+			// don't let the player look up or down more than 90 degrees
+			/*if ( temp > 16000 ) 
+			{
+				ps->delta_angles[i] = 16000 - cmd->angles[i];
+				temp = 16000;
+			} 
+			else if ( temp < -16000 ) 
+			{
+				ps->delta_angles[i] = -16000 - cmd->angles[i];
+				temp = -16000;
+			}*/
+		}
+		(&(ps->viewangles.x) )[i] = SHORT2ANGLE(temp);
+		//cmd->angles[i] += ps->delta_angles[i];
+	}
+	//ps->delta_angles[0] = ps->delta_angles[1] = ps->delta_angles[2] = 0;
+
+}
+
+void AngleVectors( const Ogre::Vector3& angles, Ogre::Vector3* const forward, Ogre::Vector3* const right, Ogre::Vector3* const up) 
+{
+	float		angle;
+	static float		sr, sp, sy, cr, cp, cy;
+	// static to help MS compiler fp bugs
+
+	//angle = angles[YAW] * (M_PI*2 / 360);
+	angle = angles.x * (M_PI * 2.0f / 360.0f);
+	sp = sinf(angle);
+	cp = cosf(angle);
+
+	//angle = angles[PITCH] * (M_PI*2 / 360);
+	angle = angles.y * (-M_PI * 2.0f / 360.0f);
+	sy = sinf(angle);
+	cy = cosf(angle);
+
+	//angle = angles[ROLL] * (M_PI*2 / 360);
+	angle = angles.z * (M_PI * 2.0f / 360.0f);
+	sr = sinf(angle);
+	cr = cosf(angle);
+
+	if (forward)
+	{
+		forward->x = cp * cy;
+		forward->z = cp * sy;
+		forward->y = -sp;
+	}
+	if (right)
+	{
+		right->x = (-1 * sr * sp * cy + -1 * cr * -sy);
+		right->z = (-1 * sr * sp * sy + -1 * cr * cy);
+		right->y = 0.0f;//-1 * sp * cp;
+	}
+	if (up)
+	{
+		up->x = (cr * sp * cy + -sr * -sy);
+		up->z = (cr * sp * sy + -sr * cy);
+		up->y = cr * cp;
+	}
+}
+
+void PM_GroundTraceMissed()
+{
+	traceResults		trace;
+	Ogre::Vector3		point;
+
+	if ( pm->ps.groundEntityNum != ENTITYNUM_NONE ) 
+	{
+		// we just transitioned into freefall
+		//if ( pm->debugLevel )
+			//Com_Printf("%i:lift\n", c_pmove);
+
+		// if they aren't in a jumping animation and the ground is a ways away, force into it
+		// if we didn't do the trace, the player would be backflipping down staircases
+		//VectorCopy( pm->ps->origin, point );
+		point = pm->ps.origin;
+		//point[2] -= 64;
+		point.y -= 64;
+
+		//pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
+		//tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -64.0f, 0.0f), 0, pml.traceObj);
+		newtrace(&trace, pm->ps.origin, point, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine);
+		if ( trace.fraction == 1.0 ) 
+		{
+			if ( pm->cmd.forwardmove >= 0 ) 
+			{
+				//PM_ForceLegsAnim( LEGS_JUMP );
+				//pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
+			} 
+			else 
+			{
+				//PM_ForceLegsAnim( LEGS_JUMPB );
+				//pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
+			}
+		}
+	}
+
+	pm->ps.groundEntityNum = ENTITYNUM_NONE;
+	pml.groundPlane = false;
+	pml.walking = false;
+}
+
+static bool PM_CorrectAllSolid(traceResults* const trace)
+{
+	int			i, j, k;
+	Ogre::Vector3	point;
+	std::cout << "Correct all solid\n";
+
+	//if ( pm->debugLevel )
+		//Com_Printf("%i:allsolid\n", c_pmove);
+	//bprintf("allsolid\n");
+
+	// jitter around
+	for (i = -1; i <= 1; i++) 
+	{
+		for (j = -1; j <= 1; j++) 
+		{
+			for (k = -1; k <= 1; k++) 
+			{
+				//VectorCopy(pm->ps->origin, point);
+				point = pm->ps.origin;
+
+				/*point[0] += (float) i;
+				point[1] += (float) j;
+				point[2] += (float) k;*/
+				point += Ogre::Vector3( (const float)i, (const float)j, (const float)k);
+
+				//pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
+				//tracefunc(trace, *(const D3DXVECTOR3* const)&point, *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, 0.0f, 0.0f), 0, pml.traceObj);
+				newtrace(trace, point, point, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine);
+
+				if ( !trace->allsolid ) 
+				{
+					/*point[0] = pm->ps->origin[0];
+					point[1] = pm->ps->origin[1];
+					point[2] = pm->ps->origin[2] - 0.25;*/
+					point = pm->ps.origin;
+					point.y -= 0.25f;
+
+					//pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
+					//tracefunc(trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -0.25f, 0.0f), 0, pml.traceObj);
+					newtrace(trace, pm->ps.origin, point, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine);
+					pml.groundTrace = *trace;
+					return true;
+				}
+			}
+		}
+	}
+
+	//pm->ps->groundEntityNum = ENTITYNUM_NONE;
+	pm->ps.groundEntityNum = ENTITYNUM_NONE;
+	pml.groundPlane = false;
+	pml.walking = false;
+
+	return false;
+}
+
+static void PM_CrashLand( void ) 
+{
+	float		delta;
+	float		dist ;
+	float		vel, acc;
+	float		t;
+	float		a, b, c, den;
+
+	// decide which landing animation to use
+	/*if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) 
+		PM_ForceLegsAnim( LEGS_LANDB );
+	else
+		PM_ForceLegsAnim( LEGS_LAND );
+		
+	pm->ps->legsTimer = TIMER_LAND;*/
+
+	// calculate the exact velocity on landing
+	//dist = pm->ps->origin[2] - pml.previous_origin[2];
+
+	dist = pm->ps.origin.y - pml.previous_origin.y;
+
+	//vel = pml.previous_velocity[2];
+	vel = pml.previous_velocity.y;
+
+	//acc = -pm->ps->gravity;
+	acc = -pm->ps.gravity;
+
+	a = acc / 2;
+	b = vel;
+	c = -dist;
+
+	den =  b * b - 4 * a * c;
+	if ( den < 0 ) 
+		return;
+
+	t = (-b - sqrtf( den ) ) / ( 2 * a );
+
+	delta = vel + t * acc; 
+	delta = delta * delta * 0.0001f;
+
+	// ducking while falling doubles damage
+	/*if ( pm->ps->pm_flags & PMF_DUCKED )
+		delta *= 2;*/
+	if (pm->cmd.upmove < -20)
+		delta *= 2;
+
+	// never take falling damage if completely underwater
+	if ( pm->ps.waterlevel == 3 ) 
+		return;
+
+	// reduce falling damage if there is standing water
+	if ( pm->ps.waterlevel == 2 )
+		delta *= 0.25;
+	if ( pm->ps.waterlevel == 1 )
+		delta *= 0.5;
+
+	if ( delta < 1 ) 
+		return;
+
+	if (delta > 60)
+		printf("Far crashland: %f\n", delta);
+	else if (delta > 40)
+		printf("Medium crashland: %f\n", delta);
+	else if (delta > 4)
+		printf("Short crashland: %f\n", delta);
+
+	if (delta > 60)
+	{
+		/*
+		static const namestruct healthDamage("Health Damage");
+		const SOUN* const soun = SOUN::GetSound(healthDamage);
+		if (soun)
+		{
+			PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() );
+		}*/
+	}
+
+	if (delta > 3) // We need at least a short crashland to proc the sound effects:
+	{
+		bool splashSound = false;
+		
+			if (pm->hasWater)
+			{
+				
+					const float waterHeight = pm->waterHeight;
+					const float waterHeightSplash = waterHeight + halfExtents.y;
+					if (pm->ps.origin.y < waterHeightSplash)
+					{
+						splashSound = true;
+					}
+				
+			}
+		
+
+		if (splashSound)
+		{
+			//Change this later-----------------------------------
+			/*
+			const namestruct ns("DefaultLandWater");
+			const SOUN* const soun = SOUN::GetSound(ns);
+			if (soun)
+			{
+				PlaySound2D(soun->soundFilename, soun->soundDatga->GetVolumeFloat() );
+			}*/
+		}
+		else
+		{
+			//Change this later---------------------------------
+			/*
+			namestruct defaultCreature;
+			const SNDG* const sndg = SNDG::GetFromMap(defaultCreature, SNDG::land);
+			if (sndg)
+			{
+				const namestruct& SOUNID = sndg->soundID;
+				const SOUN* const soun = SOUN::GetSound(SOUNID);
+				if (soun)
+				{
+					PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() );
+				}
+			}*/
+		}
+	}
+
+	// create a local entity event to play the sound
+
+	// SURF_NODAMAGE is used for bounce pads where you don't ever
+	// want to take damage or play a crunch sound
+	//if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) )  
+	{
+		/*if ( delta > 60 ) 
+			PM_AddEvent( EV_FALL_FAR );
+		else if ( delta > 40 ) 
+		{
+			// this is a pain grunt, so don't play it if dead
+			if ( pm->ps->stats[STAT_HEALTH] > 0 )
+				PM_AddEvent( EV_FALL_MEDIUM );
+		} 
+		else if ( delta > 7 ) 
+			PM_AddEvent( EV_FALL_SHORT );
+		else 
+			PM_AddEvent( PM_FootstepForSurface() );*/
+	}
+
+	// start footstep cycle over
+	//pm->ps->bobCycle = 0;
+}
+
+static void PM_GroundTrace( void ) 
+{
+	std::cout << "Ground trace\n";
+	Ogre::Vector3		point;
+	traceResults		trace;
+
+	/*point[0] = pm->ps->origin[0];
+	point[1] = pm->ps->origin[1];
+	point[2] = pm->ps->origin[2] - 0.25;*/
+	point = pm->ps.origin;
+	point.y -= 0.25f;
+
+	//pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
+	//tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -0.25f, 0.0f), 0, pml.traceObj);
+	newtrace(&trace, pm->ps.origin, point, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine);
+	pml.groundTrace = trace;
+
+	// do something corrective if the trace starts in a solid...
+	if ( trace.allsolid ) {
+		//std::cout << "ALL SOLID\n";
+		if ( !PM_CorrectAllSolid(&trace) ){
+			//std::cout << "Returning after correct all solid\n";
+			return;
+		}
+	}
+
+	// if the trace didn't hit anything, we are in free fall
+	if ( trace.fraction == 1.0 ) 
+	{
+		PM_GroundTraceMissed();
+		pml.groundPlane = false;
+		pml.walking = false;
+		return;
+	}
+
+	// check if getting thrown off the ground
+	//if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) 
+	if (pm->ps.velocity.y > 0 && pm->ps.velocity.dotProduct(trace.planenormal) > 10.0f)
+	{
+		//if ( pm->debugLevel ) 
+			//Com_Printf("%i:kickoff\n", c_pmove);
+
+		// go into jump animation
+		/*if ( pm->cmd.forwardmove >= 0 ) 
+		{
+			PM_ForceLegsAnim( LEGS_JUMP );
+			pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
+		} 
+		else 
+		{
+			PM_ForceLegsAnim( LEGS_JUMPB );
+			pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
+		}*/
+
+		pm->ps.groundEntityNum = ENTITYNUM_NONE;
+		pml.groundPlane = false;
+		pml.walking = false;
+		return;
+	}
+	
+	// slopes that are too steep will not be considered onground
+	//if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) 
+	if (trace.planenormal.y < MIN_WALK_NORMAL)
+	{
+		//if ( pm->debugLevel )
+			//Com_Printf("%i:steep\n", c_pmove);
+
+		// FIXME: if they can't slide down the slope, let them
+		// walk (sharp crevices)
+		pm->ps.groundEntityNum = ENTITYNUM_NONE;
+		pml.groundPlane = true;
+		pml.walking = false;
+		return;
+	}
+
+	pml.groundPlane = true;
+	pml.walking = true;
+
+	// hitting solid ground will end a waterjump
+	/*if (pm->ps.pm_flags & PMF_TIME_WATERJUMP)
+	{
+		pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
+		pm->ps->pm_time = 0;
+	}*/
+
+	if ( pm->ps.groundEntityNum == ENTITYNUM_NONE ) 
+	{
+		// just hit the ground
+		/*if ( pm->debugLevel )
+			Com_Printf("%i:Land\n", c_pmove);*/
+		//bprintf("Land\n");
+		
+		PM_CrashLand();
+
+		// don't do landing time if we were just going down a slope
+		//if ( pml.previous_velocity[2] < -200 ) 
+		if (pml.previous_velocity.y < -200)
+		{
+			// don't allow another jump for a little while
+			//pm->ps->pm_flags |= PMF_TIME_LAND;
+			pm->ps.pm_time = 250;
+		}
+	}
+
+	pm->ps.groundEntityNum = trace.entityNum;
+
+	// don't reset the z velocity for slopes
+//	pm->ps->velocity[2] = 0;
+
+	//PM_AddTouchEnt( trace.entityNum );
+}
+
+static void PM_AirMove()
+{
+	//int			i;
+	Ogre::Vector3		wishvel;
+	float		fmove, smove;
+	Ogre::Vector3		wishdir;
+	float		wishspeed;
+	float		scale;
+	playerMove::playercmd	cmd;
+
+	PM_Friction();
+
+	fmove = pm->cmd.forwardmove;
+	smove = pm->cmd.rightmove;
+
+	cmd = pm->cmd;
+	scale = PM_CmdScale( &cmd );
+
+	// set the movementDir so clients can rotate the legs for strafing
+	//PM_SetMovementDir();
+
+	// project moves down to flat plane
+	//pml.forward[2] = 0;
+	pml.forward.y = 0;
+	//pml.right[2] = 0;
+	pml.right.y = 0;
+	//VectorNormalize (pml.forward);
+	pml.forward = Ogre::Vector3(pml.forward.normalise());
+	pml.right = Ogre::Vector3(pml.right.normalise());
+	//VectorNormalize (pml.right);
+
+	//for ( i = 0 ; i < 2 ; i++ )
+		//wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove;
+	wishvel = pml.forward * fmove + pml.right * smove;
+
+	//wishvel[2] = 0;
+	wishvel.y = 0;
+
+	//VectorCopy (wishvel, wishdir);
+	wishdir = wishvel;
+	//wishspeed = VectorNormalize(wishdir);
+	wishspeed = VectorNormalize(wishdir);
+
+	wishspeed *= scale;
+
+	// not on ground, so little effect on velocity
+	PM_Accelerate (wishdir, wishspeed, pm_airaccelerate);
+
+	// we may have a ground plane that is very steep, even
+	// though we don't have a groundentity
+	// slide along the steep plane
+	if ( pml.groundPlane )
+		PM_ClipVelocity (pm->ps.velocity, pml.groundTrace.planenormal, pm->ps.velocity, OVERCLIP );
+
+/*#if 0
+	//ZOID:  If we are on the grapple, try stair-stepping
+	//this allows a player to use the grapple to pull himself
+	//over a ledge
+	if (pm->ps->pm_flags & PMF_GRAPPLE_PULL)
+		PM_StepSlideMove ( qtrue );
+	else
+		PM_SlideMove ( qtrue );
+#endif*/
+
+	/*bprintf("%i ", */PM_StepSlideMove ( true )/* )*/;
+}
+
+static void PM_NoclipMove( void ) 
+{
+	float	speed, drop, friction, control, newspeed;
+//	int			i;
+	Ogre::Vector3		wishvel;
+	float		fmove, smove;
+	Ogre::Vector3		wishdir;
+	float		wishspeed;
+	float		scale;
+
+	//pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
+
+	// friction
+
+	//speed = VectorLength (pm->ps->velocity);
+	speed = pm->ps.velocity.length();
+	if (speed < 1)
+		//VectorCopy (vec3_origin, pm->ps->velocity);
+		pm->ps.velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f);
+	else
+	{
+		drop = 0;
+
+		friction = pm_friction * 1.5f;	// extra friction
+		control = speed < pm_stopspeed ? pm_stopspeed : speed;
+		drop += control * friction * pml.frametime;
+
+		// scale the velocity
+		newspeed = speed - drop;
+		if (newspeed < 0)
+			newspeed = 0;
+		newspeed /= speed;
+
+		//VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity);
+		pm->ps.velocity = pm->ps.velocity * newspeed;
+	}
+
+	// accelerate
+	scale = PM_CmdScale( &pm->cmd );
+
+	fmove = pm->cmd.forwardmove;
+	smove = pm->cmd.rightmove;
+	
+	//for (i=0 ; i<3 ; i++)
+		//wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove;
+	wishvel = pml.forward * fmove + pml.right * smove;
+	//wishvel[2] += pm->cmd.upmove;
+	wishvel.y += pm->cmd.upmove;
+
+	//VectorCopy (wishvel, wishdir);
+	wishdir = wishvel;
+	wishspeed = VectorNormalize(wishdir);
+	wishspeed *= scale;
+
+	PM_Accelerate( wishdir, wishspeed, pm_accelerate );
+
+	// move
+	//VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin);
+	pm->ps.origin = pm->ps.origin + pm->ps.velocity * pml.frametime;
+}
+
+static void PM_DropTimers( void ) 
+{
+	// drop misc timing counter
+	if ( pm->ps.pm_time ) 
+	{
+		if ( pml.msec >= pm->ps.pm_time ) 
+		{
+			//pm->ps->pm_flags &= ~PMF_ALL_TIMES;
+			pm->ps.pm_time = 0;
+		} 
+		else
+			pm->ps.pm_time -= pml.msec;
+	}
+
+	//bprintf("Time: %i\n", pm->ps.pm_time);
+
+	// drop animation counter
+	/*if ( pm->ps->legsTimer > 0 ) 
+	{
+		pm->ps->legsTimer -= pml.msec;
+		if ( pm->ps->legsTimer < 0 ) 
+			pm->ps->legsTimer = 0;
+	}
+
+	if ( pm->ps->torsoTimer > 0 ) 
+	{
+		pm->ps->torsoTimer -= pml.msec;
+		if ( pm->ps->torsoTimer < 0 )
+			pm->ps->torsoTimer = 0;
+	}*/
+}
+
+static void PM_FlyMove( void ) 
+{
+	//int		i;
+	Ogre::Vector3	wishvel;
+	float	wishspeed;
+	Ogre::Vector3	wishdir;
+	float	scale;
+
+	// normal slowdown
+	PM_Friction ();
+
+	scale = PM_CmdScale( &pm->cmd );
+	//
+	// user intentions
+	//
+	if ( !scale ) 
+	{
+		/*wishvel[0] = 0;
+		wishvel[1] = 0;
+		wishvel[2] = 0;*/
+		wishvel = Ogre::Vector3(0,0,0);
+	} 
+	else 
+	{
+		//for (i=0 ; i<3 ; i++) 
+			//wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
+		wishvel = pml.forward * scale * pm->cmd.forwardmove + pml.right * scale * pm->cmd.rightmove;
+
+		//wishvel[2] += scale * pm->cmd.upmove;
+		wishvel.y += /*6.35f * */pm->cmd.upmove * scale;
+	}
+
+	//VectorCopy (wishvel, wishdir);
+	wishdir = wishvel;
+
+	//wishspeed = VectorNormalize(wishdir);
+	wishspeed = VectorNormalize(wishdir);
+
+	PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);
+
+	PM_StepSlideMove( false );
+}
+
+
+void PM_SetWaterLevel( playerMove* const pm ) 
+{
+	Ogre::Vector3 point;
+	//int			cont;
+	int			sample1;
+	int			sample2;
+
+	//
+	// get waterlevel, accounting for ducking
+	//
+
+	pm->ps.waterlevel = WL_DRYLAND;
+	pm->ps.watertype = 0;
+
+	/*point[0] = pm->ps->origin[0];
+	point[1] = pm->ps->origin[1];
+	point[2] = pm->ps->origin[2] + MINS_Z + 1;	*/
+	point.x = pm->ps.origin.x;
+	point.y = pm->ps.origin.y + MINS_Z + 1;
+	point.z = pm->ps.origin.z;
+
+	//cont = pm->pointcontents( point, pm->ps->clientNum );
+	bool checkWater = (pml.hasWater && pml.waterHeight > point.y);
+	//if ( cont & MASK_WATER ) 
+	if ( checkWater)
+	{
+		sample2 = /*pm->ps.viewheight*/DEFAULT_VIEWHEIGHT - MINS_Z;
+		sample1 = sample2 / 2;
+
+		pm->ps.watertype = CONTENTS_WATER;//cont;
+		pm->ps.waterlevel = WL_ANKLE;
+		//point[2] = pm->ps->origin[2] + MINS_Z + sample1;
+		point.y = pm->ps.origin.y + MINS_Z + sample1;
+		//cont = pm->pointcontents (point, pm->ps->clientNum );
+		//if ( cont & MASK_WATER ) 
+		if (checkWater)
+		{
+			pm->ps.waterlevel = WL_WAIST;
+			//point[2] = pm->ps->origin[2] + MINS_Z + sample2;
+			point.y = pm->ps.origin.y + MINS_Z + sample2;
+			//cont = pm->pointcontents (point, pm->ps->clientNum );
+			//if ( cont & MASK_WATER )
+			if (checkWater )
+				pm->ps.waterlevel = WL_UNDERWATER;
+		}
+	}
+}
+
+void PmoveSingle (playerMove* const pmove) 
+{
+	std::cout << "Pmove single\n";
+	//pm = pmove;
+
+	// Aedra doesn't support Q3-style VM traps D:	//while(1);
+
+	// this counter lets us debug movement problems with a journal
+	// by setting a conditional breakpoint fot the previous frame
+	//c_pmove++;
+
+	// clear results
+	//pm->numtouch = 0;
+	pm->ps.watertype = 0;
+	pm->ps.waterlevel = WL_DRYLAND;
+
+	//if ( pm->ps->stats[STAT_HEALTH] <= 0 )
+		//pm->tracemask &= ~CONTENTS_BODY;	// corpses can fly through bodies
+		
+
+	// make sure walking button is clear if they are running, to avoid
+	// proxy no-footsteps cheats
+	//if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 )
+		//pm->cmd.buttons &= ~BUTTON_WALKING;
+
+
+	// set the talk balloon flag
+	//if ( pm->cmd.buttons & BUTTON_TALK )
+		//pm->ps->eFlags |= EF_TALK;
+	//else
+		//pm->ps->eFlags &= ~EF_TALK;
+
+	// set the firing flag for continuous beam weapons
+	/*if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION
+		&& ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] )
+		pm->ps->eFlags |= EF_FIRING;
+	else
+		pm->ps->eFlags &= ~EF_FIRING;*/
+
+	// clear the respawned flag if attack and use are cleared
+	/*if ( pm->ps->stats[STAT_HEALTH] > 0 && 
+		!( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) )
+		pm->ps->pm_flags &= ~PMF_RESPAWNED;*/
+
+	// if talk button is down, dissallow all other input
+	// this is to prevent any possible intercept proxy from
+	// adding fake talk balloons
+	/*if ( pmove->cmd.buttons & BUTTON_TALK ) 
+	{
+		// keep the talk button set tho for when the cmd.serverTime > 66 msec
+		// and the same cmd is used multiple times in Pmove
+		pmove->cmd.buttons = BUTTON_TALK;
+		pmove->cmd.forwardmove = 0;
+		pmove->cmd.rightmove = 0;
+		pmove->cmd.upmove = 0;
+	}*/
+
+	// clear all pmove local vars
+	memset (&pml, 0, sizeof(pml) );
+
+	// Aedra-specific code:
+	//pml.scene = global_lastscene;
+	
+
+	// End Aedra-specific code
+	pml.hasWater = pmove->hasWater;
+	pml.isInterior = pmove->isInterior;
+	pml.waterHeight = pmove->waterHeight;
+#ifdef _DEBUG
+	if (!pml.traceObj)
+		__debugbreak();
+
+	if (!pml.traceObj->incellptr)
+		__debugbreak();
+#endif
+
+	// determine the time
+	pml.msec = pmove->cmd.serverTime - pm->ps.commandTime;
+	if ( pml.msec < 1 )
+		pml.msec = 1;
+	else if ( pml.msec > 200 )
+		pml.msec = 200;
+
+	//pm->ps->commandTime = pmove->cmd.serverTime;
+
+	// Commented out as a hack
+	pm->ps.commandTime = pmove->cmd.serverTime;
+
+	// Handle state change procs:
+	if (pm->cmd.activating != pm->cmd.lastActivatingState)
+	{
+		if (!pm->cmd.lastActivatingState && pm->cmd.activating)
+			pm->cmd.procActivating = playerMove::playercmd::KEYDOWN;
+		else
+			pm->cmd.procActivating = playerMove::playercmd::KEYUP;
+	}
+	else
+	{
+		pm->cmd.procActivating = playerMove::playercmd::NO_CHANGE;
+	}
+	pm->cmd.lastActivatingState = pm->cmd.activating;
+
+	if (pm->cmd.dropping != pm->cmd.lastDroppingState)
+	{
+		if (!pm->cmd.lastDroppingState && pm->cmd.dropping)
+			pm->cmd.procDropping = playerMove::playercmd::KEYDOWN;
+		else
+			pm->cmd.procDropping = playerMove::playercmd::KEYUP;
+	}
+	else
+	{
+		pm->cmd.procDropping = playerMove::playercmd::NO_CHANGE;
+	}
+	pm->cmd.lastDroppingState = pm->cmd.dropping;
+
+	// save old org in case we get stuck
+	//VectorCopy (pm->ps->origin, pml.previous_origin);
+	pml.previous_origin = pm->ps.origin;
+
+	// Copy over the lastframe origin
+	pmove->ps.lastframe_origin = pmove->ps.origin;
+
+	//pmove->ps.lastframe_origin = pmove->ps.origin;
+
+	// save old velocity for crashlanding
+	//VectorCopy (pm->ps->velocity, pml.previous_velocity);
+	pml.previous_velocity = pm->ps.velocity;
+
+	pml.frametime = pml.msec * 0.001f;
+
+	// update the viewangles
+	//PM_UpdateViewAngles( &(pm->ps), &(pm->cmd) );
+
+	AngleVectors (pm->ps.viewangles, &(pml.forward), &(pml.right), &(pml.up) );
+
+	//if ( pm->cmd.upmove < 10 ) 
+		// not holding jump
+		//pm->ps->pm_flags &= ~PMF_JUMP_HELD;
+
+	// decide if backpedaling animations should be used
+	/*if ( pm->cmd.forwardmove < 0 ) 
+		pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
+	else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) )
+		pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;*/
+
+	/*if ( pm->ps->pm_type >= PM_DEAD ) 
+	{
+		pm->cmd.forwardmove = 0;
+		pm->cmd.rightmove = 0;
+		pm->cmd.upmove = 0;
+	}*/
+
+	if ( pm->ps.move_type == PM_SPECTATOR ) 
+	{
+		std::cout << "SPECTATOR\n";
+		//PM_CheckDuck ();
+		PM_FlyMove ();
+		PM_DropTimers ();
+		return;
+	}
+
+	if ( pm->ps.move_type == PM_NOCLIP ) 
+	{
+		std::cout << "NOCLIP\n";
+		PM_NoclipMove ();
+		PM_DropTimers ();
+		return;
+	}
+
+	if (pm->ps.move_type == PM_FREEZE){
+		std::cout << "FREEZE\n";
+		return;		// no movement at all
+
+	}
+
+	if ( pm->ps.move_type == PM_INTERMISSION || pm->ps.move_type == PM_SPINTERMISSION){
+		std::cout << "INTERMISSION\n";
+		return;		// no movement at all
+	}
+
+	// set watertype, and waterlevel
+	PM_SetWaterLevel(pmove);
+	pml.previous_waterlevel = pmove->ps.waterlevel;
+
+	// set mins, maxs, and viewheight
+	//PM_CheckDuck ();
+
+	// set groundentity
+	PM_GroundTrace();
+
+	/*if ( pm->ps->pm_type == PM_DEAD )
+		PM_DeadMove ();
+
+	PM_DropTimers();*/
+
+	PM_DropTimers();
+
+/*#ifdef MISSIONPACK
+	if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
+		PM_InvulnerabilityMove();
+	} else
+#endif*/
+	/*if ( pm->ps->powerups[PW_FLIGHT] ) 
+		// flight powerup doesn't allow jump and has different friction
+		PM_FlyMove();
+	else if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) 
+	{
+		PM_GrappleMove();
+		// We can wiggle a bit
+		PM_AirMove();
+	} 
+	else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) 
+		PM_WaterJumpMove();*/
+	if ( pmove->ps.waterlevel > 1 ) 
+		// swimming
+		PM_WaterMove(pmove);
+	else if ( pml.walking ) 
+	{
+		std::cout << "WALKING\n";
+		// walking on ground
+		PM_WalkMove(pmove);
+		//bprintf("WalkMove\n");
+	}
+	else 
+	{
+		// airborne
+		std::cout << "AIRMOVE\n";
+		PM_AirMove();
+		//bprintf("AirMove\n");
+	}
+
+	//PM_Animate();
+
+	// set groundentity, watertype, and waterlevel
+	PM_GroundTrace();
+	PM_SetWaterLevel(pmove);
+
+	// weapons
+	/*PM_Weapon();
+
+	// torso animation
+	PM_TorsoAnimation();
+
+	// footstep events / legs animations
+	PM_Footsteps();
+
+	// entering / leaving water splashes
+	PM_WaterEvents();
+
+	// snap some parts of playerstate to save network bandwidth
+	trap_SnapVector( pm->ps->velocity );*/
+}
+
+void Ext_UpdateViewAngles(playerMove* const pm)
+{
+	playerMove::playerStruct* const ps = &(pm->ps);
+	playerMove::playercmd* const cmd = &(pm->cmd);
+	PM_UpdateViewAngles(ps, cmd);
+}
+
+void Pmove (playerMove* const pmove) 
+{
+	pm = pmove;
+
+	int			finalTime;
+
+	finalTime = pmove->cmd.serverTime;
+
+	pmove->ps.commandTime = 40;
+
+	if ( finalTime < pmove->ps.commandTime )
+		return;	// should not happen
+
+	if ( finalTime > pmove->ps.commandTime + 1000 )
+		pmove->ps.commandTime = finalTime - 1000;
+
+	pmove->ps.pmove_framecount = (pmove->ps.pmove_framecount + 1) & ( (1 << PS_PMOVEFRAMECOUNTBITS) - 1);
+
+	// chop the move up if it is too long, to prevent framerate
+	// dependent behavior
+	while ( pmove->ps.commandTime != finalTime ) 
+	{
+		int		msec;
+
+		msec = finalTime - pmove->ps.commandTime;
+
+		if ( pmove->pmove_fixed ) 
+			if ( msec > pmove->pmove_msec ) 
+				msec = pmove->pmove_msec;
+		else 
+			if ( msec > 66 ) 
+				msec = 66;
+
+		pmove->cmd.serverTime = pmove->ps.commandTime + msec;
+
+		if (pmove->isInterior)
+		{
+			PmoveSingle( pmove );
+		}
+		else
+		{
+			PmoveSingle( pmove );
+			/*
+			std::map<CellCoords, CELL* const>::const_iterator it = ExtCellLookup.find(PositionToCell(pmove->ps.origin) );
+			if (it != ExtCellLookup.end() )
+			{
+				pmove->traceObj->incellptr = it->second;
+			}*/
+		}
+
+		//if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) 
+			//pmove->cmd.upmove = 20;
+	}
+
+	//pmove->ps.last_compute_time = GetTimeQPC();
+	//pmove->ps.lerp_multiplier = (pmove->ps.origin - pmove->ps.lastframe_origin);// * (1.000 / 31.0);
+
+	//PM_CheckStuck();
+
+}
+
+
diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h
new file mode 100644
index 0000000000..e90cc3b35d
--- /dev/null
+++ b/libs/openengine/bullet/pmove.h
@@ -0,0 +1,197 @@
+#ifndef OENGINE_BULLET_PMOVE_H
+#define OENGINE_BULLET_PMOVE_H
+/*
+This source file is a *modified* version of various header files from the Quake 3 Arena source code,
+which was released under the GNU GPL (v2) in 2005.
+Quake 3 Arena is copyright (C) 1999-2005 Id Software, Inc.
+*/
+
+#include <Ogre.h>
+#include <OgreMath.h>
+#include <float.h>
+#include "trace.h"
+#include "physic.hpp"
+
+
+//#include "GameMath.h"
+//#include "GameTime.h"
+
+// Forwards-declare it!
+
+/*#ifndef COMPILING_PMOVE
+#include "Scene.h"
+extern SceneInstance* global_lastscene;
+#endif*/
+
+static const Ogre::Vector3 halfExtents(14.64f * 2, 14.24f * 2, 33.25f * 2);
+
+#define	MAX_CLIP_PLANES	5
+#define	OVERCLIP 1.001f
+//#define	STEPSIZE 18 // 18 is way too much
+#define STEPSIZE (18 / 2)
+#ifndef M_PI
+	#define M_PI 3.14159265358979323846f
+#endif
+#define YAW 0
+#define PITCH /*1*/2
+#define ROLL /*2*/1
+#define	SHORT2ANGLE(x) ( (x) * (360.0f / 65536.0f) )
+#define	ANGLE2SHORT(x) ( (const short)( (x) / (360.0f / 65536.0f) ) )
+#define	GENTITYNUM_BITS 10 // don't need to send any more
+#define	MAX_GENTITIES (1 << GENTITYNUM_BITS)
+#define	ENTITYNUM_NONE (MAX_GENTITIES - 1)
+#define ENTITYNUM_WORLD (MAX_GENTITIES - 2)
+#define	MIN_WALK_NORMAL 0.7f // can't walk on very steep slopes
+#define	JUMP_VELOCITY (270 * 1)
+#define PS_PMOVEFRAMECOUNTBITS 6
+#define	MINS_Z -24
+#define	DEFAULT_VIEWHEIGHT 26
+#define CROUCH_VIEWHEIGHT 12
+#define	DEAD_VIEWHEIGHT (-16)
+#define	CONTENTS_SOLID			1		// an eye is never valid in a solid
+#define	CONTENTS_LAVA			8
+#define	CONTENTS_SLIME			16
+#define	CONTENTS_WATER			32
+#define	CONTENTS_FOG			64
+static const float	pm_accelerate = 10.0f;
+static const float	pm_stopspeed = 100.0f;
+static const float	pm_friction = 6.0f;
+static const float  pm_flightfriction = 3.0f;
+static const float	pm_waterfriction = 1.0f;
+static const float	pm_airaccelerate = 1.0f;
+static const float	pm_swimScale = 0.50f;
+static const float	pm_duckScale = 0.25f;
+static const float  pm_flyaccelerate = 8.0f;
+static const float	pm_wateraccelerate = 4.0f;
+
+enum pmtype_t : unsigned char
+{
+	PM_NORMAL,		// can accelerate and turn
+	PM_NOCLIP,		// noclip movement
+	PM_SPECTATOR,	// still run into walls
+	PM_DEAD,		// no acceleration or turning, but free falling
+	PM_FREEZE,		// stuck in place with no control
+	PM_INTERMISSION,	// no movement or status bar
+	PM_SPINTERMISSION	// no movement or status bar
+};
+
+enum waterlevel_t : unsigned char
+{
+	WL_DRYLAND = 0,
+	WL_ANKLE,
+	WL_WAIST,
+	WL_UNDERWATER
+};
+
+
+//#include "bprintf.h"
+
+struct playerMove
+{
+	struct playerStruct
+	{
+		playerStruct() : gravity(50.0f), speed(320.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NORMAL), pm_time(0)
+		{
+			origin = Ogre::Vector3(733.164f,1000.0f, 839.432f);
+			velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f);
+
+			viewangles = Ogre::Vector3(0.0f, 0.0f, 0.0f);
+
+			delta_angles[0] = delta_angles[1] = delta_angles[2] = 0;
+
+			lastframe_origin.x = lastframe_origin.y = lastframe_origin.z = 0;
+			lerp_multiplier.x = lerp_multiplier.y = lerp_multiplier.z = 0;
+		}
+
+		inline void SpeedUp(void)
+		{
+			printf("speed up to: %f\n", speed);
+			speed *= 1.25f;
+		}
+
+		inline void SpeedDown(void)
+		{
+			printf("speed down to %f\n", speed);
+			speed /= 1.25f;
+		}
+
+		Ogre::Vector3 velocity;
+		Ogre::Vector3 origin;
+		float gravity; // default = 800
+		float speed; // default = 320
+
+		int commandTime; // the time at which this command was issued (in milliseconds)
+
+		int pm_time;
+
+		Ogre::Vector3 viewangles;
+
+		int groundEntityNum;
+
+		int pmove_framecount;
+
+		int watertype;
+		waterlevel_t waterlevel;
+
+		signed short delta_angles[3];
+
+		pmtype_t move_type;
+
+		float last_compute_time;
+		Ogre::Vector3 lastframe_origin;
+		Ogre::Vector3 lerp_multiplier;
+	} ps;
+
+	struct playercmd
+	{
+		enum CMDstateChange
+		{
+			NO_CHANGE,
+			KEYDOWN,
+			KEYUP
+		};
+
+		playercmd() : forwardmove(0), rightmove(0), upmove(0), serverTime(50), ducking(false), 
+			activating(false), lastActivatingState(false), procActivating(NO_CHANGE),
+			dropping(false), lastDroppingState(false), procDropping(NO_CHANGE)
+		{
+			angles[0] = angles[1] = angles[2] = 0;
+		}
+
+		int serverTime;
+
+		short angles[3];
+
+		signed char forwardmove;
+		signed char rightmove;
+		signed char upmove;
+
+		bool ducking;
+		bool activating; // if the user is holding down the activate button
+		bool dropping; // if the user is dropping an item
+
+		bool lastActivatingState;
+		bool lastDroppingState;
+
+		CMDstateChange procActivating;
+		CMDstateChange procDropping;
+	} cmd;
+
+	playerMove() : msec(50), pmove_fixed(false), pmove_msec(50), waterHeight(0), isInterior(true), hasWater(false)
+	{
+	}
+
+	int msec;
+	int pmove_msec;
+	bool pmove_fixed;
+	int waterHeight;
+	bool hasWater;
+	bool isInterior;
+	//Object* traceObj;
+	OEngine::Physic::PhysicEngine* mEngine;
+};
+
+void Pmove (playerMove* const pmove);
+void Ext_UpdateViewAngles(playerMove* const pm);
+void AngleVectors( const Ogre::Vector3& angles, Ogre::Vector3* const forward, Ogre::Vector3* const right, Ogre::Vector3* const up) ;
+#endif
diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp
new file mode 100644
index 0000000000..6baec8c83a
--- /dev/null
+++ b/libs/openengine/bullet/trace.cpp
@@ -0,0 +1,188 @@
+
+#include "trace.h"
+
+
+
+#include <map>
+
+
+
+
+
+
+
+void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, const float rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass)  //Traceobj was a Aedra Object
+{
+
+	//if (!traceobj)
+	//	return;
+
+	//if (!traceobj->incellptr)
+	//	return;
+
+	const Ogre::Vector3 rayDir = end - start;
+
+	// Nudge starting point backwards
+	//const Position3D nudgestart = start + (rayDir * -0.1f); // by 10% (isn't that too much?)
+	//const Position3D nudgestart = start;
+
+	NewPhysTraceResults out;
+	//std::cout << "Starting trace\n";
+	Ogre::Vector3 startReplace = Ogre::Vector3(650,950, 45);
+	Ogre::Vector3 endReplace = startReplace;
+	endReplace.y -= .25;
+	
+	const bool hasHit = NewPhysicsTrace<collisionWorldTrace>(&out, startReplace, endReplace, BBHalfExtents, Ogre::Vector3(0.0f, rotation, 0.0f), isInterior, enginePass);
+	if(hasHit)
+		std::cout << "Has hit\n";
+	if (out.fraction < 0.001f)
+		results->startsolid = true;
+	else
+		results->startsolid = false;
+	
+
+	//results->allsolid = out.startSolid;
+
+	// If outside and underground, we're solid
+	/*if (isInterior)
+	{
+		const Ogre::Vector3 height = GetGroundPosition(start, CellCoords(traceCell->data->gridX, traceCell->data->gridY) );
+		if (start.yPos - height.yPos < (-2.0f * BBHalfExtents.yPos) )
+		{
+			results->allsolid = true;
+		}
+		else
+			results->allsolid = false;
+	}*/
+
+	// If inside and out of the tree, we're solid
+	//else
+	//{
+		results->allsolid = out.startSolid;
+		//std::cout << "allsolid" << results->allsolid << "\n";
+	//}
+
+	if (!hasHit)
+	{
+		results->endpos = end;
+		results->planenormal = Ogre::Vector3(0.0f, 1.0f, 0.0f);
+		results->entityNum = ENTITYNUM_NONE;
+		results->fraction = 1.0f;
+	}
+	else
+	{
+		results->fraction = out.fraction;
+		results->planenormal = out.hitNormal;
+		results->endpos = rayDir * results->fraction + start;
+		results->entityNum = ENTITYNUM_WORLD;
+		/*bprintf("Start: (%f, %f, %f) End: (%f, %f, %f) TraceDir: (%f, %f, %f) HitNormal: (%f, %f, %f) Fraction: %f Hitpos: (%f, %f, %f) CompensatedHitpos: (%f, %f, %f)\n", 
+			start.xPos, start.yPos, start.zPos,
+			end.xPos, end.yPos, end.zPos,
+			rayDir.xPos, rayDir.yPos, rayDir.zPos,
+			results->planenormal.xPos, results->planenormal.yPos, results->planenormal.zPos, results->fraction,
+			out.endPos.xPos, out.endPos.yPos, out.endPos.zPos,
+			results->endpos.xPos, results->endpos.yPos, results->endpos.zPos);*/
+	}
+}
+
+
+
+template <const traceWorldType traceType>
+const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, 
+	const Ogre::Vector3& BBHalfExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass)
+{
+	//if (!traceobj->incellptr)
+	//	return false;
+	//if(enginePass->dynamicsWorld->getCollisionObjectArray().at(60)->getCollisionShape()->isConvex())
+	//	std::cout << "It's convex\n";
+	
+
+
+	const btVector3 btstart(start.x, start.y, start.z);
+	const btVector3 btend(end.x, end.y, end.z);
+	const btQuaternion btrot(rotation.y, rotation.x, rotation.z);
+
+	const btBoxShape newshape(btVector3(1000, 1000, 1000) );
+	const btTransform from(btrot, btstart);
+	const btTransform to(btrot, btend);	
+	float x = from.getOrigin().getX();
+	float y = from.getOrigin().getY();
+	float z = from.getOrigin().getZ();
+	float x2 = to.getOrigin().getX();
+	float y2 = to.getOrigin().getY();
+	float z2 = to.getOrigin().getZ();
+	
+	std::cout << "BtFrom: " << x << "," << y << "," << z << "\n";
+	std::cout << "BtTo: " << x2 << "," << y2 << "," << z2 << "\n";
+	//std::cout << "BtTo: " << to.getOrigin().getX() << "," << to.getOrigin().getY() << "," << to.getOrigin().getZ() << "\n";
+
+
+	btCollisionWorld::ClosestConvexResultCallback
+		newTraceCallback(btstart, btend);
+
+	newTraceCallback.m_collisionFilterMask = (traceType == collisionWorldTrace) ? Only_Collision : Only_Pickup;
+	
+	
+	enginePass->dynamicsWorld->convexSweepTest(&newshape, from, to, newTraceCallback);
+	//newTraceCallback.
+	
+	
+	//std::cout << "NUM: " << enginePass->dynamicsWorld->getNumCollisionObjects() << "\n";
+
+	// Copy the hit data over to our trace results struct:
+	out->fraction = newTraceCallback.m_closestHitFraction;
+
+	Ogre::Vector3& outhitnormal = out->hitNormal;
+	const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld;
+
+	outhitnormal.x = tracehitnormal.x();
+	outhitnormal.y = tracehitnormal.y();
+	outhitnormal.z = tracehitnormal.z();
+
+	Ogre::Vector3& outhitpos = out->endPos;
+	const btVector3& tracehitpos = newTraceCallback.m_hitPointWorld;
+
+	outhitpos.x = tracehitpos.x();
+	outhitpos.y = tracehitpos.y();
+	outhitpos.z= tracehitpos.z();
+
+	// StartSolid test:
+	{
+		out->startSolid = false;
+		//btCollisionObject collision;
+		//collision.setCollisionShape(const_cast<btBoxShape* const>(&newshape) );
+
+		//CustomContactCallback crb;
+
+		//world.world->contactTest(&collision, crb);
+		//out->startSolid = crb.hit;
+
+		// If outside and underground, we're solid
+		if (!isInterior)   //Check if we are interior
+		{
+		}
+
+		// If inside and out of the tree, we're solid
+		else
+		{
+			btVector3 aabbMin, aabbMax;
+			enginePass->broadphase->getBroadphaseAabb(aabbMin, aabbMax);
+			//std::cout << "AABBMIN" << aabbMin.getX() <<"," <<aabbMin.getY() << "," << aabbMin.getZ()  << "\n";
+			//std::cout << "AABBMAX" << aabbMax.getX() <<"," <<aabbMax.getY() << "," << aabbMax.getZ()  << "\n";
+			//std::cout << "AABBMAX" << aabbMax << "\n";
+			if (!TestPointAgainstAabb2(aabbMin, aabbMax, *(const btVector3* const)&(start) ) )
+			{
+				//We're solid
+				out->startSolid = false;
+			}
+		}
+	}
+
+	const bool hasHit = newTraceCallback.hasHit();
+
+	if(hasHit)
+		std::cout << "HIT\n";
+
+	
+	return hasHit;
+}
diff --git a/libs/openengine/bullet/trace.h b/libs/openengine/bullet/trace.h
new file mode 100644
index 0000000000..025673ab7a
--- /dev/null
+++ b/libs/openengine/bullet/trace.h
@@ -0,0 +1,62 @@
+#ifndef OENGINE_BULLET_TRACE_H
+#define OENGINE_BULLET_TRACE_H
+
+
+#include <btBulletDynamicsCommon.h>
+#include <btBulletCollisionCommon.h>
+#include <components/nifbullet/bullet_nif_loader.hpp>
+//#include <apps\openmw\mwworld\world.hpp>
+#include <openengine/bullet/pmove.h>
+#include <openengine/bullet/physic.hpp>
+
+
+ 
+enum traceWorldType
+{
+	collisionWorldTrace = 1,
+	pickWorldTrace = 2,
+	bothWorldTrace = collisionWorldTrace | pickWorldTrace
+};
+
+enum collaborativePhysicsType : unsigned
+{
+	No_Physics = 0, // Both are empty (example: statics you can walk through, like tall grass)
+	Only_Collision = 1, // This object only has collision physics but no pickup physics (example: statics)
+	Only_Pickup = 2, // This object only has pickup physics but no collision physics (example: items dropped on the ground)
+	Both_Physics = 3 // This object has both kinds of physics (example: activators)
+};
+
+struct NewPhysTraceResults
+{
+	Ogre::Vector3 endPos;
+	Ogre::Vector3 hitNormal;
+	float fraction;
+	bool startSolid;
+	//const Object* hitObj;
+}; 
+struct traceResults
+{
+	Ogre::Vector3 endpos;
+	Ogre::Vector3 planenormal;
+
+	float fraction;
+
+	int surfaceFlags;
+	int contents;
+	int entityNum;
+
+	bool allsolid;
+	bool startsolid;
+};
+
+
+
+template <const traceWorldType traceType>
+const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass);
+template const bool NewPhysicsTrace<collisionWorldTrace>(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass);
+template const bool NewPhysicsTrace<pickWorldTrace>(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass);
+
+void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const float rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass);
+
+
+#endif
\ No newline at end of file
diff --git a/libs/openengine/gui/events.cpp b/libs/openengine/gui/events.cpp
new file mode 100644
index 0000000000..713db85858
--- /dev/null
+++ b/libs/openengine/gui/events.cpp
@@ -0,0 +1,80 @@
+#include <MyGUI.h>
+#include <OIS/OIS.h>
+#include <assert.h>
+
+#include "events.hpp"
+
+using namespace OIS;
+using namespace OEngine::GUI;
+
+EventInjector::EventInjector(MyGUI::Gui *g)
+  : gui(g), mouseX(0), mouseY(0), enabled(true)
+{
+  assert(gui);
+  maxX = gui->getViewSize().width;
+  maxY = gui->getViewSize().height;
+}
+
+template <typename X>
+void setRange(X &x, X min, X max)
+{
+  if(x < min) x = min;
+  else if(x > max) x = max;
+}
+
+void EventInjector::event(Type type, int index, const void *p)
+{
+  if(!enabled) return;
+
+  if(type & EV_Keyboard)
+    {
+      KeyEvent *key = (KeyEvent*)p;
+      MyGUI::KeyCode code = MyGUI::KeyCode::Enum(key->key);
+      if(type == EV_KeyDown)
+        {
+          /*
+            This is just a first approximation. Apparently, OIS is
+            unable to provide reliable unicode characters on all
+            platforms. At least that's what I surmise from the amount
+            of workaround that the MyGUI folks have put in place for
+            this. See Common/Input/OIS/InputManager.cpp in the MyGUI
+            sources for details.
+
+            If the work they have done there is indeed necessary (I
+            haven't tested that it is, although I have had dubious
+            experinces with OIS events in the past), then we should
+            probably adapt all that code here. Or even better,
+            directly into the OIS input manager in Mangle.
+
+            Note that all this only affects the 'text' field, and
+            should thus only affect typed text in input boxes (which
+            is still pretty significant.)
+          */
+          MyGUI::Char text = (MyGUI::Char)key->text;
+          gui->injectKeyPress(code,text);
+        }
+      else
+        {
+          gui->injectKeyRelease(code);
+        }
+    }
+  else if(type & EV_Mouse)
+    {
+      MouseEvent *mouse = (MouseEvent*)p;
+      MyGUI::MouseButton id = MyGUI::MouseButton::Enum(index);
+
+      // Update mouse position
+      mouseX += mouse->state.X.rel;
+      mouseY += mouse->state.Y.rel;
+
+      setRange(mouseX,0,maxX);
+      setRange(mouseY,0,maxY);
+
+      if(type == EV_MouseDown)
+        gui->injectMousePress(mouseX, mouseY, id);
+      else if(type == EV_MouseUp)
+        gui->injectMouseRelease(mouseX, mouseY, id);
+      else
+        gui->injectMouseMove(mouseX, mouseY, mouse->state.Z.abs);
+    }
+}
diff --git a/libs/openengine/gui/events.hpp b/libs/openengine/gui/events.hpp
new file mode 100644
index 0000000000..6ca83cf753
--- /dev/null
+++ b/libs/openengine/gui/events.hpp
@@ -0,0 +1,31 @@
+#ifndef OENGINE_MYGUI_EVENTS_H
+#define OENGINE_MYGUI_EVENTS_H
+
+#include <mangle/input/event.hpp>
+
+namespace MyGUI
+{
+  class Gui;
+}
+
+namespace OEngine {
+namespace GUI
+{
+  /** Event handler that injects OIS events into MyGUI
+   */
+  class EventInjector : public Mangle::Input::Event
+  {
+    MyGUI::Gui *gui;
+    int mouseX, mouseY;
+    int maxX, maxY;
+
+  public:
+    bool enabled;
+
+    EventInjector(MyGUI::Gui *g);
+    void event(Type type, int index, const void *p);
+  };
+
+  typedef boost::shared_ptr<EventInjector> EventInjectorPtr;
+}}
+#endif
diff --git a/libs/openengine/gui/layout.hpp b/libs/openengine/gui/layout.hpp
new file mode 100644
index 0000000000..f02ddbdff1
--- /dev/null
+++ b/libs/openengine/gui/layout.hpp
@@ -0,0 +1,119 @@
+#ifndef OENGINE_MYGUI_LAYOUT_H
+#define OENGINE_MYGUI_LAYOUT_H
+
+#include <assert.h>
+#include <MyGUI.h>
+
+namespace OEngine {
+namespace GUI
+{
+  /** The Layout class is an utility class used to load MyGUI layouts
+      from xml files, and to manipulate member widgets.
+   */
+  class Layout
+  {
+  public:
+    Layout(const std::string & _layout, MyGUI::WidgetPtr _parent = nullptr)
+      : mMainWidget(nullptr)
+    { initialise(_layout, _parent); }
+    virtual ~Layout() { shutdown();  }
+
+    template <typename T>
+    void getWidget(T * & _widget, const std::string & _name, bool _throw = true)
+    {
+      _widget = nullptr;
+      for (MyGUI::VectorWidgetPtr::iterator iter=mListWindowRoot.begin();
+           iter!=mListWindowRoot.end(); ++iter)
+        {
+          MyGUI::WidgetPtr find = (*iter)->findWidget(mPrefix + _name);
+          if (nullptr != find)
+            {
+              T * cast = find->castType<T>(false);
+              if (nullptr != cast)
+                _widget = cast;
+              else if (_throw)
+                {
+                  MYGUI_EXCEPT("Error cast : dest type = '" << T::getClassTypeName()
+                               << "' source name = '" << find->getName()
+                               << "' source type = '" << find->getTypeName() << "' in layout '" << mLayoutName << "'");
+                }
+              return;
+            }
+        }
+      MYGUI_ASSERT( ! _throw, "widget name '" << _name << "' in layout '" << mLayoutName << "' not found.");
+    }
+
+    void initialise(const std::string & _layout,
+                    MyGUI::WidgetPtr _parent = nullptr)
+    {
+      const std::string MAIN_WINDOW = "_Main";
+      mLayoutName = _layout;
+
+      if (mLayoutName.empty())
+        mMainWidget = _parent;
+      else
+        {
+          mPrefix = MyGUI::utility::toString(this, "_");
+          mListWindowRoot = MyGUI::LayoutManager::getInstance().loadLayout(mLayoutName, mPrefix, _parent);
+
+          const std::string main_name = mPrefix + MAIN_WINDOW;
+          for (MyGUI::VectorWidgetPtr::iterator iter=mListWindowRoot.begin(); iter!=mListWindowRoot.end(); ++iter)
+            {
+              if ((*iter)->getName() == main_name)
+                {
+                  mMainWidget = (*iter);
+                  break;
+                }
+            }
+          MYGUI_ASSERT(mMainWidget, "root widget name '" << MAIN_WINDOW << "' in layout '" << mLayoutName << "' not found.");
+        }
+    }
+
+    void shutdown()
+    {
+      MyGUI::LayoutManager::getInstance().unloadLayout(mListWindowRoot);
+      mListWindowRoot.clear();
+    }
+
+    void setCoord(int x, int y, int w, int h)
+    {
+      mMainWidget->setCoord(x,y,w,h);
+    }
+
+    void setVisible(bool b)
+    {
+      mMainWidget->setVisible(b);
+    }
+
+    void setText(const std::string& name, const std::string& caption)
+    {
+      MyGUI::WidgetPtr pt;
+      getWidget(pt, name);
+      pt->setCaption(caption);
+    }
+
+    void setTextColor(const std::string& name, float r, float g, float b)
+    {
+      MyGUI::WidgetPtr pt;
+      getWidget(pt, name);
+      MyGUI::StaticText *st = dynamic_cast<MyGUI::StaticText*>(pt);
+      if(st != NULL)
+        st->setTextColour(MyGUI::Colour(b,g,r));
+    }
+
+    void setImage(const std::string& name, const std::string& imgName)
+    {
+      MyGUI::StaticImagePtr pt;
+      getWidget(pt, name);
+      pt->setImageTexture(imgName);
+    }
+
+  protected:
+
+    MyGUI::WidgetPtr mMainWidget;
+    std::string mPrefix;
+    std::string mLayoutName;
+    MyGUI::VectorWidgetPtr mListWindowRoot;
+  };
+}}
+#endif
diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp
new file mode 100644
index 0000000000..b606d7d16f
--- /dev/null
+++ b/libs/openengine/gui/manager.cpp
@@ -0,0 +1,47 @@
+#include <MyGUI.h>
+#include <MyGUI_OgrePlatform.h>
+#include <assert.h>
+
+#include "manager.hpp"
+
+using namespace OEngine::GUI;
+
+void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging, const std::string& logDir)
+{
+  assert(wnd);
+  assert(mgr);
+
+  using namespace MyGUI;
+
+  // Enable/disable MyGUI logging to stdout. (Logging to MyGUI.log is
+  // still enabled.) In order to do this we have to initialize the log
+  // manager before the main gui system itself, otherwise the main
+  // object will get the chance to spit out a few messages before we
+  // can able to disable it.
+  LogManager::initialise();
+  LogManager::setSTDOutputEnabled(logging);
+
+  std::string theLogFile = std::string(MYGUI_PLATFORM_LOG_FILENAME);
+  if(!logDir.empty())
+      theLogFile.insert(0, logDir);
+
+  // Set up OGRE platform. We might make this more generic later.
+  mPlatform = new OgrePlatform();
+  mPlatform->initialise(wnd, mgr, "General", theLogFile);
+
+  // Create GUI
+  mGui = new Gui();
+  mGui->initialise("core.xml", theLogFile);
+}
+
+void MyGUIManager::shutdown()
+{
+  if(mGui) delete mGui;
+  if(mPlatform)
+    {
+      mPlatform->shutdown();
+      delete mPlatform;
+    }
+  mGui = NULL;
+  mPlatform = NULL;
+}
diff --git a/libs/openengine/gui/manager.hpp b/libs/openengine/gui/manager.hpp
new file mode 100644
index 0000000000..781685acfe
--- /dev/null
+++ b/libs/openengine/gui/manager.hpp
@@ -0,0 +1,36 @@
+#ifndef OENGINE_MYGUI_MANAGER_H
+#define OENGINE_MYGUI_MANAGER_H
+
+namespace MyGUI
+{
+  class OgrePlatform;
+  class Gui;
+}
+
+namespace Ogre
+{
+  class RenderWindow;
+  class SceneManager;
+}
+
+namespace OEngine {
+namespace GUI
+{
+  class MyGUIManager
+  {
+    MyGUI::OgrePlatform *mPlatform;
+    MyGUI::Gui *mGui;
+
+  public:
+    MyGUIManager() : mPlatform(NULL), mGui(NULL) {}
+    MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string(""))
+    { setup(wnd,mgr,logging, logDir); }
+    ~MyGUIManager() { shutdown(); }
+
+    void setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string(""));
+    void shutdown();
+
+    MyGUI::Gui *getGui() { return mGui; }
+  };
+}}
+#endif
diff --git a/libs/openengine/input/dispatch_map.hpp b/libs/openengine/input/dispatch_map.hpp
new file mode 100644
index 0000000000..f0d4cabe92
--- /dev/null
+++ b/libs/openengine/input/dispatch_map.hpp
@@ -0,0 +1,75 @@
+#ifndef OENGINE_INPUT_DISPATCHMAP_H
+#define OENGINE_INPUT_DISPATCHMAP_H
+
+#include <set>
+#include <map>
+#include <assert.h>
+
+namespace OEngine {
+namespace Input {
+
+/**
+   DispatchMap is a simple connection system that connects incomming
+   signals with outgoing signals.
+
+   The signals can be connected one-to-one, many-to-one, one-to-many
+   or many-to-many.
+
+   The dispatch map is completely system agnostic. It is a pure data
+   structure and all signals are just integer indices. It does not
+   delegate any actions, but used together with Dispatcher it can be
+   used to build an event system.
+ */
+struct DispatchMap
+{
+  typedef std::set<int> OutList;
+  typedef std::map<int, OutList> InMap;
+
+  typedef OutList::iterator Oit;
+  typedef InMap::iterator Iit;
+
+  InMap map;
+
+  void bind(int in, int out)
+  {
+    map[in].insert(out);
+  }
+
+  void unbind(int in, int out)
+  {
+    Iit it = map.find(in);
+    if(it != map.end())
+      {
+        it->second.erase(out);
+
+        // If there are no more elements, then remove the entire list
+        if(it->second.empty())
+          map.erase(it);
+      }
+  }
+
+  /// Check if a given input is bound to anything
+  bool isBound(int in) const
+  {
+    return map.find(in) != map.end();
+  }
+
+  /**
+     Get the list of outputs bound to the given input. Only call this
+     on inputs that you know are bound to something.
+
+     The returned set is only intended for immediate iteration. Do not
+     store references to it.
+  */
+  const OutList &getList(int in) const
+  {
+    assert(isBound(in));
+    InMap::const_iterator it = map.find(in);
+    assert(it != map.end());
+    const OutList &out = it->second;
+    assert(!out.empty());
+    return out;
+  }
+};
+}}
+#endif
diff --git a/libs/openengine/input/dispatcher.hpp b/libs/openengine/input/dispatcher.hpp
new file mode 100644
index 0000000000..a8d480d4b0
--- /dev/null
+++ b/libs/openengine/input/dispatcher.hpp
@@ -0,0 +1,78 @@
+#ifndef OENGINE_INPUT_DISPATCHER_H
+#define OENGINE_INPUT_DISPATCHER_H
+
+#include <cassert>
+#include <iostream>
+
+#include <mangle/input/event.hpp>
+
+#include "dispatch_map.hpp"
+#include "func_binder.hpp"
+
+namespace OEngine {
+namespace Input {
+
+struct Dispatcher : Mangle::Input::Event
+{
+  DispatchMap map;
+  FuncBinder funcs;
+
+  /**
+     Constructor. Takes the number of actions and passes it to
+     FuncBinder.
+  */
+  Dispatcher(int actions) : funcs(actions) {}
+
+  void bind(unsigned int action, int key)
+  {
+    assert(action < funcs.getSize());
+    map.bind(key, action);
+  }
+  void unbind(unsigned int action, int key)
+  {
+    assert(action < funcs.getSize());
+    map.unbind(key, action);
+  }
+  bool isBound(int key) const { return map.isBound(key); }
+
+  /**
+     Instigate an event. It is translated through the dispatch map and
+     sent to the function bindings.
+   */
+  typedef DispatchMap::OutList _O;
+  void event(Type type, int index, const void* p)
+  {
+    // No bindings, nothing happens
+    if(!isBound(index))
+      return;
+
+    // Get the mapped actions and execute them
+    const _O &list = map.getList(index);
+    _O::const_iterator it;
+    for(it = list.begin(); it != list.end(); it++)
+    {
+      //catch exceptions thrown in the input handlers so that pressing a key
+      //doesn't cause OpenMw to crash
+      try
+      {
+          funcs.call(*it, p);
+      }
+      catch(const std::exception& e)
+      {
+          std::cerr << "Exception in input handler: " << e.what() << std::endl;
+      }
+      catch(...)
+      {
+          std::cerr << "Unknown exception in input handler" << std::endl;
+      }
+
+    }
+  }
+};
+
+// This helps us play nice with Mangle's EventPtr, but it should
+// really be defined for all the classes in OEngine.
+  typedef boost::shared_ptr<Dispatcher> DispatcherPtr;
+
+}}
+#endif
diff --git a/libs/openengine/input/func_binder.hpp b/libs/openengine/input/func_binder.hpp
new file mode 100644
index 0000000000..7aa733edf8
--- /dev/null
+++ b/libs/openengine/input/func_binder.hpp
@@ -0,0 +1,106 @@
+#ifndef OENGINE_INPUT_FUNCBINDER_H
+#define OENGINE_INPUT_FUNCBINDER_H
+
+#include <string>
+#include <vector>
+#include <boost/function.hpp>
+#include <assert.h>
+
+namespace OEngine {
+namespace Input {
+
+/**
+   An Action defines the user defined action corresponding to a
+   binding.
+
+   The first parameter is the action index that invoked this call. You
+   can assign the same function to multiple actions, and this can help
+   you keep track of which action was invoked.
+
+   The second parameter is an optional user-defined parameter,
+   represented by a void pointer. In many cases it is practical to
+   point this to temporaries (stack values), so make sure not to store
+   permanent references to it unless you've planning for this on the
+   calling side as well.
+ */
+typedef boost::function<void(int,const void*)> Action;
+
+/**
+   The FuncBinder is a simple struct that binds user-defined indices
+   to functions. It is useful for binding eg. keyboard events to
+   specific actions in your program, but can potentially have many
+   other uses as well.
+ */
+class FuncBinder
+{
+  struct FuncBinding
+  {
+    std::string name;
+    Action action;
+  };
+
+  std::vector<FuncBinding> bindings;
+
+public:
+  /**
+     Constructor. Initialize the struct by telling it how many action
+     indices you intend to bind.
+
+     The indices you use should be 0 <= i < number.
+  */
+  FuncBinder(int number) : bindings(number) {}
+
+  unsigned int getSize() { return bindings.size(); }
+
+  /**
+     Bind an action to an index.
+   */
+  void bind(int index, Action action, const std::string &name="")
+  {
+    assert(index >= 0 && index < (int)bindings.size());
+
+    FuncBinding &fb = bindings[index];
+    fb.action = action;
+    fb.name = name;
+  }
+
+  /**
+     Unbind an index, reverting a previous bind().
+   */
+  void unbind(int index)
+  {
+    assert(index >= 0 && index < (int)bindings.size());
+
+    bindings[index] = FuncBinding();
+  }
+
+  /**
+     Call a specific action. Takes an optional parameter that is
+     passed to the action.
+   */
+  void call(int index, const void *p=NULL) const
+  {
+    assert(index >= 0 && index < (int)bindings.size());
+
+    const FuncBinding &fb = bindings[index];
+    if(fb.action) fb.action(index, p);
+  }
+
+  /// Check if a given index is bound to anything
+  bool isBound(int index) const
+  {
+    assert(index >= 0 && index < (int)bindings.size());
+
+    return !bindings[index].action.empty();
+  }
+
+  /// Return the name associated with an action (empty if not bound)
+  const std::string &getName(int index) const
+  {
+    assert(index >= 0 && index < (int)bindings.size());
+
+    return bindings[index].name;
+  }
+};
+}}
+#endif
diff --git a/libs/openengine/input/poller.hpp b/libs/openengine/input/poller.hpp
new file mode 100644
index 0000000000..c544aed529
--- /dev/null
+++ b/libs/openengine/input/poller.hpp
@@ -0,0 +1,46 @@
+#ifndef OENGINE_INPUT_POLLER_H
+#define OENGINE_INPUT_POLLER_H
+
+#include "dispatch_map.hpp"
+#include <mangle/input/driver.hpp>
+
+namespace OEngine {
+namespace Input {
+
+/** The poller is used to check (poll) for keys rather than waiting
+    for events. */
+struct Poller
+{
+  DispatchMap map;
+  Mangle::Input::Driver &input;
+
+  Poller(Mangle::Input::Driver &drv)
+    : input(drv) {}
+
+  /** Bind or unbind a given action with a key. The action is the first
+      parameter, the key is the second.
+  */
+  void bind(int in, int out) { map.bind(in, out); }
+  void unbind(int in, int out) { map.unbind(in, out); }
+  bool isBound(int in) const { return map.isBound(in); }
+
+  /// Check whether a given action button is currently pressed.
+  typedef DispatchMap::OutList _O;
+  bool isDown(int index) const
+  {
+    // No bindings, no action
+    if(!isBound(index))
+      return false;
+
+    // Get all the keys bound to this action, and check them.
+    const _O &list = map.getList(index);
+    _O::const_iterator it;
+    for(it = list.begin(); it != list.end(); it++)
+      // If there's any match, we're good to go.
+      if(input.isDown(*it)) return true;
+
+    return false;
+  }
+};
+}}
+#endif
diff --git a/libs/openengine/input/tests/Makefile b/libs/openengine/input/tests/Makefile
new file mode 100644
index 0000000000..91a0b26636
--- /dev/null
+++ b/libs/openengine/input/tests/Makefile
@@ -0,0 +1,18 @@
+GCC=g++
+
+all: funcbind_test dispatch_map_test sdl_driver_test sdl_binder_test
+
+funcbind_test: funcbind_test.cpp ../func_binder.hpp
+	$(GCC) $< -o $@
+
+dispatch_map_test: dispatch_map_test.cpp ../dispatch_map.hpp
+	$(GCC) $< -o $@
+
+sdl_driver_test: sdl_driver_test.cpp
+	$(GCC) $< ../../mangle/input/servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -I../../
+
+sdl_binder_test: sdl_binder_test.cpp
+	$(GCC) $< ../../mangle/input/servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -I../../
+
+clean:
+	rm *_test
diff --git a/libs/openengine/input/tests/dispatch_map_test.cpp b/libs/openengine/input/tests/dispatch_map_test.cpp
new file mode 100644
index 0000000000..5f262494e5
--- /dev/null
+++ b/libs/openengine/input/tests/dispatch_map_test.cpp
@@ -0,0 +1,54 @@
+#include <iostream>
+using namespace std;
+
+#include "../dispatch_map.hpp"
+
+using namespace OEngine::Input;
+
+typedef DispatchMap::OutList OutList;
+typedef OutList::const_iterator Cit;
+
+void showList(const DispatchMap::OutList &out)
+{
+  for(Cit it = out.begin();
+      it != out.end(); it++)
+    {
+      cout << "  " << *it << endl;
+    }
+}
+
+void showAll(DispatchMap &map)
+{
+  cout << "\nPrinting everything:\n";
+  for(DispatchMap::Iit it = map.map.begin();
+      it != map.map.end(); it++)
+    {
+      cout << it->first << ":\n";
+      showList(map.getList(it->first));
+    }
+}
+
+int main()
+{
+  cout << "Testing the dispatch map\n";
+
+  DispatchMap dsp;
+
+  dsp.bind(1,9);
+  dsp.bind(2,-5);
+  dsp.bind(2,9);
+  dsp.bind(3,10);
+  dsp.bind(3,12);
+  dsp.bind(3,10);
+
+  showAll(dsp);
+
+  dsp.unbind(1,9);
+  dsp.unbind(5,8);
+  dsp.unbind(3,11);
+  dsp.unbind(3,12);
+  dsp.unbind(3,12);
+
+  showAll(dsp);
+  return 0;
+}
diff --git a/libs/openengine/input/tests/funcbind_test.cpp b/libs/openengine/input/tests/funcbind_test.cpp
new file mode 100644
index 0000000000..bb4ad34e18
--- /dev/null
+++ b/libs/openengine/input/tests/funcbind_test.cpp
@@ -0,0 +1,47 @@
+#include <iostream>
+using namespace std;
+
+#include "../func_binder.hpp"
+
+void f1(int i, const void *p)
+{
+  cout << "  F1 i=" << i << endl;
+
+  if(p)
+    cout << "  Got a nice gift: "
+         << *((const float*)p) << endl;
+}
+
+void f2(int i, const void *p)
+{
+  cout << "  F2 i=" << i << endl;
+}
+
+using namespace OEngine::Input;
+
+int main()
+{
+  cout << "This will test the function binding system\n";
+
+  FuncBinder bnd(5);
+
+  bnd.bind(0, &f1, "This is action 1");
+  bnd.bind(1, &f2);
+  bnd.bind(2, &f1, "This is action 3");
+  bnd.bind(3, &f2, "This is action 4");
+
+  bnd.unbind(2);
+
+  for(int i=0; i<5; i++)
+    {
+      cout << "Calling " << i << ": '" << bnd.getName(i) << "'\n";
+      bnd.call(i);
+      if(!bnd.isBound(i)) cout << "  (not bound)\n";
+    }
+
+  cout << "\nCalling with parameter:\n";
+  float f = 3.1415;
+  bnd.call(0, &f);
+
+  return 0;
+}
diff --git a/libs/openengine/input/tests/output/dispatch_map_test.out b/libs/openengine/input/tests/output/dispatch_map_test.out
new file mode 100644
index 0000000000..01aa9d9d93
--- /dev/null
+++ b/libs/openengine/input/tests/output/dispatch_map_test.out
@@ -0,0 +1,18 @@
+Testing the dispatch map
+
+Printing everything:
+1:
+  9
+2:
+  -5
+  9
+3:
+  10
+  12
+
+Printing everything:
+2:
+  -5
+  9
+3:
+  10
diff --git a/libs/openengine/input/tests/output/funcbind_test.out b/libs/openengine/input/tests/output/funcbind_test.out
new file mode 100644
index 0000000000..862c5c9729
--- /dev/null
+++ b/libs/openengine/input/tests/output/funcbind_test.out
@@ -0,0 +1,15 @@
+This will test the function binding system
+Calling 0: 'This is action 1'
+  F1 i=0
+Calling 1: ''
+  F2 i=1
+Calling 2: ''
+  (not bound)
+Calling 3: 'This is action 4'
+  F2 i=3
+Calling 4: ''
+  (not bound)
+
+Calling with parameter:
+  F1 i=0
+  Got a nice gift: 3.1415
diff --git a/libs/openengine/input/tests/output/sdl_binder_test.out b/libs/openengine/input/tests/output/sdl_binder_test.out
new file mode 100644
index 0000000000..fd4eb90e3e
--- /dev/null
+++ b/libs/openengine/input/tests/output/sdl_binder_test.out
@@ -0,0 +1,4 @@
+Hold the Q key to quit:
+You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly
+
+Bye bye!
diff --git a/libs/openengine/input/tests/output/sdl_driver_test.out b/libs/openengine/input/tests/output/sdl_driver_test.out
new file mode 100644
index 0000000000..fd4eb90e3e
--- /dev/null
+++ b/libs/openengine/input/tests/output/sdl_driver_test.out
@@ -0,0 +1,4 @@
+Hold the Q key to quit:
+You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly
+
+Bye bye!
diff --git a/libs/openengine/input/tests/sdl_binder_test.cpp b/libs/openengine/input/tests/sdl_binder_test.cpp
new file mode 100644
index 0000000000..7de5f5d4fd
--- /dev/null
+++ b/libs/openengine/input/tests/sdl_binder_test.cpp
@@ -0,0 +1,71 @@
+#include <iostream>
+#include <mangle/input/servers/sdl_driver.hpp>
+#include <SDL.h>
+#include "../dispatcher.hpp"
+#include "../poller.hpp"
+
+using namespace std;
+using namespace Mangle::Input;
+using namespace OEngine::Input;
+
+enum Actions
+  {
+    A_Quit,
+    A_Left,
+    A_Right,
+
+    A_LAST
+  };
+
+bool quit=false;
+
+void doExit(int,const void*)
+{
+  quit = true;
+}
+
+void goLeft(int,const void*)
+{
+  cout << "Going left\n";
+}
+
+int main(int argc, char** argv)
+{
+  SDL_Init(SDL_INIT_VIDEO);
+  SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
+  SDLDriver input;
+  Dispatcher *disp = new Dispatcher(A_LAST);
+  Poller poll(input);
+
+  input.setEvent(EventPtr(disp));
+
+  disp->funcs.bind(A_Quit, &doExit);
+  disp->funcs.bind(A_Left, &goLeft);
+
+  disp->bind(A_Quit, SDLK_q);
+  disp->bind(A_Left, SDLK_a);
+  disp->bind(A_Left, SDLK_LEFT);
+
+  poll.bind(A_Right, SDLK_d);
+  poll.bind(A_Right, SDLK_RIGHT);
+
+  cout << "Hold the Q key to quit:\n";
+  //input->setEvent(&mycb);
+  while(!quit)
+    {
+      input.capture();
+      if(poll.isDown(A_Right))
+        cout << "We're going right!\n";
+      SDL_Delay(20);
+
+      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;
+        }
+    }
+  cout << "\nBye bye!\n";
+
+  SDL_Quit();
+  return 0;
+}
diff --git a/libs/openengine/input/tests/sdl_driver_test.cpp b/libs/openengine/input/tests/sdl_driver_test.cpp
new file mode 100644
index 0000000000..1771bcfe40
--- /dev/null
+++ b/libs/openengine/input/tests/sdl_driver_test.cpp
@@ -0,0 +1,31 @@
+#include <iostream>
+#include <mangle/input/servers/sdl_driver.hpp>
+#include <SDL.h>
+
+using namespace std;
+using namespace Mangle::Input;
+
+int main(int argc, char** argv)
+{
+  SDL_Init(SDL_INIT_VIDEO);
+  SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
+  SDLDriver input;
+
+  cout << "Hold the Q key to quit:\n";
+  //input->setEvent(&mycb);
+  while(!input.isDown(SDLK_q))
+    {
+      input.capture();
+      SDL_Delay(20);
+
+      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;
+        }
+    }
+  cout << "\nBye bye!\n";
+
+  SDL_Quit();
+  return 0;
+}
diff --git a/libs/openengine/input/tests/test.sh b/libs/openengine/input/tests/test.sh
new file mode 100755
index 0000000000..2d07708adc
--- /dev/null
+++ b/libs/openengine/input/tests/test.sh
@@ -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
diff --git a/libs/openengine/misc/list.hpp b/libs/openengine/misc/list.hpp
new file mode 100644
index 0000000000..b08b57494d
--- /dev/null
+++ b/libs/openengine/misc/list.hpp
@@ -0,0 +1,178 @@
+#ifndef MISC_LIST_H
+#define MISC_LIST_H
+
+#include <assert.h>
+
+namespace Misc{
+
+/*
+  A simple and completely allocation-less doubly linked list. The
+  class only manages pointers to and between elements. It leaving all
+  memory management to the user.
+*/
+template <typename Elem>
+struct List
+{
+  List() : head(0), tail(0), totalNum(0) {}
+
+  // Empty the list.
+  void reset()
+  {
+    head = 0;
+    tail = 0;
+    totalNum = 0;
+  }
+
+  // Insert an element at the end of the list. The element cannot be
+  // part of any other list when this is called.
+  void insert(Elem *p)
+  {
+    if(tail)
+      {
+        // There are existing elements. Insert the node at the end of
+        // the list.
+        assert(head && totalNum > 0);
+        tail->next = p;
+      }
+    else
+      {
+        // This is the first element
+        assert(head == 0 && totalNum == 0);
+        head = p;
+      }
+
+    // These have to be done in either case
+    p->prev = tail;
+    p->next = 0;
+    tail = p;
+
+    totalNum++;
+  }
+
+  // Remove element from the list. The element MUST be part of the
+  // list when this is called.
+  void remove(Elem *p)
+  {
+    assert(totalNum > 0);
+
+    if(p->next)
+      {
+        // There's an element following us. Set it up correctly.
+        p->next->prev = p->prev;
+        assert(tail && tail != p);
+      }
+    else
+      {
+        // We're the tail
+        assert(tail == p);
+        tail = p->prev;
+      }
+
+    // Now do exactly the same for the previous element
+    if(p->prev)
+      {
+        p->prev->next = p->next;
+        assert(head && head != p);
+      }
+    else
+      {
+        assert(head == p);
+        head = p->next;
+      }
+
+    totalNum--;
+  }
+
+  // Pop the first element off the list
+  Elem *pop()
+  {
+    Elem *res = getHead();
+    if(res) remove(res);
+    return res;
+  }
+
+  // Swap the contents of this list with another of the same type
+  void swap(List &other)
+  {
+    Elem *tmp;
+
+    tmp = head;
+    head = other.head;
+    other.head = tmp;
+
+    tmp = tail;
+    tail = other.tail;
+    other.tail = tmp;
+
+    unsigned int tmp2 = totalNum;
+    totalNum = other.totalNum;
+    other.totalNum = tmp2;
+  }
+
+  /* Absorb the contents of another list. All the elements from the
+     list are moved to the end of this list, and the other list is
+     cleared.
+   */
+  void absorb(List &other)
+  {
+    assert(&other != this);
+    if(other.totalNum)
+      {
+        absorb(other.head, other.tail, other.totalNum);
+        other.reset();
+      }
+    assert(other.totalNum == 0);
+  }
+
+  /* Absorb a range of elements, endpoints included. The elements are
+     assumed NOT to belong to any list, but they ARE assumed to be
+     connected with a chain between them.
+
+     The connection MUST run all the way from 'first' to 'last'
+     through the ->next pointers, and vice versa through ->prev
+     pointers.
+
+     The parameter 'num' must give the exact number of elements in the
+     chain.
+
+     Passing first == last, num == 1 is allowed and is equivalent to
+     calling insert().
+  */
+  void absorb(Elem* first, Elem *last, int num)
+  {
+    assert(first && last && num>=1);
+    if(tail)
+      {
+        // There are existing elements. Insert the first node at the
+        // end of the list.
+        assert(head && totalNum > 0);
+        tail->next = first;
+      }
+    else
+      {
+        // This is the first element
+        assert(head == 0 && totalNum == 0);
+        head = first;
+      }
+
+    // These have to be done in either case
+    first->prev = tail;
+    last->next = 0;
+    tail = last;
+
+    totalNum += num;
+  }
+
+  Elem* getHead() const { return head; }
+  Elem* getTail() const { return tail; }
+  unsigned int getNum() const { return totalNum; }
+
+private:
+
+  Elem *head;
+  Elem *tail;
+  unsigned int totalNum;
+};
+
+}
+#endif
diff --git a/libs/openengine/ogre/.gitignore b/libs/openengine/ogre/.gitignore
new file mode 100644
index 0000000000..3367afdbbf
--- /dev/null
+++ b/libs/openengine/ogre/.gitignore
@@ -0,0 +1 @@
+old
diff --git a/libs/openengine/ogre/exitlistener.hpp b/libs/openengine/ogre/exitlistener.hpp
new file mode 100644
index 0000000000..5a9d1ff68f
--- /dev/null
+++ b/libs/openengine/ogre/exitlistener.hpp
@@ -0,0 +1,37 @@
+#ifndef OENGINE_OGRE_EXITLISTEN_H
+#define OENGINE_OGRE_EXITLISTEN_H
+
+/*
+  This FrameListener simply exits the rendering loop when the window
+  is closed. You can also tell it to exit manually by setting the exit
+  member to true;
+ */
+
+#include <OgreFrameListener.h>
+#include <OgreRenderWindow.h>
+
+namespace OEngine {
+namespace Render
+{
+  struct ExitListener : Ogre::FrameListener
+  {
+    Ogre::RenderWindow *window;
+    bool exit;
+
+    ExitListener(Ogre::RenderWindow *wnd)
+      : window(wnd), exit(false) {}
+
+    bool frameStarted(const Ogre::FrameEvent &evt)
+    {
+      if(window->isClosed())
+        exit = true;
+
+      return !exit;
+    }
+
+    // Function equivalent of setting exit=true. Handy when you need a
+    // delegate to bind to an event.
+    void exitNow() { exit = true; }
+  };
+}}
+#endif
diff --git a/libs/openengine/ogre/fader.cpp b/libs/openengine/ogre/fader.cpp
new file mode 100644
index 0000000000..062559e000
--- /dev/null
+++ b/libs/openengine/ogre/fader.cpp
@@ -0,0 +1,136 @@
+#include "fader.hpp"
+
+#include <OgreOverlayManager.h>
+#include <OgreOverlayContainer.h>
+#include <OgreOverlay.h>
+#include <OgreMaterial.h>
+#include <OgreTechnique.h>
+#include <OgreMaterialManager.h>
+#include <OgreResourceGroupManager.h>
+
+#include <assert.h>
+
+#define FADE_OVERLAY_NAME       "FadeInOutOverlay"
+#define FADE_OVERLAY_PANEL_NAME "FadeInOutOverlayPanel"
+#define FADE_MATERIAL_NAME      "FadeInOutMaterial"
+
+using namespace Ogre;
+using namespace OEngine::Render;
+
+Fader::Fader() : 
+    mMode(FadingMode_In),
+    mRemainingTime(0.f),
+    mTargetTime(0.f),
+    mTargetAlpha(0.f),
+    mCurrentAlpha(0.f),
+    mStartAlpha(0.f)
+{
+
+    // Create the fading material
+    MaterialPtr material = MaterialManager::getSingleton().create( FADE_MATERIAL_NAME, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME );
+    Pass* pass = material->getTechnique(0)->getPass(0);
+    pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
+    mFadeTextureUnit = pass->createTextureUnitState();
+    mFadeTextureUnit->setColourOperationEx(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, ColourValue(0.f, 0.f, 0.f)); // always black colour    
+
+    // Create the overlay
+    OverlayManager& ovm = OverlayManager::getSingleton();
+
+    mOverlay = ovm.create( FADE_OVERLAY_NAME );
+    
+    OverlayContainer* overlay_panel;
+    overlay_panel = (OverlayContainer*)ovm.createOverlayElement("Panel", FADE_OVERLAY_PANEL_NAME);
+    
+    // position it over the whole screen
+    overlay_panel->_setPosition(0, 0);
+    overlay_panel->_setDimensions(1, 1);
+    
+    overlay_panel->setMaterialName( FADE_MATERIAL_NAME );
+    overlay_panel->show();
+    mOverlay->add2D(overlay_panel);
+    mOverlay->hide();
+}
+
+void Fader::update(float dt)
+{    
+    if (mRemainingTime > 0)
+    {
+        if (mMode == FadingMode_In)
+        {
+            mCurrentAlpha -= dt/mTargetTime * (mStartAlpha-mTargetAlpha);
+            if (mCurrentAlpha < mTargetAlpha) mCurrentAlpha = mTargetAlpha;
+        }
+        else if (mMode == FadingMode_Out)
+        {
+            mCurrentAlpha += dt/mTargetTime * (mTargetAlpha-mStartAlpha);
+            if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha;
+        }
+        
+        applyAlpha();
+        
+        mRemainingTime -= dt;
+    }
+    
+   if (mCurrentAlpha == 0.f) mOverlay->hide();
+}
+
+void Fader::applyAlpha()
+{
+    mOverlay->show();
+    mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, mCurrentAlpha);
+}
+
+void Fader::fadeIn(float time)
+{
+    if (time<0.f) return;
+    if (time==0.f)
+    {
+        mCurrentAlpha = 0.f;
+        applyAlpha();
+        return;
+    }
+        
+    mStartAlpha = mCurrentAlpha;
+    mTargetAlpha = 0.f;
+    mMode = FadingMode_In;
+    mTargetTime = time;
+    mRemainingTime = time;
+}
+
+void Fader::fadeOut(const float time)
+{
+    if (time<0.f) return;
+    if (time==0.f)
+    {
+        mCurrentAlpha = 1.f;
+        applyAlpha();
+        return;
+    }
+        
+    mStartAlpha = mCurrentAlpha;
+    mTargetAlpha = 1.f;
+    mMode = FadingMode_Out;
+    mTargetTime = time;
+    mRemainingTime = time;
+}
+
+void Fader::fadeTo(const int percent, const float time)
+{
+    if (time<0.f) return;
+    if (time==0.f)
+    {
+        mCurrentAlpha = percent/100.f;
+        applyAlpha();
+        return;
+    }
+    
+    mStartAlpha = mCurrentAlpha;
+    mTargetAlpha = percent/100.f;
+    
+    if (mTargetAlpha == mStartAlpha) return;
+    else if (mTargetAlpha > mStartAlpha) mMode = FadingMode_Out;
+    else mMode = FadingMode_In;
+    
+    mTargetTime = time;
+    mRemainingTime = time;
+}
diff --git a/libs/openengine/ogre/fader.hpp b/libs/openengine/ogre/fader.hpp
new file mode 100644
index 0000000000..f76ac51ef9
--- /dev/null
+++ b/libs/openengine/ogre/fader.hpp
@@ -0,0 +1,57 @@
+#ifndef OENGINE_OGRE_FADE_H
+#define OENGINE_OGRE_FADE_H
+
+/*
+  A class that handles fading in the screen from black or fading it out to black.
+  
+  To achieve this, it uses a full-screen Ogre::Overlay
+  
+  inspired by http://www.ogre3d.org/tikiwiki/FadeEffectOverlay (heavily adjusted)
+ */
+ 
+#include <OgreFrameListener.h>
+
+namespace Ogre
+{
+    class TextureUnitState;
+    class Overlay;
+}
+
+namespace OEngine {
+namespace Render
+{
+  class Fader
+  {
+  public:
+    Fader();
+    
+    void update(float dt);
+    
+    void fadeIn(const float time);
+    void fadeOut(const float time);
+    void fadeTo(const int percent, const float time);
+        
+  private:
+    enum FadingMode
+    {
+        FadingMode_In,
+        FadingMode_Out
+    };
+    
+    void applyAlpha();
+  
+    Ogre::TextureUnitState* mFadeTextureUnit;
+    Ogre::Overlay* mOverlay;
+    
+    FadingMode mMode;
+    
+    float mRemainingTime;
+    float mTargetTime;
+    float mTargetAlpha;
+    float mCurrentAlpha;
+    float mStartAlpha;
+    
+  protected:
+  };
+}}
+#endif
diff --git a/libs/openengine/ogre/mouselook.cpp b/libs/openengine/ogre/mouselook.cpp
new file mode 100644
index 0000000000..841bab603a
--- /dev/null
+++ b/libs/openengine/ogre/mouselook.cpp
@@ -0,0 +1,57 @@
+#include "mouselook.hpp"
+
+#include <OIS/OIS.h>
+#include <OgreCamera.h>
+#include <OgreSceneNode.h>
+
+using namespace OIS;
+using namespace Ogre;
+using namespace OEngine::Render;
+
+void MouseLookEvent::event(Type type, int index, const void *p)
+{
+    if(type != EV_MouseMove || camera == NULL) return;
+
+    MouseEvent *arg = (MouseEvent*)(p);
+
+    float x = arg->state.X.rel * sensX;
+    float y = arg->state.Y.rel * sensY;
+
+    camera->getParentSceneNode()->getParentSceneNode()->yaw(Degree(-x));
+    camera->getParentSceneNode()->pitch(Degree(-y));
+    if(flipProt)
+    {
+        // The camera before pitching
+        /*Quaternion nopitch = camera->getParentSceneNode()->getOrientation();
+
+          camera->getParentSceneNode()->pitch(Degree(-y));
+
+        // Apply some failsafe measures against the camera flipping
+        // upside down. Is the camera close to pointing straight up or
+        // down?
+        if(Ogre::Vector3(camera->getParentSceneNode()->getOrientation()*Ogre::Vector3::UNIT_Y)[1] <= 0.1)
+        // If so, undo the last pitch
+        camera->getParentSceneNode()->setOrientation(nopitch);*/
+        //camera->getU
+
+        // Angle of rotation around the X-axis.
+        float pitchAngle = (2 * Ogre::Degree(Ogre::Math::ACos(camera->getParentSceneNode()->getOrientation().w)).valueDegrees());
+
+        // Just to determine the sign of the angle we pick up above, the
+        // value itself does not interest us.
+        float pitchAngleSign = camera->getParentSceneNode()->getOrientation().x;
+
+        // Limit the pitch between -90 degress and +90 degrees, Quake3-style.
+        if (pitchAngle > 90.0f)
+        {
+            if (pitchAngleSign > 0)
+                // Set orientation to 90 degrees on X-axis.
+                camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f),
+                            Ogre::Math::Sqrt(0.5f), 0, 0));
+            else if (pitchAngleSign < 0)
+                // Sets orientation to -90 degrees on X-axis.
+                camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f),
+                            -Ogre::Math::Sqrt(0.5f), 0, 0));
+        }
+    }
+}
diff --git a/libs/openengine/ogre/mouselook.hpp b/libs/openengine/ogre/mouselook.hpp
new file mode 100644
index 0000000000..6e09ff4a10
--- /dev/null
+++ b/libs/openengine/ogre/mouselook.hpp
@@ -0,0 +1,56 @@
+#ifndef OENGINE_OGRE_MOUSELOOK_H
+#define OENGINE_OGRE_MOUSELOOK_H
+
+/*
+  A mouse-look class for Ogre. Accepts input events from Mangle::Input
+  and translates them.
+
+  You can adjust the mouse sensibility and switch to a different
+  camera. The mouselook class also has an optional wrap protection
+  that keeps the camera from flipping upside down.
+
+  You can disable the mouse looker at any time by calling
+  setCamera(NULL), and reenable it by setting the camera back.
+
+  NOTE: The current implementation will ONLY work for native OIS
+  events.
+ */
+
+#include <mangle/input/event.hpp>
+
+namespace Ogre
+{
+    class Camera;
+}
+
+namespace OEngine {
+namespace Render
+{
+  class MouseLookEvent : public Mangle::Input::Event
+  {
+    Ogre::Camera* camera;
+    float sensX, sensY; // Mouse sensibility
+    bool flipProt;      // Flip protection
+
+  public:
+    MouseLookEvent(Ogre::Camera *cam=NULL,
+                   float sX=0.2, float sY=0.2,
+                   bool prot=true)
+      : camera(cam)
+      , sensX(sX)
+      , sensY(sY)
+      , flipProt(prot)
+    {}
+
+    void setCamera(Ogre::Camera *cam)
+    { camera = cam; }
+    void setSens(float sX, float sY)
+    { sensX = sX; sensY = sY; }
+    void setProt(bool p) { flipProt = p; }
+
+    void event(Type type, int index, const void *p);
+  };
+
+  typedef boost::shared_ptr<MouseLookEvent> MouseLookEventPtr;
+}}
+#endif
diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp
new file mode 100644
index 0000000000..4ded3343f5
--- /dev/null
+++ b/libs/openengine/ogre/renderer.cpp
@@ -0,0 +1,148 @@
+#include "renderer.hpp"
+#include "fader.hpp"
+
+#include "OgreRoot.h"
+#include "OgreRenderWindow.h"
+#include "OgreLogManager.h"
+#include "OgreLog.h"
+
+#include <assert.h>
+
+using namespace Ogre;
+using namespace OEngine::Render;
+
+void OgreRenderer::cleanup()
+{
+  if (mFader)
+    delete mFader;
+  
+  if(mRoot)
+    delete mRoot;
+  mRoot = NULL;
+}
+
+void OgreRenderer::start()
+{
+  mRoot->startRendering();
+}
+
+bool OgreRenderer::loadPlugins()
+{
+    #ifdef ENABLE_PLUGIN_GL
+    mGLPlugin = new Ogre::GLPlugin();
+    mRoot->installPlugin(mGLPlugin);
+    #endif
+    #ifdef ENABLE_PLUGIN_Direct3D9
+    mD3D9Plugin = new Ogre::D3D9Plugin();
+    mRoot->installPlugin(mD3D9Plugin);
+    #endif
+    #ifdef ENABLE_PLUGIN_CgProgramManager
+    mCgPlugin = new Ogre::CgPlugin();
+    mRoot->installPlugin(mCgPlugin);
+    #endif
+    #ifdef ENABLE_PLUGIN_OctreeSceneManager
+    mOctreePlugin = new Ogre::OctreePlugin();
+    mRoot->installPlugin(mOctreePlugin);
+    #endif
+    #ifdef ENABLE_PLUGIN_ParticleFX
+    mParticleFXPlugin = new Ogre::ParticleFXPlugin();
+    mRoot->installPlugin(mParticleFXPlugin);
+    #endif
+    return true;
+}
+
+void OgreRenderer::update(float dt)
+{
+  mFader->update(dt);
+}
+
+void OgreRenderer::screenshot(const std::string &file)
+{
+  mWindow->writeContentsToFile(file);
+}
+
+float OgreRenderer::getFPS()
+{
+  return mWindow->getLastFPS();
+}
+
+bool OgreRenderer::configure(bool showConfig,
+                             const std::string &cfgPath,
+                             const std::string &logPath,
+                             const std::string &pluginCfg,
+                             bool _logging)
+{
+  // Set up logging first
+  new LogManager;
+  Log *log = LogManager::getSingleton().createLog(logPath + std::string("Ogre.log"));
+  logging = _logging;
+
+  if(logging)
+    // Full log detail
+    log->setLogDetail(LL_BOREME);
+  else
+    // Disable logging
+    log->setDebugOutputEnabled(false);
+
+#if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) || defined(ENABLE_PLUGIN_CgProgramManager) || defined(ENABLE_PLUGIN_OctreeSceneManager) || defined(ENABLE_PLUGIN_ParticleFX)
+  mRoot = new Root("", cfgPath, "");
+  loadPlugins();
+#else
+  mRoot = new Root(pluginCfg, cfgPath, "");
+#endif
+
+  // Show the configuration dialog and initialise the system, if the
+  // showConfig parameter is specified. The settings are stored in
+  // ogre.cfg. If showConfig is false, the settings are assumed to
+  // already exist in ogre.cfg.
+  int result;
+  if(showConfig)
+    result = mRoot->showConfigDialog();
+  else
+    result = mRoot->restoreConfig();
+
+  return !result;
+}
+
+bool OgreRenderer::configure(bool showConfig,
+                             const std::string &cfgPath,
+                             const std::string &pluginCfg,
+                             bool _logging)
+{
+    return configure(showConfig, cfgPath, cfgPath, pluginCfg, _logging);
+}
+
+bool OgreRenderer::configure(bool showConfig,
+                             const std::string &pluginCfg,
+                             bool _logging)
+{
+    return configure(showConfig, "", pluginCfg, _logging);
+}
+
+void OgreRenderer::createWindow(const std::string &title)
+{
+  assert(mRoot);
+  // Initialize OGRE window
+  mWindow = mRoot->initialise(true, title, "");
+}
+
+void OgreRenderer::createScene(const std::string camName, float fov, float nearClip)
+{
+  assert(mRoot);
+  assert(mWindow);
+  // Get the SceneManager, in this case a generic one
+  mScene = mRoot->createSceneManager(ST_GENERIC);
+
+  // Create the camera
+  mCamera = mScene->createCamera(camName);
+  mCamera->setNearClipDistance(nearClip);
+  mCamera->setFOVy(Degree(fov));
+
+  // Create one viewport, entire window
+  mView = mWindow->addViewport(mCamera);
+
+  // Alter the camera aspect ratio to match the viewport
+  mCamera->setAspectRatio(Real(mView->getActualWidth()) / Real(mView->getActualHeight()));
+  
+  mFader = new Fader();
+}
diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp
new file mode 100644
index 0000000000..6516b0a80c
--- /dev/null
+++ b/libs/openengine/ogre/renderer.hpp
@@ -0,0 +1,132 @@
+#ifndef OENGINE_OGRE_RENDERER_H
+#define OENGINE_OGRE_RENDERER_H
+
+/*
+  Ogre renderer class
+ */
+
+#include <string>
+
+// Static plugin headers
+#ifdef ENABLE_PLUGIN_CgProgramManager
+# include "OgreCgPlugin.h"
+#endif
+#ifdef ENABLE_PLUGIN_OctreeSceneManager
+# include "OgreOctreePlugin.h"
+#endif
+#ifdef ENABLE_PLUGIN_ParticleFX
+# include "OgreParticleFXPlugin.h"
+#endif
+#ifdef ENABLE_PLUGIN_GL
+# include "OgreGLPlugin.h"
+#endif
+#ifdef ENABLE_PLUGIN_Direct3D9
+# include "OgreD3D9Plugin.h"
+#endif
+
+namespace Ogre
+{
+    class Root;
+    class RenderWindow;
+    class SceneManager;
+    class Camera;
+    class Viewport;
+}
+
+namespace OEngine {
+namespace Render
+{
+  class Fader;
+  class OgreRenderer
+  {
+    Ogre::Root *mRoot;
+    Ogre::RenderWindow *mWindow;
+    Ogre::SceneManager *mScene;
+    Ogre::Camera *mCamera;
+    Ogre::Viewport *mView;
+	#ifdef ENABLE_PLUGIN_CgProgramManager
+ 	Ogre::CgPlugin* mCgPlugin;
+ 	#endif
+ 	#ifdef ENABLE_PLUGIN_OctreeSceneManager
+ 	Ogre::OctreePlugin* mOctreePlugin;
+ 	#endif
+ 	#ifdef ENABLE_PLUGIN_ParticleFX
+ 	Ogre::ParticleFXPlugin* mParticleFXPlugin;
+ 	#endif
+ 	#ifdef ENABLE_PLUGIN_GL
+ 	Ogre::GLPlugin* mGLPlugin;
+ 	#endif
+	#ifdef ENABLE_PLUGIN_Direct3D9
+ 	Ogre::D3D9Plugin* mD3D9Plugin;
+ 	#endif
+    Fader* mFader;
+    bool logging;
+
+  public:
+    OgreRenderer()
+      : mRoot(NULL), mWindow(NULL), mScene(NULL), mFader(NULL) {}
+    ~OgreRenderer() { cleanup(); }
+
+    /** Configure the renderer. This will load configuration files and
+        set up the Root and logging classes. */
+    bool configure(bool showConfig,     // Show config dialog box?
+                   const std::string &cfgPath, // Path to directory where to store config files
+                   const std::string &logPath, // Path to directory where to store log files
+                   const std::string &pluginCfg, // plugin.cfg file
+                   bool _logging);      // Enable or disable logging
+
+    bool configure(bool showConfig,     // Show config dialog box?
+                   const std::string &cfgPath, // Path to directory where to store config files
+                   const std::string &pluginCfg, // plugin.cfg file
+                   bool _logging);      // Enable or disable logging
+
+    /** Configure the renderer. This will load configuration files and
+        set up the Root and logging classes. */
+    bool configure(bool showConfig,     // Show config dialog box?
+                   const std::string &pluginCfg, // plugin.cfg file
+                   bool _logging);      // Enable or disable logging
+
+    /// Create a window with the given title
+    void createWindow(const std::string &title);
+
+    /// Set up the scene manager, camera and viewport
+    void createScene(const std::string camName="Camera",// Camera name
+                     float fov=55,                      // Field of view angle
+                     float nearClip=5                   // Near clip distance
+                     );
+
+    /// Kill the renderer.
+    void cleanup();
+
+    /// Start the main rendering loop
+    void start();
+
+    bool loadPlugins();
+    
+    void update(float dt);
+
+    /// Write a screenshot to file
+    void screenshot(const std::string &file);
+
+    float getFPS();
+
+    /// Get the Root
+    Ogre::Root *getRoot() { return mRoot; }
+
+    /// Get the rendering window
+    Ogre::RenderWindow *getWindow() { return mWindow; }
+
+    /// Get the scene manager
+    Ogre::SceneManager *getScene() { return mScene; }
+    
+    /// Get the screen colour fader
+    Fader *getFader() { return mFader; }
+
+    /// Camera
+    Ogre::Camera *getCamera() { return mCamera; }
+
+    /// Viewport
+    Ogre::Viewport *getViewport() { return mView; }
+  };
+}}
+#endif
diff --git a/libs/openengine/sound/sndmanager.cpp b/libs/openengine/sound/sndmanager.cpp
new file mode 100644
index 0000000000..02c6ba1e77
--- /dev/null
+++ b/libs/openengine/sound/sndmanager.cpp
@@ -0,0 +1,219 @@
+#include "sndmanager.hpp"
+
+#include "../misc/list.hpp"
+#include <boost/weak_ptr.hpp>
+
+using namespace OEngine::Sound;
+using namespace Mangle::Sound;
+
+/** This is our own internal implementation of the
+    Mangle::Sound::Sound interface. This class links a SoundPtr to
+    itself and prevents itself from being deleted as long as the sound
+    is playing.
+ */
+struct OEngine::Sound::ManagedSound : SoundFilter
+{
+private:
+  /** Who's your daddy? This is set if and only if we are listed
+      internally in the given SoundManager.
+
+      It may be NULL if the manager has been deleted but the user
+      keeps their own SoundPtrs to the object.
+  */
+  SoundManager *mgr;
+
+  /** Keep a weak pointer to ourselves, which we convert into a
+      'strong' pointer when we are playing. When 'self' is pointing to
+      ourselves, the object will never be deleted.
+
+      This is used to make sure the sound is not deleted while
+      playing, unless it is explicitly ordered to do so by the
+      manager.
+
+      TODO: This kind of construct is useful. If we need it elsewhere
+      later, template it. It would be generally useful in any system
+      where we poll to check if a resource is still needed, but where
+      manual references are allowed.
+  */
+  WSoundPtr weak;
+  SoundPtr self;
+
+  // Keep this object from being deleted
+  void lock()
+  {
+    self = SoundPtr(weak);
+  }
+
+  // Release the lock. This may or may not delete the object. Never do
+  // anything after calling unlock()!
+  void unlock()
+  {
+    self.reset();
+  }
+
+public:
+  // Used for putting ourselves in linked lists
+  ManagedSound *next, *prev;
+
+  /** Detach this sound from its manager. This means that the manager
+      will no longer know we exist. Typically only called when either
+      the sound or the manager is about to get deleted.
+
+      Since this means update() will no longer be called, we also have
+      to unlock the sound manually since it will no longer be able to
+      do that itself. This means that the sound may be deleted, even
+      if it is still playing, when the manager is deleted.
+
+      However, you are still allowed to keep and manage your own
+      SoundPtr references, but the lock/unlock system is disabled
+      after the manager is gone.
+  */
+  void detach()
+  {
+    if(mgr)
+      {
+        mgr->detach(this);
+        mgr = NULL;
+      }
+
+    // Unlock must be last command. Object may get deleted at this
+    // point.
+    unlock();
+  }
+
+  ManagedSound(SoundPtr snd, SoundManager *mg)
+    : SoundFilter(snd), mgr(mg)
+  {}
+  ~ManagedSound() { detach(); }
+
+  // Needed to set up the weak pointer
+  void setup(SoundPtr self)
+  {
+    weak = WSoundPtr(self);
+  }
+
+  // Override play() to mark the object as locked
+  void play()
+  {
+    SoundFilter::play();
+
+    // Lock the object so that it is not deleted while playing. Only
+    // do this if we have a manager, otherwise the object will never
+    // get unlocked.
+    if(mgr) lock();
+  }
+
+  // Called regularly by the manager
+  void update()
+  {
+    // If we're no longer playing, don't force object retention.
+    if(!isPlaying())
+      unlock();
+
+    // unlock() may delete the object, so don't do anything below this
+    // point.
+  }
+
+  SoundPtr clone()
+  {
+    // Cloning only works when we have a manager.
+    assert(mgr);
+    return mgr->wrap(client->clone());
+  }
+};
+
+struct SoundManager::SoundManagerList
+{
+private:
+  // A linked list of ManagedSound objects.
+  typedef Misc::List<ManagedSound> SoundList;
+  SoundList list;
+
+public:
+  // Add a new sound to the list
+  void addNew(ManagedSound* snd)
+  {
+    list.insert(snd);
+  }
+
+  // Remove a sound from the list
+  void remove(ManagedSound *snd)
+  {
+    list.remove(snd);
+  }
+
+  // Number of sounds in the list
+  int numSounds() { return list.getNum(); }
+
+  // Update all sounds
+  void updateAll()
+  {
+    ManagedSound *s = list.getHead();
+    while(s)
+      {
+        ManagedSound *cur = s;
+        // Propagate first, since update() may delete object
+        s = s->next;
+        cur->update();
+      }
+  }
+
+  // Detach and unlock all sounds
+  void detachAll()
+  {
+    ManagedSound *s = list.getHead();
+    while(s)
+      {
+        ManagedSound *cur = s;
+        s = s->next;
+        cur->detach();
+      }
+  }
+};
+
+SoundManager::SoundManager(SoundFactoryPtr fact)
+  : FactoryFilter(fact)
+{
+  needsUpdate = true;
+  list = new SoundManagerList;
+}
+
+SoundManager::~SoundManager()
+{
+  // Detach all sounds
+  list->detachAll();
+}
+
+SoundPtr SoundManager::wrap(SoundPtr client)
+{
+  // Create and set up the sound wrapper
+  ManagedSound *snd = new ManagedSound(client,this);
+  SoundPtr ptr(snd);
+  snd->setup(ptr);
+
+  // Add ourselves to the list of all sounds
+  list->addNew(snd);
+
+  return ptr;
+}
+
+// Remove the sound from this manager.
+void SoundManager::detach(ManagedSound *sound)
+{
+  list->remove(sound);
+}
+
+int SoundManager::numSounds()
+{
+  return list->numSounds();
+}
+
+void SoundManager::update()
+{
+  // Update all the sounds we own
+  list->updateAll();
+
+  // Update the source if it needs it
+  if(client->needsUpdate)
+    client->update();
+}
diff --git a/libs/openengine/sound/sndmanager.hpp b/libs/openengine/sound/sndmanager.hpp
new file mode 100644
index 0000000000..5ea0c4fc37
--- /dev/null
+++ b/libs/openengine/sound/sndmanager.hpp
@@ -0,0 +1,93 @@
+#ifndef OENGINE_SOUND_MANAGER_H
+#define OENGINE_SOUND_MANAGER_H
+
+#include <mangle/sound/filters/pure_filter.hpp>
+
+namespace OEngine
+{
+  namespace Sound
+  {
+    using namespace Mangle::Sound;
+
+    class ManagedSound;
+
+    /** A manager of Mangle::Sounds.
+
+        The sound manager is a wrapper around the more low-level
+        SoundFactory - although it is also itself an implementation of
+        SoundFactory. It will:
+        - keep a list of all created sounds
+        - let you iterate the list
+        - keep references to playing sounds so you don't have to
+        - auto-release references to sounds that are finished playing
+          (ie. deleting them if you're not referencing them)
+     */
+    class SoundManager : public FactoryFilter
+    {
+      // Shove the implementation details into the cpp file.
+      struct SoundManagerList;
+      SoundManagerList *list;
+
+      // Create a new sound wrapper based on the given source sound.
+      SoundPtr wrap(SoundPtr snd);
+
+      /** Internal function. Will completely disconnect the given
+          sound from this manager. Called from ManagedSound.
+       */
+      friend class ManagedSound;
+      void detach(ManagedSound *sound);
+    public:
+      SoundManager(SoundFactoryPtr fact);
+      ~SoundManager();
+      void update();
+
+      /// Get number of sounds currently managed by this manager.
+      int numSounds();
+
+      SoundPtr loadRaw(SampleSourcePtr input)
+      { return wrap(client->loadRaw(input)); }
+
+      SoundPtr load(Mangle::Stream::StreamPtr input)
+      { return wrap(client->load(input)); }
+
+      SoundPtr load(const std::string &file)
+      { return wrap(client->load(file)); }
+
+      // Play a sound immediately, and release when done unless you
+      // keep the returned SoundPtr.
+      SoundPtr play(Mangle::Stream::StreamPtr sound)
+      {
+        SoundPtr snd = load(sound);
+        snd->play();
+        return snd;
+      }
+
+      SoundPtr play(const std::string &sound)
+      {
+        SoundPtr snd = load(sound);
+        snd->play();
+        return snd;
+      }
+
+      // Ditto for 3D sounds
+      SoundPtr play3D(Mangle::Stream::StreamPtr sound, float x, float y, float z)
+      {
+        SoundPtr snd = load(sound);
+        snd->setPos(x,y,z);
+        snd->play();
+        return snd;
+      }
+
+      SoundPtr play3D(const std::string &sound, float x, float y, float z)
+      {
+        SoundPtr snd = load(sound);
+        snd->setPos(x,y,z);
+        snd->play();
+        return snd;
+      }
+    };
+
+    typedef boost::shared_ptr<SoundManager> SoundManagerPtr;
+  }
+}
+#endif
diff --git a/libs/openengine/sound/tests/Makefile b/libs/openengine/sound/tests/Makefile
new file mode 100644
index 0000000000..04952167f7
--- /dev/null
+++ b/libs/openengine/sound/tests/Makefile
@@ -0,0 +1,16 @@
+GCC=g++ -I../
+
+all: sound_manager_test sound_3d_test
+
+L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat)
+L_OPENAL=$(shell pkg-config --libs openal)
+L_AUDIERE=-laudiere
+
+sound_manager_test: sound_manager_test.cpp ../../mangle/sound/sources/audiere_source.cpp ../../mangle/sound/outputs/openal_out.cpp ../../mangle/stream/clients/audiere_file.cpp ../sndmanager.cpp
+	$(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -I../..
+
+sound_3d_test: sound_3d_test.cpp ../../mangle/sound/sources/audiere_source.cpp ../../mangle/sound/outputs/openal_out.cpp ../../mangle/stream/clients/audiere_file.cpp ../sndmanager.cpp
+	$(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -I../..
+
+clean:
+	rm *_test
diff --git a/libs/openengine/sound/tests/output/sound_3d_test.out b/libs/openengine/sound/tests/output/sound_3d_test.out
new file mode 100644
index 0000000000..a443c84f02
--- /dev/null
+++ b/libs/openengine/sound/tests/output/sound_3d_test.out
@@ -0,0 +1,3 @@
+Playing at 0,0,0
+Playing at 1,1,0
+Playing at -1,0,0
diff --git a/libs/openengine/sound/tests/output/sound_manager_test.out b/libs/openengine/sound/tests/output/sound_manager_test.out
new file mode 100644
index 0000000000..2b458493d2
--- /dev/null
+++ b/libs/openengine/sound/tests/output/sound_manager_test.out
@@ -0,0 +1,5 @@
+Playing ../../mangle/sound/tests/cow.wav
+Replaying
+pause
+restart
+Done playing.
diff --git a/libs/openengine/sound/tests/sound_3d_test.cpp b/libs/openengine/sound/tests/sound_3d_test.cpp
new file mode 100644
index 0000000000..f5b197fd0b
--- /dev/null
+++ b/libs/openengine/sound/tests/sound_3d_test.cpp
@@ -0,0 +1,46 @@
+#include <iostream>
+#include <exception>
+#include <assert.h>
+
+#include <mangle/stream/servers/file_stream.hpp>
+#include <mangle/sound/filters/openal_audiere.hpp>
+
+#include <sound/sndmanager.hpp>
+
+using namespace std;
+using namespace Mangle::Stream;
+using namespace Mangle::Sound;
+using namespace OEngine::Sound;
+
+const std::string sound = "../../mangle/sound/tests/cow.wav";
+
+SoundManagerPtr m;
+
+// Play and wait for finish
+void play(float x, float y, float z)
+{
+  cout << "Playing at " << x << "," << y << "," << z << endl;
+
+  SoundPtr snd = m->play3D(sound,x,y,z);
+
+  while(snd->isPlaying())
+    {
+      usleep(10000);
+      m->update();
+    }
+}
+
+int main()
+{
+  SoundFactoryPtr oaf(new OpenAL_Audiere_Factory);
+  SoundManagerPtr mg(new SoundManager(oaf));
+  m = mg;
+
+  mg->setListenerPos(0,0,0,0,1,0,0,0,1);
+
+  play(0,0,0);
+  play(1,1,0);
+  play(-1,0,0);
+
+  return 0;
+}
diff --git a/libs/openengine/sound/tests/sound_manager_test.cpp b/libs/openengine/sound/tests/sound_manager_test.cpp
new file mode 100644
index 0000000000..3794c4a3cf
--- /dev/null
+++ b/libs/openengine/sound/tests/sound_manager_test.cpp
@@ -0,0 +1,73 @@
+#include <iostream>
+#include <exception>
+#include <assert.h>
+
+#include <mangle/stream/servers/file_stream.hpp>
+#include <mangle/sound/filters/openal_audiere.hpp>
+
+#include <sound/sndmanager.hpp>
+
+using namespace std;
+using namespace Mangle::Stream;
+using namespace Mangle::Sound;
+using namespace OEngine::Sound;
+
+const std::string sound = "../../mangle/sound/tests/cow.wav";
+
+int main()
+{
+  SoundFactoryPtr oaf(new OpenAL_Audiere_Factory);
+  SoundManagerPtr mg(new SoundManager(oaf));
+
+  cout << "Playing " << sound << "\n";
+
+  assert(mg->numSounds() == 0);
+
+  /** Start the sound playing, and then let the pointer go out of
+      scope. Lower-level players (like 'oaf' above) will immediately
+      delete the sound. SoundManager OTOH will keep it until it's
+      finished.
+  */
+  mg->play(sound);
+
+  assert(mg->numSounds() == 1);
+
+  // Loop while there are still sounds to manage
+  while(mg->numSounds() != 0)
+    {
+      assert(mg->numSounds() == 1);
+      usleep(10000);
+      if(mg->needsUpdate)
+        mg->update();
+    }
+
+  SoundPtr snd = mg->play(sound);
+  cout << "Replaying\n";
+  int i = 0;
+  while(mg->numSounds() != 0)
+    {
+      assert(mg->numSounds() == 1);
+      usleep(10000);
+      if(mg->needsUpdate)
+        mg->update();
+
+      if(i++ == 70)
+        {
+          cout << "pause\n";
+          snd->pause();
+        }
+      if(i == 130)
+        {
+          cout << "restart\n";
+          snd->play();
+          // Let the sound go out of scope
+          snd.reset();
+        }
+    }
+
+  cout << "Done playing.\n";
+
+  assert(mg->numSounds() == 0);
+
+  return 0;
+}
diff --git a/libs/openengine/sound/tests/test.sh b/libs/openengine/sound/tests/test.sh
new file mode 100755
index 0000000000..2d07708adc
--- /dev/null
+++ b/libs/openengine/sound/tests/test.sh
@@ -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
diff --git a/libs/openengine/testall.sh b/libs/openengine/testall.sh
new file mode 100755
index 0000000000..097fdabd5b
--- /dev/null
+++ b/libs/openengine/testall.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+function run()
+{
+  echo "TESTING $1"
+  cd "$1/tests/"
+  ./test.sh
+  cd ../../
+}
+
+run input
diff --git a/readme.txt b/readme.txt
index d9e6cbfa47..149fd4f193 100644
--- a/readme.txt
+++ b/readme.txt
@@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind
 OpenMW is an attempt at recreating the engine for the popular role-playing game
 Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.
 
-Version: 0.12.0
+Version: 0.13.0
 License: GPL (see GPL3.txt for more information)
 Website: http://www.openmw.org
 
@@ -25,8 +25,7 @@ There's an OpenMW package available in the AUR Repository:
 http://aur.archlinux.org/packages.php?ID=21419
 
 OS X:
-TODO add description for OS X
-
+Open DMG file, copy OpenMW folder anywhere, for example in /Applications
 
 BUILD FROM SOURCE
 
@@ -97,9 +96,12 @@ athile
 Cris “Mirceam” Mihalache
 gugus / gus
 Jacob “Yacoby” Essex
+Jannik “scrawl” Heller
 Jason “jhooks” Hooks
+Karl-Felix “k1ll” Glatzer
 Lukasz “lgro” Gromanowski
 Marc “Zini” Zinnschlag
+Michael “werdanith” Papageorgiou
 Nikolay “corristo” Kasyanov
 Pieter “pvdk” van der Kloet
 Sebastian “swick” Wick
@@ -111,7 +113,6 @@ Diggory Hardy
 Jan Borsodi
 Jan-Peter “peppe” Nilsson
 Josua Grawitter
-Karl-Felix “k1ll” Glatzer
 Nicolay Korslund
 sergoz
 Star-Demon
@@ -126,6 +127,36 @@ Thanks to Kevin Ryan for kindly providing us with the icon used for the Data Fil
 
 CHANGELOG
 
+0.13.0
+
+Bug #145: Fixed sound problems after cell change
+Bug #179: Pressing space in console triggers activation
+Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux
+Bug #189: ASCII 16 character added to console on it's activation on Mac OS X
+Bug #190: Case Folding fails with music files
+Bug #196: Collision shapes out of place
+Bug #202: ESMTool doesn't not work with localised ESM files anymore
+Bug #203: Torch lights only visible on short distance
+Bug #207: Ogre.log not written
+Bug #209: Sounds do not play
+Bug #210: Ogre crash at Dren plantation
+Feature #9: NPC Dialogue Window
+Feature #16/42: New sky/weather implementation
+Feature #40: Fading
+Feature #48: NPC Dialogue System
+Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet)
+Feature #161: Load REC_PGRD records
+Feature #195: Wireframe-mode
+Feature #198/199: Various sound effects
+Feature #206: Allow picking data path from launcher if non is set
+Task #108: Refactor window manager class
+Task #172: Sound Manager Cleanup
+Task #173: Create OpenEngine systems in the appropriate manager classes
+Task #184: Adjust MSVC and gcc warning levels
+Task #185: RefData rewrite
+Task #201: Workaround for transparency issues
+Task #208: silenced esm_reader.hpp warning
+
 0.12.0
 
 Bug #154: FPS Drop