Since the geometry class handles all the collision stuff, it might be better to put it into the geometry class. Imagine a case where a body has no geometry attached, the event can still be subscribed on the body class, but the expected behavior would only happen
when a geometry is attached.

Just out of curiosity, how practically does this differ from OnCollision? Besides the fact that the impulse and impulse world offset is included in the event callback.

I guess it seems more relevant to me at the body level, since that is where impulses are applied. Even though the geometries are being checked individually, it is really the bodies that are colliding. To be honest, it would probably be more convenient
from my perspective if OnCollision was at the body level - when I have bodies with multiple geoms, I always end up setting the same event handler on all of them. I see your point about a body with no geometry, but you could say the same of a non-colliding
geom. It's really just a matter of convenience for me, though - it would work either way.

Regarding the difference from OnCollision - I see OnCollision as a "should I collide" check, while this is more of a "this collision happened, with this resulting impulse". Calculating the resulting impulses accurately on your own in an
OnCollision callback is non-trivial, since you basically would have to duplicate what the Arbiter ends up doing.

I'm not sure if this exact solution is the best, but something like it would be really nice to have in the Engine. It is a really common problem that game developers need to solve.

My current project is a little demolition derby game where this issue is particularly relevant...

The OnCollision event is indeed called before an arbiter is created. This makes it possible for the users to cancel an update, but is really meant to fire when a collision occurs.

I can see your point in both the collision event being on the body and why the OnCollisionImpulse event should exist. However, there is not much I can do about the event on the body. It's a matter of the lesser of two evils.

Why would you calculate the impulse by yourself? If it is for damage detection, you have access to the list of contacts and they have a Separation property that contains the amount of penetration. The distance into the geometry from the edge.
If you have a geom that is 100x100, a seperation value of -50 would mean the two geometries have collided half way into the 100x100 geometry.

I absolutely don't want to calculate the impulse by myself - hence having the physics engine tell me when it is applying one in response to a collision.

I've tried to use Separation for calculating damage, but I haven't been able to make it work very well. One of the biggest reasons, I think, is because of the tolerance caused by the engine's "AllowedPenetration" constant - which results in situations
where there is "separation" without real resulting force going on in the engine. I've tried to account for that, but without success thus far. Even if I had been able to make it work, it is still an ad hoc calculation I am making to try to reproduce
what the engine is doing.

What is really needed is a value which indicates how much "stress" the body is taking from collisions. The impulses the engine is applying as a response seem like a good place to start, and this method given me my best results so far.

The bottom line is that in the game context (as opposed to the physics engine context) you don't care about these details. You just want to know that an object was hit, and how hard. The "how hard" is the hard part :-)

I can understand why you don't want to calculate the impulse by yourself, it is the engines job and it does already do it. However, the impulse calculated is based off many things and if you use it for damage detection, you might get unexpected results.

The impulse calculated is the amount that needs to be applied to separate the two geometries,
and have an opposite reaction.
If you use the code you provided, that not such a big deal, but if you hook into the ApplyImpulse() method, you will run into trouble.
The impulse there is also affected by e.i. the restitution coefficient. That would mean the higher bounce you want, the higher damage you will detect. That is not expected behavior since a higher restitution coefficient does not give an impact with higher force,
but is the coefficient of how much the geometry should bounce back.

Anyway, your problem with the separation amount is easily fixed. Cast it to an integer.
The amount of separation comes directly from the narrow phase collider and we are aware that some floating point problems can happen in that part of the engine. The separation value can be translated to pixels and you can't have ½ a pixel ;)