Merge branch 'master' into dialogue

Conflicts:
	apps/openmw/mwclass/creature.cpp
	apps/openmw/mwclass/creature.hpp
	apps/openmw/mwclass/npc.cpp
	apps/openmw/mwclass/npc.hpp
	apps/openmw/mwworld/class.cpp
	apps/openmw/mwworld/class.hpp
actorid
Marc Zinnschlag 15 years ago
commit 2cfe5f0697

@ -1,5 +1,10 @@
project(OpenMW)
# Sound source selection
option(USE_AUDIERE "use Audiere for sound" OFF)
option(USE_FFMPEG "use ffmpeg for sound" OFF)
option(USE_MPG123 "use mpg123 + libsndfile for sound" ON)
# We probably support older versions than this.
cmake_minimum_required(VERSION 2.6)
@ -48,7 +53,10 @@ set(ESM_STORE_HEADER
source_group(components\\esm_store FILES ${ESM_STORE} ${ESM_STORE_HEADER})
file(GLOB ESM_HEADER ${COMP_DIR}/esm/*.hpp)
source_group(components\\esm FILES ${ESM_HEADER})
set(ESM
${COMP_DIR}/esm/load_impl.cpp
)
source_group(components\\esm FILES ${ESM_HEADER} ${ESM})
set(MISC
${COMP_DIR}/misc/stringops.cpp
@ -68,7 +76,7 @@ file(GLOB INTERPRETER_HEADER ${COMP_DIR}/interpreter/*.hpp)
source_group(components\\interpreter FILES ${INTERPRETER} ${INTERPRETER_HEADER})
set(COMPONENTS ${BSA} ${NIF} ${NIFOGRE} ${ESM_STORE} ${MISC}
${COMPILER} ${INTERPRETER})
${COMPILER} ${INTERPRETER} ${ESM})
set(COMPONENTS_HEADER ${BSA_HEADER} ${NIF_HEADER} ${NIFOGRE_HEADER} ${ESM_STORE_HEADER}
${ESM_HEADER} ${MISC_HEADER} ${COMPILER_HEADER}
${INTERPRETER_HEADER})
@ -90,7 +98,48 @@ set(OENGINE_GUI
${LIBDIR}/openengine/gui/events.cpp
${LIBDIR}/openengine/gui/manager.cpp
)
set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI})
# Sound setup
if (USE_AUDIERE)
set(MANGLE_SOUND_OUTPUT
${LIBDIR}/mangle/sound/sources/audiere_source.cpp
${LIBDIR}/mangle/sound/sources/sample_reader.cpp
${LIBDIR}/mangle/stream/clients/audiere_file.cpp)
find_package(Audiere REQUIRED)
set(SOUND_INPUT_INCLUDES ${AUDIERE_INCLUDE_DIR})
set(SOUND_INPUT_LIBRARY ${AUDIERE_LIBRARY})
set(SOUND_DEFINE -DOPENMW_USE_AUDIERE)
endif (USE_AUDIERE)
if (USE_FFMPEG)
set(MANGLE_SOUND_OUTPUT
${LIBDIR}/mangle/sound/sources/ffmpeg_source.cpp)
find_package(FFMPEG REQUIRED)
set(SOUND_INPUT_INCLUDES ${FFMPEG_INCLUDE_DIR})
set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES})
set(SOUND_DEFINE -DOPENMW_USE_FFMPEG)
endif (USE_FFMPEG)
if (USE_MPG123)
set(MANGLE_SOUND_OUTPUT
${LIBDIR}/mangle/sound/sources/mpg123_source.cpp
${LIBDIR}/mangle/sound/sources/libsndfile.cpp
${LIBDIR}/mangle/sound/sources/sample_reader.cpp)
find_package(MPG123 REQUIRED)
find_package(SNDFILE REQUIRED)
set(SOUND_INPUT_INCLUDES ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR})
set(SOUND_INPUT_LIBRARY ${MPG123_LIBRARY} ${SNDFILE_LIBRARY})
set(SOUND_DEFINE -DOPENMW_USE_MPG123)
endif (USE_MPG123)
set(OENGINE_SOUND
# Mangle and OEngine sound files are sort of intertwined, so put
# them together here
${LIBDIR}/openengine/sound/sndmanager.cpp
${LIBDIR}/mangle/sound/outputs/openal_out.cpp
${MANGLE_SOUND_OUTPUT}
)
set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_SOUND})
source_group(libs\\openengine FILES ${OENGINE_ALL})
set(OPENMW_LIBS ${MANGLE_ALL} ${OENGINE_ALL})
@ -110,6 +159,7 @@ find_package(OGRE REQUIRED)
find_package(Boost REQUIRED COMPONENTS system filesystem program_options thread)
find_package(OIS REQUIRED)
find_package(Iconv REQUIRED)
find_package(OpenAL REQUIRED)
include_directories("."
${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre
${OIS_INCLUDE_DIR} ${Boost_INCLUDE_DIR}

@ -188,10 +188,17 @@ add_executable(openmw
${APPLE_BUNDLE_RESOURCES}
)
# Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING
# when we change the backend.
include_directories(${SOUND_INPUT_INCLUDES})
add_definitions(${SOUND_DEFINE})
target_link_libraries(openmw
${OGRE_LIBRARIES}
${OIS_LIBRARIES}
${Boost_LIBRARIES}
${OPENAL_LIBRARY}
${SOUND_INPUT_LIBRARY}
${ICONV_LIBRARIES}
caelum
MyGUIEngine

@ -40,7 +40,7 @@ void OMW::Engine::executeLocalScripts()
mEnvironment.mWorld->getLocalScripts().begin());
iter!=mEnvironment.mWorld->getLocalScripts().end(); ++iter)
{
if (!mIgnoreLocalPtr.isEmpty() && mIgnoreLocalPtr!=iter->second)
if (mIgnoreLocalPtr.isEmpty() || mIgnoreLocalPtr!=iter->second)
{
MWScript::InterpreterContext interpreterContext (mEnvironment,
&iter->second.getRefData().getLocals(), MWWorld::Ptr (iter->second));
@ -236,7 +236,10 @@ void OMW::Engine::go()
mExtensions, mNewGame);
// Create sound system
mEnvironment.mSoundManager = new MWSound::SoundManager;
mEnvironment.mSoundManager = new MWSound::SoundManager(mOgre.getRoot(),
mOgre.getCamera(),
mEnvironment.mWorld->getStore(),
(mDataDir / "Sound").file_string());
// Create script system
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full,
@ -272,6 +275,18 @@ void OMW::Engine::go()
mOgre.getRoot()->addFrameListener (this);
// Play some good 'ol tunes
std::string music = (mDataDir / "Music/Explore/mx_explore_5.mp3").file_string();
try
{
std::cout << "Playing " << music << "\n";
mEnvironment.mSoundManager->streamMusic(music);
}
catch(std::exception &e)
{
std::cout << " Music Error: " << e.what() << "\n";
}
// Start the main rendering loop
mOgre.start();

@ -7,8 +7,26 @@
#include "../mwworld/ptr.hpp"
#include "../mwrender/cellimp.hpp"
namespace MWClass
{
void Activator::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Activator, MWWorld::RefData> *ref =
ptr.get<ESM::Activator>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Activator::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Activator, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -8,10 +8,28 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwrender/cellimp.hpp"
#include "containerutil.hpp"
namespace MWClass
{
void Apparatus::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Apparatus, MWWorld::RefData> *ref =
ptr.get<ESM::Apparatus>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Apparatus::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Apparatus, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -8,10 +8,28 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwrender/cellimp.hpp"
#include "containerutil.hpp"
namespace MWClass
{
void Armor::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Armor, MWWorld::RefData> *ref =
ptr.get<ESM::Armor>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Armor::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Armor, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -8,10 +8,28 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwrender/cellimp.hpp"
#include "containerutil.hpp"
namespace MWClass
{
void Book::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Book, MWWorld::RefData> *ref =
ptr.get<ESM::Book>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Book::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Book, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -8,10 +8,28 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwrender/cellimp.hpp"
#include "containerutil.hpp"
namespace MWClass
{
void Clothing::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Clothing, MWWorld::RefData> *ref =
ptr.get<ESM::Clothing>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Clothing::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Clothing, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -7,8 +7,26 @@
#include "../mwworld/ptr.hpp"
#include "../mwrender/cellimp.hpp"
namespace MWClass
{
void Container::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Container, MWWorld::RefData> *ref =
ptr.get<ESM::Container>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Container::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Container, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -7,6 +7,11 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontalk.hpp"
#include "../mwworld/environment.hpp"
#include "../mwrender/cellimp.hpp"
#include "../mwmechanics/mechanicsmanager.hpp"
namespace MWClass
{
@ -18,6 +23,32 @@ namespace MWClass
return ref->base->mId;
}
void Creature::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Creature, MWWorld::RefData> *ref =
ptr.get<ESM::Creature>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
void Creature::enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const
{
environment.mMechanicsManager->addActor (ptr);
}
void Creature::disable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const
{
environment.mMechanicsManager->removeActor (ptr);
}
std::string Creature::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Creature, MWWorld::RefData> *ref =

@ -12,6 +12,16 @@ namespace MWClass
virtual std::string getId (const MWWorld::Ptr& ptr) const;
///< Return ID of \a ptr
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual void enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const;
///< Enable reference; only does the non-rendering part
virtual void disable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const;
///< Enable reference; only does the non-rendering part
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -13,8 +13,26 @@
#include "../mwworld/environment.hpp"
#include "../mwworld/world.hpp"
#include "../mwrender/cellimp.hpp"
namespace MWClass
{
void Door::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Door, MWWorld::RefData> *ref =
ptr.get<ESM::Door>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Door::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Door, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -8,10 +8,28 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwrender/cellimp.hpp"
#include "containerutil.hpp"
namespace MWClass
{
void Ingredient::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Ingredient, MWWorld::RefData> *ref =
ptr.get<ESM::Ingredient>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Ingredient::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Ingredient, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -8,11 +8,53 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/nullaction.hpp"
#include "../mwworld/environment.hpp"
#include "../mwrender/cellimp.hpp"
#include "../mwsound/soundmanager.hpp"
#include "containerutil.hpp"
namespace MWClass
{
void Light::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Light, MWWorld::RefData> *ref =
ptr.get<ESM::Light>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
// Extract the color and convert to floating point
const int color = ref->base->data.color;
const float r = ((color >> 0) & 0xFF) / 255.0f;
const float g = ((color >> 8) & 0xFF) / 255.0f;
const float b = ((color >> 16) & 0xFF) / 255.0f;
const float radius = float (ref->base->data.radius);
cellRender.insertLight (r, g, b, radius);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
void Light::enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Light, MWWorld::RefData> *ref =
ptr.get<ESM::Light>();
if (!ref->base->sound.empty())
{
environment.mSoundManager->playSound3D (ptr, ref->base->sound, 1.0, 1.0, true);
}
}
std::string Light::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Light, MWWorld::RefData> *ref =

@ -9,6 +9,15 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual void enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const;
///< Enable reference; only does the non-rendering part
/// \attention This is not the same as the script instruction with the same name. References
/// should only be enabled while in an active cell.
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -8,10 +8,28 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwrender/cellimp.hpp"
#include "containerutil.hpp"
namespace MWClass
{
void Lockpick::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Tool, MWWorld::RefData> *ref =
ptr.get<ESM::Tool>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Lockpick::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Tool, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -8,10 +8,28 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwrender/cellimp.hpp"
#include "containerutil.hpp"
namespace MWClass
{
void Misc::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Misc, MWWorld::RefData> *ref =
ptr.get<ESM::Misc>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Misc::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Misc, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -7,6 +7,12 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontalk.hpp"
#include "../mwworld/environment.hpp"
#include "../mwworld/world.hpp"
#include "../mwrender/cellimp.hpp"
#include "../mwmechanics/mechanicsmanager.hpp"
namespace MWClass
{
@ -18,6 +24,45 @@ namespace MWClass
return ref->base->mId;
}
void Npc::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *ref =
ptr.get<ESM::NPC>();
assert (ref->base != NULL);
std::string headID = ref->base->head;
//get the part of the bodypart id which describes the race and the gender
std::string bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4);
std::string headModel = "meshes\\" +
environment.mWorld->getStore().bodyParts.find(headID)->model;
cellRender.insertBegin (ref->ref);
cellRender.insertMesh (headModel);
//TODO: define consts for each bodypart e.g. chest, foot, wrist... and put the parts in the
// right place
const ESM::BodyPart *bodyPart =
environment.mWorld->getStore().bodyParts.search (bodyRaceID + "chest");
if (bodyPart)
cellRender.insertMesh("meshes\\" + bodyPart->model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
void Npc::enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const
{
environment.mMechanicsManager->addActor (ptr);
}
void Npc::disable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const
{
environment.mMechanicsManager->removeActor (ptr);
}
std::string Npc::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *ref =
@ -53,6 +98,15 @@ namespace MWClass
return *ptr.getRefData().getCreatureStats();
}
<<<<<<< HEAD:apps/openmw/mwclass/npc.cpp
=======
boost::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
return boost::shared_ptr<MWWorld::Action> (new MWWorld::ActionTalk (ptr));
}
>>>>>>> master:apps/openmw/mwclass/npc.cpp
MWWorld::ContainerStore<MWWorld::RefData>& Npc::getContainerStore (const MWWorld::Ptr& ptr)
const
{

@ -12,6 +12,16 @@ namespace MWClass
virtual std::string getId (const MWWorld::Ptr& ptr) const;
///< Return ID of \a ptr
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual void enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const;
///< Enable reference; only does the non-rendering part
virtual void disable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const;
///< Enable reference; only does the non-rendering part
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -8,10 +8,28 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwrender/cellimp.hpp"
#include "containerutil.hpp"
namespace MWClass
{
void Potion::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Potion, MWWorld::RefData> *ref =
ptr.get<ESM::Potion>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Potion::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Potion, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -8,10 +8,28 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwrender/cellimp.hpp"
#include "containerutil.hpp"
namespace MWClass
{
void Probe::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Probe, MWWorld::RefData> *ref =
ptr.get<ESM::Probe>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Probe::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Probe, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -8,10 +8,28 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwrender/cellimp.hpp"
#include "containerutil.hpp"
namespace MWClass
{
void Repair::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Repair, MWWorld::RefData> *ref =
ptr.get<ESM::Repair>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Repair::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Repair, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -3,8 +3,28 @@
#include <components/esm/loadstat.hpp>
#include "../mwworld/ptr.hpp"
#include "../mwrender/cellimp.hpp"
namespace MWClass
{
void Static::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Static, MWWorld::RefData> *ref =
ptr.get<ESM::Static>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Static::getName (const MWWorld::Ptr& ptr) const
{
return "";

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -8,10 +8,28 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwrender/cellimp.hpp"
#include "containerutil.hpp"
namespace MWClass
{
void Weapon::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
ESMS::LiveCellRef<ESM::Weapon, MWWorld::RefData> *ref =
ptr.get<ESM::Weapon>();
assert (ref->base != NULL);
const std::string &model = ref->base->model;
if (!model.empty())
{
cellRender.insertBegin (ref->ref);
cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled()));
}
}
std::string Weapon::getName (const MWWorld::Ptr& ptr) const
{
ESMS::LiveCellRef<ESM::Weapon, MWWorld::RefData> *ref =

@ -9,6 +9,10 @@ namespace MWClass
{
public:
virtual void insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::Ptr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -47,8 +47,8 @@ namespace MWGui
getWidget(crosshair, "Crosshair");
compass->setImageTexture("compass.dds");
crosshair->setImageTexture("target.dds");
compass->setImageTexture("textures\\compass.dds");
crosshair->setImageTexture("textures\\target.dds");
// These are just demo values, you should replace these with
// real calls from outside the class later.

@ -2,94 +2,56 @@
#include <cassert>
#include "../mwworld/class.hpp"
#include "../mwworld/ptr.hpp"
using namespace MWRender;
template<typename T>
void insertObj(CellRenderImp& cellRender, T& liveRef, const ESMS::ESMStore& store)
void insertCellRefList (CellRenderImp& cellRender, MWWorld::Environment& environment,
T& cellRefList, ESMS::CellStore<MWWorld::RefData> &cell)
{
assert (liveRef.base != NULL);
const std::string &model = liveRef.base->model;
if(!model.empty())
if (!cellRefList.list.empty())
{
cellRender.insertBegin (liveRef.ref);
cellRender.insertMesh ("meshes\\" + model);
liveRef.mData.setHandle (cellRender.insertEnd (liveRef.mData.isEnabled()));
}
}
const MWWorld::Class& class_ =
MWWorld::Class::get (MWWorld::Ptr (&*cellRefList.list.begin(), &cell));
template<>
void insertObj(CellRenderImp& cellRender, ESMS::LiveCellRef<ESM::Light, MWWorld::RefData>& liveRef, const ESMS::ESMStore& store)
{
assert (liveRef.base != NULL);
const std::string &model = liveRef.base->model;
if(!model.empty())
for (typename T::List::iterator it = cellRefList.list.begin();
it != cellRefList.list.end(); it++)
{
cellRender.insertBegin (liveRef.ref);
cellRender.insertMesh ("meshes\\" + model);
// Extract the color and convert to floating point
const int color = liveRef.base->data.color;
const float r = ((color >> 0) & 0xFF) / 255.0f;
const float g = ((color >> 8) & 0xFF) / 255.0f;
const float b = ((color >> 16) & 0xFF) / 255.0f;
const float radius = float(liveRef.base->data.radius);
cellRender.insertLight(r, g, b, radius);
if (it->mData.getCount() || it->mData.isEnabled())
{
MWWorld::Ptr ptr (&*it, &cell);
liveRef.mData.setHandle (cellRender.insertEnd (liveRef.mData.isEnabled()));
class_.insertObj (ptr, cellRender, environment);
class_.enable (ptr, environment);
}
}
}
template<>
void insertObj(CellRenderImp& cellRender, ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData>& liveRef, const ESMS::ESMStore& store)
{
std::string headID = liveRef.base->head;
//get the part of the bodypart id which describes the race and the gender
std::string bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4);
std::string headModel = "meshes\\" + store.bodyParts.find(headID)->model;
cellRender.insertBegin(liveRef.ref);
cellRender.insertMesh(headModel);
//TODO: define consts for each bodypart e.g. chest, foot, wrist... and put the parts in the right place
cellRender.insertMesh("meshes\\" + store.bodyParts.find(bodyRaceID + "chest")->model);
liveRef.mData.setHandle (cellRender.insertEnd (liveRef.mData.isEnabled()));
}
template<typename T>
void insertCellRefList (CellRenderImp& cellRender, const ESMS::ESMStore& store, T& cellRefList)
{
for(typename T::List::iterator it = cellRefList.list.begin();
it != cellRefList.list.end(); it++)
{
if (it->mData.getCount())
insertObj (cellRender, *it, store);
}
}
void CellRenderImp::insertCell(ESMS::CellStore<MWWorld::RefData> &cell, const ESMS::ESMStore& store)
void CellRenderImp::insertCell(ESMS::CellStore<MWWorld::RefData> &cell,
MWWorld::Environment& environment)
{
// Loop through all references in the cell
insertCellRefList (*this, store, cell.activators);
insertCellRefList (*this, store, cell.potions);
insertCellRefList (*this, store, cell.appas);
insertCellRefList (*this, store, cell.armors);
insertCellRefList (*this, store, cell.books);
insertCellRefList (*this, store, cell.clothes);
insertCellRefList (*this, store, cell.containers);
insertCellRefList (*this, store, cell.creatures);
insertCellRefList (*this, store, cell.doors);
insertCellRefList (*this, store, cell.ingreds);
// insertCellRefList (*this, store, cell.creatureLists);
// insertCellRefList (*this, store, cell.itemLists);
insertCellRefList (*this, store, cell.lights);
insertCellRefList (*this, store, cell.lockpicks);
insertCellRefList (*this, store, cell.miscItems);
insertCellRefList (*this, store, cell.npcs);
insertCellRefList (*this, store, cell.probes);
insertCellRefList (*this, store, cell.repairs);
insertCellRefList (*this, store, cell.statics);
insertCellRefList (*this, store, cell.weapons);
insertCellRefList (*this, environment, cell.activators, cell);
insertCellRefList (*this, environment, cell.potions, cell);
insertCellRefList (*this, environment, cell.appas, cell);
insertCellRefList (*this, environment, cell.armors, cell);
insertCellRefList (*this, environment, cell.books, cell);
insertCellRefList (*this, environment, cell.clothes, cell);
insertCellRefList (*this, environment, cell.containers, cell);
insertCellRefList (*this, environment, cell.creatures, cell);
insertCellRefList (*this, environment, cell.doors, cell);
insertCellRefList (*this, environment, cell.ingreds, cell);
insertCellRefList (*this, environment, cell.creatureLists, cell);
insertCellRefList (*this, environment, cell.itemLists, cell);
insertCellRefList (*this, environment, cell.lights, cell);
insertCellRefList (*this, environment, cell.lockpicks, cell);
insertCellRefList (*this, environment, cell.miscItems, cell);
insertCellRefList (*this, environment, cell.npcs, cell);
insertCellRefList (*this, environment, cell.probes, cell);
insertCellRefList (*this, environment, cell.repairs, cell);
insertCellRefList (*this, environment, cell.statics, cell);
insertCellRefList (*this, environment, cell.weapons, cell);
}

@ -12,9 +12,9 @@ namespace ESM
class CellRef;
}
namespace ESMS
namespace MWWorld
{
class ESMStore;
class Environment;
}
namespace MWRender
@ -40,7 +40,7 @@ namespace MWRender
/// finish inserting a new reference and return a handle to it.
virtual std::string insertEnd (bool Enable) = 0;
void insertCell(ESMS::CellStore<MWWorld::RefData> &cell, const ESMS::ESMStore& store);
void insertCell(ESMS::CellStore<MWWorld::RefData> &cell, MWWorld::Environment& environment);
};
}

@ -174,19 +174,12 @@ void InteriorCellRender::setAmbientMode()
void InteriorCellRender::show()
{
// If already loaded, just make the cell visible.
if(base)
{
base->setVisible(true);
return;
}
base = scene.getRoot()->createChildSceneNode();
configureAmbient();
configureFog();
insertCell(cell, store);
insertCell(cell, mEnvironment);
}
void InteriorCellRender::hide()

@ -3,7 +3,6 @@
#include "cell.hpp"
#include "cellimp.hpp"
#include "components/esm_store/cell_store.hpp"
#include "OgreColourValue.h"
@ -12,6 +11,11 @@ namespace Ogre
class SceneNode;
}
namespace MWWorld
{
class Environment;
}
namespace MWRender
{
class MWScene;
@ -43,7 +47,7 @@ namespace MWRender
static bool lightOutQuadInLin;
ESMS::CellStore<MWWorld::RefData> &cell;
const ESMS::ESMStore &store;
MWWorld::Environment &mEnvironment;
MWScene &scene;
/// The scene node that contains all objects belonging to this
@ -79,8 +83,9 @@ namespace MWRender
public:
InteriorCellRender(ESMS::CellStore<MWWorld::RefData> &_cell, const ESMS::ESMStore& _store, MWScene &_scene)
: cell(_cell), store(_store), scene(_scene), base(NULL), insert(NULL), ambientMode (0) {}
InteriorCellRender(ESMS::CellStore<MWWorld::RefData> &_cell, MWWorld::Environment& environment,
MWScene &_scene)
: cell(_cell), mEnvironment (environment), scene(_scene), base(NULL), insert(NULL), ambientMode (0) {}
virtual ~InteriorCellRender() { destroy(); }

@ -51,6 +51,7 @@ namespace MWScript
virtual void setLocalFloat (int index, float value);
using Interpreter::Context::messageBox;
virtual void messageBox (const std::string& message,
const std::vector<std::string>& buttons);

@ -32,8 +32,8 @@ namespace MWScript
std::string text = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
context.getSoundManager().say (context.getReference(), file, text,
context);
context.getSoundManager().say (context.getReference(), file);
context.messageBox (text);
}
};
@ -46,8 +46,7 @@ namespace MWScript
MWScript::InterpreterContext& context
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
runtime.push (context.getSoundManager().sayDone (context.getReference(),
context));
runtime.push (context.getSoundManager().sayDone (context.getReference()));
}
};
@ -63,7 +62,7 @@ namespace MWScript
std::string sound = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
context.getSoundManager().streamMusic (sound, context);
context.getSoundManager().streamMusic (sound);
}
};
@ -79,7 +78,7 @@ namespace MWScript
std::string sound = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
context.getSoundManager().playSound (sound, 1.0, 1.0, context);
context.getSoundManager().playSound (sound, 1.0, 1.0);
}
};
@ -101,7 +100,7 @@ namespace MWScript
Interpreter::Type_Float pitch = runtime[0].mFloat;
runtime.pop();
context.getSoundManager().playSound (sound, volume, pitch, context);
context.getSoundManager().playSound (sound, volume, pitch);
}
};
@ -122,7 +121,7 @@ namespace MWScript
runtime.pop();
context.getSoundManager().playSound3D (context.getReference(), sound,
1.0, 1.0, mLoop, context);
1.0, 1.0, mLoop);
}
};
@ -149,7 +148,7 @@ namespace MWScript
runtime.pop();
context.getSoundManager().playSound3D (context.getReference(), sound, volume,
pitch, mLoop, context);
pitch, mLoop);
}
};
@ -166,7 +165,7 @@ namespace MWScript
std::string sound = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
context.getSoundManager().stopSound3D (context.getReference(), sound, context);
context.getSoundManager().stopSound3D (context.getReference(), sound);
}
};
@ -183,7 +182,7 @@ namespace MWScript
runtime.pop();
runtime.push (context.getSoundManager().getSoundPlaying (
context.getReference(), runtime.getStringLiteral (index), context));
context.getReference(), runtime.getStringLiteral (index)));
}
};
@ -206,7 +205,7 @@ namespace MWScript
runtime.pop();
context.getSoundManager().say (context.getWorld().getPtr (id, true),
file, text, context);
file);
}
};
@ -223,7 +222,7 @@ namespace MWScript
runtime.pop();
runtime.push (context.getSoundManager().sayDone (
context.getWorld().getPtr (id, true), context));
context.getWorld().getPtr (id, true)));
}
};
@ -247,7 +246,7 @@ namespace MWScript
runtime.pop();
context.getSoundManager().playSound3D (
context.getWorld().getPtr (id, true), sound, 1.0, 1.0, mLoop, context);
context.getWorld().getPtr (id, true), sound, 1.0, 1.0, mLoop);
}
};
@ -277,7 +276,7 @@ namespace MWScript
runtime.pop();
context.getSoundManager().playSound3D (
context.getWorld().getPtr (id, true), sound, volume, pitch, mLoop, context);
context.getWorld().getPtr (id, true), sound, volume, pitch, mLoop);
}
};
@ -298,7 +297,7 @@ namespace MWScript
runtime.pop();
context.getSoundManager().stopSound3D (
context.getWorld().getPtr (id, true), sound, context);
context.getWorld().getPtr (id, true), sound);
}
};
@ -319,7 +318,7 @@ namespace MWScript
runtime.push (context.getSoundManager().getSoundPlaying (
context.getWorld().getPtr (id, true),
runtime.getStringLiteral (index), context));
runtime.getStringLiteral (index)));
}
};

@ -1,71 +1,238 @@
#include "soundmanager.hpp"
#include <iostream> // TODO remove this line, once the real code is in place.
#include <iostream>
using namespace std;
#include <openengine/sound/sndmanager.hpp>
#include <mangle/sound/clients/ogre_listener_mover.hpp>
#include <mangle/sound/clients/ogre_output_updater.hpp>
#include <components/esm_store/store.hpp>
#include <algorithm>
#include <OgreRoot.h>
/* Set up the sound manager to use Audiere, FFMPEG or
MPG123/libsndfile for input. The OPENMW_USE_x macros are set in
CMakeLists.txt.
*/
#ifdef OPENMW_USE_AUDIERE
#include <mangle/sound/filters/openal_audiere.hpp>
#define SOUND_FACTORY OpenAL_Audiere_Factory
#endif
#ifdef OPENMW_USE_FFMPEG
#include <mangle/sound/filters/openal_ffmpeg.hpp>
#define SOUND_FACTORY OpenAL_FFMpeg_Factory
#endif
#ifdef OPENMW_USE_MPG123
#include <mangle/sound/filters/openal_sndfile_mpg123.hpp>
#define SOUND_FACTORY OpenAL_SndFile_Mpg123_Factory
#endif
using namespace Mangle::Sound;
typedef OEngine::Sound::SoundManager OEManager;
typedef OEngine::Sound::SoundManagerPtr OEManagerPtr;
/* Set the position on a sound based on a Ptr. TODO: We do not support
tracking moving objects yet, once a sound is started it stays in
the same place until it finishes.
This obviously has to be fixed at some point for player/npc
footstep sounds and the like. However, updating all sounds each
frame is expensive, so there should be a special flag for sounds
that need to track their attached object.
*/
static void setPos(SoundPtr &snd, const MWWorld::Ptr ref)
{
// Get sound position from the reference
const float *pos = ref.getCellRef().pos.pos;
#include <components/interpreter/context.hpp>
// Move the sound, converting from MW coordinates to Ogre
// coordinates.
snd->setPos(pos[0], pos[2], -pos[1]);
}
namespace MWSound
{
void SoundManager::say (MWWorld::Ptr reference, const std::string& filename,
const std::string& text, Interpreter::Context& context)
struct SoundManager::SoundImpl
{
/* This is the sound manager. It loades, stores and deletes
sounds based on the sound factory it is given.
*/
OEManagerPtr mgr;
/* This class calls update() on the sound manager each frame
using and Ogre::FrameListener
*/
Mangle::Sound::OgreOutputUpdater updater;
/* This class tracks the movement of an Ogre::Camera and moves
a sound listener automatically to follow it.
*/
Mangle::Sound::OgreListenerMover cameraTracker;
const ESMS::ESMStore &store;
std::string dir;
SoundImpl(Ogre::Root *root, Ogre::Camera *camera,
const ESMS::ESMStore &str,
const std::string &soundDir)
: mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY)))
, updater(mgr)
, cameraTracker(mgr)
, store(str)
{
std::cout << "sound effect: " << reference.getRefData().getHandle() << " is speaking" << std::endl;
// Attach the camera to the camera tracker
cameraTracker.followCamera(camera);
context.messageBox (text);
// Tell Ogre to update the sound system each frame
root->addFrameListener(&updater);
dir = soundDir + "/";
}
bool SoundManager::sayDone (MWWorld::Ptr reference, Interpreter::Context& context) const
// Convert a soundId to file name, and modify the volume
// according to the sounds local volume setting, minRange and
// maxRange.
std::string lookup(const std::string &soundId,
float &volume, float &min, float &max)
{
return false;
const ESM::Sound *snd = store.sounds.search(soundId);
if(snd == NULL) return "";
volume *= snd->data.volume / 255.0;
// These factors are not very fine tuned.
min = snd->data.minRange * 7;
max = snd->data.maxRange * 2000;
std::string file = dir + snd->sound;
std::replace(file.begin(), file.end(), '\\', '/');
return file;
}
void SoundManager::streamMusic (const std::string& filename, Interpreter::Context& context)
// Add a sound to the list and play it
void add(const std::string &file,
MWWorld::Ptr reference,
const std::string &id,
float volume, float pitch,
float min, float max,
bool loop)
{
try
{
std::cout << "sound effect: playing music" << filename << std::endl;
SoundPtr snd = mgr->load(file);
snd->setRepeat(loop);
snd->setVolume(volume);
snd->setPitch(pitch);
snd->setRange(min,max);
setPos(snd, reference);
snd->play();
}
catch(...)
{
cout << "Error loading " << file << ", skipping.\n";
}
}
void SoundManager::playSound (const std::string& soundId, float volume, float pitch,
Interpreter::Context& context)
// Stop a sound and remove it from the list. If id="" then
// remove the entire object and stop all its sounds.
void remove(MWWorld::Ptr reference, const std::string &id = "")
{
std::cout
<< "sound effect: playing sound " << soundId
<< " at volume " << volume << ", at pitch " << pitch
<< std::endl;
}
void SoundManager::playSound3D (MWWorld::Ptr reference, const std::string& soundId,
float volume, float pitch, bool loop, Interpreter::Context& context)
bool isPlaying(MWWorld::Ptr reference, const std::string &id) const
{
std::cout
<< "sound effect: playing sound " << soundId
<< " from " << reference.getRefData().getHandle()
<< " at volume " << volume << ", at pitch " << pitch
<< std::endl;
return true;
}
mSounds[reference.getRefData().getHandle()] = soundId;
void removeCell(const MWWorld::Ptr::CellStore *cell)
{
// Note to Nico: You can get the cell of a Ptr via the getCell
// function. Just iterate over all sounds and remove those
// with matching cell.
}
void SoundManager::stopSound3D (MWWorld::Ptr reference, const std::string& soundId,
Interpreter::Context& context)
void updatePositions(MWWorld::Ptr reference)
{
std::cout
<< "sound effect : stop playing sound " << soundId
<< " from " << reference.getRefData().getHandle() << std::endl;
}
};
mSounds[reference.getRefData().getHandle()] = "";
SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera,
const ESMS::ESMStore &store,
const std::string &soundDir)
{
mData = new SoundImpl(root, camera, store, soundDir);
}
SoundManager::~SoundManager()
{
delete mData;
}
bool SoundManager::getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId,
Interpreter::Context& context) const
void SoundManager::say (MWWorld::Ptr reference, const std::string& filename)
{
std::map<std::string, std::string>::const_iterator iter =
mSounds.find (reference.getRefData().getHandle());
// The range values are not tested
mData->add(filename, reference, "_say_sound", 1, 1, 100, 10000, false);
}
if (iter==mSounds.end())
return false;
bool SoundManager::sayDone (MWWorld::Ptr reference) const
{
return !mData->isPlaying(reference, "_say_sound");
}
return iter->second==soundId;
void SoundManager::streamMusic (const std::string& filename)
{
// Play the sound and tell it to stream, if possible. TODO:
// Store the reference, the jukebox will need to check status,
// control volume etc.
SoundPtr music = mData->mgr->play(filename);
music->setStreaming(true);
music->setVolume(0.4);
}
}
void SoundManager::playSound (const std::string& soundId, float volume, float pitch)
{
// Play and forget
float min, max;
const std::string &file = mData->lookup(soundId, volume, min, max);
if(file != "")
{
SoundPtr snd = mData->mgr->play(file);
snd->setVolume(volume);
snd->setRange(min,max);
snd->setPitch(pitch);
}
}
void SoundManager::playSound3D (MWWorld::Ptr reference, const std::string& soundId,
float volume, float pitch, bool loop)
{
// Look up the sound in the ESM data
float min, max;
const std::string &file = mData->lookup(soundId, volume, min, max);
if(file != "")
mData->add(file, reference, soundId, volume, pitch, min, max, loop);
}
void SoundManager::stopSound3D (MWWorld::Ptr reference, const std::string& soundId)
{
mData->remove(reference, soundId);
}
void SoundManager::stopSound (MWWorld::Ptr::CellStore *cell)
{
mData->removeCell(cell);
}
bool SoundManager::getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId) const
{
return mData->isPlaying(reference, soundId);
}
void SoundManager::updateObject(MWWorld::Ptr reference)
{
mData->updatePositions(reference);
}
}

