Skip to content
Permalink
Browse files

fixed physics around corners

Until now, only the physics at corners between solids worked.
Corners between open space cubes (like sockets or platforms) did not work.
Basically, those corners were treated as square cubes. Higher mip corners
were worse, since they were treated as one square piece. To verify this,
raise an aligned 16x16 field on an empty map, then make an 8x8 corner.
Making the corner makes no difference for the physics...

This patch fixes all kinds of corners. It uses code from the renderer to
interpret the map geometry, so that the physics matches the visual.

All corner types now work also with higher mips (for example 4x4 corners).

Corner bounces are preferred in some cases, to prevent hitting hidden
geometry (for mappers who make larger corners from several small corners).
We lose some precision here, but it fixes the ancient drop bug.

I also cleaned up some unused bs.
  • Loading branch information...
ac-stef committed Jan 31, 2014
1 parent af92c3c commit e5a0ed37d59635a4b3a7e0e3036c49ddff27ab2e
Showing with 75 additions and 25 deletions.
  1. +74 −24 source/src/physics.cpp
  2. +1 −1 source/src/protos.h
@@ -91,22 +91,42 @@ bool plcollide(physent *d, physent *o, float &headspace, float &hi, float &lo)
return false;
}

bool cornertest(int mip, int x, int y, int dx, int dy, int &bx, int &by, int &bs) // recursively collide with a mipmapped corner cube
int cornertest(int x, int y, int &bx, int &by, int &bs, sqr *&s, sqr *&h) // iteratively collide with a mipmapped corner cube
{
sqr *w = wmip[mip];
int mip = 1, res = -1;
while(SWS(wmip[mip], x>>mip, y>>mip, sfactor-mip)->type==CORNER) mip++;
mip--;
x >>= mip;
y >>= mip;
int mfactor = sfactor - mip;
bool stest = SOLID(SWS(w, x+dx, y, mfactor)) && SOLID(SWS(w, x, y+dy, mfactor));
mip++;
x /= 2;
y /= 2;
if(SWS(wmip[mip], x, y, mfactor-1)->type==CORNER)
bx = x<<mip;
by = y<<mip;
bs = 1<<mip;
sqr *z = SWS(wmip[mip],x - 1, y,mfactor);
sqr *t = SWS(z,2,0,mfactor); // w
sqr *w = SWS(z,1,-1,mfactor); // zst
sqr *v = SWS(z,1,1,mfactor); // v
s = SWS(z,1,0,mfactor);

// now, this is _exactly_ how the renderer interprets map geometry...
if(SOLID(z))
{
bx = x<<mip;
by = y<<mip;
bs = 1<<mip;
return cornertest(mip, x, y, dx, dy, bx, by, bs);
if(SOLID(w)) res = 2; // corners between solids are solid behind the wall or SPACE in front of it
else if(SOLID(v)) res = 3;
}
return stest;
else if(SOLID(t))
{
if(SOLID(w)) res = 1;
else if(SOLID(v)) res = 0;
}
else
{ // not a corner between solids
bool wv = w->ceil-w->floor < v->ceil-v->floor;
h = wv ? v : w; // in front of the corner, use floor and ceil from h
if(z->ceil-z->floor < t->ceil-t->floor) res = wv ? 2 : 3;
else res = wv ? 1 : 0;
} // 03
return res; // 12
}

bool mmcollide(physent *d, float &hi, float &lo) // collide with a mapmodel
@@ -164,7 +184,7 @@ bool objcollide(physent *d, const vec &objpos, float objrad, float objheight) //
// drop & rise are supplied by the physics below to indicate gravity/push for current mini-timestep
static int cornersurface = 0;

