| #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 |