Several collision fixes

This commit is contained in:
Jack Andersen 2017-12-17 16:54:50 -10:00
parent 2a8edf9da8
commit 73ae278c87
29 changed files with 114 additions and 110 deletions

View File

@ -69,7 +69,7 @@ bool ReadMLVLToBlender(hecl::BlenderConnection& conn,
zeus::CVector3f pvAvg; zeus::CVector3f pvAvg;
for (const atVec3f& pv : dock.planeVerts) for (const atVec3f& pv : dock.planeVerts)
pvAvg += pv; pvAvg += pv;
pvAvg /= dock.planeVerts.size(); pvAvg /= zeus::CVector3f(dock.planeVerts.size());
int idx = 0; int idx = 0;
for (const atVec3f& pv : dock.planeVerts) for (const atVec3f& pv : dock.planeVerts)
{ {

View File

@ -55,7 +55,7 @@ RigInverter<CINFType>::Bone::Bone(const CINFType& cinf, const typename CINFType:
m_tail += chBone.origin; m_tail += chBone.origin;
} }
} }
m_tail /= float(actualChildren); m_tail /= zeus::CVector3f(float(actualChildren));
if ((m_tail - boneOrigin).magSquared() < 0.001f) if ((m_tail - boneOrigin).magSquared() < 0.001f)
m_tail = naturalTail; m_tail = naturalTail;
else if (isLCTR) else if (isLCTR)

View File

@ -41,7 +41,7 @@ CMapUniverse::CMapWorldData::CMapWorldData(CInputStream& in, u32 version)
for (const zeus::CTransform& xf : x44_hexagonXfs) for (const zeus::CTransform& xf : x44_hexagonXfs)
x64_centerPoint += xf.origin; x64_centerPoint += xf.origin;
x64_centerPoint *= 1.0f / float(x44_hexagonXfs.size()); x64_centerPoint *= zeus::CVector3f(1.0f / float(x44_hexagonXfs.size()));
} }
void CMapUniverse::Draw(const CMapUniverseDrawParms& parms, const zeus::CVector3f&, float, float) const void CMapUniverse::Draw(const CMapUniverseDrawParms& parms, const zeus::CVector3f&, float, float) const

View File

@ -89,7 +89,7 @@ void CBSAttack::UpdatePhysicsActor(CBodyController& bc, float dt)
zeus::CVector3f delta = x20_targetPos - act->GetTranslation(); zeus::CVector3f delta = x20_targetPos - act->GetTranslation();
float td = x30_alignTargetPosTime - x2c_alignTargetPosStartTime; float td = x30_alignTargetPosTime - x2c_alignTargetPosStartTime;
if (dt > 0.f) if (dt > 0.f)
delta *= dt / td; delta *= zeus::CVector3f(dt / td);
zeus::CVector3f localDelta = act->GetTransform().transposeRotate(delta); zeus::CVector3f localDelta = act->GetTransform().transposeRotate(delta);
act->ApplyImpulseWR(act->GetMoveToORImpulseWR(localDelta, dt), zeus::CAxisAngle::sIdentity); act->ApplyImpulseWR(act->GetMoveToORImpulseWR(localDelta, dt), zeus::CAxisAngle::sIdentity);
} }

View File

