blob: a74ec89db1b1d90b88d1fdd9d0e1c4c828f60527 [file]
#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