Skip to content

Commit dd6eb5c

Browse files
luigi-rossoluigi-rosso
andcommitted
feat(scripting): 3D Vector ops, Mat4 lookAt/ortho, Vector buffer writes, GPUBuffer write source range (#13003) ef0e100413
* feat(scripting): 3D Vector ops, Mat4 lookAt/ortho, Vector buffer writes, GPUBuffer write source range * test(scripting): pin Vector fastcall and C binding paths bit-identical, align lerp endpoint Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
1 parent 2ddd458 commit dd6eb5c

10 files changed

Lines changed: 473 additions & 36 deletions

File tree

.rive_head

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
eeb280d7f906f2e47bb77c02600a301dda7e600d
1+
ef0e10041348832ffb0d398b9bb20df97a1d00b8

include/rive/lua/rive_lua_libs.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,9 @@ enum class LuaAtoms : int16_t
347347
writeToBuffer,
348348
invertAffine,
349349

350+
// Vector
351+
writeVec4,
352+
350353
// Gamepad
351354
axes,
352355
gamepadMapping,

include/rive/math/mat4.hpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,73 @@ class Mat4
178178
return m;
179179
}
180180

181+
// Right-handed view matrix looking from `eye` toward `center` (GLM
182+
// convention). `up` need not be normalized.
183+
static Mat4 lookAt(const float eye[3],
184+
const float center[3],
185+
const float up[3])
186+
{
187+
float f[3] = {center[0] - eye[0],
188+
center[1] - eye[1],
189+
center[2] - eye[2]};
190+
float fInv = 1.f / std::sqrt(f[0] * f[0] + f[1] * f[1] + f[2] * f[2]);
191+
f[0] *= fInv;
192+
f[1] *= fInv;
193+
f[2] *= fInv;
194+
float s[3] = {f[1] * up[2] - f[2] * up[1],
195+
f[2] * up[0] - f[0] * up[2],
196+
f[0] * up[1] - f[1] * up[0]};
197+
float sInv = 1.f / std::sqrt(s[0] * s[0] + s[1] * s[1] + s[2] * s[2]);
198+
s[0] *= sInv;
199+
s[1] *= sInv;
200+
s[2] *= sInv;
201+
float u[3] = {s[1] * f[2] - s[2] * f[1],
202+
s[2] * f[0] - s[0] * f[2],
203+
s[0] * f[1] - s[1] * f[0]};
204+
Mat4 m;
205+
m.m_buffer[0] = s[0];
206+
m.m_buffer[1] = u[0];
207+
m.m_buffer[2] = -f[0];
208+
m.m_buffer[4] = s[1];
209+
m.m_buffer[5] = u[1];
210+
m.m_buffer[6] = -f[1];
211+
m.m_buffer[8] = s[2];
212+
m.m_buffer[9] = u[2];
213+
m.m_buffer[10] = -f[2];
214+
m.m_buffer[12] = -(s[0] * eye[0] + s[1] * eye[1] + s[2] * eye[2]);
215+
m.m_buffer[13] = -(u[0] * eye[0] + u[1] * eye[1] + u[2] * eye[2]);
216+
m.m_buffer[14] = f[0] * eye[0] + f[1] * eye[1] + f[2] * eye[2];
217+
return m;
218+
}
219+
220+
// Right-handed orthographic projection. Maps view-space z=[-near, -far]
221+
// to NDC z in either [0, 1] (default, depthZeroToOne=true) or [-1, 1].
222+
static Mat4 ortho(float left,
223+
float right,
224+
float bottom,
225+
float top,
226+
float near_,
227+
float far_,
228+
bool depthZeroToOne = true)
229+
{
230+
Mat4 m;
231+
m.m_buffer[0] = 2.f / (right - left);
232+
m.m_buffer[5] = 2.f / (top - bottom);
233+
m.m_buffer[12] = -(right + left) / (right - left);
234+
m.m_buffer[13] = -(top + bottom) / (top - bottom);
235+
if (depthZeroToOne)
236+
{
237+
m.m_buffer[10] = -1.f / (far_ - near_);
238+
m.m_buffer[14] = -near_ / (far_ - near_);
239+
}
240+
else
241+
{
242+
m.m_buffer[10] = -2.f / (far_ - near_);
243+
m.m_buffer[14] = -(far_ + near_) / (far_ - near_);
244+
}
245+
return m;
246+
}
247+
181248
// SIMD: out = lhs * rhs. Both column-major.
182249
static Mat4 multiply(const Mat4& lhs, const Mat4& rhs)
183250
{

scripting/premake5.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
local dependency = require('dependency')
2-
local luau = dependency.github('luigi-rosso/luau', 'rive_0_728')
2+
local luau = dependency.github('luigi-rosso/luau', 'rive_0_728_vec3')
33
local libhydrogen = dependency.github('luigi-rosso/libhydrogen', 'rive_0_2')
44

55
dofile('rive_build_config.lua')

src/lua/math/lua_mat4.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,27 @@ static int mat4_perspectiveReverseZ(lua_State* L)
9090
return 1;
9191
}
9292

