mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 18:19:55 +00:00
Merge branch 'master' into new-script-api
This commit is contained in:
commit
045dc566ea
66 changed files with 802 additions and 537 deletions
|
@ -34,6 +34,7 @@ Programmers
|
|||
Ben Shealy (bentsherman)
|
||||
Bret Curtis (psi29a)
|
||||
Britt Mathis (galdor557)
|
||||
Capostrophic
|
||||
cc9cii
|
||||
Chris Boyce (slothlife)
|
||||
Chris Robinson (KittyCat)
|
||||
|
|
|
@ -1,3 +1,43 @@
|
|||
# Apps and tools
|
||||
option(BUILD_OPENMW "build OpenMW" ON)
|
||||
option(BUILD_OPENMW_MP "build OpenMW-MP" ON)
|
||||
option(BUILD_MASTER "build tes3mp master server" OFF)
|
||||
option(BUILD_BSATOOL "build BSA extractor" ON)
|
||||
option(BUILD_ESMTOOL "build ESM inspector" ON)
|
||||
option(BUILD_LAUNCHER "build Launcher" ON)
|
||||
option(BUILD_BROWSER "build tes3mp Server Browser" ON)
|
||||
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
|
||||
option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON)
|
||||
option(BUILD_OPENCS "build OpenMW Construction Set" ON)
|
||||
option(BUILD_WIZARD "build Installation Wizard" ON)
|
||||
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
|
||||
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
|
||||
option(BUILD_NIFTEST "build nif file tester" OFF)
|
||||
option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON)
|
||||
option(BUILD_DOCS "build documentation." OFF )
|
||||
|
||||
if (NOT BUILD_LAUNCHER AND NOT BUILD_BROWSER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
|
||||
set(USE_QT FALSE)
|
||||
else()
|
||||
set(USE_QT TRUE)
|
||||
endif()
|
||||
|
||||
if (USE_QT)
|
||||
set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)")
|
||||
set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
# OS X build process relies on this fix: https://github.com/Kitware/CMake/commit/3df5147043d83aa09acd5c9ce31d5c602efb99db
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
elseif (USE_QT AND DESIRED_QT_VERSION MATCHES 5)
|
||||
# 2.8.11+ is required to make Qt5 happy and allow linking QtMain on Windows.
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
else()
|
||||
# We probably support older versions than this.
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
endif()
|
||||
|
||||
project(OpenMW)
|
||||
|
||||
# If the user doesn't supply a CMAKE_BUILD_TYPE via command line, choose one for them.
|
||||
|
@ -59,24 +99,6 @@ option(QT_STATIC "Link static build of QT into the binaries" FALSE)
|
|||
|
||||
option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE)
|
||||
|
||||
# Apps and tools
|
||||
option(BUILD_OPENMW "build OpenMW" ON)
|
||||
option(BUILD_OPENMW_MP "build OpenMW-MP" ON)
|
||||
option(BUILD_MASTER "build tes3mp master server" OFF)
|
||||
option(BUILD_BSATOOL "build BSA extractor" ON)
|
||||
option(BUILD_ESMTOOL "build ESM inspector" ON)
|
||||
option(BUILD_LAUNCHER "build Launcher" ON)
|
||||
option(BUILD_BROWSER "build tes3mp Server Browser" ON)
|
||||
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
|
||||
option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON)
|
||||
option(BUILD_OPENCS "build OpenMW Construction Set" ON)
|
||||
option(BUILD_WIZARD "build Installation Wizard" ON)
|
||||
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
|
||||
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
|
||||
option(BUILD_NIFTEST "build nif file tester" OFF)
|
||||
option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON)
|
||||
option(BUILD_DOCS "build documentation." OFF )
|
||||
|
||||
# what is necessary to build documentation
|
||||
IF( BUILD_DOCS )
|
||||
# Builds the documentation.
|
||||
|
@ -126,16 +148,8 @@ endif()
|
|||
find_package(RakNet REQUIRED)
|
||||
include_directories(${RakNet_INCLUDES})
|
||||
|
||||
if (NOT BUILD_LAUNCHER AND NOT BUILD_BROWSER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
|
||||
set(USE_QT FALSE)
|
||||
else()
|
||||
set(USE_QT TRUE)
|
||||
endif()
|
||||
|
||||
# Dependencies
|
||||
if (USE_QT)
|
||||
set(DESIRED_QT_VERSION 5 CACHE STRING "The QT version OpenMW should use (4 or 5)")
|
||||
set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5)
|
||||
message(STATUS "Using Qt${DESIRED_QT_VERSION}")
|
||||
|
||||
if (DESIRED_QT_VERSION MATCHES 4)
|
||||
|
@ -150,17 +164,6 @@ if (USE_QT)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
# OS X build process relies on this fix: https://github.com/Kitware/CMake/commit/3df5147043d83aa09acd5c9ce31d5c602efb99db
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
elseif (USE_QT AND DESIRED_QT_VERSION MATCHES 5)
|
||||
# 2.8.11+ is required to make Qt5 happy and allow linking QtMain on Windows.
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
else()
|
||||
# We probably support older versions than this.
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
endif()
|
||||
|
||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
||||
# Sound setup
|
||||
find_package(FFmpeg REQUIRED COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE)
|
||||
|
@ -217,7 +220,8 @@ endif()
|
|||
|
||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
||||
|
||||
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle osgUtil osgFX)
|
||||
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX)
|
||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||
|
||||
set(USED_OSG_PLUGINS
|
||||
osgdb_bmp
|
||||
|
@ -261,8 +265,6 @@ IF(BUILD_OPENMW OR BUILD_OPENCS)
|
|||
|
||||
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
|
||||
|
||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||
|
||||
|
||||
set(BOOST_COMPONENTS system filesystem program_options)
|
||||
if(WIN32)
|
||||
|
@ -357,6 +359,8 @@ if (NOT WIN32 AND NOT APPLE)
|
|||
"${OpenMW_BINARY_DIR}/openmw.desktop")
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.appdata.xml
|
||||
"${OpenMW_BINARY_DIR}/openmw.appdata.xml")
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/tes3mp-browser.desktop
|
||||
"${OpenMW_BINARY_DIR}/tes3mp-browser.desktop")
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.desktop
|
||||
"${OpenMW_BINARY_DIR}/openmw-cs.desktop")
|
||||
endif()
|
||||
|
@ -437,6 +441,9 @@ IF(NOT WIN32 AND NOT APPLE)
|
|||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw")
|
||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" COMPONENT "openmw")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.appdata.xml" DESTINATION "${DATAROOTDIR}/metainfo" COMPONENT "openmw")
|
||||
IF(BUILD_BROWSER)
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/tes3mp-browser.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "browser")
|
||||
ENDIF(BUILD_BROWSER)
|
||||
IF(BUILD_OPENCS)
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "opencs")
|
||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/openmw-cs.png" DESTINATION "${ICONDIR}" COMPONENT "opencs")
|
||||
|
|
|
@ -4,7 +4,7 @@ set(BSATOOL
|
|||
source_group(apps\\bsatool FILES ${BSATOOL})
|
||||
|
||||
# Main executable
|
||||
add_executable(bsatool
|
||||
openmw_add_executable(bsatool
|
||||
${BSATOOL}
|
||||
)
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ set(ESMTOOL
|
|||
source_group(apps\\esmtool FILES ${ESMTOOL})
|
||||
|
||||
# Main executable
|
||||
add_executable(esmtool
|
||||
openmw_add_executable(esmtool
|
||||
${ESMTOOL}
|
||||
)
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ set(ESSIMPORTER_FILES
|
|||
convertplayer.cpp
|
||||
)
|
||||
|
||||
add_executable(openmw-essimporter
|
||||
openmw_add_executable(openmw-essimporter
|
||||
${ESSIMPORTER_FILES}
|
||||
)
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ if(NOT WIN32)
|
|||
endif(NOT WIN32)
|
||||
|
||||
# Main executable
|
||||
add_executable(openmw-launcher
|
||||
openmw_add_executable(openmw-launcher
|
||||
${GUI_TYPE}
|
||||
${LAUNCHER}
|
||||
${LAUNCHER_HEADER}
|
||||
|
|
|
@ -9,7 +9,7 @@ set(MWINIIMPORT_HEADER
|
|||
|
||||
source_group(launcher FILES ${MWINIIMPORT} ${MWINIIMPORT_HEADER})
|
||||
|
||||
add_executable(openmw-iniimporter
|
||||
openmw_add_executable(openmw-iniimporter
|
||||
${MWINIIMPORT}
|
||||
)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ set(NIFTEST
|
|||
source_group(components\\nif\\tests FILES ${NIFTEST})
|
||||
|
||||
# Main executable
|
||||
add_executable(niftest
|
||||
openmw_add_executable(niftest
|
||||
${NIFTEST}
|
||||
)
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ else()
|
|||
set (OPENCS_OPENMW_CFG "")
|
||||
endif(APPLE)
|
||||
|
||||
add_executable(openmw-cs
|
||||
openmw_add_executable(openmw-cs
|
||||
MACOSX_BUNDLE
|
||||
${OPENCS_SRC}
|
||||
${OPENCS_UI_HDR}
|
||||
|
@ -199,7 +199,7 @@ if(APPLE)
|
|||
RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}"
|
||||
OUTPUT_NAME ${OPENCS_BUNDLE_NAME}
|
||||
MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "OpenCS"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "OpenMW-CS"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION}
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION ${OPENMW_VERSION}
|
||||
|
|
|
@ -132,7 +132,7 @@ namespace CSMPrefs
|
|||
if (mods && i == 0)
|
||||
{
|
||||
if (mods & Qt::ControlModifier)
|
||||
result.append("Ctl+");
|
||||
result.append("Ctrl+");
|
||||
if (mods & Qt::ShiftModifier)
|
||||
result.append("Shift+");
|
||||
if (mods & Qt::AltModifier)
|
||||
|
@ -196,7 +196,7 @@ namespace CSMPrefs
|
|||
|
||||
std::string name = value.substr(start, end - start);
|
||||
|
||||
if (name == "Ctl")
|
||||
if (name == "Ctrl")
|
||||
{
|
||||
mods |= Qt::ControlModifier;
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ add_openmw_dir (mwmp/processors/world BaseObjectProcessor ProcessorConsoleComman
|
|||
# Main executable
|
||||
|
||||
if (NOT ANDROID)
|
||||
add_executable(tes3mp
|
||||
openmw_add_executable(tes3mp
|
||||
${OPENMW_FILES}
|
||||
${GAME} ${GAME_HEADER}
|
||||
${APPLE_BUNDLE_RESOURCES}
|
||||
|
|
|
@ -257,6 +257,7 @@ namespace MWBase
|
|||
|
||||
virtual void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count) = 0;
|
||||
|
||||
virtual bool isAttackPrepairing(const MWWorld::Ptr& ptr) = 0;
|
||||
virtual bool isRunning(const MWWorld::Ptr& ptr) = 0;
|
||||
virtual bool isSneaking(const MWWorld::Ptr& ptr) = 0;
|
||||
};
|
||||
|
|
|
@ -380,7 +380,7 @@ namespace MWBase
|
|||
|
||||
// In WindowManager for now since there isn't a VFS singleton
|
||||
virtual std::string correctIconPath(const std::string& path) = 0;
|
||||
virtual std::string correctBookartPath(const std::string& path, int width, int height) = 0;
|
||||
virtual std::string correctBookartPath(const std::string& path, int width, int height, bool* exists = nullptr) = 0;
|
||||
virtual std::string correctTexturePath(const std::string& path) = 0;
|
||||
virtual bool textureExists(const std::string& path) = 0;
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <components/esm/loadlock.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
|
@ -155,6 +156,16 @@ namespace MWClass
|
|||
return MWWorld::Ptr(cell.insert(ref), &cell);
|
||||
}
|
||||
|
||||
std::pair<int, std::string> Lockpick::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const
|
||||
{
|
||||
// Do not allow equip tools from inventory during attack
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc)
|
||||
&& MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
return std::make_pair(0, "#{sCantEquipWeapWarning}");
|
||||
|
||||
return std::make_pair(1, "");
|
||||
}
|
||||
|
||||
bool Lockpick::canSell (const MWWorld::ConstPtr& item, int npcServices) const
|
||||
{
|
||||
return (npcServices & ESM::NPC::Picks) != 0;
|
||||
|
|
|
@ -51,6 +51,8 @@ namespace MWClass
|
|||
virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const;
|
||||
///< Return name of inventory icon.
|
||||
|
||||
virtual std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const;
|
||||
|
||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr)
|
||||
const;
|
||||
///< Generate action for using via inventory menu
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <components/esm/loadprob.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
|
@ -155,6 +156,16 @@ namespace MWClass
|
|||
return MWWorld::Ptr(cell.insert(ref), &cell);
|
||||
}
|
||||
|
||||
std::pair<int, std::string> Probe::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const
|
||||
{
|
||||
// Do not allow equip tools from inventory during attack
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc)
|
||||
&& MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
return std::make_pair(0, "#{sCantEquipWeapWarning}");
|
||||
|
||||
return std::make_pair(1, "");
|
||||
}
|
||||
|
||||
bool Probe::canSell (const MWWorld::ConstPtr& item, int npcServices) const
|
||||
{
|
||||
return (npcServices & ESM::NPC::Probes) != 0;
|
||||
|
|
|
@ -51,6 +51,8 @@ namespace MWClass
|
|||
virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const;
|
||||
///< Return name of inventory icon.
|
||||
|
||||
virtual std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const;
|
||||
|
||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr)
|
||||
const;
|
||||
///< Generate action for using via inventory menu
|
||||
|
|
|
@ -374,7 +374,9 @@ namespace MWClass
|
|||
if (hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0)
|
||||
return std::make_pair(0, "#{sInventoryMessage1}");
|
||||
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc))
|
||||
// Do not allow equip weapons from inventory during attack
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc)
|
||||
&& MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
return std::make_pair(0, "#{sCantEquipWeapWarning}");
|
||||
|
||||
std::pair<std::vector<int>, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr);
|
||||
|
|
|
@ -634,7 +634,7 @@ namespace MWGui
|
|||
|
||||
void DialogueWindow::onFrame()
|
||||
{
|
||||
if(mMainWidget->getVisible() && mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name())
|
||||
if(mMainWidget->getVisible() && mPtr.getTypeName() == typeid(ESM::NPC).name())
|
||||
{
|
||||
int disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr);
|
||||
mDispositionBar->setProgressRange(100);
|
||||
|
|
|
@ -275,8 +275,6 @@ namespace MWGui
|
|||
{
|
||||
case BookTextParser::Event_ImgTag:
|
||||
{
|
||||
pag.setIgnoreLeadingEmptyLines(false);
|
||||
|
||||
const BookTextParser::Attributes & attr = parser.getAttributes();
|
||||
|
||||
if (attr.find("src") == attr.end() || attr.find("width") == attr.end() || attr.find("height") == attr.end())
|
||||
|
@ -286,8 +284,19 @@ namespace MWGui
|
|||
int width = MyGUI::utility::parseInt(attr.at("width"));
|
||||
int height = MyGUI::utility::parseInt(attr.at("height"));
|
||||
|
||||
bool exists;
|
||||
std::string correctedSrc = MWBase::Environment::get().getWindowManager()->correctBookartPath(src, width, height, &exists);
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
std::cerr << "Warning: Could not find \"" << src << "\" referenced by an <img> tag." << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
pag.setIgnoreLeadingEmptyLines(false);
|
||||
|
||||
ImageElement elem(paper, pag, mBlockStyle,
|
||||
src, width, height);
|
||||
correctedSrc, width, height);
|
||||
elem.paginate();
|
||||
break;
|
||||
}
|
||||
|
@ -471,8 +480,7 @@ namespace MWGui
|
|||
MyGUI::IntCoord(left, pag.getCurrentTop(), width, mImageHeight), MyGUI::Align::Left | MyGUI::Align::Top,
|
||||
parent->getName() + MyGUI::utility::toString(parent->getChildCount()));
|
||||
|
||||
std::string image = MWBase::Environment::get().getWindowManager()->correctBookartPath(src, width, mImageHeight);
|
||||
mImageBox->setImageTexture(image);
|
||||
mImageBox->setImageTexture(src);
|
||||
mImageBox->setProperty("NeedMouse", "false");
|
||||
}
|
||||
|
||||
|
|
|
@ -269,6 +269,19 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
// If we unequip weapon during attack, it can lead to unexpected behaviour
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPtr))
|
||||
{
|
||||
bool isWeapon = item.mBase.getTypeName() == typeid(ESM::Weapon).name();
|
||||
MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);
|
||||
|
||||
if (isWeapon && invStore.isEquipped(item.mBase))
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sCantEquipWeapWarning}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 1 && !shift)
|
||||
{
|
||||
CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog();
|
||||
|
|
|
@ -129,7 +129,7 @@ namespace MWGui
|
|||
Disable increases for Security and Sneak when using ignoreJailSkillIncreases
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->ignoreJailSkillIncreases)
|
||||
value.setBase(value.getBase() - 1);
|
||||
value.setBase(std::max(0, value.getBase()-1));
|
||||
else if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak)
|
||||
/*
|
||||
End of tes3mp change (minor)
|
||||
|
|
|
@ -143,10 +143,10 @@ namespace MWGui
|
|||
mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + MyGUI::utility::toString(level));
|
||||
|
||||
std::string levelupdescription;
|
||||
if(level > 20)
|
||||
levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+MyGUI::utility::toString(level));
|
||||
|
||||
if (levelupdescription == "")
|
||||
levelupdescription=world->getFallback()->getFallbackString("Level_Up_Default");
|
||||
else
|
||||
levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+MyGUI::utility::toString(level));
|
||||
|
||||
mLevelDescription->setCaption (levelupdescription);
|
||||
|
||||
|
|
|
@ -344,16 +344,17 @@ namespace MWGui
|
|||
{
|
||||
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
|
||||
bool isWeapon = item.getTypeName() == typeid(ESM::Weapon).name();
|
||||
bool isTool = item.getTypeName() == typeid(ESM::Probe).name() || item.getTypeName() == typeid(ESM::Lockpick).name();
|
||||
|
||||
// delay weapon switching if player is busy
|
||||
if (isDelayNeeded && isWeapon)
|
||||
if (isDelayNeeded && (isWeapon || isTool))
|
||||
{
|
||||
mActivatedIndex = index;
|
||||
return;
|
||||
}
|
||||
|
||||
// disable weapon switching if player is dead or paralyzed
|
||||
if (isReturnNeeded && isWeapon)
|
||||
if (isReturnNeeded && (isWeapon || isTool))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -2128,9 +2128,12 @@ namespace MWGui
|
|||
return Misc::ResourceHelpers::correctIconPath(path, mResourceSystem->getVFS());
|
||||
}
|
||||
|
||||
std::string WindowManager::correctBookartPath(const std::string& path, int width, int height)
|
||||
std::string WindowManager::correctBookartPath(const std::string& path, int width, int height, bool* exists)
|
||||
{
|
||||
return Misc::ResourceHelpers::correctBookartPath(path, width, height, mResourceSystem->getVFS());
|
||||
std::string corrected = Misc::ResourceHelpers::correctBookartPath(path, width, height, mResourceSystem->getVFS());
|
||||
if (exists)
|
||||
*exists = mResourceSystem->getVFS()->exists(corrected);
|
||||
return corrected;
|
||||
}
|
||||
|
||||
std::string WindowManager::correctTexturePath(const std::string& path)
|
||||
|
|
|
@ -408,7 +408,7 @@ namespace MWGui
|
|||
|
||||
// In WindowManager for now since there isn't a VFS singleton
|
||||
virtual std::string correctIconPath(const std::string& path);
|
||||
virtual std::string correctBookartPath(const std::string& path, int width, int height);
|
||||
virtual std::string correctBookartPath(const std::string& path, int width, int height, bool* exists = nullptr);
|
||||
virtual std::string correctTexturePath(const std::string& path);
|
||||
virtual bool textureExists(const std::string& path);
|
||||
|
||||
|
|
|
@ -985,7 +985,11 @@ namespace MWInput
|
|||
if (!mControlSwitch["playerfighting"] || !mControlSwitch["playercontrols"])
|
||||
return;
|
||||
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer()))
|
||||
// We want to interrupt animation only if attack is prepairing, but still is not triggered
|
||||
// Otherwise we will get a "speedshooting" exploit, when player can skip reload animation by hitting "Toggle Weapon" key twice
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackPrepairing(mPlayer->getPlayer()))
|
||||
mPlayer->setAttackingOrSpell(false);
|
||||
else if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer()))
|
||||
return;
|
||||
|
||||
MWMechanics::DrawState_ state = mPlayer->getDrawState();
|
||||
|
|
|
@ -443,10 +443,15 @@ namespace MWMechanics
|
|||
End of tes3mp addition
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// Make guards go aggressive with creatures that are in combat, unless the creature is a follower or escorter
|
||||
if (actor1.getClass().isClass(actor1, "Guard") && !actor2.getClass().isNpc())
|
||||
{
|
||||
// Check if the creature is too far
|
||||
static const float fAlarmRadius = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fAlarmRadius")->getFloat();
|
||||
if (sqrDist > fAlarmRadius * fAlarmRadius)
|
||||
return;
|
||||
|
||||
bool followerOrEscorter = false;
|
||||
for (std::list<MWMechanics::AiPackage*>::const_iterator it = creatureStats2.getAiSequence().begin(); it != creatureStats2.getAiSequence().end(); ++it)
|
||||
{
|
||||
|
@ -851,6 +856,16 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
bool Actors::isAttackPrepairing(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
PtrActorMap::iterator it = mActors.find(ptr);
|
||||
if (it == mActors.end())
|
||||
return false;
|
||||
CharacterController* ctrl = it->second->getCharacterController();
|
||||
|
||||
return ctrl->isAttackPrepairing();
|
||||
}
|
||||
|
||||
bool Actors::isRunning(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
PtrActorMap::iterator it = mActors.find(ptr);
|
||||
|
@ -1065,7 +1080,18 @@ namespace MWMechanics
|
|||
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr))
|
||||
{
|
||||
static const int iCrimeThresholdMultiplier = esmStore.get<ESM::GameSetting>().find("iCrimeThresholdMultiplier")->getInt();
|
||||
if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier)
|
||||
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Only attack players based on their high bounty if they haven't died since the last
|
||||
time an attempt was made to arrest them
|
||||
*/
|
||||
if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier
|
||||
&& !mwmp::Main::get().getLocalPlayer()->diedSinceArrestAttempt)
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
{
|
||||
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player);
|
||||
creatureStats.setHitAttemptActorId(player.getClass().getCreatureStats(player).getActorId()); // Stops the guard from quitting combat if player is unreachable
|
||||
|
@ -1096,6 +1122,24 @@ namespace MWMechanics
|
|||
// Update witness crime id
|
||||
npcStats.setCrimeId(-1);
|
||||
}
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
If the player has died, stop combat with them as though they had
|
||||
paid their bounty
|
||||
*/
|
||||
else if (mwmp::Main::get().getLocalPlayer()->diedSinceArrestAttempt)
|
||||
{
|
||||
if (creatureStats.getAiSequence().isInCombat(player))
|
||||
{
|
||||
creatureStats.getAiSequence().stopCombat();
|
||||
creatureStats.setAttacked(false);
|
||||
creatureStats.setAlarmed(false);
|
||||
}
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ namespace MWMechanics
|
|||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
bool isAttackPrepairing(const MWWorld::Ptr& ptr);
|
||||
bool isRunning(const MWWorld::Ptr& ptr);
|
||||
bool isSneaking(const MWWorld::Ptr& ptr);
|
||||
|
||||
|
|
|
@ -13,7 +13,10 @@
|
|||
|
||||
Include additional headers for multiplayer purposes
|
||||
*/
|
||||
#include <components/openmw-mp/Log.hpp>
|
||||
#include "../mwgui/windowmanagerimp.hpp"
|
||||
#include "../mwmp/Main.hpp"
|
||||
#include "../mwmp/LocalPlayer.hpp"
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
@ -81,6 +84,18 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
|||
|
||||
if (pathTo(actor, dest, duration, 100)) {
|
||||
target.getClass().activate(target,actor).get()->execute(actor); //Arrest player when reached
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Record that the player has not died since the last attempt to arrest them
|
||||
*/
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "After being pursued by %s, diedSinceArrestAttempt is now false", actor.getCellRef().getRefId().c_str());
|
||||
mwmp::Main::get().getLocalPlayer()->diedSinceArrestAttempt = false;
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -262,22 +262,16 @@ const ESM::Potion *MWMechanics::Alchemy::getRecord(const ESM::Potion& toFind) co
|
|||
|
||||
void MWMechanics::Alchemy::removeIngredients()
|
||||
{
|
||||
bool needsUpdate = false;
|
||||
|
||||
for (TIngredientsContainer::iterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter)
|
||||
if (!iter->isEmpty())
|
||||
{
|
||||
iter->getContainerStore()->remove(*iter, 1, mAlchemist);
|
||||
|
||||
if (iter->getRefData().getCount()<1)
|
||||
{
|
||||
needsUpdate = true;
|
||||
*iter = MWWorld::Ptr();
|
||||
}
|
||||
}
|
||||
|
||||
if (needsUpdate)
|
||||
updateEffects();
|
||||
updateEffects();
|
||||
}
|
||||
|
||||
void MWMechanics::Alchemy::addPotion (const std::string& name)
|
||||
|
|
|
@ -2357,6 +2357,12 @@ void CharacterController::setAttackTypeBasedOnMovement()
|
|||
mAttackType = "chop";
|
||||
}
|
||||
|
||||
bool CharacterController::isAttackPrepairing() const
|
||||
{
|
||||
return mUpperBodyState == UpperCharState_StartToMinAttack ||
|
||||
mUpperBodyState == UpperCharState_MinAttackToMaxAttack;
|
||||
}
|
||||
|
||||
bool CharacterController::isReadyToBlock() const
|
||||
{
|
||||
return updateCarriedLeftVisible(mWeaponType);
|
||||
|
|
|
@ -263,6 +263,7 @@ public:
|
|||
|
||||
void forceStateUpdate();
|
||||
|
||||
bool isAttackPrepairing() const;
|
||||
bool isReadyToBlock() const;
|
||||
bool isKnockedOut() const;
|
||||
bool isSneaking() const;
|
||||
|
|
|
@ -436,6 +436,11 @@ namespace MWMechanics
|
|||
mObjects.update(duration, paused);
|
||||
}
|
||||
|
||||
bool MechanicsManager::isAttackPrepairing(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
return mActors.isAttackPrepairing(ptr);
|
||||
}
|
||||
|
||||
bool MechanicsManager::isRunning(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
return mActors.isRunning(ptr);
|
||||
|
|
|
@ -220,8 +220,10 @@ namespace MWMechanics
|
|||
|
||||
virtual void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count);
|
||||
|
||||
virtual bool isAttackPrepairing(const MWWorld::Ptr& ptr);
|
||||
virtual bool isRunning(const MWWorld::Ptr& ptr);
|
||||
virtual bool isSneaking(const MWWorld::Ptr& ptr);
|
||||
|
||||
private:
|
||||
void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
|
||||
OffenseType type, int arg=0);
|
||||
|
|
|
@ -422,7 +422,7 @@ bool MWMechanics::NpcStats::hasSkillsForRank (const std::string& factionId, int
|
|||
for (int i=0; i<7; ++i)
|
||||
{
|
||||
if (faction.mData.mSkills[i] != -1)
|
||||
skills.push_back (static_cast<int> (getSkill (faction.mData.mSkills[i]).getModified()));
|
||||
skills.push_back (static_cast<int> (getSkill (faction.mData.mSkills[i]).getBase()));
|
||||
}
|
||||
|
||||
if (skills.empty())
|
||||
|
|
|
@ -61,6 +61,10 @@ LocalPlayer::LocalPlayer()
|
|||
|
||||
jailProgressText = "";
|
||||
jailEndText = "";
|
||||
|
||||
isWerewolf = false;
|
||||
|
||||
diedSinceArrestAttempt = false;
|
||||
}
|
||||
|
||||
LocalPlayer::~LocalPlayer()
|
||||
|
@ -93,15 +97,8 @@ void LocalPlayer::update()
|
|||
updateDeadState();
|
||||
updateEquipment();
|
||||
updateStatsDynamic();
|
||||
|
||||
// Only send attributes and skills if we are not a werewolf, or they will be
|
||||
// overwritten by the werewolf ones
|
||||
if (!isWerewolf)
|
||||
{
|
||||
updateAttributes();
|
||||
updateSkills();
|
||||
}
|
||||
|
||||
updateAttributes();
|
||||
updateSkills();
|
||||
updateLevel();
|
||||
updateBounty();
|
||||
}
|
||||
|
@ -230,6 +227,10 @@ void LocalPlayer::updateStatsDynamic(bool forceUpdate)
|
|||
|
||||
void LocalPlayer::updateAttributes(bool forceUpdate)
|
||||
{
|
||||
// Only send attributes if we are not a werewolf, or they will be
|
||||
// overwritten by the werewolf ones
|
||||
if (isWerewolf) return;
|
||||
|
||||
MWWorld::Ptr ptrPlayer = getPlayerPtr();
|
||||
const MWMechanics::NpcStats &ptrNpcStats = ptrPlayer.getClass().getNpcStats(ptrPlayer);
|
||||
bool attributesChanged = false;
|
||||
|
@ -252,6 +253,10 @@ void LocalPlayer::updateAttributes(bool forceUpdate)
|
|||
|
||||
void LocalPlayer::updateSkills(bool forceUpdate)
|
||||
{
|
||||
// Only send skills if we are not a werewolf, or they will be
|
||||
// overwritten by the werewolf ones
|
||||
if (isWerewolf) return;
|
||||
|
||||
MWWorld::Ptr ptrPlayer = getPlayerPtr();
|
||||
const MWMechanics::NpcStats &ptrNpcStats = ptrPlayer.getClass().getNpcStats(ptrPlayer);
|
||||
|
||||
|
|
|
@ -52,6 +52,12 @@ namespace mwmp
|
|||
// readied but be unable to use it unless we clear it here
|
||||
playerPtr.getClass().getNpcStats(playerPtr).setDrawState(MWMechanics::DrawState_Nothing);
|
||||
|
||||
// Record that the player has died since the last attempt was made to arrest them,
|
||||
// used to make guards lenient enough to attempt an arrest again
|
||||
player->diedSinceArrestAttempt = true;
|
||||
|
||||
LOG_APPEND(Log::LOG_INFO, "- diedSinceArrestAttempt is now true");
|
||||
|
||||
packet.setPlayer(player);
|
||||
packet.Send(serverAddr);
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace MWPhysics
|
|||
|
||||
Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<const Resource::BulletShape> shape, btCollisionWorld* world)
|
||||
: mCanWaterWalk(false), mWalkingOnWater(false)
|
||||
, mCollisionObject(nullptr), mForce(0.f, 0.f, 0.f), mOnGround(false), mOnSlope(false)
|
||||
, mCollisionObject(nullptr), mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false)
|
||||
, mInternalCollisionMode(true)
|
||||
, mExternalCollisionMode(true)
|
||||
, mCollisionWorld(world)
|
||||
|
|
|
@ -182,7 +182,10 @@ namespace
|
|||
void remove()
|
||||
{
|
||||
for (RemoveVec::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it)
|
||||
it->second->removeChild(it->first);
|
||||
{
|
||||
if (!it->second->removeChild(it->first))
|
||||
std::cerr << "error removing " << it->first->getName() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -1192,6 +1195,9 @@ namespace MWRender
|
|||
mObjectRoot->addChild(created);
|
||||
mInsert->addChild(mObjectRoot);
|
||||
}
|
||||
osg::ref_ptr<SceneUtil::Skeleton> skel = dynamic_cast<SceneUtil::Skeleton*>(mObjectRoot.get());
|
||||
if (skel)
|
||||
mSkeleton = skel.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -118,7 +118,7 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot)
|
|||
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(item.getClass().getModel(item));
|
||||
|
||||
const NodeMap& nodeMap = getNodeMap();
|
||||
NodeMap::const_iterator found = getNodeMap().find(Misc::StringUtils::lowerCase(bonename));
|
||||
NodeMap::const_iterator found = nodeMap.find(Misc::StringUtils::lowerCase(bonename));
|
||||
if (found == nodeMap.end())
|
||||
throw std::runtime_error("Can't find attachment node " + bonename);
|
||||
osg::ref_ptr<osg::Node> attached = SceneUtil::attach(node, mObjectRoot, bonename, found->second.get());
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
|
@ -919,6 +920,9 @@ void NpcAnimation::showWeapons(bool showWeapon)
|
|||
else
|
||||
{
|
||||
removeIndividualPart(ESM::PRT_Weapon);
|
||||
// If we remove/hide weapon from player, we should reset attack animation as well
|
||||
if (mPtr == MWMechanics::getPlayer())
|
||||
MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1310,6 +1310,14 @@ namespace MWScript
|
|||
if (mNegativeEffect != -1)
|
||||
currentValue -= effects.get(mNegativeEffect).getMagnitude();
|
||||
|
||||
// GetResist* should take in account elemental shields
|
||||
if (mPositiveEffect == ESM::MagicEffect::ResistFire)
|
||||
currentValue += effects.get(ESM::MagicEffect::FireShield).getMagnitude();
|
||||
if (mPositiveEffect == ESM::MagicEffect::ResistShock)
|
||||
currentValue += effects.get(ESM::MagicEffect::LightningShield).getMagnitude();
|
||||
if (mPositiveEffect == ESM::MagicEffect::ResistFrost)
|
||||
currentValue += effects.get(ESM::MagicEffect::FrostShield).getMagnitude();
|
||||
|
||||
int ret = static_cast<int>(currentValue);
|
||||
runtime.push(ret);
|
||||
}
|
||||
|
@ -1336,6 +1344,14 @@ namespace MWScript
|
|||
if (mNegativeEffect != -1)
|
||||
currentValue -= effects.get(mNegativeEffect).getMagnitude();
|
||||
|
||||
// SetResist* should take in account elemental shields
|
||||
if (mPositiveEffect == ESM::MagicEffect::ResistFire)
|
||||
currentValue += effects.get(ESM::MagicEffect::FireShield).getMagnitude();
|
||||
if (mPositiveEffect == ESM::MagicEffect::ResistShock)
|
||||
currentValue += effects.get(ESM::MagicEffect::LightningShield).getMagnitude();
|
||||
if (mPositiveEffect == ESM::MagicEffect::ResistFrost)
|
||||
currentValue += effects.get(ESM::MagicEffect::FrostShield).getMagnitude();
|
||||
|
||||
int arg = runtime[0].mInteger;
|
||||
runtime.pop();
|
||||
effects.modifyBase(mPositiveEffect, (arg - static_cast<int>(currentValue)));
|
||||
|
|
|
@ -17,7 +17,7 @@ if (GTEST_FOUND)
|
|||
|
||||
source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES})
|
||||
|
||||
add_executable(openmw_test_suite openmw_test_suite.cpp ${UNITTEST_SRC_FILES})
|
||||
openmw_add_executable(openmw_test_suite openmw_test_suite.cpp ${UNITTEST_SRC_FILES})
|
||||
|
||||
target_link_libraries(openmw_test_suite ${GTEST_BOTH_LIBRARIES} components)
|
||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||
|
|
|
@ -96,7 +96,7 @@ if (OPENMW_USE_UNSHIELD)
|
|||
include_directories(${LIBUNSHIELD_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
add_executable(openmw-wizard
|
||||
openmw_add_executable(openmw-wizard
|
||||
${GUI_TYPE}
|
||||
${WIZARD}
|
||||
${WIZARD_HEADER}
|
||||
|
|
|
@ -145,3 +145,32 @@ foreach (u ${ARGN})
|
|||
add_hdr (OPENCS ${dir} ${u})
|
||||
endforeach (u)
|
||||
endmacro (opencs_hdrs_noqt)
|
||||
|
||||
include(CMakeParseArguments)
|
||||
|
||||
macro (openmw_add_executable target)
|
||||
set(OMW_ADD_EXE_OPTIONS WIN32 MACOSX_BUNDLE EXCLUDE_FROM_ALL)
|
||||
set(OMW_ADD_EXE_VALUES)
|
||||
set(OMW_ADD_EXE_MULTI_VALUES)
|
||||
cmake_parse_arguments(OMW_ADD_EXE "${OMW_ADD_EXE_OPTIONS}" "${OMW_ADD_EXE_VALUES}" "${OMW_ADD_EXE_MULTI_VALUES}" ${ARGN})
|
||||
|
||||
if (OMW_ADD_EXE_WIN32)
|
||||
set(OMW_ADD_EXE_WIN32_VALUE WIN32)
|
||||
endif (OMW_ADD_EXE_WIN32)
|
||||
|
||||
if (OMW_ADD_EXE_MACOSX_BUNDLE)
|
||||
set(OMW_ADD_EXE_MACOSX_BUNDLE_VALUE MACOSX_BUNDLE)
|
||||
endif (OMW_ADD_EXE_MACOSX_BUNDLE)
|
||||
|
||||
if (OMW_ADD_EXE_EXCLUDE_FROM_ALL)
|
||||
set(OMW_ADD_EXE_EXCLUDE_FROM_ALL_VALUE EXCLUDE_FROM_ALL)
|
||||
endif (OMW_ADD_EXE_EXCLUDE_FROM_ALL)
|
||||
|
||||
add_executable(${target} ${OMW_ADD_EXE_WIN32_VALUE} ${OMW_ADD_EXE_MACOSX_BUNDLE_VALUE} ${OMW_ADD_EXE_EXCLUDE_FROM_ALL_VALUE} ${OMW_ADD_EXE_UNPARSED_ARGUMENTS})
|
||||
|
||||
if (MSVC)
|
||||
if (CMAKE_VERSION VERSION_GREATER 3.8 OR CMAKE_VERSION VERSION_EQUAL 3.8)
|
||||
set_target_properties(${target} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$(TargetDir)")
|
||||
endif (CMAKE_VERSION VERSION_GREATER 3.8 OR CMAKE_VERSION VERSION_EQUAL 3.8)
|
||||
endif (MSVC)
|
||||
endmacro (openmw_add_executable)
|
||||
|
|
|
@ -52,7 +52,7 @@ add_component_dir (shader
|
|||
)
|
||||
|
||||
add_component_dir (sceneutil
|
||||
clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller
|
||||
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
|
||||
lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer
|
||||
)
|
||||
|
||||
|
|
|
@ -6,11 +6,10 @@
|
|||
#include <osg/Texture2D>
|
||||
#include <osg/UserDataContainer>
|
||||
|
||||
#include <osgAnimation/MorphGeometry>
|
||||
|
||||
#include <osgParticle/Emitter>
|
||||
|
||||
#include <components/nif/data.hpp>
|
||||
#include <components/sceneutil/morphgeometry.hpp>
|
||||
|
||||
#include "userdata.hpp"
|
||||
|
||||
|
@ -188,7 +187,7 @@ GeomMorpherController::GeomMorpherController(const Nif::NiMorphData *data)
|
|||
|
||||
void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable)
|
||||
{
|
||||
osgAnimation::MorphGeometry* morphGeom = static_cast<osgAnimation::MorphGeometry*>(drawable);
|
||||
SceneUtil::MorphGeometry* morphGeom = static_cast<SceneUtil::MorphGeometry*>(drawable);
|
||||
if (hasInput())
|
||||
{
|
||||
if (mKeyFrames.size() <= 1)
|
||||
|
@ -202,7 +201,7 @@ void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable
|
|||
val = it->interpKey(input);
|
||||
val = std::max(0.f, std::min(1.f, val));
|
||||
|
||||
osgAnimation::MorphGeometry::MorphTarget& target = morphGeom->getMorphTarget(i);
|
||||
SceneUtil::MorphGeometry::MorphTarget& target = morphGeom->getMorphTarget(i);
|
||||
if (target.getWeight() != val)
|
||||
{
|
||||
target.setWeight(val);
|
||||
|
@ -210,8 +209,6 @@ void GeomMorpherController::update(osg::NodeVisitor *nv, osg::Drawable *drawable
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// morphGeometry::transformSoftwareMethod() done in cull callback i.e. only for visible morph geometries
|
||||
}
|
||||
|
||||
UVController::UVController()
|
||||
|
|
|
@ -31,11 +31,6 @@ namespace osgParticle
|
|||
class Emitter;
|
||||
}
|
||||
|
||||
namespace osgAnimation
|
||||
{
|
||||
class MorphGeometry;
|
||||
}
|
||||
|
||||
namespace NifOsg
|
||||
{
|
||||
|
||||
|
@ -172,7 +167,7 @@ namespace NifOsg
|
|||
virtual float getMaximum() const;
|
||||
};
|
||||
|
||||
/// Must be set on an osgAnimation::MorphGeometry.
|
||||
/// Must be set on a SceneUtil::MorphGeometry.
|
||||
class GeomMorpherController : public osg::Drawable::UpdateCallback, public SceneUtil::Controller
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -13,9 +13,6 @@
|
|||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/imagemanager.hpp>
|
||||
|
||||
// skel
|
||||
#include <osgAnimation/MorphGeometry>
|
||||
|
||||
// particle
|
||||
#include <osgParticle/ParticleSystem>
|
||||
#include <osgParticle/ParticleSystemUpdater>
|
||||
|
@ -39,6 +36,7 @@
|
|||
#include <components/nif/effect.hpp>
|
||||
#include <components/sceneutil/skeleton.hpp>
|
||||
#include <components/sceneutil/riggeometry.hpp>
|
||||
#include <components/sceneutil/morphgeometry.hpp>
|
||||
|
||||
#include "particle.hpp"
|
||||
#include "userdata.hpp"
|
||||
|
@ -83,35 +81,6 @@ namespace
|
|||
collectDrawableProperties(nifNode->parent, out);
|
||||
}
|
||||
|
||||
class FrameSwitch : public osg::Group
|
||||
{
|
||||
public:
|
||||
FrameSwitch()
|
||||
{
|
||||
}
|
||||
|
||||
FrameSwitch(const FrameSwitch& copy, const osg::CopyOp& copyop)
|
||||
: osg::Group(copy, copyop)
|
||||
{
|
||||
}
|
||||
|
||||
META_Object(NifOsg, FrameSwitch)
|
||||
|
||||
virtual void traverse(osg::NodeVisitor& nv)
|
||||
{
|
||||
if (nv.getTraversalMode() != osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN && nv.getVisitorType() != osg::NodeVisitor::UPDATE_VISITOR)
|
||||
osg::Group::traverse(nv);
|
||||
else
|
||||
{
|
||||
for (unsigned int i=0; i<getNumChildren(); ++i)
|
||||
{
|
||||
if (i%2 == nv.getTraversalNumber()%2)
|
||||
getChild(i)->accept(nv);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// NodeCallback used to have a node always oriented towards the camera. The node can have translation and scale
|
||||
// set just like a regular MatrixTransform, but the rotation set will be overridden in order to face the camera.
|
||||
// Must be set as a cull callback.
|
||||
|
@ -154,70 +123,6 @@ namespace
|
|||
}
|
||||
};
|
||||
|
||||
struct UpdateMorphGeometry : public osg::Drawable::CullCallback
|
||||
{
|
||||
UpdateMorphGeometry()
|
||||
: mLastFrameNumber(0)
|
||||
{
|
||||
}
|
||||
|
||||
UpdateMorphGeometry(const UpdateMorphGeometry& copy, const osg::CopyOp& copyop)
|
||||
: osg::Drawable::CullCallback(copy, copyop)
|
||||
, mLastFrameNumber(0)
|
||||
{
|
||||
}
|
||||
|
||||
META_Object(NifOsg, UpdateMorphGeometry)
|
||||
|
||||
virtual bool cull(osg::NodeVisitor* nv, osg::Drawable * drw, osg::State *) const
|
||||
{
|
||||
osgAnimation::MorphGeometry* geom = static_cast<osgAnimation::MorphGeometry*>(drw);
|
||||
if (!geom)
|
||||
return false;
|
||||
|
||||
if (mLastFrameNumber == nv->getTraversalNumber())
|
||||
return false;
|
||||
mLastFrameNumber = nv->getTraversalNumber();
|
||||
|
||||
geom->transformSoftwareMethod();
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable unsigned int mLastFrameNumber;
|
||||
};
|
||||
|
||||
// Callback to return a static bounding box for a MorphGeometry. The idea is to not recalculate the bounding box
|
||||
// every time the morph weights change. To do so we return a maximum containing box that is big enough for all possible combinations of morph targets.
|
||||
class StaticBoundingBoxCallback : public osg::Drawable::ComputeBoundingBoxCallback
|
||||
{
|
||||
public:
|
||||
StaticBoundingBoxCallback()
|
||||
{
|
||||
}
|
||||
|
||||
StaticBoundingBoxCallback(const osg::BoundingBox& bounds)
|
||||
: mBoundingBox(bounds)
|
||||
{
|
||||
}
|
||||
|
||||
StaticBoundingBoxCallback(const StaticBoundingBoxCallback& copy, const osg::CopyOp& copyop)
|
||||
: osg::Drawable::ComputeBoundingBoxCallback(copy, copyop)
|
||||
, mBoundingBox(copy.mBoundingBox)
|
||||
{
|
||||
}
|
||||
|
||||
META_Object(NifOsg, StaticBoundingBoxCallback)
|
||||
|
||||
virtual osg::BoundingBox computeBound(const osg::Drawable&) const
|
||||
{
|
||||
return mBoundingBox;
|
||||
}
|
||||
|
||||
private:
|
||||
osg::BoundingBox mBoundingBox;
|
||||
};
|
||||
|
||||
void extractTextKeys(const Nif::NiTextKeyExtraData *tk, NifOsg::TextKeyMap &textkeys)
|
||||
{
|
||||
for(size_t i = 0;i < tk->list.size();i++)
|
||||
|
@ -1107,106 +1012,49 @@ namespace NifOsg
|
|||
|
||||
void handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<int>& boundTextures, int animflags)
|
||||
{
|
||||
osg::ref_ptr<osg::Geometry> geometry;
|
||||
osg::ref_ptr<osg::Drawable> drawable;
|
||||
for (Nif::ControllerPtr ctrl = triShape->controller; !ctrl.empty(); ctrl = ctrl->next)
|
||||
{
|
||||
if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active))
|
||||
continue;
|
||||
if(ctrl->recType == Nif::RC_NiGeomMorpherController)
|
||||
{
|
||||
geometry = handleMorphGeometry(static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr()), triShape, parentNode, composite, boundTextures, animflags);
|
||||
drawable = handleMorphGeometry(static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr()), triShape, parentNode, composite, boundTextures, animflags);
|
||||
|
||||
osg::ref_ptr<GeomMorpherController> morphctrl = new GeomMorpherController(
|
||||
static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr())->data.getPtr());
|
||||
setupController(ctrl.getPtr(), morphctrl, animflags);
|
||||
geometry->setUpdateCallback(morphctrl);
|
||||
drawable->setUpdateCallback(morphctrl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!geometry.get())
|
||||
if (!drawable.get())
|
||||
{
|
||||
geometry = new osg::Geometry;
|
||||
triShapeToGeometry(triShape, geometry, parentNode, composite, boundTextures, animflags);
|
||||
osg::ref_ptr<osg::Geometry> geom (new osg::Geometry);
|
||||
drawable = geom;
|
||||
triShapeToGeometry(triShape, geom, parentNode, composite, boundTextures, animflags);
|
||||
}
|
||||
|
||||
if (geometry->getDataVariance() == osg::Object::DYNAMIC)
|
||||
{
|
||||
// Add a copy, we will alternate between the two copies every other frame using the FrameSwitch
|
||||
// This is so we can set the DataVariance as STATIC, giving a huge performance boost
|
||||
geometry->setDataVariance(osg::Object::STATIC);
|
||||
osg::ref_ptr<FrameSwitch> frameswitch = new FrameSwitch;
|
||||
drawable->setName(triShape->name);
|
||||
|
||||
osg::ref_ptr<osg::Geometry> geom2 = osg::clone(geometry.get(), osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES);
|
||||
frameswitch->addChild(geometry);
|
||||
frameswitch->addChild(geom2);
|
||||
|
||||
parentNode->addChild(frameswitch);
|
||||
}
|
||||
else
|
||||
parentNode->addChild(geometry);
|
||||
parentNode->addChild(drawable);
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Geometry> handleMorphGeometry(const Nif::NiGeomMorpherController* morpher, const Nif::NiTriShape *triShape, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<int>& boundTextures, int animflags)
|
||||
osg::ref_ptr<osg::Drawable> handleMorphGeometry(const Nif::NiGeomMorpherController* morpher, const Nif::NiTriShape *triShape, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<int>& boundTextures, int animflags)
|
||||
{
|
||||
osg::ref_ptr<osgAnimation::MorphGeometry> morphGeom = new osgAnimation::MorphGeometry;
|
||||
morphGeom->setMethod(osgAnimation::MorphGeometry::RELATIVE);
|
||||
// No normals available in the MorphData
|
||||
morphGeom->setMorphNormals(false);
|
||||
osg::ref_ptr<SceneUtil::MorphGeometry> morphGeom = new SceneUtil::MorphGeometry;
|
||||
|
||||
morphGeom->setUpdateCallback(NULL);
|
||||
morphGeom->setCullCallback(new UpdateMorphGeometry);
|
||||
morphGeom->setUseVertexBufferObjects(true);
|
||||
|
||||
triShapeToGeometry(triShape, morphGeom, parentNode, composite, boundTextures, animflags);
|
||||
|
||||
morphGeom->getOrCreateVertexBufferObject()->setUsage(GL_DYNAMIC_DRAW_ARB);
|
||||
osg::ref_ptr<osg::Geometry> sourceGeometry (new osg::Geometry);
|
||||
triShapeToGeometry(triShape, sourceGeometry, parentNode, composite, boundTextures, animflags);
|
||||
morphGeom->setSourceGeometry(sourceGeometry);
|
||||
|
||||
const std::vector<Nif::NiMorphData::MorphData>& morphs = morpher->data.getPtr()->mMorphs;
|
||||
if (morphs.empty())
|
||||
return morphGeom;
|
||||
// Note we are not interested in morph 0, which just contains the original vertices
|
||||
for (unsigned int i = 1; i < morphs.size(); ++i)
|
||||
{
|
||||
osg::ref_ptr<osg::Geometry> morphTarget = new osg::Geometry;
|
||||
morphTarget->setVertexArray(new osg::Vec3Array(morphs[i].mVertices.size(), &morphs[i].mVertices[0]));
|
||||
morphGeom->addMorphTarget(morphTarget, 0.f);
|
||||
}
|
||||
|
||||
// build the bounding box containing all possible morph combinations
|
||||
|
||||
std::vector<osg::BoundingBox> vertBounds(morphs[0].mVertices.size());
|
||||
|
||||
// Since we don't know what combinations of morphs are being applied we need to keep track of a bounding box for each vertex.
|
||||
// The minimum/maximum of the box is the minimum/maximum offset the vertex can have from its starting position.
|
||||
|
||||
// Start with zero offsets which will happen when no morphs are applied.
|
||||
for (unsigned int i=0; i<vertBounds.size(); ++i)
|
||||
vertBounds[i].set(osg::Vec3f(0,0,0), osg::Vec3f(0,0,0));
|
||||
|
||||
for (unsigned int i = 1; i < morphs.size(); ++i)
|
||||
{
|
||||
for (unsigned int j=0; j<morphs[i].mVertices.size() && vertBounds.size(); ++j)
|
||||
{
|
||||
osg::BoundingBox& bounds = vertBounds[j];
|
||||
bounds.expandBy(bounds._max + morphs[i].mVertices[j]);
|
||||
bounds.expandBy(bounds._min + morphs[i].mVertices[j]);
|
||||
}
|
||||
}
|
||||
|
||||
osg::BoundingBox box;
|
||||
for (unsigned int i=0; i<vertBounds.size(); ++i)
|
||||
{
|
||||
vertBounds[i]._max += morphs[0].mVertices[i];
|
||||
vertBounds[i]._min += morphs[0].mVertices[i];
|
||||
box.expandBy(vertBounds[i]);
|
||||
}
|
||||
|
||||
// For the initial bounding box (used for object placement) use the default pose, fire off a bounding compute to set this initial box
|
||||
morphGeom->getBound();
|
||||
|
||||
// Now set up the callback so that we get properly enlarged bounds if/when the mesh starts animating
|
||||
morphGeom->setComputeBoundingBoxCallback(new StaticBoundingBoxCallback(box));
|
||||
morphGeom->addMorphTarget(new osg::Vec3Array(morphs[i].mVertices.size(), &morphs[i].mVertices[0]), 0.f);
|
||||
|
||||
return morphGeom;
|
||||
}
|
||||
|
@ -1219,6 +1067,7 @@ namespace NifOsg
|
|||
|
||||
osg::ref_ptr<SceneUtil::RigGeometry> rig(new SceneUtil::RigGeometry);
|
||||
rig->setSourceGeometry(geometry);
|
||||
rig->setName(triShape->name);
|
||||
|
||||
const Nif::NiSkinInstance *skin = triShape->skin.getPtr();
|
||||
|
||||
|
@ -1233,7 +1082,6 @@ namespace NifOsg
|
|||
|
||||
SceneUtil::RigGeometry::BoneInfluence influence;
|
||||
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[i].weights;
|
||||
//influence.mWeights.reserve(weights.size());
|
||||
for(size_t j = 0;j < weights.size();j++)
|
||||
{
|
||||
std::pair<unsigned short, float> indexWeight = std::make_pair(weights[j].vertex, weights[j].weight);
|
||||
|
@ -1246,17 +1094,7 @@ namespace NifOsg
|
|||
}
|
||||
rig->setInfluenceMap(map);
|
||||
|
||||
// Add a copy, we will alternate between the two copies every other frame using the FrameSwitch
|
||||
// This is so we can set the DataVariance as STATIC, giving a huge performance boost
|
||||
rig->setDataVariance(osg::Object::STATIC);
|
||||
|
||||
osg::ref_ptr<FrameSwitch> frameswitch = new FrameSwitch;
|
||||
|
||||
SceneUtil::RigGeometry* rig2 = osg::clone(rig.get(), osg::CopyOp::DEEP_COPY_NODES|osg::CopyOp::DEEP_COPY_DRAWABLES);
|
||||
frameswitch->addChild(rig);
|
||||
frameswitch->addChild(rig2);
|
||||
|
||||
parentNode->addChild(frameswitch);
|
||||
parentNode->addChild(rig);
|
||||
}
|
||||
|
||||
osg::BlendFunc::BlendFuncMode getBlendMode(int mode)
|
||||
|
|
|
@ -243,6 +243,8 @@ namespace mwmp
|
|||
std::string jailEndText;
|
||||
|
||||
unsigned int resurrectType;
|
||||
|
||||
bool diedSinceArrestAttempt;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -80,11 +80,14 @@ namespace mwmp
|
|||
{
|
||||
if (write)
|
||||
{
|
||||
RakNet::RakString rstr("%s", str.c_str());
|
||||
if (compress)
|
||||
rstr.SerializeCompressed(bs);
|
||||
RakNet::RakString::SerializeCompressed(str.c_str(), bs);
|
||||
else
|
||||
{
|
||||
RakNet::RakString rstr;
|
||||
rstr = str.c_str();
|
||||
bs->Write(rstr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -32,29 +32,29 @@ namespace SceneUtil
|
|||
|
||||
virtual void apply(osg::MatrixTransform& node)
|
||||
{
|
||||
applyNode(node);
|
||||
}
|
||||
virtual void apply(osg::Geometry& node)
|
||||
{
|
||||
applyNode(node);
|
||||
traverse(node);
|
||||
}
|
||||
virtual void apply(osg::Node& node)
|
||||
{
|
||||
applyNode(node);
|
||||
traverse(node);
|
||||
}
|
||||
virtual void apply(osg::Group& node)
|
||||
{
|
||||
applyNode(node);
|
||||
traverse(node);
|
||||
}
|
||||
|
||||
void applyNode(osg::Node& node)
|
||||
virtual void apply(osg::Drawable& drawable)
|
||||
{
|
||||
std::string lowerName = Misc::StringUtils::lowerCase(node.getName());
|
||||
std::string lowerName = Misc::StringUtils::lowerCase(drawable.getName());
|
||||
if ((lowerName.size() >= mFilter.size() && lowerName.compare(0, mFilter.size(), mFilter) == 0)
|
||||
|| (lowerName.size() >= mFilter2.size() && lowerName.compare(0, mFilter2.size(), mFilter2) == 0))
|
||||
mToCopy.push_back(&node);
|
||||
else
|
||||
traverse(node);
|
||||
{
|
||||
osg::Node* node = &drawable;
|
||||
while (node && node->getNumParents() && !node->getStateSet())
|
||||
node = node->getParent(0);
|
||||
if (node)
|
||||
mToCopy.push_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
void doCopy()
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <osgParticle/Emitter>
|
||||
#include <osgParticle/Program>
|
||||
|
||||
#include <osgAnimation/MorphGeometry>
|
||||
#include <components/sceneutil/morphgeometry.hpp>
|
||||
|
||||
#include <components/sceneutil/riggeometry.hpp>
|
||||
|
||||
|
@ -49,46 +49,12 @@ namespace SceneUtil
|
|||
{
|
||||
if (const osgParticle::ParticleSystem* partsys = dynamic_cast<const osgParticle::ParticleSystem*>(drawable))
|
||||
return operator()(partsys);
|
||||
if (dynamic_cast<const osgAnimation::MorphGeometry*>(drawable))
|
||||
{
|
||||
osg::CopyOp copyop = *this;
|
||||
copyop.setCopyFlags(copyop.getCopyFlags()|osg::CopyOp::DEEP_COPY_ARRAYS);
|
||||
|
||||
#if OSG_VERSION_LESS_THAN(3,5,0)
|
||||
/*
|
||||
|
||||
Deep copy of primitives required to work around the following (bad?) code in osg::Geometry copy constructor:
|
||||
|
||||
if ((copyop.getCopyFlags() & osg::CopyOp::DEEP_COPY_ARRAYS))
|
||||
{
|
||||
if (_useVertexBufferObjects)
|
||||
{
|
||||
// copying of arrays doesn't set up buffer objects so we'll need to force
|
||||
// Geometry to assign these, we'll do this by switching off VBO's then renabling them.
|
||||
setUseVertexBufferObjects(false);
|
||||
setUseVertexBufferObjects(true);
|
||||
}
|
||||
}
|
||||
|
||||
In case of DEEP_COPY_PRIMITIVES=Off, DEEP_COPY_ARRAYS=On, the above code makes a modification to the original const Geometry& we copied from,
|
||||
causing problems if we relied on the original Geometry to remain static such as when it was added to an osgUtil::IncrementalCompileOperation.
|
||||
|
||||
Fixed in OSG 3.5 ( http://forum.openscenegraph.org/viewtopic.php?t=15217 ).
|
||||
|
||||
*/
|
||||
|
||||
copyop.setCopyFlags(copyop.getCopyFlags()|osg::CopyOp::DEEP_COPY_PRIMITIVES);
|
||||
#endif
|
||||
|
||||
osg::Drawable* cloned = osg::clone(drawable, copyop);
|
||||
return cloned;
|
||||
}
|
||||
if (dynamic_cast<const SceneUtil::RigGeometry*>(drawable))
|
||||
if (dynamic_cast<const SceneUtil::RigGeometry*>(drawable) || dynamic_cast<const SceneUtil::MorphGeometry*>(drawable))
|
||||
{
|
||||
return osg::clone(drawable, *this);
|
||||
}
|
||||
|
||||
|
||||
return osg::CopyOp::operator()(drawable);
|
||||
}
|
||||
|
||||
|
|
190
components/sceneutil/morphgeometry.cpp
Normal file
190
components/sceneutil/morphgeometry.cpp
Normal file
|
@ -0,0 +1,190 @@
|
|||
#include "morphgeometry.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
|
||||
MorphGeometry::MorphGeometry()
|
||||
: mLastFrameNumber(0)
|
||||
, mDirty(true)
|
||||
, mMorphedBoundingBox(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
MorphGeometry::MorphGeometry(const MorphGeometry ©, const osg::CopyOp ©op)
|
||||
: osg::Drawable(copy, copyop)
|
||||
, mMorphTargets(copy.mMorphTargets)
|
||||
, mLastFrameNumber(0)
|
||||
, mDirty(true)
|
||||
, mMorphedBoundingBox(false)
|
||||
{
|
||||
setSourceGeometry(copy.getSourceGeometry());
|
||||
}
|
||||
|
||||
void MorphGeometry::setSourceGeometry(osg::ref_ptr<osg::Geometry> sourceGeom)
|
||||
{
|
||||
mSourceGeometry = sourceGeom;
|
||||
|
||||
for (unsigned int i=0; i<2; ++i)
|
||||
{
|
||||
mGeometry[i] = new osg::Geometry(*mSourceGeometry, osg::CopyOp::SHALLOW_COPY);
|
||||
|
||||
const osg::Geometry& from = *mSourceGeometry;
|
||||
osg::Geometry& to = *mGeometry[i];
|
||||
to.setSupportsDisplayList(false);
|
||||
to.setUseVertexBufferObjects(true);
|
||||
to.setCullingActive(false); // make sure to disable culling since that's handled by this class
|
||||
|
||||
// vertices are modified every frame, so we need to deep copy them.
|
||||
// assign a dedicated VBO to make sure that modifications don't interfere with source geometry's VBO.
|
||||
osg::ref_ptr<osg::VertexBufferObject> vbo (new osg::VertexBufferObject);
|
||||
vbo->setUsage(GL_DYNAMIC_DRAW_ARB);
|
||||
|
||||
osg::ref_ptr<osg::Array> vertexArray = osg::clone(from.getVertexArray(), osg::CopyOp::DEEP_COPY_ALL);
|
||||
if (vertexArray)
|
||||
{
|
||||
vertexArray->setVertexBufferObject(vbo);
|
||||
to.setVertexArray(vertexArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MorphGeometry::addMorphTarget(osg::Vec3Array *offsets, float weight)
|
||||
{
|
||||
mMorphTargets.push_back(MorphTarget(offsets, weight));
|
||||
mMorphedBoundingBox = false;
|
||||
dirty();
|
||||
}
|
||||
|
||||
void MorphGeometry::dirty()
|
||||
{
|
||||
mDirty = true;
|
||||
if (!mMorphedBoundingBox)
|
||||
dirtyBound();
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Geometry> MorphGeometry::getSourceGeometry() const
|
||||
{
|
||||
return mSourceGeometry;
|
||||
}
|
||||
|
||||
void MorphGeometry::accept(osg::NodeVisitor &nv)
|
||||
{
|
||||
if (!nv.validNodeMask(*this))
|
||||
return;
|
||||
|
||||
nv.pushOntoNodePath(this);
|
||||
|
||||
if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
|
||||
cull(&nv);
|
||||
else
|
||||
nv.apply(*this);
|
||||
|
||||
nv.popFromNodePath();
|
||||
}
|
||||
|
||||
void MorphGeometry::accept(osg::PrimitiveFunctor& func) const
|
||||
{
|
||||
getGeometry(mLastFrameNumber)->accept(func);
|
||||
}
|
||||
|
||||
osg::BoundingBox MorphGeometry::computeBoundingBox() const
|
||||
{
|
||||
bool anyMorphTarget = false;
|
||||
for (unsigned int i=0; i<mMorphTargets.size(); ++i)
|
||||
if (mMorphTargets[i].getWeight() > 0)
|
||||
{
|
||||
anyMorphTarget = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// before the MorphGeometry has started animating, we will use a regular bounding box (this is required
|
||||
// for correct object placements, which uses the bounding box)
|
||||
if (!mMorphedBoundingBox && !anyMorphTarget)
|
||||
{
|
||||
return mSourceGeometry->getBoundingBox();
|
||||
}
|
||||
// once it animates, use a bounding box that encompasses all possible animations so as to avoid recalculating
|
||||
else
|
||||
{
|
||||
mMorphedBoundingBox = true;
|
||||
|
||||
osg::Vec3Array& sourceVerts = *static_cast<osg::Vec3Array*>(mSourceGeometry->getVertexArray());
|
||||
std::vector<osg::BoundingBox> vertBounds(sourceVerts.size());
|
||||
|
||||
// Since we don't know what combinations of morphs are being applied we need to keep track of a bounding box for each vertex.
|
||||
// The minimum/maximum of the box is the minimum/maximum offset the vertex can have from its starting position.
|
||||
|
||||
// Start with zero offsets which will happen when no morphs are applied.
|
||||
for (unsigned int i=0; i<vertBounds.size(); ++i)
|
||||
vertBounds[i].set(osg::Vec3f(0,0,0), osg::Vec3f(0,0,0));
|
||||
|
||||
for (unsigned int i = 0; i < mMorphTargets.size(); ++i)
|
||||
{
|
||||
const osg::Vec3Array& offsets = *mMorphTargets[i].getOffsets();
|
||||
for (unsigned int j=0; j<offsets.size() && j<vertBounds.size(); ++j)
|
||||
{
|
||||
osg::BoundingBox& bounds = vertBounds[j];
|
||||
bounds.expandBy(bounds._max + offsets[j]);
|
||||
bounds.expandBy(bounds._min + offsets[j]);
|
||||
}
|
||||
}
|
||||
|
||||
osg::BoundingBox box;
|
||||
for (unsigned int i=0; i<vertBounds.size(); ++i)
|
||||
{
|
||||
vertBounds[i]._max += sourceVerts[i];
|
||||
vertBounds[i]._min += sourceVerts[i];
|
||||
box.expandBy(vertBounds[i]);
|
||||
}
|
||||
return box;
|
||||
}
|
||||
}
|
||||
|
||||
void MorphGeometry::cull(osg::NodeVisitor *nv)
|
||||
{
|
||||
if (mLastFrameNumber == nv->getTraversalNumber() || !mDirty)
|
||||
{
|
||||
osg::Geometry& geom = *getGeometry(mLastFrameNumber);
|
||||
nv->pushOntoNodePath(&geom);
|
||||
nv->apply(geom);
|
||||
nv->popFromNodePath();
|
||||
return;
|
||||
}
|
||||
|
||||
mDirty = false;
|
||||
mLastFrameNumber = nv->getTraversalNumber();
|
||||
osg::Geometry& geom = *getGeometry(mLastFrameNumber);
|
||||
|
||||
const osg::Vec3Array* positionSrc = static_cast<osg::Vec3Array*>(mSourceGeometry->getVertexArray());
|
||||
osg::Vec3Array* positionDst = static_cast<osg::Vec3Array*>(geom.getVertexArray());
|
||||
assert(positionSrc->size() == positionDst->size());
|
||||
for (unsigned int vertex=0; vertex<positionSrc->size(); ++vertex)
|
||||
(*positionDst)[vertex] = (*positionSrc)[vertex];
|
||||
|
||||
for (unsigned int i=0; i<mMorphTargets.size(); ++i)
|
||||
{
|
||||
float weight = mMorphTargets[i].getWeight();
|
||||
if (weight == 0.f)
|
||||
continue;
|
||||
const osg::Vec3Array* offsets = mMorphTargets[i].getOffsets();
|
||||
for (unsigned int vertex=0; vertex<positionSrc->size(); ++vertex)
|
||||
(*positionDst)[vertex] += (*offsets)[vertex] * weight;
|
||||
}
|
||||
|
||||
positionDst->dirty();
|
||||
|
||||
nv->pushOntoNodePath(&geom);
|
||||
nv->apply(geom);
|
||||
nv->popFromNodePath();
|
||||
}
|
||||
|
||||
osg::Geometry* MorphGeometry::getGeometry(unsigned int frame) const
|
||||
{
|
||||
return mGeometry[frame%2];
|
||||
}
|
||||
|
||||
|
||||
}
|
83
components/sceneutil/morphgeometry.hpp
Normal file
83
components/sceneutil/morphgeometry.hpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
#ifndef OPENMW_COMPONENTS_MORPHGEOMETRY_H
|
||||
#define OPENMW_COMPONENTS_MORPHGEOMETRY_H
|
||||
|
||||
#include <osg/Geometry>
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
|
||||
/// @brief Vertex morphing implementation.
|
||||
/// @note The internal Geometry used for rendering is double buffered, this allows updates to be done in a thread safe way while
|
||||
/// not compromising rendering performance. This is crucial when using osg's default threading model of DrawThreadPerContext.
|
||||
class MorphGeometry : public osg::Drawable
|
||||
{
|
||||
public:
|
||||
MorphGeometry();
|
||||
MorphGeometry(const MorphGeometry& copy, const osg::CopyOp& copyop);
|
||||
|
||||
META_Object(SceneUtil, MorphGeometry)
|
||||
|
||||
/// Initialize this geometry from the source geometry.
|
||||
/// @note The source geometry will not be modified.
|
||||
void setSourceGeometry(osg::ref_ptr<osg::Geometry> sourceGeom);
|
||||
|
||||
class MorphTarget
|
||||
{
|
||||
protected:
|
||||
osg::ref_ptr<osg::Vec3Array> mOffsets;
|
||||
float mWeight;
|
||||
public:
|
||||
MorphTarget(osg::Vec3Array* offsets, float w = 1.0) : mOffsets(offsets), mWeight(w) {}
|
||||
void setWeight(float weight) { mWeight = weight; }
|
||||
float getWeight() const { return mWeight; }
|
||||
osg::Vec3Array* getOffsets() { return mOffsets.get(); }
|
||||
const osg::Vec3Array* getOffsets() const { return mOffsets.get(); }
|
||||
void setOffsets(osg::Vec3Array* offsets) { mOffsets = offsets; }
|
||||
};
|
||||
|
||||
typedef std::vector<MorphTarget> MorphTargetList;
|
||||
|
||||
virtual void addMorphTarget( osg::Vec3Array* offsets, float weight = 1.0 );
|
||||
|
||||
/** Set the MorphGeometry dirty.*/
|
||||
void dirty();
|
||||
|
||||
/** Get the list of MorphTargets.*/
|
||||
const MorphTargetList& getMorphTargetList() const { return mMorphTargets; }
|
||||
|
||||
/** Get the list of MorphTargets. Warning if you modify this array you will have to call dirty() */
|
||||
MorphTargetList& getMorphTargetList() { return mMorphTargets; }
|
||||
|
||||
/** Return the \c MorphTarget at position \c i.*/
|
||||
inline const MorphTarget& getMorphTarget( unsigned int i ) const { return mMorphTargets[i]; }
|
||||
|
||||
/** Return the \c MorphTarget at position \c i.*/
|
||||
inline MorphTarget& getMorphTarget( unsigned int i ) { return mMorphTargets[i]; }
|
||||
|
||||
osg::ref_ptr<osg::Geometry> getSourceGeometry() const;
|
||||
|
||||
virtual void accept(osg::NodeVisitor &nv);
|
||||
virtual bool supports(const osg::PrimitiveFunctor&) const { return true; }
|
||||
virtual void accept(osg::PrimitiveFunctor&) const;
|
||||
|
||||
virtual osg::BoundingBox computeBoundingBox() const;
|
||||
|
||||
private:
|
||||
void cull(osg::NodeVisitor* nv);
|
||||
|
||||
MorphTargetList mMorphTargets;
|
||||
|
||||
osg::ref_ptr<osg::Geometry> mSourceGeometry;
|
||||
|
||||
osg::ref_ptr<osg::Geometry> mGeometry[2];
|
||||
osg::Geometry* getGeometry(unsigned int frame) const;
|
||||
|
||||
unsigned int mLastFrameNumber;
|
||||
bool mDirty; // Have any morph targets changed?
|
||||
|
||||
mutable bool mMorphedBoundingBox;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -10,73 +10,17 @@
|
|||
namespace SceneUtil
|
||||
{
|
||||
|
||||
class UpdateRigBounds : public osg::Drawable::UpdateCallback
|
||||
{
|
||||
public:
|
||||
UpdateRigBounds()
|
||||
{
|
||||
}
|
||||
|
||||
UpdateRigBounds(const UpdateRigBounds& copy, const osg::CopyOp& copyop)
|
||||
: osg::Drawable::UpdateCallback(copy, copyop)
|
||||
{
|
||||
}
|
||||
|
||||
META_Object(SceneUtil, UpdateRigBounds)
|
||||
|
||||
void update(osg::NodeVisitor* nv, osg::Drawable* drw)
|
||||
{
|
||||
RigGeometry* rig = static_cast<RigGeometry*>(drw);
|
||||
|
||||
rig->updateBounds(nv);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: make threadsafe for multiple cull threads
|
||||
class UpdateRigGeometry : public osg::Drawable::CullCallback
|
||||
{
|
||||
public:
|
||||
UpdateRigGeometry()
|
||||
{
|
||||
}
|
||||
|
||||
UpdateRigGeometry(const UpdateRigGeometry& copy, const osg::CopyOp& copyop)
|
||||
: osg::Drawable::CullCallback(copy, copyop)
|
||||
{
|
||||
}
|
||||
|
||||
META_Object(SceneUtil, UpdateRigGeometry)
|
||||
|
||||
virtual bool cull(osg::NodeVisitor* nv, osg::Drawable* drw, osg::State*) const
|
||||
{
|
||||
RigGeometry* geom = static_cast<RigGeometry*>(drw);
|
||||
geom->update(nv);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// We can't compute the bounds without a NodeVisitor, since we need the current geomToSkelMatrix.
|
||||
// So we return nothing. Bounds are updated every frame in the UpdateCallback.
|
||||
class DummyComputeBoundCallback : public osg::Drawable::ComputeBoundingBoxCallback
|
||||
{
|
||||
public:
|
||||
virtual osg::BoundingBox computeBound(const osg::Drawable&) const { return osg::BoundingBox(); }
|
||||
};
|
||||
|
||||
RigGeometry::RigGeometry()
|
||||
: mSkeleton(NULL)
|
||||
, mLastFrameNumber(0)
|
||||
, mBoundsFirstFrame(true)
|
||||
{
|
||||
setCullCallback(new UpdateRigGeometry);
|
||||
setUpdateCallback(new UpdateRigBounds);
|
||||
setSupportsDisplayList(false);
|
||||
setUseVertexBufferObjects(true);
|
||||
setComputeBoundingBoxCallback(new DummyComputeBoundCallback);
|
||||
setUpdateCallback(new osg::Callback); // dummy to make sure getNumChildrenRequiringUpdateTraversal() is correct
|
||||
// update done in accept(NodeVisitor&)
|
||||
}
|
||||
|
||||
RigGeometry::RigGeometry(const RigGeometry ©, const osg::CopyOp ©op)
|
||||
: osg::Geometry(copy, copyop)
|
||||
: Drawable(copy, copyop)
|
||||
, mSkeleton(NULL)
|
||||
, mInfluenceMap(copy.mInfluenceMap)
|
||||
, mLastFrameNumber(0)
|
||||
|
@ -89,57 +33,47 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr<osg::Geometry> sourceGeometry)
|
|||
{
|
||||
mSourceGeometry = sourceGeometry;
|
||||
|
||||
osg::Geometry& from = *sourceGeometry;
|
||||
|
||||
if (from.getStateSet())
|
||||
setStateSet(from.getStateSet());
|
||||
|
||||
// shallow copy primitive sets & vertex attributes that we will not modify
|
||||
setPrimitiveSetList(from.getPrimitiveSetList());
|
||||
setColorArray(from.getColorArray());
|
||||
setSecondaryColorArray(from.getSecondaryColorArray());
|
||||
setFogCoordArray(from.getFogCoordArray());
|
||||
|
||||
// need to copy over texcoord list manually due to a missing null pointer check in setTexCoordArrayList(), this has been fixed in OSG 3.5
|
||||
osg::Geometry::ArrayList& texCoordList = from.getTexCoordArrayList();
|
||||
for (unsigned int i=0; i<texCoordList.size(); ++i)
|
||||
if (texCoordList[i])
|
||||
setTexCoordArray(i, texCoordList[i], osg::Array::BIND_PER_VERTEX);
|
||||
|
||||
setVertexAttribArrayList(from.getVertexAttribArrayList());
|
||||
|
||||
// vertices and normals are modified every frame, so we need to deep copy them.
|
||||
// assign a dedicated VBO to make sure that modifications don't interfere with source geometry's VBO.
|
||||
osg::ref_ptr<osg::VertexBufferObject> vbo (new osg::VertexBufferObject);
|
||||
vbo->setUsage(GL_DYNAMIC_DRAW_ARB);
|
||||
|
||||
osg::ref_ptr<osg::Array> vertexArray = osg::clone(from.getVertexArray(), osg::CopyOp::DEEP_COPY_ALL);
|
||||
if (vertexArray)
|
||||
for (unsigned int i=0; i<2; ++i)
|
||||
{
|
||||
vertexArray->setVertexBufferObject(vbo);
|
||||
setVertexArray(vertexArray);
|
||||
}
|
||||
const osg::Geometry& from = *sourceGeometry;
|
||||
mGeometry[i] = new osg::Geometry(from, osg::CopyOp::SHALLOW_COPY);
|
||||
osg::Geometry& to = *mGeometry[i];
|
||||
to.setSupportsDisplayList(false);
|
||||
to.setUseVertexBufferObjects(true);
|
||||
to.setCullingActive(false); // make sure to disable culling since that's handled by this class
|
||||
|
||||
if (osg::Array* normals = from.getNormalArray())
|
||||
{
|
||||
osg::ref_ptr<osg::Array> normalArray = osg::clone(normals, osg::CopyOp::DEEP_COPY_ALL);
|
||||
if (normalArray)
|
||||
// vertices and normals are modified every frame, so we need to deep copy them.
|
||||
// assign a dedicated VBO to make sure that modifications don't interfere with source geometry's VBO.
|
||||
osg::ref_ptr<osg::VertexBufferObject> vbo (new osg::VertexBufferObject);
|
||||
vbo->setUsage(GL_DYNAMIC_DRAW_ARB);
|
||||
|
||||
osg::ref_ptr<osg::Array> vertexArray = osg::clone(from.getVertexArray(), osg::CopyOp::DEEP_COPY_ALL);
|
||||
if (vertexArray)
|
||||
{
|
||||
normalArray->setVertexBufferObject(vbo);
|
||||
setNormalArray(normalArray, osg::Array::BIND_PER_VERTEX);
|
||||
vertexArray->setVertexBufferObject(vbo);
|
||||
to.setVertexArray(vertexArray);
|
||||
}
|
||||
}
|
||||
|
||||
if (const osg::Array* normals = from.getNormalArray())
|
||||
{
|
||||
osg::ref_ptr<osg::Array> normalArray = osg::clone(normals, osg::CopyOp::DEEP_COPY_ALL);
|
||||
if (normalArray)
|
||||
{
|
||||
normalArray->setVertexBufferObject(vbo);
|
||||
to.setNormalArray(normalArray, osg::Array::BIND_PER_VERTEX);
|
||||
}
|
||||
}
|
||||
|
||||
if (osg::Vec4Array* tangents = dynamic_cast<osg::Vec4Array*>(from.getTexCoordArray(7)))
|
||||
{
|
||||
mSourceTangents = tangents;
|
||||
osg::ref_ptr<osg::Array> tangentArray = osg::clone(tangents, osg::CopyOp::DEEP_COPY_ALL);
|
||||
tangentArray->setVertexBufferObject(vbo);
|
||||
setTexCoordArray(7, tangentArray, osg::Array::BIND_PER_VERTEX);
|
||||
if (const osg::Vec4Array* tangents = dynamic_cast<const osg::Vec4Array*>(from.getTexCoordArray(7)))
|
||||
{
|
||||
mSourceTangents = tangents;
|
||||
osg::ref_ptr<osg::Array> tangentArray = osg::clone(tangents, osg::CopyOp::DEEP_COPY_ALL);
|
||||
tangentArray->setVertexBufferObject(vbo);
|
||||
to.setTexCoordArray(7, tangentArray, osg::Array::BIND_PER_VERTEX);
|
||||
}
|
||||
else
|
||||
mSourceTangents = NULL;
|
||||
}
|
||||
else
|
||||
mSourceTangents = NULL;
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Geometry> RigGeometry::getSourceGeometry()
|
||||
|
@ -228,7 +162,7 @@ void accumulateMatrix(const osg::Matrixf& invBindMatrix, const osg::Matrixf& mat
|
|||
ptrresult[14] += ptr[14] * weight;
|
||||
}
|
||||
|
||||
void RigGeometry::update(osg::NodeVisitor* nv)
|
||||
void RigGeometry::cull(osg::NodeVisitor* nv)
|
||||
{
|
||||
if (!mSkeleton)
|
||||
{
|
||||
|
@ -238,23 +172,27 @@ void RigGeometry::update(osg::NodeVisitor* nv)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!mSkeleton->getActive() && mLastFrameNumber != 0)
|
||||
return;
|
||||
|
||||
if (mLastFrameNumber == nv->getTraversalNumber())
|
||||
if ((!mSkeleton->getActive() && mLastFrameNumber != 0) || mLastFrameNumber == nv->getTraversalNumber())
|
||||
{
|
||||
osg::Geometry& geom = *getGeometry(mLastFrameNumber);
|
||||
nv->pushOntoNodePath(&geom);
|
||||
nv->apply(geom);
|
||||
nv->popFromNodePath();
|
||||
return;
|
||||
}
|
||||
mLastFrameNumber = nv->getTraversalNumber();
|
||||
osg::Geometry& geom = *getGeometry(mLastFrameNumber);
|
||||
|
||||
mSkeleton->updateBoneMatrices(nv->getTraversalNumber());
|
||||
|
||||
// skinning
|
||||
osg::Vec3Array* positionSrc = static_cast<osg::Vec3Array*>(mSourceGeometry->getVertexArray());
|
||||
osg::Vec3Array* normalSrc = static_cast<osg::Vec3Array*>(mSourceGeometry->getNormalArray());
|
||||
osg::Vec4Array* tangentSrc = mSourceTangents;
|
||||
const osg::Vec3Array* positionSrc = static_cast<osg::Vec3Array*>(mSourceGeometry->getVertexArray());
|
||||
const osg::Vec3Array* normalSrc = static_cast<osg::Vec3Array*>(mSourceGeometry->getNormalArray());
|
||||
const osg::Vec4Array* tangentSrc = mSourceTangents;
|
||||
|
||||
osg::Vec3Array* positionDst = static_cast<osg::Vec3Array*>(getVertexArray());
|
||||
osg::Vec3Array* normalDst = static_cast<osg::Vec3Array*>(getNormalArray());
|
||||
osg::Vec4Array* tangentDst = static_cast<osg::Vec4Array*>(getTexCoordArray(7));
|
||||
osg::Vec3Array* positionDst = static_cast<osg::Vec3Array*>(geom.getVertexArray());
|
||||
osg::Vec3Array* normalDst = static_cast<osg::Vec3Array*>(geom.getNormalArray());
|
||||
osg::Vec4Array* tangentDst = static_cast<osg::Vec4Array*>(geom.getTexCoordArray(7));
|
||||
|
||||
for (Bone2VertexMap::const_iterator it = mBone2VertexMap.begin(); it != mBone2VertexMap.end(); ++it)
|
||||
{
|
||||
|
@ -294,6 +232,10 @@ void RigGeometry::update(osg::NodeVisitor* nv)
|
|||
normalDst->dirty();
|
||||
if (tangentDst)
|
||||
tangentDst->dirty();
|
||||
|
||||
nv->pushOntoNodePath(&geom);
|
||||
nv->apply(geom);
|
||||
nv->popFromNodePath();
|
||||
}
|
||||
|
||||
void RigGeometry::updateBounds(osg::NodeVisitor *nv)
|
||||
|
@ -365,5 +307,32 @@ void RigGeometry::setInfluenceMap(osg::ref_ptr<InfluenceMap> influenceMap)
|
|||
mInfluenceMap = influenceMap;
|
||||
}
|
||||
|
||||
void RigGeometry::accept(osg::NodeVisitor &nv)
|
||||
{
|
||||
if (!nv.validNodeMask(*this))
|
||||
return;
|
||||
|
||||
nv.pushOntoNodePath(this);
|
||||
|
||||
if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
|
||||
cull(&nv);
|
||||
else if (nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
|
||||
updateBounds(&nv);
|
||||
else
|
||||
nv.apply(*this);
|
||||
|
||||
nv.popFromNodePath();
|
||||
}
|
||||
|
||||
void RigGeometry::accept(osg::PrimitiveFunctor& func) const
|
||||
{
|
||||
getGeometry(mLastFrameNumber)->accept(func);
|
||||
}
|
||||
|
||||
osg::Geometry* RigGeometry::getGeometry(unsigned int frame) const
|
||||
{
|
||||
return mGeometry[frame%2].get();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -13,10 +13,9 @@ namespace SceneUtil
|
|||
/// @brief Mesh skinning implementation.
|
||||
/// @note A RigGeometry may be attached directly to a Skeleton, or somewhere below a Skeleton.
|
||||
/// Note though that the RigGeometry ignores any transforms below the Skeleton, so the attachment point is not that important.
|
||||
/// @note To avoid race conditions, the rig geometry needs to be double buffered. This can be done
|
||||
/// using a FrameSwitch node that has two RigGeometry children. In the future we may want to consider implementing
|
||||
/// the double buffering inside RigGeometry.
|
||||
class RigGeometry : public osg::Geometry
|
||||
/// @note The internal Geometry used for rendering is double buffered, this allows updates to be done in a thread safe way while
|
||||
/// not compromising rendering performance. This is crucial when using osg's default threading model of DrawThreadPerContext.
|
||||
class RigGeometry : public osg::Drawable
|
||||
{
|
||||
public:
|
||||
RigGeometry();
|
||||
|
@ -24,6 +23,9 @@ namespace SceneUtil
|
|||
|
||||
META_Object(SceneUtil, RigGeometry)
|
||||
|
||||
// At this point compileGLObjects() remains unimplemented, hard to avoid race conditions
|
||||
// and there is limited value in compiling anyway since the data will change again for the next frame
|
||||
|
||||
struct BoneInfluence
|
||||
{
|
||||
osg::Matrixf mInvBindMatrix;
|
||||
|
@ -45,15 +47,19 @@ namespace SceneUtil
|
|||
|
||||
osg::ref_ptr<osg::Geometry> getSourceGeometry();
|
||||
|
||||
// Called automatically by our CullCallback
|
||||
void update(osg::NodeVisitor* nv);
|
||||
|
||||
// Called automatically by our UpdateCallback
|
||||
void updateBounds(osg::NodeVisitor* nv);
|
||||
virtual void accept(osg::NodeVisitor &nv);
|
||||
virtual bool supports(const osg::PrimitiveFunctor&) const { return true; }
|
||||
virtual void accept(osg::PrimitiveFunctor&) const;
|
||||
|
||||
private:
|
||||
void cull(osg::NodeVisitor* nv);
|
||||
void updateBounds(osg::NodeVisitor* nv);
|
||||
|
||||
osg::ref_ptr<osg::Geometry> mGeometry[2];
|
||||
osg::Geometry* getGeometry(unsigned int frame) const;
|
||||
|
||||
osg::ref_ptr<osg::Geometry> mSourceGeometry;
|
||||
osg::ref_ptr<osg::Vec4Array> mSourceTangents;
|
||||
osg::ref_ptr<const osg::Vec4Array> mSourceTangents;
|
||||
Skeleton* mSkeleton;
|
||||
|
||||
osg::ref_ptr<osg::RefMatrix> mGeomToSkelMatrix;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
#include <components/sceneutil/skeleton.hpp>
|
||||
#include <components/sceneutil/riggeometry.hpp>
|
||||
#include <components/sceneutil/morphgeometry.hpp>
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
|
@ -37,20 +38,20 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class FrameSwitchSerializer : public osgDB::ObjectWrapper
|
||||
{
|
||||
public:
|
||||
FrameSwitchSerializer()
|
||||
: osgDB::ObjectWrapper(createInstanceFunc<osg::Group>, "NifOsg::FrameSwitch", "osg::Object osg::Node osg::Group NifOsg::FrameSwitch")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class RigGeometrySerializer : public osgDB::ObjectWrapper
|
||||
{
|
||||
public:
|
||||
RigGeometrySerializer()
|
||||
: osgDB::ObjectWrapper(createInstanceFunc<SceneUtil::RigGeometry>, "SceneUtil::RigGeometry", "osg::Object osg::Node osg::Drawable osg::Geometry SceneUtil::RigGeometry")
|
||||
: osgDB::ObjectWrapper(createInstanceFunc<SceneUtil::RigGeometry>, "SceneUtil::RigGeometry", "osg::Object osg::Node osg::Drawable SceneUtil::RigGeometry")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class MorphGeometrySerializer : public osgDB::ObjectWrapper
|
||||
{
|
||||
public:
|
||||
MorphGeometrySerializer()
|
||||
: osgDB::ObjectWrapper(createInstanceFunc<SceneUtil::MorphGeometry>, "SceneUtil::MorphGeometry", "osg::Object osg::Node osg::Drawable SceneUtil::MorphGeometry")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
@ -95,8 +96,8 @@ void registerSerializers()
|
|||
osgDB::ObjectWrapperManager* mgr = osgDB::Registry::instance()->getObjectWrapperManager();
|
||||
mgr->addWrapper(new PositionAttitudeTransformSerializer);
|
||||
mgr->addWrapper(new SkeletonSerializer);
|
||||
mgr->addWrapper(new FrameSwitchSerializer);
|
||||
mgr->addWrapper(new RigGeometrySerializer);
|
||||
mgr->addWrapper(new MorphGeometrySerializer);
|
||||
mgr->addWrapper(new LightManagerSerializer);
|
||||
mgr->addWrapper(new CameraRelativeTransformSerializer);
|
||||
|
||||
|
|
|
@ -38,8 +38,6 @@ Skeleton::Skeleton()
|
|||
, mNeedToUpdateBoneMatrices(true)
|
||||
, mActive(true)
|
||||
, mLastFrameNumber(0)
|
||||
, mTraversedEvenFrame(false)
|
||||
, mTraversedOddFrame(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -50,8 +48,6 @@ Skeleton::Skeleton(const Skeleton ©, const osg::CopyOp ©op)
|
|||
, mNeedToUpdateBoneMatrices(true)
|
||||
, mActive(copy.mActive)
|
||||
, mLastFrameNumber(0)
|
||||
, mTraversedEvenFrame(false)
|
||||
, mTraversedOddFrame(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -115,11 +111,6 @@ void Skeleton::updateBoneMatrices(unsigned int traversalNumber)
|
|||
|
||||
mLastFrameNumber = traversalNumber;
|
||||
|
||||
if (mLastFrameNumber % 2 == 0)
|
||||
mTraversedEvenFrame = true;
|
||||
else
|
||||
mTraversedOddFrame = true;
|
||||
|
||||
if (mNeedToUpdateBoneMatrices)
|
||||
{
|
||||
if (mRootBone.get())
|
||||
|
@ -144,18 +135,14 @@ bool Skeleton::getActive() const
|
|||
|
||||
void Skeleton::markDirty()
|
||||
{
|
||||
mTraversedEvenFrame = false;
|
||||
mTraversedOddFrame = false;
|
||||
mLastFrameNumber = 0;
|
||||
mBoneCache.clear();
|
||||
mBoneCacheInit = false;
|
||||
}
|
||||
|
||||
void Skeleton::traverse(osg::NodeVisitor& nv)
|
||||
{
|
||||
if (!getActive() && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR
|
||||
// need to process at least 2 frames before shutting off update, since we need to have both frame-alternating RigGeometries initialized
|
||||
// this would be more naturally handled if the double-buffering was implemented in RigGeometry itself rather than in a FrameSwitch decorator node
|
||||
&& mLastFrameNumber != 0 && mTraversedEvenFrame && mTraversedOddFrame)
|
||||
if (!getActive() && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR && mLastFrameNumber != 0)
|
||||
return;
|
||||
osg::Group::traverse(nv);
|
||||
}
|
||||
|
|
|
@ -74,8 +74,6 @@ namespace SceneUtil
|
|||
bool mActive;
|
||||
|
||||
unsigned int mLastFrameNumber;
|
||||
bool mTraversedEvenFrame;
|
||||
bool mTraversedOddFrame;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <components/resource/imagemanager.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/sceneutil/riggeometry.hpp>
|
||||
#include <components/sceneutil/morphgeometry.hpp>
|
||||
|
||||
#include "shadermanager.hpp"
|
||||
|
||||
|
@ -26,6 +27,7 @@ namespace Shader
|
|||
, mMaterialOverridden(false)
|
||||
, mNormalHeight(false)
|
||||
, mTexStageRequiringTangents(-1)
|
||||
, mNode(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -69,7 +71,7 @@ namespace Shader
|
|||
{
|
||||
if (node.getStateSet())
|
||||
{
|
||||
pushRequirements();
|
||||
pushRequirements(node);
|
||||
applyStateSet(node.getStateSet(), node);
|
||||
traverse(node);
|
||||
popRequirements();
|
||||
|
@ -234,9 +236,10 @@ namespace Shader
|
|||
}
|
||||
}
|
||||
|
||||
void ShaderVisitor::pushRequirements()
|
||||
void ShaderVisitor::pushRequirements(osg::Node& node)
|
||||
{
|
||||
mRequirements.push_back(mRequirements.back());
|
||||
mRequirements.back().mNode = &node;
|
||||
}
|
||||
|
||||
void ShaderVisitor::popRequirements()
|
||||
|
@ -244,8 +247,12 @@ namespace Shader
|
|||
mRequirements.pop_back();
|
||||
}
|
||||
|
||||
void ShaderVisitor::createProgram(const ShaderRequirements &reqs, osg::Node& node)
|
||||
void ShaderVisitor::createProgram(const ShaderRequirements &reqs)
|
||||
{
|
||||
if (!reqs.mShaderRequired && !mForceShaders)
|
||||
return;
|
||||
|
||||
osg::Node& node = *reqs.mNode;
|
||||
osg::StateSet* writableStateSet = NULL;
|
||||
if (mAllowedToModifyStateSets)
|
||||
writableStateSet = node.getOrCreateStateSet();
|
||||
|
@ -302,12 +309,42 @@ namespace Shader
|
|||
}
|
||||
}
|
||||
|
||||
bool ShaderVisitor::adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs)
|
||||
{
|
||||
bool useShader = reqs.mShaderRequired || mForceShaders;
|
||||
bool generateTangents = reqs.mTexStageRequiringTangents != -1;
|
||||
bool changed = false;
|
||||
|
||||
if (mAllowedToModifyStateSets && (useShader || generateTangents))
|
||||
{
|
||||
// make sure that all UV sets are there
|
||||
for (std::map<int, std::string>::const_iterator it = reqs.mTextures.begin(); it != reqs.mTextures.end(); ++it)
|
||||
{
|
||||
if (sourceGeometry.getTexCoordArray(it->first) == NULL)
|
||||
{
|
||||
sourceGeometry.setTexCoordArray(it->first, sourceGeometry.getTexCoordArray(0));
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (generateTangents)
|
||||
{
|
||||
osg::ref_ptr<osgUtil::TangentSpaceGenerator> generator (new osgUtil::TangentSpaceGenerator);
|
||||
generator->generate(&sourceGeometry, reqs.mTexStageRequiringTangents);
|
||||
|
||||
sourceGeometry.setTexCoordArray(7, generator->getTangentArray(), osg::Array::BIND_PER_VERTEX);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
void ShaderVisitor::apply(osg::Geometry& geometry)
|
||||
{
|
||||
bool needPop = (geometry.getStateSet() != NULL);
|
||||
if (geometry.getStateSet())
|
||||
if (geometry.getStateSet()) // TODO: check if stateset affects shader permutation before pushing it
|
||||
{
|
||||
pushRequirements();
|
||||
pushRequirements(geometry);
|
||||
applyStateSet(geometry.getStateSet(), geometry);
|
||||
}
|
||||
|
||||
|
@ -315,44 +352,9 @@ namespace Shader
|
|||
{
|
||||
const ShaderRequirements& reqs = mRequirements.back();
|
||||
|
||||
bool useShader = reqs.mShaderRequired || mForceShaders;
|
||||
bool generateTangents = reqs.mTexStageRequiringTangents != -1;
|
||||
adjustGeometry(geometry, reqs);
|
||||
|
||||
if (mAllowedToModifyStateSets && (useShader || generateTangents))
|
||||
{
|
||||
osg::ref_ptr<osg::Geometry> sourceGeometry = &geometry;
|
||||
SceneUtil::RigGeometry* rig = dynamic_cast<SceneUtil::RigGeometry*>(&geometry);
|
||||
if (rig)
|
||||
sourceGeometry = rig->getSourceGeometry();
|
||||
|
||||
bool requiresSetGeometry = false;
|
||||
|
||||
// make sure that all UV sets are there
|
||||
for (std::map<int, std::string>::const_iterator it = reqs.mTextures.begin(); it != reqs.mTextures.end(); ++it)
|
||||
{
|
||||
if (sourceGeometry->getTexCoordArray(it->first) == NULL)
|
||||
{
|
||||
sourceGeometry->setTexCoordArray(it->first, sourceGeometry->getTexCoordArray(0));
|
||||
requiresSetGeometry = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (generateTangents)
|
||||
{
|
||||
osg::ref_ptr<osgUtil::TangentSpaceGenerator> generator (new osgUtil::TangentSpaceGenerator);
|
||||
generator->generate(sourceGeometry, reqs.mTexStageRequiringTangents);
|
||||
|
||||
sourceGeometry->setTexCoordArray(7, generator->getTangentArray(), osg::Array::BIND_PER_VERTEX);
|
||||
requiresSetGeometry = true;
|
||||
}
|
||||
|
||||
if (rig && requiresSetGeometry)
|
||||
rig->setSourceGeometry(sourceGeometry);
|
||||
}
|
||||
|
||||
// TODO: find a better place for the stateset
|
||||
if (useShader)
|
||||
createProgram(reqs, geometry);
|
||||
createProgram(reqs);
|
||||
}
|
||||
|
||||
if (needPop)
|
||||
|
@ -366,16 +368,27 @@ namespace Shader
|
|||
|
||||
if (drawable.getStateSet())
|
||||
{
|
||||
pushRequirements();
|
||||
pushRequirements(drawable);
|
||||
applyStateSet(drawable.getStateSet(), drawable);
|
||||
}
|
||||
|
||||
if (!mRequirements.empty())
|
||||
{
|
||||
const ShaderRequirements& reqs = mRequirements.back();
|
||||
// TODO: find a better place for the stateset
|
||||
if (reqs.mShaderRequired || mForceShaders)
|
||||
createProgram(reqs, drawable);
|
||||
createProgram(reqs);
|
||||
|
||||
if (auto rig = dynamic_cast<SceneUtil::RigGeometry*>(&drawable))
|
||||
{
|
||||
osg::ref_ptr<osg::Geometry> sourceGeometry = rig->getSourceGeometry();
|
||||
if (sourceGeometry && adjustGeometry(*sourceGeometry, reqs))
|
||||
rig->setSourceGeometry(sourceGeometry);
|
||||
}
|
||||
else if (auto morph = dynamic_cast<SceneUtil::MorphGeometry*>(&drawable))
|
||||
{
|
||||
osg::ref_ptr<osg::Geometry> sourceGeometry = morph->getSourceGeometry();
|
||||
if (sourceGeometry && adjustGeometry(*sourceGeometry, reqs))
|
||||
morph->setSourceGeometry(sourceGeometry);
|
||||
}
|
||||
}
|
||||
|
||||
if (needPop)
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace Shader
|
|||
|
||||
void applyStateSet(osg::ref_ptr<osg::StateSet> stateset, osg::Node& node);
|
||||
|
||||
void pushRequirements();
|
||||
void pushRequirements(osg::Node& node);
|
||||
void popRequirements();
|
||||
|
||||
private:
|
||||
|
@ -89,13 +89,17 @@ namespace Shader
|
|||
|
||||
// -1 == no tangents required
|
||||
int mTexStageRequiringTangents;
|
||||
|
||||
// the Node that requested these requirements
|
||||
osg::Node* mNode;
|
||||
};
|
||||
std::vector<ShaderRequirements> mRequirements;
|
||||
|
||||
std::string mDefaultVsTemplate;
|
||||
std::string mDefaultFsTemplate;
|
||||
|
||||
void createProgram(const ShaderRequirements& reqs, osg::Node& node);
|
||||
void createProgram(const ShaderRequirements& reqs);
|
||||
bool adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace Gui
|
|||
private:
|
||||
MyGUI::Widget* mLeft;
|
||||
MyGUI::Widget* mRight;
|
||||
MyGUI::Widget* mClient;
|
||||
|
||||
void align();
|
||||
};
|
||||
|
|
|
@ -56,7 +56,7 @@ Enabling this feature results in better visuals, and a marginally lower frame ra
|
|||
|
||||
This setting has no effect if the shader setting is false.
|
||||
|
||||
This setting can be toggled with the Refraction button in the Water tab of the Video panel of the Options menu.
|
||||
This setting can be toggled with the 'Refraction' button in the Water tab of the Video panel of the Options menu.
|
||||
|
||||
reflect actors
|
||||
--------------
|
||||
|
@ -68,6 +68,8 @@ reflect actors
|
|||
This setting controls whether or not NPCs and creatures are drawn in water reflections.
|
||||
Setting this to true will enable actors in reflections and increase realism with a likely decrease in performance.
|
||||
|
||||
This setting can be toggled with the 'Reflect actors' button in the Water tab of the Video panel of the Options menu.
|
||||
|
||||
small feature culling pixel size
|
||||
--------------------------------
|
||||
|
||||
|
|
10
files/tes3mp-browser.desktop
Normal file
10
files/tes3mp-browser.desktop
Normal file
|
@ -0,0 +1,10 @@
|
|||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=TES3MP Server Browser
|
||||
GenericName=Server Browser
|
||||
Comment=Multiplayer extension for TES3.
|
||||
Keywords=Morrowind;Multiplayer;Server Browser;TES;openmw;
|
||||
TryExec=tes3mp-browser
|
||||
Exec=tes3mp-browser
|
||||
Icon=openmw
|
||||
Categories=Game;RolePlaying;
|
Loading…
Reference in a new issue