1 /*
2     Copyright © 2020, Luna Nielsen
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4     
5     Authors: Luna Nielsen
6 */
7 module engine.math;
8 public import engine.math.camera;
9 public import engine.math.transform;
10 public import engine.math.obb;
11 public import std.math;
12 public import gl3n.math;
13 public import gl3n.linalg;
14 public import gl3n.aabb;
15 public import gl3n.interpolate;
16 import engine.core.log;
17 
18 /**
19     Smoothly dampens from a position to a target
20 */
21 vec3 dampen(vec3 pos, vec3 target, float delta, float speed = 1) {
22     return (pos - target) * pow(1e-4f, delta*speed) + target;
23 }
24 
25 /**
26     A basic ray
27 */
28 struct Ray {
29     /**
30         Origin of the ray
31     */
32     vec3 origin;
33 
34     /**
35         Direction of the ray (unit vector)
36     */
37     vec3 direction;
38 
39     string toString() {
40         import std.format : format;
41         return "origin=<%s, %s, %s> dir=<%s, %s, %s>".format(origin.x, origin.y, origin.z, direction.x, direction.y, direction.z);
42     }
43 }
44 /**
45     Casts a screen space ray from the mouse position
46 */
47 Ray castScreenSpaceRay(vec2 mouse, vec2 viewSize, mat4 vp) {
48 
49     // mouse to homogenus coordinates
50     double x = mouse.x / (viewSize.x * 0.5) - 1.0;
51     double y = mouse.y / (viewSize.y * 0.5) - 1.0;
52 
53     x = clamp(x, -1, 1);
54     y = clamp(y, -1, 1);
55 
56     vec4 rayStart = vec4(
57         x, 
58         -y,
59         0,
60         1
61     );
62 
63     vec4 rayEnd = vec4(
64         x, 
65         -y,
66         1,
67         1
68     );
69 
70     // Get inverse vp matrix
71     immutable(mat4) vpInverse = vp.inverse;
72     vec4 rayStartWorld =    vpInverse * rayStart;   rayStartWorld /= rayStartWorld.w;
73     vec4 rayEndWorld =      vpInverse * rayEnd;     rayEndWorld /= rayEndWorld.w;
74 
75     // Get the ray direction
76     vec3 rayDir = vec3(rayEndWorld-rayStartWorld).normalized;
77     return Ray(rayStartWorld.xyz, rayDir);
78 }
79 
80 /**
81     Gets whether a ray is intersecting a bounding box
82 */
83 bool isRayIntersecting(OBB boundingBox, Ray ray, mat4 modelmatrix, ref float iDist) {
84     float min = 0f;
85     float max = 100_000f;
86 
87     vec3 oobWorldspace = vec3(modelmatrix[3][0], modelmatrix[3][1], modelmatrix[3][2]);
88     vec3 delta = oobWorldspace-ray.origin;
89 
90     {
91         vec3 xaxis = vec3(modelmatrix[0][0], modelmatrix[0][1], modelmatrix[0][2]);
92         float e = dot(xaxis, delta);
93         float f = dot(ray.direction, xaxis);
94 
95         if (fabs(f) > 0.0001f) {
96             float t1 = (e+boundingBox.min.x)/f;
97             float t2 = (e+boundingBox.max.x)/f;
98 
99             // Swap if t1 is larger than t2
100             if (t1 > t2) {
101                 float w = t1;
102                 t1 = t2;
103                 t2 = w;
104             }
105 
106             if (t2 < max) max = t2;
107             if (t1 > min) min = t1;
108 
109             if (max < min) return false;
110         }
111     }
112 
113     {
114         vec3 yaxis = vec3(modelmatrix[1][0], modelmatrix[1][1], modelmatrix[1][2]);
115         float e = dot(yaxis, delta);
116         float f = dot(ray.direction, yaxis);
117 
118         if (fabs(f) > 0.0001f) {
119             float t1 = (e+boundingBox.min.y)/f;
120             float t2 = (e+boundingBox.max.y)/f;
121 
122             // Swap if t1 is larger than t2
123             if (t1 > t2) {
124                 float w = t1;
125                 t1 = t2;
126                 t2 = w;
127             }
128 
129             if (t2 < max) max = t2;
130             if (t1 > min) min = t1;
131 
132             if (max < min) return false;
133         }
134     }
135 
136     {
137         vec3 zaxis = vec3(modelmatrix[2][0], modelmatrix[2][1], modelmatrix[2][2]);
138         float e = dot(zaxis, delta);
139         float f = dot(ray.direction, zaxis);
140 
141         if (fabs(f) > 0.0001f) {
142             float t1 = (e+boundingBox.min.z)/f;
143             float t2 = (e+boundingBox.max.z)/f;
144 
145             // Swap if t1 is larger than t2
146             if (t1 > t2) {
147                 float w = t1;
148                 t1 = t2;
149                 t2 = w;
150             }
151 
152             if (t2 < max) max = t2;
153             if (t1 > min) min = t1;
154 
155             if (max < min) return false;
156         }
157     }
158     
159     iDist = min;
160     return true;
161 }