93+
static int mat4_lookAt(lua_State* L)
94+
{
95+
const float* eye = luaL_checkvector(L, 1);
96+
const float* center = luaL_checkvector(L, 2);
97+
const float* up = luaL_checkvector(L, 3);
98+
lua_pushmat4(L, Mat4::lookAt(eye, center, up));
99+
return 1;
100+
}
101+
102+
static int mat4_ortho(lua_State* L)
103+
{
104+
float l = float(luaL_checknumber(L, 1));
105+
float r = float(luaL_checknumber(L, 2));
106+
float b = float(luaL_checknumber(L, 3));
107+
float t = float(luaL_checknumber(L, 4));
108+
float n = float(luaL_checknumber(L, 5));
109+
float f = float(luaL_checknumber(L, 6));
110+
lua_pushmat4(L, Mat4::ortho(l, r, b, t, n, f, /*zeroToOne=*/true));
111+
return 1;
112+
}
113+
93114
// In-place: Mat4.multiply(out, a, b) -> out = a * b. Returns out.
94115
// Avoids per-call userdata allocation in tight loops.
95116
static int mat4_static_multiply(lua_State* L)
@@ -379,6 +400,8 @@ static const luaL_Reg mat4StaticMethods[] = {
379400
{"fromRotationZ", mat4_fromRotationZ},
380401
{"perspective", mat4_perspective},
381402
{"perspectiveReverseZ", mat4_perspectiveReverseZ},
403+
{"lookAt", mat4_lookAt},
404+
{"ortho", mat4_ortho},
382405
{"multiply", mat4_static_multiply},
383406
{"multiplyAffine", mat4_static_multiplyAffine},
384407
{"invert", mat4_static_invert},

src/lua/math/lua_vec2d.cpp

Lines changed: 121 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#ifdef WITH_RIVE_SCRIPTING
33
#include "rive/lua/rive_lua_libs.hpp"
44
#include "lualib.h"
5+
#include <cmath>
6+
#include <cstring>
57

68
using namespace rive;
79

@@ -40,49 +42,59 @@ static int vector_index(lua_State* L)
4042
return 0;
4143
}
4244

45+
// All magnitude and interpolation ops read all 3 components. For genuine 2D
46+
// vectors z is 0, so results are identical to the old 2D math.
47+
static inline float dot3(const float* a, const float* b)
48+
{
49+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
50+
}
51+
4352
static int vector_length(lua_State* L)
4453
{
45-
auto vec = lua_checkvec2d(L, 1);
46-
lua_pushnumber(L, vec->length());
54+
const float* vec = luaL_checkvector(L, 1);
55+
lua_pushnumber(L, std::sqrt(dot3(vec, vec)));
4756
return 1;
4857
}
4958

5059
static int vector_normalized(lua_State* L)
5160
{
52-
auto vec = lua_checkvec2d(L, 1);
53-
lua_pushvec2d(L, vec->normalized());
61+
const float* vec = luaL_checkvector(L, 1);
62+
float len2 = dot3(vec, vec);
63+
float scale = len2 > 0 ? 1.f / std::sqrt(len2) : 1.f;
64+
lua_pushvector(L, vec[0] * scale, vec[1] * scale, vec[2] * scale);
5465
return 1;
5566
}
5667

5768
static int vector_lengthSquared(lua_State* L)
5869
{
59-
auto vec = lua_checkvec2d(L, 1);
60-
lua_pushnumber(L, vec->lengthSquared());
70+
const float* vec = luaL_checkvector(L, 1);
71+
lua_pushnumber(L, dot3(vec, vec));
6172
return 1;
6273
}
6374