@ -6,63 +6,62 @@
#include "../mwworld/ptr.hpp"
namespace Interpreter
namespace Ogre
{
class Context;
class Root;
class Camera;
}
namespace MWSound
namespace ESMS
{
// Note to the sound implementor (can be removed once the implementation is complete):
//
// - the dummy implementation allows only one sound effect per object at a time. I am
// not sure, if that is what Morrowind does. Beyond the dummy code in this class the script
// system does not make any assumption about the number of sound effects.
//
// - all text-output (error messages and such) must be directed through the
// context.messageBox interface.
//
// - the -> script syntax is not implemented yet ( script instructions of the type
// npc_x -> say "file", "text"
// aren't working)
struct ESMStore;
}
namespace MWSound
{
class SoundManager
{
std::map<std::string, std::string> mSounds; // object, sound (for testing only)
// Hide implementation details - engine.cpp is compiling
// enough as it is.
struct SoundImpl;
SoundImpl *mData;
public:
SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store,
const std::string &soundDir);
~SoundManager();
void say (MWWorld::Ptr reference, const std::string& filename,
const std::string& text, Interpreter::Context& context);
void say (MWWorld::Ptr reference, const std::string& filename);
///< Make an actor say some text.
/// \param filename name of a sound file in "Sound/Vo/" in the data directory.
/// \param text Subtitle
bool sayDone (MWWorld::Ptr reference, Interpreter::Context& context) const;
bool sayDone (MWWorld::Ptr reference) const;
///< Is actor not speaking?
void streamMusic (const std::string& filename, Interpreter::Context& context);
void streamMusic (const std::string& filename);
///< Play a soundifle
/// \param filename name of a sound file in "Music/" in the data directory.
void playSound (const std::string& soundId, float volume, float pitch,
Interpreter::Context& context);
void playSound (const std::string& soundId, float volume, float pitch);
///< Play a sound, independently of 3D-position
void playSound3D (MWWorld::Ptr reference, const std::string& soundId,
float volume, float pitch, bool loop, Interpreter::Context& context);
float volume, float pitch, bool loop);
///< Play a sound from an object
void stopSound3D (MWWorld::Ptr reference, const std::string& soundId,
Interpreter::Context& context);
///< Stop the given object from playing the given sound.
void stopSound3D (MWWorld::Ptr reference, const std::string& soundId = "");
///< Stop the given object from playing the given sound, If no soundId is given,
/// all sounds for this reference will stop.
bool getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId,
Interpreter::Context& context) const;
void stopSound (MWWorld::Ptr::CellStore *cell);
///< Stop all sounds for the given cell.
bool getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId) const;
///< Is the given sound currently playing on the given object?
void updateObject(MWWorld::Ptr reference);
///< Update the position of all sounds connected to the given object.
};
}
#endif

