diff --git a/apps/openmw/mwlua/landbindings.cpp b/apps/openmw/mwlua/landbindings.cpp index 3b07e119be..cfa8fd58d3 100644 --- a/apps/openmw/mwlua/landbindings.cpp +++ b/apps/openmw/mwlua/landbindings.cpp @@ -4,72 +4,67 @@ #include #include +#include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" +static const ESM::RefId worldspaceAt(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; + + return worldspace; +} + +static bool fillLandData(const MWWorld::Store* landStore, const osg::Vec3f& pos, const float cellSize, + const ESM::Land** land, const ESM::Land::LandData** landData) +{ + int cellX = static_cast(std::floor(pos.x() / cellSize)); + int cellY = static_cast(std::floor(pos.y() / cellSize)); + + *land = landStore->search(cellX, cellY); + + if (*land != nullptr) + *landData = (*land)->getLandData(ESM::Land::DATA_VTEX); + + // 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 + if (*landData == nullptr) + return false; + + return true; +} + namespace MWLua { sol::table initCoreLandBindings(const Context& context) { - sol::state_view& lua = context.mLua->sol(); + auto lua = context.sol(); sol::table landApi(lua, sol::create); 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) - { - // 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); + auto worldspace = worldspaceAt(pos, cellOrName); + return MWBase::Environment::get().getWorld()->getTerrainHeightAt(pos, worldspace); }; - landApi["getTextureAt"] = [lua = context.mLua](const osg::Vec3f& pos, sol::object cellOrName) { + landApi["getTextureAt"] = [lua = lua](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(); + auto landStore = store->get(); + + const float cellSize = ESM::getCellSize(worldspaceAt(pos, cellOrName)); // 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 // This is because the visual land textures are offset with regards to quads that are rendered for terrain. @@ -77,36 +72,22 @@ namespace MWLua // as it differs between tes3 and tes4. It's equal - // Once we know the value, we will calculate the offset and retrieve a sample again, this time // with the offset taken into account. - auto landStore = store->get(); - auto land = landStore.search(cellX, cellY); + const ESM::Land* land = nullptr; const ESM::Land::LandData* landData = nullptr; - if (land != nullptr) - { - 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 + + if (!fillLandData(&landStore, pos, cellSize, &land, &landData)) return values; - } // Use landData to get amount of sampler per cell edge (sLandTextureSize) // and then get the corrected position that will map to the rendered texture 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* correctedLand = nullptr; const ESM::Land::LandData* correctedLandData = nullptr; - if (correctedLand != nullptr) - { - correctedLandData = correctedLand->getLandData(ESM::Land::DATA_VTEX); - } - if (correctedLandData == nullptr) - { + + if (!fillLandData(&landStore, correctedPos, cellSize, &correctedLand, &correctedLandData)) return values; - } // We're passing in sLandTextureSize, NOT sLandSize like with getHeightAt const ESMTerrain::UniqueTextureId textureId @@ -116,15 +97,17 @@ namespace MWLua // 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 store = MWBase::Environment::get().getESMStore(); 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)); - } + values.push_back(sol::make_object(lua, *textureString)); + else + return values; + + const std::vector& contentList = MWBase::Environment::get().getWorld()->getContentFiles(); + if (textureId.second > 0 && textureId.second < contentList.size()) + values.push_back(sol::make_object(lua, contentList[textureId.second])); } return values; diff --git a/files/lua_api/openmw/core.lua b/files/lua_api/openmw/core.lua index 0636682669..8ad7a973da 100644 --- a/files/lua_api/openmw/core.lua +++ b/files/lua_api/openmw/core.lua @@ -470,9 +470,8 @@ -- @function [parent=#Land] getTextureAt -- @param openmw.util#Vector3 position -- @param #any cellOrName (optional) cell or cell name in their exterior world space to query --- @return #nil, #number Land texture index or nil if failed to retrieve the texture. Landscape textures created through editors such as openmw-cs can be assigned an id to differentiate them, that is also used for terrain rendering. The value returned here corresponds to that value. See also LTEX records (https://en.uesp.net/wiki/Morrowind_Mod:Mod_File_Format/LTEX) --- @return #nil, #number Plugin id or nil if failed to retrieve the texture -- @return #nil, #string Texture path or nil if one isn't defined +-- @return #nil, #string Plugin name or nil if failed to retrieve the texture --- Possible @{#SpellRange} values -- @field [parent=#Magic] #SpellRange RANGE