64-
static int vector_distance(lua_State* L)
75+
static int vector_distanceSquared(lua_State* L)
6576
{
66-
auto lhs = lua_checkvec2d(L, 1);
67-
auto rhs = lua_checkvec2d(L, 2);
68-
lua_pushnumber(L, Vec2D::distance(*lhs, *rhs));
77+
const float* lhs = luaL_checkvector(L, 1);
78+
const float* rhs = luaL_checkvector(L, 2);
79+
float d[3] = {rhs[0] - lhs[0], rhs[1] - lhs[1], rhs[2] - lhs[2]};
80+
lua_pushnumber(L, dot3(d, d));
6981
return 1;
7082
}
7183

72-
static int vector_distanceSquared(lua_State* L)
84+
static int vector_distance(lua_State* L)
7385
{
74-
auto lhs = lua_checkvec2d(L, 1);
75-
auto rhs = lua_checkvec2d(L, 2);
76-
lua_pushnumber(L, Vec2D::distanceSquared(*lhs, *rhs));
86+
const float* lhs = luaL_checkvector(L, 1);
87+
const float* rhs = luaL_checkvector(L, 2);
88+
float d[3] = {rhs[0] - lhs[0], rhs[1] - lhs[1], rhs[2] - lhs[2]};
89+
lua_pushnumber(L, std::sqrt(dot3(d, d)));
7790
return 1;
7891
}
7992

8093
static int vector_dot(lua_State* L)
8194
{
82-
auto lhs = lua_checkvec2d(L, 1);
83-
auto rhs = lua_checkvec2d(L, 2);
84-
85-
lua_pushnumber(L, Vec2D::dot(*lhs, *rhs));
95+
const float* lhs = luaL_checkvector(L, 1);
96+
const float* rhs = luaL_checkvector(L, 2);
97+
lua_pushnumber(L, dot3(lhs, rhs));
8698
return 1;
8799
}
88100

@@ -94,33 +106,98 @@ static int vector_cross(lua_State* L)
94106
return 1;
95107
}
96108

109+
// 3D cross product. Distinct from `cross`, which returns the scalar 2D
110+
// perp-dot.
111+
static int vector_cross3(lua_State* L)
112+
{
113+
const float* a = luaL_checkvector(L, 1);
114+
const float* b = luaL_checkvector(L, 2);
115+
lua_pushvector(L,
116+
a[1] * b[2] - a[2] * b[1],
117+
a[2] * b[0] - a[0] * b[2],
118+
a[0] * b[1] - a[1] * b[0]);
119+
return 1;
120+
}
121+
97122
static int vector_scaleAndAdd(lua_State* L)
98123
{
99-
auto a = lua_checkvec2d(L, 1);
100-
auto b = lua_checkvec2d(L, 2);
124+
const float* a = luaL_checkvector(L, 1);
125+
const float* b = luaL_checkvector(L, 2);
101126
float scale = float(luaL_checknumber(L, 3));
102-
lua_pushvec2d(L, Vec2D::scaleAndAdd(*a, *b, scale));
127+
lua_pushvector(L,
128+
a[0] + b[0] * scale,
129+
a[1] + b[1] * scale,
130+
a[2] + b[2] * scale);
103131
return 1;
104132
}
105133

106134
static int vector_scaleAndSub(lua_State* L)
107135
{
108-
auto a = lua_checkvec2d(L, 1);
109-
auto b = lua_checkvec2d(L, 2);
136+
const float* a = luaL_checkvector(L, 1);
137+
const float* b = luaL_checkvector(L, 2);
110138
float scale = float(luaL_checknumber(L, 3));
111-
lua_pushvec2d(L, *a - *b * scale);
139+
lua_pushvector(L,
140+
a[0] - b[0] * scale,
141+
a[1] - b[1] * scale,
142+
a[2] - b[2] * scale);
112143
return 1;
113144
}
114145