@ -19,6 +19,22 @@ namespace MWWorld
throw std::runtime_error ("class does not support ID retrieval");
}
void Class::insertObj (const Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const
{
}
void Class::enable (const Ptr& ptr, MWWorld::Environment& environment) const
{
}
void Class::disable (const Ptr& ptr, MWWorld::Environment& environment) const
{
}
MWMechanics::CreatureStats& Class::getCreatureStats (const Ptr& ptr) const
{
throw std::runtime_error ("class does not have creature stats");

@ -10,6 +10,11 @@
#include "containerstore.hpp"
#include "refdata.hpp"
namespace MWRender
{
class CellRenderImp;
}
namespace MWMechanics
{
struct CreatureStats;
@ -18,6 +23,7 @@ namespace MWMechanics
namespace MWWorld
{
class Ptr;
class Environment;
/// \brief Base class for referenceable esm records
class Class
@ -40,6 +46,20 @@ namespace MWWorld
///< Return ID of \a ptr or throw an exception, if class does not support ID retrieval
/// (default implementation: throw an exception)
virtual void insertObj (const Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const;
///< Add reference into a cell for rendering (default implementation: don't render anything).
virtual void enable (const Ptr& ptr, MWWorld::Environment& environment) const;
///< Enable reference; only does the non-rendering part (default implementation: ignore)
/// \attention This is not the same as the script instruction with the same name. References
/// should only be enabled while in an active cell.
virtual void disable (const Ptr& ptr, MWWorld::Environment& environment) const;
///< Enable reference; only does the non-rendering part (default implementation: ignore)
/// \attention This is not the same as the script instruction with the same name. References
/// should only be enabled while in an active cell.
virtual std::string getName (const Ptr& ptr) const = 0;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

@ -11,8 +11,11 @@
#include "../mwmechanics/mechanicsmanager.hpp"
#include "../mwsound/soundmanager.hpp"
#include "ptr.hpp"
#include "environment.hpp"
#include "class.hpp"
namespace
{
@ -372,12 +375,8 @@ namespace MWWorld
{
render->enable (reference.getRefData().getHandle());
if (mActiveCells.find (reference.getCell())!=mActiveCells.end() &&
(reference.getType()==typeid (ESMS::LiveCellRef<ESM::NPC, RefData>) ||
reference.getType()==typeid (ESMS::LiveCellRef<ESM::Creature, RefData>)))
{
mEnvironment.mMechanicsManager->addActor (reference);
}
if (mActiveCells.find (reference.getCell())!=mActiveCells.end())
Class::get (reference).enable (reference, mEnvironment);
}
}
}
@ -392,11 +391,10 @@ namespace MWWorld
{
render->disable (reference.getRefData().getHandle());
if (mActiveCells.find (reference.getCell())!=mActiveCells.end() &&
(reference.getType()==typeid (ESMS::LiveCellRef<ESM::NPC, RefData>) ||
reference.getType()==typeid (ESMS::LiveCellRef<ESM::Creature, RefData>)))
if (mActiveCells.find (reference.getCell())!=mActiveCells.end())
{
mEnvironment.mMechanicsManager->removeActor (reference);
Class::get (reference).disable (reference, mEnvironment);
mEnvironment.mSoundManager->stopSound3D (reference);
}
}
}
@ -535,6 +533,7 @@ namespace MWWorld
{
mEnvironment.mMechanicsManager->dropActors (active->first);
active->second->destroy();
mEnvironment.mSoundManager->stopSound (active->first);
delete active->second;
mActiveCells.erase (active);
}
@ -551,7 +550,7 @@ namespace MWWorld
// This connects the cell data with the rendering scene.
std::pair<CellRenderCollection::iterator, bool> result =
mActiveCells.insert (std::make_pair (cell,
new MWRender::InteriorCellRender (*cell, mStore, mScene)));
new MWRender::InteriorCellRender (*cell, mEnvironment, mScene)));
if (result.second)
{
@ -563,28 +562,6 @@ namespace MWWorld
mEnvironment.mMechanicsManager->addActor (mPlayerPos->getPlayer());
mEnvironment.mMechanicsManager->watchActor (mPlayerPos->getPlayer());
for (ESMS::CellRefList<ESM::Creature, RefData>::List::iterator iter (
cell->creatures.list.begin());
iter!=cell->creatures.list.end(); ++iter)
{
if (iter->mData.isEnabled())
{
Ptr ptr (&*iter, cell);
mEnvironment.mMechanicsManager->addActor (ptr);
}
}
for (ESMS::CellRefList<ESM::NPC, RefData>::List::iterator iter (
cell->npcs.list.begin());
iter!=cell->npcs.list.end(); ++iter)
{
if (iter->mData.isEnabled())
{
Ptr ptr (&*iter, cell);
mEnvironment.mMechanicsManager->addActor (ptr);
}
}
// Sky system
if (mSky)
{
@ -623,11 +600,10 @@ namespace MWWorld
render->deleteObject (ptr.getRefData().getHandle());
ptr.getRefData().setHandle ("");
if (mActiveCells.find (ptr.getCell())!=mActiveCells.end() &&
(ptr.getType()==typeid (ESMS::LiveCellRef<ESM::NPC, RefData>) ||
ptr.getType()==typeid (ESMS::LiveCellRef<ESM::Creature, RefData>)))
if (mActiveCells.find (ptr.getCell())!=mActiveCells.end())
{
mEnvironment.mMechanicsManager->removeActor (ptr);
Class::get (ptr).disable (ptr, mEnvironment);
mEnvironment.mSoundManager->stopSound3D (ptr);
}
}
}

@ -0,0 +1,57 @@
# Locate Audiere
# This module defines
# AUDIERE_LIBRARY
# AUDIERE_FOUND, if false, do not try to link to Audiere
# AUDIERE_INCLUDE_DIR, where to find the headers
#
# Created by Nicolay Korslund for OpenMW (http://openmw.com)
#
# More or less a direct ripoff of FindOpenAL.cmake by Eric Wing.
#=============================================================================
# Copyright 2005-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distributed this file outside of CMake, substitute the full
# License text for the above reference.)
FIND_PATH(AUDIERE_INCLUDE_DIR audiere.h
HINTS
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
FIND_LIBRARY(AUDIERE_LIBRARY
NAMES audiere
HINTS
PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw
/opt/local
/opt/csw
/opt
)
SET(AUDIERE_FOUND "NO")
IF(AUDIERE_LIBRARY AND AUDIERE_INCLUDE_DIR)
SET(AUDIERE_FOUND "YES")
ENDIF(AUDIERE_LIBRARY AND AUDIERE_INCLUDE_DIR)

@ -0,0 +1,90 @@
# Find the FFmpeg library
#
# Sets
# FFMPEG_FOUND. If false, don't try to use ffmpeg
# FFMPEG_INCLUDE_DIR
# FFMPEG_LIBRARIES
#
# Modified by Nicolay Korslund for OpenMW
SET( FFMPEG_FOUND "NO" )
FIND_PATH( FFMPEG_avcodec_INCLUDE_DIR avcodec.h
HINTS
PATHS
/usr/include
/usr/local/include
/usr/include/ffmpeg
/usr/local/include/ffmpeg
/usr/include/ffmpeg/libavcodec
/usr/local/include/ffmpeg/libavcodec
/usr/include/libavcodec
/usr/local/include/libavcodec
)
FIND_PATH( FFMPEG_avformat_INCLUDE_DIR avformat.h
HINTS
PATHS
/usr/include
/usr/local/include
/usr/include/ffmpeg
/usr/local/include/ffmpeg
/usr/include/ffmpeg/libavformat
/usr/local/include/ffmpeg/libavformat
/usr/include/libavformat
/usr/local/include/libavformat
)
set(FFMPEG_INCLUDE_DIR ${FFMPEG_avcodec_INCLUDE_DIR} ${FFMPEG_avformat_INCLUDE_DIR})
IF( FFMPEG_INCLUDE_DIR )
FIND_PROGRAM( FFMPEG_CONFIG ffmpeg-config
/usr/bin
/usr/local/bin
${HOME}/bin
)
IF( FFMPEG_CONFIG )
EXEC_PROGRAM( ${FFMPEG_CONFIG} ARGS "--libs avformat" OUTPUT_VARIABLE FFMPEG_LIBS )
SET( FFMPEG_FOUND "YES" )
SET( FFMPEG_LIBRARIES "${FFMPEG_LIBS}" )
ELSE( FFMPEG_CONFIG )
FIND_LIBRARY( FFMPEG_avcodec_LIBRARY avcodec
/usr/lib
/usr/local/lib
/usr/lib64
/usr/local/lib64
)
FIND_LIBRARY( FFMPEG_avformat_LIBRARY avformat
/usr/lib
/usr/local/lib
/usr/lib64
/usr/local/lib64
)
FIND_LIBRARY( FFMPEG_avutil_LIBRARY avutil
/usr/lib
/usr/local/lib
/usr/lib64
/usr/local/lib64
)
IF( FFMPEG_avcodec_LIBRARY )
IF( FFMPEG_avformat_LIBRARY )
SET( FFMPEG_FOUND "YES" )
SET( FFMPEG_LIBRARIES ${FFMPEG_avformat_LIBRARY} ${FFMPEG_avcodec_LIBRARY} )
IF( FFMPEG_avutil_LIBRARY )
SET( FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${FFMPEG_avutil_LIBRARY} )
ENDIF( FFMPEG_avutil_LIBRARY )
ENDIF( FFMPEG_avformat_LIBRARY )
ENDIF( FFMPEG_avcodec_LIBRARY )
ENDIF( FFMPEG_CONFIG )
ENDIF( FFMPEG_INCLUDE_DIR )

@ -14,6 +14,11 @@ IF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
SET(ICONV_FIND_QUIETLY TRUE)
ENDIF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
IF(WIN32)
SET(ICONV_INCLUDE_DIR $ENV{ICONV_INCLUDE_DIR})
SET(ICONV_LIBRARIES $ENV{ICONV_LIBRARIES})
ENDIF(WIN32)
FIND_PATH(ICONV_INCLUDE_DIR iconv.h)
FIND_LIBRARY(ICONV_LIBRARIES NAMES iconv libiconv c)

@ -0,0 +1,47 @@
# Locate MPG123
# This module defines
# MPG123_LIBRARY
# MPG123_FOUND, if false, do not try to link to Mpg123
# MPG123_INCLUDE_DIR, where to find the headers
#
# Created by Nicolay Korslund for OpenMW (http://openmw.com)
#
# Ripped off from other sources. In fact, this file is so generic (I
# just did a search and replace on another file) that I wonder why the
# CMake guys haven't wrapped this entire thing in a single
# function. Do we really need to repeat this stuff for every single
# library when they all work the same? </today's rant>
FIND_PATH(MPG123_INCLUDE_DIR mpg123.h
HINTS
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
FIND_LIBRARY(MPG123_LIBRARY
NAMES mpg123
HINTS
PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw
/opt/local
/opt/csw
/opt
)
SET(MPG123_FOUND "NO")
IF(MPG123_LIBRARY AND MPG123_INCLUDE_DIR)
SET(MPG123_FOUND "YES")
ENDIF(MPG123_LIBRARY AND MPG123_INCLUDE_DIR)

@ -0,0 +1,41 @@
# Locate SNDFILE
# This module defines
# SNDFILE_LIBRARY
# SNDFILE_FOUND, if false, do not try to link to Sndfile
# SNDFILE_INCLUDE_DIR, where to find the headers
#
# Created by Nicolay Korslund for OpenMW (http://openmw.com)
FIND_PATH(SNDFILE_INCLUDE_DIR sndfile.h
HINTS
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
FIND_LIBRARY(SNDFILE_LIBRARY
NAMES sndfile
HINTS
PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw
/opt/local
/opt/csw
/opt
)
SET(SNDFILE_FOUND "NO")
IF(SNDFILE_LIBRARY AND SNDFILE_INCLUDE_DIR)
SET(SNDFILE_FOUND "YES")
ENDIF(SNDFILE_LIBRARY AND SNDFILE_INCLUDE_DIR)

@ -316,14 +316,15 @@ namespace Compiler
name += c;
break;
}
else if (c=='\\')
{
if (!get (c))
{
mErrorHandler.error ("incomplete escape sequence", mLoc);
break;
}
}
// ignoring escape sequences for now, because they are messing up stupid Windows path names.
// else if (c=='\\')
// {
// if (!get (c))
// {
// mErrorHandler.error ("incomplete escape sequence", mLoc);
// break;
// }
// }
else if (c=='\n')
{
mErrorHandler.error ("incomplete string or name", mLoc);
@ -513,4 +514,3 @@ namespace Compiler
mPutbackLoc = loc;
}
}

@ -9,7 +9,10 @@
#include <sstream>
#include <iomanip>
#include <errno.h>
#include <iconv.h>
#ifndef __WIN32__
#include <iconv.h>
#endif
#include <libs/mangle/stream/stream.hpp>
#include <libs/mangle/stream/servers/file_stream.hpp>
@ -619,12 +622,15 @@ public:
}
// Convert a string from the encoding used by Morrowind to UTF-8
std::string convertToUTF8(std::string input)
std::string convertToUTF8 (std::string input)
{
#ifdef __WIN32__
return input;
#else
std::string output = "";
//create convert description
iconv_t cd = iconv_open("UTF-8", "WINDOWS-1252");
iconv_t cd = iconv_open ("UTF-8", "WINDOWS-1252");
if (cd == (iconv_t)-1) //error handling
{
@ -645,7 +651,7 @@ public:
errMsg += "Unknown Error\n";
}
fail(errMsg);
fail (errMsg);
}
else
@ -655,7 +661,7 @@ public:
if (inputSize) //input is not empty
{
//convert function doesn't accept const char *, therefore copy content into an char *
std::vector<char> inputBuffer(input.begin(), input.end());
std::vector<char> inputBuffer (input.begin(), input.end());
char *inputBufferBegin = &inputBuffer[0];
size_t inputBytesLeft = inputSize; //bytes to convert
@ -666,33 +672,33 @@ public:
char outputBuffer[outputSize];
char *outputBufferBegin;
while (inputBytesLeft > 0 )
while (inputBytesLeft > 0)
{
outputBytesLeft = outputSize;
outputBufferBegin = outputBuffer;
if (iconv(cd, &inputBufferBegin, &inputBytesLeft, &outputBufferBegin, &outputBytesLeft) == (size_t)-1)
if (iconv (cd, &inputBufferBegin, &inputBytesLeft, &outputBufferBegin, &outputBytesLeft) == (size_t)-1)
{
switch (errno)
{
case E2BIG: //outputBuffer is full
output += std::string(outputBuffer, outputSize);
output += std::string (outputBuffer, outputSize);
break;
case EILSEQ:
fail("Iconv: Invalid multibyte sequence.\n");
fail ("Iconv: Invalid multibyte sequence.\n");
break;
case EINVAL:
fail("Iconv: Incomplete multibyte sequence.\n");
fail ("Iconv: Incomplete multibyte sequence.\n");
break;
default:
fail("Iconv: Unknown Error\n");
fail ("Iconv: Unknown Error\n");
}
}
}
//read only relevant bytes from outputBuffer
output += std::string(outputBuffer, outputSize - outputBytesLeft);
output += std::string (outputBuffer, outputSize - outputBytesLeft);
}
}
@ -701,6 +707,7 @@ public:
return output;
}
#endif
void skip(int bytes) { esm->seek(esm->tell()+bytes); }
uint64_t getOffset() { return esm->tell(); }

