basin/src/raytrace.c

182 lines
6.8 KiB
C

#include <basin/raytrace.h>
#include <basin/world.h>
#include <avuna/string.h>
#include <math.h>
int wrt_intermediate(double v1x, double v1y, double v1z, double v2x, double v2y, double v2z, double coord, int ct, double* rx, double* ry, double* rz) {
double dx = v2x - v1x;
double dy = v2y - v1y;
double dz = v2z - v1z;
double* mcd = NULL;
double* mc = NULL;
if (ct == 0) {
mcd = &dx;
mc = &v1x;
} else if (ct == 1) {
mcd = &dy;
mc = &v1y;
} else if (ct == 2) {
mcd = &dz;
mc = &v1z;
}
if ((*mcd) * (*mcd) < 1.0000000116860974e-7) return 1;
else {
double dc = (coord - (*mc)) / (*mcd);
if (dc >= 0. && dc <= 1.) {
*rx = v1x + dx * dc;
*ry = v1y + dy * dc;
*rz = v1z + dz * dc;
return 0;
} else return 1;
}
}
int wrt_intersectsPlane(double rc1, double rc2, double minc1, double minc2, double maxc1, double maxc2) {
return rc1 >= minc1 && rc1 <= maxc1 && rc2 >= minc2 && rc2 <= maxc2;
}
int world_isColliding(struct block_info* bi, int32_t x, int32_t y, int32_t z, double px, double py, double pz) {
for (size_t i = 0; i < bi->boundingBox_count; i++) {
struct boundingbox* bb = &bi->boundingBoxes[i];
if (bb->minX + x < px && bb->maxX + x > px && bb->minY + y < py && bb->maxY + y > py && bb->minZ + z < pz && bb->maxZ + z > pz) return 1;
}
return 0;
}
int wrt_isClosest(double tx, double ty, double tz, double x1, double y1, double z1, double x2, double y2, double z2) {
return (x1 - tx) * (x1 - tx) + (y1 - ty) * (y1 - ty) + (z1 - tz) * (z1 - tz) > (x2 - tx) * (x2 - tx) + (y2 - ty) * (y2 - ty) + (z2 - tz) * (z2 - tz);
}
int raytrace_block(struct boundingbox* bb, int32_t x, int32_t y, int32_t z, double px, double py, double pz, double ex, double ey, double ez, double* qx, double* qy, double* qz) {
double bx = ex;
double by = ey;
double bz = ez;
double rx = 0.;
double ry = 0.;
double rz = 0.;
int face = -1;
if (!wrt_intermediate(px, py, pz, ex, ey, ez, bb->minX + x, 0, &rx, &ry, &rz) && wrt_intersectsPlane(ry, rz, bb->minY + y, bb->maxY + y, bb->minZ + z, bb->maxZ + z)) {
face = XN;
bx = rx;
by = ry;
bz = rz;
}
if (!wrt_intermediate(px, py, pz, ex, ey, ez, bb->maxX + x, 0, &rx, &ry, &rz) && wrt_intersectsPlane(ry, rz, bb->minX + x, bb->maxX + x, bb->minZ + z, bb->maxZ + z) && wrt_isClosest(px, py, pz, bx, by, bz, rx, ry, rz)) {
face = XP;
bx = rx;
by = ry;
bz = rz;
}
if (!wrt_intermediate(px, py, pz, ex, ey, ez, bb->minY + y, 1, &rx, &ry, &rz) && wrt_intersectsPlane(rx, rz, bb->minX + x, bb->maxX + x, bb->minZ + z, bb->maxZ + z) && wrt_isClosest(px, py, pz, bx, by, bz, rx, ry, rz)) {
face = YN;
bx = rx;
by = ry;
bz = rz;
}
if (!wrt_intermediate(px, py, pz, ex, ey, ez, bb->maxY + y, 1, &rx, &ry, &rz) && wrt_intersectsPlane(rx, rz, bb->minX + x, bb->maxX + x, bb->minZ + z, bb->maxZ + z) && wrt_isClosest(px, py, pz, bx, by, bz, rx, ry, rz)) {
face = YP;
bx = rx;
by = ry;
bz = rz;
}
if (!wrt_intermediate(px, py, pz, ex, ey, ez, bb->minZ + z, 2, &rx, &ry, &rz) && wrt_intersectsPlane(rx, ry, bb->minX + x, bb->maxX + x, bb->minY + y, bb->maxY + y) && wrt_isClosest(px, py, pz, bx, by, bz, rx, ry, rz)) {
face = ZN;
bx = rx;
by = ry;
bz = rz;
}
if (!wrt_intermediate(px, py, pz, ex, ey, ez, bb->maxZ + z, 2, &rx, &ry, &rz) && wrt_intersectsPlane(rx, ry, bb->minX + x, bb->maxX + x, bb->minY + y, bb->maxY + y) && wrt_isClosest(px, py, pz, bx, by, bz, rx, ry, rz)) {
face = ZP;
bx = rx;
by = ry;
bz = rz;
}
*qx = bx;
*qy = by;
*qz = bz;
return face;
}
int raytrace(struct world* world, double x, double y, double z, double ex, double ey, double ez, int stopOnLiquid, int ignoreNonCollidable, int returnLast, double* rx, double* ry, double* rz) {
int32_t ix = (uint32_t) floor(x);
int32_t iy = (uint32_t) floor(y);
int32_t iz = (uint32_t) floor(z);
int32_t iex = (uint32_t) floor(ex);
int32_t iey = (uint32_t) floor(ey);
int32_t iez = (uint32_t) floor(ez);
block b = world_get_block(world, ix, iy, iz);
struct block_info* bbi = getBlockInfo(b);
if (bbi != NULL && (!ignoreNonCollidable || bbi->boundingBox_count > 0)) {
for (size_t i = 0; i < bbi->boundingBox_count; i++) {
struct boundingbox* bb = &bbi->boundingBoxes[i];
int face = raytrace_block(bb, ix, iy, iz, x, y, z, ex, ey, ez, rx, ry, rz);
if (face >= 0) return face;
}
}
int k = 200;
int cface = -1;
while (k-- >= 0) {
if (ix == iex && iy == iey && iz == iez) {
return returnLast ? cface : -1;
}
int hx = 1;
int hy = 1;
int hz = 1;
double mX = 999.;
double mY = 999.;
double mZ = 999.;
if (iex > ix) mX = (double) ix + 1.;
else if (iex < ix) mX = (double) ix;
else hx = 0;
if (iey > iy) mY = (double) iy + 1.;
else if (iey < iy) mY = (double) iy;
else hy = 0;
if (iez > iz) mZ = (double) iz + 1.;
else if (iez < iz) mZ = (double) iz;
else hz = 0;
double ax = 999.;
double ay = 999.;
double az = 999.;
double dx = ex - x;
double dy = ey - y;
double dz = ez - z;
if (hx) ax = (mX - x) / dx;
if (hy) ay = (mY - y) / dy;
if (hz) az = (mZ - z) / dz;
if (ax == 0.) ax = -1e-4;
if (ay == 0.) ay = -1e-4;
if (az == 0.) az = -1e-4;
if (ax < ay && ax < az) {
cface = iex > ix ? XN : XP;
x = mX;
y += dy * ax;
z += dz * ax;
} else if (ay < az) {
cface = iey > iy ? YN : YP;
x += dx * ay;
y = mY;
z += dz * ay;
} else {
cface = iez > iz ? ZN : ZP;
x += dx * az;
y += dy * az;
z = mZ;
}
ix = (int32_t) (floor(x) - (cface == XP ? 1 : 0));
iy = (int32_t) (floor(y) - (cface == YP ? 1 : 0));
iz = (int32_t) (floor(z) - (cface == ZP ? 1 : 0));
block nb = world_get_block(world, ix, iy, iz);
struct block_info* bi = getBlockInfo(nb);
if (bi != NULL && (!ignoreNonCollidable || str_eq(bi->material->name, "portal") || bi->boundingBox_count > 0)) {
//todo: cancollidecheck?
for (size_t i = 0; i < bi->boundingBox_count; i++) {
struct boundingbox* bb = &bi->boundingBoxes[i];
int face = raytrace_block(bb, ix, iy, iz, x, y, z, ex, ey, ez, rx, ry, rz);
if (face >= 0) return face;
}
//TODO: returnlast finish
}
}
return returnLast ? cface : -1;
}