Chaos Contact Modification behaves oddly with CCD.

We are using an ISimCallbackObject to modify contacts between certain collision object types. We are essentially detecting collisions between ECC_Vehicle and ECC_Pawn shapes, and converting those contacts to probes. The idea here is that we want hit events being sent to gameplay, but we do not want the solver to process those collisions for physics integration.

However, what we have found is that when we enable CCD on the simulated body (ECC_Vehicle in this case) - we get very strange physics behaviour, in that the vehicle jolts or jitters unexpectedly. The collision behaviour becomes wildly unpredictable, sometimes it an pass through if we already have a contact, othertimes it stops dead, and usually the contact causes bizaree depenetration behaviour too. We are using Async Physics so I would expect the behaviour to be more deterministic, but it’s not.

How do we apply the same treatment to CCD collisions, globally? I notice that there is a separate way to modify CCD contacts via an ISimCallbackObject - but there is no way to actually iterate over all contacts, you need to have a specific particle reference which is very limiting. There is also very limited access to the CCD contact data, we have had to make engine changes to expose the shape-pair of the constraint for example.

Is there a better way to fix this, or is there something we should change regarding CCD collision to stop it affecting the physics integration unexpectedly?

On further investigation, we found that back in 5.3, Contact Modifiers were changes to operate after CCD instead of before by default. It seems we had previous engine merges that failed to update the default correctly, so only recently are we seeing this behaviour.

The CCD Modifier certainly has a less useful default implementation since it requires a specific particle, and a mid-phase isn’t suitable for us because we need per-shape filtering data (We are using per-shape collision profiles in this project in various places).

We’ve made a change to FCCDModifierAccessor to include a TArrayView of all CCD Collision Constraints, so we can just iterate over them on a global level rather than based on a specific particle, exactly the same way FCollisionContactModifier does. We also benefit from not creating temporary arrays of helper objects to modify those contacts this way it seems.

If this is something the engine team would be interested in taking, I can prepare a more suitable change in a PR. We’re now able to ignore the CCD contacts globally based on shape filters like so:

[Image Removed]

Further to this, it appears this solution only partially works. SetIsProbe appears to have the same effect as SetDisabled() on the CCD contact, so in actual fact the bodies act as if CCD is disabled between those objects specifically, and depending on body velocity/size, no contact may be generated at the solver stage.

Ideally, we would like these particular CCD contacts to be forwarded onto the solver stage, and treated as probes there too. Could you suggest how might we achieve that?

Hi James, and thank you for the question.

Could you send me a CVD capture of this interaction and I’ll take a look to see if there is anything which stands out.

Can I also check if this is in single player?

Thanks

Geoff Stacey

Developer Relations

EPIC Games

Hi James, apologies - I’ve been catching up with things after UEFest. I’m on holidays this week, but will take a look at this next week if that works for you?

Best

Geoff

Hi James,

Apologies, I’ve been looking into this. I think so far I’ve managed to find the information you already know - ie that there is nothing turning this middle cube into a probe which is why it is hitting.

I’m curious if the scale is actually caused by the way we actually turn on CCD (a CCD flagged object which is moving slowly won’t actually run CCD), but I haven’t tested that yet.

What is taking a bit of time here is trying to figure out if there is another way we expect this to be run (ie without making engine changes). Where there is a gray area is that CCD isn’t run through the solver, but it does alter position - so semantically it can sometimes be a slightly different thought process to normal.

Geoff

Agreed, and understood.

In the 2 different scenarios (ie the platform is large, vs the platform is small) - the result from CollisionResolution::ShouldUseCCD is different. This is expected since a larger object won’t be susceptible to tunnelling (which is the primary issue CCD is used for).

This is essentially the issue you are running into - the CCD sweep is causing these collisions, and the change of platform size is then triggering the CCD Sweep to run when it is over a certain size.

I haven’t verified yet, but that would also be my working assumption with the Async physics side, that the movement thresholds are being triggered in the function above.

The question I am still chewing over is what exactly to do about it, and if there is a good non engine side fix, or if this is lacking some functionality.

I can’t recall if I have asked this - but did you explore switching off the collisions between Pawn and Vehicle, and use Scene queries?

The other thing I am trying to find out is if the expectation for the CCD callbacks is that you store the handles in an instance so that you can pass it in - but I’ll need to find out why this is such a different interface to normal collisions. (CCD and normal collisions are in some senses very similar, but in others very different)

Hi James,

Thanks for the clarification.

