Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

My customizations for consideration - Boolean Sweeps, Fast Mesh Bounds, some small API changes #150

Draft
wants to merge 44 commits into
base: master
Choose a base branch
from

Conversation

Frooxius
Copy link
Contributor

Hello!

Now that I finished the swap from BEPUv1, I wanted to share some of the modifications I've done to BEPUv2 to better suit the needs of my project, in case you'd find them interesting for main integration (and probably know a better way of doing those :D ).

This PR isn't good for merging itself, I've made the changes in a bit dirty way, plus I keep our fork .NET Standard 2.0 compatible (I'm ok taking that burden, I don't want to block you from going .NET 5), but hopefully you'll find some things in this useful.

There's a few main things:

Boolean sweeps

Not sure if this is the right name, but one common pattern is checking if there's any object hit by a sweep. A lot of our users use mesh colliders for things because they're convenient (though we do encourage them to optimize), in some cases necessary - a user might want to import a 3D scan or any 3D model and manipulate it from get-go by grabbing and moving around, without having to use tools to setup colliders first.

Whenever user grabs something, it essentially does a zero distance sphere sweep around the user's hand to figure out what is within that volume. In this case I only care if there's a hit or not, but don't care where exactly it is. With dense meshes the regular sweep tests all the triangles, which causes a noticeable lag.

I've made some modifications to allow passing a custom checker instead of the enumerator and so tree sweeps have ability to exit out early if the maximumT is set negative. That way on the first hit I can just make the system stop testing further triangles. This actually works really well and makes the interactions with those objects convenient. Of course unless someone marks it for player collisions and dies as a result :D

It's useful in a few other cases as well. I've implemented our own step up logic. It does a boolean sweep ahead of the character to figure out if there are any obstacles - it doesn't care where the hit is exactly, just if there's any, so it works as an optimization in this case too.

I figure this might be probably most useful addition as it adds another type of query. I have only optimized the mesh collider right now, but haven't touched the bigcompounds and others yet.

Fast Mesh Bounds

This is related to the above. Since our frequent use of mesh colliders (I'm sorry, I know it' nasty! But convenient x3 ), especially ones that end up being moved constantly in our simulation, the updating of the bounds that recomputes exact bounds ends up being really slow.

To work around this I added alternate method, which just assigns the bounding box of the original mesh and only rotates this. This results in suboptimal bounds, but in this case that's preferable.

In this it's done in a bit dirty way as compilation switch. I'm wondering if it'd be worth it to make it a toggleable feature for the Mesh struct, but that would mean adding constant memory overhead and probably some extra branching (though might not be bad if meshes don't move much?).

One thing I'm considering in our usecase is adding a pass that computes the exact bounds asynchronously - use fast bounds for direct updates, then if the mesh is still for a bit tighten the bounding box from a background thread computation.

Small API changes

  • I've made Profiler methods public instead of internal so I can add extra stages to it externally
  • I've exposed Tree leaf/node counts for external assignment. I ended up doing custom serialization/deserialization (the triangles are filled from already loaded mesh data and only the nodes & leafs are serialized, plus I skip metanodes for meshes since they do not get updated after being loaded). This lets me deserialize everything and assign the counts
  • I've added configurable minimum number of bodies that are to be slept in the IslandSleeper. Not sure how useful it is, I used it when investigating the bodies not sleeping, but I ended up not using it in the end.
  • Fixed a bug(?) where the counter for the sleeping candidates would wrap around. I found this while diagnosing some of the issues with sleeping, not sure how problematic it is after you added the control over awakening
  • Split up calculation of the Sweep parameters so I can calculate the parameters externally based on provided ratios, but still use some of the logic you provided

Anyway that's all that I remember that I changed (that matters anyways), I hope it's interesting at least for your consideration! :D If you're interested in any of these changes, I can clean them up a bit and offer a clean PR for them.

Anyway thanks for all your help again and I hope this can help contribute something back at least! ^^;

Update to latest from upstream
…ingle element of compound colliders, only cares if there's any hit or not, currently only homogenous ones)

- Tree leaf/node count access to support external serialization
- Fast mesh bounds option
- Cherry picked convex hull transformed copy from upstream
- Added ability to configure minimum tests and slept colliders in IslandSleeper
- Made profiler API be public
- Allow sweep parameters to be computed externally based on ratios
- Fixed bodies not sleeping in some cases due to the timestep counter wrapping around
Merge latest changes from upstream
@RossNordby
Copy link
Member

Thanks! It's going to take me a while to poke through things (possibly post-2.4), but it does look like there are some things in here that would be worth pulling in.

Boolean sweeps

This is probably worth implementing. It's a relatively simple change, but I'll need to fiddle around with the details of the API surface.

Also tempted to do a dedicated batched collision query and non-sweep boolean query since they're such common patterns. Not sure how deep I'd propagate a boolean query internally, but there are some pretty massive optimization opportunities available relative to both collision testing and sweep testing. I've skipped it so far just due to infinitetodolist syndrome, but perhaps later...

Fast Mesh Bounds

Something like this could be useful for an interim solution.

I'd probably do something like:

  1. Guard the feature behind a compilation symbol, as you've already done.
  2. Make the use of fast updates runtime conditional too. Since it's all behind a compilation symbol, a little extra metadata and branching won't be a meaningful concern.
  3. Optional local orientation for the bounds calculation (rather than just the mesh's default local space); a mesh that's long, thin, and locally aligned with (1,1,1) could benefit a lot from an oriented bounding box without significantly increasing the cost of the bounding box update; just an extra transform.

If you would like to do a PR for that, I'll take it. Could leave out number 3 if you want.

I'm hesitant to add any extra fields to Mesh for use outside a compilation symbol with clear 'hey, are you sure you wanna do this' guidance, since in the longer term Mesh won't be the right choice for that use case. I want a SolidMesh or MobileMesh that actually behaves well dynamically with other meshes, but that's gonna take a while.

I've made Profiler methods public instead of internal so I can add extra stages to it externally
I've exposed Tree leaf/node counts for external assignment.
Fixed a bug(?) where the counter for the sleeping candidates would wrap around. I found this while diagnosing some of the issues with sleeping, not sure how problematic it is after you added the control over awakening

These three sound simple enough that I could accept PRs without much fuss if you'd like to submit them.

@Frooxius
Copy link
Contributor Author

Frooxius commented Sep 7, 2021

Ah thanks for response! Having full boolean query support with proper API would definitely be awesome! I'm happy keeping the change in my fork in the meanwhile though, I just wanted to share this since I thought it could be useful.

Same with the fast mesh bounds. The SolidMesh and MobileMesh sound like a really good robust solution there. I can submit PR for the fast mesh bounds as interim, but I'm also ok just keeping that on my fork, I'm not sure how useful it would be to others. Would you like that one merged or is it better to leave as is until SolidMesh and MobileMesh are added at some point in the future?

I've made PR's for the three smaller things though!

@RossNordby
Copy link
Member

I've made PR's for the three smaller things though!

Thanks, merged!

For fast bounds, it's up to you- my understanding of most current use cases is that conditionally compiled fast bounds would be pretty rarely used, given the standing recommendations. There's a risk you'd end up doing a chunk of work for strictly hypothetical people, but they may hypothetically appreciate it :P

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants