From 8917103bf36172f2f7e4583cfed840702d4ec80b Mon Sep 17 00:00:00 2001 From: Sebastian Fieber Date: Tue, 29 Apr 2025 01:20:13 +0200 Subject: [PATCH] put land bindings in a table in openmw.core --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwlua/corebindings.cpp | 13 +-- apps/openmw/mwlua/landbindings.cpp | 153 +++++++++++++++++++++++++++++ apps/openmw/mwlua/landbindings.hpp | 11 +++ 4 files changed, 168 insertions(+), 11 deletions(-) create mode 100644 apps/openmw/mwlua/landbindings.cpp create mode 100644 apps/openmw/mwlua/landbindings.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 37de0abeab..48dcf41aae 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -62,7 +62,7 @@ add_openmw_dir (mwlua luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings dialoguebindings - postprocessingbindings stats recordstore debugbindings corebindings worldbindings worker magicbindings factionbindings + postprocessingbindings stats recordstore debugbindings corebindings worldbindings worker landbindings magicbindings factionbindings classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings markupbindings types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus diff --git a/apps/openmw/mwlua/corebindings.cpp b/apps/openmw/mwlua/corebindings.cpp index aa0b321f6c..8bef77f4ae 100644 --- a/apps/openmw/mwlua/corebindings.cpp +++ b/apps/openmw/mwlua/corebindings.cpp @@ -2,20 +2,10 @@ #include #include -#include - -#include - -#include -#include -#include #include -#include #include -#include #include -#include #include #include #include @@ -32,6 +22,7 @@ #include "dialoguebindings.hpp" #include "factionbindings.hpp" +#include "landbindings.hpp" #include "luaevents.hpp" #include "magicbindings.hpp" #include "soundbindings.hpp" @@ -108,6 +99,8 @@ namespace MWLua api["stats"] = context.cachePackage("openmw_core_stats", [context]() { return initCoreStatsBindings(context); }); + api["land"] = context.cachePackage("openmw_core_land", [context]() { return initCoreLandBindings(context); }); + api["factions"] = context.cachePackage("openmw_core_factions", [context]() { return initCoreFactionBindings(context); }); api["dialogue"] diff --git a/apps/openmw/mwlua/landbindings.cpp b/apps/openmw/mwlua/landbindings.cpp new file mode 100644 index 0000000000..37e95183ca --- /dev/null +++ b/apps/openmw/mwlua/landbindings.cpp @@ -0,0 +1,153 @@ +#include "landbindings.hpp" + +#include +#include +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/esmstore.hpp" + +namespace MWLua +{ + sol::table initCoreLandBindings(const Context& context) + { + sol::state_view& lua = context.mLua->sol(); + sol::table landApi(lua, sol::create); + + // Constants + landApi["RANGE"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs({ + { "Self", ESM::RT_Self }, + { "Touch", ESM::RT_Touch }, + { "Target", ESM::RT_Target }, + })); + + landApi["getHeightAt"] = [](const osg::Vec3f& pos, sol::object cellOrName) { + ESM::RefId worldspace; + if (cellOrName.is()) + worldspace = cellOrName.as().mStore->getCell()->getWorldSpace(); + else if (cellOrName.is() && !cellOrName.as().empty()) + worldspace = MWBase::Environment::get() + .getWorldModel() + ->getCell(cellOrName.as()) + .getCell() + ->getWorldSpace(); + else + worldspace = ESM::Cell::sDefaultWorldspaceId; + + const float cellSize = ESM::getCellSize(worldspace); + int cellX = static_cast(std::floor(pos.x() / cellSize)); + int cellY = static_cast(std::floor(pos.y() / cellSize)); + + auto store = MWBase::Environment::get().getESMStore(); + auto landStore = store->get(); + auto land = landStore.search(cellX, cellY); + const ESM::Land::LandData* landData = nullptr; + if (land != nullptr) + { + landData = land->getLandData(ESM::Land::DATA_VHGT); + if (landData != nullptr) + { + // Ensure data is loaded if necessary + land->loadData(ESM::Land::DATA_VHGT); + landData = land->getLandData(ESM::Land::DATA_VHGT); + } + } + if (landData == nullptr) + { + // If we failed to load data, return the default height + return static_cast(ESM::Land::DEFAULT_HEIGHT); + } + return ESMTerrain::Storage::getHeightAt(landData->mHeights, landData->sLandSize, pos, cellSize); + }; + + landApi["getLandTextureAt"] = [lua = context.mLua](const osg::Vec3f& pos, sol::object cellOrName) { + sol::variadic_results values; + ESM::RefId worldspace; + if (cellOrName.is()) + worldspace = cellOrName.as().mStore->getCell()->getWorldSpace(); + else if (cellOrName.is() && !cellOrName.as().empty()) + worldspace = MWBase::Environment::get() + .getWorldModel() + ->getCell(cellOrName.as()) + .getCell() + ->getWorldSpace(); + else + worldspace = ESM::Cell::sDefaultWorldspaceId; + + const float cellSize = ESM::getCellSize(worldspace); + + int cellX = static_cast(std::floor(pos.x() / cellSize)); + int cellY = static_cast(std::floor(pos.y() / cellSize)); + + auto store = MWBase::Environment::get().getESMStore(); + // We need to read land twice. Once to get the amount of texture samples per cell edge, and the second time + // to get the actual data + auto landStore = store->get(); + auto land = landStore.search(cellX, cellY); + const ESM::Land::LandData* landData = nullptr; + if (land != nullptr) + { + landData = land->getLandData(ESM::Land::DATA_VTEX); + if (landData != nullptr) + { + // Ensure data is loaded if necessary + land->loadData(ESM::Land::DATA_VTEX); + landData = land->getLandData(ESM::Land::DATA_VTEX); + } + } + if (landData == nullptr) + { + // If we fail to preload land data, return, we need to be able to get *any* land to know how to correct + // the position used to sample terrain + return values; + } + + const osg::Vec3f correctedPos + = ESMTerrain::Storage::getTextureCorrectedWorldPos(pos, landData->sLandTextureSize, cellSize); + int correctedCellX = static_cast(std::floor(correctedPos.x() / cellSize)); + int correctedCellY = static_cast(std::floor(correctedPos.y() / cellSize)); + auto correctedLand = landStore.search(correctedCellX, correctedCellY); + const ESM::Land::LandData* correctedLandData = nullptr; + if (correctedLand != nullptr) + { + correctedLandData = correctedLand->getLandData(ESM::Land::DATA_VTEX); + if (correctedLandData != nullptr) + { + // Ensure data is loaded if necessary + land->loadData(ESM::Land::DATA_VTEX); + correctedLandData = correctedLand->getLandData(ESM::Land::DATA_VTEX); + } + } + if (correctedLandData == nullptr) + { + return values; + } + + // We're passing in sLandTextureSize, NOT sLandSize like with getHeightAt + const ESMTerrain::UniqueTextureId textureId + = ESMTerrain::Storage::getLandTextureAt(correctedLandData->mTextures, correctedLand->getPlugin(), + correctedLandData->sLandTextureSize, correctedPos, cellSize); + + // Need to check for 0, 0 so that we can safely subtract 1 later, as per documentation on UniqueTextureId + if (textureId.first != 0) + { + values.push_back(sol::make_object(lua->sol(), textureId.first - 1)); + values.push_back(sol::make_object(lua->sol(), textureId.second)); + + auto textureStore = store->get(); + const std::string* textureString = textureStore.search(textureId.first - 1, textureId.second); + if (textureString) + { + values.push_back(sol::make_object(lua->sol(), *textureString)); + } + } + + return values; + }; + + return LuaUtil::makeReadOnly(landApi); + } +} diff --git a/apps/openmw/mwlua/landbindings.hpp b/apps/openmw/mwlua/landbindings.hpp new file mode 100644 index 0000000000..8cdf30046b --- /dev/null +++ b/apps/openmw/mwlua/landbindings.hpp @@ -0,0 +1,11 @@ +#ifndef MWLUA_LANDBINDINGS_H +#define MWLUA_LANDBINDINGS_H + +#include "context.hpp" + +namespace MWLua +{ + sol::table initCoreLandBindings(const Context& context); +} + +#endif // MWLUA_LANDBINDINGS_H