mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-17 01:10:10 +00:00
3D transforms in Lua
This commit is contained in:
parent
5794a3b346
commit
48538d5cef
@ -25,7 +25,7 @@ namespace MWLua
|
||||
{
|
||||
auto* lua = context.mLua;
|
||||
sol::table api(lua->sol(), sol::create);
|
||||
api["API_REVISION"] = 6;
|
||||
api["API_REVISION"] = 7;
|
||||
api["quit"] = [lua]()
|
||||
{
|
||||
std::string traceback = lua->sol()["debug"]["traceback"]().get<std::string>();
|
||||
|
@ -10,30 +10,41 @@ namespace
|
||||
{
|
||||
using namespace testing;
|
||||
|
||||
template <typename T>
|
||||
T get(sol::state& lua, std::string luaCode)
|
||||
{
|
||||
return lua.safe_script("return " + luaCode).get<T>();
|
||||
}
|
||||
|
||||
std::string getAsString(sol::state& lua, std::string luaCode)
|
||||
{
|
||||
return LuaUtil::toString(lua.safe_script("return " + luaCode));
|
||||
}
|
||||
|
||||
TEST(LuaUtilPackageTest, Vector2)
|
||||
{
|
||||
sol::state lua;
|
||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
||||
lua.safe_script("v = util.vector2(3, 4)");
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return v.x").get<float>(), 3);
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return v.y").get<float>(), 4);
|
||||
EXPECT_EQ(lua.safe_script("return tostring(v)").get<std::string>(), "(3, 4)");
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return v:length()").get<float>(), 5);
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return v:length2()").get<float>(), 25);
|
||||
EXPECT_FALSE(lua.safe_script("return util.vector2(1, 2) == util.vector2(1, 3)").get<bool>());
|
||||
EXPECT_TRUE(lua.safe_script("return util.vector2(1, 2) + util.vector2(2, 5) == util.vector2(3, 7)").get<bool>());
|
||||
EXPECT_TRUE(lua.safe_script("return util.vector2(1, 2) - util.vector2(2, 5) == -util.vector2(1, 3)").get<bool>());
|
||||
EXPECT_TRUE(lua.safe_script("return util.vector2(1, 2) == util.vector2(2, 4) / 2").get<bool>());
|
||||
EXPECT_TRUE(lua.safe_script("return util.vector2(1, 2) * 2 == util.vector2(2, 4)").get<bool>());
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return util.vector2(3, 2) * v").get<float>(), 17);
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return util.vector2(3, 2):dot(v)").get<float>(), 17);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 3);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 4);
|
||||
EXPECT_EQ(get<std::string>(lua, "tostring(v)"), "(3, 4)");
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v:length()"), 5);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v:length2()"), 25);
|
||||
EXPECT_FALSE(get<bool>(lua, "util.vector2(1, 2) == util.vector2(1, 3)"));
|
||||
EXPECT_TRUE(get<bool>(lua, "util.vector2(1, 2) + util.vector2(2, 5) == util.vector2(3, 7)"));
|
||||
EXPECT_TRUE(get<bool>(lua, "util.vector2(1, 2) - util.vector2(2, 5) == -util.vector2(1, 3)"));
|
||||
EXPECT_TRUE(get<bool>(lua, "util.vector2(1, 2) == util.vector2(2, 4) / 2"));
|
||||
EXPECT_TRUE(get<bool>(lua, "util.vector2(1, 2) * 2 == util.vector2(2, 4)"));
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.vector2(3, 2) * v"), 17);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.vector2(3, 2):dot(v)"), 17);
|
||||
EXPECT_ERROR(lua.safe_script("v2, len = v.normalize()"), "value is not a valid userdata"); // checks that it doesn't segfault
|
||||
lua.safe_script("v2, len = v:normalize()");
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return len").get<float>(), 5);
|
||||
EXPECT_TRUE(lua.safe_script("return v2 == util.vector2(3/5, 4/5)").get<bool>());
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "len"), 5);
|
||||
EXPECT_TRUE(get<bool>(lua, "v2 == util.vector2(3/5, 4/5)"));
|
||||
lua.safe_script("_, len = util.vector2(0, 0):normalize()");
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return len").get<float>(), 0);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "len"), 0);
|
||||
}
|
||||
|
||||
TEST(LuaUtilPackageTest, Vector3)
|
||||
@ -42,27 +53,59 @@ namespace
|
||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
||||
lua.safe_script("v = util.vector3(5, 12, 13)");
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return v.x").get<float>(), 5);
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return v.y").get<float>(), 12);
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return v.z").get<float>(), 13);
|
||||
EXPECT_EQ(lua.safe_script("return tostring(v)").get<std::string>(), "(5, 12, 13)");
|
||||
EXPECT_EQ(LuaUtil::toString(lua.safe_script("return v")), "(5, 12, 13)");
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return util.vector3(4, 0, 3):length()").get<float>(), 5);
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return util.vector3(4, 0, 3):length2()").get<float>(), 25);
|
||||
EXPECT_FALSE(lua.safe_script("return util.vector3(1, 2, 3) == util.vector3(1, 3, 2)").get<bool>());
|
||||
EXPECT_TRUE(lua.safe_script("return util.vector3(1, 2, 3) + util.vector3(2, 5, 1) == util.vector3(3, 7, 4)").get<bool>());
|
||||
EXPECT_TRUE(lua.safe_script("return util.vector3(1, 2, 3) - util.vector3(2, 5, 1) == -util.vector3(1, 3, -2)").get<bool>());
|
||||
EXPECT_TRUE(lua.safe_script("return util.vector3(1, 2, 3) == util.vector3(2, 4, 6) / 2").get<bool>());
|
||||
EXPECT_TRUE(lua.safe_script("return util.vector3(1, 2, 3) * 2 == util.vector3(2, 4, 6)").get<bool>());
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return util.vector3(3, 2, 1) * v").get<float>(), 5*3 + 12*2 + 13*1);
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return util.vector3(3, 2, 1):dot(v)").get<float>(), 5*3 + 12*2 + 13*1);
|
||||
EXPECT_TRUE(lua.safe_script("return util.vector3(1, 0, 0) ^ util.vector3(0, 1, 0) == util.vector3(0, 0, 1)").get<bool>());
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 5);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 12);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.z"), 13);
|
||||
EXPECT_EQ(get<std::string>(lua, "tostring(v)"), "(5, 12, 13)");
|
||||
EXPECT_EQ(getAsString(lua, "v"), "(5, 12, 13)");
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.vector3(4, 0, 3):length()"), 5);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.vector3(4, 0, 3):length2()"), 25);
|
||||
EXPECT_FALSE(get<bool>(lua, "util.vector3(1, 2, 3) == util.vector3(1, 3, 2)"));
|
||||
EXPECT_TRUE(get<bool>(lua, "util.vector3(1, 2, 3) + util.vector3(2, 5, 1) == util.vector3(3, 7, 4)"));
|
||||
EXPECT_TRUE(get<bool>(lua, "util.vector3(1, 2, 3) - util.vector3(2, 5, 1) == -util.vector3(1, 3, -2)"));
|
||||
EXPECT_TRUE(get<bool>(lua, "util.vector3(1, 2, 3) == util.vector3(2, 4, 6) / 2"));
|
||||
EXPECT_TRUE(get<bool>(lua, "util.vector3(1, 2, 3) * 2 == util.vector3(2, 4, 6)"));
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.vector3(3, 2, 1) * v"), 5*3 + 12*2 + 13*1);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.vector3(3, 2, 1):dot(v)"), 5*3 + 12*2 + 13*1);
|
||||
EXPECT_TRUE(get<bool>(lua, "util.vector3(1, 0, 0) ^ util.vector3(0, 1, 0) == util.vector3(0, 0, 1)"));
|
||||
EXPECT_ERROR(lua.safe_script("v2, len = util.vector3(3, 4, 0).normalize()"), "value is not a valid userdata");
|
||||
lua.safe_script("v2, len = util.vector3(3, 4, 0):normalize()");
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return len").get<float>(), 5);
|
||||
EXPECT_TRUE(lua.safe_script("return v2 == util.vector3(3/5, 4/5, 0)").get<bool>());
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "len"), 5);
|
||||
EXPECT_TRUE(get<bool>(lua, "v2 == util.vector3(3/5, 4/5, 0)"));
|
||||
lua.safe_script("_, len = util.vector3(0, 0, 0):normalize()");
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return len").get<float>(), 0);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "len"), 0);
|
||||
}
|
||||
|
||||
TEST(LuaUtilPackageTest, Transform)
|
||||
{
|
||||
sol::state lua;
|
||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
||||
lua["T"] = lua["util"]["transform"];
|
||||
lua["v"] = lua["util"]["vector3"];
|
||||
EXPECT_ERROR(lua.safe_script("T.identity = nil"), "attempt to index");
|
||||
EXPECT_EQ(getAsString(lua, "T.identity * v(3, 4, 5)"), "(3, 4, 5)");
|
||||
EXPECT_EQ(getAsString(lua, "T.move(1, 2, 3) * v(3, 4, 5)"), "(4, 6, 8)");
|
||||
EXPECT_EQ(getAsString(lua, "T.scale(1, -2, 3) * v(3, 4, 5)"), "(3, -8, 15)");
|
||||
EXPECT_EQ(getAsString(lua, "T.scale(v(1, 2, 3)) * v(3, 4, 5)"), "(3, 8, 15)");
|
||||
lua.safe_script("moveAndScale = T.move(v(1, 2, 3)) * T.scale(0.5, 1, 0.5) * T.move(10, 20, 30)");
|
||||
EXPECT_EQ(getAsString(lua, "moveAndScale * v(0, 0, 0)"), "(6, 22, 18)");
|
||||
EXPECT_EQ(getAsString(lua, "moveAndScale * v(300, 200, 100)"), "(156, 222, 68)");
|
||||
EXPECT_EQ(getAsString(lua, "moveAndScale"), "TransformM{ move(6, 22, 18) scale(0.5, 1, 0.5) }");
|
||||
EXPECT_EQ(getAsString(lua, "T.identity"), "TransformM{ }");
|
||||
lua.safe_script("rx = T.rotateX(math.pi / 2)");
|
||||
lua.safe_script("ry = T.rotateY(math.pi / 2)");
|
||||
lua.safe_script("rz = T.rotateZ(math.pi / 2)");
|
||||
EXPECT_LT(get<float>(lua, "(rx * v(1, 2, 3) - v(1, -3, 2)):length()"), 1e-6);
|
||||
EXPECT_LT(get<float>(lua, "(ry * v(1, 2, 3) - v(3, 2, -1)):length()"), 1e-6);
|
||||
EXPECT_LT(get<float>(lua, "(rz * v(1, 2, 3) - v(-2, 1, 3)):length()"), 1e-6);
|
||||
lua.safe_script("rot = T.rotate(math.pi / 2, v(-1, -1, 0)) * T.rotateZ(-math.pi / 4)");
|
||||
EXPECT_THAT(getAsString(lua, "rot"), HasSubstr("TransformQ"));
|
||||
EXPECT_LT(get<float>(lua, "(rot * v(1, 0, 0) - v(0, 0, 1)):length()"), 1e-6);
|
||||
EXPECT_LT(get<float>(lua, "(rot * rot:inverse() * v(1, 0, 0) - v(1, 0, 0)):length()"), 1e-6);
|
||||
lua.safe_script("rz_move_rx = rz * T.move(0, 3, 0) * rx");
|
||||
EXPECT_LT(get<float>(lua, "(rz_move_rx * v(1, 2, 3) - v(0, 1, 2)):length()"), 1e-6);
|
||||
EXPECT_LT(get<float>(lua, "(rz_move_rx:inverse() * v(0, 1, 2) - v(1, 2, 3)):length()"), 1e-6);
|
||||
}
|
||||
|
||||
TEST(LuaUtilPackageTest, UtilityFunctions)
|
||||
@ -71,12 +114,12 @@ namespace
|
||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
||||
lua.safe_script("v = util.vector2(1, 0):rotate(math.rad(120))");
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return v.x").get<float>(), -0.5);
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return v.y").get<float>(), 0.86602539);
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return util.normalizeAngle(math.pi * 10 + 0.1)").get<float>(), 0.1);
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return util.clamp(0.1, 0, 1.5)").get<float>(), 0.1);
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return util.clamp(-0.1, 0, 1.5)").get<float>(), 0);
|
||||
EXPECT_FLOAT_EQ(lua.safe_script("return util.clamp(2.1, 0, 1.5)").get<float>(), 1.5);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), -0.5);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 0.86602539);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.normalizeAngle(math.pi * 10 + 0.1)"), 0.1);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(0.1, 0, 1.5)"), 0.1);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(-0.1, 0, 1.5)"), 0);
|
||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(2.1, 0, 1.5)"), 1.5);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,17 +3,23 @@
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include <osg/Vec3f>
|
||||
|
||||
#include <components/misc/mathutil.hpp>
|
||||
|
||||
#include "luastate.hpp"
|
||||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
struct is_automagical<osg::Vec2f> : std::false_type {};
|
||||
struct is_automagical<LuaUtil::Vec2> : std::false_type {};
|
||||
|
||||
template <>
|
||||
struct is_automagical<osg::Vec3f> : std::false_type {};
|
||||
struct is_automagical<LuaUtil::Vec3> : std::false_type {};
|
||||
|
||||
template <>
|
||||
struct is_automagical<LuaUtil::TransformM> : std::false_type {};
|
||||
|
||||
template <>
|
||||
struct is_automagical<LuaUtil::TransformQ> : std::false_type {};
|
||||
}
|
||||
|
||||
namespace LuaUtil
|
||||
@ -23,70 +29,148 @@ namespace LuaUtil
|
||||
{
|
||||
sol::table util(lua, sol::create);
|
||||
|
||||
// TODO: Add bindings for osg::Matrix
|
||||
|
||||
// Lua bindings for osg::Vec2f
|
||||
util["vector2"] = [](float x, float y) { return osg::Vec2f(x, y); };
|
||||
sol::usertype<osg::Vec2f> vec2Type = lua.new_usertype<osg::Vec2f>("Vec2");
|
||||
vec2Type["x"] = sol::readonly_property([](const osg::Vec2f& v) -> float { return v.x(); } );
|
||||
vec2Type["y"] = sol::readonly_property([](const osg::Vec2f& v) -> float { return v.y(); } );
|
||||
vec2Type[sol::meta_function::to_string] = [](const osg::Vec2f& v) {
|
||||
// Lua bindings for Vec2
|
||||
util["vector2"] = [](float x, float y) { return Vec2(x, y); };
|
||||
sol::usertype<Vec2> vec2Type = lua.new_usertype<Vec2>("Vec2");
|
||||
vec2Type["x"] = sol::readonly_property([](const Vec2& v) -> float { return v.x(); } );
|
||||
vec2Type["y"] = sol::readonly_property([](const Vec2& v) -> float { return v.y(); } );
|
||||
vec2Type[sol::meta_function::to_string] = [](const Vec2& v) {
|
||||
std::stringstream ss;
|
||||
ss << "(" << v.x() << ", " << v.y() << ")";
|
||||
return ss.str();
|
||||
};
|
||||
vec2Type[sol::meta_function::unary_minus] = [](const osg::Vec2f& a) { return -a; };
|
||||
vec2Type[sol::meta_function::addition] = [](const osg::Vec2f& a, const osg::Vec2f& b) { return a + b; };
|
||||
vec2Type[sol::meta_function::subtraction] = [](const osg::Vec2f& a, const osg::Vec2f& b) { return a - b; };
|
||||
vec2Type[sol::meta_function::equal_to] = [](const osg::Vec2f& a, const osg::Vec2f& b) { return a == b; };
|
||||
vec2Type[sol::meta_function::unary_minus] = [](const Vec2& a) { return -a; };
|
||||
vec2Type[sol::meta_function::addition] = [](const Vec2& a, const Vec2& b) { return a + b; };
|
||||
vec2Type[sol::meta_function::subtraction] = [](const Vec2& a, const Vec2& b) { return a - b; };
|
||||
vec2Type[sol::meta_function::equal_to] = [](const Vec2& a, const Vec2& b) { return a == b; };
|
||||
vec2Type[sol::meta_function::multiplication] = sol::overload(
|
||||
[](const osg::Vec2f& a, float c) { return a * c; },
|
||||
[](const osg::Vec2f& a, const osg::Vec2f& b) { return a * b; });
|
||||
vec2Type[sol::meta_function::division] = [](const osg::Vec2f& a, float c) { return a / c; };
|
||||
vec2Type["dot"] = [](const osg::Vec2f& a, const osg::Vec2f& b) { return a * b; };
|
||||
vec2Type["length"] = &osg::Vec2f::length;
|
||||
vec2Type["length2"] = &osg::Vec2f::length2;
|
||||
vec2Type["normalize"] = [](const osg::Vec2f& v) {
|
||||
[](const Vec2& a, float c) { return a * c; },
|
||||
[](const Vec2& a, const Vec2& b) { return a * b; });
|
||||
vec2Type[sol::meta_function::division] = [](const Vec2& a, float c) { return a / c; };
|
||||
vec2Type["dot"] = [](const Vec2& a, const Vec2& b) { return a * b; };
|
||||
vec2Type["length"] = &Vec2::length;
|
||||
vec2Type["length2"] = &Vec2::length2;
|
||||
vec2Type["normalize"] = [](const Vec2& v) {
|
||||
float len = v.length();
|
||||
if (len == 0)
|
||||
return std::make_tuple(osg::Vec2f(), 0.f);
|
||||
return std::make_tuple(Vec2(), 0.f);
|
||||
else
|
||||
return std::make_tuple(v * (1.f / len), len);
|
||||
};
|
||||
vec2Type["rotate"] = &Misc::rotateVec2f;
|
||||
|
||||
// Lua bindings for osg::Vec3f
|
||||
util["vector3"] = [](float x, float y, float z) { return osg::Vec3f(x, y, z); };
|
||||
sol::usertype<osg::Vec3f> vec3Type = lua.new_usertype<osg::Vec3f>("Vec3");
|
||||
vec3Type["x"] = sol::readonly_property([](const osg::Vec3f& v) -> float { return v.x(); } );
|
||||
vec3Type["y"] = sol::readonly_property([](const osg::Vec3f& v) -> float { return v.y(); } );
|
||||
vec3Type["z"] = sol::readonly_property([](const osg::Vec3f& v) -> float { return v.z(); } );
|
||||
vec3Type[sol::meta_function::to_string] = [](const osg::Vec3f& v) {
|
||||
// Lua bindings for Vec3
|
||||
util["vector3"] = [](float x, float y, float z) { return Vec3(x, y, z); };
|
||||
sol::usertype<Vec3> vec3Type = lua.new_usertype<Vec3>("Vec3");
|
||||
vec3Type["x"] = sol::readonly_property([](const Vec3& v) -> float { return v.x(); } );
|
||||
vec3Type["y"] = sol::readonly_property([](const Vec3& v) -> float { return v.y(); } );
|
||||
vec3Type["z"] = sol::readonly_property([](const Vec3& v) -> float { return v.z(); } );
|
||||
vec3Type[sol::meta_function::to_string] = [](const Vec3& v) {
|
||||
std::stringstream ss;
|
||||
ss << "(" << v.x() << ", " << v.y() << ", " << v.z() << ")";
|
||||
return ss.str();
|
||||
};
|
||||
vec3Type[sol::meta_function::unary_minus] = [](const osg::Vec3f& a) { return -a; };
|
||||
vec3Type[sol::meta_function::addition] = [](const osg::Vec3f& a, const osg::Vec3f& b) { return a + b; };
|
||||
vec3Type[sol::meta_function::subtraction] = [](const osg::Vec3f& a, const osg::Vec3f& b) { return a - b; };
|
||||
vec3Type[sol::meta_function::equal_to] = [](const osg::Vec3f& a, const osg::Vec3f& b) { return a == b; };
|
||||
vec3Type[sol::meta_function::unary_minus] = [](const Vec3& a) { return -a; };
|
||||
vec3Type[sol::meta_function::addition] = [](const Vec3& a, const Vec3& b) { return a + b; };
|
||||
vec3Type[sol::meta_function::subtraction] = [](const Vec3& a, const Vec3& b) { return a - b; };
|
||||
vec3Type[sol::meta_function::equal_to] = [](const Vec3& a, const Vec3& b) { return a == b; };
|
||||
vec3Type[sol::meta_function::multiplication] = sol::overload(
|
||||
[](const osg::Vec3f& a, float c) { return a * c; },
|
||||
[](const osg::Vec3f& a, const osg::Vec3f& b) { return a * b; });
|
||||
vec3Type[sol::meta_function::division] = [](const osg::Vec3f& a, float c) { return a / c; };
|
||||
vec3Type[sol::meta_function::involution] = [](const osg::Vec3f& a, const osg::Vec3f& b) { return a ^ b; };
|
||||
vec3Type["dot"] = [](const osg::Vec3f& a, const osg::Vec3f& b) { return a * b; };
|
||||
vec3Type["cross"] = [](const osg::Vec3f& a, const osg::Vec3f& b) { return a ^ b; };
|
||||
vec3Type["length"] = &osg::Vec3f::length;
|
||||
vec3Type["length2"] = &osg::Vec3f::length2;
|
||||
vec3Type["normalize"] = [](const osg::Vec3f& v) {
|
||||
[](const Vec3& a, float c) { return a * c; },
|
||||
[](const Vec3& a, const Vec3& b) { return a * b; });
|
||||
vec3Type[sol::meta_function::division] = [](const Vec3& a, float c) { return a / c; };
|
||||
vec3Type[sol::meta_function::involution] = [](const Vec3& a, const Vec3& b) { return a ^ b; };
|
||||
vec3Type["dot"] = [](const Vec3& a, const Vec3& b) { return a * b; };
|
||||
vec3Type["cross"] = [](const Vec3& a, const Vec3& b) { return a ^ b; };
|
||||
vec3Type["length"] = &Vec3::length;
|
||||
vec3Type["length2"] = &Vec3::length2;
|
||||
vec3Type["normalize"] = [](const Vec3& v) {
|
||||
float len = v.length();
|
||||
if (len == 0)
|
||||
return std::make_tuple(osg::Vec3f(), 0.f);
|
||||
return std::make_tuple(Vec3(), 0.f);
|
||||
else
|
||||
return std::make_tuple(v * (1.f / len), len);
|
||||
};
|
||||
|
||||
// Lua bindings for Transform
|
||||
sol::usertype<TransformM> transMType = lua.new_usertype<TransformM>("TransformM");
|
||||
sol::usertype<TransformQ> transQType = lua.new_usertype<TransformQ>("TransformQ");
|
||||
sol::table transforms(lua, sol::create);
|
||||
util["transform"] = LuaUtil::makeReadOnly(transforms);
|
||||
|
||||
transforms["identity"] = sol::make_object(lua, TransformM{osg::Matrixf::identity()});
|
||||
transforms["move"] = sol::overload(
|
||||
[](const Vec3& v) { return TransformM{osg::Matrixf::translate(v)}; },
|
||||
[](float x, float y, float z) { return TransformM{osg::Matrixf::translate(x, y, z)}; });
|
||||
transforms["scale"] = sol::overload(
|
||||
[](const Vec3& v) { return TransformM{osg::Matrixf::scale(v)}; },
|
||||
[](float x, float y, float z) { return TransformM{osg::Matrixf::scale(x, y, z)}; });
|
||||
transforms["rotate"] = [](float angle, const Vec3& axis) { return TransformQ{osg::Quat(angle, axis)}; };
|
||||
transforms["rotateX"] = [](float angle) { return TransformQ{osg::Quat(angle, Vec3(1, 0, 0))}; };
|
||||
transforms["rotateY"] = [](float angle) { return TransformQ{osg::Quat(angle, Vec3(0, 1, 0))}; };
|
||||
transforms["rotateZ"] = [](float angle) { return TransformQ{osg::Quat(angle, Vec3(0, 0, 1))}; };
|
||||
|
||||
transMType[sol::meta_function::multiplication] = sol::overload(
|
||||
[](const TransformM& a, const Vec3& b) { return a.mM.preMult(b); },
|
||||
[](const TransformM& a, const TransformM& b) { return TransformM{b.mM * a.mM}; },
|
||||
[](const TransformM& a, const TransformQ& b)
|
||||
{
|
||||
TransformM res{a.mM};
|
||||
res.mM.preMultRotate(b.mQ);
|
||||
return res;
|
||||
});
|
||||
transMType[sol::meta_function::to_string] = [](const TransformM& m)
|
||||
{
|
||||
osg::Vec3f trans, scale;
|
||||
osg::Quat rotation, so;
|
||||
m.mM.decompose(trans, rotation, scale, so);
|
||||
osg::Quat::value_type rot_angle, so_angle;
|
||||
osg::Vec3f rot_axis, so_axis;
|
||||
rotation.getRotate(rot_angle, rot_axis);
|
||||
so.getRotate(so_angle, so_axis);
|
||||
std::stringstream ss;
|
||||
ss << "TransformM{ ";
|
||||
if (trans.length2() > 0)
|
||||
ss << "move(" << trans.x() << ", " << trans.y() << ", " << trans.z() << ") ";
|
||||
if (rot_angle != 0)
|
||||
ss << "rotation(angle=" << rot_angle << ", axis=("
|
||||
<< rot_axis.x() << ", " << rot_axis.y() << ", " << rot_axis.z() << ")) ";
|
||||
if (scale.x() != 1 || scale.y() != 1 || scale.z() != 1)
|
||||
ss << "scale(" << scale.x() << ", " << scale.y() << ", " << scale.z() << ") ";
|
||||
if (so_angle != 0)
|
||||
ss << "rotation(angle=" << so_angle << ", axis=("
|
||||
<< so_axis.x() << ", " << so_axis.y() << ", " << so_axis.z() << ")) ";
|
||||
ss << "}";
|
||||
return ss.str();
|
||||
};
|
||||
transMType["inverse"] = [](const TransformM& m)
|
||||
{
|
||||
TransformM res;
|
||||
if (!res.mM.invert_4x3(m.mM))
|
||||
throw std::runtime_error("This Transform is not invertible");
|
||||
return res;
|
||||
};
|
||||
|
||||
transQType[sol::meta_function::multiplication] = sol::overload(
|
||||
[](const TransformQ& a, const Vec3& b) { return a.mQ * b; },
|
||||
[](const TransformQ& a, const TransformQ& b) { return TransformQ{b.mQ * a.mQ}; },
|
||||
[](const TransformQ& a, const TransformM& b)
|
||||
{
|
||||
TransformM res{b};
|
||||
res.mM.postMultRotate(a.mQ);
|
||||
return res;
|
||||
});
|
||||
transQType[sol::meta_function::to_string] = [](const TransformQ& q)
|
||||
{
|
||||
osg::Quat::value_type angle;
|
||||
osg::Vec3f axis;
|
||||
q.mQ.getRotate(angle, axis);
|
||||
std::stringstream ss;
|
||||
ss << "TransformQ{ rotation(angle=" << angle << ", axis=("
|
||||
<< axis.x() << ", " << axis.y() << ", " << axis.z() << ")) }";
|
||||
return ss.str();
|
||||
};
|
||||
transQType["inverse"] = [](const TransformQ& q) { return TransformQ{q.mQ.inverse()}; };
|
||||
|
||||
// Utility functions
|
||||
util["clamp"] = [](float value, float from, float to) { return std::clamp(value, from, to); };
|
||||
// NOTE: `util["clamp"] = std::clamp<float>` causes error 'AddressSanitizer: stack-use-after-scope'
|
||||
|
@ -1,11 +1,24 @@
|
||||
#ifndef COMPONENTS_LUA_UTILPACKAGE_H
|
||||
#define COMPONENTS_LUA_UTILPACKAGE_H
|
||||
|
||||
#include <limits> // missing from sol/sol.hpp
|
||||
#include <osg/Vec2>
|
||||
#include <osg/Vec3>
|
||||
#include <osg/Matrix>
|
||||
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
namespace LuaUtil
|
||||
{
|
||||
using Vec2 = osg::Vec2f;
|
||||
using Vec3 = osg::Vec3f;
|
||||
|
||||
// For performance reasons "Transform" is implemented as 2 types with the same interface.
|
||||
// Transform supports only composition, inversion, and applying to a 3d vector.
|
||||
struct TransformM { osg::Matrixf mM; };
|
||||
struct TransformQ { osg::Quat mQ; };
|
||||
|
||||
inline TransformM asTransform(const osg::Matrixf& m) { return {m}; }
|
||||
inline TransformQ asTransform(const osg::Quat& q) { return {q}; }
|
||||
|
||||
sol::table initUtilPackage(sol::state&);
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Limits given value to the interval [`from`, `to`]
|
||||
-- Limits given value to the interval [`from`, `to`].
|
||||
-- @function [parent=#util] clamp
|
||||
-- @param #number value
|
||||
-- @param #number from
|
||||
@ -14,7 +14,7 @@
|
||||
-- @return #number min(max(value, from), to)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Adds `2pi*k` and puts the angle in range `[-pi, pi]`
|
||||
-- Adds `2pi*k` and puts the angle in range `[-pi, pi]`.
|
||||
-- @function [parent=#util] normalizeAngle
|
||||
-- @param #number angle Angle in radians
|
||||
-- @return #number Angle in range `[-pi, pi]`
|
||||
@ -48,13 +48,13 @@
|
||||
-- @return #Vector2.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Length of the vector
|
||||
-- Length of the vector.
|
||||
-- @function [parent=#Vector2] length
|
||||
-- @param self
|
||||
-- @return #number
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Square of the length of the vector
|
||||
-- Square of the length of the vector.
|
||||
-- @function [parent=#Vector2] length2
|
||||
-- @param self
|
||||
-- @return #number
|
||||
@ -146,5 +146,84 @@
|
||||
-- @param #Vector3 v
|
||||
-- @return #Vector3
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- @type Transform
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Returns the inverse transform.
|
||||
-- @function [parent=#Transform] inverse
|
||||
-- @param self
|
||||
-- @return #Transform.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- @type TRANSFORM
|
||||
-- @field [parent=#TRANSFORM] #Transform identity Empty transform.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Movement by given vector.
|
||||
-- @function [parent=#TRANSFORM] move
|
||||
-- @param #Vector3 offset.
|
||||
-- @return #Transform.
|
||||
-- @usage
|
||||
-- -- Accepts either 3 numbers or a 3D vector
|
||||
-- util.transform.move(x, y, z)
|
||||
-- util.transform.move(util.vector3(x, y, z))
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Scale transform.
|
||||
-- @function [parent=#TRANSFORM] scale
|
||||
-- @param #number scaleX.
|
||||
-- @param #number scaleY.
|
||||
-- @param #number scaleZ.
|
||||
-- @return #Transform.
|
||||
-- @usage
|
||||
-- -- Accepts either 3 numbers or a 3D vector
|
||||
-- util.transform.scale(x, y, z)
|
||||
-- util.transform.scale(util.vector3(x, y, z))
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Rotation (any axis).
|
||||
-- @function [parent=#TRANSFORM] rotate
|
||||
-- @param #number angle
|
||||
-- @param #Vector3 axis.
|
||||
-- @return #Transform.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- X-axis rotation.
|
||||
-- @function [parent=#TRANSFORM] rotateX
|
||||
-- @param #number angle
|
||||
-- @return #Transform.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Y-axis rotation.
|
||||
-- @function [parent=#TRANSFORM] rotateY
|
||||
-- @param #number angle
|
||||
-- @return #Transform.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Z-axis rotation.
|
||||
-- @function [parent=#TRANSFORM] rotateZ
|
||||
-- @param #number angle
|
||||
-- @return #Transform.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- 3D transforms (scale/move/rotate) that can be applied to 3D vectors.
|
||||
-- Several transforms can be combined and applied to a vector using multiplication.
|
||||
-- Combined transforms apply in reverse order (from right to left).
|
||||
-- @field [parent=#util] #TRANSFORM transform
|
||||
-- @usage
|
||||
-- local util = require('openmw.util')
|
||||
-- local trans = util.transform
|
||||
-- local fromActorSpace = trans.move(actor.position) * trans.rotateZ(actor.rotation.z)
|
||||
--
|
||||
-- -- rotation is applied first, movement is second
|
||||
-- local posBehindActor = fromActorSpace * util.vector3(0, -100, 0)
|
||||
--
|
||||
-- -- equivalent to trans.rotateZ(-actor.rotation.z) * trans.move(-actor.position)
|
||||
-- local toActorSpace = fromActorSpace:inverse()
|
||||
-- local relativeTargetPos = toActorSpace * target.position
|
||||
-- local deltaAngle = math.atan2(relativeTargetPos.y, relativeTargetPos.x)
|
||||
|
||||
return nil
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user