mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-09-07 19:30:04 -04:00
Merge branch 'landscape-data-bindings' into 'master'
Landscape height and texture bindings for Lua (Revised) See merge request OpenMW/openmw!4500
This commit is contained in:
commit
0589b57a47
@ -1,3 +1,8 @@
|
|||||||
|
0.50.0
|
||||||
|
------
|
||||||
|
|
||||||
|
Feature #8112: Expose landscape record data to Lua
|
||||||
|
|
||||||
0.49.0
|
0.49.0
|
||||||
------
|
------
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ add_openmw_dir (mwlua
|
|||||||
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
|
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
|
||||||
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings
|
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings
|
||||||
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings dialoguebindings
|
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
|
classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings markupbindings
|
||||||
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc
|
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
|
types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "dialoguebindings.hpp"
|
#include "dialoguebindings.hpp"
|
||||||
#include "factionbindings.hpp"
|
#include "factionbindings.hpp"
|
||||||
|
#include "landbindings.hpp"
|
||||||
#include "luaevents.hpp"
|
#include "luaevents.hpp"
|
||||||
#include "magicbindings.hpp"
|
#include "magicbindings.hpp"
|
||||||
#include "soundbindings.hpp"
|
#include "soundbindings.hpp"
|
||||||
@ -97,6 +98,8 @@ namespace MWLua
|
|||||||
api["stats"]
|
api["stats"]
|
||||||
= context.cachePackage("openmw_core_stats", [context]() { return initCoreStatsBindings(context); });
|
= context.cachePackage("openmw_core_stats", [context]() { return initCoreStatsBindings(context); });
|
||||||
|
|
||||||
|
api["land"] = context.cachePackage("openmw_core_land", [context]() { return initCoreLandBindings(context); });
|
||||||
|
|
||||||
api["factions"]
|
api["factions"]
|
||||||
= context.cachePackage("openmw_core_factions", [context]() { return initCoreFactionBindings(context); });
|
= context.cachePackage("openmw_core_factions", [context]() { return initCoreFactionBindings(context); });
|
||||||
api["dialogue"]
|
api["dialogue"]
|
||||||
|
121
apps/openmw/mwlua/landbindings.cpp
Normal file
121
apps/openmw/mwlua/landbindings.cpp
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#include "landbindings.hpp"
|
||||||
|
|
||||||
|
#include <apps/openmw/mwlua/object.hpp>
|
||||||
|
#include <apps/openmw/mwworld/cellstore.hpp>
|
||||||
|
#include <apps/openmw/mwworld/worldmodel.hpp>
|
||||||
|
|
||||||
|
#include <components/esm/util.hpp>
|
||||||
|
#include <components/esmterrain/storage.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Takes in a corrected world pos to match the visuals.
|
||||||
|
ESMTerrain::UniqueTextureId getTextureAt(const std::span<const std::uint16_t> landData, const int plugin,
|
||||||
|
const osg::Vec3f& correctedWorldPos, const float cellSize)
|
||||||
|
{
|
||||||
|
int cellX = static_cast<int>(std::floor(correctedWorldPos.x() / cellSize));
|
||||||
|
int cellY = static_cast<int>(std::floor(correctedWorldPos.y() / cellSize));
|
||||||
|
|
||||||
|
// Normalized position in the cell
|
||||||
|
float nX = (correctedWorldPos.x() - (cellX * cellSize)) / cellSize;
|
||||||
|
float nY = (correctedWorldPos.y() - (cellY * cellSize)) / cellSize;
|
||||||
|
|
||||||
|
int startX = static_cast<int>(nX * ESM::Land::LAND_TEXTURE_SIZE);
|
||||||
|
int startY = static_cast<int>(nY * ESM::Land::LAND_TEXTURE_SIZE);
|
||||||
|
|
||||||
|
assert(startX < ESM::Land::LAND_TEXTURE_SIZE);
|
||||||
|
assert(startY < ESM::Land::LAND_TEXTURE_SIZE);
|
||||||
|
|
||||||
|
const std::uint16_t tex = landData[startY * ESM::Land::LAND_TEXTURE_SIZE + startX];
|
||||||
|
if (tex == 0)
|
||||||
|
return { 0, 0 }; // vtex 0 is always the base texture, regardless of plugin
|
||||||
|
|
||||||
|
return { tex, plugin };
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESM::RefId worldspaceAt(sol::object cellOrName)
|
||||||
|
{
|
||||||
|
const MWWorld::Cell* cell = nullptr;
|
||||||
|
if (cellOrName.is<MWLua::GCell>())
|
||||||
|
cell = cellOrName.as<MWLua::GCell>().mStore->getCell();
|
||||||
|
else if (cellOrName.is<MWLua::LCell>())
|
||||||
|
cell = cellOrName.as<MWLua::LCell>().mStore->getCell();
|
||||||
|
else if (cellOrName.is<std::string_view>() && !cellOrName.as<std::string_view>().empty())
|
||||||
|
cell = MWBase::Environment::get().getWorldModel()->getCell(cellOrName.as<std::string_view>()).getCell();
|
||||||
|
if (cell == nullptr)
|
||||||
|
throw std::runtime_error("Invalid cell");
|
||||||
|
else if (!cell->isExterior())
|
||||||
|
throw std::runtime_error("Cell cannot be interior");
|
||||||
|
|
||||||
|
return cell->getWorldSpace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
sol::table initCoreLandBindings(const Context& context)
|
||||||
|
{
|
||||||
|
sol::state_view lua = context.sol();
|
||||||
|
sol::table landApi(lua, sol::create);
|
||||||
|
|
||||||
|
landApi["getHeightAt"] = [](const osg::Vec3f& pos, sol::object cellOrName) {
|
||||||
|
ESM::RefId worldspace = worldspaceAt(cellOrName);
|
||||||
|
return MWBase::Environment::get().getWorld()->getTerrainHeightAt(pos, worldspace);
|
||||||
|
};
|
||||||
|
|
||||||
|
landApi["getTextureAt"] = [lua = lua](const osg::Vec3f& pos, sol::object cellOrName) {
|
||||||
|
sol::variadic_results values;
|
||||||
|
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
||||||
|
const MWWorld::Store<ESM::Land>& landStore = store.get<ESM::Land>();
|
||||||
|
ESM::RefId worldspace = worldspaceAt(cellOrName);
|
||||||
|
|
||||||
|
if (worldspace != ESM::Cell::sDefaultWorldspaceId)
|
||||||
|
return values;
|
||||||
|
|
||||||
|
const float cellSize = ESM::getCellSize(worldspace);
|
||||||
|
const float offset = (cellSize / ESM::LandRecordData::sLandTextureSize) * 0.25;
|
||||||
|
const osg::Vec3f correctedPos = pos + osg::Vec3f{ -offset, +offset, 0.0f };
|
||||||
|
|
||||||
|
const ESM::Land* land = nullptr;
|
||||||
|
const ESM::Land::LandData* landData = nullptr;
|
||||||
|
|
||||||
|
int cellX = static_cast<int>(std::floor(correctedPos.x() / cellSize));
|
||||||
|
int cellY = static_cast<int>(std::floor(correctedPos.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 values;
|
||||||
|
|
||||||
|
const ESMTerrain::UniqueTextureId textureId
|
||||||
|
= getTextureAt(landData->mTextures, land->getPlugin(), 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)
|
||||||
|
{
|
||||||
|
const MWWorld::Store<ESM::LandTexture>& textureStore = store.get<ESM::LandTexture>();
|
||||||
|
const std::string* textureString = textureStore.search(textureId.first - 1, textureId.second);
|
||||||
|
if (!textureString)
|
||||||
|
return values;
|
||||||
|
|
||||||
|
values.push_back(sol::make_object<std::string>(lua, *textureString));
|
||||||
|
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
|
||||||
|
if (textureId.second >= 0 && static_cast<size_t>(textureId.second) < contentList.size())
|
||||||
|
values.push_back(sol::make_object<std::string>(lua, contentList[textureId.second]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
|
||||||
|
return LuaUtil::makeReadOnly(landApi);
|
||||||
|
}
|
||||||
|
}
|
11
apps/openmw/mwlua/landbindings.hpp
Normal file
11
apps/openmw/mwlua/landbindings.hpp
Normal file
@ -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
|
@ -450,6 +450,28 @@
|
|||||||
-- @usage for _, item in ipairs(inventory:findAll('common_shirt_01')) do ... end
|
-- @usage for _, item in ipairs(inventory:findAll('common_shirt_01')) do ... end
|
||||||
|
|
||||||
|
|
||||||
|
--- @{#Land}: Functions for interacting with land data
|
||||||
|
-- @field [parent=#core] #Land land
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Get the terrain height at a given location.
|
||||||
|
-- @function [parent=#Land] getHeightAt
|
||||||
|
-- @param openmw.util#Vector3 position
|
||||||
|
-- @param #any cellOrName (optional) cell or cell name in their exterior world space to query
|
||||||
|
-- @return #number
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Get the terrain texture at a given location. As textures are blended and
|
||||||
|
-- multiple textures can be at one specific position the texture whose center is
|
||||||
|
-- closest to the position will be returned.
|
||||||
|
--
|
||||||
|
-- @function [parent=#Land] getTextureAt
|
||||||
|
-- @param openmw.util#Vector3 position
|
||||||
|
-- @param #any cellOrName cell or cell name in their exterior world space to query
|
||||||
|
-- @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
|
||||||
|
|
||||||
|
|
||||||
--- @{#Magic}: spells and spell effects
|
--- @{#Magic}: spells and spell effects
|
||||||
-- @field [parent=#core] #Magic magic
|
-- @field [parent=#core] #Magic magic
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user