A game about forced loneliness, made by TACStudios
1#ifndef UNITY_GEOMETRICTOOLS_INCLUDED
2#define UNITY_GEOMETRICTOOLS_INCLUDED
3
4//-----------------------------------------------------------------------------
5// Transform functions
6//-----------------------------------------------------------------------------
7
8// Rotate around a pivot point and an axis
9float3 Rotate(float3 pivot, float3 position, float3 rotationAxis, float angle)
10{
11 rotationAxis = normalize(rotationAxis);
12 float3 cpa = pivot + rotationAxis * dot(rotationAxis, position - pivot);
13 return cpa + ((position - cpa) * cos(angle) + cross(rotationAxis, (position - cpa)) * sin(angle));
14}
15
16float3x3 RotationFromAxisAngle(float3 A, float sinAngle, float cosAngle)
17{
18 float c = cosAngle;
19 float s = sinAngle;
20
21 return float3x3(A.x * A.x * (1 - c) + c, A.x * A.y * (1 - c) - A.z * s, A.x * A.z * (1 - c) + A.y * s,
22 A.x * A.y * (1 - c) + A.z * s, A.y * A.y * (1 - c) + c, A.y * A.z * (1 - c) - A.x * s,
23 A.x * A.z * (1 - c) - A.y * s, A.y * A.z * (1 - c) + A.x * s, A.z * A.z * (1 - c) + c);
24}
25
26//-----------------------------------------------------------------------------
27// Solver
28//-----------------------------------------------------------------------------
29
30// Solves the quadratic equation of the form: a*t^2 + b*t + c = 0.
31// Returns 'false' if there are no real roots, 'true' otherwise.
32// Ensures that roots.x <= roots.y.
33bool SolveQuadraticEquation(float a, float b, float c, out float2 roots)
34{
35 float det = Sq(b) - 4.0 * a * c;
36
37 float sqrtDet = sqrt(det);
38 roots.x = (-b - sign(a) * sqrtDet) / (2.0 * a);
39 roots.y = (-b + sign(a) * sqrtDet) / (2.0 * a);
40
41 return (det >= 0.0);
42}
43
44//-----------------------------------------------------------------------------
45// Intersection functions
46//-----------------------------------------------------------------------------
47
48bool IntersectRayAABB(float3 rayOrigin, float3 rayDirection,
49 float3 boxMin, float3 boxMax,
50 float tMin, float tMax,
51 out float tEntr, out float tExit)
52{
53 // Could be precomputed. Clamp to avoid INF. clamp() is a single ALU on GCN.
54 // rcp(FLT_EPS) = 16,777,216, which is large enough for our purposes,
55 // yet doesn't cause a lot of numerical issues associated with FLT_MAX.
56 float3 rayDirInv = clamp(rcp(rayDirection), -rcp(FLT_EPS), rcp(FLT_EPS));
57
58 // Perform ray-slab intersection (component-wise).
59 float3 t0 = boxMin * rayDirInv - (rayOrigin * rayDirInv);
60 float3 t1 = boxMax * rayDirInv - (rayOrigin * rayDirInv);
61
62 // Find the closest/farthest distance (component-wise).
63 float3 tSlabEntr = min(t0, t1);
64 float3 tSlabExit = max(t0, t1);
65
66 // Find the farthest entry and the nearest exit.
67 tEntr = Max3(tSlabEntr.x, tSlabEntr.y, tSlabEntr.z);
68 tExit = Min3(tSlabExit.x, tSlabExit.y, tSlabExit.z);
69
70 // Clamp to the range.
71 tEntr = max(tEntr, tMin);
72 tExit = min(tExit, tMax);
73
74 return tEntr < tExit;
75}
76
77// This simplified version assume that we care about the result only when we are inside the box
78float IntersectRayAABBSimple(float3 start, float3 dir, float3 boxMin, float3 boxMax)
79{
80 float3 invDir = rcp(dir);
81
82 // Find the ray intersection with box plane
83 float3 rbmin = (boxMin - start) * invDir;
84 float3 rbmax = (boxMax - start) * invDir;
85
86 float3 rbminmax = float3((dir.x > 0.0) ? rbmax.x : rbmin.x, (dir.y > 0.0) ? rbmax.y : rbmin.y, (dir.z > 0.0) ? rbmax.z : rbmin.z);
87
88 return min(min(rbminmax.x, rbminmax.y), rbminmax.z);
89}
90
91// Assume Sphere is at the origin (i.e start = position - spherePosition)
92bool IntersectRaySphere(float3 start, float3 dir, float radius, out float2 intersections)
93{
94 float a = dot(dir, dir);
95 float b = dot(dir, start) * 2.0;
96 float c = dot(start, start) - radius * radius;
97
98 return SolveQuadraticEquation(a, b, c, intersections);
99}
100
101// This simplified version assume that we care about the result only when we are inside the sphere
102// Assume Sphere is at the origin (i.e start = position - spherePosition) and dir is normalized
103// Ref: http://http.developer.nvidia.com/GPUGems/gpugems_ch19.html
104float IntersectRaySphereSimple(float3 start, float3 dir, float radius)
105{
106 float b = dot(dir, start) * 2.0;
107 float c = dot(start, start) - radius * radius;
108 float discriminant = b * b - 4.0 * c;
109
110 return abs(sqrt(discriminant) - b) * 0.5;
111}
112
113float3 IntersectRayPlane(float3 rayOrigin, float3 rayDirection, float3 planeOrigin, float3 planeNormal)
114{
115 float dist = dot(planeNormal, planeOrigin - rayOrigin) / dot(planeNormal, rayDirection);
116 return rayOrigin + rayDirection * dist;
117}
118
119// Same as above but return intersection distance and true / false if the ray hit/miss
120bool IntersectRayPlane(float3 rayOrigin, float3 rayDirection, float3 planePosition, float3 planeNormal, out float t)
121{
122 bool res = false;
123 t = -1.0;
124
125 float denom = dot(planeNormal, rayDirection);
126 if (abs(denom) > 1e-5)
127 {
128 float3 d = planePosition - rayOrigin;
129 t = dot(d, planeNormal) / denom;
130 res = (t >= 0);
131 }
132
133 return res;
134}
135
136bool RayPlaneSegmentIntersect(in float3 rayOrigin, in float3 rayDirection, float3 planeNormal, float planeDistance, inout float3 intersectionPoint)
137{
138 float denom = dot(rayDirection, planeNormal);
139 float lambda = (denom != 0.0) ? (planeDistance - dot(rayOrigin, planeNormal)) / denom : -1.0;
140 if ((lambda >= 0.0) && (lambda <= 1.0))
141 {
142 intersectionPoint = rayOrigin + lambda * rayDirection;
143 return true;
144 }
145 else
146 return false;
147}
148
149// Can support cones with an elliptic base: pre-scale 'coneAxisX' and 'coneAxisY' by (h/r_x) and (h/r_y).
150// Returns parametric distances 'tEntr' and 'tExit' along the ray,
151// subject to constraints 'tMin' and 'tMax'.
152bool IntersectRayCone(float3 rayOrigin, float3 rayDirection,
153 float3 coneOrigin, float3 coneDirection,
154 float3 coneAxisX, float3 coneAxisY,
155 float tMin, float tMax,
156 out float tEntr, out float tExit)
157{
158 // Inverse transform the ray into a coordinate system with the cone at the origin facing along the Z axis.
159 float3x3 rotMat = float3x3(coneAxisX, coneAxisY, coneDirection);
160
161 float3 o = mul(rotMat, rayOrigin - coneOrigin);
162 float3 d = mul(rotMat, rayDirection);
163
164 // Cone equation (facing along Z): (h/r*x)^2 + (h/r*y)^2 - z^2 = 0.
165 // Cone axes are premultiplied with (h/r).
166 // Set up the quadratic equation: a*t^2 + b*t + c = 0.
167 float a = d.x * d.x + d.y * d.y - d.z * d.z;
168 float b = o.x * d.x + o.y * d.y - o.z * d.z;
169 float c = o.x * o.x + o.y * o.y - o.z * o.z;
170
171 float2 roots;
172
173 // Check whether we have at least 1 root.
174 bool hit = SolveQuadraticEquation(a, 2 * b, c, roots);
175
176 tEntr = roots.x;
177 tExit = roots.y;
178 float3 pEntr = o + tEntr * d;
179 float3 pExit = o + tExit * d;
180
181 // Clip the negative cone.
182 bool pEntrNeg = pEntr.z < 0;
183 bool pExitNeg = pExit.z < 0;
184 if (pEntrNeg && pExitNeg) { hit = false; }
185 if (pEntrNeg) { tEntr = tExit; tExit = tMax; }
186 if (pExitNeg) { tExit = tEntr; tEntr = tMin; }
187
188 // Clamp using the values passed into the function.
189 tEntr = clamp(tEntr, tMin, tMax);
190 tExit = clamp(tExit, tMin, tMax);
191
192 // Check for grazing intersections.
193 if (tEntr == tExit) { hit = false; }
194
195 return hit;
196}
197
198bool IntersectSphereAABB(float3 position, float radius, float3 aabbMin, float3 aabbMax)
199{
200 float x = max(aabbMin.x, min(position.x, aabbMax.x));
201 float y = max(aabbMin.y, min(position.y, aabbMax.y));
202 float z = max(aabbMin.z, min(position.z, aabbMax.z));
203 float distance2 = ((x - position.x) * (x - position.x) + (y - position.y) * (y - position.y) + (z - position.z) * (z - position.z));
204 return distance2 < radius * radius;
205}
206
207//-----------------------------------------------------------------------------
208// Miscellaneous functions
209//-----------------------------------------------------------------------------
210
211// Box is AABB
212float DistancePointBox(float3 position, float3 boxMin, float3 boxMax)
213{
214 return length(max(max(position - boxMax, boxMin - position), float3(0.0, 0.0, 0.0)));
215}
216
217float3 ProjectPointOnPlane(float3 position, float3 planePosition, float3 planeNormal)
218{
219 return position - (dot(position - planePosition, planeNormal) * planeNormal);
220}
221
222// Plane equation: {(a, b, c) = N, d = -dot(N, P)}.
223// Returns the distance from the plane to the point 'p' along the normal.
224// Positive -> in front (above), negative -> behind (below).
225float DistanceFromPlane(float3 p, float4 plane)
226{
227 return dot(float4(p, 1.0), plane);
228}
229
230// Returns 'true' if the triangle is outside of the frustum.
231// 'epsilon' is the (negative) distance to (outside of) the frustum below which we cull the triangle.
232bool CullTriangleFrustum(float3 p0, float3 p1, float3 p2, float epsilon, float4 frustumPlanes[6], int numPlanes)
233{
234 bool outside = false;
235
236 for (int i = 0; i < numPlanes; i++)
237 {
238 // If all 3 points are behind any of the planes, we cull.
239 outside = outside || Max3(DistanceFromPlane(p0, frustumPlanes[i]),
240 DistanceFromPlane(p1, frustumPlanes[i]),
241 DistanceFromPlane(p2, frustumPlanes[i])) < epsilon;
242 }
243
244 return outside;
245}
246
247// Returns 'true' if the edge of the triangle is outside of the frustum.
248// The edges are defined s.t. they are on the opposite side of the point with the given index.
249// 'epsilon' is the (negative) distance to (outside of) the frustum below which we cull the triangle.
250//output packing:
251// x,y,z - one component per triangle edge, true if outside, false otherwise
252// w - true if entire triangle is outside of at least 1 plane of the frustum, false otherwise
253bool4 CullFullTriangleAndEdgesFrustum(float3 p0, float3 p1, float3 p2, float epsilon, float4 frustumPlanes[6], int numPlanes)
254{
255 bool4 edgesOutsideXYZ_triangleOutsideW = false;
256
257 for (int i = 0; i < numPlanes; i++)
258 {
259 bool3 pointsOutside = bool3(DistanceFromPlane(p0, frustumPlanes[i]) < epsilon,
260 DistanceFromPlane(p1, frustumPlanes[i]) < epsilon,
261 DistanceFromPlane(p2, frustumPlanes[i]) < epsilon);
262
263 bool3 edgesOutside;
264 // If both points of the edge are behind any of the planes, we cull.
265 edgesOutside.x = pointsOutside.y && pointsOutside.z;
266 edgesOutside.y = pointsOutside.x && pointsOutside.z;
267 edgesOutside.z = pointsOutside.x && pointsOutside.y;
268
269 edgesOutsideXYZ_triangleOutsideW = bool4(edgesOutsideXYZ_triangleOutsideW.x || edgesOutside.x,
270 edgesOutsideXYZ_triangleOutsideW.y || edgesOutside.y,
271 edgesOutsideXYZ_triangleOutsideW.z || edgesOutside.z,
272 all(pointsOutside));
273 }
274
275 return edgesOutsideXYZ_triangleOutsideW;
276}
277
278// Returns 'true' if the edge of the triangle is outside of the frustum.
279// The edges are defined s.t. they are on the opposite side of the point with the given index.
280// 'epsilon' is the (negative) distance to (outside of) the frustum below which we cull the triangle.
281//output packing:
282// x,y,z - one component per triangle edge, true if outside, false otherwise
283bool3 CullTriangleEdgesFrustum(float3 p0, float3 p1, float3 p2, float epsilon, float4 frustumPlanes[6], int numPlanes)
284{
285 return CullFullTriangleAndEdgesFrustum(p0, p1, p2, epsilon, frustumPlanes, numPlanes).xyz;
286}
287
288bool CullTriangleBackFaceView(float3 p0, float3 p1, float3 p2, float epsilon, float3 V, float winding)
289{
290 float3 edge1 = p1 - p0;
291 float3 edge2 = p2 - p0;
292
293 float3 N = cross(edge1, edge2);
294 float NdotV = dot(N, V) * winding;
295
296 // Optimize:
297 // NdotV / (length(N) * length(V)) < Epsilon
298 // NdotV < Epsilon * length(N) * length(V)
299 // NdotV < Epsilon * sqrt(dot(N, N)) * sqrt(dot(V, V))
300 // NdotV < Epsilon * sqrt(dot(N, N) * dot(V, V))
301 return NdotV < epsilon * sqrt(dot(N, N) * dot(V, V));
302}
303
304// Returns 'true' if a triangle defined by 3 vertices is back-facing.
305// 'epsilon' is the (negative) value of dot(N, V) below which we cull the triangle.
306// 'winding' can be used to change the order: pass 1 for (p0 -> p1 -> p2), or -1 for (p0 -> p2 -> p1).
307bool CullTriangleBackFace(float3 p0, float3 p1, float3 p2, float epsilon, float3 viewPos, float winding)
308{
309 float3 V = viewPos - p0;
310 return CullTriangleBackFaceView(p0, p1, p2, epsilon, V, winding);
311}
312
313#endif // UNITY_GEOMETRICTOOLS_INCLUDED