diff --git a/doc/en/scripting.md b/doc/en/scripting.md index 18b1e762..2f815077 100644 --- a/doc/en/scripting.md +++ b/doc/en/scripting.md @@ -7,6 +7,8 @@ Subsections: - [User input](scripting/user-input.md) - [Filesystem and serialization](scripting/filesystem.md) - [UI properties and methods](scripting/ui.md) +- [Libraries](#) + - [mat4](scripting/builtins/libmat4.md) - [Module core:bit_converter](scripting/modules/core_bit_converter.md) - [Module core:data_buffer](scripting/modules/core_data_buffer.md) - [Module core:vector2, core:vector3](scripting/modules/core_vector2_vector3.md) diff --git a/doc/en/scripting/builtins/libmat4.md b/doc/en/scripting/builtins/libmat4.md new file mode 100644 index 00000000..4327fc34 --- /dev/null +++ b/doc/en/scripting/builtins/libmat4.md @@ -0,0 +1,100 @@ +# *mat4* library + +*mat4* contains a set of functions for work with transformation 4x4 matrices. + +Most functions have several options for argument lists (overloads). + +## Data types + +Type conventions will be used on this page. +- vector - an array of three or four numbers +- vec3 - array of three numbers +- matrix - array of 16 numbers - matrix + +>[!ATTENTION] +> Type annotations are part of the documentation and are not present in Lua. + +## Identity matrix - *mat4.idt(...)* + +```lua +-- creates an identity matrix +mat4.idt() + +-- writes an identity matrix to dst +mat4.idt(dst: matrix) +``` + +## Matrix multiplication - *mat4.mul(...)* + +```lua +-- returns the result of matrix multiplication +mat4.mul(a: matrix, b: matrix) +-- writes the result of matrix multiplication to dst +mat4.mul(a: matrix, b: matrix, dst: matrix) + +-- returns the result of multiplying a matrix and a vector +mat4.mul(a: matrix, v: vector) +-- writes the result of matrix and vector multiplication to dst +mat4.mul(a: matrix, v: vector, dst: vector) +``` + +## Matrix inversion - *mat4.inverse(...)* + +```lua +-- returns the result of matrix inversion +mat4.inverse(m: matrix) +-- writes the result of matrix inversion to dst +mat4.inverse(m: matrix, dst: matrix) +``` + +## Matrix transposition - *mat4.transpose(...)* + +```lua +-- returns the result of matrix transposition +mat4.transpose(m: matrix) +-- writes the result of matrix transposition to dst +mat4.transpose(m: matrix, dst: matrix) +``` + +## Offset - *mat4.translate(...)* + +```lua +-- creates a translation matrix +mat4.translate(translation: vec3) +-- returns the result of applying a translation to matrix m +mat4.translate(m: matrix, translation: vec3) +-- writes the result of applying a translation to matrix m to dst +mat4.translate(m: matrix, translation: vec3, dst: matrix) +``` +## Scaling - *mat4.scale(...)* + +```lua +-- creates a scaling matrix +mat4.scale(scale: vec3) +-- returns the result of applying scaling to matrix m +mat4.scale(m: matrix, scale: vec3) +-- writes the result of applying scaling to matrix m to dst +mat4.scale(m: matrix, scale: vec3, dst: matrix) +``` + +## Rotation - *mat4.rotate(...)* + +The angle of rotation is indicated in degrees. + +```lua +-- creates a rotation matrix (angle - rotation angle) along a given axis (axis is an unit vector) +mat4.rotate(axis: vec3, angle: number) +-- returns the result of applying rotation to matrix m +mat4.rotate(m: matrix, axis: vec3, angle: number) +-- writes the result of applying rotation to matrix m to dst +mat4.rotate(m: matrix, axis: vec3, angle: number, dst: matrix) +``` + +## Translation to string - *mat4.tostring(...)* + +```lua +-- returns a string representing the contents of the matrix +mat4.tostring(m: matrix) +-- returns a string representing the contents of the matrix, human-readable if multiline = true +mat4.tostring(m: matrix, multiline: bool) +``` diff --git a/doc/ru/scripting.md b/doc/ru/scripting.md index 80a6f3c6..2335431e 100644 --- a/doc/ru/scripting.md +++ b/doc/ru/scripting.md @@ -7,6 +7,8 @@ - [Пользовательский ввод](scripting/user-input.md) - [Файловая система и сериализация](scripting/filesystem.md) - [Свойства и методы UI элементов](scripting/ui.md) +- [Библиотеки](#) + - [mat4](scripting/builtins/libmat4.md) - [Модуль core:bit_converter](scripting/modules/core_bit_converter.md) - [Модуль core:data_buffer](scripting/modules/core_data_buffer.md) - [Модули core:vector2, core:vector3](scripting/modules/core_vector2_vector3.md) diff --git a/doc/ru/scripting/builtins/libmat4.md b/doc/ru/scripting/builtins/libmat4.md new file mode 100644 index 00000000..9be91165 --- /dev/null +++ b/doc/ru/scripting/builtins/libmat4.md @@ -0,0 +1,100 @@ +# Библиотека *mat4* + +*mat4* содержит набор функций для работы с матрицами трансформации размерностью 4x4. + +Большинство функций имеют несколько вариантов списка агрументов (перегрузок). + +## Типы данных + +На данной странице будут использоваться условные обозначения типов. +- vector - массив из трех или четырех чисел +- vec3 - массив из трех чисел +- matrix - массив из 16 чисел - матрица + +>[!ATTENTION] +> Аннотации типов являются частью документации и не указываются при вызове использовании. + +## Единичная матрица - *mat4.idt(...)* + +```lua +-- создает единичную матрицу +mat4.idt() + +-- записывает единичную матрицу в dst +mat4.idt(dst: matrix) +``` + +## Умножение матриц - *mat4.mul(...)* + +```lua +-- возвращает результат умножения матриц +mat4.mul(a: matrix, b: matrix) +-- записывает результат умножения матриц в dst +mat4.mul(a: matrix, b: matrix, dst: matrix) + +-- возвращает результат умножения матрицы и вектора +mat4.mul(a: matrix, v: vector) +-- записывает результат умножения матрицы и вектора в dst +mat4.mul(a: matrix, v: vector, dst: vector) +``` + +## Инверсия матрицы - *mat4.inverse(...)* + +```lua +-- возвращает результат инверсии матрицы +mat4.inverse(m: matrix) +-- записывает результат инверсии матрицы в dst +mat4.inverse(m: matrix, dst: matrix) +``` + +## Транспонирование матрицы - *mat4.transpose(...)* + +```lua +-- возвращает результат транспонирования матрицы +mat4.transpose(m: matrix) +-- записывает результат транспонирования матрицы в dst +mat4.transpose(m: matrix, dst: matrix) +``` + +## Смещение - *mat4.translate(...)* + +```lua +-- создает матрицу смещения +mat4.translate(translation: vec3) +-- возвращает результат применения смещения к матрице m +mat4.translate(m: matrix, translation: vec3) +-- записывает результат применения смещения к матрице m в dst +mat4.translate(m: matrix, translation: vec3, dst: matrix) +``` +## Масштабирование - *mat4.scale(...)* + +```lua +-- создает матрицу масштабирования +mat4.scale(scale: vec3) +-- возвращает результат применения масштабирования к матрице m +mat4.scale(m: matrix, scale: vec3) +-- записывает результат применения масштабирования к матрице m в dst +mat4.scale(m: matrix, scale: vec3, dst: matrix) +``` + +## Вращение - *mat4.rotate(...)* + +Угол поворота (angle) указывается в градусах. + +```lua +-- создает матрицу поворота (angle - угол поворота) по заданной оси (axis - единичный вектор) +mat4.rotate(axis: vec3, angle: number) +-- возвращает результат применения вращения к матрице m +mat4.rotate(m: matrix, axis: vec3, angle: number) +-- записывает результат применения вращения к матрице m в dst +mat4.rotate(m: matrix, axis: vec3, angle: number, dst: matrix) +``` + +## Перевод в строку - *mat4.tostring(...)* + +```lua +-- возвращает строку представляющую содержимое матрицы +mat4.tostring(m: matrix) +-- возвращает строку представляющую содержимое матрицы, многострочную, если multiline = true +mat4.tostring(m: matrix, multiline: bool) +``` diff --git a/src/logic/scripting/lua/api_lua.hpp b/src/logic/scripting/lua/api_lua.hpp index 81c20644..83130ec2 100644 --- a/src/logic/scripting/lua/api_lua.hpp +++ b/src/logic/scripting/lua/api_lua.hpp @@ -17,6 +17,7 @@ extern const luaL_Reg inputlib []; extern const luaL_Reg inventorylib []; extern const luaL_Reg itemlib []; extern const luaL_Reg jsonlib []; +extern const luaL_Reg mat4lib []; extern const luaL_Reg packlib []; extern const luaL_Reg playerlib []; extern const luaL_Reg timelib []; diff --git a/src/logic/scripting/lua/libmat4.cpp b/src/logic/scripting/lua/libmat4.cpp new file mode 100644 index 00000000..97e9ae82 --- /dev/null +++ b/src/logic/scripting/lua/libmat4.cpp @@ -0,0 +1,199 @@ +#include "api_lua.hpp" + +#include +#include + +/// Overloads: +/// mat4.idt() -> float[16] - creates identity matrix +/// mat4.idt(dst: float[16]) -> float[16] - sets dst to identity matrix +static int l_idt(lua::State* L) { + uint argc = lua::gettop(L); + switch (argc) { + case 0: { + return lua::pushmat4(L, glm::mat4(1.0f)); + } + case 1: { + return lua::setmat4(L, 1, glm::mat4(1.0f)); + } + default: { + throw std::runtime_error("invalid arguments number (0 or 1 expected)"); + } + } + return 0; +} + +/// Overloads: +/// mat4.mul(m1: float[16], m2: float[16]) -> float[16] - creates matrix of m1 and m2 multiplication result +/// mat4.mul(m1: float[16], m2: float[16], dst: float[16]) -> float[16] - updates dst matrix with m1 and m2 multiplication result +/// mat4.mul(m1: float[16], v: float[3 or 4]) -> float[3 or 4] - creates vector of m1 and v multiplication result +/// mat4.mul(m1: float[16], v: float[3 or 4], dst: float[3 or 4]) -> float[3 or 4] - updates dst vector with m1 and v multiplication result +static int l_mul(lua::State* L) { + uint argc = lua::gettop(L); + if (argc < 2 || argc > 3) { + throw std::runtime_error("invalid arguments number (2 or 3 expected)"); + } + auto matrix1 = lua::tomat4(L, 1); + uint len2 = lua::objlen(L, 2); + if (len2 < 3) { + throw std::runtime_error("argument #2: vec3 or vec4 expected"); + } + switch (argc) { + case 2: { + if (len2 == 4) { + return lua::pushvec4(L, matrix1 * lua::tovec4(L, 2)); + } else if (len2 == 3) { + return lua::pushvec3(L, matrix1 * glm::vec4(lua::tovec3(L, 2), 1.0f)); + } + return lua::pushmat4(L, matrix1 * lua::tomat4(L, 2)); + } + case 3: { + if (len2 == 4) { + return lua::setvec4(L, 3, matrix1 * lua::tovec4(L, 2)); + } else if (len2 == 3) { + return lua::setvec3(L, 3, matrix1 * glm::vec4(lua::tovec3(L, 2), 1.0f)); + } + return lua::setmat4(L, 3, matrix1 * lua::tomat4(L, 2)); + } + } + return 0; +} + +/// Overloads: +/// mat4.(vec: float[3]) -> float[16] - creates transform matrix +/// mat4.(matrix: float[16], vec: float[3]) -> float[16] - creates transformed copy of matrix +/// mat4.(matrix: float[16], vec: float[3], dst: float[16]) -> sets dst to transformed version of matrix +template +inline int l_transform_func(lua::State* L) { + uint argc = lua::gettop(L); + switch (argc) { + case 1: { + auto vec = lua::tovec3(L, 1); + return lua::pushmat4(L, func(glm::mat4(1.0f), vec)); + } + case 2: { + auto matrix = lua::tomat4(L, 1); + auto vec = lua::tovec3(L, 2); + return lua::pushmat4(L, func(matrix, vec)); + } + case 3: { + auto matrix = lua::tomat4(L, 1); + auto vec = lua::tovec3(L, 2); + return lua::setmat4(L, 3, func(matrix, vec)); + } + default: { + throw std::runtime_error("invalid arguments number (1, 2 or 3 expected)"); + } + } + return 0; +} + +/// Overloads: +/// mat4.rotate(vec: float[3], angle: float) -> float[16] - creates rotation matrix +/// mat4.rotate(matrix: float[16], vec: float[3], angle: float) -> float[16] - creates rotated copy of matrix +/// mat4.rotate(matrix: float[16], vec: float[3], angle: float, dst: float[16]) -> sets dst to rotated version of matrix +inline int l_rotate(lua::State* L) { + uint argc = lua::gettop(L); + switch (argc) { + case 2: { + auto vec = lua::tovec3(L, 1); + auto angle = glm::radians(static_cast(lua::tonumber(L, 2))); + return lua::pushmat4(L, glm::rotate(glm::mat4(1.0f), angle, vec)); + } + case 3: { + auto matrix = lua::tomat4(L, 1); + auto vec = lua::tovec3(L, 2); + auto angle = glm::radians(static_cast(lua::tonumber(L, 3))); + return lua::pushmat4(L, glm::rotate(matrix, angle, vec)); + } + case 4: { + auto matrix = lua::tomat4(L, 1); + auto vec = lua::tovec3(L, 2); + auto angle = glm::radians(static_cast(lua::tonumber(L, 3))); + return lua::setmat4(L, 3, glm::rotate(matrix, angle, vec)); + } + default: { + throw std::runtime_error("invalid arguments number (2, 3 or 4 expected)"); + } + } + return 0; +} + +/// Overloads: +/// mat4.inverse(matrix: float[16]) -> float[16] - creates inversed version of the matrix +/// mat4.inverse(matrix: float[16], dst: float[16]) -> float[16] - updates dst matrix with inversed version of the matrix +static int l_inverse(lua::State* L) { + uint argc = lua::gettop(L); + auto matrix = lua::tomat4(L, 1); + switch (argc) { + case 1: { + return lua::pushmat4(L, glm::inverse(matrix)); + } + case 2: { + return lua::setmat4(L, 2, glm::inverse(matrix)); + } + default: { + throw std::runtime_error("invalid arguments number (1 or 2 expected)"); + } + } +} + +/// Overloads: +/// mat4.transpose(matrix: float[16]) -> float[16] - creates transposed version of the matrix +/// mat4.transpose(matrix: float[16], dst: float[16]) -> float[16] - updates dst matrix with transposed version of the matrix +static int l_transpose(lua::State* L) { + uint argc = lua::gettop(L); + auto matrix = lua::tomat4(L, 1); + switch (argc) { + case 1: { + return lua::pushmat4(L, glm::transpose(matrix)); + } + case 2: { + return lua::setmat4(L, 2, glm::transpose(matrix)); + } + default: { + throw std::runtime_error("invalid arguments number (1 or 2 expected)"); + } + } + return 0; +} + +static int l_tostring(lua::State* L) { + auto matrix = lua::tomat4(L, 1); + bool multiline = lua::toboolean(L, 2); + std::stringstream ss; + ss << "mat4 {"; + if (multiline) { + ss << "\n"; + } + for (uint y = 0; y < 4; y++) { + for (uint x = 0; x < 4; x++) { + if (multiline) { + ss << "\t" << matrix[y][x]; + } else if (x > 0) { + ss << " " << matrix[y][x]; + } else { + ss << matrix[y][x]; + } + } + if (multiline) { + ss << "\n"; + } else { + ss << "; "; + } + } + ss << "}"; + return lua::pushstring(L, ss.str()); +} + +const luaL_Reg mat4lib [] = { + {"idt", lua::wrap}, + {"mul", lua::wrap}, + {"scale", lua::wrap>}, + {"rotate", lua::wrap}, + {"translate", lua::wrap>}, + {"inverse", lua::wrap}, + {"transpose", lua::wrap}, + {"tostring", lua::wrap}, + {NULL, NULL} +}; + diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 75ef541f..a64216a6 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -37,6 +37,7 @@ static void create_libs(lua::State* L) { openlib(L, "inventory", inventorylib); openlib(L, "item", itemlib); openlib(L, "json", jsonlib); + openlib(L, "mat4", mat4lib); openlib(L, "pack", packlib); openlib(L, "player", playerlib); openlib(L, "time", timelib); diff --git a/src/logic/scripting/lua/lua_util.hpp b/src/logic/scripting/lua/lua_util.hpp index af58ea11..a774d283 100644 --- a/src/logic/scripting/lua/lua_util.hpp +++ b/src/logic/scripting/lua/lua_util.hpp @@ -138,7 +138,10 @@ namespace lua { inline void setmetatable(lua::State* L, int idx=-2) { lua_setmetatable(L, idx); } - + inline int pushvalue(lua::State* L, int idx) { + lua_pushvalue(L, idx); + return 1; + } inline int pushvec2_arr(lua::State* L, glm::vec2 vec) { createtable(L, 2, 0); getglobal(L, "vec2_mt"); @@ -195,6 +198,47 @@ namespace lua { rawseti(L, 4); return 1; } + inline int pushmat4(lua::State* L, glm::mat4 matrix) { + createtable(L, 16, 0); + for (uint y = 0; y < 4; y++) { + for (uint x = 0; x < 4; x++) { + uint i = y * 4 + x; + pushnumber(L, matrix[y][x]); + rawseti(L, i+1); + } + } + return 1; + } + /// @brief pushes matrix table to the stack and updates it with glm matrix + inline int setmat4(lua::State* L, int idx, glm::mat4 matrix) { + pushvalue(L, idx); + for (uint y = 0; y < 4; y++) { + for (uint x = 0; x < 4; x++) { + uint i = y * 4 + x; + pushnumber(L, matrix[y][x]); + rawseti(L, i+1); + } + } + return 1; + } + /// @brief pushes vector table to the stack and updates it with glm vec4 + inline int setvec4(lua::State* L, int idx, glm::vec4 vec) { + pushvalue(L, idx); + for (uint i = 0; i < 4; i++) { + pushnumber(L, vec[i]); + rawseti(L, i+1); + } + return 1; + } + /// @brief pushes vector table to the stack and updates it with glm vec3 + inline int setvec3(lua::State* L, int idx, glm::vec3 vec) { + pushvalue(L, idx); + for (uint i = 0; i < 3; i++) { + pushnumber(L, vec[i]); + rawseti(L, i+1); + } + return 1; + } inline int pushcfunction(lua::State* L, lua_CFunction func) { lua_pushcfunction(L, func); return 1; @@ -216,10 +260,6 @@ namespace lua { lua_pushboolean(L, value); return 1; } - inline int pushvalue(lua::State* L, int idx) { - lua_pushvalue(L, idx); - return 1; - } inline int pushglobals(lua::State* L) { return pushvalue(L, LUA_GLOBALSINDEX); } @@ -309,6 +349,53 @@ namespace lua { pop(L); return glm::vec2(x, y); } + inline glm::vec3 tovec3(lua::State* L, int idx) { + pushvalue(L, idx); + if (!istable(L, idx) || objlen(L, idx) < 3) { + throw std::runtime_error("value must be an array of three numbers"); + } + rawgeti(L, 1); + auto x = tonumber(L, -1); pop(L); + rawgeti(L, 2); + auto y = tonumber(L, -1); pop(L); + rawgeti(L, 3); + auto z = tonumber(L, -1); pop(L); + pop(L); + return glm::vec3(x, y, z); + } + inline glm::vec4 tovec4(lua::State* L, int idx) { + pushvalue(L, idx); + if (!istable(L, idx) || objlen(L, idx) < 4) { + throw std::runtime_error("value must be an array of four numbers"); + } + rawgeti(L, 1); + auto x = tonumber(L, -1); pop(L); + rawgeti(L, 2); + auto y = tonumber(L, -1); pop(L); + rawgeti(L, 3); + auto z = tonumber(L, -1); pop(L); + rawgeti(L, 4); + auto w = tonumber(L, -1); pop(L); + pop(L); + return glm::vec4(x, y, z, w); + } + inline glm::mat4 tomat4(lua::State* L, int idx) { + pushvalue(L, idx); + if (!istable(L, idx) || objlen(L, idx) < 16) { + throw std::runtime_error("value must be an array of 16 numbers"); + } + glm::mat4 matrix; + for (uint y = 0; y < 4; y++) { + for (uint x = 0; x < 4; x++) { + uint i = y * 4 + x; + rawgeti(L, i+1); + matrix[y][x] = static_cast(tonumber(L, -1)); + pop(L); + } + } + pop(L); + return matrix; + } inline glm::vec4 tocolor(lua::State* L, int idx) { pushvalue(L, idx);