Closed jankrassnigg closed 4 months ago
The assert handler passes the filename and line number, so in principle you could use that to ignore certain asserts (albeit with extra work if the line numbers change).
I'd be interested in seeing why this particular assert triggers. The value is nearly 8 mm from the origin which is quite a lot. Would it be possible to isolate an example when this assert triggers? What I usually do is to go a few levels up in the call stack and copy the values from the debugger in a unit test like here:
That's going to be a bit of work, I'll have to see when I can find the time for that.
If you paste the debugger values here then I can also create the example. I don't know your callstack so it's hard to tell you exactly which values I need, but it probably comes from CollideConvexVsTriangles::Collide in which case I need the values passed to the constructor of that class (should be a bit higher up in the callstack) + all the parameters to the Collide function and I'm going to need all the internal members of inShape1 passed to the constructor. The classes should all be opened up fully so that I can copy paste the float values with their significant digits.
Finally had some time to investigate.
So in my character code I call NarrowPhaseQuery::CollideShape()
directly to detect contacts with the surroundings. See here.
Arguments to NarrowPhaseQuery::CollideShape()
are:
inShape
is a CapsuleShape
with half height 0.00999999978
and radius 0.300000012
.
inShapeScale
is just all 1, inBaseOffset
is all 0.
inCenterOfMassTransform
is
[Row 0] 1.00000000, 0.00000000, 0.00000000, 7.33719635
[Row 1] 0.00000000, -1.19209290e-07, -1.00000012, 16.2943859
[Row 2] 0.00000000, 1.00000012, -1.19209290e-07, 0.0854607671
[Row 3] 0.00000000, 0.00000000, 0.00000000, 1.00000000
inCenterOfMassTransform
was built from a quaternion {x=0.707106829 y=0.00000000 z=0.00000000 w=0.707106829}
and position { x=7.33719635, y=16.2943859, z=0.0854607671 }
.
The callstack looks like this:
Jolt.dll!JPH::EPAPenetrationDepth::GetPenetrationDepthStepEPA<JPH::AddConvexRadius<JPH::ConvexShape::Support>,JPH::TriangleConvexSupport>(const JPH::AddConvexRadius<JPH::ConvexShape::Support> & inAIncludingConvexRadius, const JPH::TriangleConvexSupport & inBIncludingConvexRadius, float inTolerance, JPH::Vec3 & outV, JPH::Vec3 & outPointA, JPH::Vec3 & outPointB) Line 157 C++
Jolt.dll!JPH::CollideConvexVsTriangles::Collide(const JPH::Vec3 inV0, const JPH::Vec3 inV1, const JPH::Vec3 inV2, unsigned char inActiveEdges, const JPH::SubShapeID & inSubShapeID2) Line 99 C++
Jolt.dll!JPH::MeshShape::sCollideConvexVsMesh(const JPH::Shape * inShape1, const JPH::Shape * inShape2, const JPH::Vec3 inScale1, const JPH::Vec3 inScale2, const JPH::Mat44 & inCenterOfMassTransform1, const JPH::Mat44 & inCenterOfMassTransform2, const JPH::SubShapeIDCreator & inSubShapeIDCreator1, const JPH::SubShapeIDCreator & inSubShapeIDCreator2, const JPH::CollideShapeSettings & inCollideShapeSettings, JPH::CollisionCollector<JPH::CollideShapeResult,JPH::CollisionCollectorTraitsCollideShape> & ioCollector, const JPH::ShapeFilter & inShapeFilter) Line 1116 C++
Jolt.dll!JPH::CollisionDispatch::sCollideShapeVsShape(const JPH::Shape * inShape1, const JPH::Shape * inShape2, const JPH::Vec3 inScale1, const JPH::Vec3 inScale2, const JPH::Mat44 & inCenterOfMassTransform1, const JPH::Mat44 & inCenterOfMassTransform2, const JPH::SubShapeIDCreator & inSubShapeIDCreator1, const JPH::SubShapeIDCreator & inSubShapeIDCreator2, const JPH::CollideShapeSettings & inCollideShapeSettings, JPH::CollisionCollector<JPH::CollideShapeResult,JPH::CollisionCollectorTraitsCollideShape> & ioCollector, const JPH::ShapeFilter & inShapeFilter) Line 40 C++
ezJoltPlugin.dll!ezJoltCustomShapeInfo::sCollideShapeVsUser1(const JPH::Shape * inShape1, const JPH::Shape * inShape2, const JPH::Vec3 inScale1, const JPH::Vec3 inScale2, const JPH::Mat44 & inCenterOfMassTransform1, const JPH::Mat44 & inCenterOfMassTransform2, const JPH::SubShapeIDCreator & inSubShapeIDCreator1, const JPH::SubShapeIDCreator & inSubShapeIDCreator2, const JPH::CollideShapeSettings & inCollideShapeSettings, JPH::CollisionCollector<JPH::CollideShapeResult,JPH::CollisionCollectorTraitsCollideShape> & ioCollector, const JPH::ShapeFilter & inShapeFilter) Line 154 C++
Jolt.dll!JPH::CollisionDispatch::sCollideShapeVsShape(const JPH::Shape * inShape1, const JPH::Shape * inShape2, const JPH::Vec3 inScale1, const JPH::Vec3 inScale2, const JPH::Mat44 & inCenterOfMassTransform1, const JPH::Mat44 & inCenterOfMassTransform2, const JPH::SubShapeIDCreator & inSubShapeIDCreator1, const JPH::SubShapeIDCreator & inSubShapeIDCreator2, const JPH::CollideShapeSettings & inCollideShapeSettings, JPH::CollisionCollector<JPH::CollideShapeResult,JPH::CollisionCollectorTraitsCollideShape> & ioCollector, const JPH::ShapeFilter & inShapeFilter) Line 40 C++
Jolt.dll!JPH::TransformedShape::CollideShape(const JPH::Shape * inShape, const JPH::Vec3 inShapeScale, const JPH::Mat44 & inCenterOfMassTransform, const JPH::CollideShapeSettings & inCollideShapeSettings, const JPH::Vec3 inBaseOffset, JPH::CollisionCollector<JPH::CollideShapeResult,JPH::CollisionCollectorTraitsCollideShape> & ioCollector, const JPH::ShapeFilter & inShapeFilter) Line 95 C++
Jolt.dll!`JPH::NarrowPhaseQuery::CollideShape'::`2'::MyCollector::AddHit(const JPH::BodyID & inResult) Line 258 C++
Jolt.dll!JPH::QuadTree::CollideAABox(const JPH::AABox & inBox, JPH::CollisionCollector<JPH::BodyID,JPH::CollisionCollectorTraitsCollideShape> & ioCollector, const JPH::ObjectLayerFilter & inObjectLayerFilter, const std::vector<JPH::QuadTree::Tracking,JPH::STLAllocator<JPH::QuadTree::Tracking>> & inTracking) Line 1149 C++
Jolt.dll!JPH::BroadPhaseQuadTree::CollideAABox(const JPH::AABox & inBox, JPH::CollisionCollector<JPH::BodyID,JPH::CollisionCollectorTraitsCollideShape> & ioCollector, const JPH::BroadPhaseLayerFilter & inBroadPhaseLayerFilter, const JPH::ObjectLayerFilter & inObjectLayerFilter) Line 440 C++
Jolt.dll!JPH::NarrowPhaseQuery::CollideShape(const JPH::Shape * inShape, const JPH::Vec3 inShapeScale, const JPH::Mat44 & inCenterOfMassTransform, const JPH::CollideShapeSettings & inCollideShapeSettings, const JPH::Vec3 inBaseOffset, JPH::CollisionCollector<JPH::CollideShapeResult,JPH::CollisionCollectorTraitsCollideShape> & ioCollector, const JPH::BroadPhaseLayerFilter & inBroadPhaseLayerFilter, const JPH::ObjectLayerFilter & inObjectLayerFilter, const JPH::BodyFilter & inBodyFilter, const JPH::ShapeFilter & inShapeFilter) Line 281 C++
> ezJoltPlugin.dll!ezJoltCharacterControllerComponent::CollectContacts(ezDynamicArray<ezJoltCharacterControllerComponent::ContactPoint,ezDefaultAllocatorWrapper> & out_Contacts, const JPH::Shape * pShape, const ezVec3Template<float> & vQueryPosition, const ezQuatTemplate<float> & qQueryRotation, float fCollisionTolerance) Line 314 C++
ezJoltPlugin.dll!ezJoltDefaultCharacterComponent::CheckFeet() Line 463 C++
ezJoltPlugin.dll!ezJoltDefaultCharacterComponent::UpdateCharacter() Line 587 C++
ezJoltPlugin.dll!ezJoltCharacterControllerComponent::Update(ezTime deltaTime) Line 402 C++
ezJoltPlugin.dll!ezJoltWorldModule::FetchResults(const ezWorldModule::UpdateContext & context) Line 672 C++
Now when we reach CollideConvexVsTriangles::Collide()
the arguments are as follows:
inV0
is 7.02168655, 15.9783554, 0.0718481541, L^2=304.617096
inV1
is 7.64038849, 15.9783554, 0.0336148739, L^2=313.684509
inV2
is 7.33101273, 16.2876892, 0.0787103176, L^2=319.038757
inActiveEdges
is 0
inSubShapeID2
is {mValue=4293976543 }
One step up in the callstack Visitor
is initialized with these values:
shape1
is the CapsuleShape
with {mRadius=0.300000012 mHalfHeightOfCylinder=0.00999999978 }
inScale
is 1.00000000, 1.00000000, 1.00000000, L^2=3.00000000
inScale2
is 1.00000000, 1.00000000, 1.00000000, L^2=3.00000000
inCenterOfMassTransform1
is 1.00000000, 0.00000000, 0.00000000, 7.33719635 | 0.00000000, -1.19209290e-07, -1.00000012, 16.2943859 | 0.00000000, 1.00000012, -1.19209290e-07, 0.0854607671
inCenterOfMassTransform2
is 1.00000000, 0.00000000, 0.00000000, 0.00000000 | 0.00000000, 1.00000000, 0.00000000, 0.00000000 | 0.00000000, 0.00000000, 1.00000000, 0.00000000
If you are wondering that in the callstack there is a custum shape type ezJoltCustomShapeInfo::sCollideShapeVsUser1() which is just passing things through.
Finally in GetPenetrationDepthStepEPA()
where it asserts, support_points.mY.size()
is 1
and the first array item is 0.00618362427, -0.00324955024, -0.00669670105, L^2=9.36425931e-05
.
mGJK
is this:
- mGJK {mY=0x00000037e59efa60 {0.00618362427, -0.00324955024, -0.00669670105, L^2=9.36425931e-05, 0.00618362427, -0.00324955024, -0.00669670105, L^2=9.36425931e-05, ...} ...} JPH::GJKClosestPoint
JPH::NonCopyable {...} JPH::NonCopyable
- mY 0x00000037e59efa60 {0.00618362427, -0.00324955024, -0.00669670105, L^2=9.36425931e-05, 0.00618362427, -0.00324955024, -0.00669670105, L^2=9.36425931e-05, ...} JPH::Vec3[4]
+ [0] 0.00618362427, -0.00324955024, -0.00669670105, L^2=9.36425931e-05 JPH::Vec3
+ [1] 0.00618362427, -0.00324955024, -0.00669670105, L^2=9.36425931e-05 JPH::Vec3
+ [2] -107374176., -107374176., -107374176., L^2=3.45876422e+16 JPH::Vec3
+ [3] -107374176., -107374176., -107374176., L^2=3.45876422e+16 JPH::Vec3
- mP 0x00000037e59efaa0 {0.00000000, -0.00999999978, 0.00000000, L^2=9.99999975e-05, 0.00000000, -0.00999999978, 0.00000000, L^2=9.99999975e-05, ...} JPH::Vec3[4]
+ [0] 0.00000000, -0.00999999978, 0.00000000, L^2=9.99999975e-05 JPH::Vec3
+ [1] 0.00000000, -0.00999999978, 0.00000000, L^2=9.99999975e-05 JPH::Vec3
+ [2] -107374176., -107374176., -107374176., L^2=3.45876422e+16 JPH::Vec3
+ [3] -107374176., -107374176., -107374176., L^2=3.45876422e+16 JPH::Vec3
- mQ 0x00000037e59efae0 {-0.00618362427, -0.00675044954, 0.00669670105, L^2=0.000128651576, -0.00618362427, -0.00675044954, 0.00669670105, L^2=0.000128651576, ...} JPH::Vec3[4]
+ [0] -0.00618362427, -0.00675044954, 0.00669670105, L^2=0.000128651576 JPH::Vec3
+ [1] -0.00618362427, -0.00675044954, 0.00669670105, L^2=0.000128651576 JPH::Vec3
+ [2] -107374176., -107374176., -107374176., L^2=3.45876422e+16 JPH::Vec3
+ [3] -107374176., -107374176., -107374176., L^2=3.45876422e+16 JPH::Vec3
mNumPoints 1 int
As far as I can tell, there is no badly normalized rotation that I pass in (I only have one quaternion that rotates the shape by 90 degree and it looks fine to me). The triangle also seems to not be degenerate (it shouldn't be, it is basically a heightmap, so just a grid of quads, don't see how those can deform too badly). It does seem to only happen with this capsule, I have used capsules with a different radius before and never encountered the issue with them.
This is in a Debug build, and I don't use the Jolt CMake file, so maybe some code generation option is different.
I hope that's all the data you need. Thanks for the help!
I hijacked CapsuleVsBoxTest.cpp
:
#include <TestFramework.h>
#include <Tests/ConvexCollision/CapsuleVsBoxTest.h>
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
#include <Jolt/Physics/Collision/CollideShape.h>
#include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
#include <Jolt/Physics/Collision/CollideConvexVsTriangles.h>
#include <Jolt/Renderer/DebugRenderer.h>
JPH_IMPLEMENT_RTTI_VIRTUAL(CapsuleVsBoxTest)
{
JPH_ADD_BASE_CLASS(CapsuleVsBoxTest, Test)
}
void CapsuleVsBoxTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
{
Ref<CapsuleShape> shape = new CapsuleShape(0.00999999978f, 0.300000012f);
Mat44 com1 = Mat44(Vec4(1.00000000f, 0.00000000f, 0.00000000f, 7.33719635f), Vec4(0.00000000f, -1.19209290e-07f, -1.00000012f, 16.2943859f), Vec4(0.00000000f, 1.00000012f, -1.19209290e-07f, 0.0854607671f), Vec4(0, 0, 0, 1)).Transposed();
Mat44 com2 = Mat44(Vec4(1.00000000f, 0.00000000f, 0.00000000f, 0.00000000f), Vec4(0.00000000f, 1.00000000f, 0.00000000f, 0.00000000f), Vec4(0.00000000f, 0.00000000f, 1.00000000f, 0.00000000f), Vec4(0, 0, 0, 1)).Transposed();
CollideShapeSettings settings; // <--- What are these?
AllHitCollisionCollector<CollideShapeCollector> collector;
CollideConvexVsTriangles collider(shape, Vec3::sReplicate(1.0f), Vec3::sReplicate(1.0f), com1, com2, SubShapeID(), settings, collector);
Vec3 v0(7.02168655f, 15.9783554f, 0.0718481541f);
Vec3 v1(7.64038849f, 15.9783554f, 0.0336148739f);
Vec3 v2(7.33101273f, 16.2876892f, 0.0787103176f);
collider.Collide(v0, v1, v2, 0, SubShapeID());
shape->Draw(DebugRenderer::sInstance, RMat44(com1), Vec3::sReplicate(1.0f), Color::sGreen, false, true);
DebugRenderer::sInstance->DrawWireTriangle(RVec3(com2 * v0), RVec3(com2 * v1), RVec3(com2 * v2), Color::sGreen);
DebugRenderer::sInstance->DrawMarker(RVec3(collector.mHits[0].mContactPointOn1), Color::sRed, 0.1f);
DebugRenderer::sInstance->DrawMarker(RVec3(collector.mHits[0].mContactPointOn2), Color::sRed, 0.1f);
}
I tried in Debug mode under MSVC 2022 17.9.6 and in Clang 17.0.3 (both under Windows) with the following defines:
JPH_DOUBLE_PRECISION;JPH_DEBUG_RENDERER;JPH_PROFILE_ENABLED;JPH_USE_AVX2;JPH_USE_AVX;JPH_USE_SSE4_1;JPH_USE_SSE4_2;JPH_USE_LZCNT;JPH_USE_TZCNT;JPH_USE_F16C;JPH_USE_FMADD
I don't get any asserts (b.t.w. JPH_DOUBLE_PRECISION didn't matter) and it does produce a collision:
but it doesn't even go through the EPA path (GJK already finds a valid contact between the 2 shapes, so it earlies out) and if I modify the code so that the early out doesn't exist then it finds support_points.mY.size() == 2
and also doesn't assert.
The only values that I didn't have are those from the CollideShapeSettings
object. Perhaps you can provide these? (although I don't think it will make much difference).
Another thing I'd like you to try is if you copy paste the contents of PrePhysicsUpdate
in your own codebase, will it then trigger the assert? (just to make sure that I got all the values right and to see if it could indeed be compiler config specific)
The CollideShapeSettings
are indeed the key to making it crash!
I pasted your code into mine and it worked just fine. I then copied the settings from the failing code path, which are like this:
JPH::CollideShapeSettings settings;
settings.mCollisionTolerance = 0.00999999978f;
settings.mBackFaceMode = JPH::EBackFaceMode::CollideWithBackFaces;
Now it crashes reproducably. I hardcoded the collision tolerance to 0.01f, I assume that's the problem. Though frankly it's a value that I don't understand well, anyway.
Ah, that makes perfect sense now. You're telling the system that you're ok with reporting a collision if objects are actually separated by 0.01 (this is quite a high number). In this case we should have mY[0].Length() < 0.01 instead of 1.0e-4. I'll see if I can patch this up (unfortunately a different tolerance is passed to that function)
Should be fixed now!
Awesome! Thank you so much!
I typically use Debug builds with Jolt asserts enabled, and I often encounter situations where Jolt asserts that something isn't perfectly normalized and such.
So far I have been able to fix those issues by ensuring that values really are normalized before sending them to Jolt, which is a bit of a performance waste, because they already are normalized, just not with the precision that Jolt expects. So I've already considered just disabling Jolt asserts in Debug builds.
Anyway, recently I've run into a problem with a character controller moving over a landscape and colliding with triangle meshes, which I'm not able to fix this way.
In
GetPenetrationDepthStepEPA()
there is an assertJPH_ASSERT(support_points.mY[0].IsNearZero(1.0e-8f));
that easily fires for this character (not for others, but I guess it's just because the character uses a wider capsule shape than the others).I've updated to latest Jolt version as of April 5th and can easily reproduce this still. Any ideas? It would be nice if Jolt would either use larger epsilon values in Debug builds, or different types of asserts, that I could disable or where I could configure how sensitive it should be. Because the precision of a lot of computations also depends on the compiler and the compiler settings (e.g. I use my own CMake files).
For reference, here is a video where it happens. There isn't anything to see, though, it crashes at the last second of the video, and the assert message box isn't captured by the recording software, but it shows the type of geometry involved.
https://1drv.ms/v/s!Ajrhg3sdAbZvmIZ3z-EkzppIMhScPg?e=bNv8q7