FossilOrigin-Name: d7661be1b1dc8fda8e4de50f9a9d75907f498e6e07530241fb04be015ca3d9ae
195 lines
4.4 KiB
Objective-C
195 lines
4.4 KiB
Objective-C
// worldocull.cpp: occlusion map and occlusion test
|
|
|
|
#include "cube.h"
|
|
|
|
#import "Command.h"
|
|
#import "DynamicEntity.h"
|
|
|
|
#define NUMRAYS 512
|
|
|
|
float rdist[NUMRAYS];
|
|
bool ocull = true;
|
|
float odist = 256;
|
|
|
|
COMMAND(toggleocull, ARG_NONE, ^{
|
|
ocull = !ocull;
|
|
})
|
|
|
|
// constructs occlusion map: cast rays in all directions on the 2d plane and
|
|
// record distance. done exactly once per frame.
|
|
|
|
void
|
|
computeraytable(float vx, float vy)
|
|
{
|
|
if (!ocull)
|
|
return;
|
|
|
|
odist = getvar(@"fog") * 1.5f;
|
|
|
|
float apitch = (float)fabs(player1.pitch);
|
|
float af = getvar(@"fov") / 2 + apitch / 1.5f + 3;
|
|
float byaw = (player1.yaw - 90 + af) / 360 * PI2;
|
|
float syaw = (player1.yaw - 90 - af) / 360 * PI2;
|
|
|
|
for (int i = 0; i < NUMRAYS; i++) {
|
|
float angle = i * PI2 / NUMRAYS;
|
|
// try to avoid tracing ray if outside of frustrum
|
|
// apitch must be bigger if fov > 120
|
|
if ((apitch > 45 || (angle < byaw && angle > syaw) ||
|
|
(angle < byaw - PI2 && angle > syaw - PI2) ||
|
|
(angle < byaw + PI2 && angle > syaw + PI2)) &&
|
|
!OUTBORD(vx, vy) && !SOLID(S((int)vx, (int)vy))) {
|
|
float ray = i * 8 / (float)NUMRAYS;
|
|
float dx, dy;
|
|
if (ray > 1 && ray < 3) {
|
|
dx = -(ray - 2);
|
|
dy = 1;
|
|
} else if (ray >= 3 && ray < 5) {
|
|
dx = -1;
|
|
dy = -(ray - 4);
|
|
} else if (ray >= 5 && ray < 7) {
|
|
dx = ray - 6;
|
|
dy = -1;
|
|
} else {
|
|
dx = 1;
|
|
dy = ray > 4 ? ray - 8 : ray;
|
|
}
|
|
float sx = vx;
|
|
float sy = vy;
|
|
for (;;) {
|
|
sx += dx;
|
|
sy += dy;
|
|
// 90% of time spend in this function is on this
|
|
// line
|
|
if (SOLID(S((int)sx, (int)sy))) {
|
|
rdist[i] = (float)(fabs(sx - vx) +
|
|
fabs(sy - vy));
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
rdist[i] = 2;
|
|
}
|
|
}
|
|
|
|
// test occlusion for a cube... one of the most computationally expensive
|
|
// functions in the engine as its done for every cube and entity, but its effect
|
|
// is more than worth it!
|
|
|
|
inline float
|
|
ca(float x, float y)
|
|
{
|
|
return x > y ? y / x : 2 - x / y;
|
|
}
|
|
inline float
|
|
ma(float x, float y)
|
|
{
|
|
return x == 0 ? (y > 0 ? 2 : -2) : y / x;
|
|
}
|
|
|
|
int
|
|
isoccluded(float vx, float vy, float cx, float cy,
|
|
float csize) // v = viewer, c = cube to test
|
|
{
|
|
if (!ocull)
|
|
return 0;
|
|
|
|
float
|
|
nx = vx,
|
|
ny = vy; // n = point on the border of the cube that is closest to v
|
|
if (nx < cx)
|
|
nx = cx;
|
|
else if (nx > cx + csize)
|
|
nx = cx + csize;
|
|
if (ny < cy)
|
|
ny = cy;
|
|
else if (ny > cy + csize)
|
|
ny = cy + csize;
|
|
float xdist = (float)fabs(nx - vx);
|
|
float ydist = (float)fabs(ny - vy);
|
|
if (xdist > odist || ydist > odist)
|
|
return 2;
|
|
float dist = xdist + ydist - 1; // 1 needed?
|
|
|
|
// ABC
|
|
// D E
|
|
// FGH
|
|
|
|
// - check middle cube? BG
|
|
|
|
// find highest and lowest angle in the occlusion map that this cube
|
|
// spans, based on its most left and right points on the border from the
|
|
// viewer pov... I see no easier way to do this than this silly code
|
|
// below
|
|
|
|
float h, l;
|
|
if (cx <= vx) // ABDFG
|
|
{
|
|
if (cx + csize < vx) // ADF
|
|
{
|
|
if (cy <= vy) // AD
|
|
{
|
|
if (cy + csize < vy) {
|
|
h = ca(-(cx - vx), -(cy + csize - vy)) +
|
|
4;
|
|
l = ca(-(cx + csize - vx), -(cy - vy)) +
|
|
4;
|
|
} // A
|
|
else {
|
|
h = ma(-(cx + csize - vx),
|
|
-(cy + csize - vy)) +
|
|
4;
|
|
l = ma(-(cx + csize - vx), -(cy - vy)) +
|
|
4;
|
|
} // D
|
|
} else {
|
|
h = ca(cy + csize - vy, -(cx + csize - vx)) + 2;
|
|
l = ca(cy - vy, -(cx - vx)) + 2;
|
|
} // F
|
|
} else { // BG
|
|
if (cy <= vy) {
|
|
if (cy + csize < vy) {
|
|
h = ma(-(cy + csize - vy), cx - vx) + 6;
|
|
l = ma(-(cy + csize - vy),
|
|
cx + csize - vx) +
|
|
6;
|
|
} // B
|
|
else
|
|
return 0;
|
|
} else {
|
|
h = ma(cy - vy, -(cx + csize - vx)) + 2;
|
|
l = ma(cy - vy, -(cx - vx)) + 2;
|
|
} // G
|
|
}
|
|
} else // CEH
|
|
{
|
|
if (cy <= vy) // CE
|
|
{
|
|
if (cy + csize < vy) {
|
|
h = ca(-(cy - vy), cx - vx) + 6;
|
|
l = ca(-(cy + csize - vy), cx + csize - vx) + 6;
|
|
} // C
|
|
else {
|
|
h = ma(cx - vx, cy - vy);
|
|
l = ma(cx - vx, cy + csize - vy);
|
|
} // E
|
|
} else {
|
|
h = ca(cx + csize - vx, cy - vy);
|
|
l = ca(cx - vx, cy + csize - vy);
|
|
} // H
|
|
}
|
|
// get indexes into occlusion map from angles
|
|
int si = h * (NUMRAYS / 8) + NUMRAYS;
|
|
int ei = l * (NUMRAYS / 8) + NUMRAYS + 1;
|
|
if (ei <= si)
|
|
ei += NUMRAYS;
|
|
|
|
for (int i = si; i <= ei; i++) {
|
|
if (dist < rdist[i & (NUMRAYS - 1)])
|
|
// if any value in this segment of the occlusion map is
|
|
// further away then cube is not occluded
|
|
return 0;
|
|
}
|
|
|
|
return 1; // cube is entirely occluded
|
|
}
|