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