@ -50,22 +50,22 @@ void CBodyStateCmdMgr::BlendSteeringCmds()
if (x3c_steeringSpeed > FLT_EPSILON) if (x3c_steeringSpeed > FLT_EPSILON)
{ {
float stepMul = 1.f / x3c_steeringSpeed; float stepMul = 1.f / x3c_steeringSpeed;
xc_face *= stepMul; xc_face *= zeus::CVector3f(stepMul);
switch (x30_steeringMode) switch (x30_steeringMode)
{ {
case ESteeringBlendMode::Normal: case ESteeringBlendMode::Normal:
x0_move *= stepMul; x0_move *= zeus::CVector3f(stepMul);
break; break;
case ESteeringBlendMode::FullSpeed: case ESteeringBlendMode::FullSpeed:
if (!zeus::close_enough(x0_move, zeus::CVector3f::skZero, 0.0001f)) if (!zeus::close_enough(x0_move, zeus::CVector3f::skZero, 0.0001f))
{ {
x0_move.normalize(); x0_move.normalize();
x0_move *= x38_steeringSpeedMax; x0_move *= zeus::CVector3f(x38_steeringSpeedMax);
} }
break; break;
case ESteeringBlendMode::Clamped: case ESteeringBlendMode::Clamped:
x0_move *= stepMul; x0_move *= zeus::CVector3f(stepMul);
if (!zeus::close_enough(x0_move, zeus::CVector3f::skZero, 0.0001f)) if (!zeus::close_enough(x0_move, zeus::CVector3f::skZero, 0.0001f))
{ {
if (x0_move.magnitude() < x34_steeringSpeedMin) if (x0_move.magnitude() < x34_steeringSpeedMin)

View File

@ -321,8 +321,8 @@ void CGroundMovement::MoveGroundColliderXY(CAreaCollisionCache& cache, CStateMan
if (floorFilteredList.GetCount() == 0 && isPlayer) if (floorFilteredList.GetCount() == 0 && isPlayer)
{ {
CMotionState lastNonCollideState = actor.GetLastNonCollidingState(); CMotionState lastNonCollideState = actor.GetLastNonCollidingState();
lastNonCollideState.x1c_velocity *= 0.5f; lastNonCollideState.x1c_velocity *= zeus::CVector3f(0.5f);
lastNonCollideState.x28_angularMomentum *= 0.5f; lastNonCollideState.x28_angularMomentum *= zeus::CVector3f(0.5f);
actor.SetMotionState(lastNonCollideState); actor.SetMotionState(lastNonCollideState);
} }
for (const CCollisionInfo& info : floorFilteredList) for (const CCollisionInfo& info : floorFilteredList)
@ -434,10 +434,10 @@ void CGroundMovement::MoveGroundCollider_New(CStateManager& mgr, CPhysicsActor&
} }
CPhysicsState physStatePre = actor.GetPhysicsState(); CPhysicsState physStatePre = actor.GetPhysicsState();
CMaterialList material = MoveObjectAnalytical(mgr, actor, dt, useNearList, cache, opts, result); CMaterialList material2 = MoveObjectAnalytical(mgr, actor, dt, useNearList, cache, opts, result);
CPhysicsState physStatePost = actor.GetPhysicsState(); CPhysicsState physStatePost = actor.GetPhysicsState();
if (material.XOR({EMaterialTypes::Unknown})) if (material2.XOR({EMaterialTypes::Unknown}))
{ {
SMovementOptions optsCopy = opts; SMovementOptions optsCopy = opts;
zeus::CVector3f postToPre = physStatePre.GetTranslation() - physStatePost.GetTranslation(); zeus::CVector3f postToPre = physStatePre.GetTranslation() - physStatePost.GetTranslation();
@ -470,38 +470,38 @@ void CGroundMovement::MoveGroundCollider_New(CStateManager& mgr, CPhysicsActor&
{ {
actor.SetTranslation(actor.GetTranslation() + zeus::CVector3f(0.f, 0.f, useStepUp)); actor.SetTranslation(actor.GetTranslation() + zeus::CVector3f(0.f, 0.f, useStepUp));
SMoveObjectResult result; SMoveObjectResult result2;
CMaterialList material = MoveObjectAnalytical(mgr, actor, dt, useNearList, cache, optsCopy, result); CMaterialList material3 = MoveObjectAnalytical(mgr, actor, dt, useNearList, cache, optsCopy, result2);
CCollisionInfo collisionInfo; CCollisionInfo collisionInfo2;
double useStepDown = useStepUp + stepDown; double useStepDown2 = useStepUp + stepDown;
TUniqueId id = kInvalidUniqueId; TUniqueId id2 = kInvalidUniqueId;
if (useStepDown > 0.0) if (useStepDown2 > 0.0)
{ {
CGameCollision::DetectCollision_Cached_Moving(mgr, cache, *actor.GetCollisionPrimitive(), CGameCollision::DetectCollision_Cached_Moving(mgr, cache, *actor.GetCollisionPrimitive(),
actor.GetTransform(), actor.GetMaterialFilter(), actor.GetTransform(), actor.GetMaterialFilter(),
useNearList, {0.f, 0.f, -1.f}, id, collisionInfo, useNearList, {0.f, 0.f, -1.f}, id2, collisionInfo2,
useStepDown); useStepDown2);
} }
else else
{ {
useStepDown = 0.0; useStepDown2 = 0.0;
} }
float minStep = std::min(useStepUp, useStepDown); float minStep = std::min(useStepUp, useStepDown2);
zeus::CVector3f offsetStep = actor.GetTranslation() - zeus::CVector3f(0.f, 0.f, minStep); zeus::CVector3f offsetStep = actor.GetTranslation() - zeus::CVector3f(0.f, 0.f, minStep);
bool floor = (collisionInfo.IsValid() && bool floor = (collisionInfo2.IsValid() &&
CGameCollision::CanBlock(collisionInfo.GetMaterialLeft(), collisionInfo.GetNormalLeft())); CGameCollision::CanBlock(collisionInfo2.GetMaterialLeft(), collisionInfo2.GetNormalLeft()));
zeus::CVector3f postToPre = physStatePre.GetTranslation() - offsetStep; zeus::CVector3f postToPre2 = physStatePre.GetTranslation() - offsetStep;
float stepDelta = postToPre.magSquared(); float stepDelta = postToPre2.magSquared();
if (floor && postToPreMag < stepDelta) if (floor && postToPreMag < stepDelta)
{ {
useStepDown = std::max(0.0, useStepDown - 0.0005); useStepDown2 = std::max(0.0, useStepDown2 - 0.0005);
actor.SetTranslation(actor.GetTranslation() - zeus::CVector3f(0.f, 0.f, useStepDown)); actor.SetTranslation(actor.GetTranslation() - zeus::CVector3f(0.f, 0.f, useStepDown2));
physStateList.push_back(actor.GetPhysicsState()); physStateList.push_back(actor.GetPhysicsState());
stepDeltaList.push_back(stepDelta); stepDeltaList.push_back(stepDelta);
collisionInfoList.push_back(collisionInfo); collisionInfoList.push_back(collisionInfo2);
uniqueIdList.push_back(id); uniqueIdList.push_back(id2);
materialListList.push_back(material); materialListList.push_back(material3);
} }
} }
} }
@ -509,6 +509,7 @@ void CGroundMovement::MoveGroundCollider_New(CStateManager& mgr, CPhysicsActor&
if (physStateList.size() == 0) if (physStateList.size() == 0)
{ {
actor.SetPhysicsState(physStatePost); actor.SetPhysicsState(physStatePost);
material = material2;
} }
else else
{ {
@ -567,10 +568,10 @@ void CGroundMovement::MoveGroundCollider_New(CStateManager& mgr, CPhysicsActor&
if (doStepDown) if (doStepDown)
{ {
CCollisionInfo collisionInfo; CCollisionInfo collisionInfo;
double stepDown = actor.GetStepDownHeight(); double stepDown2 = actor.GetStepDownHeight();
float zOffset = 0.f; float zOffset = 0.f;
TUniqueId id = kInvalidUniqueId; TUniqueId id = kInvalidUniqueId;
if (stepDown > FLT_EPSILON) if (stepDown2 > FLT_EPSILON)
{ {
zeus::CTransform xf = actor.GetTransform(); zeus::CTransform xf = actor.GetTransform();
xf.origin += zeus::CVector3f(0.f, 0.f, 0.0005f); xf.origin += zeus::CVector3f(0.f, 0.f, 0.0005f);
@ -579,13 +580,13 @@ void CGroundMovement::MoveGroundCollider_New(CStateManager& mgr, CPhysicsActor&
{ {
actor.SetTranslation(xf.origin); actor.SetTranslation(xf.origin);
zOffset = 0.0005f; zOffset = 0.0005f;
stepDown += 0.0005; stepDown2 += 0.0005;
} }
CGameCollision::DetectCollision_Cached_Moving(mgr, cache, *actor.GetCollisionPrimitive(), CGameCollision::DetectCollision_Cached_Moving(mgr, cache, *actor.GetCollisionPrimitive(),
actor.GetTransform(), actor.GetMaterialFilter(), actor.GetTransform(), actor.GetMaterialFilter(),
useNearList, {0.f, 0.f, -1.f}, id, collisionInfo, useNearList, {0.f, 0.f, -1.f}, id, collisionInfo,
stepDown); stepDown2);
} }
if (id != kInvalidUniqueId) if (id != kInvalidUniqueId)
@ -611,8 +612,8 @@ void CGroundMovement::MoveGroundCollider_New(CStateManager& mgr, CPhysicsActor&
else else
{ {
mgr.SendScriptMsg(&actor, kInvalidUniqueId, EScriptObjectMessage::OnFloor); mgr.SendScriptMsg(&actor, kInvalidUniqueId, EScriptObjectMessage::OnFloor);
stepDown = std::max(0.0, stepDown - 0.0005); stepDown2 = std::max(0.0, stepDown2 - 0.0005);
actor.SetTranslation(actor.GetTranslation() - zeus::CVector3f(0.f, 0.f, stepDown)); actor.SetTranslation(actor.GetTranslation() - zeus::CVector3f(0.f, 0.f, stepDown2));
if (TCastToPtr<CScriptPlatform> plat = mgr.ObjectById(id)) if (TCastToPtr<CScriptPlatform> plat = mgr.ObjectById(id))
mgr.SendScriptMsg(plat.GetPtr(), actor.GetUniqueId(), EScriptObjectMessage::AddPlatformRider); mgr.SendScriptMsg(plat.GetPtr(), actor.GetUniqueId(), EScriptObjectMessage::AddPlatformRider);
CGameCollision::SendMaterialMessage(mgr, collisionInfo.GetMaterialLeft(), actor); CGameCollision::SendMaterialMessage(mgr, collisionInfo.GetMaterialLeft(), actor);
@ -795,7 +796,7 @@ CMaterialList CGroundMovement::MoveObjectAnalytical(CStateManager& mgr, CPhysics
if (!CGroundMovement::RemoveNormalComponent(floorPlaneNormal, vel)) if (!CGroundMovement::RemoveNormalComponent(floorPlaneNormal, vel))
vel.z = 0.f; vel.z = 0.f;
if (vel.z > opts.x38_maxPositiveVerticalVelocity) if (vel.z > opts.x38_maxPositiveVerticalVelocity)
vel *= (opts.x38_maxPositiveVerticalVelocity / vel.z); vel *= zeus::CVector3f(opts.x38_maxPositiveVerticalVelocity / vel.z);
if (opts.x18_dampForceAndMomentum) if (opts.x18_dampForceAndMomentum)
{ {
@ -804,6 +805,8 @@ CMaterialList CGroundMovement::MoveObjectAnalytical(CStateManager& mgr, CPhysics
actor.x15c_force = CGroundMovement::CollisionDamping(actor.x15c_force, actor.x15c_force = CGroundMovement::CollisionDamping(actor.x15c_force,
actor.x15c_force.normalized(), actor.x15c_force.normalized(),
collisionNorm, 0.f, 1.f); collisionNorm, 0.f, 1.f);
if (actor.x15c_force.z < -1000000.f)
printf("");
} }
if (actor.x150_momentum.canBeNormalized()) if (actor.x150_momentum.canBeNormalized())
{ {
@ -824,7 +827,7 @@ CMaterialList CGroundMovement::MoveObjectAnalytical(CStateManager& mgr, CPhysics
actor.x15c_force = zeus::CVector3f(0.f, 0.f, actor.x15c_force = zeus::CVector3f(0.f, 0.f,
-(1.f + std::max(opts.x4_waterLandingForceCoefficient * zNormAbs, -(1.f + std::max(opts.x4_waterLandingForceCoefficient * zNormAbs,
opts.x8_minimumWaterLandingForce)) * actor.GetWeight()); opts.x8_minimumWaterLandingForce)) * actor.GetWeight());
vel *= (1.f - opts.x14_waterLandingVelocityReduction); vel *= zeus::CVector3f(1.f - opts.x14_waterLandingVelocityReduction);
} }
} }
@ -834,7 +837,7 @@ CMaterialList CGroundMovement::MoveObjectAnalytical(CStateManager& mgr, CPhysics
{ {
zeus::CVector3f vel = actor.x138_velocity; zeus::CVector3f vel = actor.x138_velocity;
if (actor.x138_velocity.z > opts.x38_maxPositiveVerticalVelocity) if (actor.x138_velocity.z > opts.x38_maxPositiveVerticalVelocity)
vel *= (opts.x38_maxPositiveVerticalVelocity / vel.z); vel *= zeus::CVector3f(opts.x38_maxPositiveVerticalVelocity / vel.z);
actor.SetVelocityWR(vel); actor.SetVelocityWR(vel);
} }

View File

@ -36,7 +36,7 @@ void CIkChain::Solve(zeus::CQuaternion& q1, zeus::CQuaternion& q2, const zeus::C
zeus::CVector3f vecA = q2.transform(x10_); zeus::CVector3f vecA = q2.transform(x10_);
zeus::CVector3f crossVecA = x4_.cross(vecA); zeus::CVector3f crossVecA = x4_.cross(vecA);
float crossAMag = crossVecA.magnitude(); float crossAMag = crossVecA.magnitude();
crossVecA *= (1.f / crossVecA.magnitude()); crossVecA *= zeus::CVector3f(1.f / crossVecA.magnitude());
float angle = std::asin(zeus::min(crossAMag, 1.f)); float angle = std::asin(zeus::min(crossAMag, 1.f));
if (x4_.dot(vecA) < 0.f) if (x4_.dot(vecA) < 0.f)
angle = M_PIF - angle; angle = M_PIF - angle;

View File

@ -224,12 +224,12 @@ bool CCollidableOBBTree::SphereCollideWithLeafMoving(const COBBTree::CLeafData&
{ {
CMetroidAreaCollider::g_DupEdgeList[edgeIdx] = CMetroidAreaCollider::g_DupPrimitiveCheckCount; CMetroidAreaCollider::g_DupEdgeList[edgeIdx] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
CMaterialList edgeMat(x10_tree->GetEdgeMaterial(edgeIdx)); CMaterialList edgeMat(x10_tree->GetEdgeMaterial(edgeIdx));
if (!edgeMat.HasMaterial(EMaterialTypes::TwentyFour)) if (!edgeMat.HasMaterial(EMaterialTypes::NoEdgeCollision))
{ {
int nextIdx = (k + 1) % 3; int nextIdx = (k + 1) % 3;
zeus::CVector3f edgeVec = surf.GetVert(nextIdx) - surf.GetVert(k); zeus::CVector3f edgeVec = surf.GetVert(nextIdx) - surf.GetVert(k);
float edgeVecMag = edgeVec.magnitude(); float edgeVecMag = edgeVec.magnitude();
edgeVec *= (1.f / edgeVecMag); edgeVec *= zeus::CVector3f(1.f / edgeVecMag);
float dirDotEdge = dir.dot(edgeVec); float dirDotEdge = dir.dot(edgeVec);
zeus::CVector3f edgeRej = dir - dirDotEdge * edgeVec; zeus::CVector3f edgeRej = dir - dirDotEdge * edgeVec;
float edgeRejMagSq = edgeRej.magSquared(); float edgeRejMagSq = edgeRej.magSquared();
@ -429,7 +429,7 @@ bool CCollidableOBBTree::AABoxCollideWithLeafMoving(const COBBTree::CLeafData& l
{ {
CMetroidAreaCollider::g_DupEdgeList[edgeIdx] = CMetroidAreaCollider::g_DupPrimitiveCheckCount; CMetroidAreaCollider::g_DupEdgeList[edgeIdx] = CMetroidAreaCollider::g_DupPrimitiveCheckCount;
CMaterialList edgeMat(x10_tree->GetEdgeMaterial(edgeIdx)); CMaterialList edgeMat(x10_tree->GetEdgeMaterial(edgeIdx));
if (!edgeMat.HasMaterial(EMaterialTypes::TwentyFour)) if (!edgeMat.HasMaterial(EMaterialTypes::NoEdgeCollision))
{ {
d = dOut; d = dOut;
const CCollisionEdge& edge = x10_tree->GetEdge(edgeIdx); const CCollisionEdge& edge = x10_tree->GetEdge(edgeIdx);

View File

@ -7,9 +7,9 @@ CCollisionInfo CCollisionInfo::GetSwapped() const
{ {
CCollisionInfo ret; CCollisionInfo ret;
ret.x0_point = x0_point; ret.x0_point = x0_point;
ret.xc_ = xc_; ret.xc_extentX = xc_extentX;
ret.x30_valid = x30_valid; ret.x30_valid = x30_valid;
ret.x31_ = x31_; ret.x31_hasExtents = x31_hasExtents;
ret.x38_materialLeft = x40_materialRight; ret.x38_materialLeft = x40_materialRight;
ret.x40_materialRight = x38_materialLeft; ret.x40_materialRight = x38_materialLeft;
ret.x48_normalLeft = x54_normalRight; ret.x48_normalLeft = x54_normalRight;
@ -26,7 +26,7 @@ void CCollisionInfo::Swap()
zeus::CVector3f CCollisionInfo::GetExtreme() const zeus::CVector3f CCollisionInfo::GetExtreme() const
{ {
return x0_point + xc_ + x18_ + x24_; return x0_point + xc_extentX + x18_extentY + x24_extentZ;
} }
} }

View File

@ -11,11 +11,11 @@ namespace urde
class CCollisionInfo class CCollisionInfo
{ {
zeus::CVector3f x0_point; zeus::CVector3f x0_point;
zeus::CVector3f xc_; zeus::CVector3f xc_extentX;
zeus::CVector3f x18_; zeus::CVector3f x18_extentY;
zeus::CVector3f x24_; zeus::CVector3f x24_extentZ;
bool x30_valid = false; bool x30_valid = false;
bool x31_ = false; bool x31_hasExtents = false;
CMaterialList x38_materialLeft; CMaterialList x38_materialLeft;
CMaterialList x40_materialRight; CMaterialList x40_materialRight;
zeus::CVector3f x48_normalLeft; zeus::CVector3f x48_normalLeft;
@ -24,19 +24,19 @@ public:
CCollisionInfo() = default; CCollisionInfo() = default;
CCollisionInfo(const zeus::CVector3f& point, const CMaterialList& list1, const CMaterialList& list2, CCollisionInfo(const zeus::CVector3f& point, const CMaterialList& list1, const CMaterialList& list2,
const zeus::CVector3f& normalLeft, const zeus::CVector3f& normalRight) const zeus::CVector3f& normalLeft, const zeus::CVector3f& normalRight)
: x0_point(point), x30_valid(true), x31_(false), x38_materialLeft(list2), x40_materialRight(list1), : x0_point(point), x30_valid(true), x31_hasExtents(false), x38_materialLeft(list2), x40_materialRight(list1),
x48_normalLeft(normalLeft), x54_normalRight(normalRight) {} x48_normalLeft(normalLeft), x54_normalRight(normalRight) {}
CCollisionInfo(const zeus::CVector3f& point, const CMaterialList& list1, const CMaterialList& list2, CCollisionInfo(const zeus::CVector3f& point, const CMaterialList& list1, const CMaterialList& list2,
const zeus::CVector3f& normal) const zeus::CVector3f& normal)
: x0_point(point), x30_valid(true), x31_(false), x38_materialLeft(list2), x40_materialRight(list1), : x0_point(point), x30_valid(true), x31_hasExtents(false), x38_materialLeft(list2), x40_materialRight(list1),
x48_normalLeft(normal), x54_normalRight(-normal) {} x48_normalLeft(normal), x54_normalRight(-normal) {}
CCollisionInfo(const zeus::CAABox& aabox, const CMaterialList& list1, const CMaterialList& list2, CCollisionInfo(const zeus::CAABox& aabox, const CMaterialList& list1, const CMaterialList& list2,
const zeus::CVector3f& normalLeft, const zeus::CVector3f& normalRight) const zeus::CVector3f& normalLeft, const zeus::CVector3f& normalRight)
: x0_point(aabox.min), : x0_point(aabox.min),
xc_(aabox.max.x - aabox.min.x, 0.f, 0.f), xc_extentX(aabox.max.x - aabox.min.x, 0.f, 0.f),
x18_(0.f, aabox.max.y - aabox.min.y, 0.f), x18_extentY(0.f, aabox.max.y - aabox.min.y, 0.f),
x24_(0.f, 0.f, aabox.max.z - aabox.min.z), x24_extentZ(0.f, 0.f, aabox.max.z - aabox.min.z),
x30_valid(true), x31_(true), x38_materialLeft(list2), x30_valid(true), x31_hasExtents(true), x38_materialLeft(list2),
x40_materialRight(list1), x48_normalLeft(normalLeft), x40_materialRight(list1), x48_normalLeft(normalLeft),
x54_normalRight(normalRight) x54_normalRight(normalRight)
{} {}
@ -47,10 +47,8 @@ public:
const CMaterialList& GetMaterialRight() const { return x40_materialRight; } const CMaterialList& GetMaterialRight() const { return x40_materialRight; }
zeus::CVector3f GetExtreme() const; zeus::CVector3f GetExtreme() const;
void Swap(); void Swap();
void Transform(const zeus::CTransform&);
zeus::CVector3f GetNormalLeft() const { return x48_normalLeft; } zeus::CVector3f GetNormalLeft() const { return x48_normalLeft; }
zeus::CVector3f GetNormalRight() const { return x54_normalRight; } zeus::CVector3f GetNormalRight() const { return x54_normalRight; }
zeus::CVector3f GetOrigin() const;
zeus::CVector3f GetPoint() const { return x0_point; } zeus::CVector3f GetPoint() const { return x0_point; }
}; };

View File

@ -18,7 +18,6 @@ public:
zeus::CVector3f GetNormal() const; zeus::CVector3f GetNormal() const;
const zeus::CVector3f& GetVert(s32 idx) const { return (&x0_a)[idx]; } const zeus::CVector3f& GetVert(s32 idx) const { return (&x0_a)[idx]; }
const zeus::CVector3f* GetVerts() const { return &x0_a; } const zeus::CVector3f* GetVerts() const { return &x0_a; }
zeus::CVector3f GetPoint(s32) const;
zeus::CPlane GetPlane() const; zeus::CPlane GetPlane() const;
u32 GetSurfaceFlags() const { return x24_flags; } u32 GetSurfaceFlags() const { return x24_flags; }
}; };

View File

@ -150,8 +150,8 @@ void CGameCollision::MoveAndCollide(CStateManager& mgr, CPhysicsActor& actor, fl
if (!filterList1.GetCount() && actor.GetMaterialList().HasMaterial(EMaterialTypes::Player)) if (!filterList1.GetCount() && actor.GetMaterialList().HasMaterial(EMaterialTypes::Player))
{ {
CMotionState mState = actor.GetLastNonCollidingState(); CMotionState mState = actor.GetLastNonCollidingState();
mState.x1c_velocity *= 0.5f; mState.x1c_velocity *= zeus::CVector3f(0.5f);
mState.x28_angularMomentum *= 0.5f; mState.x28_angularMomentum *= zeus::CVector3f(0.5f);
actor.SetMotionState(mState); actor.SetMotionState(mState);
} }
} }
@ -994,8 +994,8 @@ void CGameCollision::CollisionFailsafe(const CStateManager& mgr, CAreaCollisionC
if (!DetectCollisionBoolean_Cached(mgr, cache, prim, actor.GetPrimitiveTransform(), if (!DetectCollisionBoolean_Cached(mgr, cache, prim, actor.GetPrimitiveTransform(),
actor.GetMaterialFilter(), nearList)) actor.GetMaterialFilter(), nearList))
{ {
lastNonCollide.x1c_velocity *= 0.5f; lastNonCollide.x1c_velocity *= zeus::CVector3f(0.5f);
lastNonCollide.x28_angularMomentum *= 0.5f; lastNonCollide.x28_angularMomentum *= zeus::CVector3f(0.5f);
actor.SetLastNonCollidingState(lastNonCollide); actor.SetLastNonCollidingState(lastNonCollide);
//++gDebugPrintCount; //++gDebugPrintCount;
actor.SetNumTicksStuck(0); actor.SetNumTicksStuck(0);
@ -1013,8 +1013,8 @@ void CGameCollision::CollisionFailsafe(const CStateManager& mgr, CAreaCollisionC
else else
{ {
//++gDebugPrintCount; //++gDebugPrintCount;
lastNonCollide.x1c_velocity *= 0.5f; lastNonCollide.x1c_velocity *= zeus::CVector3f(0.5f);
lastNonCollide.x28_angularMomentum *= 0.5f; lastNonCollide.x28_angularMomentum *= zeus::CVector3f(0.5f);
actor.SetLastNonCollidingState(lastNonCollide); actor.SetLastNonCollidingState(lastNonCollide);
} }
} }

View File

@ -31,7 +31,7 @@ enum class EMaterialTypes
CameraPassthrough = 21, CameraPassthrough = 21,
Wood = 22, Wood = 22,
Organic = 23, Organic = 23,
TwentyFour = 24, NoEdgeCollision = 24,
RedundantEdgeOrFlippedTri = 25, RedundantEdgeOrFlippedTri = 25,
SeeThrough = 26, SeeThrough = 26,
ScanPassthrough = 27, ScanPassthrough = 27,

View File

@ -36,9 +36,9 @@ CBooleanSphereAreaCache::CBooleanSphereAreaCache(const zeus::CAABox& aabb, const
{} {}
SBoxEdge::SBoxEdge(const zeus::CAABox& aabb, int idx, const zeus::CVector3f& dir) SBoxEdge::SBoxEdge(const zeus::CAABox& aabb, int idx, const zeus::CVector3f& dir)
: x0_seg(aabb.getEdge(zeus::CAABox::EBoxEdgeId(idx))), x28_dir(x0_seg.dir), x40_end(x0_seg.end), : x0_seg(aabb.getEdge(zeus::CAABox::EBoxEdgeId(idx))), x28_start(x0_seg.x0_start), x40_end(x0_seg.x18_end),
x58_start(x40_end - x28_dir), x70_coDir(x58_start.cross(dir).asNormalized()), x58_delta(x40_end - x28_start), x70_coDir(x58_delta.cross(dir).asNormalized()),
x88_dirCoDirDot(x28_dir.dot(x70_coDir)) x88_dirCoDirDot(x28_start.dot(x70_coDir))
{} {}
static void FlagEdgeIndicesForFace(int face, bool edgeFlags[12]) static void FlagEdgeIndicesForFace(int face, bool edgeFlags[12])
@ -148,7 +148,8 @@ CMovingAABoxComponents::CMovingAABoxComponents(const zeus::CAABox& aabb, const z
} }
for (int i=0 ; i<12 ; ++i) for (int i=0 ; i<12 ; ++i)
x0_edges.push_back(SBoxEdge(aabb, i, dir)); if (edgeFlags[i])
x0_edges.push_back(SBoxEdge(aabb, i, dir));
for (int i=0 ; i<8 ; ++i) for (int i=0 ; i<8 ; ++i)
if (vertFlags[i]) if (vertFlags[i])
@ -701,12 +702,12 @@ bool CMetroidAreaCollider::MovingAABoxCollisionCheck_Edge(const zeus::CVector3f&
{ {
zeus::CVector3d ev0d = ev0; zeus::CVector3d ev0d = ev0;
zeus::CVector3d ev1d = ev1; zeus::CVector3d ev1d = ev1;
if ((edge.x70_coDir.dot(ev0d) >= edge.x88_dirCoDirDot) != if ((edge.x70_coDir.dot(ev1d) >= edge.x88_dirCoDirDot) !=
(edge.x70_coDir.dot(ev1d) >= edge.x88_dirCoDirDot)) (edge.x70_coDir.dot(ev0d) >= edge.x88_dirCoDirDot))
{ {
zeus::CVector3d delta = ev0d - ev1d; zeus::CVector3d delta = ev0d - ev1d;
zeus::CVector3d cross0 = edge.x58_start.cross(delta); zeus::CVector3d cross0 = edge.x58_delta.cross(delta);
if (cross0.magSquared() > DBL_EPSILON) if (cross0.magSquared() >= DBL_EPSILON)
{ {
zeus::CVector3d cross0Norm = cross0.asNormalized(); zeus::CVector3d cross0Norm = cross0.asNormalized();
if (cross0Norm.dot(dir) >= 0.0) if (cross0Norm.dot(dir) >= 0.0)
@ -714,11 +715,11 @@ bool CMetroidAreaCollider::MovingAABoxCollisionCheck_Edge(const zeus::CVector3f&
ev1d = ev0; ev1d = ev0;
ev0d = ev1; ev0d = ev1;
delta = ev0d - ev1d; delta = ev0d - ev1d;
cross0Norm = edge.x58_start.cross(delta).asNormalized(); cross0Norm = edge.x58_delta.cross(delta).asNormalized();
} }
zeus::CVector3d clipped = ev0d + -(ev0d.dot(edge.x70_coDir) - edge.x88_dirCoDirDot) / zeus::CVector3d clipped = ev0d + (-(ev0d.dot(edge.x70_coDir) - edge.x88_dirCoDirDot) /
delta.dot(edge.x70_coDir) * delta; delta.dot(edge.x70_coDir)) * delta;
int maxCompIdx = (std::fabs(edge.x70_coDir.x) > std::fabs(edge.x70_coDir.y)) ? 0 : 1; int maxCompIdx = (std::fabs(edge.x70_coDir.x) > std::fabs(edge.x70_coDir.y)) ? 0 : 1;
if (std::fabs(edge.x70_coDir[maxCompIdx]) < std::fabs(edge.x70_coDir.z)) if (std::fabs(edge.x70_coDir[maxCompIdx]) < std::fabs(edge.x70_coDir.z))
maxCompIdx = 2; maxCompIdx = 2;
@ -740,15 +741,16 @@ bool CMetroidAreaCollider::MovingAABoxCollisionCheck_Edge(const zeus::CVector3f&
break; break;
} }
double mag = edge.x58_start[ci0] * (clipped[ci1] - edge.x28_dir[ci1]) - double mag = (edge.x58_delta[ci0] * (clipped[ci1] - edge.x28_start[ci1]) -
edge.x58_start[ci1] * (clipped[ci0] - edge.x28_dir[ci0]) / edge.x58_delta[ci1] * (clipped[ci0] - edge.x28_start[ci0])) /
edge.x58_start[ci0] * dir[ci1] - edge.x58_start[ci1] * dir[ci0]; (edge.x58_delta[ci0] * dir[ci1] - edge.x58_delta[ci1] * dir[ci0]);
if (mag > 0.0 && mag < d) if (mag >= 0.0 && mag < d)
{ {
zeus::CVector3d clippedMag = clipped - mag * zeus::CVector3d(dir); zeus::CVector3d clippedMag = clipped - mag * zeus::CVector3d(dir);
if ((edge.x28_dir.x - clippedMag).dot(edge.x40_end.x - clippedMag) < 0.0) if ((edge.x28_start - clippedMag).dot(edge.x40_end - clippedMag) < 0.0 && mag < d)
{ {
normal = cross0Norm.asCVector3f(); normal = cross0Norm.asCVector3f();
d = mag;
point = clipped.asCVector3f(); point = clipped.asCVector3f();
ret = true; ret = true;
} }
@ -848,7 +850,7 @@ bool CMetroidAreaCollider::MovingAABoxCollisionCheck_Cached(const COctreeLeafCac
{ {
g_DupEdgeList[edgeIdx] = g_DupPrimitiveCheckCount; g_DupEdgeList[edgeIdx] = g_DupPrimitiveCheckCount;
CMaterialList edgeMat(node.GetOwner().GetEdgeMaterial(edgeIdx)); CMaterialList edgeMat(node.GetOwner().GetEdgeMaterial(edgeIdx));
if (!edgeMat.HasMaterial(EMaterialTypes::TwentyFour)) if (!edgeMat.HasMaterial(EMaterialTypes::NoEdgeCollision))
{ {
d = dOut; d = dOut;
const CCollisionEdge& edge = node.GetOwner().GetEdge(edgeIdx); const CCollisionEdge& edge = node.GetOwner().GetEdge(edgeIdx);
@ -971,12 +973,12 @@ bool CMetroidAreaCollider::MovingSphereCollisionCheck_Cached(const COctreeLeafCa
{ {
g_DupEdgeList[edgeIdx] = g_DupPrimitiveCheckCount; g_DupEdgeList[edgeIdx] = g_DupPrimitiveCheckCount;
CMaterialList edgeMat(node.GetOwner().GetEdgeMaterial(edgeIdx)); CMaterialList edgeMat(node.GetOwner().GetEdgeMaterial(edgeIdx));
if (!edgeMat.HasMaterial(EMaterialTypes::TwentyFour)) if (!edgeMat.HasMaterial(EMaterialTypes::NoEdgeCollision))
{ {
int nextIdx = (k + 1) % 3; int nextIdx = (k + 1) % 3;
zeus::CVector3f edgeVec = surf.GetVert(nextIdx) - surf.GetVert(k); zeus::CVector3f edgeVec = surf.GetVert(nextIdx) - surf.GetVert(k);
float edgeVecMag = edgeVec.magnitude(); float edgeVecMag = edgeVec.magnitude();
edgeVec *= (1.f / edgeVecMag); edgeVec *= zeus::CVector3f(1.f / edgeVecMag);
float dirDotEdge = dir.dot(edgeVec); float dirDotEdge = dir.dot(edgeVec);
zeus::CVector3f edgeRej = dir - dirDotEdge * edgeVec; zeus::CVector3f edgeRej = dir - dirDotEdge * edgeVec;
float edgeRejMagSq = edgeRej.magSquared(); float edgeRejMagSq = edgeRej.magSquared();

View File

@ -64,9 +64,9 @@ public:
struct SBoxEdge struct SBoxEdge
{ {
zeus::CLineSeg x0_seg; zeus::CLineSeg x0_seg;
zeus::CVector3d x28_dir; zeus::CVector3d x28_start;
zeus::CVector3d x40_end; zeus::CVector3d x40_end;
zeus::CVector3d x58_start; zeus::CVector3d x58_delta;
zeus::CVector3d x70_coDir; zeus::CVector3d x70_coDir;
double x88_dirCoDirDot; double x88_dirCoDirDot;
SBoxEdge(const zeus::CAABox& aabb, int idx, const zeus::CVector3f& dir); SBoxEdge(const zeus::CAABox& aabb, int idx, const zeus::CVector3f& dir);

View File

@ -328,14 +328,14 @@ bool RayTriangleIntersection(const zeus::CVector3f& point, const zeus::CVector3f
return false; return false;
zeus::CVector3f v0toPoint = point - verts[0]; zeus::CVector3f v0toPoint = point - verts[0];
float dot1 = v0toPoint.dot(cross0); float dot1 = v0toPoint.dot(cross0);
if (dot1 < 0.0 || dot1 > dot0) if (dot1 < 0.f || dot1 > dot0)
return false; return false;
zeus::CVector3f cross1 = v0toPoint.cross(v0tov1); zeus::CVector3f cross1 = v0toPoint.cross(v0tov1);
float dot2 = cross1.dot(dir); float dot2 = cross1.dot(dir);
if (dot2 < 0.0 || dot1 + dot2 > dot0) if (dot2 < 0.f || dot1 + dot2 > dot0)
return false; return false;
float final = 1.0 / dot0 * cross1.dot(v0tov2); float final = 1.f / dot0 * cross1.dot(v0tov2);
if (final < 0.0 || final >= d) if (final < 0.f || final >= d)
return false; return false;
d = final; d = final;
return true; return true;

View File

@ -429,7 +429,7 @@ void CBooRenderer::DrawFogSlices(const zeus::CPlane* planes, int numPlanes,
zeus::CVector3d planeNormal = plane.normal(); zeus::CVector3d planeNormal = plane.normal();
for (const zeus::CVector3d& vert : verts) for (const zeus::CVector3d& vert : verts)
verts2[vert2Count++] = vert - (planeNormal * (planeNormal.dot(vert) - plane.d)); verts2[vert2Count++] = vert - (planeNormal * zeus::CVector3f(planeNormal.dot(vert) - plane.d));
CalcDrawFogFan(planes, numPlanes, verts2, vert2Count, iteration, 0, fogVol); CalcDrawFogFan(planes, numPlanes, verts2, vert2Count, iteration, 0, fogVol);
} }

View File

@ -20,7 +20,7 @@ void CSpaceWarpFilter::GenerateWarpRampTex(boo::IGraphicsDataFactory::Context& c
if (mag < 1.f && vec.canBeNormalized()) if (mag < 1.f && vec.canBeNormalized())
{ {
vec.normalize(); vec.normalize();
vec *= std::sqrt(mag); vec *= zeus::CVector2f(std::sqrt(mag));
} }
data[y][x][0] = zeus::clamp(0, int((((vec.x / 2.f + 0.5f) - x / float(WARP_RAMP_RES)) + 0.5f) * 255), 255); data[y][x][0] = zeus::clamp(0, int((((vec.x / 2.f + 0.5f) - x / float(WARP_RAMP_RES)) + 0.5f) * 255), 255);
data[y][x][1] = zeus::clamp(0, int((((vec.y / 2.f + 0.5f) - y / float(WARP_RAMP_RES)) + 0.5f) * 255), 255); data[y][x][1] = zeus::clamp(0, int((((vec.y / 2.f + 0.5f) - y / float(WARP_RAMP_RES)) + 0.5f) * 255), 255);

View File

@ -1058,7 +1058,10 @@ void CSamusHud::UpdateHudDamage(float dt, const CStateManager& mgr,
zeus::CVector3f::skForward, zeus::CVector3f::skForward,
zeus::CVector3f::skUp}; zeus::CVector3f::skUp};
for (int i=0 ; i<4 ; ++i) for (int i=0 ; i<4 ; ++i)
vecs[int(rand() / float(RAND_MAX) * 4.f) & 0x3] += (rand() / float(RAND_MAX) - dt) * rotMul; {
int sel = int(rand() / float(RAND_MAX) * 9.f);
vecs[sel & 0x3][(sel / 3) & 0x3] += (rand() / float(RAND_MAX) - dt) * rotMul;
}
x428_decoShakeRotate = zeus::CMatrix3f(vecs[0], vecs[1], vecs[2]).transposed(); x428_decoShakeRotate = zeus::CMatrix3f(vecs[0], vecs[1], vecs[2]).transposed();
transformUpdate = true; transformUpdate = true;
} }

View File

@ -198,7 +198,7 @@ bool CMVEExplode::GetValue(int frame, zeus::CVector3f& pVel, zeus::CVector3f& /*
{ {
float b; float b;
x8_b->GetValue(frame, b); x8_b->GetValue(frame, b);
pVel *= b; pVel *= zeus::CVector3f(b);
} }
return false; return false;

View File

@ -124,8 +124,8 @@ void CParticleSwoosh::UpdateSwooshTranslation(const zeus::CVector3f& translation
void CParticleSwoosh::UpdateTranslationAndOrientation() void CParticleSwoosh::UpdateTranslationAndOrientation()
{ {
x208_maxRadius = 0.f; x208_maxRadius = 0.f;
x1f0_aabbMin = FLT_MAX; x1f0_aabbMin = zeus::CVector3f(FLT_MAX);
x1fc_aabbMax = -FLT_MAX; x1fc_aabbMax = zeus::CVector3f(-FLT_MAX);
CParticleGlobals::SetParticleLifetime(x1b4_LENG); CParticleGlobals::SetParticleLifetime(x1b4_LENG);
CParticleGlobals::SetEmitterTime(x28_curFrame); CParticleGlobals::SetEmitterTime(x28_curFrame);

View File

@ -267,7 +267,6 @@ void CPhysicsActor::SetPhysicsState(const CPhysicsState& state)
xfc_constantForce = state.GetConstantForceWR(); xfc_constantForce = state.GetConstantForceWR();
x108_angularMomentum = state.GetAngularMomentumWR(); x108_angularMomentum = state.GetAngularMomentumWR();
;
x150_momentum = state.GetMomentumWR(); x150_momentum = state.GetMomentumWR();
x15c_force = state.GetForceWR(); x15c_force = state.GetForceWR();
x168_impulse = state.GetImpulseWR(); x168_impulse = state.GetImpulseWR();

View File

@ -2860,7 +2860,7 @@ void CPlayer::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CState
if (result.IsInvalid()) if (result.IsInvalid())
{ {
SetVelocityWR(x138_velocity * 0.095f); SetVelocityWR(x138_velocity * 0.095f);
xfc_constantForce *= 0.095f; xfc_constantForce *= zeus::CVector3f(0.095f);
} }
break; break;
} }

View File

@ -9,7 +9,7 @@
namespace urde namespace urde
{ {
const zeus::CVector3f CScriptCameraPitchVolume::skScaleFactor = {0.5f}; const zeus::CVector3f CScriptCameraPitchVolume::skScaleFactor = zeus::CVector3f(0.5f);
CScriptCameraPitchVolume::CScriptCameraPitchVolume(TUniqueId uid, bool active, std::string_view name, CScriptCameraPitchVolume::CScriptCameraPitchVolume(TUniqueId uid, bool active, std::string_view name,
const CEntityInfo& info, const zeus::CVector3f& scale, const CEntityInfo& info, const zeus::CVector3f& scale,

View File

@ -65,7 +65,7 @@ bool CScriptCoverPoint::Blown(const zeus::CVector3f& point) const
if (ShouldWallHang()) if (ShouldWallHang())
{ {
zeus::CVector3f posDif = point - x34_transform.origin; zeus::CVector3f posDif = point - x34_transform.origin;
posDif *= (1.0 / posDif.magnitude()); posDif *= zeus::CVector3f(1.f / posDif.magnitude());
zeus::CVector3f normDif = posDif.normalized(); zeus::CVector3f normDif = posDif.normalized();
zeus::CVector3f frontVec = x34_transform.frontVector(); zeus::CVector3f frontVec = x34_transform.frontVector();

View File

@ -75,9 +75,9 @@ void CScriptSpecialFunction::ThinkActorScale(float dt, CStateManager& mgr)
zeus::CVector3f scale = mData->GetScale(); zeus::CVector3f scale = mData->GetScale();
if (deltaScale > 0.f) if (deltaScale > 0.f)
scale = zeus::min(deltaScale + scale, {x100_}); scale = zeus::min(zeus::CVector3f(deltaScale) + scale, zeus::CVector3f(x100_));
else else
scale = zeus::max(deltaScale + scale, {x100_}); scale = zeus::max(zeus::CVector3f(deltaScale) + scale, zeus::CVector3f(x100_));
mData->SetScale(scale); mData->SetScale(scale);
} }

View File

@ -43,7 +43,7 @@ CLight CWorldLight::GetAsCGraphicsLight() const
if (x0_type == EWorldLightType::LocalAmbient) if (x0_type == EWorldLightType::LocalAmbient)
{ {
float_color *= q; float_color *= zeus::CVector3f(q);
if (float_color.x >= 1.f) if (float_color.x >= 1.f)
float_color.x = 1.f; float_color.x = 1.f;

View File

@ -1844,7 +1844,7 @@ CEntity* ScriptLoader::LoadPlayerActor(CStateManager& mgr, CInputStream& in, int
list.Add(EMaterialTypes::Solid); list.Add(EMaterialTypes::Solid);
if ((extents.x < 0.f || extents.y < 0.f || extents.z < 0.f) || extents.isZero()) if ((extents.x < 0.f || extents.y < 0.f || extents.z < 0.f) || extents.isZero())
aabox = zeus::CAABox({-.5f}, {0.5f}); aabox = zeus::CAABox(-.5f, 0.5f);
return new CScriptPlayerActor(mgr.AllocateUniqueId(), aHead.x0_name, info, aHead.x10_transform, return new CScriptPlayerActor(mgr.AllocateUniqueId(), aHead.x0_name, info, aHead.x10_transform,
CAnimRes(animParms.GetACSFile(), animParms.GetCharacter(), aHead.x40_scale, CAnimRes(animParms.GetACSFile(), animParms.GetCharacter(), aHead.x40_scale,
@ -2095,9 +2095,9 @@ CEntity* ScriptLoader::LoadWallCrawlerSwarm(CStateManager& mgr, CInputStream& in
u32 w15 = in.readUint32Big(); u32 w15 = in.readUint32Big();
return new CWallCrawlerSwarm(mgr.AllocateUniqueId(), active, aHead.x0_name, info, aHead.x40_scale, return new CWallCrawlerSwarm(mgr.AllocateUniqueId(), active, aHead.x0_name, info, aHead.x40_scale,
aHead.x10_transform, w1, CAnimRes(w2, w3, {1.5f}, w4, true), w5, w6, w7, w8, w9, w10, aHead.x10_transform, w1, CAnimRes(w2, w3, zeus::CVector3f(1.5f), w4, true), w5, w6,
dInfo1, dInfo2, f1, f2, f3, f4, w11, w12, f5, f6, f7, f8, f9, f10, f11, f12, f13, w13, w7, w8, w9, w10, dInfo1, dInfo2, f1, f2, f3, f4, w11, w12, f5, f6, f7, f8, f9, f10,
f14, f15, f16, hInfo, dVulns, w14, w15, aParams); f11, f12, f13, w13, f14, f15, f16, hInfo, dVulns, w14, w15, aParams);
} }
CEntity* ScriptLoader::LoadAiJumpPoint(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) CEntity* ScriptLoader::LoadAiJumpPoint(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info)

@ -1 +1 @@
Subproject commit bb4729d02d7dd34bb6387aee0981199805914ea5 Subproject commit 40ac3c12cab6d0eb3d442586ed7abc31b40b747a