Files
botw/src/KingSystem/Physics/RigidBody/physRigidBodyMotionSensor.cpp
T
2022-12-20 01:58:44 +01:00

405 lines
13 KiB
C++

#include "KingSystem/Physics/RigidBody/physRigidBodyMotionSensor.h"
#include <Havok/Common/Base/hkBase.h>
#include <Havok/Physics2012/Dynamics/Entity/hkpRigidBody.h>
#include "KingSystem/Physics/RigidBody/physRigidBodyMotionEntity.h"
#include "KingSystem/Physics/RigidBody/physRigidBodyParam.h"
#include "KingSystem/Physics/physConversions.h"
namespace ksys::phys {
RigidBodyMotionSensor::RigidBodyMotionSensor(RigidBody* body) : MotionAccessor(body) {}
RigidBodyMotionSensor::~RigidBodyMotionSensor() {
resetLinkedRigidBody();
}
bool RigidBodyMotionSensor::init(const RigidBodyInstanceParam& params, sead::Heap* heap) {
mMaxLinearVelocity = params.max_linear_velocity;
mMaxAngularVelocity = params.max_angular_velocity_rad;
mTimeFactor = params.time_factor;
return true;
}
KSYS_ALWAYS_INLINE void RigidBodyMotionSensor::setTransformImpl(const sead::Matrix34f& mtx) {
if (mBody->isAddedToWorld()) {
setMotionFlag(RigidBody::MotionFlag::DirtyTransform);
return;
}
hkTransformf transform;
toHkTransform(&transform, mtx);
mBody->getHkBody()->getMotion()->setTransform(transform);
}
void RigidBodyMotionSensor::setTransform(const sead::Matrix34f& mtx,
bool propagate_to_linked_motions) {
mTransform = mtx;
setTransformImpl(mtx);
}
void RigidBodyMotionSensor::setPosition(const sead::Vector3f& position,
bool propagate_to_linked_motions) {
if (hasMotionFlagDisabled(RigidBody::MotionFlag::DirtyTransform)) {
getTransform(&mTransform);
}
mTransform.setTranslation(position);
setTransformImpl(mTransform);
}
void RigidBodyMotionSensor::setTransformFromLinkedBody(const sead::Matrix34f& mtx) {
if (mFlags.isOff(Flag::HasExtraTranslateForLinkedRigidBody) &&
mFlags.isOff(Flag::HasExtraRotateForLinkedRigidBody)) {
setTransform(mtx, false);
return;
}
sead::Matrix34f new_mtx = mtx;
sead::Vector3f translate;
if (mFlags.isOn(Flag::HasExtraTranslateForLinkedRigidBody)) {
translate.setMul(mtx, mLinkedBodyExtraTranslate);
} else {
mtx.getTranslation(translate);
}
if (mFlags.isOn(Flag::HasExtraRotateForLinkedRigidBody)) {
sead::Quatf quat;
mtx.toQuat(quat);
quat *= mLinkedBodyExtraRotate;
new_mtx.fromQuat(quat);
}
// This must be done after fromQuat() because fromQuat resets the translation component.
new_mtx.setTranslation(translate);
setTransform(new_mtx, false);
}
static sead::Matrix34f makeSRT(const hkVector4f& s, const hkQuaternionf& r, const hkVector4f& t) {
sead::Vector3f ss;
toVec3(&ss, s);
sead::Quatf rr;
toQuat(&rr, r);
sead::Vector3f tt;
toVec3(&tt, t);
sead::Matrix34f o;
o.makeSQT(ss, rr, tt);
return o;
}
static sead::Matrix34f makeRT(const hkQuaternionf& r, const hkVector4f& t) {
return makeSRT(hkVector4f::getConstant<HK_QUADREAL_1>(), r, t);
}
// NON_MATCHING: two fmul instructions reordered in sead::Matrix34f mtx = makeRT(...)
void RigidBodyMotionSensor::setTransformFromLinkedBody(const hkVector4f& hk_translate,
const hkQuaternionf& hk_rotate) {
if (mFlags.isOff(Flag::HasExtraTranslateForLinkedRigidBody) &&
mFlags.isOff(Flag::HasExtraRotateForLinkedRigidBody)) {
setTransform(makeRT(hk_rotate, hk_translate), false);
return;
}
sead::Matrix34f mtx = makeRT(hk_rotate, hk_translate);
sead::Vector3f translate;
if (mFlags.isOn(Flag::HasExtraTranslateForLinkedRigidBody)) {
translate.setMul(mtx, mLinkedBodyExtraTranslate);
} else {
storeToVec3(&translate, hk_translate);
}
if (mFlags.isOn(Flag::HasExtraRotateForLinkedRigidBody)) {
sead::Quatf quat;
toQuat(&quat, hk_rotate);
quat *= mLinkedBodyExtraRotate;
mtx.fromQuat(quat);
}
mtx.setTranslation(translate);
setTransform(mtx, false);
}
void RigidBodyMotionSensor::getPosition(sead::Vector3f* position) {
if (hasMotionFlagSet(RigidBody::MotionFlag::DirtyTransform)) {
mTransform.getTranslation(*position);
} else {
storeToVec3(position, getRigidBodyMotion()->getPosition());
}
}
void RigidBodyMotionSensor::getRotation(sead::Quatf* rotation) {
if (hasMotionFlagSet(RigidBody::MotionFlag::DirtyTransform)) {
mTransform.toQuat(*rotation);
} else {
toQuat(rotation, getRigidBodyMotion()->getRotation());
}
rotation->normalize();
}
void RigidBodyMotionSensor::getTransform(sead::Matrix34f* mtx) {
if (hasMotionFlagSet(RigidBody::MotionFlag::DirtyTransform)) {
*mtx = mTransform;
} else {
const auto& transform = getRigidBodyMotion()->getTransform();
setMtxRotation(mtx, transform.getRotation());
setMtxTranslation(mtx, transform.getTranslation());
}
}
void RigidBodyMotionSensor::setCenterOfMassInLocal(const sead::Vector3f& center) {
mCenterOfMassInLocal.e = center.e;
if (mBody->isAddedToWorld()) {
setMotionFlag(RigidBody::MotionFlag::DirtyCenterOfMassLocal);
return;
}
mBody->getHkBody()->setCenterOfMassLocal(toHkVec4(center));
}
void RigidBodyMotionSensor::getCenterOfMassInLocal(sead::Vector3f* center) {
if (hasMotionFlagSet(RigidBody::MotionFlag::DirtyCenterOfMassLocal)) {
center->e = mCenterOfMassInLocal.e;
} else {
storeToVec3(center, getRigidBodyMotion()->getCenterOfMassLocal());
}
}
bool RigidBodyMotionSensor::setLinearVelocity(const sead::Vector3f& velocity, float epsilon) {
if (velocity.equals(mLinearVelocity, epsilon))
return false;
mLinearVelocity.e = velocity.e;
setMotionFlag(RigidBody::MotionFlag::DirtyLinearVelocity);
return true;
}
bool RigidBodyMotionSensor::setLinearVelocity(const hkVector4f& velocity, float epsilon) {
sead::Vector3f vec;
storeToVec3(&vec, velocity);
return setLinearVelocity(vec, epsilon);
}
void RigidBodyMotionSensor::getLinearVelocity(sead::Vector3f* velocity) {
if (hasMotionFlagSet(RigidBody::MotionFlag::DirtyLinearVelocity)) {
velocity->e = mLinearVelocity.e;
} else {
storeToVec3(velocity, getRigidBodyMotion()->getLinearVelocity());
}
}
bool RigidBodyMotionSensor::setAngularVelocity(const sead::Vector3f& velocity, float epsilon) {
if (velocity.equals(mAngularVelocity, sead::Mathf::epsilon()))
return false;
mAngularVelocity.e = velocity.e;
setMotionFlag(RigidBody::MotionFlag::DirtyAngularVelocity);
return true;
}
bool RigidBodyMotionSensor::setAngularVelocity(const hkVector4f& velocity, float epsilon) {
sead::Vector3f vec;
storeToVec3(&vec, velocity);
return setAngularVelocity(vec, epsilon);
}
void RigidBodyMotionSensor::getAngularVelocity(sead::Vector3f* velocity) {
if (hasMotionFlagSet(RigidBody::MotionFlag::DirtyAngularVelocity)) {
velocity->e = mAngularVelocity.e;
} else {
storeToVec3(velocity, getRigidBodyMotion()->getAngularVelocity());
}
}
void RigidBodyMotionSensor::setMaxLinearVelocity(float max) {
mMaxLinearVelocity = max;
setMotionFlag(RigidBody::MotionFlag::DirtyMaxVelOrTimeFactor);
}
float RigidBodyMotionSensor::getMaxLinearVelocity() {
if (hasMotionFlagSet(RigidBody::MotionFlag::DirtyMaxVelOrTimeFactor))
return mMaxLinearVelocity;
return getRigidBodyMotion()->getMotionState()->m_maxLinearVelocity;
}
void RigidBodyMotionSensor::setMaxAngularVelocity(float max) {
mMaxAngularVelocity = max;
setMotionFlag(RigidBody::MotionFlag::DirtyMaxVelOrTimeFactor);
}
float RigidBodyMotionSensor::getMaxAngularVelocity() {
if (hasMotionFlagSet(RigidBody::MotionFlag::DirtyMaxVelOrTimeFactor))
return mMaxAngularVelocity;
return getRigidBodyMotion()->getMotionState()->m_maxAngularVelocity;
}
void RigidBodyMotionSensor::setLinkedRigidBody(RigidBody* body) {
auto lock = mBody->makeScopedLock();
if (mLinkedRigidBody == body)
return;
if (mLinkedRigidBody) {
// If we already have a linked rigid body, unlink it first.
mLinkedRigidBody->getEntityMotionAccessorForSensor()->deregisterAccessor(this);
mLinkedRigidBody = nullptr;
}
if (body) {
if (body->isEntity() && mFlags.isOff(Flag::HasLinkedRigidBodyWithoutFlag10)) {
RigidBodyMotionEntity* accessor = body->getEntityMotionAccessorForSensor();
if (accessor && accessor->registerAccessor(this)) {
mLinkedRigidBody = body;
if (mBody->hasFlag(RigidBody::Flag::_10))
mFlags.reset(Flag::HasLinkedRigidBodyWithoutFlag10);
else
mFlags.set(Flag::HasLinkedRigidBodyWithoutFlag10);
}
}
} else {
mLinkedRigidBody = nullptr;
mFlags.reset(Flag::HasLinkedRigidBodyWithoutFlag10);
}
}
void RigidBodyMotionSensor::resetLinkedRigidBody() {
if (!mLinkedRigidBody)
return;
auto lock = mBody->makeScopedLock();
if (mLinkedRigidBody) {
mLinkedRigidBody->getEntityMotionAccessorForSensor()->deregisterAccessor(this);
mLinkedRigidBody = nullptr;
mFlags.reset(Flag::HasLinkedRigidBodyWithoutFlag10);
}
}
RigidBody* RigidBodyMotionSensor::getLinkedRigidBody() const {
return mLinkedRigidBody;
}
bool RigidBodyMotionSensor::isFlag40000Set() const {
return mFlags.isOn(Flag::_40000);
}
void RigidBodyMotionSensor::copyMotionFromLinkedRigidBody() {
auto lock = mBody->makeScopedLock();
auto* accessor = mLinkedRigidBody->getEntityMotionAccessorForSensor();
auto* linked_hk_body = mLinkedRigidBody->getHkBody();
auto* this_hk_body = mBody->getHkBody();
bool reset_needed = false;
if (mFlags.isOn(Flag::HasLinkedRigidBodyWithoutFlag10)) {
if (_14 != accessor->get14()) {
_14 = accessor->get14();
this_hk_body->setShape(linked_hk_body->getCollidable()->getShape());
reset_needed = true;
}
if (_10 != accessor->get10()) {
_10 = accessor->get10();
this_hk_body->updateShape();
reset_needed = true;
}
}
if (mFlags.isOff(Flag::_40000)) {
hkVector4f position;
if (mFlags.isOn(Flag::HasExtraTranslateForLinkedRigidBody)) {
position.setTransformedPos(linked_hk_body->getTransform(),
toHkVec4(mLinkedBodyExtraTranslate));
} else {
position = linked_hk_body->getPosition();
}
hkQuaternionf rotation;
if (mFlags.isOn(Flag::HasExtraRotateForLinkedRigidBody)) {
rotation.setMul(linked_hk_body->getRotation(), toHkQuat(mLinkedBodyExtraRotate));
} else {
rotation = linked_hk_body->getRotation();
}
if (mLinkedRigidBody->getMotionType() != MotionType::Fixed) {
hkVector4f lin_vel;
hkVector4f ang_vel;
mBody->computeVelocities(&lin_vel, &ang_vel, position, rotation);
hkVector4f zero;
zero.setZero();
hkVector4f vel_threshold;
vel_threshold.setAll(0.01);
constexpr auto mask = hkVector4fComparison::MASK_XYZ;
const auto set_velocity = [&](const hkVector4f& velocity, auto get, auto set) {
// abs(vel) > 0.01?
hkVector4f abs_vel;
abs_vel.setAbs(velocity);
if (!vel_threshold.greaterEqual(abs_vel).allAreSet<mask>()) {
this_hk_body->activate();
set(velocity);
} else if (!get().equalZero().template allAreSet<mask>()) {
this_hk_body->activate();
set(zero);
}
};
set_velocity(
lin_vel, [&] { return this_hk_body->getLinearVelocity(); },
[&](const auto& vel) { this_hk_body->setLinearVelocity(vel); });
set_velocity(
ang_vel, [&] { return this_hk_body->getAngularVelocity(); },
[&](const auto& vel) { this_hk_body->setAngularVelocity(vel); });
}
}
if (reset_needed)
hkpRigidBody::updateBroadphaseAndResetCollisionInformationOfWarpedBody(this_hk_body);
}
void RigidBodyMotionSensor::getRotation(hkQuaternionf* quat) {
sead::Quatf rotation;
getRotation(&rotation);
toHkQuat(quat, rotation);
}
void RigidBodyMotionSensor::setTimeFactor(float factor) {
mTimeFactor = factor;
setMotionFlag(RigidBody::MotionFlag::DirtyMaxVelOrTimeFactor);
}
float RigidBodyMotionSensor::getTimeFactor() {
return mTimeFactor;
}
void RigidBodyMotionSensor::freeze(bool freeze, bool preserve_velocities,
bool preserve_max_impulse) {
if (!freeze) {
mBody->setLinearVelocity(mFrozenLinearVelocity);
mBody->setAngularVelocity(mFrozenAngularVelocity);
return;
}
if (preserve_velocities) {
mFrozenLinearVelocity = mBody->getLinearVelocity();
mFrozenAngularVelocity = mBody->getAngularVelocity();
} else {
mFrozenLinearVelocity.set(0, 0, 0);
mFrozenAngularVelocity.set(0, 0, 0);
}
mBody->setLinearVelocity(sead::Vector3f::zero);
mBody->setAngularVelocity(sead::Vector3f::zero);
}
} // namespace ksys::phys