summaryrefslogtreecommitdiff
path: root/lib/ray.h
blob: e1f43c33c5d9b65f459e54be6c8348051d575909 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#pragma once

#include "config/types.h"
#include "maths.h"

#include <algorithm>
#include <glm/glm.hpp>
#include <glm/gtx/intersect.hpp>
#include <span>

template<typename PositionType> class Ray {
public:
#ifndef __cpp_aggregate_paren_init
	Ray(PositionType start, Direction3D direction) : start {start}, direction {direction} { }
#endif

	PositionType start;
	Direction3D direction;

	[[nodiscard]] PositionType::value_type
	distanceToLine(const PositionType & p1, const PositionType & e1) const
	{
		// https://en.wikipedia.org/wiki/Skew_lines
		const RelativePosition3D diff = p1 - e1;
		const auto d1 = glm::normalize(diff);
		const auto n = crossProduct(d1, direction);
		const auto n2 = crossProduct(direction, n);
		const auto c1 = p1 + PositionType((glm::dot(RelativePosition3D(start - p1), n2) / glm::dot(d1, n2)) * d1);
		const auto difflength = glm::length(diff);
		if (glm::length(RelativePosition3D(c1 - p1)) > difflength
				|| glm::length(RelativePosition3D(c1 - e1)) > difflength) {
			return std::numeric_limits<typename PositionType::value_type>::infinity();
		}
		return static_cast<PositionType::value_type>(glm::abs(glm::dot(n, RelativePosition3D(p1 - start))));
	}

	[[nodiscard]] bool
	passesCloseToEdges(const std::span<const PositionType> positions, const PositionType::value_type distance) const
	{
		return std::adjacent_find(positions.begin(), positions.end(), [this, distance](const auto & a, const auto & b) {
			return distanceToLine(a, b) <= distance;
		}) != positions.end();
	}

	bool
	intersectTriangle(const PositionType t0, const PositionType t1, const PositionType t2, BaryPosition & bary,
			RelativeDistance & distance) const
	{
		if constexpr (std::is_floating_point_v<typename PositionType::value_type>) {
			return glm::intersectRayTriangle(start, direction, t0, t1, t2, bary, distance);
		}
		else {
			const RelativePosition3D t0r = t0 - start, t1r = t1 - start, t2r = t2 - start;
			return glm::intersectRayTriangle({}, direction, t0r, t1r, t2r, bary, distance);
		}
	}

	bool
	intersectSphere(const PositionType centre, const PositionType::value_type size, PositionType & position,
			Normal3D & normal) const
	{
		if constexpr (std::is_floating_point_v<typename PositionType::value_type>) {
			return glm::intersectRaySphere(start, direction, centre, size, position, normal);
		}
		else {
			const RelativePosition3D cr = centre - start;
			RelativePosition3D positionF {};
			const auto r = glm::intersectRaySphere(
					{}, direction, cr, static_cast<RelativeDistance>(size), positionF, normal);
			position = GlobalPosition3D(positionF) + start;
			return r;
		}
	}
};

class RayFactory {
public:
	template<typename PositionType>
	static Ray<PositionType>
	fromPoints(PositionType start, PositionType p)
	{
		return {start, glm::normalize(p - start)};
	}
};