directx_math/
matrix.rs

1use crate::*;
2use std::mem;
3
4/// Tests whether any of the elements of a matrix are NaN.
5///
6/// ## Parameters
7///
8/// `M` Matrix to test.
9///
10/// ## Return value
11///
12/// Returns `true` if any element of `M` is `NaN`, and `false` otherwise.
13///
14/// ## Reference
15///
16/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixIsNaN>
17#[inline]
18pub fn XMMatrixIsNaN(
19    M: FXMMATRIX,
20) -> bool
21{
22    #[cfg(_XM_NO_INTRINSICS_)]
23    unsafe {
24        let pWork: &[u32; 16] = mem::transmute(&M.m[0][0]);
25        for mut uTest in pWork.iter().cloned() {
26            // Remove sign
27            uTest &= 0x7FFFFFFFu32;
28            // NaN is 0x7F800001 through 0x7FFFFFFF inclusive
29            uTest = uTest.wrapping_sub(0x7F800001u32);
30            if (uTest < 0x007FFFFFu32) {
31                return true;
32            }
33        }
34        false
35    }
36
37    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
38    {
39        unimplemented!()
40    }
41
42    #[cfg(_XM_SSE_INTRINSICS_)]
43    unsafe {
44        // Load in registers
45        let mut vX: XMVECTOR = M.r[0];
46        let mut vY: XMVECTOR = M.r[1];
47        let mut vZ: XMVECTOR = M.r[2];
48        let mut vW: XMVECTOR = M.r[3];
49        // Test themselves to check for NaN
50        vX = _mm_cmpneq_ps(vX, vX);
51        vY = _mm_cmpneq_ps(vY, vY);
52        vZ = _mm_cmpneq_ps(vZ, vZ);
53        vW = _mm_cmpneq_ps(vW, vW);
54        // Or all the results
55        vX = _mm_or_ps(vX, vZ);
56        vY = _mm_or_ps(vY, vW);
57        vX = _mm_or_ps(vX, vY);
58        // If any tested true, return true
59        return (_mm_movemask_ps(vX) != 0);
60    }
61}
62
63#[test]
64fn test_XMMatrixIsNan() {
65    let m = XMMATRIX {
66        r: [*g_XMZero, *g_XMZero, *g_XMZero, *g_XMZero],
67    };
68    assert_eq!(false, XMMatrixIsNaN(m));
69
70    let m = XMMATRIX {
71        r: [*g_XMInfinity, *g_XMInfinity, *g_XMInfinity, *g_XMInfinity],
72    };
73    assert_eq!(false, XMMatrixIsNaN(m));
74
75    let m = XMMATRIX {
76        r: [*g_XMZero, *g_XMZero, *g_XMZero, *g_XMNegQNaN],
77    };
78    assert_eq!(true, XMMatrixIsNaN(m));
79
80    let m = XMMATRIX {
81        r: [XMVectorReplicate(std::f32::NAN), *g_XMZero, *g_XMZero, *g_XMZero],
82    };
83    assert_eq!(true, XMMatrixIsNaN(m));
84}
85
86/// Tests whether any of the elements of a matrix are infinite.
87///
88/// ## Parameters
89///
90/// `M` Matrix to test.
91///
92/// ## Return value
93///
94/// Returns `true` if any element of `M` is either positive or negative infinity, and `false` otherwise.
95///
96/// ## Reference
97///
98/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixIsInfinite>
99#[inline]
100pub fn XMMatrixIsInfinite(
101    M: FXMMATRIX,
102) -> bool
103{
104    #[cfg(_XM_NO_INTRINSICS_)]
105    unsafe {
106        let pWork: &[u32; 16] = mem::transmute(&M.m[0][0]);
107        for mut uTest in pWork.iter().cloned() {
108            // Remove sign
109            uTest &= 0x7FFFFFFFu32;
110            // INF is 0x7F800000
111            if (uTest == 0x7F800000u32) {
112                return true;
113            }
114        }
115        false
116    }
117
118    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
119    {
120        unimplemented!()
121    }
122
123    #[cfg(_XM_SSE_INTRINSICS_)]
124    unsafe {
125        // Mask off the sign bits
126        let mut vTemp1: XMVECTOR = _mm_and_ps(M.r[0], *g_XMAbsMask);
127        let mut vTemp2: XMVECTOR = _mm_and_ps(M.r[1], *g_XMAbsMask);
128        let mut vTemp3: XMVECTOR = _mm_and_ps(M.r[2], *g_XMAbsMask);
129        let mut vTemp4: XMVECTOR = _mm_and_ps(M.r[3], *g_XMAbsMask);
130        // Compare to infinity
131        vTemp1 = _mm_cmpeq_ps(vTemp1, *g_XMInfinity);
132        vTemp2 = _mm_cmpeq_ps(vTemp2, *g_XMInfinity);
133        vTemp3 = _mm_cmpeq_ps(vTemp3, *g_XMInfinity);
134        vTemp4 = _mm_cmpeq_ps(vTemp4, *g_XMInfinity);
135        // Or the answers together
136        vTemp1 = _mm_or_ps(vTemp1, vTemp2);
137        vTemp3 = _mm_or_ps(vTemp3, vTemp4);
138        vTemp1 = _mm_or_ps(vTemp1, vTemp3);
139        // If any are infinity, the signs are true.
140        return (_mm_movemask_ps(vTemp1) != 0);
141    }
142}
143
144#[test]
145fn test_XMMatrixIsInfinite() {
146    let m = XMMATRIX {
147        r: [*g_XMZero, *g_XMZero, *g_XMZero, *g_XMZero],
148    };
149    assert_eq!(false, XMMatrixIsInfinite(m));
150
151    let m = XMMATRIX {
152        r: [*g_XMQNaN, *g_XMQNaN, *g_XMQNaN, *g_XMQNaN],
153    };
154    assert_eq!(false, XMMatrixIsInfinite(m));
155
156    let m = XMMATRIX {
157        r: [*g_XMZero, *g_XMZero, *g_XMZero, *g_XMNegInfinity],
158    };
159    assert_eq!(true, XMMatrixIsInfinite(m));
160
161    let m = XMMATRIX {
162        r: [*g_XMZero, *g_XMZero, *g_XMZero, *g_XMInfinity],
163    };
164    assert_eq!(true, XMMatrixIsInfinite(m));
165}
166
167/// Tests whether a matrix is the identity matrix.
168///
169/// ## Parameters
170///
171/// `M` Matrix to test.
172///
173/// ## Return value
174///
175/// Returns `true` if `M` is the identity matrix, and `false` otherwise.
176///
177/// ## Reference
178///
179/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixIsIdentity>
180#[inline]
181pub fn XMMatrixIsIdentity(
182    M: FXMMATRIX,
183) -> bool
184{
185    #[cfg(_XM_NO_INTRINSICS_)]
186    unsafe {
187        // Use the integer pipeline to reduce branching to a minimum
188        let pWork: &[u32; 16] = mem::transmute(&M.m[0][0]);
189        // Convert 1.0f to zero and or them together
190        let mut uOne: u32 = pWork[0] ^ 0x3F800000u32;
191        // Or all the 0.0f entries together
192        let mut uZero: u32 = pWork[1];
193        uZero |= pWork[2];
194        uZero |= pWork[3];
195        // 2nd row
196        uZero |= pWork[4];
197        uOne |= pWork[5] ^ 0x3F800000u32;
198        uZero |= pWork[6];
199        uZero |= pWork[7];
200        // 3rd row
201        uZero |= pWork[8];
202        uZero |= pWork[9];
203        uOne |= pWork[10] ^ 0x3F800000u32;
204        uZero |= pWork[11];
205        // 4th row
206        uZero |= pWork[12];
207        uZero |= pWork[13];
208        uZero |= pWork[14];
209        uOne |= pWork[15] ^ 0x3F800000u32;
210        // If all zero entries are zero, the uZero==0
211        uZero &= 0x7FFFFFFF;    // Allow -0.0f
212        // If all 1.0f entries are 1.0f, then uOne==0
213        uOne |= uZero;
214        return (uOne == 0);
215    }
216
217    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
218    {
219        unimplemented!()
220    }
221
222    #[cfg(_XM_SSE_INTRINSICS_)]
223    unsafe {
224        let mut vTemp1: XMVECTOR = _mm_cmpeq_ps(M.r[0], *g_XMIdentityR0);
225        let vTemp2: XMVECTOR = _mm_cmpeq_ps(M.r[1], *g_XMIdentityR1);
226        let mut vTemp3: XMVECTOR = _mm_cmpeq_ps(M.r[2], *g_XMIdentityR2);
227        let vTemp4: XMVECTOR = _mm_cmpeq_ps(M.r[3], *g_XMIdentityR3);
228        vTemp1 = _mm_and_ps(vTemp1, vTemp2);
229        vTemp3 = _mm_and_ps(vTemp3, vTemp4);
230        vTemp1 = _mm_and_ps(vTemp1, vTemp3);
231        return (_mm_movemask_ps(vTemp1) == 0x0f);
232    }
233}
234
235/// Computes the product of two matrices.
236///
237/// ## Parameters
238///
239/// `M1` First matrix to multiply.
240///
241/// `M2` Second matrix to multiply.
242///
243/// ## Return value
244///
245/// Returns the product of `M1` and `M2`.
246///
247/// ## Reference
248///
249/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixMultiply>
250#[inline]
251pub fn XMMatrixMultiply(
252    M1: FXMMATRIX,
253    M2: CXMMATRIX,
254) -> XMMATRIX
255{
256    #[cfg(_XM_NO_INTRINSICS_)]
257    unsafe {
258        let mut mResult: XMMATRIX = crate::undefined();
259        // Cache the invariants in registers
260        let mut x: f32 = M1.m[0][0];
261        let mut y: f32 = M1.m[0][1];
262        let mut z: f32 = M1.m[0][2];
263        let mut w: f32 = M1.m[0][3];
264        // Perform the operation on the first row
265        mResult.m[0][0] = (M2.m[0][0] * x) + (M2.m[1][0] * y) + (M2.m[2][0] * z) + (M2.m[3][0] * w);
266        mResult.m[0][1] = (M2.m[0][1] * x) + (M2.m[1][1] * y) + (M2.m[2][1] * z) + (M2.m[3][1] * w);
267        mResult.m[0][2] = (M2.m[0][2] * x) + (M2.m[1][2] * y) + (M2.m[2][2] * z) + (M2.m[3][2] * w);
268        mResult.m[0][3] = (M2.m[0][3] * x) + (M2.m[1][3] * y) + (M2.m[2][3] * z) + (M2.m[3][3] * w);
269        // Repeat for all the other rows
270        x = M1.m[1][0];
271        y = M1.m[1][1];
272        z = M1.m[1][2];
273        w = M1.m[1][3];
274        mResult.m[1][0] = (M2.m[0][0] * x) + (M2.m[1][0] * y) + (M2.m[2][0] * z) + (M2.m[3][0] * w);
275        mResult.m[1][1] = (M2.m[0][1] * x) + (M2.m[1][1] * y) + (M2.m[2][1] * z) + (M2.m[3][1] * w);
276        mResult.m[1][2] = (M2.m[0][2] * x) + (M2.m[1][2] * y) + (M2.m[2][2] * z) + (M2.m[3][2] * w);
277        mResult.m[1][3] = (M2.m[0][3] * x) + (M2.m[1][3] * y) + (M2.m[2][3] * z) + (M2.m[3][3] * w);
278        x = M1.m[2][0];
279        y = M1.m[2][1];
280        z = M1.m[2][2];
281        w = M1.m[2][3];
282        mResult.m[2][0] = (M2.m[0][0] * x) + (M2.m[1][0] * y) + (M2.m[2][0] * z) + (M2.m[3][0] * w);
283        mResult.m[2][1] = (M2.m[0][1] * x) + (M2.m[1][1] * y) + (M2.m[2][1] * z) + (M2.m[3][1] * w);
284        mResult.m[2][2] = (M2.m[0][2] * x) + (M2.m[1][2] * y) + (M2.m[2][2] * z) + (M2.m[3][2] * w);
285        mResult.m[2][3] = (M2.m[0][3] * x) + (M2.m[1][3] * y) + (M2.m[2][3] * z) + (M2.m[3][3] * w);
286        x = M1.m[3][0];
287        y = M1.m[3][1];
288        z = M1.m[3][2];
289        w = M1.m[3][3];
290        mResult.m[3][0] = (M2.m[0][0] * x) + (M2.m[1][0] * y) + (M2.m[2][0] * z) + (M2.m[3][0] * w);
291        mResult.m[3][1] = (M2.m[0][1] * x) + (M2.m[1][1] * y) + (M2.m[2][1] * z) + (M2.m[3][1] * w);
292        mResult.m[3][2] = (M2.m[0][2] * x) + (M2.m[1][2] * y) + (M2.m[2][2] * z) + (M2.m[3][2] * w);
293        mResult.m[3][3] = (M2.m[0][3] * x) + (M2.m[1][3] * y) + (M2.m[2][3] * z) + (M2.m[3][3] * w);
294        return mResult;
295    }
296
297    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
298    {
299        unimplemented!()
300    }
301
302    #[cfg(_XM_AVX2_INTRINSICS_)]
303    unsafe {
304        let mut t0: __m256 = _mm256_castps128_ps256(M1.r[0]);
305        t0 = _mm256_insertf128_ps(t0, M1.r[1], 1);
306        let mut t1: __m256 = _mm256_castps128_ps256(M1.r[2]);
307        t1 = _mm256_insertf128_ps(t1, M1.r[3], 1);
308
309        let mut u0: __m256 = _mm256_castps128_ps256(M2.r[0]);
310        u0 = _mm256_insertf128_ps(u0, M2.r[1], 1);
311        let mut u1: __m256 = _mm256_castps128_ps256(M2.r[2]);
312        u1 = _mm256_insertf128_ps(u1, M2.r[3], 1);
313
314        let mut a0: __m256 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(0, 0, 0, 0));
315        let mut a1: __m256 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(0, 0, 0, 0));
316        let mut b0: __m256 = _mm256_permute2f128_ps(u0, u0, 0x00);
317        let c0: __m256 = _mm256_mul_ps(a0, b0);
318        let c1: __m256 = _mm256_mul_ps(a1, b0);
319
320        a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(1, 1, 1, 1));
321        a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(1, 1, 1, 1));
322        b0 = _mm256_permute2f128_ps(u0, u0, 0x11);
323        let c2: __m256 = _mm256_fmadd_ps(a0, b0, c0);
324        let c3: __m256 = _mm256_fmadd_ps(a1, b0, c1);
325
326        a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(2, 2, 2, 2));
327        a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(2, 2, 2, 2));
328        let mut b1: __m256 = _mm256_permute2f128_ps(u1, u1, 0x00);
329        let c4: __m256 = _mm256_mul_ps(a0, b1);
330        let c5: __m256 = _mm256_mul_ps(a1, b1);
331
332        a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(3, 3, 3, 3));
333        a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(3, 3, 3, 3));
334        b1 = _mm256_permute2f128_ps(u1, u1, 0x11);
335        let c6: __m256 = _mm256_fmadd_ps(a0, b1, c4);
336        let c7: __m256 = _mm256_fmadd_ps(a1, b1, c5);
337
338        t0 = _mm256_add_ps(c2, c6);
339        t1 = _mm256_add_ps(c3, c7);
340
341        let mut mResult: XMMATRIX = crate::undefined();
342        mResult.r[0] = _mm256_castps256_ps128(t0);
343        mResult.r[1] = _mm256_extractf128_ps(t0, 1);
344        mResult.r[2] = _mm256_castps256_ps128(t1);
345        mResult.r[3] = _mm256_extractf128_ps(t1, 1);
346        return mResult;
347    }
348
349    #[cfg(all(_XM_AVX_INTRINSICS_, not(_XM_AVX2_INTRINSICS_)))]
350    unsafe {
351        let mut mResult: XMMATRIX = crate::undefined();
352
353        // Splat the component X,Y,Z then W
354        let mut vX: XMVECTOR = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[0])[0])));
355        let mut vY: XMVECTOR = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[0])[1])));
356        let mut vZ: XMVECTOR = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[0])[2])));
357        let mut vW: XMVECTOR = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[0])[3])));
358
359        // Perform the operation on the first row
360        vX = _mm_mul_ps(vX, M2.r[0]);
361        vY = _mm_mul_ps(vY, M2.r[1]);
362        vZ = _mm_mul_ps(vZ, M2.r[2]);
363        vW = _mm_mul_ps(vW, M2.r[3]);
364        // Perform a binary add to reduce cumulative errors
365        vX = _mm_add_ps(vX, vZ);
366        vY = _mm_add_ps(vY, vW);
367        vX = _mm_add_ps(vX, vY);
368        mResult.r[0] = vX;
369
370        // Repeat for the other 3 rows
371        vX = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[1])[0])));
372        vY = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[1])[1])));
373        vZ = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[1])[2])));
374        vW = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[1])[3])));
375
376        vX = _mm_mul_ps(vX, M2.r[0]);
377        vY = _mm_mul_ps(vY, M2.r[1]);
378        vZ = _mm_mul_ps(vZ, M2.r[2]);
379        vW = _mm_mul_ps(vW, M2.r[3]);
380
381        vX = _mm_add_ps(vX, vZ);
382        vY = _mm_add_ps(vY, vW);
383        vX = _mm_add_ps(vX, vY);
384        mResult.r[1] = vX;
385
386        vX = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[2])[0])));
387        vY = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[2])[1])));
388        vZ = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[2])[2])));
389        vW = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[2])[3])));
390
391        vX = _mm_mul_ps(vX, M2.r[0]);
392        vY = _mm_mul_ps(vY, M2.r[1]);
393        vZ = _mm_mul_ps(vZ, M2.r[2]);
394        vW = _mm_mul_ps(vW, M2.r[3]);
395
396        vX = _mm_add_ps(vX, vZ);
397        vY = _mm_add_ps(vY, vW);
398        vX = _mm_add_ps(vX, vY);
399        mResult.r[2] = vX;
400
401        vX = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[3])[0])));
402        vY = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[3])[1])));
403        vZ = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[3])[2])));
404        vW = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[3])[3])));
405
406        vX = _mm_mul_ps(vX, M2.r[0]);
407        vY = _mm_mul_ps(vY, M2.r[1]);
408        vZ = _mm_mul_ps(vZ, M2.r[2]);
409        vW = _mm_mul_ps(vW, M2.r[3]);
410
411        vX = _mm_add_ps(vX, vZ);
412        vY = _mm_add_ps(vY, vW);
413        vX = _mm_add_ps(vX, vY);
414        mResult.r[3] = vX;
415        return mResult;
416    }
417
418    #[cfg(all(_XM_SSE_INTRINSICS_, not(_XM_AVX_INTRINSICS_), not(_XM_AVX2_INTRINSICS_)))]
419    unsafe {
420        let mut mResult: XMMATRIX = crate::undefined();
421
422        // Use vW to hold the original row
423        let mut vW: XMVECTOR = M1.r[0];
424        let mut vX: XMVECTOR = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(0, 0, 0, 0));
425        let mut vY: XMVECTOR = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(1, 1, 1, 1));
426        let mut vZ: XMVECTOR = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(2, 2, 2, 2));
427        vW = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(3, 3, 3, 3));
428
429        // Perform the operation on the first row
430        vX = _mm_mul_ps(vX, M2.r[0]);
431        vY = _mm_mul_ps(vY, M2.r[1]);
432        vZ = _mm_mul_ps(vZ, M2.r[2]);
433        vW = _mm_mul_ps(vW, M2.r[3]);
434        // Perform a binary add to reduce cumulative errors
435        vX = _mm_add_ps(vX, vZ);
436        vY = _mm_add_ps(vY, vW);
437        vX = _mm_add_ps(vX, vY);
438        mResult.r[0] = vX;
439        // Repeat for the other 3 rows
440
441        vW = M1.r[1];
442        vX = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(0, 0, 0, 0));
443        vY = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(1, 1, 1, 1));
444        vZ = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(2, 2, 2, 2));
445        vW = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(3, 3, 3, 3));
446
447        vX = _mm_mul_ps(vX, M2.r[0]);
448        vY = _mm_mul_ps(vY, M2.r[1]);
449        vZ = _mm_mul_ps(vZ, M2.r[2]);
450        vW = _mm_mul_ps(vW, M2.r[3]);
451        vX = _mm_add_ps(vX, vZ);
452        vY = _mm_add_ps(vY, vW);
453        vX = _mm_add_ps(vX, vY);
454        mResult.r[1] = vX;
455
456        vW = M1.r[2];
457        vX = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(0, 0, 0, 0));
458        vY = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(1, 1, 1, 1));
459        vZ = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(2, 2, 2, 2));
460        vW = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(3, 3, 3, 3));
461
462        vX = _mm_mul_ps(vX, M2.r[0]);
463        vY = _mm_mul_ps(vY, M2.r[1]);
464        vZ = _mm_mul_ps(vZ, M2.r[2]);
465        vW = _mm_mul_ps(vW, M2.r[3]);
466        vX = _mm_add_ps(vX, vZ);
467        vY = _mm_add_ps(vY, vW);
468        vX = _mm_add_ps(vX, vY);
469        mResult.r[2] = vX;
470
471        vW = M1.r[3];
472        vX = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(0, 0, 0, 0));
473        vY = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(1, 1, 1, 1));
474        vZ = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(2, 2, 2, 2));
475        vW = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(3, 3, 3, 3));
476
477        vX = _mm_mul_ps(vX, M2.r[0]);
478        vY = _mm_mul_ps(vY, M2.r[1]);
479        vZ = _mm_mul_ps(vZ, M2.r[2]);
480        vW = _mm_mul_ps(vW, M2.r[3]);
481        vX = _mm_add_ps(vX, vZ);
482        vY = _mm_add_ps(vY, vW);
483        vX = _mm_add_ps(vX, vY);
484        mResult.r[3] = vX;
485        return mResult;
486    }
487}
488
489#[test]
490fn test_XMMatrixMultiply() {
491    let a = XMMATRIX {
492        r: [
493            XMVectorSet( 1.0,  2.0,  3.0,  4.0),
494            XMVectorSet( 5.0,  6.0,  7.0,  8.0),
495            XMVectorSet( 9.0, 10.0, 11.0, 12.0),
496            XMVectorSet(13.0, 14.0, 15.0, 16.0),
497        ]
498    };
499    let b = XMMATRIX {
500        r: [
501            XMVectorSet(13.0, 14.0, 15.0, 16.0),
502            XMVectorSet( 9.0, 10.0, 11.0, 12.0),
503            XMVectorSet( 5.0,  6.0,  7.0,  8.0),
504            XMVectorSet( 1.0,  2.0,  3.0,  4.0),
505        ]
506    };
507
508    let c = XMMatrixMultiply(a, &b);
509
510    let d = XMMATRIX {
511        r: [
512            XMVectorSet( 50.0,  60.0,  70.0,  80.0),
513            XMVectorSet(162.0, 188.0, 214.0, 240.0),
514            XMVectorSet(274.0, 316.0, 358.0, 400.0),
515            XMVectorSet(386.0, 444.0, 502.0, 560.0),
516        ]
517    };
518
519    unsafe {
520        let mut cr = 0;
521        XMVectorEqualR(&mut cr, c.r[0], d.r[0]);
522        assert!(XMComparisonAllTrue(cr));
523
524        XMVectorEqualR(&mut cr, c.r[1], d.r[1]);
525        assert!(XMComparisonAllTrue(cr));
526
527        XMVectorEqualR(&mut cr, c.r[2], d.r[2]);
528        assert!(XMComparisonAllTrue(cr));
529
530        XMVectorEqualR(&mut cr, c.r[3], d.r[3]);
531        assert!(XMComparisonAllTrue(cr));
532    }
533}
534
535/// Computes the transpose of the product of two matrices.
536///
537/// ## Parameters
538///
539/// `M1` First matrix to multiply.
540///
541/// `M2` Second matrix to multiply.
542///
543/// ## Return value
544///
545/// Returns the transpose of the product of `M1` and `M2`.
546///
547/// ## Reference
548///
549/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixMultiplyTranspose>
550#[inline]
551pub fn XMMatrixMultiplyTranspose(
552    M1: FXMMATRIX,
553    M2: CXMMATRIX,
554) -> XMMATRIX
555{
556    #[cfg(_XM_NO_INTRINSICS_)]
557    unsafe {
558        let mut mResult: XMMATRIX = crate::undefined();
559        // Cache the invariants in registers
560        let mut x: f32 = M2.m[0][0];
561        let mut y: f32 = M2.m[1][0];
562        let mut z: f32 = M2.m[2][0];
563        let mut w: f32 = M2.m[3][0];
564        // Perform the operation on the first row
565        mResult.m[0][0] = (M1.m[0][0] * x) + (M1.m[0][1] * y) + (M1.m[0][2] * z) + (M1.m[0][3] * w);
566        mResult.m[0][1] = (M1.m[1][0] * x) + (M1.m[1][1] * y) + (M1.m[1][2] * z) + (M1.m[1][3] * w);
567        mResult.m[0][2] = (M1.m[2][0] * x) + (M1.m[2][1] * y) + (M1.m[2][2] * z) + (M1.m[2][3] * w);
568        mResult.m[0][3] = (M1.m[3][0] * x) + (M1.m[3][1] * y) + (M1.m[3][2] * z) + (M1.m[3][3] * w);
569        // Repeat for all the other rows
570        x = M2.m[0][1];
571        y = M2.m[1][1];
572        z = M2.m[2][1];
573        w = M2.m[3][1];
574        mResult.m[1][0] = (M1.m[0][0] * x) + (M1.m[0][1] * y) + (M1.m[0][2] * z) + (M1.m[0][3] * w);
575        mResult.m[1][1] = (M1.m[1][0] * x) + (M1.m[1][1] * y) + (M1.m[1][2] * z) + (M1.m[1][3] * w);
576        mResult.m[1][2] = (M1.m[2][0] * x) + (M1.m[2][1] * y) + (M1.m[2][2] * z) + (M1.m[2][3] * w);
577        mResult.m[1][3] = (M1.m[3][0] * x) + (M1.m[3][1] * y) + (M1.m[3][2] * z) + (M1.m[3][3] * w);
578        x = M2.m[0][2];
579        y = M2.m[1][2];
580        z = M2.m[2][2];
581        w = M2.m[3][2];
582        mResult.m[2][0] = (M1.m[0][0] * x) + (M1.m[0][1] * y) + (M1.m[0][2] * z) + (M1.m[0][3] * w);
583        mResult.m[2][1] = (M1.m[1][0] * x) + (M1.m[1][1] * y) + (M1.m[1][2] * z) + (M1.m[1][3] * w);
584        mResult.m[2][2] = (M1.m[2][0] * x) + (M1.m[2][1] * y) + (M1.m[2][2] * z) + (M1.m[2][3] * w);
585        mResult.m[2][3] = (M1.m[3][0] * x) + (M1.m[3][1] * y) + (M1.m[3][2] * z) + (M1.m[3][3] * w);
586        x = M2.m[0][3];
587        y = M2.m[1][3];
588        z = M2.m[2][3];
589        w = M2.m[3][3];
590        mResult.m[3][0] = (M1.m[0][0] * x) + (M1.m[0][1] * y) + (M1.m[0][2] * z) + (M1.m[0][3] * w);
591        mResult.m[3][1] = (M1.m[1][0] * x) + (M1.m[1][1] * y) + (M1.m[1][2] * z) + (M1.m[1][3] * w);
592        mResult.m[3][2] = (M1.m[2][0] * x) + (M1.m[2][1] * y) + (M1.m[2][2] * z) + (M1.m[2][3] * w);
593        mResult.m[3][3] = (M1.m[3][0] * x) + (M1.m[3][1] * y) + (M1.m[3][2] * z) + (M1.m[3][3] * w);
594        return mResult;
595    }
596
597    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
598    {
599        unimplemented!()
600    }
601
602    #[cfg(_XM_AVX2_INTRINSICS_)]
603    unsafe {
604        let mut t0: __m256 = _mm256_castps128_ps256(M1.r[0]);
605        t0 = _mm256_insertf128_ps(t0, M1.r[1], 1);
606        let mut t1: __m256 = _mm256_castps128_ps256(M1.r[2]);
607        t1 = _mm256_insertf128_ps(t1, M1.r[3], 1);
608
609        let mut u0: __m256 = _mm256_castps128_ps256(M2.r[0]);
610        u0 = _mm256_insertf128_ps(u0, M2.r[1], 1);
611        let mut u1: __m256 = _mm256_castps128_ps256(M2.r[2]);
612        u1 = _mm256_insertf128_ps(u1, M2.r[3], 1);
613
614        let mut a0: __m256 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(0, 0, 0, 0));
615        let mut a1: __m256 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(0, 0, 0, 0));
616        let mut b0: __m256 = _mm256_permute2f128_ps(u0, u0, 0x00);
617        let c0: __m256 = _mm256_mul_ps(a0, b0);
618        let c1: __m256 = _mm256_mul_ps(a1, b0);
619
620        a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(1, 1, 1, 1));
621        a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(1, 1, 1, 1));
622        b0 = _mm256_permute2f128_ps(u0, u0, 0x11);
623        let c2: __m256 = _mm256_fmadd_ps(a0, b0, c0);
624        let c3: __m256 = _mm256_fmadd_ps(a1, b0, c1);
625
626        a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(2, 2, 2, 2));
627        a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(2, 2, 2, 2));
628        let mut b1: __m256 = _mm256_permute2f128_ps(u1, u1, 0x00);
629        let c4: __m256 = _mm256_mul_ps(a0, b1);
630        let c5: __m256 = _mm256_mul_ps(a1, b1);
631
632        a0 = _mm256_shuffle_ps(t0, t0, _MM_SHUFFLE(3, 3, 3, 3));
633        a1 = _mm256_shuffle_ps(t1, t1, _MM_SHUFFLE(3, 3, 3, 3));
634        b1 = _mm256_permute2f128_ps(u1, u1, 0x11);
635        let c6: __m256 = _mm256_fmadd_ps(a0, b1, c4);
636        let c7: __m256 = _mm256_fmadd_ps(a1, b1, c5);
637
638        t0 = _mm256_add_ps(c2, c6);
639        t1 = _mm256_add_ps(c3, c7);
640
641        // Transpose result
642        let mut vTemp: __m256 = _mm256_unpacklo_ps(t0, t1);
643        let mut vTemp2: __m256 = _mm256_unpackhi_ps(t0, t1);
644        let vTemp3: __m256 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x20);
645        let vTemp4: __m256 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x31);
646        vTemp = _mm256_unpacklo_ps(vTemp3, vTemp4);
647        vTemp2 = _mm256_unpackhi_ps(vTemp3, vTemp4);
648        t0 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x20);
649        t1 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x31);
650
651        let mut mResult: XMMATRIX = crate::undefined();
652        mResult.r[0] = _mm256_castps256_ps128(t0);
653        mResult.r[1] = _mm256_extractf128_ps(t0, 1);
654        mResult.r[2] = _mm256_castps256_ps128(t1);
655        mResult.r[3] = _mm256_extractf128_ps(t1, 1);
656        return mResult;
657    }
658
659    #[cfg(all(_XM_AVX_INTRINSICS_, not(_XM_AVX2_INTRINSICS_)))]
660    unsafe {
661        // Splat the component X,Y,Z then W
662        let mut vX: XMVECTOR = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[0])[0])));
663        let mut vY: XMVECTOR = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[0])[1])));
664        let mut vZ: XMVECTOR = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[0])[2])));
665        let mut vW: XMVECTOR = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[0])[3])));
666
667        // Perform the operation on the first row
668        vX = _mm_mul_ps(vX, M2.r[0]);
669        vY = _mm_mul_ps(vY, M2.r[1]);
670        vZ = _mm_mul_ps(vZ, M2.r[2]);
671        vW = _mm_mul_ps(vW, M2.r[3]);
672        // Perform a binary add to reduce cumulative errors
673        vX = _mm_add_ps(vX, vZ);
674        vY = _mm_add_ps(vY, vW);
675        vX = _mm_add_ps(vX, vY);
676        let r0: XMVECTOR = vX;
677
678        // Repeat for the other 3 rows
679        vX = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[1])[0])));
680        vY = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[1])[1])));
681        vZ = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[1])[2])));
682        vW = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[1])[3])));
683
684        vX = _mm_mul_ps(vX, M2.r[0]);
685        vY = _mm_mul_ps(vY, M2.r[1]);
686        vZ = _mm_mul_ps(vZ, M2.r[2]);
687        vW = _mm_mul_ps(vW, M2.r[3]);
688
689        vX = _mm_add_ps(vX, vZ);
690        vY = _mm_add_ps(vY, vW);
691        vX = _mm_add_ps(vX, vY);
692        let r1: XMVECTOR = vX;
693
694        vX = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[2])[0])));
695        vY = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[2])[1])));
696        vZ = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[2])[2])));
697        vW = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[2])[3])));
698
699        vX = _mm_mul_ps(vX, M2.r[0]);
700        vY = _mm_mul_ps(vY, M2.r[1]);
701        vZ = _mm_mul_ps(vZ, M2.r[2]);
702        vW = _mm_mul_ps(vW, M2.r[3]);
703
704        vX = _mm_add_ps(vX, vZ);
705        vY = _mm_add_ps(vY, vW);
706        vX = _mm_add_ps(vX, vY);
707        let r2: XMVECTOR = vX;
708
709        vX = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[3])[0])));
710        vY = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[3])[1])));
711        vZ = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[3])[2])));
712        vW = _mm_broadcast_ss(&*mem::transmute::<_, *const f32>(&idx!(f32x4(M1.r[3])[3])));
713
714        vX = _mm_mul_ps(vX, M2.r[0]);
715        vY = _mm_mul_ps(vY, M2.r[1]);
716        vZ = _mm_mul_ps(vZ, M2.r[2]);
717        vW = _mm_mul_ps(vW, M2.r[3]);
718
719        vX = _mm_add_ps(vX, vZ);
720        vY = _mm_add_ps(vY, vW);
721        vX = _mm_add_ps(vX, vY);
722        let r3: XMVECTOR = vX;
723
724        // Transpose result
725        // x.x,x.y,y.x,y.y
726        let vTemp1: XMVECTOR = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(1, 0, 1, 0));
727        // x.z,x.w,y.z,y.w
728        let vTemp3: XMVECTOR = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(3, 2, 3, 2));
729        // z.x,z.y,w.x,w.y
730        let vTemp2: XMVECTOR = _mm_shuffle_ps(r2, r3, _MM_SHUFFLE(1, 0, 1, 0));
731        // z.z,z.w,w.z,w.w
732        let vTemp4: XMVECTOR = _mm_shuffle_ps(r2, r3, _MM_SHUFFLE(3, 2, 3, 2));
733
734        let mut mResult: XMMATRIX = crate::undefined();
735        // x.x,y.x,z.x,w.x
736        mResult.r[0] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0));
737        // x.y,y.y,z.y,w.y
738        mResult.r[1] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1));
739        // x.z,y.z,z.z,w.z
740        mResult.r[2] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0));
741        // x.w,y.w,z.w,w.w
742        mResult.r[3] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(3, 1, 3, 1));
743        return mResult;
744    }
745
746    #[cfg(all(_XM_SSE_INTRINSICS_, not(_XM_AVX_INTRINSICS_), not(_XM_AVX2_INTRINSICS_)))]
747    unsafe {
748        // Use vW to hold the original row
749        let mut vW: XMVECTOR = M1.r[0];
750        let mut vX: XMVECTOR = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(0, 0, 0, 0));
751        let mut vY: XMVECTOR = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(1, 1, 1, 1));
752        let mut vZ: XMVECTOR = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(2, 2, 2, 2));
753        vW = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(3, 3, 3, 3));
754
755        // Perform the operation on the first row
756        vX = _mm_mul_ps(vX, M2.r[0]);
757        vY = _mm_mul_ps(vY, M2.r[1]);
758        vZ = _mm_mul_ps(vZ, M2.r[2]);
759        vW = _mm_mul_ps(vW, M2.r[3]);
760        // Perform a binary add to reduce cumulative errors
761        vX = _mm_add_ps(vX, vZ);
762        vY = _mm_add_ps(vY, vW);
763        vX = _mm_add_ps(vX, vY);
764        let r0: XMVECTOR = vX;
765        // Repeat for the other 3 rows
766
767        vW = M1.r[1];
768        vX = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(0, 0, 0, 0));
769        vY = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(1, 1, 1, 1));
770        vZ = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(2, 2, 2, 2));
771        vW = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(3, 3, 3, 3));
772
773        vX = _mm_mul_ps(vX, M2.r[0]);
774        vY = _mm_mul_ps(vY, M2.r[1]);
775        vZ = _mm_mul_ps(vZ, M2.r[2]);
776        vW = _mm_mul_ps(vW, M2.r[3]);
777        vX = _mm_add_ps(vX, vZ);
778        vY = _mm_add_ps(vY, vW);
779        vX = _mm_add_ps(vX, vY);
780        let r1: XMVECTOR = vX;
781
782        vW = M1.r[2];
783        vX = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(0, 0, 0, 0));
784        vY = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(1, 1, 1, 1));
785        vZ = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(2, 2, 2, 2));
786        vW = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(3, 3, 3, 3));
787
788        vX = _mm_mul_ps(vX, M2.r[0]);
789        vY = _mm_mul_ps(vY, M2.r[1]);
790        vZ = _mm_mul_ps(vZ, M2.r[2]);
791        vW = _mm_mul_ps(vW, M2.r[3]);
792        vX = _mm_add_ps(vX, vZ);
793        vY = _mm_add_ps(vY, vW);
794        vX = _mm_add_ps(vX, vY);
795        let r2: XMVECTOR = vX;
796
797        vW = M1.r[3];
798        vX = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(0, 0, 0, 0));
799        vY = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(1, 1, 1, 1));
800        vZ = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(2, 2, 2, 2));
801        vW = XM_PERMUTE_PS!(vW, _MM_SHUFFLE(3, 3, 3, 3));
802
803        vX = _mm_mul_ps(vX, M2.r[0]);
804        vY = _mm_mul_ps(vY, M2.r[1]);
805        vZ = _mm_mul_ps(vZ, M2.r[2]);
806        vW = _mm_mul_ps(vW, M2.r[3]);
807        vX = _mm_add_ps(vX, vZ);
808        vY = _mm_add_ps(vY, vW);
809        vX = _mm_add_ps(vX, vY);
810        let r3: XMVECTOR = vX;
811
812        // Transpose result
813        // x.x,x.y,y.x,y.y
814        let vTemp1: XMVECTOR = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(1, 0, 1, 0));
815        // x.z,x.w,y.z,y.w
816        let vTemp3: XMVECTOR = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(3, 2, 3, 2));
817        // z.x,z.y,w.x,w.y
818        let vTemp2: XMVECTOR = _mm_shuffle_ps(r2, r3, _MM_SHUFFLE(1, 0, 1, 0));
819        // z.z,z.w,w.z,w.w
820        let vTemp4: XMVECTOR = _mm_shuffle_ps(r2, r3, _MM_SHUFFLE(3, 2, 3, 2));
821
822        let mut mResult: XMMATRIX = crate::undefined();
823        // x.x,y.x,z.x,w.x
824        mResult.r[0] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0));
825        // x.y,y.y,z.y,w.y
826        mResult.r[1] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1));
827        // x.z,y.z,z.z,w.z
828        mResult.r[2] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0));
829        // x.w,y.w,z.w,w.w
830        mResult.r[3] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(3, 1, 3, 1));
831        return mResult;
832    }
833}
834
835#[test]
836fn test_XMMatrixMultiplyTranspose() {
837    let a = XMMATRIX {
838        r: [
839            XMVectorSet( 1.0,  2.0,  3.0,  4.0),
840            XMVectorSet( 5.0,  6.0,  7.0,  8.0),
841            XMVectorSet( 9.0, 10.0, 11.0, 12.0),
842            XMVectorSet(13.0, 14.0, 15.0, 16.0),
843        ]
844    };
845    let b = XMMATRIX {
846        r: [
847            XMVectorSet(13.0, 14.0, 15.0, 16.0),
848            XMVectorSet( 9.0, 10.0, 11.0, 12.0),
849            XMVectorSet( 5.0,  6.0,  7.0,  8.0),
850            XMVectorSet( 1.0,  2.0,  3.0,  4.0),
851        ]
852    };
853
854    let c = XMMatrixMultiplyTranspose(a, &b);
855
856    let d = XMMATRIX {
857        r: [
858            XMVectorSet( 50.0,  60.0,  70.0,  80.0),
859            XMVectorSet(162.0, 188.0, 214.0, 240.0),
860            XMVectorSet(274.0, 316.0, 358.0, 400.0),
861            XMVectorSet(386.0, 444.0, 502.0, 560.0),
862        ]
863    };
864
865    let d = XMMatrixTranspose(d);
866
867    unsafe {
868        let mut cr = 0;
869        XMVectorEqualR(&mut cr, c.r[0], d.r[0]);
870        assert!(XMComparisonAllTrue(cr));
871
872        XMVectorEqualR(&mut cr, c.r[1], d.r[1]);
873        assert!(XMComparisonAllTrue(cr));
874
875        XMVectorEqualR(&mut cr, c.r[2], d.r[2]);
876        assert!(XMComparisonAllTrue(cr));
877
878        XMVectorEqualR(&mut cr, c.r[3], d.r[3]);
879        assert!(XMComparisonAllTrue(cr));
880    }
881}
882
883/// Computes the transpose of a matrix.
884///
885/// ## Parameters
886///
887/// `M` Matrix to transpose.
888///
889/// ## Return value
890///
891/// Returns the transpose of M.
892///
893/// ## Reference
894///
895/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixTranspose>
896#[inline]
897pub fn XMMatrixTranspose(
898    M: FXMMATRIX,
899) -> XMMATRIX
900{
901    #[cfg(_XM_NO_INTRINSICS_)]
902    unsafe {
903        // Original matrix:
904        //
905        //     m00m01m02m03
906        //     m10m11m12m13
907        //     m20m21m22m23
908        //     m30m31m32m33
909
910        let mut P: XMMATRIX = crate::undefined();
911        P.r[0] = XMVectorMergeXY(M.r[0], M.r[2]); // m00m20m01m21
912        P.r[1] = XMVectorMergeXY(M.r[1], M.r[3]); // m10m30m11m31
913        P.r[2] = XMVectorMergeZW(M.r[0], M.r[2]); // m02m22m03m23
914        P.r[3] = XMVectorMergeZW(M.r[1], M.r[3]); // m12m32m13m33
915
916        let mut MT: XMMATRIX = crate::undefined();
917        MT.r[0] = XMVectorMergeXY(P.r[0], P.r[1]); // m00m10m20m30
918        MT.r[1] = XMVectorMergeZW(P.r[0], P.r[1]); // m01m11m21m31
919        MT.r[2] = XMVectorMergeXY(P.r[2], P.r[3]); // m02m12m22m32
920        MT.r[3] = XMVectorMergeZW(P.r[2], P.r[3]); // m03m13m23m33
921        return MT;
922    }
923
924    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
925    {
926        unimplemented!()
927    }
928
929    #[cfg(_XM_AVX2_INTRINSICS_)]
930    unsafe {
931        let mut t0: __m256 = _mm256_castps128_ps256(M.r[0]);
932        t0 = _mm256_insertf128_ps(t0, M.r[1], 1);
933        let mut t1: __m256 = _mm256_castps128_ps256(M.r[2]);
934        t1 = _mm256_insertf128_ps(t1, M.r[3], 1);
935
936        let mut vTemp: __m256 = _mm256_unpacklo_ps(t0, t1);
937        let mut vTemp2: __m256 = _mm256_unpackhi_ps(t0, t1);
938        let vTemp3: __m256 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x20);
939        let vTemp4: __m256 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x31);
940        vTemp = _mm256_unpacklo_ps(vTemp3, vTemp4);
941        vTemp2 = _mm256_unpackhi_ps(vTemp3, vTemp4);
942        t0 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x20);
943        t1 = _mm256_permute2f128_ps(vTemp, vTemp2, 0x31);
944
945        let mut mResult: XMMATRIX = crate::undefined();
946        mResult.r[0] = _mm256_castps256_ps128(t0);
947        mResult.r[1] = _mm256_extractf128_ps(t0, 1);
948        mResult.r[2] = _mm256_castps256_ps128(t1);
949        mResult.r[3] = _mm256_extractf128_ps(t1, 1);
950        return mResult;
951    }
952
953    #[cfg(all(_XM_SSE_INTRINSICS_, not(_XM_AVX2_INTRINSICS_)))]
954    unsafe {
955        // x.x,x.y,y.x,y.y
956        let vTemp1: XMVECTOR = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(1, 0, 1, 0));
957        // x.z,x.w,y.z,y.w
958        let vTemp3: XMVECTOR = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(3, 2, 3, 2));
959        // z.x,z.y,w.x,w.y
960        let vTemp2: XMVECTOR = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(1, 0, 1, 0));
961        // z.z,z.w,w.z,w.w
962        let vTemp4: XMVECTOR = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(3, 2, 3, 2));
963
964        let mut mResult: XMMATRIX = crate::undefined();
965        // x.x,y.x,z.x,w.x
966        mResult.r[0] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0));
967        // x.y,y.y,z.y,w.y
968        mResult.r[1] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1));
969        // x.z,y.z,z.z,w.z
970        mResult.r[2] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0));
971        // x.w,y.w,z.w,w.w
972        mResult.r[3] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(3, 1, 3, 1));
973        return mResult;
974    }
975}
976
977/// Computes the inverse of a matrix.
978///
979/// ## Parameters
980///
981/// `pDeterminant` Address of a vector, each of whose components receives the determinant of `M`. This parameter may be `None`
982///  if the determinant is not desired.
983///
984/// `M` Matrix to invert.
985///
986/// ## Return value
987///
988/// Returns the matrix inverse of `M`. If there is no inverse (that is, if the determinant is `0`), XMMatrixInverse
989/// returns an infinite matrix.
990///
991/// ## Reference
992///
993/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixInverse>
994#[inline]
995pub fn XMMatrixInverse(
996    pDeterminant: Option<&mut XMVECTOR>,
997    M: FXMMATRIX,
998) -> XMMATRIX
999{
1000    #[cfg(any(_XM_NO_INTRINSICS_, _XM_ARM_NEON_INTRINSICS_))]
1001    unsafe {
1002        let MT: XMMATRIX = XMMatrixTranspose(M);
1003
1004        let mut V0: [XMVECTOR; 4] = crate::undefined();
1005        let mut V1: [XMVECTOR; 4] = crate::undefined();
1006        V0[0] = <(XM_SWIZZLE_X, XM_SWIZZLE_X, XM_SWIZZLE_Y, XM_SWIZZLE_Y)>::XMVectorSwizzle(MT.r[2]);
1007        V1[0] = <(XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_Z, XM_SWIZZLE_W)>::XMVectorSwizzle(MT.r[3]);
1008        V0[1] = <(XM_SWIZZLE_X, XM_SWIZZLE_X, XM_SWIZZLE_Y, XM_SWIZZLE_Y)>::XMVectorSwizzle(MT.r[0]);
1009        V1[1] = <(XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_Z, XM_SWIZZLE_W)>::XMVectorSwizzle(MT.r[1]);
1010        V1[2] = <(XM_PERMUTE_0Y, XM_PERMUTE_0W, XM_PERMUTE_1Y, XM_PERMUTE_1W)>::XMVectorPermute(MT.r[3], MT.r[1]);
1011        V0[2] = <(XM_PERMUTE_0X, XM_PERMUTE_0Z, XM_PERMUTE_1X, XM_PERMUTE_1Z)>::XMVectorPermute(MT.r[2], MT.r[0]);
1012
1013        let mut D0: XMVECTOR = XMVectorMultiply(V0[0], V1[0]);
1014        let mut D1: XMVECTOR = XMVectorMultiply(V0[1], V1[1]);
1015        let mut D2: XMVECTOR = XMVectorMultiply(V0[2], V1[2]);
1016
1017        V0[0] = <(XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_Z, XM_SWIZZLE_W)>::XMVectorSwizzle(MT.r[2]);
1018        V1[0] = <(XM_SWIZZLE_X, XM_SWIZZLE_X, XM_SWIZZLE_Y, XM_SWIZZLE_Y)>::XMVectorSwizzle(MT.r[3]);
1019        V0[1] = <(XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_Z, XM_SWIZZLE_W)>::XMVectorSwizzle(MT.r[0]);
1020        V1[1] = <(XM_SWIZZLE_X, XM_SWIZZLE_X, XM_SWIZZLE_Y, XM_SWIZZLE_Y)>::XMVectorSwizzle(MT.r[1]);
1021        V0[2] = <(XM_PERMUTE_0Y, XM_PERMUTE_0W, XM_PERMUTE_1Y, XM_PERMUTE_1W)>::XMVectorPermute(MT.r[2], MT.r[0]);
1022        V1[2] = <(XM_PERMUTE_0X, XM_PERMUTE_0Z, XM_PERMUTE_1X, XM_PERMUTE_1Z)>::XMVectorPermute(MT.r[3], MT.r[1]);
1023
1024        D0 = XMVectorNegativeMultiplySubtract(V0[0], V1[0], D0);
1025        D1 = XMVectorNegativeMultiplySubtract(V0[1], V1[1], D1);
1026        D2 = XMVectorNegativeMultiplySubtract(V0[2], V1[2], D2);
1027
1028        V0[0] = <(XM_SWIZZLE_Y, XM_SWIZZLE_Z, XM_SWIZZLE_X, XM_SWIZZLE_Y)>::XMVectorSwizzle(MT.r[1]);
1029        V1[0] = <(XM_PERMUTE_1Y, XM_PERMUTE_0Y, XM_PERMUTE_0W, XM_PERMUTE_0X)>::XMVectorPermute(D0, D2);
1030        V0[1] = <(XM_SWIZZLE_Z, XM_SWIZZLE_X, XM_SWIZZLE_Y, XM_SWIZZLE_X)>::XMVectorSwizzle(MT.r[0]);
1031        V1[1] = <(XM_PERMUTE_0W, XM_PERMUTE_1Y, XM_PERMUTE_0Y, XM_PERMUTE_0Z)>::XMVectorPermute(D0, D2);
1032        V0[2] = <(XM_SWIZZLE_Y, XM_SWIZZLE_Z, XM_SWIZZLE_X, XM_SWIZZLE_Y)>::XMVectorSwizzle(MT.r[3]);
1033        V1[2] = <(XM_PERMUTE_1W, XM_PERMUTE_0Y, XM_PERMUTE_0W, XM_PERMUTE_0X)>::XMVectorPermute(D1, D2);
1034        V0[3] = <(XM_SWIZZLE_Z, XM_SWIZZLE_X, XM_SWIZZLE_Y, XM_SWIZZLE_X)>::XMVectorSwizzle(MT.r[2]);
1035        V1[3] = <(XM_PERMUTE_0W, XM_PERMUTE_1W, XM_PERMUTE_0Y, XM_PERMUTE_0Z)>::XMVectorPermute(D1, D2);
1036
1037        let mut C0: XMVECTOR = XMVectorMultiply(V0[0], V1[0]);
1038        let mut C2: XMVECTOR = XMVectorMultiply(V0[1], V1[1]);
1039        let mut C4: XMVECTOR = XMVectorMultiply(V0[2], V1[2]);
1040        let mut C6: XMVECTOR = XMVectorMultiply(V0[3], V1[3]);
1041
1042        V0[0] = <(XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_Y, XM_SWIZZLE_Z)>::XMVectorSwizzle(MT.r[1]);
1043        V1[0] = <(XM_PERMUTE_0W, XM_PERMUTE_0X, XM_PERMUTE_0Y, XM_PERMUTE_1X)>::XMVectorPermute(D0, D2);
1044        V0[1] = <(XM_SWIZZLE_W, XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_Y)>::XMVectorSwizzle(MT.r[0]);
1045        V1[1] = <(XM_PERMUTE_0Z, XM_PERMUTE_0Y, XM_PERMUTE_1X, XM_PERMUTE_0X)>::XMVectorPermute(D0, D2);
1046        V0[2] = <(XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_Y, XM_SWIZZLE_Z)>::XMVectorSwizzle(MT.r[3]);
1047        V1[2] = <(XM_PERMUTE_0W, XM_PERMUTE_0X, XM_PERMUTE_0Y, XM_PERMUTE_1Z)>::XMVectorPermute(D1, D2);
1048        V0[3] = <(XM_SWIZZLE_W, XM_SWIZZLE_Z, XM_SWIZZLE_W, XM_SWIZZLE_Y)>::XMVectorSwizzle(MT.r[2]);
1049        V1[3] = <(XM_PERMUTE_0Z, XM_PERMUTE_0Y, XM_PERMUTE_1Z, XM_PERMUTE_0X)>::XMVectorPermute(D1, D2);
1050
1051        C0 = XMVectorNegativeMultiplySubtract(V0[0], V1[0], C0);
1052        C2 = XMVectorNegativeMultiplySubtract(V0[1], V1[1], C2);
1053        C4 = XMVectorNegativeMultiplySubtract(V0[2], V1[2], C4);
1054        C6 = XMVectorNegativeMultiplySubtract(V0[3], V1[3], C6);
1055
1056        V0[0] = <(XM_SWIZZLE_W, XM_SWIZZLE_X, XM_SWIZZLE_W, XM_SWIZZLE_X)>::XMVectorSwizzle(MT.r[1]);
1057        V1[0] = <(XM_PERMUTE_0Z, XM_PERMUTE_1Y, XM_PERMUTE_1X, XM_PERMUTE_0Z)>::XMVectorPermute(D0, D2);
1058        V0[1] = <(XM_SWIZZLE_Y, XM_SWIZZLE_W, XM_SWIZZLE_X, XM_SWIZZLE_Z)>::XMVectorSwizzle(MT.r[0]);
1059        V1[1] = <(XM_PERMUTE_1Y, XM_PERMUTE_0X, XM_PERMUTE_0W, XM_PERMUTE_1X)>::XMVectorPermute(D0, D2);
1060        V0[2] = <(XM_SWIZZLE_W, XM_SWIZZLE_X, XM_SWIZZLE_W, XM_SWIZZLE_X)>::XMVectorSwizzle(MT.r[3]);
1061        V1[2] = <(XM_PERMUTE_0Z, XM_PERMUTE_1W, XM_PERMUTE_1Z, XM_PERMUTE_0Z)>::XMVectorPermute(D1, D2);
1062        V0[3] = <(XM_SWIZZLE_Y, XM_SWIZZLE_W, XM_SWIZZLE_X, XM_SWIZZLE_Z)>::XMVectorSwizzle(MT.r[2]);
1063        V1[3] = <(XM_PERMUTE_1W, XM_PERMUTE_0X, XM_PERMUTE_0W, XM_PERMUTE_1Z)>::XMVectorPermute(D1, D2);
1064
1065        let C1: XMVECTOR = XMVectorNegativeMultiplySubtract(V0[0], V1[0], C0);
1066        C0 = XMVectorMultiplyAdd(V0[0], V1[0], C0);
1067        let C3: XMVECTOR = XMVectorMultiplyAdd(V0[1], V1[1], C2);
1068        C2 = XMVectorNegativeMultiplySubtract(V0[1], V1[1], C2);
1069        let C5: XMVECTOR = XMVectorNegativeMultiplySubtract(V0[2], V1[2], C4);
1070        C4 = XMVectorMultiplyAdd(V0[2], V1[2], C4);
1071        let C7: XMVECTOR = XMVectorMultiplyAdd(V0[3], V1[3], C6);
1072        C6 = XMVectorNegativeMultiplySubtract(V0[3], V1[3], C6);
1073
1074        let mut R: XMMATRIX = crate::undefined();
1075        R.r[0] = XMVectorSelect(C0, C1, g_XMSelect0101.v);
1076        R.r[1] = XMVectorSelect(C2, C3, g_XMSelect0101.v);
1077        R.r[2] = XMVectorSelect(C4, C5, g_XMSelect0101.v);
1078        R.r[3] = XMVectorSelect(C6, C7, g_XMSelect0101.v);
1079
1080        let Determinant: XMVECTOR = XMVector4Dot(R.r[0], MT.r[0]);
1081
1082        match pDeterminant {
1083            Some(determinant) => *determinant = Determinant,
1084            None => ()
1085        };
1086
1087        let Reciprocal: XMVECTOR = XMVectorReciprocal(Determinant);
1088
1089        let mut Result: XMMATRIX = crate::undefined();
1090        Result.r[0] = XMVectorMultiply(R.r[0], Reciprocal);
1091        Result.r[1] = XMVectorMultiply(R.r[1], Reciprocal);
1092        Result.r[2] = XMVectorMultiply(R.r[2], Reciprocal);
1093        Result.r[3] = XMVectorMultiply(R.r[3], Reciprocal);
1094        return Result;
1095    }
1096
1097    #[cfg(_XM_SSE_INTRINSICS_)]
1098    unsafe {
1099        // Transpose matrix
1100        let vTemp1: XMVECTOR = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(1, 0, 1, 0));
1101        let vTemp3: XMVECTOR = _mm_shuffle_ps(M.r[0], M.r[1], _MM_SHUFFLE(3, 2, 3, 2));
1102        let vTemp2: XMVECTOR = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(1, 0, 1, 0));
1103        let vTemp4: XMVECTOR = _mm_shuffle_ps(M.r[2], M.r[3], _MM_SHUFFLE(3, 2, 3, 2));
1104
1105        let mut MT: XMMATRIX = crate::undefined();
1106        MT.r[0] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(2, 0, 2, 0));
1107        MT.r[1] = _mm_shuffle_ps(vTemp1, vTemp2, _MM_SHUFFLE(3, 1, 3, 1));
1108        MT.r[2] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(2, 0, 2, 0));
1109        MT.r[3] = _mm_shuffle_ps(vTemp3, vTemp4, _MM_SHUFFLE(3, 1, 3, 1));
1110
1111        let mut V00: XMVECTOR = XM_PERMUTE_PS!(MT.r[2], _MM_SHUFFLE(1, 1, 0, 0));
1112        let mut V10: XMVECTOR = XM_PERMUTE_PS!(MT.r[3], _MM_SHUFFLE(3, 2, 3, 2));
1113        let mut V01: XMVECTOR = XM_PERMUTE_PS!(MT.r[0], _MM_SHUFFLE(1, 1, 0, 0));
1114        let mut V11: XMVECTOR = XM_PERMUTE_PS!(MT.r[1], _MM_SHUFFLE(3, 2, 3, 2));
1115        let mut V02: XMVECTOR = _mm_shuffle_ps(MT.r[2], MT.r[0], _MM_SHUFFLE(2, 0, 2, 0));
1116        let mut V12: XMVECTOR = _mm_shuffle_ps(MT.r[3], MT.r[1], _MM_SHUFFLE(3, 1, 3, 1));
1117
1118        let mut D0: XMVECTOR = _mm_mul_ps(V00, V10);
1119        let mut D1: XMVECTOR = _mm_mul_ps(V01, V11);
1120        let mut D2: XMVECTOR = _mm_mul_ps(V02, V12);
1121
1122        V00 = XM_PERMUTE_PS!(MT.r[2], _MM_SHUFFLE(3, 2, 3, 2));
1123        V10 = XM_PERMUTE_PS!(MT.r[3], _MM_SHUFFLE(1, 1, 0, 0));
1124        V01 = XM_PERMUTE_PS!(MT.r[0], _MM_SHUFFLE(3, 2, 3, 2));
1125        V11 = XM_PERMUTE_PS!(MT.r[1], _MM_SHUFFLE(1, 1, 0, 0));
1126        V02 = _mm_shuffle_ps(MT.r[2], MT.r[0], _MM_SHUFFLE(3, 1, 3, 1));
1127        V12 = _mm_shuffle_ps(MT.r[3], MT.r[1], _MM_SHUFFLE(2, 0, 2, 0));
1128
1129        D0 = XM_FNMADD_PS!(V00, V10, D0);
1130        D1 = XM_FNMADD_PS!(V01, V11, D1);
1131        D2 = XM_FNMADD_PS!(V02, V12, D2);
1132        // V11 = D0Y,D0W,D2Y,D2Y
1133        V11 = _mm_shuffle_ps(D0, D2, _MM_SHUFFLE(1, 1, 3, 1));
1134        V00 = XM_PERMUTE_PS!(MT.r[1], _MM_SHUFFLE(1, 0, 2, 1));
1135        V10 = _mm_shuffle_ps(V11, D0, _MM_SHUFFLE(0, 3, 0, 2));
1136        V01 = XM_PERMUTE_PS!(MT.r[0], _MM_SHUFFLE(0, 1, 0, 2));
1137        V11 = _mm_shuffle_ps(V11, D0, _MM_SHUFFLE(2, 1, 2, 1));
1138        // V13 = D1Y,D1W,D2W,D2W
1139        let mut V13: XMVECTOR = _mm_shuffle_ps(D1, D2, _MM_SHUFFLE(3, 3, 3, 1));
1140        V02 = XM_PERMUTE_PS!(MT.r[3], _MM_SHUFFLE(1, 0, 2, 1));
1141        V12 = _mm_shuffle_ps(V13, D1, _MM_SHUFFLE(0, 3, 0, 2));
1142        let mut V03: XMVECTOR = XM_PERMUTE_PS!(MT.r[2], _MM_SHUFFLE(0, 1, 0, 2));
1143        V13 = _mm_shuffle_ps(V13, D1, _MM_SHUFFLE(2, 1, 2, 1));
1144
1145        let mut C0: XMVECTOR = _mm_mul_ps(V00, V10);
1146        let mut C2: XMVECTOR = _mm_mul_ps(V01, V11);
1147        let mut C4: XMVECTOR = _mm_mul_ps(V02, V12);
1148        let mut C6: XMVECTOR = _mm_mul_ps(V03, V13);
1149
1150        // V11 = D0X,D0Y,D2X,D2X
1151        V11 = _mm_shuffle_ps(D0, D2, _MM_SHUFFLE(0, 0, 1, 0));
1152        V00 = XM_PERMUTE_PS!(MT.r[1], _MM_SHUFFLE(2, 1, 3, 2));
1153        V10 = _mm_shuffle_ps(D0, V11, _MM_SHUFFLE(2, 1, 0, 3));
1154        V01 = XM_PERMUTE_PS!(MT.r[0], _MM_SHUFFLE(1, 3, 2, 3));
1155        V11 = _mm_shuffle_ps(D0, V11, _MM_SHUFFLE(0, 2, 1, 2));
1156        // V13 = D1X,D1Y,D2Z,D2Z
1157        V13 = _mm_shuffle_ps(D1, D2, _MM_SHUFFLE(2, 2, 1, 0));
1158        V02 = XM_PERMUTE_PS!(MT.r[3], _MM_SHUFFLE(2, 1, 3, 2));
1159        V12 = _mm_shuffle_ps(D1, V13, _MM_SHUFFLE(2, 1, 0, 3));
1160        V03 = XM_PERMUTE_PS!(MT.r[2], _MM_SHUFFLE(1, 3, 2, 3));
1161        V13 = _mm_shuffle_ps(D1, V13, _MM_SHUFFLE(0, 2, 1, 2));
1162
1163        C0 = XM_FNMADD_PS!(V00, V10, C0);
1164        C2 = XM_FNMADD_PS!(V01, V11, C2);
1165        C4 = XM_FNMADD_PS!(V02, V12, C4);
1166        C6 = XM_FNMADD_PS!(V03, V13, C6);
1167
1168        V00 = XM_PERMUTE_PS!(MT.r[1], _MM_SHUFFLE(0, 3, 0, 3));
1169        // V10 = D0Z,D0Z,D2X,D2Y
1170        V10 = _mm_shuffle_ps(D0, D2, _MM_SHUFFLE(1, 0, 2, 2));
1171        V10 = XM_PERMUTE_PS!(V10, _MM_SHUFFLE(0, 2, 3, 0));
1172        V01 = XM_PERMUTE_PS!(MT.r[0], _MM_SHUFFLE(2, 0, 3, 1));
1173        // V11 = D0X,D0W,D2X,D2Y
1174        V11 = _mm_shuffle_ps(D0, D2, _MM_SHUFFLE(1, 0, 3, 0));
1175        V11 = XM_PERMUTE_PS!(V11, _MM_SHUFFLE(2, 1, 0, 3));
1176        V02 = XM_PERMUTE_PS!(MT.r[3], _MM_SHUFFLE(0, 3, 0, 3));
1177        // V12 = D1Z,D1Z,D2Z,D2W
1178        V12 = _mm_shuffle_ps(D1, D2, _MM_SHUFFLE(3, 2, 2, 2));
1179        V12 = XM_PERMUTE_PS!(V12, _MM_SHUFFLE(0, 2, 3, 0));
1180        V03 = XM_PERMUTE_PS!(MT.r[2], _MM_SHUFFLE(2, 0, 3, 1));
1181        // V13 = D1X,D1W,D2Z,D2W
1182        V13 = _mm_shuffle_ps(D1, D2, _MM_SHUFFLE(3, 2, 3, 0));
1183        V13 = XM_PERMUTE_PS!(V13, _MM_SHUFFLE(2, 1, 0, 3));
1184
1185        V00 = _mm_mul_ps(V00, V10);
1186        V01 = _mm_mul_ps(V01, V11);
1187        V02 = _mm_mul_ps(V02, V12);
1188        V03 = _mm_mul_ps(V03, V13);
1189        let C1: XMVECTOR = _mm_sub_ps(C0, V00);
1190        C0 = _mm_add_ps(C0, V00);
1191        let C3: XMVECTOR = _mm_add_ps(C2, V01);
1192        C2 = _mm_sub_ps(C2, V01);
1193        let C5: XMVECTOR = _mm_sub_ps(C4, V02);
1194        C4 = _mm_add_ps(C4, V02);
1195        let C7: XMVECTOR = _mm_add_ps(C6, V03);
1196        C6 = _mm_sub_ps(C6, V03);
1197
1198        C0 = _mm_shuffle_ps(C0, C1, _MM_SHUFFLE(3, 1, 2, 0));
1199        C2 = _mm_shuffle_ps(C2, C3, _MM_SHUFFLE(3, 1, 2, 0));
1200        C4 = _mm_shuffle_ps(C4, C5, _MM_SHUFFLE(3, 1, 2, 0));
1201        C6 = _mm_shuffle_ps(C6, C7, _MM_SHUFFLE(3, 1, 2, 0));
1202        C0 = XM_PERMUTE_PS!(C0, _MM_SHUFFLE(3, 1, 2, 0));
1203        C2 = XM_PERMUTE_PS!(C2, _MM_SHUFFLE(3, 1, 2, 0));
1204        C4 = XM_PERMUTE_PS!(C4, _MM_SHUFFLE(3, 1, 2, 0));
1205        C6 = XM_PERMUTE_PS!(C6, _MM_SHUFFLE(3, 1, 2, 0));
1206        // Get the determinant
1207        let mut vTemp: XMVECTOR = XMVector4Dot(C0, MT.r[0]);
1208
1209        if let Some(determinant) = pDeterminant {
1210            *determinant = vTemp;
1211        }
1212
1213        vTemp = _mm_div_ps(g_XMOne.v, vTemp);
1214        let mut mResult: XMMATRIX = crate::undefined();
1215        mResult.r[0] = _mm_mul_ps(C0, vTemp);
1216        mResult.r[1] = _mm_mul_ps(C2, vTemp);
1217        mResult.r[2] = _mm_mul_ps(C4, vTemp);
1218        mResult.r[3] = _mm_mul_ps(C6, vTemp);
1219        return mResult;
1220    }
1221}
1222
1223/// XMMatrixVectorTensorProduct
1224///
1225/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixVectorTensorProduct>
1226#[inline]
1227pub fn XMMatrixVectorTensorProduct(
1228    V1: FXMVECTOR,
1229    V2: FXMVECTOR
1230) -> XMMATRIX
1231{
1232    unsafe {
1233        let mut mResult: XMMATRIX = crate::undefined();
1234        type _0 = XM_SWIZZLE_X;
1235        type _1 = XM_SWIZZLE_Y;
1236        type _2 = XM_SWIZZLE_Z;
1237        type _3 = XM_SWIZZLE_W;
1238        mResult.r[0] = XMVectorMultiply(<(_0, _0, _0, _0)>::XMVectorSwizzle(V1), V2);
1239        mResult.r[1] = XMVectorMultiply(<(_1, _1, _1, _1)>::XMVectorSwizzle(V1), V2);
1240        mResult.r[2] = XMVectorMultiply(<(_2, _2, _2, _2)>::XMVectorSwizzle(V1), V2);
1241        mResult.r[3] = XMVectorMultiply(<(_3, _3, _3, _3)>::XMVectorSwizzle(V1), V2);
1242        return mResult;
1243    }
1244}
1245
1246/// Computes the determinant of a matrix.
1247///
1248/// ## Parameters
1249///
1250/// `M` Matrix from which to compute the determinant.
1251///
1252/// ## Return value
1253///
1254/// Returns a vector. The determinant of `M` is replicated into each component.
1255///
1256/// ## Reference
1257///
1258/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixDeterminant>
1259#[inline]
1260pub fn XMMatrixDeterminant(
1261    M: FXMMATRIX,
1262) -> FXMVECTOR
1263{
1264    unsafe {
1265        const Sign: XMVECTORF32 = XMVECTORF32 { f: [ 1.0, -1.0, 1.0, -1.0 ] };
1266
1267        let mut V0: XMVECTOR = <(XM_SWIZZLE_Y, XM_SWIZZLE_X, XM_SWIZZLE_X, XM_SWIZZLE_X)>::XMVectorSwizzle(M.r[2]);
1268        let mut V1: XMVECTOR = <(XM_SWIZZLE_Z, XM_SWIZZLE_Z, XM_SWIZZLE_Y, XM_SWIZZLE_Y)>::XMVectorSwizzle(M.r[3]);
1269        let mut V2: XMVECTOR = <(XM_SWIZZLE_Y, XM_SWIZZLE_X, XM_SWIZZLE_X, XM_SWIZZLE_X)>::XMVectorSwizzle(M.r[2]);
1270        let mut V3: XMVECTOR = <(XM_SWIZZLE_W, XM_SWIZZLE_W, XM_SWIZZLE_W, XM_SWIZZLE_Z)>::XMVectorSwizzle(M.r[3]);
1271        let mut V4: XMVECTOR = <(XM_SWIZZLE_Z, XM_SWIZZLE_Z, XM_SWIZZLE_Y, XM_SWIZZLE_Y)>::XMVectorSwizzle(M.r[2]);
1272        let mut V5: XMVECTOR = <(XM_SWIZZLE_W, XM_SWIZZLE_W, XM_SWIZZLE_W, XM_SWIZZLE_Z)>::XMVectorSwizzle(M.r[3]);
1273
1274        let mut P0: XMVECTOR = XMVectorMultiply(V0, V1);
1275        let mut P1: XMVECTOR = XMVectorMultiply(V2, V3);
1276        let mut P2: XMVECTOR = XMVectorMultiply(V4, V5);
1277
1278        V0 = <(XM_SWIZZLE_Z, XM_SWIZZLE_Z, XM_SWIZZLE_Y, XM_SWIZZLE_Y)>::XMVectorSwizzle(M.r[2]);
1279        V1 = <(XM_SWIZZLE_Y, XM_SWIZZLE_X, XM_SWIZZLE_X, XM_SWIZZLE_X)>::XMVectorSwizzle(M.r[3]);
1280        V2 = <(XM_SWIZZLE_W, XM_SWIZZLE_W, XM_SWIZZLE_W, XM_SWIZZLE_Z)>::XMVectorSwizzle(M.r[2]);
1281        V3 = <(XM_SWIZZLE_Y, XM_SWIZZLE_X, XM_SWIZZLE_X, XM_SWIZZLE_X)>::XMVectorSwizzle(M.r[3]);
1282        V4 = <(XM_SWIZZLE_W, XM_SWIZZLE_W, XM_SWIZZLE_W, XM_SWIZZLE_Z)>::XMVectorSwizzle(M.r[2]);
1283        V5 = <(XM_SWIZZLE_Z, XM_SWIZZLE_Z, XM_SWIZZLE_Y, XM_SWIZZLE_Y)>::XMVectorSwizzle(M.r[3]);
1284
1285        P0 = XMVectorNegativeMultiplySubtract(V0, V1, P0);
1286        P1 = XMVectorNegativeMultiplySubtract(V2, V3, P1);
1287        P2 = XMVectorNegativeMultiplySubtract(V4, V5, P2);
1288
1289        V0 = <(XM_SWIZZLE_W, XM_SWIZZLE_W, XM_SWIZZLE_W, XM_SWIZZLE_Z)>::XMVectorSwizzle(M.r[1]);
1290        V1 = <(XM_SWIZZLE_Z, XM_SWIZZLE_Z, XM_SWIZZLE_Y, XM_SWIZZLE_Y)>::XMVectorSwizzle(M.r[1]);
1291        V2 = <(XM_SWIZZLE_Y, XM_SWIZZLE_X, XM_SWIZZLE_X, XM_SWIZZLE_X)>::XMVectorSwizzle(M.r[1]);
1292
1293        let S: XMVECTOR = XMVectorMultiply(M.r[0], Sign.v);
1294        let mut R: XMVECTOR = XMVectorMultiply(V0, P0);
1295        R = XMVectorNegativeMultiplySubtract(V1, P1, R);
1296        R = XMVectorMultiplyAdd(V2, P2, R);
1297
1298        return XMVector4Dot(S, R);
1299    }
1300}
1301
1302/// Breaks down a general 3D transformation matrix into its scalar, rotational, and translational components.
1303///
1304/// ## Parameters
1305///
1306/// `outScale` Pointer to the output XMVECTOR that contains scaling factors applied along the `x`, `y`, and `z-axes`.
1307///
1308/// `outRotQuat` Pointer to the XMVECTOR quaternion that describes the rotation.
1309///
1310/// `outTrans` Pointer to the XMVECTOR vector that describes a translation along the `x`, `y`, and `z-axes`.
1311///
1312/// `M` Pointer to an input XMMATRIX matrix to decompose.
1313///
1314/// ## Return value
1315///
1316/// If the function succeeds, the return value is `true`. If the function fails, the return value is `false`.
1317///
1318/// ## Reference
1319///
1320/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixDecompose>
1321#[inline]
1322pub fn XMMatrixDecompose(
1323    outScale: &mut XMVECTOR,
1324    outRotQuat: &mut XMVECTOR,
1325    outTrans: &mut XMVECTOR,
1326    M: FXMMATRIX,
1327) -> bool
1328{
1329    macro_rules! XM3RANKDECOMPOSE {
1330        ($a:expr, $b:expr, $c:expr, $x:expr, $y:expr, $z:expr) => {
1331            if $x < $y {
1332                if $y < $z {
1333                    $a = 2;
1334                    $b = 1;
1335                    $c = 0;
1336                } else {
1337                    $a = 1;
1338
1339                    if $x < $z {
1340                        $b = 2;
1341                        $c = 0;
1342                    } else {
1343                        $b = 0;
1344                        $c = 2;
1345                    }
1346                }
1347            } else {
1348                if $x < $z {
1349                    $a = 2;
1350                    $b = 0;
1351                    $c = 1;
1352                } else {
1353                    $a = 0;
1354
1355                    if $y < $z {
1356                        $b = 2;
1357                        $c = 1;
1358                    } else {
1359                        $b = 1;
1360                        $c = 2;
1361                    }
1362                }
1363            }
1364        }
1365    }
1366
1367    const XM3_DECOMP_EPSILON: f32 = 0.0001;
1368
1369    unsafe {
1370        // PERFORMANCE: We're using a static here insteads of const due to the references
1371        static pvCanonicalBasis: [&XMVECTOR; 3] = unsafe {[
1372            &g_XMIdentityR0.v,
1373            &g_XMIdentityR2.v,
1374            &g_XMIdentityR3.v,
1375        ]};
1376
1377        // Get the translation
1378        *outTrans = M.r[3];
1379
1380        let mut ppvBasis: [*mut XMVECTOR; 3] = [std::ptr::null_mut(); 3];
1381        let mut matTemp: XMMATRIX = crate::undefined();
1382        ppvBasis[0] = &mut matTemp.r[0];
1383        ppvBasis[1] = &mut matTemp.r[1];
1384        ppvBasis[2] = &mut matTemp.r[2];
1385
1386        matTemp.r[0] = M.r[0];
1387        matTemp.r[1] = M.r[1];
1388        matTemp.r[2] = M.r[2];
1389        matTemp.r[3] = g_XMIdentityR3.v;
1390
1391        let pfScales = mem::transmute::<_, *mut f32>(outScale);
1392        XMVectorGetXPtr(&mut *pfScales.add(0), XMVector3Length(*ppvBasis[0]));
1393        XMVectorGetXPtr(&mut *pfScales.add(1), XMVector3Length(*ppvBasis[1]));
1394        XMVectorGetXPtr(&mut *pfScales.add(2), XMVector3Length(*ppvBasis[2]));
1395        *pfScales.add(3) = 0.0;
1396
1397        let a: usize;
1398        let b: usize;
1399        let c: usize;
1400
1401        let x = *pfScales.add(0);
1402        let y = *pfScales.add(1);
1403        let z = *pfScales.add(2);
1404        XM3RANKDECOMPOSE!(a, b, c, x, y, z);
1405
1406        if (*pfScales.add(a) < XM3_DECOMP_EPSILON)
1407        {
1408            *ppvBasis[a] = *pvCanonicalBasis[a];
1409        }
1410
1411        *ppvBasis[a] = XMVector3Normalize(*ppvBasis[a]);
1412
1413        if (*pfScales.add(b) < XM3_DECOMP_EPSILON)
1414        {
1415            let _aa: usize;
1416            let _bb: usize;
1417            let cc: usize;
1418
1419            let fAbsX = fabsf(XMVectorGetX(*ppvBasis[a]));
1420            let fAbsY = fabsf(XMVectorGetY(*ppvBasis[a]));
1421            let fAbsZ = fabsf(XMVectorGetZ(*ppvBasis[a]));
1422
1423            XM3RANKDECOMPOSE!(_aa, _bb, cc, fAbsX, fAbsY, fAbsZ);
1424
1425            *ppvBasis[b] = XMVector3Cross(*ppvBasis[a], *pvCanonicalBasis[cc]);
1426        }
1427
1428        *ppvBasis[b] = XMVector3Normalize(*ppvBasis[b]);
1429
1430        if (*pfScales.add(c) < XM3_DECOMP_EPSILON)
1431        {
1432            *ppvBasis[c] = XMVector3Cross(*ppvBasis[a], *ppvBasis[b]);
1433        }
1434
1435        *ppvBasis[c] = XMVector3Normalize(*ppvBasis[c]);
1436
1437        let mut fDet: f32 = XMVectorGetX(XMMatrixDeterminant(matTemp));
1438
1439        // use Kramer's rule to check for handedness of coordinate system
1440        if (fDet < 0.0)
1441        {
1442            // switch coordinate system by negating the scale and inverting the basis vector on the x-axis
1443            *pfScales.add(a) = -(*pfScales.add(a));
1444            *ppvBasis[a] = XMVectorNegate(*ppvBasis[a]);
1445
1446            fDet = -fDet;
1447        }
1448
1449        fDet -= 1.0;
1450        fDet *= fDet;
1451
1452        if (XM3_DECOMP_EPSILON < fDet)
1453        {
1454            // Non-SRT matrix encountered
1455            return false;
1456        }
1457
1458        // generate the quaternion from the matrix
1459        *outRotQuat = XMQuaternionRotationMatrix(matTemp);
1460        return true;
1461    }
1462}
1463
1464#[test]
1465fn test_XMMatrixDecompose() {
1466    let scaling_origin = XMVectorSet(0.0, 0.0, 0.0, 0.0);
1467    let scaling_orientation_quaternion = XMQuaternionRotationRollPitchYaw(0.1 ,0.2, 0.3);
1468    let scaling = XMVectorSet(1.1, 1.2, 1.3, 0.0);
1469    let rotation_origin = XMVectorSet(0.0, 0.0, 0.0, 0.0);
1470    let rotation_quaternion = XMQuaternionRotationRollPitchYaw(0.4 ,0.5, 0.6);
1471    let translation = XMVectorSet(7.0, 8.0, 9.0, 0.0);
1472
1473    let transform = XMMatrixTransformation(
1474        scaling_origin,
1475        scaling_orientation_quaternion,
1476        scaling,
1477        rotation_origin,
1478        rotation_quaternion,
1479        translation
1480    );
1481
1482    let mut out_scale = XMVectorZero();
1483    let mut out_rot_quat = XMVectorZero();
1484    let mut out_trans = XMVectorZero();
1485
1486    assert!(XMMatrixDecompose(&mut out_scale, &mut out_rot_quat, &mut out_trans, transform));
1487
1488    let epsilon = XMVectorReplicate(1.0e-1);
1489
1490    assert!(XMVector3NearEqual(out_scale, scaling, epsilon));
1491    assert!(XMVector4NearEqual(out_rot_quat, rotation_quaternion, epsilon));
1492    assert!(XMVector3NearEqual(out_trans, translation, epsilon));
1493}
1494
1495/// Builds the identity matrix.
1496///
1497/// ## Parameters
1498///
1499/// This function has no parameters.
1500///
1501/// ## Return value
1502///
1503/// Returns the identity matrix.
1504///
1505/// ## Reference
1506///
1507/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixIdentity>
1508#[inline]
1509pub fn XMMatrixIdentity() -> XMMATRIX
1510{
1511    unsafe {
1512        let mut M: XMMATRIX = crate::undefined();
1513        M.r[0] = g_XMIdentityR0.v;
1514        M.r[1] = g_XMIdentityR1.v;
1515        M.r[2] = g_XMIdentityR2.v;
1516        M.r[3] = g_XMIdentityR3.v;
1517        return M;
1518    }
1519}
1520
1521/// Creates a matrix with float values.
1522///
1523/// ## Parameters
1524///
1525/// `m00` Value to assign to the `(0,0)` element.
1526///
1527/// `m01` Value to assign to the `(0,1)` element.
1528///
1529/// `m02` Value to assign to the `(0,2)` element.
1530///
1531/// `m03` Value to assign to the `(0,3)` element.
1532///
1533/// `m10` Value to assign to the `(1,0)` element.
1534///
1535/// `m11` Value to assign to the `(1,1)` element.
1536///
1537/// `m12` Value to assign to the `(1,2)` element.
1538///
1539/// `m13` Value to assign to the `(1,3)` element.
1540///
1541/// `m20` Value to assign to the `(2,0)` element.
1542///
1543/// `m21` Value to assign to the `(2,1)` element.
1544///
1545/// `m22` Value to assign to the `(2,2)` element.
1546///
1547/// `m23` Value to assign to the `(2,3)` element.
1548///
1549/// `m30` Value to assign to the `(3,0)` element.
1550///
1551/// `m31` Value to assign to the `(3,1)` element.
1552///
1553/// `m32` Value to assign to the `(3,2)` element.
1554///
1555/// `m33` Value to assign to the `(3,3)` element.
1556///
1557/// ## Return value
1558///
1559/// Returns the XMMATRIX with the specified elements.
1560///
1561/// ## Reference
1562///
1563/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixSet>
1564#[inline]
1565pub fn XMMatrixSet(
1566    m00: f32, m01: f32, m02: f32, m03: f32,
1567    m10: f32, m11: f32, m12: f32, m13: f32,
1568    m20: f32, m21: f32, m22: f32, m23: f32,
1569    m30: f32, m31: f32, m32: f32, m33: f32
1570) -> XMMATRIX
1571{
1572    let mut M: XMMATRIX = unsafe { crate::undefined() };
1573
1574    #[cfg(_XM_NO_INTRINSICS_)]
1575    unsafe {
1576        M.m[0][0] = m00; M.m[0][1] = m01; M.m[0][2] = m02; M.m[0][3] = m03;
1577        M.m[1][0] = m10; M.m[1][1] = m11; M.m[1][2] = m12; M.m[1][3] = m13;
1578        M.m[2][0] = m20; M.m[2][1] = m21; M.m[2][2] = m22; M.m[2][3] = m23;
1579        M.m[3][0] = m30; M.m[3][1] = m31; M.m[3][2] = m32; M.m[3][3] = m33;
1580    }
1581
1582    #[cfg(not(_XM_NO_INTRINSICS_))]
1583    unsafe {
1584        M.r[0] = XMVectorSet(m00, m01, m02, m03);
1585        M.r[1] = XMVectorSet(m10, m11, m12, m13);
1586        M.r[2] = XMVectorSet(m20, m21, m22, m23);
1587        M.r[3] = XMVectorSet(m30, m31, m32, m33);
1588    }
1589
1590    return M;
1591}
1592
1593/// Builds a translation matrix from the specified offsets.
1594///
1595/// ## Parameters
1596///
1597/// `OffsetX` Translation along the `x-axis`.
1598///
1599/// `OffsetY` Translation along the `y-axis`.
1600///
1601/// `OffsetZ` Translation along the `z-axis`.
1602///
1603/// ## Return value
1604///
1605/// Returns the translation matrix.
1606///
1607/// ## Reference
1608///
1609/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixTranslation>
1610#[inline]
1611pub fn XMMatrixTranslation(
1612    OffsetX: f32,
1613    OffsetY: f32,
1614    OffsetZ: f32,
1615) -> XMMATRIX
1616{
1617    let mut M: XMMATRIX = unsafe { crate::undefined() };
1618
1619    #[cfg(_XM_NO_INTRINSICS_)]
1620    unsafe {
1621        M.m[0][0] = 1.0;
1622        M.m[0][1] = 0.0;
1623        M.m[0][2] = 0.0;
1624        M.m[0][3] = 0.0;
1625
1626        M.m[1][0] = 0.0;
1627        M.m[1][1] = 1.0;
1628        M.m[1][2] = 0.0;
1629        M.m[1][3] = 0.0;
1630
1631        M.m[2][0] = 0.0;
1632        M.m[2][1] = 0.0;
1633        M.m[2][2] = 1.0;
1634        M.m[2][3] = 0.0;
1635
1636        M.m[3][0] = OffsetX;
1637        M.m[3][1] = OffsetY;
1638        M.m[3][2] = OffsetZ;
1639        M.m[3][3] = 1.0;
1640    }
1641
1642    #[cfg(any(_XM_SSE_INTRINSICS_, _XM_ARM_NEON_INTRINSICS_))]
1643    unsafe {
1644        M.r[0] = g_XMIdentityR0.v;
1645        M.r[1] = g_XMIdentityR1.v;
1646        M.r[2] = g_XMIdentityR2.v;
1647        M.r[3] = XMVectorSet(OffsetX, OffsetY, OffsetZ, 1.0);
1648    }
1649
1650    return M;
1651}
1652
1653/// Builds a translation matrix from a vector.
1654///
1655/// ## Parameters
1656///
1657/// `Offset` 3D vector describing the translations along the `x-axis`, `y-axis`, and `z-axis`.
1658///
1659/// ## Return value
1660///
1661/// Returns the translation matrix.
1662///
1663/// ## Reference
1664///
1665/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixTranslationFromVector>
1666#[inline]
1667pub fn XMMatrixTranslationFromVector(
1668    Offset: XMVECTOR,
1669) -> XMMATRIX
1670{
1671    #[cfg(_XM_NO_INTRINSICS_)]
1672    unsafe {
1673        let mut M: XMMATRIX = crate::undefined();
1674        M.m[0][0] = 1.0;
1675        M.m[0][1] = 0.0;
1676        M.m[0][2] = 0.0;
1677        M.m[0][3] = 0.0;
1678
1679        M.m[1][0] = 0.0;
1680        M.m[1][1] = 1.0;
1681        M.m[1][2] = 0.0;
1682        M.m[1][3] = 0.0;
1683
1684        M.m[2][0] = 0.0;
1685        M.m[2][1] = 0.0;
1686        M.m[2][2] = 1.0;
1687        M.m[2][3] = 0.0;
1688
1689        M.m[3][0] = Offset.vector4_f32[0];
1690        M.m[3][1] = Offset.vector4_f32[1];
1691        M.m[3][2] = Offset.vector4_f32[2];
1692        M.m[3][3] = 1.0;
1693        return M;
1694    }
1695
1696    #[cfg(any(_XM_SSE_INTRINSICS_, _XM_ARM_NEON_INTRINSICS_))]
1697    unsafe {
1698        let mut M: XMMATRIX = crate::undefined();
1699        M.r[0] = g_XMIdentityR0.v;
1700        M.r[1] = g_XMIdentityR1.v;
1701        M.r[2] = g_XMIdentityR2.v;
1702        M.r[3] = XMVectorSelect(g_XMIdentityR3.v, Offset, g_XMSelect1110.v);
1703        return M;
1704    }
1705}
1706
1707/// Builds a matrix that scales along the x-axis, y-axis, and z-axis.
1708///
1709/// ## Parameters
1710///
1711/// `ScaleX` Scaling factor along the `x-axis`.
1712///
1713/// `ScaleY` Scaling factor along the `y-axis`.
1714///
1715/// `ScaleZ` Scaling factor along the `z-axis`.
1716///
1717/// ## Return value
1718///
1719/// Returns the scaling matrix.
1720///
1721/// ## Reference
1722///
1723/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixScaling>
1724#[inline]
1725pub fn XMMatrixScaling(
1726    ScaleX: f32,
1727    ScaleY: f32,
1728    ScaleZ: f32,
1729) -> XMMATRIX
1730{
1731    #[cfg(_XM_NO_INTRINSICS_)]
1732    unsafe {
1733        let mut M: XMMATRIX = crate::undefined();
1734        M.m[0][0] = ScaleX;
1735        M.m[0][1] = 0.0;
1736        M.m[0][2] = 0.0;
1737        M.m[0][3] = 0.0;
1738
1739        M.m[1][0] = 0.0;
1740        M.m[1][1] = ScaleY;
1741        M.m[1][2] = 0.0;
1742        M.m[1][3] = 0.0;
1743
1744        M.m[2][0] = 0.0;
1745        M.m[2][1] = 0.0;
1746        M.m[2][2] = ScaleZ;
1747        M.m[2][3] = 0.0;
1748
1749        M.m[3][0] = 0.0;
1750        M.m[3][1] = 0.0;
1751        M.m[3][2] = 0.0;
1752        M.m[3][3] = 1.0;
1753        return M;
1754    }
1755
1756    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
1757    unsafe {
1758        unimplemented!()
1759    }
1760
1761    #[cfg(_XM_SSE_INTRINSICS_)]
1762    unsafe {
1763        let mut M: XMMATRIX = crate::undefined();
1764        M.r[0] = _mm_set_ps(0.0, 0.0, 0.0, ScaleX);
1765        M.r[1] = _mm_set_ps(0.0, 0.0, ScaleY, 0.0);
1766        M.r[2] = _mm_set_ps(0.0, ScaleZ, 0.0, 0.0);
1767        M.r[3] = g_XMIdentityR3.v;
1768        return M;
1769    }
1770}
1771
1772/// Builds a matrix that scales along the x-axis, y-axis, and z-axis.
1773///
1774/// ## Parameters
1775///
1776/// `Scale` 3D vector describing the scaling along the `x-axis`, `y-axis`, and `z-axis`.
1777///
1778/// ## Return value
1779///
1780/// Returns the scaling matrix.
1781///
1782/// ## Reference
1783///
1784/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixScalingFromVector>
1785#[inline]
1786pub fn XMMatrixScalingFromVector(
1787    Scale: XMVECTOR,
1788) -> XMMATRIX
1789{
1790    #[cfg(_XM_NO_INTRINSICS_)]
1791    unsafe {
1792        let mut M: XMMATRIX = crate::undefined();
1793        M.m[0][0] = Scale.vector4_f32[0];
1794        M.m[0][1] = 0.0;
1795        M.m[0][2] = 0.0;
1796        M.m[0][3] = 0.0;
1797
1798        M.m[1][0] = 0.0;
1799        M.m[1][1] = Scale.vector4_f32[1];
1800        M.m[1][2] = 0.0;
1801        M.m[1][3] = 0.0;
1802
1803        M.m[2][0] = 0.0;
1804        M.m[2][1] = 0.0;
1805        M.m[2][2] = Scale.vector4_f32[2];
1806        M.m[2][3] = 0.0;
1807
1808        M.m[3][0] = 0.0;
1809        M.m[3][1] = 0.0;
1810        M.m[3][2] = 0.0;
1811        M.m[3][3] = 1.0;
1812        return M;
1813    }
1814
1815    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
1816    unsafe {
1817        unimplemented!()
1818    }
1819
1820    #[cfg(_XM_SSE_INTRINSICS_)]
1821    unsafe {
1822        let mut M: XMMATRIX = crate::undefined();
1823        M.r[0] = _mm_and_ps(Scale, *g_XMMaskX);
1824        M.r[1] = _mm_and_ps(Scale, *g_XMMaskY);
1825        M.r[2] = _mm_and_ps(Scale, *g_XMMaskZ);
1826        M.r[3] = g_XMIdentityR3.v;
1827        return M;
1828    }
1829}
1830
1831/// Builds a matrix that rotates around the `x-axis`.
1832///
1833/// ## Parameters
1834///
1835/// `Angle` Angle of rotation around the `x-axis`, in radians. Angles are measured clockwise when looking along the
1836/// rotation axis toward the origin.
1837///
1838/// ## Return value
1839///
1840/// Returns the rotation matrix.
1841///
1842/// ## Reference
1843///
1844/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixRotationX>
1845#[inline]
1846pub fn XMMatrixRotationX(
1847    Angle: f32,
1848) -> XMMATRIX
1849{
1850    #[cfg(_XM_NO_INTRINSICS_)]
1851    unsafe {
1852        let mut fSinAngle: f32 = 0.0;
1853        let mut fCosAngle: f32 = 0.0;
1854        XMScalarSinCos(&mut fSinAngle, &mut fCosAngle, Angle);
1855
1856        let mut M: XMMATRIX = crate::undefined();
1857        M.m[0][0] = 1.0;
1858        M.m[0][1] = 0.0;
1859        M.m[0][2] = 0.0;
1860        M.m[0][3] = 0.0;
1861
1862        M.m[1][0] = 0.0;
1863        M.m[1][1] = fCosAngle;
1864        M.m[1][2] = fSinAngle;
1865        M.m[1][3] = 0.0;
1866
1867        M.m[2][0] = 0.0;
1868        M.m[2][1] = -fSinAngle;
1869        M.m[2][2] = fCosAngle;
1870        M.m[2][3] = 0.0;
1871
1872        M.m[3][0] = 0.0;
1873        M.m[3][1] = 0.0;
1874        M.m[3][2] = 0.0;
1875        M.m[3][3] = 1.0;
1876        return M;
1877    }
1878
1879    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
1880    {
1881        unimplemented!()
1882    }
1883
1884    #[cfg(_XM_SSE_INTRINSICS_)]
1885    unsafe {
1886        let mut SinAngle: f32 = 0.0;
1887        let mut CosAngle: f32 = 0.0;
1888        XMScalarSinCos(&mut SinAngle, &mut CosAngle, Angle);
1889
1890        let vSin: XMVECTOR = _mm_set_ss(SinAngle);
1891        let mut vCos: XMVECTOR = _mm_set_ss(CosAngle);
1892        // x = 0,y = cos,z = sin, w = 0
1893        vCos = _mm_shuffle_ps(vCos, vSin, _MM_SHUFFLE(3, 0, 0, 3));
1894        let mut M: XMMATRIX = undefined();
1895        M.r[0] = g_XMIdentityR0.v;
1896        M.r[1] = vCos;
1897        // x = 0,y = sin,z = cos, w = 0
1898        vCos = XM_PERMUTE_PS!(vCos, _MM_SHUFFLE(3, 1, 2, 0));
1899        // x = 0,y = -sin,z = cos, w = 0
1900        vCos = _mm_mul_ps(vCos, g_XMNegateY.v);
1901        M.r[2] = vCos;
1902        M.r[3] = g_XMIdentityR3.v;
1903        return M;
1904    }
1905}
1906
1907/// Builds a matrix that rotates around the `y-axis`.
1908///
1909/// ## Parameters
1910///
1911/// `Angle` Angle of rotation around the `y-axis`, in radians. Angles are measured clockwise when looking along
1912/// the rotation axis toward the origin.
1913///
1914/// ## Return value
1915///
1916/// Returns the rotation matrix.
1917///
1918/// ## Reference
1919///
1920/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixRotationY>
1921#[inline]
1922pub fn XMMatrixRotationY(
1923    Angle: f32,
1924) -> XMMATRIX
1925{
1926    #[cfg(_XM_NO_INTRINSICS_)]
1927    unsafe {
1928        let mut fSinAngle: f32 = 0.0;
1929        let mut fCosAngle: f32 = 0.0;
1930        XMScalarSinCos(&mut fSinAngle, &mut fCosAngle, Angle);
1931
1932        let mut M: XMMATRIX = crate::undefined();
1933        M.m[0][0] = fCosAngle;
1934        M.m[0][1] = 0.0;
1935        M.m[0][2] = -fSinAngle;
1936        M.m[0][3] = 0.0;
1937
1938        M.m[1][0] = 0.0;
1939        M.m[1][1] = 1.0;
1940        M.m[1][2] = 0.0;
1941        M.m[1][3] = 0.0;
1942
1943        M.m[2][0] = fSinAngle;
1944        M.m[2][1] = 0.0;
1945        M.m[2][2] = fCosAngle;
1946        M.m[2][3] = 0.0;
1947
1948        M.m[3][0] = 0.0;
1949        M.m[3][1] = 0.0;
1950        M.m[3][2] = 0.0;
1951        M.m[3][3] = 1.0;
1952        return M;
1953    }
1954
1955    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
1956    {
1957        unimplemented!()
1958    }
1959
1960    #[cfg(_XM_SSE_INTRINSICS_)]
1961    unsafe {
1962        let mut SinAngle: f32 = 0.0;
1963        let mut CosAngle: f32 = 0.0;
1964        XMScalarSinCos(&mut SinAngle, &mut CosAngle, Angle);
1965
1966        let mut vSin: XMVECTOR = _mm_set_ss(SinAngle);
1967        let vCos: XMVECTOR = _mm_set_ss(CosAngle);
1968        // x = sin,y = 0,z = cos, w = 0
1969        vSin = _mm_shuffle_ps(vSin, vCos, _MM_SHUFFLE(3, 0, 3, 0));
1970        let mut M: XMMATRIX = undefined();
1971        M.r[2] = vSin;
1972        M.r[1] = g_XMIdentityR1.v;
1973        // x = cos,y = 0,z = sin, w = 0
1974        vSin = XM_PERMUTE_PS!(vSin, _MM_SHUFFLE(3, 0, 1, 2));
1975        // x = cos,y = 0,z = -sin, w = 0
1976        vSin = _mm_mul_ps(vSin, g_XMNegateZ.v);
1977        M.r[0] = vSin;
1978        M.r[3] = g_XMIdentityR3.v;
1979        return M;
1980    }
1981}
1982
1983/// Builds a matrix that rotates around the `z-axis`.
1984///
1985/// ## Parameters
1986///
1987/// `Angle` Angle of rotation around the `z-axis`, in radians. Angles are measured clockwise when looking along
1988/// the rotation axis toward the origin.
1989///
1990/// ## Return value
1991///
1992/// Returns the rotation matrix.
1993///
1994/// ## Reference
1995///
1996/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixRotationZ>
1997#[inline]
1998pub fn XMMatrixRotationZ(
1999    Angle: f32,
2000) -> XMMATRIX
2001{
2002    #[cfg(_XM_NO_INTRINSICS_)]
2003    unsafe {
2004        let mut fSinAngle: f32 = 0.0;
2005        let mut fCosAngle: f32 = 0.0;
2006        XMScalarSinCos(&mut fSinAngle, &mut fCosAngle, Angle);
2007
2008        let mut M: XMMATRIX = crate::undefined();
2009        M.m[0][0] = fCosAngle;
2010        M.m[0][1] = fSinAngle;
2011        M.m[0][2] = 0.0;
2012        M.m[0][3] = 0.0;
2013
2014        M.m[1][0] = -fSinAngle;
2015        M.m[1][1] = fCosAngle;
2016        M.m[1][2] = 0.0;
2017        M.m[1][3] = 0.0;
2018
2019        M.m[2][0] = 0.0;
2020        M.m[2][1] = 0.0;
2021        M.m[2][2] = 1.0;
2022        M.m[2][3] = 0.0;
2023
2024        M.m[3][0] = 0.0;
2025        M.m[3][1] = 0.0;
2026        M.m[3][2] = 0.0;
2027        M.m[3][3] = 1.0;
2028        return M;
2029    }
2030
2031    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
2032    {
2033        unimplemented!()
2034    }
2035
2036    #[cfg(_XM_SSE_INTRINSICS_)]
2037    unsafe {
2038        let mut SinAngle: f32 = 0.0;
2039        let mut CosAngle: f32 = 0.0;
2040        XMScalarSinCos(&mut SinAngle, &mut CosAngle, Angle);
2041
2042        let vSin: XMVECTOR = _mm_set_ss(SinAngle);
2043        let mut vCos: XMVECTOR = _mm_set_ss(CosAngle);
2044        // x = cos,y = sin,z = 0, w = 0
2045        vCos = _mm_unpacklo_ps(vCos, vSin);
2046        let mut M: XMMATRIX = undefined();
2047        M.r[0] = vCos;
2048        // x = sin,y = cos,z = 0, w = 0
2049        vCos = XM_PERMUTE_PS!(vCos, _MM_SHUFFLE(3, 2, 0, 1));
2050        // x = cos,y = -sin,z = 0, w = 0
2051        vCos = _mm_mul_ps(vCos, g_XMNegateX.v);
2052        M.r[1] = vCos;
2053        M.r[2] = g_XMIdentityR2.v;
2054        M.r[3] = g_XMIdentityR3.v;
2055        return M;
2056    }
2057}
2058
2059/// Builds a rotation matrix based on a given `pitch`, `yaw`, and `roll` (Euler angles).
2060///
2061/// ## Parameters
2062///
2063/// `Pitch` Angle of rotation around the `x-axis`, in radians.
2064///
2065/// `Yaw` Angle of rotation around the `y-axis`, in radians.
2066///
2067/// `Roll` Angle of rotation around the `z-axis`, in radians.
2068///
2069/// ## Return value
2070///
2071/// Returns the rotation matrix.
2072///
2073/// ## Remarks
2074///
2075/// Angles are measured clockwise when looking along the rotation axis toward the origin. **This is a left-handed
2076/// coordinate system. To use right-handed coordinates, negate all three angles.**
2077///
2078/// The order of transformations is `roll` first, then `pitch`, and then `yaw`. The rotations are all applied
2079/// in the global coordinate frame.
2080///
2081/// ## Reference
2082///
2083/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixRotationRollPitchYaw>
2084#[inline]
2085pub fn XMMatrixRotationRollPitchYaw(
2086    Pitch: f32,
2087    Yaw: f32,
2088    Roll: f32,
2089) -> XMMATRIX
2090{
2091    let Angles: XMVECTOR = XMVectorSet(Pitch, Yaw, Roll, 0.0);
2092    return XMMatrixRotationRollPitchYawFromVector(Angles);
2093}
2094
2095/// Builds a rotation matrix based on a vector containing the Euler angles (`pitch`, `yaw`, and `roll`).
2096///
2097/// ## Parameters
2098///
2099/// `Angles` 3D vector containing the Euler angles in the order `pitch`, then `yaw`, and then `roll`.
2100///
2101/// ## Return value
2102///
2103/// Returns the rotation matrix.
2104///
2105/// ## Remarks
2106///
2107/// Angles are measured clockwise when looking along the rotation axis toward the origin. **This is a left-handed
2108/// coordinate system. To use right-handed coordinates, negate all three angles**.
2109///
2110/// The order of transformations is `roll` first, then `pitch`, and then `yaw`. The rotations are all applied
2111/// in the global coordinate frame.
2112///
2113/// ## Reference
2114///
2115/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixRotationRollPitchYawFromVector>
2116#[inline]
2117pub fn XMMatrixRotationRollPitchYawFromVector(
2118    Angles: FXMVECTOR,
2119) -> XMMATRIX
2120{
2121    let Q: XMVECTOR = XMQuaternionRotationRollPitchYawFromVector(Angles);
2122    return XMMatrixRotationQuaternion(Q);
2123}
2124
2125/// Builds a matrix that rotates around an arbitrary normal vector.
2126///
2127/// ## Parameters
2128///
2129/// `NormalAxis` Normal vector describing the axis of rotation.
2130///
2131/// `Angle` Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward
2132/// the origin.
2133///
2134/// ## Return value
2135///
2136/// Returns the rotation matrix.
2137///
2138/// ## Reference
2139///
2140/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixRotationNormal>
2141#[inline]
2142pub fn XMMatrixRotationNormal(
2143    NormalAxis: FXMVECTOR,
2144    Angle: f32,
2145) -> XMMATRIX
2146{
2147    #[cfg(any(_XM_NO_INTRINSICS_, _XM_ARM_NEON_INTRINSICS_))]
2148    unsafe {
2149        let mut fSinAngle: f32 = 0.0;
2150        let mut fCosAngle: f32 = 0.0;
2151        XMScalarSinCos(&mut fSinAngle, &mut fCosAngle, Angle);
2152
2153        let A: XMVECTOR = XMVectorSet(fSinAngle, fCosAngle, 1.0 - fCosAngle, 0.0);
2154
2155        let C2: XMVECTOR = XMVectorSplatZ(A);
2156        let C1: XMVECTOR = XMVectorSplatY(A);
2157        let C0: XMVECTOR = XMVectorSplatX(A);
2158
2159        let N0: XMVECTOR = <(XM_SWIZZLE_Y, XM_SWIZZLE_Z, XM_SWIZZLE_X, XM_SWIZZLE_W)>::XMVectorSwizzle(NormalAxis);
2160        let N1: XMVECTOR = <(XM_SWIZZLE_Z, XM_SWIZZLE_X, XM_SWIZZLE_Y, XM_SWIZZLE_W)>::XMVectorSwizzle(NormalAxis);
2161
2162        let mut V0: XMVECTOR = XMVectorMultiply(C2, N0);
2163        V0 = XMVectorMultiply(V0, N1);
2164
2165        let mut R0: XMVECTOR = XMVectorMultiply(C2, NormalAxis);
2166        R0 = XMVectorMultiplyAdd(R0, NormalAxis, C1);
2167
2168        let R1: XMVECTOR = XMVectorMultiplyAdd(C0, NormalAxis, V0);
2169        let R2: XMVECTOR = XMVectorNegativeMultiplySubtract(C0, NormalAxis, V0);
2170
2171        V0 = XMVectorSelect(A, R0, g_XMSelect1110.v);
2172        let V1: XMVECTOR = <(XM_PERMUTE_0Z, XM_PERMUTE_1Y, XM_PERMUTE_1Z, XM_PERMUTE_0X)>::XMVectorPermute(R1, R2);
2173        let V2: XMVECTOR = <(XM_PERMUTE_0Y, XM_PERMUTE_1X, XM_PERMUTE_0Y, XM_PERMUTE_1X)>::XMVectorPermute(R1, R2);
2174
2175        let mut M: XMMATRIX = crate::undefined();
2176        M.r[0] = <(XM_PERMUTE_0X, XM_PERMUTE_1X, XM_PERMUTE_1Y, XM_PERMUTE_0W)>::XMVectorPermute(V0, V1);
2177        M.r[1] = <(XM_PERMUTE_1Z, XM_PERMUTE_0Y, XM_PERMUTE_1W, XM_PERMUTE_0W)>::XMVectorPermute(V0, V1);
2178        M.r[2] = <(XM_PERMUTE_1X, XM_PERMUTE_1Y, XM_PERMUTE_0Z, XM_PERMUTE_0W)>::XMVectorPermute(V0, V2);
2179        M.r[3] = g_XMIdentityR3.v;
2180        return M;
2181    }
2182
2183    #[cfg(_XM_SSE_INTRINSICS_)]
2184    unsafe {
2185        let mut fSinAngle: f32 = 0.0;
2186        let mut fCosAngle: f32 = 0.0;
2187        XMScalarSinCos(&mut fSinAngle, &mut fCosAngle, Angle);
2188
2189        let C2: XMVECTOR = _mm_set_ps1(1.0 - fCosAngle);
2190        let C1: XMVECTOR = _mm_set_ps1(fCosAngle);
2191        let C0: XMVECTOR = _mm_set_ps1(fSinAngle);
2192
2193        let N0: XMVECTOR = XM_PERMUTE_PS!(NormalAxis, _MM_SHUFFLE(3, 0, 2, 1));
2194        let N1: XMVECTOR = XM_PERMUTE_PS!(NormalAxis, _MM_SHUFFLE(3, 1, 0, 2));
2195
2196        let mut V0: XMVECTOR = _mm_mul_ps(C2, N0);
2197        V0 = _mm_mul_ps(V0, N1);
2198
2199        let mut R0: XMVECTOR = _mm_mul_ps(C2, NormalAxis);
2200        R0 = _mm_mul_ps(R0, NormalAxis);
2201        R0 = _mm_add_ps(R0, C1);
2202
2203        let mut R1: XMVECTOR = _mm_mul_ps(C0, NormalAxis);
2204        R1 = _mm_add_ps(R1, V0);
2205        let mut R2: XMVECTOR = _mm_mul_ps(C0, NormalAxis);
2206        R2 = _mm_sub_ps(V0, R2);
2207
2208        V0 = _mm_and_ps(R0, g_XMMask3.v);
2209        let mut V1: XMVECTOR = _mm_shuffle_ps(R1, R2, _MM_SHUFFLE(2, 1, 2, 0));
2210        V1 = XM_PERMUTE_PS!(V1, _MM_SHUFFLE(0, 3, 2, 1));
2211        let mut V2: XMVECTOR = _mm_shuffle_ps(R1, R2, _MM_SHUFFLE(0, 0, 1, 1));
2212        V2 = XM_PERMUTE_PS!(V2, _MM_SHUFFLE(2, 0, 2, 0));
2213
2214        R2 = _mm_shuffle_ps(V0, V1, _MM_SHUFFLE(1, 0, 3, 0));
2215        R2 = XM_PERMUTE_PS!(R2, _MM_SHUFFLE(1, 3, 2, 0));
2216
2217        let mut M: XMMATRIX = undefined();
2218        M.r[0] = R2;
2219
2220        R2 = _mm_shuffle_ps(V0, V1, _MM_SHUFFLE(3, 2, 3, 1));
2221        R2 = XM_PERMUTE_PS!(R2, _MM_SHUFFLE(1, 3, 0, 2));
2222        M.r[1] = R2;
2223
2224        V2 = _mm_shuffle_ps(V2, V0, _MM_SHUFFLE(3, 2, 1, 0));
2225        M.r[2] = V2;
2226        M.r[3] = g_XMIdentityR3.v;
2227        return M;
2228    }
2229}
2230
2231/// Builds a matrix that rotates around an arbitrary axis.
2232///
2233/// ## Parameters
2234///
2235/// `Axis` Vector describing the axis of rotation.
2236///
2237/// `Angle` Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward
2238/// the origin.
2239///
2240/// ## Return value
2241///
2242/// Returns the rotation matrix.
2243///
2244/// ## Remarks
2245///
2246/// If Axis is a normalized vector, it is faster to use the [`XMMatrixRotationNormal`] function to build this
2247/// type of matrix.
2248///
2249/// ## Reference
2250///
2251/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixRotationAxis>
2252///
2253/// [`XMMatrixRotationNormal`]: crate::matrix::XMMatrixRotationNormal
2254#[inline]
2255pub fn XMMatrixRotationAxis(
2256    Axis: FXMVECTOR,
2257    Angle: f32,
2258) -> XMMATRIX
2259{
2260    debug_assert!(!XMVector3Equal(Axis, XMVectorZero()));
2261    debug_assert!(!XMVector3IsInfinite(Axis));
2262
2263    let Normal: XMVECTOR = XMVector3Normalize(Axis);
2264    return XMMatrixRotationNormal(Normal, Angle);
2265}
2266
2267/// Builds a rotation matrix from a quaternion.
2268///
2269/// ## Parameters
2270///
2271/// `Quaternion` Quaternion defining the rotation.
2272///
2273/// ## Return value
2274///
2275/// Returns the rotation matrix.
2276///
2277/// ## Reference
2278///
2279/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixRotationQuaternion>
2280#[inline]
2281pub fn XMMatrixRotationQuaternion(
2282    Quaternion: FXMVECTOR ,
2283) -> XMMATRIX
2284{
2285    #[cfg(any(_XM_NO_INTRINSICS_, _XM_ARM_NEON_INTRINSICS_))]
2286    unsafe {
2287        // PERFORMANCE: static const
2288        const Constant1110: XMVECTORF32 = XMVECTORF32 { f: [ 1.0, 1.0, 1.0, 0.0 ] };
2289
2290        let Q0: XMVECTOR = XMVectorAdd(Quaternion, Quaternion);
2291        let Q1: XMVECTOR = XMVectorMultiply(Quaternion, Q0);
2292
2293        let mut V0: XMVECTOR = <(XM_PERMUTE_0Y, XM_PERMUTE_0X, XM_PERMUTE_0X, XM_PERMUTE_1W)>::XMVectorPermute(Q1, Constant1110.v);
2294        let mut V1: XMVECTOR = <(XM_PERMUTE_0Z, XM_PERMUTE_0Z, XM_PERMUTE_0Y, XM_PERMUTE_1W)>::XMVectorPermute(Q1, Constant1110.v);
2295        let mut R0: XMVECTOR = XMVectorSubtract(*Constant1110, V0);
2296        R0 = XMVectorSubtract(R0, V1);
2297
2298        V0 = <(XM_SWIZZLE_X, XM_SWIZZLE_X, XM_SWIZZLE_Y, XM_SWIZZLE_W)>::XMVectorSwizzle(Quaternion);
2299        V1 = <(XM_SWIZZLE_Z, XM_SWIZZLE_Y, XM_SWIZZLE_Z, XM_SWIZZLE_W)>::XMVectorSwizzle(Q0);
2300        V0 = XMVectorMultiply(V0, V1);
2301
2302        V1 = XMVectorSplatW(Quaternion);
2303        let V2: XMVECTOR = <(XM_SWIZZLE_Y, XM_SWIZZLE_Z, XM_SWIZZLE_X, XM_SWIZZLE_W)>::XMVectorSwizzle(Q0);
2304        V1 = XMVectorMultiply(V1, V2);
2305
2306        let R1: XMVECTOR = XMVectorAdd(V0, V1);
2307        let R2: XMVECTOR = XMVectorSubtract(V0, V1);
2308
2309        V0 = <(XM_PERMUTE_0Y, XM_PERMUTE_1X, XM_PERMUTE_1Y, XM_PERMUTE_0Z)>::XMVectorPermute(R1, R2);
2310        V1 = <(XM_PERMUTE_0X, XM_PERMUTE_1Z, XM_PERMUTE_0X, XM_PERMUTE_1Z)>::XMVectorPermute(R1, R2);
2311
2312        let mut M: XMMATRIX = crate::undefined();
2313        M.r[0] = <(XM_PERMUTE_0X, XM_PERMUTE_1X, XM_PERMUTE_1Y, XM_PERMUTE_0W)>::XMVectorPermute(R0, V0);
2314        M.r[1] = <(XM_PERMUTE_1Z, XM_PERMUTE_0Y, XM_PERMUTE_1W, XM_PERMUTE_0W)>::XMVectorPermute(R0, V0);
2315        M.r[2] = <(XM_PERMUTE_1X, XM_PERMUTE_1Y, XM_PERMUTE_0Z, XM_PERMUTE_0W)>::XMVectorPermute(R0, V1);
2316        M.r[3] = g_XMIdentityR3.v;
2317        return M;
2318    }
2319
2320    #[cfg(_XM_SSE_INTRINSICS_)]
2321    unsafe {
2322        // PERFORMANCE: static const
2323        const Constant1110: XMVECTORF32 = XMVECTORF32 { f: [ 1.0, 1.0, 1.0, 0.0 ] };
2324
2325        let Q0: XMVECTOR = _mm_add_ps(Quaternion, Quaternion);
2326        let mut Q1: XMVECTOR = _mm_mul_ps(Quaternion, Q0);
2327
2328        let mut V0: XMVECTOR = XM_PERMUTE_PS!(Q1, _MM_SHUFFLE(3, 0, 0, 1));
2329        V0 = _mm_and_ps(V0, *g_XMMask3);
2330        let mut V1: XMVECTOR = XM_PERMUTE_PS!(Q1, _MM_SHUFFLE(3, 1, 2, 2));
2331        V1 = _mm_and_ps(V1, *g_XMMask3);
2332        let mut R0: XMVECTOR = _mm_sub_ps(*Constant1110, V0);
2333        R0 = _mm_sub_ps(R0, V1);
2334
2335        V0 = XM_PERMUTE_PS!(Quaternion, _MM_SHUFFLE(3, 1, 0, 0));
2336        V1 = XM_PERMUTE_PS!(Q0, _MM_SHUFFLE(3, 2, 1, 2));
2337        V0 = _mm_mul_ps(V0, V1);
2338
2339        V1 = XM_PERMUTE_PS!(Quaternion, _MM_SHUFFLE(3, 3, 3, 3));
2340        let V2: XMVECTOR = XM_PERMUTE_PS!(Q0, _MM_SHUFFLE(3, 0, 2, 1));
2341        V1 = _mm_mul_ps(V1, V2);
2342
2343        let R1: XMVECTOR = _mm_add_ps(V0, V1);
2344        let R2: XMVECTOR = _mm_sub_ps(V0, V1);
2345
2346        V0 = _mm_shuffle_ps(R1, R2, _MM_SHUFFLE(1, 0, 2, 1));
2347        V0 = XM_PERMUTE_PS!(V0, _MM_SHUFFLE(1, 3, 2, 0));
2348        V1 = _mm_shuffle_ps(R1, R2, _MM_SHUFFLE(2, 2, 0, 0));
2349        V1 = XM_PERMUTE_PS!(V1, _MM_SHUFFLE(2, 0, 2, 0));
2350
2351        Q1 = _mm_shuffle_ps(R0, V0, _MM_SHUFFLE(1, 0, 3, 0));
2352        Q1 = XM_PERMUTE_PS!(Q1, _MM_SHUFFLE(1, 3, 2, 0));
2353
2354        let mut M: XMMATRIX = crate::undefined();
2355        M.r[0] = Q1;
2356
2357        Q1 = _mm_shuffle_ps(R0, V0, _MM_SHUFFLE(3, 2, 3, 1));
2358        Q1 = XM_PERMUTE_PS!(Q1, _MM_SHUFFLE(1, 3, 0, 2));
2359        M.r[1] = Q1;
2360
2361        Q1 = _mm_shuffle_ps(V1, R0, _MM_SHUFFLE(3, 2, 1, 0));
2362        M.r[2] = Q1;
2363        M.r[3] = *g_XMIdentityR3;
2364        return M;
2365    }
2366}
2367
2368/// Builds a 2D transformation matrix.
2369///
2370/// ## Parameters
2371///
2372/// `ScalingOrigin` 2D vector describing the center of the scaling.
2373///
2374/// `ScalingOrientation` Scaling rotation factor.
2375///
2376/// `Scaling` 2D vector containing the scaling factors for the `x-axis` and `y-axis`.
2377///
2378/// `RotationOrigin` 2D vector describing the center of the rotation.
2379///
2380/// `Rotation` Angle of rotation, in radians.
2381///
2382/// `Translation` 2D vector describing the translation.
2383///
2384/// ## Return value
2385///
2386/// Returns the transformation matrix.
2387///
2388/// ## Reference
2389///
2390/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixTransformation2D>
2391#[inline]
2392pub fn XMMatrixTransformation2D(
2393    ScalingOrigin: FXMVECTOR,
2394    ScalingOrientation: f32,
2395    Scaling: FXMVECTOR,
2396    RotationOrigin: FXMVECTOR,
2397    Rotation: f32,
2398    Translation: GXMVECTOR
2399) -> XMMATRIX
2400{
2401    unsafe {
2402        // M = Inverse(MScalingOrigin) * Transpose(MScalingOrientation) * MScaling * MScalingOrientation *
2403        //         MScalingOrigin * Inverse(MRotationOrigin) * MRotation * MRotationOrigin * MTranslation;
2404
2405        let VScalingOrigin: XMVECTOR = XMVectorSelect(g_XMSelect1100.v, ScalingOrigin, g_XMSelect1100.v);
2406        let NegScalingOrigin: XMVECTOR = XMVectorNegate(VScalingOrigin);
2407
2408        let MScalingOriginI: XMMATRIX = XMMatrixTranslationFromVector(NegScalingOrigin);
2409        let MScalingOrientation: XMMATRIX = XMMatrixRotationZ(ScalingOrientation);
2410        let MScalingOrientationT: XMMATRIX = XMMatrixTranspose(MScalingOrientation);
2411        let VScaling: XMVECTOR = XMVectorSelect(g_XMOne.v, Scaling, g_XMSelect1100.v);
2412        let MScaling: XMMATRIX = XMMatrixScalingFromVector(VScaling);
2413        let VRotationOrigin: XMVECTOR = XMVectorSelect(g_XMSelect1100.v, RotationOrigin, g_XMSelect1100.v);
2414        let MRotation: XMMATRIX = XMMatrixRotationZ(Rotation);
2415        let VTranslation: XMVECTOR = XMVectorSelect(g_XMSelect1100.v, Translation, g_XMSelect1100.v);
2416
2417        let mut M: XMMATRIX = XMMatrixMultiply(MScalingOriginI, &MScalingOrientationT);
2418        M = XMMatrixMultiply(M, &MScaling);
2419        M = XMMatrixMultiply(M, &MScalingOrientation);
2420        M.r[3] = XMVectorAdd(M.r[3], VScalingOrigin);
2421        M.r[3] = XMVectorSubtract(M.r[3], VRotationOrigin);
2422        M = XMMatrixMultiply(M, &MRotation);
2423        M.r[3] = XMVectorAdd(M.r[3], VRotationOrigin);
2424        M.r[3] = XMVectorAdd(M.r[3], VTranslation);
2425
2426        return M;
2427    }
2428}
2429
2430/// Builds a transformation matrix.
2431///
2432/// ## Parameters
2433///
2434/// `ScalingOrigin` 3D vector describing the center of the scaling.
2435///
2436/// `ScalingOrientationQuaternion` Quaternion describing the orientation of the scaling.
2437///
2438/// `Scaling` 3D vector containing the scaling factors for the `x-axis`, `y-axis`, and `z-axis`.
2439///
2440/// `RotationOrigin` 3D vector describing the center of the rotation.
2441///
2442/// `RotationQuaternion` Quaternion describing the rotation around the origin indicated by RotationOrigin.
2443///
2444/// `Translation` 3D vector describing the translations along the `x-axis`, `y-axis`, and `z-axis`.
2445///
2446/// ## Return value
2447///
2448/// Returns the transformation matrix.
2449///
2450/// ## Reference
2451///
2452/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixTransformation>
2453#[inline]
2454pub fn XMMatrixTransformation(
2455    ScalingOrigin: FXMVECTOR,
2456    ScalingOrientationQuaternion: FXMVECTOR,
2457    Scaling: FXMVECTOR,
2458    RotationOrigin: GXMVECTOR,
2459    RotationQuaternion: HXMVECTOR,
2460    Translation: HXMVECTOR
2461) -> XMMATRIX
2462{
2463    unsafe {
2464        // M = Inverse(MScalingOrigin) * Transpose(MScalingOrientation) * MScaling * MScalingOrientation *
2465        //         MScalingOrigin * Inverse(MRotationOrigin) * MRotation * MRotationOrigin * MTranslation;
2466
2467        let VScalingOrigin: XMVECTOR = XMVectorSelect(g_XMSelect1110.v, ScalingOrigin, g_XMSelect1110.v);
2468        let NegScalingOrigin: XMVECTOR = XMVectorNegate(ScalingOrigin);
2469
2470        let MScalingOriginI: XMMATRIX = XMMatrixTranslationFromVector(NegScalingOrigin);
2471        let MScalingOrientation: XMMATRIX = XMMatrixRotationQuaternion(ScalingOrientationQuaternion);
2472        let MScalingOrientationT: XMMATRIX = XMMatrixTranspose(MScalingOrientation);
2473        let MScaling: XMMATRIX = XMMatrixScalingFromVector(Scaling);
2474        let VRotationOrigin: XMVECTOR = XMVectorSelect(g_XMSelect1110.v, RotationOrigin, g_XMSelect1110.v);
2475        let MRotation: XMMATRIX = XMMatrixRotationQuaternion(RotationQuaternion);
2476        let VTranslation: XMVECTOR = XMVectorSelect(g_XMSelect1110.v, Translation, g_XMSelect1110.v);
2477
2478        let mut M: XMMATRIX;
2479        M = XMMatrixMultiply(MScalingOriginI, &MScalingOrientationT);
2480        M = XMMatrixMultiply(M, &MScaling);
2481        M = XMMatrixMultiply(M, &MScalingOrientation);
2482        M.r[3] = XMVectorAdd(M.r[3], VScalingOrigin);
2483        M.r[3] = XMVectorSubtract(M.r[3], VRotationOrigin);
2484        M = XMMatrixMultiply(M, &MRotation);
2485        M.r[3] = XMVectorAdd(M.r[3], VRotationOrigin);
2486        M.r[3] = XMVectorAdd(M.r[3], VTranslation);
2487        return M;
2488    }
2489}
2490
2491/// Builds a 2D affine transformation matrix in the xy plane.
2492///
2493/// ## Parameters
2494///
2495/// `Scaling` 2D vector of scaling factors for the x-coordinate and y-coordinate.
2496///
2497/// `RotationOrigin` 2D vector describing the center of rotation.
2498///
2499/// `Rotation` Radian angle of rotation.
2500///
2501/// `Translation` 2D vector translation offsets.
2502///
2503/// ## Return value
2504///
2505/// Returns the 2D affine transformation matrix.
2506///
2507/// ## Reference
2508//
2509/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixAffineTransformation2D>
2510#[inline]
2511pub fn XMMatrixAffineTransformation2D(
2512    Scaling: FXMVECTOR,
2513    RotationOrigin: FXMVECTOR,
2514    Rotation: f32,
2515    Translation: GXMVECTOR
2516) -> XMMATRIX
2517{
2518    unsafe {
2519        // M = MScaling * Inverse(MRotationOrigin) * MRotation * MRotationOrigin * MTranslation;
2520
2521        let VScaling: XMVECTOR = XMVectorSelect(g_XMOne.v, Scaling, g_XMSelect1100.v);
2522        let MScaling: XMMATRIX = XMMatrixScalingFromVector(VScaling);
2523        let VRotationOrigin: XMVECTOR = XMVectorSelect(g_XMSelect1100.v, RotationOrigin, g_XMSelect1100.v);
2524        let MRotation: XMMATRIX = XMMatrixRotationZ(Rotation);
2525        let VTranslation: XMVECTOR = XMVectorSelect(g_XMSelect1100.v, Translation, g_XMSelect1100.v);
2526
2527        let mut M: XMMATRIX;
2528        M = MScaling;
2529        M.r[3] = XMVectorSubtract(M.r[3], VRotationOrigin);
2530        M = XMMatrixMultiply(M, &MRotation);
2531        M.r[3] = XMVectorAdd(M.r[3], VRotationOrigin);
2532        M.r[3] = XMVectorAdd(M.r[3], VTranslation);
2533        return M;
2534    }
2535}
2536
2537/// Builds an affine transformation matrix.
2538///
2539/// ## Parameters
2540///
2541/// `Scaling` Vector of scaling factors for each dimension.
2542///
2543/// `RotationOrigin` Point identifying the center of rotation.
2544///
2545/// `RotationQuaternion` Rotation factors.
2546///
2547/// `Translation` Translation offsets.
2548///
2549/// ## Return value
2550///
2551/// Returns the affine transformation matrix, built from the scaling, rotation, and translation information.
2552///
2553/// ## Reference
2554///
2555/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixAffineTransformation>
2556#[inline]
2557pub fn XMMatrixAffineTransformation(
2558    Scaling: FXMVECTOR,
2559    RotationOrigin: FXMVECTOR,
2560    RotationQuaternion: FXMVECTOR,
2561    Translation: GXMVECTOR
2562) -> XMMATRIX
2563{
2564    unsafe {
2565        // M = MScaling * Inverse(MRotationOrigin) * MRotation * MRotationOrigin * MTranslation;
2566
2567        let MScaling: XMMATRIX = XMMatrixScalingFromVector(Scaling);
2568        let VRotationOrigin: XMVECTOR = XMVectorSelect(g_XMSelect1110.v, RotationOrigin, g_XMSelect1110.v);
2569        let MRotation: XMMATRIX = XMMatrixRotationQuaternion(RotationQuaternion);
2570        let VTranslation: XMVECTOR = XMVectorSelect(g_XMSelect1110.v, Translation, g_XMSelect1110.v);
2571
2572        let mut M: XMMATRIX;
2573        M = MScaling;
2574        M.r[3] = XMVectorSubtract(M.r[3], VRotationOrigin);
2575        M = XMMatrixMultiply(M, &MRotation);
2576        M.r[3] = XMVectorAdd(M.r[3], VRotationOrigin);
2577        M.r[3] = XMVectorAdd(M.r[3], VTranslation);
2578        return M;
2579    }
2580}
2581
2582/// Builds a transformation matrix designed to reflect vectors through a given plane.
2583///
2584/// ## Parameters
2585///
2586/// `ReflectionPlane` Plane to reflect through.
2587///
2588/// ## Return value
2589///
2590/// Returns the transformation matrix.
2591///
2592/// ## Reference
2593///
2594/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixReflect>
2595#[inline]
2596pub fn XMMatrixReflect(
2597    ReflectionPlane: FXMVECTOR
2598) -> XMMATRIX
2599{
2600    unsafe {
2601        debug_assert!(!XMVector3Equal(ReflectionPlane, XMVectorZero()));
2602        debug_assert!(!XMPlaneIsInfinite(ReflectionPlane));
2603
2604        const NegativeTwo: XMVECTORF32 = XMVECTORF32 { f: [ -2.0, -2.0, -2.0, 0.0 ] };
2605
2606        let P: XMVECTOR = XMPlaneNormalize(ReflectionPlane);
2607        let S: XMVECTOR = XMVectorMultiply(P, NegativeTwo.v);
2608
2609        let A: XMVECTOR = XMVectorSplatX(P);
2610        let B: XMVECTOR = XMVectorSplatY(P);
2611        let C: XMVECTOR = XMVectorSplatZ(P);
2612        let D: XMVECTOR = XMVectorSplatW(P);
2613
2614        let mut M: XMMATRIX = undefined();
2615        M.r[0] = XMVectorMultiplyAdd(A, S, g_XMIdentityR0.v);
2616        M.r[1] = XMVectorMultiplyAdd(B, S, g_XMIdentityR1.v);
2617        M.r[2] = XMVectorMultiplyAdd(C, S, g_XMIdentityR2.v);
2618        M.r[3] = XMVectorMultiplyAdd(D, S, g_XMIdentityR3.v);
2619        return M;
2620    }
2621}
2622
2623/// Builds a transformation matrix that flattens geometry into a plane.
2624///
2625/// ## Parameters
2626///
2627/// `ShadowPlane` Reference plane.
2628///
2629/// `LightPosition` 4D vector describing the light's position. If the light's w-component is `0.0`, the ray from the origin
2630/// to the light represents a directional light. If it is `1.0`, the light is a point light.
2631///
2632/// ## Return value
2633///
2634/// Returns the transformation matrix that flattens the geometry into the plane ShadowPlane.
2635///
2636/// ## Remarks
2637///
2638/// This function is useful for forming planar-projected shadows from a light source.
2639///
2640/// ## Reference
2641///
2642/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixShadow>
2643#[inline]
2644pub fn XMMatrixShadow(
2645    ShadowPlane: FXMVECTOR,
2646    LightPosition: FXMVECTOR,
2647) -> XMMATRIX
2648{
2649    unsafe {
2650        const Select0001: XMVECTORU32 = XMVECTORU32 { u: [ XM_SELECT_0, XM_SELECT_0, XM_SELECT_0, XM_SELECT_1 ] };
2651
2652        debug_assert!(!XMVector3Equal(ShadowPlane, XMVectorZero()));
2653        debug_assert!(!XMPlaneIsInfinite(ShadowPlane));
2654
2655        let mut P: XMVECTOR = XMPlaneNormalize(ShadowPlane);
2656        let mut Dot: XMVECTOR = XMPlaneDot(P, LightPosition);
2657        P = XMVectorNegate(P);
2658        let D: XMVECTOR = XMVectorSplatW(P);
2659        let C: XMVECTOR = XMVectorSplatZ(P);
2660        let B: XMVECTOR = XMVectorSplatY(P);
2661        let A: XMVECTOR = XMVectorSplatX(P);
2662        Dot = XMVectorSelect(Select0001.v, Dot, Select0001.v);
2663
2664        let mut M: XMMATRIX = undefined();
2665        M.r[3] = XMVectorMultiplyAdd(D, LightPosition, Dot);
2666        Dot = XMVectorRotateLeft(Dot, 1);
2667        M.r[2] = XMVectorMultiplyAdd(C, LightPosition, Dot);
2668        Dot = XMVectorRotateLeft(Dot, 1);
2669        M.r[1] = XMVectorMultiplyAdd(B, LightPosition, Dot);
2670        Dot = XMVectorRotateLeft(Dot, 1);
2671        M.r[0] = XMVectorMultiplyAdd(A, LightPosition, Dot);
2672        return M;
2673    }
2674}
2675
2676/// Builds a view matrix for a left-handed coordinate system using a camera position, an up direction, and a focal point.
2677///
2678/// ## Parameters
2679///
2680/// `EyePosition` Position of the camera.
2681///
2682/// `FocusPosition` Position of the focal point.
2683///
2684/// `UpDirection` Up direction of the camera, typically < `0.0`, `1.0`, `0.0` >.
2685///
2686/// ## Return value
2687///
2688/// Returns a view matrix that transforms a point from world space into view space.
2689///
2690/// ## Reference
2691///
2692/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixLookAtLH>
2693#[inline]
2694pub fn XMMatrixLookAtLH(
2695    EyePosition: FXMVECTOR,
2696    FocusPosition: FXMVECTOR,
2697    UpDirection: FXMVECTOR,
2698) -> XMMATRIX
2699{
2700    let EyeDirection: XMVECTOR = XMVectorSubtract(FocusPosition, EyePosition);
2701    return XMMatrixLookToLH(EyePosition, EyeDirection, UpDirection);
2702}
2703
2704/// Builds a view matrix for a right-handed coordinate system using a camera position, an up direction, and a focal point.
2705///
2706/// ## Parameters
2707///
2708/// `EyePosition` Position of the camera.
2709///
2710/// `FocusPosition` Position of the focal point.
2711///
2712/// `UpDirection` Up direction of the camera, typically < `0.0`, `1.0`, `0.0` >.
2713///
2714/// ## Return value
2715///
2716/// Returns a view matrix that transforms a point from world space into view space.
2717///
2718/// ## Reference
2719///
2720/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixLookAtRH>
2721#[inline]
2722pub fn XMMatrixLookAtRH(
2723    EyePosition: FXMVECTOR,
2724    FocusPosition: FXMVECTOR,
2725    UpDirection: FXMVECTOR,
2726) -> XMMATRIX
2727{
2728    let NegEyeDirection: XMVECTOR = XMVectorSubtract(EyePosition, FocusPosition);
2729    return XMMatrixLookToLH(EyePosition, NegEyeDirection, UpDirection);
2730}
2731
2732/// Builds a view matrix for a left-handed coordinate system using a camera position, an up direction, and a camera direction.
2733///
2734/// ## Parameters
2735///
2736/// `EyePosition` Position of the camera.
2737///
2738/// `EyeDirection` Direction of the camera.
2739///
2740/// `UpDirection` Up direction of the camera, typically < `0.0`, `1.0`, `0.0` >.
2741///
2742/// ## Return value
2743///
2744/// Returns a view matrix that transforms a point from world space into view space.
2745///
2746/// ## Reference
2747///
2748/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixLookToLH>
2749#[inline]
2750pub fn XMMatrixLookToLH(
2751    EyePosition: FXMVECTOR,
2752    EyeDirection: FXMVECTOR,
2753    UpDirection: FXMVECTOR,
2754) -> XMMATRIX
2755{
2756    unsafe {
2757        debug_assert!(!XMVector3Equal(EyeDirection, XMVectorZero()));
2758        debug_assert!(!XMVector3IsInfinite(EyeDirection));
2759        debug_assert!(!XMVector3Equal(UpDirection, XMVectorZero()));
2760        debug_assert!(!XMVector3IsInfinite(UpDirection));
2761
2762        let R2: XMVECTOR = XMVector3Normalize(EyeDirection);
2763
2764        let mut R0: XMVECTOR = XMVector3Cross(UpDirection, R2);
2765        R0 = XMVector3Normalize(R0);
2766
2767        let R1: XMVECTOR = XMVector3Cross(R2, R0);
2768
2769        let NegEyePosition: XMVECTOR = XMVectorNegate(EyePosition);
2770
2771        let D0: XMVECTOR = XMVector3Dot(R0, NegEyePosition);
2772        let D1: XMVECTOR = XMVector3Dot(R1, NegEyePosition);
2773        let D2: XMVECTOR = XMVector3Dot(R2, NegEyePosition);
2774
2775        let mut M: XMMATRIX = crate::undefined();
2776        M.r[0] = XMVectorSelect(D0, R0, g_XMSelect1110.v);
2777        M.r[1] = XMVectorSelect(D1, R1, g_XMSelect1110.v);
2778        M.r[2] = XMVectorSelect(D2, R2, g_XMSelect1110.v);
2779        M.r[3] = g_XMIdentityR3.v;
2780
2781        M = XMMatrixTranspose(M);
2782
2783        return M;
2784    }
2785}
2786
2787/// Builds a view matrix for a right-handed coordinate system using a camera position, an up direction, and a camera direction.
2788///
2789/// ## Parameters
2790///
2791/// `EyePosition` Position of the camera.
2792///
2793/// `EyeDirection` Direction of the camera.
2794///
2795/// `UpDirection` Up direction of the camera, typically < `0.0`, `1.0`, `0.0` >.
2796///
2797/// ## Return value
2798///
2799/// Returns a view matrix that transforms a point from world space into view space.
2800///
2801/// ## Reference
2802///
2803/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixLookToRH>
2804#[inline]
2805pub fn XMMatrixLookToRH(
2806    EyePosition: FXMVECTOR,
2807    EyeDirection: FXMVECTOR,
2808    UpDirection: FXMVECTOR,
2809) -> XMMATRIX
2810{
2811    let NegEyeDirection: XMVECTOR = XMVectorNegate(EyeDirection);
2812    return XMMatrixLookToLH(EyePosition, NegEyeDirection, UpDirection);
2813}
2814
2815/// Builds a left-handed perspective projection matrix.
2816///
2817/// ## Parameters
2818///
2819/// `ViewWidth` Width of the frustum at the near clipping plane.
2820///
2821/// `ViewHeight` Height of the frustum at the near clipping plane.
2822///
2823/// `NearZ` Distance to the near clipping plane. Must be greater than zero.
2824///
2825/// `FarZ` Distance to the far clipping plane. Must be greater than zero.
2826///
2827/// ## Return value
2828///
2829/// Returns the perspective projection matrix.
2830///
2831/// ## Remarks
2832///
2833/// For typical usage, `NearZ` is less than `FarZ`. However, if you flip these values so `FarZ` is less than `NearZ`,
2834/// the result is an inverted `z` buffer which can provide increased floating-point precision. `NearZ` and `FarZ`
2835/// cannot be the same value.
2836///
2837/// ## Reference
2838///
2839/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixPerspectiveLH>
2840#[inline]
2841pub fn XMMatrixPerspectiveLH(
2842    ViewWidth: f32,
2843    ViewHeight: f32,
2844    NearZ: f32,
2845    FarZ: f32
2846) -> XMMATRIX
2847{
2848    debug_assert!(NearZ > 0.0 && FarZ > 0.0);
2849    debug_assert!(!XMScalarNearEqual(ViewWidth, 0.0, 0.00001));
2850    debug_assert!(!XMScalarNearEqual(ViewHeight, 0.0, 0.00001));
2851    debug_assert!(!XMScalarNearEqual(FarZ, NearZ, 0.00001));
2852
2853    #[cfg(_XM_NO_INTRINSICS_)]
2854    unsafe {
2855        let TwoNearZ: f32 = NearZ + NearZ;
2856        let fRange: f32 = FarZ / (FarZ - NearZ);
2857
2858        let mut M: XMMATRIX = crate::undefined();
2859        M.m[0][0] = TwoNearZ / ViewWidth;
2860        M.m[0][1] = 0.0;
2861        M.m[0][2] = 0.0;
2862        M.m[0][3] = 0.0;
2863
2864        M.m[1][0] = 0.0;
2865        M.m[1][1] = TwoNearZ / ViewHeight;
2866        M.m[1][2] = 0.0;
2867        M.m[1][3] = 0.0;
2868
2869        M.m[2][0] = 0.0;
2870        M.m[2][1] = 0.0;
2871        M.m[2][2] = fRange;
2872        M.m[2][3] = 1.0;
2873
2874        M.m[3][0] = 0.0;
2875        M.m[3][1] = 0.0;
2876        M.m[3][2] = -fRange * NearZ;
2877        M.m[3][3] = 0.0;
2878        return M;
2879    }
2880
2881    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
2882    {
2883        unimplemented!()
2884    }
2885
2886    #[cfg(_XM_SSE_INTRINSICS_)]
2887    unsafe {
2888        let mut M: XMMATRIX = crate::undefined();
2889        let TwoNearZ: f32 = NearZ + NearZ;
2890        let fRange: f32 = FarZ / (FarZ - NearZ);
2891        // Note: This is recorded on the stack
2892        let rMem: XMVECTORF32 = XMVECTORF32 { f: [
2893            TwoNearZ / ViewWidth,
2894            TwoNearZ / ViewHeight,
2895            fRange,
2896            -fRange * NearZ
2897        ]};
2898        // Copy from memory to SSE register
2899        let mut vValues: XMVECTOR = rMem.v;
2900        let mut vTemp: XMVECTOR = _mm_setzero_ps();
2901        // Copy x only
2902        vTemp = _mm_move_ss(vTemp, vValues);
2903        // TwoNearZ / ViewWidth,0,0,0
2904        M.r[0] = vTemp;
2905        // 0,TwoNearZ / ViewHeight,0,0
2906        vTemp = vValues;
2907        vTemp = _mm_and_ps(vTemp, *g_XMMaskY);
2908        M.r[1] = vTemp;
2909        // x=fRange,y=-fRange * NearZ,0,1.0f
2910        vValues = _mm_shuffle_ps(vValues, *g_XMIdentityR3, _MM_SHUFFLE(3, 2, 3, 2));
2911        // 0,0,fRange,1.0f
2912        vTemp = _mm_setzero_ps();
2913        vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(3, 0, 0, 0));
2914        M.r[2] = vTemp;
2915        // 0,0,-fRange * NearZ,0
2916        vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(2, 1, 0, 0));
2917        M.r[3] = vTemp;
2918        return M;
2919    }
2920}
2921
2922/// Builds a right-handed perspective projection matrix.
2923///
2924/// ## Parameters
2925///
2926/// `ViewWidth` Width of the frustum at the near clipping plane.
2927///
2928/// `ViewHeight` Height of the frustum at the near clipping plane.
2929///
2930/// `NearZ` Distance to the near clipping plane. Must be greater than zero.
2931///
2932/// `FarZ` Distance to the far clipping plane. Must be greater than zero.
2933///
2934/// ## Return value
2935///
2936/// Returns the perspective projection matrix.
2937///
2938/// ## Remarks
2939///
2940/// For typical usage, `NearZ` is less than `FarZ`. However, if you flip these values so `FarZ` is less than `NearZ`,
2941/// the result is an inverted `z` buffer which can provide increased floating-point precision. `NearZ` and `FarZ`
2942/// cannot be the same value.
2943///
2944/// ## Reference
2945///
2946/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixPerspectiveRH>
2947#[inline]
2948pub fn XMMatrixPerspectiveRH(
2949    ViewWidth: f32,
2950    ViewHeight: f32,
2951    NearZ: f32,
2952    FarZ: f32
2953) -> XMMATRIX
2954{
2955    debug_assert!(NearZ > 0.0 && FarZ > 0.0);
2956    debug_assert!(!XMScalarNearEqual(ViewWidth, 0.0, 0.00001));
2957    debug_assert!(!XMScalarNearEqual(ViewHeight, 0.0, 0.00001));
2958    debug_assert!(!XMScalarNearEqual(FarZ, NearZ, 0.00001));
2959
2960    #[cfg(_XM_NO_INTRINSICS_)]
2961    unsafe {
2962        let TwoNearZ: f32 = NearZ + NearZ;
2963        let fRange: f32 = FarZ / (NearZ - FarZ);
2964
2965        let mut M: XMMATRIX = crate::undefined();
2966        M.m[0][0] = TwoNearZ / ViewWidth;
2967        M.m[0][1] = 0.0;
2968        M.m[0][2] = 0.0;
2969        M.m[0][3] = 0.0;
2970
2971        M.m[1][0] = 0.0;
2972        M.m[1][1] = TwoNearZ / ViewHeight;
2973        M.m[1][2] = 0.0;
2974        M.m[1][3] = 0.0;
2975
2976        M.m[2][0] = 0.0;
2977        M.m[2][1] = 0.0;
2978        M.m[2][2] = fRange;
2979        M.m[2][3] = -1.0;
2980
2981        M.m[3][0] = 0.0;
2982        M.m[3][1] = 0.0;
2983        M.m[3][2] = fRange * NearZ;
2984        M.m[3][3] = 0.0;
2985        return M;
2986    }
2987
2988    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
2989    {
2990        unimplemented!()
2991    }
2992
2993    #[cfg(_XM_SSE_INTRINSICS_)]
2994    unsafe {
2995        let mut M: XMMATRIX = crate::undefined();
2996        let TwoNearZ: f32 = NearZ + NearZ;
2997        let fRange: f32 = FarZ / (NearZ - FarZ);
2998        // Note: This is recorded on the stack
2999        let rMem: XMVECTORF32 = XMVECTORF32 { f: [
3000            TwoNearZ / ViewWidth,
3001            TwoNearZ / ViewHeight,
3002            fRange,
3003            fRange * NearZ
3004        ]};
3005        // Copy from memory to SSE register
3006        let mut vValues: XMVECTOR = rMem.v;
3007        let mut vTemp: XMVECTOR = _mm_setzero_ps();
3008        // Copy x only
3009        vTemp = _mm_move_ss(vTemp, vValues);
3010        // TwoNearZ / ViewWidth,0,0,0
3011        M.r[0] = vTemp;
3012        // 0,TwoNearZ / ViewHeight,0,0
3013        vTemp = vValues;
3014        vTemp = _mm_and_ps(vTemp, *g_XMMaskY);
3015        M.r[1] = vTemp;
3016        // x=fRange,y=-fRange * NearZ,0,-1.0f
3017        vValues = _mm_shuffle_ps(vValues, *g_XMNegIdentityR3, _MM_SHUFFLE(3, 2, 3, 2));
3018        // 0,0,fRange,-1.0f
3019        vTemp = _mm_setzero_ps();
3020        vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(3, 0, 0, 0));
3021        M.r[2] = vTemp;
3022        // 0,0,-fRange * NearZ,0
3023        vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(2, 1, 0, 0));
3024        M.r[3] = vTemp;
3025        return M;
3026    }
3027}
3028
3029/// Builds a left-handed perspective projection matrix based on a field of view.
3030///
3031/// ## Parameters
3032///
3033/// `FovAngleY` Top-down field-of-view angle in radians.
3034///
3035/// `AspectRatio` Aspect ratio of view-space `X:Y`.
3036///
3037/// `NearZ` Distance to the near clipping plane. Must be greater than zero.
3038///
3039/// `FarZ` Distance to the far clipping plane. Must be greater than zero.
3040///
3041/// ## Return value
3042///
3043/// Returns the perspective projection matrix.
3044///
3045/// ## Remarks
3046///
3047/// For typical usage, `NearZ` is less than `FarZ`. However, if you flip these values so `FarZ` is less than `NearZ`,
3048/// the result is an inverted `z` buffer which can provide increased floating-point precision. `NearZ` and `FarZ`
3049/// cannot be the same value.
3050///
3051/// ## Reference
3052///
3053/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixPerspectiveFovLH>
3054#[inline]
3055pub fn XMMatrixPerspectiveFovLH(
3056    FovAngleY: f32,
3057    AspectRatio: f32,
3058    NearZ: f32,
3059    FarZ: f32
3060) -> XMMATRIX
3061{
3062    debug_assert!(NearZ > 0.0 && FarZ > 0.0);
3063    debug_assert!(!XMScalarNearEqual(FovAngleY, 0.0, 0.00001 * 2.0));
3064    debug_assert!(!XMScalarNearEqual(AspectRatio, 0.0, 0.00001));
3065    debug_assert!(!XMScalarNearEqual(FarZ, NearZ, 0.00001));
3066
3067    #[cfg(_XM_NO_INTRINSICS_)]
3068    unsafe {
3069        let mut SinFov: f32 = 0.0;
3070        let mut CosFov: f32 = 0.0;
3071        XMScalarSinCos(&mut SinFov, &mut CosFov, 0.5 * FovAngleY);
3072
3073        let Height: f32 = CosFov / SinFov;
3074        let Width: f32 = Height / AspectRatio;
3075        let fRange: f32 = FarZ / (FarZ - NearZ);
3076
3077        let mut M: XMMATRIX = crate::undefined();
3078        M.m[0][0] = Width;
3079        M.m[0][1] = 0.0;
3080        M.m[0][2] = 0.0;
3081        M.m[0][3] = 0.0;
3082
3083        M.m[1][0] = 0.0;
3084        M.m[1][1] = Height;
3085        M.m[1][2] = 0.0;
3086        M.m[1][3] = 0.0;
3087
3088        M.m[2][0] = 0.0;
3089        M.m[2][1] = 0.0;
3090        M.m[2][2] = fRange;
3091        M.m[2][3] = 1.0;
3092
3093        M.m[3][0] = 0.0;
3094        M.m[3][1] = 0.0;
3095        M.m[3][2] = -fRange * NearZ;
3096        M.m[3][3] = 0.0;
3097        return M;
3098    }
3099
3100    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
3101    {
3102        unimplemented!()
3103    }
3104
3105    #[cfg(_XM_SSE_INTRINSICS_)]
3106    unsafe {
3107        let mut SinFov: f32 = 0.0;
3108        let mut CosFov: f32 = 0.0;
3109        XMScalarSinCos(&mut SinFov, &mut CosFov, 0.5 * FovAngleY);
3110
3111        let fRange: f32 = FarZ / (FarZ - NearZ);
3112        // Note: This is recorded on the stack
3113        let Height: f32 = CosFov / SinFov;
3114        let rMem: XMVECTORF32 = XMVECTORF32 { f: [
3115            Height / AspectRatio,
3116            Height,
3117            fRange,
3118            -fRange * NearZ
3119        ]};
3120        // Copy from memory to SSE register
3121        let mut vValues: XMVECTOR = rMem.v;
3122        let mut vTemp: XMVECTOR = _mm_setzero_ps();
3123        // Copy x only
3124        vTemp = _mm_move_ss(vTemp, vValues);
3125        // CosFov / SinFov,0,0,0
3126        let mut M: XMMATRIX = crate::undefined();
3127        M.r[0] = vTemp;
3128        // 0,Height / AspectRatio,0,0
3129        vTemp = vValues;
3130        vTemp = _mm_and_ps(vTemp, *g_XMMaskY);
3131        M.r[1] = vTemp;
3132        // x=fRange,y=-fRange * NearZ,0,1.0f
3133        vTemp = _mm_setzero_ps();
3134        vValues = _mm_shuffle_ps(vValues, *g_XMIdentityR3, _MM_SHUFFLE(3, 2, 3, 2));
3135        // 0,0,fRange,1.0f
3136        vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(3, 0, 0, 0));
3137        M.r[2] = vTemp;
3138        // 0,0,-fRange * NearZ,0.0f
3139        vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(2, 1, 0, 0));
3140        M.r[3] = vTemp;
3141        return M;
3142    }
3143}
3144
3145
3146/// Builds a right-handed perspective projection matrix based on a field of view.
3147///
3148/// ## Parameters
3149///
3150/// `FovAngleY` Top-down field-of-view angle in radians.
3151///
3152/// `AspectRatio` Aspect ratio of view-space `X:Y`.
3153///
3154/// `NearZ` Distance to the near clipping plane. Must be greater than zero.
3155///
3156/// `FarZ` Distance to the far clipping plane. Must be greater than zero.
3157///
3158/// ## Return value
3159///
3160/// Returns the perspective projection matrix.
3161///
3162/// ## Remarks
3163///
3164/// For typical usage, `NearZ` is less than `FarZ`. However, if you flip these values so `FarZ` is less than `NearZ`,
3165/// the result is an inverted `z` buffer which can provide increased floating-point precision. `NearZ` and `FarZ`
3166/// cannot be the same value.
3167///
3168/// ## Reference
3169///
3170/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixPerspectiveFovRH>
3171#[inline]
3172pub fn XMMatrixPerspectiveFovRH(
3173    FovAngleY: f32,
3174    AspectRatio: f32,
3175    NearZ: f32,
3176    FarZ: f32
3177) -> XMMATRIX
3178{
3179    debug_assert!(NearZ > 0.0 && FarZ > 0.0);
3180    debug_assert!(!XMScalarNearEqual(FovAngleY, 0.0, 0.00001 * 2.0));
3181    debug_assert!(!XMScalarNearEqual(AspectRatio, 0.0, 0.00001));
3182    debug_assert!(!XMScalarNearEqual(FarZ, NearZ, 0.00001));
3183
3184    #[cfg(_XM_NO_INTRINSICS_)]
3185    unsafe {
3186        let mut SinFov: f32 = 0.0;
3187        let mut CosFov: f32 = 0.0;
3188        XMScalarSinCos(&mut SinFov, &mut CosFov, 0.5 * FovAngleY);
3189
3190        let Height: f32 = CosFov / SinFov;
3191        let Width: f32 = Height / AspectRatio;
3192        let fRange: f32 = FarZ / (NearZ - FarZ);
3193
3194        let mut M: XMMATRIX = crate::undefined();
3195        M.m[0][0] = Width;
3196        M.m[0][1] = 0.0;
3197        M.m[0][2] = 0.0;
3198        M.m[0][3] = 0.0;
3199
3200        M.m[1][0] = 0.0;
3201        M.m[1][1] = Height;
3202        M.m[1][2] = 0.0;
3203        M.m[1][3] = 0.0;
3204
3205        M.m[2][0] = 0.0;
3206        M.m[2][1] = 0.0;
3207        M.m[2][2] = fRange;
3208        M.m[2][3] = -1.0;
3209
3210        M.m[3][0] = 0.0;
3211        M.m[3][1] = 0.0;
3212        M.m[3][2] = fRange * NearZ;
3213        M.m[3][3] = 0.0;
3214        return M;
3215    }
3216
3217    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
3218    {
3219        unimplemented!()
3220    }
3221
3222    #[cfg(_XM_SSE_INTRINSICS_)]
3223    unsafe {
3224        let mut SinFov: f32 = 0.0;
3225        let mut CosFov: f32 = 0.0;
3226        XMScalarSinCos(&mut SinFov, &mut CosFov, 0.5 * FovAngleY);
3227        let fRange: f32 = FarZ / (NearZ - FarZ);
3228        let Height: f32 = CosFov / SinFov;
3229        // Note: This is recorded on the stack
3230        let rMem: XMVECTORF32 = XMVECTORF32 { f: [
3231            Height / AspectRatio,
3232            Height,
3233            fRange,
3234            fRange * NearZ
3235        ]};
3236        // Copy from memory to SSE register
3237        let mut vValues: XMVECTOR = rMem.v;
3238        let mut vTemp: XMVECTOR = _mm_setzero_ps();
3239        // Copy x only
3240        vTemp = _mm_move_ss(vTemp, vValues);
3241        // CosFov / SinFov,0,0,0
3242        let mut M: XMMATRIX = crate::undefined();
3243        M.r[0] = vTemp;
3244        // 0,Height / AspectRatio,0,0
3245        vTemp = vValues;
3246        vTemp = _mm_and_ps(vTemp, *g_XMMaskY);
3247        M.r[1] = vTemp;
3248        // x=fRange,y=-fRange * NearZ,0,-1.0f
3249        vTemp = _mm_setzero_ps();
3250        vValues = _mm_shuffle_ps(vValues, *g_XMNegIdentityR3, _MM_SHUFFLE(3, 2, 3, 2));
3251        // 0,0,fRange,-1.0f
3252        vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(3, 0, 0, 0));
3253        M.r[2] = vTemp;
3254        // 0,0,fRange * NearZ,0.0f
3255        vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(2, 1, 0, 0));
3256        M.r[3] = vTemp;
3257        return M;
3258    }
3259}
3260
3261/// Builds a custom version of a left-handed perspective projection matrix.
3262/// 
3263/// ## Parameters
3264/// 
3265/// `ViewLeft` The x-coordinate of the left side of the clipping frustum at the near clipping plane.
3266/// 
3267/// `ViewRight` The x-coordinate of the right side of the clipping frustum at the near clipping plane.
3268/// 
3269/// `ViewBottom` The y-coordinate of the bottom side of the clipping frustum at the near clipping plane.
3270/// 
3271/// `ViewTop` The y-coordinate of the top side of the clipping frustum at the near clipping plane.
3272/// 
3273/// `NearZ` Distance to the near clipping plane. Must be greater than zero.
3274/// 
3275/// `FarZ` Distance to the far clipping plane. Must be greater than zero.
3276/// 
3277/// ## Return Values
3278/// 
3279/// Returns the custom perspective projection matrix.
3280/// 
3281/// ## Remarks
3282/// 
3283/// For typical usage, NearZ is less than FarZ. However, if you flip these values so FarZ is less than NearZ, 
3284/// the result is an inverted z buffer (also known as a "reverse z buffer") which can provide increased floating-point precision.
3285///
3286/// NearZ and FarZ cannot be the same value and must be greater than 0.
3287/// 
3288/// ## Reference
3289/// 
3290/// <https://learn.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-xmmatrixperspectiveoffcenterlh>
3291#[inline]
3292pub fn XMMatrixPerspectiveOffCenterLH(
3293    ViewLeft: f32,
3294    ViewRight: f32,
3295    ViewBottom: f32,
3296    ViewTop: f32,
3297    NearZ: f32,
3298    FarZ: f32,
3299) -> XMMATRIX {
3300    debug_assert!(NearZ > 0.0 && FarZ > 0.0);
3301    debug_assert!(!XMScalarNearEqual(ViewRight, ViewLeft, 0.00001));
3302    debug_assert!(!XMScalarNearEqual(ViewTop, ViewBottom, 0.00001));
3303    debug_assert!(!XMScalarNearEqual(FarZ, NearZ, 0.00001));
3304
3305    #[cfg(_XM_NO_INTRINSICS_)]
3306    unsafe {
3307        let TwoNearZ = NearZ + NearZ;
3308        let ReciprocalWidth = 1.0 / (ViewRight - ViewLeft);
3309        let ReciprocalHeight = 1.0 / (ViewTop - ViewBottom);
3310        let fRange = FarZ / (FarZ - NearZ);
3311
3312        let mut M: XMMATRIX = crate::undefined();
3313        M.m[0][0] = TwoNearZ * ReciprocalWidth;
3314        M.m[0][1] = 0.0;
3315        M.m[0][2] = 0.0;
3316        M.m[0][3] = 0.0;
3317
3318        M.m[1][0] = 0.0;
3319        M.m[1][1] = TwoNearZ * ReciprocalHeight;
3320        M.m[1][2] = 0.0;
3321        M.m[1][3] = 0.0;
3322        
3323        M.m[2][0] = -(ViewLeft + ViewRight) * ReciprocalWidth;
3324        M.m[2][1] = -(ViewTop + ViewBottom) * ReciprocalHeight;
3325        M.m[2][2] = fRange;
3326        M.m[2][3] = 1.0;
3327
3328        M.m[3][0] = 0.0;
3329        M.m[3][1] = 0.0;
3330        M.m[3][2] = -fRange * NearZ;
3331        M.m[3][3] = 0.0;
3332        return M;
3333    }
3334
3335    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
3336    {
3337        unimplemented!()
3338    }
3339
3340    #[cfg(_XM_SSE_INTRINSICS_)]
3341    unsafe {
3342        let mut M: XMMATRIX = crate::undefined();
3343        let TwoNearZ = NearZ + NearZ;
3344        let ReciprocalWidth = 1.0 / (ViewRight - ViewLeft);
3345        let ReciprocalHeight = 1.0 / (ViewTop - ViewBottom);
3346        let fRange = FarZ / (FarZ - NearZ);
3347        // Note: This is recorded on the stack
3348        let rMem: XMVECTORF32 = XMVECTORF32 { f: [
3349            TwoNearZ * ReciprocalWidth,
3350            TwoNearZ * ReciprocalHeight,
3351            -fRange * NearZ,
3352            0.0,
3353        ]};
3354        // Copy from memory to SSE register
3355        let mut vValues: XMVECTOR = rMem.v;
3356        let mut vTemp: XMVECTOR = _mm_setzero_ps();
3357        // Copy x only
3358        vTemp = _mm_move_ss(vTemp, vValues);
3359        // TwoNearZ * ReciprocalWidth,0,0,0
3360        M.r[0] = vTemp;
3361        // 0,TwoNearZ * ReciprocalHeight,0,0
3362        vTemp = vValues;
3363        vTemp = _mm_and_ps(vTemp, *g_XMMaskY);
3364        M.r[1] = vTemp;
3365        // 0,0,fRange,1.0f
3366        M.r[2] = XMVectorSet(-(ViewLeft + ViewRight) * ReciprocalWidth, 
3367            -(ViewTop + ViewBottom) * ReciprocalHeight, 
3368            fRange, 
3369            1.0);
3370        // 0,0,-fRange * NearZ,0.0f
3371        vValues = _mm_and_ps(vValues, *g_XMMaskZ);
3372        M.r[3] = vValues;
3373        return M;
3374    }
3375}
3376
3377
3378/// Builds a custom version of a right-handed perspective projection matrix.
3379/// 
3380/// ## Parameters
3381/// 
3382/// `ViewLeft` The x-coordinate of the left side of the clipping frustum at the near clipping plane.
3383/// 
3384/// `ViewRight` The x-coordinate of the right side of the clipping frustum at the near clipping plane.
3385/// 
3386/// `ViewBottom` The y-coordinate of the bottom side of the clipping frustum at the near clipping plane.
3387/// 
3388/// `ViewTop` The y-coordinate of the top side of the clipping frustum at the near clipping plane.
3389/// 
3390/// `NearZ` Distance to the near clipping plane. Must be greater than zero.
3391/// 
3392/// `FarZ` Distance to the far clipping plane. Must be greater than zero.
3393/// 
3394/// ## Return value
3395/// 
3396/// Returns the custom perspective projection matrix.
3397/// 
3398/// ## Remarks
3399/// 
3400/// For typical usage, NearZ is less than FarZ. However, if you flip these values so FarZ is less than NearZ,
3401/// the result is an inverted z buffer (also known as a "reverse z buffer") which can provide increased floating-point precision.
3402///
3403/// NearZ and FarZ cannot be the same value and must be greater than 0.
3404/// 
3405/// ## Reference
3406/// 
3407/// <https://learn.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-xmmatrixperspectiveoffcenterrh>
3408#[inline]
3409pub fn XMMatrixPerspectiveOffCenterRH(
3410    ViewLeft: f32,
3411    ViewRight: f32,
3412    ViewBottom: f32,
3413    ViewTop: f32,
3414    NearZ: f32,
3415    FarZ: f32,
3416) -> XMMATRIX {
3417    debug_assert!(NearZ > 0.0 && FarZ > 0.0);
3418    debug_assert!(!XMScalarNearEqual(ViewRight, ViewLeft, 0.00001));
3419    debug_assert!(!XMScalarNearEqual(ViewTop, ViewBottom, 0.00001));
3420    debug_assert!(!XMScalarNearEqual(FarZ, NearZ, 0.00001));
3421
3422    #[cfg(_XM_NO_INTRINSICS_)]
3423    unsafe {
3424        let TwoNearZ = NearZ + NearZ;
3425        let ReciprocalWidth = 1.0 / (ViewRight - ViewLeft);
3426        let ReciprocalHeight = 1.0 / (ViewTop - ViewBottom);
3427        let fRange = FarZ / (NearZ - FarZ);
3428
3429        let mut M: XMMATRIX = crate::undefined();
3430        M.m[0][0] = TwoNearZ * ReciprocalWidth;
3431        M.m[0][1] = 0.0;
3432        M.m[0][2] = 0.0;
3433        M.m[0][3] = 0.0;
3434
3435        M.m[1][0] = 0.0;
3436        M.m[1][1] = TwoNearZ * ReciprocalHeight;
3437        M.m[1][2] = 0.0;
3438        M.m[1][3] = 0.0;
3439
3440        M.m[2][0] = (ViewLeft + ViewRight) * ReciprocalWidth;
3441        M.m[2][1] = (ViewTop + ViewBottom) * ReciprocalHeight;
3442        M.m[2][2] = fRange;
3443        M.m[2][3] = -1.0;
3444
3445        M.m[3][0] = 0.0;
3446        M.m[3][1] = 0.0;
3447        M.m[3][2] = fRange * NearZ;
3448        M.m[3][3] = 0.0;
3449        return M;
3450    }
3451
3452    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
3453    {
3454        unimplemented!()
3455    }
3456
3457    #[cfg(_XM_SSE_INTRINSICS_)]
3458    unsafe {
3459        let mut M: XMMATRIX = crate::undefined();
3460        let TwoNearZ = NearZ + NearZ;
3461        let ReciprocalWidth = 1.0 / (ViewRight - ViewLeft);
3462        let ReciprocalHeight = 1.0 / (ViewTop - ViewBottom);
3463        let fRange = FarZ / (NearZ - FarZ);
3464        // Note: This is recorded on the stack
3465        let rMem: XMVECTORF32 = XMVECTORF32 { f: [
3466            TwoNearZ * ReciprocalWidth,
3467            TwoNearZ * ReciprocalHeight,
3468            fRange * NearZ,
3469            0.0,
3470        ]};
3471        // Copy from memory to SSE register
3472        let mut vValues: XMVECTOR = rMem.v;
3473        let mut vTemp: XMVECTOR = _mm_setzero_ps();
3474        // Copy x only
3475        vTemp = _mm_move_ss(vTemp, vValues);
3476        // TwoNearZ*ReciprocalWidth,0,0,0
3477        M.r[0] = vTemp;
3478        // 0,TwoNearZ*ReciprocalHeight,0,0
3479        vTemp = vValues;
3480        vTemp = _mm_and_ps(vTemp, *g_XMMaskY);
3481        M.r[1] = vTemp;
3482        // 0,0,fRange,1.0f
3483        M.r[2] = XMVectorSet((ViewLeft + ViewRight) * ReciprocalWidth,
3484            (ViewTop + ViewBottom) * ReciprocalHeight,
3485            fRange,
3486            -1.0);
3487        // 0,0,-fRange * NearZ,0.0f
3488        vValues = _mm_and_ps(vValues, *g_XMMaskZ);
3489        M.r[3] = vValues;
3490        return M;
3491    }
3492}
3493
3494/// Builds an orthogonal projection matrix for a left-handed coordinate system.
3495///
3496/// ## Parameters
3497///
3498/// `ViewWidth` Width of the frustum at the near clipping plane.
3499///
3500/// `ViewHeight` Height of the frustum at the near clipping plane.
3501///
3502/// `NearZ` Distance to the near clipping plane.
3503///
3504/// `FarZ` Distance to the far clipping plane.
3505///
3506/// ## Return value
3507///
3508/// Returns the orthogonal projection matrix.
3509///
3510/// ## Remarks
3511///
3512/// All the parameters of XMMatrixOrthographicLH are distances in camera space.
3513///
3514/// ## Reference
3515///
3516/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixOrthographicLH>
3517#[inline]
3518pub fn XMMatrixOrthographicLH(
3519    ViewWidth: f32,
3520    ViewHeight: f32,
3521    NearZ: f32,
3522    FarZ: f32
3523) -> XMMATRIX
3524{
3525    debug_assert!(!XMScalarNearEqual(ViewWidth, 0.0, 0.00001));
3526    debug_assert!(!XMScalarNearEqual(ViewHeight, 0.0, 0.00001));
3527    debug_assert!(!XMScalarNearEqual(FarZ, NearZ, 0.00001));
3528
3529    #[cfg(_XM_NO_INTRINSICS_)]
3530    unsafe {
3531        let fRange: f32 = 1.0 / (FarZ - NearZ);
3532        let mut M: XMMATRIX = crate::undefined();
3533        M.m[0][0] = 2.0 / ViewWidth;
3534        M.m[0][1] = 0.0;
3535        M.m[0][2] = 0.0;
3536        M.m[0][3] = 0.0;
3537
3538        M.m[1][0] = 0.0;
3539        M.m[1][1] = 2.0 / ViewHeight;
3540        M.m[1][2] = 0.0;
3541        M.m[1][3] = 0.0;
3542
3543        M.m[2][0] = 0.0;
3544        M.m[2][1] = 0.0;
3545        M.m[2][2] = fRange;
3546        M.m[2][3] = 0.0;
3547
3548        M.m[3][0] = 0.0;
3549        M.m[3][1] = 0.0;
3550        M.m[3][2] = -fRange * NearZ;
3551        M.m[3][3] = 1.0;
3552        return M;
3553    }
3554
3555    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
3556    {
3557        unimplemented!()
3558    }
3559
3560    #[cfg(_XM_SSE_INTRINSICS_)]
3561    unsafe {
3562        let mut M: XMMATRIX = crate::undefined();
3563        let fRange: f32 = 1.0 / (FarZ - NearZ);
3564        // Note: This is recorded on the stack
3565        let rMem: XMVECTORF32 = XMVECTORF32 { f: [
3566            2.0 / ViewWidth,
3567            2.0 / ViewHeight,
3568            fRange,
3569            -fRange * NearZ
3570        ]};
3571        // Copy from memory to SSE register
3572        let mut vValues: XMVECTOR = rMem.v;
3573        let mut vTemp: XMVECTOR = _mm_setzero_ps();
3574        // Copy x only
3575        vTemp = _mm_move_ss(vTemp, vValues);
3576        // 2.0f / ViewWidth,0,0,0
3577        M.r[0] = vTemp;
3578        // 0,2.0f / ViewHeight,0,0
3579        vTemp = vValues;
3580        vTemp = _mm_and_ps(vTemp, *g_XMMaskY);
3581        M.r[1] = vTemp;
3582        // x=fRange,y=-fRange * NearZ,0,1.0f
3583        vTemp = _mm_setzero_ps();
3584        vValues = _mm_shuffle_ps(vValues, *g_XMIdentityR3, _MM_SHUFFLE(3, 2, 3, 2));
3585        // 0,0,fRange,0.0f
3586        vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(2, 0, 0, 0));
3587        M.r[2] = vTemp;
3588        // 0,0,-fRange * NearZ,1.0f
3589        vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(3, 1, 0, 0));
3590        M.r[3] = vTemp;
3591        return M;
3592    }
3593}
3594
3595/// Builds an orthogonal projection matrix for a right-handed coordinate system.
3596///
3597/// ## Parameters
3598///
3599/// `ViewWidth` Width of the frustum at the near clipping plane.
3600///
3601/// `ViewHeight` Height of the frustum at the near clipping plane.
3602///
3603/// `NearZ` Distance to the near clipping plane.
3604///
3605/// `FarZ` Distance to the far clipping plane.
3606///
3607/// ## Return value
3608///
3609/// Returns the orthogonal projection matrix.
3610///
3611/// ## Remarks
3612///
3613/// All the parameters of XMMatrixOrthographicRH are distances in camera space.
3614///
3615/// ## Reference
3616///
3617/// <https://docs.microsoft.com/en-us/windows/win32/api/directxmath/nf-directxmath-XMMatrixOrthographicRH>
3618#[inline]
3619pub fn XMMatrixOrthographicRH(
3620    ViewWidth: f32,
3621    ViewHeight: f32,
3622    NearZ: f32,
3623    FarZ: f32
3624) -> XMMATRIX
3625{
3626    debug_assert!(!XMScalarNearEqual(ViewWidth, 0.0, 0.00001));
3627    debug_assert!(!XMScalarNearEqual(ViewHeight, 0.0, 0.00001));
3628    debug_assert!(!XMScalarNearEqual(FarZ, NearZ, 0.00001));
3629
3630    #[cfg(_XM_NO_INTRINSICS_)]
3631    unsafe {
3632        let fRange: f32 = 1.0 / (NearZ - FarZ);
3633        let mut M: XMMATRIX = crate::undefined();
3634        M.m[0][0] = 2.0 / ViewWidth;
3635        M.m[0][1] = 0.0;
3636        M.m[0][2] = 0.0;
3637        M.m[0][3] = 0.0;
3638
3639        M.m[1][0] = 0.0;
3640        M.m[1][1] = 2.0 / ViewHeight;
3641        M.m[1][2] = 0.0;
3642        M.m[1][3] = 0.0;
3643
3644        M.m[2][0] = 0.0;
3645        M.m[2][1] = 0.0;
3646        M.m[2][2] = fRange;
3647        M.m[2][3] = 0.0;
3648
3649        M.m[3][0] = 0.0;
3650        M.m[3][1] = 0.0;
3651        M.m[3][2] = fRange * NearZ;
3652        M.m[3][3] = 1.0;
3653        return M;
3654    }
3655
3656    #[cfg(_XM_ARM_NEON_INTRINSICS_)]
3657    {
3658        unimplemented!()
3659    }
3660
3661    #[cfg(_XM_SSE_INTRINSICS_)]
3662    unsafe {
3663        let mut M: XMMATRIX = crate::undefined();
3664        let fRange: f32 = 1.0 / (NearZ - FarZ);
3665        // Note: This is recorded on the stack
3666        let rMem: XMVECTORF32 = XMVECTORF32 { f: [
3667            2.0 / ViewWidth,
3668            2.0 / ViewHeight,
3669            fRange,
3670            fRange * NearZ
3671        ]};
3672        // Copy from memory to SSE register
3673        let mut vValues: XMVECTOR = rMem.v;
3674        let mut vTemp: XMVECTOR = _mm_setzero_ps();
3675        // Copy x only
3676        vTemp = _mm_move_ss(vTemp, vValues);
3677        // 2.0f / ViewWidth,0,0,0
3678        M.r[0] = vTemp;
3679        // 0,2.0f / ViewHeight,0,0
3680        vTemp = vValues;
3681        vTemp = _mm_and_ps(vTemp, *g_XMMaskY);
3682        M.r[1] = vTemp;
3683        // x=fRange,y=fRange * NearZ,0,1.0f
3684        vTemp = _mm_setzero_ps();
3685        vValues = _mm_shuffle_ps(vValues, *g_XMIdentityR3, _MM_SHUFFLE(3, 2, 3, 2));
3686        // 0,0,fRange,0.0f
3687        vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(2, 0, 0, 0));
3688        M.r[2] = vTemp;
3689        // 0,0,fRange * NearZ,1.0f
3690        vTemp = _mm_shuffle_ps(vTemp, vValues, _MM_SHUFFLE(3, 1, 0, 0));
3691        M.r[3] = vTemp;
3692        return M;
3693    }
3694}
3695
3696// TODO: XMMatrixOrthographicOffCenterLH
3697// TODO: XMMatrixOrthographicOffCenterRH
3698
3699impl std::ops::Deref for XMMatrix {
3700    type Target = XMMATRIX;
3701    #[inline(always)]
3702    fn deref(&self) -> &Self::Target {
3703        &self.0
3704    }
3705}
3706
3707impl std::ops::DerefMut for XMMatrix {
3708    #[inline(always)]
3709    fn deref_mut(&mut self) -> &mut Self::Target {
3710        &mut self.0
3711    }
3712}
3713
3714impl XMMatrix {
3715    #[inline]
3716    pub fn set(
3717        m00: f32, m01: f32, m02: f32, m03: f32,
3718        m10: f32, m11: f32, m12: f32, m13: f32,
3719        m20: f32, m21: f32, m22: f32, m23: f32,
3720        m30: f32, m31: f32, m32: f32, m33: f32,
3721    ) -> XMMatrix
3722    {
3723        XMMatrix(XMMatrixSet(
3724            m00, m01, m02, m03,
3725            m10, m11, m12, m13,
3726            m20, m21, m22, m23,
3727            m30, m31, m32, m33,
3728        ))
3729    }
3730}
3731
3732impl From<&[f32; 16]> for XMMatrix {
3733    #[inline]
3734    fn from(m: &[f32; 16]) -> XMMatrix {
3735        XMMatrix(XMLoadFloat4x4(m.into()))
3736    }
3737}
3738
3739impl From<&[[f32; 4]; 4]> for XMMatrix {
3740    #[inline]
3741    fn from(m: &[[f32; 4]; 4]) -> XMMatrix {
3742        XMMatrix(XMLoadFloat4x4(m.into()))
3743    }
3744}
3745
3746impl Into<[f32; 16]> for XMMatrix {
3747    #[inline]
3748    fn into(self) -> [f32; 16] {
3749        unsafe {
3750            let mut R: XMFLOAT4X4 = crate::undefined();
3751            XMStoreFloat4x4(&mut R, self.0);
3752            mem::transmute(R)
3753        }
3754    }
3755}
3756
3757impl Into<[[f32; 4]; 4]> for XMMatrix {
3758    #[inline]
3759    fn into(self) -> [[f32; 4]; 4] {
3760        unsafe {
3761            let mut R: XMFLOAT4X4 = crate::undefined();
3762            XMStoreFloat4x4(&mut R, self.0);
3763            mem::transmute(R)
3764        }
3765    }
3766}
3767
3768impl std::ops::Add for XMMatrix {
3769    type Output = XMMatrix;
3770    #[inline]
3771    fn add(self, M: XMMatrix) -> Self::Output {
3772        unsafe {
3773            XMMatrix(XMMATRIX { r: [
3774                XMVectorAdd(self.r[0], M.r[0]),
3775                XMVectorAdd(self.r[1], M.r[1]),
3776                XMVectorAdd(self.r[2], M.r[2]),
3777                XMVectorAdd(self.r[3], M.r[3]),
3778            ]})
3779        }
3780    }
3781}
3782
3783impl std::ops::AddAssign for XMMatrix {
3784    #[inline]
3785    fn add_assign(&mut self, M: XMMatrix) {
3786        unsafe {
3787            self.r[0] = XMVectorAdd(self.r[0], M.r[0]);
3788            self.r[0] = XMVectorAdd(self.r[1], M.r[1]);
3789            self.r[0] = XMVectorAdd(self.r[2], M.r[2]);
3790            self.r[0] = XMVectorAdd(self.r[3], M.r[3]);
3791        }
3792    }
3793}
3794
3795impl std::ops::Sub for XMMatrix {
3796    type Output = XMMatrix;
3797    #[inline]
3798    fn sub(self, M: XMMatrix) -> XMMatrix{
3799        unsafe {
3800            XMMatrix(XMMATRIX { r: [
3801                XMVectorSubtract(self.r[0], M.r[0]),
3802                XMVectorSubtract(self.r[1], M.r[1]),
3803                XMVectorSubtract(self.r[2], M.r[2]),
3804                XMVectorSubtract(self.r[3], M.r[3]),
3805            ]})
3806        }
3807    }
3808}
3809
3810impl std::ops::SubAssign for XMMatrix {
3811    #[inline]
3812    fn sub_assign(&mut self, M: XMMatrix) {
3813        unsafe {
3814            self.r[0] = XMVectorSubtract(self.r[0], M.r[0]);
3815            self.r[0] = XMVectorSubtract(self.r[1], M.r[1]);
3816            self.r[0] = XMVectorSubtract(self.r[2], M.r[2]);
3817            self.r[0] = XMVectorSubtract(self.r[3], M.r[3]);
3818        }
3819    }
3820}
3821
3822impl std::ops::Mul for XMMatrix {
3823    type Output = XMMatrix;
3824    #[inline]
3825    fn mul(self, M: XMMatrix) -> XMMatrix{
3826        XMMatrix(XMMatrixMultiply(self.0, &M))
3827    }
3828}
3829
3830impl std::ops::MulAssign for XMMatrix {
3831    #[inline]
3832    fn mul_assign(&mut self, M: XMMatrix) {
3833        self.0 = XMMatrixMultiply(self.0, &M)
3834    }
3835}
3836
3837impl std::ops::Mul<XMMatrix> for f32 {
3838    type Output = XMMatrix;
3839    #[inline]
3840    fn mul(self, M: XMMatrix) -> XMMatrix {
3841        unsafe {
3842            let S = self;
3843            let mut R: XMMATRIX = crate::undefined();
3844            R.r[0] = XMVectorScale(M.r[0], S);
3845            R.r[1] = XMVectorScale(M.r[1], S);
3846            R.r[2] = XMVectorScale(M.r[2], S);
3847            R.r[3] = XMVectorScale(M.r[3], S);
3848            return XMMatrix(R);
3849        }
3850    }
3851}
3852
3853impl std::ops::Mul<f32> for XMMatrix {
3854    type Output = XMMatrix;
3855    #[inline]
3856    fn mul(self, S: f32) -> XMMatrix {
3857        unsafe {
3858            let mut R: XMMATRIX = crate::undefined();
3859            R.r[0] = XMVectorScale(self.r[0], S);
3860            R.r[1] = XMVectorScale(self.r[1], S);
3861            R.r[2] = XMVectorScale(self.r[2], S);
3862            R.r[3] = XMVectorScale(self.r[3], S);
3863            return XMMatrix(R);
3864        }
3865    }
3866}
3867
3868impl std::ops::MulAssign<f32> for XMMatrix {
3869    #[inline]
3870    fn mul_assign(&mut self, S: f32) {
3871        unsafe {
3872            self.r[0] = XMVectorScale(self.r[0], S);
3873            self.r[1] = XMVectorScale(self.r[1], S);
3874            self.r[2] = XMVectorScale(self.r[2], S);
3875            self.r[3] = XMVectorScale(self.r[3], S);
3876        }
3877    }
3878}
3879
3880impl std::ops::Div<f32> for XMMatrix {
3881    type Output = XMMatrix;
3882    #[inline]
3883    fn div(self, S: f32) -> XMMatrix {
3884        #[cfg(_XM_NO_INTRINSICS_)]
3885        unsafe {
3886            let vS: XMVECTOR = XMVectorReplicate(S);
3887            let mut R: XMMATRIX = crate::undefined();
3888            R.r[0] = XMVectorDivide(self.r[0], vS);
3889            R.r[1] = XMVectorDivide(self.r[1], vS);
3890            R.r[2] = XMVectorDivide(self.r[2], vS);
3891            R.r[3] = XMVectorDivide(self.r[3], vS);
3892            return XMMatrix(R);
3893        }
3894
3895        #[cfg(_XM_ARM_NEON_INTRINSICS_)]
3896        {
3897            unimplemented!()
3898        }
3899
3900        #[cfg(_XM_SSE_INTRINSICS_)]
3901        unsafe {
3902            let vS: __m128 = _mm_set_ps1(S);
3903            let mut R: XMMATRIX = crate::undefined();
3904            R.r[0] = _mm_div_ps(self.r[0], vS);
3905            R.r[1] = _mm_div_ps(self.r[1], vS);
3906            R.r[2] = _mm_div_ps(self.r[2], vS);
3907            R.r[3] = _mm_div_ps(self.r[3], vS);
3908            return XMMatrix(R);
3909        }
3910    }
3911}
3912
3913impl std::ops::DivAssign<f32> for XMMatrix {
3914    #[inline]
3915    fn div_assign(&mut self, S: f32) {
3916        #[cfg(_XM_NO_INTRINSICS_)]
3917        unsafe {
3918            let vS: XMVECTOR = XMVectorReplicate(S);
3919            self.r[0] = XMVectorDivide(self.r[0], vS);
3920            self.r[1] = XMVectorDivide(self.r[1], vS);
3921            self.r[2] = XMVectorDivide(self.r[2], vS);
3922            self.r[3] = XMVectorDivide(self.r[3], vS);
3923        }
3924
3925        #[cfg(_XM_ARM_NEON_INTRINSICS_)]
3926        {
3927            unimplemented!()
3928        }
3929
3930        #[cfg(_XM_SSE_INTRINSICS_)]
3931        unsafe {
3932            let vS: __m128 = _mm_set_ps1(S);
3933            self.r[0] = _mm_div_ps(self.r[0], vS);
3934            self.r[1] = _mm_div_ps(self.r[1], vS);
3935            self.r[2] = _mm_div_ps(self.r[2], vS);
3936            self.r[3] = _mm_div_ps(self.r[3], vS);
3937        }
3938    }
3939}
3940
3941impl std::ops::Neg for XMMatrix {
3942    type Output = XMMatrix;
3943    #[inline]
3944    fn neg(self) -> Self::Output {
3945        unsafe {
3946            let mut R: XMMATRIX = crate::undefined();
3947            R.r[0] = XMVectorNegate(self.r[0]);
3948            R.r[1] = XMVectorNegate(self.r[1]);
3949            R.r[2] = XMVectorNegate(self.r[2]);
3950            R.r[3] = XMVectorNegate(self.r[3]);
3951            XMMatrix(R)
3952        }
3953    }
3954}
3955
3956impl std::cmp::PartialEq for XMMatrix {
3957    #[inline]
3958    fn eq(&self, rhs: &Self) -> bool {
3959        unsafe {
3960            XMVector(self.r[0]) == XMVector(rhs.r[0]) &&
3961            XMVector(self.r[1]) == XMVector(rhs.r[1]) &&
3962            XMVector(self.r[2]) == XMVector(rhs.r[2]) &&
3963            XMVector(self.r[3]) == XMVector(rhs.r[3])
3964        }
3965    }
3966}
3967
3968impl std::fmt::Debug for XMMatrix {
3969    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3970        let rows = unsafe {
3971            &[
3972                [
3973                    XMVectorGetX(self.r[0]),
3974                    XMVectorGetY(self.r[0]),
3975                    XMVectorGetZ(self.r[0]),
3976                    XMVectorGetW(self.r[0]),
3977                ],
3978                [
3979                    XMVectorGetX(self.r[1]),
3980                    XMVectorGetY(self.r[1]),
3981                    XMVectorGetZ(self.r[1]),
3982                    XMVectorGetW(self.r[1]),
3983                ],
3984                [
3985                    XMVectorGetX(self.r[2]),
3986                    XMVectorGetY(self.r[2]),
3987                    XMVectorGetZ(self.r[2]),
3988                    XMVectorGetW(self.r[2]),
3989                ],
3990                [
3991                    XMVectorGetX(self.r[3]),
3992                    XMVectorGetY(self.r[3]),
3993                    XMVectorGetZ(self.r[3]),
3994                    XMVectorGetW(self.r[3]),
3995                ],
3996            ]
3997        };
3998        f.debug_list()
3999            .entries(rows)
4000            .finish()
4001    }
4002}
4003
4004#[test]
4005fn test_debug() {
4006    #[rustfmt::skip]
4007    let m = XMMatrix::from(&[
4008        [ 1.0,  2.0,  3.0,  4.0],
4009        [ 5.0,  6.0,  7.0,  8.0],
4010        [ 9.0, 10.0, 11.0, 12.0],
4011        [13.0, 14.0, 15.0, 16.0],
4012    ]);
4013    let s = format!("{:?}", m);
4014    assert_eq!("[[1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0], [9.0, 10.0, 11.0, 12.0], [13.0, 14.0, 15.0, 16.0]]", s);
4015}