@ -0,0 +1,48 @@
#include "records.hpp"
/** Implementation for some of the load() functions. Most are found in
the header files themselves, but this is a bit irritating to
compile if you're changing the functions often, as virtually the
entire engine depends on these headers.
*/
namespace ESM
{
void NPC::load(ESMReader &esm, const std::string& id)
{
mId = id;
npdt52.gold = -10;
model = esm.getHNOString("MODL");
name = esm.getHNOString("FNAM");
race = esm.getHNString("RNAM");
cls = esm.getHNString("CNAM");
faction = esm.getHNString("ANAM");
head = esm.getHNString("BNAM");
hair = esm.getHNString("KNAM");
script = esm.getHNOString("SCRI");
esm.getSubNameIs("NPDT");
esm.getSubHeader();
if(esm.getSubSize() == 52) esm.getExact(&npdt52, 52);
else if(esm.getSubSize() == 12) esm.getExact(&npdt12, 12);
else esm.fail("NPC_NPDT must be 12 or 52 bytes long");
esm.getHNT(flags, "FLAG");
inventory.load(esm);
spells.load(esm);
if(esm.isNextSub("AIDT"))
{
esm.getHExact(&AI, sizeof(AI));
hasAI = true;
}
else hasAI = false;
esm.skipRecord();
}
}

