Skip to content

Commit a962fc3

Browse files
committed
Expose Luau 3-vectors support
The Luau VM supports native f32 3-vectors so that typical linear algebra operations are fast in game code. TODO: Luau can also be built for native 4-vectors through a compilation option. This path should be supported also, as that's most probably a better default for 3d-games. 3-vectors are fine for 2d-games :)
1 parent 7f1e80c commit a962fc3

File tree

2 files changed

+63
-2
lines changed

2 files changed

+63
-2
lines changed

src/zigluau/lib.zig

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,14 @@ pub const Lua = struct {
244244
}
245245
}
246246

247+
fn vectorCtor(l: *Lua) i32 {
248+
const x = l.toNumber(1) catch unreachable;
249+
const y = l.toNumber(2) catch unreachable;
250+
const z = l.toNumber(3) catch unreachable;
251+
c.lua_pushvector(l.state, @floatCast(x), @floatCast(y), @floatCast(z));
252+
return 1;
253+
}
254+
247255
/// Initialize a Lua state with the given allocator
248256
/// See https://www.lua.org/manual/5.1/manual.html#lua_newstate
249257
pub fn init(allocator: Allocator) !Lua {
@@ -255,10 +263,12 @@ pub const Lua = struct {
255263
allocator_ptr.* = allocator;
256264

257265
const state = c.lua_newstate(alloc, allocator_ptr) orelse return error.Memory;
258-
return Lua{
266+
var lua = Lua{
259267
.allocator = allocator_ptr,
260268
.state = state,
261269
};
270+
lua.register("vector", wrap(vectorCtor));
271+
return lua;
262272
}
263273

264274
/// Deinitialize a Lua state and free all memory
@@ -670,6 +680,12 @@ pub const Lua = struct {
670680
c.lua_pushvalue(lua.state, index);
671681
}
672682

683+
/// Pushes a floating point 3-vector (or 4-vector if configured) `v` onto the stack
684+
/// See https://www.lua.org/manual/5.1/manual.html#lua_pushinteger
685+
pub fn pushVector(lua: *Lua, v: @Vector(3, f32)) void {
686+
c.lua_pushvector(lua.state, v[0], v[1], v[2]);
687+
}
688+
673689
/// Returns true if the two values in indices `index1` and `index2` are primitively equal
674690
/// Bypasses __eq metamethods
675691
/// Returns false if not equal, or if any index is invalid
@@ -879,6 +895,17 @@ pub const Lua = struct {
879895
return error.Fail;
880896
}
881897

898+
/// Converts the Lua value at the given `index` to a f32 3-vector
899+
/// The Lua value must be an integer, or a number, or a string convertible to an integer otherwise toInteger returns 0
900+
/// See https://www.lua.org/manual/5.1/manual.html#lua_tointeger
901+
pub fn toVector(lua: *Lua, index: i32) !@Vector(3, f32) {
902+
const res = c.lua_tovector(lua.state, index);
903+
if (res) |r| {
904+
return @Vector(3, f32){ r[0], r[1], r[2] };
905+
}
906+
return @Vector(3, f32){ 0, 0, 0 };
907+
}
908+
882909
/// Returns the `LuaType` of the value at the given index
883910
/// Note that this is equivalent to lua_type but because type is a Zig primitive it is renamed to `typeOf`
884911
/// See https://www.lua.org/manual/5.1/manual.html#lua_type
@@ -1136,7 +1163,15 @@ pub const Lua = struct {
11361163
/// TODO: does it make sense to have this in Luau?
11371164
pub fn loadString(lua: *Lua, str: [:0]const u8) !void {
11381165
var size: usize = 0;
1139-
const bytecode = c.luau_compile(str.ptr, str.len, null, &size);
1166+
var opts = c.lua_CompileOptions{
1167+
.optimizationLevel = 1,
1168+
.debugLevel = 1,
1169+
.coverageLevel = 0,
1170+
.vectorLib = null,
1171+
.vectorCtor = "vector",
1172+
.mutableGlobals = null,
1173+
};
1174+
const bytecode = c.luau_compile(str.ptr, str.len, &opts, &size);
11401175

11411176
// Failed to allocate memory for the out buffer
11421177
if (bytecode == null) return error.Memory;

src/zigluau/tests.zig

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,32 @@ test "objectLen" {
10891089
try testing.expectEqual(@as(i32, 3), lua.objectLen(-1));
10901090
}
10911091

1092+
// Luau supports a 'vector' type that's either a 3-vector or 4-vector (if configured at build-time)
1093+
test "luau vectors" {
1094+
var lua = try Lua.init(testing.allocator);
1095+
defer lua.deinit();
1096+
lua.openLibs();
1097+
1098+
try lua.doString(
1099+
\\function test()
1100+
\\ local a = vector(1, 2, 3)
1101+
\\ local b = vector(4, 5, 6)
1102+
\\ local c = (a + b) * vector(2, 2, 2)
1103+
\\ return c
1104+
\\end
1105+
);
1106+
_ = lua.getGlobal("test");
1107+
try lua.protectedCall(0, 1, 0);
1108+
var v = try lua.toVector(-1);
1109+
try testing.expectEqual(v[0], 10.0);
1110+
try testing.expectEqual(v[1], 14.0);
1111+
try testing.expectEqual(v[2], 18.0);
1112+
1113+
lua.pushVector(@Vector(3, f32){ 1, 2, 3 });
1114+
v = try lua.toVector(-1);
1115+
try testing.expectEqual(v, @Vector(3, f32){ 1, 2, 3 });
1116+
}
1117+
10921118
test {
10931119
testing.refAllDecls(Lua);
10941120
testing.refAllDecls(Buffer);

0 commit comments

Comments
 (0)