summaryrefslogtreecommitdiff
path: root/src/engine/physics.cpp
diff options
context:
space:
mode:
authorxolatile2025-08-04 22:53:42 +0200
committerxolatile2025-08-04 22:53:42 +0200
commitd309df4ce4d8ad0ed995a8e1c4267412a7782021 (patch)
tree999ca8d785ecc1681e5eb7538ce2e6a18d244fa5 /src/engine/physics.cpp
parent29d613d9cb65a0faa7e3f80e75bea0b6d910cb9a (diff)
downloadxolatile-badassbug-d309df4ce4d8ad0ed995a8e1c4267412a7782021.tar.xz
xolatile-badassbug-d309df4ce4d8ad0ed995a8e1c4267412a7782021.tar.zst
Bunch of small changes...
Diffstat (limited to 'src/engine/physics.cpp')
-rw-r--r--src/engine/physics.cpp3244
1 files changed, 1529 insertions, 1715 deletions
diff --git a/src/engine/physics.cpp b/src/engine/physics.cpp
index 4c23739..19eee8b 100644
--- a/src/engine/physics.cpp
+++ b/src/engine/physics.cpp
@@ -12,88 +12,88 @@ static int clipcacheversion = -2;
static inline clipplanes &getclipplanes(const cube &c, const ivec &o, int size, bool collide = true, int offset = 0)
{
- clipplanes &p = clipcache[int(&c - worldroot)&(MAXCLIPPLANES-1)];
- if(p.owner != &c || p.version != clipcacheversion+offset)
- {
- p.owner = &c;
- p.version = clipcacheversion+offset;
- genclipplanes(c, o, size, p, collide);
- }
- return p;
+ clipplanes &p = clipcache[int(&c - worldroot)&(MAXCLIPPLANES-1)];
+ if(p.owner != &c || p.version != clipcacheversion+offset)
+ {
+ p.owner = &c;
+ p.version = clipcacheversion+offset;
+ genclipplanes(c, o, size, p, collide);
+ }
+ return p;
}
void resetclipplanes()
{
- clipcacheversion += 2;
- if(!clipcacheversion)
- {
- memclear(clipcache);
- clipcacheversion = 2;
- }
+ clipcacheversion += 2;
+ if(!clipcacheversion)
+ {
+ memclear(clipcache);
+ clipcacheversion = 2;
+ }
}
///////////////////////// ray - cube collision ///////////////////////////////////////////////
#define INTERSECTPLANES(setentry, exit) \
- float enterdist = -1e16f, exitdist = 1e16f; \
- loopi(p.size) \
- { \
- float pdist = p.p[i].dist(v), facing = ray.dot(p.p[i]); \
- if(facing < 0) \
- { \
- pdist /= -facing; \
- if(pdist > enterdist) \
- { \
- if(pdist > exitdist) exit; \
- enterdist = pdist; \
- setentry; \
- } \
- } \
- else if(facing > 0) \
- { \
- pdist /= -facing; \
- if(pdist < exitdist) \
- { \
- if(pdist < enterdist) exit; \
- exitdist = pdist; \
- } \
- } \
- else if(pdist > 0) exit; \
- }
+ float enterdist = -1e16f, exitdist = 1e16f; \
+ loopi(p.size) \
+ { \
+ float pdist = p.p[i].dist(v), facing = ray.dot(p.p[i]); \
+ if(facing < 0) \
+ { \
+ pdist /= -facing; \
+ if(pdist > enterdist) \
+ { \
+ if(pdist > exitdist) exit; \
+ enterdist = pdist; \
+ setentry; \
+ } \
+ } \
+ else if(facing > 0) \
+ { \
+ pdist /= -facing; \
+ if(pdist < exitdist) \
+ { \
+ if(pdist < enterdist) exit; \
+ exitdist = pdist; \
+ } \
+ } \
+ else if(pdist > 0) exit; \
+ }
#define INTERSECTBOX(setentry, exit) \
- loop(i, 3) \
- { \
- if(ray[i]) \
- { \
- float prad = fabs(p.r[i] * invray[i]), pdist = (p.o[i] - v[i]) * invray[i], pmin = pdist - prad, pmax = pdist + prad; \
- if(pmin > enterdist) \
- { \
- if(pmin > exitdist) exit; \
- enterdist = pmin; \
- setentry; \
- } \
- if(pmax < exitdist) \
- { \
- if(pmax < enterdist) exit; \
- exitdist = pmax; \
- } \
- } \
- else if(v[i] < p.o[i]-p.r[i] || v[i] > p.o[i]+p.r[i]) exit; \
- }
+ loop(i, 3) \
+ { \
+ if(ray[i]) \
+ { \
+ float prad = fabs(p.r[i] * invray[i]), pdist = (p.o[i] - v[i]) * invray[i], pmin = pdist - prad, pmax = pdist + prad; \
+ if(pmin > enterdist) \
+ { \
+ if(pmin > exitdist) exit; \
+ enterdist = pmin; \
+ setentry; \
+ } \
+ if(pmax < exitdist) \
+ { \
+ if(pmax < enterdist) exit; \
+ exitdist = pmax; \
+ } \
+ } \
+ else if(v[i] < p.o[i]-p.r[i] || v[i] > p.o[i]+p.r[i]) exit; \
+ }
vec hitsurface;
static inline bool raycubeintersect(const clipplanes &p, const cube &c, const vec &v, const vec &ray, const vec &invray, float &dist)
{
- int entry = -1, bbentry = -1;
- INTERSECTPLANES(entry = i, return false);
- INTERSECTBOX(bbentry = i, return false);
- if(exitdist < 0) return false;
- dist = max(enterdist+0.1f, 0.0f);
- if(bbentry>=0) { hitsurface = vec(0, 0, 0); hitsurface[bbentry] = ray[bbentry]>0 ? -1 : 1; }
- else hitsurface = p.p[entry];
- return true;
+ int entry = -1, bbentry = -1;
+ INTERSECTPLANES(entry = i, return false);
+ INTERSECTBOX(bbentry = i, return false);
+ if(exitdist < 0) return false;
+ dist = max(enterdist+0.1f, 0.0f);
+ if(bbentry>=0) { hitsurface = vec(0, 0, 0); hitsurface[bbentry] = ray[bbentry]>0 ? -1 : 1; }
+ else hitsurface = p.p[entry];
+ return true;
}
extern void entselectionbox(const entity &e, vec &eo, vec &es);
@@ -102,257 +102,257 @@ int hitent, hitorient;
static float disttoent(octaentities *oc, const vec &o, const vec &ray, float radius, int mode, extentity *t)
{
- vec eo, es;
- int orient = -1;
- float dist = radius, f = 0.0f;
- const vector<extentity *> &ents = entities::getents();
-
- #define entintersect(mask, type, func) {\
- if((mode&(mask))==(mask)) loopv(oc->type) \
- { \
- extentity &e = *ents[oc->type[i]]; \
- if(!(e.flags&EF_OCTA) || &e==t) continue; \
- func; \
- if(f<dist && f>0 && vec(ray).mul(f).add(o).insidebb(oc->o, oc->size)) \
- { \
- hitentdist = dist = f; \
- hitent = oc->type[i]; \
- hitorient = orient; \
- } \
- } \
- }
-
- entintersect(RAY_POLY, mapmodels,
- if(!mmintersect(e, o, ray, radius, mode, f)) continue;
- );
-
- entintersect(RAY_ENTS, other,
- entselectionbox(e, eo, es);
- if(!rayboxintersect(eo, es, o, ray, f, orient)) continue;
- );
-
- entintersect(RAY_ENTS, mapmodels,
- entselectionbox(e, eo, es);
- if(!rayboxintersect(eo, es, o, ray, f, orient)) continue;
- );
-
- return dist;
+ vec eo, es;
+ int orient = -1;
+ float dist = radius, f = 0.0f;
+ const vector<extentity *> &ents = entities::getents();
+
+ #define entintersect(mask, type, func) {\
+ if((mode&(mask))==(mask)) loopv(oc->type) \
+ { \
+ extentity &e = *ents[oc->type[i]]; \
+ if(!(e.flags&EF_OCTA) || &e==t) continue; \
+ func; \
+ if(f<dist && f>0 && vec(ray).mul(f).add(o).insidebb(oc->o, oc->size)) \
+ { \
+ hitentdist = dist = f; \
+ hitent = oc->type[i]; \
+ hitorient = orient; \
+ } \
+ } \
+ }
+
+ entintersect(RAY_POLY, mapmodels,
+ if(!mmintersect(e, o, ray, radius, mode, f)) continue;
+ );
+
+ entintersect(RAY_ENTS, other,
+ entselectionbox(e, eo, es);
+ if(!rayboxintersect(eo, es, o, ray, f, orient)) continue;
+ );
+
+ entintersect(RAY_ENTS, mapmodels,
+ entselectionbox(e, eo, es);
+ if(!rayboxintersect(eo, es, o, ray, f, orient)) continue;
+ );
+
+ return dist;
}
static float disttooutsideent(const vec &o, const vec &ray, float radius, int mode, extentity *t)
{
- vec eo, es;
- int orient;
- float dist = radius, f = 0.0f;
- const vector<extentity *> &ents = entities::getents();
- loopv(outsideents)
- {
- extentity &e = *ents[outsideents[i]];
- if(!(e.flags&EF_OCTA) || &e==t) continue;
- entselectionbox(e, eo, es);
- if(!rayboxintersect(eo, es, o, ray, f, orient)) continue;
- if(f<dist && f>0)
- {
- hitentdist = dist = f;
- hitent = outsideents[i];
- hitorient = orient;
- }
- }
- return dist;
+ vec eo, es;
+ int orient;
+ float dist = radius, f = 0.0f;
+ const vector<extentity *> &ents = entities::getents();
+ loopv(outsideents)
+ {
+ extentity &e = *ents[outsideents[i]];
+ if(!(e.flags&EF_OCTA) || &e==t) continue;
+ entselectionbox(e, eo, es);
+ if(!rayboxintersect(eo, es, o, ray, f, orient)) continue;
+ if(f<dist && f>0)
+ {
+ hitentdist = dist = f;
+ hitent = outsideents[i];
+ hitorient = orient;
+ }
+ }
+ return dist;
}
// optimized shadow version
static float shadowent(octaentities *oc, const vec &o, const vec &ray, float radius, int mode, extentity *t)
{
- float dist = radius, f = 0.0f;
- const vector<extentity *> &ents = entities::getents();
- loopv(oc->mapmodels)
- {
- extentity &e = *ents[oc->mapmodels[i]];
- if(!(e.flags&EF_OCTA) || &e==t) continue;
- if(!mmintersect(e, o, ray, radius, mode, f)) continue;
- if(f>0 && f<dist) dist = f;
- }
- return dist;
+ float dist = radius, f = 0.0f;
+ const vector<extentity *> &ents = entities::getents();
+ loopv(oc->mapmodels)
+ {
+ extentity &e = *ents[oc->mapmodels[i]];
+ if(!(e.flags&EF_OCTA) || &e==t) continue;
+ if(!mmintersect(e, o, ray, radius, mode, f)) continue;
+ if(f>0 && f<dist) dist = f;
+ }
+ return dist;
}
#define INITRAYCUBE \
- float dist = 0, dent = radius > 0 ? radius : 1e16f; \
- vec v(o), invray(ray.x ? 1/ray.x : 1e16f, ray.y ? 1/ray.y : 1e16f, ray.z ? 1/ray.z : 1e16f); \
- cube *levels[20]; \
- levels[worldscale] = worldroot; \
- int lshift = worldscale, elvl = mode&RAY_BB ? worldscale : 0; \
- ivec lsizemask(invray.x>0 ? 1 : 0, invray.y>0 ? 1 : 0, invray.z>0 ? 1 : 0); \
+ float dist = 0, dent = radius > 0 ? radius : 1e16f; \
+ vec v(o), invray(ray.x ? 1/ray.x : 1e16f, ray.y ? 1/ray.y : 1e16f, ray.z ? 1/ray.z : 1e16f); \
+ cube *levels[20]; \
+ levels[worldscale] = worldroot; \
+ int lshift = worldscale, elvl = mode&RAY_BB ? worldscale : 0; \
+ ivec lsizemask(invray.x>0 ? 1 : 0, invray.y>0 ? 1 : 0, invray.z>0 ? 1 : 0); \
#define CHECKINSIDEWORLD \
- if(!insideworld(o)) \
- { \
- float disttoworld = 0, exitworld = 1e16f; \
- loopi(3) \
- { \
- float c = v[i]; \
- if(c<0 || c>=worldsize) \
- { \
- float d = ((invray[i]>0?0:worldsize)-c)*invray[i]; \
- if(d<0) return (radius>0?radius:-1); \
- disttoworld = max(disttoworld, 0.1f + d); \
- } \
- float e = ((invray[i]>0?worldsize:0)-c)*invray[i]; \
- exitworld = min(exitworld, e); \
- } \
- if(disttoworld > exitworld) return (radius>0?radius:-1); \
- v.add(vec(ray).mul(disttoworld)); \
- dist += disttoworld; \
- }
+ if(!insideworld(o)) \
+ { \
+ float disttoworld = 0, exitworld = 1e16f; \
+ loopi(3) \
+ { \
+ float c = v[i]; \
+ if(c<0 || c>=worldsize) \
+ { \
+ float d = ((invray[i]>0?0:worldsize)-c)*invray[i]; \
+ if(d<0) return (radius>0?radius:-1); \
+ disttoworld = max(disttoworld, 0.1f + d); \
+ } \
+ float e = ((invray[i]>0?worldsize:0)-c)*invray[i]; \
+ exitworld = min(exitworld, e); \
+ } \
+ if(disttoworld > exitworld) return (radius>0?radius:-1); \
+ v.add(vec(ray).mul(disttoworld)); \
+ dist += disttoworld; \
+ }
#define DOWNOCTREE(disttoent, earlyexit) \
- cube *lc = levels[lshift]; \
- for(;;) \
- { \
- lshift--; \
- lc += octastep(x, y, z, lshift); \
- if(lc->ext && lc->ext->ents && lshift < elvl) \
- { \
- float edist = disttoent(lc->ext->ents, o, ray, dent, mode, t); \
- if(edist < dent) \
- { \
- earlyexit return min(edist, dist); \
- elvl = lshift; \
- dent = min(dent, edist); \
- } \
- } \
- if(lc->children==NULL) break; \
- lc = lc->children; \
- levels[lshift] = lc; \
- }
+ cube *lc = levels[lshift]; \
+ for(;;) \
+ { \
+ lshift--; \
+ lc += octastep(x, y, z, lshift); \
+ if(lc->ext && lc->ext->ents && lshift < elvl) \
+ { \
+ float edist = disttoent(lc->ext->ents, o, ray, dent, mode, t); \
+ if(edist < dent) \
+ { \
+ earlyexit return min(edist, dist); \
+ elvl = lshift; \
+ dent = min(dent, edist); \
+ } \
+ } \
+ if(lc->children==NULL) break; \
+ lc = lc->children; \
+ levels[lshift] = lc; \
+ }
#define FINDCLOSEST(xclosest, yclosest, zclosest) \
- float dx = (lo.x+(lsizemask.x<<lshift)-v.x)*invray.x, \
- dy = (lo.y+(lsizemask.y<<lshift)-v.y)*invray.y, \
- dz = (lo.z+(lsizemask.z<<lshift)-v.z)*invray.z; \
- float disttonext = dx; \
- xclosest; \
- if(dy < disttonext) { disttonext = dy; yclosest; } \
- if(dz < disttonext) { disttonext = dz; zclosest; } \
- disttonext += 0.1f; \
- v.add(vec(ray).mul(disttonext)); \
- dist += disttonext;
+ float dx = (lo.x+(lsizemask.x<<lshift)-v.x)*invray.x, \
+ dy = (lo.y+(lsizemask.y<<lshift)-v.y)*invray.y, \
+ dz = (lo.z+(lsizemask.z<<lshift)-v.z)*invray.z; \
+ float disttonext = dx; \
+ xclosest; \
+ if(dy < disttonext) { disttonext = dy; yclosest; } \
+ if(dz < disttonext) { disttonext = dz; zclosest; } \
+ disttonext += 0.1f; \
+ v.add(vec(ray).mul(disttonext)); \
+ dist += disttonext;
#define UPOCTREE(exitworld) \
- x = int(v.x); \
- y = int(v.y); \
- z = int(v.z); \
- uint diff = uint(lo.x^x)|uint(lo.y^y)|uint(lo.z^z); \
- if(diff >= uint(worldsize)) exitworld; \
- diff >>= lshift; \
- if(!diff) exitworld; \
- do \
- { \
- lshift++; \
- diff >>= 1; \
- } while(diff);
+ x = int(v.x); \
+ y = int(v.y); \
+ z = int(v.z); \
+ uint diff = uint(lo.x^x)|uint(lo.y^y)|uint(lo.z^z); \
+ if(diff >= uint(worldsize)) exitworld; \
+ diff >>= lshift; \
+ if(!diff) exitworld; \
+ do \
+ { \
+ lshift++; \
+ diff >>= 1; \
+ } while(diff);
float raycube(const vec &o, const vec &ray, float radius, int mode, int size, extentity *t)
{
- if(ray.iszero()) return 0;
-
- INITRAYCUBE;
- CHECKINSIDEWORLD;
-
- int closest = -1, x = int(v.x), y = int(v.y), z = int(v.z);
- for(;;)
- {
- DOWNOCTREE(disttoent, if(mode&RAY_SHADOW));
-
- int lsize = 1<<lshift;
-
- cube &c = *lc;
- if((dist>0 || !(mode&RAY_SKIPFIRST)) &&
- (((mode&RAY_CLIPMAT) && isclipped(c.material&MATF_VOLUME)) ||
- ((mode&RAY_EDITMAT) && c.material != MAT_AIR) ||
- (!(mode&RAY_PASS) && lsize==size && !isempty(c)) ||
- isentirelysolid(c) ||
- dent < dist))
- {
- if(closest >= 0) { hitsurface = vec(0, 0, 0); hitsurface[closest] = ray[closest]>0 ? -1 : 1; }
- return min(dent, dist);
- }
-
- ivec lo(x&(~0U<<lshift), y&(~0U<<lshift), z&(~0U<<lshift));
-
- if(!isempty(c))
- {
- const clipplanes &p = getclipplanes(c, lo, lsize, false, 1);
- float f = 0;
- if(raycubeintersect(p, c, v, ray, invray, f) && (dist+f>0 || !(mode&RAY_SKIPFIRST)))
- return min(dent, dist+f);
- }
-
- FINDCLOSEST(closest = 0, closest = 1, closest = 2);
-
- if(radius>0 && dist>=radius) return min(dent, dist);
-
- UPOCTREE(return min(dent, radius>0 ? radius : dist));
- }
+ if(ray.iszero()) return 0;
+
+ INITRAYCUBE;
+ CHECKINSIDEWORLD;
+
+ int closest = -1, x = int(v.x), y = int(v.y), z = int(v.z);
+ for(;;)
+ {
+ DOWNOCTREE(disttoent, if(mode&RAY_SHADOW));
+
+ int lsize = 1<<lshift;
+
+ cube &c = *lc;
+ if((dist>0 || !(mode&RAY_SKIPFIRST)) &&
+ (((mode&RAY_CLIPMAT) && isclipped(c.material&MATF_VOLUME)) ||
+ ((mode&RAY_EDITMAT) && c.material != MAT_AIR) ||
+ (!(mode&RAY_PASS) && lsize==size && !isempty(c)) ||
+ isentirelysolid(c) ||
+ dent < dist))
+ {
+ if(closest >= 0) { hitsurface = vec(0, 0, 0); hitsurface[closest] = ray[closest]>0 ? -1 : 1; }
+ return min(dent, dist);
+ }
+
+ ivec lo(x&(~0U<<lshift), y&(~0U<<lshift), z&(~0U<<lshift));
+
+ if(!isempty(c))
+ {
+ const clipplanes &p = getclipplanes(c, lo, lsize, false, 1);
+ float f = 0;
+ if(raycubeintersect(p, c, v, ray, invray, f) && (dist+f>0 || !(mode&RAY_SKIPFIRST)))
+ return min(dent, dist+f);
+ }
+
+ FINDCLOSEST(closest = 0, closest = 1, closest = 2);
+
+ if(radius>0 && dist>=radius) return min(dent, dist);
+
+ UPOCTREE(return min(dent, radius>0 ? radius : dist));
+ }
}
// optimized version for lightmap shadowing... every cycle here counts!!!
float shadowray(const vec &o, const vec &ray, float radius, int mode, extentity *t)
{
- INITRAYCUBE;
- CHECKINSIDEWORLD;
-
- int side = O_BOTTOM, x = int(v.x), y = int(v.y), z = int(v.z);
- for(;;)
- {
- DOWNOCTREE(shadowent, );
-
- cube &c = *lc;
- ivec lo(x&(~0U<<lshift), y&(~0U<<lshift), z&(~0U<<lshift));
-
- if(!isempty(c) && !(c.material&MAT_ALPHA))
- {
- if(isentirelysolid(c))
- {
- if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
- {
- if(mode&RAY_SKYTEX) return radius;
- }
- else return dist;
- }
- else
- {
- const clipplanes &p = getclipplanes(c, lo, 1<<lshift, false, 1);
- INTERSECTPLANES(side = p.side[i], goto nextcube);
- INTERSECTBOX(side = (i<<1) + 1 - lsizemask[i], goto nextcube);
- if(exitdist >= 0)
- {
- if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
- {
- if(mode&RAY_SKYTEX) return radius;
- }
- else return dist+max(enterdist+0.1f, 0.0f);
- }
- }
- }
-
- nextcube:
- FINDCLOSEST(side = O_RIGHT - lsizemask.x, side = O_FRONT - lsizemask.y, side = O_TOP - lsizemask.z);
-
- if(dist>=radius) return dist;
-
- UPOCTREE(return radius);
- }
+ INITRAYCUBE;
+ CHECKINSIDEWORLD;
+
+ int side = O_BOTTOM, x = int(v.x), y = int(v.y), z = int(v.z);
+ for(;;)
+ {
+ DOWNOCTREE(shadowent, );
+
+ cube &c = *lc;
+ ivec lo(x&(~0U<<lshift), y&(~0U<<lshift), z&(~0U<<lshift));
+
+ if(!isempty(c) && !(c.material&MAT_ALPHA))
+ {
+ if(isentirelysolid(c))
+ {
+ if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
+ {
+ if(mode&RAY_SKYTEX) return radius;
+ }
+ else return dist;
+ }
+ else
+ {
+ const clipplanes &p = getclipplanes(c, lo, 1<<lshift, false, 1);
+ INTERSECTPLANES(side = p.side[i], goto nextcube);
+ INTERSECTBOX(side = (i<<1) + 1 - lsizemask[i], goto nextcube);
+ if(exitdist >= 0)
+ {
+ if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
+ {
+ if(mode&RAY_SKYTEX) return radius;
+ }
+ else return dist+max(enterdist+0.1f, 0.0f);
+ }
+ }
+ }
+
+ nextcube:
+ FINDCLOSEST(side = O_RIGHT - lsizemask.x, side = O_FRONT - lsizemask.y, side = O_TOP - lsizemask.z);
+
+ if(dist>=radius) return dist;
+
+ UPOCTREE(return radius);
+ }
}
// thread safe version
struct ShadowRayCache
{
- clipplanes clipcache[MAXCLIPPLANES];
- int version;
+ clipplanes clipcache[MAXCLIPPLANES];
+ int version;
- ShadowRayCache() : version(-1) {}
+ ShadowRayCache() : version(-1) {}
};
ShadowRayCache *newshadowraycache() { return new ShadowRayCache; }
@@ -361,106 +361,106 @@ void freeshadowraycache(ShadowRayCache *&cache) { delete cache; cache = NULL; }
void resetshadowraycache(ShadowRayCache *cache)
{
- cache->version++;
- if(!cache->version)
- {
- memclear(cache->clipcache);
- cache->version = 1;
- }
+ cache->version++;
+ if(!cache->version)
+ {
+ memclear(cache->clipcache);
+ cache->version = 1;
+ }
}
float shadowray(ShadowRayCache *cache, const vec &o, const vec &ray, float radius, int mode, extentity *t)
{
- INITRAYCUBE;
- CHECKINSIDEWORLD;
-
- int side = O_BOTTOM, x = int(v.x), y = int(v.y), z = int(v.z);
- for(;;)
- {
- DOWNOCTREE(shadowent, );
-
- cube &c = *lc;
- ivec lo(x&(~0U<<lshift), y&(~0U<<lshift), z&(~0U<<lshift));
-
- if(!isempty(c) && !(c.material&MAT_ALPHA))
- {
- if(isentirelysolid(c))
- {
- if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
- {
- if(mode&RAY_SKYTEX) return radius;
- }
- else return dist;
- }
- else
- {
- clipplanes &p = cache->clipcache[int(&c - worldroot)&(MAXCLIPPLANES-1)];
- if(p.owner != &c || p.version != cache->version) { p.owner = &c; p.version = cache->version; genclipplanes(c, lo, 1<<lshift, p, false); }
- INTERSECTPLANES(side = p.side[i], goto nextcube);
- INTERSECTBOX(side = (i<<1) + 1 - lsizemask[i], goto nextcube);
- if(exitdist >= 0)
- {
- if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
- {
- if(mode&RAY_SKYTEX) return radius;
- }
- else return dist+max(enterdist+0.1f, 0.0f);
- }
- }
- }
-
- nextcube:
- FINDCLOSEST(side = O_RIGHT - lsizemask.x, side = O_FRONT - lsizemask.y, side = O_TOP - lsizemask.z);
-
- if(dist>=radius) return dist;
-
- UPOCTREE(return radius);
- }
+ INITRAYCUBE;
+ CHECKINSIDEWORLD;
+
+ int side = O_BOTTOM, x = int(v.x), y = int(v.y), z = int(v.z);
+ for(;;)
+ {
+ DOWNOCTREE(shadowent, );
+
+ cube &c = *lc;
+ ivec lo(x&(~0U<<lshift), y&(~0U<<lshift), z&(~0U<<lshift));
+
+ if(!isempty(c) && !(c.material&MAT_ALPHA))
+ {
+ if(isentirelysolid(c))
+ {
+ if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
+ {
+ if(mode&RAY_SKYTEX) return radius;
+ }
+ else return dist;
+ }
+ else
+ {
+ clipplanes &p = cache->clipcache[int(&c - worldroot)&(MAXCLIPPLANES-1)];
+ if(p.owner != &c || p.version != cache->version) { p.owner = &c; p.version = cache->version; genclipplanes(c, lo, 1<<lshift, p, false); }
+ INTERSECTPLANES(side = p.side[i], goto nextcube);
+ INTERSECTBOX(side = (i<<1) + 1 - lsizemask[i], goto nextcube);
+ if(exitdist >= 0)
+ {
+ if(c.texture[side]==DEFAULT_SKY && mode&RAY_SKIPSKY)
+ {
+ if(mode&RAY_SKYTEX) return radius;
+ }
+ else return dist+max(enterdist+0.1f, 0.0f);
+ }
+ }
+ }
+
+ nextcube:
+ FINDCLOSEST(side = O_RIGHT - lsizemask.x, side = O_FRONT - lsizemask.y, side = O_TOP - lsizemask.z);
+
+ if(dist>=radius) return dist;
+
+ UPOCTREE(return radius);
+ }
}
float rayent(const vec &o, const vec &ray, float radius, int mode, int size, int &orient, int &ent)
{
- hitent = -1;
- hitentdist = radius;
- hitorient = -1;
- float dist = raycube(o, ray, radius, mode, size);
- if((mode&RAY_ENTS) == RAY_ENTS)
- {
- float dent = disttooutsideent(o, ray, dist < 0 ? 1e16f : dist, mode, NULL);
- if(dent < 1e15f && (dist < 0 || dent < dist)) dist = dent;
- }
- orient = hitorient;
- ent = hitentdist == dist ? hitent : -1;
- return dist;
+ hitent = -1;
+ hitentdist = radius;
+ hitorient = -1;
+ float dist = raycube(o, ray, radius, mode, size);
+ if((mode&RAY_ENTS) == RAY_ENTS)
+ {
+ float dent = disttooutsideent(o, ray, dist < 0 ? 1e16f : dist, mode, NULL);
+ if(dent < 1e15f && (dist < 0 || dent < dist)) dist = dent;
+ }
+ orient = hitorient;
+ ent = hitentdist == dist ? hitent : -1;
+ return dist;
}
float raycubepos(const vec &o, const vec &ray, vec &hitpos, float radius, int mode, int size)
{
- hitpos = ray;
- float dist = raycube(o, ray, radius, mode, size);
- if(radius>0 && dist>=radius) dist = radius;
- hitpos.mul(dist).add(o);
- return dist;
+ hitpos = ray;
+ float dist = raycube(o, ray, radius, mode, size);
+ if(radius>0 && dist>=radius) dist = radius;
+ hitpos.mul(dist).add(o);
+ return dist;
}
bool raycubelos(const vec &o, const vec &dest, vec &hitpos)
{
- vec ray(dest);
- ray.sub(o);
- float mag = ray.magnitude();
- ray.mul(1/mag);
- float distance = raycubepos(o, ray, hitpos, mag, RAY_CLIPMAT|RAY_POLY);
- return distance >= mag;
+ vec ray(dest);
+ ray.sub(o);
+ float mag = ray.magnitude();
+ ray.mul(1/mag);
+ float distance = raycubepos(o, ray, hitpos, mag, RAY_CLIPMAT|RAY_POLY);
+ return distance >= mag;
}
float rayfloor(const vec &o, vec &floor, int mode, float radius)
{
- if(o.z<=0) return -1;
- hitsurface = vec(0, 0, 1);
- float dist = raycube(o, vec(0, 0, -1), radius, mode);
- if(dist<0 || (radius>0 && dist>=radius)) return dist;
- floor = hitsurface;
- return dist;
+ if(o.z<=0) return -1;
+ hitsurface = vec(0, 0, 1);
+ float dist = raycube(o, vec(0, 0, -1), radius, mode);
+ if(dist<0 || (radius>0 && dist>=radius)) return dist;
+ floor = hitsurface;
+ return dist;
}
///////////////////////// entity collision ///////////////////////////////////////////////
@@ -479,96 +479,96 @@ extern const float GRAVITY = 200.0f;
bool ellipseboxcollide(physent *d, const vec &dir, const vec &o, const vec &center, float yaw, float xr, float yr, float hi, float lo)
{
- float below = (o.z+center.z-lo) - (d->o.z+d->aboveeye),
- above = (d->o.z-d->eyeheight) - (o.z+center.z+hi);
- if(below>=0 || above>=0) return false;
-
- vec yo(d->o);
- yo.sub(o);
- yo.rotate_around_z(-yaw*RAD);
- yo.sub(center);
-
- float dx = clamp(yo.x, -xr, xr) - yo.x, dy = clamp(yo.y, -yr, yr) - yo.y,
- dist = sqrtf(dx*dx + dy*dy) - d->radius;
- if(dist < 0)
- {
- int sx = yo.x <= -xr ? -1 : (yo.x >= xr ? 1 : 0),
- sy = yo.y <= -yr ? -1 : (yo.y >= yr ? 1 : 0);
- if(dist > (yo.z < 0 ? below : above) && (sx || sy))
- {
- vec ydir(dir);
- ydir.rotate_around_z(-yaw*RAD);
- if(sx*yo.x - xr > sy*yo.y - yr)
- {
- if(dir.iszero() || sx*ydir.x < -1e-6f)
- {
- collidewall = vec(sx, 0, 0);
- collidewall.rotate_around_z(yaw*RAD);
- return true;
- }
- }
- else if(dir.iszero() || sy*ydir.y < -1e-6f)
- {
- collidewall = vec(0, sy, 0);
- collidewall.rotate_around_z(yaw*RAD);
- return true;
- }
- }
- if(yo.z < 0)
- {
- if(dir.iszero() || (dir.z > 0 && (d->type>=ENT_INANIMATE || below >= d->zmargin-(d->eyeheight+d->aboveeye)/4.0f)))
- {
- collidewall = vec(0, 0, -1);
- return true;
- }
- }
- else if(dir.iszero() || (dir.z < 0 && (d->type>=ENT_INANIMATE || above >= d->zmargin-(d->eyeheight+d->aboveeye)/3.0f)))
- {
- collidewall = vec(0, 0, 1);
- return true;
- }
- collideinside++;
- }
- return false;
+ float below = (o.z+center.z-lo) - (d->o.z+d->aboveeye),
+ above = (d->o.z-d->eyeheight) - (o.z+center.z+hi);
+ if(below>=0 || above>=0) return false;
+
+ vec yo(d->o);
+ yo.sub(o);
+ yo.rotate_around_z(-yaw*RAD);
+ yo.sub(center);
+
+ float dx = clamp(yo.x, -xr, xr) - yo.x, dy = clamp(yo.y, -yr, yr) - yo.y,
+ dist = sqrtf(dx*dx + dy*dy) - d->radius;
+ if(dist < 0)
+ {
+ int sx = yo.x <= -xr ? -1 : (yo.x >= xr ? 1 : 0),
+ sy = yo.y <= -yr ? -1 : (yo.y >= yr ? 1 : 0);
+ if(dist > (yo.z < 0 ? below : above) && (sx || sy))
+ {
+ vec ydir(dir);
+ ydir.rotate_around_z(-yaw*RAD);
+ if(sx*yo.x - xr > sy*yo.y - yr)
+ {
+ if(dir.iszero() || sx*ydir.x < -1e-6f)
+ {
+ collidewall = vec(sx, 0, 0);
+ collidewall.rotate_around_z(yaw*RAD);
+ return true;
+ }
+ }
+ else if(dir.iszero() || sy*ydir.y < -1e-6f)
+ {
+ collidewall = vec(0, sy, 0);
+ collidewall.rotate_around_z(yaw*RAD);
+ return true;
+ }
+ }
+ if(yo.z < 0)
+ {
+ if(dir.iszero() || (dir.z > 0 && (d->type>=ENT_INANIMATE || below >= d->zmargin-(d->eyeheight+d->aboveeye)/4.0f)))
+ {
+ collidewall = vec(0, 0, -1);
+ return true;
+ }
+ }
+ else if(dir.iszero() || (dir.z < 0 && (d->type>=ENT_INANIMATE || above >= d->zmargin-(d->eyeheight+d->aboveeye)/3.0f)))
+ {
+ collidewall = vec(0, 0, 1);
+ return true;
+ }
+ collideinside++;
+ }
+ return false;
}
bool ellipsecollide(physent *d, const vec &dir, const vec &o, const vec &center, float yaw, float xr, float yr, float hi, float lo)
{
- float below = (o.z+center.z-lo) - (d->o.z+d->aboveeye),
- above = (d->o.z-d->eyeheight) - (o.z+center.z+hi);
- if(below>=0 || above>=0) return false;
- vec yo(center);
- yo.rotate_around_z(yaw*RAD);
- yo.add(o);
- float x = yo.x - d->o.x, y = yo.y - d->o.y;
- float angle = atan2f(y, x), dangle = angle-(d->yaw+90)*RAD, eangle = angle-(yaw+90)*RAD;
- float dx = d->xradius*cosf(dangle), dy = d->yradius*sinf(dangle);
- float ex = xr*cosf(eangle), ey = yr*sinf(eangle);
- float dist = sqrtf(x*x + y*y) - sqrtf(dx*dx + dy*dy) - sqrtf(ex*ex + ey*ey);
- if(dist < 0)
- {
- if(dist > (d->o.z < yo.z ? below : above) && (dir.iszero() || x*dir.x + y*dir.y > 0))
- {
- collidewall = vec(-x, -y, 0);
- if(!collidewall.iszero()) collidewall.normalize();
- return true;
- }
- if(d->o.z < yo.z)
- {
- if(dir.iszero() || (dir.z > 0 && (d->type>=ENT_INANIMATE || below >= d->zmargin-(d->eyeheight+d->aboveeye)/4.0f)))
- {
- collidewall = vec(0, 0, -1);
- return true;
- }
- }
- else if(dir.iszero() || (dir.z < 0 && (d->type>=ENT_INANIMATE || above >= d->zmargin-(d->eyeheight+d->aboveeye)/3.0f)))
- {
- collidewall = vec(0, 0, 1);
- return true;
- }
- collideinside++;
- }
- return false;
+ float below = (o.z+center.z-lo) - (d->o.z+d->aboveeye),
+ above = (d->o.z-d->eyeheight) - (o.z+center.z+hi);
+ if(below>=0 || above>=0) return false;
+ vec yo(center);
+ yo.rotate_around_z(yaw*RAD);
+ yo.add(o);
+ float x = yo.x - d->o.x, y = yo.y - d->o.y;
+ float angle = atan2f(y, x), dangle = angle-(d->yaw+90)*RAD, eangle = angle-(yaw+90)*RAD;
+ float dx = d->xradius*cosf(dangle), dy = d->yradius*sinf(dangle);
+ float ex = xr*cosf(eangle), ey = yr*sinf(eangle);
+ float dist = sqrtf(x*x + y*y) - sqrtf(dx*dx + dy*dy) - sqrtf(ex*ex + ey*ey);
+ if(dist < 0)
+ {
+ if(dist > (d->o.z < yo.z ? below : above) && (dir.iszero() || x*dir.x + y*dir.y > 0))
+ {
+ collidewall = vec(-x, -y, 0);
+ if(!collidewall.iszero()) collidewall.normalize();
+ return true;
+ }
+ if(d->o.z < yo.z)
+ {
+ if(dir.iszero() || (dir.z > 0 && (d->type>=ENT_INANIMATE || below >= d->zmargin-(d->eyeheight+d->aboveeye)/4.0f)))
+ {
+ collidewall = vec(0, 0, -1);
+ return true;
+ }
+ }
+ else if(dir.iszero() || (dir.z < 0 && (d->type>=ENT_INANIMATE || above >= d->zmargin-(d->eyeheight+d->aboveeye)/3.0f)))
+ {
+ collidewall = vec(0, 0, 1);
+ return true;
+ }
+ collideinside++;
+ }
+ return false;
}
#define DYNENTCACHESIZE 1024
@@ -577,16 +577,16 @@ static uint dynentframe = 0;
static struct dynentcacheentry
{
- int x, y;
- uint frame;
- vector<physent *> dynents;
+ int x, y;
+ uint frame;
+ vector<physent *> dynents;
} dynentcache[DYNENTCACHESIZE];
void cleardynentcache()
{
- dynentframe++;
- if(!dynentframe || dynentframe == 1) loopi(DYNENTCACHESIZE) dynentcache[i].frame = 0;
- if(!dynentframe) dynentframe = 1;
+ dynentframe++;
+ if(!dynentframe || dynentframe == 1) loopi(DYNENTCACHESIZE) dynentcache[i].frame = 0;
+ if(!dynentframe) dynentframe = 1;
}
VARF(dynentsize, 4, 7, 12, cleardynentcache());
@@ -595,989 +595,962 @@ VARF(dynentsize, 4, 7, 12, cleardynentcache());
const vector<physent *> &checkdynentcache(int x, int y)
{
- dynentcacheentry &dec = dynentcache[DYNENTHASH(x, y)];
- if(dec.x == x && dec.y == y && dec.frame == dynentframe) return dec.dynents;
- dec.x = x;
- dec.y = y;
- dec.frame = dynentframe;
- dec.dynents.shrink(0);
- int numdyns = game::numdynents(), dsize = 1<<dynentsize, dx = x<<dynentsize, dy = y<<dynentsize;
- loopi(numdyns)
- {
- dynent *d = game::iterdynents(i);
- if(d->state != CS_ALIVE ||
- d->o.x+d->radius <= dx || d->o.x-d->radius >= dx+dsize ||
- d->o.y+d->radius <= dy || d->o.y-d->radius >= dy+dsize)
- continue;
- dec.dynents.add(d);
- }
- return dec.dynents;
+ dynentcacheentry &dec = dynentcache[DYNENTHASH(x, y)];
+ if(dec.x == x && dec.y == y && dec.frame == dynentframe) return dec.dynents;
+ dec.x = x;
+ dec.y = y;
+ dec.frame = dynentframe;
+ dec.dynents.shrink(0);
+ int numdyns = game::numdynents(), dsize = 1<<dynentsize, dx = x<<dynentsize, dy = y<<dynentsize;
+ loopi(numdyns)
+ {
+ dynent *d = game::iterdynents(i);
+ if(d->state != CS_ALIVE ||
+ d->o.x+d->radius <= dx || d->o.x-d->radius >= dx+dsize ||
+ d->o.y+d->radius <= dy || d->o.y-d->radius >= dy+dsize)
+ continue;
+ dec.dynents.add(d);
+ }
+ return dec.dynents;
}
#define loopdynentcache(curx, cury, o, radius) \
- for(int curx = max(int(o.x-radius), 0)>>dynentsize, endx = min(int(o.x+radius), worldsize-1)>>dynentsize; curx <= endx; curx++) \
- for(int cury = max(int(o.y-radius), 0)>>dynentsize, endy = min(int(o.y+radius), worldsize-1)>>dynentsize; cury <= endy; cury++)
+ for(int curx = max(int(o.x-radius), 0)>>dynentsize, endx = min(int(o.x+radius), worldsize-1)>>dynentsize; curx <= endx; curx++) \
+ for(int cury = max(int(o.y-radius), 0)>>dynentsize, endy = min(int(o.y+radius), worldsize-1)>>dynentsize; cury <= endy; cury++)
void updatedynentcache(physent *d)
{
- loopdynentcache(x, y, d->o, d->radius)
- {
- dynentcacheentry &dec = dynentcache[DYNENTHASH(x, y)];
- if(dec.x != x || dec.y != y || dec.frame != dynentframe || dec.dynents.find(d) >= 0) continue;
- dec.dynents.add(d);
- }
+ loopdynentcache(x, y, d->o, d->radius)
+ {
+ dynentcacheentry &dec = dynentcache[DYNENTHASH(x, y)];
+ if(dec.x != x || dec.y != y || dec.frame != dynentframe || dec.dynents.find(d) >= 0) continue;
+ dec.dynents.add(d);
+ }
}
bool overlapsdynent(const vec &o, float radius)
{
- loopdynentcache(x, y, o, radius)
- {
- const vector<physent *> &dynents = checkdynentcache(x, y);
- loopv(dynents)
- {
- physent *d = dynents[i];
- if(o.dist(d->o)-d->radius < radius) return true;
- }
- }
- return false;
+ loopdynentcache(x, y, o, radius)
+ {
+ const vector<physent *> &dynents = checkdynentcache(x, y);
+ loopv(dynents)
+ {
+ physent *d = dynents[i];
+ if(o.dist(d->o)-d->radius < radius) return true;
+ }
+ }
+ return false;
}
template<class E, class O>
static inline bool plcollide(physent *d, const vec &dir, physent *o)
{
- E entvol(d);
- O obvol(o);
- vec cp;
- if(mpr::collide(entvol, obvol, NULL, NULL, &cp))
- {
- vec wn = vec(cp).sub(obvol.center());
- collidewall = obvol.contactface(wn, dir.iszero() ? vec(wn).neg() : dir);
- if(!collidewall.iszero()) return true;
- collideinside++;
- }
- return false;
+ E entvol(d);
+ O obvol(o);
+ vec cp;
+ if(mpr::collide(entvol, obvol, NULL, NULL, &cp))
+ {
+ vec wn = vec(cp).sub(obvol.center());
+ collidewall = obvol.contactface(wn, dir.iszero() ? vec(wn).neg() : dir);
+ if(!collidewall.iszero()) return true;
+ collideinside++;
+ }
+ return false;
}
static inline bool plcollide(physent *d, const vec &dir, physent *o)
{
- switch(d->collidetype)
- {
- case COLLIDE_ELLIPSE:
- case COLLIDE_ELLIPSE_PRECISE:
- if(o->collidetype == COLLIDE_OBB) return ellipseboxcollide(d, dir, o->o, vec(0, 0, 0), o->yaw, o->xradius, o->yradius, o->aboveeye, o->eyeheight);
- else return ellipsecollide(d, dir, o->o, vec(0, 0, 0), o->yaw, o->xradius, o->yradius, o->aboveeye, o->eyeheight);
- case COLLIDE_OBB:
- if(o->collidetype == COLLIDE_OBB) return plcollide<mpr::EntOBB, mpr::EntOBB>(d, dir, o);
- else return plcollide<mpr::EntOBB, mpr::EntCylinder>(d, dir, o);
- default: return false;
- }
-}
-
-bool plcollide(physent *d, const vec &dir, bool insideplayercol) // collide with player or monster
-{
- if(d->type==ENT_CAMERA || d->state!=CS_ALIVE) return false;
- int lastinside = collideinside;
- physent *insideplayer = NULL;
- loopdynentcache(x, y, d->o, d->radius)
- {
- const vector<physent *> &dynents = checkdynentcache(x, y);
- loopv(dynents)
- {
- physent *o = dynents[i];
- if(o==d || d->o.reject(o->o, d->radius+o->radius)) continue;
- if(plcollide(d, dir, o))
- {
- collideplayer = o;
- game::dynentcollide(d, o, collidewall);
- return true;
- }
- if(collideinside > lastinside)
- {
- lastinside = collideinside;
- insideplayer = o;
- }
- }
- }
- if(insideplayer && insideplayercol)
- {
- collideplayer = insideplayer;
- game::dynentcollide(d, insideplayer, vec(0, 0, 0));
- return true;
- }
- return false;
+ switch(d->collidetype)
+ {
+ case COLLIDE_ELLIPSE:
+ case COLLIDE_ELLIPSE_PRECISE:
+ if(o->collidetype == COLLIDE_OBB) return ellipseboxcollide(d, dir, o->o, vec(0, 0, 0), o->yaw, o->xradius, o->yradius, o->aboveeye, o->eyeheight);
+ else return ellipsecollide(d, dir, o->o, vec(0, 0, 0), o->yaw, o->xradius, o->yradius, o->aboveeye, o->eyeheight);
+ case COLLIDE_OBB:
+ if(o->collidetype == COLLIDE_OBB) return plcollide<mpr::EntOBB, mpr::EntOBB>(d, dir, o);
+ else return plcollide<mpr::EntOBB, mpr::EntCylinder>(d, dir, o);
+ default: return false;
+ }
+}
+
+bool plcollide(physent *d, const vec &dir, bool insideplayercol) // collide with player or monster
+{
+ if(d->type==ENT_CAMERA || d->state!=CS_ALIVE) return false;
+ int lastinside = collideinside;
+ physent *insideplayer = NULL;
+ loopdynentcache(x, y, d->o, d->radius)
+ {
+ const vector<physent *> &dynents = checkdynentcache(x, y);
+ loopv(dynents)
+ {
+ physent *o = dynents[i];
+ if(o==d || d->o.reject(o->o, d->radius+o->radius)) continue;
+ if(plcollide(d, dir, o))
+ {
+ collideplayer = o;
+ game::dynentcollide(d, o, collidewall);
+ return true;
+ }
+ if(collideinside > lastinside)
+ {
+ lastinside = collideinside;
+ insideplayer = o;
+ }
+ }
+ }
+ if(insideplayer && insideplayercol)
+ {
+ collideplayer = insideplayer;
+ game::dynentcollide(d, insideplayer, vec(0, 0, 0));
+ return true;
+ }
+ return false;
}
void rotatebb(vec &center, vec &radius, int yaw)
{
- if(yaw < 0) yaw = 360 + yaw%360;
- else if(yaw >= 360) yaw %= 360;
- const vec2 &rot = sincos360[yaw];
- vec2 oldcenter(center), oldradius(radius);
- center.x = oldcenter.x*rot.x - oldcenter.y*rot.y;
- center.y = oldcenter.y*rot.x + oldcenter.x*rot.y;
- radius.x = fabs(oldradius.x*rot.x) + fabs(oldradius.y*rot.y);
- radius.y = fabs(oldradius.y*rot.x) + fabs(oldradius.x*rot.y);
+ if(yaw < 0) yaw = 360 + yaw%360;
+ else if(yaw >= 360) yaw %= 360;
+ const vec2 &rot = sincos360[yaw];
+ vec2 oldcenter(center), oldradius(radius);
+ center.x = oldcenter.x*rot.x - oldcenter.y*rot.y;
+ center.y = oldcenter.y*rot.x + oldcenter.x*rot.y;
+ radius.x = fabs(oldradius.x*rot.x) + fabs(oldradius.y*rot.y);
+ radius.y = fabs(oldradius.y*rot.x) + fabs(oldradius.x*rot.y);
}
template<class E, class M>
static inline bool mmcollide(physent *d, const vec &dir, const extentity &e, const vec &center, const vec &radius, float yaw)
{
- E entvol(d);
- M mdlvol(e.o, center, radius, yaw);
- vec cp;
- if(mpr::collide(entvol, mdlvol, NULL, NULL, &cp))
- {
- vec wn = vec(cp).sub(mdlvol.center());
- collidewall = mdlvol.contactface(wn, dir.iszero() ? vec(wn).neg() : dir);
- if(!collidewall.iszero()) return true;
- collideinside++;
- }
- return false;
-}
-
-bool mmcollide(physent *d, const vec &dir, octaentities &oc) // collide with a mapmodel
-{
- const vector<extentity *> &ents = entities::getents();
- loopv(oc.mapmodels)
- {
- extentity &e = *ents[oc.mapmodels[i]];
- if(e.flags&EF_NOCOLLIDE) continue;
- model *m = loadmapmodel(e.attr2);
- if(!m || !m->collide) continue;
-
- vec center, radius;
- float rejectradius = m->collisionbox(center, radius);
- if(d->o.reject(e.o, d->radius + rejectradius)) continue;
-
- float yaw = e.attr1;
- switch(d->collidetype)
- {
- case COLLIDE_ELLIPSE:
- case COLLIDE_ELLIPSE_PRECISE:
- if(m->ellipsecollide)
- {
- if(ellipsecollide(d, dir, e.o, center, yaw, radius.x, radius.y, radius.z, radius.z)) return true;
- }
- else if(ellipseboxcollide(d, dir, e.o, center, yaw, radius.x, radius.y, radius.z, radius.z)) return true;
- break;
- case COLLIDE_OBB:
- if(m->ellipsecollide)
- {
- if(mmcollide<mpr::EntOBB, mpr::ModelEllipse>(d, dir, e, center, radius, yaw)) return true;
- }
- else if(mmcollide<mpr::EntOBB, mpr::ModelOBB>(d, dir, e, center, radius, yaw)) return true;
- break;
- default: continue;
- }
- }
- return false;
+ E entvol(d);
+ M mdlvol(e.o, center, radius, yaw);
+ vec cp;
+ if(mpr::collide(entvol, mdlvol, NULL, NULL, &cp))
+ {
+ vec wn = vec(cp).sub(mdlvol.center());
+ collidewall = mdlvol.contactface(wn, dir.iszero() ? vec(wn).neg() : dir);
+ if(!collidewall.iszero()) return true;
+ collideinside++;
+ }
+ return false;
+}
+
+bool mmcollide(physent *d, const vec &dir, octaentities &oc) // collide with a mapmodel
+{
+ const vector<extentity *> &ents = entities::getents();
+ loopv(oc.mapmodels)
+ {
+ extentity &e = *ents[oc.mapmodels[i]];
+ if(e.flags&EF_NOCOLLIDE) continue;
+ model *m = loadmapmodel(e.attr2);
+ if(!m || !m->collide) continue;
+
+ vec center, radius;
+ float rejectradius = m->collisionbox(center, radius);
+ if(d->o.reject(e.o, d->radius + rejectradius)) continue;
+
+ float yaw = e.attr1;
+ switch(d->collidetype)
+ {
+ case COLLIDE_ELLIPSE:
+ case COLLIDE_ELLIPSE_PRECISE:
+ if(m->ellipsecollide)
+ {
+ if(ellipsecollide(d, dir, e.o, center, yaw, radius.x, radius.y, radius.z, radius.z)) return true;
+ }
+ else if(ellipseboxcollide(d, dir, e.o, center, yaw, radius.x, radius.y, radius.z, radius.z)) return true;
+ break;
+ case COLLIDE_OBB:
+ if(m->ellipsecollide)
+ {
+ if(mmcollide<mpr::EntOBB, mpr::ModelEllipse>(d, dir, e, center, radius, yaw)) return true;
+ }
+ else if(mmcollide<mpr::EntOBB, mpr::ModelOBB>(d, dir, e, center, radius, yaw)) return true;
+ break;
+ default: continue;
+ }
+ }
+ return false;
}
template<class E>
static bool fuzzycollidesolid(physent *d, const vec &dir, float cutoff, const cube &c, const ivec &co, int size) // collide with solid cube geometry
{
- int crad = size/2;
- if(fabs(d->o.x - co.x - crad) > d->radius + crad || fabs(d->o.y - co.y - crad) > d->radius + crad ||
- d->o.z + d->aboveeye < co.z || d->o.z - d->eyeheight > co.z + size)
- return false;
-
- E entvol(d);
- collidewall = vec(0, 0, 0);
- float bestdist = -1e10f;
- int visible = isentirelysolid(c) ? c.visible : 0xFF;
- #define CHECKSIDE(side, distval, dotval, margin, normal) if(visible&(1<<side)) do \
- { \
- float dist = distval; \
- if(dist > 0) return false; \
- if(dist <= bestdist) continue; \
- if(!dir.iszero()) \
- { \
- if(dotval >= -cutoff*dir.magnitude()) continue; \
- if(d->type<ENT_CAMERA && dotval < 0 && dist < margin) continue; \
- } \
- collidewall = normal; \
- bestdist = dist; \
- } while(0)
- CHECKSIDE(O_LEFT, co.x - (d->o.x + d->radius), -dir.x, -d->radius, vec(-1, 0, 0));
- CHECKSIDE(O_RIGHT, d->o.x - d->radius - (co.x + size), dir.x, -d->radius, vec(1, 0, 0));
- CHECKSIDE(O_BACK, co.y - (d->o.y + d->radius), -dir.y, -d->radius, vec(0, -1, 0));
- CHECKSIDE(O_FRONT, d->o.y - d->radius - (co.y + size), dir.y, -d->radius, vec(0, 1, 0));
- CHECKSIDE(O_BOTTOM, co.z - (d->o.z + d->aboveeye), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
- CHECKSIDE(O_TOP, d->o.z - d->eyeheight - (co.z + size), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
-
- if(collidewall.iszero())
- {
- collideinside++;
- return false;
- }
- return true;
+ int crad = size/2;
+ if(fabs(d->o.x - co.x - crad) > d->radius + crad || fabs(d->o.y - co.y - crad) > d->radius + crad ||
+ d->o.z + d->aboveeye < co.z || d->o.z - d->eyeheight > co.z + size)
+ return false;
+
+ E entvol(d);
+ collidewall = vec(0, 0, 0);
+ float bestdist = -1e10f;
+ int visible = isentirelysolid(c) ? c.visible : 0xFF;
+ #define CHECKSIDE(side, distval, dotval, margin, normal) if(visible&(1<<side)) do \
+ { \
+ float dist = distval; \
+ if(dist > 0) return false; \
+ if(dist <= bestdist) continue; \
+ if(!dir.iszero()) \
+ { \
+ if(dotval >= -cutoff*dir.magnitude()) continue; \
+ if(d->type<ENT_CAMERA && dotval < 0 && dist < margin) continue; \
+ } \
+ collidewall = normal; \
+ bestdist = dist; \
+ } while(0)
+ CHECKSIDE(O_LEFT, co.x - (d->o.x + d->radius), -dir.x, -d->radius, vec(-1, 0, 0));
+ CHECKSIDE(O_RIGHT, d->o.x - d->radius - (co.x + size), dir.x, -d->radius, vec(1, 0, 0));
+ CHECKSIDE(O_BACK, co.y - (d->o.y + d->radius), -dir.y, -d->radius, vec(0, -1, 0));
+ CHECKSIDE(O_FRONT, d->o.y - d->radius - (co.y + size), dir.y, -d->radius, vec(0, 1, 0));
+ CHECKSIDE(O_BOTTOM, co.z - (d->o.z + d->aboveeye), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
+ CHECKSIDE(O_TOP, d->o.z - d->eyeheight - (co.z + size), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
+
+ if(collidewall.iszero())
+ {
+ collideinside++;
+ return false;
+ }
+ return true;
}
template<class E>
static inline bool clampcollide(const clipplanes &p, const E &entvol, const plane &w, const vec &pw)
{
- if(w.x && (w.y || w.z) && fabs(pw.x - p.o.x) > p.r.x)
- {
- vec c = entvol.center();
- float fv = pw.x < p.o.x ? p.o.x-p.r.x : p.o.x+p.r.x, fdist = (w.x*fv + w.y*c.y + w.z*c.z + w.offset) / (w.y*w.y + w.z*w.z);
- vec fdir(fv - c.x, -w.y*fdist, -w.z*fdist);
- if((pw.y-c.y-fdir.y)*w.y + (pw.z-c.z-fdir.z)*w.z >= 0 && entvol.supportpoint(fdir).squaredist(c) < fdir.squaredlen()) return true;
- }
- if(w.y && (w.x || w.z) && fabs(pw.y - p.o.y) > p.r.y)
- {
- vec c = entvol.center();
- float fv = pw.y < p.o.y ? p.o.y-p.r.y : p.o.y+p.r.y, fdist = (w.x*c.x + w.y*fv + w.z*c.z + w.offset) / (w.x*w.x + w.z*w.z);
- vec fdir(-w.x*fdist, fv - c.y, -w.z*fdist);
- if((pw.x-c.x-fdir.x)*w.x + (pw.z-c.z-fdir.z)*w.z >= 0 && entvol.supportpoint(fdir).squaredist(c) < fdir.squaredlen()) return true;
- }
- if(w.z && (w.x || w.y) && fabs(pw.z - p.o.z) > p.r.z)
- {
- vec c = entvol.center();
- float fv = pw.z < p.o.z ? p.o.z-p.r.z : p.o.z+p.r.z, fdist = (w.x*c.x + w.y*c.y + w.z*fv + w.offset) / (w.x*w.x + w.y*w.y);
- vec fdir(-w.x*fdist, -w.y*fdist, fv - c.z);
- if((pw.x-c.x-fdir.x)*w.x + (pw.y-c.y-fdir.y)*w.y >= 0 && entvol.supportpoint(fdir).squaredist(c) < fdir.squaredlen()) return true;
- }
- return false;
+ if(w.x && (w.y || w.z) && fabs(pw.x - p.o.x) > p.r.x)
+ {
+ vec c = entvol.center();
+ float fv = pw.x < p.o.x ? p.o.x-p.r.x : p.o.x+p.r.x, fdist = (w.x*fv + w.y*c.y + w.z*c.z + w.offset) / (w.y*w.y + w.z*w.z);
+ vec fdir(fv - c.x, -w.y*fdist, -w.z*fdist);
+ if((pw.y-c.y-fdir.y)*w.y + (pw.z-c.z-fdir.z)*w.z >= 0 && entvol.supportpoint(fdir).squaredist(c) < fdir.squaredlen()) return true;
+ }
+ if(w.y && (w.x || w.z) && fabs(pw.y - p.o.y) > p.r.y)
+ {
+ vec c = entvol.center();
+ float fv = pw.y < p.o.y ? p.o.y-p.r.y : p.o.y+p.r.y, fdist = (w.x*c.x + w.y*fv + w.z*c.z + w.offset) / (w.x*w.x + w.z*w.z);
+ vec fdir(-w.x*fdist, fv - c.y, -w.z*fdist);
+ if((pw.x-c.x-fdir.x)*w.x + (pw.z-c.z-fdir.z)*w.z >= 0 && entvol.supportpoint(fdir).squaredist(c) < fdir.squaredlen()) return true;
+ }
+ if(w.z && (w.x || w.y) && fabs(pw.z - p.o.z) > p.r.z)
+ {
+ vec c = entvol.center();
+ float fv = pw.z < p.o.z ? p.o.z-p.r.z : p.o.z+p.r.z, fdist = (w.x*c.x + w.y*c.y + w.z*fv + w.offset) / (w.x*w.x + w.y*w.y);
+ vec fdir(-w.x*fdist, -w.y*fdist, fv - c.z);
+ if((pw.x-c.x-fdir.x)*w.x + (pw.y-c.y-fdir.y)*w.y >= 0 && entvol.supportpoint(fdir).squaredist(c) < fdir.squaredlen()) return true;
+ }
+ return false;
}
template<class E>
static bool fuzzycollideplanes(physent *d, const vec &dir, float cutoff, const cube &c, const ivec &co, int size) // collide with deformed cube geometry
{
- const clipplanes &p = getclipplanes(c, co, size);
-
- if(fabs(d->o.x - p.o.x) > p.r.x + d->radius || fabs(d->o.y - p.o.y) > p.r.y + d->radius ||
- d->o.z + d->aboveeye < p.o.z - p.r.z || d->o.z - d->eyeheight > p.o.z + p.r.z)
- return false;
-
- collidewall = vec(0, 0, 0);
- float bestdist = -1e10f;
- int visible = p.visible;
- CHECKSIDE(O_LEFT, p.o.x - p.r.x - (d->o.x + d->radius), -dir.x, -d->radius, vec(-1, 0, 0));
- CHECKSIDE(O_RIGHT, d->o.x - d->radius - (p.o.x + p.r.x), dir.x, -d->radius, vec(1, 0, 0));
- CHECKSIDE(O_BACK, p.o.y - p.r.y - (d->o.y + d->radius), -dir.y, -d->radius, vec(0, -1, 0));
- CHECKSIDE(O_FRONT, d->o.y - d->radius - (p.o.y + p.r.y), dir.y, -d->radius, vec(0, 1, 0));
- CHECKSIDE(O_BOTTOM, p.o.z - p.r.z - (d->o.z + d->aboveeye), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
- CHECKSIDE(O_TOP, d->o.z - d->eyeheight - (p.o.z + p.r.z), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
-
- E entvol(d);
- int bestplane = -1;
- loopi(p.size)
- {
- const plane &w = p.p[i];
- vec pw = entvol.supportpoint(vec(w).neg());
- float dist = w.dist(pw);
- if(dist >= 0) return false;
- if(dist <= bestdist) continue;
- bestplane = -1;
- bestdist = dist;
- if(!dir.iszero())
- {
- if(w.dot(dir) >= -cutoff*dir.magnitude()) continue;
- if(d->type<ENT_CAMERA &&
- dist < (dir.z*w.z < 0 ?
- d->zmargin-(d->eyeheight+d->aboveeye)/(dir.z < 0 ? 3.0f : 4.0f) :
- ((dir.x*w.x < 0 || dir.y*w.y < 0) ? -d->radius : 0)))
- continue;
- }
- if(clampcollide(p, entvol, w, pw)) continue;
- bestplane = i;
- }
- if(bestplane >= 0) collidewall = p.p[bestplane];
- else if(collidewall.iszero())
- {
- collideinside++;
- return false;
- }
- return true;
+ const clipplanes &p = getclipplanes(c, co, size);
+
+ if(fabs(d->o.x - p.o.x) > p.r.x + d->radius || fabs(d->o.y - p.o.y) > p.r.y + d->radius ||
+ d->o.z + d->aboveeye < p.o.z - p.r.z || d->o.z - d->eyeheight > p.o.z + p.r.z)
+ return false;
+
+ collidewall = vec(0, 0, 0);
+ float bestdist = -1e10f;
+ int visible = p.visible;
+ CHECKSIDE(O_LEFT, p.o.x - p.r.x - (d->o.x + d->radius), -dir.x, -d->radius, vec(-1, 0, 0));
+ CHECKSIDE(O_RIGHT, d->o.x - d->radius - (p.o.x + p.r.x), dir.x, -d->radius, vec(1, 0, 0));
+ CHECKSIDE(O_BACK, p.o.y - p.r.y - (d->o.y + d->radius), -dir.y, -d->radius, vec(0, -1, 0));
+ CHECKSIDE(O_FRONT, d->o.y - d->radius - (p.o.y + p.r.y), dir.y, -d->radius, vec(0, 1, 0));
+ CHECKSIDE(O_BOTTOM, p.o.z - p.r.z - (d->o.z + d->aboveeye), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
+ CHECKSIDE(O_TOP, d->o.z - d->eyeheight - (p.o.z + p.r.z), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
+
+ E entvol(d);
+ int bestplane = -1;
+ loopi(p.size)
+ {
+ const plane &w = p.p[i];
+ vec pw = entvol.supportpoint(vec(w).neg());
+ float dist = w.dist(pw);
+ if(dist >= 0) return false;
+ if(dist <= bestdist) continue;
+ bestplane = -1;
+ bestdist = dist;
+ if(!dir.iszero())
+ {
+ if(w.dot(dir) >= -cutoff*dir.magnitude()) continue;
+ if(d->type<ENT_CAMERA &&
+ dist < (dir.z*w.z < 0 ?
+ d->zmargin-(d->eyeheight+d->aboveeye)/(dir.z < 0 ? 3.0f : 4.0f) :
+ ((dir.x*w.x < 0 || dir.y*w.y < 0) ? -d->radius : 0)))
+ continue;
+ }
+ if(clampcollide(p, entvol, w, pw)) continue;
+ bestplane = i;
+ }
+ if(bestplane >= 0) collidewall = p.p[bestplane];
+ else if(collidewall.iszero())
+ {
+ collideinside++;
+ return false;
+ }
+ return true;
}
template<class E>
static bool cubecollidesolid(physent *d, const vec &dir, float cutoff, const cube &c, const ivec &co, int size) // collide with solid cube geometry
{
- int crad = size/2;
- if(fabs(d->o.x - co.x - crad) > d->radius + crad || fabs(d->o.y - co.y - crad) > d->radius + crad ||
- d->o.z + d->aboveeye < co.z || d->o.z - d->eyeheight > co.z + size)
- return false;
-
- E entvol(d);
- bool collided = mpr::collide(mpr::SolidCube(co, size), entvol);
- if(!collided) return false;
-
- collidewall = vec(0, 0, 0);
- float bestdist = -1e10f;
- int visible = isentirelysolid(c) ? c.visible : 0xFF;
- CHECKSIDE(O_LEFT, co.x - entvol.right(), -dir.x, -d->radius, vec(-1, 0, 0));
- CHECKSIDE(O_RIGHT, entvol.left() - (co.x + size), dir.x, -d->radius, vec(1, 0, 0));
- CHECKSIDE(O_BACK, co.y - entvol.front(), -dir.y, -d->radius, vec(0, -1, 0));
- CHECKSIDE(O_FRONT, entvol.back() - (co.y + size), dir.y, -d->radius, vec(0, 1, 0));
- CHECKSIDE(O_BOTTOM, co.z - entvol.top(), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
- CHECKSIDE(O_TOP, entvol.bottom() - (co.z + size), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
-
- if(collidewall.iszero())
- {
- collideinside++;
- return false;
- }
- return true;
+ int crad = size/2;
+ if(fabs(d->o.x - co.x - crad) > d->radius + crad || fabs(d->o.y - co.y - crad) > d->radius + crad ||
+ d->o.z + d->aboveeye < co.z || d->o.z - d->eyeheight > co.z + size)
+ return false;
+
+ E entvol(d);
+ bool collided = mpr::collide(mpr::SolidCube(co, size), entvol);
+ if(!collided) return false;
+
+ collidewall = vec(0, 0, 0);
+ float bestdist = -1e10f;
+ int visible = isentirelysolid(c) ? c.visible : 0xFF;
+ CHECKSIDE(O_LEFT, co.x - entvol.right(), -dir.x, -d->radius, vec(-1, 0, 0));
+ CHECKSIDE(O_RIGHT, entvol.left() - (co.x + size), dir.x, -d->radius, vec(1, 0, 0));
+ CHECKSIDE(O_BACK, co.y - entvol.front(), -dir.y, -d->radius, vec(0, -1, 0));
+ CHECKSIDE(O_FRONT, entvol.back() - (co.y + size), dir.y, -d->radius, vec(0, 1, 0));
+ CHECKSIDE(O_BOTTOM, co.z - entvol.top(), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
+ CHECKSIDE(O_TOP, entvol.bottom() - (co.z + size), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
+
+ if(collidewall.iszero())
+ {
+ collideinside++;
+ return false;
+ }
+ return true;
}
template<class E>
static bool cubecollideplanes(physent *d, const vec &dir, float cutoff, const cube &c, const ivec &co, int size) // collide with deformed cube geometry
{
- const clipplanes &p = getclipplanes(c, co, size);
-
- if(fabs(d->o.x - p.o.x) > p.r.x + d->radius || fabs(d->o.y - p.o.y) > p.r.y + d->radius ||
- d->o.z + d->aboveeye < p.o.z - p.r.z || d->o.z - d->eyeheight > p.o.z + p.r.z)
- return false;
-
- E entvol(d);
- bool collided = mpr::collide(mpr::CubePlanes(p), entvol);
- if(!collided) return false;
-
- collidewall = vec(0, 0, 0);
- float bestdist = -1e10f;
- int visible = p.visible;
- CHECKSIDE(O_LEFT, p.o.x - p.r.x - entvol.right(), -dir.x, -d->radius, vec(-1, 0, 0));
- CHECKSIDE(O_RIGHT, entvol.left() - (p.o.x + p.r.x), dir.x, -d->radius, vec(1, 0, 0));
- CHECKSIDE(O_BACK, p.o.y - p.r.y - entvol.front(), -dir.y, -d->radius, vec(0, -1, 0));
- CHECKSIDE(O_FRONT, entvol.back() - (p.o.y + p.r.y), dir.y, -d->radius, vec(0, 1, 0));
- CHECKSIDE(O_BOTTOM, p.o.z - p.r.z - entvol.top(), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
- CHECKSIDE(O_TOP, entvol.bottom() - (p.o.z + p.r.z), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
-
- int bestplane = -1;
- loopi(p.size)
- {
- const plane &w = p.p[i];
- vec pw = entvol.supportpoint(vec(w).neg());
- float dist = w.dist(pw);
- if(dist <= bestdist) continue;
- bestplane = -1;
- bestdist = dist;
- if(!dir.iszero())
- {
- if(w.dot(dir) >= -cutoff*dir.magnitude()) continue;
- if(d->type<ENT_CAMERA &&
- dist < (dir.z*w.z < 0 ?
- d->zmargin-(d->eyeheight+d->aboveeye)/(dir.z < 0 ? 3.0f : 4.0f) :
- ((dir.x*w.x < 0 || dir.y*w.y < 0) ? -d->radius : 0)))
- continue;
- }
- if(clampcollide(p, entvol, w, pw)) continue;
- bestplane = i;
- }
- if(bestplane >= 0) collidewall = p.p[bestplane];
- else if(collidewall.iszero())
- {
- collideinside++;
- return false;
- }
- return true;
+ const clipplanes &p = getclipplanes(c, co, size);
+
+ if(fabs(d->o.x - p.o.x) > p.r.x + d->radius || fabs(d->o.y - p.o.y) > p.r.y + d->radius ||
+ d->o.z + d->aboveeye < p.o.z - p.r.z || d->o.z - d->eyeheight > p.o.z + p.r.z)
+ return false;
+
+ E entvol(d);
+ bool collided = mpr::collide(mpr::CubePlanes(p), entvol);
+ if(!collided) return false;
+
+ collidewall = vec(0, 0, 0);
+ float bestdist = -1e10f;
+ int visible = p.visible;
+ CHECKSIDE(O_LEFT, p.o.x - p.r.x - entvol.right(), -dir.x, -d->radius, vec(-1, 0, 0));
+ CHECKSIDE(O_RIGHT, entvol.left() - (p.o.x + p.r.x), dir.x, -d->radius, vec(1, 0, 0));
+ CHECKSIDE(O_BACK, p.o.y - p.r.y - entvol.front(), -dir.y, -d->radius, vec(0, -1, 0));
+ CHECKSIDE(O_FRONT, entvol.back() - (p.o.y + p.r.y), dir.y, -d->radius, vec(0, 1, 0));
+ CHECKSIDE(O_BOTTOM, p.o.z - p.r.z - entvol.top(), -dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/4.0f, vec(0, 0, -1));
+ CHECKSIDE(O_TOP, entvol.bottom() - (p.o.z + p.r.z), dir.z, d->zmargin-(d->eyeheight+d->aboveeye)/3.0f, vec(0, 0, 1));
+
+ int bestplane = -1;
+ loopi(p.size)
+ {
+ const plane &w = p.p[i];
+ vec pw = entvol.supportpoint(vec(w).neg());
+ float dist = w.dist(pw);
+ if(dist <= bestdist) continue;
+ bestplane = -1;
+ bestdist = dist;
+ if(!dir.iszero())
+ {
+ if(w.dot(dir) >= -cutoff*dir.magnitude()) continue;
+ if(d->type<ENT_CAMERA &&
+ dist < (dir.z*w.z < 0 ?
+ d->zmargin-(d->eyeheight+d->aboveeye)/(dir.z < 0 ? 3.0f : 4.0f) :
+ ((dir.x*w.x < 0 || dir.y*w.y < 0) ? -d->radius : 0)))
+ continue;
+ }
+ if(clampcollide(p, entvol, w, pw)) continue;
+ bestplane = i;
+ }
+ if(bestplane >= 0) collidewall = p.p[bestplane];
+ else if(collidewall.iszero())
+ {
+ collideinside++;
+ return false;
+ }
+ return true;
}
static inline bool cubecollide(physent *d, const vec &dir, float cutoff, const cube &c, const ivec &co, int size, bool solid)
{
- switch(d->collidetype)
- {
- case COLLIDE_OBB:
- if(isentirelysolid(c) || solid) return cubecollidesolid<mpr::EntOBB>(d, dir, cutoff, c, co, size);
- else return cubecollideplanes<mpr::EntOBB>(d, dir, cutoff, c, co, size);
- case COLLIDE_ELLIPSE:
- if(isentirelysolid(c) || solid) return fuzzycollidesolid<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
- else return fuzzycollideplanes<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
- case COLLIDE_ELLIPSE_PRECISE:
- if(isentirelysolid(c) || solid) return cubecollidesolid<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
- else return cubecollideplanes<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
- default: return false;
- }
+ switch(d->collidetype)
+ {
+ case COLLIDE_OBB:
+ if(isentirelysolid(c) || solid) return cubecollidesolid<mpr::EntOBB>(d, dir, cutoff, c, co, size);
+ else return cubecollideplanes<mpr::EntOBB>(d, dir, cutoff, c, co, size);
+ case COLLIDE_ELLIPSE:
+ if(isentirelysolid(c) || solid) return fuzzycollidesolid<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
+ else return fuzzycollideplanes<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
+ case COLLIDE_ELLIPSE_PRECISE:
+ if(isentirelysolid(c) || solid) return cubecollidesolid<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
+ else return cubecollideplanes<mpr::EntCapsule>(d, dir, cutoff, c, co, size);
+ default: return false;
+ }
}
static inline bool octacollide(physent *d, const vec &dir, float cutoff, const ivec &bo, const ivec &bs, const cube *c, const ivec &cor, int size) // collide with octants
{
- loopoctabox(cor, size, bo, bs)
- {
- if(c[i].ext && c[i].ext->ents) if(mmcollide(d, dir, *c[i].ext->ents)) return true;
- ivec o(i, cor, size);
- if(c[i].children)
- {
- if(octacollide(d, dir, cutoff, bo, bs, c[i].children, o, size>>1)) return true;
- }
- else
- {
- bool solid = false;
- switch(c[i].material&MATF_CLIP)
- {
- case MAT_NOCLIP: continue;
- case MAT_GAMECLIP: if(d->type==ENT_AI) solid = true; break;
- case MAT_CLIP: if(isclipped(c[i].material&MATF_VOLUME) || d->type<ENT_CAMERA) solid = true; break;
- }
- if(!solid && isempty(c[i])) continue;
- if(cubecollide(d, dir, cutoff, c[i], o, size, solid)) return true;
- }
- }
- return false;
+ loopoctabox(cor, size, bo, bs)
+ {
+ if(c[i].ext && c[i].ext->ents) if(mmcollide(d, dir, *c[i].ext->ents)) return true;
+ ivec o(i, cor, size);
+ if(c[i].children)
+ {
+ if(octacollide(d, dir, cutoff, bo, bs, c[i].children, o, size>>1)) return true;
+ }
+ else
+ {
+ bool solid = false;
+ switch(c[i].material&MATF_CLIP)
+ {
+ case MAT_NOCLIP: continue;
+ case MAT_GAMECLIP: if(d->type==ENT_AI) solid = true; break;
+ case MAT_CLIP: if(isclipped(c[i].material&MATF_VOLUME) || d->type<ENT_CAMERA) solid = true; break;
+ }
+ if(!solid && isempty(c[i])) continue;
+ if(cubecollide(d, dir, cutoff, c[i], o, size, solid)) return true;
+ }
+ }
+ return false;
}
static inline bool octacollide(physent *d, const vec &dir, float cutoff, const ivec &bo, const ivec &bs)
{
- int diff = (bo.x^bs.x) | (bo.y^bs.y) | (bo.z^bs.z),
- scale = worldscale-1;
- if(diff&~((1<<scale)-1) || uint(bo.x|bo.y|bo.z|bs.x|bs.y|bs.z) >= uint(worldsize))
- return octacollide(d, dir, cutoff, bo, bs, worldroot, ivec(0, 0, 0), worldsize>>1);
- const cube *c = &worldroot[octastep(bo.x, bo.y, bo.z, scale)];
- if(c->ext && c->ext->ents && mmcollide(d, dir, *c->ext->ents)) return true;
- scale--;
- while(c->children && !(diff&(1<<scale)))
- {
- c = &c->children[octastep(bo.x, bo.y, bo.z, scale)];
- if(c->ext && c->ext->ents && mmcollide(d, dir, *c->ext->ents)) return true;
- scale--;
- }
- if(c->children) return octacollide(d, dir, cutoff, bo, bs, c->children, ivec(bo).mask(~((2<<scale)-1)), 1<<scale);
- bool solid = false;
- switch(c->material&MATF_CLIP)
- {
- case MAT_NOCLIP: return false;
- case MAT_GAMECLIP: if(d->type==ENT_AI) solid = true; break;
- case MAT_CLIP: if(isclipped(c->material&MATF_VOLUME) || d->type<ENT_CAMERA) solid = true; break;
- }
- if(!solid && isempty(*c)) return false;
- int csize = 2<<scale, cmask = ~(csize-1);
- return cubecollide(d, dir, cutoff, *c, ivec(bo).mask(cmask), csize, solid);
+ int diff = (bo.x^bs.x) | (bo.y^bs.y) | (bo.z^bs.z),
+ scale = worldscale-1;
+ if(diff&~((1<<scale)-1) || uint(bo.x|bo.y|bo.z|bs.x|bs.y|bs.z) >= uint(worldsize))
+ return octacollide(d, dir, cutoff, bo, bs, worldroot, ivec(0, 0, 0), worldsize>>1);
+ const cube *c = &worldroot[octastep(bo.x, bo.y, bo.z, scale)];
+ if(c->ext && c->ext->ents && mmcollide(d, dir, *c->ext->ents)) return true;
+ scale--;
+ while(c->children && !(diff&(1<<scale)))
+ {
+ c = &c->children[octastep(bo.x, bo.y, bo.z, scale)];
+ if(c->ext && c->ext->ents && mmcollide(d, dir, *c->ext->ents)) return true;
+ scale--;
+ }
+ if(c->children) return octacollide(d, dir, cutoff, bo, bs, c->children, ivec(bo).mask(~((2<<scale)-1)), 1<<scale);
+ bool solid = false;
+ switch(c->material&MATF_CLIP)
+ {
+ case MAT_NOCLIP: return false;
+ case MAT_GAMECLIP: if(d->type==ENT_AI) solid = true; break;
+ case MAT_CLIP: if(isclipped(c->material&MATF_VOLUME) || d->type<ENT_CAMERA) solid = true; break;
+ }
+ if(!solid && isempty(*c)) return false;
+ int csize = 2<<scale, cmask = ~(csize-1);
+ return cubecollide(d, dir, cutoff, *c, ivec(bo).mask(cmask), csize, solid);
}
// all collision happens here
bool collide(physent *d, const vec &dir, float cutoff, bool playercol, bool insideplayercol)
{
- collideinside = 0;
- collideplayer = NULL;
- collidewall = vec(0, 0, 0);
- ivec bo(int(d->o.x-d->radius), int(d->o.y-d->radius), int(d->o.z-d->eyeheight)),
- bs(int(d->o.x+d->radius), int(d->o.y+d->radius), int(d->o.z+d->aboveeye));
- bs.add(1); // guard space for rounding errors
- return octacollide(d, dir, cutoff, bo, bs) || (playercol && plcollide(d, dir, insideplayercol));
+ collideinside = 0;
+ collideplayer = NULL;
+ collidewall = vec(0, 0, 0);
+ ivec bo(int(d->o.x-d->radius), int(d->o.y-d->radius), int(d->o.z-d->eyeheight)),
+ bs(int(d->o.x+d->radius), int(d->o.y+d->radius), int(d->o.z+d->aboveeye));
+ bs.add(1); // guard space for rounding errors
+ return octacollide(d, dir, cutoff, bo, bs) || (playercol && plcollide(d, dir, insideplayercol));
}
void recalcdir(physent *d, const vec &oldvel, vec &dir)
{
- float speed = oldvel.magnitude();
- if(speed > 1e-6f)
- {
- float step = dir.magnitude();
- dir = d->vel;
- dir.add(d->falling);
- dir.mul(step/speed);
- }
+ float speed = oldvel.magnitude();
+ if(speed > 1e-6f)
+ {
+ float step = dir.magnitude();
+ dir = d->vel;
+ dir.add(d->falling);
+ dir.mul(step/speed);
+ }
}
void slideagainst(physent *d, vec &dir, const vec &obstacle, bool foundfloor, bool slidecollide)
{
- vec wall(obstacle);
- if(foundfloor ? wall.z > 0 : slidecollide)
- {
- wall.z = 0;
- if(!wall.iszero()) wall.normalize();
- }
- vec oldvel(d->vel);
- oldvel.add(d->falling);
- d->vel.project(wall);
- d->falling.project(wall);
- recalcdir(d, oldvel, dir);
+ vec wall(obstacle);
+ if(foundfloor ? wall.z > 0 : slidecollide)
+ {
+ wall.z = 0;
+ if(!wall.iszero()) wall.normalize();
+ }
+ vec oldvel(d->vel);
+ oldvel.add(d->falling);
+ d->vel.project(wall);
+ d->falling.project(wall);
+ recalcdir(d, oldvel, dir);
}
void switchfloor(physent *d, vec &dir, const vec &floor)
{
- if(floor.z >= FLOORZ) d->falling = vec(0, 0, 0);
-
- vec oldvel(d->vel);
- oldvel.add(d->falling);
- if(dir.dot(floor) >= 0)
- {
- if(d->physstate < PHYS_SLIDE || fabs(dir.dot(d->floor)) > 0.01f*dir.magnitude()) return;
- d->vel.projectxy(floor, 0.0f);
- }
- else d->vel.projectxy(floor);
- d->falling.project(floor);
- recalcdir(d, oldvel, dir);
+ if(floor.z >= FLOORZ) d->falling = vec(0, 0, 0);
+
+ vec oldvel(d->vel);
+ oldvel.add(d->falling);
+ if(dir.dot(floor) >= 0)
+ {
+ if(d->physstate < PHYS_SLIDE || fabs(dir.dot(d->floor)) > 0.01f*dir.magnitude()) return;
+ d->vel.projectxy(floor, 0.0f);
+ }
+ else d->vel.projectxy(floor);
+ d->falling.project(floor);
+ recalcdir(d, oldvel, dir);
}
bool trystepup(physent *d, vec &dir, const vec &obstacle, float maxstep, const vec &floor)
{
- vec old(d->o), stairdir = (obstacle.z >= 0 && obstacle.z < SLOPEZ ? vec(-obstacle.x, -obstacle.y, 0) : vec(dir.x, dir.y, 0)).rescale(1);
- bool cansmooth = true;
- /* check if there is space atop the stair to move to */
- if(d->physstate != PHYS_STEP_UP)
- {
- vec checkdir = stairdir;
- checkdir.mul(0.1f);
- checkdir.z += maxstep + 0.1f;
- d->o.add(checkdir);
- if(collide(d))
- {
- d->o = old;
- if(!collide(d, vec(0, 0, -1), SLOPEZ)) return false;
- cansmooth = false;
- }
- }
-
- if(cansmooth)
- {
- vec checkdir = stairdir;
- checkdir.z += 1;
- checkdir.mul(maxstep);
- d->o = old;
- d->o.add(checkdir);
- int scale = 2;
- if(collide(d, checkdir))
- {
- if(!collide(d, vec(0, 0, -1), SLOPEZ))
- {
- d->o = old;
- return false;
- }
- d->o.add(checkdir);
- if(collide(d, vec(0, 0, -1), SLOPEZ)) scale = 1;
- }
- if(scale != 1)
- {
- d->o = old;
- d->o.sub(checkdir.mul(vec(2, 2, 1)));
- if(!collide(d, vec(0, 0, -1), SLOPEZ)) scale = 1;
- }
-
- d->o = old;
- vec smoothdir(dir.x, dir.y, 0);
- float magxy = smoothdir.magnitude();
- if(magxy > 1e-9f)
- {
- if(magxy > scale*dir.z)
- {
- smoothdir.mul(1/magxy);
- smoothdir.z = 1.0f/scale;
- smoothdir.mul(dir.magnitude()/smoothdir.magnitude());
- }
- else smoothdir.z = dir.z;
- d->o.add(smoothdir);
- d->o.z += maxstep + 0.1f;
- if(!collide(d, smoothdir))
- {
- d->o.z -= maxstep + 0.1f;
- if(d->physstate == PHYS_FALL || d->floor != floor)
- {
- d->timeinair = 0;
- d->floor = floor;
- switchfloor(d, dir, d->floor);
- }
- d->physstate = PHYS_STEP_UP;
- return true;
- }
- }
- }
-
- /* try stepping up */
- d->o = old;
- d->o.z += dir.magnitude();
- if(!collide(d, vec(0, 0, 1)))
- {
- if(d->physstate == PHYS_FALL || d->floor != floor)
- {
- d->timeinair = 0;
- d->floor = floor;
- switchfloor(d, dir, d->floor);
- }
- if(cansmooth) d->physstate = PHYS_STEP_UP;
- return true;
- }
- d->o = old;
- return false;
+ vec old(d->o), stairdir = (obstacle.z >= 0 && obstacle.z < SLOPEZ ? vec(-obstacle.x, -obstacle.y, 0) : vec(dir.x, dir.y, 0)).rescale(1);
+ bool cansmooth = true;
+ /* check if there is space atop the stair to move to */
+ if(d->physstate != PHYS_STEP_UP)
+ {
+ vec checkdir = stairdir;
+ checkdir.mul(0.1f);
+ checkdir.z += maxstep + 0.1f;
+ d->o.add(checkdir);
+ if(collide(d))
+ {
+ d->o = old;
+ if(!collide(d, vec(0, 0, -1), SLOPEZ)) return false;
+ cansmooth = false;
+ }
+ }
+
+ if(cansmooth)
+ {
+ vec checkdir = stairdir;
+ checkdir.z += 1;
+ checkdir.mul(maxstep);
+ d->o = old;
+ d->o.add(checkdir);
+ int scale = 2;
+ if(collide(d, checkdir))
+ {
+ if(!collide(d, vec(0, 0, -1), SLOPEZ))
+ {
+ d->o = old;
+ return false;
+ }
+ d->o.add(checkdir);
+ if(collide(d, vec(0, 0, -1), SLOPEZ)) scale = 1;
+ }
+ if(scale != 1)
+ {
+ d->o = old;
+ d->o.sub(checkdir.mul(vec(2, 2, 1)));
+ if(!collide(d, vec(0, 0, -1), SLOPEZ)) scale = 1;
+ }
+
+ d->o = old;
+ vec smoothdir(dir.x, dir.y, 0);
+ float magxy = smoothdir.magnitude();
+ if(magxy > 1e-9f)
+ {
+ if(magxy > scale*dir.z)
+ {
+ smoothdir.mul(1/magxy);
+ smoothdir.z = 1.0f/scale;
+ smoothdir.mul(dir.magnitude()/smoothdir.magnitude());
+ }
+ else smoothdir.z = dir.z;
+ d->o.add(smoothdir);
+ d->o.z += maxstep + 0.1f;
+ if(!collide(d, smoothdir))
+ {
+ d->o.z -= maxstep + 0.1f;
+ if(d->physstate == PHYS_FALL || d->floor != floor)
+ {
+ d->timeinair = 0;
+ d->floor = floor;
+ switchfloor(d, dir, d->floor);
+ }
+ d->physstate = PHYS_STEP_UP;
+ return true;
+ }
+ }
+ }
+
+ /* try stepping up */
+ d->o = old;
+ d->o.z += dir.magnitude();
+ if(!collide(d, vec(0, 0, 1)))
+ {
+ if(d->physstate == PHYS_FALL || d->floor != floor)
+ {
+ d->timeinair = 0;
+ d->floor = floor;
+ switchfloor(d, dir, d->floor);
+ }
+ if(cansmooth) d->physstate = PHYS_STEP_UP;
+ return true;
+ }
+ d->o = old;
+ return false;
}
bool trystepdown(physent *d, vec &dir, float step, float xy, float z, bool init = false)
{
- vec stepdir(dir.x, dir.y, 0);
- stepdir.z = -stepdir.magnitude2()*z/xy;
- if(!stepdir.z) return false;
- stepdir.normalize();
-
- vec old(d->o);
- d->o.add(vec(stepdir).mul(STAIRHEIGHT/fabs(stepdir.z))).z -= STAIRHEIGHT;
- d->zmargin = -STAIRHEIGHT;
- if(collide(d, vec(0, 0, -1), SLOPEZ))
- {
- d->o = old;
- d->o.add(vec(stepdir).mul(step));
- d->zmargin = 0;
- if(!collide(d, vec(0, 0, -1)))
- {
- vec stepfloor(stepdir);
- stepfloor.mul(-stepfloor.z).z += 1;
- stepfloor.normalize();
- if(d->physstate >= PHYS_SLOPE && d->floor != stepfloor)
- {
- // prevent alternating step-down/step-up states if player would keep bumping into the same floor
- vec stepped(d->o);
- d->o.z -= 0.5f;
- d->zmargin = -0.5f;
- if(collide(d, stepdir) && collidewall == d->floor)
- {
- d->o = old;
- if(!init) { d->o.x += dir.x; d->o.y += dir.y; if(dir.z <= 0 || collide(d, dir)) d->o.z += dir.z; }
- d->zmargin = 0;
- d->physstate = PHYS_STEP_DOWN;
- d->timeinair = 0;
- return true;
- }
- d->o = init ? old : stepped;
- d->zmargin = 0;
- }
- else if(init) d->o = old;
- switchfloor(d, dir, stepfloor);
- d->floor = stepfloor;
- d->physstate = PHYS_STEP_DOWN;
- d->timeinair = 0;
- return true;
- }
- }
- d->o = old;
- d->zmargin = 0;
- return false;
+ vec stepdir(dir.x, dir.y, 0);
+ stepdir.z = -stepdir.magnitude2()*z/xy;
+ if(!stepdir.z) return false;
+ stepdir.normalize();
+
+ vec old(d->o);
+ d->o.add(vec(stepdir).mul(STAIRHEIGHT/fabs(stepdir.z))).z -= STAIRHEIGHT;
+ d->zmargin = -STAIRHEIGHT;
+ if(collide(d, vec(0, 0, -1), SLOPEZ))
+ {
+ d->o = old;
+ d->o.add(vec(stepdir).mul(step));
+ d->zmargin = 0;
+ if(!collide(d, vec(0, 0, -1)))
+ {
+ vec stepfloor(stepdir);
+ stepfloor.mul(-stepfloor.z).z += 1;
+ stepfloor.normalize();
+ if(d->physstate >= PHYS_SLOPE && d->floor != stepfloor)
+ {
+ // prevent alternating step-down/step-up states if player would keep bumping into the same floor
+ vec stepped(d->o);
+ d->o.z -= 0.5f;
+ d->zmargin = -0.5f;
+ if(collide(d, stepdir) && collidewall == d->floor)
+ {
+ d->o = old;
+ if(!init) { d->o.x += dir.x; d->o.y += dir.y; if(dir.z <= 0 || collide(d, dir)) d->o.z += dir.z; }
+ d->zmargin = 0;
+ d->physstate = PHYS_STEP_DOWN;
+ d->timeinair = 0;
+ return true;
+ }
+ d->o = init ? old : stepped;
+ d->zmargin = 0;
+ }
+ else if(init) d->o = old;
+ switchfloor(d, dir, stepfloor);
+ d->floor = stepfloor;
+ d->physstate = PHYS_STEP_DOWN;
+ d->timeinair = 0;
+ return true;
+ }
+ }
+ d->o = old;
+ d->zmargin = 0;
+ return false;
}
bool trystepdown(physent *d, vec &dir, bool init = false)
{
- if((!d->move && !d->strafe) || !game::allowmove(d)) return false;
- vec old(d->o);
- d->o.z -= STAIRHEIGHT;
- d->zmargin = -STAIRHEIGHT;
- if(!collide(d, vec(0, 0, -1), SLOPEZ))
- {
- d->o = old;
- d->zmargin = 0;
- return false;
- }
- d->o = old;
- d->zmargin = 0;
- float step = dir.magnitude();
+ if((!d->move && !d->strafe) || !game::allowmove(d)) return false;
+ vec old(d->o);
+ d->o.z -= STAIRHEIGHT;
+ d->zmargin = -STAIRHEIGHT;
+ if(!collide(d, vec(0, 0, -1), SLOPEZ))
+ {
+ d->o = old;
+ d->zmargin = 0;
+ return false;
+ }
+ d->o = old;
+ d->zmargin = 0;
+ float step = dir.magnitude();
#if 1
- // weaker check, just enough to avoid hopping up slopes
- if(trystepdown(d, dir, step, 4, 1, init)) return true;
+ // weaker check, just enough to avoid hopping up slopes
+ if(trystepdown(d, dir, step, 4, 1, init)) return true;
#else
- if(trystepdown(d, dir, step, 2, 1, init)) return true;
- if(trystepdown(d, dir, step, 1, 1, init)) return true;
- if(trystepdown(d, dir, step, 1, 2, init)) return true;
+ if(trystepdown(d, dir, step, 2, 1, init)) return true;
+ if(trystepdown(d, dir, step, 1, 1, init)) return true;
+ if(trystepdown(d, dir, step, 1, 2, init)) return true;
#endif
- return false;
+ return false;
}
void falling(physent *d, vec &dir, const vec &floor)
{
- if(floor.z > 0.0f && floor.z < SLOPEZ)
- {
- if(floor.z >= WALLZ) switchfloor(d, dir, floor);
- d->timeinair = 0;
- d->physstate = PHYS_SLIDE;
- d->floor = floor;
- }
- else if(d->physstate < PHYS_SLOPE || dir.dot(d->floor) > 0.01f*dir.magnitude() || (floor.z != 0.0f && floor.z != 1.0f) || !trystepdown(d, dir, true))
- d->physstate = PHYS_FALL;
+ if(floor.z > 0.0f && floor.z < SLOPEZ)
+ {
+ if(floor.z >= WALLZ) switchfloor(d, dir, floor);
+ d->timeinair = 0;
+ d->physstate = PHYS_SLIDE;
+ d->floor = floor;
+ }
+ else if(d->physstate < PHYS_SLOPE || dir.dot(d->floor) > 0.01f*dir.magnitude() || (floor.z != 0.0f && floor.z != 1.0f) || !trystepdown(d, dir, true))
+ d->physstate = PHYS_FALL;
}
void landing(physent *d, vec &dir, const vec &floor, bool collided)
{
#if 0
- if(d->physstate == PHYS_FALL)
- {
- d->timeinair = 0;
- if(dir.z < 0.0f) dir.z = d->vel.z = 0.0f;
- }
+ if(d->physstate == PHYS_FALL)
+ {
+ d->timeinair = 0;
+ if(dir.z < 0.0f) dir.z = d->vel.z = 0.0f;
+ }
#endif
- switchfloor(d, dir, floor);
- d->timeinair = 0;
- if((d->physstate!=PHYS_STEP_UP && d->physstate!=PHYS_STEP_DOWN) || !collided)
- d->physstate = floor.z >= FLOORZ ? PHYS_FLOOR : PHYS_SLOPE;
- d->floor = floor;
+ switchfloor(d, dir, floor);
+ d->timeinair = 0;
+ if((d->physstate!=PHYS_STEP_UP && d->physstate!=PHYS_STEP_DOWN) || !collided)
+ d->physstate = floor.z >= FLOORZ ? PHYS_FLOOR : PHYS_SLOPE;
+ d->floor = floor;
}
bool findfloor(physent *d, bool collided, const vec &obstacle, bool &slide, vec &floor)
{
- bool found = false;
- vec moved(d->o);
- d->o.z -= 0.1f;
- if(collide(d, vec(0, 0, -1), d->physstate == PHYS_SLOPE || d->physstate == PHYS_STEP_DOWN ? SLOPEZ : FLOORZ))
- {
- floor = collidewall;
- found = true;
- }
- else if(collided && obstacle.z >= SLOPEZ)
- {
- floor = obstacle;
- found = true;
- slide = false;
- }
- else if(d->physstate == PHYS_STEP_UP || d->physstate == PHYS_SLIDE)
- {
- if(collide(d, vec(0, 0, -1)) && collidewall.z > 0.0f)
- {
- floor = collidewall;
- if(floor.z >= SLOPEZ) found = true;
- }
- }
- else if(d->physstate >= PHYS_SLOPE && d->floor.z < 1.0f)
- {
- if(collide(d, vec(d->floor).neg(), 0.95f) || collide(d, vec(0, 0, -1)))
- {
- floor = collidewall;
- if(floor.z >= SLOPEZ && floor.z < 1.0f) found = true;
- }
- }
- if(collided && (!found || obstacle.z > floor.z))
- {
- floor = obstacle;
- slide = !found && (floor.z < WALLZ || floor.z >= SLOPEZ);
- }
- d->o = moved;
- return found;
+ bool found = false;
+ vec moved(d->o);
+ d->o.z -= 0.1f;
+ if(collide(d, vec(0, 0, -1), d->physstate == PHYS_SLOPE || d->physstate == PHYS_STEP_DOWN ? SLOPEZ : FLOORZ))
+ {
+ floor = collidewall;
+ found = true;
+ }
+ else if(collided && obstacle.z >= SLOPEZ)
+ {
+ floor = obstacle;
+ found = true;
+ slide = false;
+ }
+ else if(d->physstate == PHYS_STEP_UP || d->physstate == PHYS_SLIDE)
+ {
+ if(collide(d, vec(0, 0, -1)) && collidewall.z > 0.0f)
+ {
+ floor = collidewall;
+ if(floor.z >= SLOPEZ) found = true;
+ }
+ }
+ else if(d->physstate >= PHYS_SLOPE && d->floor.z < 1.0f)
+ {
+ if(collide(d, vec(d->floor).neg(), 0.95f) || collide(d, vec(0, 0, -1)))
+ {
+ floor = collidewall;
+ if(floor.z >= SLOPEZ && floor.z < 1.0f) found = true;
+ }
+ }
+ if(collided && (!found || obstacle.z > floor.z))
+ {
+ floor = obstacle;
+ slide = !found && (floor.z < WALLZ || floor.z >= SLOPEZ);
+ }
+ d->o = moved;
+ return found;
}
bool move(physent *d, vec &dir)
{
- vec old(d->o);
- bool collided = false, slidecollide = false;
- vec obstacle = vec(0, 0, 0);
- d->o.add(dir);
- if(collide(d, dir) || ((d->type==ENT_AI || d->type==ENT_INANIMATE) && collide(d, vec(0, 0, 0), 0, false)))
- {
- obstacle = collidewall;
- /* check to see if there is an obstacle that would prevent this one from being used as a floor (or ceiling bump) */
- if(d->type==ENT_PLAYER && ((collidewall.z>=SLOPEZ && dir.z<0) || (collidewall.z<=-SLOPEZ && dir.z>0)) && (dir.x || dir.y) && collide(d, vec(dir.x, dir.y, 0)))
- {
- if(collidewall.dot(dir) >= 0) slidecollide = true;
- obstacle = collidewall;
- }
- d->o = old;
- d->o.z -= STAIRHEIGHT;
- d->zmargin = -STAIRHEIGHT;
- if(d->physstate == PHYS_SLOPE || d->physstate == PHYS_FLOOR || (collide(d, vec(0, 0, -1), SLOPEZ) && (d->physstate==PHYS_STEP_UP || d->physstate==PHYS_STEP_DOWN || collidewall.z>=FLOORZ)))
- {
- d->o = old;
- d->zmargin = 0;
- if(trystepup(d, dir, obstacle, STAIRHEIGHT, d->physstate == PHYS_SLOPE || d->physstate == PHYS_FLOOR ? d->floor : vec(collidewall))) return true;
- }
- else
- {
- d->o = old;
- d->zmargin = 0;
- }
- /* can't step over the obstacle, so just slide against it */
- collided = true;
- }
- else if(d->physstate == PHYS_STEP_UP)
- {
- if(collide(d, vec(0, 0, -1), SLOPEZ))
- {
- d->o = old;
- if(trystepup(d, dir, vec(0, 0, 1), STAIRHEIGHT, vec(collidewall))) return true;
- d->o.add(dir);
- }
- }
- else if(d->physstate == PHYS_STEP_DOWN && dir.dot(d->floor) <= 1e-6f)
- {
- vec moved(d->o);
- d->o = old;
- if(trystepdown(d, dir)) return true;
- d->o = moved;
- }
- vec floor(0, 0, 0);
- bool slide = collided,
- found = findfloor(d, collided, obstacle, slide, floor);
- if(slide || (!collided && floor.z > 0 && floor.z < WALLZ))
- {
- slideagainst(d, dir, slide ? obstacle : floor, found, slidecollide);
- //if(d->type == ENT_AI || d->type == ENT_INANIMATE)
- d->blocked = true;
- }
- if(found) landing(d, dir, floor, collided);
- else falling(d, dir, floor);
- return !collided;
+ vec old(d->o);
+ bool collided = false, slidecollide = false;
+ vec obstacle = vec(0, 0, 0);
+ d->o.add(dir);
+ if(collide(d, dir) || ((d->type==ENT_AI || d->type==ENT_INANIMATE) && collide(d, vec(0, 0, 0), 0, false)))
+ {
+ obstacle = collidewall;
+ /* check to see if there is an obstacle that would prevent this one from being used as a floor (or ceiling bump) */
+ if(d->type==ENT_PLAYER && ((collidewall.z>=SLOPEZ && dir.z<0) || (collidewall.z<=-SLOPEZ && dir.z>0)) && (dir.x || dir.y) && collide(d, vec(dir.x, dir.y, 0)))
+ {
+ if(collidewall.dot(dir) >= 0) slidecollide = true;
+ obstacle = collidewall;
+ }
+ d->o = old;
+ d->o.z -= STAIRHEIGHT;
+ d->zmargin = -STAIRHEIGHT;
+ if(d->physstate == PHYS_SLOPE || d->physstate == PHYS_FLOOR || (collide(d, vec(0, 0, -1), SLOPEZ) && (d->physstate==PHYS_STEP_UP || d->physstate==PHYS_STEP_DOWN || collidewall.z>=FLOORZ)))
+ {
+ d->o = old;
+ d->zmargin = 0;
+ if(trystepup(d, dir, obstacle, STAIRHEIGHT, d->physstate == PHYS_SLOPE || d->physstate == PHYS_FLOOR ? d->floor : vec(collidewall))) return true;
+ }
+ else
+ {
+ d->o = old;
+ d->zmargin = 0;
+ }
+ /* can't step over the obstacle, so just slide against it */
+ collided = true;
+ }
+ else if(d->physstate == PHYS_STEP_UP)
+ {
+ if(collide(d, vec(0, 0, -1), SLOPEZ))
+ {
+ d->o = old;
+ if(trystepup(d, dir, vec(0, 0, 1), STAIRHEIGHT, vec(collidewall))) return true;
+ d->o.add(dir);
+ }
+ }
+ else if(d->physstate == PHYS_STEP_DOWN && dir.dot(d->floor) <= 1e-6f)
+ {
+ vec moved(d->o);
+ d->o = old;
+ if(trystepdown(d, dir)) return true;
+ d->o = moved;
+ }
+ vec floor(0, 0, 0);
+ bool slide = collided,
+ found = findfloor(d, collided, obstacle, slide, floor);
+ if(slide || (!collided && floor.z > 0 && floor.z < WALLZ))
+ {
+ slideagainst(d, dir, slide ? obstacle : floor, found, slidecollide);
+ //if(d->type == ENT_AI || d->type == ENT_INANIMATE)
+ d->blocked = true;
+ }
+ if(found) landing(d, dir, floor, collided);
+ else falling(d, dir, floor);
+ return !collided;
}
bool bounce(physent *d, float secs, float elasticity, float waterfric, float grav)
{
- // make sure bouncers don't start inside geometry
- if(d->physstate!=PHYS_BOUNCE && collide(d, vec(0, 0, 0), 0, false)) return true;
- int mat = lookupmaterial(vec(d->o.x, d->o.y, d->o.z + (d->aboveeye - d->eyeheight)/2));
- bool water = isliquid(mat);
- if(water)
- {
- d->vel.z -= grav*GRAVITY/16*secs;
- d->vel.mul(max(1.0f - secs/waterfric, 0.0f));
- }
- else d->vel.z -= grav*GRAVITY*secs;
- vec old(d->o);
- loopi(2)
- {
- vec dir(d->vel);
- dir.mul(secs);
- d->o.add(dir);
- if(!collide(d, dir, 0, true, true))
- {
- if(collideinside)
- {
- d->o = old;
- d->vel.mul(-elasticity);
- }
- break;
- }
- else if(collideplayer) break;
- d->o = old;
- game::bounced(d, collidewall);
- float c = collidewall.dot(d->vel),
- k = 1.0f + (1.0f-elasticity)*c/d->vel.magnitude();
- d->vel.mul(k);
- d->vel.sub(vec(collidewall).mul(elasticity*2.0f*c));
- }
- if(d->physstate!=PHYS_BOUNCE)
- {
- // make sure bouncers don't start inside geometry
- if(d->o == old) return !collideplayer;
- d->physstate = PHYS_BOUNCE;
- }
- return collideplayer!=NULL;
+ // make sure bouncers don't start inside geometry
+ if(d->physstate!=PHYS_BOUNCE && collide(d, vec(0, 0, 0), 0, false)) return true;
+ int mat = lookupmaterial(vec(d->o.x, d->o.y, d->o.z + (d->aboveeye - d->eyeheight)/2));
+ bool water = isliquid(mat);
+ if(water)
+ {
+ d->vel.z -= grav*GRAVITY/16*secs;
+ d->vel.mul(max(1.0f - secs/waterfric, 0.0f));
+ }
+ else d->vel.z -= grav*GRAVITY*secs;
+ vec old(d->o);
+ loopi(2)
+ {
+ vec dir(d->vel);
+ dir.mul(secs);
+ d->o.add(dir);
+ if(!collide(d, dir, 0, true, true))
+ {
+ if(collideinside)
+ {
+ d->o = old;
+ d->vel.mul(-elasticity);
+ }
+ break;
+ }
+ else if(collideplayer) break;
+ d->o = old;
+ game::bounced(d, collidewall);
+ float c = collidewall.dot(d->vel),
+ k = 1.0f + (1.0f-elasticity)*c/d->vel.magnitude();
+ d->vel.mul(k);
+ d->vel.sub(vec(collidewall).mul(elasticity*2.0f*c));
+ }
+ if(d->physstate!=PHYS_BOUNCE)
+ {
+ // make sure bouncers don't start inside geometry
+ if(d->o == old) return !collideplayer;
+ d->physstate = PHYS_BOUNCE;
+ }
+ return collideplayer!=NULL;
}
void avoidcollision(physent *d, const vec &dir, physent *obstacle, float space)
{
- float rad = obstacle->radius+d->radius;
- vec bbmin(obstacle->o);
- bbmin.x -= rad;
- bbmin.y -= rad;
- bbmin.z -= obstacle->eyeheight+d->aboveeye;
- bbmin.sub(space);
- vec bbmax(obstacle->o);
- bbmax.x += rad;
- bbmax.y += rad;
- bbmax.z += obstacle->aboveeye+d->eyeheight;
- bbmax.add(space);
-
- loopi(3) if(d->o[i] <= bbmin[i] || d->o[i] >= bbmax[i]) return;
-
- float mindist = 1e16f;
- loopi(3) if(dir[i] != 0)
- {
- float dist = ((dir[i] > 0 ? bbmax[i] : bbmin[i]) - d->o[i]) / dir[i];
- mindist = min(mindist, dist);
- }
- if(mindist >= 0.0f && mindist < 1e15f) d->o.add(vec(dir).mul(mindist));
+ float rad = obstacle->radius+d->radius;
+ vec bbmin(obstacle->o);
+ bbmin.x -= rad;
+ bbmin.y -= rad;
+ bbmin.z -= obstacle->eyeheight+d->aboveeye;
+ bbmin.sub(space);
+ vec bbmax(obstacle->o);
+ bbmax.x += rad;
+ bbmax.y += rad;
+ bbmax.z += obstacle->aboveeye+d->eyeheight;
+ bbmax.add(space);
+
+ loopi(3) if(d->o[i] <= bbmin[i] || d->o[i] >= bbmax[i]) return;
+
+ float mindist = 1e16f;
+ loopi(3) if(dir[i] != 0)
+ {
+ float dist = ((dir[i] > 0 ? bbmax[i] : bbmin[i]) - d->o[i]) / dir[i];
+ mindist = min(mindist, dist);
+ }
+ if(mindist >= 0.0f && mindist < 1e15f) d->o.add(vec(dir).mul(mindist));
}
bool movecamera(physent *pl, const vec &dir, float dist, float stepdist)
{
- int steps = (int)ceil(dist/stepdist);
- if(steps <= 0) return true;
-
- vec d(dir);
- d.mul(dist/steps);
- loopi(steps)
- {
- vec oldpos(pl->o);
- pl->o.add(d);
- if(collide(pl, vec(0, 0, 0), 0, false))
- {
- pl->o = oldpos;
- return false;
- }
- }
- return true;
-}
-
-bool droptofloor(vec &o, float radius, float height)
-{
- static struct dropent : physent
- {
- dropent()
- {
- type = ENT_BOUNCE;
- vel = vec(0, 0, -1);
- }
- } d;
- d.o = o;
- if(!insideworld(d.o))
- {
- if(d.o.z < worldsize) return false;
- d.o.z = worldsize - 1e-3f;
- if(!insideworld(d.o)) return false;
- }
- vec v(0.0001f, 0.0001f, -1);
- v.normalize();
- if(raycube(d.o, v, worldsize) >= worldsize) return false;
- d.radius = d.xradius = d.yradius = radius;
- d.eyeheight = height;
- d.aboveeye = radius;
- if(!movecamera(&d, d.vel, worldsize, 1))
- {
- o = d.o;
- return true;
- }
- return false;
-}
+ int steps = (int)ceil(dist/stepdist);
+ if(steps <= 0) return true;
-float dropheight(entity &e)
-{
- switch(e.type)
- {
- case ET_PARTICLES:
- case ET_MAPMODEL:
- return 0.0f;
- default:
- return 4.0f;
- }
+ vec d(dir);
+ d.mul(dist/steps);
+ loopi(steps)
+ {
+ vec oldpos(pl->o);
+ pl->o.add(d);
+ if(collide(pl, vec(0, 0, 0), 0, false))
+ {
+ pl->o = oldpos;
+ return false;
+ }
+ }
+ return true;
}
void dropenttofloor(entity *e)
{
- droptofloor(e->o, 1.0f, 4.0f);
+ float radius = 1.0f;
+ float height = 4.0f;
+ vec o = e->o;
+ static struct dropent : physent
+ {
+ dropent()
+ {
+ type = ENT_BOUNCE;
+ vel = vec(0, 0, -1);
+ }
+ } d;
+ d.o = o;
+ if(!insideworld(d.o))
+ {
+ if(d.o.z < worldsize) return;
+ d.o.z = worldsize - 1e-3f;
+ if(!insideworld(d.o)) return;
+ }
+ vec v(0.0001f, 0.0001f, -1);
+ v.normalize();
+ if(raycube(d.o, v, worldsize) >= worldsize) return;
+ d.radius = d.xradius = d.yradius = radius;
+ d.eyeheight = height;
+ d.aboveeye = radius;
+ if(!movecamera(&d, d.vel, worldsize, 1))
+ o = d.o;
}
-void phystest()
+void vecfromyawpitch(float yaw, float pitch, int move, int strafe, vec &m)
{
- static const char * const states[] = {"float", "fall", "slide", "slope", "floor", "step up", "step down", "bounce"};
- printf ("PHYS(pl): %s, air %d, floor: (%f, %f, %f), vel: (%f, %f, %f), g: (%f, %f, %f)\n", states[player->physstate], player->timeinair, player->floor.x, player->floor.y, player->floor.z, player->vel.x, player->vel.y, player->vel.z, player->falling.x, player->falling.y, player->falling.z);
- printf ("PHYS(cam): %s, air %d, floor: (%f, %f, %f), vel: (%f, %f, %f), g: (%f, %f, %f)\n", states[camera1->physstate], camera1->timeinair, camera1->floor.x, camera1->floor.y, camera1->floor.z, camera1->vel.x, camera1->vel.y, camera1->vel.z, camera1->falling.x, camera1->falling.y, camera1->falling.z);
-}
+ if(move)
+ {
+ m.x = move*-sinf(RAD*yaw);
+ m.y = move*cosf(RAD*yaw);
+ }
+ else m.x = m.y = 0;
-COMMAND(phystest, "");
+ if(pitch)
+ {
+ m.x *= cosf(RAD*pitch);
+ m.y *= cosf(RAD*pitch);
+ m.z = move*sinf(RAD*pitch);
+ }
+ else m.z = 0;
-void vecfromyawpitch(float yaw, float pitch, int move, int strafe, vec &m)
-{
- if(move)
- {
- m.x = move*-sinf(RAD*yaw);
- m.y = move*cosf(RAD*yaw);
- }
- else m.x = m.y = 0;
-
- if(pitch)
- {
- m.x *= cosf(RAD*pitch);
- m.y *= cosf(RAD*pitch);
- m.z = move*sinf(RAD*pitch);
- }
- else m.z = 0;
-
- if(strafe)
- {
- m.x += strafe*cosf(RAD*yaw);
- m.y += strafe*sinf(RAD*yaw);
- }
+ if(strafe)
+ {
+ m.x += strafe*cosf(RAD*yaw);
+ m.y += strafe*sinf(RAD*yaw);
+ }
}
void vectoyawpitch(const vec &v, float &yaw, float &pitch)
{
- if(v.iszero()) yaw = pitch = 0;
- else
- {
- yaw = -atan2(v.x, v.y)/RAD;
- pitch = asin(v.z/v.magnitude())/RAD;
- }
+ if(v.iszero()) yaw = pitch = 0;
+ else
+ {
+ yaw = -atan2(v.x, v.y)/RAD;
+ pitch = asin(v.z/v.magnitude())/RAD;
+ }
}
#define PHYSFRAMETIME 5
@@ -1589,93 +1562,93 @@ VAR(floatspeed, 1, 100, 10000);
void modifyvelocity(physent *pl, bool local, bool water, bool floating, int curtime)
{
- bool allowmove = game::allowmove(pl);
- if(floating)
- {
- if(pl->jumping && allowmove)
- {
- pl->jumping = false;
- pl->vel.z = max(pl->vel.z, JUMPVEL);
- }
- }
- else if(pl->physstate >= PHYS_SLOPE || water)
- {
- if(water && !pl->inwater) pl->vel.div(8);
- if(pl->jumping && allowmove)
- {
- pl->jumping = false;
-
- pl->vel.z = max(pl->vel.z, JUMPVEL); // physics impulse upwards
- if(water) { pl->vel.x /= 8.0f; pl->vel.y /= 8.0f; } // dampen velocity change even harder, gives correct water feel
-
- game::physicstrigger(pl, local, 1, 0);
- }
- }
- if(!floating && pl->physstate == PHYS_FALL) pl->timeinair = min(pl->timeinair + curtime, 1000);
-
- vec m(0.0f, 0.0f, 0.0f);
- if((pl->move || pl->strafe) && allowmove)
- {
- vecfromyawpitch(pl->yaw, floating || water || pl->type==ENT_CAMERA ? pl->pitch : 0, pl->move, pl->strafe, m);
-
- if(!floating && pl->physstate >= PHYS_SLOPE)
- {
- /* move up or down slopes in air
- * but only move up slopes in water
- */
- float dz = -(m.x*pl->floor.x + m.y*pl->floor.y)/pl->floor.z;
- m.z = water ? max(m.z, dz) : dz;
- }
-
- m.normalize();
- }
-
- vec d(m);
- speedmodifier*=(pl->physstate!=PHYS_FLOOR)*(speedmodifier>0);
- speedmodifier=(speedmodifier>100.0f)?100.0f:speedmodifier;
- d.mul(pl->maxspeed + speedmodifier);
-
- if(pl->type==ENT_PLAYER)
- {
- if(floating)
- {
- if(pl==player) d.mul(floatspeed/100.0f);
- }
- else if(!water && allowmove) d.mul((pl->move && !pl->strafe ? 1.3f : 1.0f) * (pl->physstate < PHYS_SLOPE ? 1.3f : 1.0f));
- }
- float fric = water && !floating ? 20.0f : (pl->physstate >= PHYS_SLOPE || floating ? 6.0f : 30.0f);
- pl->vel.lerp(d, pl->vel, pow(1 - 1/fric, curtime/20.0f));
+ bool allowmove = game::allowmove(pl);
+ if(floating)
+ {
+ if(pl->jumping && allowmove)
+ {
+ pl->jumping = false;
+ pl->vel.z = max(pl->vel.z, JUMPVEL);
+ }
+ }
+ else if(pl->physstate >= PHYS_SLOPE || water)
+ {
+ if(water && !pl->inwater) pl->vel.div(8);
+ if(pl->jumping && allowmove)
+ {
+ pl->jumping = false;
+
+ pl->vel.z = max(pl->vel.z, JUMPVEL); // physics impulse upwards
+ if(water) { pl->vel.x /= 8.0f; pl->vel.y /= 8.0f; } // dampen velocity change even harder, gives correct water feel
+
+ game::physicstrigger(pl, local, 1, 0);
+ }
+ }
+ if(!floating && pl->physstate == PHYS_FALL) pl->timeinair = min(pl->timeinair + curtime, 1000);
+
+ vec m(0.0f, 0.0f, 0.0f);
+ if((pl->move || pl->strafe) && allowmove)
+ {
+ vecfromyawpitch(pl->yaw, floating || water || pl->type==ENT_CAMERA ? pl->pitch : 0, pl->move, pl->strafe, m);
+
+ if(!floating && pl->physstate >= PHYS_SLOPE)
+ {
+ /* move up or down slopes in air
+ * but only move up slopes in water
+ */
+ float dz = -(m.x*pl->floor.x + m.y*pl->floor.y)/pl->floor.z;
+ m.z = water ? max(m.z, dz) : dz;
+ }
+
+ m.normalize();
+ }
+
+ vec d(m);
+ speedmodifier*=(pl->physstate!=PHYS_FLOOR)*(speedmodifier>0);
+ speedmodifier=(speedmodifier>100.0f)?100.0f:speedmodifier;
+ d.mul(pl->maxspeed + speedmodifier);
+
+ if(pl->type==ENT_PLAYER)
+ {
+ if(floating)
+ {
+ if(pl==player) d.mul(floatspeed/100.0f);
+ }
+ else if(!water && allowmove) d.mul((pl->move && !pl->strafe ? 1.3f : 1.0f) * (pl->physstate < PHYS_SLOPE ? 1.3f : 1.0f));
+ }
+ float fric = water && !floating ? 20.0f : (pl->physstate >= PHYS_SLOPE || floating ? 6.0f : 30.0f);
+ pl->vel.lerp(d, pl->vel, pow(1 - 1/fric, curtime/20.0f));
// old fps friction
-// float friction = water && !floating ? 20.0f : (pl->physstate >= PHYS_SLOPE || floating ? 6.0f : 30.0f);
-// float fpsfric = min(curtime/(20.0f*friction), 1.0f);
-// pl->vel.lerp(pl->vel, d, fpsfric);
+// float friction = water && !floating ? 20.0f : (pl->physstate >= PHYS_SLOPE || floating ? 6.0f : 30.0f);
+// float fpsfric = min(curtime/(20.0f*friction), 1.0f);
+// pl->vel.lerp(pl->vel, d, fpsfric);
}
void modifygravity(physent *pl, bool water, int curtime)
{
- float secs = curtime/1000.0f;
- vec g(0, 0, 0);
- if(pl->physstate == PHYS_FALL) g.z -= GRAVITY*secs;
- else if(pl->floor.z > 0 && pl->floor.z < FLOORZ)
- {
- g.z = -1;
- g.project(pl->floor);
- g.normalize();
- g.mul(GRAVITY*secs);
- }
- if(!water || !game::allowmove(pl) || (!pl->move && !pl->strafe)) pl->falling.add(g);
-
- if(water || pl->physstate >= PHYS_SLOPE)
- {
- float fric = water ? 2.0f : 6.0f,
- c = water ? 1.0f : clamp((pl->floor.z - SLOPEZ)/(FLOORZ-SLOPEZ), 0.0f, 1.0f);
- pl->falling.mul(pow(1 - c/fric, curtime/20.0f));
+ float secs = curtime/1000.0f;
+ vec g(0, 0, 0);
+ if(pl->physstate == PHYS_FALL) g.z -= GRAVITY*secs;
+ else if(pl->floor.z > 0 && pl->floor.z < FLOORZ)
+ {
+ g.z = -1;
+ g.project(pl->floor);
+ g.normalize();
+ g.mul(GRAVITY*secs);
+ }
+ if(!water || !game::allowmove(pl) || (!pl->move && !pl->strafe)) pl->falling.add(g);
+
+ if(water || pl->physstate >= PHYS_SLOPE)
+ {
+ float fric = water ? 2.0f : 6.0f,
+ c = water ? 1.0f : clamp((pl->floor.z - SLOPEZ)/(FLOORZ-SLOPEZ), 0.0f, 1.0f);
+ pl->falling.mul(pow(1 - c/fric, curtime/20.0f));
// old fps friction
-// float friction = water ? 2.0f : 6.0f,
-// fpsfric = friction/curtime*20.0f,
-// c = water ? 1.0f : clamp((pl->floor.z - SLOPEZ)/(FLOORZ-SLOPEZ), 0.0f, 1.0f);
-// pl->falling.mul(1 - c/fpsfric);
- }
+// float friction = water ? 2.0f : 6.0f,
+// fpsfric = friction/curtime*20.0f,
+// c = water ? 1.0f : clamp((pl->floor.z - SLOPEZ)/(FLOORZ-SLOPEZ), 0.0f, 1.0f);
+// pl->falling.mul(1 - c/fpsfric);
+ }
}
// main physics routine, moves a player/monster for a curtime step
@@ -1684,377 +1657,218 @@ void modifygravity(physent *pl, bool water, int curtime)
bool moveplayer(physent *pl, int moveres, bool local, int curtime)
{
- int material = lookupmaterial(vec(pl->o.x, pl->o.y, pl->o.z + (3*pl->aboveeye - pl->eyeheight)/4));
- bool water = isliquid(material&MATF_VOLUME);
- bool floating = pl->type==ENT_PLAYER && (pl->state==CS_EDITING || pl->state==CS_SPECTATOR);
- float secs = curtime/1000.f;
-
- // apply gravity
- if(!floating) modifygravity(pl, water, curtime);
- // apply any player generated changes in velocity
- modifyvelocity(pl, local, water, floating, curtime);
-
- vec d(pl->vel);
- if(!floating && water) d.mul(0.5f);
- d.add(pl->falling);
- d.mul(secs);
-
- pl->blocked = false;
-
- if(floating) // just apply velocity
- {
- if(pl->physstate != PHYS_FLOAT)
- {
- pl->physstate = PHYS_FLOAT;
- pl->timeinair = 0;
- pl->falling = vec(0, 0, 0);
- }
- pl->o.add(d);
- }
- else // apply velocity with collision
- {
- const float f = 1.0f/moveres;
- const int timeinair = pl->timeinair;
- int collisions = 0;
-
- d.mul(f);
- loopi(moveres) if(!move(pl, d) && ++collisions<5) i--; // discrete steps collision detection & sliding
- if(timeinair > 800 && !pl->timeinair && !water) // if we land after long time must have been a high jump, make thud sound
- {
- game::physicstrigger(pl, local, -1, 0);
- }
- }
-
- if(pl->state==CS_ALIVE) updatedynentcache(pl);
-
- // automatically apply smooth roll when strafing
-
- if(pl->strafe && maxroll) pl->roll = clamp(pl->roll - pow(clamp(1.0f + pl->strafe*pl->roll/maxroll, 0.0f, 1.0f), 0.33f)*pl->strafe*curtime*straferoll, -maxroll, maxroll);
- else pl->roll *= curtime == PHYSFRAMETIME ? faderoll : pow(faderoll, curtime/float(PHYSFRAMETIME));
-
- // play sounds on water transitions
-
- if(pl->inwater && !water)
- {
- material = lookupmaterial(vec(pl->o.x, pl->o.y, pl->o.z + (pl->aboveeye - pl->eyeheight)/2));
- water = isliquid(material&MATF_VOLUME);
- }
- if(!pl->inwater && water) game::physicstrigger(pl, local, 0, -1, material&MATF_VOLUME);
- else if(pl->inwater && !water) game::physicstrigger(pl, local, 0, 1, pl->inwater);
- pl->inwater = water ? material&MATF_VOLUME : MAT_AIR;
-
- if(pl->state==CS_ALIVE && (pl->o.z < 0 || material&MAT_DEATH)) game::suicide(pl);
-
- return true;
+ int material = lookupmaterial(vec(pl->o.x, pl->o.y, pl->o.z + (3*pl->aboveeye - pl->eyeheight)/4));
+ bool water = isliquid(material&MATF_VOLUME);
+ bool floating = pl->type==ENT_PLAYER && (pl->state==CS_EDITING || pl->state==CS_SPECTATOR);
+ float secs = curtime/1000.f;
+
+ // apply gravity
+ if(!floating) modifygravity(pl, water, curtime);
+ // apply any player generated changes in velocity
+ modifyvelocity(pl, local, water, floating, curtime);
+
+ vec d(pl->vel);
+ if(!floating && water) d.mul(0.5f);
+ d.add(pl->falling);
+ d.mul(secs);
+
+ pl->blocked = false;
+
+ if(floating) // just apply velocity
+ {
+ if(pl->physstate != PHYS_FLOAT)
+ {
+ pl->physstate = PHYS_FLOAT;
+ pl->timeinair = 0;
+ pl->falling = vec(0, 0, 0);
+ }
+ pl->o.add(d);
+ }
+ else // apply velocity with collision
+ {
+ const float f = 1.0f/moveres;
+ const int timeinair = pl->timeinair;
+ int collisions = 0;
+
+ d.mul(f);
+ loopi(moveres) if(!move(pl, d) && ++collisions<5) i--; // discrete steps collision detection & sliding
+ if(timeinair > 800 && !pl->timeinair && !water) // if we land after long time must have been a high jump, make thud sound
+ {
+ game::physicstrigger(pl, local, -1, 0);
+ }
+ }
+
+ if(pl->state==CS_ALIVE) updatedynentcache(pl);
+
+ // automatically apply smooth roll when strafing
+
+ if(pl->strafe && maxroll) pl->roll = clamp(pl->roll - pow(clamp(1.0f + pl->strafe*pl->roll/maxroll, 0.0f, 1.0f), 0.33f)*pl->strafe*curtime*straferoll, -maxroll, maxroll);
+ else pl->roll *= curtime == PHYSFRAMETIME ? faderoll : pow(faderoll, curtime/float(PHYSFRAMETIME));
+
+ // play sounds on water transitions
+
+ if(pl->inwater && !water)
+ {
+ material = lookupmaterial(vec(pl->o.x, pl->o.y, pl->o.z + (pl->aboveeye - pl->eyeheight)/2));
+ water = isliquid(material&MATF_VOLUME);
+ }
+ if(!pl->inwater && water) game::physicstrigger(pl, local, 0, -1, material&MATF_VOLUME);
+ else if(pl->inwater && !water) game::physicstrigger(pl, local, 0, 1, pl->inwater);
+ pl->inwater = water ? material&MATF_VOLUME : MAT_AIR;
+
+ if(pl->state==CS_ALIVE && (pl->o.z < 0 || material&MAT_DEATH)) game::suicide(pl);
+
+ return true;
}
int physsteps = 0, physframetime = PHYSFRAMETIME, lastphysframe = 0;
-void physicsframe() // optimally schedule physics frames inside the graphics frames
+void physicsframe() // optimally schedule physics frames inside the graphics frames
{
- int diff = lastmillis - lastphysframe;
- if(diff <= 0) physsteps = 0;
- else
- {
- physframetime = clamp(game::scaletime(PHYSFRAMETIME)/100, 1, PHYSFRAMETIME);
- physsteps = (diff + physframetime - 1)/physframetime;
- lastphysframe += physsteps * physframetime;
- }
- cleardynentcache();
+ int diff = lastmillis - lastphysframe;
+ if(diff <= 0) physsteps = 0;
+ else
+ {
+ physframetime = clamp(game::scaletime(PHYSFRAMETIME)/100, 1, PHYSFRAMETIME);
+ physsteps = (diff + physframetime - 1)/physframetime;
+ lastphysframe += physsteps * physframetime;
+ }
+ cleardynentcache();
}
VAR(physinterp, 0, 1, 1);
void interppos(physent *pl)
{
- pl->o = pl->newpos;
+ pl->o = pl->newpos;
- int diff = lastphysframe - lastmillis;
- if(diff <= 0 || !physinterp) return;
+ int diff = lastphysframe - lastmillis;
+ if(diff <= 0 || !physinterp) return;
- vec deltapos(pl->deltapos);
- deltapos.mul(min(diff, physframetime)/float(physframetime));
- pl->o.add(deltapos);
+ vec deltapos(pl->deltapos);
+ deltapos.mul(min(diff, physframetime)/float(physframetime));
+ pl->o.add(deltapos);
}
void moveplayer(physent *pl, int moveres, bool local)
{
- if(physsteps <= 0)
- {
- if(local) interppos(pl);
- return;
- }
-
- if(local) pl->o = pl->newpos;
- loopi(physsteps-1) moveplayer(pl, moveres, local, physframetime);
- if(local) pl->deltapos = pl->o;
- moveplayer(pl, moveres, local, physframetime);
- if(local)
- {
- pl->newpos = pl->o;
- pl->deltapos.sub(pl->newpos);
- interppos(pl);
- }
+ if(physsteps <= 0)
+ {
+ if(local) interppos(pl);
+ return;
+ }
+
+ if(local) pl->o = pl->newpos;
+ loopi(physsteps-1) moveplayer(pl, moveres, local, physframetime);
+ if(local) pl->deltapos = pl->o;
+ moveplayer(pl, moveres, local, physframetime);
+ if(local)
+ {
+ pl->newpos = pl->o;
+ pl->deltapos.sub(pl->newpos);
+ interppos(pl);
+ }
}
bool bounce(physent *d, float elasticity, float waterfric, float grav)
{
- if(physsteps <= 0)
- {
- interppos(d);
- return false;
- }
-
- d->o = d->newpos;
- bool hitplayer = false;
- loopi(physsteps-1)
- {
- if(bounce(d, physframetime/1000.0f, elasticity, waterfric, grav)) hitplayer = true;
- }
- d->deltapos = d->o;
- if(bounce(d, physframetime/1000.0f, elasticity, waterfric, grav)) hitplayer = true;
- d->newpos = d->o;
- d->deltapos.sub(d->newpos);
- interppos(d);
- return hitplayer;
+ if(physsteps <= 0)
+ {
+ interppos(d);
+ return false;
+ }
+
+ d->o = d->newpos;
+ bool hitplayer = false;
+ loopi(physsteps-1)
+ {
+ if(bounce(d, physframetime/1000.0f, elasticity, waterfric, grav)) hitplayer = true;
+ }
+ d->deltapos = d->o;
+ if(bounce(d, physframetime/1000.0f, elasticity, waterfric, grav)) hitplayer = true;
+ d->newpos = d->o;
+ d->deltapos.sub(d->newpos);
+ interppos(d);
+ return hitplayer;
}
void updatephysstate(physent *d)
{
- if(d->physstate == PHYS_FALL) return;
- d->timeinair = 0;
- vec old(d->o);
- /* Attempt to reconstruct the floor state.
- * May be inaccurate since movement collisions are not considered.
- * If good floor is not found, just keep the old floor and hope it's correct enough.
- */
- switch(d->physstate)
- {
- case PHYS_SLOPE:
- case PHYS_FLOOR:
- case PHYS_STEP_DOWN:
- d->o.z -= 0.15f;
- if(collide(d, vec(0, 0, -1), d->physstate == PHYS_SLOPE || d->physstate == PHYS_STEP_DOWN ? SLOPEZ : FLOORZ))
- d->floor = collidewall;
- break;
-
- case PHYS_STEP_UP:
- d->o.z -= STAIRHEIGHT+0.15f;
- if(collide(d, vec(0, 0, -1), SLOPEZ))
- d->floor = collidewall;
- break;
-
- case PHYS_SLIDE:
- d->o.z -= 0.15f;
- if(collide(d, vec(0, 0, -1)) && collidewall.z < SLOPEZ)
- d->floor = collidewall;
- break;
- }
- if(d->physstate > PHYS_FALL && d->floor.z <= 0) d->floor = vec(0, 0, 1);
- d->o = old;
-}
-
-const float PLATFORMMARGIN = 0.2f;
-const float PLATFORMBORDER = 10.0f;
-
-struct platforment
-{
- physent *d;
- int stacks, chains;
-
- platforment() {}
- platforment(physent *d) : d(d), stacks(-1), chains(-1) {}
-
- bool operator==(const physent *o) const { return d == o; }
-};
-
-struct platformcollision
-{
- platforment *ent;
- int next;
-
- platformcollision() {}
- platformcollision(platforment *ent, int next) : ent(ent), next(next) {}
-};
-
-template<class E, class O>
-static inline bool platformcollide(physent *d, const vec &dir, physent *o, float margin)
-{
- E entvol(d);
- O obvol(o, margin);
- vec cp;
- if(mpr::collide(entvol, obvol, NULL, NULL, &cp))
- {
- vec wn = vec(cp).sub(obvol.center());
- return !obvol.contactface(wn, dir.iszero() ? vec(wn).neg() : dir).iszero();
- }
- return false;
-}
-
-bool platformcollide(physent *d, physent *o, const vec &dir, float margin = 0)
-{
- if(d->collidetype == COLLIDE_OBB)
- {
- if(o->collidetype == COLLIDE_OBB) return platformcollide<mpr::EntOBB, mpr::EntOBB>(d, dir, o, margin);
- else return platformcollide<mpr::EntOBB, mpr::EntCylinder>(d, dir, o, margin);
-
- }
- else if(o->collidetype == COLLIDE_OBB) return ellipseboxcollide(d, dir, o->o, vec(0, 0, 0), o->yaw, o->xradius, o->yradius, o->aboveeye, o->eyeheight + margin);
- else return ellipsecollide(d, dir, o->o, vec(0, 0, 0), o->yaw, o->xradius, o->yradius, o->aboveeye, o->eyeheight + margin);
-}
-
-bool moveplatform(physent *p, const vec &dir)
-{
- if(!insideworld(p->newpos)) return false;
-
- vec oldpos(p->o);
- (p->o = p->newpos).add(dir);
- if(collide(p, dir, 0, dir.z<=0))
- {
- p->o = oldpos;
- return false;
- }
- p->o = oldpos;
-
- static vector<platforment> ents;
- ents.setsize(0);
- for(int x = int(max(p->o.x-p->radius-PLATFORMBORDER, 0.0f))>>dynentsize, ex = int(min(p->o.x+p->radius+PLATFORMBORDER, worldsize-1.0f))>>dynentsize; x <= ex; x++)
- for(int y = int(max(p->o.y-p->radius-PLATFORMBORDER, 0.0f))>>dynentsize, ey = int(min(p->o.y+p->radius+PLATFORMBORDER, worldsize-1.0f))>>dynentsize; y <= ey; y++)
- {
- const vector<physent *> &dynents = checkdynentcache(x, y);
- loopv(dynents)
- {
- physent *d = dynents[i];
- if(p==d || d->o.z-d->eyeheight < p->o.z+p->aboveeye || p->o.reject(d->o, p->radius+PLATFORMBORDER+d->radius) || ents.find(d) >= 0) continue;
- ents.add(d);
- }
- }
- static vector<platforment *> passengers, colliders;
- passengers.setsize(0);
- colliders.setsize(0);
- static vector<platformcollision> collisions;
- collisions.setsize(0);
- // build up collision DAG of colliders to be pushed off, and DAG of stacked passengers
- loopv(ents)
- {
- platforment &ent = ents[i];
- physent *d = ent.d;
- // check if the dynent is on top of the platform
- if(platformcollide(p, d, vec(0, 0, 1), PLATFORMMARGIN)) passengers.add(&ent);
- vec doldpos(d->o);
- (d->o = d->newpos).add(dir);
- if(collide(d, dir, 0, false)) colliders.add(&ent);
- d->o = doldpos;
- loopvj(ents)
- {
- platforment &o = ents[j];
- if(platformcollide(d, o.d, dir))
- {
- collisions.add(platformcollision(&ent, o.chains));
- o.chains = collisions.length() - 1;
- }
- if(d->o.z < o.d->o.z && platformcollide(d, o.d, vec(0, 0, 1), PLATFORMMARGIN))
- {
- collisions.add(platformcollision(&o, ent.stacks));
- ent.stacks = collisions.length() - 1;
- }
- }
- }
- loopv(colliders) // propagate collisions
- {
- platforment *ent = colliders[i];
- for(int n = ent->chains; n>=0; n = collisions[n].next)
- {
- platforment *o = collisions[n].ent;
- if(colliders.find(o)<0) colliders.add(o);
- }
- }
- if(dir.z>0)
- {
- loopv(passengers) // if any stacked passengers collide, stop the platform
- {
- platforment *ent = passengers[i];
- if(colliders.find(ent)>=0) return false;
- for(int n = ent->stacks; n>=0; n = collisions[n].next)
- {
- platforment *o = collisions[n].ent;
- if(passengers.find(o)<0) passengers.add(o);
- }
- }
- loopv(passengers)
- {
- physent *d = passengers[i]->d;
- d->o.add(dir);
- d->newpos.add(dir);
- if(dir.x || dir.y) updatedynentcache(d);
- }
- }
- else loopv(passengers) // move any stacked passengers who aren't colliding with non-passengers
- {
- platforment *ent = passengers[i];
- if(colliders.find(ent)>=0) continue;
-
- physent *d = ent->d;
- d->o.add(dir);
- d->newpos.add(dir);
- if(dir.x || dir.y) updatedynentcache(d);
-
- for(int n = ent->stacks; n>=0; n = collisions[n].next)
- {
- platforment *o = collisions[n].ent;
- if(passengers.find(o)<0) passengers.add(o);
- }
- }
-
- p->o.add(dir);
- p->newpos.add(dir);
- if(dir.x || dir.y) updatedynentcache(p);
-
- return true;
+ if(d->physstate == PHYS_FALL) return;
+ d->timeinair = 0;
+ vec old(d->o);
+ /* Attempt to reconstruct the floor state.
+ * May be inaccurate since movement collisions are not considered.
+ * If good floor is not found, just keep the old floor and hope it's correct enough.
+ */
+ switch(d->physstate)
+ {
+ case PHYS_SLOPE:
+ case PHYS_FLOOR:
+ case PHYS_STEP_DOWN:
+ d->o.z -= 0.15f;
+ if(collide(d, vec(0, 0, -1), d->physstate == PHYS_SLOPE || d->physstate == PHYS_STEP_DOWN ? SLOPEZ : FLOORZ))
+ d->floor = collidewall;
+ break;
+
+ case PHYS_STEP_UP:
+ d->o.z -= STAIRHEIGHT+0.15f;
+ if(collide(d, vec(0, 0, -1), SLOPEZ))
+ d->floor = collidewall;
+ break;
+
+ case PHYS_SLIDE:
+ d->o.z -= 0.15f;
+ if(collide(d, vec(0, 0, -1)) && collidewall.z < SLOPEZ)
+ d->floor = collidewall;
+ break;
+ }
+ if(d->physstate > PHYS_FALL && d->floor.z <= 0) d->floor = vec(0, 0, 1);
+ d->o = old;
}
#define dir(name,v,d,s,os) ICOMMAND(name, "D", (int *down), { player->s = *down!=0; player->v = player->s ? d : (player->os ? -(d) : 0); });
dir(backward, move, -1, k_down, k_up);
-dir(forward, move, 1, k_up, k_down);
-dir(left, strafe, 1, k_left, k_right);
-dir(right, strafe, -1, k_right, k_left);
+dir(forward, move, 1, k_up, k_down);
+dir(left, strafe, 1, k_left, k_right);
+dir(right, strafe, -1, k_right, k_left);
ICOMMAND(jump, "D", (int *down), { if(!*down || game::canjump()) player->jumping = *down!=0; });
ICOMMAND(attack, "D", (int *down), { game::doattack(*down!=0); });
-bool entinmap(dynent *d, bool avoidplayers) // brute force but effective way to find a free spawn spot in the map
-{
- d->o.z += d->eyeheight; // pos specified is at feet
- vec orig = d->o;
- loopi(100) // try max 100 times
- {
- if(i)
- {
- d->o = orig;
- d->o.x += (rnd(21)-10)*i/5; // increasing distance
- d->o.y += (rnd(21)-10)*i/5;
- d->o.z += (rnd(21)-10)*i/5;
- }
-
- if(!collide(d) && !collideinside)
- {
- if(collideplayer)
- {
- if(!avoidplayers) continue;
- d->o = orig;
- d->resetinterp();
- return false;
- }
-
- d->resetinterp();
- return true;
- }
- }
- // leave ent at original pos, possibly stuck
- d->o = orig;
- d->resetinterp();
- conoutf(CON_WARN, "can't find entity spawn spot! (%.1f, %.1f, %.1f)", d->o.x, d->o.y, d->o.z);
- return false;
+bool entinmap(dynent *d, bool avoidplayers) // brute force but effective way to find a free spawn spot in the map
+{
+ d->o.z += d->eyeheight; // pos specified is at feet
+ vec orig = d->o;
+ loopi(100) // try max 100 times
+ {
+ if(i)
+ {
+ d->o = orig;
+ d->o.x += (rnd(21)-10)*i/5; // increasing distance
+ d->o.y += (rnd(21)-10)*i/5;
+ d->o.z += (rnd(21)-10)*i/5;
+ }
+
+ if(!collide(d) && !collideinside)
+ {
+ if(collideplayer)
+ {
+ if(!avoidplayers) continue;
+ d->o = orig;
+ d->resetinterp();
+ return false;
+ }
+
+ d->resetinterp();
+ return true;
+ }
+ }
+ // leave ent at original pos, possibly stuck
+ d->o = orig;
+ d->resetinterp();
+ conoutf(CON_WARN, "can't find entity spawn spot! (%.1f, %.1f, %.1f)", d->o.x, d->o.y, d->o.z);
+ return false;
}