@ -57,10 +57,10 @@ struct NPC
char strength, intelligence, willpower, agility,
speed, endurance, personality, luck;
char skills[27];
char reputation;
short health, mana, fatigue;
char disposition;
char reputation; // Was "factionID", but that makes no sense.
char rank, unknown, u2;
char disposition, factionID, rank;
char unknown;
int gold;
}; // 52 bytes
@ -99,43 +99,8 @@ struct NPC
std::string mId;
void load(ESMReader &esm, const std::string& id)
{
mId = id;
npdt52.gold = -10;
model = esm.getHNOString("MODL");
name = esm.getHNOString("FNAM");
race = esm.getHNString("RNAM");
cls = esm.getHNString("CNAM");
faction = esm.getHNString("ANAM");
head = esm.getHNString("BNAM");
hair = esm.getHNString("KNAM");
script = esm.getHNOString("SCRI");
esm.getSubNameIs("NPDT");
esm.getSubHeader();
if(esm.getSubSize() == 52) esm.getExact(&npdt52, 52);
else if(esm.getSubSize() == 12) esm.getExact(&npdt12, 12);
else esm.fail("NPC_NPDT must be 12 or 52 bytes long");
esm.getHNT(flags, "FLAG");
inventory.load(esm);
spells.load(esm);
if(esm.isNextSub("AIDT"))
{
esm.getHExact(&AI, sizeof(AI));
hasAI = true;
}
else hasAI = false;
esm.skipRecord();
}
// Implementation moved to load_impl.cpp
void load(ESMReader &esm, const std::string& id);
};
}
#endif

