luau/VM/src/lveclib.cpp
Petri Häkkinen 67e9d85124
Some checks are pending
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Waiting to run
build / macos (push) Waiting to run
build / macos-arm (push) Waiting to run
build / ubuntu (push) Waiting to run
build / windows (Win32) (push) Waiting to run
build / windows (x64) (push) Waiting to run
build / coverage (push) Waiting to run
build / web (push) Waiting to run
release / macos (push) Waiting to run
release / ubuntu (push) Waiting to run
release / windows (push) Waiting to run
release / web (push) Waiting to run
Add 2-component vector constructor (#1569)
Implement RFC: 2-component vector constructor. This includes 2-component
overload for `vector.create` and associated fastcall function, and its
type definition. These features are controlled by a new feature flag
`LuauVector2Constructor`. Additionally constant folding now supports two
components when `LuauVector2Constants` feature flag is set.

Note: this work does not include changes to CodeGen. Thus calls to
`vector.create` with only two arguments are not natively compiled
currently. This is left for future work.
2025-01-17 08:45:03 -08:00

352 lines
8.7 KiB
C++

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lualib.h"
#include "lcommon.h"
#include "lnumutils.h"
#include <math.h>
LUAU_FASTFLAGVARIABLE(LuauVectorMetatable)
LUAU_FASTFLAGVARIABLE(LuauVector2Constructor)
static int vector_create(lua_State* L)
{
// checking argument count to avoid accepting 'nil' as a valid value
int count = lua_gettop(L);
double x = luaL_checknumber(L, 1);
double y = luaL_checknumber(L, 2);
double z = FFlag::LuauVector2Constructor ? (count >= 3 ? luaL_checknumber(L, 3) : 0.0) : luaL_checknumber(L, 3);
#if LUA_VECTOR_SIZE == 4
double w = count >= 4 ? luaL_checknumber(L, 4) : 0.0;
lua_pushvector(L, float(x), float(y), float(z), float(w));
#else
lua_pushvector(L, float(x), float(y), float(z));
#endif
return 1;
}
static int vector_magnitude(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]));
#else
lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]));
#endif
return 1;
}
static int vector_normalize(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
float invSqrt = 1.0f / sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]);
lua_pushvector(L, v[0] * invSqrt, v[1] * invSqrt, v[2] * invSqrt, v[3] * invSqrt);
#else
float invSqrt = 1.0f / sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
lua_pushvector(L, v[0] * invSqrt, v[1] * invSqrt, v[2] * invSqrt);
#endif
return 1;
}
static int vector_cross(lua_State* L)
{
const float* a = luaL_checkvector(L, 1);
const float* b = luaL_checkvector(L, 2);
#if LUA_VECTOR_SIZE == 4
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], 0.0f);
#else
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]);
#endif
return 1;
}
static int vector_dot(lua_State* L)
{
const float* a = luaL_checkvector(L, 1);
const float* b = luaL_checkvector(L, 2);
#if LUA_VECTOR_SIZE == 4
lua_pushnumber(L, a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]);
#else
lua_pushnumber(L, a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);
#endif
return 1;
}
static int vector_angle(lua_State* L)
{
const float* a = luaL_checkvector(L, 1);
const float* b = luaL_checkvector(L, 2);
const float* axis = luaL_optvector(L, 3, nullptr);
// cross(a, b)
float cross[] = {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]};
double sinA = sqrt(cross[0] * cross[0] + cross[1] * cross[1] + cross[2] * cross[2]);
double cosA = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
double angle = atan2(sinA, cosA);
if (axis)
{
if (cross[0] * axis[0] + cross[1] * axis[1] + cross[2] * axis[2] < 0.0f)
angle = -angle;
}
lua_pushnumber(L, angle);
return 1;
}
static int vector_floor(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, floorf(v[0]), floorf(v[1]), floorf(v[2]), floorf(v[3]));
#else
lua_pushvector(L, floorf(v[0]), floorf(v[1]), floorf(v[2]));
#endif
return 1;
}
static int vector_ceil(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, ceilf(v[0]), ceilf(v[1]), ceilf(v[2]), ceilf(v[3]));
#else
lua_pushvector(L, ceilf(v[0]), ceilf(v[1]), ceilf(v[2]));
#endif
return 1;
}
static int vector_abs(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, fabsf(v[0]), fabsf(v[1]), fabsf(v[2]), fabsf(v[3]));
#else
lua_pushvector(L, fabsf(v[0]), fabsf(v[1]), fabsf(v[2]));
#endif
return 1;
}
static int vector_sign(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, luaui_signf(v[0]), luaui_signf(v[1]), luaui_signf(v[2]), luaui_signf(v[3]));
#else
lua_pushvector(L, luaui_signf(v[0]), luaui_signf(v[1]), luaui_signf(v[2]));
#endif
return 1;
}
static int vector_clamp(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
const float* min = luaL_checkvector(L, 2);
const float* max = luaL_checkvector(L, 3);
luaL_argcheck(L, min[0] <= max[0], 3, "max.x must be greater than or equal to min.x");
luaL_argcheck(L, min[1] <= max[1], 3, "max.y must be greater than or equal to min.y");
luaL_argcheck(L, min[2] <= max[2], 3, "max.z must be greater than or equal to min.z");
#if LUA_VECTOR_SIZE == 4
lua_pushvector(
L,
luaui_clampf(v[0], min[0], max[0]),
luaui_clampf(v[1], min[1], max[1]),
luaui_clampf(v[2], min[2], max[2]),
luaui_clampf(v[3], min[3], max[3])
);
#else
lua_pushvector(L, luaui_clampf(v[0], min[0], max[0]), luaui_clampf(v[1], min[1], max[1]), luaui_clampf(v[2], min[2], max[2]));
#endif
return 1;
}
static int vector_min(lua_State* L)
{
int n = lua_gettop(L);
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
float result[] = {v[0], v[1], v[2], v[3]};
#else
float result[] = {v[0], v[1], v[2]};
#endif
for (int i = 2; i <= n; i++)
{
const float* b = luaL_checkvector(L, i);
if (b[0] < result[0])
result[0] = b[0];
if (b[1] < result[1])
result[1] = b[1];
if (b[2] < result[2])
result[2] = b[2];
#if LUA_VECTOR_SIZE == 4
if (b[3] < result[3])
result[3] = b[3];
#endif
}
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, result[0], result[1], result[2], result[3]);
#else
lua_pushvector(L, result[0], result[1], result[2]);
#endif
return 1;
}
static int vector_max(lua_State* L)
{
int n = lua_gettop(L);
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
float result[] = {v[0], v[1], v[2], v[3]};
#else
float result[] = {v[0], v[1], v[2]};
#endif
for (int i = 2; i <= n; i++)
{
const float* b = luaL_checkvector(L, i);
if (b[0] > result[0])
result[0] = b[0];
if (b[1] > result[1])
result[1] = b[1];
if (b[2] > result[2])
result[2] = b[2];
#if LUA_VECTOR_SIZE == 4
if (b[3] > result[3])
result[3] = b[3];
#endif
}
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, result[0], result[1], result[2], result[3]);
#else
lua_pushvector(L, result[0], result[1], result[2]);
#endif
return 1;
}
static int vector_index(lua_State* L)
{
LUAU_ASSERT(FFlag::LuauVectorMetatable);
const float* v = luaL_checkvector(L, 1);
size_t namelen = 0;
const char* name = luaL_checklstring(L, 2, &namelen);
// field access implementation mirrors the fast-path we have in the VM
if (namelen == 1)
{
int ic = (name[0] | ' ') - 'x';
#if LUA_VECTOR_SIZE == 4
// 'w' is before 'x' in ascii, so ic is -1 when indexing with 'w'
if (ic == -1)
ic = 3;
#endif
if (unsigned(ic) < LUA_VECTOR_SIZE)
{
lua_pushnumber(L, v[ic]);
return 1;
}
}
luaL_error(L, "attempt to index vector with '%s'", name);
}
static const luaL_Reg vectorlib[] = {
{"create", vector_create},
{"magnitude", vector_magnitude},
{"normalize", vector_normalize},
{"cross", vector_cross},
{"dot", vector_dot},
{"angle", vector_angle},
{"floor", vector_floor},
{"ceil", vector_ceil},
{"abs", vector_abs},
{"sign", vector_sign},
{"clamp", vector_clamp},
{"max", vector_max},
{"min", vector_min},
{NULL, NULL},
};
static void createmetatable(lua_State* L)
{
LUAU_ASSERT(FFlag::LuauVectorMetatable);
lua_createtable(L, 0, 1); // create metatable for vectors
// push dummy vector
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, 0.0f, 0.0f, 0.0f, 0.0f);
#else
lua_pushvector(L, 0.0f, 0.0f, 0.0f);
#endif
lua_pushvalue(L, -2);
lua_setmetatable(L, -2); // set vector metatable
lua_pop(L, 1); // pop dummy vector
lua_pushcfunction(L, vector_index, nullptr);
lua_setfield(L, -2, "__index");
lua_setreadonly(L, -1, true);
lua_pop(L, 1); // pop the metatable
}
int luaopen_vector(lua_State* L)
{
luaL_register(L, LUA_VECLIBNAME, vectorlib);
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, 0.0f, 0.0f, 0.0f, 0.0f);
lua_setfield(L, -2, "zero");
lua_pushvector(L, 1.0f, 1.0f, 1.0f, 1.0f);
lua_setfield(L, -2, "one");
#else
lua_pushvector(L, 0.0f, 0.0f, 0.0f);
lua_setfield(L, -2, "zero");
lua_pushvector(L, 1.0f, 1.0f, 1.0f);
lua_setfield(L, -2, "one");
#endif
if (FFlag::LuauVectorMetatable)
createmetatable(L);
return 1;
}