1
0
Fork 0
Univerxel/src/core/geometry/Ray.hpp

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;
}
};
}