There is a bit of context here - so please let me know if anything doesn’t make sense.

CCD even when enabled will take any opportunity to bail out of needing to do CCD - ie you can see in Chaos::Collisions::ShouldUseCCD there is a DeltaExceedsThreshold check - so it won’t run if there isn’t enough movement for example relative to it’s size . You are correct - the reasoning behind this is that you only need this (expensive) operation if the normal collision system might fail - this isn’t 100% accurate since it won’t take into account exact geometry or rotation, so we ‘best guess’. That is fine in nearly all cases though.

When there is a CCD event, we very roughly move the object so it is within a tolerance of penetration (ie touching/intersecting, but not so far that the collision system will tunnel and push the object out the wrong side). This essentially means there are 2 collisions happening in a sense. Your system probably does change a contact to a probe, but only after the CCD system has already physically altered the movement.

In the cases where the scaling is NOT creating contacts - the object is large enough that the CCD doesn’t need to be run, and conversely when the platform becomes smaller CCD will begin firing again since the CCD system has detected the geometry may tunnel.

In the cases there the geometry is physically colliding - the contact isn’t being changed to a probe at all. This is filtered out in the creation of FCCDManager::Init in the ApplyCCD function.

And this is where is gets complicated - essentially how do we get what you are looking for? Even if you turn the CCD contacts to probes - there is a risk of tunnelling (since the core collision detection doesn’t account for movement - which is why we have CCD in the first place)

For the moment, can I check this all makes sense and we can go from there?

Geoff

Hi James, no worries at all - although that amount of holiday is making me jealous.

Nearly - the Pink Cube gets stuck as due to the explaination you have, but not every step - more likely one frame. After that it’ll drop below the CCD threshold for all other calls.

Chaos is a ‘positional based solver’ - which in clearer terms means we alter postions directly, and then infer the velocity change (and hence impulses) - these are usually far more stable than the previous ‘velocity based’ impulse solvers. This is why you are seeing a direct transform update. I’m wondering now why it doesn’t ‘snag’ and then go through as I’m typing this (since then it’d be more like the non ccd one) - it could be the threshold is small enough velocity wise that it is being put to sleep?

I’ll recheck the order as I didn’t think is was an issue, but the main brain bender here is what semantically we’d need to do here. In a sense the concept of a non-physical CCD is a very unusual thing - most of the time we’d see clients using scene queries and using shape sweeps. The only reason why we don’t miss the collision in these cases is because we move the object back. If we stopped that from happening, it’d miss the collision and then tunnel, which won’t work in your case since you need the callback to be fired.

Geoff

Hi Geoff,

Sorry for the delayed response. I have attached a very lightweight test project that showcases the starnge sovler behaviour with CCD and contact modifiers. In the process I also found some other interesting behaviours. To demonstate, the project has a “Platform” set to the ECC_Pawn object type, and three cubes:

  • Yellow = ECC_Vehicle, CCD Off.
  • Pink = ECC_Vehicle, CCD On.
  • Red = WorldStatic (normal, unmodified collision).

The Collision Modifier Callback is setup so that ECC_Pawn and ECC_Vehicle contacts are converted to probes. The yellow cube behaves as expected, falling through the platform, and generating hit events for the gamecode. The red cube also behaves as expected, colliding with the platform.

The pink cube however exhibits wildly different behaviour based not only on project settings, but also interestingly, the scale of the platform.

Async Physics OFF - Platform Scale to X = 4.0, Y = 4.0, Z = 0.25

Cube passes through:

[Image Removed]

Async Physics ON - Platform Scale X = 4.0, Y = 4.0, Z = 0.25

Cube gets stuck:

[Image Removed]

Async Physics ON - Platform Scale to 5.0, Y = 5.0, Z = 0.25

Cube passes through:

[Image Removed]

The difference between Async On/Off is one thing, but the difference caused by the platform being a difference scale with Async On is perhaps more concerning, and might explain why we see such random behaviour in our game where we make use of this.

Our real project uses Async Physics at 30Hz, and is multiplayer. This issue manifests in multi/single player. The test project is multiplayer “safe” for PIE.

As mentioned above - in our real project, we have made some engine modifications so that we can apply the same filtering/conversion logic to CCD Collisions that we do to non-CCD collisions, since the out-of-the-box API for CCD modifications is quite limiting. This is ultimately what allowed us to circumvent the issue you see in the second gif above, where the simulated object gets stuck on something it should only be “probing”.

