directx_math/
collision.rs

1
2//! Collision functions and bounding volumes
3//!
4//! <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/>
5
6use crate::*;
7
8/// Indicates whether an object contains another object.
9///
10/// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/ne-directxcollision-containmenttype>
11#[repr(C)]
12#[derive(Copy, Clone, Debug, PartialEq, Eq)]
13pub enum ContainmentType {
14    /// The object does not contain the specified object.
15    DISJOINT = 0,
16    /// The objects intersect.
17    INTERSECTS = 1,
18    /// The object contains the specified object.
19    CONTAINS = 2,
20}
21
22/// Indicates whether an object intersects a plane.
23///
24/// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/ne-directxcollision-planeintersectiontype>
25#[repr(C)]
26#[derive(Copy, Clone, Debug, PartialEq, Eq)]
27pub enum PlaneIntersectionType {
28    /// The object is in front of the plane.
29    FRONT = 0,
30    /// The object intersects the plane.
31    INTERSECTING = 1,
32    /// The object is behind the plane.
33    BACK = 2,
34}
35
36use ContainmentType::{DISJOINT, INTERSECTS, CONTAINS};
37use PlaneIntersectionType::{FRONT, INTERSECTING, BACK};
38
39/// A bounding sphere object.
40#[repr(C)]
41#[derive(Copy, Clone, Debug, Default)]
42pub struct BoundingSphere {
43    // Center of the sphere.
44    pub Center: XMFLOAT3,
45
46    // Radius of the sphere.
47    pub Radius: f32,
48}
49
50/// A bounding axis-aligned object (`AABB`).
51///
52/// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/ns-directxcollision-boundingbox>
53#[repr(C)]
54#[derive(Copy, Clone, Debug, Default)]
55pub struct BoundingBox {
56    // Center of the box.
57    pub Center: XMFLOAT3,
58
59    // Distance from the center to each side.
60    pub Extents: XMFLOAT3,
61}
62
63/// An oriented bounding box object (`OBB`).
64///
65/// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/ns-directxcollision-boundingorientedbox>
66#[repr(C)]
67#[derive(Copy, Clone, Debug, Default)]
68pub struct BoundingOrientedBox {
69    // Center of the box.
70    pub Center: XMFLOAT3,
71
72    // Distance from the center to each side.
73    pub Extents: XMFLOAT3,
74
75    // Unit quaternion representing rotation (box -> world).
76    pub Orientation: XMFLOAT4,
77}
78
79/// A bounding frustum object.
80///
81/// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/ns-directxcollision-boundingfrustum>
82#[repr(C)]
83#[derive(Copy, Clone, Debug, Default)]
84pub struct BoundingFrustum {
85    // Origin of the frustum (and projection).
86    pub Origin: XMFLOAT3,
87
88    // Quaternion representing rotation.
89    pub Orientation: XMFLOAT4,
90
91    // Positive X (X/Z)
92    pub RightSlope: f32,
93
94    // Negative X
95    pub LeftSlope: f32,
96
97    // Positive Y (Y/Z)
98    pub TopSlope: f32,
99
100    // Negative Y
101    pub BottomSlope: f32,
102
103    // Z of the near plane.
104    pub Near: f32,
105
106    // Z of far plane.
107    pub Far: f32,
108}
109
110const g_BoxOffset: [XMVECTORF32; BoundingBox::CORNER_COUNT] = [
111    XMVECTORF32 { f: [ -1.0, -1.0,  1.0, 0.0 ] },
112    XMVECTORF32 { f: [  1.0, -1.0,  1.0, 0.0 ] },
113    XMVECTORF32 { f: [  1.0,  1.0,  1.0, 0.0 ] },
114    XMVECTORF32 { f: [ -1.0,  1.0,  1.0, 0.0 ] },
115    XMVECTORF32 { f: [ -1.0, -1.0, -1.0, 0.0 ] },
116    XMVECTORF32 { f: [  1.0, -1.0, -1.0, 0.0 ] },
117    XMVECTORF32 { f: [  1.0,  1.0, -1.0, 0.0 ] },
118    XMVECTORF32 { f: [ -1.0,  1.0, -1.0, 0.0 ] },
119];
120
121const FLT_MAX: f32 = std::f32::MAX;
122
123const g_RayEpsilon: XMVECTORF32 = XMVECTORF32 { f: [ 1e-20, 1e-20, 1e-20, 1e-20 ] };
124const g_RayNegEpsilon: XMVECTORF32 = XMVECTORF32 { f: [ -1e-20, -1e-20, -1e-20, -1e-20 ] };
125const g_FltMin: XMVECTORF32 = XMVECTORF32 { f: [ -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX ] };
126const g_FltMax: XMVECTORF32 = XMVECTORF32 { f: [ FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX ] };
127
128mod internal {
129    use crate::*;
130
131    const g_UnitVectorEpsilon: XMVECTORF32 = XMVECTORF32 { f: [ 1.0e-4, 1.0e-4, 1.0e-4, 1.0e-4 ] };
132    const g_UnitQuaternionEpsilon: XMVECTORF32 = XMVECTORF32 { f: [ 1.0e-4, 1.0e-4, 1.0e-4, 1.0e-4 ] };
133    const g_UnitPlaneEpsilon: XMVECTORF32 = XMVECTORF32 { f: [ 1.0e-4, 1.0e-4, 1.0e-4, 1.0e-4 ] };
134
135    /// Return true if any of the elements of a 3 vector are equal to 0xffffffff.
136    /// Slightly more efficient than using XMVector3EqualInt.
137    #[inline]
138    pub fn XMVector3AnyTrue(V: FXMVECTOR) -> bool {
139        // Duplicate the fourth element from the first element.
140        let C: XMVECTOR = <(XM_SWIZZLE_X, XM_SWIZZLE_Y, XM_SWIZZLE_Z, XM_SWIZZLE_X)>::XMVectorSwizzle(V);
141        return XMComparisonAnyTrue(XMVector4EqualIntR(C, XMVectorTrueInt()));
142    }
143
144    /// Return true if all of the elements of a 3 vector are equal to 0xffffffff.
145    /// Slightly more efficient than using XMVector3EqualInt.
146    #[inline]
147    pub fn XMVector3AllTrue(V: FXMVECTOR) -> bool {
148        // Duplicate the fourth element from the first element.
149        let C: XMVECTOR = <(XM_SWIZZLE_X, XM_SWIZZLE_Y, XM_SWIZZLE_Z, XM_SWIZZLE_X)>::XMVectorSwizzle(V);
150        return XMComparisonAllTrue(XMVector4EqualIntR(C, XMVectorTrueInt()));
151    }
152
153    /// Return true if the vector is a unit vector (length == 1).
154    #[inline]
155    pub fn XMVector3IsUnit(V: FXMVECTOR) -> bool {
156        let Difference: XMVECTOR = XMVectorSubtract(XMVector3Length(V), XMVectorSplatOne());
157        return XMVector4Less(XMVectorAbs(Difference), g_UnitVectorEpsilon.v());
158    }
159
160    /// Return true if the quaterion is a unit quaternion.
161    #[inline]
162    pub fn XMQuaternionIsUnit(Q: FXMVECTOR) -> bool {
163        let Difference: XMVECTOR = XMVectorSubtract(XMVector4Length(Q), XMVectorSplatOne());
164        return XMVector4Less(XMVectorAbs(Difference), g_UnitQuaternionEpsilon.v());
165    }
166
167    /// Return true if the plane is a unit plane.
168    #[inline]
169    pub fn XMPlaneIsUnit(Plane: FXMVECTOR) -> bool {
170        let Difference: XMVECTOR = XMVectorSubtract(XMVector3Length(Plane), XMVectorSplatOne());
171        return XMVector4Less(XMVectorAbs(Difference), g_UnitPlaneEpsilon.v());
172    }
173
174    #[inline]
175    pub fn XMPlaneTransform(Plane: FXMVECTOR, Rotation: FXMVECTOR, Translation: FXMVECTOR) -> XMVECTOR {
176        let vNormal: XMVECTOR = XMVector3Rotate(Plane, Rotation);
177        let vD: XMVECTOR = XMVectorSubtract(XMVectorSplatW(Plane), XMVector3Dot(vNormal, Translation));
178
179        // TODO: template
180        return XMVectorInsert(vNormal, vD, 0, 0, 0, 0, 1);
181    }
182
183    /// Return the point on the line segement (S1, S2) nearest the point P.
184    #[inline]
185    pub fn PointOnLineSegmentNearestPoint(S1: FXMVECTOR, S2: FXMVECTOR, P: FXMVECTOR) -> XMVECTOR {
186        let Dir: XMVECTOR = XMVectorSubtract(S2, S1);
187        let Projection: XMVECTOR = XMVectorSubtract(XMVector3Dot(P, Dir), XMVector3Dot(S1, Dir));
188        let LengthSq: XMVECTOR = XMVector3Dot(Dir, Dir);
189
190        let t: XMVECTOR = XMVectorMultiply(Projection, XMVectorReciprocal(LengthSq));
191        let mut Point: XMVECTOR = XMVectorMultiplyAdd(t, Dir, S1);
192
193        // t < 0
194        let SelectS1: XMVECTOR = XMVectorLess(Projection, XMVectorZero());
195        Point = XMVectorSelect(Point, S1, SelectS1);
196
197        // t > 1
198        let SelectS2: XMVECTOR = XMVectorGreater(Projection, LengthSq);
199        Point = XMVectorSelect(Point, S2, SelectS2);
200
201        return Point;
202    }
203
204    /// Test if the point (P) on the plane of the triangle is inside the triangle
205    /// (V0, V1, V2).
206    #[inline]
207    pub fn PointOnPlaneInsideTriangle(P: FXMVECTOR, V0: FXMVECTOR, V1: FXMVECTOR, V2: GXMVECTOR) -> XMVECTOR {
208        // Compute the triangle normal.
209        let N: XMVECTOR = XMVector3Cross(XMVectorSubtract(V2, V0), XMVectorSubtract(V1, V0));
210
211        // Compute the cross products of the vector from the base of each edge to
212        // the point with each edge vector.
213        let C0: XMVECTOR = XMVector3Cross(XMVectorSubtract(P, V0), XMVectorSubtract(V1, V0));
214        let C1: XMVECTOR = XMVector3Cross(XMVectorSubtract(P, V1), XMVectorSubtract(V2, V1));
215        let C2: XMVECTOR = XMVector3Cross(XMVectorSubtract(P, V2), XMVectorSubtract(V0, V2));
216
217        // If the cross product points in the same direction as the normal the the
218        // point is inside the edge (it is zero if is on the edge).
219        let Zero: XMVECTOR = XMVectorZero();
220        let Inside0: XMVECTOR = XMVectorGreaterOrEqual(XMVector3Dot(C0, N), Zero);
221        let Inside1: XMVECTOR = XMVectorGreaterOrEqual(XMVector3Dot(C1, N), Zero);
222        let Inside2: XMVECTOR = XMVectorGreaterOrEqual(XMVector3Dot(C2, N), Zero);
223
224        // If the point inside all of the edges it is inside.
225        return XMVectorAndInt(XMVectorAndInt(Inside0, Inside1), Inside2);
226    }
227
228    #[inline]
229    pub fn SolveCubic(e: f32, f: f32, g: f32, t: &mut f32, u: &mut f32, v: &mut f32) -> bool {
230        let p: f32;
231        let q: f32;
232        let h: f32;
233        let rc: f32;
234        let d: f32;
235        let theta: f32;
236        let costh3: f32;
237        let sinth3: f32;
238
239        p = f - e * e / 3.0;
240        q = g - e * f / 3.0 + e * e * e * 2.0 / 27.0;
241        h = q * q / 4.0 + p * p * p / 27.0;
242
243        if (h > 0.0)
244        {
245            *t = 0.0;
246            *u = 0.0;
247            *v = 0.0;
248            return false; // only one real root
249        }
250
251        if ((h == 0.0) && (q == 0.0)) // all the same root
252        {
253            *t = -e / 3.0;
254            *u = -e / 3.0;
255            *v = -e / 3.0;
256
257            return true;
258        }
259
260        d = sqrtf(q * q / 4.0 - h);
261        if (d < 0.0) {
262            rc = -powf(-d, 1.0 / 3.0);
263        }
264        else {
265            rc = powf(d, 1.0 / 3.0);
266        }
267
268        theta = XMScalarACos(-q / (2.0 * d));
269        costh3 = XMScalarCos(theta / 3.0);
270        sinth3 = sqrtf(3.0) * XMScalarSin(theta / 3.0);
271        *t = 2.0 * rc * costh3 - e / 3.0;
272        *u = -rc * (costh3 + sinth3) - e / 3.0;
273        *v = -rc * (costh3 - sinth3) - e / 3.0;
274
275        return true;
276    }
277
278    #[inline]
279    pub fn CalculateEigenVector(
280        m11: f32, m12: f32, m13: f32,
281        m22: f32, m23: f32, m33: f32,
282        e: f32,
283    ) -> XMVECTOR {
284        let mut fTmp: [f32; 3] = unsafe { undefined() };
285        fTmp[0] = m12 * m23 - m13 * (m22 - e);
286        fTmp[1] = m13 * m12 - m23 * (m11 - e);
287        fTmp[2] = (m11 - e) * (m22 - e) - m12 * m12;
288
289        let fTmp: XMFLOAT3 = unsafe { mem::transmute(fTmp) };
290        let mut vTmp: XMVECTOR = XMLoadFloat3(&fTmp);
291
292        if (XMVector3Equal(vTmp, XMVectorZero())) // planar or linear
293        {
294            let f1: f32;
295            let f2: f32;
296            let f3: f32;
297
298            // we only have one equation - find a valid one
299            if ((m11 - e != 0.0) || (m12 != 0.0) || (m13 != 0.0))
300            {
301                f1 = m11 - e; f2 = m12; f3 = m13;
302            }
303            else if ((m12 != 0.0) || (m22 - e != 0.0) || (m23 != 0.0))
304            {
305                f1 = m12; f2 = m22 - e; f3 = m23;
306            }
307            else if ((m13 != 0.0) || (m23 != 0.0) || (m33 - e != 0.0))
308            {
309                f1 = m13; f2 = m23; f3 = m33 - e;
310            }
311            else
312            {
313                // error, we'll just make something up - we have NO context
314                f1 = 1.0; f2 = 0.0; f3 = 0.0;
315            }
316
317            if (f1 == 0.0) {
318                vTmp = XMVectorSetX(vTmp, 0.0);
319            } else {
320                vTmp = XMVectorSetX(vTmp, 1.0);
321            }
322            if (f2 == 0.0) {
323                vTmp = XMVectorSetY(vTmp, 0.0);
324            } else {
325                vTmp = XMVectorSetY(vTmp, 1.0);
326            }
327            if (f3 == 0.0)
328            {
329                vTmp = XMVectorSetZ(vTmp, 0.0);
330                // recalculate y to make equation work
331                if (m12 != 0.0) {
332                    vTmp = XMVectorSetY(vTmp, -f1 / f2);
333                }
334            }
335            else
336            {
337                vTmp = XMVectorSetZ(vTmp, (f2 - f1) / f3);
338            }
339        }
340
341        if (XMVectorGetX(XMVector3LengthSq(vTmp)) > 1e-5)
342        {
343            return XMVector3Normalize(vTmp);
344        }
345        else
346        {
347            // Multiply by a value large enough to make the vector non-zero.
348            vTmp = XMVectorScale(vTmp, 1e5);
349            return XMVector3Normalize(vTmp);
350        }
351    }
352
353    #[inline]
354    pub fn CalculateEigenVectors(
355        m11: f32, m12: f32, m13: f32,
356        m22: f32, m23: f32, m33: f32,
357        e1: f32, e2: f32, e3: f32,
358        pV1: &mut XMVECTOR,
359        pV2: &mut XMVECTOR,
360        pV3: &mut XMVECTOR,
361    ) -> bool {
362        *pV1 = CalculateEigenVector(m11, m12, m13, m22, m23, m33, e1);
363        *pV2 = CalculateEigenVector(m11, m12, m13, m22, m23, m33, e2);
364        *pV3 = CalculateEigenVector(m11, m12, m13, m22, m23, m33, e3);
365
366        let mut v1z: bool = false;
367        let mut v2z: bool = false;
368        let mut v3z: bool = false;
369
370        let Zero: XMVECTOR = XMVectorZero();
371
372        if (XMVector3Equal(*pV1, Zero)) {
373            v1z = true;
374        }
375
376        if (XMVector3Equal(*pV2, Zero)) {
377            v2z = true;
378        }
379
380        if (XMVector3Equal(*pV3, Zero)) {
381            v3z = true;
382        }
383
384        let e12: bool = (fabsf(XMVectorGetX(XMVector3Dot(*pV1, *pV2))) > 0.1); // check for non-orthogonal vectors
385        let e13: bool = (fabsf(XMVectorGetX(XMVector3Dot(*pV1, *pV3))) > 0.1);
386        let e23: bool = (fabsf(XMVectorGetX(XMVector3Dot(*pV2, *pV3))) > 0.1);
387
388        if ((v1z && v2z && v3z) || (e12 && e13 && e23) ||
389            (e12 && v3z) || (e13 && v2z) || (e23 && v1z)) // all eigenvectors are 0- any basis set
390        {
391            *pV1 = g_XMIdentityR0.v();
392            *pV2 = g_XMIdentityR1.v();
393            *pV3 = g_XMIdentityR2.v();
394            return true;
395        }
396
397        if (v1z && v2z)
398        {
399            let mut vTmp: XMVECTOR = XMVector3Cross(g_XMIdentityR1.v(), *pV3);
400            if (XMVectorGetX(XMVector3LengthSq(vTmp)) < 1e-5)
401            {
402                vTmp = XMVector3Cross(g_XMIdentityR0.v(), *pV3);
403            }
404            *pV1 = XMVector3Normalize(vTmp);
405            *pV2 = XMVector3Cross(*pV3, *pV1);
406            return true;
407        }
408
409        if (v3z && v1z)
410        {
411            let mut vTmp: XMVECTOR = XMVector3Cross(g_XMIdentityR1.v(), *pV2);
412            if (XMVectorGetX(XMVector3LengthSq(vTmp)) < 1e-5)
413            {
414                vTmp = XMVector3Cross(g_XMIdentityR0.v(), *pV2);
415            }
416            *pV3 = XMVector3Normalize(vTmp);
417            *pV1 = XMVector3Cross(*pV2, *pV3);
418            return true;
419        }
420
421        if (v2z && v3z)
422        {
423            let mut vTmp: XMVECTOR = XMVector3Cross(g_XMIdentityR1.v(), *pV1);
424            if (XMVectorGetX(XMVector3LengthSq(vTmp)) < 1e-5)
425            {
426                vTmp = XMVector3Cross(g_XMIdentityR0.v(), *pV1);
427            }
428            *pV2 = XMVector3Normalize(vTmp);
429            *pV3 = XMVector3Cross(*pV1, *pV2);
430            return true;
431        }
432
433        if ((v1z) || e12)
434        {
435            *pV1 = XMVector3Cross(*pV2, *pV3);
436            return true;
437        }
438
439        if ((v2z) || e23)
440        {
441            *pV2 = XMVector3Cross(*pV3, *pV1);
442            return true;
443        }
444
445        if ((v3z) || e13)
446        {
447            *pV3 = XMVector3Cross(*pV1, *pV2);
448            return true;
449        }
450
451        return true;
452    }
453
454    #[inline]
455    pub fn CalculateEigenVectorsFromCovarianceMatrix(
456        Cxx: f32,
457        Cyy: f32,
458        Czz: f32,
459        Cxy: f32,
460        Cxz: f32,
461        Cyz: f32,
462        pV1: &mut XMVECTOR,
463        pV2: &mut XMVECTOR,
464        pV3: &mut XMVECTOR,
465
466    ) -> bool {
467        // Calculate the eigenvalues by solving a cubic equation.
468        let e: f32 = -(Cxx + Cyy + Czz);
469        let f: f32 = Cxx * Cyy + Cyy * Czz + Czz * Cxx - Cxy * Cxy - Cxz * Cxz - Cyz * Cyz;
470        let g: f32 = Cxy * Cxy * Czz + Cxz * Cxz * Cyy + Cyz * Cyz * Cxx - Cxy * Cyz * Cxz * 2.0 - Cxx * Cyy * Czz;
471
472        let mut ev1: f32 = 0.0;
473        let mut ev2: f32 = 0.0;
474        let mut ev3: f32 = 0.0;
475        if (!SolveCubic(e, f, g, &mut ev1, &mut ev2, &mut ev3))
476        {
477            // set them to arbitrary orthonormal basis set
478            *pV1 = g_XMIdentityR0.v();
479            *pV2 = g_XMIdentityR1.v();
480            *pV3 = g_XMIdentityR2.v();
481            return false;
482        }
483
484        return CalculateEigenVectors(Cxx, Cxy, Cxz, Cyy, Cyz, Czz, ev1, ev2, ev3, pV1, pV2, pV3);
485    }
486
487    #[inline]
488    pub fn FastIntersectTrianglePlane(
489        V0: FXMVECTOR,
490        V1: FXMVECTOR,
491        V2: FXMVECTOR,
492        Plane: GXMVECTOR,
493        Outside: &mut XMVECTOR,
494        Inside: &mut XMVECTOR
495    ) {
496        // Plane0
497        let Dist0: XMVECTOR = XMVector4Dot(V0, Plane);
498        let Dist1: XMVECTOR = XMVector4Dot(V1, Plane);
499        let Dist2: XMVECTOR = XMVector4Dot(V2, Plane);
500
501        let mut MinDist: XMVECTOR = XMVectorMin(Dist0, Dist1);
502        MinDist = XMVectorMin(MinDist, Dist2);
503
504        let mut MaxDist: XMVECTOR = XMVectorMax(Dist0, Dist1);
505        MaxDist = XMVectorMax(MaxDist, Dist2);
506
507        let Zero: XMVECTOR = XMVectorZero();
508
509        // Outside the plane?
510        *Outside = XMVectorGreater(MinDist, Zero);
511
512        // Fully inside the plane?
513        *Inside = XMVectorLess(MaxDist, Zero);
514    }
515
516    #[inline]
517    pub fn FastIntersectSpherePlane(
518        Center: FXMVECTOR,
519        Radius: FXMVECTOR,
520        Plane: FXMVECTOR,
521        Outside: &mut XMVECTOR,
522        Inside: &mut XMVECTOR,
523    ) {
524        let Dist: XMVECTOR = XMVector4Dot(Center, Plane);
525
526        // Outside the plane?
527        *Outside = XMVectorGreater(Dist, Radius);
528
529        // Fully inside the plane?
530        *Inside = XMVectorLess(Dist, XMVectorNegate(Radius));
531    }
532
533    #[inline]
534    pub fn FastIntersectAxisAlignedBoxPlane(
535        Center: FXMVECTOR,
536        Extents: FXMVECTOR,
537        Plane: FXMVECTOR,
538        Outside: &mut XMVECTOR,
539        Inside: &mut XMVECTOR,
540    ) {
541         // Compute the distance to the center of the box.
542         let Dist: XMVECTOR = XMVector4Dot(Center, Plane);
543
544         // Project the axes of the box onto the normal of the plane.  Half the
545         // length of the projection (sometime called the "radius") is equal to
546         // h(u) * abs(n dot b(u))) + h(v) * abs(n dot b(v)) + h(w) * abs(n dot b(w))
547         // where h(i) are extents of the box, n is the plane normal, and b(i) are the
548         // axes of the box. In this case b(i) = [(1,0,0), (0,1,0), (0,0,1)].
549         let Radius: XMVECTOR = XMVector3Dot(Extents, XMVectorAbs(Plane));
550
551         // Outside the plane?
552         *Outside = XMVectorGreater(Dist, Radius);
553
554         // Fully inside the plane?
555         *Inside = XMVectorLess(Dist, XMVectorNegate(Radius));
556    }
557
558    #[inline]
559    pub fn FastIntersectOrientedBoxPlane(
560        Center: FXMVECTOR,
561        Extents: FXMVECTOR,
562        Axis0: FXMVECTOR,
563        Axis1: GXMVECTOR,
564        Axis2: HXMVECTOR,
565        Plane: HXMVECTOR,
566        Outside: &mut XMVECTOR,
567        Inside: &mut XMVECTOR,
568    ) {
569        // Compute the distance to the center of the box.
570        let Dist: XMVECTOR = XMVector4Dot(Center, Plane);
571
572        // Project the axes of the box onto the normal of the plane.  Half the
573        // length of the projection (sometime called the "radius") is equal to
574        // h(u) * abs(n dot b(u))) + h(v) * abs(n dot b(v)) + h(w) * abs(n dot b(w))
575        // where h(i) are extents of the box, n is the plane normal, and b(i) are the
576        // axes of the box.
577        let mut Radius: XMVECTOR = XMVector3Dot(Plane, Axis0);
578        // TODO: template
579        Radius = XMVectorInsert(Radius, XMVector3Dot(Plane, Axis1), 0, 0, 1, 0, 0);
580        Radius = XMVectorInsert(Radius, XMVector3Dot(Plane, Axis2), 0, 0, 0, 1, 0);
581        Radius = XMVector3Dot(Extents, XMVectorAbs(Radius));
582
583        // Outside the plane?
584        *Outside = XMVectorGreater(Dist, Radius);
585
586        // Fully inside the plane?
587        *Inside = XMVectorLess(Dist, XMVectorNegate(Radius));
588    }
589
590    #[inline]
591    pub fn FastIntersectFrustumPlane(
592        Point0: FXMVECTOR,
593        Point1: FXMVECTOR,
594        Point2: FXMVECTOR,
595        Point3: GXMVECTOR,
596        Point4: HXMVECTOR,
597        Point5: HXMVECTOR,
598        Point6: CXMVECTOR,
599        Point7: CXMVECTOR,
600        Plane: CXMVECTOR,
601        Outside: &mut XMVECTOR,
602        Inside: &mut XMVECTOR,
603    ) {
604        let Plane = *Plane;
605        let Point6 = *Point6;
606        let Point7 = *Point7;
607
608        // Find the min/max projection of the frustum onto the plane normal.
609        let mut Min: XMVECTOR = XMVector3Dot(Plane, Point0);
610        let mut Max: XMVECTOR = Min;
611        let mut Dist: XMVECTOR;
612
613        Dist = XMVector3Dot(Plane, Point1);
614        Min = XMVectorMin(Min, Dist);
615        Max = XMVectorMax(Max, Dist);
616
617        Dist = XMVector3Dot(Plane, Point2);
618        Min = XMVectorMin(Min, Dist);
619        Max = XMVectorMax(Max, Dist);
620
621        Dist = XMVector3Dot(Plane, Point3);
622        Min = XMVectorMin(Min, Dist);
623        Max = XMVectorMax(Max, Dist);
624
625        Dist = XMVector3Dot(Plane, Point4);
626        Min = XMVectorMin(Min, Dist);
627        Max = XMVectorMax(Max, Dist);
628
629        Dist = XMVector3Dot(Plane, Point5);
630        Min = XMVectorMin(Min, Dist);
631        Max = XMVectorMax(Max, Dist);
632
633        Dist = XMVector3Dot(Plane, Point6);
634        Min = XMVectorMin(Min, Dist);
635        Max = XMVectorMax(Max, Dist);
636
637        Dist = XMVector3Dot(Plane, Point7);
638        Min = XMVectorMin(Min, Dist);
639        Max = XMVectorMax(Max, Dist);
640
641        let PlaneDist: XMVECTOR = XMVectorNegate(XMVectorSplatW(Plane));
642
643        // Outside the plane?
644        *Outside = XMVectorGreater(Min, PlaneDist);
645
646        // Fully inside the plane?
647        *Inside = XMVectorLess(Max, PlaneDist);
648    }
649}
650
651// BoundingSphere -------------------------------------------------------------
652
653impl BoundingSphere {
654    /// Transforms the BoundingSphere by the specified transformation matrix.
655    ///
656    /// ## Parameters
657    ///
658    /// `Out` The transformed BoundingSphere.
659    ///
660    /// `M` The matrix to transform the BoundingSphere by.
661    ///
662    /// ## Return value
663    ///
664    /// None
665    ///
666    /// ## Reference
667    ///
668    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-transform>
669    #[inline]
670    pub fn TransformMatrix(&self, Out: &mut Self, M: FXMMATRIX) {
671        unsafe {
672            // Load the center of the sphere.
673            let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
674
675            // Transform the center of the sphere.
676            let C: XMVECTOR = XMVector3Transform(vCenter, M);
677
678            let dX: XMVECTOR = XMVector3Dot(M.r[0], M.r[0]);
679            let dY: XMVECTOR = XMVector3Dot(M.r[1], M.r[1]);
680            let dZ: XMVECTOR = XMVector3Dot(M.r[2], M.r[2]);
681
682            let d: XMVECTOR = XMVectorMax(dX, XMVectorMax(dY, dZ));
683
684            // Store the center sphere.
685            XMStoreFloat3(&mut Out.Center, C);
686
687            // Scale the radius of the pshere.
688            let Scale: f32 = sqrtf(XMVectorGetX(d));
689            Out.Radius = self.Radius * Scale;
690        }
691    }
692
693    /// Transforms the BoundingSphere using the specified `scale`, `rotation` and `translation` vectors.
694    ///
695    /// ## Parameters
696    ///
697    /// `Out` The transformed BoundingSphere.
698    ///
699    /// `Scale` The value to scale the BoundingSphere by.
700    ///
701    /// `Rotation` The value to rotate the BoundingSphere by.
702    ///
703    /// `Translation` The value to translate the BoundingSphere by.
704    ///
705    /// ## Return value
706    ///
707    /// This method does not return a value.
708    ///
709    /// ## Reference
710    ///
711    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-transform(boundingsphere__float_fxmvector_fxmvector)>
712    #[inline]
713    pub fn TransformDecomposed(&self, Out: &mut Self, Scale: f32, Rotation: FXMVECTOR, Translation: FXMVECTOR) {
714        // Load the center of the sphere.
715        let mut vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
716
717        // Transform the center of the sphere.
718        vCenter = XMVectorAdd(XMVector3Rotate(XMVectorScale(vCenter, Scale), Rotation), Translation);
719
720        // Store the center sphere.
721        XMStoreFloat3(&mut Out.Center, vCenter);
722
723        // Scale the radius of the pshere.
724        Out.Radius = self.Radius * Scale;
725    }
726
727    /// Tests whether the BoundingSphere contains a specified point.
728    ///
729    /// ## Parameters
730    ///
731    /// `Point` The point to test against.
732    ///
733    /// ## Return value
734    ///
735    /// A ContainmentType value indicating whether the BoundingSphere contains the specified point.
736    ///
737    /// ## Reference
738    ///
739    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-contains>
740    #[inline]
741    pub fn ContainsPoint(&self, Point: FXMVECTOR) -> ContainmentType {
742        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
743        let vRadius: XMVECTOR = XMVectorReplicatePtr(&self.Radius);
744
745        let DistanceSquared: XMVECTOR = XMVector3LengthSq(XMVectorSubtract(Point, vCenter));
746        let RadiusSquared: XMVECTOR = XMVectorMultiply(vRadius, vRadius);
747
748        if XMVector3LessOrEqual(DistanceSquared, RadiusSquared) {
749            ContainmentType::CONTAINS
750        } else {
751            ContainmentType::DISJOINT
752        }
753    }
754
755    /// Tests whether the BoundingSphere contains a specified triangle.
756    ///
757    /// ## Parameters
758    ///
759    /// `V0` A corner of the triangle.
760    ///
761    /// `V1` A corner of the triangle.
762    ///
763    /// `V2` A corner of the triangle.
764    ///
765    /// ## Return value
766    ///
767    /// A ContainmentType value indicating whether the BoundingSphere contains the specified triangle.
768    ///
769    /// ## Reference
770    ///
771    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-contains(fxmvector_fxmvector_fxmvector)>
772    #[inline]
773    pub fn ContainsTriangle(&self, V0: FXMVECTOR, V1: FXMVECTOR, V2: FXMVECTOR) -> ContainmentType {
774        if (!self.IntersectsTriangle(V0, V1, V2)) {
775            return ContainmentType::DISJOINT;
776        }
777
778        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
779        let vRadius: XMVECTOR = XMVectorReplicatePtr(&self.Radius);
780        let RadiusSquared: XMVECTOR = XMVectorMultiply(vRadius, vRadius);
781
782        let mut DistanceSquared: XMVECTOR = XMVector3LengthSq(XMVectorSubtract(V0, vCenter));
783        let mut Inside: XMVECTOR = XMVectorLessOrEqual(DistanceSquared, RadiusSquared);
784
785        DistanceSquared = XMVector3LengthSq(XMVectorSubtract(V1, vCenter));
786        Inside = XMVectorAndInt(Inside, XMVectorLessOrEqual(DistanceSquared, RadiusSquared));
787
788        DistanceSquared = XMVector3LengthSq(XMVectorSubtract(V2, vCenter));
789        Inside = XMVectorAndInt(Inside, XMVectorLessOrEqual(DistanceSquared, RadiusSquared));
790
791        return if (XMVector3EqualInt(Inside, XMVectorTrueInt())) { CONTAINS } else { INTERSECTS }
792    }
793
794    /// Tests whether the BoundingSphere contains a specified BoundingSphere.
795    ///
796    /// ## Parameters
797    ///
798    /// `sh` The BoundingSphere to test against.
799    ///
800    /// ## Return value
801    ///
802    /// A ContainmentType value indicating whether the BoundingSphere contains the specified BoundingSphere.
803    ///
804    /// ## Reference
805    ///
806    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-contains(constboundingsphere_)>
807    #[inline]
808    pub fn ContainsSphere(&self, sh: &BoundingSphere) -> ContainmentType {
809        let Center1: XMVECTOR = XMLoadFloat3(&self.Center);
810        let r1: f32 = self.Radius;
811
812        let Center2: XMVECTOR = XMLoadFloat3(&sh.Center);
813        let r2: f32 = sh.Radius;
814
815        let V: XMVECTOR = XMVectorSubtract(Center2, Center1);
816
817        let Dist: XMVECTOR = XMVector3Length(V);
818
819        let d: f32 = XMVectorGetX(Dist);
820
821        return if (r1 + r2 >= d) { if (r1 - r2 >= d) { CONTAINS } else { INTERSECTS } } else { DISJOINT }
822    }
823
824    /// Tests whether the BoundingSphere contains a specified BoundingBox.
825    ///
826    /// ## Parameters
827    ///
828    /// `box` The BoundingBox to test against.
829    ///
830    /// ## Return value
831    ///
832    /// A ContainmentType value indicating whether the BoundingSphere contains the specified BoundingBox.
833    ///
834    /// ## Reference
835    ///
836    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-contains(constboundingbox_)>
837    #[inline]
838    pub fn ContainsBox(&self, box_: &BoundingBox) -> ContainmentType {
839        if (!box_.IntersectsSphere(self)) {
840            return DISJOINT;
841        }
842
843        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
844        let vRadius: XMVECTOR = XMVectorReplicatePtr(&self.Radius);
845        let RadiusSq: XMVECTOR = XMVectorMultiply(vRadius, vRadius);
846
847        let boxCenter: XMVECTOR = XMLoadFloat3(&box_.Center);
848        let boxExtents: XMVECTOR = XMLoadFloat3(&box_.Extents);
849
850        let mut InsideAll: XMVECTOR = XMVectorTrueInt();
851
852        let offset: XMVECTOR = XMVectorSubtract(boxCenter, vCenter);
853
854        for i in 0 .. BoundingBox::CORNER_COUNT {
855            let C: XMVECTOR = XMVectorMultiplyAdd(boxExtents, g_BoxOffset[i].v(), offset);
856            let d: XMVECTOR = XMVector3LengthSq(C);
857            InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(d, RadiusSq));
858        }
859
860        return if (XMVector3EqualInt(InsideAll, XMVectorTrueInt())) { CONTAINS } else { INTERSECTS };
861    }
862
863    /// Tests whether the BoundingSphere contains the specified BoundingOrientedBox.
864    ///
865    /// ## Parameters
866    ///
867    /// `box` The BoundingOrientedBox to test against.
868    ///
869    /// ## Return value
870    ///
871    /// A ContainmentType value indicating whether the BoundingOrientedBox is contained in the BoundingSphere.
872    ///
873    /// ## Reference
874    ///
875    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-contains(constboundingorientedbox_)>
876    #[inline]
877    pub fn ContainsOrientedBox(&self, box_: &BoundingOrientedBox) -> ContainmentType {
878        if (!box_.IntersectsSphere(self)) {
879            return DISJOINT;
880        }
881
882        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
883        let vRadius: XMVECTOR = XMVectorReplicatePtr(&self.Radius);
884        let RadiusSq: XMVECTOR = XMVectorMultiply(vRadius, vRadius);
885
886        let boxCenter: XMVECTOR = XMLoadFloat3(&box_.Center);
887        let boxExtents: XMVECTOR = XMLoadFloat3(&box_.Extents);
888        let boxOrientation: XMVECTOR = XMLoadFloat4(&box_.Orientation);
889
890        debug_assert!(internal::XMQuaternionIsUnit(boxOrientation));
891
892        let mut InsideAll: XMVECTOR = XMVectorTrueInt();
893
894        for i in 0 .. BoundingOrientedBox::CORNER_COUNT {
895            let C: XMVECTOR = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(boxExtents, g_BoxOffset[i].v()), boxOrientation), boxCenter);
896            let d: XMVECTOR = XMVector3LengthSq(XMVectorSubtract(vCenter, C));
897            InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(d, RadiusSq));
898        }
899
900        return if (XMVector3EqualInt(InsideAll, XMVectorTrueInt())) { CONTAINS } else { INTERSECTS };
901    }
902
903    /// Tests whether the BoundingSphere contains the specified BoundingFrustum.
904    ///
905    /// ## Parameters
906    ///
907    /// `fr` The BoundingFrustum to test against.
908    ///
909    /// ## Return value
910    ///
911    /// A ContainmentType value indicating whether the BoundingFrustum is contained in the BoundingSphere.
912    ///
913    /// ## Reference
914    ///
915    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-contains(constboundingfrustum_)>
916    #[inline]
917    pub fn ContainsFrustum(&self, fr: &BoundingFrustum) -> ContainmentType {
918        if (!fr.IntersectsSphere(self)) {
919            return DISJOINT;
920        }
921
922        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
923        let vRadius: XMVECTOR = XMVectorReplicatePtr(&self.Radius);
924        let RadiusSq: XMVECTOR = XMVectorMultiply(vRadius, vRadius);
925
926        let vOrigin: XMVECTOR = XMLoadFloat3(&fr.Origin);
927        let vOrientation: XMVECTOR = XMLoadFloat4(&fr.Orientation);
928
929        debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
930
931        // Build the corners of the frustum.
932        let vRightTop: XMVECTOR = XMVectorSet(fr.RightSlope, fr.TopSlope, 1.0, 0.0);
933        let vRightBottom: XMVECTOR = XMVectorSet(fr.RightSlope, fr.BottomSlope, 1.0, 0.0);
934        let vLeftTop: XMVECTOR = XMVectorSet(fr.LeftSlope, fr.TopSlope, 1.0, 0.0);
935        let vLeftBottom: XMVECTOR = XMVectorSet(fr.LeftSlope, fr.BottomSlope, 1.0, 0.0);
936        let vNear: XMVECTOR = XMVectorReplicatePtr(&fr.Near);
937        let vFar: XMVECTOR = XMVectorReplicatePtr(&fr.Far);
938
939        let mut Corners: [XMVECTOR; BoundingFrustum::CORNER_COUNT] = unsafe { undefined() };
940        Corners[0] = XMVectorMultiply(vRightTop, vNear);
941        Corners[1] = XMVectorMultiply(vRightBottom, vNear);
942        Corners[2] = XMVectorMultiply(vLeftTop, vNear);
943        Corners[3] = XMVectorMultiply(vLeftBottom, vNear);
944        Corners[4] = XMVectorMultiply(vRightTop, vFar);
945        Corners[5] = XMVectorMultiply(vRightBottom, vFar);
946        Corners[6] = XMVectorMultiply(vLeftTop, vFar);
947        Corners[7] = XMVectorMultiply(vLeftBottom, vFar);
948
949        let mut InsideAll: XMVECTOR = XMVectorTrueInt();
950        for i in 0..BoundingFrustum::CORNER_COUNT {
951            let C: XMVECTOR = XMVectorAdd(XMVector3Rotate(Corners[i], vOrientation), vOrigin);
952            let d: XMVECTOR = XMVector3LengthSq(XMVectorSubtract(vCenter, C));
953            InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(d, RadiusSq));
954        }
955
956        return if (XMVector3EqualInt(InsideAll, XMVectorTrueInt())) { CONTAINS } else { INTERSECTS };
957    }
958
959    /// Tests the BoundingSphere for intersection with a BoundingSphere.
960    ///
961    /// ## Parameters
962    ///
963    /// `sh` The BoundingSphere to test against.
964    ///
965    /// ## Return value
966    ///
967    /// A bool value indicating whether the BoundingSphere intersects the specified BoundingSphere.
968    ///
969    /// ## Reference
970    ///
971    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-intersects>
972    #[inline]
973    pub fn IntersectsSphere(&self, sh: &BoundingSphere) -> bool {
974        // Load A.
975        let vCenterA: XMVECTOR = XMLoadFloat3(&self.Center);
976        let vRadiusA: XMVECTOR = XMVectorReplicatePtr(&self.Radius);
977
978        // Load B.
979        let vCenterB: XMVECTOR = XMLoadFloat3(&sh.Center);
980        let vRadiusB: XMVECTOR = XMVectorReplicatePtr(&sh.Radius);
981
982        // Distance squared between centers.
983        let Delta: XMVECTOR = XMVectorSubtract(vCenterB, vCenterA);
984        let DistanceSquared: XMVECTOR = XMVector3LengthSq(Delta);
985
986        // Sum of the radii squared.
987        let mut RadiusSquared: XMVECTOR = XMVectorAdd(vRadiusA, vRadiusB);
988        RadiusSquared = XMVectorMultiply(RadiusSquared, RadiusSquared);
989
990        return XMVector3LessOrEqual(DistanceSquared, RadiusSquared);
991    }
992
993    /// Tests the BoundingSphere for intersection with a BoundingBox.
994    ///
995    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-intersects(constboundingbox_)>
996    #[inline]
997    pub fn IntersectsBox(&self, box_: &BoundingBox) -> bool {
998        return box_.IntersectsSphere(self);
999    }
1000
1001    /// Test the BoundingSphere for intersection with a BoundingOrientedBox.
1002    ///
1003    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-intersects(constboundingorientedbox_)>
1004    #[inline]
1005    pub fn IntersectsOrientedBox(&self, box_: &BoundingOrientedBox) -> bool {
1006        return box_.IntersectsSphere(self);
1007    }
1008
1009    /// Test the BoundingSphere for intersection with a BoundingFrustum.
1010    ///
1011    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-intersects(constboundingfrustum_)>
1012    #[inline]
1013    pub fn IntersectsFrustum(&self, fr: &BoundingFrustum) -> bool {
1014        return fr.IntersectsSphere(self);
1015    }
1016
1017    /// Tests the BoundingSphere for intersection with a triangle.
1018    ///
1019    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-intersects(fxmvector_fxmvector_fxmvector)>
1020    #[inline]
1021    pub fn IntersectsTriangle(&self, V0: FXMVECTOR, V1: FXMVECTOR, V2: FXMVECTOR) -> bool {
1022        // Load the sphere.
1023        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1024        let vRadius: XMVECTOR = XMVectorReplicatePtr(&self.Radius);
1025
1026        // Compute the plane of the triangle (has to be normalized).
1027        let N: XMVECTOR = XMVector3Normalize(XMVector3Cross(XMVectorSubtract(V1, V0), XMVectorSubtract(V2, V0)));
1028
1029        // Assert that the triangle is not degenerate.
1030        debug_assert!(!XMVector3Equal(N, XMVectorZero()));
1031
1032        // Find the nearest feature on the triangle to the sphere.
1033        let Dist: XMVECTOR = XMVector3Dot(XMVectorSubtract(vCenter, V0), N);
1034
1035        // If the center of the sphere is farther from the plane of the triangle than
1036        // the radius of the sphere, then there cannot be an intersection.
1037        let mut NoIntersection: XMVECTOR = XMVectorLess(Dist, XMVectorNegate(vRadius));
1038        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Dist, vRadius));
1039
1040        // Project the center of the sphere onto the plane of the triangle.
1041        let mut Point: XMVECTOR = XMVectorNegativeMultiplySubtract(N, Dist, vCenter);
1042
1043        // Is it inside all the edges? If so we intersect because the distance
1044        // to the plane is less than the radius.
1045        let mut Intersection: XMVECTOR = internal::PointOnPlaneInsideTriangle(Point, V0, V1, V2);
1046
1047        // Find the nearest point on each edge.
1048        let RadiusSq: XMVECTOR = XMVectorMultiply(vRadius, vRadius);
1049
1050        // Edge 0,1
1051        Point = internal::PointOnLineSegmentNearestPoint(V0, V1, vCenter);
1052
1053        // If the distance to the center of the sphere to the point is less than
1054        // the radius of the sphere then it must intersect.
1055        Intersection = XMVectorOrInt(Intersection, XMVectorLessOrEqual(XMVector3LengthSq(XMVectorSubtract(vCenter, Point)), RadiusSq));
1056
1057        // Edge 1,2
1058        Point = internal::PointOnLineSegmentNearestPoint(V1, V2, vCenter);
1059
1060        // If the distance to the center of the sphere to the point is less than
1061        // the radius of the sphere then it must intersect.
1062        Intersection = XMVectorOrInt(Intersection, XMVectorLessOrEqual(XMVector3LengthSq(XMVectorSubtract(vCenter, Point)), RadiusSq));
1063
1064        // Edge 2,0
1065        Point = internal::PointOnLineSegmentNearestPoint(V2, V0, vCenter);
1066
1067        // If the distance to the center of the sphere to the point is less than
1068        // the radius of the sphere then it must intersect.
1069        Intersection = XMVectorOrInt(Intersection, XMVectorLessOrEqual(XMVector3LengthSq(XMVectorSubtract(vCenter, Point)), RadiusSq));
1070
1071        return XMVector4EqualInt(XMVectorAndCInt(Intersection, NoIntersection), XMVectorTrueInt());
1072    }
1073
1074    /// Tests the BoundingSphere for intersection with a Plane.
1075    ///
1076    /// ## Parameters
1077    ///
1078    /// `Plane` A vector describing the plane coefficients (`A`, `B`, `C`, `D`) for the plane equation `Ax+By+Cz+D=0`.
1079    ///
1080    /// ## Return value
1081    ///
1082    /// A PlaneIntersectionType value indicating whether the BoundingSphere intersects the specified plane.
1083    ///
1084    /// ## Reference
1085    ///
1086    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-intersects(fxmvector)>
1087    #[inline]
1088    pub fn IntersectsPlane(&self, Plane: FXMVECTOR) -> PlaneIntersectionType {
1089        debug_assert!(internal::XMPlaneIsUnit(Plane));
1090
1091        // Load the sphere.
1092        let mut vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1093        let vRadius: XMVECTOR = XMVectorReplicatePtr(&self.Radius);
1094
1095        // Set w of the center to one so we can dot4 with a plane.
1096        // TODO: template
1097        vCenter = XMVectorInsert(vCenter, XMVectorSplatOne(), 0, 0, 0, 0, 1);
1098
1099        let mut Outside: XMVECTOR = unsafe { undefined() };
1100        let mut Inside: XMVECTOR = unsafe { undefined() };
1101        internal::FastIntersectSpherePlane(vCenter, vRadius, Plane, &mut Outside, &mut Inside);
1102
1103        // If the sphere is outside any plane it is outside.
1104        if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
1105            return FRONT;
1106        }
1107
1108        // If the sphere is inside all planes it is inside.
1109        if (XMVector4EqualInt(Inside, XMVectorTrueInt())) {
1110            return BACK;
1111        }
1112
1113        // The sphere is not inside all planes or outside a plane it intersects.
1114        return INTERSECTING;
1115    }
1116
1117    /// Tests the BoundingSphere for intersection with a ray.
1118    ///
1119    /// ## Parameters
1120    ///
1121    /// `Origin` The origin of the ray.
1122    ///
1123    /// `Direction` The direction of the ray.
1124    ///
1125    /// `Dist` The length of the ray.
1126    ///
1127    /// ## Return value
1128    ///
1129    /// A bool value indicating whether the BoundingSphere contains the specified ray.
1130    ///
1131    /// ## Remarks
1132    ///
1133    /// The distance from the `Origin` to the nearest intersection point is returned
1134    /// in `Dist` when the method returns `true`. Otherwise, `Dist` is set to `0.0`.
1135    ///
1136    /// ## Reference
1137    ///
1138    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-intersects(fxmvector_fxmvector_float_)>
1139    #[inline]
1140    pub fn IntersectsRay(&self, Origin: FXMVECTOR, Direction: FXMVECTOR, Dist: &mut f32) -> bool {
1141        debug_assert!(internal::XMVector3IsUnit(Direction));
1142
1143        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1144        let vRadius: XMVECTOR = XMVectorReplicatePtr(&self.Radius);
1145
1146        // l is the vector from the ray origin to the center of the sphere.
1147        let l: XMVECTOR = XMVectorSubtract(vCenter, Origin);
1148
1149        // s is the projection of the l onto the ray direction.
1150        let s: XMVECTOR = XMVector3Dot(l, Direction);
1151
1152        let l2: XMVECTOR = XMVector3Dot(l, l);
1153
1154        let r2: XMVECTOR = XMVectorMultiply(vRadius, vRadius);
1155
1156        // m2 is squared distance from the center of the sphere to the projection.
1157        let m2: XMVECTOR = XMVectorNegativeMultiplySubtract(s, s, l2);
1158
1159        let mut NoIntersection: XMVECTOR;
1160
1161        // If the ray origin is outside the sphere and the center of the sphere is
1162        // behind the ray origin there is no intersection.
1163        NoIntersection = XMVectorAndInt(XMVectorLess(s, XMVectorZero()), XMVectorGreater(l2, r2));
1164
1165        // If the squared distance from the center of the sphere to the projection
1166        // is greater than the radius squared the ray will miss the sphere.
1167        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(m2, r2));
1168
1169        // The ray hits the sphere, compute the nearest intersection point.
1170        let q: XMVECTOR = XMVectorSqrt(XMVectorSubtract(r2, m2));
1171        let t1: XMVECTOR = XMVectorSubtract(s, q);
1172        let t2: XMVECTOR = XMVectorAdd(s, q);
1173
1174        let OriginInside: XMVECTOR = XMVectorLessOrEqual(l2, r2);
1175        let t: XMVECTOR = XMVectorSelect(t1, t2, OriginInside);
1176
1177        if (XMVector4NotEqualInt(NoIntersection, XMVectorTrueInt()))
1178        {
1179            // Store the x-component to *pDist.
1180            XMStoreFloat(Dist, t);
1181            return true;
1182        }
1183
1184        *Dist = 0.0;
1185        return false;
1186    }
1187
1188    /// Tests whether the BoundingSphere is contained by the specified frustum.
1189    ///
1190    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-containedby>
1191    #[inline]
1192    pub fn ContainedBy(
1193        &self,
1194        Plane0: FXMVECTOR,
1195        Plane1: FXMVECTOR,
1196        Plane2: GXMVECTOR,
1197        Plane3: HXMVECTOR,
1198        Plane4: HXMVECTOR,
1199        Plane5: HXMVECTOR,
1200    ) -> ContainmentType
1201    {
1202        // Load the sphere.
1203        let mut vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1204        let vRadius: XMVECTOR = XMVectorReplicatePtr(&self.Radius);
1205
1206        // Set w of the center to one so we can dot4 with a plane.
1207        // TODO: template
1208        vCenter = XMVectorInsert(vCenter, XMVectorSplatOne(), 0, 0, 0, 0, 1);
1209
1210        let mut Outside: XMVECTOR = unsafe { undefined() };
1211        let mut Inside: XMVECTOR = unsafe { undefined() };
1212
1213        // Test against each plane.
1214        internal::FastIntersectSpherePlane(vCenter, vRadius, Plane0, &mut Outside, &mut Inside);
1215
1216        let mut AnyOutside: XMVECTOR = Outside;
1217        let mut AllInside: XMVECTOR = Inside;
1218
1219        internal::FastIntersectSpherePlane(vCenter, vRadius, Plane1, &mut Outside, &mut Inside);
1220        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
1221        AllInside = XMVectorAndInt(AllInside, Inside);
1222
1223        internal::FastIntersectSpherePlane(vCenter, vRadius, Plane2, &mut Outside, &mut Inside);
1224        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
1225        AllInside = XMVectorAndInt(AllInside, Inside);
1226
1227        internal::FastIntersectSpherePlane(vCenter, vRadius, Plane3, &mut Outside, &mut Inside);
1228        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
1229        AllInside = XMVectorAndInt(AllInside, Inside);
1230
1231        internal::FastIntersectSpherePlane(vCenter, vRadius, Plane4, &mut Outside, &mut Inside);
1232        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
1233        AllInside = XMVectorAndInt(AllInside, Inside);
1234
1235        internal::FastIntersectSpherePlane(vCenter, vRadius, Plane5, &mut Outside, &mut Inside);
1236        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
1237        AllInside = XMVectorAndInt(AllInside, Inside);
1238
1239        // If the sphere is outside any plane it is outside.
1240        if (XMVector4EqualInt(AnyOutside, XMVectorTrueInt())) {
1241            return DISJOINT;
1242        }
1243
1244        // If the sphere is inside all planes it is inside.
1245        if (XMVector4EqualInt(AllInside, XMVectorTrueInt())) {
1246            return CONTAINS;
1247        }
1248
1249        // The sphere is not inside all planes or outside a plane, it may intersect.
1250        return INTERSECTS;
1251    }
1252
1253    /// Creates a BoundingSphere that contains the two specified BoundingSphere objects.
1254    ///
1255    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-createmerged>
1256    #[inline]
1257    pub fn CreateMerged(Out: &mut Self, S1: &Self, S2: &Self) {
1258        let Center1: XMVECTOR = XMLoadFloat3(&S1.Center);
1259        let r1: f32 = S1.Radius;
1260
1261        let Center2: XMVECTOR = XMLoadFloat3(&S2.Center);
1262        let r2: f32 = S2.Radius;
1263
1264        let V: XMVECTOR = XMVectorSubtract(Center2, Center1);
1265
1266        let Dist: XMVECTOR = XMVector3Length(V);
1267
1268        let d: f32 = XMVectorGetX(Dist);
1269
1270        if (r1 + r2 >= d)
1271        {
1272            if (r1 - r2 >= d)
1273            {
1274                *Out = *S1;
1275                return;
1276            }
1277            else if (r2 - r1 >= d)
1278            {
1279                *Out = *S2;
1280                return;
1281            }
1282        }
1283
1284        let N: XMVECTOR = XMVectorDivide(V, Dist);
1285
1286        let t1: f32 = XMMin(-r1, d - r2);
1287        let t2: f32 = XMMax(r1, d + r2);
1288        let t_5: f32 = (t2 - t1) * 0.5;
1289
1290        let NCenter: XMVECTOR = XMVectorAdd(Center1, XMVectorMultiply(N, XMVectorReplicate(t_5 + t1)));
1291
1292        XMStoreFloat3(&mut Out.Center, NCenter);
1293        Out.Radius = t_5;
1294    }
1295
1296    /// Creates a BoundingSphere containing the specified BoundingBox.
1297    ///
1298    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-createfromboundingbox>
1299    #[inline]
1300    pub fn CreateFromBoundingBox(Out: &mut Self, box_: &BoundingBox) {
1301        Out.Center = box_.Center;
1302        let vExtents: XMVECTOR = XMLoadFloat3(&box_.Extents);
1303        Out.Radius = XMVectorGetX(XMVector3Length(vExtents));
1304    }
1305
1306    /// Creates a BoundingSphere containing the specified BoundingOrientedBox.
1307    ///
1308    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-createfromboundingbox(boundingsphere__constboundingorientedbox_)>
1309    #[inline]
1310    pub fn CreateFromBoundingOrientedBox(Out: &mut Self, box_: &BoundingOrientedBox) {
1311        // Bounding box orientation is irrelevant because a sphere is rotationally invariant
1312        Out.Center = box_.Center;
1313        let vExtents: XMVECTOR = XMLoadFloat3(&box_.Extents);
1314        Out.Radius = XMVectorGetX(XMVector3Length(vExtents));
1315    }
1316
1317    /// Creates a new BoundingSphere from a list of points.
1318    ///
1319    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingsphere-createfrompoints>
1320    #[inline]
1321    pub fn CreateFromPoints<'a>(Out: &mut Self, pPoints: impl Iterator<Item=&'a XMFLOAT3> + Clone) {
1322        // assert(Count > 0);
1323        // assert(pPoints);
1324
1325        // Find the points with minimum and maximum x, y, and z
1326        // XMVECTOR MinX, MaxX, MinY, MaxY, MinZ, MaxZ;
1327
1328        // MinX = MaxX = MinY = MaxY = MinZ = MaxZ = XMLoadFloat3(pPoints);
1329
1330        let mut MinX = g_XMInfinity.v();
1331        let mut MaxX = g_XMNegInfinity.v();
1332        let mut MinY = g_XMInfinity.v();
1333        let mut MaxY = g_XMNegInfinity.v();
1334        let mut MinZ = g_XMInfinity.v();
1335        let mut MaxZ = g_XMNegInfinity.v();
1336
1337        // NOTE: We clone the iterator because it's reused down below.
1338        for (i, pPoint) in pPoints.clone().enumerate()
1339        {
1340            // XMVECTOR Point = XMLoadFloat3(reinterpret_cast<const XMFLOAT3*>(reinterpret_cast<const uint8_t*>(pPoints) + i * Stride));
1341            let Point = XMLoadFloat3(pPoint);
1342
1343            if i == 0 {
1344                MinX = Point;
1345                MaxX = Point;
1346                MinY = Point;
1347                MaxY = Point;
1348                MinZ = Point;
1349                MaxZ = Point;
1350            }
1351
1352            let px: f32 = XMVectorGetX(Point);
1353            let py: f32 = XMVectorGetY(Point);
1354            let pz: f32 = XMVectorGetZ(Point);
1355
1356            if (px < XMVectorGetX(MinX)) {
1357                MinX = Point;
1358            }
1359
1360            if (px > XMVectorGetX(MaxX)) {
1361                MaxX = Point;
1362            }
1363
1364            if (py < XMVectorGetY(MinY)) {
1365                MinY = Point;
1366            }
1367
1368            if (py > XMVectorGetY(MaxY)) {
1369                MaxY = Point;
1370            }
1371
1372            if (pz < XMVectorGetZ(MinZ)) {
1373                MinZ = Point;
1374            }
1375
1376            if (pz > XMVectorGetZ(MaxZ)) {
1377                MaxZ = Point;
1378            }
1379        }
1380
1381        // Use the min/max pair that are farthest apart to form the initial sphere.
1382        let DeltaX: XMVECTOR = XMVectorSubtract(MaxX, MinX);
1383        let DistX: XMVECTOR = XMVector3Length(DeltaX);
1384
1385        let DeltaY: XMVECTOR = XMVectorSubtract(MaxY, MinY);
1386        let DistY: XMVECTOR = XMVector3Length(DeltaY);
1387
1388        let DeltaZ: XMVECTOR = XMVectorSubtract(MaxZ, MinZ);
1389        let DistZ: XMVECTOR = XMVector3Length(DeltaZ);
1390
1391        let mut vCenter: XMVECTOR;
1392        let mut vRadius: XMVECTOR;
1393
1394        if (XMVector3Greater(DistX, DistY))
1395        {
1396            if (XMVector3Greater(DistX, DistZ))
1397            {
1398                // Use min/max x.
1399                vCenter = XMVectorLerp(MaxX, MinX, 0.5);
1400                vRadius = XMVectorScale(DistX, 0.5);
1401            }
1402            else
1403            {
1404                // Use min/max z.
1405                vCenter = XMVectorLerp(MaxZ, MinZ, 0.5);
1406                vRadius = XMVectorScale(DistZ, 0.5);
1407            }
1408        }
1409        else // Y >= X
1410        {
1411            if (XMVector3Greater(DistY, DistZ))
1412            {
1413                // Use min/max y.
1414                vCenter = XMVectorLerp(MaxY, MinY, 0.5);
1415                vRadius = XMVectorScale(DistY, 0.5);
1416            }
1417            else
1418            {
1419                // Use min/max z.
1420                vCenter = XMVectorLerp(MaxZ, MinZ, 0.5);
1421                vRadius = XMVectorScale(DistZ, 0.5);
1422            }
1423        }
1424
1425        // Add any points not inside the sphere.
1426        for pPoint in pPoints
1427        {
1428            let Point: XMVECTOR = XMLoadFloat3(pPoint);
1429
1430            let Delta: XMVECTOR = XMVectorSubtract(Point, vCenter);
1431
1432            let Dist: XMVECTOR = XMVector3Length(Delta);
1433
1434            if (XMVector3Greater(Dist, vRadius))
1435            {
1436                // Adjust sphere to include the new point.
1437                vRadius = XMVectorScale(XMVectorAdd(vRadius, Dist), 0.5);
1438                vCenter = XMVectorAdd(vCenter, XMVectorMultiply(XMVectorSubtract(XMVectorReplicate(1.0), XMVectorDivide(vRadius, Dist)), Delta));
1439            }
1440        }
1441
1442        XMStoreFloat3(&mut Out.Center, vCenter);
1443        XMStoreFloat(&mut Out.Radius, vRadius);
1444    }
1445
1446    /// Creates a BoundingSphere containing the specified BoundingFrustum.
1447    #[inline]
1448    pub fn CreateFromFrustum(Out: &mut Self, fr: &BoundingFrustum) {
1449        let mut Corners: [XMFLOAT3; BoundingFrustum::CORNER_COUNT] = unsafe { undefined() };
1450        fr.GetCorners(&mut Corners);
1451        BoundingSphere::CreateFromPoints(Out, Corners.iter())
1452    }
1453}
1454
1455#[test]
1456fn test_BoundingSphere_CreateFromPoints() {
1457    let mut bounds: BoundingSphere = BoundingSphere::default();
1458    let points = &[
1459        XMFLOAT3 { x:  1.0, y: 0.0, z:  1.0 },
1460        XMFLOAT3 { x: -1.0, y: 0.0, z: -1.0 },
1461    ];
1462    BoundingSphere::CreateFromPoints(&mut bounds, points.iter());
1463
1464    assert_eq!(0.0, bounds.Center.x);
1465    assert_eq!(0.0, bounds.Center.y);
1466    assert_eq!(0.0, bounds.Center.z);
1467    assert_eq!(2.0f32.sqrt(), bounds.Radius);
1468
1469    assert_eq!(ContainmentType::CONTAINS, bounds.ContainsPoint(XMVectorSet(0.5, 0.5, 0.5, 0.0)));
1470    assert_eq!(ContainmentType::DISJOINT, bounds.ContainsPoint(XMVectorSet(3.0, 3.0, 3.0, 0.0)));
1471
1472    let points_f32x3: &[[f32; 3]] = &[
1473        [ 1.0, 0.0,  1.0 ],
1474        [-1.0, 0.0, -1.0 ],
1475    ];
1476    BoundingSphere::CreateFromPoints(&mut bounds, points_f32x3.iter().map(Into::into));
1477
1478    assert_eq!(0.0, bounds.Center.x);
1479    assert_eq!(0.0, bounds.Center.y);
1480    assert_eq!(0.0, bounds.Center.z);
1481    assert_eq!(2.0f32.sqrt(), bounds.Radius);
1482
1483    assert_eq!(ContainmentType::CONTAINS, bounds.ContainsPoint(XMVectorSet(0.5, 0.5, 0.5, 0.0)));
1484    assert_eq!(ContainmentType::DISJOINT, bounds.ContainsPoint(XMVectorSet(3.0, 3.0, 3.0, 0.0)));
1485
1486    let mut bounds: BoundingSphere = BoundingSphere::default();
1487    let points = &[];
1488    BoundingSphere::CreateFromPoints(&mut bounds, points.iter());
1489
1490    // NOTE: The DirectXMath source asserts points.len > 0
1491    assert!(bounds.Center.x.is_nan());
1492    assert!(bounds.Center.y.is_nan());
1493    assert!(bounds.Center.z.is_nan());
1494    assert!(bounds.Radius.is_infinite());
1495
1496    assert_eq!(ContainmentType::DISJOINT, bounds.ContainsPoint(XMVectorSet(0.0, 0.0, 0.0, 0.0)));
1497}
1498
1499// BoundingBox ----------------------------------------------------------------
1500
1501impl BoundingBox {
1502    pub const CORNER_COUNT: usize = 8;
1503
1504    #[inline]
1505    pub fn GetCorners(&self, Corners: &mut [XMFLOAT3; BoundingBox::CORNER_COUNT]) {
1506        // Load the box
1507        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1508        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
1509
1510        for i in 0..BoundingBox::CORNER_COUNT
1511        {
1512            let C: XMVECTOR = XMVectorMultiplyAdd(vExtents, g_BoxOffset[i].v(), vCenter);
1513            XMStoreFloat3(&mut Corners[i], C);
1514        }
1515    }
1516
1517    /// Transforms the BoundingBox by the specified transformation matrix..
1518    ///
1519    /// ## Parameters
1520    ///
1521    /// `Out` The transformed BoundingBox.
1522    ///
1523    /// `M` The matrix to transform the BoundingBox by.
1524    ///
1525    /// ## Return value
1526    ///
1527    /// None
1528    ///
1529    /// ## Reference
1530    ///
1531    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-transform>
1532    #[inline]
1533    pub fn TransformMatrix(&self, Out: &mut Self, M: FXMMATRIX) {
1534        // Load center and extents.
1535        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1536        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
1537
1538        // Compute and transform the corners and find new min/max bounds.
1539        let mut Corner: XMVECTOR = XMVectorMultiplyAdd(vExtents, g_BoxOffset[0].v(), vCenter);
1540        Corner = XMVector3Transform(Corner, M);
1541
1542        let mut Min: XMVECTOR = Corner;
1543        let mut Max: XMVECTOR = Corner;
1544
1545        for i in 1 .. BoundingBox::CORNER_COUNT
1546        {
1547            Corner = XMVectorMultiplyAdd(vExtents, g_BoxOffset[i].v(), vCenter);
1548            Corner = XMVector3Transform(Corner, M);
1549
1550            Min = XMVectorMin(Min, Corner);
1551            Max = XMVectorMax(Max, Corner);
1552        }
1553
1554        // Store center and extents.
1555        XMStoreFloat3(&mut Out.Center, XMVectorScale(XMVectorAdd(Min, Max), 0.5));
1556        XMStoreFloat3(&mut Out.Extents, XMVectorScale(XMVectorSubtract(Max, Min), 0.5));
1557    }
1558
1559    /// Transforms the BoundingBox using the specified `scale`, `rotation` and `translation` vectors.
1560    ///
1561    /// ## Parameters
1562    ///
1563    /// `Out` The transformed BoundingBox.
1564    ///
1565    /// `Scale` The value to scale the BoundingBox by.
1566    ///
1567    /// `Rotation` The value to rotate the BoundingBox by.
1568    ///
1569    /// `Translation` The value to translate the BoundingBox by.
1570    ///
1571    /// ## Return value
1572    ///
1573    /// This method does not return a value.
1574    ///
1575    /// ## Reference
1576    ///
1577    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-transform(BoundingBox__float_fxmvector_fxmvector)>
1578    #[inline]
1579    pub fn TransformDecomposed(&self, Out: &mut Self, Scale: f32, Rotation: FXMVECTOR, Translation: FXMVECTOR) {
1580        debug_assert!(internal::XMQuaternionIsUnit(Rotation));
1581
1582        // Load center and extents.
1583        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1584        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
1585
1586        let VectorScale: XMVECTOR = XMVectorReplicate(Scale);
1587
1588        // Compute and transform the corners and find new min/max bounds.
1589        let mut Corner: XMVECTOR = XMVectorMultiplyAdd(vExtents, g_BoxOffset[0].v(), vCenter);
1590        Corner = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(Corner, VectorScale), Rotation), Translation);
1591
1592        let mut Min: XMVECTOR = Corner;
1593        let mut Max: XMVECTOR = Corner;
1594
1595        for i in 1 .. BoundingBox::CORNER_COUNT
1596        {
1597            Corner = XMVectorMultiplyAdd(vExtents, g_BoxOffset[i].v(), vCenter);
1598            Corner = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(Corner, VectorScale), Rotation), Translation);
1599
1600            Min = XMVectorMin(Min, Corner);
1601            Max = XMVectorMax(Max, Corner);
1602        }
1603
1604        // Store center and extents.
1605        XMStoreFloat3(&mut Out.Center, XMVectorScale(XMVectorAdd(Min, Max), 0.5));
1606        XMStoreFloat3(&mut Out.Extents, XMVectorScale(XMVectorSubtract(Max, Min), 0.5));
1607    }
1608
1609    /// Tests the whether the BoundingBox contains a specified point.
1610    ///
1611    /// ## Parameters
1612    ///
1613    /// `Point` The point to test against.
1614    ///
1615    /// ## Return value
1616    ///
1617    /// A ContainmentType value indicating whether the point is contained in the BoundingBox.
1618    ///
1619    /// ## Reference
1620    ///
1621    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-contains>
1622    #[inline]
1623    pub fn ContainsPoint(&self, Point: FXMVECTOR) -> ContainmentType {
1624        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1625        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
1626
1627        return if XMVector3InBounds(XMVectorSubtract(Point, vCenter), vExtents) { CONTAINS } else { DISJOINT };
1628    }
1629
1630    /// Test whether the BoundingBox contains a specified triangle.
1631    ///
1632    /// ## Parameters
1633    ///
1634    /// `V0` A corner of the triangle.
1635    ///
1636    /// `V1` A corner of the triangle.
1637    ///
1638    /// `V2` A corner of the triangle.
1639    ///
1640    /// ## Return value
1641    ///
1642    /// A ContainmentType value indicating whether the BoundingBox contains the specified triangle.
1643    ///
1644    /// ## Reference
1645    ///
1646    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-contains(fxmvector_fxmvector_fxmvector)>
1647    #[inline]
1648    pub fn ContainsTriangle(&self, V0: FXMVECTOR, V1: FXMVECTOR, V2: FXMVECTOR) -> ContainmentType {
1649        if (!self.IntersectsTriangle(V0, V1, V2)) {
1650            return DISJOINT;
1651        }
1652
1653        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1654        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
1655
1656        let mut d: XMVECTOR = XMVectorAbs(XMVectorSubtract(V0, vCenter));
1657        let mut Inside: XMVECTOR = XMVectorLessOrEqual(d, vExtents);
1658
1659        d = XMVectorAbs(XMVectorSubtract(V1, vCenter));
1660        Inside = XMVectorAndInt(Inside, XMVectorLessOrEqual(d, vExtents));
1661
1662        d = XMVectorAbs(XMVectorSubtract(V2, vCenter));
1663        Inside = XMVectorAndInt(Inside, XMVectorLessOrEqual(d, vExtents));
1664
1665        return if (XMVector3EqualInt(Inside, XMVectorTrueInt())) { CONTAINS } else { INTERSECTS };
1666    }
1667
1668    /// Tests whether the BoundingBox contains a specified BoundingSphere.
1669    ///
1670    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-contains(constboundingsphere_)>
1671    #[inline]
1672    pub fn ContainsSphere(&self, sh: &BoundingSphere) -> ContainmentType {
1673        let SphereCenter: XMVECTOR = XMLoadFloat3(&sh.Center);
1674        let SphereRadius: XMVECTOR = XMVectorReplicatePtr(&sh.Radius);
1675
1676        let BoxCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1677        let BoxExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
1678
1679        let BoxMin: XMVECTOR = XMVectorSubtract(BoxCenter, BoxExtents);
1680        let BoxMax: XMVECTOR = XMVectorAdd(BoxCenter, BoxExtents);
1681
1682        // Find the distance to the nearest point on the box.
1683        // for each i in (x, y, z)
1684        // if (SphereCenter(i) < BoxMin(i)) d2 += (SphereCenter(i) - BoxMin(i)) ^ 2
1685        // else if (SphereCenter(i) > BoxMax(i)) d2 += (SphereCenter(i) - BoxMax(i)) ^ 2
1686
1687        let mut d: XMVECTOR = XMVectorZero();
1688
1689        // Compute d for each dimension.
1690        let LessThanMin: XMVECTOR = XMVectorLess(SphereCenter, BoxMin);
1691        let GreaterThanMax: XMVECTOR = XMVectorGreater(SphereCenter, BoxMax);
1692
1693        let MinDelta: XMVECTOR = XMVectorSubtract(SphereCenter, BoxMin);
1694        let MaxDelta: XMVECTOR = XMVectorSubtract(SphereCenter, BoxMax);
1695
1696        // Choose value for each dimension based on the comparison.
1697        d = XMVectorSelect(d, MinDelta, LessThanMin);
1698        d = XMVectorSelect(d, MaxDelta, GreaterThanMax);
1699
1700        // Use a dot-product to square them and sum them together.
1701        let d2: XMVECTOR = XMVector3Dot(d, d);
1702
1703        if (XMVector3Greater(d2, XMVectorMultiply(SphereRadius, SphereRadius))) {
1704            return DISJOINT;
1705        }
1706
1707        let mut InsideAll: XMVECTOR = XMVectorLessOrEqual(XMVectorAdd(BoxMin, SphereRadius), SphereCenter);
1708        InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(SphereCenter, XMVectorSubtract(BoxMax, SphereRadius)));
1709        InsideAll = XMVectorAndInt(InsideAll, XMVectorGreater(XMVectorSubtract(BoxMax, BoxMin), SphereRadius));
1710
1711        return if (XMVector3EqualInt(InsideAll, XMVectorTrueInt())) { CONTAINS } else { INTERSECTS };
1712    }
1713
1714    /// Tests whether the BoundingBox contains a specified BoundingBox.
1715    ///
1716    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-contains(constboundingbox_)>
1717    #[inline]
1718    pub fn ContainsBox(&self, box_: &BoundingBox) -> ContainmentType {
1719        let CenterA: XMVECTOR = XMLoadFloat3(&self.Center);
1720        let ExtentsA: XMVECTOR = XMLoadFloat3(&self.Extents);
1721
1722        let CenterB: XMVECTOR = XMLoadFloat3(&box_.Center);
1723        let ExtentsB: XMVECTOR = XMLoadFloat3(&box_.Extents);
1724
1725        let MinA: XMVECTOR = XMVectorSubtract(CenterA, ExtentsA);
1726        let MaxA: XMVECTOR = XMVectorAdd(CenterA, ExtentsA);
1727
1728        let MinB: XMVECTOR = XMVectorSubtract(CenterB, ExtentsB);
1729        let MaxB: XMVECTOR = XMVectorAdd(CenterB, ExtentsB);
1730
1731        // for each i in (x, y, z) if a_min(i) > b_max(i) or b_min(i) > a_max(i) then return false
1732        let Disjoint: XMVECTOR = XMVectorOrInt(XMVectorGreater(MinA, MaxB), XMVectorGreater(MinB, MaxA));
1733
1734        if (internal::XMVector3AnyTrue(Disjoint)) {
1735            return DISJOINT;
1736        }
1737
1738        // for each i in (x, y, z) if a_min(i) <= b_min(i) and b_max(i) <= a_max(i) then A contains B
1739        let Inside: XMVECTOR = XMVectorAndInt(XMVectorLessOrEqual(MinA, MinB), XMVectorLessOrEqual(MaxB, MaxA));
1740
1741        return if internal::XMVector3AllTrue(Inside) { CONTAINS } else { INTERSECTS };
1742    }
1743
1744    /// Tests whether the BoundingBox contains the specified BoundingOrientedBox.
1745    ///
1746    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-contains(constboundingorientedbox_)>
1747    #[inline]
1748    pub fn ContainsOrientedBox(&self, box_: &BoundingOrientedBox) -> ContainmentType {
1749        if (!box_.IntersectsBox(self)) {
1750            return DISJOINT;
1751        }
1752
1753        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1754        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
1755
1756        // Subtract off the AABB center to remove a subtract below
1757        let oCenter: XMVECTOR = XMVectorSubtract(XMLoadFloat3(&box_.Center), vCenter);
1758
1759        let oExtents: XMVECTOR = XMLoadFloat3(&box_.Extents);
1760        let oOrientation: XMVECTOR = XMLoadFloat4(&box_.Orientation);
1761
1762        debug_assert!(internal::XMQuaternionIsUnit(oOrientation));
1763
1764        let mut Inside: XMVECTOR = XMVectorTrueInt();
1765
1766        //for (size_t i = 0; i < BoundingOrientedBox::CORNER_COUNT; ++i)
1767        for i in 0 .. BoundingOrientedBox::CORNER_COUNT
1768        {
1769            let C: XMVECTOR = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(oExtents, g_BoxOffset[i].v()), oOrientation), oCenter);
1770            let d: XMVECTOR = XMVectorAbs(C);
1771            Inside = XMVectorAndInt(Inside, XMVectorLessOrEqual(d, vExtents));
1772        }
1773
1774        return if (XMVector3EqualInt(Inside, XMVectorTrueInt())) { CONTAINS } else { INTERSECTS };
1775    }
1776
1777    /// Tests whether the BoundingBox contains the specified BoundingFrustum.
1778    ///
1779    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-contains(constboundingfrustum_)>
1780    #[inline]
1781    pub fn ContainsFrustum(&self, fr: &BoundingFrustum) -> ContainmentType {
1782        if (!fr.IntersectsBox(self)) {
1783            return DISJOINT;
1784        }
1785
1786        let mut Corners: [XMFLOAT3; BoundingFrustum::CORNER_COUNT] = unsafe { undefined() };
1787        fr.GetCorners(&mut Corners);
1788
1789        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1790        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
1791
1792        let mut Inside: XMVECTOR = XMVectorTrueInt();
1793
1794        for i in 0 ..BoundingFrustum::CORNER_COUNT
1795        {
1796            let Point: XMVECTOR = XMLoadFloat3(&Corners[i]);
1797            let d: XMVECTOR = XMVectorAbs(XMVectorSubtract(Point, vCenter));
1798            Inside = XMVectorAndInt(Inside, XMVectorLessOrEqual(d, vExtents));
1799        }
1800
1801        return if (XMVector3EqualInt(Inside, XMVectorTrueInt())) { CONTAINS } else { INTERSECTS };
1802    }
1803
1804    /// Tests the BoundingBox for intersection with a BoundingSphere.
1805    ///
1806    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-intersects>
1807    #[inline]
1808    pub fn IntersectsSphere(&self, sh: &BoundingSphere) -> bool {
1809        let SphereCenter: XMVECTOR = XMLoadFloat3(&sh.Center);
1810        let SphereRadius: XMVECTOR = XMVectorReplicatePtr(&sh.Radius);
1811
1812        let BoxCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1813        let BoxExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
1814
1815        let BoxMin: XMVECTOR = XMVectorSubtract(BoxCenter, BoxExtents);
1816        let BoxMax: XMVECTOR = XMVectorAdd(BoxCenter, BoxExtents);
1817
1818        // Find the distance to the nearest point on the box.
1819        // for each i in (x, y, z)
1820        // if (SphereCenter(i) < BoxMin(i)) d2 += (SphereCenter(i) - BoxMin(i)) ^ 2
1821        // else if (SphereCenter(i) > BoxMax(i)) d2 += (SphereCenter(i) - BoxMax(i)) ^ 2
1822
1823        let mut d: XMVECTOR = XMVectorZero();
1824
1825        // Compute d for each dimension.
1826        let LessThanMin: XMVECTOR = XMVectorLess(SphereCenter, BoxMin);
1827        let GreaterThanMax: XMVECTOR = XMVectorGreater(SphereCenter, BoxMax);
1828
1829        let MinDelta: XMVECTOR = XMVectorSubtract(SphereCenter, BoxMin);
1830        let MaxDelta: XMVECTOR = XMVectorSubtract(SphereCenter, BoxMax);
1831
1832        // Choose value for each dimension based on the comparison.
1833        d = XMVectorSelect(d, MinDelta, LessThanMin);
1834        d = XMVectorSelect(d, MaxDelta, GreaterThanMax);
1835
1836        // Use a dot-product to square them and sum them together.
1837        let d2: XMVECTOR = XMVector3Dot(d, d);
1838
1839        return XMVector3LessOrEqual(d2, XMVectorMultiply(SphereRadius, SphereRadius));
1840    }
1841
1842    /// Tests the BoundingBox for intersection with a BoundingBox.
1843    ///
1844    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-intersects(constboundingbox_)>
1845    #[inline]
1846    pub fn IntersectsBox(&self, box_: &BoundingBox) -> bool {
1847        let CenterA: XMVECTOR = XMLoadFloat3(&self.Center);
1848        let ExtentsA: XMVECTOR = XMLoadFloat3(&self.Extents);
1849
1850        let CenterB: XMVECTOR = XMLoadFloat3(&box_.Center);
1851        let ExtentsB: XMVECTOR = XMLoadFloat3(&box_.Extents);
1852
1853        let MinA: XMVECTOR = XMVectorSubtract(CenterA, ExtentsA);
1854        let MaxA: XMVECTOR = XMVectorAdd(CenterA, ExtentsA);
1855
1856        let MinB: XMVECTOR = XMVectorSubtract(CenterB, ExtentsB);
1857        let MaxB: XMVECTOR = XMVectorAdd(CenterB, ExtentsB);
1858
1859        // for each i in (x, y, z) if a_min(i) > b_max(i) or b_min(i) > a_max(i) then return false
1860        let Disjoint: XMVECTOR = XMVectorOrInt(XMVectorGreater(MinA, MaxB), XMVectorGreater(MinB, MaxA));
1861
1862        return !internal::XMVector3AnyTrue(Disjoint);
1863    }
1864
1865    /// Test the BoundingBox for intersection with a BoundingOrientedBox.
1866    ///
1867    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-intersects(constboundingorientedbox_)>
1868    #[inline]
1869    pub fn IntersectsOrientedBox(&self, box_: &BoundingOrientedBox) -> bool {
1870        return box_.IntersectsBox(self);
1871    }
1872
1873    /// Test the BoundingBox for intersection with a BoundingFrustum.
1874    ///
1875    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-intersects(constboundingfrustum_)>
1876    #[inline]
1877    pub fn IntersectsFrustum(&self, fr: &BoundingFrustum) -> bool {
1878        return fr.IntersectsBox(self);
1879    }
1880
1881    /// Tests the BoundingSphere for intersection with a triangle.
1882    ///
1883    /// ## Parameters
1884    ///
1885    /// `V0` A vector describing the triangle.
1886    ///
1887    /// `V1` A vector describing the triangle.
1888    ///
1889    /// `V2` A vector describing the triangle.
1890    ///
1891    /// ## Return value
1892    ///
1893    /// A bool value indicating whether the BoundingBox intersects the triangle.
1894    ///
1895    /// ## Reference
1896    ///
1897    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-intersects(fxmvector_fxmvector_fxmvector)>
1898    #[inline]
1899    pub fn IntersectsTriangle(&self, V0: FXMVECTOR, V1: FXMVECTOR, V2: FXMVECTOR) -> bool {
1900        let Zero: XMVECTOR = XMVectorZero();
1901
1902        // Load the box.
1903        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
1904        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
1905
1906        let BoxMin: XMVECTOR = XMVectorSubtract(vCenter, vExtents);
1907        let BoxMax: XMVECTOR = XMVectorAdd(vCenter, vExtents);
1908
1909        // Test the axes of the box (in effect test the AAB against the minimal AAB
1910        // around the triangle).
1911        let TriMin: XMVECTOR = XMVectorMin(XMVectorMin(V0, V1), V2);
1912        let TriMax: XMVECTOR = XMVectorMax(XMVectorMax(V0, V1), V2);
1913
1914        // for each i in (x, y, z) if a_min(i) > b_max(i) or b_min(i) > a_max(i) then disjoint
1915        let Disjoint: XMVECTOR = XMVectorOrInt(XMVectorGreater(TriMin, BoxMax), XMVectorGreater(BoxMin, TriMax));
1916        if (internal::XMVector3AnyTrue(Disjoint)) {
1917            return false;
1918        }
1919
1920        // Test the plane of the triangle.
1921        let Normal: XMVECTOR = XMVector3Cross(XMVectorSubtract(V1, V0), XMVectorSubtract(V2, V0));
1922        let Dist: XMVECTOR = XMVector3Dot(Normal, V0);
1923
1924        // Assert that the triangle is not degenerate.
1925        debug_assert!(!XMVector3Equal(Normal, Zero));
1926
1927        // for each i in (x, y, z) if n(i) >= 0 then v_min(i)=b_min(i), v_max(i)=b_max(i)
1928        // else v_min(i)=b_max(i), v_max(i)=b_min(i)
1929        let NormalSelect: XMVECTOR = XMVectorGreater(Normal, Zero);
1930        let V_Min: XMVECTOR = XMVectorSelect(BoxMax, BoxMin, NormalSelect);
1931        let V_Max: XMVECTOR = XMVectorSelect(BoxMin, BoxMax, NormalSelect);
1932
1933        // if n dot v_min + d > 0 || n dot v_max + d < 0 then disjoint
1934        let MinDist: XMVECTOR = XMVector3Dot(V_Min, Normal);
1935        let MaxDist: XMVECTOR = XMVector3Dot(V_Max, Normal);
1936
1937        let mut NoIntersection: XMVECTOR = XMVectorGreater(MinDist, Dist);
1938        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(MaxDist, Dist));
1939
1940        // Move the box center to zero to simplify the following tests.
1941        let TV0: XMVECTOR = XMVectorSubtract(V0, vCenter);
1942        let TV1: XMVECTOR = XMVectorSubtract(V1, vCenter);
1943        let TV2: XMVECTOR = XMVectorSubtract(V2, vCenter);
1944
1945        // Test the edge/edge axes (3*3).
1946        let mut e0: XMVECTOR = XMVectorSubtract(TV1, TV0);
1947        let mut e1: XMVECTOR = XMVectorSubtract(TV2, TV1);
1948        let mut e2: XMVECTOR = XMVectorSubtract(TV0, TV2);
1949
1950        // Make w zero.
1951        // TODO: template
1952        e0 = XMVectorInsert(e0, Zero, 0, 0, 0, 0, 1);
1953        e1 = XMVectorInsert(e1, Zero, 0, 0, 0, 0, 1);
1954        e2 = XMVectorInsert(e2, Zero, 0, 0, 0, 0, 1);
1955
1956        let mut Axis: XMVECTOR;
1957        let mut p0: XMVECTOR;
1958        let mut p1: XMVECTOR;
1959        let mut p2: XMVECTOR;
1960        let mut Min: XMVECTOR;
1961        let mut Max: XMVECTOR;
1962        let mut Radius: XMVECTOR;
1963
1964        // Axis == (1,0,0) let e0: x = (0, -e0.z, e0.y)
1965        Axis = <(XM_PERMUTE_0W, XM_PERMUTE_1Z, XM_PERMUTE_0Y, XM_PERMUTE_0X)>::XMVectorPermute(e0, XMVectorNegate(e0));
1966        p0 = XMVector3Dot(TV0, Axis);
1967        // p1 = XMVector3Dot( V1, Axis ); // p1 = p0;
1968        p2 = XMVector3Dot(TV2, Axis);
1969        Min = XMVectorMin(p0, p2);
1970        Max = XMVectorMax(p0, p2);
1971        Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis));
1972        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius));
1973        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius)));
1974
1975        // Axis == (1,0,0) let e1: x = (0, -e1.z, e1.y)
1976        Axis = <(XM_PERMUTE_0W, XM_PERMUTE_1Z, XM_PERMUTE_0Y, XM_PERMUTE_0X)>::XMVectorPermute(e1, XMVectorNegate(e1));
1977        p0 = XMVector3Dot(TV0, Axis);
1978        p1 = XMVector3Dot(TV1, Axis);
1979        // p2 = XMVector3Dot( V2, Axis ); // p2 = p1;
1980        Min = XMVectorMin(p0, p1);
1981        Max = XMVectorMax(p0, p1);
1982        Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis));
1983        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius));
1984        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius)));
1985
1986        // Axis == (1,0,0) let e2: x = (0, -e2.z, e2.y)
1987        Axis = <(XM_PERMUTE_0W, XM_PERMUTE_1Z, XM_PERMUTE_0Y, XM_PERMUTE_0X)>::XMVectorPermute(e2, XMVectorNegate(e2));
1988        p0 = XMVector3Dot(TV0, Axis);
1989        p1 = XMVector3Dot(TV1, Axis);
1990        // p2 = XMVector3Dot( V2, Axis ); // p2 = p0;
1991        Min = XMVectorMin(p0, p1);
1992        Max = XMVectorMax(p0, p1);
1993        Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis));
1994        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius));
1995        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius)));
1996
1997        // Axis == (0,1,0) let e0: x = (e0.z, 0, -e0.x)
1998        Axis = <(XM_PERMUTE_0Z, XM_PERMUTE_0W, XM_PERMUTE_1X, XM_PERMUTE_0Y)>::XMVectorPermute(e0, XMVectorNegate(e0));
1999        p0 = XMVector3Dot(TV0, Axis);
2000        // p1 = XMVector3Dot( V1, Axis ); // p1 = p0;
2001        p2 = XMVector3Dot(TV2, Axis);
2002        Min = XMVectorMin(p0, p2);
2003        Max = XMVectorMax(p0, p2);
2004        Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis));
2005        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius));
2006        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius)));
2007
2008        // Axis == (0,1,0) let e1: x = (e1.z, 0, -e1.x)
2009        Axis = <(XM_PERMUTE_0Z, XM_PERMUTE_0W, XM_PERMUTE_1X, XM_PERMUTE_0Y)>::XMVectorPermute(e1, XMVectorNegate(e1));
2010        p0 = XMVector3Dot(TV0, Axis);
2011        p1 = XMVector3Dot(TV1, Axis);
2012        // p2 = XMVector3Dot( V2, Axis ); // p2 = p1;
2013        Min = XMVectorMin(p0, p1);
2014        Max = XMVectorMax(p0, p1);
2015        Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis));
2016        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius));
2017        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius)));
2018
2019        // Axis == (0,0,1) let e2: x = (e2.z, 0, -e2.x)
2020        Axis = <(XM_PERMUTE_0Z, XM_PERMUTE_0W, XM_PERMUTE_1X, XM_PERMUTE_0Y)>::XMVectorPermute(e2, XMVectorNegate(e2));
2021        p0 = XMVector3Dot(TV0, Axis);
2022        p1 = XMVector3Dot(TV1, Axis);
2023        // p2 = XMVector3Dot( V2, Axis ); // p2 = p0;
2024        Min = XMVectorMin(p0, p1);
2025        Max = XMVectorMax(p0, p1);
2026        Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis));
2027        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius));
2028        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius)));
2029
2030        // Axis == (0,0,1) let e0: x = (-e0.y, e0.x, 0)
2031        Axis = <(XM_PERMUTE_1Y, XM_PERMUTE_0X, XM_PERMUTE_0W, XM_PERMUTE_0Z)>::XMVectorPermute(e0, XMVectorNegate(e0));
2032        p0 = XMVector3Dot(TV0, Axis);
2033        // p1 = XMVector3Dot( V1, Axis ); // p1 = p0;
2034        p2 = XMVector3Dot(TV2, Axis);
2035        Min = XMVectorMin(p0, p2);
2036        Max = XMVectorMax(p0, p2);
2037        Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis));
2038        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius));
2039        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius)));
2040
2041        // Axis == (0,0,1) let e1: x = (-e1.y, e1.x, 0)
2042        Axis = <(XM_PERMUTE_1Y, XM_PERMUTE_0X, XM_PERMUTE_0W, XM_PERMUTE_0Z)>::XMVectorPermute(e1, XMVectorNegate(e1));
2043        p0 = XMVector3Dot(TV0, Axis);
2044        p1 = XMVector3Dot(TV1, Axis);
2045        // p2 = XMVector3Dot( V2, Axis ); // p2 = p1;
2046        Min = XMVectorMin(p0, p1);
2047        Max = XMVectorMax(p0, p1);
2048        Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis));
2049        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius));
2050        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius)));
2051
2052        // Axis == (0,0,1) let e2: x = (-e2.y, e2.x, 0)
2053        Axis = <(XM_PERMUTE_1Y, XM_PERMUTE_0X, XM_PERMUTE_0W, XM_PERMUTE_0Z)>::XMVectorPermute(e2, XMVectorNegate(e2));
2054        p0 = XMVector3Dot(TV0, Axis);
2055        p1 = XMVector3Dot(TV1, Axis);
2056        // p2 = XMVector3Dot( V2, Axis ); // p2 = p0;
2057        Min = XMVectorMin(p0, p1);
2058        Max = XMVectorMax(p0, p1);
2059        Radius = XMVector3Dot(vExtents, XMVectorAbs(Axis));
2060        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(Min, Radius));
2061        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(Max, XMVectorNegate(Radius)));
2062
2063        return XMVector4NotEqualInt(NoIntersection, XMVectorTrueInt());
2064    }
2065
2066    /// Tests the BoundingBox for intersection with a Plane.
2067    ///
2068    /// ## Parameters
2069    ///
2070    /// `Plane` A vector describing the plane coefficients (`A`, `B`, `C`, `D`) for the plane equation `Ax+By+Cz+D=0`.
2071    ///
2072    /// ## Return value
2073    ///
2074    /// A PlaneIntersectionType value indicating whether the BoundingSphere intersects the specified plane.
2075    ///
2076    /// ## Reference
2077    ///
2078    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-intersects(fxmvector)>
2079    #[inline]
2080    pub fn IntersectsPlane(&self, Plane: FXMVECTOR) -> PlaneIntersectionType {
2081        debug_assert!(internal::XMPlaneIsUnit(Plane));
2082
2083        // Load the box.
2084        let mut vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
2085        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
2086
2087        // Set w of the center to one so we can dot4 with a plane.
2088        // TODO: template
2089        vCenter = XMVectorInsert(vCenter, XMVectorSplatOne(), 0, 0, 0, 0, 1);
2090
2091        let mut Outside: XMVECTOR = unsafe { undefined() };
2092        let mut Inside: XMVECTOR = unsafe { undefined() };
2093        internal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane, &mut Outside, &mut Inside);
2094
2095        // If the box is outside any plane it is outside.
2096        if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
2097            return FRONT;
2098        }
2099
2100        // If the box is inside all planes it is inside.
2101        if (XMVector4EqualInt(Inside, XMVectorTrueInt())) {
2102            return BACK;
2103        }
2104
2105        // The box is not inside all planes or outside a plane it intersects.
2106        return INTERSECTING;
2107    }
2108
2109    /// Tests the BoundingBox for intersection with a ray.
2110    ///
2111    /// ## Parameters
2112    ///
2113    /// `Origin` The origin of the ray.
2114    ///
2115    /// `Direction` The direction of the ray.
2116    ///
2117    /// `Dist` The length of the ray.
2118    ///
2119    /// ## Return value
2120    ///
2121    /// A bool value indicating whether the BoundingBox intersects the ray.
2122    ///
2123    /// ## Remarks
2124    ///
2125    /// The distance from the `Origin` to the nearest intersection point is returned
2126    /// in `Dist` when the method returns `true`. Otherwise, `Dist` is set to `0.0`.
2127    ///
2128    /// ## Reference
2129    ///
2130    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-intersects(fxmvector_fxmvector_float_)>
2131    #[inline]
2132    pub fn IntersectsRay(&self, Origin: FXMVECTOR, Direction: FXMVECTOR, Dist: &mut f32) -> bool {
2133        debug_assert!(internal::XMVector3IsUnit(Direction));
2134
2135        // Load the box.
2136        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
2137        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
2138
2139        // Adjust ray origin to be relative to center of the box.
2140        let TOrigin: XMVECTOR = XMVectorSubtract(vCenter, Origin);
2141
2142        // Compute the dot product againt each axis of the box.
2143        // Since the axii are (1,0,0), (0,1,0), (0,0,1) no computation is necessary.
2144        let AxisDotOrigin: XMVECTOR = TOrigin;
2145        let AxisDotDirection: XMVECTOR = Direction;
2146
2147        // if (fabs(AxisDotDirection) <= Epsilon) the ray is nearly parallel to the slab.
2148        let IsParallel: XMVECTOR = XMVectorLessOrEqual(XMVectorAbs(AxisDotDirection), g_RayEpsilon.v());
2149
2150        // Test against all three axii simultaneously.
2151        let InverseAxisDotDirection: XMVECTOR = XMVectorReciprocal(AxisDotDirection);
2152        let t1: XMVECTOR = XMVectorMultiply(XMVectorSubtract(AxisDotOrigin, vExtents), InverseAxisDotDirection);
2153        let t2: XMVECTOR = XMVectorMultiply(XMVectorAdd(AxisDotOrigin, vExtents), InverseAxisDotDirection);
2154
2155        // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't
2156        // use the results from any directions parallel to the slab.
2157        let mut t_min: XMVECTOR = XMVectorSelect(XMVectorMin(t1, t2), g_FltMin.v(), IsParallel);
2158        let mut t_max: XMVECTOR = XMVectorSelect(XMVectorMax(t1, t2), g_FltMax.v(), IsParallel);
2159
2160        // t_min.x = maximum( t_min.x, t_min.y, t_min.z );
2161        // t_max.x = minimum( t_max.x, t_max.y, t_max.z );
2162        t_min = XMVectorMax(t_min, XMVectorSplatY(t_min));  // x = max(x,y)
2163        t_min = XMVectorMax(t_min, XMVectorSplatZ(t_min));  // x = max(max(x,y),z)
2164        t_max = XMVectorMin(t_max, XMVectorSplatY(t_max));  // x = min(x,y)
2165        t_max = XMVectorMin(t_max, XMVectorSplatZ(t_max));  // x = min(min(x,y),z)
2166
2167        // if ( t_min > t_max ) return false;
2168        let mut NoIntersection: XMVECTOR = XMVectorGreater(XMVectorSplatX(t_min), XMVectorSplatX(t_max));
2169
2170        // if ( t_max < 0.0f ) return false;
2171        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(XMVectorSplatX(t_max), XMVectorZero()));
2172
2173        // if (IsParallel && (-Extents > AxisDotOrigin || Extents < AxisDotOrigin)) return false;
2174        let ParallelOverlap: XMVECTOR = XMVectorInBounds(AxisDotOrigin, vExtents);
2175        NoIntersection = XMVectorOrInt(NoIntersection, XMVectorAndCInt(IsParallel, ParallelOverlap));
2176
2177        if (!internal::XMVector3AnyTrue(NoIntersection))
2178        {
2179            // Store the x-component to *pDist
2180            XMStoreFloat(Dist, t_min);
2181            return true;
2182        }
2183
2184        *Dist = 0.0;
2185        return false;
2186    }
2187
2188    /// Tests whether the BoundingBox is contained by the specified frustum.
2189    ///
2190    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-containedby>
2191    #[inline]
2192    pub fn ContainedBy(
2193        &self,
2194        Plane0: FXMVECTOR,
2195        Plane1: FXMVECTOR,
2196        Plane2: GXMVECTOR,
2197        Plane3: HXMVECTOR,
2198        Plane4: HXMVECTOR,
2199        Plane5: HXMVECTOR,
2200    ) -> ContainmentType
2201    {
2202        // Load the box.
2203        let mut vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
2204        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
2205
2206        // Set w of the center to one so we can dot4 with a plane.
2207        // TODO: template
2208        vCenter = XMVectorInsert(vCenter, XMVectorSplatOne(), 0, 0, 0, 0, 1);
2209
2210        let mut Outside: XMVECTOR = unsafe { undefined() };
2211        let mut Inside: XMVECTOR = unsafe { undefined() };
2212
2213        // Test against each plane.
2214        internal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane0, &mut Outside, &mut Inside);
2215
2216        let mut AnyOutside: XMVECTOR = Outside;
2217        let mut AllInside: XMVECTOR = Inside;
2218
2219        internal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane1, &mut Outside, &mut Inside);
2220        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
2221        AllInside = XMVectorAndInt(AllInside, Inside);
2222
2223        internal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane2, &mut Outside, &mut Inside);
2224        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
2225        AllInside = XMVectorAndInt(AllInside, Inside);
2226
2227        internal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane3, &mut Outside, &mut Inside);
2228        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
2229        AllInside = XMVectorAndInt(AllInside, Inside);
2230
2231        internal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane4, &mut Outside, &mut Inside);
2232        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
2233        AllInside = XMVectorAndInt(AllInside, Inside);
2234
2235        internal::FastIntersectAxisAlignedBoxPlane(vCenter, vExtents, Plane5, &mut Outside, &mut Inside);
2236        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
2237        AllInside = XMVectorAndInt(AllInside, Inside);
2238
2239        // If the box is outside any plane it is outside.
2240        if (XMVector4EqualInt(AnyOutside, XMVectorTrueInt())) {
2241            return DISJOINT;
2242        }
2243
2244        // If the box is inside all planes it is inside.
2245        if (XMVector4EqualInt(AllInside, XMVectorTrueInt())) {
2246            return CONTAINS;
2247        }
2248
2249        // The box is not inside all planes or outside a plane, it may intersect.
2250        return INTERSECTS;
2251    }
2252
2253    /// Creates a BoundingBox that contains the two specified BoundingBox objects.
2254    ///
2255    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-createmerged>
2256    #[inline]
2257    pub fn CreateMerged(Out: &mut Self, b1: &Self, b2: &Self) {
2258        let b1Center: XMVECTOR = XMLoadFloat3(&b1.Center);
2259        let b1Extents: XMVECTOR = XMLoadFloat3(&b1.Extents);
2260
2261        let b2Center: XMVECTOR = XMLoadFloat3(&b2.Center);
2262        let b2Extents: XMVECTOR = XMLoadFloat3(&b2.Extents);
2263
2264        let mut Min: XMVECTOR = XMVectorSubtract(b1Center, b1Extents);
2265        Min = XMVectorMin(Min, XMVectorSubtract(b2Center, b2Extents));
2266
2267        let mut Max: XMVECTOR = XMVectorAdd(b1Center, b1Extents);
2268        Max = XMVectorMax(Max, XMVectorAdd(b2Center, b2Extents));
2269
2270        debug_assert!(XMVector3LessOrEqual(Min, Max));
2271
2272        XMStoreFloat3(&mut Out.Center, XMVectorScale(XMVectorAdd(Min, Max), 0.5));
2273        XMStoreFloat3(&mut Out.Extents, XMVectorScale(XMVectorSubtract(Max, Min), 0.5));
2274    }
2275
2276    /// Creates a BoundingBox large enough to contain the a specified BoundingSphere.
2277    ///
2278    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingbox-createfromsphere>
2279    #[inline]
2280    pub fn CreateFromSphere(Out: &mut Self, sh: &BoundingSphere) {
2281        let spCenter: XMVECTOR = XMLoadFloat3(&sh.Center);
2282        let shRadius: XMVECTOR = XMVectorReplicatePtr(&sh.Radius);
2283
2284        let Min: XMVECTOR = XMVectorSubtract(spCenter, shRadius);
2285        let Max: XMVECTOR = XMVectorAdd(spCenter, shRadius);
2286
2287        debug_assert!(XMVector3LessOrEqual(Min, Max));
2288
2289        XMStoreFloat3(&mut Out.Center, XMVectorScale(XMVectorAdd(Min, Max), 0.5));
2290        XMStoreFloat3(&mut Out.Extents, XMVectorScale(XMVectorSubtract(Max, Min), 0.5));
2291    }
2292
2293    /// Creates a new BoundingBox from a list of points.
2294    ///
2295    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingBox-createfrompoints>
2296    #[inline]
2297    pub fn CreateFromPoints<'a>(Out: &mut Self, pPoints: impl Iterator<Item=&'a XMFLOAT3>) {
2298        // assert(Count > 0);
2299        // assert(pPoints);
2300
2301        // Find the minimum and maximum x, y, and z
2302        // NOTE: We default to Zero since we don't have the Count > 0 assertion
2303        let mut vMin: XMVECTOR = g_XMInfinity.v();
2304        let mut vMax: XMVECTOR = g_XMNegInfinity.v();
2305
2306        for (i, pPoint) in pPoints.enumerate()
2307        {
2308            let Point: XMVECTOR = XMLoadFloat3(pPoint);
2309            if i == 0 {
2310                vMin = Point;
2311                vMax = Point;
2312            } else {
2313                vMin = XMVectorMin(vMin, Point);
2314                vMax = XMVectorMax(vMax, Point);
2315            }
2316        }
2317
2318        // Store center and extents.
2319        XMStoreFloat3(&mut Out.Center, XMVectorScale(XMVectorAdd(vMin, vMax), 0.5));
2320        XMStoreFloat3(&mut Out.Extents, XMVectorScale(XMVectorSubtract(vMax, vMin), 0.5));
2321    }
2322}
2323
2324// BoundingOrientedBox --------------------------------------------------------
2325
2326impl BoundingOrientedBox {
2327    pub const CORNER_COUNT: usize = 8;
2328
2329    /// Transforms the BoundingOrientedBox by the specified transformation matrix.
2330    ///
2331    /// ## Parameters
2332    ///
2333    /// `Out` The transformed BoundingOrientedBox.
2334    ///
2335    /// `M` The matrix to transform the BoundingOrientedBox with.
2336    ///
2337    /// ## Return value
2338    ///
2339    /// This method does not return a value.
2340    ///
2341    /// ## Reference
2342    ///
2343    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-transform>
2344    #[inline]
2345    pub fn TransformMatrix(&self, Out: &mut Self, M: FXMMATRIX) {
2346        // Load the box.
2347        let mut vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
2348        let mut vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
2349        let mut vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
2350
2351        debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
2352
2353        unsafe {
2354            // Composite the box rotation and the transform rotation.
2355            let mut nM: XMMATRIX = undefined();
2356            nM.r[0] = XMVector3Normalize(M.r[0]);
2357            nM.r[1] = XMVector3Normalize(M.r[1]);
2358            nM.r[2] = XMVector3Normalize(M.r[2]);
2359            nM.r[3] = g_XMIdentityR3.v;
2360            let Rotation: XMVECTOR = XMQuaternionRotationMatrix(nM);
2361            vOrientation = XMQuaternionMultiply(vOrientation, Rotation);
2362
2363            // Transform the center.
2364            vCenter = XMVector3Transform(vCenter, M);
2365
2366            // Scale the box extents.
2367            let dX: XMVECTOR = XMVector3Length(M.r[0]);
2368            let dY: XMVECTOR = XMVector3Length(M.r[1]);
2369            let dZ: XMVECTOR = XMVector3Length(M.r[2]);
2370
2371            let mut VectorScale: XMVECTOR = XMVectorSelect(dY, dX, g_XMSelect1000.v);
2372            VectorScale = XMVectorSelect(dZ, VectorScale, g_XMSelect1100.v);
2373            vExtents = XMVectorMultiply(vExtents, VectorScale);
2374        }
2375
2376        // Store the box.
2377        XMStoreFloat3(&mut Out.Center, vCenter);
2378        XMStoreFloat3(&mut Out.Extents, vExtents);
2379        XMStoreFloat4(&mut Out.Orientation, vOrientation);
2380    }
2381
2382    /// Transforms the BoundingOrientedBox using the specified `scale`, `rotation` and `translation` vectors.
2383    ///
2384    /// ## Parameters
2385    ///
2386    /// `Out` The transformed BoundingOrientedBox.
2387    ///
2388    /// `Scale` The value to scale the BoundingOrientedBox by.
2389    ///
2390    /// `Rotation` The value to rotate the BoundingOrientedBox by.
2391    ///
2392    /// `Translation` The value to translate the BoundingOrientedBox by.
2393    ///
2394    /// ## Return value
2395    ///
2396    /// This method does not return a value.
2397    ///
2398    /// ## Reference
2399    ///
2400    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-transform(BoundingOrientedBox__float_fxmvector_fxmvector)>
2401    #[inline]
2402    pub fn TransformDecomposed(&self, Out: &mut Self, Scale: f32, Rotation: FXMVECTOR, Translation: FXMVECTOR) {
2403        debug_assert!(internal::XMQuaternionIsUnit(Rotation));
2404
2405        // Load the box.
2406        let mut vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
2407        let mut vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
2408        let mut vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
2409
2410        debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
2411
2412        // Composite the box rotation and the transform rotation.
2413        vOrientation = XMQuaternionMultiply(vOrientation, Rotation);
2414
2415        // Transform the center.
2416        let VectorScale: XMVECTOR = XMVectorReplicate(Scale);
2417        vCenter = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(vCenter, VectorScale), Rotation), Translation);
2418
2419        // Scale the box extents.
2420        vExtents = XMVectorMultiply(vExtents, VectorScale);
2421
2422        // Store the box.
2423        XMStoreFloat3(&mut Out.Center, vCenter);
2424        XMStoreFloat3(&mut Out.Extents, vExtents);
2425        XMStoreFloat4(&mut Out.Orientation, vOrientation);
2426    }
2427
2428    /// Tests whether the BoundingOrientedBox contains a specified point.
2429    ///
2430    /// ## Parameters
2431    ///
2432    /// `Point` The point to test against.
2433    ///
2434    /// ## Return value
2435    ///
2436    /// A ContainmentType indicating whether point is contained in the BoundingOrientedBox.
2437    ///
2438    /// ## Reference
2439    ///
2440    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-contains>
2441    #[inline]
2442    pub fn ContainsPoint(&self, Point: FXMVECTOR) -> ContainmentType {
2443        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
2444        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
2445        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
2446
2447        // Transform the point to be local to the box.
2448        let TPoint: XMVECTOR = XMVector3InverseRotate(XMVectorSubtract(Point, vCenter), vOrientation);
2449
2450        return if XMVector3InBounds(TPoint, vExtents) { CONTAINS } else { DISJOINT };
2451    }
2452
2453    /// Tests whether the BoundingOrientedBox contains a triangle.
2454    ///
2455    /// ## Parameters
2456    ///
2457    /// `V0` A vector describing the triangle.
2458    ///
2459    /// `V1` A vector describing the triangle.
2460    ///
2461    /// `V2` A vector describing the triangle.
2462    ///
2463    /// ## Return value
2464    ///
2465    /// A ContainmentType indicating whether triangle is contained in the BoundingOrientedBox.
2466    ///
2467    /// ## Reference
2468    ///
2469    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-contains(fxmvector_fxmvector_fxmvector)>
2470    pub fn ContainsTriangle(&self, V0: FXMVECTOR, V1: FXMVECTOR, V2: FXMVECTOR) -> ContainmentType {
2471        // Load the box center & orientation.
2472        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
2473        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
2474
2475        // Transform the triangle vertices into the space of the box.
2476        let TV0: XMVECTOR = XMVector3InverseRotate(XMVectorSubtract(V0, vCenter), vOrientation);
2477        let TV1: XMVECTOR = XMVector3InverseRotate(XMVectorSubtract(V1, vCenter), vOrientation);
2478        let TV2: XMVECTOR = XMVector3InverseRotate(XMVectorSubtract(V2, vCenter), vOrientation);
2479
2480        let mut box_: BoundingBox = BoundingBox::default();
2481        box_.Center = XMFLOAT3::set(0.0, 0.0, 0.0);
2482        box_.Extents = self.Extents;
2483
2484        // Use the triangle vs axis aligned box intersection routine.
2485        return box_.ContainsTriangle(TV0, TV1, TV2);
2486    }
2487
2488    /// Tests whether the BoundingOrientedBox contains a specified BoundingSphere.
2489    ///
2490    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-contains(constboundingsphere_)>
2491    #[inline]
2492    pub fn ContainsSphere(&self, sh: &BoundingSphere) -> ContainmentType {
2493        let mut SphereCenter: XMVECTOR = XMLoadFloat3(&sh.Center);
2494        let SphereRadius: XMVECTOR = XMVectorReplicatePtr(&sh.Radius);
2495
2496        let BoxCenter: XMVECTOR = XMLoadFloat3(&self.Center);
2497        let BoxExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
2498        let BoxOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
2499
2500        debug_assert!(internal::XMQuaternionIsUnit(BoxOrientation));
2501
2502        // Transform the center of the sphere to be local to the box.
2503        // BoxMin = -BoxExtents
2504        // BoxMax = +BoxExtents
2505        SphereCenter = XMVector3InverseRotate(XMVectorSubtract(SphereCenter, BoxCenter), BoxOrientation);
2506
2507        // Find the distance to the nearest point on the box.
2508        // for each i in (x, y, z)
2509        // if (SphereCenter(i) < BoxMin(i)) d2 += (SphereCenter(i) - BoxMin(i)) ^ 2
2510        // else if (SphereCenter(i) > BoxMax(i)) d2 += (SphereCenter(i) - BoxMax(i)) ^ 2
2511
2512        let mut d: XMVECTOR = XMVectorZero();
2513
2514        // Compute d for each dimension.
2515        let LessThanMin: XMVECTOR = XMVectorLess(SphereCenter, XMVectorNegate(BoxExtents));
2516        let GreaterThanMax: XMVECTOR = XMVectorGreater(SphereCenter, BoxExtents);
2517
2518        let MinDelta: XMVECTOR = XMVectorAdd(SphereCenter, BoxExtents);
2519        let MaxDelta: XMVECTOR = XMVectorSubtract(SphereCenter, BoxExtents);
2520
2521        // Choose value for each dimension based on the comparison.
2522        d = XMVectorSelect(d, MinDelta, LessThanMin);
2523        d = XMVectorSelect(d, MaxDelta, GreaterThanMax);
2524
2525        // Use a dot-product to square them and sum them together.
2526        let d2: XMVECTOR = XMVector3Dot(d, d);
2527        let SphereRadiusSq: XMVECTOR = XMVectorMultiply(SphereRadius, SphereRadius);
2528
2529        if (XMVector4Greater(d2, SphereRadiusSq)) {
2530            return DISJOINT;
2531        }
2532
2533        // See if we are completely inside the box
2534        let SMin: XMVECTOR = XMVectorSubtract(SphereCenter, SphereRadius);
2535        let SMax: XMVECTOR = XMVectorAdd(SphereCenter, SphereRadius);
2536
2537        return if (XMVector3InBounds(SMin, BoxExtents) && XMVector3InBounds(SMax, BoxExtents)) { CONTAINS } else { INTERSECTS };
2538    }
2539
2540    /// Tests whether the BoundingOrientedBox contains a specified BoundingBox.
2541    ///
2542    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-contains(constboundingbox_)>
2543    #[inline]
2544    pub fn ContainsBox(&self, box_: &BoundingBox) -> ContainmentType {
2545        // Make the axis aligned box oriented and do an OBB vs OBB test.
2546        let obox = BoundingOrientedBox {
2547            Center: box_.Center,
2548            Extents: box_.Extents,
2549            Orientation: XMFLOAT4::set(0.0, 0.0, 0.0, 1.0),
2550        };
2551        return self.ContainsOrientedBox(&obox);
2552    }
2553
2554    /// Tests whether the BoundingOrientedBox contains the specified BoundingOrientedBox.
2555    ///
2556    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-contains(constboundingorientedbox_)>
2557    #[inline]
2558    pub fn ContainsOrientedBox(&self, box_: &BoundingOrientedBox) -> ContainmentType {
2559        if (!self.IntersectsOrientedBox(box_)) {
2560            return DISJOINT;
2561        }
2562
2563        // Load the boxes
2564        let aCenter: XMVECTOR = XMLoadFloat3(&self.Center);
2565        let aExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
2566        let aOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
2567
2568        debug_assert!(internal::XMQuaternionIsUnit(aOrientation));
2569
2570        let bCenter: XMVECTOR = XMLoadFloat3(&box_.Center);
2571        let bExtents: XMVECTOR = XMLoadFloat3(&box_.Extents);
2572        let bOrientation: XMVECTOR = XMLoadFloat4(&box_.Orientation);
2573
2574        debug_assert!(internal::XMQuaternionIsUnit(bOrientation));
2575
2576        let offset: XMVECTOR = XMVectorSubtract(bCenter, aCenter);
2577
2578        for i in 0..BoundingOrientedBox::CORNER_COUNT
2579        {
2580            // Cb = rotate( bExtents * corneroffset[i], bOrientation ) + bcenter
2581            // Ca = invrotate( Cb - aCenter, aOrientation )
2582
2583            let mut C: XMVECTOR = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(bExtents, g_BoxOffset[i].v()), bOrientation), offset);
2584            C = XMVector3InverseRotate(C, aOrientation);
2585
2586            if (!XMVector3InBounds(C, aExtents)) {
2587                return INTERSECTS;
2588            }
2589        }
2590
2591        return CONTAINS;
2592    }
2593
2594    /// Tests whether the BoundingOrientedBox contains the specified BoundingFrustum.
2595    ///
2596    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-contains(constboundingfrustum_)>
2597    #[inline]
2598    pub fn ContainsFrustum(&self, fr: &BoundingFrustum) -> ContainmentType {
2599        if (!fr.IntersectsOrientedBox(self)) {
2600            return DISJOINT;
2601        }
2602
2603        let mut Corners: [XMFLOAT3; BoundingFrustum::CORNER_COUNT] = unsafe { undefined() };
2604        fr.GetCorners(&mut Corners);
2605
2606        // Load the box
2607        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
2608        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
2609        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
2610
2611        debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
2612
2613        for i in 0 .. BoundingFrustum::CORNER_COUNT
2614        {
2615            let C: XMVECTOR = XMVector3InverseRotate(XMVectorSubtract(XMLoadFloat3(&Corners[i]), vCenter), vOrientation);
2616
2617            if (!XMVector3InBounds(C, vExtents)) {
2618                return INTERSECTS;
2619            }
2620        }
2621
2622        return CONTAINS;
2623    }
2624
2625    /// Tests the BoundingOrientedBox for intersection with a BoundingSphere.
2626    ///
2627    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-intersects>
2628    #[inline]
2629    pub fn IntersectsSphere(&self, sh: &BoundingSphere) -> bool {
2630        let mut SphereCenter: XMVECTOR = XMLoadFloat3(&sh.Center);
2631        let SphereRadius: XMVECTOR = XMVectorReplicatePtr(&sh.Radius);
2632
2633        let BoxCenter: XMVECTOR = XMLoadFloat3(&self.Center);
2634        let BoxExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
2635        let BoxOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
2636
2637        debug_assert!(internal::XMQuaternionIsUnit(BoxOrientation));
2638
2639        // Transform the center of the sphere to be local to the box.
2640        // BoxMin = -BoxExtents
2641        // BoxMax = +BoxExtents
2642        SphereCenter = XMVector3InverseRotate(XMVectorSubtract(SphereCenter, BoxCenter), BoxOrientation);
2643
2644        // Find the distance to the nearest point on the box.
2645        // for each i in (x, y, z)
2646        // if (SphereCenter(i) < BoxMin(i)) d2 += (SphereCenter(i) - BoxMin(i)) ^ 2
2647        // else if (SphereCenter(i) > BoxMax(i)) d2 += (SphereCenter(i) - BoxMax(i)) ^ 2
2648
2649        let mut d: XMVECTOR = XMVectorZero();
2650
2651        // Compute d for each dimension.
2652        let LessThanMin: XMVECTOR = XMVectorLess(SphereCenter, XMVectorNegate(BoxExtents));
2653        let GreaterThanMax: XMVECTOR = XMVectorGreater(SphereCenter, BoxExtents);
2654
2655        let MinDelta: XMVECTOR = XMVectorAdd(SphereCenter, BoxExtents);
2656        let MaxDelta: XMVECTOR = XMVectorSubtract(SphereCenter, BoxExtents);
2657
2658        // Choose value for each dimension based on the comparison.
2659        d = XMVectorSelect(d, MinDelta, LessThanMin);
2660        d = XMVectorSelect(d, MaxDelta, GreaterThanMax);
2661
2662        // Use a dot-product to square them and sum them together.
2663        let d2: XMVECTOR = XMVector3Dot(d, d);
2664
2665        return if XMVector4LessOrEqual(d2, XMVectorMultiply(SphereRadius, SphereRadius)) { true } else { false };
2666    }
2667
2668    /// Tests the BoundingOrientedBox for intersection with a BoundingBox.
2669    ///
2670    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-intersects(constboundingbox_)>
2671    #[inline]
2672    pub fn IntersectsBox(&self, box_: &BoundingBox) -> bool {
2673        // Make the axis aligned box oriented and do an OBB vs OBB test.
2674        let obox = BoundingOrientedBox {
2675            Center: box_.Center,
2676            Extents: box_.Extents,
2677            Orientation: XMFLOAT4::set(0.0, 0.0, 0.0, 1.0),
2678        };
2679        return self.IntersectsOrientedBox(&obox);
2680    }
2681
2682    /// Test the BoundingOrientedBox for intersection with a BoundingOrientedBox.
2683    ///
2684    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-intersects(constboundingorientedbox_)>
2685    #[inline]
2686    pub fn IntersectsOrientedBox(&self, box_: &BoundingOrientedBox) -> bool {
2687        // Build the 3x3 rotation matrix that defines the orientation of B relative to A.
2688        let A_quat: XMVECTOR = XMLoadFloat4(&self.Orientation);
2689        let B_quat: XMVECTOR = XMLoadFloat4(&box_.Orientation);
2690
2691        debug_assert!(internal::XMQuaternionIsUnit(A_quat));
2692        debug_assert!(internal::XMQuaternionIsUnit(B_quat));
2693
2694        let Q: XMVECTOR = XMQuaternionMultiply(A_quat, XMQuaternionConjugate(B_quat));
2695        let R: XMMATRIX = XMMatrixRotationQuaternion(Q);
2696
2697        // Compute the translation of B relative to A.
2698        let A_cent: XMVECTOR = XMLoadFloat3(&self.Center);
2699        let B_cent: XMVECTOR = XMLoadFloat3(&box_.Center);
2700        let t: XMVECTOR = XMVector3InverseRotate(XMVectorSubtract(B_cent, A_cent), A_quat);
2701
2702        //
2703        // h(A) = extents of A.
2704        // h(B) = extents of B.
2705        //
2706        // a(u) = axes let A: of = (1,0,0), (0,1,0), (0,0,1)
2707        // b(u) = axes of B relative let A: to = (r00,r10,r20), (r01,r11,r21), (r02,r12,r22)
2708        //
2709        // For each possible separating axis l:
2710        //   d(A) = sum (let i: for = u,v,w) h(A)(i) * abs( a(i) dot l )
2711        //   d(B) = sum (let i: for = u,v,w) h(B)(i) * abs( b(i) dot l )
2712        //   if abs( t dot l ) > d(A) + d(B) then disjoint
2713        //
2714
2715        // Load extents of A and B.
2716        let h_A: XMVECTOR = XMLoadFloat3(&self.Extents);
2717        let h_B: XMVECTOR = XMLoadFloat3(&box_.Extents);
2718
2719        // Rows. Note R[0,1,2]X.w = 0.
2720        let R0X: XMVECTOR = unsafe { R.r[0] };
2721        let R1X: XMVECTOR = unsafe { R.r[1] };
2722        let R2X: XMVECTOR = unsafe { R.r[2] };
2723
2724        let R = XMMatrixTranspose(R);
2725
2726        // Columns. Note RX[0,1,2].w = 0.
2727        let RX0: XMVECTOR = unsafe { R.r[0] };
2728        let RX1: XMVECTOR = unsafe { R.r[1] };
2729        let RX2: XMVECTOR = unsafe { R.r[2] };
2730
2731        // Absolute value of rows.
2732        let AR0X: XMVECTOR = XMVectorAbs(R0X);
2733        let AR1X: XMVECTOR = XMVectorAbs(R1X);
2734        let AR2X: XMVECTOR = XMVectorAbs(R2X);
2735
2736        // Absolute value of columns.
2737        let ARX0: XMVECTOR = XMVectorAbs(RX0);
2738        let ARX1: XMVECTOR = XMVectorAbs(RX1);
2739        let ARX2: XMVECTOR = XMVectorAbs(RX2);
2740
2741        // Test each of the 15 possible seperating axii.
2742        //XMVECTOR d, d_A, d_B;
2743        let mut d: XMVECTOR;
2744        let mut d_A: XMVECTOR;
2745        let mut d_B: XMVECTOR;
2746
2747        // l = a(u) = (1, 0, 0)
2748        // t let l: dot = t.x
2749        // d(A) = h(A).x
2750        // d(B) = h(B) dot abs(r00, r01, r02)
2751        d = XMVectorSplatX(t);
2752        d_A = XMVectorSplatX(h_A);
2753        d_B = XMVector3Dot(h_B, AR0X);
2754        let mut NoIntersection: XMVECTOR = XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B));
2755
2756        // l = a(v) = (0, 1, 0)
2757        // t let l: dot = t.y
2758        // d(A) = h(A).y
2759        // d(B) = h(B) dot abs(r10, r11, r12)
2760        d = XMVectorSplatY(t);
2761        d_A = XMVectorSplatY(h_A);
2762        d_B = XMVector3Dot(h_B, AR1X);
2763        NoIntersection = XMVectorOrInt(NoIntersection,
2764            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2765
2766        // l = a(w) = (0, 0, 1)
2767        // t let l: dot = t.z
2768        // d(A) = h(A).z
2769        // d(B) = h(B) dot abs(r20, r21, r22)
2770        d = XMVectorSplatZ(t);
2771        d_A = XMVectorSplatZ(h_A);
2772        d_B = XMVector3Dot(h_B, AR2X);
2773        NoIntersection = XMVectorOrInt(NoIntersection,
2774            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2775
2776        // l = b(u) = (r00, r10, r20)
2777        // d(A) = h(A) dot abs(r00, r10, r20)
2778        // d(B) = h(B).x
2779        d = XMVector3Dot(t, RX0);
2780        d_A = XMVector3Dot(h_A, ARX0);
2781        d_B = XMVectorSplatX(h_B);
2782        NoIntersection = XMVectorOrInt(NoIntersection,
2783            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2784
2785        // l = b(v) = (r01, r11, r21)
2786        // d(A) = h(A) dot abs(r01, r11, r21)
2787        // d(B) = h(B).y
2788        d = XMVector3Dot(t, RX1);
2789        d_A = XMVector3Dot(h_A, ARX1);
2790        d_B = XMVectorSplatY(h_B);
2791        NoIntersection = XMVectorOrInt(NoIntersection,
2792            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2793
2794        // l = b(w) = (r02, r12, r22)
2795        // d(A) = h(A) dot abs(r02, r12, r22)
2796        // d(B) = h(B).z
2797        d = XMVector3Dot(t, RX2);
2798        d_A = XMVector3Dot(h_A, ARX2);
2799        d_B = XMVectorSplatZ(h_B);
2800        NoIntersection = XMVectorOrInt(NoIntersection,
2801            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2802
2803        // l = a(u) x b(u) = (0, -r20, r10)
2804        // d(A) = h(A) dot abs(0, r20, r10)
2805        // d(B) = h(B) dot abs(0, r02, r01)
2806        d = XMVector3Dot(t, <(XM_PERMUTE_0W, XM_PERMUTE_1Z, XM_PERMUTE_0Y, XM_PERMUTE_0X)>::XMVectorPermute(RX0, XMVectorNegate(RX0)));
2807        d_A = XMVector3Dot(h_A, <(XM_SWIZZLE_W, XM_SWIZZLE_Z, XM_SWIZZLE_Y, XM_SWIZZLE_X)>::XMVectorSwizzle(ARX0));
2808        d_B = XMVector3Dot(h_B, <(XM_SWIZZLE_W, XM_SWIZZLE_Z, XM_SWIZZLE_Y, XM_SWIZZLE_X)>::XMVectorSwizzle(AR0X));
2809        NoIntersection = XMVectorOrInt(NoIntersection,
2810            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2811
2812        // l = a(u) x b(v) = (0, -r21, r11)
2813        // d(A) = h(A) dot abs(0, r21, r11)
2814        // d(B) = h(B) dot abs(r02, 0, r00)
2815        d = XMVector3Dot(t, <(XM_PERMUTE_0W, XM_PERMUTE_1Z, XM_PERMUTE_0Y, XM_PERMUTE_0X)>::XMVectorPermute(RX1, XMVectorNegate(RX1)));
2816        d_A = XMVector3Dot(h_A, <(XM_SWIZZLE_W, XM_SWIZZLE_Z, XM_SWIZZLE_Y, XM_SWIZZLE_X)>::XMVectorSwizzle(ARX1));
2817        d_B = XMVector3Dot(h_B, <(XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_X, XM_SWIZZLE_Y)>::XMVectorSwizzle(AR0X));
2818        NoIntersection = XMVectorOrInt(NoIntersection,
2819            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2820
2821        // l = a(u) x b(w) = (0, -r22, r12)
2822        // d(A) = h(A) dot abs(0, r22, r12)
2823        // d(B) = h(B) dot abs(r01, r00, 0)
2824        d = XMVector3Dot(t, <(XM_PERMUTE_0W, XM_PERMUTE_1Z, XM_PERMUTE_0Y, XM_PERMUTE_0X)>::XMVectorPermute(RX2, XMVectorNegate(RX2)));
2825        d_A = XMVector3Dot(h_A, <(XM_SWIZZLE_W, XM_SWIZZLE_Z, XM_SWIZZLE_Y, XM_SWIZZLE_X)>::XMVectorSwizzle(ARX2));
2826        d_B = XMVector3Dot(h_B, <(XM_SWIZZLE_Y, XM_SWIZZLE_X, XM_SWIZZLE_W, XM_SWIZZLE_Z)>::XMVectorSwizzle(AR0X));
2827        NoIntersection = XMVectorOrInt(NoIntersection,
2828            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2829
2830        // l = a(v) x b(u) = (r20, 0, -r00)
2831        // d(A) = h(A) dot abs(r20, 0, r00)
2832        // d(B) = h(B) dot abs(0, r12, r11)
2833        d = XMVector3Dot(t, <(XM_PERMUTE_0Z, XM_PERMUTE_0W, XM_PERMUTE_1X, XM_PERMUTE_0Y)>::XMVectorPermute(RX0, XMVectorNegate(RX0)));
2834        d_A = XMVector3Dot(h_A, <(XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_X, XM_SWIZZLE_Y)>::XMVectorSwizzle(ARX0));
2835        d_B = XMVector3Dot(h_B, <(XM_SWIZZLE_W, XM_SWIZZLE_Z, XM_SWIZZLE_Y, XM_SWIZZLE_X)>::XMVectorSwizzle(AR1X));
2836        NoIntersection = XMVectorOrInt(NoIntersection,
2837            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2838
2839        // l = a(v) x b(v) = (r21, 0, -r01)
2840        // d(A) = h(A) dot abs(r21, 0, r01)
2841        // d(B) = h(B) dot abs(r12, 0, r10)
2842        d = XMVector3Dot(t, <(XM_PERMUTE_0Z, XM_PERMUTE_0W, XM_PERMUTE_1X, XM_PERMUTE_0Y)>::XMVectorPermute(RX1, XMVectorNegate(RX1)));
2843        d_A = XMVector3Dot(h_A, <(XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_X, XM_SWIZZLE_Y)>::XMVectorSwizzle(ARX1));
2844        d_B = XMVector3Dot(h_B, <(XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_X, XM_SWIZZLE_Y)>::XMVectorSwizzle(AR1X));
2845        NoIntersection = XMVectorOrInt(NoIntersection,
2846            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2847
2848        // l = a(v) x b(w) = (r22, 0, -r02)
2849        // d(A) = h(A) dot abs(r22, 0, r02)
2850        // d(B) = h(B) dot abs(r11, r10, 0)
2851        d = XMVector3Dot(t, <(XM_PERMUTE_0Z, XM_PERMUTE_0W, XM_PERMUTE_1X, XM_PERMUTE_0Y)>::XMVectorPermute(RX2, XMVectorNegate(RX2)));
2852        d_A = XMVector3Dot(h_A, <(XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_X, XM_SWIZZLE_Y)>::XMVectorSwizzle(ARX2));
2853        d_B = XMVector3Dot(h_B, <(XM_SWIZZLE_Y, XM_SWIZZLE_X, XM_SWIZZLE_W, XM_SWIZZLE_Z)>::XMVectorSwizzle(AR1X));
2854        NoIntersection = XMVectorOrInt(NoIntersection,
2855            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2856
2857        // l = a(w) x b(u) = (-r10, r00, 0)
2858        // d(A) = h(A) dot abs(r10, r00, 0)
2859        // d(B) = h(B) dot abs(0, r22, r21)
2860        d = XMVector3Dot(t, <(XM_PERMUTE_1Y, XM_PERMUTE_0X, XM_PERMUTE_0W, XM_PERMUTE_0Z)>::XMVectorPermute(RX0, XMVectorNegate(RX0)));
2861        d_A = XMVector3Dot(h_A, <(XM_SWIZZLE_Y, XM_SWIZZLE_X, XM_SWIZZLE_W, XM_SWIZZLE_Z)>::XMVectorSwizzle(ARX0));
2862        d_B = XMVector3Dot(h_B, <(XM_SWIZZLE_W, XM_SWIZZLE_Z, XM_SWIZZLE_Y, XM_SWIZZLE_X)>::XMVectorSwizzle(AR2X));
2863        NoIntersection = XMVectorOrInt(NoIntersection,
2864            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2865
2866        // l = a(w) x b(v) = (-r11, r01, 0)
2867        // d(A) = h(A) dot abs(r11, r01, 0)
2868        // d(B) = h(B) dot abs(r22, 0, r20)
2869        d = XMVector3Dot(t, <(XM_PERMUTE_1Y, XM_PERMUTE_0X, XM_PERMUTE_0W, XM_PERMUTE_0Z)>::XMVectorPermute(RX1, XMVectorNegate(RX1)));
2870        d_A = XMVector3Dot(h_A, <(XM_SWIZZLE_Y, XM_SWIZZLE_X, XM_SWIZZLE_W, XM_SWIZZLE_Z)>::XMVectorSwizzle(ARX1));
2871        d_B = XMVector3Dot(h_B, <(XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_X, XM_SWIZZLE_Y)>::XMVectorSwizzle(AR2X));
2872        NoIntersection = XMVectorOrInt(NoIntersection,
2873            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2874
2875        // l = a(w) x b(w) = (-r12, r02, 0)
2876        // d(A) = h(A) dot abs(r12, r02, 0)
2877        // d(B) = h(B) dot abs(r21, r20, 0)
2878        d = XMVector3Dot(t, <(XM_PERMUTE_1Y, XM_PERMUTE_0X, XM_PERMUTE_0W, XM_PERMUTE_0Z)>::XMVectorPermute(RX2, XMVectorNegate(RX2)));
2879        d_A = XMVector3Dot(h_A, <(XM_SWIZZLE_Y, XM_SWIZZLE_X, XM_SWIZZLE_W, XM_SWIZZLE_Z)>::XMVectorSwizzle(ARX2));
2880        d_B = XMVector3Dot(h_B, <(XM_SWIZZLE_Y, XM_SWIZZLE_X, XM_SWIZZLE_W, XM_SWIZZLE_Z)>::XMVectorSwizzle(AR2X));
2881        NoIntersection = XMVectorOrInt(NoIntersection,
2882            XMVectorGreater(XMVectorAbs(d), XMVectorAdd(d_A, d_B)));
2883
2884        // No seperating axis found, boxes must intersect.
2885        return if XMVector4NotEqualInt(NoIntersection, XMVectorTrueInt()) { true } else { false };
2886    }
2887
2888    /// Test the BoundingOrientedBox for intersection with a BoundingFrustum.
2889    ///
2890    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-intersects(constboundingfrustum_)>
2891    #[inline]
2892    pub fn IntersectsFrustum(&self, fr: &BoundingFrustum) -> bool {
2893        return fr.IntersectsOrientedBox(self);
2894    }
2895
2896    /// Tests the BoundingOrientedBox for intersection with a triangle.
2897    ///
2898    /// ## Parameters
2899    ///
2900    /// `V0` A vector describing the triangle.
2901    ///
2902    /// `V1` A vector describing the triangle.
2903    ///
2904    /// `V2` A vector describing the triangle.
2905    ///
2906    /// ## Return value
2907    ///
2908    /// A boolean value indicating whether the BoundingOrientedBox intersects the triangle.
2909    ///
2910    /// ## Reference
2911    ///
2912    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-intersects(fxmvector_fxmvector_fxmvector)>
2913    #[inline]
2914    pub fn IntersectsTriangle(&self, V0: FXMVECTOR, V1: FXMVECTOR, V2: FXMVECTOR) -> bool {
2915        // Load the box center & orientation.
2916        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
2917        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
2918
2919        // Transform the triangle vertices into the space of the box.
2920        let TV0: XMVECTOR = XMVector3InverseRotate(XMVectorSubtract(V0, vCenter), vOrientation);
2921        let TV1: XMVECTOR = XMVector3InverseRotate(XMVectorSubtract(V1, vCenter), vOrientation);
2922        let TV2: XMVECTOR = XMVector3InverseRotate(XMVectorSubtract(V2, vCenter), vOrientation);
2923
2924        let mut box_: BoundingBox = BoundingBox::default();
2925        box_.Center = XMFLOAT3::set(0.0, 0.0, 0.0);
2926        box_.Extents = self.Extents;
2927
2928        // Use the triangle vs axis aligned box intersection routine.
2929        return box_.IntersectsTriangle(TV0, TV1, TV2);
2930    }
2931
2932    /// Tests the BoundingOrientedBox for intersection with a Plane.
2933    ///
2934    /// ## Parameters
2935    ///
2936    /// `Plane` A vector describing the plane coefficients (`A`, `B`, `C`, `D`) for the plane equation `Ax+By+Cz+D=0`.
2937    ///
2938    /// ## Return value
2939    ///
2940    /// A PlaneIntersectionType value indicating whether the BoundingSphere intersects the specified plane.
2941    ///
2942    /// ## Reference
2943    ///
2944    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-intersects(fxmvector)>
2945    #[inline]
2946    pub fn IntersectsPlane(&self, Plane: FXMVECTOR) -> PlaneIntersectionType {
2947        debug_assert!(internal::XMPlaneIsUnit(Plane));
2948
2949        // Load the box.
2950        let mut vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
2951        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
2952        let BoxOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
2953
2954        debug_assert!(internal::XMQuaternionIsUnit(BoxOrientation));
2955
2956        // Set w of the center to one so we can dot4 with a plane.
2957        // TODO: template
2958        vCenter = XMVectorInsert(vCenter, XMVectorSplatOne(), 0, 0, 0, 0, 1);
2959
2960        // Build the 3x3 rotation matrix that defines the box axes.
2961        let R: XMMATRIX = XMMatrixRotationQuaternion(BoxOrientation);
2962
2963        unsafe {
2964            let mut Outside: XMVECTOR = undefined();
2965            let mut Inside: XMVECTOR = undefined();
2966            internal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane, &mut Outside, &mut Inside);
2967
2968            // If the box is outside any plane it is outside.
2969            if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
2970                return FRONT;
2971            }
2972
2973            // If the box is inside all planes it is inside.
2974            if (XMVector4EqualInt(Inside, XMVectorTrueInt())) {
2975                return BACK;
2976            }
2977        }
2978
2979        // The box is not inside all planes or outside a plane it intersects.
2980        return INTERSECTING;
2981    }
2982
2983    /// Tests the BoundingOrientedBox for intersection with a ray.
2984    ///
2985    /// ## Parameters
2986    ///
2987    /// `Origin` The origin of the ray.
2988    ///
2989    /// `Direction` The direction of the ray.
2990    ///
2991    /// `Dist` The length of the ray.
2992    ///
2993    /// ## Return value
2994    ///
2995    /// A boolean value indicating whether the BoundingOrientedBox intersects the ray.
2996    ///
2997    /// ## Remarks
2998    ///
2999    /// The distance from the `Origin` to the nearest intersection point is returned
3000    /// in `Dist` when the method returns `true`. Otherwise, `Dist` is set to `0.0`.
3001    ///
3002    /// ## Reference
3003    ///
3004    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-intersects(fxmvector_fxmvector_float_)>
3005    #[inline]
3006    pub fn IntersectsRay(&self, Origin: FXMVECTOR, Direction: FXMVECTOR, Dist: &mut f32) -> bool {
3007        unsafe {
3008            debug_assert!(internal::XMVector3IsUnit(Direction));
3009
3010            const SelectY: XMVECTORU32 = XMVECTORU32 { u: [ XM_SELECT_0, XM_SELECT_1, XM_SELECT_0, XM_SELECT_0 ] };
3011            const SelectZ: XMVECTORU32 = XMVECTORU32 { u: [ XM_SELECT_0, XM_SELECT_0, XM_SELECT_1, XM_SELECT_0 ] };
3012
3013            // Load the box.
3014            let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
3015            let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
3016            let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
3017
3018            debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
3019
3020            // Get the boxes normalized side directions.
3021            let R: XMMATRIX = XMMatrixRotationQuaternion(vOrientation);
3022
3023            // Adjust ray origin to be relative to center of the box.
3024            let TOrigin: XMVECTOR = XMVectorSubtract(vCenter, Origin);
3025
3026            // Compute the dot product againt each axis of the box.
3027            let mut AxisDotOrigin: XMVECTOR = XMVector3Dot(R.r[0], TOrigin);
3028            AxisDotOrigin = XMVectorSelect(AxisDotOrigin, XMVector3Dot(R.r[1], TOrigin), SelectY.v);
3029            AxisDotOrigin = XMVectorSelect(AxisDotOrigin, XMVector3Dot(R.r[2], TOrigin), SelectZ.v);
3030
3031            let mut AxisDotDirection: XMVECTOR = XMVector3Dot(R.r[0], Direction);
3032            AxisDotDirection = XMVectorSelect(AxisDotDirection, XMVector3Dot(R.r[1], Direction), SelectY.v);
3033            AxisDotDirection = XMVectorSelect(AxisDotDirection, XMVector3Dot(R.r[2], Direction), SelectZ.v);
3034
3035            // if (fabs(AxisDotDirection) <= Epsilon) the ray is nearly parallel to the slab.
3036            let IsParallel: XMVECTOR = XMVectorLessOrEqual(XMVectorAbs(AxisDotDirection), g_RayEpsilon.v);
3037
3038            // Test against all three axes simultaneously.
3039            let InverseAxisDotDirection: XMVECTOR = XMVectorReciprocal(AxisDotDirection);
3040            let t1: XMVECTOR = XMVectorMultiply(XMVectorSubtract(AxisDotOrigin, vExtents), InverseAxisDotDirection);
3041            let t2: XMVECTOR = XMVectorMultiply(XMVectorAdd(AxisDotOrigin, vExtents), InverseAxisDotDirection);
3042
3043            // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't
3044            // use the results from any directions parallel to the slab.
3045            let mut t_min: XMVECTOR = XMVectorSelect(XMVectorMin(t1, t2), g_FltMin.v, IsParallel);
3046            let mut t_max: XMVECTOR = XMVectorSelect(XMVectorMax(t1, t2), g_FltMax.v, IsParallel);
3047
3048            // t_min.x = maximum( t_min.x, t_min.y, t_min.z );
3049            // t_max.x = minimum( t_max.x, t_max.y, t_max.z );
3050            t_min = XMVectorMax(t_min, XMVectorSplatY(t_min));  // x = max(x,y)
3051            t_min = XMVectorMax(t_min, XMVectorSplatZ(t_min));  // x = max(max(x,y),z)
3052            t_max = XMVectorMin(t_max, XMVectorSplatY(t_max));  // x = min(x,y)
3053            t_max = XMVectorMin(t_max, XMVectorSplatZ(t_max));  // x = min(min(x,y),z)
3054
3055            // if ( t_min > t_max ) return false;
3056            let mut NoIntersection: XMVECTOR = XMVectorGreater(XMVectorSplatX(t_min), XMVectorSplatX(t_max));
3057
3058            // if ( t_max < 0.0f ) return false;
3059            NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(XMVectorSplatX(t_max), XMVectorZero()));
3060
3061            // if (IsParallel && (-Extents > AxisDotOrigin || Extents < AxisDotOrigin)) return false;
3062            let ParallelOverlap: XMVECTOR = XMVectorInBounds(AxisDotOrigin, vExtents);
3063            NoIntersection = XMVectorOrInt(NoIntersection, XMVectorAndCInt(IsParallel, ParallelOverlap));
3064
3065            if (!internal::XMVector3AnyTrue(NoIntersection))
3066            {
3067                // Store the x-component to *pDist
3068                XMStoreFloat(Dist, t_min);
3069                return true;
3070            }
3071
3072            *Dist = 0.0;
3073            return false;
3074        }
3075    }
3076
3077    /// Tests whether the BoundingOrientedBox is contained by the specified frustum.
3078    ///
3079    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-containedby>
3080    #[inline]
3081    pub fn ContainedBy(
3082        &self,
3083        Plane0: FXMVECTOR,
3084        Plane1: FXMVECTOR,
3085        Plane2: GXMVECTOR,
3086        Plane3: HXMVECTOR,
3087        Plane4: HXMVECTOR,
3088        Plane5: HXMVECTOR,
3089    ) -> ContainmentType
3090    {
3091        // Load the box.
3092        let mut vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
3093        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
3094        let BoxOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
3095
3096        debug_assert!(internal::XMQuaternionIsUnit(BoxOrientation));
3097
3098        // Set w of the center to one so we can dot4 with a plane.
3099        // TODO: template
3100        vCenter = XMVectorInsert(vCenter, XMVectorSplatOne(), 0, 0, 0, 0, 1);
3101
3102        // Build the 3x3 rotation matrix that defines the box axes.
3103        let R: XMMATRIX = XMMatrixRotationQuaternion(BoxOrientation);
3104
3105        unsafe {
3106            let mut Outside: XMVECTOR = undefined();
3107            let mut Inside: XMVECTOR = undefined();
3108
3109            // Test against each plane.
3110            internal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane0, &mut Outside, &mut Inside);
3111
3112            let mut AnyOutside: XMVECTOR = Outside;
3113            let mut AllInside: XMVECTOR = Inside;
3114
3115            internal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane1, &mut Outside, &mut Inside);
3116            AnyOutside = XMVectorOrInt(AnyOutside, Outside);
3117            AllInside = XMVectorAndInt(AllInside, Inside);
3118
3119            internal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane2, &mut Outside, &mut Inside);
3120            AnyOutside = XMVectorOrInt(AnyOutside, Outside);
3121            AllInside = XMVectorAndInt(AllInside, Inside);
3122
3123            internal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane3, &mut Outside, &mut Inside);
3124            AnyOutside = XMVectorOrInt(AnyOutside, Outside);
3125            AllInside = XMVectorAndInt(AllInside, Inside);
3126
3127            internal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane4, &mut Outside, &mut Inside);
3128            AnyOutside = XMVectorOrInt(AnyOutside, Outside);
3129            AllInside = XMVectorAndInt(AllInside, Inside);
3130
3131            internal::FastIntersectOrientedBoxPlane(vCenter, vExtents, R.r[0], R.r[1], R.r[2], Plane5, &mut Outside, &mut Inside);
3132            AnyOutside = XMVectorOrInt(AnyOutside, Outside);
3133            AllInside = XMVectorAndInt(AllInside, Inside);
3134
3135            // If the box is outside any plane it is outside.
3136            if (XMVector4EqualInt(AnyOutside, XMVectorTrueInt())) {
3137                return DISJOINT;
3138            }
3139
3140            // If the box is inside all planes it is inside.
3141            if (XMVector4EqualInt(AllInside, XMVectorTrueInt())) {
3142                return CONTAINS;
3143            }
3144        }
3145
3146        // The box is not inside all planes or outside a plane, it may intersect.
3147        return INTERSECTS;
3148    }
3149
3150    /// Create oriented bounding box from axis-aligned bounding box
3151    ///
3152    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-CreateFromBoundingBox>
3153    #[inline]
3154    pub fn CreateFromBoundingBox(Out: &mut Self, box_: &BoundingBox) {
3155        Out.Center = box_.Center;
3156        Out.Extents = box_.Extents;
3157        Out.Orientation = XMFLOAT4::set(0.0, 0.0, 0.0, 1.0);
3158    }
3159
3160    /// Creates a new BoundingOrientedBox from a list of points.
3161    ///
3162    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingOrientedBox-createfrompoints>
3163    #[inline]
3164    pub fn CreateFromPoints<'a>(Out: &mut Self, pPoints: impl Iterator<Item=&'a XMFLOAT3> + Clone) {
3165        //-----------------------------------------------------------------------------
3166        // Find the approximate minimum oriented bounding box containing a set of
3167        // points.  Exact computation of minimum oriented bounding box is possible but
3168        // is slower and requires a more complex algorithm.
3169        // The algorithm works by computing the inertia tensor of the points and then
3170        // using the eigenvectors of the intertia tensor as the axes of the box.
3171        // Computing the intertia tensor of the convex hull of the points will usually
3172        // result in better bounding box but the computation is more complex.
3173        // Exact computation of the minimum oriented bounding box is possible but the
3174        // best know algorithm is O(N^3) and is significanly more complex to implement.
3175        //-----------------------------------------------------------------------------
3176
3177        let Count = pPoints.clone().count();
3178
3179        if Count == 0 {
3180            Out.Extents = XMFLOAT3::set(std::f32::NAN, std::f32::NAN, std::f32::NAN);
3181            Out.Orientation = XMFLOAT4::set(0.0, 0.0, 0.0, 1.0);
3182            return;
3183        }
3184
3185        // assert(Count > 0);
3186        // assert(pPoints != nullptr);
3187
3188        let mut CenterOfMass: XMVECTOR = XMVectorZero();
3189
3190        // Compute the center of mass and inertia tensor of the points.
3191        //for (let i: size_t = 0; i < Count; ++i)
3192        for point in pPoints.clone()
3193        {
3194            //let Point: XMVECTOR = XMLoadFloat3(reinterpret_cast<const XMFLOAT3*>(reinterpret_cast<const uint8_t*>(pPoints) + i * Stride));
3195            let Point: XMVECTOR = XMLoadFloat3(point);
3196
3197            CenterOfMass = XMVectorAdd(CenterOfMass, Point);
3198        }
3199
3200        CenterOfMass = XMVectorMultiply(CenterOfMass, XMVectorReciprocal(XMVectorReplicate(Count as f32)));
3201
3202        // Compute the inertia tensor of the points around the center of mass.
3203        // Using the center of mass is not strictly necessary, but will hopefully
3204        // improve the stability of finding the eigenvectors.
3205        let mut XX_YY_ZZ: XMVECTOR = XMVectorZero();
3206        let mut XY_XZ_YZ: XMVECTOR = XMVectorZero();
3207
3208        //for (let i: size_t = 0; i < Count; ++i)
3209        for point in pPoints.clone().into_iter()
3210        {
3211            let Point: XMVECTOR = XMVectorSubtract(XMLoadFloat3(point), CenterOfMass);
3212
3213            XX_YY_ZZ = XMVectorAdd(XX_YY_ZZ, XMVectorMultiply(Point, Point));
3214
3215            let XXY: XMVECTOR = <(XM_SWIZZLE_X, XM_SWIZZLE_X, XM_SWIZZLE_Y, XM_SWIZZLE_W)>::XMVectorSwizzle(Point);
3216            let YZZ: XMVECTOR = <(XM_SWIZZLE_Y, XM_SWIZZLE_Z, XM_SWIZZLE_Z, XM_SWIZZLE_W)>::XMVectorSwizzle(Point);
3217
3218            XY_XZ_YZ = XMVectorAdd(XY_XZ_YZ, XMVectorMultiply(XXY, YZZ));
3219        }
3220
3221        let mut v1: XMVECTOR = unsafe { undefined() };
3222        let mut v2: XMVECTOR = unsafe { undefined() };
3223        let mut v3: XMVECTOR = unsafe { undefined() };
3224
3225        // Compute the eigenvectors of the inertia tensor.
3226        internal::CalculateEigenVectorsFromCovarianceMatrix(XMVectorGetX(XX_YY_ZZ), XMVectorGetY(XX_YY_ZZ),
3227            XMVectorGetZ(XX_YY_ZZ),
3228            XMVectorGetX(XY_XZ_YZ), XMVectorGetY(XY_XZ_YZ),
3229            XMVectorGetZ(XY_XZ_YZ),
3230            &mut v1, &mut v2, &mut v3);
3231
3232        // Put them in a matrix.
3233        let mut R: XMMATRIX = unsafe { undefined() };
3234
3235        unsafe {
3236            R.r[0] = XMVectorSetW(v1, 0.0);
3237            R.r[1] = XMVectorSetW(v2, 0.0);
3238            R.r[2] = XMVectorSetW(v3, 0.0);
3239            R.r[3] = g_XMIdentityR3.v;
3240        }
3241
3242        // Multiply by -1 to convert the matrix into a right handed coordinate
3243        // system (Det ~= 1) in case the eigenvectors form a left handed
3244        // coordinate system (Det ~= -1) because XMQuaternionRotationMatrix only
3245        // works on right handed matrices.
3246        let Det: XMVECTOR = XMMatrixDeterminant(R);
3247
3248        if (XMVector4Less(Det, XMVectorZero()))
3249        {
3250            unsafe {
3251                R.r[0] = XMVectorMultiply(R.r[0], g_XMNegativeOne.v);
3252                R.r[1] = XMVectorMultiply(R.r[1], g_XMNegativeOne.v);
3253                R.r[2] = XMVectorMultiply(R.r[2], g_XMNegativeOne.v);
3254            }
3255        }
3256
3257        // Get the rotation quaternion from the matrix.
3258        let mut vOrientation: XMVECTOR = XMQuaternionRotationMatrix(R);
3259
3260        // Make sure it is normal (in case the vectors are slightly non-orthogonal).
3261        vOrientation = XMQuaternionNormalize(vOrientation);
3262
3263        // Rebuild the rotation matrix from the quaternion.
3264        R = XMMatrixRotationQuaternion(vOrientation);
3265
3266        // Build the rotation into the rotated space.
3267        let InverseR: XMMATRIX = XMMatrixTranspose(R);
3268
3269        // Find the minimum OBB using the eigenvectors as the axes.
3270        let mut vMin: XMVECTOR = XMVectorZero();
3271        let mut vMax: XMVECTOR = XMVectorZero();
3272
3273        //for (let i: size_t = 1; i < Count; ++i)
3274        for (i, point) in pPoints.enumerate()
3275        {
3276            let Point: XMVECTOR = XMLoadFloat3(point);
3277
3278            if i == 0 {
3279                vMin = XMVector3TransformNormal(Point, InverseR);
3280                vMax = vMin;
3281            } else {
3282                vMin = XMVectorMin(vMin, Point);
3283                vMax = XMVectorMax(vMax, Point);
3284            }
3285        }
3286
3287        // Rotate the center into world space.
3288        let mut vCenter: XMVECTOR = XMVectorScale(XMVectorAdd(vMin, vMax), 0.5);
3289        vCenter = XMVector3TransformNormal(vCenter, R);
3290
3291        // Store center, extents, and orientation.
3292        XMStoreFloat3(&mut Out.Center, vCenter);
3293        XMStoreFloat3(&mut Out.Extents, XMVectorScale(XMVectorSubtract(vMax, vMin), 0.5));
3294        XMStoreFloat4(&mut Out.Orientation, vOrientation);
3295    }
3296
3297    ///  Retrieves the corners of the BoundingOrientedBox.
3298    ///
3299    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingorientedbox-getcorners>
3300    #[inline]
3301    pub fn GetCorners(&self, Corners: &mut [XMFLOAT3; 8]) {
3302        // Load the box
3303        let vCenter: XMVECTOR = XMLoadFloat3(&self.Center);
3304        let vExtents: XMVECTOR = XMLoadFloat3(&self.Extents);
3305        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
3306
3307        debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
3308
3309        //for (size_t i = 0; i < CORNER_COUNT; ++i)
3310        for i in 0 .. Self::CORNER_COUNT
3311        {
3312            let C: XMVECTOR = XMVectorAdd(XMVector3Rotate(XMVectorMultiply(vExtents, g_BoxOffset[i].v()), vOrientation), vCenter);
3313            XMStoreFloat3(&mut Corners[i], C);
3314        }
3315    }
3316}
3317
3318#[test]
3319fn test_BoundingOrientedBox_CreateFromPoints() {
3320    let mut bounds: BoundingOrientedBox = BoundingOrientedBox::default();
3321    let points = &[
3322        XMFLOAT3::set(-1.0, -1.0, -1.0),
3323        XMFLOAT3::set( 1.0,  1.0,  1.0),
3324    ];
3325    BoundingOrientedBox::CreateFromPoints(&mut bounds, points.iter());
3326    assert_eq!(ContainmentType::CONTAINS, bounds.ContainsPoint(XMVectorSet(0.0, 0.0, 0.0, 0.0)));
3327    assert_eq!(ContainmentType::CONTAINS, bounds.ContainsPoint(XMVectorSet(1.0, 1.0, 1.0, 0.0)));
3328    assert_eq!(ContainmentType::DISJOINT, bounds.ContainsPoint(XMVectorSet(2.0, 2.0, 2.0, 0.0)));
3329
3330    let mut bounds: BoundingOrientedBox = BoundingOrientedBox::default();
3331    let points = &[];
3332    BoundingOrientedBox::CreateFromPoints(&mut bounds, points.iter());
3333    assert_eq!(ContainmentType::DISJOINT, bounds.ContainsPoint(XMVectorSet(0.0, 0.0, 0.0, 0.0)));
3334}
3335
3336// BoundingFrustum ----------------------------------------------------------------
3337
3338impl BoundingFrustum {
3339    /// The number of corners defining the BoundingFrustum.
3340    pub const CORNER_COUNT: usize = 8;
3341
3342    /// Transforms the BoundingFrustum by the specified transformation matrix.
3343    ///
3344    /// ## Parameters
3345    ///
3346    /// `Out` The transformed BoundingFrustum.
3347    ///
3348    /// `M` The transformation matrix.
3349    ///
3350    /// **Note** The transformation matrix cannot contain a scale transform.
3351    ///
3352    /// ## Return value
3353    ///
3354    /// This method does not return a value.
3355    ///
3356    /// ## Reference
3357    ///
3358    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-transform>
3359    #[inline]
3360    pub fn TransformMatrix(&self, Out: &mut Self, M: FXMMATRIX) {
3361        // Load the frustum.
3362        let mut vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
3363        let mut vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
3364
3365        debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
3366
3367        unsafe {
3368            // Composite the frustum rotation and the transform rotation
3369            let mut nM: XMMATRIX = undefined();
3370            nM.r[0] = XMVector3Normalize(M.r[0]);
3371            nM.r[1] = XMVector3Normalize(M.r[1]);
3372            nM.r[2] = XMVector3Normalize(M.r[2]);
3373            nM.r[3] = g_XMIdentityR3.v;
3374            let Rotation: XMVECTOR = XMQuaternionRotationMatrix(nM);
3375            vOrientation = XMQuaternionMultiply(vOrientation, Rotation);
3376
3377            // Transform the center.
3378            vOrigin = XMVector3Transform(vOrigin, M);
3379
3380            // Store the frustum.
3381            XMStoreFloat3(&mut Out.Origin, vOrigin);
3382            XMStoreFloat4(&mut Out.Orientation, vOrientation);
3383
3384            // Scale the near and far distances (the slopes remain the same).
3385            let dX: XMVECTOR = XMVector3Dot(M.r[0], M.r[0]);
3386            let dY: XMVECTOR = XMVector3Dot(M.r[1], M.r[1]);
3387            let dZ: XMVECTOR = XMVector3Dot(M.r[2], M.r[2]);
3388
3389            let d: XMVECTOR = XMVectorMax(dX, XMVectorMax(dY, dZ));
3390            let Scale: f32 = sqrtf(XMVectorGetX(d));
3391
3392            Out.Near = self.Near * Scale;
3393            Out.Far = self.Far * Scale;
3394        }
3395
3396        // Copy the slopes.
3397        Out.RightSlope = self.RightSlope;
3398        Out.LeftSlope = self.LeftSlope;
3399        Out.TopSlope = self.TopSlope;
3400        Out.BottomSlope = self.BottomSlope;
3401    }
3402
3403    /// Transforms the BoundingFrustum using the specified `scale`, `rotation` and `translation` vectors.
3404    ///
3405    /// ## Parameters
3406    ///
3407    /// `Out` The transformed BoundingFrustum.
3408    ///
3409    /// `Scale` The value to scale the BoundingFrustum by.
3410    ///
3411    /// `Rotation` The value to rotate the BoundingFrustum by.
3412    ///
3413    /// `Translation` The value to translate the BoundingFrustum by.
3414    ///
3415    /// ## Return value
3416    ///
3417    /// This method does not return a value.
3418    ///
3419    /// ## Reference
3420    ///
3421    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-transform(BoundingFrustum__float_fxmvector_fxmvector)>
3422    #[inline]
3423    pub fn TransformDecomposed(&self, Out: &mut Self, Scale: f32, Rotation: FXMVECTOR, Translation: FXMVECTOR) {
3424        debug_assert!(internal::XMQuaternionIsUnit(Rotation));
3425
3426        // Load the frustum.
3427        let mut vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
3428        let mut vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
3429
3430        debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
3431
3432        // Composite the frustum rotation and the transform rotation.
3433        vOrientation = XMQuaternionMultiply(vOrientation, Rotation);
3434
3435        // Transform the origin.
3436        vOrigin = XMVectorAdd(XMVector3Rotate(XMVectorScale(vOrigin, Scale), Rotation), Translation);
3437
3438        // Store the frustum.
3439        XMStoreFloat3(&mut Out.Origin, vOrigin);
3440        XMStoreFloat4(&mut Out.Orientation, vOrientation);
3441
3442        // Scale the near and far distances (the slopes remain the same).
3443        Out.Near = self.Near * Scale;
3444        Out.Far = self.Far * Scale;
3445
3446        // Copy the slopes.
3447        Out.RightSlope = self.RightSlope;
3448        Out.LeftSlope = self.LeftSlope;
3449        Out.TopSlope = self.TopSlope;
3450        Out.BottomSlope = self.BottomSlope;
3451    }
3452
3453    /// Tests whether the BoundingFrustum contains the specified point.
3454    ///
3455    /// ## Parameters
3456    ///
3457    /// `Point` The point to test against.
3458    ///
3459    /// ## Return value
3460    ///
3461    /// A ContainmentType value indicating whether the point is contained in the BoundingFrustum.
3462    ///
3463    /// ## Reference
3464    ///
3465    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-contains>
3466    #[inline]
3467    pub fn ContainsPoint(&self, Point: FXMVECTOR) -> ContainmentType {
3468        // Build frustum planes.
3469        let mut Planes: [XMVECTOR; 6] = unsafe { undefined() };
3470        Planes[0] = XMVectorSet(0.0, 0.0, -1.0, self.Near);
3471        Planes[1] = XMVectorSet(0.0, 0.0, 1.0, -self.Far);
3472        Planes[2] = XMVectorSet(1.0, 0.0, -self.RightSlope, 0.0);
3473        Planes[3] = XMVectorSet(-1.0, 0.0, self.LeftSlope, 0.0);
3474        Planes[4] = XMVectorSet(0.0, 1.0, -self.TopSlope, 0.0);
3475        Planes[5] = XMVectorSet(0.0, -1.0, self.BottomSlope, 0.0);
3476
3477        // Load origin and orientation.
3478        let vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
3479        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
3480
3481        debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
3482
3483        // Transform point into local space of frustum.
3484        let mut TPoint: XMVECTOR = XMVector3InverseRotate(XMVectorSubtract(Point, vOrigin), vOrientation);
3485
3486        // Set w to one.
3487        // TODO: template
3488        TPoint = XMVectorInsert(TPoint, XMVectorSplatOne(), 0, 0, 0, 0, 1);
3489
3490        let Zero: XMVECTOR = XMVectorZero();
3491        let mut Outside: XMVECTOR = Zero;
3492
3493        // Test point against each plane of the frustum.
3494        //for (let i: size_t = 0; i < 6; ++i)
3495        for i in 0 .. 6
3496        {
3497            let Dot: XMVECTOR = XMVector4Dot(TPoint, Planes[i]);
3498            Outside = XMVectorOrInt(Outside, XMVectorGreater(Dot, Zero));
3499        }
3500
3501        return if XMVector4NotEqualInt(Outside, XMVectorTrueInt()) { CONTAINS } else { DISJOINT };
3502    }
3503
3504    /// Tests whether the BoundingFrustum contains the specified triangle.
3505    ///
3506    /// ## Parameters
3507    ///
3508    /// `V0` A corner of the triangle.
3509    ///
3510    /// `V1` A corner of the triangle.
3511    ///
3512    /// `V2` A corner of the triangle.
3513    ///
3514    /// ## Return value
3515    ///
3516    /// A ContainmentType value indicating whether the triangle is contained in the BoundingFrustum.
3517    ///
3518    /// ## Reference
3519    ///
3520    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-contains(fxmvector_fxmvector_fxmvector)>
3521    #[inline]
3522    pub fn ContainsTriangle(&self, V0: FXMVECTOR, V1: FXMVECTOR, V2: FXMVECTOR) -> ContainmentType {
3523        // Load origin and orientation of the frustum.
3524        let vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
3525        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
3526
3527        // Create 6 planes (do it inline to encourage use of registers)
3528        let mut NearPlane: XMVECTOR = XMVectorSet(0.0, 0.0, -1.0, self.Near);
3529        NearPlane = internal::XMPlaneTransform(NearPlane, vOrientation, vOrigin);
3530        NearPlane = XMPlaneNormalize(NearPlane);
3531
3532        let mut FarPlane: XMVECTOR = XMVectorSet(0.0, 0.0, 1.0, -self.Far);
3533        FarPlane = internal::XMPlaneTransform(FarPlane, vOrientation, vOrigin);
3534        FarPlane = XMPlaneNormalize(FarPlane);
3535
3536        let mut RightPlane: XMVECTOR = XMVectorSet(1.0, 0.0, -self.RightSlope, 0.0);
3537        RightPlane = internal::XMPlaneTransform(RightPlane, vOrientation, vOrigin);
3538        RightPlane = XMPlaneNormalize(RightPlane);
3539
3540        let mut LeftPlane: XMVECTOR = XMVectorSet(-1.0, 0.0, self.LeftSlope, 0.0);
3541        LeftPlane = internal::XMPlaneTransform(LeftPlane, vOrientation, vOrigin);
3542        LeftPlane = XMPlaneNormalize(LeftPlane);
3543
3544        let mut TopPlane: XMVECTOR = XMVectorSet(0.0, 1.0, -self.TopSlope, 0.0);
3545        TopPlane = internal::XMPlaneTransform(TopPlane, vOrientation, vOrigin);
3546        TopPlane = XMPlaneNormalize(TopPlane);
3547
3548        let mut BottomPlane: XMVECTOR = XMVectorSet(0.0, -1.0, self.BottomSlope, 0.0);
3549        BottomPlane = internal::XMPlaneTransform(BottomPlane, vOrientation, vOrigin);
3550        BottomPlane = XMPlaneNormalize(BottomPlane);
3551
3552        return triangle_tests::ContainedBy(V0, V1, V2, NearPlane, FarPlane, RightPlane, &LeftPlane, &TopPlane, &BottomPlane);
3553    }
3554
3555    /// Tests whether the BoundingFrustum contains a specified BoundingSphere.
3556    ///
3557    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-contains(constboundingsphere_)>
3558    #[inline]
3559    pub fn ContainsSphere(&self, sh: &BoundingSphere) -> ContainmentType {
3560        // Load origin and orientation of the frustum.
3561        let vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
3562        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
3563
3564        // Create 6 planes (do it inline to encourage use of registers)
3565        let mut NearPlane: XMVECTOR = XMVectorSet(0.0, 0.0, -1.0, self.Near);
3566        NearPlane = internal::XMPlaneTransform(NearPlane, vOrientation, vOrigin);
3567        NearPlane = XMPlaneNormalize(NearPlane);
3568
3569        let mut FarPlane: XMVECTOR = XMVectorSet(0.0, 0.0, 1.0, -self.Far);
3570        FarPlane = internal::XMPlaneTransform(FarPlane, vOrientation, vOrigin);
3571        FarPlane = XMPlaneNormalize(FarPlane);
3572
3573        let mut RightPlane: XMVECTOR = XMVectorSet(1.0, 0.0, -self.RightSlope, 0.0);
3574        RightPlane = internal::XMPlaneTransform(RightPlane, vOrientation, vOrigin);
3575        RightPlane = XMPlaneNormalize(RightPlane);
3576
3577        let mut LeftPlane: XMVECTOR = XMVectorSet(-1.0, 0.0, self.LeftSlope, 0.0);
3578        LeftPlane = internal::XMPlaneTransform(LeftPlane, vOrientation, vOrigin);
3579        LeftPlane = XMPlaneNormalize(LeftPlane);
3580
3581        let mut TopPlane: XMVECTOR = XMVectorSet(0.0, 1.0, -self.TopSlope, 0.0);
3582        TopPlane = internal::XMPlaneTransform(TopPlane, vOrientation, vOrigin);
3583        TopPlane = XMPlaneNormalize(TopPlane);
3584
3585        let mut BottomPlane: XMVECTOR = XMVectorSet(0.0, -1.0, self.BottomSlope, 0.0);
3586        BottomPlane = internal::XMPlaneTransform(BottomPlane, vOrientation, vOrigin);
3587        BottomPlane = XMPlaneNormalize(BottomPlane);
3588
3589        return sh.ContainedBy(NearPlane, FarPlane, RightPlane, LeftPlane, TopPlane, BottomPlane);
3590    }
3591
3592    /// Tests whether the BoundingFrustum contains a specified BoundingBox.
3593    ///
3594    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-contains(constboundingbox_)>
3595    #[inline]
3596    pub fn ContainsBox(&self, box_: &BoundingBox) -> ContainmentType {
3597        // Load origin and orientation of the frustum.
3598        let vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
3599        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
3600
3601        // Create 6 planes (do it inline to encourage use of registers)
3602        let mut NearPlane: XMVECTOR = XMVectorSet(0.0, 0.0, -1.0, self.Near);
3603        NearPlane = internal::XMPlaneTransform(NearPlane, vOrientation, vOrigin);
3604        NearPlane = XMPlaneNormalize(NearPlane);
3605
3606        let mut FarPlane: XMVECTOR = XMVectorSet(0.0, 0.0, 1.0, -self.Far);
3607        FarPlane = internal::XMPlaneTransform(FarPlane, vOrientation, vOrigin);
3608        FarPlane = XMPlaneNormalize(FarPlane);
3609
3610        let mut RightPlane: XMVECTOR = XMVectorSet(1.0, 0.0, -self.RightSlope, 0.0);
3611        RightPlane = internal::XMPlaneTransform(RightPlane, vOrientation, vOrigin);
3612        RightPlane = XMPlaneNormalize(RightPlane);
3613
3614        let mut LeftPlane: XMVECTOR = XMVectorSet(-1.0, 0.0, self.LeftSlope, 0.0);
3615        LeftPlane = internal::XMPlaneTransform(LeftPlane, vOrientation, vOrigin);
3616        LeftPlane = XMPlaneNormalize(LeftPlane);
3617
3618        let mut TopPlane: XMVECTOR = XMVectorSet(0.0, 1.0, -self.TopSlope, 0.0);
3619        TopPlane = internal::XMPlaneTransform(TopPlane, vOrientation, vOrigin);
3620        TopPlane = XMPlaneNormalize(TopPlane);
3621
3622        let mut BottomPlane: XMVECTOR = XMVectorSet(0.0, -1.0, self.BottomSlope, 0.0);
3623        BottomPlane = internal::XMPlaneTransform(BottomPlane, vOrientation, vOrigin);
3624        BottomPlane = XMPlaneNormalize(BottomPlane);
3625
3626        return box_.ContainedBy(NearPlane, FarPlane, RightPlane, LeftPlane, TopPlane, BottomPlane);
3627    }
3628
3629    /// Tests whether the BoundingFrustum contains the specified BoundingOrientedBox.
3630    ///
3631    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-contains(constboundingorientedbox_)>
3632    #[inline]
3633    pub fn ContainsOrientedBox(&self, box_: &BoundingOrientedBox) -> ContainmentType {
3634        // Load origin and orientation of the frustum.
3635        let vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
3636        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
3637
3638        // Create 6 planes (do it inline to encourage use of registers)
3639        let mut NearPlane: XMVECTOR = XMVectorSet(0.0, 0.0, -1.0, self.Near);
3640        NearPlane = internal::XMPlaneTransform(NearPlane, vOrientation, vOrigin);
3641        NearPlane = XMPlaneNormalize(NearPlane);
3642
3643        let mut FarPlane: XMVECTOR = XMVectorSet(0.0, 0.0, 1.0, -self.Far);
3644        FarPlane = internal::XMPlaneTransform(FarPlane, vOrientation, vOrigin);
3645        FarPlane = XMPlaneNormalize(FarPlane);
3646
3647        let mut RightPlane: XMVECTOR = XMVectorSet(1.0, 0.0, -self.RightSlope, 0.0);
3648        RightPlane = internal::XMPlaneTransform(RightPlane, vOrientation, vOrigin);
3649        RightPlane = XMPlaneNormalize(RightPlane);
3650
3651        let mut LeftPlane: XMVECTOR = XMVectorSet(-1.0, 0.0, self.LeftSlope, 0.0);
3652        LeftPlane = internal::XMPlaneTransform(LeftPlane, vOrientation, vOrigin);
3653        LeftPlane = XMPlaneNormalize(LeftPlane);
3654
3655        let mut TopPlane: XMVECTOR = XMVectorSet(0.0, 1.0, -self.TopSlope, 0.0);
3656        TopPlane = internal::XMPlaneTransform(TopPlane, vOrientation, vOrigin);
3657        TopPlane = XMPlaneNormalize(TopPlane);
3658
3659        let mut BottomPlane: XMVECTOR = XMVectorSet(0.0, -1.0, self.BottomSlope, 0.0);
3660        BottomPlane = internal::XMPlaneTransform(BottomPlane, vOrientation, vOrigin);
3661        BottomPlane = XMPlaneNormalize(BottomPlane);
3662
3663        return box_.ContainedBy(NearPlane, FarPlane, RightPlane, LeftPlane, TopPlane, BottomPlane);
3664    }
3665
3666    /// Tests whether the BoundingFrustum contains the specified BoundingFrustum.
3667    ///
3668    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-contains(constboundingfrustum_)>
3669    #[inline]
3670    pub fn ContainsFrustum(&self, fr: &BoundingFrustum) -> ContainmentType {
3671        // Load origin and orientation of the frustum.
3672        let vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
3673        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
3674
3675        // Create 6 planes (do it inline to encourage use of registers)
3676        let mut NearPlane: XMVECTOR = XMVectorSet(0.0, 0.0, -1.0, self.Near);
3677        NearPlane = internal::XMPlaneTransform(NearPlane, vOrientation, vOrigin);
3678        NearPlane = XMPlaneNormalize(NearPlane);
3679
3680        let mut FarPlane: XMVECTOR = XMVectorSet(0.0, 0.0, 1.0, -self.Far);
3681        FarPlane = internal::XMPlaneTransform(FarPlane, vOrientation, vOrigin);
3682        FarPlane = XMPlaneNormalize(FarPlane);
3683
3684        let mut RightPlane: XMVECTOR = XMVectorSet(1.0, 0.0, -self.RightSlope, 0.0);
3685        RightPlane = internal::XMPlaneTransform(RightPlane, vOrientation, vOrigin);
3686        RightPlane = XMPlaneNormalize(RightPlane);
3687
3688        let mut LeftPlane: XMVECTOR = XMVectorSet(-1.0, 0.0, self.LeftSlope, 0.0);
3689        LeftPlane = internal::XMPlaneTransform(LeftPlane, vOrientation, vOrigin);
3690        LeftPlane = XMPlaneNormalize(LeftPlane);
3691
3692        let mut TopPlane: XMVECTOR = XMVectorSet(0.0, 1.0, -self.TopSlope, 0.0);
3693        TopPlane = internal::XMPlaneTransform(TopPlane, vOrientation, vOrigin);
3694        TopPlane = XMPlaneNormalize(TopPlane);
3695
3696        let mut BottomPlane: XMVECTOR = XMVectorSet(0.0, -1.0, self.BottomSlope, 0.0);
3697        BottomPlane = internal::XMPlaneTransform(BottomPlane, vOrientation, vOrigin);
3698        BottomPlane = XMPlaneNormalize(BottomPlane);
3699
3700        return fr.ContainedBy(NearPlane, FarPlane, RightPlane, LeftPlane, TopPlane, BottomPlane);
3701    }
3702
3703    /// Tests the BoundingFrustum for intersection with a BoundingSphere.
3704    ///
3705    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-intersects>
3706    #[inline]
3707    pub fn IntersectsSphere(&self, sh: &BoundingSphere) -> bool {
3708        //-----------------------------------------------------------------------------
3709        // Exact sphere vs frustum test.  The algorithm first checks the sphere against
3710        // the planes of the frustum, then if the plane checks were indeterminate finds
3711        // the nearest feature (plane, line, point) on the frustum to the center of the
3712        // sphere and compares the distance to the nearest feature to the radius of the
3713        // sphere
3714        //-----------------------------------------------------------------------------
3715
3716        let Zero: XMVECTOR = XMVectorZero();
3717
3718        // Build the frustum planes.
3719        let mut Planes: [XMVECTOR; 6] = unsafe { undefined() };
3720        Planes[0] = XMVectorSet(0.0, 0.0, -1.0, self.Near);
3721        Planes[1] = XMVectorSet(0.0, 0.0, 1.0, -self.Far);
3722        Planes[2] = XMVectorSet(1.0, 0.0, -self.RightSlope, 0.0);
3723        Planes[3] = XMVectorSet(-1.0, 0.0, self.LeftSlope, 0.0);
3724        Planes[4] = XMVectorSet(0.0, 1.0, -self.TopSlope, 0.0);
3725        Planes[5] = XMVectorSet(0.0, -1.0, self.BottomSlope, 0.0);
3726
3727        // Normalize the planes so we can compare to the sphere radius.
3728        Planes[2] = XMVector3Normalize(Planes[2]);
3729        Planes[3] = XMVector3Normalize(Planes[3]);
3730        Planes[4] = XMVector3Normalize(Planes[4]);
3731        Planes[5] = XMVector3Normalize(Planes[5]);
3732
3733        // Load origin and orientation of the frustum.
3734        let vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
3735        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
3736
3737        debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
3738
3739        // Load the sphere.
3740        let mut vCenter: XMVECTOR = XMLoadFloat3(&sh.Center);
3741        let vRadius: XMVECTOR = XMVectorReplicatePtr(&sh.Radius);
3742
3743        // Transform the center of the sphere into the local space of frustum.
3744        vCenter = XMVector3InverseRotate(XMVectorSubtract(vCenter, vOrigin), vOrientation);
3745
3746        // Set w of the center to one so we can dot4 with the plane.
3747        vCenter = XMVectorInsert(vCenter, XMVectorSplatOne(), 0, 0, 0, 0, 1);
3748
3749        // Check against each plane of the frustum.
3750        let mut Outside: XMVECTOR = XMVectorFalseInt();
3751        let mut InsideAll: XMVECTOR = XMVectorTrueInt();
3752        let mut CenterInsideAll: XMVECTOR = XMVectorTrueInt();
3753
3754        let mut Dist: [XMVECTOR; 6] = unsafe { undefined() };
3755
3756        //for (let i: size_t = 0; i < 6; ++i)
3757        for i in 0 .. 6
3758        {
3759            Dist[i] = XMVector4Dot(vCenter, Planes[i]);
3760
3761            // Outside the plane?
3762            Outside = XMVectorOrInt(Outside, XMVectorGreater(Dist[i], vRadius));
3763
3764            // Fully inside the plane?
3765            InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(Dist[i], XMVectorNegate(vRadius)));
3766
3767            // Check if the center is inside the plane.
3768            CenterInsideAll = XMVectorAndInt(CenterInsideAll, XMVectorLessOrEqual(Dist[i], Zero));
3769        }
3770
3771        // If the sphere is outside any of the planes it is outside.
3772        if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
3773            return false;
3774        }
3775
3776        // If the sphere is inside all planes it is fully inside.
3777        if (XMVector4EqualInt(InsideAll, XMVectorTrueInt())) {
3778            return true;
3779        }
3780
3781        // If the center of the sphere is inside all planes and the sphere intersects
3782        // one or more planes then it must intersect.
3783        if (XMVector4EqualInt(CenterInsideAll, XMVectorTrueInt())) {
3784            return true;
3785        }
3786
3787        // The sphere may be outside the frustum or intersecting the frustum.
3788        // Find the nearest feature (face, edge, or corner) on the frustum
3789        // to the sphere.
3790
3791        // The faces adjacent to each face are:
3792        //static const size_t adjacent_faces[6][4] =
3793        const adjacent_faces: [[usize; 4]; 6] = [
3794            [ 2, 3, 4, 5 ],    // 0
3795            [ 2, 3, 4, 5 ],    // 1
3796            [ 0, 1, 4, 5 ],    // 2
3797            [ 0, 1, 4, 5 ],    // 3
3798            [ 0, 1, 2, 3 ],    // 4
3799            [ 0, 1, 2, 3 ]
3800        ];  // 5
3801
3802        let mut Intersects: XMVECTOR = XMVectorFalseInt();
3803
3804        // Check to see if the nearest feature is one of the planes.
3805        //for (let i: size_t = 0; i < 6; ++i)
3806        for i in 0 .. 6
3807        {
3808            // Find the nearest point on the plane to the center of the sphere.
3809            let mut Point: XMVECTOR = XMVectorNegativeMultiplySubtract(Planes[i], Dist[i], vCenter);
3810
3811            // Set w of the point to one.
3812            Point = XMVectorInsert(Point, XMVectorSplatOne(), 0, 0, 0, 0, 1);
3813
3814            // If the point is inside the face (inside the adjacent planes) then
3815            // this plane is the nearest feature.
3816            let mut InsideFace: XMVECTOR = XMVectorTrueInt();
3817
3818            //for (let j: size_t = 0; j < 4; j++)
3819            for j in 0 .. 4
3820            {
3821                let plane_index: usize = adjacent_faces[i][j];
3822
3823                InsideFace = XMVectorAndInt(InsideFace,
3824                    XMVectorLessOrEqual(XMVector4Dot(Point, Planes[plane_index]), Zero));
3825            }
3826
3827            // Since we have already checked distance from the plane we know that the
3828            // sphere must intersect if this plane is the nearest feature.
3829            Intersects = XMVectorOrInt(Intersects,
3830                XMVectorAndInt(XMVectorGreater(Dist[i], Zero), InsideFace));
3831        }
3832
3833        if (XMVector4EqualInt(Intersects, XMVectorTrueInt())) {
3834            return true;
3835        }
3836
3837        // Build the corners of the frustum.
3838        let vRightTop: XMVECTOR = XMVectorSet(self.RightSlope, self.TopSlope, 1.0, 0.0);
3839        let vRightBottom: XMVECTOR = XMVectorSet(self.RightSlope, self.BottomSlope, 1.0, 0.0);
3840        let vLeftTop: XMVECTOR = XMVectorSet(self.LeftSlope, self.TopSlope, 1.0, 0.0);
3841        let vLeftBottom: XMVECTOR = XMVectorSet(self.LeftSlope, self.BottomSlope, 1.0, 0.0);
3842        let vNear: XMVECTOR = XMVectorReplicatePtr(&self.Near);
3843        let vFar: XMVECTOR = XMVectorReplicatePtr(&self.Far);
3844
3845        let mut Corners: [XMVECTOR; BoundingFrustum::CORNER_COUNT] = unsafe { undefined() };
3846        Corners[0] = XMVectorMultiply(vRightTop, vNear);
3847        Corners[1] = XMVectorMultiply(vRightBottom, vNear);
3848        Corners[2] = XMVectorMultiply(vLeftTop, vNear);
3849        Corners[3] = XMVectorMultiply(vLeftBottom, vNear);
3850        Corners[4] = XMVectorMultiply(vRightTop, vFar);
3851        Corners[5] = XMVectorMultiply(vRightBottom, vFar);
3852        Corners[6] = XMVectorMultiply(vLeftTop, vFar);
3853        Corners[7] = XMVectorMultiply(vLeftBottom, vFar);
3854
3855        // The Edges are:
3856        //static const size_t edges[12][2] =
3857        const edges: [[usize; 2]; 12] =
3858        [
3859            [ 0, 1 ], [ 2, 3 ], [ 0, 2 ], [ 1, 3 ],    // Near plane
3860            [ 4, 5 ], [ 6, 7 ], [ 4, 6 ], [ 5, 7 ],    // Far plane
3861            [ 0, 4 ], [ 1, 5 ], [ 2, 6 ], [ 3, 7 ],
3862        ]; // Near to far
3863
3864        let RadiusSq: XMVECTOR = XMVectorMultiply(vRadius, vRadius);
3865
3866        // Check to see if the nearest feature is one of the edges (or corners).
3867        //for (let i: size_t = 0; i < 12; ++i)
3868        for i in 0 .. 12
3869        {
3870            let ei0: usize = edges[i][0];
3871            let ei1: usize = edges[i][1];
3872
3873            // Find the nearest point on the edge to the center of the sphere.
3874            // The corners of the frustum are included as the endpoints of the edges.
3875            let Point: XMVECTOR = internal::PointOnLineSegmentNearestPoint(Corners[ei0], Corners[ei1], vCenter);
3876
3877            let Delta: XMVECTOR = XMVectorSubtract(vCenter, Point);
3878
3879            let DistSq: XMVECTOR = XMVector3Dot(Delta, Delta);
3880
3881            // If the distance to the center of the sphere to the point is less than
3882            // the radius of the sphere then it must intersect.
3883            Intersects = XMVectorOrInt(Intersects, XMVectorLessOrEqual(DistSq, RadiusSq));
3884        }
3885
3886        if (XMVector4EqualInt(Intersects, XMVectorTrueInt())) {
3887            return true;
3888        }
3889
3890        // The sphere must be outside the frustum.
3891        return false;
3892    }
3893
3894    /// Tests the BoundingFrustum for intersection with a BoundingBox.
3895    ///
3896    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-intersects(constboundingbox_)>
3897    #[inline]
3898    pub fn IntersectsBox(&self, box_: &BoundingBox) -> bool {
3899        // Make the axis aligned box oriented and do an OBB vs frustum test.
3900        let obox = BoundingOrientedBox {
3901            Center: box_.Center,
3902            Extents: box_.Extents,
3903            Orientation: XMFLOAT4::set(0.0, 0.0, 0.0, 1.0)
3904        };
3905        return self.IntersectsOrientedBox(&obox);
3906    }
3907
3908    /// Test the BoundingFrustum for intersection with a BoundingOrientedBox.
3909    ///
3910    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-intersects(constboundingorientedbox_)>
3911    #[inline]
3912    pub fn IntersectsOrientedBox(&self, box_: &BoundingOrientedBox) -> bool {
3913        const SelectY: XMVECTOR = unsafe { XMVECTORU32 { u: [ XM_SELECT_0, XM_SELECT_1, XM_SELECT_0, XM_SELECT_0 ] }.v };
3914        const SelectZ: XMVECTOR = unsafe { XMVECTORU32 { u: [ XM_SELECT_0, XM_SELECT_0, XM_SELECT_1, XM_SELECT_0 ] }.v };
3915
3916        let Zero: XMVECTOR = XMVectorZero();
3917
3918        // Build the frustum planes.
3919        let mut Planes: [XMVECTOR; 6] = unsafe { undefined() };
3920        Planes[0] = XMVectorSet(0.0, 0.0, -1.0, self.Near);
3921        Planes[1] = XMVectorSet(0.0, 0.0, 1.0, -self.Far);
3922        Planes[2] = XMVectorSet(1.0, 0.0, -self.RightSlope, 0.0);
3923        Planes[3] = XMVectorSet(-1.0, 0.0, self.LeftSlope, 0.0);
3924        Planes[4] = XMVectorSet(0.0, 1.0, -self.TopSlope, 0.0);
3925        Planes[5] = XMVectorSet(0.0, -1.0, self.BottomSlope, 0.0);
3926
3927        // Load origin and orientation of the frustum.
3928        let vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
3929        let FrustumOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
3930
3931        debug_assert!(internal::XMQuaternionIsUnit(FrustumOrientation));
3932
3933        // Load the box.
3934        let mut Center: XMVECTOR = XMLoadFloat3(&box_.Center);
3935        let Extents: XMVECTOR = XMLoadFloat3(&box_.Extents);
3936        let mut BoxOrientation: XMVECTOR = XMLoadFloat4(&box_.Orientation);
3937
3938        debug_assert!(internal::XMQuaternionIsUnit(BoxOrientation));
3939
3940        // Transform the oriented box into the space of the frustum in order to
3941        // minimize the number of transforms we have to do.
3942        Center = XMVector3InverseRotate(XMVectorSubtract(Center, vOrigin), FrustumOrientation);
3943        BoxOrientation = XMQuaternionMultiply(BoxOrientation, XMQuaternionConjugate(FrustumOrientation));
3944
3945        // Set w of the center to one so we can dot4 with the plane.
3946        Center = XMVectorInsert(Center, XMVectorSplatOne(), 0, 0, 0, 0, 1);
3947
3948        // Build the 3x3 rotation matrix that defines the box axes.
3949        let R: XMMATRIX = XMMatrixRotationQuaternion(BoxOrientation);
3950
3951        // Check against each plane of the frustum.
3952        let mut Outside: XMVECTOR = XMVectorFalseInt();
3953        let mut InsideAll: XMVECTOR = XMVectorTrueInt();
3954        let mut CenterInsideAll: XMVECTOR = XMVectorTrueInt();
3955
3956        //for (let i: size_t = 0; i < 6; ++i)
3957        for i in 0 .. 6
3958        {
3959            // Compute the distance to the center of the box.
3960            let Dist: XMVECTOR = XMVector4Dot(Center, Planes[i]);
3961
3962            // Project the axes of the box onto the normal of the plane.  Half the
3963            // length of the projection (sometime called the "radius") is equal to
3964            // h(u) * abs(n dot b(u))) + h(v) * abs(n dot b(v)) + h(w) * abs(n dot b(w))
3965            // where h(i) are extents of the box, n is the plane normal, and b(i) are the
3966            // axes of the box.
3967            unsafe {
3968                let mut Radius: XMVECTOR = XMVector3Dot(Planes[i], R.r[0]);
3969                Radius = XMVectorSelect(Radius, XMVector3Dot(Planes[i], R.r[1]), SelectY);
3970                Radius = XMVectorSelect(Radius, XMVector3Dot(Planes[i], R.r[2]), SelectZ);
3971                Radius = XMVector3Dot(Extents, XMVectorAbs(Radius));
3972
3973                // Outside the plane?
3974                Outside = XMVectorOrInt(Outside, XMVectorGreater(Dist, Radius));
3975
3976                // Fully inside the plane?
3977                InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(Dist, XMVectorNegate(Radius)));
3978
3979                // Check if the center is inside the plane.
3980                CenterInsideAll = XMVectorAndInt(CenterInsideAll, XMVectorLessOrEqual(Dist, Zero));
3981            }
3982        }
3983
3984        // If the box is outside any of the planes it is outside.
3985        if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
3986            return false;
3987        }
3988
3989        // If the box is inside all planes it is fully inside.
3990        if (XMVector4EqualInt(InsideAll, XMVectorTrueInt())) {
3991            return true;
3992        }
3993
3994        // If the center of the box is inside all planes and the box intersects
3995        // one or more planes then it must intersect.
3996        if (XMVector4EqualInt(CenterInsideAll, XMVectorTrueInt())) {
3997            return true;
3998        }
3999
4000        // Build the corners of the frustum.
4001        let vRightTop: XMVECTOR = XMVectorSet(self.RightSlope, self.TopSlope, 1.0, 0.0);
4002        let vRightBottom: XMVECTOR = XMVectorSet(self.RightSlope, self.BottomSlope, 1.0, 0.0);
4003        let vLeftTop: XMVECTOR = XMVectorSet(self.LeftSlope, self.TopSlope, 1.0, 0.0);
4004        let vLeftBottom: XMVECTOR = XMVectorSet(self.LeftSlope, self.BottomSlope, 1.0, 0.0);
4005        let vNear: XMVECTOR = XMVectorReplicatePtr(&self.Near);
4006        let vFar: XMVECTOR = XMVectorReplicatePtr(&self.Far);
4007
4008        let mut Corners: [XMVECTOR; Self::CORNER_COUNT] = unsafe { undefined() };
4009        Corners[0] = XMVectorMultiply(vRightTop, vNear);
4010        Corners[1] = XMVectorMultiply(vRightBottom, vNear);
4011        Corners[2] = XMVectorMultiply(vLeftTop, vNear);
4012        Corners[3] = XMVectorMultiply(vLeftBottom, vNear);
4013        Corners[4] = XMVectorMultiply(vRightTop, vFar);
4014        Corners[5] = XMVectorMultiply(vRightBottom, vFar);
4015        Corners[6] = XMVectorMultiply(vLeftTop, vFar);
4016        Corners[7] = XMVectorMultiply(vLeftBottom, vFar);
4017
4018        unsafe {
4019            // Test against box axes (3)
4020            {
4021                // Find the min/max values of the projection of the frustum onto each axis.
4022                let mut FrustumMin: XMVECTOR;
4023                let mut FrustumMax: XMVECTOR;
4024
4025                FrustumMin = XMVector3Dot(Corners[0], R.r[0]);
4026                FrustumMin = XMVectorSelect(FrustumMin, XMVector3Dot(Corners[0], R.r[1]), SelectY);
4027                FrustumMin = XMVectorSelect(FrustumMin, XMVector3Dot(Corners[0], R.r[2]), SelectZ);
4028                FrustumMax = FrustumMin;
4029
4030                //for (let i: size_t = 1; i < BoundingOrientedBox::CORNER_COUNT; ++i)
4031                for i in 1 .. BoundingOrientedBox::CORNER_COUNT
4032                {
4033                    let mut Temp: XMVECTOR = XMVector3Dot(Corners[i], R.r[0]);
4034                    Temp = XMVectorSelect(Temp, XMVector3Dot(Corners[i], R.r[1]), SelectY);
4035                    Temp = XMVectorSelect(Temp, XMVector3Dot(Corners[i], R.r[2]), SelectZ);
4036
4037                    FrustumMin = XMVectorMin(FrustumMin, Temp);
4038                    FrustumMax = XMVectorMax(FrustumMax, Temp);
4039                }
4040
4041                // Project the center of the box onto the axes.
4042                let mut BoxDist: XMVECTOR = XMVector3Dot(Center, R.r[0]);
4043                BoxDist = XMVectorSelect(BoxDist, XMVector3Dot(Center, R.r[1]), SelectY);
4044                BoxDist = XMVectorSelect(BoxDist, XMVector3Dot(Center, R.r[2]), SelectZ);
4045
4046                // The projection of the box onto the axis is just its Center and Extents.
4047                // if (min > box_max || max < box_min) reject;
4048                let Result: XMVECTOR = XMVectorOrInt(XMVectorGreater(FrustumMin, XMVectorAdd(BoxDist, Extents)),
4049                    XMVectorLess(FrustumMax, XMVectorSubtract(BoxDist, Extents)));
4050
4051                if (internal::XMVector3AnyTrue(Result)) {
4052                    return false;
4053                }
4054            }
4055
4056            // Test against edge/edge axes (3*6).
4057            let mut FrustumEdgeAxis: [XMVECTOR; 6] = undefined();
4058
4059            FrustumEdgeAxis[0] = vRightTop;
4060            FrustumEdgeAxis[1] = vRightBottom;
4061            FrustumEdgeAxis[2] = vLeftTop;
4062            FrustumEdgeAxis[3] = vLeftBottom;
4063            FrustumEdgeAxis[4] = XMVectorSubtract(vRightTop, vLeftTop);
4064            FrustumEdgeAxis[5] = XMVectorSubtract(vLeftBottom, vLeftTop);
4065
4066            //for (let i: size_t = 0; i < 3; ++i)
4067            for i in 0 .. 3
4068            {
4069                //for (let j: size_t = 0; j < 6; j++)
4070                for j in 0 .. 6
4071                {
4072                    // Compute the axis we are going to test.
4073                    let Axis: XMVECTOR = XMVector3Cross(R.r[i], FrustumEdgeAxis[j]);
4074
4075                    // Find the min/max values of the projection of the frustum onto the axis.
4076                    let mut FrustumMin: XMVECTOR;
4077                    let mut FrustumMax: XMVECTOR;
4078
4079                    FrustumMin = XMVector3Dot(Axis, Corners[0]);
4080                    FrustumMax = FrustumMin;
4081
4082                    //for (let k: size_t = 1; k < CORNER_COUNT; k++)
4083                    for k in 1 .. Self::CORNER_COUNT
4084                    {
4085                        let Temp: XMVECTOR = XMVector3Dot(Axis, Corners[k]);
4086                        FrustumMin = XMVectorMin(FrustumMin, Temp);
4087                        FrustumMax = XMVectorMax(FrustumMax, Temp);
4088                    }
4089
4090                    // Project the center of the box onto the axis.
4091                    let Dist: XMVECTOR = XMVector3Dot(Center, Axis);
4092
4093                    // Project the axes of the box onto the axis to find the "radius" of the box.
4094                    let mut Radius: XMVECTOR = XMVector3Dot(Axis, R.r[0]);
4095                    Radius = XMVectorSelect(Radius, XMVector3Dot(Axis, R.r[1]), SelectY);
4096                    Radius = XMVectorSelect(Radius, XMVector3Dot(Axis, R.r[2]), SelectZ);
4097                    Radius = XMVector3Dot(Extents, XMVectorAbs(Radius));
4098
4099                    // if (center > max + radius || center < min - radius) reject;
4100                    Outside = XMVectorOrInt(Outside, XMVectorGreater(Dist, XMVectorAdd(FrustumMax, Radius)));
4101                    Outside = XMVectorOrInt(Outside, XMVectorLess(Dist, XMVectorSubtract(FrustumMin, Radius)));
4102                }
4103            }
4104        }
4105
4106        if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
4107            return false;
4108        }
4109
4110        // If we did not find a separating plane then the box must intersect the frustum.
4111        return true;
4112    }
4113
4114    /// Test the BoundingFrustum for intersection with a BoundingFrustum.
4115    ///
4116    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-intersects(constboundingfrustum_)>
4117    #[inline]
4118    pub fn IntersectsFrustum(&self, fr: &BoundingFrustum) -> bool {
4119        // Load origin and orientation of frustum B.
4120        let OriginB: XMVECTOR = XMLoadFloat3(&self.Origin);
4121        let OrientationB: XMVECTOR = XMLoadFloat4(&self.Orientation);
4122
4123        debug_assert!(internal::XMQuaternionIsUnit(OrientationB));
4124
4125        // Build the planes of frustum B.
4126        let mut AxisB: [XMVECTOR; 6] = unsafe { undefined() };
4127        AxisB[0] = XMVectorSet(0.0, 0.0, -1.0, 0.0);
4128        AxisB[1] = XMVectorSet(0.0, 0.0, 1.0, 0.0);
4129        AxisB[2] = XMVectorSet(1.0, 0.0, -self.RightSlope, 0.0);
4130        AxisB[3] = XMVectorSet(-1.0, 0.0, self.LeftSlope, 0.0);
4131        AxisB[4] = XMVectorSet(0.0, 1.0, -self.TopSlope, 0.0);
4132        AxisB[5] = XMVectorSet(0.0, -1.0, self.BottomSlope, 0.0);
4133
4134        let mut PlaneDistB: [XMVECTOR; 6] = unsafe { undefined() };
4135        PlaneDistB[0] = XMVectorNegate(XMVectorReplicatePtr(&self.Near));
4136        PlaneDistB[1] = XMVectorReplicatePtr(&self.Far);
4137        PlaneDistB[2] = XMVectorZero();
4138        PlaneDistB[3] = XMVectorZero();
4139        PlaneDistB[4] = XMVectorZero();
4140        PlaneDistB[5] = XMVectorZero();
4141
4142        // Load origin and orientation of frustum A.
4143        let mut OriginA: XMVECTOR = XMLoadFloat3(&fr.Origin);
4144        let mut OrientationA: XMVECTOR = XMLoadFloat4(&fr.Orientation);
4145
4146        debug_assert!(internal::XMQuaternionIsUnit(OrientationA));
4147
4148        // Transform frustum A into the space of the frustum B in order to
4149        // minimize the number of transforms we have to do.
4150        OriginA = XMVector3InverseRotate(XMVectorSubtract(OriginA, OriginB), OrientationB);
4151        OrientationA = XMQuaternionMultiply(OrientationA, XMQuaternionConjugate(OrientationB));
4152
4153        // Build the corners of frustum A (in the local space of B).
4154        let mut RightTopA: XMVECTOR = XMVectorSet(fr.RightSlope, fr.TopSlope, 1.0, 0.0);
4155        let mut RightBottomA: XMVECTOR = XMVectorSet(fr.RightSlope, fr.BottomSlope, 1.0, 0.0);
4156        let mut LeftTopA: XMVECTOR = XMVectorSet(fr.LeftSlope, fr.TopSlope, 1.0, 0.0);
4157        let mut LeftBottomA: XMVECTOR = XMVectorSet(fr.LeftSlope, fr.BottomSlope, 1.0, 0.0);
4158        let NearA: XMVECTOR = XMVectorReplicatePtr(&fr.Near);
4159        let FarA: XMVECTOR = XMVectorReplicatePtr(&fr.Far);
4160
4161        RightTopA = XMVector3Rotate(RightTopA, OrientationA);
4162        RightBottomA = XMVector3Rotate(RightBottomA, OrientationA);
4163        LeftTopA = XMVector3Rotate(LeftTopA, OrientationA);
4164        LeftBottomA = XMVector3Rotate(LeftBottomA, OrientationA);
4165
4166        let mut CornersA: [XMVECTOR; Self::CORNER_COUNT] = unsafe { undefined() };
4167        CornersA[0] = XMVectorMultiplyAdd(RightTopA, NearA, OriginA);
4168        CornersA[1] = XMVectorMultiplyAdd(RightBottomA, NearA, OriginA);
4169        CornersA[2] = XMVectorMultiplyAdd(LeftTopA, NearA, OriginA);
4170        CornersA[3] = XMVectorMultiplyAdd(LeftBottomA, NearA, OriginA);
4171        CornersA[4] = XMVectorMultiplyAdd(RightTopA, FarA, OriginA);
4172        CornersA[5] = XMVectorMultiplyAdd(RightBottomA, FarA, OriginA);
4173        CornersA[6] = XMVectorMultiplyAdd(LeftTopA, FarA, OriginA);
4174        CornersA[7] = XMVectorMultiplyAdd(LeftBottomA, FarA, OriginA);
4175
4176        // Check frustum A against each plane of frustum B.
4177        let mut Outside: XMVECTOR = XMVectorFalseInt();
4178        let mut InsideAll: XMVECTOR = XMVectorTrueInt();
4179
4180        //for (let i: size_t = 0; i < 6; ++i)
4181        for i in 0 .. 6
4182        {
4183            // Find the min/max projection of the frustum onto the plane normal.
4184            let mut Min: XMVECTOR;
4185            let mut Max: XMVECTOR;
4186
4187            Min = XMVector3Dot(AxisB[i], CornersA[0]);
4188            Max = Min;
4189
4190            //for (let j: size_t = 1; j < CORNER_COUNT; j++)
4191            for j in 1 .. Self::CORNER_COUNT
4192            {
4193                let Temp: XMVECTOR = XMVector3Dot(AxisB[i], CornersA[j]);
4194                Min = XMVectorMin(Min, Temp);
4195                Max = XMVectorMax(Max, Temp);
4196            }
4197
4198            // Outside the plane?
4199            Outside = XMVectorOrInt(Outside, XMVectorGreater(Min, PlaneDistB[i]));
4200
4201            // Fully inside the plane?
4202            InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(Max, PlaneDistB[i]));
4203        }
4204
4205        // If the frustum A is outside any of the planes of frustum B it is outside.
4206        if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
4207            return false;
4208        }
4209
4210        // If frustum A is inside all planes of frustum B it is fully inside.
4211        if (XMVector4EqualInt(InsideAll, XMVectorTrueInt())) {
4212            return true;
4213        }
4214
4215        // Build the corners of frustum B.
4216        let RightTopB: XMVECTOR = XMVectorSet(self.RightSlope, self.TopSlope, 1.0, 0.0);
4217        let RightBottomB: XMVECTOR = XMVectorSet(self.RightSlope, self.BottomSlope, 1.0, 0.0);
4218        let LeftTopB: XMVECTOR = XMVectorSet(self.LeftSlope, self.TopSlope, 1.0, 0.0);
4219        let LeftBottomB: XMVECTOR = XMVectorSet(self.LeftSlope, self.BottomSlope, 1.0, 0.0);
4220        let NearB: XMVECTOR = XMVectorReplicatePtr(&self.Near);
4221        let FarB: XMVECTOR = XMVectorReplicatePtr(&self.Far);
4222
4223        let mut CornersB: [XMVECTOR; BoundingFrustum::CORNER_COUNT] = unsafe { undefined() };
4224        CornersB[0] = XMVectorMultiply(RightTopB, NearB);
4225        CornersB[1] = XMVectorMultiply(RightBottomB, NearB);
4226        CornersB[2] = XMVectorMultiply(LeftTopB, NearB);
4227        CornersB[3] = XMVectorMultiply(LeftBottomB, NearB);
4228        CornersB[4] = XMVectorMultiply(RightTopB, FarB);
4229        CornersB[5] = XMVectorMultiply(RightBottomB, FarB);
4230        CornersB[6] = XMVectorMultiply(LeftTopB, FarB);
4231        CornersB[7] = XMVectorMultiply(LeftBottomB, FarB);
4232
4233        // Build the planes of frustum A (in the local space of B).
4234        let mut AxisA: [XMVECTOR; 6] = unsafe { undefined() };
4235        let mut PlaneDistA: [XMVECTOR; 6] = unsafe { undefined() };
4236
4237        AxisA[0] = XMVectorSet(0.0, 0.0, -1.0, 0.0);
4238        AxisA[1] = XMVectorSet(0.0, 0.0, 1.0, 0.0);
4239        AxisA[2] = XMVectorSet(1.0, 0.0, -fr.RightSlope, 0.0);
4240        AxisA[3] = XMVectorSet(-1.0, 0.0, fr.LeftSlope, 0.0);
4241        AxisA[4] = XMVectorSet(0.0, 1.0, -fr.TopSlope, 0.0);
4242        AxisA[5] = XMVectorSet(0.0, -1.0, fr.BottomSlope, 0.0);
4243
4244        AxisA[0] = XMVector3Rotate(AxisA[0], OrientationA);
4245        AxisA[1] = XMVectorNegate(AxisA[0]);
4246        AxisA[2] = XMVector3Rotate(AxisA[2], OrientationA);
4247        AxisA[3] = XMVector3Rotate(AxisA[3], OrientationA);
4248        AxisA[4] = XMVector3Rotate(AxisA[4], OrientationA);
4249        AxisA[5] = XMVector3Rotate(AxisA[5], OrientationA);
4250
4251        PlaneDistA[0] = XMVector3Dot(AxisA[0], CornersA[0]);  // Re-use corner on near plane.
4252        PlaneDistA[1] = XMVector3Dot(AxisA[1], CornersA[4]);  // Re-use corner on far plane.
4253        PlaneDistA[2] = XMVector3Dot(AxisA[2], OriginA);
4254        PlaneDistA[3] = XMVector3Dot(AxisA[3], OriginA);
4255        PlaneDistA[4] = XMVector3Dot(AxisA[4], OriginA);
4256        PlaneDistA[5] = XMVector3Dot(AxisA[5], OriginA);
4257
4258        // Check each axis of frustum A for a seperating plane (5).
4259        //for (let i: size_t = 0; i < 6; ++i)
4260        for i in 0 .. 6
4261        {
4262            // Find the minimum projection of the frustum onto the plane normal.
4263            let mut Min: XMVECTOR;
4264
4265            Min = XMVector3Dot(AxisA[i], CornersB[0]);
4266
4267            //for (let j: size_t = 1; j < CORNER_COUNT; j++)
4268            for j in 0 .. Self::CORNER_COUNT
4269            {
4270                let Temp: XMVECTOR = XMVector3Dot(AxisA[i], CornersB[j]);
4271                Min = XMVectorMin(Min, Temp);
4272            }
4273
4274            // Outside the plane?
4275            Outside = XMVectorOrInt(Outside, XMVectorGreater(Min, PlaneDistA[i]));
4276        }
4277
4278        // If the frustum B is outside any of the planes of frustum A it is outside.
4279        if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
4280            return false;
4281        }
4282
4283        // Check edge/edge axes (6 * 6).
4284        let mut FrustumEdgeAxisA: [XMVECTOR; 6] = unsafe { undefined() };
4285        FrustumEdgeAxisA[0] = RightTopA;
4286        FrustumEdgeAxisA[1] = RightBottomA;
4287        FrustumEdgeAxisA[2] = LeftTopA;
4288        FrustumEdgeAxisA[3] = LeftBottomA;
4289        FrustumEdgeAxisA[4] = XMVectorSubtract(RightTopA, LeftTopA);
4290        FrustumEdgeAxisA[5] = XMVectorSubtract(LeftBottomA, LeftTopA);
4291
4292        let mut FrustumEdgeAxisB: [XMVECTOR; 6] = unsafe { undefined() };
4293        FrustumEdgeAxisB[0] = RightTopB;
4294        FrustumEdgeAxisB[1] = RightBottomB;
4295        FrustumEdgeAxisB[2] = LeftTopB;
4296        FrustumEdgeAxisB[3] = LeftBottomB;
4297        FrustumEdgeAxisB[4] = XMVectorSubtract(RightTopB, LeftTopB);
4298        FrustumEdgeAxisB[5] = XMVectorSubtract(LeftBottomB, LeftTopB);
4299
4300        //for (let i: size_t = 0; i < 6; ++i)
4301        for i in 0 .. 6
4302        {
4303            //for (let j: size_t = 0; j < 6; j++)
4304            for j in 0 .. 6
4305            {
4306                // Compute the axis we are going to test.
4307                let Axis: XMVECTOR = XMVector3Cross(FrustumEdgeAxisA[i], FrustumEdgeAxisB[j]);
4308
4309                // Find the min/max values of the projection of both frustums onto the axis.
4310                let mut MinA: XMVECTOR; let mut MaxA: XMVECTOR;
4311                let mut MinB: XMVECTOR; let mut MaxB: XMVECTOR;
4312
4313                MinA = XMVector3Dot(Axis, CornersA[0]);
4314                MaxA = MinA;
4315                MinB = XMVector3Dot(Axis, CornersB[0]);
4316                MaxB = MinB;
4317
4318                //for (let k: size_t = 1; k < CORNER_COUNT; k++)
4319                for k in 1 .. Self::CORNER_COUNT
4320                {
4321                    let TempA: XMVECTOR = XMVector3Dot(Axis, CornersA[k]);
4322                    MinA = XMVectorMin(MinA, TempA);
4323                    MaxA = XMVectorMax(MaxA, TempA);
4324
4325                    let TempB: XMVECTOR = XMVector3Dot(Axis, CornersB[k]);
4326                    MinB = XMVectorMin(MinB, TempB);
4327                    MaxB = XMVectorMax(MaxB, TempB);
4328                }
4329
4330                // if (MinA > MaxB || MinB > MaxA) reject
4331                Outside = XMVectorOrInt(Outside, XMVectorGreater(MinA, MaxB));
4332                Outside = XMVectorOrInt(Outside, XMVectorGreater(MinB, MaxA));
4333            }
4334        }
4335
4336        // If there is a seperating plane, then the frustums do not intersect.
4337        if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
4338            return false;
4339        }
4340
4341        // If we did not find a separating plane then the frustums intersect.
4342        return true;
4343    }
4344
4345    /// Tests the BoundingFrustum for intersection with a triangle.
4346    ///
4347    /// ## Parameters
4348    ///
4349    /// `V0` A vector describing the triangle.
4350    ///
4351    /// `V1` A vector describing the triangle.
4352    ///
4353    /// `V2` A vector describing the triangle.
4354    ///
4355    /// ## Return value
4356    ///
4357    /// A boolean value indicating whether the BoundingFrustum intersects the triangle.
4358    ///
4359    /// ## Reference
4360    ///
4361    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-intersects(fxmvector_fxmvector_fxmvector)>
4362    #[inline]
4363    pub fn IntersectsTriangle(&self, V0: FXMVECTOR, V1: FXMVECTOR, V2: FXMVECTOR) -> bool {
4364       // Build the frustum planes (NOTE: D is negated from the usual).
4365       let mut Planes: [XMVECTOR; 6] = unsafe { undefined() };
4366       Planes[0] = XMVectorSet(0.0, 0.0, -1.0, -self.Near);
4367       Planes[1] = XMVectorSet(0.0, 0.0, 1.0, self.Far);
4368       Planes[2] = XMVectorSet(1.0, 0.0, -self.RightSlope, 0.0);
4369       Planes[3] = XMVectorSet(-1.0, 0.0, self.LeftSlope, 0.0);
4370       Planes[4] = XMVectorSet(0.0, 1.0, -self.TopSlope, 0.0);
4371       Planes[5] = XMVectorSet(0.0, -1.0, self.BottomSlope, 0.0);
4372
4373       // Load origin and orientation of the frustum.
4374       let vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
4375       let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
4376
4377       debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
4378
4379       // Transform triangle into the local space of frustum.
4380       let TV0: XMVECTOR = XMVector3InverseRotate(XMVectorSubtract(V0, vOrigin), vOrientation);
4381       let TV1: XMVECTOR = XMVector3InverseRotate(XMVectorSubtract(V1, vOrigin), vOrientation);
4382       let TV2: XMVECTOR = XMVector3InverseRotate(XMVectorSubtract(V2, vOrigin), vOrientation);
4383
4384       // Test each vertex of the triangle against the frustum planes.
4385       let mut Outside: XMVECTOR = XMVectorFalseInt();
4386       let mut InsideAll: XMVECTOR = XMVectorTrueInt();
4387
4388       for i in 0 .. 6
4389       {
4390           let Dist0: XMVECTOR = XMVector3Dot(TV0, Planes[i]);
4391           let Dist1: XMVECTOR = XMVector3Dot(TV1, Planes[i]);
4392           let Dist2: XMVECTOR = XMVector3Dot(TV2, Planes[i]);
4393
4394           let mut MinDist: XMVECTOR = XMVectorMin(Dist0, Dist1);
4395           MinDist = XMVectorMin(MinDist, Dist2);
4396           let mut MaxDist: XMVECTOR = XMVectorMax(Dist0, Dist1);
4397           MaxDist = XMVectorMax(MaxDist, Dist2);
4398
4399           let PlaneDist: XMVECTOR = XMVectorSplatW(Planes[i]);
4400
4401           // Outside the plane?
4402           Outside = XMVectorOrInt(Outside, XMVectorGreater(MinDist, PlaneDist));
4403
4404           // Fully inside the plane?
4405           InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(MaxDist, PlaneDist));
4406       }
4407
4408       // If the triangle is outside any of the planes it is outside.
4409       if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
4410           return false;
4411       }
4412
4413       // If the triangle is inside all planes it is fully inside.
4414       if (XMVector4EqualInt(InsideAll, XMVectorTrueInt())) {
4415           return true;
4416       }
4417
4418       // Build the corners of the frustum.
4419       let vRightTop: XMVECTOR = XMVectorSet(self.RightSlope, self.TopSlope, 1.0, 0.0);
4420       let vRightBottom: XMVECTOR = XMVectorSet(self.RightSlope, self.BottomSlope, 1.0, 0.0);
4421       let vLeftTop: XMVECTOR = XMVectorSet(self.LeftSlope, self.TopSlope, 1.0, 0.0);
4422       let vLeftBottom: XMVECTOR = XMVectorSet(self.LeftSlope, self.BottomSlope, 1.0, 0.0);
4423       let vNear: XMVECTOR = XMVectorReplicatePtr(&self.Near);
4424       let vFar: XMVECTOR = XMVectorReplicatePtr(&self.Far);
4425
4426       let mut Corners: [XMVECTOR; Self::CORNER_COUNT] = unsafe { undefined() };
4427       Corners[0] = XMVectorMultiply(vRightTop, vNear);
4428       Corners[1] = XMVectorMultiply(vRightBottom, vNear);
4429       Corners[2] = XMVectorMultiply(vLeftTop, vNear);
4430       Corners[3] = XMVectorMultiply(vLeftBottom, vNear);
4431       Corners[4] = XMVectorMultiply(vRightTop, vFar);
4432       Corners[5] = XMVectorMultiply(vRightBottom, vFar);
4433       Corners[6] = XMVectorMultiply(vLeftTop, vFar);
4434       Corners[7] = XMVectorMultiply(vLeftBottom, vFar);
4435
4436       // Test the plane of the triangle.
4437       let Normal: XMVECTOR = XMVector3Cross(XMVectorSubtract(V1, V0), XMVectorSubtract(V2, V0));
4438       let Dist: XMVECTOR = XMVector3Dot(Normal, V0);
4439
4440       let mut MinDist: XMVECTOR;
4441       let mut MaxDist: XMVECTOR;
4442
4443       MinDist = XMVector3Dot(Corners[0], Normal);
4444       MaxDist = MinDist;
4445
4446       for i in 1 .. Self::CORNER_COUNT
4447       {
4448           let Temp: XMVECTOR = XMVector3Dot(Corners[i], Normal);
4449           MinDist = XMVectorMin(MinDist, Temp);
4450           MaxDist = XMVectorMax(MaxDist, Temp);
4451       }
4452
4453       Outside = XMVectorOrInt(XMVectorGreater(MinDist, Dist), XMVectorLess(MaxDist, Dist));
4454       if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
4455           return false;
4456       }
4457
4458       // Check the edge/edge axes (3*6).
4459       let mut TriangleEdgeAxis: [XMVECTOR; 3] = unsafe { undefined() };
4460       TriangleEdgeAxis[0] = XMVectorSubtract(V1, V0);
4461       TriangleEdgeAxis[1] = XMVectorSubtract(V2, V1);
4462       TriangleEdgeAxis[2] = XMVectorSubtract(V0, V2);
4463
4464       let mut FrustumEdgeAxis: [XMVECTOR; 6] = unsafe { undefined() };
4465       FrustumEdgeAxis[0] = vRightTop;
4466       FrustumEdgeAxis[1] = vRightBottom;
4467       FrustumEdgeAxis[2] = vLeftTop;
4468       FrustumEdgeAxis[3] = vLeftBottom;
4469       FrustumEdgeAxis[4] = XMVectorSubtract(vRightTop, vLeftTop);
4470       FrustumEdgeAxis[5] = XMVectorSubtract(vLeftBottom, vLeftTop);
4471
4472       for i in 0 .. 3
4473       {
4474           for j in 0 .. 6
4475           {
4476               // Compute the axis we are going to test.
4477               let Axis: XMVECTOR = XMVector3Cross(TriangleEdgeAxis[i], FrustumEdgeAxis[j]);
4478
4479               // Find the min/max of the projection of the triangle onto the axis.
4480               let mut MinA: XMVECTOR;
4481               let mut MaxA: XMVECTOR;
4482
4483               let Dist0: XMVECTOR = XMVector3Dot(V0, Axis);
4484               let Dist1: XMVECTOR = XMVector3Dot(V1, Axis);
4485               let Dist2: XMVECTOR = XMVector3Dot(V2, Axis);
4486
4487               MinA = XMVectorMin(Dist0, Dist1);
4488               MinA = XMVectorMin(MinA, Dist2);
4489               MaxA = XMVectorMax(Dist0, Dist1);
4490               MaxA = XMVectorMax(MaxA, Dist2);
4491
4492               // Find the min/max of the projection of the frustum onto the axis.
4493               let mut MinB: XMVECTOR;
4494               let mut MaxB: XMVECTOR;
4495
4496               MinB = XMVector3Dot(Axis, Corners[0]);
4497               MaxB = MinB;
4498
4499               //for (let k: size_t = 1; k < CORNER_COUNT; k++)
4500               for k in 1 .. Self::CORNER_COUNT
4501               {
4502                   let Temp: XMVECTOR = XMVector3Dot(Axis, Corners[k]);
4503                   MinB = XMVectorMin(MinB, Temp);
4504                   MaxB = XMVectorMax(MaxB, Temp);
4505               }
4506
4507               // if (MinA > MaxB || MinB > MaxA) reject;
4508               Outside = XMVectorOrInt(Outside, XMVectorGreater(MinA, MaxB));
4509               Outside = XMVectorOrInt(Outside, XMVectorGreater(MinB, MaxA));
4510           }
4511       }
4512
4513       if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
4514           return false;
4515       }
4516
4517       // If we did not find a separating plane then the triangle must intersect the frustum.
4518       return true;
4519    }
4520
4521    /// Tests the BoundingFrustum for intersection with a Plane.
4522    ///
4523    /// ## Parameters
4524    ///
4525    /// `Plane` A vector describing the plane coefficients (`A`, `B`, `C`, `D`) for the plane equation `Ax+By+Cz+D=0`.
4526    ///
4527    /// ## Return value
4528    ///
4529    /// A PlaneIntersectionType value indicating whether the BoundingSphere intersects the specified plane.
4530    ///
4531    /// ## Reference
4532    ///
4533    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-intersects(fxmvector)>
4534    #[inline]
4535    pub fn IntersectsPlane(&self, Plane: FXMVECTOR) -> PlaneIntersectionType {
4536        debug_assert!(internal::XMPlaneIsUnit(Plane));
4537
4538        // Load origin and orientation of the frustum.
4539        let mut vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
4540        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
4541
4542        debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
4543
4544        // Set w of the origin to one so we can dot4 with a plane.
4545        // TODO: template
4546        vOrigin = XMVectorInsert(vOrigin, XMVectorSplatOne(), 0, 0, 0, 0, 1);
4547
4548        // Build the corners of the frustum (in world space).
4549        let mut RightTop: XMVECTOR = XMVectorSet(self.RightSlope, self.TopSlope, 1.0, 0.0);
4550        let mut RightBottom: XMVECTOR = XMVectorSet(self.RightSlope, self.BottomSlope, 1.0, 0.0);
4551        let mut LeftTop: XMVECTOR = XMVectorSet(self.LeftSlope, self.TopSlope, 1.0, 0.0);
4552        let mut LeftBottom: XMVECTOR = XMVectorSet(self.LeftSlope, self.BottomSlope, 1.0, 0.0);
4553        let vNear: XMVECTOR = XMVectorReplicatePtr(&self.Near);
4554        let vFar: XMVECTOR = XMVectorReplicatePtr(&self.Far);
4555
4556        RightTop = XMVector3Rotate(RightTop, vOrientation);
4557        RightBottom = XMVector3Rotate(RightBottom, vOrientation);
4558        LeftTop = XMVector3Rotate(LeftTop, vOrientation);
4559        LeftBottom = XMVector3Rotate(LeftBottom, vOrientation);
4560
4561        let Corners0: XMVECTOR = XMVectorMultiplyAdd(RightTop, vNear, vOrigin);
4562        let Corners1: XMVECTOR = XMVectorMultiplyAdd(RightBottom, vNear, vOrigin);
4563        let Corners2: XMVECTOR = XMVectorMultiplyAdd(LeftTop, vNear, vOrigin);
4564        let Corners3: XMVECTOR = XMVectorMultiplyAdd(LeftBottom, vNear, vOrigin);
4565        let Corners4: XMVECTOR = XMVectorMultiplyAdd(RightTop, vFar, vOrigin);
4566        let Corners5: XMVECTOR = XMVectorMultiplyAdd(RightBottom, vFar, vOrigin);
4567        let Corners6: XMVECTOR = XMVectorMultiplyAdd(LeftTop, vFar, vOrigin);
4568        let Corners7: XMVECTOR = XMVectorMultiplyAdd(LeftBottom, vFar, vOrigin);
4569
4570        let mut Inside: XMVECTOR = unsafe { undefined() };
4571        let mut Outside: XMVECTOR = unsafe { undefined() };
4572
4573        internal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3,
4574            Corners4, Corners5, &Corners6, &Corners7,
4575            &Plane, &mut Outside, &mut Inside);
4576
4577        // If the frustum is outside any plane it is outside.
4578        if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
4579            return FRONT;
4580        }
4581
4582        // If the frustum is inside all planes it is inside.
4583        if (XMVector4EqualInt(Inside, XMVectorTrueInt())) {
4584            return BACK;
4585        }
4586
4587        // The frustum is not inside all planes or outside a plane it intersects.
4588        return INTERSECTING;
4589    }
4590
4591    /// Tests the BoundingFrustum for intersection with a ray.
4592    ///
4593    /// ## Parameters
4594    ///
4595    /// `rayOrigin` The origin of the ray.
4596    ///
4597    /// `Direction` The direction of the ray.
4598    ///
4599    /// `Dist` The length of the ray.
4600    ///
4601    /// ## Return value
4602    ///
4603    /// A boolean value indicating whether the BoundingFrustum intersects the ray.
4604    ///
4605    /// ## Remarks
4606    ///
4607    /// The distance from the `Origin` to the nearest intersection point is returned
4608    /// in `Dist` when the method returns `true`. Otherwise, `Dist` is set to `0.0`.
4609    ///
4610    /// ## Reference
4611    ///
4612    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-intersects(fxmvector_fxmvector_float_)>
4613    #[inline]
4614    pub fn IntersectsRay(&self, rayOrigin: FXMVECTOR, Direction: FXMVECTOR, Dist: &mut f32) -> bool {
4615        // If ray starts inside the frustum, return a distance of 0 for the hit
4616        if (self.ContainsPoint(rayOrigin) == CONTAINS)
4617        {
4618            *Dist = 0.0;
4619            return true;
4620        }
4621
4622        // Build the frustum planes.
4623        let mut Planes: [XMVECTOR; 6] = unsafe { undefined() };
4624        Planes[0] = XMVectorSet(0.0, 0.0, -1.0, self.Near);
4625        Planes[1] = XMVectorSet(0.0, 0.0, 1.0, -self.Far);
4626        Planes[2] = XMVectorSet(1.0, 0.0, -self.RightSlope, 0.0);
4627        Planes[3] = XMVectorSet(-1.0, 0.0, self.LeftSlope, 0.0);
4628        Planes[4] = XMVectorSet(0.0, 1.0, -self.TopSlope, 0.0);
4629        Planes[5] = XMVectorSet(0.0, -1.0, self.BottomSlope, 0.0);
4630
4631        // Load origin and orientation of the frustum.
4632        let frOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
4633        let frOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
4634
4635        // This algorithm based on "Fast Ray-Convex Polyhedron Intersectin," in James Arvo, ed., Graphics Gems II pp. 247-250
4636        let mut tnear: f32 = -FLT_MAX;
4637        let mut tfar: f32 = FLT_MAX;
4638
4639        //for (let i: size_t = 0; i < 6; ++i)
4640        for i in 0 .. 6
4641        {
4642            let mut Plane: XMVECTOR = internal::XMPlaneTransform(Planes[i], frOrientation, frOrigin);
4643            Plane = XMPlaneNormalize(Plane);
4644
4645            let AxisDotOrigin: XMVECTOR = XMPlaneDotCoord(Plane, rayOrigin);
4646            let AxisDotDirection: XMVECTOR = XMVector3Dot(Plane, Direction);
4647
4648            if (XMVector3LessOrEqual(XMVectorAbs(AxisDotDirection), g_RayEpsilon.v()))
4649            {
4650                // Ray is parallel to plane - check if ray origin is inside plane's
4651                if (XMVector3Greater(AxisDotOrigin, g_XMZero.v()))
4652                {
4653                    // Ray origin is outside half-space.
4654                    *Dist = 0.0;
4655                    return false;
4656                }
4657            }
4658            else
4659            {
4660                // Ray not parallel - get distance to plane.
4661                let vd: f32 = XMVectorGetX(AxisDotDirection);
4662                let vn: f32 = XMVectorGetX(AxisDotOrigin);
4663                let t: f32 = -vn / vd;
4664                if (vd < 0.0)
4665                {
4666                    // Front face - T is a near point.
4667                    if (t > tfar)
4668                    {
4669                        *Dist = 0.0;
4670                        return false;
4671                    }
4672                    if (t > tnear)
4673                    {
4674                        // Hit near face.
4675                        tnear = t;
4676                    }
4677                }
4678                else
4679                {
4680                    // back face - T is far point.
4681                    if (t < tnear)
4682                    {
4683                        *Dist = 0.0;
4684                        return false;
4685                    }
4686                    if (t < tfar)
4687                    {
4688                        // Hit far face.
4689                        tfar = t;
4690                    }
4691                }
4692            }
4693        }
4694
4695        // Survived all tests.
4696        // Note: if ray originates on polyhedron, may want to change 0.0f to some
4697        // epsilon to avoid intersecting the originating face.
4698        let distance: f32 = if (tnear >= 0.00) { tnear } else { tfar };
4699        if (distance >= 0.0)
4700        {
4701            *Dist = distance;
4702            return true;
4703        }
4704
4705        *Dist = 0.0;
4706        return false;
4707    }
4708
4709    /// Tests whether the BoundingFrustum is contained by the specified frustum.
4710    ///
4711    /// ## Parameters
4712    ///
4713    /// `Plane0` A plane describing the frustum.
4714    ///
4715    /// `Plane1` A plane describing the frustum.
4716    ///
4717    /// `Plane2` A plane describing the frustum.
4718    ///
4719    /// `Plane3` A plane describing the frustum.
4720    ///
4721    /// `Plane4` A plane describing the frustum.
4722    ///
4723    /// `Plane5` A plane describing the frustum.
4724    ///
4725    /// ## Return value
4726    ///
4727    /// A ContainmentType value indicating whether the frustum contains the BoundingFrustum.
4728    ///
4729    /// ## Reference
4730    ///
4731    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-BoundingFrustum-containedby>
4732    #[inline]
4733    pub fn ContainedBy(
4734        &self,
4735        Plane0: FXMVECTOR,
4736        Plane1: FXMVECTOR,
4737        Plane2: GXMVECTOR,
4738        Plane3: HXMVECTOR,
4739        Plane4: HXMVECTOR,
4740        Plane5: HXMVECTOR,
4741    ) -> ContainmentType
4742    {
4743        // Load origin and orientation of the frustum.
4744        let mut vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
4745        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
4746
4747        debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
4748
4749        // Set w of the origin to one so we can dot4 with a plane.
4750        // TODO: template
4751        vOrigin = XMVectorInsert(vOrigin, XMVectorSplatOne(), 0, 0, 0, 0, 1);
4752
4753        // Build the corners of the frustum (in world space).
4754        let mut RightTop: XMVECTOR = XMVectorSet(self.RightSlope, self.TopSlope, 1.0, 0.0);
4755        let mut RightBottom: XMVECTOR = XMVectorSet(self.RightSlope, self.BottomSlope, 1.0, 0.0);
4756        let mut LeftTop: XMVECTOR = XMVectorSet(self.LeftSlope, self.TopSlope, 1.0, 0.0);
4757        let mut LeftBottom: XMVECTOR = XMVectorSet(self.LeftSlope, self.BottomSlope, 1.0, 0.0);
4758        let vNear: XMVECTOR = XMVectorReplicatePtr(&self.Near);
4759        let vFar: XMVECTOR = XMVectorReplicatePtr(&self.Far);
4760
4761        RightTop = XMVector3Rotate(RightTop, vOrientation);
4762        RightBottom = XMVector3Rotate(RightBottom, vOrientation);
4763        LeftTop = XMVector3Rotate(LeftTop, vOrientation);
4764        LeftBottom = XMVector3Rotate(LeftBottom, vOrientation);
4765
4766        let Corners0: XMVECTOR = XMVectorMultiplyAdd(RightTop, vNear, vOrigin);
4767        let Corners1: XMVECTOR = XMVectorMultiplyAdd(RightBottom, vNear, vOrigin);
4768        let Corners2: XMVECTOR = XMVectorMultiplyAdd(LeftTop, vNear, vOrigin);
4769        let Corners3: XMVECTOR = XMVectorMultiplyAdd(LeftBottom, vNear, vOrigin);
4770        let Corners4: XMVECTOR = XMVectorMultiplyAdd(RightTop, vFar, vOrigin);
4771        let Corners5: XMVECTOR = XMVectorMultiplyAdd(RightBottom, vFar, vOrigin);
4772        let Corners6: XMVECTOR = XMVectorMultiplyAdd(LeftTop, vFar, vOrigin);
4773        let Corners7: XMVECTOR = XMVectorMultiplyAdd(LeftBottom, vFar, vOrigin);
4774
4775        let mut Inside: XMVECTOR = unsafe { undefined() };
4776        let mut Outside: XMVECTOR = unsafe { undefined() };
4777
4778        // Test against each plane.
4779        internal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3,
4780            Corners4, Corners5, &Corners6, &Corners7,
4781            &Plane0, &mut Outside, &mut Inside);
4782
4783        let mut AnyOutside: XMVECTOR = Outside;
4784        let mut AllInside: XMVECTOR = Inside;
4785
4786        internal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3,
4787            Corners4, Corners5, &Corners6, &Corners7,
4788            &Plane1, &mut Outside, &mut Inside);
4789
4790        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
4791        AllInside = XMVectorAndInt(AllInside, Inside);
4792
4793        internal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3,
4794            Corners4, Corners5, &Corners6, &Corners7,
4795            &Plane2, &mut Outside, &mut Inside);
4796
4797        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
4798        AllInside = XMVectorAndInt(AllInside, Inside);
4799
4800        internal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3,
4801            Corners4, Corners5, &Corners6, &Corners7,
4802            &Plane3, &mut Outside, &mut Inside);
4803
4804        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
4805        AllInside = XMVectorAndInt(AllInside, Inside);
4806
4807        internal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3,
4808            Corners4, Corners5, &Corners6, &Corners7,
4809            &Plane4, &mut Outside, &mut Inside);
4810
4811        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
4812        AllInside = XMVectorAndInt(AllInside, Inside);
4813
4814        internal::FastIntersectFrustumPlane(Corners0, Corners1, Corners2, Corners3,
4815            Corners4, Corners5, &Corners6, &Corners7,
4816            &Plane5, &mut Outside, &mut Inside);
4817
4818        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
4819        AllInside = XMVectorAndInt(AllInside, Inside);
4820
4821        // If the frustum is outside any plane it is outside.
4822        if (XMVector4EqualInt(AnyOutside, XMVectorTrueInt())) {
4823            return DISJOINT;
4824        }
4825
4826        // If the frustum is inside all planes it is inside.
4827        if (XMVector4EqualInt(AllInside, XMVectorTrueInt())) {
4828            return CONTAINS;
4829        }
4830
4831        // The frustum is not inside all planes or outside a plane, it may intersect.
4832        return INTERSECTS;
4833    }
4834
4835    /// Creates a BoundingFrustum from the specified projection matrix.
4836    ///
4837    /// ## Parameters
4838    ///
4839    /// `Out` The new BoundingFrustum.
4840    ///
4841    /// `Projection` The projection matrix to create the BoundingFrustum from.
4842    ///
4843    /// `rhcoords` A value of `false` indicates that `Projection` is in a **left-handeded**
4844    ///  coordinate system.
4845    ///
4846    /// ## Return value
4847    ///
4848    /// This method does not return a value.
4849    ///
4850    /// ## Reference
4851    ///
4852    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingfrustum-createfrommatrix>
4853    #[inline]
4854    pub fn CreateFromMatrix(Out: &mut Self, Projection: FXMMATRIX, rhcoords: bool) {
4855        // Corners of the projection frustum in homogenous space.
4856        const HomogenousPoints: [XMVECTORF32; 6] =
4857        [
4858            XMVECTORF32 { f: [  1.0,  0.0, 1.0, 1.0 ] },   // right (at far plane)
4859            XMVECTORF32 { f: [ -1.0,  0.0, 1.0, 1.0 ] },   // left
4860            XMVECTORF32 { f: [  0.0,  1.0, 1.0, 1.0 ] },   // top
4861            XMVECTORF32 { f: [  0.0, -1.0, 1.0, 1.0 ] },   // bottom
4862
4863            XMVECTORF32 { f: [  0.0,  0.0, 0.0, 1.0 ] },   // near
4864            XMVECTORF32 { f: [  0.0,  0.0, 1.0, 1.0 ] }    // far
4865        ];
4866
4867        let mut Determinant: XMVECTOR = unsafe { undefined() };
4868        let matInverse: XMMATRIX = XMMatrixInverse(Some(&mut Determinant), Projection);
4869
4870        // Compute the frustum corners in world space.
4871        let mut Points: [XMVECTOR; 6] = unsafe { undefined() };
4872
4873        //for (size_t i = 0; i < 6; ++i)
4874        for i in 0 .. 6
4875        {
4876            // Transform point.
4877            Points[i] = XMVector4Transform(HomogenousPoints[i].v(), matInverse);
4878        }
4879
4880        Out.Origin = XMFLOAT3::set(0.0, 0.0, 0.0);
4881        Out.Orientation = XMFLOAT4::set(0.0, 0.0, 0.0, 1.0);
4882
4883        // Compute the slopes.
4884        Points[0] = XMVectorMultiply(Points[0], XMVectorReciprocal(XMVectorSplatZ(Points[0])));
4885        Points[1] = XMVectorMultiply(Points[1], XMVectorReciprocal(XMVectorSplatZ(Points[1])));
4886        Points[2] = XMVectorMultiply(Points[2], XMVectorReciprocal(XMVectorSplatZ(Points[2])));
4887        Points[3] = XMVectorMultiply(Points[3], XMVectorReciprocal(XMVectorSplatZ(Points[3])));
4888
4889        Out.RightSlope = XMVectorGetX(Points[0]);
4890        Out.LeftSlope = XMVectorGetX(Points[1]);
4891        Out.TopSlope = XMVectorGetY(Points[2]);
4892        Out.BottomSlope = XMVectorGetY(Points[3]);
4893
4894        // Compute near and far.
4895        Points[4] = XMVectorMultiply(Points[4], XMVectorReciprocal(XMVectorSplatW(Points[4])));
4896        Points[5] = XMVectorMultiply(Points[5], XMVectorReciprocal(XMVectorSplatW(Points[5])));
4897
4898        if (rhcoords)
4899        {
4900            Out.Near = XMVectorGetZ(Points[5]);
4901            Out.Far = XMVectorGetZ(Points[4]);
4902        }
4903        else
4904        {
4905            Out.Near = XMVectorGetZ(Points[4]);
4906            Out.Far = XMVectorGetZ(Points[5]);
4907        }
4908    }
4909
4910    /// Gets the planes making up the BoundingFrustum.
4911    ///
4912    /// ## Parameters
4913    ///
4914    /// `NearPlane` A vector that will hold the near plane.
4915    ///
4916    /// `FarPlane` A vector that will hold the far plane.
4917    ///
4918    /// `RightPlane` A vector that will hold the right plane.
4919    ///
4920    /// `LeftPlane` A vector that will hold the left plane.
4921    ///
4922    /// `TopPlane` A vector that will hold the top plane.
4923    ///
4924    /// `BottomPlane` A vector that will hold the bottom plane.
4925    ///
4926    /// ## Return value
4927    ///
4928    /// None
4929    ///
4930    /// ## Reference
4931    ///
4932    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingfrustum-getplanes>
4933    #[inline]
4934    pub fn GetPlanes(
4935        &self,
4936        NearPlane: Option<&mut XMVECTOR>,
4937        FarPlane: Option<&mut XMVECTOR>,
4938        RightPlane: Option<&mut XMVECTOR>,
4939        LeftPlane: Option<&mut XMVECTOR>,
4940        TopPlane: Option<&mut XMVECTOR>,
4941        BottomPlane: Option<&mut XMVECTOR>,
4942    ) {
4943        //-----------------------------------------------------------------------------
4944        // Build the 6 frustum planes from a frustum.
4945        //
4946        // The intended use for these routines is for fast culling to a view frustum.
4947        // When the volume being tested against a view frustum is small relative to the
4948        // view frustum it is usually either inside all six planes of the frustum
4949        // (CONTAINS) or outside one of the planes of the frustum (DISJOINT). If neither
4950        // of these cases is true then it may or may not be intersecting the frustum
4951        // (INTERSECTS)
4952        //-----------------------------------------------------------------------------
4953
4954        // Load origin and orientation of the frustum.
4955        let vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
4956        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
4957
4958        if let Some(NearPlane) = NearPlane
4959        {
4960            let mut vNearPlane: XMVECTOR = XMVectorSet(0.0, 0.0, -1.0, self.Near);
4961            vNearPlane = internal::XMPlaneTransform(vNearPlane, vOrientation, vOrigin);
4962            *NearPlane = XMPlaneNormalize(vNearPlane);
4963        }
4964
4965        if let Some(FarPlane) = FarPlane
4966        {
4967            let mut vFarPlane: XMVECTOR = XMVectorSet(0.0, 0.0, 1.0, -self.Far);
4968            vFarPlane = internal::XMPlaneTransform(vFarPlane, vOrientation, vOrigin);
4969            *FarPlane = XMPlaneNormalize(vFarPlane);
4970        }
4971
4972        if let Some(RightPlane) = RightPlane
4973        {
4974            let mut vRightPlane: XMVECTOR = XMVectorSet(1.0, 0.0, -self.RightSlope, 0.0);
4975            vRightPlane = internal::XMPlaneTransform(vRightPlane, vOrientation, vOrigin);
4976            *RightPlane = XMPlaneNormalize(vRightPlane);
4977        }
4978
4979        if let Some(LeftPlane) = LeftPlane
4980        {
4981            let mut vLeftPlane: XMVECTOR = XMVectorSet(-1.0, 0.0, self.LeftSlope, 0.0);
4982            vLeftPlane = internal::XMPlaneTransform(vLeftPlane, vOrientation, vOrigin);
4983            *LeftPlane = XMPlaneNormalize(vLeftPlane);
4984        }
4985
4986        if let Some(TopPlane) = TopPlane
4987        {
4988            let mut vTopPlane: XMVECTOR = XMVectorSet(0.0, 1.0, -self.TopSlope, 0.0);
4989            vTopPlane = internal::XMPlaneTransform(vTopPlane, vOrientation, vOrigin);
4990            *TopPlane = XMPlaneNormalize(vTopPlane);
4991        }
4992
4993        if let Some(BottomPlane) = BottomPlane
4994        {
4995            let mut vBottomPlane: XMVECTOR = XMVectorSet(0.0, -1.0, self.BottomSlope, 0.0);
4996            vBottomPlane = internal::XMPlaneTransform(vBottomPlane, vOrientation, vOrigin);
4997            *BottomPlane = XMPlaneNormalize(vBottomPlane);
4998        }
4999    }
5000
5001    /// Gets the corners making up the BoundingFrustum.
5002    ///
5003    /// ```text
5004    ///  Near    Far
5005    /// 0----1  4----5
5006    /// |    |  |    |
5007    /// |    |  |    |
5008    /// 3----2  7----6
5009    /// ```
5010    ///
5011    /// ## Returns
5012    /// ```text
5013    /// [
5014    ///    LeftTopNear, RightTopNear, RightBottomNear, LeftBottomNear,
5015    ///    LeftTopFar,  RightTopFar,  RightBottomFar,  LeftBottomFar
5016    /// ]
5017    /// ```
5018    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-boundingfrustum-getcorners>
5019    #[inline]
5020    pub fn GetCorners(&self, Corners: &mut [XMFLOAT3; BoundingFrustum::CORNER_COUNT]) {
5021        // assert(Corners != nullptr);
5022
5023        // Load origin and orientation of the frustum.
5024        let vOrigin: XMVECTOR = XMLoadFloat3(&self.Origin);
5025        let vOrientation: XMVECTOR = XMLoadFloat4(&self.Orientation);
5026
5027        debug_assert!(internal::XMQuaternionIsUnit(vOrientation));
5028
5029        // Build the corners of the frustum.
5030        let vRightTop: XMVECTOR = XMVectorSet(self.RightSlope, self.TopSlope, 1.0, 0.0);
5031        let vRightBottom: XMVECTOR = XMVectorSet(self.RightSlope, self.BottomSlope, 1.0, 0.0);
5032        let vLeftTop: XMVECTOR = XMVectorSet(self.LeftSlope, self.TopSlope, 1.0, 0.0);
5033        let vLeftBottom: XMVECTOR = XMVectorSet(self.LeftSlope, self.BottomSlope, 1.0, 0.0);
5034        let vNear: XMVECTOR = XMVectorReplicatePtr(&self.Near);
5035        let vFar: XMVECTOR = XMVectorReplicatePtr(&self.Far);
5036
5037        // Returns 8 corners position of bounding frustum.
5038        //     Near    Far
5039        //    0----1  4----5
5040        //    |    |  |    |
5041        //    |    |  |    |
5042        //    3----2  7----6
5043
5044        let mut vCorners: [XMVECTOR; Self::CORNER_COUNT] = unsafe { undefined() };
5045        vCorners[0] = XMVectorMultiply(vLeftTop, vNear);
5046        vCorners[1] = XMVectorMultiply(vRightTop, vNear);
5047        vCorners[2] = XMVectorMultiply(vRightBottom, vNear);
5048        vCorners[3] = XMVectorMultiply(vLeftBottom, vNear);
5049        vCorners[4] = XMVectorMultiply(vLeftTop, vFar);
5050        vCorners[5] = XMVectorMultiply(vRightTop, vFar);
5051        vCorners[6] = XMVectorMultiply(vRightBottom, vFar);
5052        vCorners[7] = XMVectorMultiply(vLeftBottom, vFar);
5053
5054        for i in 0.. Self::CORNER_COUNT
5055        {
5056            let C: XMVECTOR = XMVectorAdd(XMVector3Rotate(vCorners[i], vOrientation), vOrigin);
5057            XMStoreFloat3(&mut Corners[i], C);
5058        }
5059    }
5060}
5061
5062/// Triangle intersection and containment functions.
5063pub mod triangle_tests {
5064    use crate::*;
5065    use super::*;
5066
5067    /// Test whether a triangle intersects with a ray.
5068    ///
5069    /// ## Parameters
5070    ///
5071    /// `Origin` The origin of the ray.
5072    ///
5073    /// `Direction` The direction of the ray.
5074    ///
5075    /// `V0` A vector defining the triangle.
5076    ///
5077    /// `V1` A vector defining the triangle.
5078    ///
5079    /// `V2` A vector defining the triangle.
5080    ///
5081    /// `Dist` The distance along the ray where the intersection occurs.
5082    ///
5083    /// ## Return value
5084    ///
5085    /// A boolean value indicating whether the triangle intersects with the ray.
5086    ///
5087    /// ## Remarks
5088    ///
5089    /// The distance from the `Origin` to the nearest intersection point is returned
5090    /// in `Dist` when the method returns `true`. Otherwise, `Dist` is set to `0.0`.
5091    ///
5092    /// ## Reference
5093    ///
5094    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-intersects>
5095    #[inline]
5096    pub fn IntersectsRay(Origin: FXMVECTOR, Direction: FXMVECTOR, V0: FXMVECTOR, V1: GXMVECTOR, V2: HXMVECTOR, Dist: &mut f32) -> bool {
5097        debug_assert!(internal::XMVector3IsUnit(Direction));
5098
5099        let Zero: XMVECTOR = XMVectorZero();
5100
5101        let e1: XMVECTOR = XMVectorSubtract(V1, V0);
5102        let e2: XMVECTOR = XMVectorSubtract(V2, V0);
5103
5104        // p = Direction ^ e2;
5105        let p: XMVECTOR = XMVector3Cross(Direction, e2);
5106
5107        // det = e1 * p;
5108        let det: XMVECTOR = XMVector3Dot(e1, p);
5109
5110        let u: XMVECTOR;
5111        let v: XMVECTOR;
5112        let mut t: XMVECTOR;
5113
5114        if (XMVector3GreaterOrEqual(det, g_RayEpsilon.v()))
5115        {
5116            // Determinate is positive (front side of the triangle).
5117            let s: XMVECTOR = XMVectorSubtract(Origin, V0);
5118
5119            // u = s * p;
5120            u = XMVector3Dot(s, p);
5121
5122            let mut NoIntersection: XMVECTOR = XMVectorLess(u, Zero);
5123            NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(u, det));
5124
5125            // q = s ^ e1;
5126            let q: XMVECTOR = XMVector3Cross(s, e1);
5127
5128            // v = Direction * q;
5129            v = XMVector3Dot(Direction, q);
5130
5131            NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(v, Zero));
5132            NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(XMVectorAdd(u, v), det));
5133
5134            // t = e2 * q;
5135            t = XMVector3Dot(e2, q);
5136
5137            NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(t, Zero));
5138
5139            if (XMVector4EqualInt(NoIntersection, XMVectorTrueInt()))
5140            {
5141                *Dist = 0.0;
5142                return false;
5143            }
5144        }
5145        else if (XMVector3LessOrEqual(det, g_RayNegEpsilon.v()))
5146        {
5147            // Determinate is negative (back side of the triangle).
5148            let s: XMVECTOR = XMVectorSubtract(Origin, V0);
5149
5150            // u = s * p;
5151            u = XMVector3Dot(s, p);
5152
5153            let mut NoIntersection: XMVECTOR = XMVectorGreater(u, Zero);
5154            NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(u, det));
5155
5156            // q = s ^ e1;
5157            let q: XMVECTOR = XMVector3Cross(s, e1);
5158
5159            // v = Direction * q;
5160            v = XMVector3Dot(Direction, q);
5161
5162            NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(v, Zero));
5163            NoIntersection = XMVectorOrInt(NoIntersection, XMVectorLess(XMVectorAdd(u, v), det));
5164
5165            // t = e2 * q;
5166            t = XMVector3Dot(e2, q);
5167
5168            NoIntersection = XMVectorOrInt(NoIntersection, XMVectorGreater(t, Zero));
5169
5170            if (XMVector4EqualInt(NoIntersection, XMVectorTrueInt()))
5171            {
5172                *Dist = 0.0;
5173                return false;
5174            }
5175        }
5176        else
5177        {
5178            // Parallel ray.
5179            *Dist = 0.0;
5180            return false;
5181        }
5182
5183        t = XMVectorDivide(t, det);
5184
5185        // (u / det) and (v / dev) are the barycentric cooridinates of the intersection.
5186
5187        // Store the x-component to *pDist
5188        XMStoreFloat(Dist, t);
5189
5190        return true;
5191    }
5192
5193    /// Test whether two triangles intersect
5194    ///
5195    /// ## Parameters
5196    ///
5197    /// `A0` A vector defining triangle A.
5198    ///
5199    /// `A1` A vector defining triangle A.
5200    ///
5201    /// `A2` A vector defining triangle A.
5202    ///
5203    /// `B0` A vector defining triangle B.
5204    ///
5205    /// `B1` A vector defining triangle B.
5206    ///
5207    /// `B2` A vector defining triangle B.
5208    ///
5209    /// ## Return value
5210    ///
5211    /// A boolean value indicating whether the triangles intersect.
5212    ///
5213    /// ## Reference
5214    ///
5215    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-intersects~r1>
5216    #[allow(clippy::cognitive_complexity)]
5217    #[inline]
5218    pub fn IntersectsTriangle(
5219        A0: FXMVECTOR,
5220        A1: FXMVECTOR,
5221        A2: FXMVECTOR,
5222        B0: GXMVECTOR,
5223        B1: HXMVECTOR,
5224        B2: HXMVECTOR,
5225      ) -> bool {
5226        const SelectY: XMVECTORU32 = XMVECTORU32 { u: [ XM_SELECT_0, XM_SELECT_1, XM_SELECT_0, XM_SELECT_0 ] };
5227        const SelectZ: XMVECTORU32 = XMVECTORU32 { u: [ XM_SELECT_0, XM_SELECT_0, XM_SELECT_1, XM_SELECT_0 ] };
5228        const Select0111: XMVECTORU32 = XMVECTORU32 { u: [ XM_SELECT_0, XM_SELECT_1, XM_SELECT_1, XM_SELECT_1 ] };
5229        const Select1011: XMVECTORU32 = XMVECTORU32 { u: [ XM_SELECT_1, XM_SELECT_0, XM_SELECT_1, XM_SELECT_1 ] };
5230        const Select1101: XMVECTORU32 = XMVECTORU32 { u: [ XM_SELECT_1, XM_SELECT_1, XM_SELECT_0, XM_SELECT_1 ] };
5231
5232        let Zero: XMVECTOR = XMVectorZero();
5233
5234        // Compute the normal of triangle A.
5235        let N1: XMVECTOR = XMVector3Cross(XMVectorSubtract(A1, A0), XMVectorSubtract(A2, A0));
5236
5237        // Assert that the triangle is not degenerate.
5238        debug_assert!(!XMVector3Equal(N1, Zero));
5239
5240        // Test points of B against the plane of A.
5241        let mut BDist: XMVECTOR = XMVector3Dot(N1, XMVectorSubtract(B0, A0));
5242        BDist = XMVectorSelect(BDist, XMVector3Dot(N1, XMVectorSubtract(B1, A0)), SelectY.v());
5243        BDist = XMVectorSelect(BDist, XMVector3Dot(N1, XMVectorSubtract(B2, A0)), SelectZ.v());
5244
5245        // Ensure robustness with co-planar triangles by zeroing small distances.
5246        let mut BDistIsZeroCR: u32 = unsafe { undefined() };
5247        let BDistIsZero: XMVECTOR = XMVectorGreaterR(&mut BDistIsZeroCR, g_RayEpsilon.v(), XMVectorAbs(BDist));
5248        BDist = XMVectorSelect(BDist, Zero, BDistIsZero);
5249
5250        let mut BDistIsLessCR: u32 = unsafe { undefined() };
5251        let BDistIsLess: XMVECTOR = XMVectorGreaterR(&mut BDistIsLessCR, Zero, BDist);
5252
5253        let mut BDistIsGreaterCR: u32 = unsafe { undefined() };
5254        let BDistIsGreater: XMVECTOR = XMVectorGreaterR(&mut BDistIsGreaterCR, BDist, Zero);
5255
5256        // If all the points are on the same side we don't intersect.
5257        if (XMComparisonAllTrue(BDistIsLessCR) || XMComparisonAllTrue(BDistIsGreaterCR)) {
5258            return false;
5259        }
5260
5261        // Compute the normal of triangle B.
5262        let N2: XMVECTOR = XMVector3Cross(XMVectorSubtract(B1, B0), XMVectorSubtract(B2, B0));
5263
5264        // Assert that the triangle is not degenerate.
5265        debug_assert!(!XMVector3Equal(N2, Zero));
5266
5267        // Test points of A against the plane of B.
5268        let mut ADist: XMVECTOR = XMVector3Dot(N2, XMVectorSubtract(A0, B0));
5269        ADist = XMVectorSelect(ADist, XMVector3Dot(N2, XMVectorSubtract(A1, B0)), SelectY.v());
5270        ADist = XMVectorSelect(ADist, XMVector3Dot(N2, XMVectorSubtract(A2, B0)), SelectZ.v());
5271
5272        // Ensure robustness with co-planar triangles by zeroing small distances.
5273        let mut ADistIsZeroCR: u32 = unsafe { undefined() };
5274        let ADistIsZero: XMVECTOR = XMVectorGreaterR(&mut ADistIsZeroCR, g_RayEpsilon.v(), XMVectorAbs(BDist));
5275        ADist = XMVectorSelect(ADist, Zero, ADistIsZero);
5276
5277        let mut ADistIsLessCR: u32 = unsafe { undefined() };
5278        let ADistIsLess: XMVECTOR = XMVectorGreaterR(&mut ADistIsLessCR, Zero, ADist);
5279
5280        let mut ADistIsGreaterCR: u32 = unsafe { undefined() };
5281        let ADistIsGreater: XMVECTOR = XMVectorGreaterR(&mut ADistIsGreaterCR, ADist, Zero);
5282
5283        // If all the points are on the same side we don't intersect.
5284        if (XMComparisonAllTrue(ADistIsLessCR) || XMComparisonAllTrue(ADistIsGreaterCR)) {
5285            return false;
5286        }
5287
5288        // Special case for co-planar triangles.
5289        if (XMComparisonAllTrue(ADistIsZeroCR) || XMComparisonAllTrue(BDistIsZeroCR))
5290        {
5291            let mut Axis: XMVECTOR;
5292            let mut Dist: XMVECTOR;
5293            let mut MinDist: XMVECTOR;
5294
5295            // Compute an axis perpindicular to the edge (points out).
5296            Axis = XMVector3Cross(N1, XMVectorSubtract(A1, A0));
5297            Dist = XMVector3Dot(Axis, A0);
5298
5299            // Test points of B against the axis.
5300            MinDist = XMVector3Dot(B0, Axis);
5301            MinDist = XMVectorMin(MinDist, XMVector3Dot(B1, Axis));
5302            MinDist = XMVectorMin(MinDist, XMVector3Dot(B2, Axis));
5303            if (XMVector4GreaterOrEqual(MinDist, Dist)) {
5304                return false;
5305            }
5306
5307            // Edge (A1, A2)
5308            Axis = XMVector3Cross(N1, XMVectorSubtract(A2, A1));
5309            Dist = XMVector3Dot(Axis, A1);
5310
5311            MinDist = XMVector3Dot(B0, Axis);
5312            MinDist = XMVectorMin(MinDist, XMVector3Dot(B1, Axis));
5313            MinDist = XMVectorMin(MinDist, XMVector3Dot(B2, Axis));
5314            if (XMVector4GreaterOrEqual(MinDist, Dist)) {
5315                return false;
5316            }
5317
5318            // Edge (A2, A0)
5319            Axis = XMVector3Cross(N1, XMVectorSubtract(A0, A2));
5320            Dist = XMVector3Dot(Axis, A2);
5321
5322            MinDist = XMVector3Dot(B0, Axis);
5323            MinDist = XMVectorMin(MinDist, XMVector3Dot(B1, Axis));
5324            MinDist = XMVectorMin(MinDist, XMVector3Dot(B2, Axis));
5325            if (XMVector4GreaterOrEqual(MinDist, Dist)) {
5326                return false;
5327            }
5328
5329            // Edge (B0, B1)
5330            Axis = XMVector3Cross(N2, XMVectorSubtract(B1, B0));
5331            Dist = XMVector3Dot(Axis, B0);
5332
5333            MinDist = XMVector3Dot(A0, Axis);
5334            MinDist = XMVectorMin(MinDist, XMVector3Dot(A1, Axis));
5335            MinDist = XMVectorMin(MinDist, XMVector3Dot(A2, Axis));
5336            if (XMVector4GreaterOrEqual(MinDist, Dist)) {
5337                return false;
5338            }
5339
5340            // Edge (B1, B2)
5341            Axis = XMVector3Cross(N2, XMVectorSubtract(B2, B1));
5342            Dist = XMVector3Dot(Axis, B1);
5343
5344            MinDist = XMVector3Dot(A0, Axis);
5345            MinDist = XMVectorMin(MinDist, XMVector3Dot(A1, Axis));
5346            MinDist = XMVectorMin(MinDist, XMVector3Dot(A2, Axis));
5347            if (XMVector4GreaterOrEqual(MinDist, Dist)) {
5348                return false;
5349            }
5350
5351            // Edge (B2,B0)
5352            Axis = XMVector3Cross(N2, XMVectorSubtract(B0, B2));
5353            Dist = XMVector3Dot(Axis, B2);
5354
5355            MinDist = XMVector3Dot(A0, Axis);
5356            MinDist = XMVectorMin(MinDist, XMVector3Dot(A1, Axis));
5357            MinDist = XMVectorMin(MinDist, XMVector3Dot(A2, Axis));
5358            if (XMVector4GreaterOrEqual(MinDist, Dist)) {
5359                return false;
5360            }
5361
5362            return true;
5363        }
5364
5365        //
5366        // Find the single vertex of A and B (ie the vertex on the opposite side
5367        // of the plane from the other two) and reorder the edges so we can compute
5368        // the signed edge/edge distances.
5369        //
5370        // if ( (V0 >= 0 && V1 <  0 && V2 <  0) ||
5371        //      (V0 >  0 && V1 <= 0 && V2 <= 0) ||
5372        //      (V0 <= 0 && V1 >  0 && V2 >  0) ||
5373        //      (V0 <  0 && V1 >= 0 && V2 >= 0) ) then V0 is singular;
5374        //
5375        // If our singular vertex is not on the positive side of the plane we reverse
5376        // the triangle winding so that the overlap comparisons will compare the
5377        // correct edges with the correct signs.
5378        //
5379        let ADistIsLessEqual: XMVECTOR = XMVectorOrInt(ADistIsLess, ADistIsZero);
5380        let ADistIsGreaterEqual: XMVECTOR = XMVectorOrInt(ADistIsGreater, ADistIsZero);
5381
5382        let AA0: XMVECTOR;
5383        let AA1: XMVECTOR;
5384        let AA2: XMVECTOR;
5385        let bPositiveA: bool;
5386
5387        if (internal::XMVector3AllTrue(XMVectorSelect(ADistIsGreaterEqual, ADistIsLess, Select0111.v())) ||
5388            internal::XMVector3AllTrue(XMVectorSelect(ADistIsGreater, ADistIsLessEqual, Select0111.v())))
5389        {
5390            // A0 is singular, crossing from positive to negative.
5391            AA0 = A0; AA1 = A1; AA2 = A2;
5392            bPositiveA = true;
5393        }
5394        else if (internal::XMVector3AllTrue(XMVectorSelect(ADistIsLessEqual, ADistIsGreater, Select0111.v())) ||
5395            internal::XMVector3AllTrue(XMVectorSelect(ADistIsLess, ADistIsGreaterEqual, Select0111.v())))
5396        {
5397            // A0 is singular, crossing from negative to positive.
5398            AA0 = A0; AA1 = A2; AA2 = A1;
5399            bPositiveA = false;
5400        }
5401        else if (internal::XMVector3AllTrue(XMVectorSelect(ADistIsGreaterEqual, ADistIsLess, Select1011.v())) ||
5402            internal::XMVector3AllTrue(XMVectorSelect(ADistIsGreater, ADistIsLessEqual, Select1011.v())))
5403        {
5404            // A1 is singular, crossing from positive to negative.
5405            AA0 = A1; AA1 = A2; AA2 = A0;
5406            bPositiveA = true;
5407        }
5408        else if (internal::XMVector3AllTrue(XMVectorSelect(ADistIsLessEqual, ADistIsGreater, Select1011.v())) ||
5409            internal::XMVector3AllTrue(XMVectorSelect(ADistIsLess, ADistIsGreaterEqual, Select1011.v())))
5410        {
5411            // A1 is singular, crossing from negative to positive.
5412            AA0 = A1; AA1 = A0; AA2 = A2;
5413            bPositiveA = false;
5414        }
5415        else if (internal::XMVector3AllTrue(XMVectorSelect(ADistIsGreaterEqual, ADistIsLess, Select1101.v())) ||
5416            internal::XMVector3AllTrue(XMVectorSelect(ADistIsGreater, ADistIsLessEqual, Select1101.v())))
5417        {
5418            // A2 is singular, crossing from positive to negative.
5419            AA0 = A2; AA1 = A0; AA2 = A1;
5420            bPositiveA = true;
5421        }
5422        else if (internal::XMVector3AllTrue(XMVectorSelect(ADistIsLessEqual, ADistIsGreater, Select1101.v())) ||
5423            internal::XMVector3AllTrue(XMVectorSelect(ADistIsLess, ADistIsGreaterEqual, Select1101.v())))
5424        {
5425            // A2 is singular, crossing from negative to positive.
5426            AA0 = A2; AA1 = A1; AA2 = A0;
5427            bPositiveA = false;
5428        }
5429        else
5430        {
5431            debug_assert!(false);
5432            return false;
5433        }
5434
5435        let BDistIsLessEqual: XMVECTOR = XMVectorOrInt(BDistIsLess, BDistIsZero);
5436        let BDistIsGreaterEqual: XMVECTOR = XMVectorOrInt(BDistIsGreater, BDistIsZero);
5437
5438        let BB0: XMVECTOR;
5439        let BB1: XMVECTOR;
5440        let BB2: XMVECTOR;
5441        let bPositiveB: bool;
5442
5443        if (internal::XMVector3AllTrue(XMVectorSelect(BDistIsGreaterEqual, BDistIsLess, Select0111.v())) ||
5444            internal::XMVector3AllTrue(XMVectorSelect(BDistIsGreater, BDistIsLessEqual, Select0111.v())))
5445        {
5446            // B0 is singular, crossing from positive to negative.
5447            BB0 = B0; BB1 = B1; BB2 = B2;
5448            bPositiveB = true;
5449        }
5450        else if (internal::XMVector3AllTrue(XMVectorSelect(BDistIsLessEqual, BDistIsGreater, Select0111.v())) ||
5451            internal::XMVector3AllTrue(XMVectorSelect(BDistIsLess, BDistIsGreaterEqual, Select0111.v())))
5452        {
5453            // B0 is singular, crossing from negative to positive.
5454            BB0 = B0; BB1 = B2; BB2 = B1;
5455            bPositiveB = false;
5456        }
5457        else if (internal::XMVector3AllTrue(XMVectorSelect(BDistIsGreaterEqual, BDistIsLess, Select1011.v())) ||
5458            internal::XMVector3AllTrue(XMVectorSelect(BDistIsGreater, BDistIsLessEqual, Select1011.v())))
5459        {
5460            // B1 is singular, crossing from positive to negative.
5461            BB0 = B1; BB1 = B2; BB2 = B0;
5462            bPositiveB = true;
5463        }
5464        else if (internal::XMVector3AllTrue(XMVectorSelect(BDistIsLessEqual, BDistIsGreater, Select1011.v())) ||
5465            internal::XMVector3AllTrue(XMVectorSelect(BDistIsLess, BDistIsGreaterEqual, Select1011.v())))
5466        {
5467            // B1 is singular, crossing from negative to positive.
5468            BB0 = B1; BB1 = B0; BB2 = B2;
5469            bPositiveB = false;
5470        }
5471        else if (internal::XMVector3AllTrue(XMVectorSelect(BDistIsGreaterEqual, BDistIsLess, Select1101.v())) ||
5472            internal::XMVector3AllTrue(XMVectorSelect(BDistIsGreater, BDistIsLessEqual, Select1101.v())))
5473        {
5474            // B2 is singular, crossing from positive to negative.
5475            BB0 = B2; BB1 = B0; BB2 = B1;
5476            bPositiveB = true;
5477        }
5478        else if (internal::XMVector3AllTrue(XMVectorSelect(BDistIsLessEqual, BDistIsGreater, Select1101.v())) ||
5479            internal::XMVector3AllTrue(XMVectorSelect(BDistIsLess, BDistIsGreaterEqual, Select1101.v())))
5480        {
5481            // B2 is singular, crossing from negative to positive.
5482            BB0 = B2; BB1 = B1; BB2 = B0;
5483            bPositiveB = false;
5484        }
5485        else
5486        {
5487            debug_assert!(false);
5488            return false;
5489        }
5490
5491        let Delta0: XMVECTOR;
5492        let Delta1: XMVECTOR;
5493
5494        // Reverse the direction of the test depending on whether the singular vertices are
5495        // the same sign or different signs.
5496        if (bPositiveA ^ bPositiveB)
5497        {
5498            Delta0 = XMVectorSubtract(BB0, AA0);
5499            Delta1 = XMVectorSubtract(AA0, BB0);
5500        }
5501        else
5502        {
5503            Delta0 = XMVectorSubtract(AA0, BB0);
5504            Delta1 = XMVectorSubtract(BB0, AA0);
5505        }
5506
5507        // Check if the triangles overlap on the line of intersection between the
5508        // planes of the two triangles by finding the signed line distances.
5509        let Dist0: XMVECTOR = XMVector3Dot(Delta0, XMVector3Cross(XMVectorSubtract(BB2, BB0), XMVectorSubtract(AA2, AA0)));
5510        if (XMVector4Greater(Dist0, Zero)) {
5511            return false;
5512        }
5513
5514        let Dist1: XMVECTOR = XMVector3Dot(Delta1, XMVector3Cross(XMVectorSubtract(BB1, BB0), XMVectorSubtract(AA1, AA0)));
5515        if (XMVector4Greater(Dist1, Zero)) {
5516            return false;
5517        }
5518
5519        return true;
5520    }
5521
5522    /// Tests whether a triangle and a plane intersect.
5523    ///
5524    /// ## Parameters
5525    ///
5526    /// `V0` A vector defining a triangle.
5527    ///
5528    /// `V1` A vector defining a triangle.
5529    ///
5530    /// `V2` A vector defining a triangle.
5531    ///
5532    /// `Plane` A vector defining a plane coefficients (`A`, `B`, `C`, `D`) for the plane equation `Ax+By+Cz+D=0`.
5533    ///
5534    /// ## Return value
5535    ///
5536    /// A PlaneIntersectionType value indicating whether the triangle intersects the plane.
5537    ///
5538    /// ## Reference
5539    ///
5540    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-intersects~r2>
5541    #[inline]
5542    pub fn IntersectsPlane(
5543        V0: FXMVECTOR,
5544        V1: FXMVECTOR,
5545        V2: FXMVECTOR,
5546        Plane: GXMVECTOR,
5547    ) -> PlaneIntersectionType {
5548        let One: XMVECTOR = XMVectorSplatOne();
5549
5550        debug_assert!(internal::XMPlaneIsUnit(Plane));
5551
5552        // Set w of the points to one so we can dot4 with a plane.
5553        // TODO: template
5554        let TV0: XMVECTOR = XMVectorInsert(V0, One, 0, 0, 0, 0, 1);
5555        let TV1: XMVECTOR = XMVectorInsert(V1, One, 0, 0, 0, 0, 1);
5556        let TV2: XMVECTOR = XMVectorInsert(V2, One, 0, 0, 0, 0, 1);
5557
5558        let mut Outside: XMVECTOR = unsafe { undefined() };
5559        let mut Inside: XMVECTOR = unsafe { undefined() };
5560
5561        internal::FastIntersectTrianglePlane(TV0, TV1, TV2, Plane, &mut Outside, &mut Inside);
5562
5563        // If the triangle is outside any plane it is outside.
5564        if (XMVector4EqualInt(Outside, XMVectorTrueInt())) {
5565            return FRONT;
5566        }
5567
5568        // If the triangle is inside all planes it is inside.
5569        if (XMVector4EqualInt(Inside, XMVectorTrueInt())) {
5570            return BACK;
5571        }
5572
5573        // The triangle is not inside all planes or outside a plane it intersects.
5574        return INTERSECTING;
5575    }
5576
5577    /// Test a triangle vs 6 planes (typically forming a frustum).
5578    ///
5579    /// ## Parameters
5580    ///
5581    /// `V0` A vector defining the triangle.
5582    ///
5583    /// `V1` A vector defining the triangle.
5584    ///
5585    /// `V2` A vector defining the triangle.
5586    ///
5587    /// `Plane0` A vector defining a plane.
5588    ///
5589    /// `Plane1` A vector defining a plane.
5590    ///
5591    /// `Plane2` A vector defining a plane.
5592    ///
5593    /// `Plane3` A vector defining a plane.
5594    ///
5595    /// `Plane4` A vector defining a plane.
5596    ///
5597    /// `Plane5` A vector defining a plane.
5598    ///
5599    /// ## Return value
5600    ///
5601    /// A ContainmentType value indicating whether the triangle is contained within the planes.
5602    ///
5603    /// ## Remarks
5604    ///
5605    /// Typically the six planes passed to this function represent a frustum.
5606    ///
5607    /// ## Reference
5608    ///
5609    /// <https://docs.microsoft.com/en-us/windows/win32/api/directxcollision/nf-directxcollision-containedby>
5610    #[inline]
5611    pub fn ContainedBy(
5612        V0: FXMVECTOR,
5613        V1: FXMVECTOR,
5614        V2: FXMVECTOR,
5615        Plane0: GXMVECTOR,
5616        Plane1: HXMVECTOR,
5617        Plane2: HXMVECTOR,
5618        Plane3: CXMVECTOR,
5619        Plane4: CXMVECTOR,
5620        Plane5: CXMVECTOR,
5621    ) -> ContainmentType {
5622        let One: XMVECTOR = XMVectorSplatOne();
5623
5624        // Set w of the points to one so we can dot4 with a plane.
5625        // TODO: template
5626        let TV0: XMVECTOR = XMVectorInsert(V0, One, 0, 0, 0, 0, 1);
5627        let TV1: XMVECTOR = XMVectorInsert(V1, One, 0, 0, 0, 0, 1);
5628        let TV2: XMVECTOR = XMVectorInsert(V2, One, 0, 0, 0, 0, 1);
5629
5630        let mut Outside: XMVECTOR = unsafe { undefined() };
5631        let mut Inside: XMVECTOR = unsafe { undefined() };
5632
5633        // Test against each plane.
5634        internal::FastIntersectTrianglePlane(TV0, TV1, TV2, Plane0, &mut Outside, &mut Inside);
5635
5636        let mut AnyOutside: XMVECTOR = Outside;
5637        let mut AllInside: XMVECTOR = Inside;
5638
5639        internal::FastIntersectTrianglePlane(TV0, TV1, TV2, Plane1, &mut Outside, &mut Inside);
5640        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
5641        AllInside = XMVectorAndInt(AllInside, Inside);
5642
5643        internal::FastIntersectTrianglePlane(TV0, TV1, TV2, Plane2, &mut Outside, &mut Inside);
5644        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
5645        AllInside = XMVectorAndInt(AllInside, Inside);
5646
5647        internal::FastIntersectTrianglePlane(TV0, TV1, TV2, *Plane3, &mut Outside, &mut Inside);
5648        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
5649        AllInside = XMVectorAndInt(AllInside, Inside);
5650
5651        internal::FastIntersectTrianglePlane(TV0, TV1, TV2, *Plane4, &mut Outside, &mut Inside);
5652        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
5653        AllInside = XMVectorAndInt(AllInside, Inside);
5654
5655        internal::FastIntersectTrianglePlane(TV0, TV1, TV2, *Plane5, &mut Outside, &mut Inside);
5656        AnyOutside = XMVectorOrInt(AnyOutside, Outside);
5657        AllInside = XMVectorAndInt(AllInside, Inside);
5658
5659        // If the triangle is outside any plane it is outside.
5660        if (XMVector4EqualInt(AnyOutside, XMVectorTrueInt())) {
5661            return DISJOINT;
5662        }
5663
5664        // If the triangle is inside all planes it is inside.
5665        if (XMVector4EqualInt(AllInside, XMVectorTrueInt())) {
5666            return CONTAINS;
5667        }
5668
5669        // The triangle is not inside all planes or outside a plane, it may intersect.
5670        return INTERSECTS;
5671    }
5672}