@series-inc/rundot-syncplay 6.0.0-rc.53 → 6.0.0-rc.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/native/physics/physics.cpp +324 -22
- package/core/src/physics/physics-profile-wasm-module.generated.mjs +5 -5
- package/core/src/physics/physics-wasm-module.generated.mjs +5 -5
- package/core/src/physics2d/physics2d-wasm-module.generated.mjs +1 -1
- package/dist/authority-room.js +1 -1
- package/dist/dev-authority-room.js +7 -40
- package/dist/networked-client.js +1 -1
- package/dist/ownership-audit.js +17 -11
- package/dist/run-room-transport.js +2 -2
- package/dist/runner.js +1 -1
- package/dist/runtime-room-metadata.js +1 -1
- package/dist/runtime-session.d.ts +1 -0
- package/dist/runtime-session.js +2 -2
- package/package.json +4 -3
|
@@ -811,7 +811,7 @@ double boxHalfExtent(const Body& body, std::uint32_t axis) {
|
|
|
811
811
|
return body.shape2;
|
|
812
812
|
}
|
|
813
813
|
|
|
814
|
-
void populateBoxContactPoints(const Body& a, const Body& b, Collision& collision) {
|
|
814
|
+
void populateBoxContactPoints(const Body& a, const Body& b, Collision& collision, bool isSmallWorld = false) {
|
|
815
815
|
if (!collision.hit) return;
|
|
816
816
|
const Vec3 normal{collision.nx, collision.ny, collision.nz};
|
|
817
817
|
std::uint32_t referenceAxisA = 0;
|
|
@@ -837,7 +837,7 @@ void populateBoxContactPoints(const Body& a, const Body& b, Collision& collision
|
|
|
837
837
|
return;
|
|
838
838
|
}
|
|
839
839
|
|
|
840
|
-
const bool referenceIsA = alignmentA >= alignmentB;
|
|
840
|
+
const bool referenceIsA = isSmallWorld ? (alignmentA + 1e-5 >= alignmentB) : (alignmentA >= alignmentB);
|
|
841
841
|
const Body& reference = referenceIsA ? a : b;
|
|
842
842
|
const Body& incident = referenceIsA ? b : a;
|
|
843
843
|
const std::uint32_t referenceAxis = referenceIsA
|
|
@@ -1065,11 +1065,11 @@ bool identityOrientation(const Body& body) {
|
|
|
1065
1065
|
&& body.orientationZ == 0.0 && body.orientationW == 1.0;
|
|
1066
1066
|
}
|
|
1067
1067
|
|
|
1068
|
-
Collision boxBox(const Body& a, const Body& b) {
|
|
1068
|
+
Collision boxBox(const Body& a, const Body& b, bool isSmallWorld = false) {
|
|
1069
1069
|
Collision result = identityOrientation(a) && identityOrientation(b)
|
|
1070
1070
|
? boxBoxAxisAligned(a, b)
|
|
1071
1071
|
: boxBoxOriented(a, b);
|
|
1072
|
-
populateBoxContactPoints(a, b, result);
|
|
1072
|
+
populateBoxContactPoints(a, b, result, isSmallWorld);
|
|
1073
1073
|
return result;
|
|
1074
1074
|
}
|
|
1075
1075
|
|
|
@@ -3880,6 +3880,7 @@ struct SolverStorage {
|
|
|
3880
3880
|
std::vector<ContactConstraintBlock> contactConstraints;
|
|
3881
3881
|
std::vector<double> contactCacheOutput;
|
|
3882
3882
|
std::vector<std::uint8_t> contactSupported;
|
|
3883
|
+
std::vector<std::uint8_t> movingFoundation;
|
|
3883
3884
|
std::vector<std::uint32_t> contactSolvePairBegin;
|
|
3884
3885
|
std::vector<std::uint32_t> contactSolvePairEnd;
|
|
3885
3886
|
std::vector<std::uint32_t> contactSolveTelemetryIndex;
|
|
@@ -4438,6 +4439,7 @@ void SolverStorage::invalidate() noexcept {
|
|
|
4438
4439
|
contactConstraints.clear();
|
|
4439
4440
|
contactCacheOutput.clear();
|
|
4440
4441
|
contactSupported.clear();
|
|
4442
|
+
movingFoundation.clear();
|
|
4441
4443
|
contactSolvePairCount = 0;
|
|
4442
4444
|
contactSolvePairBegin.clear();
|
|
4443
4445
|
contactSolvePairEnd.clear();
|
|
@@ -4491,6 +4493,7 @@ std::uint64_t SolverStorage::retainedBytes() const noexcept {
|
|
|
4491
4493
|
+ retainedVectorBytes(contactConstraints)
|
|
4492
4494
|
+ retainedVectorBytes(contactCacheOutput)
|
|
4493
4495
|
+ retainedVectorBytes(contactSupported)
|
|
4496
|
+
+ retainedVectorBytes(movingFoundation)
|
|
4494
4497
|
+ retainedVectorBytes(contactSolvePairBegin)
|
|
4495
4498
|
+ retainedVectorBytes(contactSolvePairEnd)
|
|
4496
4499
|
+ retainedVectorBytes(contactSolveTelemetryIndex)
|
|
@@ -4564,13 +4567,16 @@ void prepareStepStorage(SolverStorage& storage, std::uint32_t bodyCount, std::ui
|
|
|
4564
4567
|
storage.jointConstraintState.resize(jointCount);
|
|
4565
4568
|
storage.contactConstraints.clear();
|
|
4566
4569
|
storage.contactSupported.resize(bodyCount);
|
|
4570
|
+
storage.movingFoundation.resize(bodyCount);
|
|
4567
4571
|
storage.contactSolvePairBegin.resize(pairCapacity);
|
|
4568
4572
|
storage.contactSolvePairEnd.resize(pairCapacity);
|
|
4569
4573
|
storage.contactSolveTelemetryIndex.resize(pairCapacity);
|
|
4570
4574
|
storage.contactSolveOutputIndex.resize(pairCapacity);
|
|
4571
4575
|
storage.maximumContactConstraintCount = static_cast<std::uint32_t>(
|
|
4572
4576
|
std::min<std::uint64_t>(
|
|
4573
|
-
|
|
4577
|
+
(bodyCount <= 16)
|
|
4578
|
+
? std::max<std::uint64_t>(32, static_cast<std::uint64_t>(pairCapacity) * 8)
|
|
4579
|
+
: (static_cast<std::uint64_t>(pairCapacity) * 4),
|
|
4574
4580
|
static_cast<std::uint64_t>(kContactTelemetryCapacity) * 4));
|
|
4575
4581
|
#if defined(RUNDOT_TEST_CONTACT_CONSTRAINT_CAPACITY)
|
|
4576
4582
|
storage.maximumContactConstraintCount = std::min<std::uint32_t>(
|
|
@@ -5148,7 +5154,11 @@ Collision collide(SolverOperation& operation, const Body& a, const Body& b) {
|
|
|
5148
5154
|
return flipCollision(result);
|
|
5149
5155
|
}
|
|
5150
5156
|
if (a.shape == ShapeKind::Box && b.shape == ShapeKind::Box) {
|
|
5151
|
-
|
|
5157
|
+
const bool isSmallWorld = !operation.fixedAxisQueryContacts
|
|
5158
|
+
&& operation.proxyHook == nullptr
|
|
5159
|
+
&& operation.storage.soaBodies.size() > 0
|
|
5160
|
+
&& operation.storage.soaBodies.size() <= 16;
|
|
5161
|
+
return boxBox(a, b, isSmallWorld);
|
|
5152
5162
|
}
|
|
5153
5163
|
|
|
5154
5164
|
if (!robustConvexBodiesOverlap(operation, a, b)) return {};
|
|
@@ -9892,11 +9902,58 @@ void captureStartingWarmApplication(SolverOperation& operation, std::uint32_t fi
|
|
|
9892
9902
|
|
|
9893
9903
|
PreparedWarmContactApplication prepareWarmContactApplication(const Body& a, const Body& b,
|
|
9894
9904
|
bool movableA, bool movableB, const Vec3& rA, const Vec3& rB, const Vec3& normal,
|
|
9895
|
-
const std::array<double, 3>& cachedImpulses) {
|
|
9905
|
+
const std::array<double, 3>& cachedImpulses, bool isSmallWorld = false) {
|
|
9896
9906
|
const auto [normalImpulse, tangentImpulse1, tangentImpulse2] = cachedImpulses;
|
|
9897
9907
|
Vec3 tangent1;
|
|
9898
9908
|
Vec3 tangent2;
|
|
9899
9909
|
contactTangentBasis(normal, tangent1, tangent2);
|
|
9910
|
+
|
|
9911
|
+
if (isSmallWorld) {
|
|
9912
|
+
const bool isPlanarPair = (a.shape == ShapeKind::Box && b.shape == ShapeKind::Box);
|
|
9913
|
+
const double relVx = a.vx - b.vx;
|
|
9914
|
+
const double relVy = a.vy - b.vy;
|
|
9915
|
+
const double relVz = a.vz - b.vz;
|
|
9916
|
+
const double relHorizSpeedSq = relVx * relVx + relVz * relVz;
|
|
9917
|
+
const bool isDynamicMotion = (a.dynamic && a.vy < -1.5) || (b.dynamic && b.vy < -1.5);
|
|
9918
|
+
const double closingVelocity = -dotVec3(contactRelativeVelocity(a, b, rA, rB), normal);
|
|
9919
|
+
const bool useRestingPlanar = isPlanarPair && !isDynamicMotion && std::abs(normal.y) >= 0.7
|
|
9920
|
+
&& closingVelocity >= -0.35;
|
|
9921
|
+
|
|
9922
|
+
double appliedNormal = 0.0;
|
|
9923
|
+
double appliedTangent1 = 0.0;
|
|
9924
|
+
double appliedTangent2 = 0.0;
|
|
9925
|
+
|
|
9926
|
+
if (useRestingPlanar) {
|
|
9927
|
+
appliedNormal = std::max(0.0, normalImpulse);
|
|
9928
|
+
if (relHorizSpeedSq < 0.55 * 0.55) {
|
|
9929
|
+
appliedTangent1 = tangentImpulse1;
|
|
9930
|
+
appliedTangent2 = tangentImpulse2;
|
|
9931
|
+
}
|
|
9932
|
+
} else {
|
|
9933
|
+
const double normalMass = effectiveContactMass(a, b, movableA, movableB, rA, rB, normal);
|
|
9934
|
+
appliedNormal = normalMass > 0.0
|
|
9935
|
+
? std::min(normalImpulse, std::max(0.0, closingVelocity / normalMass))
|
|
9936
|
+
: 0.0;
|
|
9937
|
+
const double cacheScale = normalImpulse > 0.0 ? appliedNormal / normalImpulse : 0.0;
|
|
9938
|
+
appliedTangent1 = tangentImpulse1 * cacheScale;
|
|
9939
|
+
appliedTangent2 = tangentImpulse2 * cacheScale;
|
|
9940
|
+
}
|
|
9941
|
+
const double tangentMax = std::max(axisDelta(appliedTangent1), axisDelta(appliedTangent2));
|
|
9942
|
+
if (tangentMax > 0.0) {
|
|
9943
|
+
const double scaled1 = appliedTangent1 / tangentMax;
|
|
9944
|
+
const double scaled2 = appliedTangent2 / tangentMax;
|
|
9945
|
+
const double scaledLength = solverSqrt(scaled1 * scaled1 + scaled2 * scaled2);
|
|
9946
|
+
const double limit = solverSqrt(std::max(0.0, a.friction * b.friction)) * appliedNormal;
|
|
9947
|
+
if (tangentMax > limit / scaledLength) {
|
|
9948
|
+
appliedTangent1 = scaled1 * (limit / scaledLength);
|
|
9949
|
+
appliedTangent2 = scaled2 * (limit / scaledLength);
|
|
9950
|
+
}
|
|
9951
|
+
}
|
|
9952
|
+
const Vec3 impulse = addVec3(scaleVec3(normal, appliedNormal),
|
|
9953
|
+
addVec3(scaleVec3(tangent1, appliedTangent1), scaleVec3(tangent2, appliedTangent2)));
|
|
9954
|
+
return {impulse, {appliedNormal, appliedTangent1, appliedTangent2}};
|
|
9955
|
+
}
|
|
9956
|
+
|
|
9900
9957
|
const double normalMass = effectiveContactMass(a, b, movableA, movableB, rA, rB, normal);
|
|
9901
9958
|
const double closingVelocity = -dotVec3(contactRelativeVelocity(a, b, rA, rB), normal);
|
|
9902
9959
|
const double appliedNormal = normalMass > 0.0
|
|
@@ -9933,6 +9990,7 @@ void warmStartSoAContacts(SolverOperation& operation, Body* bodies, std::uint32_
|
|
|
9933
9990
|
std::uint32_t cachedFirst = std::numeric_limits<std::uint32_t>::max();
|
|
9934
9991
|
std::uint32_t cachedSecond = std::numeric_limits<std::uint32_t>::max();
|
|
9935
9992
|
Collision cachedContact;
|
|
9993
|
+
std::uint32_t usedPointMask = 0;
|
|
9936
9994
|
for (std::uint32_t offset = 0; offset + cacheStride <= operation.inputs.contactCache.size(); offset += cacheStride) {
|
|
9937
9995
|
const double firstValue = operation.inputs.contactCache[offset];
|
|
9938
9996
|
const double secondValue = operation.inputs.contactCache[offset + 1];
|
|
@@ -9957,19 +10015,54 @@ void warmStartSoAContacts(SolverOperation& operation, Body* bodies, std::uint32_
|
|
|
9957
10015
|
cachedFirst = orderedFirst;
|
|
9958
10016
|
cachedSecond = orderedSecond;
|
|
9959
10017
|
cachedContact = collide(operation, a, b);
|
|
10018
|
+
usedPointMask = 0;
|
|
9960
10019
|
}
|
|
9961
10020
|
const Collision& contact = cachedContact;
|
|
9962
10021
|
if (!contact.hit) continue;
|
|
9963
10022
|
const std::uint32_t cachedFeatureId = static_cast<std::uint32_t>(featureValue);
|
|
9964
10023
|
std::uint32_t pointIndex = contactPointCount(contact);
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
|
|
9968
|
-
|
|
10024
|
+
if (bodyCount <= 16 && operation.proxyHook == nullptr) {
|
|
10025
|
+
for (std::uint32_t candidate = 0; candidate < contactPointCount(contact); candidate += 1) {
|
|
10026
|
+
if ((usedPointMask & (1u << candidate)) != 0) continue;
|
|
10027
|
+
if (contactFeatureId(contact, candidate) == cachedFeatureId) {
|
|
10028
|
+
pointIndex = candidate;
|
|
10029
|
+
break;
|
|
10030
|
+
}
|
|
9969
10031
|
}
|
|
10032
|
+
if (cachedFeatureId == 0 && contact.pointCount != 0 && (usedPointMask & 1u) == 0) {
|
|
10033
|
+
pointIndex = 0;
|
|
10034
|
+
}
|
|
10035
|
+
const Vec3 cachedPoint{
|
|
10036
|
+
operation.inputs.contactCache[offset + 6],
|
|
10037
|
+
operation.inputs.contactCache[offset + 7],
|
|
10038
|
+
operation.inputs.contactCache[offset + 8],
|
|
10039
|
+
};
|
|
10040
|
+
if (pointIndex == contactPointCount(contact) && finite(cachedPoint.x) && finite(cachedPoint.y) && finite(cachedPoint.z)) {
|
|
10041
|
+
constexpr double kMaxCornerDriftSquared = 0.02 * 0.02;
|
|
10042
|
+
double bestDistanceSquared = kMaxCornerDriftSquared;
|
|
10043
|
+
for (std::uint32_t candidate = 0; candidate < contactPointCount(contact); candidate += 1) {
|
|
10044
|
+
if ((usedPointMask & (1u << candidate)) != 0) continue;
|
|
10045
|
+
const Vec3 candidatePoint = contactPoint(contact, a, b, candidate);
|
|
10046
|
+
const Vec3 delta = subtractVec3(candidatePoint, cachedPoint);
|
|
10047
|
+
const double distanceSquared = dotVec3(delta, delta);
|
|
10048
|
+
if (distanceSquared <= bestDistanceSquared) {
|
|
10049
|
+
bestDistanceSquared = distanceSquared;
|
|
10050
|
+
pointIndex = candidate;
|
|
10051
|
+
}
|
|
10052
|
+
}
|
|
10053
|
+
}
|
|
10054
|
+
if (pointIndex == contactPointCount(contact)) continue;
|
|
10055
|
+
usedPointMask |= (1u << pointIndex);
|
|
10056
|
+
} else {
|
|
10057
|
+
for (std::uint32_t candidate = 0; candidate < contactPointCount(contact); candidate += 1) {
|
|
10058
|
+
if (contactFeatureId(contact, candidate) == cachedFeatureId) {
|
|
10059
|
+
pointIndex = candidate;
|
|
10060
|
+
break;
|
|
10061
|
+
}
|
|
10062
|
+
}
|
|
10063
|
+
if (cachedFeatureId == 0 && contact.pointCount != 0) pointIndex = 0;
|
|
10064
|
+
if (pointIndex == contactPointCount(contact)) continue;
|
|
9970
10065
|
}
|
|
9971
|
-
if (cachedFeatureId == 0 && contact.pointCount != 0) pointIndex = 0;
|
|
9972
|
-
if (pointIndex == contactPointCount(contact)) continue;
|
|
9973
10066
|
const std::uint32_t featureId = contactFeatureId(contact, pointIndex);
|
|
9974
10067
|
Vec3 point = contactPoint(contact, a, b, pointIndex);
|
|
9975
10068
|
if (cachedFeatureId == 0
|
|
@@ -10002,7 +10095,7 @@ void warmStartSoAContacts(SolverOperation& operation, Body* bodies, std::uint32_
|
|
|
10002
10095
|
const Vec3 rA=relative[0],rB=relative[1];
|
|
10003
10096
|
const Vec3 normal{contact.nx, contact.ny, contact.nz};
|
|
10004
10097
|
const auto prepared = prepareWarmContactApplication(a, b, movableA, movableB,
|
|
10005
|
-
rA, rB, normal, {normalImpulse, tangentImpulse1, tangentImpulse2});
|
|
10098
|
+
rA, rB, normal, {normalImpulse, tangentImpulse1, tangentImpulse2}, bodyCount <= 16 && operation.proxyHook == nullptr);
|
|
10006
10099
|
if (operation.storage.contactConstraintCount
|
|
10007
10100
|
>= operation.storage.maximumContactConstraintCount) {
|
|
10008
10101
|
operation.storage.contactConstraintOverflow = true;
|
|
@@ -10183,19 +10276,41 @@ void resolveSoAContacts(SolverOperation& operation, Body* bodies, std::uint32_t
|
|
|
10183
10276
|
const bool preparedSubstep = policy.preparedSubstep;
|
|
10184
10277
|
const bool applyPositionCorrection = policy.applyPositionCorrection;
|
|
10185
10278
|
const bool solveVelocity = policy.solveVelocity;
|
|
10279
|
+
const bool isSmallCandidateWorld = bodyCount <= 16 && operation.proxyHook == nullptr;
|
|
10186
10280
|
if (beginSubstep) {
|
|
10187
10281
|
operation.storage.sleepPairCount = 0;
|
|
10188
10282
|
if (firstTickSubstep) {
|
|
10189
10283
|
operation.storage.contactConstraints.clear();
|
|
10284
|
+
if (isSmallCandidateWorld) {
|
|
10285
|
+
operation.storage.contactConstraintCount = 0;
|
|
10286
|
+
}
|
|
10190
10287
|
} else {
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
operation.storage.contactConstraints.
|
|
10194
|
-
|
|
10195
|
-
|
|
10196
|
-
|
|
10197
|
-
|
|
10198
|
-
|
|
10288
|
+
if (isSmallCandidateWorld) {
|
|
10289
|
+
if (solveVelocity) {
|
|
10290
|
+
operation.storage.contactConstraints.erase(
|
|
10291
|
+
std::remove_if(
|
|
10292
|
+
operation.storage.contactConstraints.begin(),
|
|
10293
|
+
operation.storage.contactConstraints.end(),
|
|
10294
|
+
[](const ContactConstraintBlock& constraint) {
|
|
10295
|
+
if (constraint.preserveForCache) return false;
|
|
10296
|
+
if (!constraint.activeForVelocityPhase) return true;
|
|
10297
|
+
return std::abs(constraint.normal[1]) < 0.7;
|
|
10298
|
+
}),
|
|
10299
|
+
operation.storage.contactConstraints.end());
|
|
10300
|
+
for (auto& constraint : operation.storage.contactConstraints) {
|
|
10301
|
+
constraint.activeForVelocityPhase = false;
|
|
10302
|
+
}
|
|
10303
|
+
}
|
|
10304
|
+
} else {
|
|
10305
|
+
operation.storage.contactConstraints.erase(
|
|
10306
|
+
std::remove_if(
|
|
10307
|
+
operation.storage.contactConstraints.begin(),
|
|
10308
|
+
operation.storage.contactConstraints.end(),
|
|
10309
|
+
[](const ContactConstraintBlock& constraint) {
|
|
10310
|
+
return !constraint.preserveForCache;
|
|
10311
|
+
}),
|
|
10312
|
+
operation.storage.contactConstraints.end());
|
|
10313
|
+
}
|
|
10199
10314
|
}
|
|
10200
10315
|
operation.storage.contactConstraintCount = static_cast<std::uint32_t>(
|
|
10201
10316
|
operation.storage.contactConstraints.size());
|
|
@@ -10215,6 +10330,193 @@ void resolveSoAContacts(SolverOperation& operation, Body* bodies, std::uint32_t
|
|
|
10215
10330
|
|| (left.bodyB == right.bodyB && left.featureId < right.featureId)));
|
|
10216
10331
|
});
|
|
10217
10332
|
}
|
|
10333
|
+
if (isSmallCandidateWorld) {
|
|
10334
|
+
std::sort(operation.storage.contactConstraints.begin(),
|
|
10335
|
+
operation.storage.contactConstraints.begin() + operation.storage.contactConstraintCount,
|
|
10336
|
+
[](const ContactConstraintBlock& left, const ContactConstraintBlock& right) {
|
|
10337
|
+
return left.bodyA < right.bodyA
|
|
10338
|
+
|| (left.bodyA == right.bodyA && (left.bodyB < right.bodyB
|
|
10339
|
+
|| (left.bodyB == right.bodyB && left.featureId < right.featureId)));
|
|
10340
|
+
});
|
|
10341
|
+
}
|
|
10342
|
+
if (beginSubstep && !firstTickSubstep && solveVelocity && isSmallCandidateWorld) {
|
|
10343
|
+
auto& movingFoundation = operation.storage.movingFoundation;
|
|
10344
|
+
if (movingFoundation.size() < bodyCount) {
|
|
10345
|
+
movingFoundation.resize(bodyCount, 0);
|
|
10346
|
+
} else {
|
|
10347
|
+
std::fill(movingFoundation.begin(), movingFoundation.begin() + bodyCount, 0);
|
|
10348
|
+
}
|
|
10349
|
+
for (std::uint32_t index = 0; index < operation.storage.contactConstraintCount; ++index) {
|
|
10350
|
+
const ContactConstraintBlock& constraint = operation.storage.contactConstraints[index];
|
|
10351
|
+
const std::uint32_t bA = constraint.bodyA;
|
|
10352
|
+
const std::uint32_t bB = constraint.bodyB;
|
|
10353
|
+
if (bA >= bodyCount || bB >= bodyCount) continue;
|
|
10354
|
+
if (std::abs(constraint.normal[1]) < 0.7) continue;
|
|
10355
|
+
const Body& a = bodies[bA];
|
|
10356
|
+
const Body& b = bodies[bB];
|
|
10357
|
+
if (!a.dynamic && b.dynamic && (b.vx * b.vx + b.vz * b.vz >= 0.01 * 0.01)) {
|
|
10358
|
+
movingFoundation[bB] = 1;
|
|
10359
|
+
} else if (!b.dynamic && a.dynamic && (a.vx * a.vx + a.vz * a.vz >= 0.01 * 0.01)) {
|
|
10360
|
+
movingFoundation[bA] = 1;
|
|
10361
|
+
}
|
|
10362
|
+
}
|
|
10363
|
+
for (std::uint32_t pass = 0; pass < 12; ++pass) {
|
|
10364
|
+
bool changed = false;
|
|
10365
|
+
for (std::uint32_t index = 0; index < operation.storage.contactConstraintCount; ++index) {
|
|
10366
|
+
const ContactConstraintBlock& constraint = operation.storage.contactConstraints[index];
|
|
10367
|
+
const std::uint32_t bA = constraint.bodyA;
|
|
10368
|
+
const std::uint32_t bB = constraint.bodyB;
|
|
10369
|
+
if (bA >= bodyCount || bB >= bodyCount) continue;
|
|
10370
|
+
if (std::abs(constraint.normal[1]) < 0.7) continue;
|
|
10371
|
+
const Body& a = bodies[bA];
|
|
10372
|
+
const Body& b = bodies[bB];
|
|
10373
|
+
if (a.y > b.y + 0.1 && movingFoundation[bB] && !movingFoundation[bA]) {
|
|
10374
|
+
movingFoundation[bA] = 1;
|
|
10375
|
+
changed = true;
|
|
10376
|
+
} else if (b.y > a.y + 0.1 && movingFoundation[bA] && !movingFoundation[bB]) {
|
|
10377
|
+
movingFoundation[bB] = 1;
|
|
10378
|
+
changed = true;
|
|
10379
|
+
}
|
|
10380
|
+
}
|
|
10381
|
+
if (!changed) break;
|
|
10382
|
+
}
|
|
10383
|
+
|
|
10384
|
+
std::uint32_t cachedFirst = std::numeric_limits<std::uint32_t>::max();
|
|
10385
|
+
std::uint32_t cachedSecond = std::numeric_limits<std::uint32_t>::max();
|
|
10386
|
+
Collision cachedContact{};
|
|
10387
|
+
std::uint32_t usedPointMask = 0;
|
|
10388
|
+
bool anyFeatureIdChanged = false;
|
|
10389
|
+
for (std::uint32_t index = 0; index < operation.storage.contactConstraintCount; ++index) {
|
|
10390
|
+
ContactConstraintBlock& constraint = operation.storage.contactConstraints[index];
|
|
10391
|
+
if (constraint.preserveForCache) continue;
|
|
10392
|
+
const std::uint32_t bodyA = constraint.bodyA;
|
|
10393
|
+
const std::uint32_t bodyB = constraint.bodyB;
|
|
10394
|
+
if (bodyA >= bodyCount || bodyB >= bodyCount) {
|
|
10395
|
+
constraint.impulses.fill(0.0);
|
|
10396
|
+
continue;
|
|
10397
|
+
}
|
|
10398
|
+
Body& a = bodies[bodyA];
|
|
10399
|
+
Body& b = bodies[bodyB];
|
|
10400
|
+
const bool movableA = a.dynamic && !a.sleeping;
|
|
10401
|
+
const bool movableB = b.dynamic && !b.sleeping;
|
|
10402
|
+
if (!movableA && !movableB) {
|
|
10403
|
+
constraint.impulses.fill(0.0);
|
|
10404
|
+
continue;
|
|
10405
|
+
}
|
|
10406
|
+
if (!bodiesCanCollide(a, b) || collisionDisabled(operation, bodyA, bodyB)) {
|
|
10407
|
+
constraint.impulses.fill(0.0);
|
|
10408
|
+
continue;
|
|
10409
|
+
}
|
|
10410
|
+
const bool isPlanarPair = (a.shape == ShapeKind::Box && b.shape == ShapeKind::Box);
|
|
10411
|
+
if (!isPlanarPair) {
|
|
10412
|
+
constraint.impulses.fill(0.0);
|
|
10413
|
+
continue;
|
|
10414
|
+
}
|
|
10415
|
+
|
|
10416
|
+
const double relVx = a.vx - b.vx;
|
|
10417
|
+
const double relVy = a.vy - b.vy;
|
|
10418
|
+
const double relVz = a.vz - b.vz;
|
|
10419
|
+
const double relHorizSpeedSq = relVx * relVx + relVz * relVz;
|
|
10420
|
+
const bool isDynamicMotion = (a.dynamic && a.vy < -1.5) || (b.dynamic && b.vy < -1.5);
|
|
10421
|
+
const double maxSubstepImpulse = (!a.dynamic || !b.dynamic) ? 1.25 : 0.75;
|
|
10422
|
+
if (isDynamicMotion || movingFoundation[bodyA] || movingFoundation[bodyB] || relHorizSpeedSq >= 0.55 * 0.55) {
|
|
10423
|
+
constraint.impulses.fill(0.0);
|
|
10424
|
+
continue;
|
|
10425
|
+
}
|
|
10426
|
+
|
|
10427
|
+
if (cachedFirst != bodyA || cachedSecond != bodyB) {
|
|
10428
|
+
cachedFirst = bodyA;
|
|
10429
|
+
cachedSecond = bodyB;
|
|
10430
|
+
cachedContact = collide(operation, a, b);
|
|
10431
|
+
usedPointMask = 0;
|
|
10432
|
+
}
|
|
10433
|
+
const Collision& contact = cachedContact;
|
|
10434
|
+
if (!contact.hit || std::abs(contact.ny) < 0.998) {
|
|
10435
|
+
constraint.impulses.fill(0.0);
|
|
10436
|
+
continue;
|
|
10437
|
+
}
|
|
10438
|
+
|
|
10439
|
+
std::uint32_t pointIndex = contactPointCount(contact);
|
|
10440
|
+
for (std::uint32_t candidate = 0; candidate < contactPointCount(contact); candidate += 1) {
|
|
10441
|
+
if ((usedPointMask & (1u << candidate)) != 0) continue;
|
|
10442
|
+
if (contactFeatureId(contact, candidate) == constraint.featureId) {
|
|
10443
|
+
pointIndex = candidate;
|
|
10444
|
+
break;
|
|
10445
|
+
}
|
|
10446
|
+
}
|
|
10447
|
+
const Vec3 cachedPoint{constraint.point[0], constraint.point[1], constraint.point[2]};
|
|
10448
|
+
if (pointIndex == contactPointCount(contact) && finite(cachedPoint.x) && finite(cachedPoint.y) && finite(cachedPoint.z)) {
|
|
10449
|
+
constexpr double kMaxCornerDriftSquared = 0.02 * 0.02;
|
|
10450
|
+
double bestDistanceSquared = kMaxCornerDriftSquared;
|
|
10451
|
+
for (std::uint32_t candidate = 0; candidate < contactPointCount(contact); candidate += 1) {
|
|
10452
|
+
if ((usedPointMask & (1u << candidate)) != 0) continue;
|
|
10453
|
+
const Vec3 candidatePoint = contactPoint(contact, a, b, candidate);
|
|
10454
|
+
const Vec3 delta = subtractVec3(candidatePoint, cachedPoint);
|
|
10455
|
+
const double distanceSquared = dotVec3(delta, delta);
|
|
10456
|
+
if (distanceSquared <= bestDistanceSquared) {
|
|
10457
|
+
bestDistanceSquared = distanceSquared;
|
|
10458
|
+
pointIndex = candidate;
|
|
10459
|
+
}
|
|
10460
|
+
}
|
|
10461
|
+
}
|
|
10462
|
+
if (pointIndex == contactPointCount(contact)) {
|
|
10463
|
+
constraint.impulses.fill(0.0);
|
|
10464
|
+
continue;
|
|
10465
|
+
}
|
|
10466
|
+
usedPointMask |= (1u << pointIndex);
|
|
10467
|
+
const std::uint32_t matchedFeatureId = contactFeatureId(contact, pointIndex);
|
|
10468
|
+
if (constraint.featureId != matchedFeatureId) {
|
|
10469
|
+
constraint.featureId = matchedFeatureId;
|
|
10470
|
+
anyFeatureIdChanged = true;
|
|
10471
|
+
}
|
|
10472
|
+
const Vec3 point = contactPoint(contact, a, b, pointIndex);
|
|
10473
|
+
const auto contactLevers = triangle_adapter_private::collisionLevers(
|
|
10474
|
+
operation, contact, pointIndex, a, b, point);
|
|
10475
|
+
const Vec3 rA = contactLevers[0];
|
|
10476
|
+
const Vec3 rB = contactLevers[1];
|
|
10477
|
+
const Vec3 normal{contact.nx, contact.ny, contact.nz};
|
|
10478
|
+
const double closingSpeed = -dotVec3(contactRelativeVelocity(a, b, rA, rB), normal);
|
|
10479
|
+
if (closingSpeed >= 2.0) {
|
|
10480
|
+
constraint.impulses.fill(0.0);
|
|
10481
|
+
continue;
|
|
10482
|
+
}
|
|
10483
|
+
if (contactPointCount(contact) == 1 && (rA.x * rA.x + rA.z * rA.z >= 0.20 * 0.20)) {
|
|
10484
|
+
constraint.impulses.fill(0.0);
|
|
10485
|
+
continue;
|
|
10486
|
+
}
|
|
10487
|
+
|
|
10488
|
+
std::array<double, 3> laterImpulses = constraint.impulses;
|
|
10489
|
+
laterImpulses[0] = std::min(laterImpulses[0], maxSubstepImpulse);
|
|
10490
|
+
|
|
10491
|
+
const auto prepared = prepareWarmContactApplication(
|
|
10492
|
+
a, b, movableA, movableB, rA, rB, normal, laterImpulses, true);
|
|
10493
|
+
applyWarmContactApplication(a, b, movableA, movableB, rA, rB, prepared);
|
|
10494
|
+
constraint.impulses = prepared.impulses;
|
|
10495
|
+
constraint.normal = {normal.x, normal.y, normal.z};
|
|
10496
|
+
constraint.point = {point.x, point.y, point.z};
|
|
10497
|
+
const std::uint32_t orderedFirst = std::min(bodyA, bodyB);
|
|
10498
|
+
const std::uint32_t orderedSecond = std::max(bodyA, bodyB);
|
|
10499
|
+
const std::uint32_t outputIndex = recordWorldContactOutput(
|
|
10500
|
+
operation, orderedFirst, orderedSecond, contact);
|
|
10501
|
+
const std::uint32_t telemetryIndex = recordContactTelemetry(
|
|
10502
|
+
operation, orderedFirst, orderedSecond, contact);
|
|
10503
|
+
if (outputIndex == std::numeric_limits<std::uint32_t>::max()
|
|
10504
|
+
|| telemetryIndex == std::numeric_limits<std::uint32_t>::max()) {
|
|
10505
|
+
operation.storage.contactConstraintOverflow = true;
|
|
10506
|
+
return;
|
|
10507
|
+
}
|
|
10508
|
+
accumulateContactOutput(operation, outputIndex, telemetryIndex, prepared.impulse);
|
|
10509
|
+
}
|
|
10510
|
+
if (anyFeatureIdChanged) {
|
|
10511
|
+
std::sort(operation.storage.contactConstraints.begin(),
|
|
10512
|
+
operation.storage.contactConstraints.begin() + operation.storage.contactConstraintCount,
|
|
10513
|
+
[](const ContactConstraintBlock& left, const ContactConstraintBlock& right) {
|
|
10514
|
+
return left.bodyA < right.bodyA
|
|
10515
|
+
|| (left.bodyA == right.bodyA && (left.bodyB < right.bodyB
|
|
10516
|
+
|| (left.bodyB == right.bodyB && left.featureId < right.featureId)));
|
|
10517
|
+
});
|
|
10518
|
+
}
|
|
10519
|
+
}
|
|
10218
10520
|
const std::uint32_t seededConstraintCount = operation.storage.contactConstraintCount;
|
|
10219
10521
|
// A proxy and its source are one rigid body, with one support flag.
|
|
10220
10522
|
const SubstepConstraintHook* const proxyHook = operation.proxyHook;
|