Improving accuracy of collisions with pushables? Created 2 months ago2024-07-07 20:42:31 UTC by DaSalba DaSalba

Created 2 months ago2024-07-07 20:42:31 UTC by DaSalba DaSalba

Posted 2 months ago2024-07-07 20:42:31 UTC Post #348958
As you all probably know, the physics of a func_pushable in HL1 are pretty janky, the collisions being specially bad. The correct brush size can make a difference.

I did some tests and found out that the only XY (length/widht, will not consider height here) sizes that seem to work well are 32x32 and 64x64 units. I guess this has to do with the sizes of hulls #1 to 3 (see here). With brushes of those exact sizes, a func_pushable works and looks alright. With other sizes, HL will try to use the next biggest hull:
  • A brush smaller than 32x32 will act like it was that size. So it will collide will walls too "soon", leaving a visible gap. So for example a 24x24 brush will leave a gap of 8 units of empty space on both sides between the wall and itself.
  • A brush larger than 64x64 will partially stick into walls. A 96x96 brush will stick 32 units into the wall on both sides.
  • Brushes larger than 32x32 but smaller than 64x64 will behave as the latter. A 48x48 brush will leave a gap of 16 units on each side. BUT this will only happen if both dimensions are larger than 32x32. So for example a 32x256 brush will stick 224 units into the wall in the Y dimension...
Strangely enough, when I render the entity's bounding box everything seems to be right, it's the same size as the brush. I don't know why the engine doesn't use that for collision testing.

Changing the "size" property (point, player, big, duck) makes absolutely no difference. When I look at the source code it's not clear what it does...

Is there any way to improve this? (Apart from switching to HL2, that is).
Posted 2 months ago2024-07-07 21:36:58 UTC Post #348959
Your guess of it having something to do with the HULL sizes is correct.
While player-on-pushable collisions uses the pushable's bounding box, the pushable's collisions with the level uses clipping hulls instead.

Clipping hulls date back to Quake. Essentially, instead of continuously testing mesh-on-mesh collisions (very expensive), the player and pushables are simulated as a single point, and collision hulls are pre-calculated by expanding the solid level geometry by an appropriate amount (e.g. 16 units horizontally and 36 units vertically for HULL1). This way collisions are tested using point-on-mesh instead, which is computationally very cheap. It performs better, and that's why the engine uses this instead of bounding boxes.

It might be possible to force it to use bounding box instead, I'm guessing changing the pev->movetype to MOVETYPE_STEP should get it to use similar collisions as monsters, though it's not guaranteed to be better.
Posted 1 month ago2024-07-08 17:19:05 UTC Post #348966
Thanks for the tip. Tried it, but no luck, the collisions stay the same. For reference, these are the possible values:
// edict->movetype values
#define MOVETYPE_NONE 0 // never moves
//#define    MOVETYPE_ANGLENOCLIP    1
//#define    MOVETYPE_ANGLECLIP        2
#define MOVETYPE_WALK 3           // Player only - moving on the ground
#define MOVETYPE_STEP 4           // gravity, special edge handling -- monsters use this
#define MOVETYPE_FLY 5            // No gravity, but still collides with stuff
#define MOVETYPE_TOSS 6           // gravity/collisions
#define MOVETYPE_PUSH 7           // no clip to world, push and crush
#define MOVETYPE_NOCLIP 8         // No gravity, no collisions, still do velocity/avelocity
#define MOVETYPE_FLYMISSILE 9     // extra size to monsters
#define MOVETYPE_BOUNCE 10        // Just like Toss, but reflect velocity when contacting surfaces
#define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity
#define MOVETYPE_FOLLOW 12        // track movement of aiment
#define MOVETYPE_PUSHSTEP 13      // BSP model that needs physics/world collisions (uses nearest hull for world collision)

// edict->solid values
// NOTE: Some movetypes will cause collisions independent of SOLID_NOT/SOLID_TRIGGER when the entity moves
// SOLID only effects OTHER entities colliding with this one when they move - UGH!
#define SOLID_NOT 0      // no interaction with other objects
#define SOLID_TRIGGER 1  // touch on edge, but not blocking
#define SOLID_BBOX 2     // touch on edge, block
#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground
#define SOLID_BSP 4      // bsp clip, touch on edge, block
I've tried several combinations (some like TOSS and BOUNCE are very funny to watch), but it seems HL will always use hull-based collisions when it comes to entity vs. world. The code that decides this must be probably in that one part we can't touch (the engine).
Posted 1 month ago2024-07-08 18:48:57 UTC Post #348967
I'm sorry, I was wrong. My brain must have had a hiccup last night. Of course changing the pev->movetype to MOVETYPE_STEP won't help, and monsters uses the cliphulls as well for entity-to-world collisions.

Maybe you could force it to use the pointhull, and do your own collision checks using its bounding box. The performance won't be as good as with cliphull-based collisions, of course.
Posted 1 month ago2024-07-10 16:46:12 UTC Post #348975
This is interesting, it seems that changing the Hull size (size) property of a func_pushable makes no difference at all, because it never gets passed to the CPushable::KeyValue() method. So, the following code never gets to run:
switch (bbox)
{
case 0: // Point
    UTIL_SetSize(pev, Vector(-8, -8, -8), Vector(8, 8, 8));
    break;

case 2: // Big Hull!?!?    !!!BUGBUG Figure out what this hull really is
    UTIL_SetSize(pev, VEC_DUCK_HULL_MIN * 2, VEC_DUCK_HULL_MAX * 2);
    break;

case 3: // Player duck
    UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX);
    break;

default:
case 1: // Player
    UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX);
    break;
}
Perhaps size is one of those special properties, like friction, that are handled automatically by the engine?

Plus, even if that code got to run, the call to SET_MODEL() inside the CPushable::Spawn() method, which is run later, sets the size also, so pev->mins and pev->maxs would be overwritten (not 100% sure of this). This must be why the func_pushable ends up with an accurate bounding box, same size as the brush.
Erty said:
Maybe you could force it to use the pointhull, and do your own collision checks using its bounding box. The performance won't be as good as with cliphull-based collisions, of course.
Do you mean implementing my own collision system inside the entity code, using tracelines or something?
You must be logged in to post a response.