diff --git a/CMakeLists.txt b/CMakeLists.txt index cd058c570..32f4e483a 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 151dab9f9..35fc597fe 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 396e4bad1..8c5ee33f7 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 3cdc1f850..69a510803 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 66821a7c5..3b48796ac 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 4b94d78d1..007b2ca77 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 4a514c94d..c2e6e25e1 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 f4236b5f6..5c06077b2 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 217a74a76..040342a69 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 3bea7d5b9..179a060c6 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 3f15e2278..70d1bca3b 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 c0e43bc3e..c3bbfa9f0 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 ea358be68..fb88e25df 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 0cc516308..9c1ed7a89 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 baeac23c7..de54a9e68 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 1cdada64c..fc9515fff 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 85a89a919..9f6708f40 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 2c4bd3562..6fd8f996c 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 fa3b6d657..b6bf8808e 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 9162384af..29d66d012 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 d742fae8b..8b781ba2f 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 7cb363bf8..e57511452 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 6d08d557c..5a1a15b1d 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 ab1969480..c87d1e128 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 e15d9daee..bb9866b8c 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 f29a0be1f..57dca3148 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 01542baed..0da1f0d3a 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 b6a4bbc05..a12d939d8 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 a6f4a2965..dd54806bd 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 0799b2031..3fe38542b 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 208c26c56..9bf3b34a8 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 08ec391a8..bc6c4ba19 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 c17d53dab..11399d2f6 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 9ed7f0e77..253569afe 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 52d045ebe..3da591e01 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 f2bc4f635..211d239c6 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 fb8080182..abbf6cd5f 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 c292c32dd..5a941360a 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 6bc96381c..d9c7653dd 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 8f1d7affc..22e7b58fa 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 121f17383..a5b4b51c7 100644 --- a/apps/openmw/mwrender/cellimp.cpp +++ b/apps/openmw/mwrender/cellimp.cpp @@ -2,94 +2,56 @@ #include -using namespace MWRender; - -template -void insertObj(CellRenderImp& cellRender, T& 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); - 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); +#include "../mwworld/class.hpp" +#include "../mwworld/ptr.hpp" - 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())); -} +using namespace MWRender; template -void insertCellRefList (CellRenderImp& cellRender, const ESMS::ESMStore& store, T& cellRefList) +void insertCellRefList (CellRenderImp& cellRender, MWWorld::Environment& environment, + T& cellRefList, ESMS::CellStore &cell) { - 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 a4bb6868c..fadba79a5 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 d559a1216..1e88ecab4 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 3d375f7f2..8d69ca061 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 e63c4838c..ec3c9eb87 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 40229475e..58554ef16 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 bfe51686b..03a118eda 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 + { + /* 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); + } + + SoundManager::~SoundManager() + { + delete mData; + } + + void SoundManager::say (MWWorld::Ptr reference, const std::string& filename) { - std::cout << "sound effect: " << reference.getRefData().getHandle() << " is speaking" << std::endl; - - context.messageBox (text); + // The range values are not tested + mData->add(filename, reference, "_say_sound", 1, 1, 100, 10000, false); } - bool SoundManager::sayDone (MWWorld::Ptr reference, Interpreter::Context& context) const + bool SoundManager::sayDone (MWWorld::Ptr reference) const { - return false; + return !mData->isPlaying(reference, "_say_sound"); } - void SoundManager::streamMusic (const std::string& filename, Interpreter::Context& context) + void SoundManager::streamMusic (const std::string& filename) { - std::cout << "sound effect: playing music" << filename << std::endl; + // 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, - Interpreter::Context& context) + + void SoundManager::playSound (const std::string& soundId, float volume, float pitch) { - std::cout - << "sound effect: playing sound " << soundId - << " at volume " << volume << ", at pitch " << pitch - << std::endl; + // 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 5a79c0c27..9b4745203 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. + + void stopSound (MWWorld::Ptr::CellStore *cell); + ///< Stop all sounds for the given cell. - bool getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId, - Interpreter::Context& context) const; + 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 62e0ee8c9..bc36944e6 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 d456cc06b..4b6644e96 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 c8230f5a8..4ef20c7ff 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 000000000..4cc1fb2df --- /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 000000000..ff6d0c598 --- /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 000e09b84..571a959af 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 000000000..51e562c91 --- /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 000000000..5c7664b50 --- /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 6f43f9fde..310fc9ec5 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 70a81e6f4..32f5570ad 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 - { - std::string errMsg = "Creating description for UTF-8 converting failed: "; - - 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."; - - default: - errMsg += "Unknown Error\n"; - } - - fail(errMsg); - - } - else + // Convert a string from the encoding used by Morrowind to UTF-8 + std::string convertToUTF8 (std::string input) { - const size_t inputSize = input.size(); +#ifdef __WIN32__ + return input; +#else + std::string output = ""; - 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]; + //create convert description + iconv_t cd = iconv_open ("UTF-8", "WINDOWS-1252"); - size_t inputBytesLeft = inputSize; //bytes to convert + if (cd == (iconv_t)-1) //error handling + { + std::string errMsg = "Creating description for UTF-8 converting failed: "; - static const size_t outputSize = 1000; - size_t outputBytesLeft; + 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."; + + default: + errMsg += "Unknown Error\n"; + } - char outputBuffer[outputSize]; - char *outputBufferBegin; + fail (errMsg); - while (inputBytesLeft > 0 ) + } + else { - outputBytesLeft = outputSize; - outputBufferBegin = outputBuffer; + const size_t inputSize = input.size(); - if (iconv(cd, &inputBufferBegin, &inputBytesLeft, &outputBufferBegin, &outputBytesLeft) == (size_t)-1) - { - switch (errno) + if (inputSize) //input is not empty { - 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"); - } + //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 000000000..28ae402de --- /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 61618c731..a699c1cdb 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 670fba830..b935f49f8 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; +NIFLoader& NIFLoader::getSingleton() +{ + static NIFLoader instance; + + return instance; +} + +NIFLoader* NIFLoader::getSingletonPtr() +{ + return &getSingleton(); +} + +void NIFLoader::warn(string msg) +{ + std::cerr << "NIFLoader: Warn:" << msg << "\n"; +} -// Singleton instance used by load() -static NIFLoader g_sing; +void NIFLoader::fail(string msg) +{ + std::cerr << "NIFLoader: Fail: "<< msg << std::endl; + assert(1); +} + +Vector3 NIFLoader::convertVector3(const Nif::Vector& vec) +{ + return Ogre::Vector3(vec.array); +} -// Makeshift error reporting system -static string errName; -static void warn(const string &msg) +Quaternion NIFLoader::convertRotation(const Nif::Matrix& rot) { - cout << "WARNING (NIF:" << errName << "): " << msg << endl; + Real matrix[3][3]; + + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + matrix[i][j] = rot.v[i].array[j]; + + return Quaternion(Matrix3(matrix)); } // Helper class that computes the bounding box and of a mesh class BoundsFinder { - struct MaxMinFinder - { - float max, min; + struct MaxMinFinder + { + float max, min; + + MaxMinFinder() + { + min = numeric_limits::infinity(); + max = -min; + } + + void add(float f) + { + if (f > max) max = f; + if (f < min) min = f; + } - MaxMinFinder() + // 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) { - min = numeric_limits::infinity(); - max = -min; + for (int i=0;i max) max = f; - if(f < min) min = f; + return + minX() <= maxX() && + minY() <= maxY() && + minZ() <= maxZ(); } - // Return Max(max**2, min**2) - float getMaxSquared() + // Compute radius + float getRadius() { - float m1 = max*max; - float m2 = min*min; - if(m1 >= m2) return m1; - return m2; - } - }; + assert(isValid()); - MaxMinFinder X, Y, Z; + // The radius is computed from the origin, not from the geometric + // center of the mesh. + return sqrt(X.getMaxSquared() + Y.getMaxSquared() + Z.getMaxSquared()); + } -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"); - - // 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()) + 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()) { - 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); - - // Add transparency if NiAlphaProperty was present - if(alphaFlags != -1) + 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); + + // 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)); - } - else - pass->setDepthWriteEnabled(true); - - if((alphaFlags>>9)&1) - pass->setAlphaRejectSettings(getTestMode((alphaFlags>>10)&0x7), - alphaTest); + // Enable transparency + pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); - 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); + //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()); - - int nextBuf = 0; - - // 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); - - // Vertex normals - if(data->normals.length) + NiTriShapeData *data = shape->data.getPtr(); + SubMesh *sub = mesh->createSubMesh(shape->name.toString()); + + int nextBuf = 0; + + // 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); + + // 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]; - - // 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; + // 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; } // 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]; - - // 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]; + // 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]; } -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 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; + 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; + // Material name for this submesh, if any + String material; - // Skip the entire material phase for hidden nodes - if(!hidden) + // Skip the entire material phase for hidden nodes + if (!hidden) { - // These are set below if present - NiTexturingProperty *t = NULL; - NiMaterialProperty *m = NULL; - NiAlphaProperty *a = NULL; - - // Scan the property list for material information - PropertyList &list = shape->props; - int n = list.length(); - for(int i=0; iprops; + int n = list.length(); + for (int i=0; irecType == RC_NiTexturingProperty) - t = (NiTexturingProperty*)pr; - else if(pr->recType == RC_NiMaterialProperty) - m = (NiMaterialProperty*)pr; - else if(pr->recType == RC_NiAlphaProperty) - a = (NiAlphaProperty*)pr; + if (pr->recType == RC_NiTexturingProperty) + t = (NiTexturingProperty*)pr; + else if (pr->recType == RC_NiMaterialProperty) + m = (NiMaterialProperty*)pr; + else if (pr->recType == RC_NiAlphaProperty) + a = (NiAlphaProperty*)pr; } - // Texture - String texName; - if(t && t->textures[0].inUse) + // Texture + String texName; + if (t && t->textures[0].inUse) { - NiSourceTexture *st = t->textures[0].texture.getPtr(); - if(st->external) + NiSourceTexture *st = t->textures[0].texture.getPtr(); + if (st->external) { - SString tname = st->filename; - - /* findRealTexture checks if the file actually - exists. If it doesn't, and the name ends in .tga, it - will try replacing the extension with .dds instead - and search for that. Bethesda at some at some point - converted all their BSA textures from tga to dds for - increased load speed, but all texture file name - references were kept as .tga. - - The function replaces the name in place (that's why - we cast away the const modifier), but this is no - problem since all the nif data is stored in a local - throwaway buffer. - */ - texName = "textures\\" + tname.toString(); - findRealTexture(texName); + SString tname = st->filename; + + /* findRealTexture checks if the file actually + exists. If it doesn't, and the name ends in .tga, it + will try replacing the extension with .dds instead + and search for that. Bethesda at some at some point + converted all their BSA textures from tga to dds for + increased load speed, but all texture file name + references were kept as .tga. + + The function replaces the name in place (that's why + we cast away the const modifier), but this is no + problem since all the nif data is stored in a local + throwaway buffer. + */ + texName = "textures\\" + tname.toString(); + findRealTexture(texName); } - else warn("Found internal texture, ignoring."); + else warn("Found internal texture, ignoring."); } - // Alpha modifiers - int alphaFlags = -1; - ubyte alphaTest = 0; - if(a) + // Alpha modifiers + int alphaFlags = -1; + ubyte alphaTest = 0; + if (a) { - alphaFlags = a->flags; - alphaTest = a->data->threshold; + alphaFlags = a->flags; + alphaTest = a->data->threshold; } - // Material - if(m || !texName.empty()) + // Material + if (m || !texName.empty()) { - // If we're here, then this mesh has a material. Thus we - // need to calculate a snappy material name. It should - // contain the mesh name (mesh->getName()) but also has to - // be unique. One mesh may use many materials. - material = getUniqueName(mesh->getName()); + // If we're here, then this mesh has a material. Thus we + // need to calculate a snappy material name. It should + // contain the mesh name (mesh->getName()) but also has to + // be unique. One mesh may use many materials. + material = getUniqueName(mesh->getName()); - if(m) + if (m) { - // Use NiMaterialProperty data to create the data - const S_MaterialProperty *d = m->data; - createMaterial(material, d->ambient, d->diffuse, d->specular, d->emissive, - d->glossiness, d->alpha, alphaFlags, alphaTest, texName); + // Use NiMaterialProperty data to create the data + const S_MaterialProperty *d = m->data; + createMaterial(material, d->ambient, d->diffuse, d->specular, d->emissive, + d->glossiness, d->alpha, alphaFlags, alphaTest, texName); } - else + else { - // We only have a texture name. Create a default - // material for it. - Vector zero, one; - for(int i=0; i<3;i++) + // We only have a texture name. Create a default + // material for it. + Vector zero, one; + for (int i=0; i<3;i++) { - zero.array[i] = 0.0; - one.array[i] = 1.0; + zero.array[i] = 0.0; + one.array[i] = 1.0; } - createMaterial(material, one, one, zero, zero, 0.0, 1.0, - alphaFlags, alphaTest, texName); + createMaterial(material, one, one, zero, zero, 0.0, 1.0, + alphaFlags, alphaTest, texName); } } } // End of material block, if(!hidden) ... - /* Do in-place transformation of all the vertices and normals. This - is pretty messy stuff, but we need it to make the sub-meshes - appear in the correct place. Neither Ogre nor Bullet support - nested levels of sub-meshes with transformations applied to each - level. - */ - NiTriShapeData *data = shape->data.getPtr(); - int numVerts = data->vertices.length / 3; - - float *ptr = (float*)data->vertices.ptr; - float *optr = ptr; - - // Rotate, scale and translate all the vertices - const Matrix &rot = shape->trafo->rotation; - const Vector &pos = shape->trafo->pos; - float scale = shape->trafo->scale; - for(int i=0; idata.getPtr(); + int numVerts = data->vertices.length / 3; + + float *ptr = (float*)data->vertices.ptr; + float *optr = ptr; + + //use niskindata for the position of vertices. + if (!shape->skin.empty()) { - vectorMulAdd(rot, pos, ptr, scale); - ptr += 3; - } + // vector that stores if the position if a vertex is absolute + std::vector vertexPosAbsolut(numVerts,false); + + + float *ptrNormals = (float*)data->normals.ptr; + //the bone from skin->bones[boneIndex] is linked to skin->data->bones[boneIndex] + //the first one contains a link to the bone, the second vertex transformation + //relative to the bone + int boneIndex = 0; + Bone *bonePtr; + Vector3 vecPos; + Quaternion vecRot; + + std::vector boneList = shape->skin->data->bones; + + /* + Iterate through the boneList which contains what vertices are linked to + the bone (it->weights array) and at what position (it->trafo) + That position is added to every vertex. + */ + for (std::vector::iterator it = boneList.begin(); + it != boneList.end(); it++) + { + //get the bone from bones array of skindata + bonePtr = skel->getBone(shape->skin->bones[boneIndex].name.toString()); + + // final_vector = old_vector + old_rotation*new_vector*old_scale + vecPos = bonePtr->_getDerivedPosition() + + bonePtr->_getDerivedOrientation() * convertVector3(it->trafo->trans); + + vecRot = bonePtr->_getDerivedOrientation() * convertRotation(it->trafo->rotation); + + for (unsigned int i=0; iweights.length; i++) + { + unsigned int verIndex = (it->weights.ptr + i)->vertex; - // Remember to rotate all the vertex normals as well - if(data->normals.length) + //Check if the vertex is relativ, FIXME: Is there a better solution? + if (vertexPosAbsolut[verIndex] == false) + { + //apply transformation to the vertices + Vector3 absVertPos = vecPos + vecRot * Vector3(ptr + verIndex *3); + + //convert it back to float * + for (int j=0; j<3; j++) + (ptr + verIndex*3)[j] = absVertPos[j]; + + //apply rotation to the normals (not every vertex has a normal) + //FIXME: I guessed that vertex[i] = normal[i], is that true? + if (verIndex < data->normals.length) + { + Vector3 absNormalsPos = vecRot * Vector3(ptrNormals + verIndex *3); + + for (int j=0; j<3; j++) + (ptrNormals + verIndex*3)[j] = absNormalsPos[j]; + } + + + //TODO: create vbas, and give them to createOgreSubMesh + + vertexPosAbsolut[verIndex] = true; + } + } + + boneIndex++; + //it->trafo (pos, rot, scale) of the vertex + //it->weights array containt the vertices linked to the bone and the weight + } + } + else { - ptr = (float*)data->normals.ptr; - for(int i=0; itrafo->rotation; + const Vector &pos = shape->trafo->pos; + float scale = shape->trafo->scale; + for (int i=0; inormals.length) + { + ptr = (float*)data->normals.ptr; + for (int i=0; iflags; + // Accumulate the flags from all the child nodes. This works for all + // the flags we currently use, at least. + flags |= node->flags; - // Check for extra data - Extra *e = node; - while(!e->extra.empty()) + // Check for extra data + Extra *e = node; + while (!e->extra.empty()) { - // Get the next extra data in the list - e = e->extra.getPtr(); - assert(e != NULL); + // Get the next extra data in the list + e = e->extra.getPtr(); + assert(e != NULL); - if(e->recType == RC_NiStringExtraData) + if (e->recType == RC_NiStringExtraData) { - // String markers may contain important information - // affecting the entire subtree of this node - NiStringExtraData *sd = (NiStringExtraData*)e; - - if(sd->string == "NCO") - // No collision. Use an internal flag setting to mark this. - flags |= 0x800; - else if(sd->string == "MRK") - // Marker objects. These are only visible in the - // editor. Until and unless we add an editor component to - // the engine, just skip this entire node. - return; + // String markers may contain important information + // affecting the entire subtree of this node + NiStringExtraData *sd = (NiStringExtraData*)e; + + if (sd->string == "NCO") + // No collision. Use an internal flag setting to mark this. + flags |= 0x800; + else if (sd->string == "MRK") + // Marker objects. These are only visible in the + // editor. Until and unless we add an editor component to + // the engine, just skip this entire node. + return; } } - // Apply the parent transformation to this node. We overwrite the - // existing data with the final transformation. - if(trafo) + Bone *bone = 0; + + // create skeleton or add bones + if (node->recType == RC_NiNode) { - // Get a non-const reference to the node's data, since we're - // overwriting it. TODO: Is this necessary? - Transformation &final = *((Transformation*)node->trafo); + if (node->name == "Bip01") //root node, create a skeleton + { + skel = SkeletonManager::getSingleton().create(getSkeletonName(), resourceGroup, true); + } - // For both position and rotation we have that: - // final_vector = old_vector + old_rotation*new_vector*old_scale - vectorMulAdd(trafo->rotation, trafo->pos, final.pos.array, trafo->scale); - vectorMulAdd(trafo->rotation, trafo->velocity, final.velocity.array, trafo->scale); + if (!skel.isNull()) //if there is a skeleton + { + bone = skel->createBone(node->name.toString()); - // Merge the rotations together - matrixMul(trafo->rotation, final.rotation); + if (parentBone) + parentBone->addChild(bone); - // Scalar values are so nice to deal with. Why can't everything - // just be scalar? - final.scale *= trafo->scale; + bone->setInheritOrientation(true); + bone->setPosition(convertVector3(node->trafo->pos)); + bone->setOrientation(convertRotation(node->trafo->rotation)); + } } - // For NiNodes, loop through children - if(node->recType == RC_NiNode) + // Apply the parent transformation to this node. We overwrite the + // existing data with the final transformation. + if (trafo) { - NodeList &list = ((NiNode*)node)->children; - int n = list.length(); - for(int i=0; itrafo); + + // For both position and rotation we have that: + // final_vector = old_vector + old_rotation*new_vector*old_scale + vectorMulAdd(trafo->rotation, trafo->pos, final.pos.array, trafo->scale); + vectorMulAdd(trafo->rotation, trafo->velocity, final.velocity.array, trafo->scale); + + // Merge the rotations together + matrixMul(trafo->rotation, final.rotation); + + // Scalar values are so nice to deal with. Why can't everything + // just be scalar? + final.scale *= trafo->scale; + } + + // For NiNodes, loop through children + if (node->recType == RC_NiNode) + { + NodeList &list = ((NiNode*)node)->children; + int n = list.length(); + for (int i=0; itrafo, bounds); + if (list.has(i)) + handleNode(&list[i], flags, node->trafo, bounds, bone); } } - else if(node->recType == RC_NiTriShape) + else if (node->recType == RC_NiTriShape) // For shapes - handleNiTriShape(mesh, dynamic_cast(node), flags, bounds); + handleNiTriShape(dynamic_cast(node), flags, bounds); } void NIFLoader::loadResource(Resource *resource) { - // Set up the VFS if it hasn't been done already - if(!vfs) vfs = new OgreVFS("General"); + resourceName = ""; + mesh = 0; + skel.setNull(); + + // Set up the VFS if it hasn't been done already + if (!vfs) vfs = new OgreVFS(resourceGroup); + + // Get the mesh + mesh = dynamic_cast(resource); + assert(mesh); - // Get the mesh - Mesh *mesh = dynamic_cast(resource); - assert(mesh); + // Look it up + resourceName = mesh->getName(); - // Look it up - const String &name = mesh->getName(); - errName = name; // Set name for error messages - if(!vfs->isFile(name)) + if (!vfs->isFile(resourceName)) { - warn("File not found."); - return; + warn("File not found."); + return; } - // Helper that computes bounding boxes for us. - BoundsFinder bounds; + // Helper that computes bounding boxes for us. + BoundsFinder bounds; - // Load the NIF. TODO: Wrap this in a try-catch block once we're out - // of the early stages of development. Right now we WANT to catch - // every error as early and intrusively as possible, as it's most - // likely a sign of incomplete code rather than faulty input. - NIFFile nif(vfs->open(name), name); + // Load the NIF. TODO: Wrap this in a try-catch block once we're out + // of the early stages of development. Right now we WANT to catch + // every error as early and intrusively as possible, as it's most + // likely a sign of incomplete code rather than faulty input. + NIFFile nif(vfs->open(resourceName), resourceName); - if(nif.numRecords() < 1) + if (nif.numRecords() < 1) { - warn("Found no records in NIF."); - return; + warn("Found no records in NIF."); + return; } - // The first record is assumed to be the root node - Record *r = nif.getRecord(0); - assert(r != NULL); + // The first record is assumed to be the root node + Record *r = nif.getRecord(0); + assert(r != NULL); - Nif::Node *node = dynamic_cast(r); + Nif::Node *node = dynamic_cast(r); - if(node == NULL) + if (node == NULL) { - warn("First record in file was not a node, but a " + - r->recName.toString() + ". Skipping file."); - return; + warn("First record in file was not a node, but a " + + r->recName.toString() + ". Skipping file."); + return; } - // Handle the node - handleNode(mesh, node, 0, NULL, bounds); + // Handle the node + handleNode(node, 0, NULL, bounds, 0); - // Finally, set the bounding value. - if(bounds.isValid()) + //set skeleton +// if (!skel.isNull()) +// mesh->setSkeletonName(getSkeletonName()); + + // Finally, set the bounding value. + if (bounds.isValid()) { - mesh->_setBounds(AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(), - bounds.maxX(), bounds.maxY(), bounds.maxZ())); - mesh->_setBoundingSphereRadius(bounds.getRadius()); + mesh->_setBounds(AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(), + bounds.maxX(), bounds.maxY(), bounds.maxZ())); + mesh->_setBoundingSphereRadius(bounds.getRadius()); } } MeshPtr NIFLoader::load(const std::string &name, const std::string &group) { - MeshManager *m = MeshManager::getSingletonPtr(); + MeshManager *m = MeshManager::getSingletonPtr(); - // Check if the resource already exists - ResourcePtr ptr = m->getByName(name, group); - if(!ptr.isNull()) - return MeshPtr(ptr); + // Check if the resource already exists + ResourcePtr ptr = m->getByName(name, group); + if (!ptr.isNull()) + return MeshPtr(ptr); - // Nope, create a new one. - return MeshManager::getSingleton().createManual(name, group, &g_sing); + // Nope, create a new one. + return MeshManager::getSingleton().createManual(name, group, NIFLoader::getSingletonPtr()); } /* More code currently not in use, from the old D source. This was @@ -779,70 +910,5 @@ extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height, pixelBuffer->unlock(); } -// We need this later for animated meshes. -extern "C" void* ogre_setupSkeleton(char* name) -{ - SkeletonPtr skel = SkeletonManager::getSingleton().create( - name, "Closet", true); - - skel->load(); - - // Create all bones at the origin and unrotated. This is necessary - // since our submeshes each have their own model space. We must - // move the bones after creating an entity, then copy this entity. - return (void*)skel->createBone(); -} - -extern "C" void *ogre_insertBone(char* name, void* rootBone, int32_t index) -{ - return (void*) ( ((Bone*)rootBone)->createChild(index) ); -} -*/ -/* This was the D part: - - // Create a skeleton and get the root bone (index 0) - BonePtr bone = ogre_setupSkeleton(name); - // Reset the bone index. The next bone to be created has index 1. - boneIndex = 1; - // Create a mesh and assign the skeleton to it - MeshPtr mesh = ogre_setupMesh(name); - - // Loop through the nodes, creating submeshes, materials and - // skeleton bones in the process. - handleNode(node, bone, mesh); - - // Create the "template" entity - EntityPtr entity = ogre_createEntity(name); - - // Loop through once again, this time to set the right - // transformations on the entity's SkeletonInstance. The order of - // children will be the same, allowing us to reference bones using - // their boneIndex. - int lastBone = boneIndex; - boneIndex = 1; - transformBones(node, entity); - if(lastBone != boneIndex) writefln("WARNING: Bone number doesn't match"); - - if(!hasBBox) - ogre_setMeshBoundingBox(mesh, minX, minY, minZ, maxX, maxY, maxZ); - - return entity; -} -void handleNode(Node node, BonePtr root, MeshPtr mesh) -{ - // Insert a new bone for this node - BonePtr bone = ogre_insertBone(node.name, root, boneIndex++); - -} - -void transformBones(Node node, EntityPtr entity) -{ - ogre_transformBone(entity, &node.trafo, boneIndex++); - - NiNode n = cast(NiNode)node; - if(n !is null) - foreach(Node nd; n.children) - transformBones(nd, entity); -} */ diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 96b746e16..aea8d3a8d 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -27,6 +27,27 @@ #include #include #include +#include + + +class BoundsFinder; + +namespace Nif +{ + class Node; + class Transformation; + class NiTriShape; + class Vector; + class Matrix; +} + +namespace Mangle +{ + namespace VFS + { + class OgreVFS; + } +} /** Manual resource loader for NIF meshes. This is the main class responsible for translating the internal NIF mesh structure into @@ -43,12 +64,66 @@ very resource intensive, and can safely be done for a large number of meshes at load time. */ -struct NIFLoader : Ogre::ManualResourceLoader +class NIFLoader : Ogre::ManualResourceLoader { - void loadResource(Ogre::Resource *resource); + public: + static NIFLoader& getSingleton(); + static NIFLoader* getSingletonPtr(); + + virtual void loadResource(Ogre::Resource *resource); + + static Ogre::MeshPtr load(const std::string &name, + const std::string &group="General"); + + Ogre::Vector3 convertVector3(const Nif::Vector& vec); + Ogre::Quaternion convertRotation(const Nif::Matrix& rot); + + private: + NIFLoader() : resourceGroup("General") {} + NIFLoader(NIFLoader& n) {} + + void warn(std::string msg); + void fail(std::string msg); + + void handleNode( Nif::Node *node, int flags, + const Nif::Transformation *trafo, BoundsFinder &bounds, Ogre::Bone *parentBone); + + void handleNiTriShape(Nif::NiTriShape *shape, int flags, BoundsFinder &bounds); + + void createOgreSubMesh(Nif::NiTriShape *shape, const Ogre::String &material); + + void createMaterial(const Ogre::String &name, + const Nif::Vector &ambient, + const Nif::Vector &diffuse, + const Nif::Vector &specular, + const Nif::Vector &emissive, + float glossiness, float alpha, + float alphaFlags, float alphaTest, + const Ogre::String &texName); + + void findRealTexture(Ogre::String &texName); + + Ogre::String getUniqueName(const Ogre::String &input); + + //returns the skeleton name of this mesh + std::string getSkeletonName() + { + return resourceName + ".skel"; + } + + // This is the interface to the Ogre resource system. It allows us to + // load NIFs from BSAs, in the file system and in any other place we + // tell Ogre to look (eg. in zip or rar files.) It's also used to + // check for the existence of texture files, so we can exchange the + // extension from .tga to .dds if the texture is missing. + Mangle::VFS::OgreVFS *vfs; + + std::string resourceName; + std::string resourceGroup; - static Ogre::MeshPtr load(const std::string &name, - const std::string &group="General"); + // pointer to the ogre mesh which is currently build + Ogre::Mesh *mesh; + Ogre::SkeletonPtr skel; }; #endif diff --git a/libs/mangle b/libs/mangle index c7b179d65..c982f701c 160000 --- a/libs/mangle +++ b/libs/mangle @@ -1 +1 @@ -Subproject commit c7b179d6546688208528c8eef681d42b7c1ec7be +Subproject commit c982f701cacdd2932bfdc22b168f54221a549b62 diff --git a/libs/openengine b/libs/openengine index c04d72cbe..b9d4dc448 160000 --- a/libs/openengine +++ b/libs/openengine @@ -1 +1 @@ -Subproject commit c04d72cbe380217c2d1d60f8a2c6e4810fe4c050 +Subproject commit b9d4dc448bc3be908653f9dea3c3450fb85ed107 diff --git a/old_d_version/scene/gamesettings.d b/old_d_version/scene/gamesettings.d deleted file mode 100644 index a07133fe3..000000000 --- a/old_d_version/scene/gamesettings.d +++ /dev/null @@ -1,47 +0,0 @@ -module scene.gamesettings; - -import monster.monster; -import esm.records : gameSettings; -import esm.defs : VarType; -import std.stdio; -import std.string; - -MonsterObject *gmstObj; - -void loadGameSettings() -{ - // Load the GameSettings Monster class, and get the singleton - // instance - MonsterClass mc = vm.load("GMST"); - gmstObj = mc.getSing(); - - foreach(a, b; gameSettings.names) - { - assert(a == b.id); - assert(a[0] == 'i' || a[0] == 'f' || a[0] == 's'); - - // There's three cases of variable names containing - // spaces. Since these are so seldom, it makes more sense to - // make special workarounds for them instead of searching every - // string. - char[] name = a; - if(name.length > 13 && (name[6] == ' ' || name[5] == ' ')) - { - name = name.replace(" ", "_"); - // Make sure we don't change the original string! - assert(name != a); - } - - if(!mc.sc.lookupName(name).isVar) - { - writefln("WARNING: GMST %s not supported!", name); - continue; - } - - if(b.type == VarType.Int) gmstObj.setInt(name, b.i); - else if(b.type == VarType.Float) gmstObj.setFloat(name, b.f); - // TODO: At some point we will probably translate strings into - // UTF32 at load time, so string8 will not be needed here. - else if(b.type == VarType.String) gmstObj.setString8(name, b.str); - } -} diff --git a/old_d_version/scene/soundlist.d b/old_d_version/scene/soundlist.d deleted file mode 100644 index bad47f2df..000000000 --- a/old_d_version/scene/soundlist.d +++ /dev/null @@ -1,96 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (soundlist.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module scene.soundlist; - -import esm.loadsoun; -import sound.audio; -import sound.sfx; - -SoundList soundScene; - -// Has a list over all sounds currently playing in the -// scene. Currently only holds static, looping sounds, mainly -// torches. Later I will have to decide what to do with play-once -// sounds. Do I bother to update their position, or do I assume that -// once it starts playing, it is short enough that it doesn't matter? -// The last option is probably not viable, since some sounds can be -// very long. -struct SoundList -{ - SoundInstance[] list; - - // Get a sound instance from a Sound struct - static SoundInstance getInstance(Sound *s, bool loop=false) - { - const distFactor = 40.0; // Just guessing, really. - - assert(!s.sound.isEmpty()); - - SoundInstance inst = s.sound.getInstance(); - inst.setParams(s.data.volume/255.0, - s.data.minRange*distFactor, - s.data.maxRange*distFactor, - loop); - return inst; - } - - SoundInstance *insert(Sound *snd, bool loop=false) - { - // For some reason, we get called with empty sound instances here - // if some files are missing, but not for others. Check into it - // later. - if(snd.sound.isEmpty) return null; - - // Reuse a dead instance if one exists - foreach(ref s; list) - { - if(s.owner == null) - { - s = getInstance(snd, loop); - return &s; - } - } - // Otherwise append a new one - list ~= getInstance(snd, loop); - return &list[$-1]; - } - - void update(float x, float y, float z, - float frontx, float fronty, float frontz, - float upx, float upy, float upz) - { - SoundInstance.setPlayerPos(x,y,z,frontx,fronty,frontz,upx,upy,upz); - foreach(ref s; list) - if(s.owner) s.updateSound(); - } - - void kill() - { - foreach(ref s; list) - { - if(s.owner) s.kill(); - } - list = null; - } -}