176 lines
6.7 KiB
C++
176 lines
6.7 KiB
C++
#pragma once
|
|
|
|
#include "IBox.hpp"
|
|
#include "../world/position.h"
|
|
|
|
namespace geometry {
|
|
/// Raycast with distance
|
|
struct Ray {
|
|
camera_pos from;
|
|
glm::vec3 dir;
|
|
float dist;
|
|
|
|
// MAYBE: Ray(const glm::mat4& view_matrix) { }
|
|
Ray(const camera_pos& from, const glm::vec3& dir, float dist): from(from), dir(glm::normalize(dir)), dist(dist) { }
|
|
|
|
inline Ray operator*(float scale) const noexcept {
|
|
return Ray(from * scale, dir, dist * scale);
|
|
}
|
|
|
|
/// Get path points in integer grid
|
|
/// @note not precise enough
|
|
inline void grid(std::vector<glm::llvec3>& points) const {
|
|
glm::llvec3 current = from.as_voxel();
|
|
const glm::llvec3 d = dir * dist;
|
|
const glm::llvec3 inc = glm::llvec3((d.x < 0) ? -1 : 1, (d.y < 0) ? -1 : 1, (d.z < 0) ? -1 : 1);
|
|
const glm::llvec3 size = glm::abs(d);
|
|
const glm::llvec3 delta = size << 1ll;
|
|
|
|
if ((size.x >= size.y) && (size.x >= size.z)) {
|
|
int err_1 = delta.y - size.x;
|
|
int err_2 = delta.z - size.x;
|
|
points.reserve(size.x + 1);
|
|
for (int i = 0; i < size.x; i++) {
|
|
points.push_back(current);
|
|
if (err_1 > 0) {
|
|
current.y += inc.y;
|
|
err_1 -= delta.x;
|
|
}
|
|
if (err_2 > 0) {
|
|
current.z += inc.z;
|
|
err_2 -= delta.x;
|
|
}
|
|
err_1 += delta.y;
|
|
err_2 += delta.z;
|
|
current.x += inc.x;
|
|
}
|
|
} else if ((size.y >= size.x) && (size.y >= size.z)) {
|
|
int err_1 = delta.x - size.y;
|
|
int err_2 = delta.z - size.y;
|
|
points.reserve(size.y + 1);
|
|
for (int i = 0; i < size.y; i++) {
|
|
points.push_back(current);
|
|
if (err_1 > 0) {
|
|
current.x += inc.x;
|
|
err_1 -= delta.y;
|
|
}
|
|
if (err_2 > 0) {
|
|
current.z += inc.z;
|
|
err_2 -= delta.y;
|
|
}
|
|
err_1 += delta.x;
|
|
err_2 += delta.z;
|
|
current.y += inc.y;
|
|
}
|
|
} else {
|
|
int err_1 = delta.y - size.z;
|
|
int err_2 = delta.x - size.z;
|
|
points.reserve(size.z + 1);
|
|
for (int i = 0; i < size.z; i++) {
|
|
points.push_back(current);
|
|
if (err_1 > 0) {
|
|
current.y += inc.y;
|
|
err_1 -= delta.z;
|
|
}
|
|
if (err_2 > 0) {
|
|
current.x += inc.x;
|
|
err_2 -= delta.z;
|
|
}
|
|
err_1 += delta.y;
|
|
err_2 += delta.x;
|
|
current.z += inc.z;
|
|
}
|
|
}
|
|
points.push_back(current);
|
|
}
|
|
/// Iterator implementation of grid
|
|
struct iterator {
|
|
enum class axis { X, Y, Z };
|
|
|
|
iterator(const Ray& r): current(r.from.as_voxel()),
|
|
d(r.dir * r.dist), l(glm::abs(d)),
|
|
dir((l.x >= l.y) && (l.x >= l.z) ? axis::X : ((l.y >= l.x) && (l.y >= l.z) ? axis::Y : axis::Z)),
|
|
count(dir == axis::X ? l.x : (dir == axis::Y ? l.y : l.z)),
|
|
inc(swap(glm::llvec3((d.x < 0) ? -1 : 1, (d.y < 0) ? -1 : 1, (d.z < 0) ? -1 : 1), dir)),
|
|
delta(swap(l << 1ll, dir)), err_1(delta.y - count), err_2(delta.z - count) { }
|
|
|
|
static constexpr inline glm::llvec3 swap(const glm::llvec3& in, const axis dir) { return dir == axis::X ? in :
|
|
(dir == axis::Y ? glm::llvec3(in.y, in.z, in.x) : glm::llvec3(in.z, in.x, in.y)); }
|
|
|
|
static constexpr inline long long& get(glm::llvec3& in, const axis dir, const axis target) {
|
|
const axis v = (axis)(((int)dir + (int)target) % 3);
|
|
return v == axis::X ? in.x : (v == axis::Y ? in.y : in.z);
|
|
}
|
|
|
|
bool next(glm::llvec3& out) {
|
|
if (count < 0)
|
|
return false;
|
|
|
|
count--;
|
|
out = current;
|
|
if (err_1 > 0) {
|
|
get(current, dir, axis::Y) += inc.y;
|
|
err_1 -= delta.x;
|
|
}
|
|
if (err_2 > 0) {
|
|
get(current, dir, axis::Z) += inc.z;
|
|
err_2 -= delta.x;
|
|
}
|
|
err_1 += delta.y;
|
|
err_2 += delta.z;
|
|
get(current, dir, axis::X) += inc.x;
|
|
return true;
|
|
}
|
|
|
|
glm::llvec3 current;
|
|
const glm::llvec3 d;
|
|
const glm::llvec3 l;
|
|
const axis dir;
|
|
glm::llvec3::value_type count;
|
|
const glm::llvec3 inc;
|
|
const glm::llvec3 delta;
|
|
int err_1;
|
|
int err_2;
|
|
};
|
|
|
|
IBox::ContainmentType intersect(const IBox& box) const {
|
|
const glm::llvec3 start = from.as_voxel();
|
|
if(box.contains(start))
|
|
return IBox::ContainmentType::Contains;
|
|
|
|
const auto inv = 1. / glm::dvec3(dir);
|
|
glm::f64 tmin = ((inv.x < 0 ? box.Max : box.Min).x - start.x) * inv.x;
|
|
glm::f64 tmax = ((inv.x < 0 ? box.Min : box.Max).x - start.x) * inv.x;
|
|
glm::f64 tymin = ((inv.y < 0 ? box.Max : box.Min).y - start.y) * inv.y;
|
|
glm::f64 tymax = ((inv.y < 0 ? box.Min : box.Max).y - start.y) * inv.y;
|
|
|
|
if ((tmin > tymax) || (tymin > tmax)){
|
|
return IBox::ContainmentType::Disjoint;
|
|
}
|
|
if (tymin > tmin) {
|
|
tmin = tymin;
|
|
}
|
|
if (tymax < tmax){
|
|
tmax = tymax;
|
|
}
|
|
|
|
glm::f64 tzmin = ((inv.z < 0 ? box.Max : box.Min).z - start.z) * inv.z;
|
|
glm::f64 tzmax = ((inv.z < 0 ? box.Min : box.Max).z - start.z) * inv.z;
|
|
|
|
if ((tmin > tzmax) || (tzmin > tmax)){
|
|
return IBox::ContainmentType::Disjoint;
|
|
}
|
|
if (tzmin > tmin){
|
|
tmin = tzmin;
|
|
}
|
|
if (tzmax < tmax){
|
|
tmax = tzmax;
|
|
}
|
|
// this last check is different from the 'ray' case in below references:
|
|
// we need to check that the segment is on the span of the line
|
|
// that intersects the box
|
|
return tmax<0.0f || tmin> 1.0f ? IBox::ContainmentType::Intersects : IBox::ContainmentType::Disjoint;
|
|
}
|
|
};
|
|
}
|