@ -26,12 +26,13 @@
#include <stdio.h>
#include <libs/mangle/vfs/servers/ogre_vfs.hpp>
#include "components/nif/nif_file.hpp"
#include "components/nif/node.hpp"
#include "components/nif/data.hpp"
#include "components/nif/property.hpp"
#include "libs/platform/strings.h"
#include "../nif/nif_file.hpp"
#include "../nif/node.hpp"
#include "../nif/data.hpp"
#include "../nif/property.hpp"
#include <libs/platform/strings.h>
#include <vector>
// For warning messages
#include <iostream>
@ -45,21 +46,43 @@ using namespace Ogre;
using namespace Nif;
using namespace Mangle::VFS;
// This is the interface to the Ogre resource system. It allows us to
// load NIFs from BSAs, in the file system and in any other place we
// tell Ogre to look (eg. in zip or rar files.) It's also used to
// check for the existence of texture files, so we can exchange the
// extension from .tga to .dds if the texture is missing.
static OgreVFS *vfs;
NIFLoader& NIFLoader::getSingleton()
{
static NIFLoader instance;
return instance;
}
// Singleton instance used by load()
static NIFLoader g_sing;
NIFLoader* NIFLoader::getSingletonPtr()
{
return &getSingleton();
}
void NIFLoader::warn(string msg)
{
std::cerr << "NIFLoader: Warn:" << msg << "\n";
}
void NIFLoader::fail(string msg)
{
std::cerr << "NIFLoader: Fail: "<< msg << std::endl;
assert(1);
}
Vector3 NIFLoader::convertVector3(const Nif::Vector& vec)
{
return Ogre::Vector3(vec.array);
}
// Makeshift error reporting system
static string errName;
static void warn(const string &msg)
Quaternion NIFLoader::convertRotation(const Nif::Matrix& rot)
{
cout << "WARNING (NIF:" << errName << "): " << msg << endl;
Real matrix[3][3];
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
matrix[i][j] = rot.v[i].array[j];
return Quaternion(Matrix3(matrix));
}
// Helper class that computes the bounding box and of a mesh
@ -77,8 +100,8 @@ class BoundsFinder
void add(float f)
{
if(f > max) max = f;
if(f < min) min = f;
if (f > max) max = f;
if (f < min) min = f;
}
// Return Max(max**2, min**2)
@ -86,7 +109,7 @@ class BoundsFinder
{
float m1 = max*max;
float m2 = min*min;
if(m1 >= m2) return m1;
if (m1 >= m2) return m1;
return m2;
}
};
@ -99,7 +122,7 @@ public:
// point.
void add(float *data, int verts)
{
for(int i=0;i<verts;i++)
for (int i=0;i<verts;i++)
{
X.add(*(data++));
Y.add(*(data++));
@ -126,12 +149,24 @@ public:
return sqrt(X.getMaxSquared() + Y.getMaxSquared() + Z.getMaxSquared());
}
float minX() { return X.min; }
float maxX() { return X.max; }
float minY() { return Y.min; }
float maxY() { return Y.max; }
float minZ() { return Z.min; }
float maxZ() { return Z.max; }
float minX() {
return X.min;
}
float maxX() {
return X.max;
}
float minY() {
return Y.min;
}
float maxY() {
return Y.max;
}
float minZ() {
return Z.min;
}
float maxZ() {
return Z.max;
}
};
// Conversion of blend / test mode from NIF -> OGRE.
@ -177,7 +212,7 @@ static CompareFunction getTestMode(int mode)
}
*/
static void createMaterial(const String &name,
void NIFLoader::createMaterial(const String &name,
const Vector &ambient,
const Vector &diffuse,
const Vector &specular,
@ -186,17 +221,18 @@ static void createMaterial(const String &name,
float alphaFlags, float alphaTest,
const String &texName)
{
MaterialPtr material = MaterialManager::getSingleton().create(name, "General");
MaterialPtr material = MaterialManager::getSingleton().create(name, resourceGroup);
// This assigns the texture to this material. If the texture name is
// a file name, and this file exists (in a resource directory), it
// will automatically be loaded when needed. If not (such as for
// internal NIF textures that we might support later), we should
// already have inserted a manual loader for the texture.
if(!texName.empty())
if (!texName.empty())
{
Pass *pass = material->getTechnique(0)->getPass(0);
/*TextureUnitState *txt =*/ pass->createTextureUnitState(texName);
/*TextureUnitState *txt =*/
pass->createTextureUnitState(texName);
/* As of yet UNTESTED code from Chris:
pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC);
@ -226,12 +262,12 @@ static void createMaterial(const String &name,
*/
// Add transparency if NiAlphaProperty was present
if(alphaFlags != -1)
if (alphaFlags != -1)
{
// The 237 alpha flags are by far the most common. Check
// NiAlphaProperty in nif/property.h if you need to decode
// other values. 237 basically means normal transparencly.
if(alphaFlags == 237)
if (alphaFlags == 237)
{
// Enable transparency
pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
@ -254,14 +290,14 @@ static void createMaterial(const String &name,
// Takes a name and adds a unique part to it. This is just used to
// make sure that all materials are given unique names.
static String getUniqueName(const String &input)
String NIFLoader::getUniqueName(const String &input)
{
static int addon = 0;
static char buf[8];
snprintf(buf, 8, "_%d", addon++);
// Don't overflow the buffer
if(addon > 999999) addon = 0;
if (addon > 999999) addon = 0;
return input + buf;
}
@ -270,13 +306,13 @@ static String getUniqueName(const String &input)
// does not, change the string IN PLACE to say .dds instead and try
// that. The texture may still not exist, but no information of value
// is lost in that case.
static void findRealTexture(String &texName)
void NIFLoader::findRealTexture(String &texName)
{
assert(vfs);
if(vfs->isFile(texName)) return;
if (vfs->isFile(texName)) return;
int len = texName.size();
if(len < 4) return;
if (len < 4) return;
// Change texture extension to .dds
texName[len-3] = 'd';
@ -286,7 +322,7 @@ static void findRealTexture(String &texName)
// Convert Nif::NiTriShape to Ogre::SubMesh, attached to the given
// mesh.
static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material)
void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material)
{
NiTriShapeData *data = shape->data.getPtr();
SubMesh *sub = mesh->createSubMesh(shape->name.toString());
@ -301,18 +337,21 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
sub->vertexData = new VertexData();
sub->vertexData->vertexCount = numVerts;
sub->useSharedVertices = false;
VertexDeclaration *decl = sub->vertexData->vertexDeclaration;
decl->addElement(nextBuf, 0, VET_FLOAT3, VES_POSITION);
HardwareVertexBufferSharedPtr vbuf =
HardwareBufferManager::getSingleton().createVertexBuffer(
VertexElement::getTypeSize(VET_FLOAT3),
numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
vbuf->writeData(0, vbuf->getSizeInBytes(), data->vertices.ptr, true);
VertexBufferBinding* bind = sub->vertexData->vertexBufferBinding;
bind->setBinding(nextBuf++, vbuf);
// Vertex normals
if(data->normals.length)
if (data->normals.length)
{
decl->addElement(nextBuf, 0, VET_FLOAT3, VES_NORMAL);
vbuf = HardwareBufferManager::getSingleton().createVertexBuffer(
@ -323,13 +362,13 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
}
// Vertex colors
if(data->colors.length)
if (data->colors.length)
{
const float *colors = data->colors.ptr;
RenderSystem* rs = Root::getSingleton().getRenderSystem();
std::vector<RGBA> colorsRGB(numVerts);
RGBA *pColour = &colorsRGB.front();
for(int i=0; i<numVerts; i++)
for (int i=0; i<numVerts; i++)
{
rs->convertColourValue(ColourValue(colors[0],colors[1],colors[2],
colors[3]),pColour++);
@ -344,7 +383,7 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
}
// Texture UV coordinates
if(data->uvlist.length)
if (data->uvlist.length)
{
decl->addElement(nextBuf, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES);
vbuf = HardwareBufferManager::getSingleton().createVertexBuffer(
@ -357,7 +396,7 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
// Triangle faces
int numFaces = data->triangles.length;
if(numFaces)
if (numFaces)
{
HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton().
createIndexBuffer(HardwareIndexBuffer::IT_16BIT,
@ -370,23 +409,7 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
}
// Set material if one was given
if(!material.empty()) sub->setMaterialName(material);
/* Old commented D code. Might be useful when reimplementing
animation.
// Assign this submesh to the given bone
VertexBoneAssignment v;
v.boneIndex = ((Bone*)bone)->getHandle();
v.weight = 1.0;
std::cerr << "+ Assigning bone index " << v.boneIndex << "\n";
for(int i=0; i < numVerts; i++)
{
v.vertexIndex = i;
sub->addBoneAssignment(v);
}
*/
if (!material.empty()) sub->setMaterialName(material);
}
// Helper math functions. Reinventing linear algebra for the win!
@ -394,7 +417,7 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
// Computes B = AxB (matrix*matrix)
static void matrixMul(const Matrix &A, Matrix &B)
{
for(int i=0;i<3;i++)
for (int i=0;i<3;i++)
{
float a = B.v[0].array[i];
float b = B.v[1].array[i];
@ -415,7 +438,7 @@ static void vectorMulAdd(const Matrix &A, const Vector &B, float *C, float scale
float c = C[2];
// Perform matrix multiplication, scaling and addition
for(int i=0;i<3;i++)
for (int i=0;i<3;i++)
C[i] = B.array[i] + (a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2])*scale;
}
@ -428,11 +451,11 @@ static void vectorMul(const Matrix &A, float *C)
float c = C[2];
// Perform matrix multiplication, scaling and addition
for(int i=0;i<3;i++)
for (int i=0;i<3;i++)
C[i] = a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2];
}
static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFinder &bounds)
void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bounds)
{
assert(shape != NULL);
@ -442,7 +465,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision
// Bounding box collision isn't implemented, always use mesh for now.
if(bbcollide)
if (bbcollide)
{
collide = true;
bbcollide = false;
@ -450,10 +473,13 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
// If the object was marked "NCO" earlier, it shouldn't collide with
// anything.
if(flags & 0x800)
{ collide = false; bbcollide = false; }
if (flags & 0x800)
{
collide = false;
bbcollide = false;
}
if(!collide && !bbcollide && hidden)
if (!collide && !bbcollide && hidden)
// This mesh apparently isn't being used for anything, so don't
// bother setting it up.
return;
@ -462,7 +488,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
String material;
// Skip the entire material phase for hidden nodes
if(!hidden)
if (!hidden)
{
// These are set below if present
NiTexturingProperty *t = NULL;
@ -472,27 +498,27 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
// Scan the property list for material information
PropertyList &list = shape->props;
int n = list.length();
for(int i=0; i<n; i++)
for (int i=0; i<n; i++)
{
// Entries may be empty
if(!list.has(i)) continue;
if (!list.has(i)) continue;
Property *pr = &list[i];
if(pr->recType == RC_NiTexturingProperty)
if (pr->recType == RC_NiTexturingProperty)
t = (NiTexturingProperty*)pr;
else if(pr->recType == RC_NiMaterialProperty)
else if (pr->recType == RC_NiMaterialProperty)
m = (NiMaterialProperty*)pr;
else if(pr->recType == RC_NiAlphaProperty)
else if (pr->recType == RC_NiAlphaProperty)
a = (NiAlphaProperty*)pr;
}
// Texture
String texName;
if(t && t->textures[0].inUse)
if (t && t->textures[0].inUse)
{
NiSourceTexture *st = t->textures[0].texture.getPtr();
if(st->external)
if (st->external)
{
SString tname = st->filename;
@ -518,14 +544,14 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
// Alpha modifiers
int alphaFlags = -1;
ubyte alphaTest = 0;
if(a)
if (a)
{
alphaFlags = a->flags;
alphaTest = a->data->threshold;
}
// Material
if(m || !texName.empty())
if (m || !texName.empty())
{
// If we're here, then this mesh has a material. Thus we
// need to calculate a snappy material name. It should
@ -533,7 +559,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
// be unique. One mesh may use many materials.
material = getUniqueName(mesh->getName());
if(m)
if (m)
{
// Use NiMaterialProperty data to create the data
const S_MaterialProperty *d = m->data;
@ -545,7 +571,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
// We only have a texture name. Create a default
// material for it.
Vector zero, one;
for(int i=0; i<3;i++)
for (int i=0; i<3;i++)
{
zero.array[i] = 0.0;
one.array[i] = 1.0;
@ -569,39 +595,113 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
float *ptr = (float*)data->vertices.ptr;
float *optr = ptr;
// Rotate, scale and translate all the vertices
//use niskindata for the position of vertices.
if (!shape->skin.empty())
{
// vector that stores if the position if a vertex is absolute
std::vector<bool> vertexPosAbsolut(numVerts,false);
float *ptrNormals = (float*)data->normals.ptr;
//the bone from skin->bones[boneIndex] is linked to skin->data->bones[boneIndex]
//the first one contains a link to the bone, the second vertex transformation
//relative to the bone
int boneIndex = 0;
Bone *bonePtr;
Vector3 vecPos;
Quaternion vecRot;
std::vector<NiSkinData::BoneInfo> boneList = shape->skin->data->bones;
/*
Iterate through the boneList which contains what vertices are linked to
the bone (it->weights array) and at what position (it->trafo)
That position is added to every vertex.
*/
for (std::vector<NiSkinData::BoneInfo>::iterator it = boneList.begin();
it != boneList.end(); it++)
{
//get the bone from bones array of skindata
bonePtr = skel->getBone(shape->skin->bones[boneIndex].name.toString());
// final_vector = old_vector + old_rotation*new_vector*old_scale
vecPos = bonePtr->_getDerivedPosition() +
bonePtr->_getDerivedOrientation() * convertVector3(it->trafo->trans);
vecRot = bonePtr->_getDerivedOrientation() * convertRotation(it->trafo->rotation);
for (unsigned int i=0; i<it->weights.length; i++)
{
unsigned int verIndex = (it->weights.ptr + i)->vertex;
//Check if the vertex is relativ, FIXME: Is there a better solution?
if (vertexPosAbsolut[verIndex] == false)
{
//apply transformation to the vertices
Vector3 absVertPos = vecPos + vecRot * Vector3(ptr + verIndex *3);
//convert it back to float *
for (int j=0; j<3; j++)
(ptr + verIndex*3)[j] = absVertPos[j];
//apply rotation to the normals (not every vertex has a normal)
//FIXME: I guessed that vertex[i] = normal[i], is that true?
if (verIndex < data->normals.length)
{
Vector3 absNormalsPos = vecRot * Vector3(ptrNormals + verIndex *3);
for (int j=0; j<3; j++)
(ptrNormals + verIndex*3)[j] = absNormalsPos[j];
}
//TODO: create vbas, and give them to createOgreSubMesh
vertexPosAbsolut[verIndex] = true;
}
}
boneIndex++;
//it->trafo (pos, rot, scale) of the vertex
//it->weights array containt the vertices linked to the bone and the weight
}
}
else
{
// Rotate, scale and translate all the vertices,
const Matrix &rot = shape->trafo->rotation;
const Vector &pos = shape->trafo->pos;
float scale = shape->trafo->scale;
for(int i=0; i<numVerts; i++)
for (int i=0; i<numVerts; i++)
{
vectorMulAdd(rot, pos, ptr, scale);
ptr += 3;
}
// Remember to rotate all the vertex normals as well
if(data->normals.length)
if (data->normals.length)
{
ptr = (float*)data->normals.ptr;
for(int i=0; i<numVerts; i++)
for (int i=0; i<numVerts; i++)
{
vectorMul(rot, ptr);
ptr += 3;
}
}
}
if(!hidden)
if (!hidden)
{
// Add this vertex set to the bounding box
bounds.add(optr, numVerts);
// Create the submesh
createOgreMesh(mesh, shape, material);
createOgreSubMesh(shape, material);
}
}
static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
const Transformation *trafo, BoundsFinder &bounds)
void NIFLoader::handleNode(Nif::Node *node, int flags,
const Transformation *trafo, BoundsFinder &bounds, Bone *parentBone)
{
// Accumulate the flags from all the child nodes. This works for all
// the flags we currently use, at least.
@ -609,22 +709,22 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
// Check for extra data
Extra *e = node;
while(!e->extra.empty())
while (!e->extra.empty())
{
// Get the next extra data in the list
e = e->extra.getPtr();
assert(e != NULL);
if(e->recType == RC_NiStringExtraData)
if (e->recType == RC_NiStringExtraData)
{
// String markers may contain important information
// affecting the entire subtree of this node
NiStringExtraData *sd = (NiStringExtraData*)e;
if(sd->string == "NCO")
if (sd->string == "NCO")
// No collision. Use an internal flag setting to mark this.
flags |= 0x800;
else if(sd->string == "MRK")
else if (sd->string == "MRK")
// Marker objects. These are only visible in the
// editor. Until and unless we add an editor component to
// the engine, just skip this entire node.
@ -632,9 +732,32 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
}
}
Bone *bone = 0;
// create skeleton or add bones
if (node->recType == RC_NiNode)
{
if (node->name == "Bip01") //root node, create a skeleton
{
skel = SkeletonManager::getSingleton().create(getSkeletonName(), resourceGroup, true);
}
if (!skel.isNull()) //if there is a skeleton
{
bone = skel->createBone(node->name.toString());
if (parentBone)
parentBone->addChild(bone);
bone->setInheritOrientation(true);
bone->setPosition(convertVector3(node->trafo->pos));
bone->setOrientation(convertRotation(node->trafo->rotation));
}
}
// Apply the parent transformation to this node. We overwrite the
// existing data with the final transformation.
if(trafo)
if (trafo)
{
// Get a non-const reference to the node's data, since we're
// overwriting it. TODO: Is this necessary?
@ -654,34 +777,38 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
}
// For NiNodes, loop through children
if(node->recType == RC_NiNode)
if (node->recType == RC_NiNode)
{
NodeList &list = ((NiNode*)node)->children;
int n = list.length();
for(int i=0; i<n; i++)
for (int i=0; i<n; i++)
{
if(list.has(i))
handleNode(mesh, &list[i], flags, node->trafo, bounds);
if (list.has(i))
handleNode(&list[i], flags, node->trafo, bounds, bone);
}
}
else if(node->recType == RC_NiTriShape)
else if (node->recType == RC_NiTriShape)
// For shapes
handleNiTriShape(mesh, dynamic_cast<NiTriShape*>(node), flags, bounds);
handleNiTriShape(dynamic_cast<NiTriShape*>(node), flags, bounds);
}
void NIFLoader::loadResource(Resource *resource)
{
resourceName = "";
mesh = 0;
skel.setNull();
// Set up the VFS if it hasn't been done already
if(!vfs) vfs = new OgreVFS("General");
if (!vfs) vfs = new OgreVFS(resourceGroup);
// Get the mesh
Mesh *mesh = dynamic_cast<Mesh*>(resource);
mesh = dynamic_cast<Mesh*>(resource);
assert(mesh);
// Look it up
const String &name = mesh->getName();
errName = name; // Set name for error messages
if(!vfs->isFile(name))
resourceName = mesh->getName();
if (!vfs->isFile(resourceName))
{
warn("File not found.");
return;
@ -694,9 +821,9 @@ void NIFLoader::loadResource(Resource *resource)
// of the early stages of development. Right now we WANT to catch
// every error as early and intrusively as possible, as it's most
// likely a sign of incomplete code rather than faulty input.
NIFFile nif(vfs->open(name), name);
NIFFile nif(vfs->open(resourceName), resourceName);
if(nif.numRecords() < 1)
if (nif.numRecords() < 1)
{
warn("Found no records in NIF.");
return;
@ -708,7 +835,7 @@ void NIFLoader::loadResource(Resource *resource)
Nif::Node *node = dynamic_cast<Nif::Node*>(r);
if(node == NULL)
if (node == NULL)
{
warn("First record in file was not a node, but a " +
r->recName.toString() + ". Skipping file.");
@ -716,10 +843,14 @@ void NIFLoader::loadResource(Resource *resource)
}
// Handle the node
handleNode(mesh, node, 0, NULL, bounds);
handleNode(node, 0, NULL, bounds, 0);
//set skeleton
// if (!skel.isNull())
// mesh->setSkeletonName(getSkeletonName());
// Finally, set the bounding value.
if(bounds.isValid())
if (bounds.isValid())
{
mesh->_setBounds(AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(),
bounds.maxX(), bounds.maxY(), bounds.maxZ()));
@ -734,11 +865,11 @@ MeshPtr NIFLoader::load(const std::string &name,
// Check if the resource already exists
ResourcePtr ptr = m->getByName(name, group);
if(!ptr.isNull())
if (!ptr.isNull())
return MeshPtr(ptr);
// Nope, create a new one.
return MeshManager::getSingleton().createManual(name, group, &g_sing);
return MeshManager::getSingleton().createManual(name, group, NIFLoader::getSingletonPtr());
}
/* More code currently not in use, from the old D source. This was
@ -779,70 +910,5 @@ extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height,
pixelBuffer->unlock();
}
// We need this later for animated meshes.
extern "C" void* ogre_setupSkeleton(char* name)
{
SkeletonPtr skel = SkeletonManager::getSingleton().create(
name, "Closet", true);
skel->load();
// Create all bones at the origin and unrotated. This is necessary
// since our submeshes each have their own model space. We must
// move the bones after creating an entity, then copy this entity.
return (void*)skel->createBone();
}
extern "C" void *ogre_insertBone(char* name, void* rootBone, int32_t index)
{
return (void*) ( ((Bone*)rootBone)->createChild(index) );
}
*/
/* This was the D part:
// Create a skeleton and get the root bone (index 0)
BonePtr bone = ogre_setupSkeleton(name);
// Reset the bone index. The next bone to be created has index 1.
boneIndex = 1;
// Create a mesh and assign the skeleton to it
MeshPtr mesh = ogre_setupMesh(name);
// Loop through the nodes, creating submeshes, materials and
// skeleton bones in the process.
handleNode(node, bone, mesh);
// Create the "template" entity
EntityPtr entity = ogre_createEntity(name);
// Loop through once again, this time to set the right
// transformations on the entity's SkeletonInstance. The order of
// children will be the same, allowing us to reference bones using
// their boneIndex.
int lastBone = boneIndex;
boneIndex = 1;
transformBones(node, entity);
if(lastBone != boneIndex) writefln("WARNING: Bone number doesn't match");
if(!hasBBox)
ogre_setMeshBoundingBox(mesh, minX, minY, minZ, maxX, maxY, maxZ);
return entity;
}
void handleNode(Node node, BonePtr root, MeshPtr mesh)
{
// Insert a new bone for this node
BonePtr bone = ogre_insertBone(node.name, root, boneIndex++);
}
void transformBones(Node node, EntityPtr entity)
{
ogre_transformBone(entity, &node.trafo, boneIndex++);
NiNode n = cast(NiNode)node;
if(n !is null)
foreach(Node nd; n.children)
transformBones(nd, entity);
}
*/

@ -27,6 +27,27 @@
#include <OgreResource.h>
#include <OgreMesh.h>
#include <assert.h>
#include <string>
class BoundsFinder;
namespace Nif
{
class Node;
class Transformation;
class NiTriShape;
class Vector;
class Matrix;
}
namespace Mangle
{
namespace VFS
{
class OgreVFS;
}
}
/** Manual resource loader for NIF meshes. This is the main class
responsible for translating the internal NIF mesh structure into
@ -43,12 +64,66 @@
very resource intensive, and can safely be done for a large number
of meshes at load time.
*/
struct NIFLoader : Ogre::ManualResourceLoader
class NIFLoader : Ogre::ManualResourceLoader
{
void loadResource(Ogre::Resource *resource);
public:
static NIFLoader& getSingleton();
static NIFLoader* getSingletonPtr();
virtual void loadResource(Ogre::Resource *resource);
static Ogre::MeshPtr load(const std::string &name,
const std::string &group="General");
Ogre::Vector3 convertVector3(const Nif::Vector& vec);
Ogre::Quaternion convertRotation(const Nif::Matrix& rot);
private:
NIFLoader() : resourceGroup("General") {}
NIFLoader(NIFLoader& n) {}
void warn(std::string msg);
void fail(std::string msg);
void handleNode( Nif::Node *node, int flags,
const Nif::Transformation *trafo, BoundsFinder &bounds, Ogre::Bone *parentBone);
void handleNiTriShape(Nif::NiTriShape *shape, int flags, BoundsFinder &bounds);
void createOgreSubMesh(Nif::NiTriShape *shape, const Ogre::String &material);
void createMaterial(const Ogre::String &name,
const Nif::Vector &ambient,
const Nif::Vector &diffuse,
const Nif::Vector &specular,
const Nif::Vector &emissive,
float glossiness, float alpha,
float alphaFlags, float alphaTest,
const Ogre::String &texName);
void findRealTexture(Ogre::String &texName);
Ogre::String getUniqueName(const Ogre::String &input);
//returns the skeleton name of this mesh
std::string getSkeletonName()
{
return resourceName + ".skel";
}
// This is the interface to the Ogre resource system. It allows us to
// load NIFs from BSAs, in the file system and in any other place we
// tell Ogre to look (eg. in zip or rar files.) It's also used to
// check for the existence of texture files, so we can exchange the
// extension from .tga to .dds if the texture is missing.
Mangle::VFS::OgreVFS *vfs;
std::string resourceName;
std::string resourceGroup;
// pointer to the ogre mesh which is currently build
Ogre::Mesh *mesh;
Ogre::SkeletonPtr skel;
};
#endif

@ -1 +1 @@
Subproject commit c7b179d6546688208528c8eef681d42b7c1ec7be
Subproject commit c982f701cacdd2932bfdc22b168f54221a549b62

@ -1 +1 @@
Subproject commit c04d72cbe380217c2d1d60f8a2c6e4810fe4c050
Subproject commit b9d4dc448bc3be908653f9dea3c3450fb85ed107

@ -1,47 +0,0 @@
module scene.gamesettings;
import monster.monster;
import esm.records : gameSettings;
import esm.defs : VarType;
import std.stdio;
import std.string;
MonsterObject *gmstObj;
void loadGameSettings()
{
// Load the GameSettings Monster class, and get the singleton
// instance
MonsterClass mc = vm.load("GMST");
gmstObj = mc.getSing();
foreach(a, b; gameSettings.names)
{
assert(a == b.id);
assert(a[0] == 'i' || a[0] == 'f' || a[0] == 's');
// There's three cases of variable names containing
// spaces. Since these are so seldom, it makes more sense to
// make special workarounds for them instead of searching every
// string.
char[] name = a;
if(name.length > 13 && (name[6] == ' ' || name[5] == ' '))
{
name = name.replace(" ", "_");
// Make sure we don't change the original string!
assert(name != a);
}
if(!mc.sc.lookupName(name).isVar)
{
writefln("WARNING: GMST %s not supported!", name);
continue;
}
if(b.type == VarType.Int) gmstObj.setInt(name, b.i);
else if(b.type == VarType.Float) gmstObj.setFloat(name, b.f);
// TODO: At some point we will probably translate strings into
// UTF32 at load time, so string8 will not be needed here.
else if(b.type == VarType.String) gmstObj.setString8(name, b.str);
}
}

@ -1,96 +0,0 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (soundlist.d) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
http://www.gnu.org/licenses/ .
*/
module scene.soundlist;
import esm.loadsoun;
import sound.audio;
import sound.sfx;
SoundList soundScene;
// Has a list over all sounds currently playing in the
// scene. Currently only holds static, looping sounds, mainly
// torches. Later I will have to decide what to do with play-once
// sounds. Do I bother to update their position, or do I assume that
// once it starts playing, it is short enough that it doesn't matter?
// The last option is probably not viable, since some sounds can be
// very long.
struct SoundList
{
SoundInstance[] list;
// Get a sound instance from a Sound struct
static SoundInstance getInstance(Sound *s, bool loop=false)
{
const distFactor = 40.0; // Just guessing, really.
assert(!s.sound.isEmpty());
SoundInstance inst = s.sound.getInstance();
inst.setParams(s.data.volume/255.0,
s.data.minRange*distFactor,
s.data.maxRange*distFactor,
loop);
return inst;
}
SoundInstance *insert(Sound *snd, bool loop=false)
{
// For some reason, we get called with empty sound instances here
// if some files are missing, but not for others. Check into it
// later.
if(snd.sound.isEmpty) return null;
// Reuse a dead instance if one exists
foreach(ref s; list)
{
if(s.owner == null)
{
s = getInstance(snd, loop);
return &s;
}
}
// Otherwise append a new one
list ~= getInstance(snd, loop);
return &list[$-1];
}
void update(float x, float y, float z,
float frontx, float fronty, float frontz,
float upx, float upy, float upz)
{
SoundInstance.setPlayerPos(x,y,z,frontx,fronty,frontz,upx,upy,upz);
foreach(ref s; list)
if(s.owner) s.updateSound();
}
void kill()
{
foreach(ref s; list)
{
if(s.owner) s.kill();
}
list = null;
}
}
Loading…
Cancel
Save