blob: a38b0f31a2736662adaffb71634e5be6eb11305c [file]
#ifdef WITH_RIVE_SCRIPTING
#include "rive/lua/rive_lua_libs.hpp"
#include "lualib.h"
#include <cmath>
#include <cstring>
using namespace rive;
static int vector_index(lua_State* L)
{
const float* vec = luaL_checkvector(L, 1);
size_t namelen = 0;
const char* name = luaL_checklstring(L, 2, &namelen);
if (namelen == 1)
{
switch (name[0])
{
case 'x':
case '1':
lua_pushnumber(L, vec[0]);
return 1;
case 'y':
case '2':
lua_pushnumber(L, vec[1]);
return 1;
case 'z':
case '3':
// Luau's vector type stores 3 components; .z is reachable
// intrinsically through the VM's named-axis fastpath, but
// numeric `[3]` indexing routes through this metamethod.
lua_pushnumber(L, vec[2]);
return 1;
default:
break;
}
}
luaL_error(L, "'%s' is not a valid index of Vector", name);
return 0;
}
// All magnitude and interpolation ops read all 3 components. For genuine 2D
// vectors z is 0, so results are identical to the old 2D math.
static inline float dot3(const float* a, const float* b)
{
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
static int vector_length(lua_State* L)
{
const float* vec = luaL_checkvector(L, 1);
lua_pushnumber(L, std::sqrt(dot3(vec, vec)));
return 1;
}
static int vector_normalized(lua_State* L)
{
const float* vec = luaL_checkvector(L, 1);
float len2 = dot3(vec, vec);
float scale = len2 > 0 ? 1.f / std::sqrt(len2) : 1.f;
lua_pushvector(L, vec[0] * scale, vec[1] * scale, vec[2] * scale);
return 1;
}
static int vector_lengthSquared(lua_State* L)
{
const float* vec = luaL_checkvector(L, 1);
lua_pushnumber(L, dot3(vec, vec));
return 1;
}
static int vector_distanceSquared(lua_State* L)
{
const float* lhs = luaL_checkvector(L, 1);
const float* rhs = luaL_checkvector(L, 2);
float d[3] = {rhs[0] - lhs[0], rhs[1] - lhs[1], rhs[2] - lhs[2]};
lua_pushnumber(L, dot3(d, d));
return 1;
}
static int vector_distance(lua_State* L)
{
const float* lhs = luaL_checkvector(L, 1);
const float* rhs = luaL_checkvector(L, 2);
float d[3] = {rhs[0] - lhs[0], rhs[1] - lhs[1], rhs[2] - lhs[2]};
lua_pushnumber(L, std::sqrt(dot3(d, d)));
return 1;
}
static int vector_dot(lua_State* L)
{
const float* lhs = luaL_checkvector(L, 1);
const float* rhs = luaL_checkvector(L, 2);
lua_pushnumber(L, dot3(lhs, rhs));
return 1;
}
static int vector_cross(lua_State* L)
{
auto lhs = lua_checkvec2d(L, 1);
auto rhs = lua_checkvec2d(L, 2);
lua_pushnumber(L, Vec2D::cross(*lhs, *rhs));
return 1;
}
// 3D cross product. Distinct from `cross`, which returns the scalar 2D
// perp-dot.
static int vector_cross3(lua_State* L)
{
const float* a = luaL_checkvector(L, 1);
const float* b = luaL_checkvector(L, 2);
lua_pushvector(L,
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0]);
return 1;
}
static int vector_scaleAndAdd(lua_State* L)
{
const float* a = luaL_checkvector(L, 1);
const float* b = luaL_checkvector(L, 2);
float scale = float(luaL_checknumber(L, 3));
lua_pushvector(L,
a[0] + b[0] * scale,
a[1] + b[1] * scale,
a[2] + b[2] * scale);
return 1;
}
static int vector_scaleAndSub(lua_State* L)
{
const float* a = luaL_checkvector(L, 1);
const float* b = luaL_checkvector(L, 2);
float scale = float(luaL_checknumber(L, 3));
lua_pushvector(L,
a[0] - b[0] * scale,
a[1] - b[1] * scale,
a[2] - b[2] * scale);
return 1;
}
// Exact at t=1, mirroring the VM fastcall's luai_lerpf so both dispatch
// paths return bit-identical results.
static inline float lerpf(float a, float b, float t)
{
return t == 1.f ? b : a + (b - a) * t;
}
static int vector_lerp(lua_State* L)
{
const float* a = luaL_checkvector(L, 1);
const float* b = luaL_checkvector(L, 2);
float t = float(luaL_checknumber(L, 3));
lua_pushvector(L,
lerpf(a[0], b[0], t),
lerpf(a[1], b[1], t),
lerpf(a[2], b[2], t));
return 1;
}
// vec:writeToBuffer(buf, byteOffset) — 12 bytes (x, y, z as float32).
static int vector_writeToBuffer(lua_State* L)
{
const float* vec = luaL_checkvector(L, 1);
size_t bufLen = 0;
void* buf = luaL_checkbuffer(L, 2, &bufLen);
int off = int(luaL_checkinteger(L, 3));
if (off < 0 || size_t(off) + 12 > bufLen)
{
luaL_error(L, "Vector:writeToBuffer offset out of range");
return 0;
}
std::memcpy(static_cast<uint8_t*>(buf) + off, vec, 12);
return 0;
}
// vec:writeVec4(buf, byteOffset, w) — 16 bytes (x, y, z, w as float32),
// matching a vec4 uniform-buffer slot.
static int vector_writeVec4(lua_State* L)
{
const float* vec = luaL_checkvector(L, 1);
size_t bufLen = 0;
void* buf = luaL_checkbuffer(L, 2, &bufLen);
int off = int(luaL_checkinteger(L, 3));
float w = float(luaL_checknumber(L, 4));
if (off < 0 || size_t(off) + 16 > bufLen)
{
luaL_error(L, "Vector:writeVec4 offset out of range");
return 0;
}
uint8_t* dst = static_cast<uint8_t*>(buf) + off;
std::memcpy(dst, vec, 12);
std::memcpy(dst + 12, &w, 4);
return 0;
}
static int vector_xy(lua_State* L)
{
float x = (float)lua_tonumber(L, 1);
float y = (float)lua_tonumber(L, 2);
lua_pushvector2(L, x, y);
return 1;
}
static int vector_xyz(lua_State* L)
{
float x = (float)lua_tonumber(L, 1);
float y = (float)lua_tonumber(L, 2);
float z = (float)lua_tonumber(L, 3);
lua_pushvector(L, x, y, z);
return 1;
}
static int vector_origin(lua_State* L)
{
lua_pushvector2(L, 0.0f, 0.0f);
return 1;
}
static int vector_namecall(lua_State* L)
{
int atom;
const char* str = lua_namecallatom(L, &atom);
if (str != nullptr)
{
switch (atom)
{
case (int)LuaAtoms::length:
return vector_length(L);
case (int)LuaAtoms::lengthSquared:
return vector_lengthSquared(L);
case (int)LuaAtoms::normalized:
return vector_normalized(L);
case (int)LuaAtoms::distance:
return vector_distance(L);
case (int)LuaAtoms::distanceSquared:
return vector_distanceSquared(L);
case (int)LuaAtoms::dot:
return vector_dot(L);
case (int)LuaAtoms::lerp:
return vector_lerp(L);
case (int)LuaAtoms::writeToBuffer:
return vector_writeToBuffer(L);
case (int)LuaAtoms::writeVec4:
return vector_writeVec4(L);
}
}
luaL_error(L, "%s is not a valid method of Vector", luaL_checkstring(L, 1));
return 0;
}
static const luaL_Reg vectorStaticMethods[] = {
{"distance", vector_distance},
{"distanceSquared", vector_distanceSquared},
{"dot", vector_dot},
{"cross", vector_cross},
{"cross3", vector_cross3},
{"scaleAndAdd", vector_scaleAndAdd},
{"scaleAndSub", vector_scaleAndSub},
{"lerp", vector_lerp},
{"xy", vector_xy},
{"xyz", vector_xyz},
{"origin", vector_origin},
{"length", vector_length},
{"lengthSquared", vector_lengthSquared},
{"normalized", vector_normalized},
{nullptr, nullptr}};
int luaopen_rive_vector(lua_State* L)
{
luaL_register(L, "Vector", vectorStaticMethods);
// create metatable for T
lua_createtable(L, 0, 1);
// Dupe the metatable on the stack
lua_pushvalue(L, -1);
// Add a vector
lua_pushvector(L, 0.0f, 0.0f, 0.0f);
// Move it before the metatable
lua_insert(L, -2);
// Set metatable on the vector (globally sets the metatable for all
// vectors).
lua_setmetatable(L, -2);
// pop vector
lua_pop(L, 1);
lua_pushcfunction(L, vector_index, nullptr);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, vector_namecall, nullptr);
lua_setfield(L, -2, "__namecall");
lua_setreadonly(L, -1, true);
lua_pop(L, 1); // pop the metatable
return 1;
}
#endif