146+
// Exact at t=1, mirroring the VM fastcall's luai_lerpf so both dispatch
147+
// paths return bit-identical results.
148+
static inline float lerpf(float a, float b, float t)
149+
{
150+
return t == 1.f ? b : a + (b - a) * t;
151+
}
152+
115153
static int vector_lerp(lua_State* L)
116154
{
117-
auto lhs = lua_checkvec2d(L, 1);
118-
auto rhs = lua_checkvec2d(L, 2);
119-
float factor = float(luaL_checknumber(L, 3));
120-
lua_pushvec2d(L, Vec2D::lerp(*lhs, *rhs, factor));
155+
const float* a = luaL_checkvector(L, 1);
156+
const float* b = luaL_checkvector(L, 2);
157+
float t = float(luaL_checknumber(L, 3));
158+
lua_pushvector(L,
159+
lerpf(a[0], b[0], t),
160+
lerpf(a[1], b[1], t),
161+
lerpf(a[2], b[2], t));
121162
return 1;
122163
}
123164

165+
// vec:writeToBuffer(buf, byteOffset) — 12 bytes (x, y, z as float32).
166+
static int vector_writeToBuffer(lua_State* L)
167+
{
168+
const float* vec = luaL_checkvector(L, 1);
169+
size_t bufLen = 0;
170+
void* buf = luaL_checkbuffer(L, 2, &bufLen);
171+
int off = int(luaL_checkinteger(L, 3));
172+
if (off < 0 || size_t(off) + 12 > bufLen)
173+
{
174+
luaL_error(L, "Vector:writeToBuffer offset out of range");
175+
return 0;
176+
}
177+
std::memcpy(static_cast<uint8_t*>(buf) + off, vec, 12);
178+
return 0;
179+
}
180+
181+
// vec:writeVec4(buf, byteOffset, w) — 16 bytes (x, y, z, w as float32),
182+
// matching a vec4 uniform-buffer slot.
183+
static int vector_writeVec4(lua_State* L)
184+
{
185+
const float* vec = luaL_checkvector(L, 1);
186+
size_t bufLen = 0;
187+
void* buf = luaL_checkbuffer(L, 2, &bufLen);
188+
int off = int(luaL_checkinteger(L, 3));
189+
float w = float(luaL_checknumber(L, 4));
190+
if (off < 0 || size_t(off) + 16 > bufLen)
191+
{
192+
luaL_error(L, "Vector:writeVec4 offset out of range");
193+
return 0;
194+
}
195+
uint8_t* dst = static_cast<uint8_t*>(buf) + off;
196+
std::memcpy(dst, vec, 12);
197+
std::memcpy(dst + 12, &w, 4);
198+
return 0;
199+
}
200+
124201
static int vector_xy(lua_State* L)
125202
{
126203
float x = (float)lua_tonumber(L, 1);
@@ -130,6 +207,16 @@ static int vector_xy(lua_State* L)
130207
return 1;
131208
}
132209

210+
static int vector_xyz(lua_State* L)
211+
{
212+
float x = (float)lua_tonumber(L, 1);
213+
float y = (float)lua_tonumber(L, 2);
214+
float z = (float)lua_tonumber(L, 3);
215+
216+
lua_pushvector(L, x, y, z);
217+
return 1;
218+
}
219+
133220
static int vector_origin(lua_State* L)
134221
{
135222
lua_pushvector2(L, 0.0f, 0.0f);
@@ -158,6 +245,10 @@ static int vector_namecall(lua_State* L)
158245
return vector_dot(L);
159246
case (int)LuaAtoms::lerp:
160247
return vector_lerp(L);
248+
case (int)LuaAtoms::writeToBuffer:
249+
return vector_writeToBuffer(L);
250+
case (int)LuaAtoms::writeVec4:
251+
return vector_writeVec4(L);
161252
}
162253
}
163254

@@ -170,10 +261,12 @@ static const luaL_Reg vectorStaticMethods[] = {
170261
{"distanceSquared", vector_distanceSquared},
171262
{"dot", vector_dot},
172263
{"cross", vector_cross},
264+
{"cross3", vector_cross3},
173265
{"scaleAndAdd", vector_scaleAndAdd},
174266
{"scaleAndSub", vector_scaleAndSub},
175267
{"lerp", vector_lerp},
176268
{"xy", vector_xy},
269+
{"xyz", vector_xyz},
177270
{"origin", vector_origin},
178271
{"length", vector_length},
179272
{"lengthSquared", vector_lengthSquared},

0 commit comments

Comments
 (0)