bool collide(physent *d, bool spawn, float drop, float rise, int level) // levels 1 = map, 2 = players, 4 = models, default: 1+2+4
bool collide(physent *d, bool spawn, float drop, float rise)
{
cornersurface = 0;
const float fx1 = d->o.x-d->radius; // figure out integer cube rectangle this entity covers in map
@@ -178,9 +198,11 @@ bool collide(physent *d, bool spawn, float drop, float rise, int level) // level
float hi = 127, lo = -128;
const float eyeheight = d->eyeheight;
const float playerheight = eyeheight + d->aboveeye;
float z1 = d->o.z-eyeheight, z2 = z1 + playerheight;
if(d->type != ENT_BOUNCE) z1 += 1.26;
const int applyclip = d->type == ENT_BOT || d->type == ENT_PLAYER || (d->type == ENT_BOUNCE && ((bounceent *)d)->plclipped) ? TAGANYCLIP : TAGCLIP;

if(level&1) for(int y = y1; y<=y2; y++) for(int x = x1; x<=x2; x++) // collide with map
for(int y = y1; y<=y2; y++) for(int x = x1; x<=x2; x++) // collide with map
{
if(OUTBORD(x,y)) return true;
sqr *s = S(x,y);
@@ -194,13 +216,41 @@ bool collide(physent *d, bool spawn, float drop, float rise, int level) // level

case CORNER:
{
sqr *ns = NULL, *h = NULL;
int bx = x, by = y, bs = 1;
cornersurface = 1;
if((x==x1 && y==y2 && cornertest(0, x, y, -1, 1, bx, by, bs) && fx1-bx<=fy2-by)
|| (x==x2 && y==y1 && cornertest(0, x, y, 1, -1, bx, by, bs) && fx2-bx>=fy1-by) || !(++cornersurface)
|| (x==x1 && y==y1 && cornertest(0, x, y, -1, -1, bx, by, bs) && fx1-bx+fy1-by<=bs)
|| (x==x2 && y==y2 && cornertest(0, x, y, 1, 1, bx, by, bs) && fx2-bx+fy2-by>=bs))
return true;
int q = cornertest(x, y, bx, by, bs, ns, h);
bool matter = false, match = false;
switch(q) // 0XX3
{ // XXXX
case 0: // 1XX2
match = x==x2 && y==y2;
matter = fx2-bx+fy2-by>=bs;
break;
case 1:
match = x==x2 && y==y1;
matter = fx2-bx>=fy1-by;
break;
case 2:
match = x==x1 && y==y1;
matter = fx1-bx+fy1-by<=bs;
break;
case 3:
match = x==x1 && y==y2;
matter = fx1-bx<=fy2-by;
break;
default:
return true; // mapper's fault: corner with unsufficient solids: renderer can't handle those anyway: treat as solid
}
cornersurface = (q & 1) ? 1 : 2;
sqr *n = h && !matter ? h : ns;
ceil = n->ceil; // use floor & ceil from higher mips (like the renderer)
floor = n->floor;
if(!match && d->type == ENT_BOUNCE) match = q == (d->vel.x < 0) * 2 + (d->vel.y * d->vel.x < 0); // when coming towards corner surface: prefer corner bounce
if(match && matter)
{
if(!h) return true; // we hit a corner between solids...
else if(z1 < ns->floor || z2 > ns->ceil) return true; // corner is not between solids, but we hit it...
}
cornersurface = 0;
break;
}
@@ -217,11 +267,11 @@ bool collide(physent *d, bool spawn, float drop, float rise, int level) // level
if(floor>lo) lo = floor;
}

if(level&1 && hi-lo < playerheight) return true;
if(hi - lo < playerheight) return true;

float headspace = 10.0f;

if( level&2 && d->type!=ENT_CAMERA)
if(d->type!=ENT_CAMERA)
{
loopv(players) // collide with other players
{
@@ -233,7 +283,7 @@ bool collide(physent *d, bool spawn, float drop, float rise, int level) // level
}

headspace -= 0.01f;
if( level&4 && mmcollide(d, hi, lo)) return true; // collide with map models
if(mmcollide(d, hi, lo)) return true; // collide with map models

if(spawn)
{
@@ -350,7 +400,7 @@ void moveplayer(physent *pl, int moveres, bool local, int curtime)
if(pl->onfloor) // apply friction
{
pl->vel.mul(fpsfric-1);
pl->vel.div(fpsfric);
pl->vel.div(fpsfric);
}
else // apply gravity
{
@@ -700,7 +700,7 @@ extern void mousemove(int dx, int dy);
extern void fixcamerarange(physent *cam = camera1);
extern void updatecrouch(playerent *p, bool on);
extern bool objcollide(physent *d, const vec &objpos, float objrad, float objheight);
extern bool collide(physent *d, bool spawn = false, float drop = 0, float rise = 0, int level = 7);
extern bool collide(physent *d, bool spawn = false, float drop = 0, float rise = 0);
extern void attack(bool on);
extern void vecfromyawpitch(float yaw, float pitch, int move, int strafe, vec &m);
extern void vectoyawpitch(const vec &v, float &yaw, float &pitch);

0 comments on commit e5a0ed3

Please sign in to comment.
You can’t perform that action at this time.