diff --git a/CMakeLists.txt b/CMakeLists.txt index cd058c570e..32f4e483a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) @@ -9,17 +14,17 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) # source directory: components set(COMP_DIR ${CMAKE_SOURCE_DIR}/components) -set(BSA +set(BSA ${COMP_DIR}/bsa/bsa_archive.cpp ${COMP_DIR}/bsa/bsa_file.cpp) -set(BSA_HEADER +set(BSA_HEADER ${COMP_DIR}/bsa/bsa_archive.hpp ${COMP_DIR}/bsa/bsa_file.hpp) source_group(components\\bsa FILES ${BSA} ${BSA_HEADER}) -set(NIF +set(NIF ${COMP_DIR}/nif/nif_file.cpp) -set(NIF_HEADER +set(NIF_HEADER ${COMP_DIR}/nif/controlled.hpp ${COMP_DIR}/nif/effect.hpp ${COMP_DIR}/nif/nif_types.hpp @@ -31,44 +36,47 @@ set(NIF_HEADER ${COMP_DIR}/nif/data.hpp ${COMP_DIR}/nif/nif_file.hpp ${COMP_DIR}/nif/property.hpp) -source_group(components\\nif FILES ${NIF} ${NIF_HEADER}) +source_group(components\\nif FILES ${NIF} ${NIF_HEADER}) -set(NIFOGRE +set(NIFOGRE ${COMP_DIR}/nifogre/ogre_nif_loader.cpp) -set(NIFOGRE_HEADER +set(NIFOGRE_HEADER ${COMP_DIR}/nifogre/ogre_nif_loader.hpp) -source_group(components\\nifogre FILES ${NIFOGRE} ${NIFOGRE_HEADER}) +source_group(components\\nifogre FILES ${NIFOGRE} ${NIFOGRE_HEADER}) -set(ESM_STORE +set(ESM_STORE ${COMP_DIR}/esm_store/store.cpp) -set(ESM_STORE_HEADER +set(ESM_STORE_HEADER ${COMP_DIR}/esm_store/cell_store.hpp ${COMP_DIR}/esm_store/reclists.hpp ${COMP_DIR}/esm_store/store.hpp) 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 +set(MISC ${COMP_DIR}/misc/stringops.cpp ${COMP_DIR}/misc/fileops.cpp) -set(MISC_HEADER +set(MISC_HEADER ${COMP_DIR}/misc/fileops.hpp ${COMP_DIR}/misc/slice_array.hpp ${COMP_DIR}/misc/stringops.hpp) -source_group(components\\misc FILES ${MISC} ${MISC_HEADER}) - +source_group(components\\misc FILES ${MISC} ${MISC_HEADER}) + file(GLOB COMPILER ${COMP_DIR}/compiler/*.cpp) file(GLOB COMPILER_HEADER ${COMP_DIR}/compiler/*.hpp) -source_group(components\\compiler FILES ${COMPILER} ${COMPILER_HEADER}) +source_group(components\\compiler FILES ${COMPILER} ${COMPILER_HEADER}) file(GLOB INTERPRETER ${COMP_DIR}/interpreter/*.cpp) file(GLOB INTERPRETER_HEADER ${COMP_DIR}/interpreter/*.hpp) -source_group(components\\interpreter FILES ${INTERPRETER} ${INTERPRETER_HEADER}) +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,12 +98,53 @@ 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}) set(OPENMW_LIBS_HEADER) - + # Platform specific if (WIN32) set(PLATFORM_INCLUDE_DIR "platform") @@ -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} @@ -158,13 +208,13 @@ endif (CMAKE_COMPILER_IS_GNUCC) # Apple bundling if (APPLE) - set(MISC_FILES + set(MISC_FILES ${CMAKE_SOURCE_DIR}/files/openmw.cfg ${CMAKE_SOURCE_DIR}/files/mac/plugins.cfg ${CMAKE_SOURCE_DIR}/files/mac/ogre.cfg) -install(TARGETS openmw +install(TARGETS openmw BUNDLE DESTINATION . - RUNTIME DESTINATION ../MacOS + RUNTIME DESTINATION ../MacOS COMPONENT Runtime) install(FILES ${MISC_FILES} DESTINATION ../MacOS) set(CPACK_GENERATOR "Bundle") diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 151dab9f9a..35fc597fe9 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -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 diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 396e4bad13..8c5ee33f76 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -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(); diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 3cdc1f8508..69a5108030 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 66821a7c5d..3b48796ac9 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -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. diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 4b94d78d10..007b2ca77f 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index 4a514c94de..c2e6e25e12 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -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. diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index f4236b5f6c..5c06077b20 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 217a74a763..040342a69f 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -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. diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 3bea7d5b9c..179a060c6a 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index 3f15e2278e..70d1bca3b5 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -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. diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index c0e43bc3e9..c3bbfa9f0f 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index ea358be685..fb88e25dfd 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -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. diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 0cc5163085..9c1ed7a897 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index baeac23c7d..de54a9e68e 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -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. diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 1cdada64c5..fc9515fff6 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 85a89a9192..9f6708f401 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -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. diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 2c4bd35629..6fd8f996cc 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index fa3b6d6573..b6bf8808e5 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -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. diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 9162384af7..29d66d0128 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index d742fae8bd..8b781ba2f0 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -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. diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 7cb363bf81..e57511452b 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 6d08d557ca..5a1a15b1d4 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -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. diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index ab19694802..c87d1e128f 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index e15d9daee3..bb9866b8cf 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -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. diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index f29a0be1f7..57dca31481 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 01542baedd..0da1f0d3a0 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -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. diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b6a4bbc058..a12d939d8c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = @@ -53,6 +98,15 @@ namespace MWClass return *ptr.getRefData().getCreatureStats(); } +<<<<<<< HEAD:apps/openmw/mwclass/npc.cpp +======= + boost::shared_ptr Npc::activate (const MWWorld::Ptr& ptr, + const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const + { + return boost::shared_ptr (new MWWorld::ActionTalk (ptr)); + } + +>>>>>>> master:apps/openmw/mwclass/npc.cpp MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr) const { diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index a6f4a29652..dd54806bdf 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -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. diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 0799b20310..3fe38542ba 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index 208c26c560..9bf3b34a8e 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -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. diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 08ec391a85..bc6c4ba196 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index c17d53dabb..11399d2f6e 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -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. diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 9ed7f0e77a..253569afe6 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index 52d045ebed..3da591e01b 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -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. diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index f2bc4f6357..211d239c60 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -3,8 +3,28 @@ #include +#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 *ref = + ptr.get(); + + 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 ""; diff --git a/apps/openmw/mwclass/static.hpp b/apps/openmw/mwclass/static.hpp index fb80801827..abbf6cd5f5 100644 --- a/apps/openmw/mwclass/static.hpp +++ b/apps/openmw/mwclass/static.hpp @@ -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. diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index c292c32dd1..5a941360af 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -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 *ref = + ptr.get(); + + 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 *ref = diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 6bc96381cf..d9c7653ddd 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -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. diff --git a/apps/openmw/mwgui/mw_layouts.hpp b/apps/openmw/mwgui/mw_layouts.hpp index 8f1d7affc2..22e7b58fab 100644 --- a/apps/openmw/mwgui/mw_layouts.hpp +++ b/apps/openmw/mwgui/mw_layouts.hpp @@ -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. diff --git a/apps/openmw/mwrender/cellimp.cpp b/apps/openmw/mwrender/cellimp.cpp index 121f173835..a5b4b51c77 100644 --- a/apps/openmw/mwrender/cellimp.cpp +++ b/apps/openmw/mwrender/cellimp.cpp @@ -2,94 +2,56 @@ #include +#include "../mwworld/class.hpp" +#include "../mwworld/ptr.hpp" + using namespace MWRender; template -void insertObj(CellRenderImp& cellRender, T& liveRef, const ESMS::ESMStore& store) +void insertCellRefList (CellRenderImp& cellRender, MWWorld::Environment& environment, + T& cellRefList, ESMS::CellStore &cell) { - assert (liveRef.base != NULL); - const std::string &model = liveRef.base->model; - if(!model.empty()) - { - cellRender.insertBegin (liveRef.ref); - cellRender.insertMesh ("meshes\\" + model); - liveRef.mData.setHandle (cellRender.insertEnd (liveRef.mData.isEnabled())); - } -} - -template<> -void insertObj(CellRenderImp& cellRender, ESMS::LiveCellRef& liveRef, const ESMS::ESMStore& store) -{ - assert (liveRef.base != NULL); - const std::string &model = liveRef.base->model; - if(!model.empty()) - { - 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); - - liveRef.mData.setHandle (cellRender.insertEnd (liveRef.mData.isEnabled())); - } -} - -template<> -void insertObj(CellRenderImp& cellRender, ESMS::LiveCellRef& 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 -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 (!cellRefList.list.empty()) { - if (it->mData.getCount()) - insertObj (cellRender, *it, store); + const MWWorld::Class& class_ = + MWWorld::Class::get (MWWorld::Ptr (&*cellRefList.list.begin(), &cell)); + + for (typename T::List::iterator it = cellRefList.list.begin(); + it != cellRefList.list.end(); it++) + { + if (it->mData.getCount() || it->mData.isEnabled()) + { + MWWorld::Ptr ptr (&*it, &cell); + + class_.insertObj (ptr, cellRender, environment); + class_.enable (ptr, environment); + } + } } } -void CellRenderImp::insertCell(ESMS::CellStore &cell, const ESMS::ESMStore& store) +void CellRenderImp::insertCell(ESMS::CellStore &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); } diff --git a/apps/openmw/mwrender/cellimp.hpp b/apps/openmw/mwrender/cellimp.hpp index a4bb6868ce..fadba79a55 100644 --- a/apps/openmw/mwrender/cellimp.hpp +++ b/apps/openmw/mwrender/cellimp.hpp @@ -12,9 +12,9 @@ namespace ESM class CellRef; } -namespace ESMS +namespace MWWorld { - class ESMStore; + class Environment; } namespace MWRender @@ -33,14 +33,14 @@ namespace MWRender /// insert a mesh related to the most recent insertBegin call. virtual void insertMesh(const std::string &mesh) = 0; - + /// insert a light related to the most recent insertBegin call. virtual void insertLight(float r, float g, float b, float radius) = 0; - + /// finish inserting a new reference and return a handle to it. virtual std::string insertEnd (bool Enable) = 0; - - void insertCell(ESMS::CellStore &cell, const ESMS::ESMStore& store); + + void insertCell(ESMS::CellStore &cell, MWWorld::Environment& environment); }; } diff --git a/apps/openmw/mwrender/interior.cpp b/apps/openmw/mwrender/interior.cpp index d559a12160..1e88ecab49 100644 --- a/apps/openmw/mwrender/interior.cpp +++ b/apps/openmw/mwrender/interior.cpp @@ -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() diff --git a/apps/openmw/mwrender/interior.hpp b/apps/openmw/mwrender/interior.hpp index 3d375f7f23..8d69ca0615 100644 --- a/apps/openmw/mwrender/interior.hpp +++ b/apps/openmw/mwrender/interior.hpp @@ -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 &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 &_cell, const ESMS::ESMStore& _store, MWScene &_scene) - : cell(_cell), store(_store), scene(_scene), base(NULL), insert(NULL), ambientMode (0) {} + InteriorCellRender(ESMS::CellStore &_cell, MWWorld::Environment& environment, + MWScene &_scene) + : cell(_cell), mEnvironment (environment), scene(_scene), base(NULL), insert(NULL), ambientMode (0) {} virtual ~InteriorCellRender() { destroy(); } diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index e63c4838c3..ec3c9eb878 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -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& buttons); diff --git a/apps/openmw/mwscript/soundextensions.cpp b/apps/openmw/mwscript/soundextensions.cpp index 40229475ed..58554ef163 100644 --- a/apps/openmw/mwscript/soundextensions.cpp +++ b/apps/openmw/mwscript/soundextensions.cpp @@ -25,15 +25,15 @@ namespace MWScript { MWScript::InterpreterContext& context = static_cast (runtime.getContext()); - + std::string file = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); 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 (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))); } }; diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index bfe51686ba..03a118edaf 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -1,71 +1,238 @@ #include "soundmanager.hpp" -#include // TODO remove this line, once the real code is in place. +#include +using namespace std; -#include +#include +#include +#include + +#include +#include + +#include + +/* 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 +#define SOUND_FACTORY OpenAL_Audiere_Factory +#endif + +#ifdef OPENMW_USE_FFMPEG +#include +#define SOUND_FACTORY OpenAL_FFMpeg_Factory +#endif + +#ifdef OPENMW_USE_MPG123 +#include +#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; + + // 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 { - std::cout << "sound effect: " << reference.getRefData().getHandle() << " is speaking" << std::endl; - - context.messageBox (text); + /* 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) + { + // Attach the camera to the camera tracker + cameraTracker.followCamera(camera); + + // Tell Ogre to update the sound system each frame + root->addFrameListener(&updater); + + dir = soundDir + "/"; + } + + // 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) + { + 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; + } + + // 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 + { + 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"; + } + } + + // 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 = "") + { + } + + bool isPlaying(MWWorld::Ptr reference, const std::string &id) const + { + return true; + } + + 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 updatePositions(MWWorld::Ptr reference) + { + } + }; + + SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera, + const ESMS::ESMStore &store, + const std::string &soundDir) + { + mData = new SoundImpl(root, camera, store, soundDir); } - bool SoundManager::sayDone (MWWorld::Ptr reference, Interpreter::Context& context) const + SoundManager::~SoundManager() { - return false; + delete mData; } - void SoundManager::streamMusic (const std::string& filename, Interpreter::Context& context) + void SoundManager::say (MWWorld::Ptr reference, const std::string& filename) { - std::cout << "sound effect: playing music" << filename << std::endl; + // The range values are not tested + mData->add(filename, reference, "_say_sound", 1, 1, 100, 10000, false); } - - void SoundManager::playSound (const std::string& soundId, float volume, float pitch, - Interpreter::Context& context) + + bool SoundManager::sayDone (MWWorld::Ptr reference) const { - std::cout - << "sound effect: playing sound " << soundId - << " at volume " << volume << ", at pitch " << pitch - << std::endl; + return !mData->isPlaying(reference, "_say_sound"); + } + + 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, Interpreter::Context& context) + float volume, float pitch, bool loop) { - std::cout - << "sound effect: playing sound " << soundId - << " from " << reference.getRefData().getHandle() - << " at volume " << volume << ", at pitch " << pitch - << std::endl; - - mSounds[reference.getRefData().getHandle()] = soundId; + // 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, - Interpreter::Context& context) + void SoundManager::stopSound3D (MWWorld::Ptr reference, const std::string& soundId) { - std::cout - << "sound effect : stop playing sound " << soundId - << " from " << reference.getRefData().getHandle() << std::endl; - - mSounds[reference.getRefData().getHandle()] = ""; + mData->remove(reference, soundId); } - bool SoundManager::getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId, - Interpreter::Context& context) const + void SoundManager::stopSound (MWWorld::Ptr::CellStore *cell) { - std::map::const_iterator iter = - mSounds.find (reference.getRefData().getHandle()); - - if (iter==mSounds.end()) - return false; - - return iter->second==soundId; + 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); } } - diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 5a79c0c27b..9b47452037 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -6,63 +6,62 @@ #include "../mwworld/ptr.hpp" -namespace Interpreter +namespace Ogre { - class Context; + class Root; + class Camera; +} + +namespace ESMS +{ + struct ESMStore; } namespace MWSound { - // 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) - class SoundManager { - std::map mSounds; // object, sound (for testing only) - + // Hide implementation details - engine.cpp is compiling + // enough as it is. + struct SoundImpl; + SoundImpl *mData; + public: - - void say (MWWorld::Ptr reference, const std::string& filename, - const std::string& text, Interpreter::Context& context); + SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store, + const std::string &soundDir); + ~SoundManager(); + + 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 - - diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 62e0ee8c9f..bc36944e65 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -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"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index d456cc06b6..4b6644e964 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -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. diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index c8230f5a8c..4ef20c7ffc 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -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) || - reference.getType()==typeid (ESMS::LiveCellRef))) - { - 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) || - reference.getType()==typeid (ESMS::LiveCellRef))) + 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 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::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::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) || - ptr.getType()==typeid (ESMS::LiveCellRef))) + if (mActiveCells.find (ptr.getCell())!=mActiveCells.end()) { - mEnvironment.mMechanicsManager->removeActor (ptr); + Class::get (ptr).disable (ptr, mEnvironment); + mEnvironment.mSoundManager->stopSound3D (ptr); } } } diff --git a/cmake/FindAudiere.cmake b/cmake/FindAudiere.cmake new file mode 100644 index 0000000000..4cc1fb2df3 --- /dev/null +++ b/cmake/FindAudiere.cmake @@ -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) + diff --git a/cmake/FindFFMPEG.cmake b/cmake/FindFFMPEG.cmake new file mode 100644 index 0000000000..ff6d0c598d --- /dev/null +++ b/cmake/FindFFMPEG.cmake @@ -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 ) diff --git a/cmake/FindIconv.cmake b/cmake/FindIconv.cmake index 000e09b845..571a959afd 100644 --- a/cmake/FindIconv.cmake +++ b/cmake/FindIconv.cmake @@ -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) diff --git a/cmake/FindMPG123.cmake b/cmake/FindMPG123.cmake new file mode 100644 index 0000000000..51e562c910 --- /dev/null +++ b/cmake/FindMPG123.cmake @@ -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? + +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) + diff --git a/cmake/FindSNDFILE.cmake b/cmake/FindSNDFILE.cmake new file mode 100644 index 0000000000..5c7664b502 --- /dev/null +++ b/cmake/FindSNDFILE.cmake @@ -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) + diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 6f43f9fde9..310fc9ec5e 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -48,35 +48,35 @@ namespace Compiler switch (mPutback) { case Putback_Special: - - mPutback = Putback_None; + + mPutback = Putback_None; return parser.parseSpecial (mPutbackCode, mPutbackLoc, *this); - + case Putback_Integer: mPutback = Putback_None; return parser.parseInt (mPutbackInteger, mPutbackLoc, *this); - + case Putback_Float: mPutback = Putback_None; return parser.parseFloat (mPutbackFloat, mPutbackLoc, *this); - + case Putback_Name: - + mPutback = Putback_None; return parser.parseName (mPutbackName, mPutbackLoc, *this); - + case Putback_Keyword: mPutback = Putback_None; return parser.parseKeyword (mPutbackCode, mPutbackLoc, *this); - + case Putback_None: - + break; } - + char c; if (!get (c)) @@ -265,12 +265,12 @@ namespace Compiler cont = parser.parseName (name, loc, *this); return true; } - + int i = 0; std::string lowerCase; lowerCase.reserve (name.size()); - + std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), (int(*)(int)) std::tolower); @@ -283,13 +283,13 @@ namespace Compiler cont = parser.parseKeyword (i, loc, *this); return true; } - + if (mExtensions) { if (int keyword = mExtensions->searchKeyword (lowerCase)) { cont = parser.parseKeyword (keyword, loc, *this); - return true; + return true; } } @@ -316,19 +316,20 @@ 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); break; - } + } } else if (!(c=='"' && name.empty())) { @@ -341,7 +342,7 @@ namespace Compiler if (first && std::isdigit (c)) error = true; } - + name += c; first = false; } @@ -374,7 +375,7 @@ namespace Compiler else { putback (c); - return false; + return false; } } else if (c=='!') @@ -449,8 +450,8 @@ namespace Compiler return false; if (special==S_newline) - mLoc.mLiteral = ""; - + mLoc.mLiteral = ""; + TokenLoc loc (mLoc); mLoc.mLiteral.clear(); @@ -477,40 +478,39 @@ namespace Compiler { while (scanToken (parser)); } - + void Scanner::putbackSpecial (int code, const TokenLoc& loc) { mPutback = Putback_Special; mPutbackCode = code; - mPutbackLoc = loc; + mPutbackLoc = loc; } - + void Scanner::putbackInt (int value, const TokenLoc& loc) { mPutback = Putback_Integer; mPutbackInteger = value; - mPutbackLoc = loc; - } + mPutbackLoc = loc; + } void Scanner::putbackFloat (float value, const TokenLoc& loc) { mPutback = Putback_Float; mPutbackFloat = value; - mPutbackLoc = loc; - } + mPutbackLoc = loc; + } void Scanner::putbackName (const std::string& name, const TokenLoc& loc) { mPutback = Putback_Name; mPutbackName = name; - mPutbackLoc = loc; - } + mPutbackLoc = loc; + } void Scanner::putbackKeyword (int keyword, const TokenLoc& loc) { mPutback = Putback_Keyword; mPutbackCode = keyword; - mPutbackLoc = loc; - } + mPutbackLoc = loc; + } } - diff --git a/components/esm/esm_reader.hpp b/components/esm/esm_reader.hpp index 70a81e6f45..32f5570ad5 100644 --- a/components/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -9,7 +9,10 @@ #include #include #include -#include + +#ifndef __WIN32__ + #include +#endif #include #include @@ -618,89 +621,93 @@ public: return convertToUTF8(res); } - // Convert a string from the encoding used by Morrowind to UTF-8 - std::string convertToUTF8(std::string input) - { - std::string output = ""; - - //create convert description - iconv_t cd = iconv_open("UTF-8", "WINDOWS-1252"); - - if (cd == (iconv_t)-1) //error handling + // Convert a string from the encoding used by Morrowind to UTF-8 + std::string convertToUTF8 (std::string input) { - std::string errMsg = "Creating description for UTF-8 converting failed: "; +#ifdef __WIN32__ + return input; +#else + std::string output = ""; - switch (errno) //detailed error messages (maybe it contains too much detail :) - { - case EMFILE: - errMsg += "{OPEN_MAX} files descriptors are currently open in the calling process."; - case ENFILE: - errMsg += "Too many files are currently open in the system."; - case ENOMEM: - errMsg +="Insufficient storage space is available."; - case EINVAL: - errMsg += "The conversion specified by fromcode and tocode is not supported by the implementation."; + //create convert description + iconv_t cd = iconv_open ("UTF-8", "WINDOWS-1252"); - default: - errMsg += "Unknown Error\n"; - } - - fail(errMsg); - - } - else - { - const size_t inputSize = input.size(); - - if (inputSize) //input is not empty - { - //convert function doesn't accept const char *, therefore copy content into an char * - std::vector inputBuffer(input.begin(), input.end()); - char *inputBufferBegin = &inputBuffer[0]; - - size_t inputBytesLeft = inputSize; //bytes to convert - - static const size_t outputSize = 1000; - size_t outputBytesLeft; - - char outputBuffer[outputSize]; - char *outputBufferBegin; - - while (inputBytesLeft > 0 ) + if (cd == (iconv_t)-1) //error handling { - outputBytesLeft = outputSize; - outputBufferBegin = outputBuffer; + std::string errMsg = "Creating description for UTF-8 converting failed: "; - if (iconv(cd, &inputBufferBegin, &inputBytesLeft, &outputBufferBegin, &outputBytesLeft) == (size_t)-1) - { - switch (errno) + switch (errno) //detailed error messages (maybe it contains too much detail :) { - case E2BIG: //outputBuffer is full - output += std::string(outputBuffer, outputSize); - break; - case EILSEQ: - fail("Iconv: Invalid multibyte sequence.\n"); - break; - case EINVAL: - fail("Iconv: Incomplete multibyte sequence.\n"); - break; - default: - fail("Iconv: Unknown Error\n"); + case EMFILE: + errMsg += "{OPEN_MAX} files descriptors are currently open in the calling process."; + case ENFILE: + errMsg += "Too many files are currently open in the system."; + case ENOMEM: + errMsg +="Insufficient storage space is available."; + case EINVAL: + errMsg += "The conversion specified by fromcode and tocode is not supported by the implementation."; + + default: + errMsg += "Unknown Error\n"; } - } + fail (errMsg); + + } + else + { + const size_t inputSize = input.size(); + + if (inputSize) //input is not empty + { + //convert function doesn't accept const char *, therefore copy content into an char * + std::vector inputBuffer (input.begin(), input.end()); + char *inputBufferBegin = &inputBuffer[0]; + + size_t inputBytesLeft = inputSize; //bytes to convert + + static const size_t outputSize = 1000; + size_t outputBytesLeft; + + char outputBuffer[outputSize]; + char *outputBufferBegin; + + while (inputBytesLeft > 0) + { + outputBytesLeft = outputSize; + outputBufferBegin = outputBuffer; + + if (iconv (cd, &inputBufferBegin, &inputBytesLeft, &outputBufferBegin, &outputBytesLeft) == (size_t)-1) + { + switch (errno) + { + case E2BIG: //outputBuffer is full + output += std::string (outputBuffer, outputSize); + break; + case EILSEQ: + fail ("Iconv: Invalid multibyte sequence.\n"); + break; + case EINVAL: + fail ("Iconv: Incomplete multibyte sequence.\n"); + break; + default: + fail ("Iconv: Unknown Error\n"); + } + + } + } + + //read only relevant bytes from outputBuffer + output += std::string (outputBuffer, outputSize - outputBytesLeft); + + } } - //read only relevant bytes from outputBuffer - output += std::string(outputBuffer, outputSize - outputBytesLeft); + iconv_close (cd); - } + return output; } - - iconv_close (cd); - - return output; - } +#endif void skip(int bytes) { esm->seek(esm->tell()+bytes); } uint64_t getOffset() { return esm->tell(); } diff --git a/components/esm/load_impl.cpp b/components/esm/load_impl.cpp new file mode 100644 index 0000000000..28ae402ded --- /dev/null +++ b/components/esm/load_impl.cpp @@ -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(); + } +} diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 61618c7312..a699c1cdbd 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -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 diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 670fba8307..b935f49f8f 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -26,12 +26,13 @@ #include #include -#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 +#include // For warning messages #include @@ -45,93 +46,127 @@ 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; - -// Singleton instance used by load() -static NIFLoader g_sing; - -// Makeshift error reporting system -static string errName; -static void warn(const string &msg) +NIFLoader& NIFLoader::getSingleton() { - cout << "WARNING (NIF:" << errName << "): " << msg << endl; + static NIFLoader instance; + + return instance; +} + +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); +} + +Quaternion NIFLoader::convertRotation(const Nif::Matrix& rot) +{ + 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 class BoundsFinder { - struct MaxMinFinder - { - float max, min; - - MaxMinFinder() + struct MaxMinFinder { - min = numeric_limits::infinity(); - max = -min; - } + float max, min; - void add(float f) - { - if(f > max) max = f; - if(f < min) min = f; - } + MaxMinFinder() + { + min = numeric_limits::infinity(); + max = -min; + } - // Return Max(max**2, min**2) - float getMaxSquared() - { - float m1 = max*max; - float m2 = min*min; - if(m1 >= m2) return m1; - return m2; - } - }; + void add(float f) + { + if (f > max) max = f; + if (f < min) min = f; + } - MaxMinFinder X, Y, Z; + // Return Max(max**2, min**2) + float getMaxSquared() + { + float m1 = max*max; + float m2 = min*min; + if (m1 >= m2) return m1; + return m2; + } + }; + + MaxMinFinder X, Y, Z; public: - // Add 'verts' vertices to the calculation. The 'data' pointer is - // expected to point to 3*verts floats representing x,y,z for each - // point. - void add(float *data, int verts) - { - for(int i=0;i 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,207 +221,195 @@ 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()) + // 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()) { - Pass *pass = material->getTechnique(0)->getPass(0); - /*TextureUnitState *txt =*/ pass->createTextureUnitState(texName); + Pass *pass = material->getTechnique(0)->getPass(0); + /*TextureUnitState *txt =*/ + pass->createTextureUnitState(texName); - /* As of yet UNTESTED code from Chris: - pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC); - pass->setDepthFunction(Ogre::CMPF_LESS_EQUAL); - pass->setDepthCheckEnabled(true); + /* As of yet UNTESTED code from Chris: + pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC); + pass->setDepthFunction(Ogre::CMPF_LESS_EQUAL); + pass->setDepthCheckEnabled(true); - // Add transparency if NiAlphaProperty was present - if(alphaFlags != -1) + // Add transparency if NiAlphaProperty was present + if(alphaFlags != -1) + { + if((alphaFlags&1)) + { + pass->setDepthWriteEnabled(false); + pass->setSceneBlending(getBlendFactor((alphaFlags>>1)&0xf), + getBlendFactor((alphaFlags>>5)&0xf)); + } + else + pass->setDepthWriteEnabled(true); + + if((alphaFlags>>9)&1) + pass->setAlphaRejectSettings(getTestMode((alphaFlags>>10)&0x7), + alphaTest); + + pass->setTransparentSortingEnabled(!((alphaFlags>>13)&1)); + } + else + pass->setDepthWriteEnabled(true); + */ + + // 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) { - pass->setDepthWriteEnabled(false); - pass->setSceneBlending(getBlendFactor((alphaFlags>>1)&0xf), - getBlendFactor((alphaFlags>>5)&0xf)); + // Enable transparency + pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); + + //pass->setDepthCheckEnabled(false); + pass->setDepthWriteEnabled(false); } - else - pass->setDepthWriteEnabled(true); - - if((alphaFlags>>9)&1) - pass->setAlphaRejectSettings(getTestMode((alphaFlags>>10)&0x7), - alphaTest); - - pass->setTransparentSortingEnabled(!((alphaFlags>>13)&1)); - } - else - pass->setDepthWriteEnabled(true); - */ - - // Add transparency if NiAlphaProperty was present - 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) - { - // Enable transparency - pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); - - //pass->setDepthCheckEnabled(false); - pass->setDepthWriteEnabled(false); - } - else - warn("Unhandled alpha setting for texture " + texName); + else + warn("Unhandled alpha setting for texture " + texName); } } - // Add material bells and whistles - material->setAmbient(ambient.array[0], ambient.array[1], ambient.array[2]); - material->setDiffuse(diffuse.array[0], diffuse.array[1], diffuse.array[2], alpha); - material->setSpecular(specular.array[0], specular.array[1], specular.array[2], alpha); - material->setSelfIllumination(emissive.array[0], emissive.array[1], emissive.array[2]); - material->setShininess(glossiness); + // Add material bells and whistles + material->setAmbient(ambient.array[0], ambient.array[1], ambient.array[2]); + material->setDiffuse(diffuse.array[0], diffuse.array[1], diffuse.array[2], alpha); + material->setSpecular(specular.array[0], specular.array[1], specular.array[2], alpha); + material->setSelfIllumination(emissive.array[0], emissive.array[1], emissive.array[2]); + material->setShininess(glossiness); } // 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++); + static int addon = 0; + static char buf[8]; + snprintf(buf, 8, "_%d", addon++); - // Don't overflow the buffer - if(addon > 999999) addon = 0; + // Don't overflow the buffer + if (addon > 999999) addon = 0; - return input + buf; + return input + buf; } // Check if the given texture name exists in the real world. If it // 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; + assert(vfs); + if (vfs->isFile(texName)) return; - int len = texName.size(); - if(len < 4) return; + int len = texName.size(); + if (len < 4) return; - // Change texture extension to .dds - texName[len-3] = 'd'; - texName[len-2] = 'd'; - texName[len-1] = 's'; + // Change texture extension to .dds + texName[len-3] = 'd'; + texName[len-2] = 'd'; + texName[len-1] = 's'; } // 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()); + NiTriShapeData *data = shape->data.getPtr(); + SubMesh *sub = mesh->createSubMesh(shape->name.toString()); - int nextBuf = 0; + int nextBuf = 0; - // This function is just one long stream of Ogre-barf, but it works - // great. + // This function is just one long stream of Ogre-barf, but it works + // great. - // Add vertices - int numVerts = data->vertices.length / 3; - 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); + // Add vertices + int numVerts = data->vertices.length / 3; + 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) + // Vertex normals + if (data->normals.length) { - decl->addElement(nextBuf, 0, VET_FLOAT3, VES_NORMAL); - vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( - VertexElement::getTypeSize(VET_FLOAT3), - numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY); - vbuf->writeData(0, vbuf->getSizeInBytes(), data->normals.ptr, true); - bind->setBinding(nextBuf++, vbuf); + decl->addElement(nextBuf, 0, VET_FLOAT3, VES_NORMAL); + vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( + VertexElement::getTypeSize(VET_FLOAT3), + numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY); + vbuf->writeData(0, vbuf->getSizeInBytes(), data->normals.ptr, true); + bind->setBinding(nextBuf++, vbuf); } - // Vertex colors - if(data->colors.length) + // Vertex colors + if (data->colors.length) { - const float *colors = data->colors.ptr; - RenderSystem* rs = Root::getSingleton().getRenderSystem(); - std::vector colorsRGB(numVerts); - RGBA *pColour = &colorsRGB.front(); - for(int i=0; iconvertColourValue(ColourValue(colors[0],colors[1],colors[2], - colors[3]),pColour++); - colors += 4; - } - decl->addElement(nextBuf, 0, VET_COLOUR, VES_DIFFUSE); - vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( - VertexElement::getTypeSize(VET_COLOUR), - numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY); - vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB.front(), true); - bind->setBinding(nextBuf++, vbuf); + const float *colors = data->colors.ptr; + RenderSystem* rs = Root::getSingleton().getRenderSystem(); + std::vector colorsRGB(numVerts); + RGBA *pColour = &colorsRGB.front(); + for (int i=0; iconvertColourValue(ColourValue(colors[0],colors[1],colors[2], + colors[3]),pColour++); + colors += 4; + } + decl->addElement(nextBuf, 0, VET_COLOUR, VES_DIFFUSE); + vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( + VertexElement::getTypeSize(VET_COLOUR), + numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY); + vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB.front(), true); + bind->setBinding(nextBuf++, vbuf); } - // Texture UV coordinates - if(data->uvlist.length) + // Texture UV coordinates + if (data->uvlist.length) { - decl->addElement(nextBuf, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES); - vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( - VertexElement::getTypeSize(VET_FLOAT2), - numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY); + decl->addElement(nextBuf, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES); + vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( + VertexElement::getTypeSize(VET_FLOAT2), + numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY); - vbuf->writeData(0, vbuf->getSizeInBytes(), data->uvlist.ptr, true); - bind->setBinding(nextBuf++, vbuf); + vbuf->writeData(0, vbuf->getSizeInBytes(), data->uvlist.ptr, true); + bind->setBinding(nextBuf++, vbuf); } - // Triangle faces - int numFaces = data->triangles.length; - if(numFaces) + // Triangle faces + int numFaces = data->triangles.length; + if (numFaces) { - HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton(). - createIndexBuffer(HardwareIndexBuffer::IT_16BIT, - numFaces, - HardwareBuffer::HBU_STATIC_WRITE_ONLY); - ibuf->writeData(0, ibuf->getSizeInBytes(), data->triangles.ptr, true); - sub->indexData->indexBuffer = ibuf; - sub->indexData->indexCount = numFaces; - sub->indexData->indexStart = 0; + HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton(). + createIndexBuffer(HardwareIndexBuffer::IT_16BIT, + numFaces, + HardwareBuffer::HBU_STATIC_WRITE_ONLY); + ibuf->writeData(0, ibuf->getSizeInBytes(), data->triangles.ptr, true); + sub->indexData->indexBuffer = ibuf; + sub->indexData->indexCount = numFaces; + sub->indexData->indexStart = 0; } - // 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); - } - */ + // Set material if one was given + if (!material.empty()) sub->setMaterialName(material); } // Helper math functions. Reinventing linear algebra for the win! @@ -394,351 +417,459 @@ 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]; - float c = B.v[2].array[i]; + float a = B.v[0].array[i]; + float b = B.v[1].array[i]; + float c = B.v[2].array[i]; - B.v[0].array[i] = a*A.v[0].array[0] + b*A.v[0].array[1] + c*A.v[0].array[2]; - B.v[1].array[i] = a*A.v[1].array[0] + b*A.v[1].array[1] + c*A.v[1].array[2]; - B.v[2].array[i] = a*A.v[2].array[0] + b*A.v[2].array[1] + c*A.v[2].array[2]; + B.v[0].array[i] = a*A.v[0].array[0] + b*A.v[0].array[1] + c*A.v[0].array[2]; + B.v[1].array[i] = a*A.v[1].array[0] + b*A.v[1].array[1] + c*A.v[1].array[2]; + B.v[2].array[i] = a*A.v[2].array[0] + b*A.v[2].array[1] + c*A.v[2].array[2]; } } // Computes C = B + AxC*scale static void vectorMulAdd(const Matrix &A, const Vector &B, float *C, float scale) { - // Keep the original values - float a = C[0]; - float b = C[1]; - float c = C[2]; + // Keep the original values + float a = C[0]; + float b = C[1]; + float c = C[2]; - // Perform matrix multiplication, scaling and addition - 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; + // Perform matrix multiplication, scaling and addition + 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; } // Computes B = AxB (matrix*vector) static void vectorMul(const Matrix &A, float *C) { - // Keep the original values - float a = C[0]; - float b = C[1]; - float c = C[2]; + // Keep the original values + float a = C[0]; + float b = C[1]; + float c = C[2]; - // Perform matrix multiplication, scaling and addition - 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]; + // Perform matrix multiplication, scaling and addition + 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); + assert(shape != NULL); - // Interpret flags - bool hidden = (flags & 0x01) != 0; // Not displayed - bool collide = (flags & 0x02) != 0; // Use mesh for collision - bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision + // Interpret flags + bool hidden = (flags & 0x01) != 0; // Not displayed + bool collide = (flags & 0x02) != 0; // Use mesh for collision + bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision - // Bounding box collision isn't implemented, always use mesh for now. - if(bbcollide) + // Bounding box collision isn't implemented, always use mesh for now. + if (bbcollide) { - collide = true; - bbcollide = false; + collide = true; + bbcollide = false; } - // If the object was marked "NCO" earlier, it shouldn't collide with - // anything. - if(flags & 0x800) - { collide = false; bbcollide = false; } - - if(!collide && !bbcollide && hidden) - // This mesh apparently isn't being used for anything, so don't - // bother setting it up. - return; - - // Material name for this submesh, if any - String material; - - // Skip the entire material phase for hidden nodes - if(!hidden) + // If the object was marked "NCO" earlier, it shouldn't collide with + // anything. + if (flags & 0x800) { - // These are set below if present - NiTexturingProperty *t = NULL; - NiMaterialProperty *m = NULL; - NiAlphaProperty *a = NULL; + collide = false; + bbcollide = false; + } - // Scan the property list for material information - PropertyList &list = shape->props; - int n = list.length(); - for(int i=0; i