I have left that custom CCD Modifier code in the test project, but it is #if/def’d out since it would obviously require those engine changes. If you would like to investigate that, these are the relevant areas of the engine that need to be changed:

[Image Removed]

It’s worth flagging that although these changes “fix” the issue of the CCD Contact causing the object to get stuck - CCD “Probes” are effectively just ignored/treated as disabled by the CCD rewind step and not forwarded to any future step. Ideally, we’d like it so that a probe CDD constraint generated by the CCD sweep does not apply CCD rewind, but can still be passed along to the collision modifier step so we can generate hit events for gamecode - as currently our “ccd probe” contacts have phasing issues with fast moving objects.

Probes are an incredibly useful feature, so it woud be great to see them supported this way at a lower level.

[mention removed]​ apologies for a ping - but any updates/progress on this?

No worries thanks!

Hi [mention removed]​ - sorry to chase again, but any updates on this? Particularly the strange behaviour and test project in the post above?

Hey Geoff, thanks for lookign. Just to be clear the Middle Cube (Pink) is being being turned into a probe contact both from CCD and regular solver contacts - so it *should* be passing through as if there is no collision, but for whatever reason when the platform is scaled up, CCD stops it. But ofc I would not have expected that behaviour to be based on the size/scale of the objects being contacted, it’s very unstable/unreliable behaviour.

Yeah using queries was the original naïve implementation, but the main issues with that are:

A) Have to do the queries every frame, so quite wasteful.

B) Verrrrry expensive (especially for a multi-body primitive like a vehicle SKM).

C) Prone to the same “tunnelling” issue where you miss contacts/overlaps at high speed etc.

Doing it the way we are currently just gives us essentially “free” events from the solver running naturally, so the difference is huge, especially when you consider the scale we’re going for. We’re using this approach for other things besides characters too, like destroying smalls meshes the vehicle hits as a naive destruction system etc. It’s super effective when it works, but ofc the tunneling issue is still a bit prevalent without CCD enabled.

Right I think I understand now, so in essence the CCD sweep is generating a contact, but because the platform is below a certain threshold, the positiol offset generated by that sweep is not applied in some cases because it knows that the regular solver step will generate a contact?

When the platform is scaled up however, the position offset is applied even though we’ve converted the contact to a probe/effectively disabled it?

Interestingly I think this behaviour persists if you “Disable” the CCD contact too, instead of converting it to a probe. IMO the desired outcome would be to avoid applying the offset from any “Disabled” CCD contact, but potentially forward a “probe” CCD contact on to the regular contact solving step somehow? Tricky one..

Hi Geoff,

Sorry I keep having time off and the thread closes :smiley: I think I understand now.

So in the test project above - the reason the Pink Cube gets stuck only when Async tick is ON and the Platform is actually smaller, is because CCD only runs in that particular instance due to the thresholds. It gets “stuck” because CCD is applying a position change every step. Looking at FCCDManager::UpdateSweptConstraints, it appears to only modify the transform not velocity, so I imagine that if I inspected the body I’d also see it’s velocity climbing frame-to-frame due to acceleration.

What I can’t figure out though is why the CCD positional offset is still being applied even after I disable the contact/convert it to a probe. I’m looking at the order of the solver stages in FPBDRigidsEvolutionGBF::AdvanceOneTimeStepImpl, and from what I can tell, CCDModifiers are applied before the ApplyCCD step. FCCDManager::Init also explicitly skips over disabled/probe contacts, so they *should* for all intents and purposes be ignored altogether. However, there are other areas in FCCDManager where it doesn’t seem to check those flags, so I wonder if the issue is actually an oversight there?

In our particular case, tunnelling isn’t an issue - we actually want the tunnelling, we just want a reliable notification (via hit event) when it happens which is why we convert both types of contact (CCD and regular) to probes. CCD contacts it seems are used by the solver only and are never used for collision “events”, hence my idea to “forward” a probe CCD contact by adding it to the list of normal collisions/constraints.

But even if the latter is not possible for now, if a disabled/probe CCD constraint is still applying the transform offset, that seems like a bug.

Cheers,

James

Hi James,

Apologies, I completely missed a far simpler way to solve this issue. There is a CVar (bChaosCollisionModiferBeforeCCD) which will change the functionality back to the earlier type (ie running normal modifiers before the CCD system has a chance to run).

This should solve your issue by just adding a line of text into the DefaultEngine.ini (or similar) file and using the non ccd modifiers in the way you were at the start!

Best

Geoff