diff --git a/doc/en/scripting.md b/doc/en/scripting.md index b9661cd3..05a89a26 100644 --- a/doc/en/scripting.md +++ b/doc/en/scripting.md @@ -20,6 +20,7 @@ Subsections: - [gfx.blockwraps](scripting/builtins/libgfx-blockwraps.md) - [gfx.particles](particles.md#gfxparticles-library) - [gfx.posteffects](scripting/builtins/libgfx-posteffects.md) + - [gfx.skeletons](scripting/builtins/libgfx-skeletons.md) - [gfx.text3d](3d-text.md#gfxtext3d-library) - [gfx.weather](scripting/builtins/libgfx-weather.md) - [gui](scripting/builtins/libgui.md) diff --git a/doc/en/scripting/builtins/libgfx-skeletons.md b/doc/en/scripting/builtins/libgfx-skeletons.md new file mode 100644 index 00000000..4146b4be --- /dev/null +++ b/doc/en/scripting/builtins/libgfx-skeletons.md @@ -0,0 +1,48 @@ +# gfx.skeletons library + +A library for working with named skeletons, such as 'hand', +used to control the hand and the carried item displayed in first-person view. +The set of functions is similar to the skeleton component of entities. + +The first argument to the function is the name of the skeleton. + +```lua +-- Returns an object wrapper over the skeleton +local skeleton = gfx.skeletons.get(name: str) + +-- Returns the index of the bone by name or nil +skeleton:index(name: str) -> int + +-- Returns the name of the model assigned to the bone with the specified index +skeleton:get_model(index: int) -> str + +-- Reassigns the model of the bone with the specified index +-- Resets to the original if you do not specify a name +skeleton:set_model(index: int, name: str) + +-- Returns the transformation matrix of the bone with the specified index +skeleton:get_matrix(index: int) -> mat4 + +-- Sets the transformation matrix of the bone with the specified index +skeleton:set_matrix(index: int, matrix: mat4) + +-- Returns the texture by key (dynamically assigned textures - '$name') +skeleton:get_texture(key: str) -> str + +-- Assigns a texture by key +skeleton:set_texture(key: str, value: str) + +-- Checks the visibility status of a bone by index +-- or the entire skeleton if index is not specified +skeleton:is_visible([optional] index: int) -> bool + +-- Sets the visibility status of a bone by index +-- or the entire skeleton if index is not specified +skeleton:set_visible([optional] index: int, status: bool) + +-- Returns the color of the entity +skeleton:get_color() -> vec3 + +-- Sets the color of the entity +skeleton:set_color(color: vec3) +``` diff --git a/doc/en/scripting/builtins/libhud.md b/doc/en/scripting/builtins/libhud.md index 06766422..58d78302 100644 --- a/doc/en/scripting/builtins/libhud.md +++ b/doc/en/scripting/builtins/libhud.md @@ -65,4 +65,7 @@ hud.is_inventory_open() -> bool -- Sets whether to allow pausing. If false, the pause menu will not pause the game. hud.set_allow_pause(flag: bool) + +-- Function that controls the named skeleton 'hand' (see gfx.skeletons) +hud.hand_controller: function() ``` diff --git a/doc/ru/scripting.md b/doc/ru/scripting.md index 6fb86dce..eaae3302 100644 --- a/doc/ru/scripting.md +++ b/doc/ru/scripting.md @@ -20,6 +20,7 @@ - [gfx.blockwraps](scripting/builtins/libgfx-blockwraps.md) - [gfx.particles](particles.md#библиотека-gfxparticles) - [gfx.posteffects](scripting/builtins/libgfx-posteffects.md) + - [gfx.skeletons](scripting/builtins/libgfx-skeletons.md) - [gfx.text3d](3d-text.md#библиотека-gfxtext3d) - [gfx.weather](scripting/builtins/libgfx-weather.md) - [gui](scripting/builtins/libgui.md) diff --git a/doc/ru/scripting/builtins/libgfx-skeletons.md b/doc/ru/scripting/builtins/libgfx-skeletons.md new file mode 100644 index 00000000..0e80b8f9 --- /dev/null +++ b/doc/ru/scripting/builtins/libgfx-skeletons.md @@ -0,0 +1,49 @@ +# Библиотека gfx.skeletons + +Библиотека для работы с именованными скелетами, такими как 'hand', +использующийся для управления, отображаемыми при виде от первого лица, +рукой и переносимым предметом. Набор функций аналогичен компоненту skeleton +у сущностей. + +Первым аргументом в функции передаётся имя скелета. + +```lua +-- Возвращает объектную обёртку над скелетом +local skeleton = gfx.skeletons.get(name: str) + +-- Возвращает индекс кости по имени или nil +skeleton:index(name: str) -> int + +-- Возвращает имя модели, назначенной на кость с указанным индексом +skeleton:get_model(index: int) -> str + +-- Переназначает модель кости с указанным индексом +-- Сбрасывает до изначальной, если не указывать имя +skeleton:set_model(index: int, name: str) + +-- Возвращает матрицу трансформации кости с указанным индексом +skeleton:get_matrix(index: int) -> mat4 + +-- Устанавливает матрицу трансформации кости с указанным индексом +skeleton:set_matrix(index: int, matrix: mat4) + +-- Возвращает текстуру по ключу (динамически назначаемые текстуры - '$имя') +skeleton:get_texture(key: str) -> str + +-- Назначает текстуру по ключу +skeleton:set_texture(key: str, value: str) + +-- Проверяет статус видимости кости по индесу +-- или всего скелета, если индекс не указан +skeleton:is_visible([опционально] index: int) -> bool + +-- Устанавливает статус видимости кости по индексу +-- или всего скелета, если индекс не указан +skeleton:set_visible([опционально] index: int, status: bool) + +-- Возвращает цвет сущности +skeleton:get_color() -> vec3 + +-- Устанавливает цвет сущности +skeleton:set_color(color: vec3) +``` diff --git a/doc/ru/scripting/builtins/libhud.md b/doc/ru/scripting/builtins/libhud.md index 311542cb..5f0856b3 100644 --- a/doc/ru/scripting/builtins/libhud.md +++ b/doc/ru/scripting/builtins/libhud.md @@ -68,4 +68,7 @@ hud.is_inventory_open() -> bool -- Устанавливает разрешение на паузу. При значении false меню паузы не приостанавливает игру. hud.set_allow_pause(flag: bool) + +-- Функция, управляющая именованным скелетом 'hand' (см. gfx.skeletons) +hud.hand_controller: function() ``` diff --git a/res/content/base/config/defaults.toml b/res/content/base/config/defaults.toml index a099c198..650c24d7 100644 --- a/res/content/base/config/defaults.toml +++ b/res/content/base/config/defaults.toml @@ -1,2 +1,3 @@ generator = "base:demo" player-entity = "base:player" +hand-skeleton = "base:hand" diff --git a/res/content/base/skeletons/hand.json b/res/content/base/skeletons/hand.json new file mode 100644 index 00000000..8ff9d841 --- /dev/null +++ b/res/content/base/skeletons/hand.json @@ -0,0 +1,9 @@ +{ + "root": { + "nodes": [ + { + "name": "item" + } + ] + } +} diff --git a/res/scripts/hud.lua b/res/scripts/hud.lua index 3fd1375c..abd1eaf4 100644 --- a/res/scripts/hud.lua +++ b/res/scripts/hud.lua @@ -82,3 +82,44 @@ function on_hud_open() configure_SSAO() end + +local function update_hand() + local skeleton = gfx.skeletons + local pid = hud.get_player() + local invid, slot = player.get_inventory(pid) + local itemid = inventory.get(invid, slot) + + local cam = cameras.get("core:first-person") + local bone = skeleton.index("hand", "item") + + local offset = vec3.mul(vec3.sub(cam:get_pos(), {player.get_pos(pid)}), -1) + + local rotation = cam:get_rot() + + local angle = player.get_rot() - 90 + local cos = math.cos(angle / (180 / math.pi)) + local sin = math.sin(angle / (180 / math.pi)) + + local newX = offset[1] * cos - offset[3] * sin + local newZ = offset[1] * sin + offset[3] * cos + + offset[1] = newX + offset[3] = newZ + + local mat = mat4.translate(mat4.idt(), {0.06, 0.035, -0.1}) + mat4.scale(mat, {0.1, 0.1, 0.1}, mat) + mat4.mul(rotation, mat, mat) + mat4.rotate(mat, {0, 1, 0}, -90, mat) + mat4.translate(mat, offset, mat) + + skeleton.set_matrix("hand", bone, mat) + skeleton.set_model("hand", bone, item.model_name(itemid)) +end + +function on_hud_render() + if hud.hand_controller then + hud.hand_controller() + else + update_hand() + end +end diff --git a/res/scripts/hud_classes.lua b/res/scripts/hud_classes.lua index dc373c6e..f8a28944 100644 --- a/res/scripts/hud_classes.lua +++ b/res/scripts/hud_classes.lua @@ -12,7 +12,27 @@ local Text3D = {__index={ update_settings=function(self, t) return gfx.text3d.update_settings(self.id, t) end, }} +local Skeleton = {__index={ + index=function(self, s) return gfx.skeletons.index(self.name, s) end, + get_model=function(self, i) return gfx.skeletons.get_model(self.name, i) end, + set_model=function(self, i, s) return gfx.skeletons.set_model(self.name, i, s) end, + get_matrix=function(self, i) return gfx.skeletons.get_matrix(self.name, i) end, + set_matrix=function(self, i, m) return gfx.skeletons.set_matrix(self.name, i, m) end, + get_texture=function(self, i) return gfx.skeletons.get_texture(self.name, i) end, + set_texture=function(self, i, s) return gfx.skeletons.set_texture(self.name, i, s) end, + is_visible=function(self, i) return gfx.skeletons.is_visible(self.name, i) end, + set_visible=function(self, i, b) return gfx.skeletons.set_visible(self.name, i, b) end, + get_color=function(self, i) return gfx.skeletons.get_color(self.name, i) end, + set_color=function(self, i, c) return gfx.skeletons.set_color(self.name, i, c) end, +}} + gfx.text3d.new = function(pos, text, preset, extension) local id = gfx.text3d.show(pos, text, preset, extension) return setmetatable({id=id}, Text3D) end + +gfx.skeletons.get = function(name) + if gfx.skeletons.exists(name) then + return setmetatable({name=name}, Skeleton) + end +end diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 765d2c08..d4be90b3 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -396,6 +396,8 @@ function _rules.clear() end function __vc_on_hud_open() + gfx.skeletons = __skeleton + _rules.create("allow-cheats", true) _rules.create("allow-content-access", hud._is_content_access(), function(value) diff --git a/src/content/Content.cpp b/src/content/Content.cpp index ce92be18..5ee68859 100644 --- a/src/content/Content.cpp +++ b/src/content/Content.cpp @@ -63,6 +63,14 @@ const rigging::SkeletonConfig* Content::getSkeleton(const std::string& id return found->second.get(); } +const rigging::SkeletonConfig& Content::requireSkeleton(const std::string& id) const { + auto skeleton = getSkeleton(id); + if (skeleton == nullptr) { + throw std::runtime_error("skeleton '" + id + "' not loaded"); + } + return *skeleton; +} + const BlockMaterial* Content::findBlockMaterial(const std::string& id) const { auto found = blockMaterials.find(id); if (found == blockMaterials.end()) { diff --git a/src/content/Content.hpp b/src/content/Content.hpp index 539325a8..81e816b6 100644 --- a/src/content/Content.hpp +++ b/src/content/Content.hpp @@ -212,6 +212,7 @@ public: } const rigging::SkeletonConfig* getSkeleton(const std::string& id) const; + const rigging::SkeletonConfig& requireSkeleton(const std::string& id) const; const BlockMaterial* findBlockMaterial(const std::string& id) const; const ContentPackRuntime* getPackRuntime(const std::string& id) const; ContentPackRuntime* getPackRuntime(const std::string& id); diff --git a/src/graphics/render/HandsRenderer.cpp b/src/graphics/render/HandsRenderer.cpp new file mode 100644 index 00000000..db598a43 --- /dev/null +++ b/src/graphics/render/HandsRenderer.cpp @@ -0,0 +1,34 @@ +#include "HandsRenderer.hpp" + +#include +#include + +#include "ModelBatch.hpp" +#include "content/Content.hpp" +#include "graphics/commons/Model.hpp" +#include "objects/rigging.hpp" +#include "window/Camera.hpp" + +using namespace rigging; + +HandsRenderer::HandsRenderer( + const Assets& assets, + ModelBatch& modelBatch, + std::shared_ptr skeleton +) + : assets(assets), + modelBatch(modelBatch), + skeleton(std::move(skeleton)) { +} + +void HandsRenderer::renderHands( + const Camera& camera, float delta +) { + auto& skeleton = *this->skeleton; + const auto& config = *skeleton.config; + + // render + modelBatch.setLightsOffset(camera.position); + config.update(skeleton, glm::mat4(1.0f), glm::vec3()); + config.render(assets, modelBatch, skeleton, glm::mat4(1.0f), glm::vec3()); +} diff --git a/src/graphics/render/HandsRenderer.hpp b/src/graphics/render/HandsRenderer.hpp new file mode 100644 index 00000000..1aba08a2 --- /dev/null +++ b/src/graphics/render/HandsRenderer.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +class Assets; +class Camera; +class ModelBatch; + +namespace rigging { + struct Skeleton; +} + +class HandsRenderer { +public: + HandsRenderer( + const Assets& assets, + ModelBatch& modelBatch, + std::shared_ptr skeleton + ); + + void renderHands(const Camera& camera, float delta); +private: + const Assets& assets; + ModelBatch& modelBatch; + std::shared_ptr skeleton; +}; diff --git a/src/graphics/render/NamedSkeletons.cpp b/src/graphics/render/NamedSkeletons.cpp new file mode 100644 index 00000000..35dd57ec --- /dev/null +++ b/src/graphics/render/NamedSkeletons.cpp @@ -0,0 +1,23 @@ +#include "NamedSkeletons.hpp" + +#include "objects/rigging.hpp" + +using namespace rigging; + +NamedSkeletons::NamedSkeletons() = default; + +std::shared_ptr NamedSkeletons::createSkeleton( + const std::string& name, const SkeletonConfig* config +) { + auto skeleton = std::make_shared(config); + skeletons[name] = skeleton; + return skeleton; +} + +rigging::Skeleton* NamedSkeletons::getSkeleton(const std::string& name) { + const auto& found = skeletons.find(name); + if (found == skeletons.end()) { + return nullptr; + } + return found->second.get(); +} diff --git a/src/graphics/render/NamedSkeletons.hpp b/src/graphics/render/NamedSkeletons.hpp new file mode 100644 index 00000000..4e4fb77a --- /dev/null +++ b/src/graphics/render/NamedSkeletons.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +namespace rigging { + struct Skeleton; + class SkeletonConfig; +} + +class NamedSkeletons { +public: + NamedSkeletons(); + + std::shared_ptr createSkeleton( + const std::string& name, const rigging::SkeletonConfig* config + ); + + rigging::Skeleton* getSkeleton(const std::string& name); +private: + std::unordered_map> skeletons; +}; diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp index e8511fa5..ba1e580b 100644 --- a/src/graphics/render/WorldRenderer.cpp +++ b/src/graphics/render/WorldRenderer.cpp @@ -47,6 +47,8 @@ #include "BlockWrapsRenderer.hpp" #include "ParticlesRenderer.hpp" #include "PrecipitationRenderer.hpp" +#include "HandsRenderer.hpp" +#include "NamedSkeletons.hpp" #include "TextsRenderer.hpp" #include "ChunksRenderer.hpp" #include "GuidesRenderer.hpp" @@ -107,6 +109,15 @@ WorldRenderer::WorldRenderer( settings.graphics.skyboxResolution.get(), assets->require("skybox_gen") ); + + const auto& content = level.content; + skeletons = std::make_unique(); + const auto& skeletonConfig = content.requireSkeleton( + content.getDefaults()["hand-skeleton"].asString() + ); + hands = std::make_unique( + *assets, *modelBatch, skeletons->createSkeleton("hand", &skeletonConfig) + ); } WorldRenderer::~WorldRenderer() = default; @@ -273,70 +284,6 @@ void WorldRenderer::renderLines( } } -void WorldRenderer::renderHands( - const Camera& camera, float delta -) { - auto& entityShader = assets.require("entity"); - auto indices = level.content.getIndices(); - - // get current chosen item - const auto& inventory = player.getInventory(); - int slot = player.getChosenSlot(); - const ItemStack& stack = inventory->getSlot(slot); - const auto& def = indices->items.require(stack.getItemId()); - - // prepare modified HUD camera - Camera hudcam = camera; - hudcam.far = 10.0f; - hudcam.setFov(0.9f); - hudcam.position = {}; - - // configure model matrix - const glm::vec3 itemOffset(0.06f, 0.035f, -0.1); - - static glm::mat4 prevRotation(1.0f); - - const float speed = 24.0f; - glm::mat4 matrix = glm::translate(glm::mat4(1.0f), itemOffset); - matrix = glm::scale(matrix, glm::vec3(0.1f)); - glm::mat4 rotation = camera.rotation; - glm::quat rot0 = glm::quat_cast(prevRotation); - glm::quat rot1 = glm::quat_cast(rotation); - glm::quat finalRot = - glm::slerp(rot0, rot1, static_cast(delta * speed)); - rotation = glm::mat4_cast(finalRot); - matrix = rotation * matrix * - glm::rotate( - glm::mat4(1.0f), -glm::pi() * 0.5f, glm::vec3(0, 1, 0) - ); - prevRotation = rotation; - glm::vec3 cameraRotation = player.getRotation(); - auto offset = -(camera.position - player.getPosition()); - float angle = glm::radians(cameraRotation.x - 90); - float cos = glm::cos(angle); - float sin = glm::sin(angle); - - float newX = offset.x * cos - offset.z * sin; - float newZ = offset.x * sin + offset.z * cos; - offset = glm::vec3(newX, offset.y, newZ); - matrix = matrix * glm::translate(glm::mat4(1.0f), offset); - - // render - modelBatch->setLightsOffset(camera.position); - modelBatch->draw( - matrix, - glm::vec3(1.0f), - assets.get(def.modelName), - nullptr - ); - display::clearDepth(); - setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f); - skybox->bind(); - modelBatch->render(); - modelBatch->setLightsOffset(glm::vec3()); - skybox->unbind(); -} - void WorldRenderer::generateShadowsMap( const Camera& camera, const DrawContext& pctx, @@ -568,7 +515,22 @@ void WorldRenderer::draw( DrawContext ctx = pctx.sub(); ctx.setDepthTest(true); ctx.setCullFace(true); - renderHands(camera, delta); + + // prepare modified HUD camera + Camera hudcam = camera; + hudcam.far = 10.0f; + hudcam.setFov(0.9f); + hudcam.position = {}; + + hands->renderHands(camera, delta); + + display::clearDepth(); + setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f); + + skybox->bind(); + modelBatch->render(); + modelBatch->setLightsOffset(glm::vec3()); + skybox->unbind(); } renderBlockOverlay(pctx); diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp index 5d33f8cd..e53a5d80 100644 --- a/src/graphics/render/WorldRenderer.hpp +++ b/src/graphics/render/WorldRenderer.hpp @@ -20,6 +20,8 @@ class ChunksRenderer; class ParticlesRenderer; class BlockWrapsRenderer; class PrecipitationRenderer; +class HandsRenderer; +class NamedSkeletons; class GuidesRenderer; class TextsRenderer; class Shader; @@ -52,6 +54,7 @@ class WorldRenderer { std::unique_ptr modelBatch; std::unique_ptr guides; std::unique_ptr chunks; + std::unique_ptr hands; std::unique_ptr skybox; std::unique_ptr shadowMap; std::unique_ptr wideShadowMap; @@ -69,8 +72,6 @@ class WorldRenderer { /// @brief Render block selection lines void renderBlockSelection(); - - void renderHands(const Camera& camera, float delta); /// @brief Render lines (selection and debug) /// @param camera active camera @@ -100,6 +101,7 @@ public: std::unique_ptr texts; std::unique_ptr blockWraps; std::unique_ptr precipitation; + std::unique_ptr skeletons; static bool showChunkBorders; static bool showEntitiesDebug; diff --git a/src/logic/scripting/lua/libs/lib__skeleton.cpp b/src/logic/scripting/lua/libs/lib__skeleton.cpp index 2cb9c44e..7272548c 100644 --- a/src/logic/scripting/lua/libs/lib__skeleton.cpp +++ b/src/logic/scripting/lua/libs/lib__skeleton.cpp @@ -1,6 +1,13 @@ #include "objects/rigging.hpp" #include "libentity.hpp" +#include "graphics/render/WorldRenderer.hpp" +#include "graphics/render/NamedSkeletons.hpp" + +namespace scripting { + extern WorldRenderer* renderer; +} + static int index_range_check( const rigging::Skeleton& skeleton, lua::Integer index ) { @@ -13,25 +20,33 @@ static int index_range_check( return static_cast(index); } -static int l_get_model(lua::State* L) { +static rigging::Skeleton* get_skeleton(lua::State* L) { + if (lua::isstring(L, 1)) { + return scripting::renderer->skeletons->getSkeleton(lua::tostring(L, 1)); + } if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - auto* rigConfig = skeleton.config; - auto index = index_range_check(skeleton, lua::tointeger(L, 2)); - const auto& modelOverride = skeleton.modelOverrides[index]; + return &entity->getSkeleton(); + } + return nullptr; +} + +static int l_get_model(lua::State* L) { + if (auto skeleton = get_skeleton(L)) { + auto& rigConfig = *skeleton->config; + auto index = index_range_check(*skeleton, lua::tointeger(L, 2)); + const auto& modelOverride = skeleton->modelOverrides[index]; if (!modelOverride.model) { return lua::pushstring(L, modelOverride.name); } - return lua::pushstring(L, rigConfig->getBones()[index]->model.name); + return lua::pushstring(L, rigConfig.getBones()[index]->model.name); } return 0; } static int l_set_model(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - auto index = index_range_check(skeleton, lua::tointeger(L, 2)); - auto& modelOverride = skeleton.modelOverrides[index]; + if (auto skeleton = get_skeleton(L)) { + auto index = index_range_check(*skeleton, lua::tointeger(L, 2)); + auto& modelOverride = skeleton->modelOverrides[index]; if (lua::isnoneornil(L, 3)) { modelOverride = {"", nullptr, true}; } else { @@ -42,28 +57,25 @@ static int l_set_model(lua::State* L) { } static int l_get_matrix(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - auto index = index_range_check(skeleton, lua::tointeger(L, 2)); - return lua::pushmat4(L, skeleton.pose.matrices[index]); + if (auto skeleton = get_skeleton(L)) { + auto index = index_range_check(*skeleton, lua::tointeger(L, 2)); + return lua::pushmat4(L, skeleton->pose.matrices[index]); } return 0; } static int l_set_matrix(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - auto index = index_range_check(skeleton, lua::tointeger(L, 2)); - skeleton.pose.matrices[index] = lua::tomat4(L, 3); + if (auto skeleton = get_skeleton(L)) { + auto index = index_range_check(*skeleton, lua::tointeger(L, 2)); + skeleton->pose.matrices[index] = lua::tomat4(L, 3); } return 0; } static int l_get_texture(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - const auto& found = skeleton.textures.find(lua::require_string(L, 2)); - if (found != skeleton.textures.end()) { + if (auto skeleton = get_skeleton(L)) { + const auto& found = skeleton->textures.find(lua::require_string(L, 2)); + if (found != skeleton->textures.end()) { return lua::pushstring(L, found->second); } } @@ -71,18 +83,16 @@ static int l_get_texture(lua::State* L) { } static int l_set_texture(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - skeleton.textures[lua::require_string(L, 2)] = + if (auto skeleton = get_skeleton(L)) { + skeleton->textures[lua::require_string(L, 2)] = lua::require_string(L, 3); } return 0; } static int l_index(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - if (auto bone = skeleton.config->find(lua::require_string(L, 2))) { + if (auto skeleton= get_skeleton(L)) { + if (auto bone = skeleton->config->find(lua::require_string(L, 2))) { return lua::pushinteger(L, bone->getIndex()); } } @@ -90,62 +100,60 @@ static int l_index(lua::State* L) { } static int l_is_visible(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); + if (auto skeleton = get_skeleton(L)) { if (!lua::isnoneornil(L, 2)) { - auto index = index_range_check(skeleton, lua::tointeger(L, 2)); - return lua::pushboolean(L, skeleton.flags.at(index).visible); + auto index = index_range_check(*skeleton, lua::tointeger(L, 2)); + return lua::pushboolean(L, skeleton->flags.at(index).visible); } - return lua::pushboolean(L, skeleton.visible); + return lua::pushboolean(L, skeleton->visible); } return 0; } static int l_set_visible(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); + if (auto skeleton = get_skeleton(L)) { if (!lua::isnoneornil(L, 3)) { - auto index = index_range_check(skeleton, lua::tointeger(L, 2)); - skeleton.flags.at(index).visible = lua::toboolean(L, 3); + auto index = index_range_check(*skeleton, lua::tointeger(L, 2)); + skeleton->flags.at(index).visible = lua::toboolean(L, 3); } else { - skeleton.visible = lua::toboolean(L, 2); + skeleton->visible = lua::toboolean(L, 2); } } return 0; } static int l_get_color(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - return lua::pushvec(L, skeleton.tint); + if (auto skeleton = get_skeleton(L)) { + return lua::pushvec(L, skeleton->tint); } return 0; } static int l_set_color(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - skeleton.tint = lua::tovec3(L, 2); + if (auto skeleton = get_skeleton(L)) { + skeleton->tint = lua::tovec3(L, 2); } return 0; } static int l_is_interpolated(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - return lua::pushboolean(L, skeleton.interpolation.isEnabled()); + if (auto skeleton = get_skeleton(L)) { + return lua::pushboolean(L, skeleton->interpolation.isEnabled()); } return 0; } static int l_set_interpolated(lua::State* L) { - if (auto entity = get_entity(L, 1)) { - auto& skeleton = entity->getSkeleton(); - skeleton.interpolation.setEnabled(lua::toboolean(L, 2)); + if (auto skeleton = get_skeleton(L)) { + skeleton->interpolation.setEnabled(lua::toboolean(L, 2)); } return 0; } +static int l_exists(lua::State* L) { + return lua::pushboolean(L, get_skeleton(L)); +} + const luaL_Reg skeletonlib[] = { {"get_model", lua::wrap}, {"set_model", lua::wrap}, @@ -160,4 +168,6 @@ const luaL_Reg skeletonlib[] = { {"set_color", lua::wrap}, {"is_interpolated", lua::wrap}, {"set_interpolated", lua::wrap}, - {NULL, NULL}}; + {"exists", lua::wrap}, + {NULL, NULL} +};