Skip to content

Expose Luau 3-vectors support #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 15, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions build.zig
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ pub fn build(b: *Build) void {

const lang = b.option(Language, "lang", "Lua language version to build") orelse .lua54;
const shared = b.option(bool, "shared", "Build shared library instead of static") orelse false;

const luau_use_4_vector = b.option(bool, "luau_use_4_vector", "Build Luau to use 4-vectors instead of the default 3-vector.") orelse false;
const upstream = b.dependency(@tagName(lang), .{});

// Zig module
@@ -37,10 +37,16 @@ pub fn build(b: *Build) void {
// Expose build configuration to the ziglua module
const config = b.addOptions();
config.addOption(Language, "lang", lang);
config.addOption(bool, "luau_use_4_vector", luau_use_4_vector);
ziglua.addOptions("config", config);

if (lang == .luau) {
const vector_size: usize = if (luau_use_4_vector) 4 else 3;
ziglua.addCMacro("LUA_VECTOR_SIZE", b.fmt("{}", .{vector_size}));
}

const lib = switch (lang) {
.luau => buildLuau(b, target, optimize, upstream, shared),
.luau => buildLuau(b, target, optimize, upstream, shared, luau_use_4_vector),
else => buildLua(b, target, optimize, upstream, lang, shared),
};

@@ -152,7 +158,7 @@ fn buildLua(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.Optim
}

/// Luau has diverged enough from Lua (C++, project structure, ...) that it is easier to separate the build logic
fn buildLuau(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, upstream: *Build.Dependency, shared: bool) *Step.Compile {
fn buildLuau(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, upstream: *Build.Dependency, shared: bool, luau_use_4_vector: bool) *Step.Compile {
const lib_opts = .{
.name = "luau",
.target = target,
@@ -174,6 +180,7 @@ fn buildLuau(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.Opti
"-DLUA_API=extern\"C\"",
"-DLUACODE_API=extern\"C\"",
"-DLUACODEGEN_API=extern\"C\"",
if (luau_use_4_vector) "-DLUA_VECTOR_SIZE=4" else "",
};

for (luau_source_files) |file| {
34 changes: 34 additions & 0 deletions src/libluau.zig
Original file line number Diff line number Diff line change
@@ -12,6 +12,9 @@ const c = @cImport({
const config = @import("config");
pub const lang = config.lang;

/// The length of Luau vector values, either 3 or 4.
pub const luau_vector_size = if (config.luau_use_4_vector) 4 else 3;

/// This function is defined in luau.cpp and must be called to define the assertion printer
extern "c" fn zig_registerAssertionHandler() void;

@@ -139,6 +142,7 @@ pub const LuaType = enum(i5) {
boolean = c.LUA_TBOOLEAN,
light_userdata = c.LUA_TLIGHTUSERDATA,
number = c.LUA_TNUMBER,
vector = c.LUA_TVECTOR,
string = c.LUA_TSTRING,
table = c.LUA_TTABLE,
function = c.LUA_TFUNCTION,
@@ -520,6 +524,11 @@ pub const Lua = struct {
return c.lua_isuserdata(lua.state, index) != 0;
}

/// Returns true if the value at the given index is a vector
pub fn isVector(lua: *Lua, index: i32) bool {
return c.lua_isvector(lua.state, index);
}

/// Returns true if the value at index1 is smaller than the value at index2, following the
/// semantics of the Lua < operator.
/// See https://www.lua.org/manual/5.4/manual.html#lua_lessthan
@@ -713,6 +722,17 @@ pub const Lua = struct {
c.lua_pushvalue(lua.state, index);
}

fn pushVector3(lua: *Lua, x: f32, y: f32, z: f32) void {
c.lua_pushvector(lua.state, x, y, z);
}

fn pushVector4(lua: *Lua, x: f32, y: f32, z: f32, w: f32) void {
c.lua_pushvector(lua.state, x, y, z, w);
}

/// Pushes a floating point 3-vector (or 4-vector if configured) `v` onto the stack
pub const pushVector = if (luau_vector_size == 3) pushVector3 else pushVector4;

/// Returns true if the two values in indices `index1` and `index2` are primitively equal
/// Bypasses __eq metamethods
/// Returns false if not equal, or if any index is invalid
@@ -927,6 +947,20 @@ pub const Lua = struct {
return error.Fail;
}

/// Converts the Lua value at the given `index` to a 3- or 4-vector.
/// The Lua value must be a vector.
pub fn toVector(lua: *Lua, index: i32) ![luau_vector_size]f32 {
const res = c.lua_tovector(lua.state, index);
if (res) |r| {
switch (luau_vector_size) {
3 => return [_]f32{ r[0], r[1], r[2] },
4 => return [_]f32{ r[0], r[1], r[2], r[3] },
else => @compileError("invalid luau_vector_size - should not happen"),
}
}
return error.Fail;
}

/// Returns the `LuaType` of the value at the given index
/// Note that this is equivalent to lua_type but because type is a Zig primitive it is renamed to `typeOf`
/// See https://www.lua.org/manual/5.1/manual.html#lua_type
69 changes: 69 additions & 0 deletions src/tests.zig
Original file line number Diff line number Diff line change
@@ -381,6 +381,10 @@ test "typenames" {
try expectEqualStrings("function", lua.typeName(.function));
try expectEqualStrings("userdata", lua.typeName(.userdata));
try expectEqualStrings("thread", lua.typeName(.thread));

if (ziglua.lang == .luau) {
try expectEqualStrings("vector", lua.typeName(.vector));
}
}

test "unsigned" {
@@ -2249,3 +2253,68 @@ test "tagged userdata" {
lua.pushInteger(13);
try expectError(error.Fail, lua.userdataTag(-1));
}

fn vectorCtor(l: *Lua) i32 {
const x = l.toNumber(1) catch unreachable;
const y = l.toNumber(2) catch unreachable;
const z = l.toNumber(3) catch unreachable;
if (ziglua.luau_vector_size == 4) {
const w = l.optNumber(4, 0);
l.pushVector(@floatCast(x), @floatCast(y), @floatCast(z), @floatCast(w));
} else {
l.pushVector(@floatCast(x), @floatCast(y), @floatCast(z));
}
return 1;
}

test "luau vectors" {
if (ziglua.lang != .luau) return;

var lua = try Lua.init(testing.allocator);
defer lua.deinit();
lua.openLibs();
lua.register("vector", ziglua.wrap(vectorCtor));

try lua.doString(
\\function test()
\\ local a = vector(1, 2, 3)
\\ local b = vector(4, 5, 6)
\\ local c = (a + b) * vector(2, 2, 2)
\\ return vector(c.x, c.y, c.z)
\\end
);
_ = try lua.getGlobal("test");
try lua.protectedCall(0, 1, 0);
var v = try lua.toVector(-1);
try testing.expectEqualSlices(f32, &[3]f32{ 10, 14, 18 }, v[0..3]);

if (ziglua.luau_vector_size == 3) lua.pushVector(1, 2, 3) else lua.pushVector(1, 2, 3, 4);
try expect(lua.isVector(-1));
v = try lua.toVector(-1);
const expected = if (ziglua.luau_vector_size == 3) [3]f32{ 1, 2, 3 } else [4]f32{ 1, 2, 3, 4 };
try expectEqual(expected, v);
try expectEqualStrings("vector", lua.typeNameIndex(-1));

lua.pushInteger(5);
try expect(!lua.isVector(-1));
}

test "luau 4-vectors" {
if (ziglua.lang != .luau) return;

var lua = try Lua.init(testing.allocator);
defer lua.deinit();
lua.openLibs();
lua.register("vector", ziglua.wrap(vectorCtor));

// More specific 4-vector tests
if (ziglua.luau_vector_size == 4) {
try lua.doString(
\\local a = vector(1, 2, 3, 4)
\\local b = vector(5, 6, 7, 8)
\\return a + b
);
const vec4 = try lua.toVector(-1);
try expectEqual([4]f32{ 6, 8, 10, 12 }, vec4);
}
}