Just a bit of a follow-up question: is there anything wrong with storing an instance of a SubgroupCollisionFilter and reusing that for multiple physics bodies? Would you recommend that or not? If I don't reuse filters, couldn't I theoretically have two bodies that belong to the same group and subgroup, but have different collision masks, and therefore the bodies with which they would collide could differ?
There's nothing fundamentally wrong about reusing filters- they're just a struct containing a few integers, after all. As long as the resulting behavior is what you want.
And yes, same group, same subgroup, different masks could result in different filtering behavior.
Additionally, when it comes to substepping, I wanted to clarify—is a substep kind of like a regular physics timestep, except that it's run internally? I've already encountered this with cannonballs, so I can only imagine it getting worse once I work on bullets, but is one better than the other (extra timesteps vs substeps) at preventing fast moving objects from going through solid objects (like walls)?
I'm still not sure why I would choose to timestep more often instead of using substepping, or vice versa—I don't really understand the pros and cons of each.
The SubsteppingTimestepper performs multiple substeps internally, but only for the velocity integrator, solver, and pose integrator. You can see exactly what it does here:
https://github.com/bepu/bepuphysics2/bl ... per.cs#L81
So each substep can be a good bit cheaper than a full Simulation.Timestep since they skip collision detection and bookkeeping. Since it focuses on the solver/integrator, the SubsteppingTimestepper is primarily useful when there is a really difficult constraint configuration but you don't need a great deal of collision detection precision. The SubsteppingTimestepper won't typically help much with objects tunneling through each other unless it's the result of a difficult solver configuration.
Performing more frequent full timesteps will perform collision detection more frequently, so it can help when the simulation has difficult collision detection requirements. It can help with fast objects tunneling through surfaces. Of course, since it's doing more, it costs more.
However, you usually don't have to use either to stop tunneling. Increasing the speculative margin on a collider will let it generate speculative contacts further away. If an object is moving 100 units/second and the timestep duration is 1/60, the object can move up to 1.67 units in a single frame. If the speculative margin is less than that, it might not generate contacts soon enough, causing tunneling. Using a larger speculative margin can help.
Higher speculative margins can cause 'ghost' collisions, though, where the speculation gets it wrong. This will look like stuff bouncing off invisible surfaces nearby objects that shouldn't have been hit. It's a fairly rare event, so the standard recommendation is to not worry about it until it becomes a problem.
If it does become a problem, you can enable the actual continuous collision detection mode. For sufficiently fast moving pairs, this performs a full sweep test to determine a time of impact and generates contacts accordingly. You can keep your speculative margin smaller with this approach, avoiding ghost collisions. Sweeps are relatively expensive so you might not want to use this mode absolutely everywhere, but for bullets and stuff it''ll be fine. (Just avoid having several thousand fast moving bullets smacking into each other at the same time

)
An example of using the continuous sweeping mode can be found here:
https://github.com/bepu/bepuphysics2/bl ... emo.cs#L71
EDIT: I've got the capstan collision working now, but I forgot to ask—is there a way to prevent the capstan body from rotating around its local Y axis due to forces, while allowing me to change its rotation programmatically/directly? I'd like to manually rotate the body when a player uses the capstan to raise the anchor, but I don't want it spinning around when the player stands on it or something.
A few options:
1) Add an AngularAxisMotor with a zero target velocity to resist attempts to rotate it. It'll behave like friction, so only sufficiently large torques will rotate it. A good option if you want to maintain physical consistency, where players aren't the only things that can move it.
2) Replace the Hinge with a Weld. Rotate the capstan by adjusting the Weld's LocalOrientation programmatically.
3) Replace the Hinge with a Weld. If the capstan is a rotationally invariant shape (like a cylinder), leave the physical representation of the capstan untouched and just rotate it graphically.
4) If you have no reason to have the capstan as a separate physical object and it has a rotationally invariant shape (like a cylinder), just add its collider to the compound shape and handle rotation graphically.