| #ifdef WITH_RIVE_SCRIPTING | |
| #include "lualib.h" | |
| #include "rive/math/vec2d.hpp" | |
| #include "rive/lua/rive_lua_libs.hpp" | |
| #include "rive/factory.hpp" | |
| #include <algorithm> | |
| #include <math.h> | |
| #include <stdio.h> | |
| using namespace rive; | |
| static int vertex_buffer_construct(lua_State* L) | |
| { | |
| lua_newrive<ScriptedVertexBuffer>(L); | |
| return 1; | |
| } | |
| static int vertex_buffer_reset(lua_State* L) | |
| { | |
| auto vertexBuffer = lua_torive<ScriptedVertexBuffer>(L, 1); | |
| vertexBuffer->values.clear(); | |
| vertexBuffer->vertexBuffer = nullptr; | |
| return 0; | |
| } | |
| static int vertex_buffer_add(lua_State* L) | |
| { | |
| auto vertexBuffer = lua_torive<ScriptedVertexBuffer>(L, 1); | |
| int nargs = lua_gettop(L) - 1; | |
| for (int i = 0; i < nargs; i++) | |
| { | |
| auto vec = lua_checkvec2d(L, 2 + i); | |
| vertexBuffer->values.push_back(*vec); | |
| } | |
| vertexBuffer->vertexBuffer = nullptr; | |
| return 0; | |
| } | |
| static int vertex_buffer_namecall(lua_State* L) | |
| { | |
| int atom; | |
| const char* str = lua_namecallatom(L, &atom); | |
| if (str != nullptr) | |
| { | |
| switch (atom) | |
| { | |
| case (int)LuaAtoms::reset: | |
| return vertex_buffer_reset(L); | |
| case (int)LuaAtoms::add: | |
| return vertex_buffer_add(L); | |
| } | |
| } | |
| luaL_error( | |
| L, | |
| "%s is not a valid method of %s", | |
| str, | |
| ScriptedVertexBuffer::luaName); | |
| return 0; | |
| } | |
| static int index_buffer_construct(lua_State* L) | |
| { | |
| lua_newrive<ScriptedTriangleBuffer>(L); | |
| return 1; | |
| } | |
| static int index_buffer_reset(lua_State* L) | |
| { | |
| auto indexBuffer = lua_torive<ScriptedTriangleBuffer>(L, 1); | |
| indexBuffer->values.clear(); | |
| indexBuffer->max = 0; | |
| indexBuffer->indexBuffer = nullptr; | |
| return 0; | |
| } | |
| static int index_buffer_add(lua_State* L) | |
| { | |
| auto indexBuffer = lua_torive<ScriptedTriangleBuffer>(L, 1); | |
| for (int i = 0; i < 3; i++) | |
| { | |
| auto index = luaL_checkunsigned(L, 2 + i); | |
| if (index > std::numeric_limits<uint16_t>::max()) | |
| { | |
| luaL_error( | |
| L, | |
| "index %i exceeds %i", | |
| index, | |
| std::numeric_limits<uint16_t>::max()); | |
| } | |
| if (index > indexBuffer->max) | |
| { | |
| indexBuffer->max = index; | |
| } | |
| indexBuffer->values.push_back((uint16_t)index); | |
| } | |
| indexBuffer->indexBuffer = nullptr; | |
| return 0; | |
| } | |
| static int index_buffer_namecall(lua_State* L) | |
| { | |
| int atom; | |
| const char* str = lua_namecallatom(L, &atom); | |
| if (str != nullptr) | |
| { | |
| switch (atom) | |
| { | |
| case (int)LuaAtoms::reset: | |
| return index_buffer_reset(L); | |
| case (int)LuaAtoms::add: | |
| return index_buffer_add(L); | |
| } | |
| } | |
| luaL_error( | |
| L, | |
| "%s is not a valid method of %s", | |
| str, | |
| ScriptedTriangleBuffer::luaName); | |
| return 0; | |
| } | |
| static int mesh_instances_construct(lua_State* L) | |
| { | |
| ScriptingContext* context = static_cast<ScriptingContext*>( | |
| lua_getthreaddata(L)); | |
| lua_newrive<ScriptedImageMeshInstances>( | |
| L, | |
| context->factory(), | |
| (size_t)luaL_optunsigned(L, 2, 0)); | |
| return 1; | |
| } | |
| static int mesh_instances_resize(lua_State* L) | |
| { | |
| auto scripted = lua_torive<ScriptedImageMeshInstances>(L, 1); | |
| auto count = luaL_checkunsigned(L, 2); | |
| scripted->resize((size_t)count); | |
| return 0; | |
| } | |
| // set(index, transform, opacity, additiveness, uvTranslate, uvScale), where | |
| // everything past the transform is optional and defaults to a plain draw. | |
| static int mesh_instances_set(lua_State* L) | |
| { | |
| auto scripted = lua_torive<ScriptedImageMeshInstances>(L, 1); | |
| auto index = luaL_checkunsigned(L, 2); | |
| if (index >= scripted->count()) | |
| { | |
| luaL_error( | |
| L, | |
| "index %d is past the end of %s", | |
| index, | |
| ScriptedImageMeshInstances::luaName); | |
| } | |
| auto mat2d = lua_torive<ScriptedMat2D>(L, 3); | |
| // Staged: the renderer gets every set() of a frame in one commit when the | |
| // instances are drawn. | |
| ImageMeshInstanceData& data = scripted->stage(index); | |
| data.transform = mat2d->value; | |
| data.opacity = (float)luaL_optnumber(L, 4, 1.0); | |
| data.additiveness = (float)luaL_optnumber(L, 5, 0.0); | |
| data.uvTranslate = lua_gettop(L) >= 6 ? *lua_checkvec2d(L, 6) | |
| : Vec2D(0.0f, 0.0f); | |
| data.uvScale = lua_gettop(L) >= 7 ? *lua_checkvec2d(L, 7) : Vec2D(1.0f, 1.0f); | |
| return 0; | |
| } | |
| static int mesh_instances_namecall(lua_State* L) | |
| { | |
| int atom; | |
| const char* str = lua_namecallatom(L, &atom); | |
| if (str != nullptr) | |
| { | |
| switch (atom) | |
| { | |
| case (int)LuaAtoms::resize: | |
| return mesh_instances_resize(L); | |
| case (int)LuaAtoms::set: | |
| return mesh_instances_set(L); | |
| } | |
| } | |
| luaL_error( | |
| L, | |
| "%s is not a valid method of %s", | |
| str, | |
| ScriptedImageMeshInstances::luaName); | |
| return 0; | |
| } | |
| static const luaL_Reg empty[] = { | |
| {NULL, NULL}, | |
| }; | |
| static void register_vertex_buffer(lua_State* L) | |
| { | |
| luaL_register(L, ScriptedVertexBuffer::luaName, empty); | |
| lua_register_rive<ScriptedVertexBuffer>(L); | |
| lua_pushcfunction(L, vertex_buffer_namecall, nullptr); | |
| lua_setfield(L, -2, "__namecall"); | |
| // Create metatable for the metatable (so we can call it). | |
| lua_createtable(L, 0, 1); | |
| lua_pushcfunction(L, vertex_buffer_construct, nullptr); | |
| lua_setfield(L, -2, "__call"); | |
| // -3 as it's the library (Path) that we're setting this metatable on. | |
| lua_setmetatable(L, -3); | |
| lua_setreadonly(L, -1, true); | |
| lua_pop(L, 1); // pop the metatable | |
| } | |
| static void register_index_buffer(lua_State* L) | |
| { | |
| luaL_register(L, ScriptedTriangleBuffer::luaName, empty); | |
| lua_register_rive<ScriptedTriangleBuffer>(L); | |
| lua_pushcfunction(L, index_buffer_namecall, nullptr); | |
| lua_setfield(L, -2, "__namecall"); | |
| // Create metatable for the metatable (so we can call it). | |
| lua_createtable(L, 0, 1); | |
| lua_pushcfunction(L, index_buffer_construct, nullptr); | |
| lua_setfield(L, -2, "__call"); | |
| // -3 as it's the library (Path) that we're setting this metatable on. | |
| lua_setmetatable(L, -3); | |
| lua_setreadonly(L, -1, true); | |
| lua_pop(L, 1); // pop the metatable | |
| } | |
| static void register_mesh_instances(lua_State* L) | |
| { | |
| luaL_register(L, ScriptedImageMeshInstances::luaName, empty); | |
| lua_register_rive<ScriptedImageMeshInstances>(L); | |
| lua_pushcfunction(L, mesh_instances_namecall, nullptr); | |
| lua_setfield(L, -2, "__namecall"); | |
| // Create metatable for the metatable (so we can call it). | |
| lua_createtable(L, 0, 1); | |
| lua_pushcfunction(L, mesh_instances_construct, nullptr); | |
| lua_setfield(L, -2, "__call"); | |
| // -3 as it's the library that we're setting this metatable on. | |
| lua_setmetatable(L, -3); | |
| lua_setreadonly(L, -1, true); | |
| lua_pop(L, 1); // pop the metatable | |
| } | |
| void ScriptedVertexBuffer::update(Factory* factory) | |
| { | |
| // still valid? | |
| if (vertexBuffer) | |
| { | |
| return; | |
| } | |
| auto buffer = factory->makeRenderBuffer( | |
| RenderBufferType::vertex, | |
| RenderBufferFlags::mappedOnceAtInitialization, | |
| values.size() * sizeof(Vec2D)); | |
| if (buffer != nullptr) | |
| { | |
| float* pos = static_cast<float*>(buffer->map()); | |
| if (pos != nullptr) | |
| { | |
| memcpy(pos, values.data(), buffer->sizeInBytes()); | |
| buffer->unmap(); | |
| } | |
| } | |
| vertexBuffer = buffer; | |
| } | |
| void ScriptedTriangleBuffer::update(Factory* factory) | |
| { | |
| // still valid? | |
| if (indexBuffer) | |
| { | |
| return; | |
| } | |
| auto buffer = factory->makeRenderBuffer( | |
| RenderBufferType::index, | |
| RenderBufferFlags::mappedOnceAtInitialization, | |
| values.size() * sizeof(uint16_t)); | |
| if (buffer != nullptr) | |
| { | |
| void* indexData = buffer->map(); | |
| if (indexData != nullptr) | |
| { | |
| memcpy(indexData, values.data(), buffer->sizeInBytes()); | |
| buffer->unmap(); | |
| } | |
| } | |
| indexBuffer = buffer; | |
| } | |
| void ScriptedImageMeshInstances::resize(size_t count) | |
| { | |
| if (m_staged.size() != count) | |
| { | |
| m_staged.resize(count); | |
| m_dirty = true; | |
| } | |
| } | |
| void ScriptedImageMeshInstances::commit() | |
| { | |
| if (!m_dirty) | |
| { | |
| return; | |
| } | |
| Span<ImageMeshInstanceData> data = instances->edit(m_staged.size()); | |
| std::copy(m_staged.begin(), m_staged.end(), data.data()); | |
| instances->endEdit(); | |
| m_dirty = false; | |
| } | |
| int luaopen_rive_mesh(lua_State* L) | |
| { | |
| register_vertex_buffer(L); | |
| register_index_buffer(L); | |
| register_mesh_instances(L); | |
| return 3; | |
| } | |
| #endif |