mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 15:29:55 +00:00
Lua: change obj.rotation from Euler angles to Quaternion
This commit is contained in:
parent
1d5b73f20a
commit
3b43cc2aea
14 changed files with 175 additions and 30 deletions
|
@ -111,7 +111,7 @@ namespace MWLua
|
||||||
{
|
{
|
||||||
auto* lua = context.mLua;
|
auto* lua = context.mLua;
|
||||||
sol::table api(lua->sol(), sol::create);
|
sol::table api(lua->sol(), sol::create);
|
||||||
api["API_REVISION"] = 39;
|
api["API_REVISION"] = 40;
|
||||||
api["quit"] = [lua]() {
|
api["quit"] = [lua]() {
|
||||||
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
||||||
MWBase::Environment::get().getStateManager()->requestQuit();
|
MWBase::Environment::get().getStateManager()->requestQuit();
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
#include <components/esm3/loadnpc.hpp>
|
#include <components/esm3/loadnpc.hpp>
|
||||||
#include <components/lua/luastate.hpp>
|
#include <components/lua/luastate.hpp>
|
||||||
#include <components/lua/shapes/box.hpp>
|
#include <components/lua/shapes/box.hpp>
|
||||||
|
#include <components/lua/utilpackage.hpp>
|
||||||
|
#include <components/misc/convert.hpp>
|
||||||
|
#include <components/misc/mathutil.hpp>
|
||||||
|
|
||||||
#include "../mwworld/cellstore.hpp"
|
#include "../mwworld/cellstore.hpp"
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
|
@ -141,6 +144,28 @@ namespace MWLua
|
||||||
listT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get<sol::function>();
|
listT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get<sol::function>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::Vec3f toEulerRotation(const sol::object& transform, bool isActor)
|
||||||
|
{
|
||||||
|
if (transform.is<LuaUtil::TransformQ>())
|
||||||
|
{
|
||||||
|
const osg::Quat& q = transform.as<LuaUtil::TransformQ>().mQ;
|
||||||
|
return isActor ? Misc::toEulerAnglesXZ(q) : Misc::toEulerAnglesZYX(q);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const osg::Matrixf& m = LuaUtil::cast<LuaUtil::TransformM>(transform).mM;
|
||||||
|
return isActor ? Misc::toEulerAnglesXZ(m) : Misc::toEulerAnglesZYX(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::Quat toQuat(const ESM::Position& pos, bool isActor)
|
||||||
|
{
|
||||||
|
if (isActor)
|
||||||
|
return osg::Quat(pos.rot[0], osg::Vec3(-1, 0, 0)) * osg::Quat(pos.rot[2], osg::Vec3(0, 0, -1));
|
||||||
|
else
|
||||||
|
return Misc::Convert::makeOsgQuat(pos.rot);
|
||||||
|
}
|
||||||
|
|
||||||
template <class ObjectT>
|
template <class ObjectT>
|
||||||
void addBasicBindings(sol::usertype<ObjectT>& objectT, const Context& context)
|
void addBasicBindings(sol::usertype<ObjectT>& objectT, const Context& context)
|
||||||
{
|
{
|
||||||
|
@ -166,12 +191,14 @@ namespace MWLua
|
||||||
[](const ObjectT& o) -> osg::Vec3f { return o.ptr().getRefData().getPosition().asVec3(); });
|
[](const ObjectT& o) -> osg::Vec3f { return o.ptr().getRefData().getPosition().asVec3(); });
|
||||||
objectT["scale"]
|
objectT["scale"]
|
||||||
= sol::readonly_property([](const ObjectT& o) -> float { return o.ptr().getCellRef().getScale(); });
|
= sol::readonly_property([](const ObjectT& o) -> float { return o.ptr().getCellRef().getScale(); });
|
||||||
objectT["rotation"] = sol::readonly_property(
|
objectT["rotation"] = sol::readonly_property([](const ObjectT& o) -> LuaUtil::TransformQ {
|
||||||
[](const ObjectT& o) -> osg::Vec3f { return o.ptr().getRefData().getPosition().asRotationVec3(); });
|
return { toQuat(o.ptr().getRefData().getPosition(), o.ptr().getClass().isActor()) };
|
||||||
|
});
|
||||||
objectT["startingPosition"] = sol::readonly_property(
|
objectT["startingPosition"] = sol::readonly_property(
|
||||||
[](const ObjectT& o) -> osg::Vec3f { return o.ptr().getCellRef().getPosition().asVec3(); });
|
[](const ObjectT& o) -> osg::Vec3f { return o.ptr().getCellRef().getPosition().asVec3(); });
|
||||||
objectT["startingRotation"] = sol::readonly_property(
|
objectT["startingRotation"] = sol::readonly_property([](const ObjectT& o) -> LuaUtil::TransformQ {
|
||||||
[](const ObjectT& o) -> osg::Vec3f { return o.ptr().getCellRef().getPosition().asRotationVec3(); });
|
return { toQuat(o.ptr().getCellRef().getPosition(), o.ptr().getClass().isActor()) };
|
||||||
|
});
|
||||||
objectT["getBoundingBox"] = [](const ObjectT& o) {
|
objectT["getBoundingBox"] = [](const ObjectT& o) {
|
||||||
MWRender::RenderingManager* renderingManager
|
MWRender::RenderingManager* renderingManager
|
||||||
= MWBase::Environment::get().getWorld()->getRenderingManager();
|
= MWBase::Environment::get().getWorld()->getRenderingManager();
|
||||||
|
@ -401,12 +428,14 @@ namespace MWLua
|
||||||
throw std::runtime_error("Object is either removed or already in the process of teleporting");
|
throw std::runtime_error("Object is either removed or already in the process of teleporting");
|
||||||
osg::Vec3f rot = ptr.getRefData().getPosition().asRotationVec3();
|
osg::Vec3f rot = ptr.getRefData().getPosition().asRotationVec3();
|
||||||
bool placeOnGround = false;
|
bool placeOnGround = false;
|
||||||
if (options.is<osg::Vec3f>())
|
if (LuaUtil::isTransform(options))
|
||||||
rot = options.as<osg::Vec3f>();
|
rot = toEulerRotation(options, ptr.getClass().isActor());
|
||||||
else if (options != sol::nil)
|
else if (options != sol::nil)
|
||||||
{
|
{
|
||||||
sol::table t = LuaUtil::cast<sol::table>(options);
|
sol::table t = LuaUtil::cast<sol::table>(options);
|
||||||
rot = LuaUtil::getValueOrDefault(t["rotation"], rot);
|
sol::object rotationArg = t["rotation"];
|
||||||
|
if (rotationArg != sol::nil)
|
||||||
|
rot = toEulerRotation(rotationArg, ptr.getClass().isActor());
|
||||||
placeOnGround = LuaUtil::getValueOrDefault(t["onGround"], placeOnGround);
|
placeOnGround = LuaUtil::getValueOrDefault(t["onGround"], placeOnGround);
|
||||||
}
|
}
|
||||||
if (ptr.getContainerStore())
|
if (ptr.getContainerStore())
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <components/esm3/loaddoor.hpp>
|
#include <components/esm3/loaddoor.hpp>
|
||||||
#include <components/esm4/loaddoor.hpp>
|
#include <components/esm4/loaddoor.hpp>
|
||||||
|
#include <components/lua/utilpackage.hpp>
|
||||||
|
#include <components/misc/convert.hpp>
|
||||||
#include <components/misc/resourcehelpers.hpp>
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
|
|
||||||
|
@ -41,8 +43,9 @@ namespace MWLua
|
||||||
door["isTeleport"] = [](const Object& o) { return doorPtr(o).getCellRef().getTeleport(); };
|
door["isTeleport"] = [](const Object& o) { return doorPtr(o).getCellRef().getTeleport(); };
|
||||||
door["destPosition"]
|
door["destPosition"]
|
||||||
= [](const Object& o) -> osg::Vec3f { return doorPtr(o).getCellRef().getDoorDest().asVec3(); };
|
= [](const Object& o) -> osg::Vec3f { return doorPtr(o).getCellRef().getDoorDest().asVec3(); };
|
||||||
door["destRotation"]
|
door["destRotation"] = [](const Object& o) -> LuaUtil::TransformQ {
|
||||||
= [](const Object& o) -> osg::Vec3f { return doorPtr(o).getCellRef().getDoorDest().asRotationVec3(); };
|
return { Misc::Convert::makeOsgQuat(doorPtr(o).getCellRef().getDoorDest().rot) };
|
||||||
|
};
|
||||||
door["destCell"] = [](sol::this_state lua, const Object& o) -> sol::object {
|
door["destCell"] = [](sol::this_state lua, const Object& o) -> sol::object {
|
||||||
const MWWorld::CellRef& cellRef = doorPtr(o).getCellRef();
|
const MWWorld::CellRef& cellRef = doorPtr(o).getCellRef();
|
||||||
if (!cellRef.getTeleport())
|
if (!cellRef.getTeleport())
|
||||||
|
@ -80,8 +83,9 @@ namespace MWLua
|
||||||
door["isTeleport"] = [](const Object& o) { return door4Ptr(o).getCellRef().getTeleport(); };
|
door["isTeleport"] = [](const Object& o) { return door4Ptr(o).getCellRef().getTeleport(); };
|
||||||
door["destPosition"]
|
door["destPosition"]
|
||||||
= [](const Object& o) -> osg::Vec3f { return door4Ptr(o).getCellRef().getDoorDest().asVec3(); };
|
= [](const Object& o) -> osg::Vec3f { return door4Ptr(o).getCellRef().getDoorDest().asVec3(); };
|
||||||
door["destRotation"]
|
door["destRotation"] = [](const Object& o) -> LuaUtil::TransformQ {
|
||||||
= [](const Object& o) -> osg::Vec3f { return door4Ptr(o).getCellRef().getDoorDest().asRotationVec3(); };
|
return { Misc::Convert::makeOsgQuat(door4Ptr(o).getCellRef().getDoorDest().rot) };
|
||||||
|
};
|
||||||
door["destCell"] = [](sol::this_state lua, const Object& o) -> sol::object {
|
door["destCell"] = [](sol::this_state lua, const Object& o) -> sol::object {
|
||||||
const MWWorld::CellRef& cellRef = door4Ptr(o).getCellRef();
|
const MWWorld::CellRef& cellRef = door4Ptr(o).getCellRef();
|
||||||
if (!cellRef.getTeleport())
|
if (!cellRef.getTeleport())
|
||||||
|
|
|
@ -157,7 +157,7 @@ namespace
|
||||||
EXPECT_EQ(getAsString(lua, "moveAndScale:apply(v(300, 200, 100))"), "(156, 222, 68)");
|
EXPECT_EQ(getAsString(lua, "moveAndScale:apply(v(300, 200, 100))"), "(156, 222, 68)");
|
||||||
EXPECT_THAT(getAsString(lua, "moveAndScale"),
|
EXPECT_THAT(getAsString(lua, "moveAndScale"),
|
||||||
AllOf(StartsWith("TransformM{ move(6, 22, 18) scale(0.5, 1, 0.5) "), EndsWith(" }")));
|
AllOf(StartsWith("TransformM{ move(6, 22, 18) scale(0.5, 1, 0.5) "), EndsWith(" }")));
|
||||||
EXPECT_EQ(getAsString(lua, "T.identity"), "TransformM{ }");
|
EXPECT_EQ(getAsString(lua, "T.identity"), "TransformQ{ rotation(angle=0, axis=(0, 0, 1)) }");
|
||||||
lua.safe_script("rx = T.rotateX(-math.pi / 2)");
|
lua.safe_script("rx = T.rotateX(-math.pi / 2)");
|
||||||
lua.safe_script("ry = T.rotateY(-math.pi / 2)");
|
lua.safe_script("ry = T.rotateY(-math.pi / 2)");
|
||||||
lua.safe_script("rz = T.rotateZ(-math.pi / 2)");
|
lua.safe_script("rz = T.rotateZ(-math.pi / 2)");
|
||||||
|
|
|
@ -175,7 +175,7 @@ namespace LuaUtil
|
||||||
sol::table transforms(lua, sol::create);
|
sol::table transforms(lua, sol::create);
|
||||||
util["transform"] = LuaUtil::makeReadOnly(transforms);
|
util["transform"] = LuaUtil::makeReadOnly(transforms);
|
||||||
|
|
||||||
transforms["identity"] = sol::make_object(lua, TransformM{ osg::Matrixf::identity() });
|
transforms["identity"] = sol::make_object(lua, TransformQ{ osg::Quat() });
|
||||||
transforms["move"] = sol::overload([](const Vec3& v) { return TransformM{ osg::Matrixf::translate(v) }; },
|
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) }; });
|
[](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) }; },
|
transforms["scale"] = sol::overload([](const Vec3& v) { return TransformM{ osg::Matrixf::scale(v) }; },
|
||||||
|
@ -223,6 +223,22 @@ namespace LuaUtil
|
||||||
throw std::runtime_error("This Transform is not invertible");
|
throw std::runtime_error("This Transform is not invertible");
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
transMType["getYaw"] = [](const TransformM& m) {
|
||||||
|
osg::Vec3f angles = Misc::toEulerAnglesXZ(m.mM);
|
||||||
|
return angles.z();
|
||||||
|
};
|
||||||
|
transMType["getPitch"] = [](const TransformM& m) {
|
||||||
|
osg::Vec3f angles = Misc::toEulerAnglesXZ(m.mM);
|
||||||
|
return angles.x();
|
||||||
|
};
|
||||||
|
transMType["getAnglesXZ"] = [](const TransformM& m) {
|
||||||
|
osg::Vec3f angles = Misc::toEulerAnglesXZ(m.mM);
|
||||||
|
return std::make_tuple(angles.x(), angles.z());
|
||||||
|
};
|
||||||
|
transMType["getAnglesZYX"] = [](const TransformM& m) {
|
||||||
|
osg::Vec3f angles = Misc::toEulerAnglesXZ(m.mM);
|
||||||
|
return std::make_tuple(angles.z(), angles.y(), angles.x());
|
||||||
|
};
|
||||||
|
|
||||||
transQType[sol::meta_function::multiplication]
|
transQType[sol::meta_function::multiplication]
|
||||||
= sol::overload([](const TransformQ& a, const Vec3& b) { return a.mQ * b; },
|
= sol::overload([](const TransformQ& a, const Vec3& b) { return a.mQ * b; },
|
||||||
|
@ -243,6 +259,22 @@ namespace LuaUtil
|
||||||
};
|
};
|
||||||
transQType["apply"] = [](const TransformQ& a, const Vec3& b) { return a.mQ * b; },
|
transQType["apply"] = [](const TransformQ& a, const Vec3& b) { return a.mQ * b; },
|
||||||
transQType["inverse"] = [](const TransformQ& q) { return TransformQ{ q.mQ.inverse() }; };
|
transQType["inverse"] = [](const TransformQ& q) { return TransformQ{ q.mQ.inverse() }; };
|
||||||
|
transQType["getYaw"] = [](const TransformQ& q) {
|
||||||
|
osg::Vec3f angles = Misc::toEulerAnglesXZ(q.mQ);
|
||||||
|
return angles.z();
|
||||||
|
};
|
||||||
|
transQType["getPitch"] = [](const TransformQ& q) {
|
||||||
|
osg::Vec3f angles = Misc::toEulerAnglesXZ(q.mQ);
|
||||||
|
return angles.x();
|
||||||
|
};
|
||||||
|
transQType["getAnglesXZ"] = [](const TransformQ& q) {
|
||||||
|
osg::Vec3f angles = Misc::toEulerAnglesXZ(q.mQ);
|
||||||
|
return std::make_tuple(angles.x(), angles.z());
|
||||||
|
};
|
||||||
|
transQType["getAnglesZYX"] = [](const TransformQ& q) {
|
||||||
|
osg::Vec3f angles = Misc::toEulerAnglesXZ(q.mQ);
|
||||||
|
return std::make_tuple(angles.z(), angles.y(), angles.x());
|
||||||
|
};
|
||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
util["clamp"] = [](double value, double from, double to) { return std::clamp(value, from, to); };
|
util["clamp"] = [](double value, double from, double to) { return std::clamp(value, from, to); };
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define COMPONENTS_LUA_UTILPACKAGE_H
|
#define COMPONENTS_LUA_UTILPACKAGE_H
|
||||||
|
|
||||||
#include <osg/Matrix>
|
#include <osg/Matrix>
|
||||||
|
#include <osg/Quat>
|
||||||
#include <osg/Vec2>
|
#include <osg/Vec2>
|
||||||
#include <osg/Vec3>
|
#include <osg/Vec3>
|
||||||
#include <osg/Vec4>
|
#include <osg/Vec4>
|
||||||
|
@ -34,6 +35,11 @@ namespace LuaUtil
|
||||||
return { q };
|
return { q };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isTransform(const sol::object& obj)
|
||||||
|
{
|
||||||
|
return obj.is<TransformM>() || obj.is<TransformQ>();
|
||||||
|
}
|
||||||
|
|
||||||
sol::table initUtilPackage(lua_State*);
|
sol::table initUtilPackage(lua_State*);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
#define MISC_MATHUTIL_H
|
#define MISC_MATHUTIL_H
|
||||||
|
|
||||||
#include <osg/Math>
|
#include <osg/Math>
|
||||||
|
#include <osg/Matrixf>
|
||||||
|
#include <osg/Quat>
|
||||||
#include <osg/Vec2f>
|
#include <osg/Vec2f>
|
||||||
|
#include <osg/Vec3f>
|
||||||
|
|
||||||
namespace Misc
|
namespace Misc
|
||||||
{
|
{
|
||||||
|
@ -22,6 +25,44 @@ namespace Misc
|
||||||
return osg::Vec2f(vec.x() * c + vec.y() * -s, vec.x() * s + vec.y() * c);
|
return osg::Vec2f(vec.x() * c + vec.y() * -s, vec.x() * s + vec.y() * c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline osg::Vec3f toEulerAnglesXZ(osg::Vec3f forward)
|
||||||
|
{
|
||||||
|
float x = -asin(forward.z());
|
||||||
|
float z = atan2(forward.x(), forward.y());
|
||||||
|
return osg::Vec3f(x, 0, z);
|
||||||
|
}
|
||||||
|
inline osg::Vec3f toEulerAnglesXZ(osg::Quat quat)
|
||||||
|
{
|
||||||
|
return toEulerAnglesXZ(quat * osg::Vec3f(0, 1, 0));
|
||||||
|
}
|
||||||
|
inline osg::Vec3f toEulerAnglesXZ(osg::Matrixf m)
|
||||||
|
{
|
||||||
|
osg::Vec3f forward(m(1, 0), m(1, 1), m(1, 2));
|
||||||
|
forward.normalize();
|
||||||
|
return toEulerAnglesXZ(forward);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline osg::Vec3f toEulerAnglesZYX(osg::Vec3f forward, osg::Vec3f up)
|
||||||
|
{
|
||||||
|
float y = -asin(up.x());
|
||||||
|
float x = atan2(up.y(), up.z());
|
||||||
|
osg::Vec3f forwardZ = (osg::Quat(x, osg::Vec3f(1, 0, 0)) * osg::Quat(y, osg::Vec3f(0, 1, 0))) * forward;
|
||||||
|
float z = atan2(forwardZ.x(), forwardZ.y());
|
||||||
|
return osg::Vec3f(x, y, z);
|
||||||
|
}
|
||||||
|
inline osg::Vec3f toEulerAnglesZYX(osg::Quat quat)
|
||||||
|
{
|
||||||
|
return toEulerAnglesZYX(quat * osg::Vec3f(0, 1, 0), quat * osg::Vec3f(0, 0, 1));
|
||||||
|
}
|
||||||
|
inline osg::Vec3f toEulerAnglesZYX(osg::Matrixf m)
|
||||||
|
{
|
||||||
|
osg::Vec3f forward(m(1, 0), m(1, 1), m(1, 2));
|
||||||
|
osg::Vec3f up(m(2, 0), m(2, 1), m(2, 2));
|
||||||
|
forward.normalize();
|
||||||
|
up.normalize();
|
||||||
|
return toEulerAnglesZYX(forward, up);
|
||||||
|
}
|
||||||
|
|
||||||
inline bool isPowerOfTwo(int x)
|
inline bool isPowerOfTwo(int x)
|
||||||
{
|
{
|
||||||
return ((x > 0) && ((x & (x - 1)) == 0));
|
return ((x > 0) && ((x & (x - 1)) == 0));
|
||||||
|
|
|
@ -34,7 +34,7 @@ function M.onUpdate(dt)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if camera.getMode() == camera.MODE.ThirdPerson and camera.getThirdPersonDistance() < limitSwitch
|
if camera.getMode() == camera.MODE.ThirdPerson and camera.getThirdPersonDistance() < limitSwitch
|
||||||
and math.abs(util.normalizeAngle(camera.getYaw() - self.rotation.z)) < math.rad(10) then
|
and math.abs(util.normalizeAngle(camera.getYaw() - self.rotation:getYaw())) < math.rad(10) then
|
||||||
if castRayBackward() <= limitSwitch then
|
if castRayBackward() <= limitSwitch then
|
||||||
camera.setMode(camera.MODE.FirstPerson, true)
|
camera.setMode(camera.MODE.FirstPerson, true)
|
||||||
forcedFirstPerson = true
|
forcedFirstPerson = true
|
||||||
|
|
|
@ -43,11 +43,11 @@ function M.onFrame(dt)
|
||||||
if camera.getMode() == MODE.Preview and not input.isActionPressed(input.ACTION.TogglePOV) then
|
if camera.getMode() == MODE.Preview and not input.isActionPressed(input.ACTION.TogglePOV) then
|
||||||
camera.showCrosshair(camera.getFocalPreferredOffset():length() > 5)
|
camera.showCrosshair(camera.getFocalPreferredOffset():length() > 5)
|
||||||
local move = util.vector2(self.controls.sideMovement, self.controls.movement)
|
local move = util.vector2(self.controls.sideMovement, self.controls.movement)
|
||||||
local yawDelta = camera.getYaw() - self.rotation.z
|
local yawDelta = camera.getYaw() - self.rotation:getYaw()
|
||||||
move = move:rotate(-yawDelta)
|
move = move:rotate(-yawDelta)
|
||||||
self.controls.sideMovement = move.x
|
self.controls.sideMovement = move.x
|
||||||
self.controls.movement = move.y
|
self.controls.movement = move.y
|
||||||
self.controls.pitchChange = camera.getPitch() * math.cos(yawDelta) - self.rotation.x
|
self.controls.pitchChange = camera.getPitch() * math.cos(yawDelta) - self.rotation:getPitch()
|
||||||
if move:length() > 0.05 then
|
if move:length() > 0.05 then
|
||||||
local delta = math.atan2(move.x, move.y)
|
local delta = math.atan2(move.x, move.y)
|
||||||
local maxDelta = math.max(delta, 1) * M.turnSpeed * dt
|
local maxDelta = math.max(delta, 1) * M.turnSpeed * dt
|
||||||
|
@ -68,7 +68,7 @@ function M.onInputAction(action)
|
||||||
end
|
end
|
||||||
if action == input.ACTION.ZoomIn and camera.getMode() == MODE.Preview
|
if action == input.ACTION.ZoomIn and camera.getMode() == MODE.Preview
|
||||||
and I.Camera.getBaseThirdPersonDistance() == 30 then
|
and I.Camera.getBaseThirdPersonDistance() == 30 then
|
||||||
self.controls.yawChange = camera.getYaw() - self.rotation.z
|
self.controls.yawChange = camera.getYaw() - self.rotation:getYaw()
|
||||||
camera.setMode(MODE.FirstPerson)
|
camera.setMode(MODE.FirstPerson)
|
||||||
elseif action == input.ACTION.ZoomOut and camera.getMode() == MODE.FirstPerson then
|
elseif action == input.ACTION.ZoomOut and camera.getMode() == MODE.FirstPerson then
|
||||||
camera.setMode(MODE.Preview)
|
camera.setMode(MODE.Preview)
|
||||||
|
|
|
@ -152,9 +152,9 @@
|
||||||
-- @field #boolean enabled Whether the object is enabled or disabled. Global scripts can set the value. Items in containers or inventories can't be disabled.
|
-- @field #boolean enabled Whether the object is enabled or disabled. Global scripts can set the value. Items in containers or inventories can't be disabled.
|
||||||
-- @field openmw.util#Vector3 position Object position.
|
-- @field openmw.util#Vector3 position Object position.
|
||||||
-- @field #number scale Object scale.
|
-- @field #number scale Object scale.
|
||||||
-- @field openmw.util#Vector3 rotation Object rotation (ZXY order).
|
-- @field openmw.util#Transform rotation Object rotation.
|
||||||
-- @field openmw.util#Vector3 startingPosition The object original position
|
-- @field openmw.util#Vector3 startingPosition The object original position
|
||||||
-- @field openmw.util#Vector3 startingRotation The object original rotation
|
-- @field openmw.util#Transform startingRotation The object original rotation
|
||||||
-- @field #string ownerRecordId NPC who owns the object (nil if missing). Global and self scripts can set the value.
|
-- @field #string ownerRecordId NPC who owns the object (nil if missing). Global and self scripts can set the value.
|
||||||
-- @field #string ownerFactionId Faction who owns the object (nil if missing). Global and self scripts can set the value.
|
-- @field #string ownerFactionId Faction who owns the object (nil if missing). Global and self scripts can set the value.
|
||||||
-- @field #number ownerFactionRank Rank required to be allowed to pick up the object. Global and self scripts can set the value.
|
-- @field #number ownerFactionRank Rank required to be allowed to pick up the object. Global and self scripts can set the value.
|
||||||
|
@ -228,12 +228,12 @@
|
||||||
-- @param #any cellOrName A cell to define the destination worldspace; can be either #Cell, or cell name, or an empty string (empty string means the default exterior worldspace).
|
-- @param #any cellOrName A cell to define the destination worldspace; can be either #Cell, or cell name, or an empty string (empty string means the default exterior worldspace).
|
||||||
-- If the worldspace has multiple cells (i.e. an exterior), the destination cell is calculated using `position`.
|
-- If the worldspace has multiple cells (i.e. an exterior), the destination cell is calculated using `position`.
|
||||||
-- @param openmw.util#Vector3 position New position.
|
-- @param openmw.util#Vector3 position New position.
|
||||||
-- @param #TeleportOptions options (optional) Either table @{#TeleportOptions} or @{openmw.util#Vector3} rotation.
|
-- @param #TeleportOptions options (optional) Either table @{#TeleportOptions} or @{openmw.util#Transform} rotation.
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Either table with options or @{openmw.util#Vector3} rotation.
|
-- Either table with options or @{openmw.util#Vector3} rotation.
|
||||||
-- @type TeleportOptions
|
-- @type TeleportOptions
|
||||||
-- @field openmw.util#Vector3 rotation New rotation; if missing, then the current rotation is used.
|
-- @field openmw.util#Transform rotation New rotation; if missing, then the current rotation is used.
|
||||||
-- @field #boolean onGround If true, adjust destination position to the ground.
|
-- @field #boolean onGround If true, adjust destination position to the ground.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
@ -1333,7 +1333,7 @@
|
||||||
-- Destination rotation (only if a teleport door).
|
-- Destination rotation (only if a teleport door).
|
||||||
-- @function [parent=#Door] destRotation
|
-- @function [parent=#Door] destRotation
|
||||||
-- @param openmw.core#GameObject object
|
-- @param openmw.core#GameObject object
|
||||||
-- @return openmw.util#Vector3
|
-- @return openmw.util#Transform
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Destination cell (only if a teleport door).
|
-- Destination cell (only if a teleport door).
|
||||||
|
@ -1443,7 +1443,7 @@
|
||||||
-- Destination rotation (only if a teleport door).
|
-- Destination rotation (only if a teleport door).
|
||||||
-- @function [parent=#ESM4Door] destRotation
|
-- @function [parent=#ESM4Door] destRotation
|
||||||
-- @param openmw.core#GameObject object
|
-- @param openmw.core#GameObject object
|
||||||
-- @return openmw.util#Vector3
|
-- @return openmw.util#Transform
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Destination cell (only if a teleport door).
|
-- Destination cell (only if a teleport door).
|
||||||
|
|
|
@ -501,6 +501,33 @@
|
||||||
-- @param #Vector3 v
|
-- @param #Vector3 v
|
||||||
-- @return #Vector3
|
-- @return #Vector3
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Get yaw angle (radians)
|
||||||
|
-- @function [parent=#Transform] getYaw
|
||||||
|
-- @param self
|
||||||
|
-- @return #number
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Get pitch angle (radians)
|
||||||
|
-- @function [parent=#Transform] getPitch
|
||||||
|
-- @param self
|
||||||
|
-- @return #number
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Get Euler angles for XZ rotation order (pitch and yaw; radians)
|
||||||
|
-- @function [parent=#Transform] getAnglesXZ
|
||||||
|
-- @param self
|
||||||
|
-- @return #number pitch (rotation around X axis)
|
||||||
|
-- @return #number yaw (rotation around Z axis)
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Get Euler angles for ZYX rotation order (radians)
|
||||||
|
-- @function [parent=#Transform] getAnglesZYX
|
||||||
|
-- @param self
|
||||||
|
-- @return #number rotation around Z axis (first rotation)
|
||||||
|
-- @return #number rotation around Y axis (second rotation)
|
||||||
|
-- @return #number rotation around X axis (third rotation)
|
||||||
|
|
||||||
---
|
---
|
||||||
-- @type TRANSFORM
|
-- @type TRANSFORM
|
||||||
-- @field [parent=#TRANSFORM] #Transform identity Empty transform.
|
-- @field [parent=#TRANSFORM] #Transform identity Empty transform.
|
||||||
|
|
|
@ -21,10 +21,10 @@ testing.registerLocalTest('playerRotation',
|
||||||
self.controls.run = true
|
self.controls.run = true
|
||||||
self.controls.movement = 0
|
self.controls.movement = 0
|
||||||
self.controls.sideMovement = 0
|
self.controls.sideMovement = 0
|
||||||
self.controls.yawChange = util.normalizeAngle(math.rad(90) - self.rotation.z) * 0.5
|
self.controls.yawChange = util.normalizeAngle(math.rad(90) - self.rotation:getYaw()) * 0.5
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
end
|
end
|
||||||
testing.expectEqualWithDelta(self.rotation.z, math.rad(90), 0.05, 'Incorrect rotation')
|
testing.expectEqualWithDelta(self.rotation:getYaw(), math.rad(90), 0.05, 'Incorrect rotation')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
testing.registerLocalTest('playerForwardRunning',
|
testing.registerLocalTest('playerForwardRunning',
|
||||||
|
|
|
@ -36,23 +36,29 @@ local function testTimers()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function testTeleport()
|
local function testTeleport()
|
||||||
player:teleport('', util.vector3(100, 50, 0), util.vector3(0, 0, math.rad(-90)))
|
player:teleport('', util.vector3(100, 50, 500), util.transform.rotateZ(math.rad(90)))
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
testing.expect(player.cell.isExterior, 'teleport to exterior failed')
|
testing.expect(player.cell.isExterior, 'teleport to exterior failed')
|
||||||
testing.expectEqualWithDelta(player.position.x, 100, 1, 'incorrect position after teleporting')
|
testing.expectEqualWithDelta(player.position.x, 100, 1, 'incorrect position after teleporting')
|
||||||
testing.expectEqualWithDelta(player.position.y, 50, 1, 'incorrect position after teleporting')
|
testing.expectEqualWithDelta(player.position.y, 50, 1, 'incorrect position after teleporting')
|
||||||
testing.expectEqualWithDelta(player.rotation.z, math.rad(-90), 0.05, 'incorrect rotation after teleporting')
|
testing.expectEqualWithDelta(player.position.z, 500, 1, 'incorrect position after teleporting')
|
||||||
|
testing.expectEqualWithDelta(player.rotation:getYaw(), math.rad(90), 0.05, 'incorrect rotation after teleporting')
|
||||||
|
|
||||||
|
player:teleport('', player.position, {rotation=util.transform.rotateZ(math.rad(-90)), onGround=true})
|
||||||
|
coroutine.yield()
|
||||||
|
testing.expectEqualWithDelta(player.rotation:getYaw(), math.rad(-90), 0.05, 'options.rotation is not working')
|
||||||
|
testing.expectLessOrEqual(player.position.z, 400, 'options.onGround is not working')
|
||||||
|
|
||||||
player:teleport('', util.vector3(50, -100, 0))
|
player:teleport('', util.vector3(50, -100, 0))
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
testing.expect(player.cell.isExterior, 'teleport to exterior failed')
|
testing.expect(player.cell.isExterior, 'teleport to exterior failed')
|
||||||
testing.expectEqualWithDelta(player.position.x, 50, 1, 'incorrect position after teleporting')
|
testing.expectEqualWithDelta(player.position.x, 50, 1, 'incorrect position after teleporting')
|
||||||
testing.expectEqualWithDelta(player.position.y, -100, 1, 'incorrect position after teleporting')
|
testing.expectEqualWithDelta(player.position.y, -100, 1, 'incorrect position after teleporting')
|
||||||
testing.expectEqualWithDelta(player.rotation.z, math.rad(-90), 0.05, 'teleporting changes rotation')
|
testing.expectEqualWithDelta(player.rotation:getYaw(), math.rad(-90), 0.05, 'teleporting changes rotation')
|
||||||
end
|
end
|
||||||
|
|
||||||
local function initPlayer()
|
local function initPlayer()
|
||||||
player:teleport('', util.vector3(4096, 4096, 867.237), util.vector3(0, 0, 0))
|
player:teleport('', util.vector3(4096, 4096, 867.237), util.transform.identity)
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue