2024-11-10 22:11:29 +01:00
|
|
|
|
|
|
|
|
type ray_t = {
|
|
|
|
|
origin?: vec4_t;
|
|
|
|
|
dir?: vec4_t;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type plane_t = {
|
|
|
|
|
normal?: vec4_t;
|
|
|
|
|
constant?: f32;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let _raycast_vp_inv: mat4_t = mat4_identity();
|
|
|
|
|
let _raycast_p_inv: mat4_t = mat4_identity();
|
|
|
|
|
let _raycast_v_inv: mat4_t = mat4_identity();
|
|
|
|
|
|
|
|
|
|
function raycast_get_ray(input_x: f32, input_y: f32, camera: camera_object_t): ray_t {
|
|
|
|
|
let start: vec4_t = vec4_create();
|
|
|
|
|
let end: vec4_t = vec4_create();
|
|
|
|
|
|
|
|
|
|
// Get 3D point form screen coords
|
|
|
|
|
// Set two vectors with opposing z values
|
2025-03-11 19:49:42 +01:00
|
|
|
start.x = (input_x / sys_w()) * 2.0 - 1.0;
|
|
|
|
|
start.y = -((input_y / sys_h()) * 2.0 - 1.0);
|
2024-11-10 22:11:29 +01:00
|
|
|
start.z = -1.0;
|
|
|
|
|
end.x = start.x;
|
|
|
|
|
end.y = start.y;
|
|
|
|
|
end.z = 1.0;
|
|
|
|
|
|
|
|
|
|
_raycast_p_inv = mat4_inv(camera.p);
|
|
|
|
|
_raycast_v_inv = mat4_inv(camera.v);
|
|
|
|
|
_raycast_vp_inv = mat4_mult_mat(_raycast_p_inv, _raycast_v_inv);
|
|
|
|
|
start = vec4_apply_proj(start, _raycast_vp_inv);
|
|
|
|
|
end = vec4_apply_proj(end, _raycast_vp_inv);
|
|
|
|
|
|
|
|
|
|
// Find direction from start to end
|
|
|
|
|
end = vec4_sub(end, start);
|
|
|
|
|
end = vec4_norm(end);
|
|
|
|
|
end.x *= camera.data.far_plane;
|
|
|
|
|
end.y *= camera.data.far_plane;
|
|
|
|
|
end.z *= camera.data.far_plane;
|
|
|
|
|
|
|
|
|
|
return ray_create(start, end);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function raycast_box_intersect(transform: transform_t, input_x: f32, input_y: f32, camera: camera_object_t): vec4_t {
|
|
|
|
|
let ray: ray_t = raycast_get_ray(input_x, input_y, camera);
|
|
|
|
|
|
|
|
|
|
let t: transform_t = transform;
|
|
|
|
|
let c: vec4_t = vec4_create(transform_world_x(t), transform_world_y(t), transform_world_z(t));
|
|
|
|
|
let s: vec4_t = vec4_create(t.dim.x, t.dim.y, t.dim.z);
|
|
|
|
|
return ray_intersect_box(ray, c, s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function raycast_closest_box_intersect(transforms: transform_t[], input_x: f32, input_y: f32, camera: camera_object_t): transform_t {
|
|
|
|
|
let intersects: transform_t[] = [];
|
|
|
|
|
|
|
|
|
|
// Get intersects
|
|
|
|
|
for (let i: i32 = 0; i < transforms.length; ++i) {
|
|
|
|
|
let t: transform_t = transforms[i];
|
|
|
|
|
let intersect: vec4_t = raycast_box_intersect(t, input_x, input_y, camera);
|
|
|
|
|
if (!vec4_isnan(intersect)) {
|
|
|
|
|
array_push(intersects, t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No intersects
|
|
|
|
|
if (intersects.length == 0) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get closest intersect
|
|
|
|
|
let closest: transform_t = null;
|
|
|
|
|
let inf: f32 = 100000;
|
|
|
|
|
let min_dist: f32 = inf;
|
|
|
|
|
for (let i: i32 = 0; i < intersects.length; ++i) {
|
|
|
|
|
let t: transform_t = intersects[i];
|
|
|
|
|
let dist: f32 = vec4_dist(t.loc, camera.base.transform.loc);
|
|
|
|
|
if (dist < min_dist) {
|
|
|
|
|
min_dist = dist;
|
|
|
|
|
closest = t;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return closest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function plane_create(): plane_t {
|
|
|
|
|
let raw: plane_t = {};
|
|
|
|
|
raw.normal = vec4_create(1.0, 0.0, 0.0);
|
|
|
|
|
raw.constant = 0.0;
|
|
|
|
|
return raw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function raycast_plane_intersect(normal: vec4_t, a: vec4_t, input_x: f32, input_y: f32, camera: camera_object_t): vec4_t {
|
|
|
|
|
let ray: ray_t = raycast_get_ray(input_x, input_y, camera);
|
|
|
|
|
|
|
|
|
|
let plane: plane_t = plane_create();
|
|
|
|
|
plane_set(plane, normal, a);
|
|
|
|
|
|
|
|
|
|
return ray_intersect_plane(ray, plane);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ray_create(origin: vec4_t, dir: vec4_t): ray_t {
|
|
|
|
|
let raw: ray_t = {};
|
|
|
|
|
raw.origin = origin;
|
|
|
|
|
raw.dir = dir;
|
|
|
|
|
return raw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ray_at(raw: ray_t, t: f32): vec4_t {
|
|
|
|
|
return vec4_add(vec4_mult(raw.dir, t), raw.origin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ray_dist_to_point(raw: ray_t, point: vec4_t): f32 {
|
|
|
|
|
let v1: vec4_t = vec4_create();
|
|
|
|
|
let dir_dist: f32 = vec4_dot(vec4_sub(point, raw.origin), raw.dir);
|
|
|
|
|
|
|
|
|
|
// Point behind the ray
|
|
|
|
|
if (dir_dist < 0) {
|
|
|
|
|
return vec4_dist(raw.origin, point);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
raw.dir = vec4_mult(raw.dir, dir_dist);
|
|
|
|
|
raw.dir = vec4_add(raw.dir, raw.origin);
|
|
|
|
|
|
|
|
|
|
return vec4_dist(v1, point);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ray_intersects_sphere(raw: ray_t, sphere_center: vec4_t, sphere_radius: f32): bool {
|
|
|
|
|
return ray_dist_to_point(raw, sphere_center) <= sphere_radius;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ray_intersects_plane(raw: ray_t, plane: plane_t): bool {
|
|
|
|
|
// Check if the ray lies on the plane first
|
|
|
|
|
let dist_to_point: f32 = plane_dist_to_point(plane, raw.origin);
|
|
|
|
|
if (dist_to_point == 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let denominator: f32 = vec4_dot(plane.normal, raw.dir);
|
|
|
|
|
if (denominator * dist_to_point < 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ray origin is behind the plane (and is pointing behind it)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ray_dist_to_plane(raw: ray_t, plane: plane_t): f32 {
|
|
|
|
|
let denominator: f32 = vec4_dot(plane.normal, raw.dir);
|
|
|
|
|
if (denominator == 0) {
|
|
|
|
|
// Line is coplanar, return origin
|
|
|
|
|
if (plane_dist_to_point(plane, raw.origin) == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let t: f32 = -(vec4_dot(raw.origin, plane.normal) + plane.constant) / denominator;
|
|
|
|
|
|
|
|
|
|
// Return if the ray never intersects the plane
|
|
|
|
|
return t >= 0 ? t : -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ray_intersect_plane(raw: ray_t, plane: plane_t): vec4_t {
|
|
|
|
|
let t: f32 = ray_dist_to_plane(raw, plane);
|
|
|
|
|
if (t == -1) {
|
|
|
|
|
return vec4_nan();
|
|
|
|
|
}
|
|
|
|
|
return ray_at(raw, t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ray_intersect_box(raw: ray_t, center: vec4_t, dim: vec4_t): vec4_t {
|
|
|
|
|
// http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/
|
|
|
|
|
let tmin: f32;
|
|
|
|
|
let tmax: f32;
|
|
|
|
|
let tymin: f32;
|
|
|
|
|
let tymax: f32;
|
|
|
|
|
let tzmin: f32;
|
|
|
|
|
let tzmax: f32;
|
|
|
|
|
|
|
|
|
|
let half_x: f32 = dim.x / 2;
|
|
|
|
|
let half_y: f32 = dim.y / 2;
|
|
|
|
|
let half_z: f32 = dim.z / 2;
|
|
|
|
|
let box_min_x: f32 = center.x - half_x;
|
|
|
|
|
let box_min_y: f32 = center.y - half_y;
|
|
|
|
|
let box_min_z: f32 = center.z - half_z;
|
|
|
|
|
let box_max_x: f32 = center.x + half_x;
|
|
|
|
|
let box_max_y: f32 = center.y + half_y;
|
|
|
|
|
let box_max_z: f32 = center.z + half_z;
|
|
|
|
|
|
|
|
|
|
let invdirx: f32 = 1 / raw.dir.x;
|
|
|
|
|
let invdiry: f32 = 1 / raw.dir.y;
|
|
|
|
|
let invdirz: f32 = 1 / raw.dir.z;
|
|
|
|
|
|
|
|
|
|
let origin: vec4_t = raw.origin;
|
|
|
|
|
|
|
|
|
|
if (invdirx >= 0) {
|
|
|
|
|
tmin = (box_min_x - origin.x) * invdirx;
|
|
|
|
|
tmax = (box_max_x - origin.x) * invdirx;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
tmin = (box_max_x - origin.x) * invdirx;
|
|
|
|
|
tmax = (box_min_x - origin.x) * invdirx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (invdiry >= 0) {
|
|
|
|
|
tymin = (box_min_y - origin.y) * invdiry;
|
|
|
|
|
tymax = (box_max_y - origin.y) * invdiry;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
tymin = (box_max_y - origin.y) * invdiry;
|
|
|
|
|
tymax = (box_min_y - origin.y) * invdiry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((tmin > tymax) || (tymin > tmax)) {
|
|
|
|
|
return vec4_nan();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// These lines also handle the case where tmin or tmax is nan
|
|
|
|
|
// (result of 0 * inf). x !== x returns true if x is nan
|
|
|
|
|
if (tymin > tmin || tmin != tmin) {
|
|
|
|
|
tmin = tymin;
|
|
|
|
|
}
|
|
|
|
|
if (tymax < tmax || tmax != tmax) {
|
|
|
|
|
tmax = tymax;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (invdirz >= 0) {
|
|
|
|
|
tzmin = (box_min_z - origin.z) * invdirz;
|
|
|
|
|
tzmax = (box_max_z - origin.z) * invdirz;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
tzmin = (box_max_z - origin.z) * invdirz;
|
|
|
|
|
tzmax = (box_min_z - origin.z) * invdirz;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((tmin > tzmax) || (tzmin > tmax)) {
|
|
|
|
|
return vec4_nan();
|
|
|
|
|
}
|
|
|
|
|
if (tzmin > tmin || tmin != tmin) {
|
|
|
|
|
tmin = tzmin;
|
|
|
|
|
}
|
|
|
|
|
if (tzmax < tmax || tmax != tmax) {
|
|
|
|
|
tmax = tzmax;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return point closest to the ray (positive side)
|
|
|
|
|
if (tmax < 0) {
|
|
|
|
|
return vec4_nan();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ray_at(raw, tmin >= 0 ? tmin : tmax);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ray_intersect_triangle(raw: ray_t, a: vec4_t, b: vec4_t, c: vec4_t, cull_backface: bool): vec4_t {
|
|
|
|
|
// Compute the offset origin, edges, and normal
|
|
|
|
|
let diff: vec4_t = vec4_create();
|
|
|
|
|
let edge1: vec4_t = vec4_create();
|
|
|
|
|
let edge2: vec4_t = vec4_create();
|
|
|
|
|
let normal: vec4_t = vec4_create();
|
|
|
|
|
|
|
|
|
|
// from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp
|
|
|
|
|
edge1 = vec4_sub(b, a);
|
|
|
|
|
edge2 = vec4_sub(c, a);
|
|
|
|
|
normal = vec4_cross(edge1, edge2);
|
|
|
|
|
|
|
|
|
|
let ddn: f32 = vec4_dot(raw.dir, normal);
|
|
|
|
|
let sign: i32;
|
|
|
|
|
|
|
|
|
|
if (ddn > 0) {
|
|
|
|
|
if (cull_backface) {
|
|
|
|
|
return vec4_nan();
|
|
|
|
|
}
|
|
|
|
|
sign = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (ddn < 0) {
|
|
|
|
|
sign = -1;
|
|
|
|
|
ddn = -ddn;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return vec4_nan();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
diff = vec4_sub(raw.origin, a);
|
|
|
|
|
let ddqxe2: f32 = sign * vec4_dot(raw.dir, vec4_cross(diff, edge2));
|
|
|
|
|
|
|
|
|
|
// b1 < 0, no intersection
|
|
|
|
|
if (ddqxe2 < 0) {
|
|
|
|
|
return vec4_nan();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let dde1xq: f32 = sign * vec4_dot(raw.dir, vec4_cross(edge1, diff));
|
|
|
|
|
|
|
|
|
|
// b2 < 0, no intersection
|
|
|
|
|
if (dde1xq < 0) {
|
|
|
|
|
return vec4_nan();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// b1+b2 > 1, no intersection
|
|
|
|
|
if (ddqxe2 + dde1xq > ddn) {
|
|
|
|
|
return vec4_nan();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Line intersects triangle, check if ray does.
|
|
|
|
|
let qdn: f32 = -sign * vec4_dot(diff, normal);
|
|
|
|
|
|
|
|
|
|
// t < 0, no intersection
|
|
|
|
|
if (qdn < 0) {
|
|
|
|
|
return vec4_nan();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ray intersects triangle.
|
|
|
|
|
return ray_at(raw, qdn / ddn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function plane_dist_to_point(raw: plane_t, point: vec4_t): f32 {
|
|
|
|
|
return vec4_dot(raw.normal, point) + raw.constant;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function plane_set(raw: plane_t, normal: vec4_t, point: vec4_t): plane_t {
|
|
|
|
|
raw.normal = vec4_clone(normal);
|
|
|
|
|
raw.constant = -vec4_dot(point, raw.normal);
|
|
|
|
|
return raw;
|
|
|
|
|
}
|