1 /**
2  * Contains the definition of a 4x4 Matrix of doubles, and associated functions to transform
3  * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this:
4  * <p>
5  *      m00  m10  m20  m30<br>
6  *      m01  m11  m21  m31<br>
7  *      m02  m12  m22  m32<br>
8  *      m03  m13  m23  m33<br>
9  * 
10  * @author Richard Greenlees
11  * @author Kai Burjack
12  */
13 module doml.matrix_4d;
14 
15 import Math = doml.math;
16 import MemUtil = doml.mem_util;
17 
18 import doml.matrix_3d;
19 import doml.matrix_4x3d;
20 import doml.matrix_3x2d;
21 
22 import doml.vector_2d;
23 import doml.vector_3d;
24 import doml.vector_4d;
25 
26 import doml.axis_angle_4d;
27 import doml.quaternion_d;
28 
29 /*
30  * The MIT License
31  $!#@$@ Translated by jordan4ibanez
32  *
33  * Copyright (c) 2015-2021 Richard Greenlees
34  *
35  * Permission is hereby granted, free of charge, to any person obtaining a copy
36  * of this software and associated documentation files (the "Software"), to deal
37  * in the Software without restriction, including without limitation the rights
38  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
39  * copies of the Software, and to permit persons to whom the Software is
40  * furnished to do so, subject to the following conditions:
41  *
42  * The above copyright notice and this permission notice shall be included in
43  * all copies or substantial portions of the Software.
44  *
45  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
51  * THE SOFTWARE.
52  */
53 
54 /**
55  * Contains the definition of a 4x4 Matrix of doubles, and associated functions to transform
56  * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this:
57  * <p>
58  *      m00  m10  m20  m30<br>
59  *      m01  m11  m21  m31<br>
60  *      m02  m12  m22  m32<br>
61  *      m03  m13  m23  m33<br>
62  * 
63  * @author Richard Greenlees
64  * @author Kai Burjack
65  */
66 struct Matrix4d {
67 
68     double m00 = 1.0;
69     double m01 = 0.0;
70     double m02 = 0.0;
71     double m03 = 0.0;
72 
73     double m10 = 0.0;
74     double m11 = 1.0;
75     double m12 = 0.0;
76     double m13 = 0.0;
77 
78     double m20 = 0.0;
79     double m21 = 0.0;
80     double m22 = 1.0;
81     double m23 = 0.0;
82 
83     double m30 = 0.0;
84     double m31 = 0.0;
85     double m32 = 0.0;
86     double m33 = 1.0;
87 
88    /**
89 
90     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
91     * identifying the plane with equation <code>x=-1</code> when using the identity matrix.  
92     */
93     static immutable int PLANE_NX = 0;
94     /**
95     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
96     * identifying the plane with equation <code>x=1</code> when using the identity matrix.  
97     */
98     static immutable int PLANE_PX = 1;
99     /**
100     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
101     * identifying the plane with equation <code>y=-1</code> when using the identity matrix.  
102     */
103     static immutable int PLANE_NY = 2;
104     /**
105     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
106     * identifying the plane with equation <code>y=1</code> when using the identity matrix.  
107     */
108     static immutable int PLANE_PY = 3;
109     /**
110     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
111     * identifying the plane with equation <code>z=-1</code> when using the identity matrix.  
112     */
113     static immutable int PLANE_NZ = 4;
114     /**
115     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
116     * identifying the plane with equation <code>z=1</code> when using the identity matrix.  
117     */
118     static immutable int PLANE_PZ = 5;
119     /**
120     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
121     * identifying the corner <code>(-1, -1, -1)</code> when using the identity matrix.
122     */
123     static immutable int CORNER_NXNYNZ = 0;
124     /**
125     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
126     * identifying the corner <code>(1, -1, -1)</code> when using the identity matrix.
127     */
128     static immutable int CORNER_PXNYNZ = 1;
129     /**
130     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
131     * identifying the corner <code>(1, 1, -1)</code> when using the identity matrix.
132     */
133     static immutable int CORNER_PXPYNZ = 2;
134     /**
135     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
136     * identifying the corner <code>(-1, 1, -1)</code> when using the identity matrix.
137     */
138     static immutable int CORNER_NXPYNZ = 3;
139     /**
140     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
141     * identifying the corner <code>(1, -1, 1)</code> when using the identity matrix.
142     */
143     static immutable int CORNER_PXNYPZ = 4;
144     /**
145     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
146     * identifying the corner <code>(-1, -1, 1)</code> when using the identity matrix.
147     */
148     static immutable int CORNER_NXNYPZ = 5;
149     /**
150     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
151     * identifying the corner <code>(-1, 1, 1)</code> when using the identity matrix.
152     */
153     static immutable int CORNER_NXPYPZ = 6;
154     /**
155     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
156     * identifying the corner <code>(1, 1, 1)</code> when using the identity matrix.
157     */
158     static immutable int CORNER_PXPYPZ = 7;
159 
160     /**
161     * Bit returned by {@link #properties()} to indicate that the matrix represents a perspective transformation.
162     */
163     static immutable byte PROPERTY_PERSPECTIVE = 1<<0;
164     /**
165     * Bit returned by {@link #properties()} to indicate that the matrix represents an affine transformation.
166     */
167     static immutable byte PROPERTY_AFFINE = 1<<1;
168     /**
169     * Bit returned by {@link #properties()} to indicate that the matrix represents the identity transformation.
170     */
171     static immutable byte PROPERTY_IDENTITY = 1<<2;
172     /**
173     * Bit returned by {@link #properties()} to indicate that the matrix represents a pure translation transformation.
174     */
175     static immutable byte PROPERTY_TRANSLATION = 1<<3;
176     /**
177     * Bit returned by {@link #properties()} to indicate that the upper-left 3x3 submatrix represents an orthogonal
178     * matrix (i.e. orthonormal basis). For practical reasons, this property also always implies 
179     * {@link #PROPERTY_AFFINE} in this implementation.
180     */
181     static immutable byte PROPERTY_ORTHONORMAL = 1<<4;
182 
183 
184 
185     int properties = PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL;
186 
187     /**
188      * Create a new {@link Matrix4d} and make it a copy of the given matrix.
189      * 
190      * @param mat
191      *          the {@link Matrix4d} to copy the values from
192      */
193     this(Matrix4d mat) {
194         set(mat);
195     }
196 
197     /**
198      * Create a new {@link Matrix4d} and set its upper 4x3 submatrix to the given matrix <code>mat</code>
199      * and all other elements to identity.
200      * 
201      * @param mat
202      *          the {@link Matrix4x3d} to copy the values from
203      */
204     this(Matrix4x3d mat) {
205         set(mat);
206     }
207 
208     /**
209      * Create a new {@link Matrix4d} by setting its uppper left 3x3 submatrix to the values of the given {@link Matrix3d}
210      * and the rest to identity.
211      * 
212      * @param mat
213      *          the {@link Matrix3d}
214      */
215     this(Matrix3d mat) {
216         set(mat);
217     }
218 
219     /**
220      * Create a new 4x4 matrix using the supplied double values.
221      * <p>
222      * The matrix layout will be:<br><br>
223      *   m00, m10, m20, m30<br>
224      *   m01, m11, m21, m31<br>
225      *   m02, m12, m22, m32<br>
226      *   m03, m13, m23, m33
227      * 
228      * @param m00
229      *          the value of m00
230      * @param m01
231      *          the value of m01
232      * @param m02
233      *          the value of m02
234      * @param m03
235      *          the value of m03
236      * @param m10
237      *          the value of m10
238      * @param m11
239      *          the value of m11
240      * @param m12
241      *          the value of m12
242      * @param m13
243      *          the value of m13
244      * @param m20
245      *          the value of m20
246      * @param m21
247      *          the value of m21
248      * @param m22
249      *          the value of m22
250      * @param m23
251      *          the value of m23
252      * @param m30
253      *          the value of m30
254      * @param m31
255      *          the value of m31
256      * @param m32
257      *          the value of m32
258      * @param m33
259      *          the value of m33
260      */
261     this(double m00, double m01, double m02, double m03,
262                     double m10, double m11, double m12, double m13, 
263                     double m20, double m21, double m22, double m23, 
264                     double m30, double m31, double m32, double m33) {
265         setm00(m00);
266         setm01(m01);
267         setm02(m02);
268         setm03(m03);
269         setm10(m10);
270         setm11(m11);
271         setm12(m12);
272         setm13(m13);
273         setm20(m20);
274         setm21(m21);
275         setm22(m22);
276         setm23(m23);
277         setm30(m30);
278         setm31(m31);
279         setm32(m32);
280         setm33(m33);
281         determineProperties();
282     }
283 
284     /**
285      * Create a new {@link Matrix4d} and initialize its four columns using the supplied vectors.
286      * 
287      * @param col0
288      *          the first column
289      * @param col1
290      *          the second column
291      * @param col2
292      *          the third column
293      * @param col3
294      *          the fourth column
295      */
296     this(ref Vector4d col0, Vector4d col1, Vector4d col2, Vector4d col3) {
297         set(col0, col1, col2, col3);
298     }
299 
300     /**
301      * Assume the given properties about this matrix.
302      * <p>
303      * Use one or multiple of 0, {@link Matrix4d#PROPERTY_IDENTITY},
304      * {@link Matrix4d#PROPERTY_TRANSLATION}, {@link Matrix4d#PROPERTY_AFFINE},
305      * {@link Matrix4d#PROPERTY_PERSPECTIVE}, {@link Matrix4fc#PROPERTY_ORTHONORMAL}.
306      * 
307      * @param properties
308      *          bitset of the properties to assume about this matrix
309      * @return this
310      */
311     ref public Matrix4d assume(int properties) return {
312         this.properties = cast(byte) properties;
313         return this;
314     }
315 
316     /**
317      * Compute and set the matrix properties returned by {@link #properties()} based
318      * on the current matrix element values.
319      * 
320      * @return this
321      */
322     ref public Matrix4d determineProperties() return {
323         int __properties = 0;
324         if (m03 == 0.0 && m13 == 0.0) {
325             if (m23 == 0.0 && m33 == 1.0) {
326                 __properties |= PROPERTY_AFFINE;
327                 if (m00 == 1.0 && m01 == 0.0 && m02 == 0.0 && m10 == 0.0 && m11 == 1.0 && m12 == 0.0 && m20 == 0.0
328                         && m21 == 0.0 && m22 == 1.0) {
329                     __properties |= PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL;
330                     if (m30 == 0.0 && m31 == 0.0 && m32 == 0.0)
331                         __properties |= PROPERTY_IDENTITY;
332                 }
333                 /* 
334                  * We do not determine orthogonality, since it would require arbitrary epsilons
335                  * and is rather expensive (6 dot products) in the worst case.
336                  */
337             } else if (m01 == 0.0 && m02 == 0.0 && m10 == 0.0 && m12 == 0.0 && m20 == 0.0 && m21 == 0.0 && m30 == 0.0
338                     && m31 == 0.0 && m33 == 0.0) {
339                 __properties |= PROPERTY_PERSPECTIVE;
340             }
341         }
342         this.properties = __properties;
343         return this;
344     }
345 
346     public int getProperties() {
347         return this.properties;
348     }
349 
350     /**
351      * Set the value of the matrix element at column 0 and row 0.
352      * 
353      * @param m00
354      *          the new value
355      * @return this
356      */
357     ref public Matrix4d setm00(double m00) return {
358         this.m00 = m00;
359         properties &= ~PROPERTY_ORTHONORMAL;
360         if (m00 != 1.0)
361             properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
362         return this;
363     }
364     /**
365      * Set the value of the matrix element at column 0 and row 1.
366      * 
367      * @param m01
368      *          the new value
369      * @return this
370      */
371     ref public Matrix4d setm01(double m01) return {
372         this.m01 = m01;
373         properties &= ~PROPERTY_ORTHONORMAL;
374         if (m01 != 0.0)
375             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
376         return this;
377     }
378     /**
379      * Set the value of the matrix element at column 0 and row 2.
380      * 
381      * @param m02
382      *          the new value
383      * @return this
384      */
385     ref public Matrix4d setm02(double m02) return {
386         this.m02 = m02;
387         properties &= ~PROPERTY_ORTHONORMAL;
388         if (m02 != 0.0)
389             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
390         return this;
391     }
392     /**
393      * Set the value of the matrix element at column 0 and row 3.
394      * 
395      * @param m03
396      *          the new value
397      * @return this
398      */
399     ref public Matrix4d setm03(double m03) return {
400         this.m03 = m03;
401         if (m03 != 0.0)
402             properties = 0;
403         return this;
404     }
405     /**
406      * Set the value of the matrix element at column 1 and row 0.
407      * 
408      * @param m10
409      *          the new value
410      * @return this
411      */
412     ref public Matrix4d setm10(double m10) return {
413         this.m10 = m10;
414         properties &= ~PROPERTY_ORTHONORMAL;
415         if (m10 != 0.0)
416             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
417         return this;
418     }
419     /**
420      * Set the value of the matrix element at column 1 and row 1.
421      * 
422      * @param m11
423      *          the new value
424      * @return this
425      */
426     ref public Matrix4d setm11(double m11) return {
427         this.m11 = m11;
428         properties &= ~PROPERTY_ORTHONORMAL;
429         if (m11 != 1.0)
430             properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
431         return this;
432     }
433     /**
434      * Set the value of the matrix element at column 1 and row 2.
435      * 
436      * @param m12
437      *          the new value
438      * @return this
439      */
440     ref public Matrix4d setm12(double m12) return {
441         this.m12 = m12;
442         properties &= ~PROPERTY_ORTHONORMAL;
443         if (m12 != 0.0)
444             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
445         return this;
446     }
447     /**
448      * Set the value of the matrix element at column 1 and row 3.
449      * 
450      * @param m13
451      *          the new value
452      * @return this
453      */
454     ref public Matrix4d setm13(double m13) return {
455         this.m13 = m13;
456         if (m03 != 0.0)
457             properties = 0;
458         return this;
459     }
460     /**
461      * Set the value of the matrix element at column 2 and row 0.
462      * 
463      * @param m20
464      *          the new value
465      * @return this
466      */
467     ref public Matrix4d setm20(double m20) return {
468         this.m20 = m20;
469         properties &= ~PROPERTY_ORTHONORMAL;
470         if (m20 != 0.0)
471             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
472         return this;
473     }
474     /**
475      * Set the value of the matrix element at column 2 and row 1.
476      * 
477      * @param m21
478      *          the new value
479      * @return this
480      */
481     ref public Matrix4d setm21(double m21) return {
482         this.m21 = m21;
483         properties &= ~PROPERTY_ORTHONORMAL;
484         if (m21 != 0.0)
485             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
486         return this;
487     }
488     /**
489      * Set the value of the matrix element at column 2 and row 2.
490      * 
491      * @param m22
492      *          the new value
493      * @return this
494      */
495     ref public Matrix4d setm22(double m22) return {
496         this.m22 = m22;
497         properties &= ~PROPERTY_ORTHONORMAL;
498         if (m22 != 1.0)
499             properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
500         return this;
501     }
502     /**
503      * Set the value of the matrix element at column 2 and row 3.
504      * 
505      * @param m23
506      *          the new value
507      * @return this
508      */
509     ref public Matrix4d setm23(double m23) return {
510         this.m23 = m23;
511         if (m23 != 0.0)
512             properties &= ~(PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL);
513         return this;
514     }
515     /**
516      * Set the value of the matrix element at column 3 and row 0.
517      * 
518      * @param m30
519      *          the new value
520      * @return this
521      */
522     ref public Matrix4d setm30(double m30) return {
523         this.m30 = m30;
524         if (m30 != 0.0)
525             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE);
526         return this;
527     }
528     /**
529      * Set the value of the matrix element at column 3 and row 1.
530      * 
531      * @param m31
532      *          the new value
533      * @return this
534      */
535     ref public Matrix4d setm31(double m31) return {
536         this.m31 = m31;
537         if (m31 != 0.0)
538             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE);
539         return this;
540     }
541     /**
542      * Set the value of the matrix element at column 3 and row 2.
543      * 
544      * @param m32
545      *          the new value
546      * @return this
547      */
548     ref public Matrix4d setm32(double m32) return {
549         this.m32 = m32;
550         if (m32 != 0.0)
551             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE);
552         return this;
553     }
554     /**
555      * Set the value of the matrix element at column 3 and row 3.
556      * 
557      * @param m33
558      *          the new value
559      * @return this
560      */
561     ref public Matrix4d setm33(double m33) return {
562         this.m33 = m33;
563         if (m33 != 0.0)
564             properties &= ~(PROPERTY_PERSPECTIVE);
565         if (m33 != 1.0)
566             properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL | PROPERTY_AFFINE);
567         return this;
568     }
569 
570     ref Matrix4d _properties(int properties) return {
571         this.properties = properties;
572         return this;
573     }
574 
575     /**
576      * Set the value of the matrix element at column 0 and row 0 without updating the properties of the matrix.
577      * 
578      * @param m00
579      *          the new value
580      * @return this
581      */
582     ref Matrix4d _m00(double m00) return {
583         setm00(m00);
584         return this;
585     }
586     /**
587      * Set the value of the matrix element at column 0 and row 1 without updating the properties of the matrix.
588      * 
589      * @param m01
590      *          the new value
591      * @return this
592      */
593     ref Matrix4d _m01(double m01) return {
594         setm01(m01);
595         return this;
596     }
597     /**
598      * Set the value of the matrix element at column 0 and row 2 without updating the properties of the matrix.
599      * 
600      * @param m02
601      *          the new value
602      * @return this
603      */
604     ref Matrix4d _m02(double m02) return {
605         setm02(m02);
606         return this;
607     }
608     /**
609      * Set the value of the matrix element at column 0 and row 3 without updating the properties of the matrix.
610      * 
611      * @param m03
612      *          the new value
613      * @return this
614      */
615     ref Matrix4d _m03(double m03) return {
616         setm03(m03);
617         return this;
618     }
619     /**
620      * Set the value of the matrix element at column 1 and row 0 without updating the properties of the matrix.
621      * 
622      * @param m10
623      *          the new value
624      * @return this
625      */
626     ref Matrix4d _m10(double m10) return {
627         setm10(m10);
628         return this;
629     }
630     /**
631      * Set the value of the matrix element at column 1 and row 1 without updating the properties of the matrix.
632      * 
633      * @param m11
634      *          the new value
635      * @return this
636      */
637     ref Matrix4d _m11(double m11) return {
638         setm11(m11);
639         return this;
640     }
641     /**
642      * Set the value of the matrix element at column 1 and row 2 without updating the properties of the matrix.
643      * 
644      * @param m12
645      *          the new value
646      * @return this
647      */
648     ref Matrix4d _m12(double m12) return {
649         setm12(m12);
650         return this;
651     }
652     /**
653      * Set the value of the matrix element at column 1 and row 3 without updating the properties of the matrix.
654      * 
655      * @param m13
656      *          the new value
657      * @return this
658      */
659     ref Matrix4d _m13(double m13) return {
660         setm13(m13);
661         return this;
662     }
663     /**
664      * Set the value of the matrix element at column 2 and row 0 without updating the properties of the matrix.
665      * 
666      * @param m20
667      *          the new value
668      * @return this
669      */
670     ref Matrix4d _m20(double m20) return {
671         setm20(m20);
672         return this;
673     }
674     /**
675      * Set the value of the matrix element at column 2 and row 1 without updating the properties of the matrix.
676      * 
677      * @param m21
678      *          the new value
679      * @return this
680      */
681     ref Matrix4d _m21(double m21) return {
682         setm21(m21);
683         return this;
684     }
685     /**
686      * Set the value of the matrix element at column 2 and row 2 without updating the properties of the matrix.
687      * 
688      * @param m22
689      *          the new value
690      * @return this
691      */
692     ref Matrix4d _m22(double m22) return {
693         setm22(m22);
694         return this;
695     }
696     /**
697      * Set the value of the matrix element at column 2 and row 3 without updating the properties of the matrix.
698      * 
699      * @param m23
700      *          the new value
701      * @return this
702      */
703     ref Matrix4d _m23(double m23) return {
704         setm23(m23);
705         return this;
706     }
707     /**
708      * Set the value of the matrix element at column 3 and row 0 without updating the properties of the matrix.
709      * 
710      * @param m30
711      *          the new value
712      * @return this
713      */
714     ref Matrix4d _m30(double m30) return {
715         setm30(m30);
716         return this;
717     }
718     /**
719      * Set the value of the matrix element at column 3 and row 1 without updating the properties of the matrix.
720      * 
721      * @param m31
722      *          the new value
723      * @return this
724      */
725     ref Matrix4d _m31(double m31) return {
726         setm31(m31);
727         return this;
728     }
729     /**
730      * Set the value of the matrix element at column 3 and row 2 without updating the properties of the matrix.
731      * 
732      * @param m32
733      *          the new value
734      * @return this
735      */
736     ref Matrix4d _m32(double m32) return {
737         setm32(m32);
738         return this;
739     }
740     /**
741      * Set the value of the matrix element at column 3 and row 3 without updating the properties of the matrix.
742      * 
743      * @param m33
744      *          the new value
745      * @return this
746      */
747     ref Matrix4d _m33(double m33) return {
748         setm33(m33);
749         return this;
750     }
751 
752     /**
753      * Reset this matrix to the identity.
754      * <p>
755      * Please note that if a call to {@link #identity()} is immediately followed by a call to:
756      * {@link #translate(double, double, double) translate}, 
757      * {@link #rotate(double, double, double, double) rotate},
758      * {@link #scale(double, double, double) scale},
759      * {@link #perspective(double, double, double, double) perspective},
760      * {@link #frustum(double, double, double, double, double, double) frustum},
761      * {@link #ortho(double, double, double, double, double, double) ortho},
762      * {@link #ortho2D(double, double, double, double) ortho2D},
763      * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt},
764      * {@link #lookAlong(double, double, double, double, double, double) lookAlong},
765      * or any of their overloads, then the call to {@link #identity()} can be omitted and the subsequent call replaced with:
766      * {@link #translation(double, double, double) translation},
767      * {@link #rotation(double, double, double, double) rotation},
768      * {@link #scaling(double, double, double) scaling},
769      * {@link #setPerspective(double, double, double, double) setPerspective},
770      * {@link #setFrustum(double, double, double, double, double, double) setFrustum},
771      * {@link #setOrtho(double, double, double, double, double, double) setOrtho},
772      * {@link #setOrtho2D(double, double, double, double) setOrtho2D},
773      * {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt},
774      * {@link #setLookAlong(double, double, double, double, double, double) setLookAlong},
775      * or any of their overloads.
776      * 
777      * @return this
778      */
779     ref public Matrix4d identity() return {
780         if ((properties & PROPERTY_IDENTITY) != 0)
781             return this;
782         _identity();
783         properties = PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL;
784         return this;
785     }
786     private void _identity() {
787         _m00(1.0).
788         _m10(0.0).
789         _m20(0.0).
790         _m30(0.0).
791         _m01(0.0).
792         _m11(1.0).
793         _m21(0.0).
794         _m31(0.0).
795         _m02(0.0).
796         _m12(0.0).
797         _m22(1.0).
798         _m32(0.0).
799         _m03(0.0).
800         _m13(0.0).
801         _m23(0.0).
802         _m33(1.0);
803     }
804 
805     /**
806      * Store the values of the given matrix <code>m</code> into <code>this</code> matrix.
807      * 
808      * @see #Matrix4d(Matrix4d)
809      * @see #get(Matrix4d)
810      * 
811      * @param m
812      *          the matrix to copy the values from
813      * @return this
814      */
815     ref public Matrix4d set(Matrix4d m) return {
816         return
817         _m00(m.m00).
818         _m01(m.m01).
819         _m02(m.m02).
820         _m03(m.m03).
821         _m10(m.m10).
822         _m11(m.m11).
823         _m12(m.m12).
824         _m13(m.m13).
825         _m20(m.m20).
826         _m21(m.m21).
827         _m22(m.m22).
828         _m23(m.m23).
829         _m30(m.m30).
830         _m31(m.m31).
831         _m32(m.m32).
832         _m33(m.m33).
833         _properties(m.properties);
834     }
835 
836 
837     /**
838      * Store the values of the transpose of the given matrix <code>m</code> into <code>this</code> matrix.
839      * 
840      * @param m
841      *          the matrix to copy the transposed values from
842      * @return this
843      */
844     ref public Matrix4d setTransposed(Matrix4d m) return {
845         if ((m.properties & PROPERTY_IDENTITY) != 0)
846             return this.identity();
847         return setTransposedInternal(m);
848     }
849     ref private Matrix4d setTransposedInternal(Matrix4d m) return {
850         double nm10 = m.m01, nm12 = m.m21, nm13 = m.m31;
851         double nm20 = m.m02, nm21 = m.m12, nm30 = m.m03;
852         double nm31 = m.m13, nm32 = m.m23;
853         return this
854         ._m00(m.m00)._m01(m.m10)._m02(m.m20)._m03(m.m30)
855         ._m10(nm10)._m11(m.m11)._m12(nm12)._m13(nm13)
856         ._m20(nm20)._m21(nm21)._m22(m.m22)._m23(m.m32)
857         ._m30(nm30)._m31(nm31)._m32(nm32)._m33(m.m33)
858         ._properties(m.properties & PROPERTY_IDENTITY);
859     }
860 
861     /**
862      * Store the values of the given matrix <code>m</code> into <code>this</code> matrix
863      * and set the other matrix elements to identity.
864      * 
865      * @see #Matrix4d(Matrix4x3d)
866      * 
867      * @param m
868      *          the matrix to copy the values from
869      * @return this
870      */
871     ref public Matrix4d set(Matrix4x3d m) return {
872         return
873         _m00(m.m00).
874         _m01(m.m01).
875         _m02(m.m02).
876         _m03(0.0).
877         _m10(m.m10).
878         _m11(m.m11).
879         _m12(m.m12).
880         _m13(0.0).
881         _m20(m.m20).
882         _m21(m.m21).
883         _m22(m.m22).
884         _m23(0.0).
885         _m30(m.m30).
886         _m31(m.m31).
887         _m32(m.m32).
888         _m33(1.0).
889         _properties(m.properties | PROPERTY_AFFINE);
890     }
891 
892    
893     /**
894      * Set the upper left 3x3 submatrix of this {@link Matrix4d} to the given {@link Matrix3d} 
895      * and the rest to identity.
896      * 
897      * @see #Matrix4d(Matrix3d)
898      * 
899      * @param mat
900      *          the {@link Matrix3d}
901      * @return this
902      */
903     ref public Matrix4d set(Matrix3d mat) return {
904         return
905         _m00(mat.m00).
906         _m01(mat.m01).
907         _m02(mat.m02).
908         _m03(0.0).
909         _m10(mat.m10).
910         _m11(mat.m11).
911         _m12(mat.m12).
912         _m13(0.0).
913         _m20(mat.m20).
914         _m21(mat.m21).
915         _m22(mat.m22).
916         _m23(0.0).
917         _m30(0.0).
918         _m31(0.0).
919         _m32(0.0).
920         _m33(1.0).
921         _properties(PROPERTY_AFFINE);
922     }
923 
924     /**
925      * Set the upper left 3x3 submatrix of this {@link Matrix4d} to that of the given {@link Matrix4d} 
926      * and don't change the other elements.
927      * 
928      * @param mat
929      *          the {@link Matrix4d}
930      * @return this
931      */
932     ref public Matrix4d set3x3(Matrix4d mat) return {
933         return
934         _m00(mat.m00).
935         _m01(mat.m01).
936         _m02(mat.m02).
937         _m10(mat.m10).
938         _m11(mat.m11).
939         _m12(mat.m12).
940         _m20(mat.m20).
941         _m21(mat.m21).
942         _m22(mat.m22).
943         _properties(properties & mat.properties & ~(PROPERTY_PERSPECTIVE));
944     }
945 
946     /**
947      * Set the upper 4x3 submatrix of this {@link Matrix4d} to the given {@link Matrix4x3d} 
948      * and don't change the other elements.
949      * 
950      * @see Matrix4x3d#get(Matrix4d)
951      * 
952      * @param mat
953      *          the {@link Matrix4x3d}
954      * @return this
955      */
956     ref public Matrix4d set4x3(Matrix4x3d mat) return {
957         return
958         _m00(mat.m00).
959         _m01(mat.m01).
960         _m02(mat.m02).
961         _m10(mat.m10).
962         _m11(mat.m11).
963         _m12(mat.m12).
964         _m20(mat.m20).
965         _m21(mat.m21).
966         _m22(mat.m22).
967         _m30(mat.m30).
968         _m31(mat.m31).
969         _m32(mat.m32).
970         _properties(properties & mat.properties & ~(PROPERTY_PERSPECTIVE));
971     }
972 
973 
974     /**
975      * Set the upper 4x3 submatrix of this {@link Matrix4d} to the upper 4x3 submatrix of the given {@link Matrix4d} 
976      * and don't change the other elements.
977      * 
978      * @param mat
979      *          the {@link Matrix4d}
980      * @return this
981      */
982     ref public Matrix4d set4x3(Matrix4d mat) return {
983         return
984         _m00(mat.m00).
985         _m01(mat.m01).
986         _m02(mat.m02).
987         _m10(mat.m10).
988         _m11(mat.m11).
989         _m12(mat.m12).
990         _m20(mat.m20).
991         _m21(mat.m21).
992         _m22(mat.m22).
993         _m30(mat.m30).
994         _m31(mat.m31).
995         _m32(mat.m32).
996         _properties(properties & mat.properties & ~(PROPERTY_PERSPECTIVE));
997     }
998 
999     /**
1000      * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4d}.
1001      * 
1002      * @param axisAngle
1003      *          the {@link AxisAngle4d}
1004      * @return this
1005      */
1006     ref public Matrix4d set(ref AxisAngle4d axisAngle) return {
1007         double x = axisAngle.x;
1008         double y = axisAngle.y;
1009         double z = axisAngle.z;
1010         double angle = axisAngle.angle;
1011         double invLength = Math.invsqrt(x*x + y*y + z*z);
1012         x *= invLength;
1013         y *= invLength;
1014         z *= invLength;
1015         double s = Math.sin(angle);
1016         double c = Math.cosFromSin(s, angle);
1017         double omc = 1.0 - c;
1018         _m00(c + x*x*omc).
1019         _m11(c + y*y*omc).
1020         _m22(c + z*z*omc);
1021         double tmp1 = x*y*omc;
1022         double tmp2 = z*s;
1023         _m10(tmp1 - tmp2).
1024         _m01(tmp1 + tmp2);
1025         tmp1 = x*z*omc;
1026         tmp2 = y*s;
1027         _m20(tmp1 + tmp2).
1028         _m02(tmp1 - tmp2);
1029         tmp1 = y*z*omc;
1030         tmp2 = x*s;
1031         _m21(tmp1 - tmp2).
1032         _m12(tmp1 + tmp2).
1033         _m03(0.0).
1034         _m13(0.0).
1035         _m23(0.0).
1036         _m30(0.0).
1037         _m31(0.0).
1038         _m32(0.0).
1039         _m33(1.0).
1040         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
1041         return this;
1042     }
1043 
1044 
1045     /**
1046      * Set this matrix to be equivalent to the rotation - and possibly scaling - specified by the given {@link Quaterniond}.
1047      * <p>
1048      * This method is equivalent to calling: <code>rotation(q)</code>
1049      * <p>
1050      * Reference: <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/">http://www.euclideanspace.com/</a>
1051      * 
1052      * @see #rotation(ref Quaterniond)
1053      * 
1054      * @param q
1055      *          the {@link Quaterniond}
1056      * @return this
1057      */
1058     ref public Matrix4d set(ref Quaterniond q) return {
1059         return rotation(q);
1060     }
1061 
1062     /**
1063      * Multiply this matrix by the supplied <code>right</code> matrix.
1064      * <p>
1065      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1066      * then the new matrix will be <code>M * R</code>. So when transforming a
1067      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1068      * transformation of the right matrix will be applied first!
1069      * 
1070      * @param right
1071      *          the right operand of the multiplication
1072      * @return this
1073      */
1074     ref public Matrix4d mul(Matrix4d right) return {
1075         mul(right, this);
1076         return this;
1077     }
1078 
1079     public Matrix4d mul(Matrix4d right, ref Matrix4d dest) {
1080         if ((properties & PROPERTY_IDENTITY) != 0)
1081             return dest.set(right);
1082         else if ((right.properties & PROPERTY_IDENTITY) != 0)
1083             return dest.set(this);
1084         else if ((properties & PROPERTY_TRANSLATION) != 0 && (right.properties & PROPERTY_AFFINE) != 0)
1085             return mulTranslationAffine(right, dest);
1086         else if ((properties & PROPERTY_AFFINE) != 0 && (right.properties & PROPERTY_AFFINE) != 0)
1087             return mulAffine(right, dest);
1088         else if ((properties & PROPERTY_PERSPECTIVE) != 0 && (right.properties & PROPERTY_AFFINE) != 0)
1089             return mulPerspectiveAffine(right, dest);
1090         else if ((right.properties & PROPERTY_AFFINE) != 0)
1091             return mulAffineR(right, dest);
1092         return mul0(right, dest);
1093     }
1094 
1095     /**
1096      * Multiply this matrix by the supplied <code>right</code> matrix.
1097      * <p>
1098      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1099      * then the new matrix will be <code>M * R</code>. So when transforming a
1100      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1101      * transformation of the right matrix will be applied first!
1102      * <p>
1103      * This method neither assumes nor checks for any matrix properties of <code>this</code> or <code>right</code>
1104      * and will always perform a complete 4x4 matrix multiplication. This method should only be used whenever the
1105      * multiplied matrices do not have any properties for which there are optimized multiplication methods available.
1106      * 
1107      * @param right
1108      *          the right operand of the matrix multiplication
1109      * @return this
1110      */
1111     ref public Matrix4d mul0(Matrix4d right) return {
1112        mul0(right, this);
1113        return this;
1114     }
1115 
1116     public Matrix4d mul0(Matrix4d right, ref Matrix4d dest) {
1117         double nm00 = Math.fma(m00, right.m00, Math.fma(m10, right.m01, Math.fma(m20, right.m02, m30 * right.m03)));
1118         double nm01 = Math.fma(m01, right.m00, Math.fma(m11, right.m01, Math.fma(m21, right.m02, m31 * right.m03)));
1119         double nm02 = Math.fma(m02, right.m00, Math.fma(m12, right.m01, Math.fma(m22, right.m02, m32 * right.m03)));
1120         double nm03 = Math.fma(m03, right.m00, Math.fma(m13, right.m01, Math.fma(m23, right.m02, m33 * right.m03)));
1121         double nm10 = Math.fma(m00, right.m10, Math.fma(m10, right.m11, Math.fma(m20, right.m12, m30 * right.m13)));
1122         double nm11 = Math.fma(m01, right.m10, Math.fma(m11, right.m11, Math.fma(m21, right.m12, m31 * right.m13)));
1123         double nm12 = Math.fma(m02, right.m10, Math.fma(m12, right.m11, Math.fma(m22, right.m12, m32 * right.m13)));
1124         double nm13 = Math.fma(m03, right.m10, Math.fma(m13, right.m11, Math.fma(m23, right.m12, m33 * right.m13)));
1125         double nm20 = Math.fma(m00, right.m20, Math.fma(m10, right.m21, Math.fma(m20, right.m22, m30 * right.m23)));
1126         double nm21 = Math.fma(m01, right.m20, Math.fma(m11, right.m21, Math.fma(m21, right.m22, m31 * right.m23)));
1127         double nm22 = Math.fma(m02, right.m20, Math.fma(m12, right.m21, Math.fma(m22, right.m22, m32 * right.m23)));
1128         double nm23 = Math.fma(m03, right.m20, Math.fma(m13, right.m21, Math.fma(m23, right.m22, m33 * right.m23)));
1129         double nm30 = Math.fma(m00, right.m30, Math.fma(m10, right.m31, Math.fma(m20, right.m32, m30 * right.m33)));
1130         double nm31 = Math.fma(m01, right.m30, Math.fma(m11, right.m31, Math.fma(m21, right.m32, m31 * right.m33)));
1131         double nm32 = Math.fma(m02, right.m30, Math.fma(m12, right.m31, Math.fma(m22, right.m32, m32 * right.m33)));
1132         double nm33 = Math.fma(m03, right.m30, Math.fma(m13, right.m31, Math.fma(m23, right.m32, m33 * right.m33)));
1133         return dest
1134         ._m00(nm00)
1135         ._m01(nm01)
1136         ._m02(nm02)
1137         ._m03(nm03)
1138         ._m10(nm10)
1139         ._m11(nm11)
1140         ._m12(nm12)
1141         ._m13(nm13)
1142         ._m20(nm20)
1143         ._m21(nm21)
1144         ._m22(nm22)
1145         ._m23(nm23)
1146         ._m30(nm30)
1147         ._m31(nm31)
1148         ._m32(nm32)
1149         ._m33(nm33)
1150         ._properties(0);
1151     }
1152 
1153     /**
1154      * Multiply this matrix by the matrix with the supplied elements.
1155      * <p>
1156      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix whose 
1157      * elements are supplied via the parameters, then the new matrix will be <code>M * R</code>.
1158      * So when transforming a vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1159      * transformation of the right matrix will be applied first!
1160      *
1161      * @param r00
1162      *          the m00 element of the right matrix
1163      * @param r01
1164      *          the m01 element of the right matrix
1165      * @param r02
1166      *          the m02 element of the right matrix
1167      * @param r03
1168      *          the m03 element of the right matrix
1169      * @param r10
1170      *          the m10 element of the right matrix
1171      * @param r11
1172      *          the m11 element of the right matrix
1173      * @param r12
1174      *          the m12 element of the right matrix
1175      * @param r13
1176      *          the m13 element of the right matrix
1177      * @param r20
1178      *          the m20 element of the right matrix
1179      * @param r21
1180      *          the m21 element of the right matrix
1181      * @param r22
1182      *          the m22 element of the right matrix
1183      * @param r23
1184      *          the m23 element of the right matrix
1185      * @param r30
1186      *          the m30 element of the right matrix
1187      * @param r31
1188      *          the m31 element of the right matrix
1189      * @param r32
1190      *          the m32 element of the right matrix
1191      * @param r33
1192      *          the m33 element of the right matrix
1193      * @return this
1194      */
1195     ref public Matrix4d mul(
1196             double r00, double r01, double r02, double r03,
1197             double r10, double r11, double r12, double r13,
1198             double r20, double r21, double r22, double r23,
1199             double r30, double r31, double r32, double r33) return {
1200         mul(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, this);
1201         return this;
1202     }
1203 
1204     public Matrix4d mul(
1205             double r00, double r01, double r02, double r03,
1206             double r10, double r11, double r12, double r13,
1207             double r20, double r21, double r22, double r23,
1208             double r30, double r31, double r32, double r33, ref Matrix4d dest) {
1209         if ((properties & PROPERTY_IDENTITY) != 0)
1210             return dest.set(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33);
1211         else if ((properties & PROPERTY_AFFINE) != 0)
1212             return mulAffineL(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, dest);
1213         return mulGeneric(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, dest);
1214     }
1215     private Matrix4d mulAffineL(
1216             double r00, double r01, double r02, double r03,
1217             double r10, double r11, double r12, double r13,
1218             double r20, double r21, double r22, double r23,
1219             double r30, double r31, double r32, double r33, ref Matrix4d dest) {
1220         double nm00 = Math.fma(m00, r00, Math.fma(m10, r01, Math.fma(m20, r02, m30 * r03)));
1221         double nm01 = Math.fma(m01, r00, Math.fma(m11, r01, Math.fma(m21, r02, m31 * r03)));
1222         double nm02 = Math.fma(m02, r00, Math.fma(m12, r01, Math.fma(m22, r02, m32 * r03)));
1223         double nm03 = r03;
1224         double nm10 = Math.fma(m00, r10, Math.fma(m10, r11, Math.fma(m20, r12, m30 * r13)));
1225         double nm11 = Math.fma(m01, r10, Math.fma(m11, r11, Math.fma(m21, r12, m31 * r13)));
1226         double nm12 = Math.fma(m02, r10, Math.fma(m12, r11, Math.fma(m22, r12, m32 * r13)));
1227         double nm13 = r13;
1228         double nm20 = Math.fma(m00, r20, Math.fma(m10, r21, Math.fma(m20, r22, m30 * r23)));
1229         double nm21 = Math.fma(m01, r20, Math.fma(m11, r21, Math.fma(m21, r22, m31 * r23)));
1230         double nm22 = Math.fma(m02, r20, Math.fma(m12, r21, Math.fma(m22, r22, m32 * r23)));
1231         double nm23 = r23;
1232         double nm30 = Math.fma(m00, r30, Math.fma(m10, r31, Math.fma(m20, r32, m30 * r33)));
1233         double nm31 = Math.fma(m01, r30, Math.fma(m11, r31, Math.fma(m21, r32, m31 * r33)));
1234         double nm32 = Math.fma(m02, r30, Math.fma(m12, r31, Math.fma(m22, r32, m32 * r33)));
1235         double nm33 = r33;
1236         return dest
1237         ._m00(nm00)
1238         ._m01(nm01)
1239         ._m02(nm02)
1240         ._m03(nm03)
1241         ._m10(nm10)
1242         ._m11(nm11)
1243         ._m12(nm12)
1244         ._m13(nm13)
1245         ._m20(nm20)
1246         ._m21(nm21)
1247         ._m22(nm22)
1248         ._m23(nm23)
1249         ._m30(nm30)
1250         ._m31(nm31)
1251         ._m32(nm32)
1252         ._m33(nm33)
1253         ._properties(PROPERTY_AFFINE);
1254     }
1255     private Matrix4d mulGeneric(
1256             double r00, double r01, double r02, double r03,
1257             double r10, double r11, double r12, double r13,
1258             double r20, double r21, double r22, double r23,
1259             double r30, double r31, double r32, double r33, ref Matrix4d dest) {
1260         double nm00 = Math.fma(m00, r00, Math.fma(m10, r01, Math.fma(m20, r02, m30 * r03)));
1261         double nm01 = Math.fma(m01, r00, Math.fma(m11, r01, Math.fma(m21, r02, m31 * r03)));
1262         double nm02 = Math.fma(m02, r00, Math.fma(m12, r01, Math.fma(m22, r02, m32 * r03)));
1263         double nm03 = Math.fma(m03, r00, Math.fma(m13, r01, Math.fma(m23, r02, m33 * r03)));
1264         double nm10 = Math.fma(m00, r10, Math.fma(m10, r11, Math.fma(m20, r12, m30 * r13)));
1265         double nm11 = Math.fma(m01, r10, Math.fma(m11, r11, Math.fma(m21, r12, m31 * r13)));
1266         double nm12 = Math.fma(m02, r10, Math.fma(m12, r11, Math.fma(m22, r12, m32 * r13)));
1267         double nm13 = Math.fma(m03, r10, Math.fma(m13, r11, Math.fma(m23, r12, m33 * r13)));
1268         double nm20 = Math.fma(m00, r20, Math.fma(m10, r21, Math.fma(m20, r22, m30 * r23)));
1269         double nm21 = Math.fma(m01, r20, Math.fma(m11, r21, Math.fma(m21, r22, m31 * r23)));
1270         double nm22 = Math.fma(m02, r20, Math.fma(m12, r21, Math.fma(m22, r22, m32 * r23)));
1271         double nm23 = Math.fma(m03, r20, Math.fma(m13, r21, Math.fma(m23, r22, m33 * r23)));
1272         double nm30 = Math.fma(m00, r30, Math.fma(m10, r31, Math.fma(m20, r32, m30 * r33)));
1273         double nm31 = Math.fma(m01, r30, Math.fma(m11, r31, Math.fma(m21, r32, m31 * r33)));
1274         double nm32 = Math.fma(m02, r30, Math.fma(m12, r31, Math.fma(m22, r32, m32 * r33)));
1275         double nm33 = Math.fma(m03, r30, Math.fma(m13, r31, Math.fma(m23, r32, m33 * r33)));
1276         return dest
1277         ._m00(nm00)
1278         ._m01(nm01)
1279         ._m02(nm02)
1280         ._m03(nm03)
1281         ._m10(nm10)
1282         ._m11(nm11)
1283         ._m12(nm12)
1284         ._m13(nm13)
1285         ._m20(nm20)
1286         ._m21(nm21)
1287         ._m22(nm22)
1288         ._m23(nm23)
1289         ._m30(nm30)
1290         ._m31(nm31)
1291         ._m32(nm32)
1292         ._m33(nm33)
1293         ._properties(0);
1294     }
1295 
1296     /**
1297      * Multiply this matrix by the 3x3 matrix with the supplied elements expanded to a 4x4 matrix with 
1298      * all other matrix elements set to identity.
1299      * <p>
1300      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix whose 
1301      * elements are supplied via the parameters, then the new matrix will be <code>M * R</code>.
1302      * So when transforming a vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1303      * transformation of the right matrix will be applied first!
1304      *
1305      * @param r00
1306      *          the m00 element of the right matrix
1307      * @param r01
1308      *          the m01 element of the right matrix
1309      * @param r02
1310      *          the m02 element of the right matrix
1311      * @param r10
1312      *          the m10 element of the right matrix
1313      * @param r11
1314      *          the m11 element of the right matrix
1315      * @param r12
1316      *          the m12 element of the right matrix
1317      * @param r20
1318      *          the m20 element of the right matrix
1319      * @param r21
1320      *          the m21 element of the right matrix
1321      * @param r22
1322      *          the m22 element of the right matrix
1323      * @return this
1324      */
1325     ref public Matrix4d mul3x3(
1326             double r00, double r01, double r02,
1327             double r10, double r11, double r12,
1328             double r20, double r21, double r22) return {
1329         mul3x3(r00, r01, r02, r10, r11, r12, r20, r21, r22, this);
1330         return this;
1331     }
1332     public Matrix4d mul3x3(
1333             double r00, double r01, double r02,
1334             double r10, double r11, double r12,
1335             double r20, double r21, double r22, ref Matrix4d dest) {
1336         if ((properties & PROPERTY_IDENTITY) != 0)
1337             return dest.set(r00, r01, r02, 0, r10, r11, r12, 0, r20, r21, r22, 0, 0, 0, 0, 1);
1338         return mulGeneric3x3(r00, r01, r02, r10, r11, r12, r20, r21, r22, dest);
1339     }
1340     private Matrix4d mulGeneric3x3(
1341             double r00, double r01, double r02,
1342             double r10, double r11, double r12,
1343             double r20, double r21, double r22, ref Matrix4d dest) {
1344         double nm00 = Math.fma(m00, r00, Math.fma(m10, r01, m20 * r02));
1345         double nm01 = Math.fma(m01, r00, Math.fma(m11, r01, m21 * r02));
1346         double nm02 = Math.fma(m02, r00, Math.fma(m12, r01, m22 * r02));
1347         double nm03 = Math.fma(m03, r00, Math.fma(m13, r01, m23 * r02));
1348         double nm10 = Math.fma(m00, r10, Math.fma(m10, r11, m20 * r12));
1349         double nm11 = Math.fma(m01, r10, Math.fma(m11, r11, m21 * r12));
1350         double nm12 = Math.fma(m02, r10, Math.fma(m12, r11, m22 * r12));
1351         double nm13 = Math.fma(m03, r10, Math.fma(m13, r11, m23 * r12));
1352         double nm20 = Math.fma(m00, r20, Math.fma(m10, r21, m20 * r22));
1353         double nm21 = Math.fma(m01, r20, Math.fma(m11, r21, m21 * r22));
1354         double nm22 = Math.fma(m02, r20, Math.fma(m12, r21, m22 * r22));
1355         double nm23 = Math.fma(m03, r20, Math.fma(m13, r21, m23 * r22));
1356         return dest
1357         ._m00(nm00)
1358         ._m01(nm01)
1359         ._m02(nm02)
1360         ._m03(nm03)
1361         ._m10(nm10)
1362         ._m11(nm11)
1363         ._m12(nm12)
1364         ._m13(nm13)
1365         ._m20(nm20)
1366         ._m21(nm21)
1367         ._m22(nm22)
1368         ._m23(nm23)
1369         ._m30(m30)
1370         ._m31(m31)
1371         ._m32(m32)
1372         ._m33(m33)
1373         ._properties(this.properties & PROPERTY_AFFINE);
1374     }
1375 
1376     /**
1377      * Pre-multiply this matrix by the supplied <code>left</code> matrix and store the result in <code>this</code>.
1378      * <p>
1379      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the <code>left</code> matrix,
1380      * then the new matrix will be <code>L * M</code>. So when transforming a
1381      * vector <code>v</code> with the new matrix by using <code>L * M * v</code>, the
1382      * transformation of <code>this</code> matrix will be applied first!
1383      *
1384      * @param left
1385      *          the left operand of the matrix multiplication
1386      * @return this
1387      */
1388     ref public Matrix4d mulLocal(Matrix4d left) return {
1389        mulLocal(left, this);
1390        return this;
1391     }
1392 
1393     public Matrix4d mulLocal(Matrix4d left, ref Matrix4d dest) {
1394         if ((properties & PROPERTY_IDENTITY) != 0)
1395             return dest.set(left);
1396         else if ((left.properties & PROPERTY_IDENTITY) != 0)
1397             return dest.set(this);
1398         else if ((properties & PROPERTY_AFFINE) != 0 && (left.properties & PROPERTY_AFFINE) != 0)
1399             return mulLocalAffine(left, dest);
1400         return mulLocalGeneric(left, dest);
1401     }
1402     private Matrix4d mulLocalGeneric(Matrix4d left, ref Matrix4d dest) {
1403         double nm00 = Math.fma(left.m00, m00, Math.fma(left.m10, m01, Math.fma(left.m20, m02, left.m30 * m03)));
1404         double nm01 = Math.fma(left.m01, m00, Math.fma(left.m11, m01, Math.fma(left.m21, m02, left.m31 * m03)));
1405         double nm02 = Math.fma(left.m02, m00, Math.fma(left.m12, m01, Math.fma(left.m22, m02, left.m32 * m03)));
1406         double nm03 = Math.fma(left.m03, m00, Math.fma(left.m13, m01, Math.fma(left.m23, m02, left.m33 * m03)));
1407         double nm10 = Math.fma(left.m00, m10, Math.fma(left.m10, m11, Math.fma(left.m20, m12, left.m30 * m13)));
1408         double nm11 = Math.fma(left.m01, m10, Math.fma(left.m11, m11, Math.fma(left.m21, m12, left.m31 * m13)));
1409         double nm12 = Math.fma(left.m02, m10, Math.fma(left.m12, m11, Math.fma(left.m22, m12, left.m32 * m13)));
1410         double nm13 = Math.fma(left.m03, m10, Math.fma(left.m13, m11, Math.fma(left.m23, m12, left.m33 * m13)));
1411         double nm20 = Math.fma(left.m00, m20, Math.fma(left.m10, m21, Math.fma(left.m20, m22, left.m30 * m23)));
1412         double nm21 = Math.fma(left.m01, m20, Math.fma(left.m11, m21, Math.fma(left.m21, m22, left.m31 * m23)));
1413         double nm22 = Math.fma(left.m02, m20, Math.fma(left.m12, m21, Math.fma(left.m22, m22, left.m32 * m23)));
1414         double nm23 = Math.fma(left.m03, m20, Math.fma(left.m13, m21, Math.fma(left.m23, m22, left.m33 * m23)));
1415         double nm30 = Math.fma(left.m00, m30, Math.fma(left.m10, m31, Math.fma(left.m20, m32, left.m30 * m33)));
1416         double nm31 = Math.fma(left.m01, m30, Math.fma(left.m11, m31, Math.fma(left.m21, m32, left.m31 * m33)));
1417         double nm32 = Math.fma(left.m02, m30, Math.fma(left.m12, m31, Math.fma(left.m22, m32, left.m32 * m33)));
1418         double nm33 = Math.fma(left.m03, m30, Math.fma(left.m13, m31, Math.fma(left.m23, m32, left.m33 * m33)));
1419         return dest
1420         ._m00(nm00)
1421         ._m01(nm01)
1422         ._m02(nm02)
1423         ._m03(nm03)
1424         ._m10(nm10)
1425         ._m11(nm11)
1426         ._m12(nm12)
1427         ._m13(nm13)
1428         ._m20(nm20)
1429         ._m21(nm21)
1430         ._m22(nm22)
1431         ._m23(nm23)
1432         ._m30(nm30)
1433         ._m31(nm31)
1434         ._m32(nm32)
1435         ._m33(nm33)
1436         ._properties(0);
1437     }
1438 
1439     /**
1440      * Pre-multiply this matrix by the supplied <code>left</code> matrix, both of which are assumed to be {@link #isAffine() affine}, and store the result in <code>this</code>.
1441      * <p>
1442      * This method assumes that <code>this</code> matrix and the given <code>left</code> matrix both represent an {@link #isAffine() affine} transformation
1443      * (i.e. their last rows are equal to <code>(0, 0, 0, 1)</code>)
1444      * and can be used to speed up matrix multiplication if the matrices only represent affine transformations, such as translation, rotation, scaling and shearing (in any combination).
1445      * <p>
1446      * This method will not modify either the last row of <code>this</code> or the last row of <code>left</code>.
1447      * <p>
1448      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the <code>left</code> matrix,
1449      * then the new matrix will be <code>L * M</code>. So when transforming a
1450      * vector <code>v</code> with the new matrix by using <code>L * M * v</code>, the
1451      * transformation of <code>this</code> matrix will be applied first!
1452      *
1453      * @param left
1454      *          the left operand of the matrix multiplication (the last row is assumed to be <code>(0, 0, 0, 1)</code>)
1455      * @return this
1456      */
1457     ref public Matrix4d mulLocalAffine(Matrix4d left) return {
1458        mulLocalAffine(left, this);
1459        return this;
1460     }
1461 
1462     public Matrix4d mulLocalAffine(Matrix4d left, ref Matrix4d dest) {
1463         double nm00 = left.m00 * m00 + left.m10 * m01 + left.m20 * m02;
1464         double nm01 = left.m01 * m00 + left.m11 * m01 + left.m21 * m02;
1465         double nm02 = left.m02 * m00 + left.m12 * m01 + left.m22 * m02;
1466         double nm03 = left.m03;
1467         double nm10 = left.m00 * m10 + left.m10 * m11 + left.m20 * m12;
1468         double nm11 = left.m01 * m10 + left.m11 * m11 + left.m21 * m12;
1469         double nm12 = left.m02 * m10 + left.m12 * m11 + left.m22 * m12;
1470         double nm13 = left.m13;
1471         double nm20 = left.m00 * m20 + left.m10 * m21 + left.m20 * m22;
1472         double nm21 = left.m01 * m20 + left.m11 * m21 + left.m21 * m22;
1473         double nm22 = left.m02 * m20 + left.m12 * m21 + left.m22 * m22;
1474         double nm23 = left.m23;
1475         double nm30 = left.m00 * m30 + left.m10 * m31 + left.m20 * m32 + left.m30;
1476         double nm31 = left.m01 * m30 + left.m11 * m31 + left.m21 * m32 + left.m31;
1477         double nm32 = left.m02 * m30 + left.m12 * m31 + left.m22 * m32 + left.m32;
1478         double nm33 = left.m33;
1479         dest._m00(nm00)
1480         ._m01(nm01)
1481         ._m02(nm02)
1482         ._m03(nm03)
1483         ._m10(nm10)
1484         ._m11(nm11)
1485         ._m12(nm12)
1486         ._m13(nm13)
1487         ._m20(nm20)
1488         ._m21(nm21)
1489         ._m22(nm22)
1490         ._m23(nm23)
1491         ._m30(nm30)
1492         ._m31(nm31)
1493         ._m32(nm32)
1494         ._m33(nm33)
1495         ._properties(PROPERTY_AFFINE);
1496         return dest;
1497     }
1498 
1499     /**
1500      * Multiply this matrix by the supplied <code>right</code> matrix.
1501      * <p>
1502      * The last row of the <code>right</code> matrix is assumed to be <code>(0, 0, 0, 1)</code>.
1503      * <p>
1504      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1505      * then the new matrix will be <code>M * R</code>. So when transforming a
1506      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1507      * transformation of the right matrix will be applied first!
1508      *
1509      * @param right
1510      *          the right operand of the matrix multiplication
1511      * @return this
1512      */
1513     ref public Matrix4d mul(Matrix4x3d right) return {
1514         mul(right, this);
1515         return this;
1516     }
1517 
1518     public Matrix4d mul(Matrix4x3d right, ref Matrix4d dest) {
1519         if ((properties & PROPERTY_IDENTITY) != 0)
1520             return dest.set(right);
1521         else if ((right.properties & PROPERTY_IDENTITY) != 0)
1522             return dest.set(this);
1523         else if ((properties & PROPERTY_TRANSLATION) != 0)
1524             return mulTranslation(right, dest);
1525         else if ((properties & PROPERTY_AFFINE) != 0)
1526             return mulAffine(right, dest);
1527         else if ((properties & PROPERTY_PERSPECTIVE) != 0)
1528             return mulPerspectiveAffine(right, dest);
1529         return mulGeneric(right, dest);
1530     }
1531     private Matrix4d mulTranslation(Matrix4x3d right, ref Matrix4d dest) {
1532         return dest
1533         ._m00(right.m00)
1534         ._m01(right.m01)
1535         ._m02(right.m02)
1536         ._m03(m03)
1537         ._m10(right.m10)
1538         ._m11(right.m11)
1539         ._m12(right.m12)
1540         ._m13(m13)
1541         ._m20(right.m20)
1542         ._m21(right.m21)
1543         ._m22(right.m22)
1544         ._m23(m23)
1545         ._m30(right.m30 + m30)
1546         ._m31(right.m31 + m31)
1547         ._m32(right.m32 + m32)
1548         ._m33(m33)
1549         ._properties(PROPERTY_AFFINE | (right.properties & PROPERTY_ORTHONORMAL));
1550     }
1551     private Matrix4d mulAffine(Matrix4x3d right, ref Matrix4d dest) {
1552         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
1553         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
1554         double m20 = this.m20, m21 = this.m21, m22 = this.m22;
1555         double rm00 = right.m00, rm01 = right.m01, rm02 = right.m02;
1556         double rm10 = right.m10, rm11 = right.m11, rm12 = right.m12;
1557         double rm20 = right.m20, rm21 = right.m21, rm22 = right.m22;
1558         double rm30 = right.m30, rm31 = right.m31, rm32 = right.m32;
1559         return dest
1560         ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02)))
1561         ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02)))
1562         ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02)))
1563         ._m03(m03)
1564         ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12)))
1565         ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12)))
1566         ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12)))
1567         ._m13(m13)
1568         ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22)))
1569         ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22)))
1570         ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22)))
1571         ._m23(m23)
1572         ._m30(Math.fma(m00, rm30, Math.fma(m10, rm31, Math.fma(m20, rm32, m30))))
1573         ._m31(Math.fma(m01, rm30, Math.fma(m11, rm31, Math.fma(m21, rm32, m31))))
1574         ._m32(Math.fma(m02, rm30, Math.fma(m12, rm31, Math.fma(m22, rm32, m32))))
1575         ._m33(m33)
1576         ._properties(PROPERTY_AFFINE | (this.properties & right.properties & PROPERTY_ORTHONORMAL));
1577     }
1578     private Matrix4d mulGeneric(Matrix4x3d right, ref Matrix4d dest) {
1579         double nm00 = Math.fma(m00, right.m00, Math.fma(m10, right.m01, m20 * right.m02));
1580         double nm01 = Math.fma(m01, right.m00, Math.fma(m11, right.m01, m21 * right.m02));
1581         double nm02 = Math.fma(m02, right.m00, Math.fma(m12, right.m01, m22 * right.m02));
1582         double nm03 = Math.fma(m03, right.m00, Math.fma(m13, right.m01, m23 * right.m02));
1583         double nm10 = Math.fma(m00, right.m10, Math.fma(m10, right.m11, m20 * right.m12));
1584         double nm11 = Math.fma(m01, right.m10, Math.fma(m11, right.m11, m21 * right.m12));
1585         double nm12 = Math.fma(m02, right.m10, Math.fma(m12, right.m11, m22 * right.m12));
1586         double nm13 = Math.fma(m03, right.m10, Math.fma(m13, right.m11, m23 * right.m12));
1587         double nm20 = Math.fma(m00, right.m20, Math.fma(m10, right.m21, m20 * right.m22));
1588         double nm21 = Math.fma(m01, right.m20, Math.fma(m11, right.m21, m21 * right.m22));
1589         double nm22 = Math.fma(m02, right.m20, Math.fma(m12, right.m21, m22 * right.m22));
1590         double nm23 = Math.fma(m03, right.m20, Math.fma(m13, right.m21, m23 * right.m22));
1591         double nm30 = Math.fma(m00, right.m30, Math.fma(m10, right.m31, Math.fma(m20, right.m32, m30)));
1592         double nm31 = Math.fma(m01, right.m30, Math.fma(m11, right.m31, Math.fma(m21, right.m32, m31)));
1593         double nm32 = Math.fma(m02, right.m30, Math.fma(m12, right.m31, Math.fma(m22, right.m32, m32)));
1594         double nm33 = Math.fma(m03, right.m30, Math.fma(m13, right.m31, Math.fma(m23, right.m32, m33)));
1595         dest._m00(nm00)
1596         ._m01(nm01)
1597         ._m02(nm02)
1598         ._m03(nm03)
1599         ._m10(nm10)
1600         ._m11(nm11)
1601         ._m12(nm12)
1602         ._m13(nm13)
1603         ._m20(nm20)
1604         ._m21(nm21)
1605         ._m22(nm22)
1606         ._m23(nm23)
1607         ._m30(nm30)
1608         ._m31(nm31)
1609         ._m32(nm32)
1610         ._m33(nm33)
1611         ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
1612         return dest;
1613     }
1614     public Matrix4d mulPerspectiveAffine(Matrix4x3d view, ref Matrix4d dest) {
1615         double lm00 = m00, lm11 = m11, lm22 = m22, lm23 = m23;
1616         dest._m00(lm00 * view.m00)._m01(lm11 * view.m01)._m02(lm22 * view.m02)._m03(lm23 * view.m02).
1617         _m10(lm00 * view.m10)._m11(lm11 * view.m11)._m12(lm22 * view.m12)._m13(lm23 * view.m12).
1618         _m20(lm00 * view.m20)._m21(lm11 * view.m21)._m22(lm22 * view.m22)._m23(lm23 * view.m22).
1619         _m30(lm00 * view.m30)._m31(lm11 * view.m31)._m32(lm22 * view.m32 + m32)._m33(lm23 * view.m32)
1620         ._properties(0);
1621         return dest;
1622     }
1623 
1624 
1625     /**
1626      * Multiply this matrix by the supplied <code>right</code> matrix and store the result in <code>this</code>.
1627      * <p>
1628      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1629      * then the new matrix will be <code>M * R</code>. So when transforming a
1630      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1631      * transformation of the right matrix will be applied first!
1632      *
1633      * @param right
1634      *          the right operand of the matrix multiplication
1635      * @return this
1636      */
1637     ref public Matrix4d mul(Matrix3x2d right) return {
1638         mul(right, this);
1639         return this;
1640     }
1641 
1642     public Matrix4d mul(Matrix3x2d right, ref Matrix4d dest) {
1643         double nm00 = m00 * right.m00 + m10 * right.m01;
1644         double nm01 = m01 * right.m00 + m11 * right.m01;
1645         double nm02 = m02 * right.m00 + m12 * right.m01;
1646         double nm03 = m03 * right.m00 + m13 * right.m01;
1647         double nm10 = m00 * right.m10 + m10 * right.m11;
1648         double nm11 = m01 * right.m10 + m11 * right.m11;
1649         double nm12 = m02 * right.m10 + m12 * right.m11;
1650         double nm13 = m03 * right.m10 + m13 * right.m11;
1651         double nm30 = m00 * right.m20 + m10 * right.m21 + m30;
1652         double nm31 = m01 * right.m20 + m11 * right.m21 + m31;
1653         double nm32 = m02 * right.m20 + m12 * right.m21 + m32;
1654         double nm33 = m03 * right.m20 + m13 * right.m21 + m33;
1655         dest._m00(nm00)
1656         ._m01(nm01)
1657         ._m02(nm02)
1658         ._m03(nm03)
1659         ._m10(nm10)
1660         ._m11(nm11)
1661         ._m12(nm12)
1662         ._m13(nm13)
1663         ._m20(m20)
1664         ._m21(m21)
1665         ._m22(m22)
1666         ._m23(m23)
1667         ._m30(nm30)
1668         ._m31(nm31)
1669         ._m32(nm32)
1670         ._m33(nm33)
1671         ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
1672         return dest;
1673     }
1674 
1675 
1676 
1677 
1678     /**
1679      * Multiply <code>this</code> symmetric perspective projection matrix by the supplied {@link #isAffine() affine} <code>view</code> matrix.
1680      * <p>
1681      * If <code>P</code> is <code>this</code> matrix and <code>V</code> the <code>view</code> matrix,
1682      * then the new matrix will be <code>P * V</code>. So when transforming a
1683      * vector <code>v</code> with the new matrix by using <code>P * V * v</code>, the
1684      * transformation of the <code>view</code> matrix will be applied first!
1685      *
1686      * @param view
1687      *          the {@link #isAffine() affine} matrix to multiply <code>this</code> symmetric perspective projection matrix by
1688      * @return this
1689      */
1690     ref public Matrix4d mulPerspectiveAffine(Matrix4d view) return {
1691        mulPerspectiveAffine(view, this);
1692        return this;
1693     }
1694 
1695     public Matrix4d mulPerspectiveAffine(Matrix4d view, ref Matrix4d dest) {
1696         double nm00 = m00 * view.m00, nm01 = m11 * view.m01, nm02 = m22 * view.m02, nm03 = m23 * view.m02;
1697         double nm10 = m00 * view.m10, nm11 = m11 * view.m11, nm12 = m22 * view.m12, nm13 = m23 * view.m12;
1698         double nm20 = m00 * view.m20, nm21 = m11 * view.m21, nm22 = m22 * view.m22, nm23 = m23 * view.m22;
1699         double nm30 = m00 * view.m30, nm31 = m11 * view.m31, nm32 = m22 * view.m32 + m32, nm33 = m23 * view.m32;
1700         return dest
1701             ._m00(nm00)._m01(nm01)._m02(nm02)._m03(nm03)
1702             ._m10(nm10)._m11(nm11)._m12(nm12)._m13(nm13)
1703             ._m20(nm20)._m21(nm21)._m22(nm22)._m23(nm23)
1704             ._m30(nm30)._m31(nm31)._m32(nm32)._m33(nm33)
1705             ._properties(0);
1706     }
1707 
1708     /**
1709      * Multiply this matrix by the supplied <code>right</code> matrix, which is assumed to be {@link #isAffine() affine}, and store the result in <code>this</code>.
1710      * <p>
1711      * This method assumes that the given <code>right</code> matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to <code>(0, 0, 0, 1)</code>)
1712      * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination).
1713      * <p>
1714      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1715      * then the new matrix will be <code>M * R</code>. So when transforming a
1716      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1717      * transformation of the right matrix will be applied first!
1718      *
1719      * @param right
1720      *          the right operand of the matrix multiplication (the last row is assumed to be <code>(0, 0, 0, 1)</code>)
1721      * @return this
1722      */
1723     ref public Matrix4d mulAffineR(Matrix4d right) return {
1724        mulAffineR(right, this);
1725        return this;
1726     }
1727 
1728     public Matrix4d mulAffineR(Matrix4d right, ref Matrix4d dest) {
1729         double nm00 = Math.fma(m00, right.m00, Math.fma(m10, right.m01, m20 * right.m02));
1730         double nm01 = Math.fma(m01, right.m00, Math.fma(m11, right.m01, m21 * right.m02));
1731         double nm02 = Math.fma(m02, right.m00, Math.fma(m12, right.m01, m22 * right.m02));
1732         double nm03 = Math.fma(m03, right.m00, Math.fma(m13, right.m01, m23 * right.m02));
1733         double nm10 = Math.fma(m00, right.m10, Math.fma(m10, right.m11, m20 * right.m12));
1734         double nm11 = Math.fma(m01, right.m10, Math.fma(m11, right.m11, m21 * right.m12));
1735         double nm12 = Math.fma(m02, right.m10, Math.fma(m12, right.m11, m22 * right.m12));
1736         double nm13 = Math.fma(m03, right.m10, Math.fma(m13, right.m11, m23 * right.m12));
1737         double nm20 = Math.fma(m00, right.m20, Math.fma(m10, right.m21, m20 * right.m22));
1738         double nm21 = Math.fma(m01, right.m20, Math.fma(m11, right.m21, m21 * right.m22));
1739         double nm22 = Math.fma(m02, right.m20, Math.fma(m12, right.m21, m22 * right.m22));
1740         double nm23 = Math.fma(m03, right.m20, Math.fma(m13, right.m21, m23 * right.m22));
1741         double nm30 = Math.fma(m00, right.m30, Math.fma(m10, right.m31, Math.fma(m20, right.m32, m30)));
1742         double nm31 = Math.fma(m01, right.m30, Math.fma(m11, right.m31, Math.fma(m21, right.m32, m31)));
1743         double nm32 = Math.fma(m02, right.m30, Math.fma(m12, right.m31, Math.fma(m22, right.m32, m32)));
1744         double nm33 = Math.fma(m03, right.m30, Math.fma(m13, right.m31, Math.fma(m23, right.m32, m33)));
1745         dest._m00(nm00)
1746         ._m01(nm01)
1747         ._m02(nm02)
1748         ._m03(nm03)
1749         ._m10(nm10)
1750         ._m11(nm11)
1751         ._m12(nm12)
1752         ._m13(nm13)
1753         ._m20(nm20)
1754         ._m21(nm21)
1755         ._m22(nm22)
1756         ._m23(nm23)
1757         ._m30(nm30)
1758         ._m31(nm31)
1759         ._m32(nm32)
1760         ._m33(nm33)
1761         ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
1762         return dest;
1763     }
1764 
1765     /**
1766      * Multiply this matrix by the supplied <code>right</code> matrix, both of which are assumed to be {@link #isAffine() affine}, and store the result in <code>this</code>.
1767      * <p>
1768      * This method assumes that <code>this</code> matrix and the given <code>right</code> matrix both represent an {@link #isAffine() affine} transformation
1769      * (i.e. their last rows are equal to <code>(0, 0, 0, 1)</code>)
1770      * and can be used to speed up matrix multiplication if the matrices only represent affine transformations, such as translation, rotation, scaling and shearing (in any combination).
1771      * <p>
1772      * This method will not modify either the last row of <code>this</code> or the last row of <code>right</code>.
1773      * <p>
1774      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1775      * then the new matrix will be <code>M * R</code>. So when transforming a
1776      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1777      * transformation of the right matrix will be applied first!
1778      *
1779      * @param right
1780      *          the right operand of the matrix multiplication (the last row is assumed to be <code>(0, 0, 0, 1)</code>)
1781      * @return this
1782      */
1783     ref public Matrix4d mulAffine(Matrix4d right) return {
1784        mulAffine(right, this);
1785        return this;
1786     }
1787 
1788     public Matrix4d mulAffine(Matrix4d right, ref Matrix4d dest) {
1789         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
1790         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
1791         double m20 = this.m20, m21 = this.m21, m22 = this.m22;
1792         double rm00 = right.m00, rm01 = right.m01, rm02 = right.m02;
1793         double rm10 = right.m10, rm11 = right.m11, rm12 = right.m12;
1794         double rm20 = right.m20, rm21 = right.m21, rm22 = right.m22;
1795         double rm30 = right.m30, rm31 = right.m31, rm32 = right.m32;
1796         return dest
1797         ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02)))
1798         ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02)))
1799         ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02)))
1800         ._m03(m03)
1801         ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12)))
1802         ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12)))
1803         ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12)))
1804         ._m13(m13)
1805         ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22)))
1806         ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22)))
1807         ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22)))
1808         ._m23(m23)
1809         ._m30(Math.fma(m00, rm30, Math.fma(m10, rm31, Math.fma(m20, rm32, m30))))
1810         ._m31(Math.fma(m01, rm30, Math.fma(m11, rm31, Math.fma(m21, rm32, m31))))
1811         ._m32(Math.fma(m02, rm30, Math.fma(m12, rm31, Math.fma(m22, rm32, m32))))
1812         ._m33(m33)
1813         ._properties(PROPERTY_AFFINE | (this.properties & right.properties & PROPERTY_ORTHONORMAL));
1814     }
1815 
1816     public Matrix4d mulTranslationAffine(Matrix4d right, ref Matrix4d dest) {
1817         return dest
1818         ._m00(right.m00)
1819         ._m01(right.m01)
1820         ._m02(right.m02)
1821         ._m03(m03)
1822         ._m10(right.m10)
1823         ._m11(right.m11)
1824         ._m12(right.m12)
1825         ._m13(m13)
1826         ._m20(right.m20)
1827         ._m21(right.m21)
1828         ._m22(right.m22)
1829         ._m23(m23)
1830         ._m30(right.m30 + m30)
1831         ._m31(right.m31 + m31)
1832         ._m32(right.m32 + m32)
1833         ._m33(m33)
1834         ._properties(PROPERTY_AFFINE | (right.properties & PROPERTY_ORTHONORMAL));
1835     }
1836 
1837     /**
1838      * Multiply <code>this</code> orthographic projection matrix by the supplied {@link #isAffine() affine} <code>view</code> matrix.
1839      * <p>
1840      * If <code>M</code> is <code>this</code> matrix and <code>V</code> the <code>view</code> matrix,
1841      * then the new matrix will be <code>M * V</code>. So when transforming a
1842      * vector <code>v</code> with the new matrix by using <code>M * V * v</code>, the
1843      * transformation of the <code>view</code> matrix will be applied first!
1844      *
1845      * @param view
1846      *          the affine matrix which to multiply <code>this</code> with
1847      * @return this
1848      */
1849     ref public Matrix4d mulOrthoAffine(Matrix4d view) return {
1850         mulOrthoAffine(view, this);
1851         return this;
1852     }
1853 
1854     public Matrix4d mulOrthoAffine(Matrix4d view, ref Matrix4d dest) {
1855         double nm00 = m00 * view.m00;
1856         double nm01 = m11 * view.m01;
1857         double nm02 = m22 * view.m02;
1858         double nm03 = 0.0;
1859         double nm10 = m00 * view.m10;
1860         double nm11 = m11 * view.m11;
1861         double nm12 = m22 * view.m12;
1862         double nm13 = 0.0;
1863         double nm20 = m00 * view.m20;
1864         double nm21 = m11 * view.m21;
1865         double nm22 = m22 * view.m22;
1866         double nm23 = 0.0;
1867         double nm30 = m00 * view.m30 + m30;
1868         double nm31 = m11 * view.m31 + m31;
1869         double nm32 = m22 * view.m32 + m32;
1870         double nm33 = 1.0;
1871         dest._m00(nm00)
1872         ._m01(nm01)
1873         ._m02(nm02)
1874         ._m03(nm03)
1875         ._m10(nm10)
1876         ._m11(nm11)
1877         ._m12(nm12)
1878         ._m13(nm13)
1879         ._m20(nm20)
1880         ._m21(nm21)
1881         ._m22(nm22)
1882         ._m23(nm23)
1883         ._m30(nm30)
1884         ._m31(nm31)
1885         ._m32(nm32)
1886         ._m33(nm33)
1887         ._properties(PROPERTY_AFFINE);
1888         return dest;
1889     }
1890 
1891     /**
1892      * Component-wise add the upper 4x3 submatrices of <code>this</code> and <code>other</code>
1893      * by first multiplying each component of <code>other</code>'s 4x3 submatrix by <code>otherFactor</code> and
1894      * adding that result to <code>this</code>.
1895      * <p>
1896      * The matrix <code>other</code> will not be changed.
1897      * 
1898      * @param other
1899      *          the other matrix
1900      * @param otherFactor
1901      *          the factor to multiply each of the other matrix's 4x3 components
1902      * @return this
1903      */
1904     ref public Matrix4d fma4x3(Matrix4d other, double otherFactor) return {
1905         fma4x3(other, otherFactor, this);
1906         return this;
1907     }
1908 
1909     public Matrix4d fma4x3(Matrix4d other, double otherFactor, ref Matrix4d dest) {
1910         dest._m00(Math.fma(other.m00, otherFactor, m00))
1911         ._m01(Math.fma(other.m01, otherFactor, m01))
1912         ._m02(Math.fma(other.m02, otherFactor, m02))
1913         ._m03(m03)
1914         ._m10(Math.fma(other.m10, otherFactor, m10))
1915         ._m11(Math.fma(other.m11, otherFactor, m11))
1916         ._m12(Math.fma(other.m12, otherFactor, m12))
1917         ._m13(m13)
1918         ._m20(Math.fma(other.m20, otherFactor, m20))
1919         ._m21(Math.fma(other.m21, otherFactor, m21))
1920         ._m22(Math.fma(other.m22, otherFactor, m22))
1921         ._m23(m23)
1922         ._m30(Math.fma(other.m30, otherFactor, m30))
1923         ._m31(Math.fma(other.m31, otherFactor, m31))
1924         ._m32(Math.fma(other.m32, otherFactor, m32))
1925         ._m33(m33)
1926         ._properties(0);
1927         return dest;
1928     }
1929 
1930     /**
1931      * Component-wise add <code>this</code> and <code>other</code>.
1932      * 
1933      * @param other
1934      *          the other addend
1935      * @return this
1936      */
1937     ref public Matrix4d add(Matrix4d other) return {
1938         add(other, this);
1939         return this;
1940     }
1941 
1942     public Matrix4d add(Matrix4d other, ref Matrix4d dest) {
1943         dest._m00(m00 + other.m00)
1944         ._m01(m01 + other.m01)
1945         ._m02(m02 + other.m02)
1946         ._m03(m03 + other.m03)
1947         ._m10(m10 + other.m10)
1948         ._m11(m11 + other.m11)
1949         ._m12(m12 + other.m12)
1950         ._m13(m13 + other.m13)
1951         ._m20(m20 + other.m20)
1952         ._m21(m21 + other.m21)
1953         ._m22(m22 + other.m22)
1954         ._m23(m23 + other.m23)
1955         ._m30(m30 + other.m30)
1956         ._m31(m31 + other.m31)
1957         ._m32(m32 + other.m32)
1958         ._m33(m33 + other.m33)
1959         ._properties(0);
1960         return dest;
1961     }
1962 
1963     /**
1964      * Component-wise subtract <code>subtrahend</code> from <code>this</code>.
1965      * 
1966      * @param subtrahend
1967      *          the subtrahend
1968      * @return this
1969      */
1970     ref public Matrix4d sub(Matrix4d subtrahend) return {
1971         sub(subtrahend, this);
1972         return this;
1973     }
1974 
1975     public Matrix4d sub(Matrix4d subtrahend, ref Matrix4d dest) {
1976         dest._m00(m00 - subtrahend.m00)
1977         ._m01(m01 - subtrahend.m01)
1978         ._m02(m02 - subtrahend.m02)
1979         ._m03(m03 - subtrahend.m03)
1980         ._m10(m10 - subtrahend.m10)
1981         ._m11(m11 - subtrahend.m11)
1982         ._m12(m12 - subtrahend.m12)
1983         ._m13(m13 - subtrahend.m13)
1984         ._m20(m20 - subtrahend.m20)
1985         ._m21(m21 - subtrahend.m21)
1986         ._m22(m22 - subtrahend.m22)
1987         ._m23(m23 - subtrahend.m23)
1988         ._m30(m30 - subtrahend.m30)
1989         ._m31(m31 - subtrahend.m31)
1990         ._m32(m32 - subtrahend.m32)
1991         ._m33(m33 - subtrahend.m33)
1992         ._properties(0);
1993         return dest;
1994     }
1995 
1996     /**
1997      * Component-wise multiply <code>this</code> by <code>other</code>.
1998      * 
1999      * @param other
2000      *          the other matrix
2001      * @return this
2002      */
2003     ref public Matrix4d mulComponentWise(Matrix4d other) return {
2004         mulComponentWise(other, this);
2005         return this;
2006     }
2007 
2008     public Matrix4d mulComponentWise(Matrix4d other, ref Matrix4d dest) {
2009         dest._m00(m00 * other.m00)
2010         ._m01(m01 * other.m01)
2011         ._m02(m02 * other.m02)
2012         ._m03(m03 * other.m03)
2013         ._m10(m10 * other.m10)
2014         ._m11(m11 * other.m11)
2015         ._m12(m12 * other.m12)
2016         ._m13(m13 * other.m13)
2017         ._m20(m20 * other.m20)
2018         ._m21(m21 * other.m21)
2019         ._m22(m22 * other.m22)
2020         ._m23(m23 * other.m23)
2021         ._m30(m30 * other.m30)
2022         ._m31(m31 * other.m31)
2023         ._m32(m32 * other.m32)
2024         ._m33(m33 * other.m33)
2025         ._properties(0);
2026         return dest;
2027     }
2028 
2029     /**
2030      * Component-wise add the upper 4x3 submatrices of <code>this</code> and <code>other</code>.
2031      * 
2032      * @param other
2033      *          the other addend
2034      * @return this
2035      */
2036     ref public Matrix4d add4x3(Matrix4d other) return {
2037         add4x3(other, this);
2038         return this;
2039     }
2040 
2041     public Matrix4d add4x3(Matrix4d other, ref Matrix4d dest) {
2042         dest._m00(m00 + other.m00)
2043         ._m01(m01 + other.m01)
2044         ._m02(m02 + other.m02)
2045         ._m03(m03)
2046         ._m10(m10 + other.m10)
2047         ._m11(m11 + other.m11)
2048         ._m12(m12 + other.m12)
2049         ._m13(m13)
2050         ._m20(m20 + other.m20)
2051         ._m21(m21 + other.m21)
2052         ._m22(m22 + other.m22)
2053         ._m23(m23)
2054         ._m30(m30 + other.m30)
2055         ._m31(m31 + other.m31)
2056         ._m32(m32 + other.m32)
2057         ._m33(m33)
2058         ._properties(0);
2059         return dest;
2060     }
2061 
2062     /**
2063      * Component-wise subtract the upper 4x3 submatrices of <code>subtrahend</code> from <code>this</code>.
2064      * 
2065      * @param subtrahend
2066      *          the subtrahend
2067      * @return this
2068      */
2069     ref public Matrix4d sub4x3(Matrix4d subtrahend) return {
2070         sub4x3(subtrahend, this);
2071         return this;
2072     }
2073 
2074     public Matrix4d sub4x3(Matrix4d subtrahend, ref Matrix4d dest) {
2075         dest._m00(m00 - subtrahend.m00)
2076         ._m01(m01 - subtrahend.m01)
2077         ._m02(m02 - subtrahend.m02)
2078         ._m03(m03)
2079         ._m10(m10 - subtrahend.m10)
2080         ._m11(m11 - subtrahend.m11)
2081         ._m12(m12 - subtrahend.m12)
2082         ._m13(m13)
2083         ._m20(m20 - subtrahend.m20)
2084         ._m21(m21 - subtrahend.m21)
2085         ._m22(m22 - subtrahend.m22)
2086         ._m23(m23)
2087         ._m30(m30 - subtrahend.m30)
2088         ._m31(m31 - subtrahend.m31)
2089         ._m32(m32 - subtrahend.m32)
2090         ._m33(m33)
2091         ._properties(0);
2092         return dest;
2093     }
2094 
2095     /**
2096      * Component-wise multiply the upper 4x3 submatrices of <code>this</code> by <code>other</code>.
2097      * 
2098      * @param other
2099      *          the other matrix
2100      * @return this
2101      */
2102     ref public Matrix4d mul4x3ComponentWise(Matrix4d other) return {
2103         mul4x3ComponentWise(other, this);
2104         return this;
2105     }
2106 
2107     public Matrix4d mul4x3ComponentWise(Matrix4d other, ref Matrix4d dest) {
2108         dest._m00(m00 * other.m00)
2109         ._m01(m01 * other.m01)
2110         ._m02(m02 * other.m02)
2111         ._m03(m03)
2112         ._m10(m10 * other.m10)
2113         ._m11(m11 * other.m11)
2114         ._m12(m12 * other.m12)
2115         ._m13(m13)
2116         ._m20(m20 * other.m20)
2117         ._m21(m21 * other.m21)
2118         ._m22(m22 * other.m22)
2119         ._m23(m23)
2120         ._m30(m30 * other.m30)
2121         ._m31(m31 * other.m31)
2122         ._m32(m32 * other.m32)
2123         ._m33(m33)
2124         ._properties(0);
2125         return dest;
2126     }
2127 
2128     /** Set the values within this matrix to the supplied double values. The matrix will look like this:<br><br>
2129      *  
2130      * m00, m10, m20, m30<br>
2131      * m01, m11, m21, m31<br>
2132      * m02, m12, m22, m32<br>
2133      * m03, m13, m23, m33
2134      *
2135      * @param m00
2136      *          the new value of m00
2137      * @param m01
2138      *          the new value of m01
2139      * @param m02
2140      *          the new value of m02
2141      * @param m03
2142      *          the new value of m03
2143      * @param m10
2144      *          the new value of m10
2145      * @param m11
2146      *          the new value of m11
2147      * @param m12
2148      *          the new value of m12
2149      * @param m13
2150      *          the new value of m13
2151      * @param m20
2152      *          the new value of m20
2153      * @param m21
2154      *          the new value of m21
2155      * @param m22
2156      *          the new value of m22
2157      * @param m23
2158      *          the new value of m23
2159      * @param m30
2160      *          the new value of m30
2161      * @param m31
2162      *          the new value of m31
2163      * @param m32
2164      *          the new value of m32
2165      * @param m33
2166      *          the new value of m33
2167      * @return this
2168      */
2169     ref public Matrix4d set(double m00, double m01, double m02,double m03,
2170                         double m10, double m11, double m12, double m13,
2171                         double m20, double m21, double m22, double m23, 
2172                         double m30, double m31, double m32, double m33) return {
2173         setm00(m00);
2174         setm10(m10);
2175         setm20(m20);
2176         setm30(m30);
2177         setm01(m01);
2178         setm11(m11);
2179         setm21(m21);
2180         setm31(m31);
2181         setm02(m02);
2182         setm12(m12);
2183         setm22(m22);
2184         setm32(m32);
2185         setm03(m03);
2186         setm13(m13);
2187         setm23(m23);
2188         setm33(m33);
2189         return determineProperties();
2190     }
2191 
2192 
2193     /**
2194      * Set the values in the matrix using a double array that contains the matrix elements in column-major order.
2195      * <p>
2196      * The results will look like this:<br><br>
2197      * 
2198      * 0, 4, 8, 12<br>
2199      * 1, 5, 9, 13<br>
2200      * 2, 6, 10, 14<br>
2201      * 3, 7, 11, 15<br>
2202      * 
2203      * @see #set(double[], int)
2204      * 
2205      * @param m
2206      *          the array to read the matrix values from
2207      * @return this
2208      */
2209     ref public Matrix4d set(double[] m) return {
2210         return set(m, 0);
2211     }
2212 
2213     /**
2214      * Set the values in the matrix using a double array that contains the matrix elements in column-major order.
2215      * <p>
2216      * The results will look like this:<br><br>
2217      * 
2218      * 0, 4, 8, 12<br>
2219      * 1, 5, 9, 13<br>
2220      * 2, 6, 10, 14<br>
2221      * 3, 7, 11, 15<br>
2222      * 
2223      * @see #set(double[])
2224      * 
2225      * @param m
2226      *          the array to read the matrix values from
2227      * @param off
2228      *          the offset into the array
2229      * @return this
2230      */
2231     ref public Matrix4d set(double[] m, int off) return {
2232         return
2233         _m00(m[off+0]).
2234         _m01(m[off+1]).
2235         _m02(m[off+2]).
2236         _m03(m[off+3]).
2237         _m10(m[off+4]).
2238         _m11(m[off+5]).
2239         _m12(m[off+6]).
2240         _m13(m[off+7]).
2241         _m20(m[off+8]).
2242         _m21(m[off+9]).
2243         _m22(m[off+10]).
2244         _m23(m[off+11]).
2245         _m30(m[off+12]).
2246         _m31(m[off+13]).
2247         _m32(m[off+14]).
2248         _m33(m[off+15]).
2249         determineProperties();
2250     }
2251 
2252 
2253     /**
2254      * Set the four columns of this matrix to the supplied vectors, respectively.
2255      * 
2256      * @param col0
2257      *          the first column
2258      * @param col1
2259      *          the second column
2260      * @param col2
2261      *          the third column
2262      * @param col3
2263      *          the fourth column
2264      * @return this
2265      */
2266     ref public Matrix4d set(ref Vector4d col0, Vector4d col1, Vector4d col2, Vector4d col3) return {
2267         return
2268         _m00(col0.x).
2269         _m01(col0.y).
2270         _m02(col0.z).
2271         _m03(col0.w).
2272         _m10(col1.x).
2273         _m11(col1.y).
2274         _m12(col1.z).
2275         _m13(col1.w).
2276         _m20(col2.x).
2277         _m21(col2.y).
2278         _m22(col2.z).
2279         _m23(col2.w).
2280         _m30(col3.x).
2281         _m31(col3.y).
2282         _m32(col3.z).
2283         _m33(col3.w).
2284         determineProperties();
2285     }
2286 
2287     public double determinant() {
2288         if ((properties & PROPERTY_AFFINE) != 0)
2289             return determinantAffine();
2290         return (m00 * m11 - m01 * m10) * (m22 * m33 - m23 * m32)
2291              + (m02 * m10 - m00 * m12) * (m21 * m33 - m23 * m31)
2292              + (m00 * m13 - m03 * m10) * (m21 * m32 - m22 * m31) 
2293              + (m01 * m12 - m02 * m11) * (m20 * m33 - m23 * m30)
2294              + (m03 * m11 - m01 * m13) * (m20 * m32 - m22 * m30) 
2295              + (m02 * m13 - m03 * m12) * (m20 * m31 - m21 * m30);
2296     }
2297 
2298     public double determinant3x3() {
2299         return (m00 * m11 - m01 * m10) * m22
2300              + (m02 * m10 - m00 * m12) * m21
2301              + (m01 * m12 - m02 * m11) * m20;
2302     }
2303 
2304     public double determinantAffine() {
2305         return (m00 * m11 - m01 * m10) * m22
2306              + (m02 * m10 - m00 * m12) * m21
2307              + (m01 * m12 - m02 * m11) * m20;
2308     }
2309 
2310     /**
2311      * Invert this matrix.
2312      * <p>
2313      * If <code>this</code> matrix represents an {@link #isAffine() affine} transformation, such as translation, rotation, scaling and shearing,
2314      * and thus its last row is equal to <code>(0, 0, 0, 1)</code>, then {@link #invertAffine()} can be used instead of this method.
2315      * 
2316      * @see #invertAffine()
2317      * 
2318      * @return this
2319      */
2320     ref public Matrix4d invert() return {
2321         invert(this);
2322         return this;
2323     }
2324 
2325     public Matrix4d invert(ref Matrix4d dest) {
2326         if ((properties & PROPERTY_IDENTITY) != 0)
2327             return dest.identity();
2328         else if ((properties & PROPERTY_TRANSLATION) != 0)
2329             return invertTranslation(dest);
2330         else if ((properties & PROPERTY_ORTHONORMAL) != 0)
2331             return invertOrthonormal(dest);
2332         else if ((properties & PROPERTY_AFFINE) != 0)
2333             return invertAffine(dest);
2334         else if ((properties & PROPERTY_PERSPECTIVE) != 0)
2335             return invertPerspective(dest);
2336         return invertGeneric(dest);
2337     }
2338     private Matrix4d invertTranslation(ref Matrix4d dest) {
2339         if (dest != this)
2340             dest.set(this);
2341         dest._m30(-m30)
2342         ._m31(-m31)
2343         ._m32(-m32)
2344         ._properties(PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL);
2345         return dest;
2346     }
2347     private Matrix4d invertOrthonormal(ref Matrix4d dest) {
2348         double nm30 = -(m00 * m30 + m01 * m31 + m02 * m32);
2349         double nm31 = -(m10 * m30 + m11 * m31 + m12 * m32);
2350         double nm32 = -(m20 * m30 + m21 * m31 + m22 * m32);
2351         double m01 = this.m01;
2352         double m02 = this.m02;
2353         double m12 = this.m12;
2354         dest._m00(m00)
2355         ._m01(m10)
2356         ._m02(m20)
2357         ._m03(0.0)
2358         ._m10(m01)
2359         ._m11(m11)
2360         ._m12(m21)
2361         ._m13(0.0)
2362         ._m20(m02)
2363         ._m21(m12)
2364         ._m22(m22)
2365         ._m23(0.0)
2366         ._m30(nm30)
2367         ._m31(nm31)
2368         ._m32(nm32)
2369         ._m33(1.0)
2370         ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
2371         return dest;
2372     }
2373     private Matrix4d invertGeneric(ref Matrix4d dest) {
2374         if (this != dest)
2375             return invertGenericNonThis(dest);
2376         return invertGenericThis(dest);
2377     }
2378     private Matrix4d invertGenericNonThis(ref Matrix4d dest) {
2379         double a = m00 * m11 - m01 * m10;
2380         double b = m00 * m12 - m02 * m10;
2381         double c = m00 * m13 - m03 * m10;
2382         double d = m01 * m12 - m02 * m11;
2383         double e = m01 * m13 - m03 * m11;
2384         double f = m02 * m13 - m03 * m12;
2385         double g = m20 * m31 - m21 * m30;
2386         double h = m20 * m32 - m22 * m30;
2387         double i = m20 * m33 - m23 * m30;
2388         double j = m21 * m32 - m22 * m31;
2389         double k = m21 * m33 - m23 * m31;
2390         double l = m22 * m33 - m23 * m32;
2391         double det = a * l - b * k + c * j + d * i - e * h + f * g;
2392         det = 1.0 / det;
2393         return dest
2394         ._m00(Math.fma( m11, l, Math.fma(-m12, k,  m13 * j)) * det)
2395         ._m01(Math.fma(-m01, l, Math.fma( m02, k, -m03 * j)) * det)
2396         ._m02(Math.fma( m31, f, Math.fma(-m32, e,  m33 * d)) * det)
2397         ._m03(Math.fma(-m21, f, Math.fma( m22, e, -m23 * d)) * det)
2398         ._m10(Math.fma(-m10, l, Math.fma( m12, i, -m13 * h)) * det)
2399         ._m11(Math.fma( m00, l, Math.fma(-m02, i,  m03 * h)) * det)
2400         ._m12(Math.fma(-m30, f, Math.fma( m32, c, -m33 * b)) * det)
2401         ._m13(Math.fma( m20, f, Math.fma(-m22, c,  m23 * b)) * det)
2402         ._m20(Math.fma( m10, k, Math.fma(-m11, i,  m13 * g)) * det)
2403         ._m21(Math.fma(-m00, k, Math.fma( m01, i, -m03 * g)) * det)
2404         ._m22(Math.fma( m30, e, Math.fma(-m31, c,  m33 * a)) * det)
2405         ._m23(Math.fma(-m20, e, Math.fma( m21, c, -m23 * a)) * det)
2406         ._m30(Math.fma(-m10, j, Math.fma( m11, h, -m12 * g)) * det)
2407         ._m31(Math.fma( m00, j, Math.fma(-m01, h,  m02 * g)) * det)
2408         ._m32(Math.fma(-m30, d, Math.fma( m31, b, -m32 * a)) * det)
2409         ._m33(Math.fma( m20, d, Math.fma(-m21, b,  m22 * a)) * det)
2410         ._properties(0);
2411     }
2412     private Matrix4d invertGenericThis(ref Matrix4d dest) {
2413         double a = m00 * m11 - m01 * m10;
2414         double b = m00 * m12 - m02 * m10;
2415         double c = m00 * m13 - m03 * m10;
2416         double d = m01 * m12 - m02 * m11;
2417         double e = m01 * m13 - m03 * m11;
2418         double f = m02 * m13 - m03 * m12;
2419         double g = m20 * m31 - m21 * m30;
2420         double h = m20 * m32 - m22 * m30;
2421         double i = m20 * m33 - m23 * m30;
2422         double j = m21 * m32 - m22 * m31;
2423         double k = m21 * m33 - m23 * m31;
2424         double l = m22 * m33 - m23 * m32;
2425         double det = a * l - b * k + c * j + d * i - e * h + f * g;
2426         det = 1.0 / det;
2427         double nm00 = Math.fma( m11, l, Math.fma(-m12, k,  m13 * j)) * det;
2428         double nm01 = Math.fma(-m01, l, Math.fma( m02, k, -m03 * j)) * det;
2429         double nm02 = Math.fma( m31, f, Math.fma(-m32, e,  m33 * d)) * det;
2430         double nm03 = Math.fma(-m21, f, Math.fma( m22, e, -m23 * d)) * det;
2431         double nm10 = Math.fma(-m10, l, Math.fma( m12, i, -m13 * h)) * det;
2432         double nm11 = Math.fma( m00, l, Math.fma(-m02, i,  m03 * h)) * det;
2433         double nm12 = Math.fma(-m30, f, Math.fma( m32, c, -m33 * b)) * det;
2434         double nm13 = Math.fma( m20, f, Math.fma(-m22, c,  m23 * b)) * det;
2435         double nm20 = Math.fma( m10, k, Math.fma(-m11, i,  m13 * g)) * det;
2436         double nm21 = Math.fma(-m00, k, Math.fma( m01, i, -m03 * g)) * det;
2437         double nm22 = Math.fma( m30, e, Math.fma(-m31, c,  m33 * a)) * det;
2438         double nm23 = Math.fma(-m20, e, Math.fma( m21, c, -m23 * a)) * det;
2439         double nm30 = Math.fma(-m10, j, Math.fma( m11, h, -m12 * g)) * det;
2440         double nm31 = Math.fma( m00, j, Math.fma(-m01, h,  m02 * g)) * det;
2441         double nm32 = Math.fma(-m30, d, Math.fma( m31, b, -m32 * a)) * det;
2442         double nm33 = Math.fma( m20, d, Math.fma(-m21, b,  m22 * a)) * det;
2443         return dest
2444         ._m00(nm00)
2445         ._m01(nm01)
2446         ._m02(nm02)
2447         ._m03(nm03)
2448         ._m10(nm10)
2449         ._m11(nm11)
2450         ._m12(nm12)
2451         ._m13(nm13)
2452         ._m20(nm20)
2453         ._m21(nm21)
2454         ._m22(nm22)
2455         ._m23(nm23)
2456         ._m30(nm30)
2457         ._m31(nm31)
2458         ._m32(nm32)
2459         ._m33(nm33)
2460         ._properties(0);
2461     }
2462 
2463     public Matrix4d invertPerspective(ref Matrix4d dest) {
2464         double a =  1.0 / (m00 * m11);
2465         double l = -1.0 / (m23 * m32);
2466         dest.set(m11 * a, 0, 0, 0,
2467                  0, m00 * a, 0, 0,
2468                  0, 0, 0, -m23 * l,
2469                  0, 0, -m32 * l, m22 * l);
2470         return dest;
2471     }
2472 
2473     /**
2474      * If <code>this</code> is a perspective projection matrix obtained via one of the {@link #perspective(double, double, double, double) perspective()} methods
2475      * or via {@link #setPerspective(double, double, double, double) setPerspective()}, that is, if <code>this</code> is a symmetrical perspective frustum transformation,
2476      * then this method builds the inverse of <code>this</code>.
2477      * <p>
2478      * This method can be used to quickly obtain the inverse of a perspective projection matrix when being obtained via {@link #perspective(double, double, double, double) perspective()}.
2479      * 
2480      * @see #perspective(double, double, double, double)
2481      * 
2482      * @return this
2483      */
2484     ref public Matrix4d invertPerspective() return {
2485         invertPerspective(this);
2486         return this;
2487     }
2488 
2489     public Matrix4d invertFrustum(ref Matrix4d dest) {
2490         double invM00 = 1.0 / m00;
2491         double invM11 = 1.0 / m11;
2492         double invM23 = 1.0 / m23;
2493         double invM32 = 1.0 / m32;
2494         dest.set(invM00, 0, 0, 0,
2495                  0, invM11, 0, 0,
2496                  0, 0, 0, invM32,
2497                  -m20 * invM00 * invM23, -m21 * invM11 * invM23, invM23, -m22 * invM23 * invM32);
2498         return dest;
2499     }
2500 
2501     /**
2502      * If <code>this</code> is an arbitrary perspective projection matrix obtained via one of the {@link #frustum(double, double, double, double, double, double) frustum()}  methods
2503      * or via {@link #setFrustum(double, double, double, double, double, double) setFrustum()},
2504      * then this method builds the inverse of <code>this</code>.
2505      * <p>
2506      * This method can be used to quickly obtain the inverse of a perspective projection matrix.
2507      * <p>
2508      * If this matrix represents a symmetric perspective frustum transformation, as obtained via {@link #perspective(double, double, double, double) perspective()}, then
2509      * {@link #invertPerspective()} should be used instead.
2510      * 
2511      * @see #frustum(double, double, double, double, double, double)
2512      * @see #invertPerspective()
2513      * 
2514      * @return this
2515      */
2516     ref public Matrix4d invertFrustum() return {
2517         invertFrustum(this);
2518         return this;
2519     }
2520 
2521     public Matrix4d invertOrtho(ref Matrix4d dest) {
2522         double invM00 = 1.0 / m00;
2523         double invM11 = 1.0 / m11;
2524         double invM22 = 1.0 / m22;
2525         dest.set(invM00, 0, 0, 0,
2526                  0, invM11, 0, 0,
2527                  0, 0, invM22, 0,
2528                  -m30 * invM00, -m31 * invM11, -m32 * invM22, 1)
2529         ._properties(PROPERTY_AFFINE | (this.properties & PROPERTY_ORTHONORMAL));
2530         return dest;
2531     }
2532 
2533     /**
2534      * Invert <code>this</code> orthographic projection matrix.
2535      * <p>
2536      * This method can be used to quickly obtain the inverse of an orthographic projection matrix.
2537      * 
2538      * @return this
2539      */
2540     ref public Matrix4d invertOrtho() return {
2541         invertOrtho(this);
2542         return this;
2543     }
2544 
2545     public Matrix4d invertPerspectiveView(Matrix4d view, ref Matrix4d dest) {
2546         double a =  1.0 / (m00 * m11);
2547         double l = -1.0 / (m23 * m32);
2548         double pm00 =  m11 * a;
2549         double pm11 =  m00 * a;
2550         double pm23 = -m23 * l;
2551         double pm32 = -m32 * l;
2552         double pm33 =  m22 * l;
2553         double vm30 = -view.m00 * view.m30 - view.m01 * view.m31 - view.m02 * view.m32;
2554         double vm31 = -view.m10 * view.m30 - view.m11 * view.m31 - view.m12 * view.m32;
2555         double vm32 = -view.m20 * view.m30 - view.m21 * view.m31 - view.m22 * view.m32;
2556         double nm10 = view.m01 * pm11;
2557         double nm30 = view.m02 * pm32 + vm30 * pm33;
2558         double nm31 = view.m12 * pm32 + vm31 * pm33;
2559         double nm32 = view.m22 * pm32 + vm32 * pm33;
2560         return dest
2561         ._m00(view.m00 * pm00)
2562         ._m01(view.m10 * pm00)
2563         ._m02(view.m20 * pm00)
2564         ._m03(0.0)
2565         ._m10(nm10)
2566         ._m11(view.m11 * pm11)
2567         ._m12(view.m21 * pm11)
2568         ._m13(0.0)
2569         ._m20(vm30 * pm23)
2570         ._m21(vm31 * pm23)
2571         ._m22(vm32 * pm23)
2572         ._m23(pm23)
2573         ._m30(nm30)
2574         ._m31(nm31)
2575         ._m32(nm32)
2576         ._m33(pm33)
2577         ._properties(0);
2578     }
2579 
2580     public Matrix4d invertPerspectiveView(Matrix4x3d view, ref Matrix4d dest) {
2581         double a =  1.0 / (m00 * m11);
2582         double l = -1.0 / (m23 * m32);
2583         double pm00 =  m11 * a;
2584         double pm11 =  m00 * a;
2585         double pm23 = -m23 * l;
2586         double pm32 = -m32 * l;
2587         double pm33 =  m22 * l;
2588         double vm30 = -view.m00 * view.m30 - view.m01 * view.m31 - view.m02 * view.m32;
2589         double vm31 = -view.m10 * view.m30 - view.m11 * view.m31 - view.m12 * view.m32;
2590         double vm32 = -view.m20 * view.m30 - view.m21 * view.m31 - view.m22 * view.m32;
2591         return dest
2592         ._m00(view.m00 * pm00)
2593         ._m01(view.m10 * pm00)
2594         ._m02(view.m20 * pm00)
2595         ._m03(0.0)
2596         ._m10(view.m01 * pm11)
2597         ._m11(view.m11 * pm11)
2598         ._m12(view.m21 * pm11)
2599         ._m13(0.0)
2600         ._m20(vm30 * pm23)
2601         ._m21(vm31 * pm23)
2602         ._m22(vm32 * pm23)
2603         ._m23(pm23)
2604         ._m30(view.m02 * pm32 + vm30 * pm33)
2605         ._m31(view.m12 * pm32 + vm31 * pm33)
2606         ._m32(view.m22 * pm32 + vm32 * pm33)
2607         ._m33(pm33)
2608         ._properties(0);
2609     }
2610 
2611     public Matrix4d invertAffine(ref Matrix4d dest) {
2612         double m11m00 = m00 * m11, m10m01 = m01 * m10, m10m02 = m02 * m10;
2613         double m12m00 = m00 * m12, m12m01 = m01 * m12, m11m02 = m02 * m11;
2614         double s = 1.0 / ((m11m00 - m10m01) * m22 + (m10m02 - m12m00) * m21 + (m12m01 - m11m02) * m20);
2615         double m10m22 = m10 * m22, m10m21 = m10 * m21, m11m22 = m11 * m22;
2616         double m11m20 = m11 * m20, m12m21 = m12 * m21, m12m20 = m12 * m20;
2617         double m20m02 = m20 * m02, m20m01 = m20 * m01, m21m02 = m21 * m02;
2618         double m21m00 = m21 * m00, m22m01 = m22 * m01, m22m00 = m22 * m00;
2619         double nm00 = (m11m22 - m12m21) * s;
2620         double nm01 = (m21m02 - m22m01) * s;
2621         double nm02 = (m12m01 - m11m02) * s;
2622         double nm10 = (m12m20 - m10m22) * s;
2623         double nm11 = (m22m00 - m20m02) * s;
2624         double nm12 = (m10m02 - m12m00) * s;
2625         double nm20 = (m10m21 - m11m20) * s;
2626         double nm21 = (m20m01 - m21m00) * s;
2627         double nm22 = (m11m00 - m10m01) * s;
2628         double nm30 = (m10m22 * m31 - m10m21 * m32 + m11m20 * m32 - m11m22 * m30 + m12m21 * m30 - m12m20 * m31) * s;
2629         double nm31 = (m20m02 * m31 - m20m01 * m32 + m21m00 * m32 - m21m02 * m30 + m22m01 * m30 - m22m00 * m31) * s;
2630         double nm32 = (m11m02 * m30 - m12m01 * m30 + m12m00 * m31 - m10m02 * m31 + m10m01 * m32 - m11m00 * m32) * s;
2631         dest._m00(nm00)
2632         ._m01(nm01)
2633         ._m02(nm02)
2634         ._m03(0.0)
2635         ._m10(nm10)
2636         ._m11(nm11)
2637         ._m12(nm12)
2638         ._m13(0.0)
2639         ._m20(nm20)
2640         ._m21(nm21)
2641         ._m22(nm22)
2642         ._m23(0.0)
2643         ._m30(nm30)
2644         ._m31(nm31)
2645         ._m32(nm32)
2646         ._m33(1.0)
2647         ._properties(PROPERTY_AFFINE);
2648         return dest;
2649     }
2650 
2651     /**
2652      * Invert this matrix by assuming that it is an {@link #isAffine() affine} transformation (i.e. its last row is equal to <code>(0, 0, 0, 1)</code>).
2653      * 
2654      * @return this
2655      */
2656     ref public Matrix4d invertAffine() return {
2657         invertAffine(this);
2658         return this;
2659     }
2660 
2661     /**
2662      * Transpose this matrix.
2663      * 
2664      * @return this
2665      */
2666     ref public Matrix4d transpose() return {
2667         transpose(this);
2668         return this;
2669     }
2670 
2671     public Matrix4d transpose(ref Matrix4d dest) {
2672         if ((properties & PROPERTY_IDENTITY) != 0)
2673             return dest.identity();
2674         else if (this != dest)
2675             return transposeNonThisGeneric(dest);
2676         return transposeThisGeneric(dest);
2677     }
2678     private Matrix4d transposeNonThisGeneric(ref Matrix4d dest) {
2679         return dest
2680         ._m00(m00)
2681         ._m01(m10)
2682         ._m02(m20)
2683         ._m03(m30)
2684         ._m10(m01)
2685         ._m11(m11)
2686         ._m12(m21)
2687         ._m13(m31)
2688         ._m20(m02)
2689         ._m21(m12)
2690         ._m22(m22)
2691         ._m23(m32)
2692         ._m30(m03)
2693         ._m31(m13)
2694         ._m32(m23)
2695         ._m33(m33)
2696         ._properties(0);
2697     }
2698     private Matrix4d transposeThisGeneric(ref Matrix4d dest) {
2699         double nm10 = m01;
2700         double nm20 = m02;
2701         double nm21 = m12;
2702         double nm30 = m03;
2703         double nm31 = m13;
2704         double nm32 = m23;
2705         return dest
2706         ._m01(m10)
2707         ._m02(m20)
2708         ._m03(m30)
2709         ._m10(nm10)
2710         ._m12(m21)
2711         ._m13(m31)
2712         ._m20(nm20)
2713         ._m21(nm21)
2714         ._m23(m32)
2715         ._m30(nm30)
2716         ._m31(nm31)
2717         ._m32(nm32)
2718         ._properties(0);
2719     }
2720 
2721     /**
2722      * Transpose only the upper left 3x3 submatrix of this matrix.
2723      * <p>
2724      * All other matrix elements are left unchanged.
2725      * 
2726      * @return this
2727      */
2728     ref public Matrix4d transpose3x3() return {
2729         transpose3x3(this);
2730         return this;
2731     }
2732 
2733     public Matrix4d transpose3x3(ref Matrix4d dest) {
2734         double nm10 = m01, nm20 = m02, nm21 = m12;
2735         return dest
2736         ._m00(m00)
2737         ._m01(m10)
2738         ._m02(m20)
2739         ._m10(nm10)
2740         ._m11(m11)
2741         ._m12(m21)
2742         ._m20(nm20)
2743         ._m21(nm21)
2744         ._m22(m22)
2745         ._properties(this.properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
2746     }
2747 
2748     public Matrix3d transpose3x3(ref Matrix3d dest) {
2749         return dest
2750         ._m00(m00)
2751         ._m01(m10)
2752         ._m02(m20)
2753         ._m10(m01)
2754         ._m11(m11)
2755         ._m12(m21)
2756         ._m20(m02)
2757         ._m21(m12)
2758         ._m22(m22);
2759     }
2760 
2761     /**
2762      * Set this matrix to be a simple translation matrix.
2763      * <p>
2764      * The resulting matrix can be multiplied against another transformation
2765      * matrix to obtain an additional translation.
2766      * 
2767      * @param x
2768      *          the offset to translate in x
2769      * @param y
2770      *          the offset to translate in y
2771      * @param z
2772      *          the offset to translate in z
2773      * @return this
2774      */
2775     ref public Matrix4d translation(double x, double y, double z) return {
2776         if ((properties & PROPERTY_IDENTITY) == 0)
2777             this._identity();
2778         return this.
2779         _m30(x).
2780         _m31(y).
2781         _m32(z).
2782         _m33(1.0).
2783         _properties(PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL);
2784     }
2785 
2786 
2787     /**
2788      * Set this matrix to be a simple translation matrix.
2789      * <p>
2790      * The resulting matrix can be multiplied against another transformation
2791      * matrix to obtain an additional translation.
2792      *
2793      * @param offset
2794      *              the offsets in x, y and z to translate
2795      * @return this
2796      */
2797     ref public Matrix4d translation(ref Vector3d offset) return {
2798         return translation(offset.x, offset.y, offset.z);
2799     }
2800 
2801     /**
2802      * Set only the translation components <code>(m30, m31, m32)</code> of this matrix to the given values <code>(x, y, z)</code>.
2803      * <p>
2804      * To build a translation matrix instead, use {@link #translation(double, double, double)}.
2805      * To apply a translation, use {@link #translate(double, double, double)}.
2806      * 
2807      * @see #translation(double, double, double)
2808      * @see #translate(double, double, double)
2809      * 
2810      * @param x
2811      *          the units to translate in x
2812      * @param y
2813      *          the units to translate in y
2814      * @param z
2815      *          the units to translate in z
2816      * @return this
2817      */
2818     ref public Matrix4d setTranslation(double x, double y, double z) return {
2819         _m30(x).
2820         _m31(y).
2821         _m32(z).
2822         properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY);
2823         return this;
2824     }
2825 
2826     /**
2827      * Set only the translation components <code>(m30, m31, m32)</code> of this matrix to the given values <code>(xyz.x, xyz.y, xyz.z)</code>.
2828      * <p>
2829      * To build a translation matrix instead, use {@link #translation(ref Vector3d)}.
2830      * To apply a translation, use {@link #translate(ref Vector3d)}.
2831      * 
2832      * @see #translation(ref Vector3d)
2833      * @see #translate(ref Vector3d)
2834      * 
2835      * @param xyz
2836      *          the units to translate in <code>(x, y, z)</code>
2837      * @return this
2838      */
2839     ref public Matrix4d setTranslation(ref Vector3d xyz) return {
2840         return setTranslation(xyz.x, xyz.y, xyz.z);
2841     }
2842 
2843     public Vector3d getTranslation(ref Vector3d dest) {
2844         dest.x = m30;
2845         dest.y = m31;
2846         dest.z = m32;
2847         return dest;
2848     }
2849 
2850     public Vector3d getScale(ref Vector3d dest) {
2851         dest.x = Math.sqrt(m00 * m00 + m01 * m01 + m02 * m02);
2852         dest.y = Math.sqrt(m10 * m10 + m11 * m11 + m12 * m12);
2853         dest.z = Math.sqrt(m20 * m20 + m21 * m21 + m22 * m22);
2854         return dest;
2855     }
2856 
2857     public Matrix4d get(ref Matrix4d dest) {
2858         return dest.set(this);
2859     }
2860 
2861     public Matrix4x3d get4x3(ref Matrix4x3d dest) {
2862         return dest.set(this);
2863     }
2864 
2865     public Matrix3d get3x3(ref Matrix3d dest) {
2866         return dest.set(this);
2867     }
2868 
2869     public Quaterniond getUnnormalizedRotation(ref Quaterniond dest) {
2870         return dest.setFromUnnormalized(this);
2871     }
2872 
2873     public Quaterniond getNormalizedRotation(ref Quaterniond dest) {
2874         return dest.setFromNormalized(this);
2875     }
2876 
2877     // Additional functionality for D (becomes a DoubleBuffer)
2878     public double[16] getDoubleArray() {
2879         double[16] dest;
2880         dest[0]  = m00;
2881         dest[1]  = m01;
2882         dest[2]  = m02;
2883         dest[3]  = m03;
2884         dest[4]  = m10;
2885         dest[5]  = m11;
2886         dest[6]  = m12;
2887         dest[7]  = m13;
2888         dest[8]  = m20;
2889         dest[9]  = m21;
2890         dest[10] = m22;
2891         dest[11] = m23;
2892         dest[12] = m30;
2893         dest[13] = m31;
2894         dest[14] = m32;
2895         dest[15] = m33;
2896         return dest;
2897     }
2898 
2899     // Additional functionality for D (becomes a DoubleBuffer)
2900     public double[16] getDoubleArray(ref double[16] dest, int offset) {
2901         dest[offset+0]  = m00;
2902         dest[offset+1]  = m01;
2903         dest[offset+2]  = m02;
2904         dest[offset+3]  = m03;
2905         dest[offset+4]  = m10;
2906         dest[offset+5]  = m11;
2907         dest[offset+6]  = m12;
2908         dest[offset+7]  = m13;
2909         dest[offset+8]  = m20;
2910         dest[offset+9]  = m21;
2911         dest[offset+10] = m22;
2912         dest[offset+11] = m23;
2913         dest[offset+12] = m30;
2914         dest[offset+13] = m31;
2915         dest[offset+14] = m32;
2916         dest[offset+15] = m33;
2917         return dest;
2918     }
2919 
2920     public double[] get(ref double[] dest, int offset) {
2921         dest[offset+0]  = m00;
2922         dest[offset+1]  = m01;
2923         dest[offset+2]  = m02;
2924         dest[offset+3]  = m03;
2925         dest[offset+4]  = m10;
2926         dest[offset+5]  = m11;
2927         dest[offset+6]  = m12;
2928         dest[offset+7]  = m13;
2929         dest[offset+8]  = m20;
2930         dest[offset+9]  = m21;
2931         dest[offset+10] = m22;
2932         dest[offset+11] = m23;
2933         dest[offset+12] = m30;
2934         dest[offset+13] = m31;
2935         dest[offset+14] = m32;
2936         dest[offset+15] = m33;
2937         return dest;
2938     }
2939 
2940     public double[] get(ref double[] dest) {
2941         return get(dest, 0);
2942     }
2943 
2944     /**
2945      * Set all the values within this matrix to 0.
2946      * 
2947      * @return this
2948      */
2949     ref public Matrix4d zero() return {
2950         return
2951         _m00(0.0).
2952         _m01(0.0).
2953         _m02(0.0).
2954         _m03(0.0).
2955         _m10(0.0).
2956         _m11(0.0).
2957         _m12(0.0).
2958         _m13(0.0).
2959         _m20(0.0).
2960         _m21(0.0).
2961         _m22(0.0).
2962         _m23(0.0).
2963         _m30(0.0).
2964         _m31(0.0).
2965         _m32(0.0).
2966         _m33(0.0).
2967         _properties(0);
2968     }
2969 
2970     /**
2971      * Set this matrix to be a simple scale matrix, which scales all axes uniformly by the given factor.
2972      * <p>
2973      * The resulting matrix can be multiplied against another transformation
2974      * matrix to obtain an additional scaling.
2975      * <p>
2976      * In order to post-multiply a scaling transformation directly to a
2977      * matrix, use {@link #scale(double) scale()} instead.
2978      * 
2979      * @see #scale(double)
2980      * 
2981      * @param factor
2982      *             the scale factor in x, y and z
2983      * @return this
2984      */
2985     ref public Matrix4d scaling(double factor) return {
2986         return scaling(factor, factor, factor);
2987     }
2988 
2989     /**
2990      * Set this matrix to be a simple scale matrix.
2991      * 
2992      * @param x
2993      *          the scale in x
2994      * @param y
2995      *          the scale in y
2996      * @param z
2997      *          the scale in z         
2998      * @return this
2999      */
3000     ref public Matrix4d scaling(double x, double y, double z) return {
3001         if ((properties & PROPERTY_IDENTITY) == 0)
3002             identity();
3003         bool one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z);
3004         _m00(x).
3005         _m11(y).
3006         _m22(z).
3007         properties = PROPERTY_AFFINE | (one ? PROPERTY_ORTHONORMAL : 0);
3008         return this;
3009     }
3010 
3011     /**
3012      * Set this matrix to be a simple scale matrix which scales the base axes by
3013      * <code>xyz.x</code>, <code>xyz.y</code> and <code>xyz.z</code>, respectively.
3014      * <p>
3015      * The resulting matrix can be multiplied against another transformation
3016      * matrix to obtain an additional scaling.
3017      * <p>
3018      * In order to post-multiply a scaling transformation directly to a
3019      * matrix use {@link #scale(ref Vector3d) scale()} instead.
3020      * 
3021      * @see #scale(ref Vector3d)
3022      * 
3023      * @param xyz
3024      *             the scale in x, y and z, respectively
3025      * @return this
3026      */
3027     ref public Matrix4d scaling(ref Vector3d xyz) return {
3028         return scaling(xyz.x, xyz.y, xyz.z);
3029     }
3030 
3031     /**
3032      * Set this matrix to a rotation matrix which rotates the given radians about a given axis.
3033      * <p>
3034      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3035      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3036      * When used with a left-handed coordinate system, the rotation is clockwise.
3037      * <p>
3038      * From <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">Wikipedia</a>
3039      * 
3040      * @param angle
3041      *          the angle in radians
3042      * @param x
3043      *          the x-coordinate of the axis to rotate about
3044      * @param y
3045      *          the y-coordinate of the axis to rotate about
3046      * @param z
3047      *          the z-coordinate of the axis to rotate about
3048      * @return this
3049      */
3050     ref public Matrix4d rotation(double angle, double x, double y, double z) return {
3051         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
3052             rotationX(x * angle);
3053         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
3054             rotationY(y * angle);
3055         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
3056             rotationZ(z * angle);
3057         else
3058             rotationInternal(angle, x, y, z);
3059         return this;
3060     }
3061     private Matrix4d rotationInternal(double angle, double x, double y, double z) {
3062         double sin = Math.sin(angle);
3063         double cos = Math.cosFromSin(sin, angle);
3064         double C = 1.0 - cos;
3065         double xy = x * y, xz = x * z, yz = y * z;
3066         if ((properties & PROPERTY_IDENTITY) == 0)
3067             this._identity();
3068         _m00(cos + x * x * C).
3069         _m10(xy * C - z * sin).
3070         _m20(xz * C + y * sin).
3071         _m01(xy * C + z * sin).
3072         _m11(cos + y * y * C).
3073         _m21(yz * C - x * sin).
3074         _m02(xz * C - y * sin).
3075         _m12(yz * C + x * sin).
3076         _m22(cos + z * z * C).
3077         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3078         return this;
3079     }
3080 
3081     /**
3082      * Set this matrix to a rotation transformation about the X axis.
3083      * <p>
3084      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3085      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3086      * When used with a left-handed coordinate system, the rotation is clockwise.
3087      * <p>
3088      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
3089      * 
3090      * @param ang
3091      *            the angle in radians
3092      * @return this
3093      */
3094     ref public Matrix4d rotationX(double ang) return {
3095         double sin, cos;
3096         sin = Math.sin(ang);
3097         cos = Math.cosFromSin(sin, ang);
3098         if ((properties & PROPERTY_IDENTITY) == 0)
3099             this._identity();
3100         _m11(cos).
3101         _m12(sin).
3102         _m21(-sin).
3103         _m22(cos).
3104         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3105         return this;
3106     }
3107 
3108     /**
3109      * Set this matrix to a rotation transformation about the Y axis.
3110      * <p>
3111      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3112      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3113      * When used with a left-handed coordinate system, the rotation is clockwise.
3114      * <p>
3115      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
3116      * 
3117      * @param ang
3118      *            the angle in radians
3119      * @return this
3120      */
3121     ref public Matrix4d rotationY(double ang) return {
3122         double sin, cos;
3123         sin = Math.sin(ang);
3124         cos = Math.cosFromSin(sin, ang);
3125         if ((properties & PROPERTY_IDENTITY) == 0)
3126             this._identity();
3127         _m00(cos).
3128         _m02(-sin).
3129         _m20(sin).
3130         _m22(cos).
3131         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3132         return this;
3133     }
3134 
3135     /**
3136      * Set this matrix to a rotation transformation about the Z axis.
3137      * <p>
3138      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3139      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3140      * When used with a left-handed coordinate system, the rotation is clockwise.
3141      * <p>
3142      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
3143      * 
3144      * @param ang
3145      *            the angle in radians
3146      * @return this
3147      */
3148     ref public Matrix4d rotationZ(double ang) return {
3149         double sin, cos;
3150         sin = Math.sin(ang);
3151         cos = Math.cosFromSin(sin, ang);
3152         if ((properties & PROPERTY_IDENTITY) == 0)
3153             this._identity();
3154         _m00(cos).
3155         _m01(sin).
3156         _m10(-sin).
3157         _m11(cos).
3158         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3159         return this;
3160     }
3161 
3162     /**
3163      * Set this matrix to a rotation transformation about the Z axis to align the local <code>+X</code> towards <code>(dirX, dirY)</code>.
3164      * <p>
3165      * The vector <code>(dirX, dirY)</code> must be a unit vector.
3166      * 
3167      * @param dirX
3168      *            the x component of the normalized direction
3169      * @param dirY
3170      *            the y component of the normalized direction
3171      * @return this
3172      */
3173     ref public Matrix4d rotationTowardsXY(double dirX, double dirY) return {
3174         if ((properties & PROPERTY_IDENTITY) == 0)
3175             this._identity();
3176         setm00(dirY);
3177         setm01(dirX);
3178         setm10(-dirX);
3179         setm11(dirY);
3180         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3181         return this;
3182     }
3183 
3184     /**
3185      * Set this matrix to a rotation of <code>angleX</code> radians about the X axis, followed by a rotation
3186      * of <code>angleY</code> radians about the Y axis and followed by a rotation of <code>angleZ</code> radians about the Z axis.
3187      * <p>
3188      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3189      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3190      * When used with a left-handed coordinate system, the rotation is clockwise.
3191      * <p>
3192      * This method is equivalent to calling: <code>rotationX(angleX).rotateY(angleY).rotateZ(angleZ)</code>
3193      * 
3194      * @param angleX
3195      *            the angle to rotate about X
3196      * @param angleY
3197      *            the angle to rotate about Y
3198      * @param angleZ
3199      *            the angle to rotate about Z
3200      * @return this
3201      */
3202     ref public Matrix4d rotationXYZ(double angleX, double angleY, double angleZ) return {
3203         double sinX = Math.sin(angleX);
3204         double cosX = Math.cosFromSin(sinX, angleX);
3205         double sinY = Math.sin(angleY);
3206         double cosY = Math.cosFromSin(sinY, angleY);
3207         double sinZ = Math.sin(angleZ);
3208         double cosZ = Math.cosFromSin(sinZ, angleZ);
3209         double m_sinX = -sinX;
3210         double m_sinY = -sinY;
3211         double m_sinZ = -sinZ;
3212         if ((properties & PROPERTY_IDENTITY) == 0)
3213             this._identity();
3214 
3215         // rotateX
3216         double nm11 = cosX;
3217         double nm12 = sinX;
3218         double nm21 = m_sinX;
3219         double nm22 = cosX;
3220         // rotateY
3221         double nm00 = cosY;
3222         double nm01 = nm21 * m_sinY;
3223         double nm02 = nm22 * m_sinY;
3224         _m20(sinY).
3225         _m21(nm21 * cosY).
3226         _m22(nm22 * cosY).
3227         // rotateZ
3228         _m00(nm00 * cosZ).
3229         _m01(nm01 * cosZ + nm11 * sinZ).
3230         _m02(nm02 * cosZ + nm12 * sinZ).
3231         _m10(nm00 * m_sinZ).
3232         _m11(nm01 * m_sinZ + nm11 * cosZ).
3233         _m12(nm02 * m_sinZ + nm12 * cosZ).
3234         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3235         return this;
3236     }
3237 
3238     /**
3239      * Set this matrix to a rotation of <code>angleZ</code> radians about the Z axis, followed by a rotation
3240      * of <code>angleY</code> radians about the Y axis and followed by a rotation of <code>angleX</code> radians about the X axis.
3241      * <p>
3242      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3243      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3244      * When used with a left-handed coordinate system, the rotation is clockwise.
3245      * <p>
3246      * This method is equivalent to calling: <code>rotationZ(angleZ).rotateY(angleY).rotateX(angleX)</code>
3247      * 
3248      * @param angleZ
3249      *            the angle to rotate about Z
3250      * @param angleY
3251      *            the angle to rotate about Y
3252      * @param angleX
3253      *            the angle to rotate about X
3254      * @return this
3255      */
3256     ref public Matrix4d rotationZYX(double angleZ, double angleY, double angleX) return {
3257         double sinX = Math.sin(angleX);
3258         double cosX = Math.cosFromSin(sinX, angleX);
3259         double sinY = Math.sin(angleY);
3260         double cosY = Math.cosFromSin(sinY, angleY);
3261         double sinZ = Math.sin(angleZ);
3262         double cosZ = Math.cosFromSin(sinZ, angleZ);
3263         double m_sinZ = -sinZ;
3264         double m_sinY = -sinY;
3265         double m_sinX = -sinX;
3266         if ((properties & PROPERTY_IDENTITY) == 0)
3267             this._identity();
3268 
3269         // rotateZ
3270         double nm00 = cosZ;
3271         double nm01 = sinZ;
3272         double nm10 = m_sinZ;
3273         double nm11 = cosZ;
3274         // rotateY
3275         double nm20 = nm00 * sinY;
3276         double nm21 = nm01 * sinY;
3277         double nm22 = cosY;
3278         _m00(nm00 * cosY).
3279         _m01(nm01 * cosY).
3280         _m02(m_sinY).
3281         // rotateX
3282         _m10(nm10 * cosX + nm20 * sinX).
3283         _m11(nm11 * cosX + nm21 * sinX).
3284         _m12(nm22 * sinX).
3285         _m20(nm10 * m_sinX + nm20 * cosX).
3286         _m21(nm11 * m_sinX + nm21 * cosX).
3287         _m22(nm22 * cosX).
3288         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3289         return this;
3290     }
3291 
3292     /**
3293      * Set this matrix to a rotation of <code>angleY</code> radians about the Y axis, followed by a rotation
3294      * of <code>angleX</code> radians about the X axis and followed by a rotation of <code>angleZ</code> radians about the Z axis.
3295      * <p>
3296      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3297      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3298      * When used with a left-handed coordinate system, the rotation is clockwise.
3299      * <p>
3300      * This method is equivalent to calling: <code>rotationY(angleY).rotateX(angleX).rotateZ(angleZ)</code>
3301      * 
3302      * @param angleY
3303      *            the angle to rotate about Y
3304      * @param angleX
3305      *            the angle to rotate about X
3306      * @param angleZ
3307      *            the angle to rotate about Z
3308      * @return this
3309      */
3310     ref public Matrix4d rotationYXZ(double angleY, double angleX, double angleZ) return {
3311         double sinX = Math.sin(angleX);
3312         double cosX = Math.cosFromSin(sinX, angleX);
3313         double sinY = Math.sin(angleY);
3314         double cosY = Math.cosFromSin(sinY, angleY);
3315         double sinZ = Math.sin(angleZ);
3316         double cosZ = Math.cosFromSin(sinZ, angleZ);
3317         double m_sinY = -sinY;
3318         double m_sinX = -sinX;
3319         double m_sinZ = -sinZ;
3320 
3321         // rotateY
3322         double nm00 = cosY;
3323         double nm02 = m_sinY;
3324         double nm20 = sinY;
3325         double nm22 = cosY;
3326         // rotateX
3327         double nm10 = nm20 * sinX;
3328         double nm11 = cosX;
3329         double nm12 = nm22 * sinX;
3330         _m20(nm20 * cosX).
3331         _m21(m_sinX).
3332         _m22(nm22 * cosX).
3333         _m23(0.0).
3334         // rotateZ
3335         _m00(nm00 * cosZ + nm10 * sinZ).
3336         _m01(nm11 * sinZ).
3337         _m02(nm02 * cosZ + nm12 * sinZ).
3338         _m03(0.0).
3339         _m10(nm00 * m_sinZ + nm10 * cosZ).
3340         _m11(nm11 * cosZ).
3341         _m12(nm02 * m_sinZ + nm12 * cosZ).
3342         _m13(0.0).
3343         // set last column to identity
3344         _m30(0.0).
3345         _m31(0.0).
3346         _m32(0.0).
3347         _m33(1.0).
3348         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3349         return this;
3350     }
3351 
3352     /**
3353      * Set only the upper left 3x3 submatrix of this matrix to a rotation of <code>angleX</code> radians about the X axis, followed by a rotation
3354      * of <code>angleY</code> radians about the Y axis and followed by a rotation of <code>angleZ</code> radians about the Z axis.
3355      * <p>
3356      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3357      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3358      * When used with a left-handed coordinate system, the rotation is clockwise.
3359      * 
3360      * @param angleX
3361      *            the angle to rotate about X
3362      * @param angleY
3363      *            the angle to rotate about Y
3364      * @param angleZ
3365      *            the angle to rotate about Z
3366      * @return this
3367      */
3368     ref public Matrix4d setRotationXYZ(double angleX, double angleY, double angleZ) return {
3369         double sinX = Math.sin(angleX);
3370         double cosX = Math.cosFromSin(sinX, angleX);
3371         double sinY = Math.sin(angleY);
3372         double cosY = Math.cosFromSin(sinY, angleY);
3373         double sinZ = Math.sin(angleZ);
3374         double cosZ = Math.cosFromSin(sinZ, angleZ);
3375         double m_sinX = -sinX;
3376         double m_sinY = -sinY;
3377         double m_sinZ = -sinZ;
3378 
3379         // rotateX
3380         double nm11 = cosX;
3381         double nm12 = sinX;
3382         double nm21 = m_sinX;
3383         double nm22 = cosX;
3384         // rotateY
3385         double nm00 = cosY;
3386         double nm01 = nm21 * m_sinY;
3387         double nm02 = nm22 * m_sinY;
3388         _m20(sinY).
3389         _m21(nm21 * cosY).
3390         _m22(nm22 * cosY).
3391         // rotateZ
3392         _m00(nm00 * cosZ).
3393         _m01(nm01 * cosZ + nm11 * sinZ).
3394         _m02(nm02 * cosZ + nm12 * sinZ).
3395         _m10(nm00 * m_sinZ).
3396         _m11(nm01 * m_sinZ + nm11 * cosZ).
3397         _m12(nm02 * m_sinZ + nm12 * cosZ).
3398         properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
3399         return this;
3400     }
3401 
3402     /**
3403      * Set only the upper left 3x3 submatrix of this matrix to a rotation of <code>angleZ</code> radians about the Z axis, followed by a rotation
3404      * of <code>angleY</code> radians about the Y axis and followed by a rotation of <code>angleX</code> radians about the X axis.
3405      * <p>
3406      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3407      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3408      * When used with a left-handed coordinate system, the rotation is clockwise.
3409      * 
3410      * @param angleZ
3411      *            the angle to rotate about Z
3412      * @param angleY
3413      *            the angle to rotate about Y
3414      * @param angleX
3415      *            the angle to rotate about X
3416      * @return this
3417      */
3418     ref public Matrix4d setRotationZYX(double angleZ, double angleY, double angleX) return {
3419         double sinX = Math.sin(angleX);
3420         double cosX = Math.cosFromSin(sinX, angleX);
3421         double sinY = Math.sin(angleY);
3422         double cosY = Math.cosFromSin(sinY, angleY);
3423         double sinZ = Math.sin(angleZ);
3424         double cosZ = Math.cosFromSin(sinZ, angleZ);
3425         double m_sinZ = -sinZ;
3426         double m_sinY = -sinY;
3427         double m_sinX = -sinX;
3428 
3429         // rotateZ
3430         double nm00 = cosZ;
3431         double nm01 = sinZ;
3432         double nm10 = m_sinZ;
3433         double nm11 = cosZ;
3434         // rotateY
3435         double nm20 = nm00 * sinY;
3436         double nm21 = nm01 * sinY;
3437         double nm22 = cosY;
3438         _m00(nm00 * cosY).
3439         _m01(nm01 * cosY).
3440         _m02(m_sinY).
3441         // rotateX
3442         _m10(nm10 * cosX + nm20 * sinX).
3443         _m11(nm11 * cosX + nm21 * sinX).
3444         _m12(nm22 * sinX).
3445         _m20(nm10 * m_sinX + nm20 * cosX).
3446         _m21(nm11 * m_sinX + nm21 * cosX).
3447         _m22(nm22 * cosX).
3448         properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
3449         return this;
3450     }
3451 
3452     /**
3453      * Set only the upper left 3x3 submatrix of this matrix to a rotation of <code>angleY</code> radians about the Y axis, followed by a rotation
3454      * of <code>angleX</code> radians about the X axis and followed by a rotation of <code>angleZ</code> radians about the Z axis.
3455      * <p>
3456      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3457      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3458      * When used with a left-handed coordinate system, the rotation is clockwise.
3459      * 
3460      * @param angleY
3461      *            the angle to rotate about Y
3462      * @param angleX
3463      *            the angle to rotate about X
3464      * @param angleZ
3465      *            the angle to rotate about Z
3466      * @return this
3467      */
3468     ref public Matrix4d setRotationYXZ(double angleY, double angleX, double angleZ) return {
3469         double sinX = Math.sin(angleX);
3470         double cosX = Math.cosFromSin(sinX, angleX);
3471         double sinY = Math.sin(angleY);
3472         double cosY = Math.cosFromSin(sinY, angleY);
3473         double sinZ = Math.sin(angleZ);
3474         double cosZ = Math.cosFromSin(sinZ, angleZ);
3475         double m_sinY = -sinY;
3476         double m_sinX = -sinX;
3477         double m_sinZ = -sinZ;
3478 
3479         // rotateY
3480         double nm00 = cosY;
3481         double nm02 = m_sinY;
3482         double nm20 = sinY;
3483         double nm22 = cosY;
3484         // rotateX
3485         double nm10 = nm20 * sinX;
3486         double nm11 = cosX;
3487         double nm12 = nm22 * sinX;
3488         _m20(nm20 * cosX).
3489         _m21(m_sinX).
3490         _m22(nm22 * cosX).
3491         // rotateZ
3492         _m00(nm00 * cosZ + nm10 * sinZ).
3493         _m01(nm11 * sinZ).
3494         _m02(nm02 * cosZ + nm12 * sinZ).
3495         _m10(nm00 * m_sinZ + nm10 * cosZ).
3496         _m11(nm11 * cosZ).
3497         _m12(nm02 * m_sinZ + nm12 * cosZ).
3498         properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
3499         return this;
3500     }
3501 
3502     /**
3503      * Set this matrix to a rotation matrix which rotates the given radians about a given axis.
3504      * <p>
3505      * The axis described by the <code>axis</code> vector needs to be a unit vector.
3506      * <p>
3507      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3508      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3509      * When used with a left-handed coordinate system, the rotation is clockwise.
3510      * 
3511      * @param angle
3512      *          the angle in radians
3513      * @param axis
3514      *          the axis to rotate about
3515      * @return this
3516      */
3517     ref public Matrix4d rotation(double angle, Vector3d axis) return {
3518         return rotation(angle, axis.x, axis.y, axis.z);
3519     }
3520 
3521 
3522     public Vector4d transform(ref Vector4d v) {
3523         return v.mul(this);
3524     }
3525 
3526     public Vector4d transform(ref Vector4d v, ref Vector4d dest) {
3527         return v.mul(this, dest);
3528     }
3529 
3530     public Vector4d transform(double x, double y, double z, double w, ref Vector4d dest) {
3531         return dest.set(m00 * x + m10 * y + m20 * z + m30 * w,
3532                         m01 * x + m11 * y + m21 * z + m31 * w,
3533                         m02 * x + m12 * y + m22 * z + m32 * w,
3534                         m03 * x + m13 * y + m23 * z + m33 * w);
3535     }
3536 
3537     public Vector4d transformTranspose(ref Vector4d v) {
3538         return v.mulTranspose(this);
3539     }
3540     public Vector4d transformTranspose(ref Vector4d v, ref Vector4d dest) {
3541         return v.mulTranspose(this, dest);
3542     }
3543     public Vector4d transformTranspose(double x, double y, double z, double w, ref Vector4d dest) {
3544        return dest.set(x, y, z, w).mulTranspose(this);
3545     }
3546 
3547     public Vector4d transformProject(ref Vector4d v) {
3548         return v.mulProject(this);
3549     }
3550 
3551     public Vector4d transformProject(ref Vector4d v, ref Vector4d dest) {
3552         return v.mulProject(this, dest);
3553     }
3554 
3555     public Vector4d transformProject(double x, double y, double z, double w, ref Vector4d dest) {
3556         double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33 * w);
3557         return dest.set((m00 * x + m10 * y + m20 * z + m30 * w) * invW,
3558                         (m01 * x + m11 * y + m21 * z + m31 * w) * invW,
3559                         (m02 * x + m12 * y + m22 * z + m32 * w) * invW,
3560                         1.0);
3561     }
3562 
3563     public Vector3d transformProject(ref Vector3d v) {
3564         return v.mulProject(this);
3565     }
3566 
3567     public Vector3d transformProject(ref Vector3d v, ref Vector3d dest) {
3568         return v.mulProject(this, dest);
3569     }
3570 
3571     public Vector3d transformProject(double x, double y, double z, ref Vector3d dest) {
3572         double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33);
3573         return dest.set((m00 * x + m10 * y + m20 * z + m30) * invW,
3574                         (m01 * x + m11 * y + m21 * z + m31) * invW,
3575                         (m02 * x + m12 * y + m22 * z + m32) * invW);
3576     }
3577 
3578     public Vector3d transformProject(ref Vector4d v, ref Vector3d dest) {
3579         return v.mulProject(this, dest);
3580     }
3581 
3582     public Vector3d transformProject(double x, double y, double z, double w, ref Vector3d dest) {
3583         dest.x = x;
3584         dest.y = y;
3585         dest.z = z;
3586         return dest.mulProject(this, w, dest);
3587     }
3588 
3589     public Vector3d transformPosition(ref Vector3d dest) {
3590         return dest.set(m00 * dest.x + m10 * dest.y + m20 * dest.z + m30,
3591                         m01 * dest.x + m11 * dest.y + m21 * dest.z + m31,
3592                         m02 * dest.x + m12 * dest.y + m22 * dest.z + m32);
3593     }
3594 
3595     public Vector3d transformPosition(ref Vector3d v, ref Vector3d dest) {
3596         return transformPosition(v.x, v.y, v.z, dest);
3597     }
3598 
3599     public Vector3d transformPosition(double x, double y, double z, ref Vector3d dest) {
3600         return dest.set(m00 * x + m10 * y + m20 * z + m30,
3601                         m01 * x + m11 * y + m21 * z + m31,
3602                         m02 * x + m12 * y + m22 * z + m32);
3603     }
3604 
3605     public Vector3d transformDirection(ref Vector3d dest) {
3606         return dest.set(m00 * dest.x + m10 * dest.y + m20 * dest.z,
3607                         m01 * dest.x + m11 * dest.y + m21 * dest.z,
3608                         m02 * dest.x + m12 * dest.y + m22 * dest.z);
3609     }
3610 
3611     public Vector3d transformDirection(ref Vector3d v, ref Vector3d dest) {
3612         return dest.set(m00 * v.x + m10 * v.y + m20 * v.z,
3613                         m01 * v.x + m11 * v.y + m21 * v.z,
3614                         m02 * v.x + m12 * v.y + m22 * v.z);
3615     }
3616 
3617     public Vector3d transformDirection(double x, double y, double z, ref Vector3d dest) {
3618         return dest.set(m00 * x + m10 * y + m20 * z,
3619                         m01 * x + m11 * y + m21 * z,
3620                         m02 * x + m12 * y + m22 * z);
3621     }
3622 
3623     public Vector4d transformAffine(ref Vector4d dest) {
3624         return dest.mulAffine(this, dest);
3625     }
3626 
3627     public Vector4d transformAffine(ref Vector4d v, ref Vector4d dest) {
3628         return transformAffine(v.x, v.y, v.z, v.w, dest);
3629     }
3630 
3631     public Vector4d transformAffine(double x, double y, double z, double w, ref Vector4d dest) {
3632         double rx = m00 * x + m10 * y + m20 * z + m30 * w;
3633         double ry = m01 * x + m11 * y + m21 * z + m31 * w;
3634         double rz = m02 * x + m12 * y + m22 * z + m32 * w;
3635         dest.x = rx;
3636         dest.y = ry;
3637         dest.z = rz;
3638         dest.w = w;
3639         return dest;
3640     }
3641 
3642     /**
3643      * Set the upper left 3x3 submatrix of this {@link Matrix4d} to the given {@link Matrix3d} and don't change the other elements.
3644      * 
3645      * @param mat
3646      *          the 3x3 matrix
3647      * @return this
3648      */
3649     ref public Matrix4d set3x3(Matrix3d mat) return {
3650         return
3651         _m00(mat.m00).
3652         _m01(mat.m01).
3653         _m02(mat.m02).
3654         _m10(mat.m10).
3655         _m11(mat.m11).
3656         _m12(mat.m12).
3657         _m20(mat.m20).
3658         _m21(mat.m21).
3659         _m22(mat.m22).
3660         _properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
3661     }
3662 
3663     public Matrix4d scale(ref Vector3d xyz, ref Matrix4d dest) {
3664         return scale(xyz.x, xyz.y, xyz.z, dest);
3665     }
3666 
3667     /**
3668      * Apply scaling to this matrix by scaling the base axes by the given <code>xyz.x</code>,
3669      * <code>xyz.y</code> and <code>xyz.z</code> factors, respectively.
3670      * <p>
3671      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3672      * then the new matrix will be <code>M * S</code>. So when transforming a
3673      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
3674      * scaling will be applied first!
3675      * 
3676      * @param xyz
3677      *            the factors of the x, y and z component, respectively
3678      * @return this
3679      */
3680     ref public Matrix4d scale(ref Vector3d xyz) return {
3681         scale(xyz.x, xyz.y, xyz.z, this);
3682         return this;
3683     }
3684 
3685     public Matrix4d scale(double x, double y, double z, ref Matrix4d dest) {
3686         if ((properties & PROPERTY_IDENTITY) != 0)
3687             return dest.scaling(x, y, z);
3688         return scaleGeneric(x, y, z, dest);
3689     }
3690     private Matrix4d scaleGeneric(double x, double y, double z, ref Matrix4d dest) {
3691         bool one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z);
3692         dest._m00(m00 * x)
3693         ._m01(m01 * x)
3694         ._m02(m02 * x)
3695         ._m03(m03 * x)
3696         ._m10(m10 * y)
3697         ._m11(m11 * y)
3698         ._m12(m12 * y)
3699         ._m13(m13 * y)
3700         ._m20(m20 * z)
3701         ._m21(m21 * z)
3702         ._m22(m22 * z)
3703         ._m23(m23 * z)
3704         ._m30(m30)
3705         ._m31(m31)
3706         ._m32(m32)
3707         ._m33(m33)
3708         ._properties(properties
3709                 & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | (one ? 0 : PROPERTY_ORTHONORMAL)));
3710         return dest;
3711     }
3712 
3713     /**
3714      * Apply scaling to <code>this</code> matrix by scaling the base axes by the given x,
3715      * y and z factors.
3716      * <p>
3717      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3718      * then the new matrix will be <code>M * S</code>. So when transforming a
3719      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>
3720      * , the scaling will be applied first!
3721      * 
3722      * @param x
3723      *            the factor of the x component
3724      * @param y
3725      *            the factor of the y component
3726      * @param z
3727      *            the factor of the z component
3728      * @return this
3729      */
3730     ref public Matrix4d scale(double x, double y, double z) return {
3731         scale(x, y, z, this);
3732         return this;
3733     }
3734 
3735     public Matrix4d scale(double xyz, ref Matrix4d dest) {
3736         return scale(xyz, xyz, xyz, dest);
3737     }
3738 
3739     /**
3740      * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor.
3741      * <p>
3742      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3743      * then the new matrix will be <code>M * S</code>. So when transforming a
3744      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>
3745      * , the scaling will be applied first!
3746      * 
3747      * @see #scale(double, double, double)
3748      * 
3749      * @param xyz
3750      *            the factor for all components
3751      * @return this
3752      */
3753     ref public Matrix4d scale(double xyz) return {
3754         return scale(xyz, xyz, xyz);
3755     }
3756 
3757     public Matrix4d scaleXY(double x, double y, ref Matrix4d dest) {
3758         return scale(x, y, 1.0, dest);
3759     }
3760 
3761     /**
3762      * Apply scaling to this matrix by scaling the X axis by <code>x</code> and the Y axis by <code>y</code>.
3763      * <p>
3764      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3765      * then the new matrix will be <code>M * S</code>. So when transforming a
3766      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
3767      * scaling will be applied first!
3768      * 
3769      * @param x
3770      *            the factor of the x component
3771      * @param y
3772      *            the factor of the y component
3773      * @return this
3774      */
3775     ref public Matrix4d scaleXY(double x, double y) return {
3776         return scale(x, y, 1.0);
3777     }
3778 
3779     public Matrix4d scaleAround(double sx, double sy, double sz, double ox, double oy, double oz, ref Matrix4d dest) {
3780         double nm30 = m00 * ox + m10 * oy + m20 * oz + m30;
3781         double nm31 = m01 * ox + m11 * oy + m21 * oz + m31;
3782         double nm32 = m02 * ox + m12 * oy + m22 * oz + m32;
3783         double nm33 = m03 * ox + m13 * oy + m23 * oz + m33;
3784         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
3785         return dest
3786         ._m00(m00 * sx)
3787         ._m01(m01 * sx)
3788         ._m02(m02 * sx)
3789         ._m03(m03 * sx)
3790         ._m10(m10 * sy)
3791         ._m11(m11 * sy)
3792         ._m12(m12 * sy)
3793         ._m13(m13 * sy)
3794         ._m20(m20 * sz)
3795         ._m21(m21 * sz)
3796         ._m22(m22 * sz)
3797         ._m23(m23 * sz)
3798         ._m30(-dest.m00 * ox - dest.m10 * oy - dest.m20 * oz + nm30)
3799         ._m31(-dest.m01 * ox - dest.m11 * oy - dest.m21 * oz + nm31)
3800         ._m32(-dest.m02 * ox - dest.m12 * oy - dest.m22 * oz + nm32)
3801         ._m33(-dest.m03 * ox - dest.m13 * oy - dest.m23 * oz + nm33)
3802         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION
3803             | (one ? 0 : PROPERTY_ORTHONORMAL)));
3804     }
3805 
3806     /**
3807      * Apply scaling to this matrix by scaling the base axes by the given sx,
3808      * sy and sz factors while using <code>(ox, oy, oz)</code> as the scaling origin.
3809      * <p>
3810      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3811      * then the new matrix will be <code>M * S</code>. So when transforming a
3812      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
3813      * scaling will be applied first!
3814      * <p>
3815      * This method is equivalent to calling: <code>translate(ox, oy, oz).scale(sx, sy, sz).translate(-ox, -oy, -oz)</code>
3816      * 
3817      * @param sx
3818      *            the scaling factor of the x component
3819      * @param sy
3820      *            the scaling factor of the y component
3821      * @param sz
3822      *            the scaling factor of the z component
3823      * @param ox
3824      *            the x coordinate of the scaling origin
3825      * @param oy
3826      *            the y coordinate of the scaling origin
3827      * @param oz
3828      *            the z coordinate of the scaling origin
3829      * @return this
3830      */
3831     ref public Matrix4d scaleAround(double sx, double sy, double sz, double ox, double oy, double oz) return {
3832         scaleAround(sx, sy, sz, ox, oy, oz, this);
3833         return this;
3834     }
3835 
3836     /**
3837      * Apply scaling to this matrix by scaling all three base axes by the given <code>factor</code>
3838      * while using <code>(ox, oy, oz)</code> as the scaling origin.
3839      * <p>
3840      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3841      * then the new matrix will be <code>M * S</code>. So when transforming a
3842      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
3843      * scaling will be applied first!
3844      * <p>
3845      * This method is equivalent to calling: <code>translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz)</code>
3846      * 
3847      * @param factor
3848      *            the scaling factor for all three axes
3849      * @param ox
3850      *            the x coordinate of the scaling origin
3851      * @param oy
3852      *            the y coordinate of the scaling origin
3853      * @param oz
3854      *            the z coordinate of the scaling origin
3855      * @return this
3856      */
3857     ref public Matrix4d scaleAround(double factor, double ox, double oy, double oz) return {
3858         scaleAround(factor, factor, factor, ox, oy, oz, this);
3859         return this;
3860     }
3861 
3862     public Matrix4d scaleAround(double factor, double ox, double oy, double oz, ref Matrix4d dest) {
3863         return scaleAround(factor, factor, factor, ox, oy, oz, dest);
3864     }
3865 
3866     public Matrix4d scaleLocal(double x, double y, double z, ref Matrix4d dest) {
3867         if ((properties & PROPERTY_IDENTITY) != 0)
3868             return dest.scaling(x, y, z);
3869         return scaleLocalGeneric(x, y, z, dest);
3870     }
3871     private Matrix4d scaleLocalGeneric(double x, double y, double z, ref Matrix4d dest) {
3872         double nm00 = x * m00;
3873         double nm01 = y * m01;
3874         double nm02 = z * m02;
3875         double nm10 = x * m10;
3876         double nm11 = y * m11;
3877         double nm12 = z * m12;
3878         double nm20 = x * m20;
3879         double nm21 = y * m21;
3880         double nm22 = z * m22;
3881         double nm30 = x * m30;
3882         double nm31 = y * m31;
3883         double nm32 = z * m32;
3884         bool one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z);
3885         dest._m00(nm00)
3886         ._m01(nm01)
3887         ._m02(nm02)
3888         ._m03(m03)
3889         ._m10(nm10)
3890         ._m11(nm11)
3891         ._m12(nm12)
3892         ._m13(m13)
3893         ._m20(nm20)
3894         ._m21(nm21)
3895         ._m22(nm22)
3896         ._m23(m23)
3897         ._m30(nm30)
3898         ._m31(nm31)
3899         ._m32(nm32)
3900         ._m33(m33)
3901         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION
3902                 | (one ? 0 : PROPERTY_ORTHONORMAL)));
3903         return dest;
3904     }
3905 
3906     public Matrix4d scaleLocal(double xyz, ref Matrix4d dest) {
3907         return scaleLocal(xyz, xyz, xyz, dest);
3908     }
3909 
3910     /**
3911      * Pre-multiply scaling to this matrix by scaling the base axes by the given xyz factor.
3912      * <p>
3913      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3914      * then the new matrix will be <code>S * M</code>. So when transforming a
3915      * vector <code>v</code> with the new matrix by using <code>S * M * v</code>, the
3916      * scaling will be applied last!
3917      * 
3918      * @param xyz
3919      *            the factor of the x, y and z component
3920      * @return this
3921      */
3922     ref public Matrix4d scaleLocal(double xyz) return {
3923         scaleLocal(xyz, this);
3924         return this;
3925     }
3926 
3927     /**
3928      * Pre-multiply scaling to this matrix by scaling the base axes by the given x,
3929      * y and z factors.
3930      * <p>
3931      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3932      * then the new matrix will be <code>S * M</code>. So when transforming a
3933      * vector <code>v</code> with the new matrix by using <code>S * M * v</code>, the
3934      * scaling will be applied last!
3935      * 
3936      * @param x
3937      *            the factor of the x component
3938      * @param y
3939      *            the factor of the y component
3940      * @param z
3941      *            the factor of the z component
3942      * @return this
3943      */
3944     ref public Matrix4d scaleLocal(double x, double y, double z) return {
3945         scaleLocal(x, y, z, this);
3946         return this;
3947     }
3948 
3949     public Matrix4d scaleAroundLocal(double sx, double sy, double sz, double ox, double oy, double oz, ref Matrix4d dest) {
3950         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
3951         dest._m00(sx * (m00 - ox * m03) + ox * m03)
3952         ._m01(sy * (m01 - oy * m03) + oy * m03)
3953         ._m02(sz * (m02 - oz * m03) + oz * m03)
3954         ._m03(m03)
3955         ._m10(sx * (m10 - ox * m13) + ox * m13)
3956         ._m11(sy * (m11 - oy * m13) + oy * m13)
3957         ._m12(sz * (m12 - oz * m13) + oz * m13)
3958         ._m13(m13)
3959         ._m20(sx * (m20 - ox * m23) + ox * m23)
3960         ._m21(sy * (m21 - oy * m23) + oy * m23)
3961         ._m22(sz * (m22 - oz * m23) + oz * m23)
3962         ._m23(m23)
3963         ._m30(sx * (m30 - ox * m33) + ox * m33)
3964         ._m31(sy * (m31 - oy * m33) + oy * m33)
3965         ._m32(sz * (m32 - oz * m33) + oz * m33)
3966         ._m33(m33)
3967         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION
3968                 | (one ? 0 : PROPERTY_ORTHONORMAL)));
3969         return dest;
3970     }
3971 
3972     /**
3973      * Pre-multiply scaling to this matrix by scaling the base axes by the given sx,
3974      * sy and sz factors while using <code>(ox, oy, oz)</code> as the scaling origin.
3975      * <p>
3976      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3977      * then the new matrix will be <code>S * M</code>. So when transforming a
3978      * vector <code>v</code> with the new matrix by using <code>S * M * v</code>, the
3979      * scaling will be applied last!
3980      * <p>
3981      * This method is equivalent to calling: <code>new Matrix4d().translate(ox, oy, oz).scale(sx, sy, sz).translate(-ox, -oy, -oz).mul(this, this)</code>
3982      * 
3983      * @param sx
3984      *            the scaling factor of the x component
3985      * @param sy
3986      *            the scaling factor of the y component
3987      * @param sz
3988      *            the scaling factor of the z component
3989      * @param ox
3990      *            the x coordinate of the scaling origin
3991      * @param oy
3992      *            the y coordinate of the scaling origin
3993      * @param oz
3994      *            the z coordinate of the scaling origin
3995      * @return this
3996      */
3997     ref public Matrix4d scaleAroundLocal(double sx, double sy, double sz, double ox, double oy, double oz) return {
3998         scaleAroundLocal(sx, sy, sz, ox, oy, oz, this);
3999         return this;
4000     }
4001 
4002     /**
4003      * Pre-multiply scaling to this matrix by scaling all three base axes by the given <code>factor</code>
4004      * while using <code>(ox, oy, oz)</code> as the scaling origin.
4005      * <p>
4006      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
4007      * then the new matrix will be <code>S * M</code>. So when transforming a
4008      * vector <code>v</code> with the new matrix by using <code>S * M * v</code>, the
4009      * scaling will be applied last!
4010      * <p>
4011      * This method is equivalent to calling: <code>new Matrix4d().translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz).mul(this, this)</code>
4012      * 
4013      * @param factor
4014      *            the scaling factor for all three axes
4015      * @param ox
4016      *            the x coordinate of the scaling origin
4017      * @param oy
4018      *            the y coordinate of the scaling origin
4019      * @param oz
4020      *            the z coordinate of the scaling origin
4021      * @return this
4022      */
4023     ref public Matrix4d scaleAroundLocal(double factor, double ox, double oy, double oz) return {
4024         scaleAroundLocal(factor, factor, factor, ox, oy, oz, this);
4025         return this;
4026     }
4027 
4028     public Matrix4d scaleAroundLocal(double factor, double ox, double oy, double oz, ref Matrix4d dest) {
4029         return scaleAroundLocal(factor, factor, factor, ox, oy, oz, dest);
4030     }
4031 
4032     public Matrix4d rotate(double ang, double x, double y, double z, ref Matrix4d dest) {
4033         if ((properties & PROPERTY_IDENTITY) != 0)
4034             return dest.rotation(ang, x, y, z);
4035         else if ((properties & PROPERTY_TRANSLATION) != 0)
4036             return rotateTranslation(ang, x, y, z, dest);
4037         else if ((properties & PROPERTY_AFFINE) != 0)
4038             return rotateAffine(ang, x, y, z, dest);
4039         return rotateGeneric(ang, x, y, z, dest);
4040     }
4041     private Matrix4d rotateGeneric(double ang, double x, double y, double z, ref Matrix4d dest) {
4042         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
4043             return rotateX(x * ang, dest);
4044         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
4045             return rotateY(y * ang, dest);
4046         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
4047             return rotateZ(z * ang, dest);
4048         return rotateGenericInternal(ang, x, y, z, dest);
4049     }
4050     private Matrix4d rotateGenericInternal(double ang, double x, double y, double z, ref Matrix4d dest) {
4051         double s = Math.sin(ang);
4052         double c = Math.cosFromSin(s, ang);
4053         double C = 1.0 - c;
4054         double xx = x * x, xy = x * y, xz = x * z;
4055         double yy = y * y, yz = y * z;
4056         double zz = z * z;
4057         double rm00 = xx * C + c;
4058         double rm01 = xy * C + z * s;
4059         double rm02 = xz * C - y * s;
4060         double rm10 = xy * C - z * s;
4061         double rm11 = yy * C + c;
4062         double rm12 = yz * C + x * s;
4063         double rm20 = xz * C + y * s;
4064         double rm21 = yz * C - x * s;
4065         double rm22 = zz * C + c;
4066         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
4067         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
4068         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
4069         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
4070         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
4071         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
4072         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
4073         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
4074         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
4075         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
4076         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
4077         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
4078         ._m00(nm00)
4079         ._m01(nm01)
4080         ._m02(nm02)
4081         ._m03(nm03)
4082         ._m10(nm10)
4083         ._m11(nm11)
4084         ._m12(nm12)
4085         ._m13(nm13)
4086         ._m30(m30)
4087         ._m31(m31)
4088         ._m32(m32)
4089         ._m33(m33)
4090         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4091         return dest;
4092     }
4093 
4094     /**
4095      * Apply rotation to this matrix by rotating the given amount of radians
4096      * about the given axis specified as x, y and z components.
4097      * <p>
4098      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4099      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4100      * When used with a left-handed coordinate system, the rotation is clockwise.
4101      * <p>
4102      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4103      * then the new matrix will be <code>M * R</code>. So when transforming a
4104      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>
4105      * , the rotation will be applied first!
4106      * <p>
4107      * In order to set the matrix to a rotation matrix without post-multiplying the rotation
4108      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4109      * 
4110      * @see #rotation(double, double, double, double)
4111      *  
4112      * @param ang
4113      *            the angle is in radians
4114      * @param x
4115      *            the x component of the axis
4116      * @param y
4117      *            the y component of the axis
4118      * @param z
4119      *            the z component of the axis
4120      * @return this
4121      */
4122     ref public Matrix4d rotate(double ang, double x, double y, double z) return {
4123         rotate(ang, x, y, z, this);
4124         return this;
4125     }
4126 
4127     /**
4128      * Apply rotation to this matrix, which is assumed to only contain a translation, by rotating the given amount of radians
4129      * about the specified <code>(x, y, z)</code> axis and store the result in <code>dest</code>.
4130      * <p>
4131      * This method assumes <code>this</code> to only contain a translation.
4132      * <p>
4133      * The axis described by the three components needs to be a unit vector.
4134      * <p>
4135      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4136      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4137      * When used with a left-handed coordinate system, the rotation is clockwise.
4138      * <p>
4139      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4140      * then the new matrix will be <code>M * R</code>. So when transforming a
4141      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
4142      * rotation will be applied first!
4143      * <p>
4144      * In order to set the matrix to a rotation matrix without post-multiplying the rotation
4145      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4146      * <p>
4147      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4148      * 
4149      * @see #rotation(double, double, double, double)
4150      * 
4151      * @param ang
4152      *            the angle in radians
4153      * @param x
4154      *            the x component of the axis
4155      * @param y
4156      *            the y component of the axis
4157      * @param z
4158      *            the z component of the axis
4159      * @param dest
4160      *            will hold the result
4161      * @return dest
4162      */
4163     public Matrix4d rotateTranslation(double ang, double x, double y, double z, ref Matrix4d dest) {
4164         double tx = m30, ty = m31, tz = m32;
4165         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
4166             return dest.rotationX(x * ang).setTranslation(tx, ty, tz);
4167         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
4168             return dest.rotationY(y * ang).setTranslation(tx, ty, tz);
4169         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
4170             return dest.rotationZ(z * ang).setTranslation(tx, ty, tz);
4171         return rotateTranslationInternal(ang, x, y, z, dest);
4172     }
4173     private Matrix4d rotateTranslationInternal(double ang, double x, double y, double z, ref Matrix4d dest) {
4174         double s = Math.sin(ang);
4175         double c = Math.cosFromSin(s, ang);
4176         double C = 1.0 - c;
4177         double xx = x * x, xy = x * y, xz = x * z;
4178         double yy = y * y, yz = y * z;
4179         double zz = z * z;
4180         double rm00 = xx * C + c;
4181         double rm01 = xy * C + z * s;
4182         double rm02 = xz * C - y * s;
4183         double rm10 = xy * C - z * s;
4184         double rm11 = yy * C + c;
4185         double rm12 = yz * C + x * s;
4186         double rm20 = xz * C + y * s;
4187         double rm21 = yz * C - x * s;
4188         double rm22 = zz * C + c;
4189         return dest
4190         ._m20(rm20)
4191         ._m21(rm21)
4192         ._m22(rm22)
4193         ._m23(0.0)
4194         ._m00(rm00)
4195         ._m01(rm01)
4196         ._m02(rm02)
4197         ._m03(0.0)
4198         ._m10(rm10)
4199         ._m11(rm11)
4200         ._m12(rm12)
4201         ._m13(0.0)
4202         ._m30(m30)
4203         ._m31(m31)
4204         ._m32(m32)
4205         ._m33(1.0)
4206         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4207     }
4208 
4209     /**
4210      * Apply rotation to this {@link #isAffine() affine} matrix by rotating the given amount of radians
4211      * about the specified <code>(x, y, z)</code> axis and store the result in <code>dest</code>.
4212      * <p>
4213      * This method assumes <code>this</code> to be {@link #isAffine() affine}.
4214      * <p>
4215      * The axis described by the three components needs to be a unit vector.
4216      * <p>
4217      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4218      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4219      * When used with a left-handed coordinate system, the rotation is clockwise.
4220      * <p>
4221      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4222      * then the new matrix will be <code>M * R</code>. So when transforming a
4223      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
4224      * rotation will be applied first!
4225      * <p>
4226      * In order to set the matrix to a rotation matrix without post-multiplying the rotation
4227      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4228      * <p>
4229      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4230      * 
4231      * @see #rotation(double, double, double, double)
4232      * 
4233      * @param ang
4234      *            the angle in radians
4235      * @param x
4236      *            the x component of the axis
4237      * @param y
4238      *            the y component of the axis
4239      * @param z
4240      *            the z component of the axis
4241      * @param dest
4242      *            will hold the result
4243      * @return dest
4244      */
4245     public Matrix4d rotateAffine(double ang, double x, double y, double z, ref Matrix4d dest) {
4246         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
4247             return rotateX(x * ang, dest);
4248         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
4249             return rotateY(y * ang, dest);
4250         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
4251             return rotateZ(z * ang, dest);
4252         return rotateAffineInternal(ang, x, y, z, dest);
4253     }
4254     private Matrix4d rotateAffineInternal(double ang, double x, double y, double z, ref Matrix4d dest) {
4255         double s = Math.sin(ang);
4256         double c = Math.cosFromSin(s, ang);
4257         double C = 1.0 - c;
4258         double xx = x * x, xy = x * y, xz = x * z;
4259         double yy = y * y, yz = y * z;
4260         double zz = z * z;
4261         double rm00 = xx * C + c;
4262         double rm01 = xy * C + z * s;
4263         double rm02 = xz * C - y * s;
4264         double rm10 = xy * C - z * s;
4265         double rm11 = yy * C + c;
4266         double rm12 = yz * C + x * s;
4267         double rm20 = xz * C + y * s;
4268         double rm21 = yz * C - x * s;
4269         double rm22 = zz * C + c;
4270         // add temporaries for dependent values
4271         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
4272         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
4273         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
4274         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
4275         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
4276         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
4277         // set non-dependent values directly
4278         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
4279         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
4280         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
4281         ._m23(0.0)
4282         // set other values
4283         ._m00(nm00)
4284         ._m01(nm01)
4285         ._m02(nm02)
4286         ._m03(0.0)
4287         ._m10(nm10)
4288         ._m11(nm11)
4289         ._m12(nm12)
4290         ._m13(0.0)
4291         ._m30(m30)
4292         ._m31(m31)
4293         ._m32(m32)
4294         ._m33(m33)
4295         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4296         return dest;
4297     }
4298 
4299     /**
4300      * Apply rotation to this {@link #isAffine() affine} matrix by rotating the given amount of radians
4301      * about the specified <code>(x, y, z)</code> axis.
4302      * <p>
4303      * This method assumes <code>this</code> to be {@link #isAffine() affine}.
4304      * <p>
4305      * The axis described by the three components needs to be a unit vector.
4306      * <p>
4307      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4308      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4309      * When used with a left-handed coordinate system, the rotation is clockwise.
4310      * <p>
4311      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4312      * then the new matrix will be <code>M * R</code>. So when transforming a
4313      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
4314      * rotation will be applied first!
4315      * <p>
4316      * In order to set the matrix to a rotation matrix without post-multiplying the rotation
4317      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4318      * <p>
4319      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4320      * 
4321      * @see #rotation(double, double, double, double)
4322      * 
4323      * @param ang
4324      *            the angle in radians
4325      * @param x
4326      *            the x component of the axis
4327      * @param y
4328      *            the y component of the axis
4329      * @param z
4330      *            the z component of the axis
4331      * @return this
4332      */
4333     ref public Matrix4d rotateAffine(double ang, double x, double y, double z) return {
4334         rotateAffine(ang, x, y, z, this);
4335         return this;
4336     }
4337 
4338     /**
4339      * Apply the rotation transformation of the given {@link Quaterniond} to this matrix while using <code>(ox, oy, oz)</code> as the rotation origin.
4340      * <p>
4341      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4342      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4343      * When used with a left-handed coordinate system, the rotation is clockwise.
4344      * <p>
4345      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
4346      * then the new matrix will be <code>M * Q</code>. So when transforming a
4347      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
4348      * the quaternion rotation will be applied first!
4349      * <p>
4350      * This method is equivalent to calling: <code>translate(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz)</code>
4351      * <p>
4352      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
4353      * 
4354      * @param quat
4355      *          the {@link Quaterniond}
4356      * @param ox
4357      *          the x coordinate of the rotation origin
4358      * @param oy
4359      *          the y coordinate of the rotation origin
4360      * @param oz
4361      *          the z coordinate of the rotation origin
4362      * @return this
4363      */
4364     ref public Matrix4d rotateAround(ref Quaterniond quat, double ox, double oy, double oz) return {
4365         rotateAround(quat, ox, oy, oz, this);
4366         return this;
4367     }
4368 
4369     public Matrix4d rotateAroundAffine(ref Quaterniond quat, double ox, double oy, double oz, ref Matrix4d dest) {
4370         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
4371         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
4372         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
4373         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
4374         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
4375         double rm00 = w2 + x2 - z2 - y2;
4376         double rm01 = dxy + dzw;
4377         double rm02 = dxz - dyw;
4378         double rm10 = -dzw + dxy;
4379         double rm11 = y2 - z2 + w2 - x2;
4380         double rm12 = dyz + dxw;
4381         double rm20 = dyw + dxz;
4382         double rm21 = dyz - dxw;
4383         double rm22 = z2 - y2 - x2 + w2;
4384         double tm30 = m00 * ox + m10 * oy + m20 * oz + m30;
4385         double tm31 = m01 * ox + m11 * oy + m21 * oz + m31;
4386         double tm32 = m02 * ox + m12 * oy + m22 * oz + m32;
4387         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
4388         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
4389         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
4390         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
4391         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
4392         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
4393         dest
4394         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
4395         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
4396         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
4397         ._m23(0.0)
4398         ._m00(nm00)
4399         ._m01(nm01)
4400         ._m02(nm02)
4401         ._m03(0.0)
4402         ._m10(nm10)
4403         ._m11(nm11)
4404         ._m12(nm12)
4405         ._m13(0.0)
4406         ._m30(-nm00 * ox - nm10 * oy - m20 * oz + tm30)
4407         ._m31(-nm01 * ox - nm11 * oy - m21 * oz + tm31)
4408         ._m32(-nm02 * ox - nm12 * oy - m22 * oz + tm32)
4409         ._m33(1.0)
4410         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4411         return dest;
4412     }
4413 
4414     public Matrix4d rotateAround(ref Quaterniond quat, double ox, double oy, double oz, ref Matrix4d dest) {
4415         if ((properties & PROPERTY_IDENTITY) != 0)
4416             return rotationAround(quat, ox, oy, oz);
4417         else if ((properties & PROPERTY_AFFINE) != 0)
4418             return rotateAroundAffine(quat, ox, oy, oz, this);
4419         return rotateAroundGeneric(quat, ox, oy, oz, this);
4420     }
4421     private Matrix4d rotateAroundGeneric(ref Quaterniond quat, double ox, double oy, double oz, ref Matrix4d dest) {
4422         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
4423         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
4424         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
4425         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
4426         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
4427         double rm00 = w2 + x2 - z2 - y2;
4428         double rm01 = dxy + dzw;
4429         double rm02 = dxz - dyw;
4430         double rm10 = -dzw + dxy;
4431         double rm11 = y2 - z2 + w2 - x2;
4432         double rm12 = dyz + dxw;
4433         double rm20 = dyw + dxz;
4434         double rm21 = dyz - dxw;
4435         double rm22 = z2 - y2 - x2 + w2;
4436         double tm30 = m00 * ox + m10 * oy + m20 * oz + m30;
4437         double tm31 = m01 * ox + m11 * oy + m21 * oz + m31;
4438         double tm32 = m02 * ox + m12 * oy + m22 * oz + m32;
4439         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
4440         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
4441         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
4442         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
4443         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
4444         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
4445         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
4446         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
4447         dest
4448         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
4449         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
4450         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
4451         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
4452         ._m00(nm00)
4453         ._m01(nm01)
4454         ._m02(nm02)
4455         ._m03(nm03)
4456         ._m10(nm10)
4457         ._m11(nm11)
4458         ._m12(nm12)
4459         ._m13(nm13)
4460         ._m30(-nm00 * ox - nm10 * oy - m20 * oz + tm30)
4461         ._m31(-nm01 * ox - nm11 * oy - m21 * oz + tm31)
4462         ._m32(-nm02 * ox - nm12 * oy - m22 * oz + tm32)
4463         ._m33(m33)
4464         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4465         return dest;
4466     }
4467 
4468     /**
4469      * Set this matrix to a transformation composed of a rotation of the specified {@link Quaterniond} while using <code>(ox, oy, oz)</code> as the rotation origin.
4470      * <p>
4471      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4472      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4473      * When used with a left-handed coordinate system, the rotation is clockwise.
4474      * <p>
4475      * This method is equivalent to calling: <code>translation(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz)</code>
4476      * <p>
4477      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
4478      * 
4479      * @param quat
4480      *          the {@link Quaterniond}
4481      * @param ox
4482      *          the x coordinate of the rotation origin
4483      * @param oy
4484      *          the y coordinate of the rotation origin
4485      * @param oz
4486      *          the z coordinate of the rotation origin
4487      * @return this
4488      */
4489     ref public Matrix4d rotationAround(ref Quaterniond quat, double ox, double oy, double oz) return {
4490         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
4491         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
4492         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
4493         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
4494         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
4495         this._m20(dyw + dxz);
4496         this._m21(dyz - dxw);
4497         this._m22(z2 - y2 - x2 + w2);
4498         this._m23(0.0);
4499         this._m00(w2 + x2 - z2 - y2);
4500         this._m01(dxy + dzw);
4501         this._m02(dxz - dyw);
4502         this._m03(0.0);
4503         this._m10(-dzw + dxy);
4504         this._m11(y2 - z2 + w2 - x2);
4505         this._m12(dyz + dxw);
4506         this._m13(0.0);
4507         this._m30(-m00 * ox - m10 * oy - m20 * oz + ox);
4508         this._m31(-m01 * ox - m11 * oy - m21 * oz + oy);
4509         this._m32(-m02 * ox - m12 * oy - m22 * oz + oz);
4510         this._m33(1.0);
4511         this.properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
4512         return this;
4513     }
4514 
4515     /**
4516      * Pre-multiply a rotation to this matrix by rotating the given amount of radians
4517      * about the specified <code>(x, y, z)</code> axis and store the result in <code>dest</code>.
4518      * <p>
4519      * The axis described by the three components needs to be a unit vector.
4520      * <p>
4521      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4522      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4523      * When used with a left-handed coordinate system, the rotation is clockwise.
4524      * <p>
4525      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4526      * then the new matrix will be <code>R * M</code>. So when transforming a
4527      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
4528      * rotation will be applied last!
4529      * <p>
4530      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
4531      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4532      * <p>
4533      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4534      * 
4535      * @see #rotation(double, double, double, double)
4536      * 
4537      * @param ang
4538      *            the angle in radians
4539      * @param x
4540      *            the x component of the axis
4541      * @param y
4542      *            the y component of the axis
4543      * @param z
4544      *            the z component of the axis
4545      * @param dest
4546      *            will hold the result
4547      * @return dest
4548      */
4549     public Matrix4d rotateLocal(double ang, double x, double y, double z, ref Matrix4d dest) {
4550         if ((properties & PROPERTY_IDENTITY) != 0)
4551             return dest.rotation(ang, x, y, z);
4552         return rotateLocalGeneric(ang, x, y, z, dest);
4553     }
4554     private Matrix4d rotateLocalGeneric(double ang, double x, double y, double z, ref Matrix4d dest) {
4555         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
4556             return rotateLocalX(x * ang, dest);
4557         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
4558             return rotateLocalY(y * ang, dest);
4559         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
4560             return rotateLocalZ(z * ang, dest);
4561         return rotateLocalGenericInternal(ang, x, y, z, dest);
4562     }
4563     private Matrix4d rotateLocalGenericInternal(double ang, double x, double y, double z, ref Matrix4d dest) {
4564         double s = Math.sin(ang);
4565         double c = Math.cosFromSin(s, ang);
4566         double C = 1.0 - c;
4567         double xx = x * x, xy = x * y, xz = x * z;
4568         double yy = y * y, yz = y * z;
4569         double zz = z * z;
4570         double lm00 = xx * C + c;
4571         double lm01 = xy * C + z * s;
4572         double lm02 = xz * C - y * s;
4573         double lm10 = xy * C - z * s;
4574         double lm11 = yy * C + c;
4575         double lm12 = yz * C + x * s;
4576         double lm20 = xz * C + y * s;
4577         double lm21 = yz * C - x * s;
4578         double lm22 = zz * C + c;
4579         double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02;
4580         double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02;
4581         double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02;
4582         double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12;
4583         double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12;
4584         double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12;
4585         double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22;
4586         double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22;
4587         double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22;
4588         double nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32;
4589         double nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32;
4590         double nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32;
4591         dest._m00(nm00)
4592         ._m01(nm01)
4593         ._m02(nm02)
4594         ._m03(m03)
4595         ._m10(nm10)
4596         ._m11(nm11)
4597         ._m12(nm12)
4598         ._m13(m13)
4599         ._m20(nm20)
4600         ._m21(nm21)
4601         ._m22(nm22)
4602         ._m23(m23)
4603         ._m30(nm30)
4604         ._m31(nm31)
4605         ._m32(nm32)
4606         ._m33(m33)
4607         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4608         return dest;
4609     }
4610 
4611     /**
4612      * Pre-multiply a rotation to this matrix by rotating the given amount of radians
4613      * about the specified <code>(x, y, z)</code> axis.
4614      * <p>
4615      * The axis described by the three components needs to be a unit vector.
4616      * <p>
4617      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4618      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4619      * When used with a left-handed coordinate system, the rotation is clockwise.
4620      * <p>
4621      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4622      * then the new matrix will be <code>R * M</code>. So when transforming a
4623      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
4624      * rotation will be applied last!
4625      * <p>
4626      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
4627      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4628      * <p>
4629      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4630      * 
4631      * @see #rotation(double, double, double, double)
4632      * 
4633      * @param ang
4634      *            the angle in radians
4635      * @param x
4636      *            the x component of the axis
4637      * @param y
4638      *            the y component of the axis
4639      * @param z
4640      *            the z component of the axis
4641      * @return this
4642      */
4643     ref public Matrix4d rotateLocal(double ang, double x, double y, double z) return {
4644         rotateLocal(ang, x, y, z, this);
4645         return this;
4646     }
4647 
4648     public Matrix4d rotateAroundLocal(ref Quaterniond quat, double ox, double oy, double oz, ref Matrix4d dest) {
4649         double w2 = quat.w * quat.w;
4650         double x2 = quat.x * quat.x;
4651         double y2 = quat.y * quat.y;
4652         double z2 = quat.z * quat.z;
4653         double zw = quat.z * quat.w;
4654         double xy = quat.x * quat.y;
4655         double xz = quat.x * quat.z;
4656         double yw = quat.y * quat.w;
4657         double yz = quat.y * quat.z;
4658         double xw = quat.x * quat.w;
4659         double lm00 = w2 + x2 - z2 - y2;
4660         double lm01 = xy + zw + zw + xy;
4661         double lm02 = xz - yw + xz - yw;
4662         double lm10 = -zw + xy - zw + xy;
4663         double lm11 = y2 - z2 + w2 - x2;
4664         double lm12 = yz + yz + xw + xw;
4665         double lm20 = yw + xz + xz + yw;
4666         double lm21 = yz + yz - xw - xw;
4667         double lm22 = z2 - y2 - x2 + w2;
4668         double tm00 = m00 - ox * m03;
4669         double tm01 = m01 - oy * m03;
4670         double tm02 = m02 - oz * m03;
4671         double tm10 = m10 - ox * m13;
4672         double tm11 = m11 - oy * m13;
4673         double tm12 = m12 - oz * m13;
4674         double tm20 = m20 - ox * m23;
4675         double tm21 = m21 - oy * m23;
4676         double tm22 = m22 - oz * m23;
4677         double tm30 = m30 - ox * m33;
4678         double tm31 = m31 - oy * m33;
4679         double tm32 = m32 - oz * m33;
4680         dest._m00(lm00 * tm00 + lm10 * tm01 + lm20 * tm02 + ox * m03)
4681         ._m01(lm01 * tm00 + lm11 * tm01 + lm21 * tm02 + oy * m03)
4682         ._m02(lm02 * tm00 + lm12 * tm01 + lm22 * tm02 + oz * m03)
4683         ._m03(m03)
4684         ._m10(lm00 * tm10 + lm10 * tm11 + lm20 * tm12 + ox * m13)
4685         ._m11(lm01 * tm10 + lm11 * tm11 + lm21 * tm12 + oy * m13)
4686         ._m12(lm02 * tm10 + lm12 * tm11 + lm22 * tm12 + oz * m13)
4687         ._m13(m13)
4688         ._m20(lm00 * tm20 + lm10 * tm21 + lm20 * tm22 + ox * m23)
4689         ._m21(lm01 * tm20 + lm11 * tm21 + lm21 * tm22 + oy * m23)
4690         ._m22(lm02 * tm20 + lm12 * tm21 + lm22 * tm22 + oz * m23)
4691         ._m23(m23)
4692         ._m30(lm00 * tm30 + lm10 * tm31 + lm20 * tm32 + ox * m33)
4693         ._m31(lm01 * tm30 + lm11 * tm31 + lm21 * tm32 + oy * m33)
4694         ._m32(lm02 * tm30 + lm12 * tm31 + lm22 * tm32 + oz * m33)
4695         ._m33(m33)
4696         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4697         return dest;
4698     }
4699 
4700     /**
4701      * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix while using <code>(ox, oy, oz)</code>
4702      * as the rotation origin.
4703      * <p>
4704      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4705      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4706      * When used with a left-handed coordinate system, the rotation is clockwise.
4707      * <p>
4708      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
4709      * then the new matrix will be <code>Q * M</code>. So when transforming a
4710      * vector <code>v</code> with the new matrix by using <code>Q * M * v</code>,
4711      * the quaternion rotation will be applied last!
4712      * <p>
4713      * This method is equivalent to calling: <code>translateLocal(-ox, -oy, -oz).rotateLocal(quat).translateLocal(ox, oy, oz)</code>
4714      * <p>
4715      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
4716      * 
4717      * @param quat
4718      *          the {@link Quaterniond}
4719      * @param ox
4720      *          the x coordinate of the rotation origin
4721      * @param oy
4722      *          the y coordinate of the rotation origin
4723      * @param oz
4724      *          the z coordinate of the rotation origin
4725      * @return this
4726      */
4727     ref public Matrix4d rotateAroundLocal(ref Quaterniond quat, double ox, double oy, double oz) return {
4728         rotateAroundLocal(quat, ox, oy, oz, this);
4729         return this;
4730     }
4731 
4732     /**
4733      * Apply a translation to this matrix by translating by the given number of
4734      * units in x, y and z.
4735      * <p>
4736      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4737      * matrix, then the new matrix will be <code>M * T</code>. So when
4738      * transforming a vector <code>v</code> with the new matrix by using
4739      * <code>M * T * v</code>, the translation will be applied first!
4740      * <p>
4741      * In order to set the matrix to a translation transformation without post-multiplying
4742      * it, use {@link #translation(ref Vector3d)}.
4743      * 
4744      * @see #translation(ref Vector3d)
4745      * 
4746      * @param offset
4747      *          the number of units in x, y and z by which to translate
4748      * @return this
4749      */
4750     ref public Matrix4d translate(ref Vector3d offset) return {
4751         return translate(offset.x, offset.y, offset.z);
4752     }
4753 
4754     /**
4755      * Apply a translation to this matrix by translating by the given number of
4756      * units in x, y and z and store the result in <code>dest</code>.
4757      * <p>
4758      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4759      * matrix, then the new matrix will be <code>M * T</code>. So when
4760      * transforming a vector <code>v</code> with the new matrix by using
4761      * <code>M * T * v</code>, the translation will be applied first!
4762      * <p>
4763      * In order to set the matrix to a translation transformation without post-multiplying
4764      * it, use {@link #translation(ref Vector3d)}.
4765      * 
4766      * @see #translation(ref Vector3d)
4767      * 
4768      * @param offset
4769      *          the number of units in x, y and z by which to translate
4770      * @param dest
4771      *          will hold the result
4772      * @return dest
4773      */
4774     public Matrix4d translate(ref Vector3d offset, ref Matrix4d dest) {
4775         return translate(offset.x, offset.y, offset.z, dest);
4776     }
4777 
4778 
4779 
4780     /**
4781      * Apply a translation to this matrix by translating by the given number of
4782      * units in x, y and z and store the result in <code>dest</code>.
4783      * <p>
4784      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4785      * matrix, then the new matrix will be <code>M * T</code>. So when
4786      * transforming a vector <code>v</code> with the new matrix by using
4787      * <code>M * T * v</code>, the translation will be applied first!
4788      * <p>
4789      * In order to set the matrix to a translation transformation without post-multiplying
4790      * it, use {@link #translation(double, double, double)}.
4791      * 
4792      * @see #translation(double, double, double)
4793      * 
4794      * @param x
4795      *          the offset to translate in x
4796      * @param y
4797      *          the offset to translate in y
4798      * @param z
4799      *          the offset to translate in z
4800      * @param dest
4801      *          will hold the result
4802      * @return dest
4803      */
4804     public Matrix4d translate(double x, double y, double z, ref Matrix4d dest) {
4805         if ((properties & PROPERTY_IDENTITY) != 0)
4806             return dest.translation(x, y, z);
4807         return translateGeneric(x, y, z, dest);
4808     }
4809     private Matrix4d translateGeneric(double x, double y, double z, ref Matrix4d dest) {
4810         dest._m00(m00)
4811         ._m01(m01)
4812         ._m02(m02)
4813         ._m03(m03)
4814         ._m10(m10)
4815         ._m11(m11)
4816         ._m12(m12)
4817         ._m13(m13)
4818         ._m20(m20)
4819         ._m21(m21)
4820         ._m22(m22)
4821         ._m23(m23)
4822         ._m30(Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))))
4823         ._m31(Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))))
4824         ._m32(Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))))
4825         ._m33(Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33))))
4826         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY));
4827         return dest;
4828     }
4829 
4830     /**
4831      * Apply a translation to this matrix by translating by the given number of
4832      * units in x, y and z.
4833      * <p>
4834      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4835      * matrix, then the new matrix will be <code>M * T</code>. So when
4836      * transforming a vector <code>v</code> with the new matrix by using
4837      * <code>M * T * v</code>, the translation will be applied first!
4838      * <p>
4839      * In order to set the matrix to a translation transformation without post-multiplying
4840      * it, use {@link #translation(double, double, double)}.
4841      * 
4842      * @see #translation(double, double, double)
4843      * 
4844      * @param x
4845      *          the offset to translate in x
4846      * @param y
4847      *          the offset to translate in y
4848      * @param z
4849      *          the offset to translate in z
4850      * @return this
4851      */
4852     ref public Matrix4d translate(double x, double y, double z) return {
4853         if ((properties & PROPERTY_IDENTITY) != 0)
4854             return translation(x, y, z);
4855         this._m30(Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))));
4856         this._m31(Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))));
4857         this._m32(Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))));
4858         this._m33(Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33))));
4859         this.properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY);
4860         return this;
4861     }
4862 
4863     /**
4864      * Pre-multiply a translation to this matrix by translating by the given number of
4865      * units in x, y and z.
4866      * <p>
4867      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4868      * matrix, then the new matrix will be <code>T * M</code>. So when
4869      * transforming a vector <code>v</code> with the new matrix by using
4870      * <code>T * M * v</code>, the translation will be applied last!
4871      * <p>
4872      * In order to set the matrix to a translation transformation without pre-multiplying
4873      * it, use {@link #translation(ref Vector3d)}.
4874      * 
4875      * @see #translation(ref Vector3d)
4876      * 
4877      * @param offset
4878      *          the number of units in x, y and z by which to translate
4879      * @return this
4880      */
4881     ref public Matrix4d translateLocal(ref Vector3d offset) return {
4882         return translateLocal(offset.x, offset.y, offset.z);
4883     }
4884 
4885     /**
4886      * Pre-multiply a translation to this matrix by translating by the given number of
4887      * units in x, y and z and store the result in <code>dest</code>.
4888      * <p>
4889      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4890      * matrix, then the new matrix will be <code>T * M</code>. So when
4891      * transforming a vector <code>v</code> with the new matrix by using
4892      * <code>T * M * v</code>, the translation will be applied last!
4893      * <p>
4894      * In order to set the matrix to a translation transformation without pre-multiplying
4895      * it, use {@link #translation(ref Vector3d)}.
4896      * 
4897      * @see #translation(ref Vector3d)
4898      * 
4899      * @param offset
4900      *          the number of units in x, y and z by which to translate
4901      * @param dest
4902      *          will hold the result
4903      * @return dest
4904      */
4905     public Matrix4d translateLocal(ref Vector3d offset, ref Matrix4d dest) {
4906         return translateLocal(offset.x, offset.y, offset.z, dest);
4907     }
4908 
4909     /**
4910      * Pre-multiply a translation to this matrix by translating by the given number of
4911      * units in x, y and z and store the result in <code>dest</code>.
4912      * <p>
4913      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4914      * matrix, then the new matrix will be <code>T * M</code>. So when
4915      * transforming a vector <code>v</code> with the new matrix by using
4916      * <code>T * M * v</code>, the translation will be applied last!
4917      * <p>
4918      * In order to set the matrix to a translation transformation without pre-multiplying
4919      * it, use {@link #translation(double, double, double)}.
4920      * 
4921      * @see #translation(double, double, double)
4922      * 
4923      * @param x
4924      *          the offset to translate in x
4925      * @param y
4926      *          the offset to translate in y
4927      * @param z
4928      *          the offset to translate in z
4929      * @param dest
4930      *          will hold the result
4931      * @return dest
4932      */
4933     public Matrix4d translateLocal(double x, double y, double z, ref Matrix4d dest) {
4934         if ((properties & PROPERTY_IDENTITY) != 0)
4935             return dest.translation(x, y, z);
4936         return translateLocalGeneric(x, y, z, dest);
4937     }
4938     private Matrix4d translateLocalGeneric(double x, double y, double z, ref Matrix4d dest) {
4939         double nm00 = m00 + x * m03;
4940         double nm01 = m01 + y * m03;
4941         double nm02 = m02 + z * m03;
4942         double nm10 = m10 + x * m13;
4943         double nm11 = m11 + y * m13;
4944         double nm12 = m12 + z * m13;
4945         double nm20 = m20 + x * m23;
4946         double nm21 = m21 + y * m23;
4947         double nm22 = m22 + z * m23;
4948         double nm30 = m30 + x * m33;
4949         double nm31 = m31 + y * m33;
4950         double nm32 = m32 + z * m33;
4951         return dest
4952         ._m00(nm00)
4953         ._m01(nm01)
4954         ._m02(nm02)
4955         ._m03(m03)
4956         ._m10(nm10)
4957         ._m11(nm11)
4958         ._m12(nm12)
4959         ._m13(m13)
4960         ._m20(nm20)
4961         ._m21(nm21)
4962         ._m22(nm22)
4963         ._m23(m23)
4964         ._m30(nm30)
4965         ._m31(nm31)
4966         ._m32(nm32)
4967         ._m33(m33)
4968         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY));
4969     }
4970 
4971     /**
4972      * Pre-multiply a translation to this matrix by translating by the given number of
4973      * units in x, y and z.
4974      * <p>
4975      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4976      * matrix, then the new matrix will be <code>T * M</code>. So when
4977      * transforming a vector <code>v</code> with the new matrix by using
4978      * <code>T * M * v</code>, the translation will be applied last!
4979      * <p>
4980      * In order to set the matrix to a translation transformation without pre-multiplying
4981      * it, use {@link #translation(double, double, double)}.
4982      * 
4983      * @see #translation(double, double, double)
4984      * 
4985      * @param x
4986      *          the offset to translate in x
4987      * @param y
4988      *          the offset to translate in y
4989      * @param z
4990      *          the offset to translate in z
4991      * @return this
4992      */
4993     ref public Matrix4d translateLocal(double x, double y, double z) return {
4994         translateLocal(x, y, z, this);
4995         return this;
4996     }
4997 
4998     /**
4999      * Pre-multiply a rotation around the X axis to this matrix by rotating the given amount of radians
5000      * about the X axis and store the result in <code>dest</code>.
5001      * <p>
5002      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5003      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5004      * When used with a left-handed coordinate system, the rotation is clockwise.
5005      * <p>
5006      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5007      * then the new matrix will be <code>R * M</code>. So when transforming a
5008      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5009      * rotation will be applied last!
5010      * <p>
5011      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5012      * transformation, use {@link #rotationX(double) rotationX()}.
5013      * <p>
5014      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5015      * 
5016      * @see #rotationX(double)
5017      * 
5018      * @param ang
5019      *            the angle in radians to rotate about the X axis
5020      * @param dest
5021      *            will hold the result
5022      * @return dest
5023      */
5024     public Matrix4d rotateLocalX(double ang, ref Matrix4d dest) {
5025         double sin = Math.sin(ang);
5026         double cos = Math.cosFromSin(sin, ang);
5027         double nm02 = sin * m01 + cos * m02;
5028         double nm12 = sin * m11 + cos * m12;
5029         double nm22 = sin * m21 + cos * m22;
5030         double nm32 = sin * m31 + cos * m32;
5031         dest
5032         ._m00(m00)
5033         ._m01(cos * m01 - sin * m02)
5034         ._m02(nm02)
5035         ._m03(m03)
5036         ._m10(m10)
5037         ._m11(cos * m11 - sin * m12)
5038         ._m12(nm12)
5039         ._m13(m13)
5040         ._m20(m20)
5041         ._m21(cos * m21 - sin * m22)
5042         ._m22(nm22)
5043         ._m23(m23)
5044         ._m30(m30)
5045         ._m31(cos * m31 - sin * m32)
5046         ._m32(nm32)
5047         ._m33(m33)
5048         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5049         return dest;
5050     }
5051 
5052     /**
5053      * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the X axis.
5054      * <p>
5055      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5056      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5057      * When used with a left-handed coordinate system, the rotation is clockwise.
5058      * <p>
5059      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5060      * then the new matrix will be <code>R * M</code>. So when transforming a
5061      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5062      * rotation will be applied last!
5063      * <p>
5064      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5065      * transformation, use {@link #rotationX(double) rotationX()}.
5066      * <p>
5067      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5068      * 
5069      * @see #rotationX(double)
5070      * 
5071      * @param ang
5072      *            the angle in radians to rotate about the X axis
5073      * @return this
5074      */
5075     ref public Matrix4d rotateLocalX(double ang) return {
5076         rotateLocalX(ang, this);
5077         return this;
5078     }
5079 
5080     /**
5081      * Pre-multiply a rotation around the Y axis to this matrix by rotating the given amount of radians
5082      * about the Y axis and store the result in <code>dest</code>.
5083      * <p>
5084      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5085      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5086      * When used with a left-handed coordinate system, the rotation is clockwise.
5087      * <p>
5088      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5089      * then the new matrix will be <code>R * M</code>. So when transforming a
5090      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5091      * rotation will be applied last!
5092      * <p>
5093      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5094      * transformation, use {@link #rotationY(double) rotationY()}.
5095      * <p>
5096      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5097      * 
5098      * @see #rotationY(double)
5099      * 
5100      * @param ang
5101      *            the angle in radians to rotate about the Y axis
5102      * @param dest
5103      *            will hold the result
5104      * @return dest
5105      */
5106     public Matrix4d rotateLocalY(double ang, ref Matrix4d dest) {
5107         double sin = Math.sin(ang);
5108         double cos = Math.cosFromSin(sin, ang);
5109         double nm02 = -sin * m00 + cos * m02;
5110         double nm12 = -sin * m10 + cos * m12;
5111         double nm22 = -sin * m20 + cos * m22;
5112         double nm32 = -sin * m30 + cos * m32;
5113         dest
5114         ._m00(cos * m00 + sin * m02)
5115         ._m01(m01)
5116         ._m02(nm02)
5117         ._m03(m03)
5118         ._m10(cos * m10 + sin * m12)
5119         ._m11(m11)
5120         ._m12(nm12)
5121         ._m13(m13)
5122         ._m20(cos * m20 + sin * m22)
5123         ._m21(m21)
5124         ._m22(nm22)
5125         ._m23(m23)
5126         ._m30(cos * m30 + sin * m32)
5127         ._m31(m31)
5128         ._m32(nm32)
5129         ._m33(m33)
5130         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5131         return dest;
5132     }
5133 
5134     /**
5135      * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Y axis.
5136      * <p>
5137      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5138      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5139      * When used with a left-handed coordinate system, the rotation is clockwise.
5140      * <p>
5141      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5142      * then the new matrix will be <code>R * M</code>. So when transforming a
5143      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5144      * rotation will be applied last!
5145      * <p>
5146      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5147      * transformation, use {@link #rotationY(double) rotationY()}.
5148      * <p>
5149      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5150      * 
5151      * @see #rotationY(double)
5152      * 
5153      * @param ang
5154      *            the angle in radians to rotate about the Y axis
5155      * @return this
5156      */
5157     ref public Matrix4d rotateLocalY(double ang) return {
5158         rotateLocalY(ang, this);
5159         return this;
5160     }
5161 
5162     /**
5163      * Pre-multiply a rotation around the Z axis to this matrix by rotating the given amount of radians
5164      * about the Z axis and store the result in <code>dest</code>.
5165      * <p>
5166      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5167      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5168      * When used with a left-handed coordinate system, the rotation is clockwise.
5169      * <p>
5170      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5171      * then the new matrix will be <code>R * M</code>. So when transforming a
5172      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5173      * rotation will be applied last!
5174      * <p>
5175      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5176      * transformation, use {@link #rotationZ(double) rotationZ()}.
5177      * <p>
5178      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5179      * 
5180      * @see #rotationZ(double)
5181      * 
5182      * @param ang
5183      *            the angle in radians to rotate about the Z axis
5184      * @param dest
5185      *            will hold the result
5186      * @return dest
5187      */
5188     public Matrix4d rotateLocalZ(double ang, ref Matrix4d dest) {
5189         double sin = Math.sin(ang);
5190         double cos = Math.cosFromSin(sin, ang);
5191         double nm01 = sin * m00 + cos * m01;
5192         double nm11 = sin * m10 + cos * m11;
5193         double nm21 = sin * m20 + cos * m21;
5194         double nm31 = sin * m30 + cos * m31;
5195         dest
5196         ._m00(cos * m00 - sin * m01)
5197         ._m01(nm01)
5198         ._m02(m02)
5199         ._m03(m03)
5200         ._m10(cos * m10 - sin * m11)
5201         ._m11(nm11)
5202         ._m12(m12)
5203         ._m13(m13)
5204         ._m20(cos * m20 - sin * m21)
5205         ._m21(nm21)
5206         ._m22(m22)
5207         ._m23(m23)
5208         ._m30(cos * m30 - sin * m31)
5209         ._m31(nm31)
5210         ._m32(m32)
5211         ._m33(m33)
5212         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5213         return dest;
5214     }
5215 
5216     /**
5217      * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Z axis.
5218      * <p>
5219      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5220      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5221      * When used with a left-handed coordinate system, the rotation is clockwise.
5222      * <p>
5223      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5224      * then the new matrix will be <code>R * M</code>. So when transforming a
5225      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5226      * rotation will be applied last!
5227      * <p>
5228      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5229      * transformation, use {@link #rotationZ(double) rotationY()}.
5230      * <p>
5231      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5232      * 
5233      * @see #rotationY(double)
5234      * 
5235      * @param ang
5236      *            the angle in radians to rotate about the Z axis
5237      * @return this
5238      */
5239     ref public Matrix4d rotateLocalZ(double ang) return {
5240         rotateLocalZ(ang, this);
5241         return this;
5242     }
5243 
5244     public Matrix4d rotateX(double ang, ref Matrix4d dest) {
5245         if ((properties & PROPERTY_IDENTITY) != 0)
5246             return dest.rotationX(ang);
5247         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5248             double x = m30, y = m31, z = m32;
5249             return dest.rotationX(ang).setTranslation(x, y, z);
5250         }
5251         return rotateXInternal(ang, dest);
5252     }
5253     private Matrix4d rotateXInternal(double ang, ref Matrix4d dest) {
5254         double sin, cos;
5255         sin = Math.sin(ang);
5256         cos = Math.cosFromSin(sin, ang);
5257         double rm11 = cos;
5258         double rm12 = sin;
5259         double rm21 = -sin;
5260         double rm22 = cos;
5261 
5262         // add temporaries for dependent values
5263         double nm10 = m10 * rm11 + m20 * rm12;
5264         double nm11 = m11 * rm11 + m21 * rm12;
5265         double nm12 = m12 * rm11 + m22 * rm12;
5266         double nm13 = m13 * rm11 + m23 * rm12;
5267         // set non-dependent values directly
5268         dest._m20(m10 * rm21 + m20 * rm22)
5269         ._m21(m11 * rm21 + m21 * rm22)
5270         ._m22(m12 * rm21 + m22 * rm22)
5271         ._m23(m13 * rm21 + m23 * rm22)
5272         // set other values
5273         ._m10(nm10)
5274         ._m11(nm11)
5275         ._m12(nm12)
5276         ._m13(nm13)
5277         ._m00(m00)
5278         ._m01(m01)
5279         ._m02(m02)
5280         ._m03(m03)
5281         ._m30(m30)
5282         ._m31(m31)
5283         ._m32(m32)
5284         ._m33(m33)
5285         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5286         return dest;
5287     }
5288 
5289     /**
5290      * Apply rotation about the X axis to this matrix by rotating the given amount of radians.
5291      * <p>
5292      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5293      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5294      * When used with a left-handed coordinate system, the rotation is clockwise.
5295      * <p>
5296      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5297      * then the new matrix will be <code>M * R</code>. So when transforming a
5298      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5299      * rotation will be applied first!
5300      * <p>
5301      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
5302      * 
5303      * @param ang
5304      *            the angle in radians
5305      * @return this
5306      */
5307     ref public Matrix4d rotateX(double ang) return {
5308         rotateX(ang, this);
5309         return this;
5310     }
5311 
5312     public Matrix4d rotateY(double ang, ref Matrix4d dest) {
5313         if ((properties & PROPERTY_IDENTITY) != 0)
5314             return dest.rotationY(ang);
5315         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5316             double x = m30, y = m31, z = m32;
5317             return dest.rotationY(ang).setTranslation(x, y, z);
5318         }
5319         return rotateYInternal(ang, dest);
5320     }
5321     private Matrix4d rotateYInternal(double ang, ref Matrix4d dest) {
5322         double sin, cos;
5323         sin = Math.sin(ang);
5324         cos = Math.cosFromSin(sin, ang);
5325         double rm00 = cos;
5326         double rm02 = -sin;
5327         double rm20 = sin;
5328         double rm22 = cos;
5329 
5330         // add temporaries for dependent values
5331         double nm00 = m00 * rm00 + m20 * rm02;
5332         double nm01 = m01 * rm00 + m21 * rm02;
5333         double nm02 = m02 * rm00 + m22 * rm02;
5334         double nm03 = m03 * rm00 + m23 * rm02;
5335         // set non-dependent values directly
5336         dest._m20(m00 * rm20 + m20 * rm22)
5337         ._m21(m01 * rm20 + m21 * rm22)
5338         ._m22(m02 * rm20 + m22 * rm22)
5339         ._m23(m03 * rm20 + m23 * rm22)
5340         // set other values
5341         ._m00(nm00)
5342         ._m01(nm01)
5343         ._m02(nm02)
5344         ._m03(nm03)
5345         ._m10(m10)
5346         ._m11(m11)
5347         ._m12(m12)
5348         ._m13(m13)
5349         ._m30(m30)
5350         ._m31(m31)
5351         ._m32(m32)
5352         ._m33(m33)
5353         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5354         return dest;
5355     }
5356 
5357     /**
5358      * Apply rotation about the Y axis to this matrix by rotating the given amount of radians.
5359      * <p>
5360      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5361      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5362      * When used with a left-handed coordinate system, the rotation is clockwise.
5363      * <p>
5364      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5365      * then the new matrix will be <code>M * R</code>. So when transforming a
5366      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5367      * rotation will be applied first!
5368      * <p>
5369      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
5370      * 
5371      * @param ang
5372      *            the angle in radians
5373      * @return this
5374      */
5375     ref public Matrix4d rotateY(double ang) return {
5376         rotateY(ang, this);
5377         return this;
5378     }
5379 
5380     public Matrix4d rotateZ(double ang, ref Matrix4d dest) {
5381         if ((properties & PROPERTY_IDENTITY) != 0)
5382             return dest.rotationZ(ang);
5383         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5384             double x = m30, y = m31, z = m32;
5385             return dest.rotationZ(ang).setTranslation(x, y, z);
5386         }
5387         return rotateZInternal(ang, dest);
5388     }
5389     private Matrix4d rotateZInternal(double ang, ref Matrix4d dest) {
5390         double sin = Math.sin(ang);
5391         double cos = Math.cosFromSin(sin, ang);
5392         return rotateTowardsXY(sin, cos, dest);
5393     }
5394 
5395     /**
5396      * Apply rotation about the Z axis to this matrix by rotating the given amount of radians.
5397      * <p>
5398      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5399      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5400      * When used with a left-handed coordinate system, the rotation is clockwise.
5401      * <p>
5402      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5403      * then the new matrix will be <code>M * R</code>. So when transforming a
5404      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5405      * rotation will be applied first!
5406      * <p>
5407      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
5408      * 
5409      * @param ang
5410      *            the angle in radians
5411      * @return this
5412      */
5413     ref public Matrix4d rotateZ(double ang) return {
5414         rotateZ(ang, this);
5415         return this;
5416     }
5417 
5418     /**
5419      * Apply rotation about the Z axis to align the local <code>+X</code> towards <code>(dirX, dirY)</code>.
5420      * <p>
5421      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5422      * then the new matrix will be <code>M * R</code>. So when transforming a
5423      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5424      * rotation will be applied first!
5425      * <p>
5426      * The vector <code>(dirX, dirY)</code> must be a unit vector.
5427      * 
5428      * @param dirX
5429      *            the x component of the normalized direction
5430      * @param dirY
5431      *            the y component of the normalized direction
5432      * @return this
5433      */
5434     ref public Matrix4d rotateTowardsXY(double dirX, double dirY) return {
5435         rotateTowardsXY(dirX, dirY, this);
5436         return this;
5437     }
5438 
5439     public Matrix4d rotateTowardsXY(double dirX, double dirY, ref Matrix4d dest) {
5440         if ((properties & PROPERTY_IDENTITY) != 0)
5441             return dest.rotationTowardsXY(dirX, dirY);
5442         double rm00 = dirY;
5443         double rm01 = dirX;
5444         double rm10 = -dirX;
5445         double rm11 = dirY;
5446         double nm00 = m00 * rm00 + m10 * rm01;
5447         double nm01 = m01 * rm00 + m11 * rm01;
5448         double nm02 = m02 * rm00 + m12 * rm01;
5449         double nm03 = m03 * rm00 + m13 * rm01;
5450         dest._m10(m00 * rm10 + m10 * rm11)
5451         ._m11(m01 * rm10 + m11 * rm11)
5452         ._m12(m02 * rm10 + m12 * rm11)
5453         ._m13(m03 * rm10 + m13 * rm11)
5454         ._m00(nm00)
5455         ._m01(nm01)
5456         ._m02(nm02)
5457         ._m03(nm03)
5458         ._m20(m20)
5459         ._m21(m21)
5460         ._m22(m22)
5461         ._m23(m23)
5462         ._m30(m30)
5463         ._m31(m31)
5464         ._m32(m32)
5465         ._m33(m33)
5466         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5467         return dest;
5468     }
5469 
5470     /**
5471      * Apply rotation of <code>angles.x</code> radians about the X axis, followed by a rotation of <code>angles.y</code> radians about the Y axis and
5472      * followed by a rotation of <code>angles.z</code> radians about the Z axis.
5473      * <p>
5474      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5475      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5476      * When used with a left-handed coordinate system, the rotation is clockwise.
5477      * <p>
5478      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5479      * then the new matrix will be <code>M * R</code>. So when transforming a
5480      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5481      * rotation will be applied first!
5482      * <p>
5483      * This method is equivalent to calling: <code>rotateX(angles.x).rotateY(angles.y).rotateZ(angles.z)</code>
5484      * 
5485      * @param angles
5486      *            the Euler angles
5487      * @return this
5488      */
5489     ref public Matrix4d rotateXYZ(ref Vector3d angles) return {
5490         return rotateXYZ(angles.x, angles.y, angles.z);
5491     }
5492 
5493     /**
5494      * Apply rotation of <code>angleX</code> radians about the X axis, followed by a rotation of <code>angleY</code> radians about the Y axis and
5495      * followed by a rotation of <code>angleZ</code> radians about the Z axis.
5496      * <p>
5497      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5498      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5499      * When used with a left-handed coordinate system, the rotation is clockwise.
5500      * <p>
5501      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5502      * then the new matrix will be <code>M * R</code>. So when transforming a
5503      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5504      * rotation will be applied first!
5505      * <p>
5506      * This method is equivalent to calling: <code>rotateX(angleX).rotateY(angleY).rotateZ(angleZ)</code>
5507      * 
5508      * @param angleX
5509      *            the angle to rotate about X
5510      * @param angleY
5511      *            the angle to rotate about Y
5512      * @param angleZ
5513      *            the angle to rotate about Z
5514      * @return this
5515      */
5516     ref public Matrix4d rotateXYZ(double angleX, double angleY, double angleZ) return {
5517         rotateXYZ(angleX, angleY, angleZ, this);
5518         return this;
5519     }
5520 
5521     public Matrix4d rotateXYZ(double angleX, double angleY, double angleZ, ref Matrix4d dest) {
5522         if ((properties & PROPERTY_IDENTITY) != 0)
5523             return dest.rotationXYZ(angleX, angleY, angleZ);
5524         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5525             double tx = m30, ty = m31, tz = m32;
5526             return dest.rotationXYZ(angleX, angleY, angleZ).setTranslation(tx, ty, tz);
5527         } else if ((properties & PROPERTY_AFFINE) != 0)
5528             return dest.rotateAffineXYZ(angleX, angleY, angleZ);
5529         return rotateXYZInternal(angleX, angleY, angleZ, dest);
5530     }
5531     private Matrix4d rotateXYZInternal(double angleX, double angleY, double angleZ, ref Matrix4d dest) {
5532         double sinX = Math.sin(angleX);
5533         double cosX = Math.cosFromSin(sinX, angleX);
5534         double sinY = Math.sin(angleY);
5535         double cosY = Math.cosFromSin(sinY, angleY);
5536         double sinZ = Math.sin(angleZ);
5537         double cosZ = Math.cosFromSin(sinZ, angleZ);
5538         double m_sinX = -sinX;
5539         double m_sinY = -sinY;
5540         double m_sinZ = -sinZ;
5541 
5542         // rotateX
5543         double nm10 = m10 * cosX + m20 * sinX;
5544         double nm11 = m11 * cosX + m21 * sinX;
5545         double nm12 = m12 * cosX + m22 * sinX;
5546         double nm13 = m13 * cosX + m23 * sinX;
5547         double nm20 = m10 * m_sinX + m20 * cosX;
5548         double nm21 = m11 * m_sinX + m21 * cosX;
5549         double nm22 = m12 * m_sinX + m22 * cosX;
5550         double nm23 = m13 * m_sinX + m23 * cosX;
5551         // rotateY
5552         double nm00 = m00 * cosY + nm20 * m_sinY;
5553         double nm01 = m01 * cosY + nm21 * m_sinY;
5554         double nm02 = m02 * cosY + nm22 * m_sinY;
5555         double nm03 = m03 * cosY + nm23 * m_sinY;
5556         dest._m20(m00 * sinY + nm20 * cosY)
5557         ._m21(m01 * sinY + nm21 * cosY)
5558         ._m22(m02 * sinY + nm22 * cosY)
5559         ._m23(m03 * sinY + nm23 * cosY)
5560         // rotateZ
5561         ._m00(nm00 * cosZ + nm10 * sinZ)
5562         ._m01(nm01 * cosZ + nm11 * sinZ)
5563         ._m02(nm02 * cosZ + nm12 * sinZ)
5564         ._m03(nm03 * cosZ + nm13 * sinZ)
5565         ._m10(nm00 * m_sinZ + nm10 * cosZ)
5566         ._m11(nm01 * m_sinZ + nm11 * cosZ)
5567         ._m12(nm02 * m_sinZ + nm12 * cosZ)
5568         ._m13(nm03 * m_sinZ + nm13 * cosZ)
5569         // copy last column from 'this'
5570         ._m30(m30)
5571         ._m31(m31)
5572         ._m32(m32)
5573         ._m33(m33)
5574         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5575         return dest;
5576     }
5577 
5578     /**
5579      * Apply rotation of <code>angleX</code> radians about the X axis, followed by a rotation of <code>angleY</code> radians about the Y axis and
5580      * followed by a rotation of <code>angleZ</code> radians about the Z axis.
5581      * <p>
5582      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5583      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5584      * When used with a left-handed coordinate system, the rotation is clockwise.
5585      * <p>
5586      * This method assumes that <code>this</code> matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to <code>(0, 0, 0, 1)</code>)
5587      * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination).
5588      * <p>
5589      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5590      * then the new matrix will be <code>M * R</code>. So when transforming a
5591      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5592      * rotation will be applied first!
5593      * <p>
5594      * This method is equivalent to calling: <code>rotateX(angleX).rotateY(angleY).rotateZ(angleZ)</code>
5595      * 
5596      * @param angleX
5597      *            the angle to rotate about X
5598      * @param angleY
5599      *            the angle to rotate about Y
5600      * @param angleZ
5601      *            the angle to rotate about Z
5602      * @return this
5603      */
5604     ref public Matrix4d rotateAffineXYZ(double angleX, double angleY, double angleZ) return {
5605         rotateAffineXYZ(angleX, angleY, angleZ, this);
5606         return this;
5607     }
5608 
5609     public Matrix4d rotateAffineXYZ(double angleX, double angleY, double angleZ, ref Matrix4d dest) {
5610         if ((properties & PROPERTY_IDENTITY) != 0)
5611             return dest.rotationXYZ(angleX, angleY, angleZ);
5612         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5613             double tx = m30, ty = m31, tz = m32;
5614             return dest.rotationXYZ(angleX, angleY, angleZ).setTranslation(tx, ty, tz);
5615         }
5616         return rotateAffineXYZInternal(angleX, angleY, angleZ, dest);
5617     }
5618     private Matrix4d rotateAffineXYZInternal(double angleX, double angleY, double angleZ, ref Matrix4d dest) {
5619         double sinX = Math.sin(angleX);
5620         double cosX = Math.cosFromSin(sinX, angleX);
5621         double sinY = Math.sin(angleY);
5622         double cosY = Math.cosFromSin(sinY, angleY);
5623         double sinZ = Math.sin(angleZ);
5624         double cosZ = Math.cosFromSin(sinZ, angleZ);
5625         double m_sinX = -sinX;
5626         double m_sinY = -sinY;
5627         double m_sinZ = -sinZ;
5628 
5629         // rotateX
5630         double nm10 = m10 * cosX + m20 * sinX;
5631         double nm11 = m11 * cosX + m21 * sinX;
5632         double nm12 = m12 * cosX + m22 * sinX;
5633         double nm20 = m10 * m_sinX + m20 * cosX;
5634         double nm21 = m11 * m_sinX + m21 * cosX;
5635         double nm22 = m12 * m_sinX + m22 * cosX;
5636         // rotateY
5637         double nm00 = m00 * cosY + nm20 * m_sinY;
5638         double nm01 = m01 * cosY + nm21 * m_sinY;
5639         double nm02 = m02 * cosY + nm22 * m_sinY;
5640         dest._m20(m00 * sinY + nm20 * cosY)
5641         ._m21(m01 * sinY + nm21 * cosY)
5642         ._m22(m02 * sinY + nm22 * cosY)
5643         ._m23(0.0)
5644         // rotateZ
5645         ._m00(nm00 * cosZ + nm10 * sinZ)
5646         ._m01(nm01 * cosZ + nm11 * sinZ)
5647         ._m02(nm02 * cosZ + nm12 * sinZ)
5648         ._m03(0.0)
5649         ._m10(nm00 * m_sinZ + nm10 * cosZ)
5650         ._m11(nm01 * m_sinZ + nm11 * cosZ)
5651         ._m12(nm02 * m_sinZ + nm12 * cosZ)
5652         ._m13(0.0)
5653         // copy last column from 'this'
5654         ._m30(m30)
5655         ._m31(m31)
5656         ._m32(m32)
5657         ._m33(m33)
5658         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5659         return dest;
5660     }
5661 
5662     /**
5663      * Apply rotation of <code>angles.z</code> radians about the Z axis, followed by a rotation of <code>angles.y</code> radians about the Y axis and
5664      * followed by a rotation of <code>angles.x</code> radians about the X axis.
5665      * <p>
5666      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5667      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5668      * When used with a left-handed coordinate system, the rotation is clockwise.
5669      * <p>
5670      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5671      * then the new matrix will be <code>M * R</code>. So when transforming a
5672      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5673      * rotation will be applied first!
5674      * <p>
5675      * This method is equivalent to calling: <code>rotateZ(angles.z).rotateY(angles.y).rotateX(angles.x)</code>
5676      * 
5677      * @param angles
5678      *            the Euler angles
5679      * @return this
5680      */
5681     ref public Matrix4d rotateZYX(ref Vector3d angles) return {
5682         return rotateZYX(angles.z, angles.y, angles.x);
5683     }
5684 
5685     /**
5686      * Apply rotation of <code>angleZ</code> radians about the Z axis, followed by a rotation of <code>angleY</code> radians about the Y axis and
5687      * followed by a rotation of <code>angleX</code> radians about the X axis.
5688      * <p>
5689      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5690      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5691      * When used with a left-handed coordinate system, the rotation is clockwise.
5692      * <p>
5693      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5694      * then the new matrix will be <code>M * R</code>. So when transforming a
5695      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5696      * rotation will be applied first!
5697      * <p>
5698      * This method is equivalent to calling: <code>rotateZ(angleZ).rotateY(angleY).rotateX(angleX)</code>
5699      * 
5700      * @param angleZ
5701      *            the angle to rotate about Z
5702      * @param angleY
5703      *            the angle to rotate about Y
5704      * @param angleX
5705      *            the angle to rotate about X
5706      * @return this
5707      */
5708     ref public Matrix4d rotateZYX(double angleZ, double angleY, double angleX) return {
5709         rotateZYX(angleZ, angleY, angleX, this);
5710         return this;
5711     }
5712 
5713     public Matrix4d rotateZYX(double angleZ, double angleY, double angleX, ref Matrix4d dest) {
5714         if ((properties & PROPERTY_IDENTITY) != 0)
5715             return dest.rotationZYX(angleZ, angleY, angleX);
5716         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5717             double tx = m30, ty = m31, tz = m32;
5718             return dest.rotationZYX(angleZ, angleY, angleX).setTranslation(tx, ty, tz);
5719         } else if ((properties & PROPERTY_AFFINE) != 0)
5720             return dest.rotateAffineZYX(angleZ, angleY, angleX);
5721         return rotateZYXInternal(angleZ, angleY, angleX, dest);
5722     }
5723     private Matrix4d rotateZYXInternal(double angleZ, double angleY, double angleX, ref Matrix4d dest) {
5724         double sinX = Math.sin(angleX);
5725         double cosX = Math.cosFromSin(sinX, angleX);
5726         double sinY = Math.sin(angleY);
5727         double cosY = Math.cosFromSin(sinY, angleY);
5728         double sinZ = Math.sin(angleZ);
5729         double cosZ = Math.cosFromSin(sinZ, angleZ);
5730         double m_sinZ = -sinZ;
5731         double m_sinY = -sinY;
5732         double m_sinX = -sinX;
5733 
5734         // rotateZ
5735         double nm00 = m00 * cosZ + m10 * sinZ;
5736         double nm01 = m01 * cosZ + m11 * sinZ;
5737         double nm02 = m02 * cosZ + m12 * sinZ;
5738         double nm03 = m03 * cosZ + m13 * sinZ;
5739         double nm10 = m00 * m_sinZ + m10 * cosZ;
5740         double nm11 = m01 * m_sinZ + m11 * cosZ;
5741         double nm12 = m02 * m_sinZ + m12 * cosZ;
5742         double nm13 = m03 * m_sinZ + m13 * cosZ;
5743         // rotateY
5744         double nm20 = nm00 * sinY + m20 * cosY;
5745         double nm21 = nm01 * sinY + m21 * cosY;
5746         double nm22 = nm02 * sinY + m22 * cosY;
5747         double nm23 = nm03 * sinY + m23 * cosY;
5748         dest._m00(nm00 * cosY + m20 * m_sinY)
5749         ._m01(nm01 * cosY + m21 * m_sinY)
5750         ._m02(nm02 * cosY + m22 * m_sinY)
5751         ._m03(nm03 * cosY + m23 * m_sinY)
5752         // rotateX
5753         ._m10(nm10 * cosX + nm20 * sinX)
5754         ._m11(nm11 * cosX + nm21 * sinX)
5755         ._m12(nm12 * cosX + nm22 * sinX)
5756         ._m13(nm13 * cosX + nm23 * sinX)
5757         ._m20(nm10 * m_sinX + nm20 * cosX)
5758         ._m21(nm11 * m_sinX + nm21 * cosX)
5759         ._m22(nm12 * m_sinX + nm22 * cosX)
5760         ._m23(nm13 * m_sinX + nm23 * cosX)
5761         // copy last column from 'this'
5762         ._m30(m30)
5763         ._m31(m31)
5764         ._m32(m32)
5765         ._m33(m33)
5766         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5767         return dest;
5768     }
5769 
5770     /**
5771      * Apply rotation of <code>angleZ</code> radians about the Z axis, followed by a rotation of <code>angleY</code> radians about the Y axis and
5772      * followed by a rotation of <code>angleX</code> radians about the X axis.
5773      * <p>
5774      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5775      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5776      * When used with a left-handed coordinate system, the rotation is clockwise.
5777      * <p>
5778      * This method assumes that <code>this</code> matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to <code>(0, 0, 0, 1)</code>)
5779      * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination).
5780      * <p>
5781      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5782      * then the new matrix will be <code>M * R</code>. So when transforming a
5783      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5784      * rotation will be applied first!
5785      * 
5786      * @param angleZ
5787      *            the angle to rotate about Z
5788      * @param angleY
5789      *            the angle to rotate about Y
5790      * @param angleX
5791      *            the angle to rotate about X
5792      * @return this
5793      */
5794     ref public Matrix4d rotateAffineZYX(double angleZ, double angleY, double angleX) return {
5795         rotateAffineZYX(angleZ, angleY, angleX, this);
5796         return this;
5797     }
5798 
5799     public Matrix4d rotateAffineZYX(double angleZ, double angleY, double angleX, ref Matrix4d dest) {
5800         double sinX = Math.sin(angleX);
5801         double cosX = Math.cosFromSin(sinX, angleX);
5802         double sinY = Math.sin(angleY);
5803         double cosY = Math.cosFromSin(sinY, angleY);
5804         double sinZ = Math.sin(angleZ);
5805         double cosZ = Math.cosFromSin(sinZ, angleZ);
5806         double m_sinZ = -sinZ;
5807         double m_sinY = -sinY;
5808         double m_sinX = -sinX;
5809 
5810         // rotateZ
5811         double nm00 = m00 * cosZ + m10 * sinZ;
5812         double nm01 = m01 * cosZ + m11 * sinZ;
5813         double nm02 = m02 * cosZ + m12 * sinZ;
5814         double nm10 = m00 * m_sinZ + m10 * cosZ;
5815         double nm11 = m01 * m_sinZ + m11 * cosZ;
5816         double nm12 = m02 * m_sinZ + m12 * cosZ;
5817         // rotateY
5818         double nm20 = nm00 * sinY + m20 * cosY;
5819         double nm21 = nm01 * sinY + m21 * cosY;
5820         double nm22 = nm02 * sinY + m22 * cosY;
5821         dest._m00(nm00 * cosY + m20 * m_sinY)
5822         ._m01(nm01 * cosY + m21 * m_sinY)
5823         ._m02(nm02 * cosY + m22 * m_sinY)
5824         ._m03(0.0)
5825         // rotateX
5826         ._m10(nm10 * cosX + nm20 * sinX)
5827         ._m11(nm11 * cosX + nm21 * sinX)
5828         ._m12(nm12 * cosX + nm22 * sinX)
5829         ._m13(0.0)
5830         ._m20(nm10 * m_sinX + nm20 * cosX)
5831         ._m21(nm11 * m_sinX + nm21 * cosX)
5832         ._m22(nm12 * m_sinX + nm22 * cosX)
5833         ._m23(0.0)
5834         // copy last column from 'this'
5835         ._m30(m30)
5836         ._m31(m31)
5837         ._m32(m32)
5838         ._m33(m33)
5839         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5840         return dest;
5841     }
5842 
5843     /**
5844      * Apply rotation of <code>angles.y</code> radians about the Y axis, followed by a rotation of <code>angles.x</code> radians about the X axis and
5845      * followed by a rotation of <code>angles.z</code> radians about the Z axis.
5846      * <p>
5847      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5848      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5849      * When used with a left-handed coordinate system, the rotation is clockwise.
5850      * <p>
5851      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5852      * then the new matrix will be <code>M * R</code>. So when transforming a
5853      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5854      * rotation will be applied first!
5855      * <p>
5856      * This method is equivalent to calling: <code>rotateY(angles.y).rotateX(angles.x).rotateZ(angles.z)</code>
5857      * 
5858      * @param angles
5859      *            the Euler angles
5860      * @return this
5861      */
5862     ref public Matrix4d rotateYXZ(ref Vector3d angles) return {
5863         return rotateYXZ(angles.y, angles.x, angles.z);
5864     }
5865 
5866     /**
5867      * Apply rotation of <code>angleY</code> radians about the Y axis, followed by a rotation of <code>angleX</code> radians about the X axis and
5868      * followed by a rotation of <code>angleZ</code> radians about the Z axis.
5869      * <p>
5870      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5871      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5872      * When used with a left-handed coordinate system, the rotation is clockwise.
5873      * <p>
5874      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5875      * then the new matrix will be <code>M * R</code>. So when transforming a
5876      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5877      * rotation will be applied first!
5878      * <p>
5879      * This method is equivalent to calling: <code>rotateY(angleY).rotateX(angleX).rotateZ(angleZ)</code>
5880      * 
5881      * @param angleY
5882      *            the angle to rotate about Y
5883      * @param angleX
5884      *            the angle to rotate about X
5885      * @param angleZ
5886      *            the angle to rotate about Z
5887      * @return this
5888      */
5889     ref public Matrix4d rotateYXZ(double angleY, double angleX, double angleZ) return {
5890         rotateYXZ(angleY, angleX, angleZ, this);
5891         return this;
5892     }
5893 
5894     public Matrix4d rotateYXZ(double angleY, double angleX, double angleZ, ref Matrix4d dest) {
5895         if ((properties & PROPERTY_IDENTITY) != 0)
5896             return dest.rotationYXZ(angleY, angleX, angleZ);
5897         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5898             double tx = m30, ty = m31, tz = m32;
5899             return dest.rotationYXZ(angleY, angleX, angleZ).setTranslation(tx, ty, tz);
5900         } else if ((properties & PROPERTY_AFFINE) != 0)
5901             return dest.rotateAffineYXZ(angleY, angleX, angleZ);
5902         return rotateYXZInternal(angleY, angleX, angleZ, dest);
5903     }
5904     private Matrix4d rotateYXZInternal(double angleY, double angleX, double angleZ, ref Matrix4d dest) {
5905         double sinX = Math.sin(angleX);
5906         double cosX = Math.cosFromSin(sinX, angleX);
5907         double sinY = Math.sin(angleY);
5908         double cosY = Math.cosFromSin(sinY, angleY);
5909         double sinZ = Math.sin(angleZ);
5910         double cosZ = Math.cosFromSin(sinZ, angleZ);
5911         double m_sinY = -sinY;
5912         double m_sinX = -sinX;
5913         double m_sinZ = -sinZ;
5914 
5915         // rotateY
5916         double nm20 = m00 * sinY + m20 * cosY;
5917         double nm21 = m01 * sinY + m21 * cosY;
5918         double nm22 = m02 * sinY + m22 * cosY;
5919         double nm23 = m03 * sinY + m23 * cosY;
5920         double nm00 = m00 * cosY + m20 * m_sinY;
5921         double nm01 = m01 * cosY + m21 * m_sinY;
5922         double nm02 = m02 * cosY + m22 * m_sinY;
5923         double nm03 = m03 * cosY + m23 * m_sinY;
5924         // rotateX
5925         double nm10 = m10 * cosX + nm20 * sinX;
5926         double nm11 = m11 * cosX + nm21 * sinX;
5927         double nm12 = m12 * cosX + nm22 * sinX;
5928         double nm13 = m13 * cosX + nm23 * sinX;
5929         dest._m20(m10 * m_sinX + nm20 * cosX)
5930         ._m21(m11 * m_sinX + nm21 * cosX)
5931         ._m22(m12 * m_sinX + nm22 * cosX)
5932         ._m23(m13 * m_sinX + nm23 * cosX)
5933         // rotateZ
5934         ._m00(nm00 * cosZ + nm10 * sinZ)
5935         ._m01(nm01 * cosZ + nm11 * sinZ)
5936         ._m02(nm02 * cosZ + nm12 * sinZ)
5937         ._m03(nm03 * cosZ + nm13 * sinZ)
5938         ._m10(nm00 * m_sinZ + nm10 * cosZ)
5939         ._m11(nm01 * m_sinZ + nm11 * cosZ)
5940         ._m12(nm02 * m_sinZ + nm12 * cosZ)
5941         ._m13(nm03 * m_sinZ + nm13 * cosZ)
5942         // copy last column from 'this'
5943         ._m30(m30)
5944         ._m31(m31)
5945         ._m32(m32)
5946         ._m33(m33)
5947         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5948         return dest;
5949     }
5950 
5951     /**
5952      * Apply rotation of <code>angleY</code> radians about the Y axis, followed by a rotation of <code>angleX</code> radians about the X axis and
5953      * followed by a rotation of <code>angleZ</code> radians about the Z axis.
5954      * <p>
5955      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5956      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5957      * When used with a left-handed coordinate system, the rotation is clockwise.
5958      * <p>
5959      * This method assumes that <code>this</code> matrix represents an {@link #isAffine() affine} transformation (i.e. its last row is equal to <code>(0, 0, 0, 1)</code>)
5960      * and can be used to speed up matrix multiplication if the matrix only represents affine transformations, such as translation, rotation, scaling and shearing (in any combination).
5961      * <p>
5962      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5963      * then the new matrix will be <code>M * R</code>. So when transforming a
5964      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5965      * rotation will be applied first!
5966      * 
5967      * @param angleY
5968      *            the angle to rotate about Y
5969      * @param angleX
5970      *            the angle to rotate about X
5971      * @param angleZ
5972      *            the angle to rotate about Z
5973      * @return this
5974      */
5975     ref public Matrix4d rotateAffineYXZ(double angleY, double angleX, double angleZ) return {
5976         rotateAffineYXZ(angleY, angleX, angleZ, this);
5977         return this;
5978     }
5979 
5980     public Matrix4d rotateAffineYXZ(double angleY, double angleX, double angleZ, ref Matrix4d dest) {
5981         double sinX = Math.sin(angleX);
5982         double cosX = Math.cosFromSin(sinX, angleX);
5983         double sinY = Math.sin(angleY);
5984         double cosY = Math.cosFromSin(sinY, angleY);
5985         double sinZ = Math.sin(angleZ);
5986         double cosZ = Math.cosFromSin(sinZ, angleZ);
5987         double m_sinY = -sinY;
5988         double m_sinX = -sinX;
5989         double m_sinZ = -sinZ;
5990 
5991         // rotateY
5992         double nm20 = m00 * sinY + m20 * cosY;
5993         double nm21 = m01 * sinY + m21 * cosY;
5994         double nm22 = m02 * sinY + m22 * cosY;
5995         double nm00 = m00 * cosY + m20 * m_sinY;
5996         double nm01 = m01 * cosY + m21 * m_sinY;
5997         double nm02 = m02 * cosY + m22 * m_sinY;
5998         // rotateX
5999         double nm10 = m10 * cosX + nm20 * sinX;
6000         double nm11 = m11 * cosX + nm21 * sinX;
6001         double nm12 = m12 * cosX + nm22 * sinX;
6002         dest._m20(m10 * m_sinX + nm20 * cosX)
6003         ._m21(m11 * m_sinX + nm21 * cosX)
6004         ._m22(m12 * m_sinX + nm22 * cosX)
6005         ._m23(0.0)
6006         // rotateZ
6007         ._m00(nm00 * cosZ + nm10 * sinZ)
6008         ._m01(nm01 * cosZ + nm11 * sinZ)
6009         ._m02(nm02 * cosZ + nm12 * sinZ)
6010         ._m03(0.0)
6011         ._m10(nm00 * m_sinZ + nm10 * cosZ)
6012         ._m11(nm01 * m_sinZ + nm11 * cosZ)
6013         ._m12(nm02 * m_sinZ + nm12 * cosZ)
6014         ._m13(0.0)
6015         // copy last column from 'this'
6016         ._m30(m30)
6017         ._m31(m31)
6018         ._m32(m32)
6019         ._m33(m33)
6020         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
6021         return dest;
6022     }
6023 
6024     /**
6025      * Set this matrix to a rotation transformation using the given {@link AxisAngle4d}.
6026      * <p>
6027      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6028      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6029      * When used with a left-handed coordinate system, the rotation is clockwise.
6030      * <p>
6031      * The resulting matrix can be multiplied against another transformation
6032      * matrix to obtain an additional rotation.
6033      * <p>
6034      * In order to apply the rotation transformation to an existing transformation,
6035      * use {@link #rotate(ref AxisAngle4d) rotate()} instead.
6036      * <p>
6037      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
6038      *
6039      * @see #rotate(ref AxisAngle4d)
6040      * 
6041      * @param angleAxis
6042      *          the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized})
6043      * @return this
6044      */
6045     ref public Matrix4d rotation(ref AxisAngle4d angleAxis) return {
6046         return rotation(angleAxis.angle, angleAxis.x, angleAxis.y, angleAxis.z);
6047     }
6048 
6049     /**
6050      * Set this matrix to the rotation - and possibly scaling - transformation of the given {@link Quaterniond}.
6051      * <p>
6052      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6053      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6054      * When used with a left-handed coordinate system, the rotation is clockwise.
6055      * <p>
6056      * The resulting matrix can be multiplied against another transformation
6057      * matrix to obtain an additional rotation.
6058      * <p>
6059      * In order to apply the rotation transformation to an existing transformation,
6060      * use {@link #rotate(ref Quaterniond) rotate()} instead.
6061      * <p>
6062      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6063      * 
6064      * @see #rotate(ref Quaterniond)
6065      * 
6066      * @param quat
6067      *          the {@link Quaterniond}
6068      * @return this
6069      */
6070     ref public Matrix4d rotation(ref Quaterniond quat) return {
6071         double w2 = quat.w * quat.w;
6072         double x2 = quat.x * quat.x;
6073         double y2 = quat.y * quat.y;
6074         double z2 = quat.z * quat.z;
6075         double zw = quat.z * quat.w, dzw = zw + zw;
6076         double xy = quat.x * quat.y, dxy = xy + xy;
6077         double xz = quat.x * quat.z, dxz = xz + xz;
6078         double yw = quat.y * quat.w, dyw = yw + yw;
6079         double yz = quat.y * quat.z, dyz = yz + yz;
6080         double xw = quat.x * quat.w, dxw = xw + xw;
6081         if ((properties & PROPERTY_IDENTITY) == 0)
6082             this._identity();
6083         _m00(w2 + x2 - z2 - y2).
6084         _m01(dxy + dzw).
6085         _m02(dxz - dyw).
6086         _m10(-dzw + dxy).
6087         _m11(y2 - z2 + w2 - x2).
6088         _m12(dyz + dxw).
6089         _m20(dyw + dxz).
6090         _m21(dyz - dxw).
6091         _m22(z2 - y2 - x2 + w2).
6092         _properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
6093         return this;
6094     }
6095 
6096 
6097     /**
6098      * Set <code>this</code> matrix to <code>T * R * S</code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code>,
6099      * <code>R</code> is a rotation transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>, and <code>S</code> is a scaling transformation
6100      * which scales the three axes x, y and z by <code>(sx, sy, sz)</code>.
6101      * <p>
6102      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6103      * at last the translation.
6104      * <p>
6105      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6106      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6107      * When used with a left-handed coordinate system, the rotation is clockwise.
6108      * <p>
6109      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz)</code>
6110      * 
6111      * @see #translation(double, double, double)
6112      * @see #rotate(ref Quaterniond)
6113      * @see #scale(double, double, double)
6114      * 
6115      * @param tx
6116      *          the number of units by which to translate the x-component
6117      * @param ty
6118      *          the number of units by which to translate the y-component
6119      * @param tz
6120      *          the number of units by which to translate the z-component
6121      * @param qx
6122      *          the x-coordinate of the vector part of the quaternion
6123      * @param qy
6124      *          the y-coordinate of the vector part of the quaternion
6125      * @param qz
6126      *          the z-coordinate of the vector part of the quaternion
6127      * @param qw
6128      *          the scalar part of the quaternion
6129      * @param sx
6130      *          the scaling factor for the x-axis
6131      * @param sy
6132      *          the scaling factor for the y-axis
6133      * @param sz
6134      *          the scaling factor for the z-axis
6135      * @return this
6136      */
6137     ref public Matrix4d translationRotateScale(double tx, double ty, double tz, 
6138                                            double qx, double qy, double qz, double qw, 
6139                                            double sx, double sy, double sz) return {
6140         double dqx = qx + qx, dqy = qy + qy, dqz = qz + qz;
6141         double q00 = dqx * qx;
6142         double q11 = dqy * qy;
6143         double q22 = dqz * qz;
6144         double q01 = dqx * qy;
6145         double q02 = dqx * qz;
6146         double q03 = dqx * qw;
6147         double q12 = dqy * qz;
6148         double q13 = dqy * qw;
6149         double q23 = dqz * qw;
6150         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
6151         _m00(sx - (q11 + q22) * sx).
6152         _m01((q01 + q23) * sx).
6153         _m02((q02 - q13) * sx).
6154         _m03(0.0).
6155         _m10((q01 - q23) * sy).
6156         _m11(sy - (q22 + q00) * sy).
6157         _m12((q12 + q03) * sy).
6158         _m13(0.0).
6159         _m20((q02 + q13) * sz).
6160         _m21((q12 - q03) * sz).
6161         _m22(sz - (q11 + q00) * sz).
6162         _m23(0.0).
6163         _m30(tx).
6164         _m31(ty).
6165         _m32(tz).
6166         _m33(1.0).
6167         properties = PROPERTY_AFFINE | (one ? PROPERTY_ORTHONORMAL : 0);
6168         return this;
6169     }
6170 
6171     /**
6172      * Set <code>this</code> matrix to <code>T * R * S</code>, where <code>T</code> is the given <code>translation</code>,
6173      * <code>R</code> is a rotation transformation specified by the given quaternion, and <code>S</code> is a scaling transformation
6174      * which scales the axes by <code>scale</code>.
6175      * <p>
6176      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6177      * at last the translation.
6178      * <p>
6179      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6180      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6181      * When used with a left-handed coordinate system, the rotation is clockwise.
6182      * <p>
6183      * This method is equivalent to calling: <code>translation(translation).rotate(quat).scale(scale)</code>
6184      * 
6185      * @see #translation(ref Vector3d)
6186      * @see #rotate(ref Quaterniond)
6187      * @see #scale(ref Vector3d)
6188      * 
6189      * @param translation
6190      *          the translation
6191      * @param quat
6192      *          the quaternion representing a rotation
6193      * @param scale
6194      *          the scaling factors
6195      * @return this
6196      */
6197     ref public Matrix4d translationRotateScale(ref Vector3d translation, 
6198                                            Quaterniond quat, 
6199                                            Vector3d scale) return {
6200         return translationRotateScale(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w, scale.x, scale.y, scale.z);
6201     }
6202 
6203     /**
6204      * Set <code>this</code> matrix to <code>T * R * S</code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code>,
6205      * <code>R</code> is a rotation transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>, and <code>S</code> is a scaling transformation
6206      * which scales all three axes by <code>scale</code>.
6207      * <p>
6208      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6209      * at last the translation.
6210      * <p>
6211      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6212      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6213      * When used with a left-handed coordinate system, the rotation is clockwise.
6214      * <p>
6215      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat).scale(scale)</code>
6216      * 
6217      * @see #translation(double, double, double)
6218      * @see #rotate(ref Quaterniond)
6219      * @see #scale(double)
6220      * 
6221      * @param tx
6222      *          the number of units by which to translate the x-component
6223      * @param ty
6224      *          the number of units by which to translate the y-component
6225      * @param tz
6226      *          the number of units by which to translate the z-component
6227      * @param qx
6228      *          the x-coordinate of the vector part of the quaternion
6229      * @param qy
6230      *          the y-coordinate of the vector part of the quaternion
6231      * @param qz
6232      *          the z-coordinate of the vector part of the quaternion
6233      * @param qw
6234      *          the scalar part of the quaternion
6235      * @param scale
6236      *          the scaling factor for all three axes
6237      * @return this
6238      */
6239     ref public Matrix4d translationRotateScale(double tx, double ty, double tz, 
6240                                            double qx, double qy, double qz, double qw, 
6241                                            double scale) return {
6242         return translationRotateScale(tx, ty, tz, qx, qy, qz, qw, scale, scale, scale);
6243     }
6244 
6245     /**
6246      * Set <code>this</code> matrix to <code>T * R * S</code>, where <code>T</code> is the given <code>translation</code>,
6247      * <code>R</code> is a rotation transformation specified by the given quaternion, and <code>S</code> is a scaling transformation
6248      * which scales all three axes by <code>scale</code>.
6249      * <p>
6250      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6251      * at last the translation.
6252      * <p>
6253      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6254      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6255      * When used with a left-handed coordinate system, the rotation is clockwise.
6256      * <p>
6257      * This method is equivalent to calling: <code>translation(translation).rotate(quat).scale(scale)</code>
6258      * 
6259      * @see #translation(ref Vector3d)
6260      * @see #rotate(ref Quaterniond)
6261      * @see #scale(double)
6262      * 
6263      * @param translation
6264      *          the translation
6265      * @param quat
6266      *          the quaternion representing a rotation
6267      * @param scale
6268      *          the scaling factors
6269      * @return this
6270      */
6271     ref public Matrix4d translationRotateScale(ref Vector3d translation, 
6272                                            Quaterniond quat, 
6273                                            double scale) return {
6274         return translationRotateScale(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w, scale, scale, scale);
6275     }
6276 
6277 
6278     /**
6279      * Set <code>this</code> matrix to <code>(T * R * S)<sup>-1</sup></code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code>,
6280      * <code>R</code> is a rotation transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>, and <code>S</code> is a scaling transformation
6281      * which scales the three axes x, y and z by <code>(sx, sy, sz)</code>.
6282      * <p>
6283      * This method is equivalent to calling: <code>translationRotateScale(...).invert()</code>
6284      * 
6285      * @see #translationRotateScale(double, double, double, double, double, double, double, double, double, double)
6286      * @see #invert()
6287      * 
6288      * @param tx
6289      *          the number of units by which to translate the x-component
6290      * @param ty
6291      *          the number of units by which to translate the y-component
6292      * @param tz
6293      *          the number of units by which to translate the z-component
6294      * @param qx
6295      *          the x-coordinate of the vector part of the quaternion
6296      * @param qy
6297      *          the y-coordinate of the vector part of the quaternion
6298      * @param qz
6299      *          the z-coordinate of the vector part of the quaternion
6300      * @param qw
6301      *          the scalar part of the quaternion
6302      * @param sx
6303      *          the scaling factor for the x-axis
6304      * @param sy
6305      *          the scaling factor for the y-axis
6306      * @param sz
6307      *          the scaling factor for the z-axis
6308      * @return this
6309      */
6310     ref public Matrix4d translationRotateScaleInvert(double tx, double ty, double tz, 
6311                                                  double qx, double qy, double qz, double qw, 
6312                                                  double sx, double sy, double sz) return {
6313         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
6314         if (one)
6315             return translationRotateInvert(tx, ty, tz, qx, qy, qz, qw);
6316         double nqx = -qx, nqy = -qy, nqz = -qz;
6317         double dqx = nqx + nqx;
6318         double dqy = nqy + nqy;
6319         double dqz = nqz + nqz;
6320         double q00 = dqx * nqx;
6321         double q11 = dqy * nqy;
6322         double q22 = dqz * nqz;
6323         double q01 = dqx * nqy;
6324         double q02 = dqx * nqz;
6325         double q03 = dqx * qw;
6326         double q12 = dqy * nqz;
6327         double q13 = dqy * qw;
6328         double q23 = dqz * qw;
6329         double isx = 1/sx, isy = 1/sy, isz = 1/sz;
6330         _m00(isx * (1.0 - q11 - q22)).
6331         _m01(isy * (q01 + q23)).
6332         _m02(isz * (q02 - q13)).
6333         _m03(0.0).
6334         _m10(isx * (q01 - q23)).
6335         _m11(isy * (1.0 - q22 - q00)).
6336         _m12(isz * (q12 + q03)).
6337         _m13(0.0).
6338         _m20(isx * (q02 + q13)).
6339         _m21(isy * (q12 - q03)).
6340         _m22(isz * (1.0 - q11 - q00)).
6341         _m23(0.0).
6342         _m30(-m00 * tx - m10 * ty - m20 * tz).
6343         _m31(-m01 * tx - m11 * ty - m21 * tz).
6344         _m32(-m02 * tx - m12 * ty - m22 * tz).
6345         _m33(1.0).
6346         properties = PROPERTY_AFFINE;
6347         return this;
6348     }
6349 
6350     /**
6351      * Set <code>this</code> matrix to <code>(T * R * S)<sup>-1</sup></code>, where <code>T</code> is the given <code>translation</code>,
6352      * <code>R</code> is a rotation transformation specified by the given quaternion, and <code>S</code> is a scaling transformation
6353      * which scales the axes by <code>scale</code>.
6354      * <p>
6355      * This method is equivalent to calling: <code>translationRotateScale(...).invert()</code>
6356      * 
6357      * @see #translationRotateScale(ref Vector3d, Quaterniond, Vector3d)
6358      * @see #invert()
6359      * 
6360      * @param translation
6361      *          the translation
6362      * @param quat
6363      *          the quaternion representing a rotation
6364      * @param scale
6365      *          the scaling factors
6366      * @return this
6367      */
6368     ref public Matrix4d translationRotateScaleInvert(ref Vector3d translation, 
6369                                                  Quaterniond quat, 
6370                                                  Vector3d scale) return {
6371         return translationRotateScaleInvert(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w, scale.x, scale.y, scale.z);
6372     }
6373 
6374     
6375     /**
6376      * Set <code>this</code> matrix to <code>(T * R * S)<sup>-1</sup></code>, where <code>T</code> is the given <code>translation</code>,
6377      * <code>R</code> is a rotation transformation specified by the given quaternion, and <code>S</code> is a scaling transformation
6378      * which scales all three axes by <code>scale</code>.
6379      * <p>
6380      * This method is equivalent to calling: <code>translationRotateScale(...).invert()</code>
6381      * 
6382      * @see #translationRotateScale(ref Vector3d, Quaterniond, double)
6383      * @see #invert()
6384      * 
6385      * @param translation
6386      *          the translation
6387      * @param quat
6388      *          the quaternion representing a rotation
6389      * @param scale
6390      *          the scaling factors
6391      * @return this
6392      */
6393     ref public Matrix4d translationRotateScaleInvert(ref Vector3d translation, 
6394                                                  Quaterniond quat, 
6395                                                  double scale) return {
6396         return translationRotateScaleInvert(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w, scale, scale, scale);
6397     }
6398 
6399 
6400     /**
6401      * Set <code>this</code> matrix to <code>T * R * S * M</code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code>,
6402      * <code>R</code> is a rotation - and possibly scaling - transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>, <code>S</code> is a scaling transformation
6403      * which scales the three axes x, y and z by <code>(sx, sy, sz)</code> and <code>M</code> is an {@link #isAffine() affine} matrix.
6404      * <p>
6405      * When transforming a vector by the resulting matrix the transformation described by <code>M</code> will be applied first, then the scaling, then rotation and
6406      * at last the translation.
6407      * <p>
6408      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6409      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6410      * When used with a left-handed coordinate system, the rotation is clockwise.
6411      * <p>
6412      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz).mulAffine(m)</code>
6413      * 
6414      * @see #translation(double, double, double)
6415      * @see #rotate(ref Quaterniond)
6416      * @see #scale(double, double, double)
6417      * @see #mulAffine(Matrix4d)
6418      * 
6419      * @param tx
6420      *          the number of units by which to translate the x-component
6421      * @param ty
6422      *          the number of units by which to translate the y-component
6423      * @param tz
6424      *          the number of units by which to translate the z-component
6425      * @param qx
6426      *          the x-coordinate of the vector part of the quaternion
6427      * @param qy
6428      *          the y-coordinate of the vector part of the quaternion
6429      * @param qz
6430      *          the z-coordinate of the vector part of the quaternion
6431      * @param qw
6432      *          the scalar part of the quaternion
6433      * @param sx
6434      *          the scaling factor for the x-axis
6435      * @param sy
6436      *          the scaling factor for the y-axis
6437      * @param sz
6438      *          the scaling factor for the z-axis
6439      * @param m
6440      *          the {@link #isAffine() affine} matrix to multiply by
6441      * @return this
6442      */
6443     ref public Matrix4d translationRotateScaleMulAffine(double tx, double ty, double tz, 
6444                                                     double qx, double qy, double qz, double qw, 
6445                                                     double sx, double sy, double sz,
6446                                                     Matrix4d m) return {
6447         double w2 = qw * qw;
6448         double x2 = qx * qx;
6449         double y2 = qy * qy;
6450         double z2 = qz * qz;
6451         double zw = qz * qw;
6452         double xy = qx * qy;
6453         double xz = qx * qz;
6454         double yw = qy * qw;
6455         double yz = qy * qz;
6456         double xw = qx * qw;
6457         double nm00 = w2 + x2 - z2 - y2;
6458         double nm01 = xy + zw + zw + xy;
6459         double nm02 = xz - yw + xz - yw;
6460         double nm10 = -zw + xy - zw + xy;
6461         double nm11 = y2 - z2 + w2 - x2;
6462         double nm12 = yz + yz + xw + xw;
6463         double nm20 = yw + xz + xz + yw;
6464         double nm21 = yz + yz - xw - xw;
6465         double nm22 = z2 - y2 - x2 + w2;
6466         double m00 = nm00 * m.m00 + nm10 * m.m01 + nm20 * m.m02;
6467         double m01 = nm01 * m.m00 + nm11 * m.m01 + nm21 * m.m02;
6468         setm02(nm02 * m.m00 + nm12 * m.m01 + nm22 * m.m02);
6469         setm00(m00);
6470         setm01(m01);
6471         setm03(0.0);
6472         double m10 = nm00 * m.m10 + nm10 * m.m11 + nm20 * m.m12;
6473         double m11 = nm01 * m.m10 + nm11 * m.m11 + nm21 * m.m12;
6474         setm12(nm02 * m.m10 + nm12 * m.m11 + nm22 * m.m12);
6475         setm10(m10);
6476         setm11(m11);
6477         setm13(0.0);
6478         double m20 = nm00 * m.m20 + nm10 * m.m21 + nm20 * m.m22;
6479         double m21 = nm01 * m.m20 + nm11 * m.m21 + nm21 * m.m22;
6480         setm22(nm02 * m.m20 + nm12 * m.m21 + nm22 * m.m22);
6481         setm20(m20);
6482         setm21(m21);
6483         setm23(0.0);
6484         double m30 = nm00 * m.m30 + nm10 * m.m31 + nm20 * m.m32 + tx;
6485         double m31 = nm01 * m.m30 + nm11 * m.m31 + nm21 * m.m32 + ty;
6486         setm32(nm02 * m.m30 + nm12 * m.m31 + nm22 * m.m32 + tz);
6487         setm30(m30);
6488         setm31(m31);
6489         setm33(1.0);
6490         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
6491         properties = PROPERTY_AFFINE | (one && (m.properties & PROPERTY_ORTHONORMAL) != 0 ? PROPERTY_ORTHONORMAL : 0);
6492         return this;
6493     }
6494 
6495    
6496 
6497     /**
6498      * Set <code>this</code> matrix to <code>T * R</code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code> and
6499      * <code>R</code> is a rotation - and possibly scaling - transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>.
6500      * <p>
6501      * When transforming a vector by the resulting matrix the rotation - and possibly scaling - transformation will be applied first and then the translation.
6502      * <p>
6503      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6504      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6505      * When used with a left-handed coordinate system, the rotation is clockwise.
6506      * <p>
6507      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat)</code>
6508      * 
6509      * @see #translation(double, double, double)
6510      * @see #rotate(ref Quaterniond)
6511      * 
6512      * @param tx
6513      *          the number of units by which to translate the x-component
6514      * @param ty
6515      *          the number of units by which to translate the y-component
6516      * @param tz
6517      *          the number of units by which to translate the z-component
6518      * @param qx
6519      *          the x-coordinate of the vector part of the quaternion
6520      * @param qy
6521      *          the y-coordinate of the vector part of the quaternion
6522      * @param qz
6523      *          the z-coordinate of the vector part of the quaternion
6524      * @param qw
6525      *          the scalar part of the quaternion
6526      * @return this
6527      */
6528     ref public Matrix4d translationRotate(double tx, double ty, double tz, double qx, double qy, double qz, double qw) return {
6529         double w2 = qw * qw;
6530         double x2 = qx * qx;
6531         double y2 = qy * qy;
6532         double z2 = qz * qz;
6533         double zw = qz * qw;
6534         double xy = qx * qy;
6535         double xz = qx * qz;
6536         double yw = qy * qw;
6537         double yz = qy * qz;
6538         double xw = qx * qw;
6539         setm00(w2 + x2 - z2 - y2);
6540         setm01(xy + zw + zw + xy);
6541         setm02(xz - yw + xz - yw);
6542         setm10(-zw + xy - zw + xy);
6543         setm11(y2 - z2 + w2 - x2);
6544         setm12(yz + yz + xw + xw);
6545         setm20(yw + xz + xz + yw);
6546         setm21(yz + yz - xw - xw);
6547         setm22(z2 - y2 - x2 + w2);
6548         setm30(tx);
6549         setm31(ty);
6550         setm32(tz);
6551         setm33(1.0);
6552         this.properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
6553         return this;
6554     }
6555 
6556     /**
6557      * Set <code>this</code> matrix to <code>T * R</code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code> and
6558      * <code>R</code> is a rotation - and possibly scaling - transformation specified by the given quaternion.
6559      * <p>
6560      * When transforming a vector by the resulting matrix the rotation - and possibly scaling - transformation will be applied first and then the translation.
6561      * <p>
6562      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6563      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6564      * When used with a left-handed coordinate system, the rotation is clockwise.
6565      * <p>
6566      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat)</code>
6567      * 
6568      * @see #translation(double, double, double)
6569      * @see #rotate(ref Quaterniond)
6570      * 
6571      * @param tx
6572      *          the number of units by which to translate the x-component
6573      * @param ty
6574      *          the number of units by which to translate the y-component
6575      * @param tz
6576      *          the number of units by which to translate the z-component
6577      * @param quat
6578      *          the quaternion representing a rotation
6579      * @return this
6580      */
6581     ref public Matrix4d translationRotate(double tx, double ty, double tz, Quaterniond quat) return {
6582         return translationRotate(tx, ty, tz, quat.x, quat.y, quat.z, quat.w);
6583     }
6584 
6585     /**
6586      * Set <code>this</code> matrix to <code>T * R</code>, where <code>T</code> is the given <code>translation</code> and
6587      * <code>R</code> is a rotation transformation specified by the given quaternion.
6588      * <p>
6589      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6590      * at last the translation.
6591      * <p>
6592      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6593      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6594      * When used with a left-handed coordinate system, the rotation is clockwise.
6595      * <p>
6596      * This method is equivalent to calling: <code>translation(translation).rotate(quat)</code>
6597      * 
6598      * @see #translation(ref Vector3d)
6599      * @see #rotate(ref Quaterniond)
6600      * 
6601      * @param translation
6602      *          the translation
6603      * @param quat
6604      *          the quaternion representing a rotation
6605      * @return this
6606      */
6607     ref public Matrix4d translationRotate(ref Vector3d translation, 
6608                                       Quaterniond quat) return {
6609         return translationRotate(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w);
6610     }
6611 
6612     /**
6613      * Set <code>this</code> matrix to <code>(T * R)<sup>-1</sup></code>, where <code>T</code> is a translation by the given <code>(tx, ty, tz)</code> and
6614      * <code>R</code> is a rotation transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>.
6615      * <p>
6616      * This method is equivalent to calling: <code>translationRotate(...).invert()</code>
6617      * 
6618      * @see #translationRotate(double, double, double, double, double, double, double)
6619      * @see #invert()
6620      * 
6621      * @param tx
6622      *          the number of units by which to translate the x-component
6623      * @param ty
6624      *          the number of units by which to translate the y-component
6625      * @param tz
6626      *          the number of units by which to translate the z-component
6627      * @param qx
6628      *          the x-coordinate of the vector part of the quaternion
6629      * @param qy
6630      *          the y-coordinate of the vector part of the quaternion
6631      * @param qz
6632      *          the z-coordinate of the vector part of the quaternion
6633      * @param qw
6634      *          the scalar part of the quaternion
6635      * @return this
6636      */
6637     ref public Matrix4d translationRotateInvert(double tx, double ty, double tz, double qx, double qy, double qz, double qw) return {
6638         double nqx = -qx, nqy = -qy, nqz = -qz;
6639         double dqx = nqx + nqx;
6640         double dqy = nqy + nqy;
6641         double dqz = nqz + nqz;
6642         double q00 = dqx * nqx;
6643         double q11 = dqy * nqy;
6644         double q22 = dqz * nqz;
6645         double q01 = dqx * nqy;
6646         double q02 = dqx * nqz;
6647         double q03 = dqx * qw;
6648         double q12 = dqy * nqz;
6649         double q13 = dqy * qw;
6650         double q23 = dqz * qw;
6651         return this
6652         ._m00(1.0 - q11 - q22)
6653         ._m01(q01 + q23)
6654         ._m02(q02 - q13)
6655         ._m03(0.0)
6656         ._m10(q01 - q23)
6657         ._m11(1.0 - q22 - q00)
6658         ._m12(q12 + q03)
6659         ._m13(0.0)
6660         ._m20(q02 + q13)
6661         ._m21(q12 - q03)
6662         ._m22(1.0 - q11 - q00)
6663         ._m23(0.0)
6664         ._m30(-m00 * tx - m10 * ty - m20 * tz)
6665         ._m31(-m01 * tx - m11 * ty - m21 * tz)
6666         ._m32(-m02 * tx - m12 * ty - m22 * tz)
6667         ._m33(1.0)
6668         ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
6669     }
6670 
6671     
6672 
6673     /**
6674      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix and store
6675      * the result in <code>dest</code>.
6676      * <p>
6677      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6678      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6679      * When used with a left-handed coordinate system, the rotation is clockwise.
6680      * <p>
6681      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6682      * then the new matrix will be <code>M * Q</code>. So when transforming a
6683      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6684      * the quaternion rotation will be applied first!
6685      * <p>
6686      * In order to set the matrix to a rotation transformation without post-multiplying,
6687      * use {@link #rotation(ref Quaterniond)}.
6688      * <p>
6689      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6690      * 
6691      * @see #rotation(ref Quaterniond)
6692      * 
6693      * @param quat
6694      *          the {@link Quaterniond}
6695      * @param dest
6696      *          will hold the result
6697      * @return dest
6698      */
6699     public Matrix4d rotate(ref Quaterniond quat, ref Matrix4d dest) {
6700         if ((properties & PROPERTY_IDENTITY) != 0)
6701             return dest.rotation(quat);
6702         else if ((properties & PROPERTY_TRANSLATION) != 0)
6703             return rotateTranslation(quat, dest);
6704         else if ((properties & PROPERTY_AFFINE) != 0)
6705             return rotateAffine(quat, dest);
6706         return rotateGeneric(quat, dest);
6707     }
6708     private Matrix4d rotateGeneric(ref Quaterniond quat, ref Matrix4d dest) {
6709         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
6710         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
6711         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
6712         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
6713         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
6714         double rm00 = w2 + x2 - z2 - y2;
6715         double rm01 = dxy + dzw;
6716         double rm02 = dxz - dyw;
6717         double rm10 = -dzw + dxy;
6718         double rm11 = y2 - z2 + w2 - x2;
6719         double rm12 = dyz + dxw;
6720         double rm20 = dyw + dxz;
6721         double rm21 = dyz - dxw;
6722         double rm22 = z2 - y2 - x2 + w2;
6723         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
6724         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
6725         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
6726         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
6727         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
6728         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
6729         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
6730         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
6731         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
6732         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
6733         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
6734         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
6735         ._m00(nm00)
6736         ._m01(nm01)
6737         ._m02(nm02)
6738         ._m03(nm03)
6739         ._m10(nm10)
6740         ._m11(nm11)
6741         ._m12(nm12)
6742         ._m13(nm13)
6743         ._m30(m30)
6744         ._m31(m31)
6745         ._m32(m32)
6746         ._m33(m33)
6747         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
6748         return dest;
6749     }
6750 
6751 
6752     /**
6753      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix.
6754      * <p>
6755      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6756      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6757      * When used with a left-handed coordinate system, the rotation is clockwise.
6758      * <p>
6759      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6760      * then the new matrix will be <code>M * Q</code>. So when transforming a
6761      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6762      * the quaternion rotation will be applied first!
6763      * <p>
6764      * In order to set the matrix to a rotation transformation without post-multiplying,
6765      * use {@link #rotation(ref Quaterniond)}.
6766      * <p>
6767      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6768      * 
6769      * @see #rotation(ref Quaterniond)
6770      * 
6771      * @param quat
6772      *          the {@link Quaterniond}
6773      * @return this
6774      */
6775     ref public Matrix4d rotate(ref Quaterniond quat) return {
6776         rotate(quat, this);
6777         return this;
6778     }
6779 
6780 
6781     /**
6782      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this {@link #isAffine() affine} matrix and store
6783      * the result in <code>dest</code>.
6784      * <p>
6785      * This method assumes <code>this</code> to be {@link #isAffine() affine}.
6786      * <p>
6787      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6788      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6789      * When used with a left-handed coordinate system, the rotation is clockwise.
6790      * <p>
6791      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6792      * then the new matrix will be <code>M * Q</code>. So when transforming a
6793      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6794      * the quaternion rotation will be applied first!
6795      * <p>
6796      * In order to set the matrix to a rotation transformation without post-multiplying,
6797      * use {@link #rotation(ref Quaterniond)}.
6798      * <p>
6799      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6800      * 
6801      * @see #rotation(ref Quaterniond)
6802      * 
6803      * @param quat
6804      *          the {@link Quaterniond}
6805      * @param dest
6806      *          will hold the result
6807      * @return dest
6808      */
6809     public Matrix4d rotateAffine(ref Quaterniond quat, ref Matrix4d dest) {
6810         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
6811         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
6812         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
6813         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
6814         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
6815         double rm00 = w2 + x2 - z2 - y2;
6816         double rm01 = dxy + dzw;
6817         double rm02 = dxz - dyw;
6818         double rm10 = -dzw + dxy;
6819         double rm11 = y2 - z2 + w2 - x2;
6820         double rm12 = dyz + dxw;
6821         double rm20 = dyw + dxz;
6822         double rm21 = dyz - dxw;
6823         double rm22 = z2 - y2 - x2 + w2;
6824         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
6825         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
6826         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
6827         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
6828         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
6829         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
6830         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
6831         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
6832         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
6833         ._m23(0.0)
6834         ._m00(nm00)
6835         ._m01(nm01)
6836         ._m02(nm02)
6837         ._m03(0.0)
6838         ._m10(nm10)
6839         ._m11(nm11)
6840         ._m12(nm12)
6841         ._m13(0.0)
6842         ._m30(m30)
6843         ._m31(m31)
6844         ._m32(m32)
6845         ._m33(m33)
6846         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
6847         return dest;
6848     }
6849 
6850     /**
6851      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix.
6852      * <p>
6853      * This method assumes <code>this</code> to be {@link #isAffine() affine}.
6854      * <p>
6855      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6856      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6857      * When used with a left-handed coordinate system, the rotation is clockwise.
6858      * <p>
6859      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6860      * then the new matrix will be <code>M * Q</code>. So when transforming a
6861      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6862      * the quaternion rotation will be applied first!
6863      * <p>
6864      * In order to set the matrix to a rotation transformation without post-multiplying,
6865      * use {@link #rotation(ref Quaterniond)}.
6866      * <p>
6867      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6868      * 
6869      * @see #rotation(ref Quaterniond)
6870      * 
6871      * @param quat
6872      *          the {@link Quaterniond}
6873      * @return this
6874      */
6875     ref public Matrix4d rotateAffine(ref Quaterniond quat) return {
6876         rotateAffine(quat, this);
6877         return this;
6878     }
6879 
6880     /**
6881      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix, which is assumed to only contain a translation, and store
6882      * the result in <code>dest</code>.
6883      * <p>
6884      * This method assumes <code>this</code> to only contain a translation.
6885      * <p>
6886      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6887      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6888      * When used with a left-handed coordinate system, the rotation is clockwise.
6889      * <p>
6890      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6891      * then the new matrix will be <code>M * Q</code>. So when transforming a
6892      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6893      * the quaternion rotation will be applied first!
6894      * <p>
6895      * In order to set the matrix to a rotation transformation without post-multiplying,
6896      * use {@link #rotation(ref Quaterniond)}.
6897      * <p>
6898      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6899      * 
6900      * @see #rotation(ref Quaterniond)
6901      * 
6902      * @param quat
6903      *          the {@link Quaterniond}
6904      * @param dest
6905      *          will hold the result
6906      * @return dest
6907      */
6908     public Matrix4d rotateTranslation(ref Quaterniond quat, ref Matrix4d dest) {
6909         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
6910         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
6911         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
6912         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
6913         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
6914         double rm00 = w2 + x2 - z2 - y2;
6915         double rm01 = dxy + dzw;
6916         double rm02 = dxz - dyw;
6917         double rm10 = -dzw + dxy;
6918         double rm11 = y2 - z2 + w2 - x2;
6919         double rm12 = dyz + dxw;
6920         double rm20 = dyw + dxz;
6921         double rm21 = dyz - dxw;
6922         double rm22 = z2 - y2 - x2 + w2;
6923         dest._m20(rm20)
6924         ._m21(rm21)
6925         ._m22(rm22)
6926         ._m23(0.0)
6927         ._m00(rm00)
6928         ._m01(rm01)
6929         ._m02(rm02)
6930         ._m03(0.0)
6931         ._m10(rm10)
6932         ._m11(rm11)
6933         ._m12(rm12)
6934         ._m13(0.0)
6935         ._m30(m30)
6936         ._m31(m31)
6937         ._m32(m32)
6938         ._m33(1.0)
6939         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
6940         return dest;
6941     }
6942 
6943 
6944     /**
6945      * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix and store
6946      * the result in <code>dest</code>.
6947      * <p>
6948      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6949      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6950      * When used with a left-handed coordinate system, the rotation is clockwise.
6951      * <p>
6952      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6953      * then the new matrix will be <code>Q * M</code>. So when transforming a
6954      * vector <code>v</code> with the new matrix by using <code>Q * M * v</code>,
6955      * the quaternion rotation will be applied last!
6956      * <p>
6957      * In order to set the matrix to a rotation transformation without pre-multiplying,
6958      * use {@link #rotation(ref Quaterniond)}.
6959      * <p>
6960      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6961      * 
6962      * @see #rotation(ref Quaterniond)
6963      * 
6964      * @param quat
6965      *          the {@link Quaterniond}
6966      * @param dest
6967      *          will hold the result
6968      * @return dest
6969      */
6970     public Matrix4d rotateLocal(ref Quaterniond quat, ref Matrix4d dest) {
6971         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
6972         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
6973         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
6974         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
6975         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
6976         double lm00 = w2 + x2 - z2 - y2;
6977         double lm01 = dxy + dzw;
6978         double lm02 = dxz - dyw;
6979         double lm10 = -dzw + dxy;
6980         double lm11 = y2 - z2 + w2 - x2;
6981         double lm12 = dyz + dxw;
6982         double lm20 = dyw + dxz;
6983         double lm21 = dyz - dxw;
6984         double lm22 = z2 - y2 - x2 + w2;
6985         double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02;
6986         double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02;
6987         double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02;
6988         double nm03 = m03;
6989         double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12;
6990         double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12;
6991         double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12;
6992         double nm13 = m13;
6993         double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22;
6994         double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22;
6995         double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22;
6996         double nm23 = m23;
6997         double nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32;
6998         double nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32;
6999         double nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32;
7000         dest._m00(nm00)
7001         ._m01(nm01)
7002         ._m02(nm02)
7003         ._m03(nm03)
7004         ._m10(nm10)
7005         ._m11(nm11)
7006         ._m12(nm12)
7007         ._m13(nm13)
7008         ._m20(nm20)
7009         ._m21(nm21)
7010         ._m22(nm22)
7011         ._m23(nm23)
7012         ._m30(nm30)
7013         ._m31(nm31)
7014         ._m32(nm32)
7015         ._m33(m33)
7016         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
7017         return dest;
7018     }
7019 
7020     /**
7021      * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix.
7022      * <p>
7023      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7024      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7025      * When used with a left-handed coordinate system, the rotation is clockwise.
7026      * <p>
7027      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
7028      * then the new matrix will be <code>Q * M</code>. So when transforming a
7029      * vector <code>v</code> with the new matrix by using <code>Q * M * v</code>,
7030      * the quaternion rotation will be applied last!
7031      * <p>
7032      * In order to set the matrix to a rotation transformation without pre-multiplying,
7033      * use {@link #rotation(ref Quaterniond)}.
7034      * <p>
7035      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
7036      * 
7037      * @see #rotation(ref Quaterniond)
7038      * 
7039      * @param quat
7040      *          the {@link Quaterniond}
7041      * @return this
7042      */
7043     ref public Matrix4d rotateLocal(ref Quaterniond quat) return {
7044         rotateLocal(quat, this);
7045         return this;
7046     }
7047 
7048 
7049     /**
7050      * Apply a rotation transformation, rotating about the given {@link AxisAngle4d}, to this matrix.
7051      * <p>
7052      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7053      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7054      * When used with a left-handed coordinate system, the rotation is clockwise.
7055      * <p>
7056      * If <code>M</code> is <code>this</code> matrix and <code>A</code> the rotation matrix obtained from the given {@link AxisAngle4d},
7057      * then the new matrix will be <code>M * A</code>. So when transforming a
7058      * vector <code>v</code> with the new matrix by using <code>M * A * v</code>,
7059      * the {@link AxisAngle4d} rotation will be applied first!
7060      * <p>
7061      * In order to set the matrix to a rotation transformation without post-multiplying,
7062      * use {@link #rotation(ref AxisAngle4d)}.
7063      * <p>
7064      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
7065      * 
7066      * @see #rotate(double, double, double, double)
7067      * @see #rotation(ref AxisAngle4d)
7068      * 
7069      * @param axisAngle
7070      *          the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized})
7071      * @return this
7072      */
7073     ref public Matrix4d rotate(ref AxisAngle4d axisAngle) return {
7074         return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z);
7075     }
7076 
7077     /**
7078      * Apply a rotation transformation, rotating about the given {@link AxisAngle4d} and store the result in <code>dest</code>.
7079      * <p>
7080      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7081      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7082      * When used with a left-handed coordinate system, the rotation is clockwise.
7083      * <p>
7084      * If <code>M</code> is <code>this</code> matrix and <code>A</code> the rotation matrix obtained from the given {@link AxisAngle4d},
7085      * then the new matrix will be <code>M * A</code>. So when transforming a
7086      * vector <code>v</code> with the new matrix by using <code>M * A * v</code>,
7087      * the {@link AxisAngle4d} rotation will be applied first!
7088      * <p>
7089      * In order to set the matrix to a rotation transformation without post-multiplying,
7090      * use {@link #rotation(ref AxisAngle4d)}.
7091      * <p>
7092      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
7093      * 
7094      * @see #rotate(double, double, double, double)
7095      * @see #rotation(ref AxisAngle4d)
7096      * 
7097      * @param axisAngle
7098      *          the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized})
7099      * @param dest
7100      *          will hold the result
7101      * @return dest
7102      */
7103     public Matrix4d rotate(ref AxisAngle4d axisAngle, ref Matrix4d dest) {
7104         return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z, dest);
7105     }
7106 
7107     /**
7108      * Apply a rotation transformation, rotating the given radians about the specified axis, to this matrix.
7109      * <p>
7110      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7111      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7112      * When used with a left-handed coordinate system, the rotation is clockwise.
7113      * <p>
7114      * If <code>M</code> is <code>this</code> matrix and <code>A</code> the rotation matrix obtained from the given angle and axis,
7115      * then the new matrix will be <code>M * A</code>. So when transforming a
7116      * vector <code>v</code> with the new matrix by using <code>M * A * v</code>,
7117      * the axis-angle rotation will be applied first!
7118      * <p>
7119      * In order to set the matrix to a rotation transformation without post-multiplying,
7120      * use {@link #rotation(double, Vector3d)}.
7121      * <p>
7122      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
7123      * 
7124      * @see #rotate(double, double, double, double)
7125      * @see #rotation(double, Vector3d)
7126      * 
7127      * @param angle
7128      *          the angle in radians
7129      * @param axis
7130      *          the rotation axis (needs to be {@link Vector3d#normalize() normalized})
7131      * @return this
7132      */
7133     ref public Matrix4d rotate(double angle, Vector3d axis) return {
7134         return rotate(angle, axis.x, axis.y, axis.z);
7135     }
7136 
7137     /**
7138      * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in <code>dest</code>.
7139      * <p>
7140      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7141      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7142      * When used with a left-handed coordinate system, the rotation is clockwise.
7143      * <p>
7144      * If <code>M</code> is <code>this</code> matrix and <code>A</code> the rotation matrix obtained from the given angle and axis,
7145      * then the new matrix will be <code>M * A</code>. So when transforming a
7146      * vector <code>v</code> with the new matrix by using <code>M * A * v</code>,
7147      * the axis-angle rotation will be applied first!
7148      * <p>
7149      * In order to set the matrix to a rotation transformation without post-multiplying,
7150      * use {@link #rotation(double, Vector3d)}.
7151      * <p>
7152      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
7153      * 
7154      * @see #rotate(double, double, double, double)
7155      * @see #rotation(double, Vector3d)
7156      * 
7157      * @param angle
7158      *          the angle in radians
7159      * @param axis
7160      *          the rotation axis (needs to be {@link Vector3d#normalize() normalized})
7161      * @param dest
7162      *          will hold the result
7163      * @return dest
7164      */
7165     public Matrix4d rotate(double angle, Vector3d axis, ref Matrix4d dest) {
7166         return rotate(angle, axis.x, axis.y, axis.z, dest);
7167     }
7168 
7169     public Vector4d getRow(int row, ref Vector4d dest) {
7170         switch (row) {
7171         case 0:
7172             dest.x = m00;
7173             dest.y = m10;
7174             dest.z = m20;
7175             dest.w = m30;
7176             break;
7177         case 1:
7178             dest.x = m01;
7179             dest.y = m11;
7180             dest.z = m21;
7181             dest.w = m31;
7182             break;
7183         case 2:
7184             dest.x = m02;
7185             dest.y = m12;
7186             dest.z = m22;
7187             dest.w = m32;
7188             break;
7189         case 3:
7190             dest.x = m03;
7191             dest.y = m13;
7192             dest.z = m23;
7193             dest.w = m33;
7194             break;
7195         default: {}
7196         }
7197         return dest;
7198     }
7199 
7200     public Vector3d getRow(int row, ref Vector3d dest){
7201         switch (row) {
7202         case 0:
7203             dest.x = m00;
7204             dest.y = m10;
7205             dest.z = m20;
7206             break;
7207         case 1:
7208             dest.x = m01;
7209             dest.y = m11;
7210             dest.z = m21;
7211             break;
7212         case 2:
7213             dest.x = m02;
7214             dest.y = m12;
7215             dest.z = m22;
7216             break;
7217         case 3:
7218             dest.x = m03;
7219             dest.y = m13;
7220             dest.z = m23;
7221             break;
7222         default: {}
7223         }
7224         return dest;
7225     }
7226 
7227     /**
7228      * Set the row at the given <code>row</code> index, starting with <code>0</code>.
7229      * 
7230      * @param row
7231      *          the row index in <code>[0..3]</code>
7232      * @param src
7233      *          the row components to set
7234      * @return this
7235      * @throws IndexOutOfBoundsException if <code>row</code> is not in <code>[0..3]</code>
7236      */
7237     ref public Matrix4d setRow(int row, Vector4d src) return {
7238         switch (row) {
7239         case 0:
7240             return _m00(src.x)._m10(src.y)._m20(src.z)._m30(src.w)._properties(0);
7241         case 1:
7242             return _m01(src.x)._m11(src.y)._m21(src.z)._m31(src.w)._properties(0);
7243         case 2:
7244             return _m02(src.x)._m12(src.y)._m22(src.z)._m32(src.w)._properties(0);
7245         case 3:
7246             return _m03(src.x)._m13(src.y)._m23(src.z)._m33(src.w)._properties(0);
7247         default:
7248             return this;
7249         }
7250     }
7251 
7252     public Vector4d getColumn(int column, ref Vector4d dest) {
7253         switch (column) {
7254         case 0:
7255             dest.x = m00;
7256             dest.y = m01;
7257             dest.z = m02;
7258             dest.w = m03;
7259             break;
7260         case 1:
7261             dest.x = m10;
7262             dest.y = m11;
7263             dest.z = m12;
7264             dest.w = m13;
7265             break;
7266         case 2:
7267             dest.x = m20;
7268             dest.y = m21;
7269             dest.z = m22;
7270             dest.w = m23;
7271             break;
7272         case 3:
7273             dest.x = m30;
7274             dest.y = m31;
7275             dest.z = m32;
7276             dest.w = m33;
7277             break;
7278         default: {}
7279         }
7280         return dest;
7281     }
7282 
7283     public Vector3d getColumn(int column, ref Vector3d dest) {
7284         switch (column) {
7285         case 0:
7286             dest.x = m00;
7287             dest.y = m01;
7288             dest.z = m02;
7289             break;
7290         case 1:
7291             dest.x = m10;
7292             dest.y = m11;
7293             dest.z = m12;
7294             break;
7295         case 2:
7296             dest.x = m20;
7297             dest.y = m21;
7298             dest.z = m22;
7299             break;
7300         case 3:
7301             dest.x = m30;
7302             dest.y = m31;
7303             dest.z = m32;
7304             break;
7305         default: {}
7306         }
7307         return dest;
7308     }
7309 
7310     /**
7311      * Set the column at the given <code>column</code> index, starting with <code>0</code>.
7312      * 
7313      * @param column
7314      *          the column index in <code>[0..3]</code>
7315      * @param src
7316      *          the column components to set
7317      * @return this
7318      * @throws IndexOutOfBoundsException if <code>column</code> is not in <code>[0..3]</code>
7319      */
7320     ref public Matrix4d setColumn(int column, Vector4d src) return {
7321         switch (column) {
7322         case 0:
7323             return _m00(src.x)._m01(src.y)._m02(src.z)._m03(src.w)._properties(0);
7324         case 1:
7325             return _m10(src.x)._m11(src.y)._m12(src.z)._m13(src.w)._properties(0);
7326         case 2:
7327             return _m20(src.x)._m21(src.y)._m22(src.z)._m23(src.w)._properties(0);
7328         case 3:
7329             return _m30(src.x)._m31(src.y)._m32(src.z)._m33(src.w)._properties(0);
7330         default:
7331             return this;
7332         }
7333     }
7334 
7335     public double get(int column, int row) {
7336         return MemUtil.get(this, column, row);
7337     }
7338 
7339     /**
7340      * Set the matrix element at the given column and row to the specified value.
7341      * 
7342      * @param column
7343      *          the colum index in <code>[0..3]</code>
7344      * @param row
7345      *          the row index in <code>[0..3]</code>
7346      * @param value
7347      *          the value
7348      * @return this
7349      */
7350     ref public Matrix4d set(int column, int row, double value) return {
7351         MemUtil.set(this, column, row, value);
7352         return this;
7353     }
7354 
7355     public double getRowColumn(int row, int column) {
7356         return MemUtil.get(this, column, row);
7357     }
7358 
7359     /**
7360      * Set the matrix element at the given row and column to the specified value.
7361      * 
7362      * @param row
7363      *          the row index in <code>[0..3]</code>
7364      * @param column
7365      *          the colum index in <code>[0..3]</code>
7366      * @param value
7367      *          the value
7368      * @return this
7369      */
7370     ref public Matrix4d setRowColumn(int row, int column, double value) return {
7371         MemUtil.set(this, column, row, value);
7372         return this;
7373     }
7374 
7375     /**
7376      * Compute a normal matrix from the upper left 3x3 submatrix of <code>this</code>
7377      * and store it into the upper left 3x3 submatrix of <code>this</code>.
7378      * All other values of <code>this</code> will be set to {@link #identity() identity}.
7379      * <p>
7380      * The normal matrix of <code>m</code> is the transpose of the inverse of <code>m</code>.
7381      * <p>
7382      * Please note that, if <code>this</code> is an orthogonal matrix or a matrix whose columns are orthogonal vectors, 
7383      * then this method <i>need not</i> be invoked, since in that case <code>this</code> itself is its normal matrix.
7384      * In that case, use {@link #set3x3(Matrix4d)} to set a given Matrix4f to only the upper left 3x3 submatrix
7385      * of this matrix.
7386      * 
7387      * @see #set3x3(Matrix4d)
7388      * 
7389      * @return this
7390      */
7391     ref public Matrix4d normal() return {
7392         normal(this);
7393         return this;
7394     }
7395 
7396     /**
7397      * Compute a normal matrix from the upper left 3x3 submatrix of <code>this</code>
7398      * and store it into the upper left 3x3 submatrix of <code>dest</code>.
7399      * All other values of <code>dest</code> will be set to {@link #identity() identity}.
7400      * <p>
7401      * The normal matrix of <code>m</code> is the transpose of the inverse of <code>m</code>.
7402      * <p>
7403      * Please note that, if <code>this</code> is an orthogonal matrix or a matrix whose columns are orthogonal vectors, 
7404      * then this method <i>need not</i> be invoked, since in that case <code>this</code> itself is its normal matrix.
7405      * In that case, use {@link #set3x3(Matrix4d)} to set a given Matrix4d to only the upper left 3x3 submatrix
7406      * of a given matrix.
7407      * 
7408      * @see #set3x3(Matrix4d)
7409      * 
7410      * @param dest
7411      *             will hold the result
7412      * @return dest
7413      */
7414     public Matrix4d normal(ref Matrix4d dest) {
7415         if ((properties & PROPERTY_IDENTITY) != 0)
7416             return dest.identity();
7417         else if ((properties & PROPERTY_ORTHONORMAL) != 0)
7418             return normalOrthonormal(dest);
7419         return normalGeneric(dest);
7420     }
7421     private Matrix4d normalOrthonormal(ref Matrix4d dest) {
7422         if (dest != this)
7423             dest.set(this);
7424         return dest._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
7425     }
7426     private Matrix4d normalGeneric(ref Matrix4d dest) {
7427         double m00m11 = m00 * m11;
7428         double m01m10 = m01 * m10;
7429         double m02m10 = m02 * m10;
7430         double m00m12 = m00 * m12;
7431         double m01m12 = m01 * m12;
7432         double m02m11 = m02 * m11;
7433         double det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20;
7434         double s = 1.0 / det;
7435         /* Invert and transpose in one go */
7436         double nm00 = (m11 * m22 - m21 * m12) * s;
7437         double nm01 = (m20 * m12 - m10 * m22) * s;
7438         double nm02 = (m10 * m21 - m20 * m11) * s;
7439         double nm10 = (m21 * m02 - m01 * m22) * s;
7440         double nm11 = (m00 * m22 - m20 * m02) * s;
7441         double nm12 = (m20 * m01 - m00 * m21) * s;
7442         double nm20 = (m01m12 - m02m11) * s;
7443         double nm21 = (m02m10 - m00m12) * s;
7444         double nm22 = (m00m11 - m01m10) * s;
7445         return dest
7446         ._m00(nm00)
7447         ._m01(nm01)
7448         ._m02(nm02)
7449         ._m03(0.0)
7450         ._m10(nm10)
7451         ._m11(nm11)
7452         ._m12(nm12)
7453         ._m13(0.0)
7454         ._m20(nm20)
7455         ._m21(nm21)
7456         ._m22(nm22)
7457         ._m23(0.0)
7458         ._m30(0.0)
7459         ._m31(0.0)
7460         ._m32(0.0)
7461         ._m33(1.0)
7462         ._properties((properties | PROPERTY_AFFINE) & ~(PROPERTY_TRANSLATION | PROPERTY_PERSPECTIVE));
7463     }
7464 
7465     /**
7466      * Compute a normal matrix from the upper left 3x3 submatrix of <code>this</code>
7467      * and store it into <code>dest</code>.
7468      * <p>
7469      * The normal matrix of <code>m</code> is the transpose of the inverse of <code>m</code>.
7470      * <p>
7471      * Please note that, if <code>this</code> is an orthogonal matrix or a matrix whose columns are orthogonal vectors, 
7472      * then this method <i>need not</i> be invoked, since in that case <code>this</code> itself is its normal matrix.
7473      * In that case, use {@link Matrix3d#set(Matrix4d)} to set a given Matrix3d to only the upper left 3x3 submatrix
7474      * of this matrix.
7475      * 
7476      * @see Matrix3d#set(Matrix4d)
7477      * @see #get3x3(Matrix3d)
7478      * 
7479      * @param dest
7480      *             will hold the result
7481      * @return dest
7482      */
7483     public Matrix3d normal(ref Matrix3d dest) {
7484         if ((properties & PROPERTY_ORTHONORMAL) != 0)
7485             return normalOrthonormal(dest);
7486         return normalGeneric(dest);
7487     }
7488     private Matrix3d normalOrthonormal(ref Matrix3d dest) {
7489         dest.set(this);
7490         return dest;
7491     }
7492     private Matrix3d normalGeneric(ref Matrix3d dest) {
7493         double m00m11 = m00 * m11;
7494         double m01m10 = m01 * m10;
7495         double m02m10 = m02 * m10;
7496         double m00m12 = m00 * m12;
7497         double m01m12 = m01 * m12;
7498         double m02m11 = m02 * m11;
7499         double det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20;
7500         double s = 1.0 / det;
7501         /* Invert and transpose in one go */
7502         return dest._m00((m11 * m22 - m21 * m12) * s)
7503         ._m01((m20 * m12 - m10 * m22) * s)
7504         ._m02((m10 * m21 - m20 * m11) * s)
7505         ._m10((m21 * m02 - m01 * m22) * s)
7506         ._m11((m00 * m22 - m20 * m02) * s)
7507         ._m12((m20 * m01 - m00 * m21) * s)
7508         ._m20((m01m12 - m02m11) * s)
7509         ._m21((m02m10 - m00m12) * s)
7510         ._m22((m00m11 - m01m10) * s);
7511     }
7512 
7513     /**
7514      * Compute the cofactor matrix of the upper left 3x3 submatrix of <code>this</code>.
7515      * <p>
7516      * The cofactor matrix can be used instead of {@link #normal()} to transform normals
7517      * when the orientation of the normals with respect to the surface should be preserved.
7518      * 
7519      * @return this
7520      */
7521     ref public Matrix4d cofactor3x3() return {
7522         cofactor3x3(this);
7523         return this;
7524     }
7525 
7526     /**
7527      * Compute the cofactor matrix of the upper left 3x3 submatrix of <code>this</code>
7528      * and store it into <code>dest</code>.
7529      * <p>
7530      * The cofactor matrix can be used instead of {@link #normal(Matrix3d)} to transform normals
7531      * when the orientation of the normals with respect to the surface should be preserved.
7532      * 
7533      * @param dest
7534      *             will hold the result
7535      * @return dest
7536      */
7537     public Matrix3d cofactor3x3(ref Matrix3d dest) {
7538         return dest._m00(m11 * m22 - m21 * m12)
7539         ._m01(m20 * m12 - m10 * m22)
7540         ._m02(m10 * m21 - m20 * m11)
7541         ._m10(m21 * m02 - m01 * m22)
7542         ._m11(m00 * m22 - m20 * m02)
7543         ._m12(m20 * m01 - m00 * m21)
7544         ._m20(m01 * m12 - m02 * m11)
7545         ._m21(m02 * m10 - m00 * m12)
7546         ._m22(m00 * m11 - m01 * m10);
7547     }
7548 
7549     /**
7550      * Compute the cofactor matrix of the upper left 3x3 submatrix of <code>this</code>
7551      * and store it into <code>dest</code>.
7552      * All other values of <code>dest</code> will be set to {@link #identity() identity}.
7553      * <p>
7554      * The cofactor matrix can be used instead of {@link #normal(Matrix4d)} to transform normals
7555      * when the orientation of the normals with respect to the surface should be preserved.
7556      * 
7557      * @param dest
7558      *             will hold the result
7559      * @return dest
7560      */
7561     public Matrix4d cofactor3x3(ref Matrix4d dest) {
7562         double nm10 = m21 * m02 - m01 * m22;
7563         double nm11 = m00 * m22 - m20 * m02;
7564         double nm12 = m20 * m01 - m00 * m21;
7565         double nm20 = m01 * m12 - m11 * m02;
7566         double nm21 = m02 * m10 - m12 * m00;
7567         double nm22 = m00 * m11 - m10 * m01;
7568         return dest
7569         ._m00(m11 * m22 - m21 * m12)
7570         ._m01(m20 * m12 - m10 * m22)
7571         ._m02(m10 * m21 - m20 * m11)
7572         ._m03(0.0)
7573         ._m10(nm10)
7574         ._m11(nm11)
7575         ._m12(nm12)
7576         ._m13(0.0)
7577         ._m20(nm20)
7578         ._m21(nm21)
7579         ._m22(nm22)
7580         ._m23(0.0)
7581         ._m30(0.0)
7582         ._m31(0.0)
7583         ._m32(0.0)
7584         ._m33(1.0)
7585         ._properties((properties | PROPERTY_AFFINE) & ~(PROPERTY_TRANSLATION | PROPERTY_PERSPECTIVE));
7586     }
7587 
7588     /**
7589      * Normalize the upper left 3x3 submatrix of this matrix.
7590      * <p>
7591      * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit
7592      * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself
7593      * (i.e. had <i>skewing</i>).
7594      * 
7595      * @return this
7596      */
7597     ref public Matrix4d normalize3x3() return {
7598         normalize3x3(this);
7599         return this;
7600     }
7601 
7602     public Matrix4d normalize3x3(ref Matrix4d dest) {
7603         double invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02);
7604         double invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12);
7605         double invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22);
7606         dest._m00(m00 * invXlen)._m01(m01 * invXlen)._m02(m02 * invXlen)
7607             ._m10(m10 * invYlen)._m11(m11 * invYlen)._m12(m12 * invYlen)
7608             ._m20(m20 * invZlen)._m21(m21 * invZlen)._m22(m22 * invZlen)
7609             ._m30(m30)._m31(m31)._m32(m32)._m33(m33)
7610             ._properties(properties);
7611         return dest;
7612     }
7613 
7614     public Matrix3d normalize3x3(ref Matrix3d dest) {
7615         double invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02);
7616         double invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12);
7617         double invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22);
7618         dest.m00 = (m00 * invXlen);
7619         dest.m01 = (m01 * invXlen);
7620         dest.m02 = (m02 * invXlen);
7621 
7622         dest.m10 = (m10 * invYlen);
7623         dest.m11 = (m11 * invYlen);
7624         dest.m12 = (m12 * invYlen);
7625 
7626         dest.m20 = (m20 * invZlen);
7627         dest.m21 = (m21 * invZlen);
7628         dest.m22 = (m22 * invZlen);
7629         return dest;
7630     }
7631 
7632     public Vector4d unproject(double winX, double winY, double winZ, int[] viewport, ref Vector4d dest) {
7633         double a = m00 * m11 - m01 * m10;
7634         double b = m00 * m12 - m02 * m10;
7635         double c = m00 * m13 - m03 * m10;
7636         double d = m01 * m12 - m02 * m11;
7637         double e = m01 * m13 - m03 * m11;
7638         double f = m02 * m13 - m03 * m12;
7639         double g = m20 * m31 - m21 * m30;
7640         double h = m20 * m32 - m22 * m30;
7641         double i = m20 * m33 - m23 * m30;
7642         double j = m21 * m32 - m22 * m31;
7643         double k = m21 * m33 - m23 * m31;
7644         double l = m22 * m33 - m23 * m32;
7645         double det = a * l - b * k + c * j + d * i - e * h + f * g;
7646         det = 1.0 / det;
7647         double im00 = ( m11 * l - m12 * k + m13 * j) * det;
7648         double im01 = (-m01 * l + m02 * k - m03 * j) * det;
7649         double im02 = ( m31 * f - m32 * e + m33 * d) * det;
7650         double im03 = (-m21 * f + m22 * e - m23 * d) * det;
7651         double im10 = (-m10 * l + m12 * i - m13 * h) * det;
7652         double im11 = ( m00 * l - m02 * i + m03 * h) * det;
7653         double im12 = (-m30 * f + m32 * c - m33 * b) * det;
7654         double im13 = ( m20 * f - m22 * c + m23 * b) * det;
7655         double im20 = ( m10 * k - m11 * i + m13 * g) * det;
7656         double im21 = (-m00 * k + m01 * i - m03 * g) * det;
7657         double im22 = ( m30 * e - m31 * c + m33 * a) * det;
7658         double im23 = (-m20 * e + m21 * c - m23 * a) * det;
7659         double im30 = (-m10 * j + m11 * h - m12 * g) * det;
7660         double im31 = ( m00 * j - m01 * h + m02 * g) * det;
7661         double im32 = (-m30 * d + m31 * b - m32 * a) * det;
7662         double im33 = ( m20 * d - m21 * b + m22 * a) * det;
7663         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7664         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7665         double ndcZ = winZ+winZ-1.0;
7666         double invW = 1.0 / (im03 * ndcX + im13 * ndcY + im23 * ndcZ + im33);
7667         dest.x = (im00 * ndcX + im10 * ndcY + im20 * ndcZ + im30) * invW;
7668         dest.y = (im01 * ndcX + im11 * ndcY + im21 * ndcZ + im31) * invW;
7669         dest.z = (im02 * ndcX + im12 * ndcY + im22 * ndcZ + im32) * invW;
7670         dest.w = 1.0;
7671         return dest;
7672     }
7673 
7674     public Vector3d unproject(double winX, double winY, double winZ, int[] viewport, ref Vector3d dest) {
7675         double a = m00 * m11 - m01 * m10;
7676         double b = m00 * m12 - m02 * m10;
7677         double c = m00 * m13 - m03 * m10;
7678         double d = m01 * m12 - m02 * m11;
7679         double e = m01 * m13 - m03 * m11;
7680         double f = m02 * m13 - m03 * m12;
7681         double g = m20 * m31 - m21 * m30;
7682         double h = m20 * m32 - m22 * m30;
7683         double i = m20 * m33 - m23 * m30;
7684         double j = m21 * m32 - m22 * m31;
7685         double k = m21 * m33 - m23 * m31;
7686         double l = m22 * m33 - m23 * m32;
7687         double det = a * l - b * k + c * j + d * i - e * h + f * g;
7688         det = 1.0 / det;
7689         double im00 = ( m11 * l - m12 * k + m13 * j) * det;
7690         double im01 = (-m01 * l + m02 * k - m03 * j) * det;
7691         double im02 = ( m31 * f - m32 * e + m33 * d) * det;
7692         double im03 = (-m21 * f + m22 * e - m23 * d) * det;
7693         double im10 = (-m10 * l + m12 * i - m13 * h) * det;
7694         double im11 = ( m00 * l - m02 * i + m03 * h) * det;
7695         double im12 = (-m30 * f + m32 * c - m33 * b) * det;
7696         double im13 = ( m20 * f - m22 * c + m23 * b) * det;
7697         double im20 = ( m10 * k - m11 * i + m13 * g) * det;
7698         double im21 = (-m00 * k + m01 * i - m03 * g) * det;
7699         double im22 = ( m30 * e - m31 * c + m33 * a) * det;
7700         double im23 = (-m20 * e + m21 * c - m23 * a) * det;
7701         double im30 = (-m10 * j + m11 * h - m12 * g) * det;
7702         double im31 = ( m00 * j - m01 * h + m02 * g) * det;
7703         double im32 = (-m30 * d + m31 * b - m32 * a) * det;
7704         double im33 = ( m20 * d - m21 * b + m22 * a) * det;
7705         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7706         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7707         double ndcZ = winZ+winZ-1.0;
7708         double invW = 1.0 / (im03 * ndcX + im13 * ndcY + im23 * ndcZ + im33);
7709         dest.x = (im00 * ndcX + im10 * ndcY + im20 * ndcZ + im30) * invW;
7710         dest.y = (im01 * ndcX + im11 * ndcY + im21 * ndcZ + im31) * invW;
7711         dest.z = (im02 * ndcX + im12 * ndcY + im22 * ndcZ + im32) * invW;
7712         return dest;
7713     }
7714 
7715     public Vector4d unproject(ref Vector3d winCoords, int[] viewport, ref Vector4d dest) {
7716         return unproject(winCoords.x, winCoords.y, winCoords.z, viewport, dest);
7717     }
7718 
7719     public Vector3d unproject(ref Vector3d winCoords, int[] viewport, ref Vector3d dest) {
7720         return unproject(winCoords.x, winCoords.y, winCoords.z, viewport, dest);
7721     }
7722 
7723     ref public Matrix4d unprojectRay(double winX, double winY, int[] viewport, ref Vector3d originDest, ref Vector3d dirDest) return {
7724         double a = m00 * m11 - m01 * m10;
7725         double b = m00 * m12 - m02 * m10;
7726         double c = m00 * m13 - m03 * m10;
7727         double d = m01 * m12 - m02 * m11;
7728         double e = m01 * m13 - m03 * m11;
7729         double f = m02 * m13 - m03 * m12;
7730         double g = m20 * m31 - m21 * m30;
7731         double h = m20 * m32 - m22 * m30;
7732         double i = m20 * m33 - m23 * m30;
7733         double j = m21 * m32 - m22 * m31;
7734         double k = m21 * m33 - m23 * m31;
7735         double l = m22 * m33 - m23 * m32;
7736         double det = a * l - b * k + c * j + d * i - e * h + f * g;
7737         det = 1.0 / det;
7738         double im00 = ( m11 * l - m12 * k + m13 * j) * det;
7739         double im01 = (-m01 * l + m02 * k - m03 * j) * det;
7740         double im02 = ( m31 * f - m32 * e + m33 * d) * det;
7741         double im03 = (-m21 * f + m22 * e - m23 * d) * det;
7742         double im10 = (-m10 * l + m12 * i - m13 * h) * det;
7743         double im11 = ( m00 * l - m02 * i + m03 * h) * det;
7744         double im12 = (-m30 * f + m32 * c - m33 * b) * det;
7745         double im13 = ( m20 * f - m22 * c + m23 * b) * det;
7746         double im20 = ( m10 * k - m11 * i + m13 * g) * det;
7747         double im21 = (-m00 * k + m01 * i - m03 * g) * det;
7748         double im22 = ( m30 * e - m31 * c + m33 * a) * det;
7749         double im23 = (-m20 * e + m21 * c - m23 * a) * det;
7750         double im30 = (-m10 * j + m11 * h - m12 * g) * det;
7751         double im31 = ( m00 * j - m01 * h + m02 * g) * det;
7752         double im32 = (-m30 * d + m31 * b - m32 * a) * det;
7753         double im33 = ( m20 * d - m21 * b + m22 * a) * det;
7754         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7755         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7756         double px = im00 * ndcX + im10 * ndcY + im30;
7757         double py = im01 * ndcX + im11 * ndcY + im31;
7758         double pz = im02 * ndcX + im12 * ndcY + im32;
7759         double invNearW = 1.0 / (im03 * ndcX + im13 * ndcY - im23 + im33);
7760         double nearX = (px - im20) * invNearW;
7761         double nearY = (py - im21) * invNearW;
7762         double nearZ = (pz - im22) * invNearW;
7763         double invW0 = 1.0 / (im03 * ndcX + im13 * ndcY + im33);
7764         double x0 = px * invW0;
7765         double y0 = py * invW0;
7766         double z0 = pz * invW0;
7767         originDest.x = nearX; originDest.y = nearY; originDest.z = nearZ;
7768         dirDest.x = x0 - nearX; dirDest.y = y0 - nearY; dirDest.z = z0 - nearZ;
7769         return this;
7770     }
7771 
7772     public Matrix4d unprojectRay(ref Vector2d winCoords, int[] viewport, ref Vector3d originDest, ref Vector3d dirDest) {
7773         return unprojectRay(winCoords.x, winCoords.y, viewport, originDest, dirDest);
7774     }
7775 
7776     public Vector4d unprojectInv(ref Vector3d winCoords, int[] viewport, ref Vector4d dest) {
7777         return unprojectInv(winCoords.x, winCoords.y, winCoords.z, viewport, dest);
7778     }
7779 
7780     public Vector4d unprojectInv(double winX, double winY, double winZ, int[] viewport, ref Vector4d dest) {
7781         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7782         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7783         double ndcZ = winZ+winZ-1.0;
7784         double invW = 1.0 / (m03 * ndcX + m13 * ndcY + m23 * ndcZ + m33);
7785         dest.x = (m00 * ndcX + m10 * ndcY + m20 * ndcZ + m30) * invW;
7786         dest.y = (m01 * ndcX + m11 * ndcY + m21 * ndcZ + m31) * invW;
7787         dest.z = (m02 * ndcX + m12 * ndcY + m22 * ndcZ + m32) * invW;
7788         dest.w = 1.0;
7789         return dest;
7790     }
7791 
7792     public Vector3d unprojectInv(ref Vector3d winCoords, int[] viewport, ref Vector3d dest) {
7793         return unprojectInv(winCoords.x, winCoords.y, winCoords.z, viewport, dest);
7794     }
7795 
7796     public Vector3d unprojectInv(double winX, double winY, double winZ, int[] viewport, ref Vector3d dest) {
7797         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7798         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7799         double ndcZ = winZ+winZ-1.0;
7800         double invW = 1.0 / (m03 * ndcX + m13 * ndcY + m23 * ndcZ + m33);
7801         dest.x = (m00 * ndcX + m10 * ndcY + m20 * ndcZ + m30) * invW;
7802         dest.y = (m01 * ndcX + m11 * ndcY + m21 * ndcZ + m31) * invW;
7803         dest.z = (m02 * ndcX + m12 * ndcY + m22 * ndcZ + m32) * invW;
7804         return dest;
7805     }
7806 
7807     public Matrix4d unprojectInvRay(ref Vector2d winCoords, int[] viewport, ref Vector3d originDest, ref Vector3d dirDest) {
7808         return unprojectInvRay(winCoords.x, winCoords.y, viewport, originDest, dirDest);
7809     }
7810 
7811     ref public Matrix4d unprojectInvRay(double winX, double winY, int[] viewport, ref Vector3d originDest, ref Vector3d dirDest) return  {
7812         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7813         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7814         double px = m00 * ndcX + m10 * ndcY + m30;
7815         double py = m01 * ndcX + m11 * ndcY + m31;
7816         double pz = m02 * ndcX + m12 * ndcY + m32;
7817         double invNearW = 1.0 / (m03 * ndcX + m13 * ndcY - m23 + m33);
7818         double nearX = (px - m20) * invNearW;
7819         double nearY = (py - m21) * invNearW;
7820         double nearZ = (pz - m22) * invNearW;
7821         double invW0 = 1.0 / (m03 * ndcX + m13 * ndcY + m33);
7822         double x0 = px * invW0;
7823         double y0 = py * invW0;
7824         double z0 = pz * invW0;
7825         originDest.x = nearX; originDest.y = nearY; originDest.z = nearZ;
7826         dirDest.x = x0 - nearX; dirDest.y = y0 - nearY; dirDest.z = z0 - nearZ;
7827         return this;
7828     }
7829 
7830     public Vector4d project(double x, double y, double z, int[] viewport, ref Vector4d winCoordsDest) {
7831         double invW = 1.0 / Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33)));
7832         double nx = Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))) * invW;
7833         double ny = Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))) * invW;
7834         double nz = Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))) * invW;
7835         return winCoordsDest.set(Math.fma(Math.fma(nx, 0.5, 0.5), viewport[2], viewport[0]),
7836                                  Math.fma(Math.fma(ny, 0.5, 0.5), viewport[3], viewport[1]),
7837                                  Math.fma(0.5, nz, 0.5),
7838                                  1.0);
7839     }
7840 
7841     public Vector3d project(double x, double y, double z, int[] viewport, ref Vector3d winCoordsDest) {
7842         double invW = 1.0 / Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33)));
7843         double nx = Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))) * invW;
7844         double ny = Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))) * invW;
7845         double nz = Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))) * invW;
7846         winCoordsDest.x = Math.fma(Math.fma(nx, 0.5, 0.5), viewport[2], viewport[0]);
7847         winCoordsDest.y = Math.fma(Math.fma(ny, 0.5, 0.5), viewport[3], viewport[1]);
7848         winCoordsDest.z = Math.fma(0.5, nz, 0.5);
7849         return winCoordsDest;
7850     }
7851 
7852     public Vector4d project(ref Vector3d position, int[] viewport, ref Vector4d dest) {
7853         return project(position.x, position.y, position.z, viewport, dest);
7854     }
7855 
7856     public Vector3d project(ref Vector3d position, int[] viewport, ref Vector3d dest) {
7857         return project(position.x, position.y, position.z, viewport, dest);
7858     }
7859 
7860     public Matrix4d reflect(double a, double b, double c, double d, ref Matrix4d dest) {
7861         if ((properties & PROPERTY_IDENTITY) != 0)
7862             return dest.reflection(a, b, c, d);
7863         if ((properties & PROPERTY_IDENTITY) != 0)
7864             return dest.reflection(a, b, c, d);
7865         else if ((properties & PROPERTY_AFFINE) != 0)
7866             return reflectAffine(a, b, c, d, dest);
7867         return reflectGeneric(a, b, c, d, dest);
7868     }
7869     private Matrix4d reflectAffine(double a, double b, double c, double d, ref Matrix4d dest) {
7870         double da = a + a, db = b + b, dc = c + c, dd = d + d;
7871         double rm00 = 1.0 - da * a;
7872         double rm01 = -da * b;
7873         double rm02 = -da * c;
7874         double rm10 = -db * a;
7875         double rm11 = 1.0 - db * b;
7876         double rm12 = -db * c;
7877         double rm20 = -dc * a;
7878         double rm21 = -dc * b;
7879         double rm22 = 1.0 - dc * c;
7880         double rm30 = -dd * a;
7881         double rm31 = -dd * b;
7882         double rm32 = -dd * c;
7883         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
7884         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
7885         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
7886         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
7887         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
7888         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
7889         // matrix multiplication
7890         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
7891         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
7892         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
7893         ._m33(m33)
7894         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
7895         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
7896         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
7897         ._m23(0.0)
7898         ._m00(nm00)
7899         ._m01(nm01)
7900         ._m02(nm02)
7901         ._m03(0.0)
7902         ._m10(nm10)
7903         ._m11(nm11)
7904         ._m12(nm12)
7905         ._m13(0.0)
7906         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
7907         return dest;
7908     }
7909     private Matrix4d reflectGeneric(double a, double b, double c, double d, ref Matrix4d dest) {
7910         double da = a + a, db = b + b, dc = c + c, dd = d + d;
7911         double rm00 = 1.0 - da * a;
7912         double rm01 = -da * b;
7913         double rm02 = -da * c;
7914         double rm10 = -db * a;
7915         double rm11 = 1.0 - db * b;
7916         double rm12 = -db * c;
7917         double rm20 = -dc * a;
7918         double rm21 = -dc * b;
7919         double rm22 = 1.0 - dc * c;
7920         double rm30 = -dd * a;
7921         double rm31 = -dd * b;
7922         double rm32 = -dd * c;
7923         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
7924         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
7925         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
7926         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
7927         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
7928         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
7929         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
7930         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
7931         // matrix multiplication
7932         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
7933         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
7934         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
7935         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
7936         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
7937         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
7938         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
7939         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
7940         ._m00(nm00)
7941         ._m01(nm01)
7942         ._m02(nm02)
7943         ._m03(nm03)
7944         ._m10(nm10)
7945         ._m11(nm11)
7946         ._m12(nm12)
7947         ._m13(nm13)
7948         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
7949         return dest;
7950     }
7951 
7952     /**
7953      * Apply a mirror/reflection transformation to this matrix that reflects about the given plane
7954      * specified via the equation <code>x*a + y*b + z*c + d = 0</code>.
7955      * <p>
7956      * The vector <code>(a, b, c)</code> must be a unit vector.
7957      * <p>
7958      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the reflection matrix,
7959      * then the new matrix will be <code>M * R</code>. So when transforming a
7960      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
7961      * reflection will be applied first!
7962      * <p>
7963      * Reference: <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb281733(v=vs.85).aspx">msdn.microsoft.com</a>
7964      * 
7965      * @param a
7966      *          the x factor in the plane equation
7967      * @param b
7968      *          the y factor in the plane equation
7969      * @param c
7970      *          the z factor in the plane equation
7971      * @param d
7972      *          the constant in the plane equation
7973      * @return this
7974      */
7975     ref public Matrix4d reflect(double a, double b, double c, double d) return {
7976         reflect(a, b, c, d, this);
7977         return this;
7978     }
7979 
7980     /**
7981      * Apply a mirror/reflection transformation to this matrix that reflects about the given plane
7982      * specified via the plane normal and a point on the plane.
7983      * <p>
7984      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the reflection matrix,
7985      * then the new matrix will be <code>M * R</code>. So when transforming a
7986      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
7987      * reflection will be applied first!
7988      * 
7989      * @param nx
7990      *          the x-coordinate of the plane normal
7991      * @param ny
7992      *          the y-coordinate of the plane normal
7993      * @param nz
7994      *          the z-coordinate of the plane normal
7995      * @param px
7996      *          the x-coordinate of a point on the plane
7997      * @param py
7998      *          the y-coordinate of a point on the plane
7999      * @param pz
8000      *          the z-coordinate of a point on the plane
8001      * @return this
8002      */
8003     ref public Matrix4d reflect(double nx, double ny, double nz, double px, double py, double pz) return {
8004         reflect(nx, ny, nz, px, py, pz, this);
8005         return this;
8006     }
8007 
8008     public Matrix4d reflect(double nx, double ny, double nz, double px, double py, double pz, ref Matrix4d dest) {
8009         double invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz);
8010         double nnx = nx * invLength;
8011         double nny = ny * invLength;
8012         double nnz = nz * invLength;
8013         /* See: http://mathworld.wolfram.com/Plane.html */
8014         return reflect(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz, dest);
8015     }
8016 
8017     /**
8018      * Apply a mirror/reflection transformation to this matrix that reflects about the given plane
8019      * specified via the plane normal and a point on the plane.
8020      * <p>
8021      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the reflection matrix,
8022      * then the new matrix will be <code>M * R</code>. So when transforming a
8023      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
8024      * reflection will be applied first!
8025      * 
8026      * @param normal
8027      *          the plane normal
8028      * @param point
8029      *          a point on the plane
8030      * @return this
8031      */
8032     ref public Matrix4d reflect(ref Vector3d normal, Vector3d point) return {
8033         return reflect(normal.x, normal.y, normal.z, point.x, point.y, point.z);
8034     }
8035 
8036     /**
8037      * Apply a mirror/reflection transformation to this matrix that reflects about a plane
8038      * specified via the plane orientation and a point on the plane.
8039      * <p>
8040      * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene.
8041      * It is assumed that the default mirror plane's normal is <code>(0, 0, 1)</code>. So, if the given {@link Quaterniond} is
8042      * the identity (does not apply any additional rotation), the reflection plane will be <code>z=0</code>, offset by the given <code>point</code>.
8043      * <p>
8044      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the reflection matrix,
8045      * then the new matrix will be <code>M * R</code>. So when transforming a
8046      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
8047      * reflection will be applied first!
8048      * 
8049      * @param orientation
8050      *          the plane orientation relative to an implied normal vector of <code>(0, 0, 1)</code>
8051      * @param point
8052      *          a point on the plane
8053      * @return this
8054      */
8055     ref public Matrix4d reflect(ref Quaterniond orientation, Vector3d point) return {
8056         reflect(orientation, point, this);
8057         return this;
8058     }
8059 
8060     public Matrix4d reflect(ref Quaterniond orientation, Vector3d point, ref Matrix4d dest) {
8061         double num1 = orientation.x + orientation.x;
8062         double num2 = orientation.y + orientation.y;
8063         double num3 = orientation.z + orientation.z;
8064         double normalX = orientation.x * num3 + orientation.w * num2;
8065         double normalY = orientation.y * num3 - orientation.w * num1;
8066         double normalZ = 1.0 - (orientation.x * num1 + orientation.y * num2);
8067         return reflect(normalX, normalY, normalZ, point.x, point.y, point.z, dest);
8068     }
8069 
8070     public Matrix4d reflect(ref Vector3d normal, Vector3d point, ref Matrix4d dest) {
8071         return reflect(normal.x, normal.y, normal.z, point.x, point.y, point.z, dest);
8072     }
8073 
8074     /**
8075      * Set this matrix to a mirror/reflection transformation that reflects about the given plane
8076      * specified via the equation <code>x*a + y*b + z*c + d = 0</code>.
8077      * <p>
8078      * The vector <code>(a, b, c)</code> must be a unit vector.
8079      * <p>
8080      * Reference: <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb281733(v=vs.85).aspx">msdn.microsoft.com</a>
8081      * 
8082      * @param a
8083      *          the x factor in the plane equation
8084      * @param b
8085      *          the y factor in the plane equation
8086      * @param c
8087      *          the z factor in the plane equation
8088      * @param d
8089      *          the constant in the plane equation
8090      * @return this
8091      */
8092     ref public Matrix4d reflection(double a, double b, double c, double d) return {
8093         double da = a + a, db = b + b, dc = c + c, dd = d + d;
8094         _m00(1.0 - da * a).
8095         _m01(-da * b).
8096         _m02(-da * c).
8097         _m03(0.0).
8098         _m10(-db * a).
8099         _m11(1.0 - db * b).
8100         _m12(-db * c).
8101         _m13(0.0).
8102         _m20(-dc * a).
8103         _m21(-dc * b).
8104         _m22(1.0 - dc * c).
8105         _m23(0.0).
8106         _m30(-dd * a).
8107         _m31(-dd * b).
8108         _m32(-dd * c).
8109         _m33(1.0).
8110         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
8111         return this;
8112     }
8113 
8114     /**
8115      * Set this matrix to a mirror/reflection transformation that reflects about the given plane
8116      * specified via the plane normal and a point on the plane.
8117      * 
8118      * @param nx
8119      *          the x-coordinate of the plane normal
8120      * @param ny
8121      *          the y-coordinate of the plane normal
8122      * @param nz
8123      *          the z-coordinate of the plane normal
8124      * @param px
8125      *          the x-coordinate of a point on the plane
8126      * @param py
8127      *          the y-coordinate of a point on the plane
8128      * @param pz
8129      *          the z-coordinate of a point on the plane
8130      * @return this
8131      */
8132     ref public Matrix4d reflection(double nx, double ny, double nz, double px, double py, double pz) return {
8133         double invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz);
8134         double nnx = nx * invLength;
8135         double nny = ny * invLength;
8136         double nnz = nz * invLength;
8137         /* See: http://mathworld.wolfram.com/Plane.html */
8138         return reflection(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz);
8139     }
8140 
8141     /**
8142      * Set this matrix to a mirror/reflection transformation that reflects about the given plane
8143      * specified via the plane normal and a point on the plane.
8144      * 
8145      * @param normal
8146      *          the plane normal
8147      * @param point
8148      *          a point on the plane
8149      * @return this
8150      */
8151     ref public Matrix4d reflection(ref Vector3d normal, Vector3d point) return {
8152         return reflection(normal.x, normal.y, normal.z, point.x, point.y, point.z);
8153     }
8154 
8155     /**
8156      * Set this matrix to a mirror/reflection transformation that reflects about a plane
8157      * specified via the plane orientation and a point on the plane.
8158      * <p>
8159      * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene.
8160      * It is assumed that the default mirror plane's normal is <code>(0, 0, 1)</code>. So, if the given {@link Quaterniond} is
8161      * the identity (does not apply any additional rotation), the reflection plane will be <code>z=0</code>, offset by the given <code>point</code>.
8162      * 
8163      * @param orientation
8164      *          the plane orientation
8165      * @param point
8166      *          a point on the plane
8167      * @return this
8168      */
8169     ref public Matrix4d reflection(ref Quaterniond orientation, Vector3d point) return {
8170         double num1 = orientation.x + orientation.x;
8171         double num2 = orientation.y + orientation.y;
8172         double num3 = orientation.z + orientation.z;
8173         double normalX = orientation.x * num3 + orientation.w * num2;
8174         double normalY = orientation.y * num3 - orientation.w * num1;
8175         double normalZ = 1.0 - (orientation.x * num1 + orientation.y * num2);
8176         return reflection(normalX, normalY, normalZ, point.x, point.y, point.z);
8177     }
8178 
8179     /**
8180      * Apply an orthographic projection transformation for a right-handed coordinate system
8181      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
8182      * <p>
8183      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8184      * then the new matrix will be <code>M * O</code>. So when transforming a
8185      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8186      * orthographic projection transformation will be applied first!
8187      * <p>
8188      * In order to set the matrix to an orthographic projection without post-multiplying it,
8189      * use {@link #setOrtho(double, double, double, double, double, double, bool) setOrtho()}.
8190      * <p>
8191      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8192      * 
8193      * @see #setOrtho(double, double, double, double, double, double, bool)
8194      * 
8195      * @param left
8196      *            the distance from the center to the left frustum edge
8197      * @param right
8198      *            the distance from the center to the right frustum edge
8199      * @param bottom
8200      *            the distance from the center to the bottom frustum edge
8201      * @param top
8202      *            the distance from the center to the top frustum edge
8203      * @param zNear
8204      *            near clipping plane distance
8205      * @param zFar
8206      *            far clipping plane distance
8207      * @param zZeroToOne
8208      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8209      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8210      * @param dest
8211      *            will hold the result
8212      * @return dest
8213      */
8214     public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8215         if ((properties & PROPERTY_IDENTITY) != 0)
8216             return dest.setOrtho(left, right, bottom, top, zNear, zFar, zZeroToOne);
8217         return orthoGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest);
8218     }
8219     private Matrix4d orthoGeneric(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8220         // calculate right matrix elements
8221         double rm00 = 2.0 / (right - left);
8222         double rm11 = 2.0 / (top - bottom);
8223         double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zNear - zFar);
8224         double rm30 = (left + right) / (left - right);
8225         double rm31 = (top + bottom) / (bottom - top);
8226         double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar);
8227         // perform optimized multiplication
8228         // compute the last column first, because other columns do not depend on it
8229         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
8230         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
8231         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
8232         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
8233         ._m00(m00 * rm00)
8234         ._m01(m01 * rm00)
8235         ._m02(m02 * rm00)
8236         ._m03(m03 * rm00)
8237         ._m10(m10 * rm11)
8238         ._m11(m11 * rm11)
8239         ._m12(m12 * rm11)
8240         ._m13(m13 * rm11)
8241         ._m20(m20 * rm22)
8242         ._m21(m21 * rm22)
8243         ._m22(m22 * rm22)
8244         ._m23(m23 * rm22)
8245         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
8246         return dest;
8247     }
8248 
8249     /**
8250      * Apply an orthographic projection transformation for a right-handed coordinate system
8251      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
8252      * <p>
8253      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8254      * then the new matrix will be <code>M * O</code>. So when transforming a
8255      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8256      * orthographic projection transformation will be applied first!
8257      * <p>
8258      * In order to set the matrix to an orthographic projection without post-multiplying it,
8259      * use {@link #setOrtho(double, double, double, double, double, double) setOrtho()}.
8260      * <p>
8261      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8262      * 
8263      * @see #setOrtho(double, double, double, double, double, double)
8264      * 
8265      * @param left
8266      *            the distance from the center to the left frustum edge
8267      * @param right
8268      *            the distance from the center to the right frustum edge
8269      * @param bottom
8270      *            the distance from the center to the bottom frustum edge
8271      * @param top
8272      *            the distance from the center to the top frustum edge
8273      * @param zNear
8274      *            near clipping plane distance
8275      * @param zFar
8276      *            far clipping plane distance
8277      * @param dest
8278      *            will hold the result
8279      * @return dest
8280      */
8281     public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, ref Matrix4d dest) {
8282         return ortho(left, right, bottom, top, zNear, zFar, false, dest);
8283     }
8284 
8285     /**
8286      * Apply an orthographic projection transformation for a right-handed coordinate system
8287      * using the given NDC z range to this matrix.
8288      * <p>
8289      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8290      * then the new matrix will be <code>M * O</code>. So when transforming a
8291      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8292      * orthographic projection transformation will be applied first!
8293      * <p>
8294      * In order to set the matrix to an orthographic projection without post-multiplying it,
8295      * use {@link #setOrtho(double, double, double, double, double, double, bool) setOrtho()}.
8296      * <p>
8297      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8298      * 
8299      * @see #setOrtho(double, double, double, double, double, double, bool)
8300      * 
8301      * @param left
8302      *            the distance from the center to the left frustum edge
8303      * @param right
8304      *            the distance from the center to the right frustum edge
8305      * @param bottom
8306      *            the distance from the center to the bottom frustum edge
8307      * @param top
8308      *            the distance from the center to the top frustum edge
8309      * @param zNear
8310      *            near clipping plane distance
8311      * @param zFar
8312      *            far clipping plane distance
8313      * @param zZeroToOne
8314      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8315      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8316      * @return this
8317      */
8318     ref public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
8319         ortho(left, right, bottom, top, zNear, zFar, zZeroToOne, this);
8320         return this;
8321     }
8322 
8323     /**
8324      * Apply an orthographic projection transformation for a right-handed coordinate system
8325      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
8326      * <p>
8327      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8328      * then the new matrix will be <code>M * O</code>. So when transforming a
8329      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8330      * orthographic projection transformation will be applied first!
8331      * <p>
8332      * In order to set the matrix to an orthographic projection without post-multiplying it,
8333      * use {@link #setOrtho(double, double, double, double, double, double) setOrtho()}.
8334      * <p>
8335      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8336      * 
8337      * @see #setOrtho(double, double, double, double, double, double)
8338      * 
8339      * @param left
8340      *            the distance from the center to the left frustum edge
8341      * @param right
8342      *            the distance from the center to the right frustum edge
8343      * @param bottom
8344      *            the distance from the center to the bottom frustum edge
8345      * @param top
8346      *            the distance from the center to the top frustum edge
8347      * @param zNear
8348      *            near clipping plane distance
8349      * @param zFar
8350      *            far clipping plane distance
8351      * @return this
8352      */
8353     ref public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar) return {
8354         return ortho(left, right, bottom, top, zNear, zFar, false);
8355     }
8356 
8357     /**
8358      * Apply an orthographic projection transformation for a left-handed coordiante system
8359      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
8360      * <p>
8361      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8362      * then the new matrix will be <code>M * O</code>. So when transforming a
8363      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8364      * orthographic projection transformation will be applied first!
8365      * <p>
8366      * In order to set the matrix to an orthographic projection without post-multiplying it,
8367      * use {@link #setOrthoLH(double, double, double, double, double, double, bool) setOrthoLH()}.
8368      * <p>
8369      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8370      * 
8371      * @see #setOrthoLH(double, double, double, double, double, double, bool)
8372      * 
8373      * @param left
8374      *            the distance from the center to the left frustum edge
8375      * @param right
8376      *            the distance from the center to the right frustum edge
8377      * @param bottom
8378      *            the distance from the center to the bottom frustum edge
8379      * @param top
8380      *            the distance from the center to the top frustum edge
8381      * @param zNear
8382      *            near clipping plane distance
8383      * @param zFar
8384      *            far clipping plane distance
8385      * @param zZeroToOne
8386      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8387      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8388      * @param dest
8389      *            will hold the result
8390      * @return dest
8391      */
8392     public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8393         if ((properties & PROPERTY_IDENTITY) != 0)
8394             return dest.setOrthoLH(left, right, bottom, top, zNear, zFar, zZeroToOne);
8395         return orthoLHGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest);
8396     }
8397     private Matrix4d orthoLHGeneric(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8398         // calculate right matrix elements
8399         double rm00 = 2.0 / (right - left);
8400         double rm11 = 2.0 / (top - bottom);
8401         double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zFar - zNear);
8402         double rm30 = (left + right) / (left - right);
8403         double rm31 = (top + bottom) / (bottom - top);
8404         double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar);
8405         // perform optimized multiplication
8406         // compute the last column first, because other columns do not depend on it
8407         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
8408         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
8409         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
8410         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
8411         ._m00(m00 * rm00)
8412         ._m01(m01 * rm00)
8413         ._m02(m02 * rm00)
8414         ._m03(m03 * rm00)
8415         ._m10(m10 * rm11)
8416         ._m11(m11 * rm11)
8417         ._m12(m12 * rm11)
8418         ._m13(m13 * rm11)
8419         ._m20(m20 * rm22)
8420         ._m21(m21 * rm22)
8421         ._m22(m22 * rm22)
8422         ._m23(m23 * rm22)
8423         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
8424         return dest;
8425     }
8426 
8427     /**
8428      * Apply an orthographic projection transformation for a left-handed coordiante system
8429      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
8430      * <p>
8431      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8432      * then the new matrix will be <code>M * O</code>. So when transforming a
8433      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8434      * orthographic projection transformation will be applied first!
8435      * <p>
8436      * In order to set the matrix to an orthographic projection without post-multiplying it,
8437      * use {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()}.
8438      * <p>
8439      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8440      * 
8441      * @see #setOrthoLH(double, double, double, double, double, double)
8442      * 
8443      * @param left
8444      *            the distance from the center to the left frustum edge
8445      * @param right
8446      *            the distance from the center to the right frustum edge
8447      * @param bottom
8448      *            the distance from the center to the bottom frustum edge
8449      * @param top
8450      *            the distance from the center to the top frustum edge
8451      * @param zNear
8452      *            near clipping plane distance
8453      * @param zFar
8454      *            far clipping plane distance
8455      * @param dest
8456      *            will hold the result
8457      * @return dest
8458      */
8459     public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, ref Matrix4d dest) {
8460         return orthoLH(left, right, bottom, top, zNear, zFar, false, dest);
8461     }
8462 
8463     /**
8464      * Apply an orthographic projection transformation for a left-handed coordiante system
8465      * using the given NDC z range to this matrix.
8466      * <p>
8467      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8468      * then the new matrix will be <code>M * O</code>. So when transforming a
8469      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8470      * orthographic projection transformation will be applied first!
8471      * <p>
8472      * In order to set the matrix to an orthographic projection without post-multiplying it,
8473      * use {@link #setOrthoLH(double, double, double, double, double, double, bool) setOrthoLH()}.
8474      * <p>
8475      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8476      * 
8477      * @see #setOrthoLH(double, double, double, double, double, double, bool)
8478      * 
8479      * @param left
8480      *            the distance from the center to the left frustum edge
8481      * @param right
8482      *            the distance from the center to the right frustum edge
8483      * @param bottom
8484      *            the distance from the center to the bottom frustum edge
8485      * @param top
8486      *            the distance from the center to the top frustum edge
8487      * @param zNear
8488      *            near clipping plane distance
8489      * @param zFar
8490      *            far clipping plane distance
8491      * @param zZeroToOne
8492      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8493      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8494      * @return this
8495      */
8496     ref public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
8497         orthoLH(left, right, bottom, top, zNear, zFar, zZeroToOne, this);
8498         return this;
8499     }
8500 
8501     /**
8502      * Apply an orthographic projection transformation for a left-handed coordiante system
8503      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
8504      * <p>
8505      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8506      * then the new matrix will be <code>M * O</code>. So when transforming a
8507      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8508      * orthographic projection transformation will be applied first!
8509      * <p>
8510      * In order to set the matrix to an orthographic projection without post-multiplying it,
8511      * use {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()}.
8512      * <p>
8513      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8514      * 
8515      * @see #setOrthoLH(double, double, double, double, double, double)
8516      * 
8517      * @param left
8518      *            the distance from the center to the left frustum edge
8519      * @param right
8520      *            the distance from the center to the right frustum edge
8521      * @param bottom
8522      *            the distance from the center to the bottom frustum edge
8523      * @param top
8524      *            the distance from the center to the top frustum edge
8525      * @param zNear
8526      *            near clipping plane distance
8527      * @param zFar
8528      *            far clipping plane distance
8529      * @return this
8530      */
8531     ref public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar) return {
8532         return orthoLH(left, right, bottom, top, zNear, zFar, false);
8533     }
8534 
8535     /**
8536      * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system
8537      * using the given NDC z range.
8538      * <p>
8539      * In order to apply the orthographic projection to an already existing transformation,
8540      * use {@link #ortho(double, double, double, double, double, double, bool) ortho()}.
8541      * <p>
8542      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8543      * 
8544      * @see #ortho(double, double, double, double, double, double, bool)
8545      * 
8546      * @param left
8547      *            the distance from the center to the left frustum edge
8548      * @param right
8549      *            the distance from the center to the right frustum edge
8550      * @param bottom
8551      *            the distance from the center to the bottom frustum edge
8552      * @param top
8553      *            the distance from the center to the top frustum edge
8554      * @param zNear
8555      *            near clipping plane distance
8556      * @param zFar
8557      *            far clipping plane distance
8558      * @param zZeroToOne
8559      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8560      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8561      * @return this
8562      */
8563     ref public Matrix4d setOrtho(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
8564         if ((properties & PROPERTY_IDENTITY) == 0)
8565             _identity();
8566         _m00(2.0 / (right - left)).
8567         _m11(2.0 / (top - bottom)).
8568         _m22((zZeroToOne ? 1.0 : 2.0) / (zNear - zFar)).
8569         _m30((right + left) / (left - right)).
8570         _m31((top + bottom) / (bottom - top)).
8571         _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)).
8572         properties = PROPERTY_AFFINE;
8573         return this;
8574     }
8575 
8576     /**
8577      * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system
8578      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
8579      * <p>
8580      * In order to apply the orthographic projection to an already existing transformation,
8581      * use {@link #ortho(double, double, double, double, double, double) ortho()}.
8582      * <p>
8583      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8584      * 
8585      * @see #ortho(double, double, double, double, double, double)
8586      * 
8587      * @param left
8588      *            the distance from the center to the left frustum edge
8589      * @param right
8590      *            the distance from the center to the right frustum edge
8591      * @param bottom
8592      *            the distance from the center to the bottom frustum edge
8593      * @param top
8594      *            the distance from the center to the top frustum edge
8595      * @param zNear
8596      *            near clipping plane distance
8597      * @param zFar
8598      *            far clipping plane distance
8599      * @return this
8600      */
8601     ref public Matrix4d setOrtho(double left, double right, double bottom, double top, double zNear, double zFar) return {
8602         return setOrtho(left, right, bottom, top, zNear, zFar, false);
8603     }
8604 
8605     /**
8606      * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system
8607      * using the given NDC z range.
8608      * <p>
8609      * In order to apply the orthographic projection to an already existing transformation,
8610      * use {@link #orthoLH(double, double, double, double, double, double, bool) orthoLH()}.
8611      * <p>
8612      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8613      * 
8614      * @see #orthoLH(double, double, double, double, double, double, bool)
8615      * 
8616      * @param left
8617      *            the distance from the center to the left frustum edge
8618      * @param right
8619      *            the distance from the center to the right frustum edge
8620      * @param bottom
8621      *            the distance from the center to the bottom frustum edge
8622      * @param top
8623      *            the distance from the center to the top frustum edge
8624      * @param zNear
8625      *            near clipping plane distance
8626      * @param zFar
8627      *            far clipping plane distance
8628      * @param zZeroToOne
8629      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8630      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8631      * @return this
8632      */
8633     ref public Matrix4d setOrthoLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
8634         if ((properties & PROPERTY_IDENTITY) == 0)
8635             _identity();
8636         _m00(2.0 / (right - left)).
8637         _m11(2.0 / (top - bottom)).
8638         _m22((zZeroToOne ? 1.0 : 2.0) / (zFar - zNear)).
8639         _m30((right + left) / (left - right)).
8640         _m31((top + bottom) / (bottom - top)).
8641         _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)).
8642         properties = PROPERTY_AFFINE;
8643         return this;
8644     }
8645 
8646     /**
8647      * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system
8648      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
8649      * <p>
8650      * In order to apply the orthographic projection to an already existing transformation,
8651      * use {@link #orthoLH(double, double, double, double, double, double) orthoLH()}.
8652      * <p>
8653      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8654      * 
8655      * @see #orthoLH(double, double, double, double, double, double)
8656      * 
8657      * @param left
8658      *            the distance from the center to the left frustum edge
8659      * @param right
8660      *            the distance from the center to the right frustum edge
8661      * @param bottom
8662      *            the distance from the center to the bottom frustum edge
8663      * @param top
8664      *            the distance from the center to the top frustum edge
8665      * @param zNear
8666      *            near clipping plane distance
8667      * @param zFar
8668      *            far clipping plane distance
8669      * @return this
8670      */
8671     ref public Matrix4d setOrthoLH(double left, double right, double bottom, double top, double zNear, double zFar) return {
8672         return setOrthoLH(left, right, bottom, top, zNear, zFar, false);
8673     }
8674 
8675     /**
8676      * Apply a symmetric orthographic projection transformation for a right-handed coordinate system
8677      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
8678      * <p>
8679      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, bool, Matrix4d) ortho()} with
8680      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8681      * <p>
8682      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8683      * then the new matrix will be <code>M * O</code>. So when transforming a
8684      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8685      * orthographic projection transformation will be applied first!
8686      * <p>
8687      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8688      * use {@link #setOrthoSymmetric(double, double, double, double, bool) setOrthoSymmetric()}.
8689      * <p>
8690      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8691      * 
8692      * @see #setOrthoSymmetric(double, double, double, double, bool)
8693      * 
8694      * @param width
8695      *            the distance between the right and left frustum edges
8696      * @param height
8697      *            the distance between the top and bottom frustum edges
8698      * @param zNear
8699      *            near clipping plane distance
8700      * @param zFar
8701      *            far clipping plane distance
8702      * @param dest
8703      *            will hold the result
8704      * @param zZeroToOne
8705      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8706      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8707      * @return dest
8708      */
8709     public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8710         if ((properties & PROPERTY_IDENTITY) != 0)
8711             return dest.setOrthoSymmetric(width, height, zNear, zFar, zZeroToOne);
8712         return orthoSymmetricGeneric(width, height, zNear, zFar, zZeroToOne, dest);
8713     }
8714     private Matrix4d orthoSymmetricGeneric(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8715         // calculate right matrix elements
8716         double rm00 = 2.0 / width;
8717         double rm11 = 2.0 / height;
8718         double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zNear - zFar);
8719         double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar);
8720         // perform optimized multiplication
8721         // compute the last column first, because other columns do not depend on it
8722         dest._m30(m20 * rm32 + m30)
8723         ._m31(m21 * rm32 + m31)
8724         ._m32(m22 * rm32 + m32)
8725         ._m33(m23 * rm32 + m33)
8726         ._m00(m00 * rm00)
8727         ._m01(m01 * rm00)
8728         ._m02(m02 * rm00)
8729         ._m03(m03 * rm00)
8730         ._m10(m10 * rm11)
8731         ._m11(m11 * rm11)
8732         ._m12(m12 * rm11)
8733         ._m13(m13 * rm11)
8734         ._m20(m20 * rm22)
8735         ._m21(m21 * rm22)
8736         ._m22(m22 * rm22)
8737         ._m23(m23 * rm22)
8738         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
8739         return dest;
8740     }
8741 
8742     /**
8743      * Apply a symmetric orthographic projection transformation for a right-handed coordinate system
8744      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
8745      * <p>
8746      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4d) ortho()} with
8747      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8748      * <p>
8749      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8750      * then the new matrix will be <code>M * O</code>. So when transforming a
8751      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8752      * orthographic projection transformation will be applied first!
8753      * <p>
8754      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8755      * use {@link #setOrthoSymmetric(double, double, double, double) setOrthoSymmetric()}.
8756      * <p>
8757      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8758      * 
8759      * @see #setOrthoSymmetric(double, double, double, double)
8760      * 
8761      * @param width
8762      *            the distance between the right and left frustum edges
8763      * @param height
8764      *            the distance between the top and bottom frustum edges
8765      * @param zNear
8766      *            near clipping plane distance
8767      * @param zFar
8768      *            far clipping plane distance
8769      * @param dest
8770      *            will hold the result
8771      * @return dest
8772      */
8773     public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, ref Matrix4d dest) {
8774         return orthoSymmetric(width, height, zNear, zFar, false, dest);
8775     }
8776 
8777     /**
8778      * Apply a symmetric orthographic projection transformation for a right-handed coordinate system
8779      * using the given NDC z range to this matrix.
8780      * <p>
8781      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, bool) ortho()} with
8782      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8783      * <p>
8784      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8785      * then the new matrix will be <code>M * O</code>. So when transforming a
8786      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8787      * orthographic projection transformation will be applied first!
8788      * <p>
8789      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8790      * use {@link #setOrthoSymmetric(double, double, double, double, bool) setOrthoSymmetric()}.
8791      * <p>
8792      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8793      * 
8794      * @see #setOrthoSymmetric(double, double, double, double, bool)
8795      * 
8796      * @param width
8797      *            the distance between the right and left frustum edges
8798      * @param height
8799      *            the distance between the top and bottom frustum edges
8800      * @param zNear
8801      *            near clipping plane distance
8802      * @param zFar
8803      *            far clipping plane distance
8804      * @param zZeroToOne
8805      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8806      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8807      * @return this
8808      */
8809     ref public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
8810         orthoSymmetric(width, height, zNear, zFar, zZeroToOne, this);
8811         return this;
8812     }
8813 
8814     /**
8815      * Apply a symmetric orthographic projection transformation for a right-handed coordinate system
8816      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
8817      * <p>
8818      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double) ortho()} with
8819      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8820      * <p>
8821      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8822      * then the new matrix will be <code>M * O</code>. So when transforming a
8823      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8824      * orthographic projection transformation will be applied first!
8825      * <p>
8826      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8827      * use {@link #setOrthoSymmetric(double, double, double, double) setOrthoSymmetric()}.
8828      * <p>
8829      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8830      * 
8831      * @see #setOrthoSymmetric(double, double, double, double)
8832      * 
8833      * @param width
8834      *            the distance between the right and left frustum edges
8835      * @param height
8836      *            the distance between the top and bottom frustum edges
8837      * @param zNear
8838      *            near clipping plane distance
8839      * @param zFar
8840      *            far clipping plane distance
8841      * @return this
8842      */
8843     ref public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar) return {
8844         orthoSymmetric(width, height, zNear, zFar, false, this);
8845         return this;
8846     }
8847 
8848     /**
8849      * Apply a symmetric orthographic projection transformation for a left-handed coordinate system
8850      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
8851      * <p>
8852      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, bool, Matrix4d) orthoLH()} with
8853      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8854      * <p>
8855      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8856      * then the new matrix will be <code>M * O</code>. So when transforming a
8857      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8858      * orthographic projection transformation will be applied first!
8859      * <p>
8860      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8861      * use {@link #setOrthoSymmetricLH(double, double, double, double, bool) setOrthoSymmetricLH()}.
8862      * <p>
8863      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8864      * 
8865      * @see #setOrthoSymmetricLH(double, double, double, double, bool)
8866      * 
8867      * @param width
8868      *            the distance between the right and left frustum edges
8869      * @param height
8870      *            the distance between the top and bottom frustum edges
8871      * @param zNear
8872      *            near clipping plane distance
8873      * @param zFar
8874      *            far clipping plane distance
8875      * @param dest
8876      *            will hold the result
8877      * @param zZeroToOne
8878      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8879      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8880      * @return dest
8881      */
8882     public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8883         if ((properties & PROPERTY_IDENTITY) != 0)
8884             return dest.setOrthoSymmetricLH(width, height, zNear, zFar, zZeroToOne);
8885         return orthoSymmetricLHGeneric(width, height, zNear, zFar, zZeroToOne, dest);
8886     }
8887     private Matrix4d orthoSymmetricLHGeneric(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8888         // calculate right matrix elements
8889         double rm00 = 2.0 / width;
8890         double rm11 = 2.0 / height;
8891         double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zFar - zNear);
8892         double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar);
8893         // perform optimized multiplication
8894         // compute the last column first, because other columns do not depend on it
8895         dest._m30(m20 * rm32 + m30)
8896         ._m31(m21 * rm32 + m31)
8897         ._m32(m22 * rm32 + m32)
8898         ._m33(m23 * rm32 + m33)
8899         ._m00(m00 * rm00)
8900         ._m01(m01 * rm00)
8901         ._m02(m02 * rm00)
8902         ._m03(m03 * rm00)
8903         ._m10(m10 * rm11)
8904         ._m11(m11 * rm11)
8905         ._m12(m12 * rm11)
8906         ._m13(m13 * rm11)
8907         ._m20(m20 * rm22)
8908         ._m21(m21 * rm22)
8909         ._m22(m22 * rm22)
8910         ._m23(m23 * rm22)
8911         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
8912         return dest;
8913     }
8914 
8915     /**
8916      * Apply a symmetric orthographic projection transformation for a left-handed coordinate system
8917      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
8918      * <p>
8919      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4d) orthoLH()} with
8920      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8921      * <p>
8922      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8923      * then the new matrix will be <code>M * O</code>. So when transforming a
8924      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8925      * orthographic projection transformation will be applied first!
8926      * <p>
8927      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8928      * use {@link #setOrthoSymmetricLH(double, double, double, double) setOrthoSymmetricLH()}.
8929      * <p>
8930      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8931      * 
8932      * @see #setOrthoSymmetricLH(double, double, double, double)
8933      * 
8934      * @param width
8935      *            the distance between the right and left frustum edges
8936      * @param height
8937      *            the distance between the top and bottom frustum edges
8938      * @param zNear
8939      *            near clipping plane distance
8940      * @param zFar
8941      *            far clipping plane distance
8942      * @param dest
8943      *            will hold the result
8944      * @return dest
8945      */
8946     public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, ref Matrix4d dest) {
8947         return orthoSymmetricLH(width, height, zNear, zFar, false, dest);
8948     }
8949 
8950     /**
8951      * Apply a symmetric orthographic projection transformation for a left-handed coordinate system
8952      * using the given NDC z range to this matrix.
8953      * <p>
8954      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, bool) orthoLH()} with
8955      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8956      * <p>
8957      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8958      * then the new matrix will be <code>M * O</code>. So when transforming a
8959      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8960      * orthographic projection transformation will be applied first!
8961      * <p>
8962      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8963      * use {@link #setOrthoSymmetricLH(double, double, double, double, bool) setOrthoSymmetricLH()}.
8964      * <p>
8965      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8966      * 
8967      * @see #setOrthoSymmetricLH(double, double, double, double, bool)
8968      * 
8969      * @param width
8970      *            the distance between the right and left frustum edges
8971      * @param height
8972      *            the distance between the top and bottom frustum edges
8973      * @param zNear
8974      *            near clipping plane distance
8975      * @param zFar
8976      *            far clipping plane distance
8977      * @param zZeroToOne
8978      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8979      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8980      * @return this
8981      */
8982     ref public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
8983         orthoSymmetricLH(width, height, zNear, zFar, zZeroToOne, this);
8984         return this;
8985     }
8986 
8987     /**
8988      * Apply a symmetric orthographic projection transformation for a left-handed coordinate system
8989      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
8990      * <p>
8991      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double) orthoLH()} with
8992      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8993      * <p>
8994      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8995      * then the new matrix will be <code>M * O</code>. So when transforming a
8996      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8997      * orthographic projection transformation will be applied first!
8998      * <p>
8999      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
9000      * use {@link #setOrthoSymmetricLH(double, double, double, double) setOrthoSymmetricLH()}.
9001      * <p>
9002      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9003      * 
9004      * @see #setOrthoSymmetricLH(double, double, double, double)
9005      * 
9006      * @param width
9007      *            the distance between the right and left frustum edges
9008      * @param height
9009      *            the distance between the top and bottom frustum edges
9010      * @param zNear
9011      *            near clipping plane distance
9012      * @param zFar
9013      *            far clipping plane distance
9014      * @return this
9015      */
9016     ref public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar) return {
9017         orthoSymmetricLH(width, height, zNear, zFar, false, this);
9018         return this;
9019     }
9020 
9021     /**
9022      * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system
9023      * using the given NDC z range.
9024      * <p>
9025      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double, bool) setOrtho()} with
9026      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9027      * <p>
9028      * In order to apply the symmetric orthographic projection to an already existing transformation,
9029      * use {@link #orthoSymmetric(double, double, double, double, bool) orthoSymmetric()}.
9030      * <p>
9031      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9032      * 
9033      * @see #orthoSymmetric(double, double, double, double, bool)
9034      * 
9035      * @param width
9036      *            the distance between the right and left frustum edges
9037      * @param height
9038      *            the distance between the top and bottom frustum edges
9039      * @param zNear
9040      *            near clipping plane distance
9041      * @param zFar
9042      *            far clipping plane distance
9043      * @param zZeroToOne
9044      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
9045      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
9046      * @return this
9047      */
9048     ref public Matrix4d setOrthoSymmetric(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
9049         if ((properties & PROPERTY_IDENTITY) == 0)
9050             _identity();
9051         _m00(2.0 / width).
9052         _m11(2.0 / height).
9053         _m22((zZeroToOne ? 1.0 : 2.0) / (zNear - zFar)).
9054         _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)).
9055         properties = PROPERTY_AFFINE;
9056         return this;
9057     }
9058 
9059     /**
9060      * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system
9061      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
9062      * <p>
9063      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrtho()} with
9064      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9065      * <p>
9066      * In order to apply the symmetric orthographic projection to an already existing transformation,
9067      * use {@link #orthoSymmetric(double, double, double, double) orthoSymmetric()}.
9068      * <p>
9069      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9070      * 
9071      * @see #orthoSymmetric(double, double, double, double)
9072      * 
9073      * @param width
9074      *            the distance between the right and left frustum edges
9075      * @param height
9076      *            the distance between the top and bottom frustum edges
9077      * @param zNear
9078      *            near clipping plane distance
9079      * @param zFar
9080      *            far clipping plane distance
9081      * @return this
9082      */
9083     ref public Matrix4d setOrthoSymmetric(double width, double height, double zNear, double zFar) return {
9084         return setOrthoSymmetric(width, height, zNear, zFar, false);
9085     }
9086 
9087     /**
9088      * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system using the given NDC z range.
9089      * <p>
9090      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double, bool) setOrtho()} with
9091      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9092      * <p>
9093      * In order to apply the symmetric orthographic projection to an already existing transformation,
9094      * use {@link #orthoSymmetricLH(double, double, double, double, bool) orthoSymmetricLH()}.
9095      * <p>
9096      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9097      * 
9098      * @see #orthoSymmetricLH(double, double, double, double, bool)
9099      * 
9100      * @param width
9101      *            the distance between the right and left frustum edges
9102      * @param height
9103      *            the distance between the top and bottom frustum edges
9104      * @param zNear
9105      *            near clipping plane distance
9106      * @param zFar
9107      *            far clipping plane distance
9108      * @param zZeroToOne
9109      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
9110      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
9111      * @return this
9112      */
9113     ref public Matrix4d setOrthoSymmetricLH(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
9114         if ((properties & PROPERTY_IDENTITY) == 0)
9115             _identity();
9116         _m00(2.0 / width).
9117         _m11(2.0 / height).
9118         _m22((zZeroToOne ? 1.0 : 2.0) / (zFar - zNear)).
9119         _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)).
9120         properties = PROPERTY_AFFINE;
9121         return this;
9122     }
9123 
9124     /**
9125      * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system
9126      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
9127      * <p>
9128      * This method is equivalent to calling {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()} with
9129      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9130      * <p>
9131      * In order to apply the symmetric orthographic projection to an already existing transformation,
9132      * use {@link #orthoSymmetricLH(double, double, double, double) orthoSymmetricLH()}.
9133      * <p>
9134      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9135      * 
9136      * @see #orthoSymmetricLH(double, double, double, double)
9137      * 
9138      * @param width
9139      *            the distance between the right and left frustum edges
9140      * @param height
9141      *            the distance between the top and bottom frustum edges
9142      * @param zNear
9143      *            near clipping plane distance
9144      * @param zFar
9145      *            far clipping plane distance
9146      * @return this
9147      */
9148     ref public Matrix4d setOrthoSymmetricLH(double width, double height, double zNear, double zFar) return {
9149         return setOrthoSymmetricLH(width, height, zNear, zFar, false);
9150     }
9151 
9152     /**
9153      * Apply an orthographic projection transformation for a right-handed coordinate system
9154      * to this matrix and store the result in <code>dest</code>.
9155      * <p>
9156      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4d) ortho()} with
9157      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9158      * <p>
9159      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9160      * then the new matrix will be <code>M * O</code>. So when transforming a
9161      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9162      * orthographic projection transformation will be applied first!
9163      * <p>
9164      * In order to set the matrix to an orthographic projection without post-multiplying it,
9165      * use {@link #setOrtho2D(double, double, double, double) setOrtho()}.
9166      * <p>
9167      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9168      * 
9169      * @see #ortho(double, double, double, double, double, double, Matrix4d)
9170      * @see #setOrtho2D(double, double, double, double)
9171      * 
9172      * @param left
9173      *            the distance from the center to the left frustum edge
9174      * @param right
9175      *            the distance from the center to the right frustum edge
9176      * @param bottom
9177      *            the distance from the center to the bottom frustum edge
9178      * @param top
9179      *            the distance from the center to the top frustum edge
9180      * @param dest
9181      *            will hold the result
9182      * @return dest
9183      */
9184     public Matrix4d ortho2D(double left, double right, double bottom, double top, ref Matrix4d dest) {
9185         if ((properties & PROPERTY_IDENTITY) != 0)
9186             return dest.setOrtho2D(left, right, bottom, top);
9187         return ortho2DGeneric(left, right, bottom, top, dest);
9188     }
9189     private Matrix4d ortho2DGeneric(double left, double right, double bottom, double top, ref Matrix4d dest) {
9190         // calculate right matrix elements
9191         double rm00 = 2.0 / (right - left);
9192         double rm11 = 2.0 / (top - bottom);
9193         double rm30 = (right + left) / (left - right);
9194         double rm31 = (top + bottom) / (bottom - top);
9195         // perform optimized multiplication
9196         // compute the last column first, because other columns do not depend on it
9197         dest._m30(m00 * rm30 + m10 * rm31 + m30)
9198         ._m31(m01 * rm30 + m11 * rm31 + m31)
9199         ._m32(m02 * rm30 + m12 * rm31 + m32)
9200         ._m33(m03 * rm30 + m13 * rm31 + m33)
9201         ._m00(m00 * rm00)
9202         ._m01(m01 * rm00)
9203         ._m02(m02 * rm00)
9204         ._m03(m03 * rm00)
9205         ._m10(m10 * rm11)
9206         ._m11(m11 * rm11)
9207         ._m12(m12 * rm11)
9208         ._m13(m13 * rm11)
9209         ._m20(-m20)
9210         ._m21(-m21)
9211         ._m22(-m22)
9212         ._m23(-m23)
9213         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
9214         return dest;
9215     }
9216 
9217     /**
9218      * Apply an orthographic projection transformation for a right-handed coordinate system to this matrix.
9219      * <p>
9220      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double) ortho()} with
9221      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9222      * <p>
9223      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9224      * then the new matrix will be <code>M * O</code>. So when transforming a
9225      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9226      * orthographic projection transformation will be applied first!
9227      * <p>
9228      * In order to set the matrix to an orthographic projection without post-multiplying it,
9229      * use {@link #setOrtho2D(double, double, double, double) setOrtho2D()}.
9230      * <p>
9231      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9232      * 
9233      * @see #ortho(double, double, double, double, double, double)
9234      * @see #setOrtho2D(double, double, double, double)
9235      * 
9236      * @param left
9237      *            the distance from the center to the left frustum edge
9238      * @param right
9239      *            the distance from the center to the right frustum edge
9240      * @param bottom
9241      *            the distance from the center to the bottom frustum edge
9242      * @param top
9243      *            the distance from the center to the top frustum edge
9244      * @return this
9245      */
9246     ref public Matrix4d ortho2D(double left, double right, double bottom, double top) return {
9247         ortho2D(left, right, bottom, top, this);
9248         return this;
9249     }
9250 
9251     /**
9252      * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix and store the result in <code>dest</code>.
9253      * <p>
9254      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4d) orthoLH()} with
9255      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9256      * <p>
9257      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9258      * then the new matrix will be <code>M * O</code>. So when transforming a
9259      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9260      * orthographic projection transformation will be applied first!
9261      * <p>
9262      * In order to set the matrix to an orthographic projection without post-multiplying it,
9263      * use {@link #setOrtho2DLH(double, double, double, double) setOrthoLH()}.
9264      * <p>
9265      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9266      * 
9267      * @see #orthoLH(double, double, double, double, double, double, Matrix4d)
9268      * @see #setOrtho2DLH(double, double, double, double)
9269      * 
9270      * @param left
9271      *            the distance from the center to the left frustum edge
9272      * @param right
9273      *            the distance from the center to the right frustum edge
9274      * @param bottom
9275      *            the distance from the center to the bottom frustum edge
9276      * @param top
9277      *            the distance from the center to the top frustum edge
9278      * @param dest
9279      *            will hold the result
9280      * @return dest
9281      */
9282     public Matrix4d ortho2DLH(double left, double right, double bottom, double top, ref Matrix4d dest) {
9283         if ((properties & PROPERTY_IDENTITY) != 0)
9284             return dest.setOrtho2DLH(left, right, bottom, top);
9285         return ortho2DLHGeneric(left, right, bottom, top, dest);
9286     }
9287     private Matrix4d ortho2DLHGeneric(double left, double right, double bottom, double top, ref Matrix4d dest) {
9288         // calculate right matrix elements
9289         double rm00 = 2.0 / (right - left);
9290         double rm11 = 2.0 / (top - bottom);
9291         double rm30 = (right + left) / (left - right);
9292         double rm31 = (top + bottom) / (bottom - top);
9293         // perform optimized multiplication
9294         // compute the last column first, because other columns do not depend on it
9295         dest._m30(m00 * rm30 + m10 * rm31 + m30)
9296         ._m31(m01 * rm30 + m11 * rm31 + m31)
9297         ._m32(m02 * rm30 + m12 * rm31 + m32)
9298         ._m33(m03 * rm30 + m13 * rm31 + m33)
9299         ._m00(m00 * rm00)
9300         ._m01(m01 * rm00)
9301         ._m02(m02 * rm00)
9302         ._m03(m03 * rm00)
9303         ._m10(m10 * rm11)
9304         ._m11(m11 * rm11)
9305         ._m12(m12 * rm11)
9306         ._m13(m13 * rm11)
9307         ._m20(m20)
9308         ._m21(m21)
9309         ._m22(m22)
9310         ._m23(m23)
9311         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
9312         return dest;
9313     }
9314 
9315     /**
9316      * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix.
9317      * <p>
9318      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double) orthoLH()} with
9319      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9320      * <p>
9321      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9322      * then the new matrix will be <code>M * O</code>. So when transforming a
9323      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9324      * orthographic projection transformation will be applied first!
9325      * <p>
9326      * In order to set the matrix to an orthographic projection without post-multiplying it,
9327      * use {@link #setOrtho2DLH(double, double, double, double) setOrtho2DLH()}.
9328      * <p>
9329      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9330      * 
9331      * @see #orthoLH(double, double, double, double, double, double)
9332      * @see #setOrtho2DLH(double, double, double, double)
9333      * 
9334      * @param left
9335      *            the distance from the center to the left frustum edge
9336      * @param right
9337      *            the distance from the center to the right frustum edge
9338      * @param bottom
9339      *            the distance from the center to the bottom frustum edge
9340      * @param top
9341      *            the distance from the center to the top frustum edge
9342      * @return this
9343      */
9344     ref public Matrix4d ortho2DLH(double left, double right, double bottom, double top) return {
9345         ortho2DLH(left, right, bottom, top, this);
9346         return this;
9347     }
9348 
9349     /**
9350      * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system.
9351      * <p>
9352      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrtho()} with
9353      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9354      * <p>
9355      * In order to apply the orthographic projection to an already existing transformation,
9356      * use {@link #ortho2D(double, double, double, double) ortho2D()}.
9357      * <p>
9358      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9359      * 
9360      * @see #setOrtho(double, double, double, double, double, double)
9361      * @see #ortho2D(double, double, double, double)
9362      * 
9363      * @param left
9364      *            the distance from the center to the left frustum edge
9365      * @param right
9366      *            the distance from the center to the right frustum edge
9367      * @param bottom
9368      *            the distance from the center to the bottom frustum edge
9369      * @param top
9370      *            the distance from the center to the top frustum edge
9371      * @return this
9372      */
9373     ref public Matrix4d setOrtho2D(double left, double right, double bottom, double top) return {
9374         if ((properties & PROPERTY_IDENTITY) == 0)
9375             _identity();
9376         _m00(2.0 / (right - left)).
9377         _m11(2.0 / (top - bottom)).
9378         _m22(-1.0).
9379         _m30((right + left) / (left - right)).
9380         _m31((top + bottom) / (bottom - top)).
9381         properties = PROPERTY_AFFINE;
9382         return this;
9383     }
9384 
9385     /**
9386      * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system.
9387      * <p>
9388      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrthoLH()} with
9389      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9390      * <p>
9391      * In order to apply the orthographic projection to an already existing transformation,
9392      * use {@link #ortho2DLH(double, double, double, double) ortho2DLH()}.
9393      * <p>
9394      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9395      * 
9396      * @see #setOrthoLH(double, double, double, double, double, double)
9397      * @see #ortho2DLH(double, double, double, double)
9398      * 
9399      * @param left
9400      *            the distance from the center to the left frustum edge
9401      * @param right
9402      *            the distance from the center to the right frustum edge
9403      * @param bottom
9404      *            the distance from the center to the bottom frustum edge
9405      * @param top
9406      *            the distance from the center to the top frustum edge
9407      * @return this
9408      */
9409     ref public Matrix4d setOrtho2DLH(double left, double right, double bottom, double top) return {
9410         if ((properties & PROPERTY_IDENTITY) == 0)
9411             _identity();
9412         _m00(2.0 / (right - left)).
9413         _m11(2.0 / (top - bottom)).
9414         _m30((right + left) / (left - right)).
9415         _m31((top + bottom) / (bottom - top)).
9416         properties = PROPERTY_AFFINE;
9417         return this;
9418     }
9419 
9420     /**
9421      * Apply a rotation transformation to this matrix to make <code>-z</code> point along <code>dir</code>. 
9422      * <p>
9423      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookalong rotation matrix,
9424      * then the new matrix will be <code>M * L</code>. So when transforming a
9425      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>, the
9426      * lookalong rotation transformation will be applied first!
9427      * <p>
9428      * This is equivalent to calling
9429      * {@link #lookAt(ref Vector3d, Vector3d, Vector3d) lookAt}
9430      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9431      * <p>
9432      * In order to set the matrix to a lookalong transformation without post-multiplying it,
9433      * use {@link #setLookAlong(ref Vector3d, Vector3d) setLookAlong()}.
9434      * 
9435      * @see #lookAlong(double, double, double, double, double, double)
9436      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
9437      * @see #setLookAlong(ref Vector3d, Vector3d)
9438      * 
9439      * @param dir
9440      *            the direction in space to look along
9441      * @param up
9442      *            the direction of 'up'
9443      * @return this
9444      */
9445     ref public Matrix4d lookAlong(ref Vector3d dir, Vector3d up) return {
9446         lookAlong(dir.x, dir.y, dir.z, up.x, up.y, up.z, this);
9447         return this;
9448     }
9449 
9450     /**
9451      * Apply a rotation transformation to this matrix to make <code>-z</code> point along <code>dir</code>
9452      * and store the result in <code>dest</code>. 
9453      * <p>
9454      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookalong rotation matrix,
9455      * then the new matrix will be <code>M * L</code>. So when transforming a
9456      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>, the
9457      * lookalong rotation transformation will be applied first!
9458      * <p>
9459      * This is equivalent to calling
9460      * {@link #lookAt(ref Vector3d, Vector3d, Vector3d) lookAt}
9461      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9462      * <p>
9463      * In order to set the matrix to a lookalong transformation without post-multiplying it,
9464      * use {@link #setLookAlong(ref Vector3d, Vector3d) setLookAlong()}.
9465      * 
9466      * @see #lookAlong(double, double, double, double, double, double)
9467      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
9468      * @see #setLookAlong(ref Vector3d, Vector3d)
9469      * 
9470      * @param dir
9471      *            the direction in space to look along
9472      * @param up
9473      *            the direction of 'up'
9474      * @param dest
9475      *            will hold the result
9476      * @return dest
9477      */
9478     public Matrix4d lookAlong(ref Vector3d dir, Vector3d up, ref Matrix4d dest) {
9479         return lookAlong(dir.x, dir.y, dir.z, up.x, up.y, up.z, dest);
9480     }
9481 
9482     /**
9483      * Apply a rotation transformation to this matrix to make <code>-z</code> point along <code>dir</code>
9484      * and store the result in <code>dest</code>. 
9485      * <p>
9486      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookalong rotation matrix,
9487      * then the new matrix will be <code>M * L</code>. So when transforming a
9488      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>, the
9489      * lookalong rotation transformation will be applied first!
9490      * <p>
9491      * This is equivalent to calling
9492      * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt()}
9493      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9494      * <p>
9495      * In order to set the matrix to a lookalong transformation without post-multiplying it,
9496      * use {@link #setLookAlong(double, double, double, double, double, double) setLookAlong()}
9497      * 
9498      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9499      * @see #setLookAlong(double, double, double, double, double, double)
9500      * 
9501      * @param dirX
9502      *              the x-coordinate of the direction to look along
9503      * @param dirY
9504      *              the y-coordinate of the direction to look along
9505      * @param dirZ
9506      *              the z-coordinate of the direction to look along
9507      * @param upX
9508      *              the x-coordinate of the up vector
9509      * @param upY
9510      *              the y-coordinate of the up vector
9511      * @param upZ
9512      *              the z-coordinate of the up vector
9513      * @param dest
9514      *              will hold the result
9515      * @return dest
9516      */
9517     public Matrix4d lookAlong(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, ref Matrix4d dest) {
9518         if ((properties & PROPERTY_IDENTITY) != 0)
9519             return dest.setLookAlong(dirX, dirY, dirZ, upX, upY, upZ);
9520         return lookAlongGeneric(dirX, dirY, dirZ, upX, upY, upZ, dest);
9521     }
9522 
9523     private Matrix4d lookAlongGeneric(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, ref Matrix4d dest) {
9524         // Normalize direction
9525         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
9526         dirX *= -invDirLength;
9527         dirY *= -invDirLength;
9528         dirZ *= -invDirLength;
9529         // left = up x direction
9530         double leftX, leftY, leftZ;
9531         leftX = upY * dirZ - upZ * dirY;
9532         leftY = upZ * dirX - upX * dirZ;
9533         leftZ = upX * dirY - upY * dirX;
9534         // normalize left
9535         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
9536         leftX *= invLeftLength;
9537         leftY *= invLeftLength;
9538         leftZ *= invLeftLength;
9539         // up = direction x left
9540         double upnX = dirY * leftZ - dirZ * leftY;
9541         double upnY = dirZ * leftX - dirX * leftZ;
9542         double upnZ = dirX * leftY - dirY * leftX;
9543         // calculate right matrix elements
9544         double rm00 = leftX;
9545         double rm01 = upnX;
9546         double rm02 = dirX;
9547         double rm10 = leftY;
9548         double rm11 = upnY;
9549         double rm12 = dirY;
9550         double rm20 = leftZ;
9551         double rm21 = upnZ;
9552         double rm22 = dirZ;
9553         // perform optimized matrix multiplication
9554         // introduce temporaries for dependent results
9555         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
9556         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
9557         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
9558         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
9559         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
9560         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
9561         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
9562         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
9563         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
9564         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
9565         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
9566         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
9567         // set the rest of the matrix elements
9568         ._m00(nm00)
9569         ._m01(nm01)
9570         ._m02(nm02)
9571         ._m03(nm03)
9572         ._m10(nm10)
9573         ._m11(nm11)
9574         ._m12(nm12)
9575         ._m13(nm13)
9576         ._m30(m30)
9577         ._m31(m31)
9578         ._m32(m32)
9579         ._m33(m33)
9580         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
9581         return dest;
9582     }
9583 
9584     /**
9585      * Apply a rotation transformation to this matrix to make <code>-z</code> point along <code>dir</code>. 
9586      * <p>
9587      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookalong rotation matrix,
9588      * then the new matrix will be <code>M * L</code>. So when transforming a
9589      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>, the
9590      * lookalong rotation transformation will be applied first!
9591      * <p>
9592      * This is equivalent to calling
9593      * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt()}
9594      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9595      * <p>
9596      * In order to set the matrix to a lookalong transformation without post-multiplying it,
9597      * use {@link #setLookAlong(double, double, double, double, double, double) setLookAlong()}
9598      * 
9599      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9600      * @see #setLookAlong(double, double, double, double, double, double)
9601      * 
9602      * @param dirX
9603      *              the x-coordinate of the direction to look along
9604      * @param dirY
9605      *              the y-coordinate of the direction to look along
9606      * @param dirZ
9607      *              the z-coordinate of the direction to look along
9608      * @param upX
9609      *              the x-coordinate of the up vector
9610      * @param upY
9611      *              the y-coordinate of the up vector
9612      * @param upZ
9613      *              the z-coordinate of the up vector
9614      * @return this
9615      */
9616     ref public Matrix4d lookAlong(double dirX, double dirY, double dirZ,
9617                               double upX, double upY, double upZ) return {
9618         lookAlong(dirX, dirY, dirZ, upX, upY, upZ, this);
9619         return this;
9620     }
9621 
9622     /**
9623      * Set this matrix to a rotation transformation to make <code>-z</code>
9624      * point along <code>dir</code>.
9625      * <p>
9626      * This is equivalent to calling
9627      * {@link #setLookAt(ref Vector3d, Vector3d, Vector3d) setLookAt()} 
9628      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9629      * <p>
9630      * In order to apply the lookalong transformation to any previous existing transformation,
9631      * use {@link #lookAlong(ref Vector3d, Vector3d)}.
9632      * 
9633      * @see #setLookAlong(ref Vector3d, Vector3d)
9634      * @see #lookAlong(ref Vector3d, Vector3d)
9635      * 
9636      * @param dir
9637      *            the direction in space to look along
9638      * @param up
9639      *            the direction of 'up'
9640      * @return this
9641      */
9642     ref public Matrix4d setLookAlong(ref Vector3d dir, Vector3d up) return {
9643         return setLookAlong(dir.x, dir.y, dir.z, up.x, up.y, up.z);
9644     }
9645 
9646     /**
9647      * Set this matrix to a rotation transformation to make <code>-z</code>
9648      * point along <code>dir</code>.
9649      * <p>
9650      * This is equivalent to calling
9651      * {@link #setLookAt(double, double, double, double, double, double, double, double, double)
9652      * setLookAt()} with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9653      * <p>
9654      * In order to apply the lookalong transformation to any previous existing transformation,
9655      * use {@link #lookAlong(double, double, double, double, double, double) lookAlong()}
9656      * 
9657      * @see #setLookAlong(double, double, double, double, double, double)
9658      * @see #lookAlong(double, double, double, double, double, double)
9659      * 
9660      * @param dirX
9661      *              the x-coordinate of the direction to look along
9662      * @param dirY
9663      *              the y-coordinate of the direction to look along
9664      * @param dirZ
9665      *              the z-coordinate of the direction to look along
9666      * @param upX
9667      *              the x-coordinate of the up vector
9668      * @param upY
9669      *              the y-coordinate of the up vector
9670      * @param upZ
9671      *              the z-coordinate of the up vector
9672      * @return this
9673      */
9674     ref public Matrix4d setLookAlong(double dirX, double dirY, double dirZ,
9675                                  double upX, double upY, double upZ) return {
9676         // Normalize direction
9677         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
9678         dirX *= -invDirLength;
9679         dirY *= -invDirLength;
9680         dirZ *= -invDirLength;
9681         // left = up x direction
9682         double leftX, leftY, leftZ;
9683         leftX = upY * dirZ - upZ * dirY;
9684         leftY = upZ * dirX - upX * dirZ;
9685         leftZ = upX * dirY - upY * dirX;
9686         // normalize left
9687         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
9688         leftX *= invLeftLength;
9689         leftY *= invLeftLength;
9690         leftZ *= invLeftLength;
9691         // up = direction x left
9692         double upnX = dirY * leftZ - dirZ * leftY;
9693         double upnY = dirZ * leftX - dirX * leftZ;
9694         double upnZ = dirX * leftY - dirY * leftX;
9695         _m00(leftX).
9696         _m01(upnX).
9697         _m02(dirX).
9698         _m03(0.0).
9699         _m10(leftY).
9700         _m11(upnY).
9701         _m12(dirY).
9702         _m13(0.0).
9703         _m20(leftZ).
9704         _m21(upnZ).
9705         _m22(dirZ).
9706         _m23(0.0).
9707         _m30(0.0).
9708         _m31(0.0).
9709         _m32(0.0).
9710         _m33(1.0).
9711         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
9712         return this;
9713     }
9714 
9715     /**
9716      * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, that aligns
9717      * <code>-z</code> with <code>center - eye</code>.
9718      * <p>
9719      * In order to not make use of vectors to specify <code>eye</code>, <code>center</code> and <code>up</code> but use primitives,
9720      * like in the GLU function, use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}
9721      * instead.
9722      * <p>
9723      * In order to apply the lookat transformation to a previous existing transformation,
9724      * use {@link #lookAt(ref Vector3d, Vector3d, Vector3d) lookAt()}.
9725      * 
9726      * @see #setLookAt(double, double, double, double, double, double, double, double, double)
9727      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
9728      * 
9729      * @param eye
9730      *            the position of the camera
9731      * @param center
9732      *            the point in space to look at
9733      * @param up
9734      *            the direction of 'up'
9735      * @return this
9736      */
9737     ref public Matrix4d setLookAt(ref Vector3d eye, Vector3d center, Vector3d up) return {
9738         return setLookAt(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z);
9739     }
9740 
9741     /**
9742      * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, 
9743      * that aligns <code>-z</code> with <code>center - eye</code>.
9744      * <p>
9745      * In order to apply the lookat transformation to a previous existing transformation,
9746      * use {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt}.
9747      * 
9748      * @see #setLookAt(ref Vector3d, Vector3d, Vector3d)
9749      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9750      * 
9751      * @param eyeX
9752      *              the x-coordinate of the eye/camera location
9753      * @param eyeY
9754      *              the y-coordinate of the eye/camera location
9755      * @param eyeZ
9756      *              the z-coordinate of the eye/camera location
9757      * @param centerX
9758      *              the x-coordinate of the point to look at
9759      * @param centerY
9760      *              the y-coordinate of the point to look at
9761      * @param centerZ
9762      *              the z-coordinate of the point to look at
9763      * @param upX
9764      *              the x-coordinate of the up vector
9765      * @param upY
9766      *              the y-coordinate of the up vector
9767      * @param upZ
9768      *              the z-coordinate of the up vector
9769      * @return this
9770      */
9771     ref public Matrix4d setLookAt(double eyeX, double eyeY, double eyeZ,
9772                               double centerX, double centerY, double centerZ,
9773                               double upX, double upY, double upZ) return {
9774         // Compute direction from position to lookAt
9775         double dirX, dirY, dirZ;
9776         dirX = eyeX - centerX;
9777         dirY = eyeY - centerY;
9778         dirZ = eyeZ - centerZ;
9779         // Normalize direction
9780         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
9781         dirX *= invDirLength;
9782         dirY *= invDirLength;
9783         dirZ *= invDirLength;
9784         // left = up x direction
9785         double leftX, leftY, leftZ;
9786         leftX = upY * dirZ - upZ * dirY;
9787         leftY = upZ * dirX - upX * dirZ;
9788         leftZ = upX * dirY - upY * dirX;
9789         // normalize left
9790         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
9791         leftX *= invLeftLength;
9792         leftY *= invLeftLength;
9793         leftZ *= invLeftLength;
9794         // up = direction x left
9795         double upnX = dirY * leftZ - dirZ * leftY;
9796         double upnY = dirZ * leftX - dirX * leftZ;
9797         double upnZ = dirX * leftY - dirY * leftX;
9798         return this.
9799         _m00(leftX).
9800         _m01(upnX).
9801         _m02(dirX).
9802         _m03(0.0).
9803         _m10(leftY).
9804         _m11(upnY).
9805         _m12(dirY).
9806         _m13(0.0).
9807         _m20(leftZ).
9808         _m21(upnZ).
9809         _m22(dirZ).
9810         _m23(0.0).
9811         _m30(-(leftX * eyeX + leftY * eyeY + leftZ * eyeZ)).
9812         _m31(-(upnX * eyeX + upnY * eyeY + upnZ * eyeZ)).
9813         _m32(-(dirX * eyeX + dirY * eyeY + dirZ * eyeZ)).
9814         _m33(1.0).
9815         _properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
9816     }
9817 
9818     /**
9819      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
9820      * that aligns <code>-z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
9821      * <p>
9822      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
9823      * then the new matrix will be <code>M * L</code>. So when transforming a
9824      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
9825      * the lookat transformation will be applied first!
9826      * <p>
9827      * In order to set the matrix to a lookat transformation without post-multiplying it,
9828      * use {@link #setLookAt(ref Vector3d, Vector3d, Vector3d)}.
9829      * 
9830      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9831      * @see #setLookAlong(ref Vector3d, Vector3d)
9832      * 
9833      * @param eye
9834      *            the position of the camera
9835      * @param center
9836      *            the point in space to look at
9837      * @param up
9838      *            the direction of 'up'
9839      * @param dest
9840      *            will hold the result
9841      * @return dest
9842      */
9843     public Matrix4d lookAt(ref Vector3d eye, Vector3d center, Vector3d up, ref Matrix4d dest) {
9844         return lookAt(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z, dest);
9845     }
9846 
9847     /**
9848      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
9849      * that aligns <code>-z</code> with <code>center - eye</code>.
9850      * <p>
9851      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
9852      * then the new matrix will be <code>M * L</code>. So when transforming a
9853      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
9854      * the lookat transformation will be applied first!
9855      * <p>
9856      * In order to set the matrix to a lookat transformation without post-multiplying it,
9857      * use {@link #setLookAt(ref Vector3d, Vector3d, Vector3d)}.
9858      * 
9859      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9860      * @see #setLookAlong(ref Vector3d, Vector3d)
9861      * 
9862      * @param eye
9863      *            the position of the camera
9864      * @param center
9865      *            the point in space to look at
9866      * @param up
9867      *            the direction of 'up'
9868      * @return this
9869      */
9870     ref public Matrix4d lookAt(ref Vector3d eye, Vector3d center, Vector3d up) return {
9871         lookAt(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z, this);
9872         return this;
9873     }
9874 
9875     /**
9876      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
9877      * that aligns <code>-z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
9878      * <p>
9879      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
9880      * then the new matrix will be <code>M * L</code>. So when transforming a
9881      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
9882      * the lookat transformation will be applied first!
9883      * <p>
9884      * In order to set the matrix to a lookat transformation without post-multiplying it,
9885      * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}.
9886      * 
9887      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
9888      * @see #setLookAt(double, double, double, double, double, double, double, double, double)
9889      * 
9890      * @param eyeX
9891      *              the x-coordinate of the eye/camera location
9892      * @param eyeY
9893      *              the y-coordinate of the eye/camera location
9894      * @param eyeZ
9895      *              the z-coordinate of the eye/camera location
9896      * @param centerX
9897      *              the x-coordinate of the point to look at
9898      * @param centerY
9899      *              the y-coordinate of the point to look at
9900      * @param centerZ
9901      *              the z-coordinate of the point to look at
9902      * @param upX
9903      *              the x-coordinate of the up vector
9904      * @param upY
9905      *              the y-coordinate of the up vector
9906      * @param upZ
9907      *              the z-coordinate of the up vector
9908      * @param dest
9909      *          will hold the result
9910      * @return dest
9911      */
9912     public Matrix4d lookAt(double eyeX, double eyeY, double eyeZ,
9913                            double centerX, double centerY, double centerZ,
9914                            double upX, double upY, double upZ, ref Matrix4d dest) {
9915         if ((properties & PROPERTY_IDENTITY) != 0)
9916             return dest.setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
9917         else if ((properties & PROPERTY_PERSPECTIVE) != 0)
9918             return lookAtPerspective(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest);
9919         return lookAtGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest);
9920     }
9921     private Matrix4d lookAtGeneric(double eyeX, double eyeY, double eyeZ,
9922                                    double centerX, double centerY, double centerZ,
9923                                    double upX, double upY, double upZ, ref Matrix4d dest) {
9924         // Compute direction from position to lookAt
9925         double dirX, dirY, dirZ;
9926         dirX = eyeX - centerX;
9927         dirY = eyeY - centerY;
9928         dirZ = eyeZ - centerZ;
9929         // Normalize direction
9930         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
9931         dirX *= invDirLength;
9932         dirY *= invDirLength;
9933         dirZ *= invDirLength;
9934         // left = up x direction
9935         double leftX, leftY, leftZ;
9936         leftX = upY * dirZ - upZ * dirY;
9937         leftY = upZ * dirX - upX * dirZ;
9938         leftZ = upX * dirY - upY * dirX;
9939         // normalize left
9940         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
9941         leftX *= invLeftLength;
9942         leftY *= invLeftLength;
9943         leftZ *= invLeftLength;
9944         // up = direction x left
9945         double upnX = dirY * leftZ - dirZ * leftY;
9946         double upnY = dirZ * leftX - dirX * leftZ;
9947         double upnZ = dirX * leftY - dirY * leftX;
9948         // calculate right matrix elements
9949         double rm00 = leftX;
9950         double rm01 = upnX;
9951         double rm02 = dirX;
9952         double rm10 = leftY;
9953         double rm11 = upnY;
9954         double rm12 = dirY;
9955         double rm20 = leftZ;
9956         double rm21 = upnZ;
9957         double rm22 = dirZ;
9958         double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ);
9959         double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ);
9960         double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ);
9961         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
9962         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
9963         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
9964         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
9965         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
9966         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
9967         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
9968         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
9969         // perform optimized matrix multiplication
9970         // compute last column first, because others do not depend on it
9971         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
9972         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
9973         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
9974         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
9975         // introduce temporaries for dependent results
9976         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
9977         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
9978         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
9979         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
9980         // set the rest of the matrix elements
9981         ._m00(nm00)
9982         ._m01(nm01)
9983         ._m02(nm02)
9984         ._m03(nm03)
9985         ._m10(nm10)
9986         ._m11(nm11)
9987         ._m12(nm12)
9988         ._m13(nm13)
9989         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
9990         return dest;
9991     }
9992 
9993     /**
9994      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
9995      * that aligns <code>-z</code> with <code>center - eye</code>.
9996      * <p>
9997      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
9998      * then the new matrix will be <code>M * L</code>. So when transforming a
9999      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10000      * the lookat transformation will be applied first!
10001      * <p>
10002      * In order to set the matrix to a lookat transformation without post-multiplying it,
10003      * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}.
10004      * 
10005      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
10006      * @see #setLookAt(double, double, double, double, double, double, double, double, double)
10007      * 
10008      * @param eyeX
10009      *              the x-coordinate of the eye/camera location
10010      * @param eyeY
10011      *              the y-coordinate of the eye/camera location
10012      * @param eyeZ
10013      *              the z-coordinate of the eye/camera location
10014      * @param centerX
10015      *              the x-coordinate of the point to look at
10016      * @param centerY
10017      *              the y-coordinate of the point to look at
10018      * @param centerZ
10019      *              the z-coordinate of the point to look at
10020      * @param upX
10021      *              the x-coordinate of the up vector
10022      * @param upY
10023      *              the y-coordinate of the up vector
10024      * @param upZ
10025      *              the z-coordinate of the up vector
10026      * @return this
10027      */
10028     ref public Matrix4d lookAt(double eyeX, double eyeY, double eyeZ,
10029                            double centerX, double centerY, double centerZ,
10030                            double upX, double upY, double upZ) return {
10031         lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this);
10032         return this;
10033     }
10034 
10035     /**
10036      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
10037      * that aligns <code>-z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
10038      * <p>
10039      * This method assumes <code>this</code> to be a perspective transformation, obtained via
10040      * {@link #frustum(double, double, double, double, double, double) frustum()} or {@link #perspective(double, double, double, double) perspective()} or
10041      * one of their overloads.
10042      * <p>
10043      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10044      * then the new matrix will be <code>M * L</code>. So when transforming a
10045      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10046      * the lookat transformation will be applied first!
10047      * <p>
10048      * In order to set the matrix to a lookat transformation without post-multiplying it,
10049      * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}.
10050      * 
10051      * @see #setLookAt(double, double, double, double, double, double, double, double, double)
10052      * 
10053      * @param eyeX
10054      *              the x-coordinate of the eye/camera location
10055      * @param eyeY
10056      *              the y-coordinate of the eye/camera location
10057      * @param eyeZ
10058      *              the z-coordinate of the eye/camera location
10059      * @param centerX
10060      *              the x-coordinate of the point to look at
10061      * @param centerY
10062      *              the y-coordinate of the point to look at
10063      * @param centerZ
10064      *              the z-coordinate of the point to look at
10065      * @param upX
10066      *              the x-coordinate of the up vector
10067      * @param upY
10068      *              the y-coordinate of the up vector
10069      * @param upZ
10070      *              the z-coordinate of the up vector
10071      * @param dest
10072      *          will hold the result
10073      * @return dest
10074      */
10075     public Matrix4d lookAtPerspective(double eyeX, double eyeY, double eyeZ,
10076             double centerX, double centerY, double centerZ,
10077             double upX, double upY, double upZ, ref Matrix4d dest) {
10078         // Compute direction from position to lookAt
10079         double dirX, dirY, dirZ;
10080         dirX = eyeX - centerX;
10081         dirY = eyeY - centerY;
10082         dirZ = eyeZ - centerZ;
10083         // Normalize direction
10084         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
10085         dirX *= invDirLength;
10086         dirY *= invDirLength;
10087         dirZ *= invDirLength;
10088         // left = up x direction
10089         double leftX, leftY, leftZ;
10090         leftX = upY * dirZ - upZ * dirY;
10091         leftY = upZ * dirX - upX * dirZ;
10092         leftZ = upX * dirY - upY * dirX;
10093         // normalize left
10094         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
10095         leftX *= invLeftLength;
10096         leftY *= invLeftLength;
10097         leftZ *= invLeftLength;
10098         // up = direction x left
10099         double upnX = dirY * leftZ - dirZ * leftY;
10100         double upnY = dirZ * leftX - dirX * leftZ;
10101         double upnZ = dirX * leftY - dirY * leftX;
10102         double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ);
10103         double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ);
10104         double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ);
10105         double nm10 = m00 * leftY;
10106         double nm20 = m00 * leftZ;
10107         double nm21 = m11 * upnZ;
10108         double nm30 = m00 * rm30;
10109         double nm31 = m11 * rm31;
10110         double nm32 = m22 * rm32 + m32;
10111         double nm33 = m23 * rm32;
10112         return dest
10113         ._m00(m00 * leftX)
10114         ._m01(m11 * upnX)
10115         ._m02(m22 * dirX)
10116         ._m03(m23 * dirX)
10117         ._m10(nm10)
10118         ._m11(m11 * upnY)
10119         ._m12(m22 * dirY)
10120         ._m13(m23 * dirY)
10121         ._m20(nm20)
10122         ._m21(nm21)
10123         ._m22(m22 * dirZ)
10124         ._m23(m23 * dirZ)
10125         ._m30(nm30)
10126         ._m31(nm31)
10127         ._m32(nm32)
10128         ._m33(nm33)
10129         ._properties(0);
10130     }
10131 
10132     /**
10133      * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, that aligns
10134      * <code>+z</code> with <code>center - eye</code>.
10135      * <p>
10136      * In order to not make use of vectors to specify <code>eye</code>, <code>center</code> and <code>up</code> but use primitives,
10137      * like in the GLU function, use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}
10138      * instead.
10139      * <p>
10140      * In order to apply the lookat transformation to a previous existing transformation,
10141      * use {@link #lookAtLH(ref Vector3d, Vector3d, Vector3d) lookAt()}.
10142      * 
10143      * @see #setLookAtLH(double, double, double, double, double, double, double, double, double)
10144      * @see #lookAtLH(ref Vector3d, Vector3d, Vector3d)
10145      * 
10146      * @param eye
10147      *            the position of the camera
10148      * @param center
10149      *            the point in space to look at
10150      * @param up
10151      *            the direction of 'up'
10152      * @return this
10153      */
10154     ref public Matrix4d setLookAtLH(ref Vector3d eye, Vector3d center, Vector3d up) return {
10155         return setLookAtLH(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z);
10156     }
10157 
10158     /**
10159      * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, 
10160      * that aligns <code>+z</code> with <code>center - eye</code>.
10161      * <p>
10162      * In order to apply the lookat transformation to a previous existing transformation,
10163      * use {@link #lookAtLH(double, double, double, double, double, double, double, double, double) lookAtLH}.
10164      * 
10165      * @see #setLookAtLH(ref Vector3d, Vector3d, Vector3d)
10166      * @see #lookAtLH(double, double, double, double, double, double, double, double, double)
10167      * 
10168      * @param eyeX
10169      *              the x-coordinate of the eye/camera location
10170      * @param eyeY
10171      *              the y-coordinate of the eye/camera location
10172      * @param eyeZ
10173      *              the z-coordinate of the eye/camera location
10174      * @param centerX
10175      *              the x-coordinate of the point to look at
10176      * @param centerY
10177      *              the y-coordinate of the point to look at
10178      * @param centerZ
10179      *              the z-coordinate of the point to look at
10180      * @param upX
10181      *              the x-coordinate of the up vector
10182      * @param upY
10183      *              the y-coordinate of the up vector
10184      * @param upZ
10185      *              the z-coordinate of the up vector
10186      * @return this
10187      */
10188     ref public Matrix4d setLookAtLH(double eyeX, double eyeY, double eyeZ,
10189                                 double centerX, double centerY, double centerZ,
10190                                 double upX, double upY, double upZ) return {
10191         // Compute direction from position to lookAt
10192         double dirX, dirY, dirZ;
10193         dirX = centerX - eyeX;
10194         dirY = centerY - eyeY;
10195         dirZ = centerZ - eyeZ;
10196         // Normalize direction
10197         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
10198         dirX *= invDirLength;
10199         dirY *= invDirLength;
10200         dirZ *= invDirLength;
10201         // left = up x direction
10202         double leftX, leftY, leftZ;
10203         leftX = upY * dirZ - upZ * dirY;
10204         leftY = upZ * dirX - upX * dirZ;
10205         leftZ = upX * dirY - upY * dirX;
10206         // normalize left
10207         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
10208         leftX *= invLeftLength;
10209         leftY *= invLeftLength;
10210         leftZ *= invLeftLength;
10211         // up = direction x left
10212         double upnX = dirY * leftZ - dirZ * leftY;
10213         double upnY = dirZ * leftX - dirX * leftZ;
10214         double upnZ = dirX * leftY - dirY * leftX;
10215         _m00(leftX).
10216         _m01(upnX).
10217         _m02(dirX).
10218         _m03(0.0).
10219         _m10(leftY).
10220         _m11(upnY).
10221         _m12(dirY).
10222         _m13(0.0).
10223         _m20(leftZ).
10224         _m21(upnZ).
10225         _m22(dirZ).
10226         _m23(0.0).
10227         _m30(-(leftX * eyeX + leftY * eyeY + leftZ * eyeZ)).
10228         _m31(-(upnX * eyeX + upnY * eyeY + upnZ * eyeZ)).
10229         _m32(-(dirX * eyeX + dirY * eyeY + dirZ * eyeZ)).
10230         _m33(1.0).
10231         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
10232         return this;
10233     }
10234 
10235     /**
10236      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10237      * that aligns <code>+z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
10238      * <p>
10239      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10240      * then the new matrix will be <code>M * L</code>. So when transforming a
10241      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10242      * the lookat transformation will be applied first!
10243      * <p>
10244      * In order to set the matrix to a lookat transformation without post-multiplying it,
10245      * use {@link #setLookAtLH(ref Vector3d, Vector3d, Vector3d)}.
10246      * 
10247      * @see #lookAtLH(double, double, double, double, double, double, double, double, double)
10248      * @see #setLookAtLH(ref Vector3d, Vector3d, Vector3d)
10249      * 
10250      * @param eye
10251      *            the position of the camera
10252      * @param center
10253      *            the point in space to look at
10254      * @param up
10255      *            the direction of 'up'
10256      * @param dest
10257      *            will hold the result
10258      * @return dest
10259      */
10260     public Matrix4d lookAtLH(ref Vector3d eye, Vector3d center, Vector3d up, ref Matrix4d dest) {
10261         return lookAtLH(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z, dest);
10262     }
10263 
10264     /**
10265      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10266      * that aligns <code>+z</code> with <code>center - eye</code>.
10267      * <p>
10268      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10269      * then the new matrix will be <code>M * L</code>. So when transforming a
10270      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10271      * the lookat transformation will be applied first!
10272      * <p>
10273      * In order to set the matrix to a lookat transformation without post-multiplying it,
10274      * use {@link #setLookAtLH(ref Vector3d, Vector3d, Vector3d)}.
10275      * 
10276      * @see #lookAtLH(double, double, double, double, double, double, double, double, double)
10277      * 
10278      * @param eye
10279      *            the position of the camera
10280      * @param center
10281      *            the point in space to look at
10282      * @param up
10283      *            the direction of 'up'
10284      * @return this
10285      */
10286     ref public Matrix4d lookAtLH(ref Vector3d eye, Vector3d center, Vector3d up) return {
10287         lookAtLH(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z, this);
10288         return this;
10289     }
10290 
10291     /**
10292      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10293      * that aligns <code>+z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
10294      * <p>
10295      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10296      * then the new matrix will be <code>M * L</code>. So when transforming a
10297      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10298      * the lookat transformation will be applied first!
10299      * <p>
10300      * In order to set the matrix to a lookat transformation without post-multiplying it,
10301      * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}.
10302      * 
10303      * @see #lookAtLH(ref Vector3d, Vector3d, Vector3d)
10304      * @see #setLookAtLH(double, double, double, double, double, double, double, double, double)
10305      * 
10306      * @param eyeX
10307      *              the x-coordinate of the eye/camera location
10308      * @param eyeY
10309      *              the y-coordinate of the eye/camera location
10310      * @param eyeZ
10311      *              the z-coordinate of the eye/camera location
10312      * @param centerX
10313      *              the x-coordinate of the point to look at
10314      * @param centerY
10315      *              the y-coordinate of the point to look at
10316      * @param centerZ
10317      *              the z-coordinate of the point to look at
10318      * @param upX
10319      *              the x-coordinate of the up vector
10320      * @param upY
10321      *              the y-coordinate of the up vector
10322      * @param upZ
10323      *              the z-coordinate of the up vector
10324      * @param dest
10325      *          will hold the result
10326      * @return dest
10327      */
10328     public Matrix4d lookAtLH(double eyeX, double eyeY, double eyeZ,
10329                              double centerX, double centerY, double centerZ,
10330                              double upX, double upY, double upZ, ref Matrix4d dest) {
10331         if ((properties & PROPERTY_IDENTITY) != 0)
10332             return dest.setLookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
10333         else if ((properties & PROPERTY_PERSPECTIVE) != 0)
10334             return lookAtPerspectiveLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest);
10335         return lookAtLHGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest);
10336     }
10337     private Matrix4d lookAtLHGeneric(double eyeX, double eyeY, double eyeZ,
10338                                      double centerX, double centerY, double centerZ,
10339                                      double upX, double upY, double upZ, ref Matrix4d dest) {
10340         // Compute direction from position to lookAt
10341         double dirX, dirY, dirZ;
10342         dirX = centerX - eyeX;
10343         dirY = centerY - eyeY;
10344         dirZ = centerZ - eyeZ;
10345         // Normalize direction
10346         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
10347         dirX *= invDirLength;
10348         dirY *= invDirLength;
10349         dirZ *= invDirLength;
10350         // left = up x direction
10351         double leftX, leftY, leftZ;
10352         leftX = upY * dirZ - upZ * dirY;
10353         leftY = upZ * dirX - upX * dirZ;
10354         leftZ = upX * dirY - upY * dirX;
10355         // normalize left
10356         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
10357         leftX *= invLeftLength;
10358         leftY *= invLeftLength;
10359         leftZ *= invLeftLength;
10360         // up = direction x left
10361         double upnX = dirY * leftZ - dirZ * leftY;
10362         double upnY = dirZ * leftX - dirX * leftZ;
10363         double upnZ = dirX * leftY - dirY * leftX;
10364         // calculate right matrix elements
10365         double rm00 = leftX;
10366         double rm01 = upnX;
10367         double rm02 = dirX;
10368         double rm10 = leftY;
10369         double rm11 = upnY;
10370         double rm12 = dirY;
10371         double rm20 = leftZ;
10372         double rm21 = upnZ;
10373         double rm22 = dirZ;
10374         double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ);
10375         double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ);
10376         double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ);
10377         // introduce temporaries for dependent results
10378         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
10379         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
10380         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
10381         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
10382         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
10383         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
10384         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
10385         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
10386         // perform optimized matrix multiplication
10387         // compute last column first, because others do not depend on it
10388         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
10389         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
10390         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
10391         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
10392         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
10393         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
10394         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
10395         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
10396         // set the rest of the matrix elements
10397         ._m00(nm00)
10398         ._m01(nm01)
10399         ._m02(nm02)
10400         ._m03(nm03)
10401         ._m10(nm10)
10402         ._m11(nm11)
10403         ._m12(nm12)
10404         ._m13(nm13)
10405         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
10406         return dest;
10407     }
10408 
10409     /**
10410      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10411      * that aligns <code>+z</code> with <code>center - eye</code>.
10412      * <p>
10413      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10414      * then the new matrix will be <code>M * L</code>. So when transforming a
10415      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10416      * the lookat transformation will be applied first!
10417      * <p>
10418      * In order to set the matrix to a lookat transformation without post-multiplying it,
10419      * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}.
10420      * 
10421      * @see #lookAtLH(ref Vector3d, Vector3d, Vector3d)
10422      * @see #setLookAtLH(double, double, double, double, double, double, double, double, double)
10423      * 
10424      * @param eyeX
10425      *              the x-coordinate of the eye/camera location
10426      * @param eyeY
10427      *              the y-coordinate of the eye/camera location
10428      * @param eyeZ
10429      *              the z-coordinate of the eye/camera location
10430      * @param centerX
10431      *              the x-coordinate of the point to look at
10432      * @param centerY
10433      *              the y-coordinate of the point to look at
10434      * @param centerZ
10435      *              the z-coordinate of the point to look at
10436      * @param upX
10437      *              the x-coordinate of the up vector
10438      * @param upY
10439      *              the y-coordinate of the up vector
10440      * @param upZ
10441      *              the z-coordinate of the up vector
10442      * @return this
10443      */
10444     ref public Matrix4d lookAtLH(double eyeX, double eyeY, double eyeZ,
10445                              double centerX, double centerY, double centerZ,
10446                              double upX, double upY, double upZ) return {
10447         lookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this);
10448         return this;
10449     }
10450 
10451     /**
10452      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10453      * that aligns <code>+z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
10454      * <p>
10455      * This method assumes <code>this</code> to be a perspective transformation, obtained via
10456      * {@link #frustumLH(double, double, double, double, double, double) frustumLH()} or {@link #perspectiveLH(double, double, double, double) perspectiveLH()} or
10457      * one of their overloads.
10458      * <p>
10459      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10460      * then the new matrix will be <code>M * L</code>. So when transforming a
10461      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10462      * the lookat transformation will be applied first!
10463      * <p>
10464      * In order to set the matrix to a lookat transformation without post-multiplying it,
10465      * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}.
10466      * 
10467      * @see #setLookAtLH(double, double, double, double, double, double, double, double, double)
10468      * 
10469      * @param eyeX
10470      *              the x-coordinate of the eye/camera location
10471      * @param eyeY
10472      *              the y-coordinate of the eye/camera location
10473      * @param eyeZ
10474      *              the z-coordinate of the eye/camera location
10475      * @param centerX
10476      *              the x-coordinate of the point to look at
10477      * @param centerY
10478      *              the y-coordinate of the point to look at
10479      * @param centerZ
10480      *              the z-coordinate of the point to look at
10481      * @param upX
10482      *              the x-coordinate of the up vector
10483      * @param upY
10484      *              the y-coordinate of the up vector
10485      * @param upZ
10486      *              the z-coordinate of the up vector
10487      * @param dest
10488      *          will hold the result
10489      * @return dest
10490      */
10491     public Matrix4d lookAtPerspectiveLH(double eyeX, double eyeY, double eyeZ,
10492             double centerX, double centerY, double centerZ,
10493             double upX, double upY, double upZ, ref Matrix4d dest) {
10494         // Compute direction from position to lookAt
10495         double dirX, dirY, dirZ;
10496         dirX = centerX - eyeX;
10497         dirY = centerY - eyeY;
10498         dirZ = centerZ - eyeZ;
10499         // Normalize direction
10500         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
10501         dirX *= invDirLength;
10502         dirY *= invDirLength;
10503         dirZ *= invDirLength;
10504         // left = up x direction
10505         double leftX, leftY, leftZ;
10506         leftX = upY * dirZ - upZ * dirY;
10507         leftY = upZ * dirX - upX * dirZ;
10508         leftZ = upX * dirY - upY * dirX;
10509         // normalize left
10510         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
10511         leftX *= invLeftLength;
10512         leftY *= invLeftLength;
10513         leftZ *= invLeftLength;
10514         // up = direction x left
10515         double upnX = dirY * leftZ - dirZ * leftY;
10516         double upnY = dirZ * leftX - dirX * leftZ;
10517         double upnZ = dirX * leftY - dirY * leftX;
10518 
10519         // calculate right matrix elements
10520         double rm00 = leftX;
10521         double rm01 = upnX;
10522         double rm02 = dirX;
10523         double rm10 = leftY;
10524         double rm11 = upnY;
10525         double rm12 = dirY;
10526         double rm20 = leftZ;
10527         double rm21 = upnZ;
10528         double rm22 = dirZ;
10529         double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ);
10530         double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ);
10531         double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ);
10532 
10533         double nm00 = m00 * rm00;
10534         double nm01 = m11 * rm01;
10535         double nm02 = m22 * rm02;
10536         double nm03 = m23 * rm02;
10537         double nm10 = m00 * rm10;
10538         double nm11 = m11 * rm11;
10539         double nm12 = m22 * rm12;
10540         double nm13 = m23 * rm12;
10541         double nm20 = m00 * rm20;
10542         double nm21 = m11 * rm21;
10543         double nm22 = m22 * rm22;
10544         double nm23 = m23 * rm22;
10545         double nm30 = m00 * rm30;
10546         double nm31 = m11 * rm31;
10547         double nm32 = m22 * rm32 + m32;
10548         double nm33 = m23 * rm32;
10549         dest._m00(nm00)
10550         ._m01(nm01)
10551         ._m02(nm02)
10552         ._m03(nm03)
10553         ._m10(nm10)
10554         ._m11(nm11)
10555         ._m12(nm12)
10556         ._m13(nm13)
10557         ._m20(nm20)
10558         ._m21(nm21)
10559         ._m22(nm22)
10560         ._m23(nm23)
10561         ._m30(nm30)
10562         ._m31(nm31)
10563         ._m32(nm32)
10564         ._m33(nm33)
10565         ._properties(0);
10566 
10567         return dest;
10568     }
10569 
10570     /**
10571      * This method is equivalent to calling: <code>translate(w-1-2*x, h-1-2*y, 0).scale(w, h, 1)</code>
10572      * <p>
10573      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the created transformation matrix,
10574      * then the new matrix will be <code>M * T</code>. So when transforming a
10575      * vector <code>v</code> with the new matrix by using <code>M * T * v</code>, the
10576      * created transformation will be applied first!
10577      * 
10578      * @param x
10579      *             the tile's x coordinate/index (should be in <code>[0..w)</code>)
10580      * @param y
10581      *             the tile's y coordinate/index (should be in <code>[0..h)</code>)
10582      * @param w
10583      *             the number of tiles along the x axis
10584      * @param h
10585      *             the number of tiles along the y axis
10586      * @return this
10587      */
10588     ref public Matrix4d tile(int x, int y, int w, int h) return {
10589         tile(x, y, w, h, this);
10590         return this;
10591     }
10592     public Matrix4d tile(int x, int y, int w, int h, ref Matrix4d dest) {
10593         double tx = w - 1 - (x<<1), ty = h - 1 - (y<<1);
10594         return dest
10595         ._m30(Math.fma(m00, tx, Math.fma(m10, ty, m30)))
10596         ._m31(Math.fma(m01, tx, Math.fma(m11, ty, m31)))
10597         ._m32(Math.fma(m02, tx, Math.fma(m12, ty, m32)))
10598         ._m33(Math.fma(m03, tx, Math.fma(m13, ty, m33)))
10599         ._m00(m00 * w)
10600         ._m01(m01 * w)
10601         ._m02(m02 * w)
10602         ._m03(m03 * w)
10603         ._m10(m10 * h)
10604         ._m11(m11 * h)
10605         ._m12(m12 * h)
10606         ._m13(m13 * h)
10607         ._m20(m20)
10608         ._m21(m21)
10609         ._m22(m22)
10610         ._m23(m23)
10611         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
10612     }
10613 
10614     /**
10615      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10616      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
10617      * <p>
10618      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10619      * then the new matrix will be <code>M * P</code>. So when transforming a
10620      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10621      * the perspective projection will be applied first!
10622      * <p>
10623      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10624      * use {@link #setPerspective(double, double, double, double, bool) setPerspective}.
10625      * 
10626      * @see #setPerspective(double, double, double, double, bool)
10627      * 
10628      * @param fovy
10629      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
10630      * @param aspect
10631      *            the aspect ratio (i.e. width / height; must be greater than zero)
10632      * @param zNear
10633      *            near clipping plane distance. This value must be greater than zero.
10634      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10635      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10636      * @param zFar
10637      *            far clipping plane distance. This value must be greater than zero.
10638      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10639      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10640      * @param dest
10641      *            will hold the result
10642      * @param zZeroToOne
10643      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
10644      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
10645      * @return dest
10646      */
10647     public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
10648         if ((properties & PROPERTY_IDENTITY) != 0)
10649             return dest.setPerspective(fovy, aspect, zNear, zFar, zZeroToOne);
10650         return perspectiveGeneric(fovy, aspect, zNear, zFar, zZeroToOne, dest);
10651     }
10652     private Matrix4d perspectiveGeneric(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
10653         double h = Math.tan(fovy * 0.5);
10654         // calculate right matrix elements
10655         double rm00 = 1.0 / (h * aspect);
10656         double rm11 = 1.0 / h;
10657         double rm22;
10658         double rm32;
10659         bool farInf = zFar > 0 && Math.isInfinite(zFar);
10660         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
10661         if (farInf) {
10662             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
10663             double e = 1E-6;
10664             rm22 = e - 1.0;
10665             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
10666         } else if (nearInf) {
10667             double e = 1E-6;
10668             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
10669             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
10670         } else {
10671             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar);
10672             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
10673         }
10674         // perform optimized matrix multiplication
10675         double nm20 = m20 * rm22 - m30;
10676         double nm21 = m21 * rm22 - m31;
10677         double nm22 = m22 * rm22 - m32;
10678         double nm23 = m23 * rm22 - m33;
10679         dest._m00(m00 * rm00)
10680         ._m01(m01 * rm00)
10681         ._m02(m02 * rm00)
10682         ._m03(m03 * rm00)
10683         ._m10(m10 * rm11)
10684         ._m11(m11 * rm11)
10685         ._m12(m12 * rm11)
10686         ._m13(m13 * rm11)
10687         ._m30(m20 * rm32)
10688         ._m31(m21 * rm32)
10689         ._m32(m22 * rm32)
10690         ._m33(m23 * rm32)
10691         ._m20(nm20)
10692         ._m21(nm21)
10693         ._m22(nm22)
10694         ._m23(nm23)
10695         ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
10696         return dest;
10697     }
10698 
10699     /**
10700      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10701      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
10702      * <p>
10703      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10704      * then the new matrix will be <code>M * P</code>. So when transforming a
10705      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10706      * the perspective projection will be applied first!
10707      * <p>
10708      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10709      * use {@link #setPerspective(double, double, double, double) setPerspective}.
10710      * 
10711      * @see #setPerspective(double, double, double, double)
10712      * 
10713      * @param fovy
10714      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
10715      * @param aspect
10716      *            the aspect ratio (i.e. width / height; must be greater than zero)
10717      * @param zNear
10718      *            near clipping plane distance. This value must be greater than zero.
10719      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10720      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10721      * @param zFar
10722      *            far clipping plane distance. This value must be greater than zero.
10723      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10724      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10725      * @param dest
10726      *            will hold the result
10727      * @return dest
10728      */
10729     public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, ref Matrix4d dest) {
10730         return perspective(fovy, aspect, zNear, zFar, false, dest);
10731     }
10732 
10733     /**
10734      * Apply a symmetric perspective projection frustum transformation using for a right-handed coordinate system
10735      * using the given NDC z range to this matrix.
10736      * <p>
10737      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10738      * then the new matrix will be <code>M * P</code>. So when transforming a
10739      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10740      * the perspective projection will be applied first!
10741      * <p>
10742      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10743      * use {@link #setPerspective(double, double, double, double, bool) setPerspective}.
10744      * 
10745      * @see #setPerspective(double, double, double, double, bool)
10746      * 
10747      * @param fovy
10748      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
10749      * @param aspect
10750      *            the aspect ratio (i.e. width / height; must be greater than zero)
10751      * @param zNear
10752      *            near clipping plane distance. This value must be greater than zero.
10753      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10754      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10755      * @param zFar
10756      *            far clipping plane distance. This value must be greater than zero.
10757      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10758      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10759      * @param zZeroToOne
10760      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
10761      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
10762      * @return this
10763      */
10764     ref public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne) return {
10765         perspective(fovy, aspect, zNear, zFar, zZeroToOne, this);
10766         return this;
10767     }
10768 
10769     /**
10770      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10771      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
10772      * <p>
10773      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10774      * then the new matrix will be <code>M * P</code>. So when transforming a
10775      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10776      * the perspective projection will be applied first!
10777      * <p>
10778      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10779      * use {@link #setPerspective(double, double, double, double) setPerspective}.
10780      * 
10781      * @see #setPerspective(double, double, double, double)
10782      * 
10783      * @param fovy
10784      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
10785      * @param aspect
10786      *            the aspect ratio (i.e. width / height; must be greater than zero)
10787      * @param zNear
10788      *            near clipping plane distance. This value must be greater than zero.
10789      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10790      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10791      * @param zFar
10792      *            far clipping plane distance. This value must be greater than zero.
10793      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10794      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10795      * @return this
10796      */    
10797     ref public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar) return {
10798         perspective(fovy, aspect, zNear, zFar, this);
10799         return this;
10800     }
10801 
10802     /**
10803      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10804      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
10805      * <p>
10806      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10807      * then the new matrix will be <code>M * P</code>. So when transforming a
10808      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10809      * the perspective projection will be applied first!
10810      * <p>
10811      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10812      * use {@link #setPerspectiveRect(double, double, double, double, bool) setPerspectiveRect}.
10813      * 
10814      * @see #setPerspectiveRect(double, double, double, double, bool)
10815      * 
10816      * @param width
10817      *            the width of the near frustum plane
10818      * @param height
10819      *            the height of the near frustum plane
10820      * @param zNear
10821      *            near clipping plane distance. This value must be greater than zero.
10822      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10823      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10824      * @param zFar
10825      *            far clipping plane distance. This value must be greater than zero.
10826      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10827      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10828      * @param dest
10829      *            will hold the result
10830      * @param zZeroToOne
10831      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
10832      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
10833      * @return dest
10834      */
10835     public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
10836         if ((properties & PROPERTY_IDENTITY) != 0)
10837             return dest.setPerspectiveRect(width, height, zNear, zFar, zZeroToOne);
10838         return perspectiveRectGeneric(width, height, zNear, zFar, zZeroToOne, dest);
10839     }
10840     private Matrix4d perspectiveRectGeneric(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
10841         double rm00 = (zNear + zNear) / width;
10842         double rm11 = (zNear + zNear) / height;
10843         double rm22, rm32;
10844         bool farInf = zFar > 0 && Math.isInfinite(zFar);
10845         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
10846         if (farInf) {
10847             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
10848             double e = 1E-6f;
10849             rm22 = e - 1.0;
10850             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
10851         } else if (nearInf) {
10852             double e = 1E-6f;
10853             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
10854             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
10855         } else {
10856             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar);
10857             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
10858         }
10859         // perform optimized matrix multiplication
10860         double nm20 = m20 * rm22 - m30;
10861         double nm21 = m21 * rm22 - m31;
10862         double nm22 = m22 * rm22 - m32;
10863         double nm23 = m23 * rm22 - m33;
10864         dest._m00(m00 * rm00)
10865         ._m01(m01 * rm00)
10866         ._m02(m02 * rm00)
10867         ._m03(m03 * rm00)
10868         ._m10(m10 * rm11)
10869         ._m11(m11 * rm11)
10870         ._m12(m12 * rm11)
10871         ._m13(m13 * rm11)
10872         ._m30(m20 * rm32)
10873         ._m31(m21 * rm32)
10874         ._m32(m22 * rm32)
10875         ._m33(m23 * rm32)
10876         ._m20(nm20)
10877         ._m21(nm21)
10878         ._m22(nm22)
10879         ._m23(nm23)
10880         ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
10881         return dest;
10882     }
10883 
10884     /**
10885      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10886      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
10887      * <p>
10888      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10889      * then the new matrix will be <code>M * P</code>. So when transforming a
10890      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10891      * the perspective projection will be applied first!
10892      * <p>
10893      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10894      * use {@link #setPerspectiveRect(double, double, double, double) setPerspectiveRect}.
10895      * 
10896      * @see #setPerspectiveRect(double, double, double, double)
10897      * 
10898      * @param width
10899      *            the width of the near frustum plane
10900      * @param height
10901      *            the height of the near frustum plane
10902      * @param zNear
10903      *            near clipping plane distance. This value must be greater than zero.
10904      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10905      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10906      * @param zFar
10907      *            far clipping plane distance. This value must be greater than zero.
10908      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10909      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10910      * @param dest
10911      *            will hold the result
10912      * @return dest
10913      */
10914     public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, ref Matrix4d dest) {
10915         return perspectiveRect(width, height, zNear, zFar, false, dest);
10916     }
10917 
10918     /**
10919      * Apply a symmetric perspective projection frustum transformation using for a right-handed coordinate system
10920      * using the given NDC z range to this matrix.
10921      * <p>
10922      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10923      * then the new matrix will be <code>M * P</code>. So when transforming a
10924      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10925      * the perspective projection will be applied first!
10926      * <p>
10927      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10928      * use {@link #setPerspectiveRect(double, double, double, double, bool) setPerspectiveRect}.
10929      * 
10930      * @see #setPerspectiveRect(double, double, double, double, bool)
10931      * 
10932      * @param width
10933      *            the width of the near frustum plane
10934      * @param height
10935      *            the height of the near frustum plane
10936      * @param zNear
10937      *            near clipping plane distance. This value must be greater than zero.
10938      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10939      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10940      * @param zFar
10941      *            far clipping plane distance. This value must be greater than zero.
10942      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10943      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10944      * @param zZeroToOne
10945      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
10946      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
10947      * @return this
10948      */
10949     ref public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
10950         perspectiveRect(width, height, zNear, zFar, zZeroToOne, this);
10951         return this;
10952     }
10953 
10954     /**
10955      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10956      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
10957      * <p>
10958      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10959      * then the new matrix will be <code>M * P</code>. So when transforming a
10960      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10961      * the perspective projection will be applied first!
10962      * <p>
10963      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10964      * use {@link #setPerspectiveRect(double, double, double, double) setPerspectiveRect}.
10965      * 
10966      * @see #setPerspectiveRect(double, double, double, double)
10967      * 
10968      * @param width
10969      *            the width of the near frustum plane
10970      * @param height
10971      *            the height of the near frustum plane
10972      * @param zNear
10973      *            near clipping plane distance. This value must be greater than zero.
10974      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10975      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10976      * @param zFar
10977      *            far clipping plane distance. This value must be greater than zero.
10978      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10979      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10980      * @return this
10981      */
10982     ref public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar) return {
10983         perspectiveRect(width, height, zNear, zFar, this);
10984         return this;
10985     }
10986 
10987     /**
10988      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
10989      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
10990      * <p>
10991      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
10992      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
10993      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
10994      * is parallel to the XZ-plane.
10995      * <p>
10996      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10997      * then the new matrix will be <code>M * P</code>. So when transforming a
10998      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10999      * the perspective projection will be applied first!
11000      * <p>
11001      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11002      * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double, bool) setPerspectiveOffCenter}.
11003      * 
11004      * @see #setPerspectiveOffCenter(double, double, double, double, double, double, bool)
11005      * 
11006      * @param fovy
11007      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11008      * @param offAngleX
11009      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11010      * @param offAngleY
11011      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11012      * @param aspect
11013      *            the aspect ratio (i.e. width / height; must be greater than zero)
11014      * @param zNear
11015      *            near clipping plane distance. This value must be greater than zero.
11016      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11017      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11018      * @param zFar
11019      *            far clipping plane distance. This value must be greater than zero.
11020      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11021      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11022      * @param dest
11023      *            will hold the result
11024      * @param zZeroToOne
11025      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11026      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11027      * @return dest
11028      */
11029     public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11030         if ((properties & PROPERTY_IDENTITY) != 0)
11031             return dest.setPerspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne);
11032         return perspectiveOffCenterGeneric(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne, dest);
11033     }
11034     private Matrix4d perspectiveOffCenterGeneric(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11035         double h = Math.tan(fovy * 0.5);
11036         // calculate right matrix elements
11037         double xScale = 1.0 / (h * aspect);
11038         double yScale = 1.0 / h;
11039         double rm00 = xScale;
11040         double rm11 = yScale;
11041         double offX = Math.tan(offAngleX), offY = Math.tan(offAngleY);
11042         double rm20 = offX * xScale;
11043         double rm21 = offY * yScale;
11044         double rm22;
11045         double rm32;
11046         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11047         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11048         if (farInf) {
11049             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11050             double e = 1E-6;
11051             rm22 = e - 1.0;
11052             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
11053         } else if (nearInf) {
11054             double e = 1E-6;
11055             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
11056             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
11057         } else {
11058             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar);
11059             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
11060         }
11061         // perform optimized matrix multiplication
11062         double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 - m30;
11063         double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 - m31;
11064         double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 - m32;
11065         double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 - m33;
11066         dest._m00(m00 * rm00)
11067         ._m01(m01 * rm00)
11068         ._m02(m02 * rm00)
11069         ._m03(m03 * rm00)
11070         ._m10(m10 * rm11)
11071         ._m11(m11 * rm11)
11072         ._m12(m12 * rm11)
11073         ._m13(m13 * rm11)
11074         ._m30(m20 * rm32)
11075         ._m31(m21 * rm32)
11076         ._m32(m22 * rm32)
11077         ._m33(m23 * rm32)
11078         ._m20(nm20)
11079         ._m21(nm21)
11080         ._m22(nm22)
11081         ._m23(nm23)
11082         ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION
11083                 | PROPERTY_ORTHONORMAL | (rm20 == 0.0 && rm21 == 0.0 ? 0 : PROPERTY_PERSPECTIVE)));
11084         return dest;
11085     }
11086 
11087     /**
11088      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11089      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
11090      * <p>
11091      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11092      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11093      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11094      * is parallel to the XZ-plane.
11095      * <p>
11096      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11097      * then the new matrix will be <code>M * P</code>. So when transforming a
11098      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11099      * the perspective projection will be applied first!
11100      * <p>
11101      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11102      * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double) setPerspectiveOffCenter}.
11103      * 
11104      * @see #setPerspectiveOffCenter(double, double, double, double, double, double)
11105      * 
11106      * @param fovy
11107      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11108      * @param offAngleX
11109      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11110      * @param offAngleY
11111      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11112      * @param aspect
11113      *            the aspect ratio (i.e. width / height; must be greater than zero)
11114      * @param zNear
11115      *            near clipping plane distance. This value must be greater than zero.
11116      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11117      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11118      * @param zFar
11119      *            far clipping plane distance. This value must be greater than zero.
11120      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11121      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11122      * @param dest
11123      *            will hold the result
11124      * @return dest
11125      */
11126     public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, ref Matrix4d dest) {
11127         return perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, false, dest);
11128     }
11129 
11130     /**
11131      * Apply an asymmetric off-center perspective projection frustum transformation using for a right-handed coordinate system
11132      * using the given NDC z range to this matrix.
11133      * <p>
11134      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11135      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11136      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11137      * is parallel to the XZ-plane.
11138      * <p>
11139      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11140      * then the new matrix will be <code>M * P</code>. So when transforming a
11141      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11142      * the perspective projection will be applied first!
11143      * <p>
11144      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11145      * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double, bool) setPerspectiveOffCenter}.
11146      * 
11147      * @see #setPerspectiveOffCenter(double, double, double, double, double, double, bool)
11148      * 
11149      * @param fovy
11150      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11151      * @param offAngleX
11152      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11153      * @param offAngleY
11154      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11155      * @param aspect
11156      *            the aspect ratio (i.e. width / height; must be greater than zero)
11157      * @param zNear
11158      *            near clipping plane distance. This value must be greater than zero.
11159      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11160      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11161      * @param zFar
11162      *            far clipping plane distance. This value must be greater than zero.
11163      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11164      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11165      * @param zZeroToOne
11166      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11167      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11168      * @return this
11169      */
11170     ref public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, bool zZeroToOne) return {
11171         perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne, this);
11172         return this;
11173     }
11174 
11175     /**
11176      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11177      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
11178      * <p>
11179      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11180      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11181      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11182      * is parallel to the XZ-plane.
11183      * <p>
11184      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11185      * then the new matrix will be <code>M * P</code>. So when transforming a
11186      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11187      * the perspective projection will be applied first!
11188      * <p>
11189      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11190      * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double) setPerspectiveOffCenter}.
11191      * 
11192      * @see #setPerspectiveOffCenter(double, double, double, double, double, double)
11193      * 
11194      * @param fovy
11195      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11196      * @param offAngleX
11197      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11198      * @param offAngleY
11199      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11200      * @param aspect
11201      *            the aspect ratio (i.e. width / height; must be greater than zero)
11202      * @param zNear
11203      *            near clipping plane distance. This value must be greater than zero.
11204      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11205      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11206      * @param zFar
11207      *            far clipping plane distance. This value must be greater than zero.
11208      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11209      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11210      * @return this
11211      */
11212     ref public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar) return {
11213         perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, this);
11214         return this;
11215     }
11216 
11217     /**
11218      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11219      * using the given NDC z range to this matrix.
11220      * <p>
11221      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11222      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11223      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11224      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11225      * <p>
11226      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11227      * then the new matrix will be <code>M * P</code>. So when transforming a
11228      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11229      * the perspective projection will be applied first!
11230      * <p>
11231      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11232      * use {@link #setPerspectiveOffCenterFov(double, double, double, double, double, double, bool) setPerspectiveOffCenterFov}.
11233      * 
11234      * @see #setPerspectiveOffCenterFov(double, double, double, double, double, double, bool)
11235      * 
11236      * @param angleLeft
11237      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11238      *            For a symmetric frustum, this value is negative.
11239      * @param angleRight
11240      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11241      * @param angleDown
11242      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11243      *            For a symmetric frustum, this value is negative.
11244      * @param angleUp
11245      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11246      * @param zNear
11247      *            near clipping plane distance. This value must be greater than zero.
11248      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11249      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11250      * @param zFar
11251      *            far clipping plane distance. This value must be greater than zero.
11252      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11253      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11254      * @param zZeroToOne
11255      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11256      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11257      * @return this
11258      */
11259     ref public Matrix4d perspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne) return {
11260         perspectiveOffCenterFov(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, zZeroToOne, this);
11261         return this;
11262     }
11263     public Matrix4d perspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11264         return frustum(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, zZeroToOne, dest);
11265     }
11266 
11267     /**
11268      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11269      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
11270      * <p>
11271      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11272      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11273      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11274      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11275      * <p>
11276      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11277      * then the new matrix will be <code>M * P</code>. So when transforming a
11278      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11279      * the perspective projection will be applied first!
11280      * <p>
11281      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11282      * use {@link #setPerspectiveOffCenterFov(double, double, double, double, double, double) setPerspectiveOffCenterFov}.
11283      * 
11284      * @see #setPerspectiveOffCenterFov(double, double, double, double, double, double)
11285      * 
11286      * @param angleLeft
11287      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11288      *            For a symmetric frustum, this value is negative.
11289      * @param angleRight
11290      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11291      * @param angleDown
11292      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11293      *            For a symmetric frustum, this value is negative.
11294      * @param angleUp
11295      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11296      * @param zNear
11297      *            near clipping plane distance. This value must be greater than zero.
11298      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11299      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11300      * @param zFar
11301      *            far clipping plane distance. This value must be greater than zero.
11302      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11303      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11304      * @return this
11305      */
11306     ref public Matrix4d perspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar) return {
11307         perspectiveOffCenterFov(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, this);
11308         return this;
11309     }
11310     public Matrix4d perspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, ref Matrix4d dest) {
11311         return frustum(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, dest);
11312     }
11313 
11314     /**
11315      * Apply an asymmetric off-center perspective projection frustum transformation for a left-handed coordinate system
11316      * using the given NDC z range to this matrix.
11317      * <p>
11318      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11319      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11320      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11321      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11322      * <p>
11323      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11324      * then the new matrix will be <code>M * P</code>. So when transforming a
11325      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11326      * the perspective projection will be applied first!
11327      * <p>
11328      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11329      * use {@link #setPerspectiveOffCenterFovLH(double, double, double, double, double, double, bool) setPerspectiveOffCenterFovLH}.
11330      * 
11331      * @see #setPerspectiveOffCenterFovLH(double, double, double, double, double, double, bool)
11332      * 
11333      * @param angleLeft
11334      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11335      *            For a symmetric frustum, this value is negative.
11336      * @param angleRight
11337      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11338      * @param angleDown
11339      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11340      *            For a symmetric frustum, this value is negative.
11341      * @param angleUp
11342      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11343      * @param zNear
11344      *            near clipping plane distance. This value must be greater than zero.
11345      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11346      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11347      * @param zFar
11348      *            far clipping plane distance. This value must be greater than zero.
11349      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11350      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11351      * @param zZeroToOne
11352      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11353      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11354      * @return this
11355      */
11356     ref public Matrix4d perspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne) return {
11357         perspectiveOffCenterFovLH(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, zZeroToOne, this);
11358         return this;
11359     }
11360     public Matrix4d perspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11361         return frustumLH(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, zZeroToOne, dest);
11362     }
11363 
11364     /**
11365      * Apply an asymmetric off-center perspective projection frustum transformation for a left-handed coordinate system
11366      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
11367      * <p>
11368      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11369      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11370      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11371      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11372      * <p>
11373      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11374      * then the new matrix will be <code>M * P</code>. So when transforming a
11375      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11376      * the perspective projection will be applied first!
11377      * <p>
11378      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11379      * use {@link #setPerspectiveOffCenterFovLH(double, double, double, double, double, double) setPerspectiveOffCenterFovLH}.
11380      * 
11381      * @see #setPerspectiveOffCenterFovLH(double, double, double, double, double, double)
11382      * 
11383      * @param angleLeft
11384      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11385      *            For a symmetric frustum, this value is negative.
11386      * @param angleRight
11387      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11388      * @param angleDown
11389      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11390      *            For a symmetric frustum, this value is negative.
11391      * @param angleUp
11392      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11393      * @param zNear
11394      *            near clipping plane distance. This value must be greater than zero.
11395      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11396      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11397      * @param zFar
11398      *            far clipping plane distance. This value must be greater than zero.
11399      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11400      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11401      * @return this
11402      */
11403     ref public Matrix4d perspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar) return {
11404         perspectiveOffCenterFovLH(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, this);
11405         return this;
11406     }
11407     public Matrix4d perspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, ref Matrix4d dest) {
11408         return frustumLH(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, dest);
11409     }
11410 
11411     /**
11412      * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system
11413      * using the given NDC z range.
11414      * <p>
11415      * In order to apply the perspective projection transformation to an existing transformation,
11416      * use {@link #perspective(double, double, double, double, bool) perspective()}.
11417      * 
11418      * @see #perspective(double, double, double, double, bool)
11419      * 
11420      * @param fovy
11421      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11422      * @param aspect
11423      *            the aspect ratio (i.e. width / height; must be greater than zero)
11424      * @param zNear
11425      *            near clipping plane distance. This value must be greater than zero.
11426      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11427      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11428      * @param zFar
11429      *            far clipping plane distance. This value must be greater than zero.
11430      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11431      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11432      * @param zZeroToOne
11433      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11434      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11435      * @return this
11436      */
11437     ref public Matrix4d setPerspective(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne) return {
11438         double h = Math.tan(fovy * 0.5);
11439         _m00(1.0 / (h * aspect)).
11440         _m01(0.0).
11441         _m02(0.0).
11442         _m03(0.0).
11443         _m10(0.0).
11444         _m11(1.0 / h).
11445         _m12(0.0).
11446         _m13(0.0).
11447         _m20(0.0).
11448         _m21(0.0);
11449         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11450         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11451         if (farInf) {
11452             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11453             double e = 1E-6;
11454             _m22(e - 1.0).
11455             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
11456         } else if (nearInf) {
11457             double e = 1E-6;
11458             _m22((zZeroToOne ? 0.0 : 1.0) - e).
11459             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
11460         } else {
11461             _m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)).
11462             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
11463         }
11464         _m23(-1.0).
11465         _m30(0.0).
11466         _m31(0.0).
11467         _m33(0.0).
11468         properties = PROPERTY_PERSPECTIVE;
11469         return this;
11470     }
11471 
11472     /**
11473      * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system
11474      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
11475      * <p>
11476      * In order to apply the perspective projection transformation to an existing transformation,
11477      * use {@link #perspective(double, double, double, double) perspective()}.
11478      * 
11479      * @see #perspective(double, double, double, double)
11480      * 
11481      * @param fovy
11482      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11483      * @param aspect
11484      *            the aspect ratio (i.e. width / height; must be greater than zero)
11485      * @param zNear
11486      *            near clipping plane distance. This value must be greater than zero.
11487      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11488      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11489      * @param zFar
11490      *            far clipping plane distance. This value must be greater than zero.
11491      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11492      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11493      * @return this
11494      */
11495     ref public Matrix4d setPerspective(double fovy, double aspect, double zNear, double zFar) return {
11496         return setPerspective(fovy, aspect, zNear, zFar, false);
11497     }
11498 
11499     /**
11500      * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system
11501      * using the given NDC z range.
11502      * <p>
11503      * In order to apply the perspective projection transformation to an existing transformation,
11504      * use {@link #perspectiveRect(double, double, double, double, bool) perspectiveRect()}.
11505      * 
11506      * @see #perspectiveRect(double, double, double, double, bool)
11507      * 
11508      * @param width
11509      *            the width of the near frustum plane
11510      * @param height
11511      *            the height of the near frustum plane
11512      * @param zNear
11513      *            near clipping plane distance. This value must be greater than zero.
11514      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11515      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11516      * @param zFar
11517      *            far clipping plane distance. This value must be greater than zero.
11518      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11519      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11520      * @param zZeroToOne
11521      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11522      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11523      * @return this
11524      */
11525     ref public Matrix4d setPerspectiveRect(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
11526         this.zero();
11527         this._m00((zNear + zNear) / width);
11528         this._m11((zNear + zNear) / height);
11529         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11530         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11531         if (farInf) {
11532             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11533             double e = 1E-6;
11534             this._m22(e - 1.0);
11535             this._m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
11536         } else if (nearInf) {
11537             double e = 1E-6f;
11538             this._m22((zZeroToOne ? 0.0 : 1.0) - e);
11539             this._m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
11540         } else {
11541             this._m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar));
11542             this._m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
11543         }
11544         this._m23(-1.0);
11545         properties = PROPERTY_PERSPECTIVE;
11546         return this;
11547     }
11548 
11549     /**
11550      * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system
11551      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
11552      * <p>
11553      * In order to apply the perspective projection transformation to an existing transformation,
11554      * use {@link #perspectiveRect(double, double, double, double) perspectiveRect()}.
11555      * 
11556      * @see #perspectiveRect(double, double, double, double)
11557      * 
11558      * @param width
11559      *            the width of the near frustum plane
11560      * @param height
11561      *            the height of the near frustum plane
11562      * @param zNear
11563      *            near clipping plane distance. This value must be greater than zero.
11564      *            If the special value {@link double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11565      *            In that case, <code>zFar</code> may not also be {@link double#POSITIVE_INFINITY}.
11566      * @param zFar
11567      *            far clipping plane distance. This value must be greater than zero.
11568      *            If the special value {@link double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11569      *            In that case, <code>zNear</code> may not also be {@link double#POSITIVE_INFINITY}.
11570      * @return this
11571      */
11572     ref public Matrix4d setPerspectiveRect(double width, double height, double zNear, double zFar) return {
11573         return setPerspectiveRect(width, height, zNear, zFar, false);
11574     }
11575 
11576     /**
11577      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed
11578      * coordinate system using OpenGL's NDC z range of <code>[-1..+1]</code>.
11579      * <p>
11580      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11581      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11582      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11583      * is parallel to the XZ-plane.
11584      * <p>
11585      * In order to apply the perspective projection transformation to an existing transformation,
11586      * use {@link #perspectiveOffCenter(double, double, double, double, double, double) perspectiveOffCenter()}.
11587      * 
11588      * @see #perspectiveOffCenter(double, double, double, double, double, double)
11589      * 
11590      * @param fovy
11591      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11592      * @param offAngleX
11593      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11594      * @param offAngleY
11595      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11596      * @param aspect
11597      *            the aspect ratio (i.e. width / height; must be greater than zero)
11598      * @param zNear
11599      *            near clipping plane distance. This value must be greater than zero.
11600      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11601      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11602      * @param zFar
11603      *            far clipping plane distance. This value must be greater than zero.
11604      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11605      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11606      * @return this
11607      */
11608     ref public Matrix4d setPerspectiveOffCenter(double fovy, double offAngleX, double offAngleY,
11609             double aspect, double zNear, double zFar) return {
11610         return setPerspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, false);
11611     }
11612     /**
11613      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed
11614      * coordinate system using the given NDC z range.
11615      * <p>
11616      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11617      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11618      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11619      * is parallel to the XZ-plane.
11620      * <p>
11621      * In order to apply the perspective projection transformation to an existing transformation,
11622      * use {@link #perspectiveOffCenter(double, double, double, double, double, double) perspectiveOffCenter()}.
11623      * 
11624      * @see #perspectiveOffCenter(double, double, double, double, double, double)
11625      * 
11626      * @param fovy
11627      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11628      * @param offAngleX
11629      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11630      * @param offAngleY
11631      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11632      * @param aspect
11633      *            the aspect ratio (i.e. width / height; must be greater than zero)
11634      * @param zNear
11635      *            near clipping plane distance. This value must be greater than zero.
11636      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11637      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11638      * @param zFar
11639      *            far clipping plane distance. This value must be greater than zero.
11640      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11641      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11642      * @param zZeroToOne
11643      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11644      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11645      * @return this
11646      */
11647     ref public Matrix4d setPerspectiveOffCenter(double fovy, double offAngleX, double offAngleY,
11648                                             double aspect, double zNear, double zFar, bool zZeroToOne) return {
11649         this.zero();
11650         double h = Math.tan(fovy * 0.5);
11651         double xScale = 1.0 / (h * aspect), yScale = 1.0 / h;
11652         _m00(xScale).
11653         _m11(yScale);
11654         double offX = Math.tan(offAngleX), offY = Math.tan(offAngleY);
11655         _m20(offX * xScale).
11656         _m21(offY * yScale);
11657         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11658         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11659         if (farInf) {
11660             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11661             double e = 1E-6;
11662             _m22(e - 1.0).
11663             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
11664         } else if (nearInf) {
11665             double e = 1E-6;
11666             _m22((zZeroToOne ? 0.0 : 1.0) - e).
11667             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
11668         } else {
11669             _m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)).
11670             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
11671         }
11672         _m23(-1.0).
11673         _m30(0.0).
11674         _m31(0.0).
11675         _m33(0.0).
11676         properties = offAngleX == 0.0 && offAngleY == 0.0 ? PROPERTY_PERSPECTIVE : 0;
11677         return this;
11678     }
11679 
11680     /**
11681      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate
11682      * system using OpenGL's NDC z range of <code>[-1..+1]</code>.
11683      * <p>
11684      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11685      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11686      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11687      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11688      * <p>
11689      * In order to apply the perspective projection transformation to an existing transformation,
11690      * use {@link #perspectiveOffCenterFov(double, double, double, double, double, double) perspectiveOffCenterFov()}.
11691      * 
11692      * @see #perspectiveOffCenterFov(double, double, double, double, double, double)
11693      * 
11694      * @param angleLeft
11695      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11696      *            For a symmetric frustum, this value is negative.
11697      * @param angleRight
11698      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11699      * @param angleDown
11700      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11701      *            For a symmetric frustum, this value is negative.
11702      * @param angleUp
11703      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11704      * @param zNear
11705      *            near clipping plane distance. This value must be greater than zero.
11706      *            If the special value {@link double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11707      *            In that case, <code>zFar</code> may not also be {@link double#POSITIVE_INFINITY}.
11708      * @param zFar
11709      *            far clipping plane distance. This value must be greater than zero.
11710      *            If the special value {@link double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11711      *            In that case, <code>zNear</code> may not also be {@link double#POSITIVE_INFINITY}.
11712      * @return this
11713      */
11714     ref public Matrix4d setPerspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar) return {
11715         return setPerspectiveOffCenterFov(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, false);
11716     }
11717     /**
11718      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11719      * using the given NDC z range.
11720      * <p>
11721      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11722      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11723      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11724      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11725      * <p>
11726      * In order to apply the perspective projection transformation to an existing transformation,
11727      * use {@link #perspectiveOffCenterFov(double, double, double, double, double, double, bool) perspectiveOffCenterFov()}.
11728      * 
11729      * @see #perspectiveOffCenterFov(double, double, double, double, double, double, bool)
11730      * 
11731      * @param angleLeft
11732      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11733      *            For a symmetric frustum, this value is negative.
11734      * @param angleRight
11735      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11736      * @param angleDown
11737      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11738      *            For a symmetric frustum, this value is negative.
11739      * @param angleUp
11740      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11741      * @param zNear
11742      *            near clipping plane distance. This value must be greater than zero.
11743      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11744      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11745      * @param zFar
11746      *            far clipping plane distance. This value must be greater than zero.
11747      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11748      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11749      * @param zZeroToOne
11750      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11751      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11752      * @return this
11753      */
11754     ref public Matrix4d setPerspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne) return {
11755         return setFrustum(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, zZeroToOne);
11756     }
11757 
11758     /**
11759      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a left-handed coordinate
11760      * system using OpenGL's NDC z range of <code>[-1..+1]</code>.
11761      * <p>
11762      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11763      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11764      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11765      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11766      * <p>
11767      * In order to apply the perspective projection transformation to an existing transformation,
11768      * use {@link #perspectiveOffCenterFovLH(double, double, double, double, double, double) perspectiveOffCenterFovLH()}.
11769      * 
11770      * @see #perspectiveOffCenterFovLH(double, double, double, double, double, double)
11771      * 
11772      * @param angleLeft
11773      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11774      *            For a symmetric frustum, this value is negative.
11775      * @param angleRight
11776      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11777      * @param angleDown
11778      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11779      *            For a symmetric frustum, this value is negative.
11780      * @param angleUp
11781      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11782      * @param zNear
11783      *            near clipping plane distance. This value must be greater than zero.
11784      *            If the special value {@link double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11785      *            In that case, <code>zFar</code> may not also be {@link double#POSITIVE_INFINITY}.
11786      * @param zFar
11787      *            far clipping plane distance. This value must be greater than zero.
11788      *            If the special value {@link double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11789      *            In that case, <code>zNear</code> may not also be {@link double#POSITIVE_INFINITY}.
11790      * @return this
11791      */
11792     ref public Matrix4d setPerspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar) return {
11793         return setPerspectiveOffCenterFovLH(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, false);
11794     }
11795     /**
11796      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a left-handed coordinate system
11797      * using the given NDC z range.
11798      * <p>
11799      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11800      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11801      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11802      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11803      * <p>
11804      * In order to apply the perspective projection transformation to an existing transformation,
11805      * use {@link #perspectiveOffCenterFovLH(double, double, double, double, double, double, bool) perspectiveOffCenterFovLH()}.
11806      * 
11807      * @see #perspectiveOffCenterFovLH(double, double, double, double, double, double, bool)
11808      * 
11809      * @param angleLeft
11810      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11811      *            For a symmetric frustum, this value is negative.
11812      * @param angleRight
11813      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11814      * @param angleDown
11815      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11816      *            For a symmetric frustum, this value is negative.
11817      * @param angleUp
11818      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11819      * @param zNear
11820      *            near clipping plane distance. This value must be greater than zero.
11821      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11822      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11823      * @param zFar
11824      *            far clipping plane distance. This value must be greater than zero.
11825      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11826      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11827      * @param zZeroToOne
11828      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11829      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11830      * @return this
11831      */
11832     ref public Matrix4d setPerspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne) return {
11833         return setFrustumLH(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, zZeroToOne);
11834     }
11835 
11836     /**
11837      * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system
11838      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
11839      * <p>
11840      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11841      * then the new matrix will be <code>M * P</code>. So when transforming a
11842      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11843      * the perspective projection will be applied first!
11844      * <p>
11845      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11846      * use {@link #setPerspectiveLH(double, double, double, double, bool) setPerspectiveLH}.
11847      * 
11848      * @see #setPerspectiveLH(double, double, double, double, bool)
11849      * 
11850      * @param fovy
11851      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11852      * @param aspect
11853      *            the aspect ratio (i.e. width / height; must be greater than zero)
11854      * @param zNear
11855      *            near clipping plane distance. This value must be greater than zero.
11856      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11857      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11858      * @param zFar
11859      *            far clipping plane distance. This value must be greater than zero.
11860      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11861      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11862      * @param zZeroToOne
11863      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11864      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11865      * @param dest
11866      *            will hold the result
11867      * @return dest
11868      */
11869     public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11870         if ((properties & PROPERTY_IDENTITY) != 0)
11871             return dest.setPerspectiveLH(fovy, aspect, zNear, zFar, zZeroToOne);
11872         return perspectiveLHGeneric(fovy, aspect, zNear, zFar, zZeroToOne, dest);
11873     }
11874     private Matrix4d perspectiveLHGeneric(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11875         double h = Math.tan(fovy * 0.5);
11876         // calculate right matrix elements
11877         double rm00 = 1.0 / (h * aspect);
11878         double rm11 = 1.0 / h;
11879         double rm22;
11880         double rm32;
11881         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11882         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11883         if (farInf) {
11884             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11885             double e = 1E-6;
11886             rm22 = 1.0 - e;
11887             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
11888         } else if (nearInf) {
11889             double e = 1E-6;
11890             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
11891             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
11892         } else {
11893             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear);
11894             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
11895         }
11896         // perform optimized matrix multiplication
11897         double nm20 = m20 * rm22 + m30;
11898         double nm21 = m21 * rm22 + m31;
11899         double nm22 = m22 * rm22 + m32;
11900         double nm23 = m23 * rm22 + m33;
11901         dest._m00(m00 * rm00)
11902         ._m01(m01 * rm00)
11903         ._m02(m02 * rm00)
11904         ._m03(m03 * rm00)
11905         ._m10(m10 * rm11)
11906         ._m11(m11 * rm11)
11907         ._m12(m12 * rm11)
11908         ._m13(m13 * rm11)
11909         ._m30(m20 * rm32)
11910         ._m31(m21 * rm32)
11911         ._m32(m22 * rm32)
11912         ._m33(m23 * rm32)
11913         ._m20(nm20)
11914         ._m21(nm21)
11915         ._m22(nm22)
11916         ._m23(nm23)
11917         ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
11918         return dest;
11919     }
11920 
11921     /**
11922      * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system
11923      * using the given NDC z range to this matrix.
11924      * <p>
11925      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11926      * then the new matrix will be <code>M * P</code>. So when transforming a
11927      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11928      * the perspective projection will be applied first!
11929      * <p>
11930      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11931      * use {@link #setPerspectiveLH(double, double, double, double, bool) setPerspectiveLH}.
11932      * 
11933      * @see #setPerspectiveLH(double, double, double, double, bool)
11934      * 
11935      * @param fovy
11936      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11937      * @param aspect
11938      *            the aspect ratio (i.e. width / height; must be greater than zero)
11939      * @param zNear
11940      *            near clipping plane distance. This value must be greater than zero.
11941      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11942      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11943      * @param zFar
11944      *            far clipping plane distance. This value must be greater than zero.
11945      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11946      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11947      * @param zZeroToOne
11948      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11949      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11950      * @return this
11951      */
11952     ref public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne) return {
11953         perspectiveLH(fovy, aspect, zNear, zFar, zZeroToOne, this);
11954         return this;
11955     }
11956 
11957     /**
11958      * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system
11959      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
11960      * <p>
11961      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11962      * then the new matrix will be <code>M * P</code>. So when transforming a
11963      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11964      * the perspective projection will be applied first!
11965      * <p>
11966      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11967      * use {@link #setPerspectiveLH(double, double, double, double) setPerspectiveLH}.
11968      * 
11969      * @see #setPerspectiveLH(double, double, double, double)
11970      * 
11971      * @param fovy
11972      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11973      * @param aspect
11974      *            the aspect ratio (i.e. width / height; must be greater than zero)
11975      * @param zNear
11976      *            near clipping plane distance. This value must be greater than zero.
11977      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11978      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11979      * @param zFar
11980      *            far clipping plane distance. This value must be greater than zero.
11981      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11982      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11983      * @param dest
11984      *            will hold the result
11985      * @return dest
11986      */
11987     public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, ref Matrix4d dest) {
11988         return perspectiveLH(fovy, aspect, zNear, zFar, false, dest);
11989     }
11990 
11991     /**
11992      * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system
11993      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
11994      * <p>
11995      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11996      * then the new matrix will be <code>M * P</code>. So when transforming a
11997      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11998      * the perspective projection will be applied first!
11999      * <p>
12000      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12001      * use {@link #setPerspectiveLH(double, double, double, double) setPerspectiveLH}.
12002      * 
12003      * @see #setPerspectiveLH(double, double, double, double)
12004      * 
12005      * @param fovy
12006      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
12007      * @param aspect
12008      *            the aspect ratio (i.e. width / height; must be greater than zero)
12009      * @param zNear
12010      *            near clipping plane distance. This value must be greater than zero.
12011      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12012      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12013      * @param zFar
12014      *            far clipping plane distance. This value must be greater than zero.
12015      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12016      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12017      * @return this
12018      */
12019     ref public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar) return {
12020         perspectiveLH(fovy, aspect, zNear, zFar, this);
12021         return this;
12022     }
12023 
12024     /**
12025      * Set this matrix to be a symmetric perspective projection frustum transformation for a left-handed coordinate system
12026      * using the given NDC z range of <code>[-1..+1]</code>.
12027      * <p>
12028      * In order to apply the perspective projection transformation to an existing transformation,
12029      * use {@link #perspectiveLH(double, double, double, double, bool) perspectiveLH()}.
12030      * 
12031      * @see #perspectiveLH(double, double, double, double, bool)
12032      * 
12033      * @param fovy
12034      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
12035      * @param aspect
12036      *            the aspect ratio (i.e. width / height; must be greater than zero)
12037      * @param zNear
12038      *            near clipping plane distance. This value must be greater than zero.
12039      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12040      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12041      * @param zFar
12042      *            far clipping plane distance. This value must be greater than zero.
12043      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12044      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12045      * @param zZeroToOne
12046      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12047      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12048      * @return this
12049      */
12050     ref public Matrix4d setPerspectiveLH(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne) return {
12051         double h = Math.tan(fovy * 0.5);
12052         _m00(1.0 / (h * aspect)).
12053         _m01(0.0).
12054         _m02(0.0).
12055         _m03(0.0).
12056         _m10(0.0).
12057         _m11(1.0 / h).
12058         _m12(0.0).
12059         _m13(0.0).
12060         _m20(0.0).
12061         _m21(0.0);
12062         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12063         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12064         if (farInf) {
12065             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12066             double e = 1E-6;
12067             _m22(1.0 - e).
12068             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
12069         } else if (nearInf) {
12070             double e = 1E-6;
12071             _m22((zZeroToOne ? 0.0 : 1.0) - e).
12072             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
12073         } else {
12074             _m22((zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear)).
12075             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
12076         }
12077         _m23(1.0).
12078         _m30(0.0).
12079         _m31(0.0).
12080         _m33(0.0).
12081         properties = PROPERTY_PERSPECTIVE;
12082         return this;
12083     }
12084 
12085     /**
12086      * Set this matrix to be a symmetric perspective projection frustum transformation for a left-handed coordinate system
12087      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
12088      * <p>
12089      * In order to apply the perspective projection transformation to an existing transformation,
12090      * use {@link #perspectiveLH(double, double, double, double) perspectiveLH()}.
12091      * 
12092      * @see #perspectiveLH(double, double, double, double)
12093      * 
12094      * @param fovy
12095      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
12096      * @param aspect
12097      *            the aspect ratio (i.e. width / height; must be greater than zero)
12098      * @param zNear
12099      *            near clipping plane distance. This value must be greater than zero.
12100      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12101      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12102      * @param zFar
12103      *            far clipping plane distance. This value must be greater than zero.
12104      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12105      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12106      * @return this
12107      */
12108     ref public Matrix4d setPerspectiveLH(double fovy, double aspect, double zNear, double zFar) return {
12109         return setPerspectiveLH(fovy, aspect, zNear, zFar, false);
12110     }
12111 
12112     /**
12113      * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12114      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
12115      * <p>
12116      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12117      * then the new matrix will be <code>M * F</code>. So when transforming a
12118      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12119      * the frustum transformation will be applied first!
12120      * <p>
12121      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12122      * use {@link #setFrustum(double, double, double, double, double, double, bool) setFrustum()}.
12123      * <p>
12124      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12125      * 
12126      * @see #setFrustum(double, double, double, double, double, double, bool)
12127      * 
12128      * @param left
12129      *            the distance along the x-axis to the left frustum edge
12130      * @param right
12131      *            the distance along the x-axis to the right frustum edge
12132      * @param bottom
12133      *            the distance along the y-axis to the bottom frustum edge
12134      * @param top
12135      *            the distance along the y-axis to the top frustum edge
12136      * @param zNear
12137      *            near clipping plane distance. This value must be greater than zero.
12138      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12139      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12140      * @param zFar
12141      *            far clipping plane distance. This value must be greater than zero.
12142      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12143      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12144      * @param zZeroToOne
12145      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12146      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12147      * @param dest
12148      *            will hold the result
12149      * @return dest
12150      */
12151     public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
12152         if ((properties & PROPERTY_IDENTITY) != 0)
12153             return dest.setFrustum(left, right, bottom, top, zNear, zFar, zZeroToOne);
12154         return frustumGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest);
12155     }
12156     private Matrix4d frustumGeneric(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
12157         // calculate right matrix elements
12158         double rm00 = (zNear + zNear) / (right - left);
12159         double rm11 = (zNear + zNear) / (top - bottom);
12160         double rm20 = (right + left) / (right - left);
12161         double rm21 = (top + bottom) / (top - bottom);
12162         double rm22;
12163         double rm32;
12164         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12165         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12166         if (farInf) {
12167             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12168             double e = 1E-6;
12169             rm22 = e - 1.0;
12170             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
12171         } else if (nearInf) {
12172             double e = 1E-6;
12173             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
12174             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
12175         } else {
12176             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar);
12177             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
12178         }
12179         // perform optimized matrix multiplication
12180         double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 - m30;
12181         double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 - m31;
12182         double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 - m32;
12183         double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 - m33;
12184         dest._m00(m00 * rm00)
12185         ._m01(m01 * rm00)
12186         ._m02(m02 * rm00)
12187         ._m03(m03 * rm00)
12188         ._m10(m10 * rm11)
12189         ._m11(m11 * rm11)
12190         ._m12(m12 * rm11)
12191         ._m13(m13 * rm11)
12192         ._m30(m20 * rm32)
12193         ._m31(m21 * rm32)
12194         ._m32(m22 * rm32)
12195         ._m33(m23 * rm32)
12196         ._m20(nm20)
12197         ._m21(nm21)
12198         ._m22(nm22)
12199         ._m23(nm23)
12200         ._properties(0);
12201         return dest;
12202     }
12203 
12204     /**
12205      * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12206      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
12207      * <p>
12208      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12209      * then the new matrix will be <code>M * F</code>. So when transforming a
12210      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12211      * the frustum transformation will be applied first!
12212      * <p>
12213      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12214      * use {@link #setFrustum(double, double, double, double, double, double) setFrustum()}.
12215      * <p>
12216      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12217      * 
12218      * @see #setFrustum(double, double, double, double, double, double)
12219      * 
12220      * @param left
12221      *            the distance along the x-axis to the left frustum edge
12222      * @param right
12223      *            the distance along the x-axis to the right frustum edge
12224      * @param bottom
12225      *            the distance along the y-axis to the bottom frustum edge
12226      * @param top
12227      *            the distance along the y-axis to the top frustum edge
12228      * @param zNear
12229      *            near clipping plane distance. This value must be greater than zero.
12230      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12231      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12232      * @param zFar
12233      *            far clipping plane distance. This value must be greater than zero.
12234      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12235      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12236      * @param dest
12237      *            will hold the result
12238      * @return dest
12239      */
12240     public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, ref Matrix4d dest) {
12241         return frustum(left, right, bottom, top, zNear, zFar, false, dest);
12242     }
12243 
12244     /**
12245      * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12246      * using the given NDC z range to this matrix.
12247      * <p>
12248      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12249      * then the new matrix will be <code>M * F</code>. So when transforming a
12250      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12251      * the frustum transformation will be applied first!
12252      * <p>
12253      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12254      * use {@link #setFrustum(double, double, double, double, double, double, bool) setFrustum()}.
12255      * <p>
12256      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12257      * 
12258      * @see #setFrustum(double, double, double, double, double, double, bool)
12259      * 
12260      * @param left
12261      *            the distance along the x-axis to the left frustum edge
12262      * @param right
12263      *            the distance along the x-axis to the right frustum edge
12264      * @param bottom
12265      *            the distance along the y-axis to the bottom frustum edge
12266      * @param top
12267      *            the distance along the y-axis to the top frustum edge
12268      * @param zNear
12269      *            near clipping plane distance. This value must be greater than zero.
12270      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12271      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12272      * @param zFar
12273      *            far clipping plane distance. This value must be greater than zero.
12274      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12275      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12276      * @param zZeroToOne
12277      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12278      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12279      * @return this
12280      */
12281     ref public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
12282         frustum(left, right, bottom, top, zNear, zFar, zZeroToOne, this);
12283         return this;
12284     }
12285 
12286     /**
12287      * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12288      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
12289      * <p>
12290      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12291      * then the new matrix will be <code>M * F</code>. So when transforming a
12292      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12293      * the frustum transformation will be applied first!
12294      * <p>
12295      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12296      * use {@link #setFrustum(double, double, double, double, double, double) setFrustum()}.
12297      * <p>
12298      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12299      * 
12300      * @see #setFrustum(double, double, double, double, double, double)
12301      * 
12302      * @param left
12303      *            the distance along the x-axis to the left frustum edge
12304      * @param right
12305      *            the distance along the x-axis to the right frustum edge
12306      * @param bottom
12307      *            the distance along the y-axis to the bottom frustum edge
12308      * @param top
12309      *            the distance along the y-axis to the top frustum edge
12310      * @param zNear
12311      *            near clipping plane distance. This value must be greater than zero.
12312      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12313      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12314      * @param zFar
12315      *            far clipping plane distance. This value must be greater than zero.
12316      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12317      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12318      * @return this
12319      */
12320     ref public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar) return {
12321         frustum(left, right, bottom, top, zNear, zFar, this);
12322         return this;
12323     }
12324 
12325     /**
12326      * Set this matrix to be an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12327      * using the given NDC z range.
12328      * <p>
12329      * In order to apply the perspective frustum transformation to an existing transformation,
12330      * use {@link #frustum(double, double, double, double, double, double, bool) frustum()}.
12331      * <p>
12332      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12333      * 
12334      * @see #frustum(double, double, double, double, double, double, bool)
12335      * 
12336      * @param left
12337      *            the distance along the x-axis to the left frustum edge
12338      * @param right
12339      *            the distance along the x-axis to the right frustum edge
12340      * @param bottom
12341      *            the distance along the y-axis to the bottom frustum edge
12342      * @param top
12343      *            the distance along the y-axis to the top frustum edge
12344      * @param zNear
12345      *            near clipping plane distance. This value must be greater than zero.
12346      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12347      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12348      * @param zFar
12349      *            far clipping plane distance. This value must be greater than zero.
12350      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12351      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12352      * @param zZeroToOne
12353      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12354      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12355      * @return this
12356      */
12357     ref public Matrix4d setFrustum(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
12358         if ((properties & PROPERTY_IDENTITY) == 0)
12359             _identity();
12360         _m00((zNear + zNear) / (right - left)).
12361         _m11((zNear + zNear) / (top - bottom)).
12362         _m20((right + left) / (right - left)).
12363         _m21((top + bottom) / (top - bottom));
12364         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12365         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12366         if (farInf) {
12367             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12368             double e = 1E-6;
12369             _m22(e - 1.0).
12370             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
12371         } else if (nearInf) {
12372             double e = 1E-6;
12373             _m22((zZeroToOne ? 0.0 : 1.0) - e).
12374             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
12375         } else {
12376             _m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)).
12377             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
12378         }
12379         _m23(-1.0).
12380         _m33(0.0).
12381         properties = this.m20 == 0.0 && this.m21 == 0.0 ? PROPERTY_PERSPECTIVE : 0;
12382         return this;
12383     }
12384 
12385     /**
12386      * Set this matrix to be an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12387      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
12388      * <p>
12389      * In order to apply the perspective frustum transformation to an existing transformation,
12390      * use {@link #frustum(double, double, double, double, double, double) frustum()}.
12391      * <p>
12392      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12393      * 
12394      * @see #frustum(double, double, double, double, double, double)
12395      * 
12396      * @param left
12397      *            the distance along the x-axis to the left frustum edge
12398      * @param right
12399      *            the distance along the x-axis to the right frustum edge
12400      * @param bottom
12401      *            the distance along the y-axis to the bottom frustum edge
12402      * @param top
12403      *            the distance along the y-axis to the top frustum edge
12404      * @param zNear
12405      *            near clipping plane distance. This value must be greater than zero.
12406      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12407      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12408      * @param zFar
12409      *            far clipping plane distance. This value must be greater than zero.
12410      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12411      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12412      * @return this
12413      */
12414     ref public Matrix4d setFrustum(double left, double right, double bottom, double top, double zNear, double zFar) return {
12415         return setFrustum(left, right, bottom, top, zNear, zFar, false);
12416     }
12417 
12418     /**
12419      * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12420      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
12421      * <p>
12422      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12423      * then the new matrix will be <code>M * F</code>. So when transforming a
12424      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12425      * the frustum transformation will be applied first!
12426      * <p>
12427      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12428      * use {@link #setFrustumLH(double, double, double, double, double, double, bool) setFrustumLH()}.
12429      * <p>
12430      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12431      * 
12432      * @see #setFrustumLH(double, double, double, double, double, double, bool)
12433      * 
12434      * @param left
12435      *            the distance along the x-axis to the left frustum edge
12436      * @param right
12437      *            the distance along the x-axis to the right frustum edge
12438      * @param bottom
12439      *            the distance along the y-axis to the bottom frustum edge
12440      * @param top
12441      *            the distance along the y-axis to the top frustum edge
12442      * @param zNear
12443      *            near clipping plane distance. This value must be greater than zero.
12444      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12445      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12446      * @param zFar
12447      *            far clipping plane distance. This value must be greater than zero.
12448      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12449      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12450      * @param zZeroToOne
12451      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12452      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12453      * @param dest
12454      *            will hold the result
12455      * @return dest
12456      */
12457     public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
12458         if ((properties & PROPERTY_IDENTITY) != 0)
12459             return dest.setFrustumLH(left, right, bottom, top, zNear, zFar, zZeroToOne);
12460         return frustumLHGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest);
12461     }
12462     private Matrix4d frustumLHGeneric(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
12463         // calculate right matrix elements
12464         double rm00 = (zNear + zNear) / (right - left);
12465         double rm11 = (zNear + zNear) / (top - bottom);
12466         double rm20 = (right + left) / (right - left);
12467         double rm21 = (top + bottom) / (top - bottom);
12468         double rm22;
12469         double rm32;
12470         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12471         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12472         if (farInf) {
12473             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12474             double e = 1E-6;
12475             rm22 = 1.0 - e;
12476             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
12477         } else if (nearInf) {
12478             double e = 1E-6;
12479             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
12480             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
12481         } else {
12482             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear);
12483             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
12484         }
12485         // perform optimized matrix multiplication
12486         double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 + m30;
12487         double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 + m31;
12488         double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 + m32;
12489         double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 + m33;
12490         dest._m00(m00 * rm00)
12491         ._m01(m01 * rm00)
12492         ._m02(m02 * rm00)
12493         ._m03(m03 * rm00)
12494         ._m10(m10 * rm11)
12495         ._m11(m11 * rm11)
12496         ._m12(m12 * rm11)
12497         ._m13(m13 * rm11)
12498         ._m30(m20 * rm32)
12499         ._m31(m21 * rm32)
12500         ._m32(m22 * rm32)
12501         ._m33(m23 * rm32)
12502         ._m20(nm20)
12503         ._m21(nm21)
12504         ._m22(nm22)
12505         ._m23(nm23)
12506         ._properties(0);
12507         return dest;
12508     }
12509 
12510     /**
12511      * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12512      * using the given NDC z range to this matrix.
12513      * <p>
12514      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12515      * then the new matrix will be <code>M * F</code>. So when transforming a
12516      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12517      * the frustum transformation will be applied first!
12518      * <p>
12519      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12520      * use {@link #setFrustumLH(double, double, double, double, double, double, bool) setFrustumLH()}.
12521      * <p>
12522      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12523      * 
12524      * @see #setFrustumLH(double, double, double, double, double, double, bool)
12525      * 
12526      * @param left
12527      *            the distance along the x-axis to the left frustum edge
12528      * @param right
12529      *            the distance along the x-axis to the right frustum edge
12530      * @param bottom
12531      *            the distance along the y-axis to the bottom frustum edge
12532      * @param top
12533      *            the distance along the y-axis to the top frustum edge
12534      * @param zNear
12535      *            near clipping plane distance. This value must be greater than zero.
12536      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12537      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12538      * @param zFar
12539      *            far clipping plane distance. This value must be greater than zero.
12540      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12541      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12542      * @param zZeroToOne
12543      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12544      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12545      * @return this
12546      */
12547     ref public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
12548         frustumLH(left, right, bottom, top, zNear, zFar, zZeroToOne, this);
12549         return this;
12550     }
12551 
12552     /**
12553      * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12554      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
12555      * <p>
12556      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12557      * then the new matrix will be <code>M * F</code>. So when transforming a
12558      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12559      * the frustum transformation will be applied first!
12560      * <p>
12561      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12562      * use {@link #setFrustumLH(double, double, double, double, double, double) setFrustumLH()}.
12563      * <p>
12564      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12565      * 
12566      * @see #setFrustumLH(double, double, double, double, double, double)
12567      * 
12568      * @param left
12569      *            the distance along the x-axis to the left frustum edge
12570      * @param right
12571      *            the distance along the x-axis to the right frustum edge
12572      * @param bottom
12573      *            the distance along the y-axis to the bottom frustum edge
12574      * @param top
12575      *            the distance along the y-axis to the top frustum edge
12576      * @param zNear
12577      *            near clipping plane distance. This value must be greater than zero.
12578      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12579      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12580      * @param zFar
12581      *            far clipping plane distance. This value must be greater than zero.
12582      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12583      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12584      * @param dest
12585      *            will hold the result
12586      * @return dest
12587      */
12588     public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, ref Matrix4d dest) {
12589         return frustumLH(left, right, bottom, top, zNear, zFar, false, dest);
12590     }
12591 
12592     /**
12593      * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12594      * using the given NDC z range to this matrix.
12595      * <p>
12596      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12597      * then the new matrix will be <code>M * F</code>. So when transforming a
12598      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12599      * the frustum transformation will be applied first!
12600      * <p>
12601      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12602      * use {@link #setFrustumLH(double, double, double, double, double, double) setFrustumLH()}.
12603      * <p>
12604      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12605      * 
12606      * @see #setFrustumLH(double, double, double, double, double, double)
12607      * 
12608      * @param left
12609      *            the distance along the x-axis to the left frustum edge
12610      * @param right
12611      *            the distance along the x-axis to the right frustum edge
12612      * @param bottom
12613      *            the distance along the y-axis to the bottom frustum edge
12614      * @param top
12615      *            the distance along the y-axis to the top frustum edge
12616      * @param zNear
12617      *            near clipping plane distance. This value must be greater than zero.
12618      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12619      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12620      * @param zFar
12621      *            far clipping plane distance. This value must be greater than zero.
12622      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12623      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12624      * @return this
12625      */
12626     ref public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar) return {
12627         frustumLH(left, right, bottom, top, zNear, zFar, this);
12628         return this;
12629     }
12630 
12631     /**
12632      * Set this matrix to be an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12633      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
12634      * <p>
12635      * In order to apply the perspective frustum transformation to an existing transformation,
12636      * use {@link #frustumLH(double, double, double, double, double, double, bool) frustumLH()}.
12637      * <p>
12638      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12639      * 
12640      * @see #frustumLH(double, double, double, double, double, double, bool)
12641      * 
12642      * @param left
12643      *            the distance along the x-axis to the left frustum edge
12644      * @param right
12645      *            the distance along the x-axis to the right frustum edge
12646      * @param bottom
12647      *            the distance along the y-axis to the bottom frustum edge
12648      * @param top
12649      *            the distance along the y-axis to the top frustum edge
12650      * @param zNear
12651      *            near clipping plane distance. This value must be greater than zero.
12652      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12653      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12654      * @param zFar
12655      *            far clipping plane distance. This value must be greater than zero.
12656      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12657      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12658      * @param zZeroToOne
12659      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12660      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12661      * @return this
12662      */
12663     ref public Matrix4d setFrustumLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
12664         if ((properties & PROPERTY_IDENTITY) == 0)
12665             _identity();
12666         _m00((zNear + zNear) / (right - left)).
12667         _m11((zNear + zNear) / (top - bottom)).
12668         _m20((right + left) / (right - left)).
12669         _m21((top + bottom) / (top - bottom));
12670         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12671         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12672         if (farInf) {
12673             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12674             double e = 1E-6;
12675             _m22(1.0 - e).
12676             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
12677         } else if (nearInf) {
12678             double e = 1E-6;
12679             _m22((zZeroToOne ? 0.0 : 1.0) - e).
12680             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
12681         } else {
12682             _m22((zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear)).
12683             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
12684         }
12685         _m23(1.0).
12686         _m33(0.0).
12687         properties = this.m20 == 0.0 && this.m21 == 0.0 ? PROPERTY_PERSPECTIVE : 0;
12688         return this;
12689     }
12690 
12691     /**
12692      * Set this matrix to be an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12693      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
12694      * <p>
12695      * In order to apply the perspective frustum transformation to an existing transformation,
12696      * use {@link #frustumLH(double, double, double, double, double, double) frustumLH()}.
12697      * <p>
12698      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12699      * 
12700      * @see #frustumLH(double, double, double, double, double, double)
12701      * 
12702      * @param left
12703      *            the distance along the x-axis to the left frustum edge
12704      * @param right
12705      *            the distance along the x-axis to the right frustum edge
12706      * @param bottom
12707      *            the distance along the y-axis to the bottom frustum edge
12708      * @param top
12709      *            the distance along the y-axis to the top frustum edge
12710      * @param zNear
12711      *            near clipping plane distance. This value must be greater than zero.
12712      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12713      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12714      * @param zFar
12715      *            far clipping plane distance. This value must be greater than zero.
12716      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12717      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12718      * @return this
12719      */
12720     ref public Matrix4d setFrustumLH(double left, double right, double bottom, double top, double zNear, double zFar) return {
12721         return setFrustumLH(left, right, bottom, top, zNear, zFar, false);
12722     }
12723 
12724     /**
12725      * Set this matrix to represent a perspective projection equivalent to the given intrinsic camera calibration parameters.
12726      * The resulting matrix will be suited for a right-handed coordinate system using OpenGL's NDC z range of <code>[-1..+1]</code>.
12727      * <p>
12728      * See: <a href="https://en.wikipedia.org/wiki/Camera_resectioning#Intrinsic_parameters">https://en.wikipedia.org/</a>
12729      * <p>
12730      * Reference: <a href="http://ksimek.github.io/2013/06/03/calibrated_cameras_in_opengl/">http://ksimek.github.io/</a>
12731      * 
12732      * @param alphaX
12733      *          specifies the focal length and scale along the X axis
12734      * @param alphaY
12735      *          specifies the focal length and scale along the Y axis
12736      * @param gamma
12737      *          the skew coefficient between the X and Y axis (may be <code>0</code>)
12738      * @param u0
12739      *          the X coordinate of the principal point in image/sensor units
12740      * @param v0
12741      *          the Y coordinate of the principal point in image/sensor units
12742      * @param imgWidth
12743      *          the width of the sensor/image image/sensor units
12744      * @param imgHeight
12745      *          the height of the sensor/image image/sensor units
12746      * @param near
12747      *          the distance to the near plane
12748      * @param far
12749      *          the distance to the far plane
12750      * @return this
12751      */
12752     ref public Matrix4d setFromIntrinsic(double alphaX, double alphaY, double gamma, double u0, double v0, int imgWidth, int imgHeight, double near, double far) return {
12753         double l00 = 2.0 / imgWidth;
12754         double l11 = 2.0 / imgHeight;
12755         double l22 = 2.0 / (near - far);
12756         setm00(l00 * alphaX);
12757         setm01(0.0);
12758         setm02(0.0);
12759         setm03(0.0);
12760         setm10(l00 * gamma);
12761         setm11(l11 * alphaY);
12762         setm12(0.0);
12763         setm13(0.0);
12764         setm20(l00 * u0 - 1.0);
12765         setm21(l11 * v0 - 1.0);
12766         setm22(l22 * -(near + far) + (far + near) / (near - far));
12767         setm23(-1.0);
12768         setm30(0.0);
12769         setm31(0.0);
12770         setm32(l22 * -near * far);
12771         setm33(0.0);
12772         this.properties = PROPERTY_PERSPECTIVE;
12773         return this;
12774     }
12775 
12776     public Vector4d frustumPlane(int plane, ref Vector4d dest) {
12777         switch (plane) {
12778         case PLANE_NX:
12779             dest.set(m03 + m00, m13 + m10, m23 + m20, m33 + m30).normalize3();
12780             break;
12781         case PLANE_PX:
12782             dest.set(m03 - m00, m13 - m10, m23 - m20, m33 - m30).normalize3();
12783             break;
12784         case PLANE_NY:
12785             dest.set(m03 + m01, m13 + m11, m23 + m21, m33 + m31).normalize3();
12786             break;
12787         case PLANE_PY:
12788             dest.set(m03 - m01, m13 - m11, m23 - m21, m33 - m31).normalize3();
12789             break;
12790         case PLANE_NZ:
12791             dest.set(m03 + m02, m13 + m12, m23 + m22, m33 + m32).normalize3();
12792             break;
12793         case PLANE_PZ:
12794             dest.set(m03 - m02, m13 - m12, m23 - m22, m33 - m32).normalize3();
12795             break;
12796         default:
12797             // do nothing
12798         }
12799         return dest;
12800     }
12801 
12802     public Vector3d frustumCorner(int corner, ref Vector3d dest) {
12803         double d1, d2, d3;
12804         double n1x, n1y, n1z, n2x, n2y, n2z, n3x, n3y, n3z;
12805         switch (corner) {
12806         case CORNER_NXNYNZ: // left, bottom, near
12807             n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12808             n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom
12809             n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near
12810             break;
12811         case CORNER_PXNYNZ: // right, bottom, near
12812             n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right
12813             n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom
12814             n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near
12815             break;
12816         case CORNER_PXPYNZ: // right, top, near
12817             n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right
12818             n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top
12819             n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near
12820             break;
12821         case CORNER_NXPYNZ: // left, top, near
12822             n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12823             n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top
12824             n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near
12825             break;
12826         case CORNER_PXNYPZ: // right, bottom, far
12827             n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right
12828             n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom
12829             n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far
12830             break;
12831         case CORNER_NXNYPZ: // left, bottom, far
12832             n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12833             n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom
12834             n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far
12835             break;
12836         case CORNER_NXPYPZ: // left, top, far
12837             n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12838             n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top
12839             n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far
12840             break;
12841         case CORNER_PXPYPZ: // right, top, far
12842             n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right
12843             n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top
12844             n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far
12845             break;
12846         default:
12847             // do nothing
12848         }
12849         double c23x, c23y, c23z;
12850         c23x = n2y * n3z - n2z * n3y;
12851         c23y = n2z * n3x - n2x * n3z;
12852         c23z = n2x * n3y - n2y * n3x;
12853         double c31x, c31y, c31z;
12854         c31x = n3y * n1z - n3z * n1y;
12855         c31y = n3z * n1x - n3x * n1z;
12856         c31z = n3x * n1y - n3y * n1x;
12857         double c12x, c12y, c12z;
12858         c12x = n1y * n2z - n1z * n2y;
12859         c12y = n1z * n2x - n1x * n2z;
12860         c12z = n1x * n2y - n1y * n2x;
12861         double invDot = 1.0 / (n1x * c23x + n1y * c23y + n1z * c23z);
12862         dest.x = (-c23x * d1 - c31x * d2 - c12x * d3) * invDot;
12863         dest.y = (-c23y * d1 - c31y * d2 - c12y * d3) * invDot;
12864         dest.z = (-c23z * d1 - c31z * d2 - c12z * d3) * invDot;
12865         return dest;
12866     }
12867 
12868     public Vector3d perspectiveOrigin(ref Vector3d dest) {
12869         /*
12870          * Simply compute the intersection point of the left, right and top frustum plane.
12871          */
12872         double d1, d2, d3;
12873         double n1x, n1y, n1z, n2x, n2y, n2z, n3x, n3y, n3z;
12874         n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12875         n2x = m03 - m00; n2y = m13 - m10; n2z = m23 - m20; d2 = m33 - m30; // right
12876         n3x = m03 - m01; n3y = m13 - m11; n3z = m23 - m21; d3 = m33 - m31; // top
12877         double c23x, c23y, c23z;
12878         c23x = n2y * n3z - n2z * n3y;
12879         c23y = n2z * n3x - n2x * n3z;
12880         c23z = n2x * n3y - n2y * n3x;
12881         double c31x, c31y, c31z;
12882         c31x = n3y * n1z - n3z * n1y;
12883         c31y = n3z * n1x - n3x * n1z;
12884         c31z = n3x * n1y - n3y * n1x;
12885         double c12x, c12y, c12z;
12886         c12x = n1y * n2z - n1z * n2y;
12887         c12y = n1z * n2x - n1x * n2z;
12888         c12z = n1x * n2y - n1y * n2x;
12889         double invDot = 1.0 / (n1x * c23x + n1y * c23y + n1z * c23z);
12890         dest.x = (-c23x * d1 - c31x * d2 - c12x * d3) * invDot;
12891         dest.y = (-c23y * d1 - c31y * d2 - c12y * d3) * invDot;
12892         dest.z = (-c23z * d1 - c31z * d2 - c12z * d3) * invDot;
12893         return dest;
12894     }
12895 
12896     public Vector3d perspectiveInvOrigin(ref Vector3d dest) {
12897         double invW = 1.0 / m23;
12898         dest.x = m20 * invW;
12899         dest.y = m21 * invW;
12900         dest.z = m22 * invW;
12901         return dest;
12902     }
12903 
12904     public double perspectiveFov() {
12905         /*
12906          * Compute the angle between the bottom and top frustum plane normals.
12907          */
12908         double n1x, n1y, n1z, n2x, n2y, n2z;
12909         n1x = m03 + m01; n1y = m13 + m11; n1z = m23 + m21; // bottom
12910         n2x = m01 - m03; n2y = m11 - m13; n2z = m21 - m23; // top
12911         double n1len = Math.sqrt(n1x * n1x + n1y * n1y + n1z * n1z);
12912         double n2len = Math.sqrt(n2x * n2x + n2y * n2y + n2z * n2z);
12913         return Math.acos((n1x * n2x + n1y * n2y + n1z * n2z) / (n1len * n2len));
12914     }
12915 
12916     public double perspectiveNear() {
12917         return m32 / (m23 + m22);
12918     }
12919 
12920     public double perspectiveFar() {
12921         return m32 / (m22 - m23);
12922     }
12923 
12924     public Vector3d frustumRayDir(double x, double y, ref Vector3d dest) {
12925         /*
12926          * This method works by first obtaining the frustum plane normals,
12927          * then building the cross product to obtain the corner rays,
12928          * and finally bilinearly interpolating to obtain the desired direction.
12929          * The code below uses a condense form of doing all this making use 
12930          * of some mathematical identities to simplify the overall expression.
12931          */
12932         double a = m10 * m23, b = m13 * m21, c = m10 * m21, d = m11 * m23, e = m13 * m20, f = m11 * m20;
12933         double g = m03 * m20, h = m01 * m23, i = m01 * m20, j = m03 * m21, k = m00 * m23, l = m00 * m21;
12934         double m = m00 * m13, n = m03 * m11, o = m00 * m11, p = m01 * m13, q = m03 * m10, r = m01 * m10;
12935         double m1x, m1y, m1z;
12936         m1x = (d + e + f - a - b - c) * (1.0 - y) + (a - b - c + d - e + f) * y;
12937         m1y = (j + k + l - g - h - i) * (1.0 - y) + (g - h - i + j - k + l) * y;
12938         m1z = (p + q + r - m - n - o) * (1.0 - y) + (m - n - o + p - q + r) * y;
12939         double m2x, m2y, m2z;
12940         m2x = (b - c - d + e + f - a) * (1.0 - y) + (a + b - c - d - e + f) * y;
12941         m2y = (h - i - j + k + l - g) * (1.0 - y) + (g + h - i - j - k + l) * y;
12942         m2z = (n - o - p + q + r - m) * (1.0 - y) + (m + n - o - p - q + r) * y;
12943         dest.x = m1x * (1.0 - x) + m2x * x;
12944         dest.y = m1y * (1.0 - x) + m2y * x;
12945         dest.z = m1z * (1.0 - x) + m2z * x;
12946         return dest.normalize(dest);
12947     }
12948 
12949     public Vector3d positiveZ(ref Vector3d dir) {
12950         if ((properties & PROPERTY_ORTHONORMAL) != 0)
12951             return normalizedPositiveZ(dir);
12952         return positiveZGeneric(dir);
12953     }
12954     private Vector3d positiveZGeneric(ref Vector3d dir) {
12955         return dir.set(m10 * m21 - m11 * m20, m20 * m01 - m21 * m00, m00 * m11 - m01 * m10).normalize();
12956     }
12957 
12958     public Vector3d normalizedPositiveZ(ref Vector3d dir) {
12959         return dir.set(m02, m12, m22);
12960     }
12961 
12962     public Vector3d positiveX(ref Vector3d dir) {
12963         if ((properties & PROPERTY_ORTHONORMAL) != 0)
12964             return normalizedPositiveX(dir);
12965         return positiveXGeneric(dir);
12966     }
12967     private Vector3d positiveXGeneric(ref Vector3d dir) {
12968         return dir.set(m11 * m22 - m12 * m21, m02 * m21 - m01 * m22, m01 * m12 - m02 * m11).normalize();
12969     }
12970 
12971     public Vector3d normalizedPositiveX(ref Vector3d dir) {
12972         return dir.set(m00, m10, m20);
12973     }
12974 
12975     public Vector3d positiveY(ref Vector3d dir) {
12976         if ((properties & PROPERTY_ORTHONORMAL) != 0)
12977             return normalizedPositiveY(dir);
12978         return positiveYGeneric(dir);
12979     }
12980     private Vector3d positiveYGeneric(ref Vector3d dir) {
12981         return dir.set(m12 * m20 - m10 * m22, m00 * m22 - m02 * m20, m02 * m10 - m00 * m12).normalize();
12982     }
12983 
12984     public Vector3d normalizedPositiveY(ref Vector3d dir) {
12985         return dir.set(m01, m11, m21);
12986     }
12987 
12988     public Vector3d originAffine(ref Vector3d dest) {
12989         double a = m00 * m11 - m01 * m10;
12990         double b = m00 * m12 - m02 * m10;
12991         double d = m01 * m12 - m02 * m11;
12992         double g = m20 * m31 - m21 * m30;
12993         double h = m20 * m32 - m22 * m30;
12994         double j = m21 * m32 - m22 * m31;
12995         dest.x = -m10 * j + m11 * h - m12 * g;
12996         dest.y =  m00 * j - m01 * h + m02 * g;
12997         dest.z = -m30 * d + m31 * b - m32 * a;
12998         return dest;
12999     }
13000 
13001     public Vector3d origin(ref Vector3d dest) {
13002         if ((properties & PROPERTY_AFFINE) != 0)
13003             return originAffine(dest);
13004         return originGeneric(dest);
13005     }
13006     private Vector3d originGeneric(ref Vector3d dest) {
13007         double a = m00 * m11 - m01 * m10;
13008         double b = m00 * m12 - m02 * m10;
13009         double c = m00 * m13 - m03 * m10;
13010         double d = m01 * m12 - m02 * m11;
13011         double e = m01 * m13 - m03 * m11;
13012         double f = m02 * m13 - m03 * m12;
13013         double g = m20 * m31 - m21 * m30;
13014         double h = m20 * m32 - m22 * m30;
13015         double i = m20 * m33 - m23 * m30;
13016         double j = m21 * m32 - m22 * m31;
13017         double k = m21 * m33 - m23 * m31;
13018         double l = m22 * m33 - m23 * m32;
13019         double det = a * l - b * k + c * j + d * i - e * h + f * g;
13020         double invDet = 1.0 / det;
13021         double nm30 = (-m10 * j + m11 * h - m12 * g) * invDet;
13022         double nm31 = ( m00 * j - m01 * h + m02 * g) * invDet;
13023         double nm32 = (-m30 * d + m31 * b - m32 * a) * invDet;
13024         double nm33 = det / ( m20 * d - m21 * b + m22 * a);
13025         double x = nm30 * nm33;
13026         double y = nm31 * nm33;
13027         double z = nm32 * nm33;
13028         return dest.set(x, y, z);
13029     }
13030 
13031     /**
13032      * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation
13033      * <code>x*a + y*b + z*c + d = 0</code> as if casting a shadow from a given light position/direction <code>light</code>.
13034      * <p>
13035      * If <code>light.w</code> is <code>0.0</code> the light is being treated as a directional light; if it is <code>1.0</code> it is a point light.
13036      * <p>
13037      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the shadow matrix,
13038      * then the new matrix will be <code>M * S</code>. So when transforming a
13039      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
13040      * shadow projection will be applied first!
13041      * <p>
13042      * Reference: <a href="ftp://ftp.sgi.com/opengl/contrib/blythe/advanced99/notes/node192.html">ftp.sgi.com</a>
13043      * 
13044      * @param light
13045      *          the light's vector
13046      * @param a
13047      *          the x factor in the plane equation
13048      * @param b
13049      *          the y factor in the plane equation
13050      * @param c
13051      *          the z factor in the plane equation
13052      * @param d
13053      *          the constant in the plane equation
13054      * @return this
13055      */
13056     ref public Matrix4d shadow(ref Vector4d light, double a, double b, double c, double d) return {
13057         shadow(light.x, light.y, light.z, light.w, a, b, c, d, this);
13058         return this;
13059     }
13060 
13061     public Matrix4d shadow(ref Vector4d light, double a, double b, double c, double d, ref Matrix4d dest) {
13062         return shadow(light.x, light.y, light.z, light.w, a, b, c, d, dest);
13063     }
13064 
13065     /**
13066      * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation
13067      * <code>x*a + y*b + z*c + d = 0</code> as if casting a shadow from a given light position/direction <code>(lightX, lightY, lightZ, lightW)</code>.
13068      * <p>
13069      * If <code>lightW</code> is <code>0.0</code> the light is being treated as a directional light; if it is <code>1.0</code> it is a point light.
13070      * <p>
13071      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the shadow matrix,
13072      * then the new matrix will be <code>M * S</code>. So when transforming a
13073      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
13074      * shadow projection will be applied first!
13075      * <p>
13076      * Reference: <a href="ftp://ftp.sgi.com/opengl/contrib/blythe/advanced99/notes/node192.html">ftp.sgi.com</a>
13077      * 
13078      * @param lightX
13079      *          the x-component of the light's vector
13080      * @param lightY
13081      *          the y-component of the light's vector
13082      * @param lightZ
13083      *          the z-component of the light's vector
13084      * @param lightW
13085      *          the w-component of the light's vector
13086      * @param a
13087      *          the x factor in the plane equation
13088      * @param b
13089      *          the y factor in the plane equation
13090      * @param c
13091      *          the z factor in the plane equation
13092      * @param d
13093      *          the constant in the plane equation
13094      * @return this
13095      */
13096     ref public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, double a, double b, double c, double d) return {
13097         shadow(lightX, lightY, lightZ, lightW, a, b, c, d, this);
13098         return this;
13099     }
13100 
13101     public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, double a, double b, double c, double d, ref Matrix4d dest) {
13102         // normalize plane
13103         double invPlaneLen = Math.invsqrt(a*a + b*b + c*c);
13104         double an = a * invPlaneLen;
13105         double bn = b * invPlaneLen;
13106         double cn = c * invPlaneLen;
13107         double dn = d * invPlaneLen;
13108 
13109         double dot = an * lightX + bn * lightY + cn * lightZ + dn * lightW;
13110 
13111         // compute right matrix elements
13112         double rm00 = dot - an * lightX;
13113         double rm01 = -an * lightY;
13114         double rm02 = -an * lightZ;
13115         double rm03 = -an * lightW;
13116         double rm10 = -bn * lightX;
13117         double rm11 = dot - bn * lightY;
13118         double rm12 = -bn * lightZ;
13119         double rm13 = -bn * lightW;
13120         double rm20 = -cn * lightX;
13121         double rm21 = -cn * lightY;
13122         double rm22 = dot - cn * lightZ;
13123         double rm23 = -cn * lightW;
13124         double rm30 = -dn * lightX;
13125         double rm31 = -dn * lightY;
13126         double rm32 = -dn * lightZ;
13127         double rm33 = dot - dn * lightW;
13128 
13129         // matrix multiplication
13130         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02 + m30 * rm03;
13131         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02 + m31 * rm03;
13132         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02 + m32 * rm03;
13133         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02 + m33 * rm03;
13134         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12 + m30 * rm13;
13135         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12 + m31 * rm13;
13136         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12 + m32 * rm13;
13137         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12 + m33 * rm13;
13138         double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 + m30 * rm23;
13139         double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 + m31 * rm23;
13140         double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 + m32 * rm23;
13141         double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 + m33 * rm23;
13142         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30 * rm33)
13143         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31 * rm33)
13144         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32 * rm33)
13145         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33 * rm33)
13146         ._m00(nm00)
13147         ._m01(nm01)
13148         ._m02(nm02)
13149         ._m03(nm03)
13150         ._m10(nm10)
13151         ._m11(nm11)
13152         ._m12(nm12)
13153         ._m13(nm13)
13154         ._m20(nm20)
13155         ._m21(nm21)
13156         ._m22(nm22)
13157         ._m23(nm23)
13158         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
13159         return dest;
13160     }
13161 
13162     public Matrix4d shadow(ref Vector4d light, Matrix4d planeTransform, ref Matrix4d dest) {
13163         // compute plane equation by transforming (y = 0)
13164         double a = planeTransform.m10;
13165         double b = planeTransform.m11;
13166         double c = planeTransform.m12;
13167         double d = -a * planeTransform.m30 - b * planeTransform.m31 - c * planeTransform.m32;
13168         return shadow(light.x, light.y, light.z, light.w, a, b, c, d, dest);
13169     }
13170 
13171     /**
13172      * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation
13173      * <code>y = 0</code> as if casting a shadow from a given light position/direction <code>light</code>.
13174      * <p>
13175      * Before the shadow projection is applied, the plane is transformed via the specified <code>planeTransformation</code>.
13176      * <p>
13177      * If <code>light.w</code> is <code>0.0</code> the light is being treated as a directional light; if it is <code>1.0</code> it is a point light.
13178      * <p>
13179      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the shadow matrix,
13180      * then the new matrix will be <code>M * S</code>. So when transforming a
13181      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
13182      * shadow projection will be applied first!
13183      * 
13184      * @param light
13185      *          the light's vector
13186      * @param planeTransform
13187      *          the transformation to transform the implied plane <code>y = 0</code> before applying the projection
13188      * @return this
13189      */
13190     ref public Matrix4d shadow(ref Vector4d light, Matrix4d planeTransform) return {
13191         shadow(light, planeTransform, this);
13192         return this;
13193     }
13194 
13195     public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, Matrix4d planeTransform, ref Matrix4d dest) {
13196         // compute plane equation by transforming (y = 0)
13197         double a = planeTransform.m10;
13198         double b = planeTransform.m11;
13199         double c = planeTransform.m12;
13200         double d = -a * planeTransform.m30 - b * planeTransform.m31 - c * planeTransform.m32;
13201         return shadow(lightX, lightY, lightZ, lightW, a, b, c, d, dest);
13202     }
13203 
13204     /**
13205      * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation
13206      * <code>y = 0</code> as if casting a shadow from a given light position/direction <code>(lightX, lightY, lightZ, lightW)</code>.
13207      * <p>
13208      * Before the shadow projection is applied, the plane is transformed via the specified <code>planeTransformation</code>.
13209      * <p>
13210      * If <code>lightW</code> is <code>0.0</code> the light is being treated as a directional light; if it is <code>1.0</code> it is a point light.
13211      * <p>
13212      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the shadow matrix,
13213      * then the new matrix will be <code>M * S</code>. So when transforming a
13214      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
13215      * shadow projection will be applied first!
13216      * 
13217      * @param lightX
13218      *          the x-component of the light vector
13219      * @param lightY
13220      *          the y-component of the light vector
13221      * @param lightZ
13222      *          the z-component of the light vector
13223      * @param lightW
13224      *          the w-component of the light vector
13225      * @param planeTransform
13226      *          the transformation to transform the implied plane <code>y = 0</code> before applying the projection
13227      * @return this
13228      */
13229     ref public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, Matrix4d planeTransform) return {
13230         shadow(lightX, lightY, lightZ, lightW, planeTransform, this);
13231         return this;
13232     }
13233 
13234     /**
13235      * Set this matrix to a cylindrical billboard transformation that rotates the local +Z axis of a given object with position <code>objPos</code> towards
13236      * a target position at <code>targetPos</code> while constraining a cylindrical rotation around the given <code>up</code> vector.
13237      * <p>
13238      * This method can be used to create the complete model transformation for a given object, including the translation of the object to
13239      * its position <code>objPos</code>.
13240      * 
13241      * @param objPos
13242      *          the position of the object to rotate towards <code>targetPos</code>
13243      * @param targetPos
13244      *          the position of the target (for example the camera) towards which to rotate the object
13245      * @param up
13246      *          the rotation axis (must be {@link Vector3d#normalize() normalized})
13247      * @return this
13248      */
13249     ref public Matrix4d billboardCylindrical(ref Vector3d objPos, Vector3d targetPos, Vector3d up) return {
13250         double dirX = targetPos.x - objPos.x;
13251         double dirY = targetPos.y - objPos.y;
13252         double dirZ = targetPos.z - objPos.z;
13253         // left = up x dir
13254         double leftX = up.y * dirZ - up.z * dirY;
13255         double leftY = up.z * dirX - up.x * dirZ;
13256         double leftZ = up.x * dirY - up.y * dirX;
13257         // normalize left
13258         double invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
13259         leftX *= invLeftLen;
13260         leftY *= invLeftLen;
13261         leftZ *= invLeftLen;
13262         // recompute dir by constraining rotation around 'up'
13263         // dir = left x up
13264         dirX = leftY * up.z - leftZ * up.y;
13265         dirY = leftZ * up.x - leftX * up.z;
13266         dirZ = leftX * up.y - leftY * up.x;
13267         // normalize dir
13268         double invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
13269         dirX *= invDirLen;
13270         dirY *= invDirLen;
13271         dirZ *= invDirLen;
13272         // set matrix elements
13273         _m00(leftX).
13274         _m01(leftY).
13275         _m02(leftZ).
13276         _m03(0.0).
13277         _m10(up.x).
13278         _m11(up.y).
13279         _m12(up.z).
13280         _m13(0.0).
13281         _m20(dirX).
13282         _m21(dirY).
13283         _m22(dirZ).
13284         _m23(0.0).
13285         _m30(objPos.x).
13286         _m31(objPos.y).
13287         _m32(objPos.z).
13288         _m33(1.0).
13289         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
13290         return this;
13291     }
13292 
13293     /**
13294      * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position <code>objPos</code> towards
13295      * a target position at <code>targetPos</code>.
13296      * <p>
13297      * This method can be used to create the complete model transformation for a given object, including the translation of the object to
13298      * its position <code>objPos</code>.
13299      * <p>
13300      * If preserving an <i>up</i> vector is not necessary when rotating the +Z axis, then a shortest arc rotation can be obtained 
13301      * using {@link #billboardSpherical(ref Vector3d, Vector3d)}.
13302      * 
13303      * @see #billboardSpherical(ref Vector3d, Vector3d)
13304      * 
13305      * @param objPos
13306      *          the position of the object to rotate towards <code>targetPos</code>
13307      * @param targetPos
13308      *          the position of the target (for example the camera) towards which to rotate the object
13309      * @param up
13310      *          the up axis used to orient the object
13311      * @return this
13312      */
13313     ref public Matrix4d billboardSpherical(ref Vector3d objPos, Vector3d targetPos, Vector3d up) return {
13314         double dirX = targetPos.x - objPos.x;
13315         double dirY = targetPos.y - objPos.y;
13316         double dirZ = targetPos.z - objPos.z;
13317         // normalize dir
13318         double invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
13319         dirX *= invDirLen;
13320         dirY *= invDirLen;
13321         dirZ *= invDirLen;
13322         // left = up x dir
13323         double leftX = up.y * dirZ - up.z * dirY;
13324         double leftY = up.z * dirX - up.x * dirZ;
13325         double leftZ = up.x * dirY - up.y * dirX;
13326         // normalize left
13327         double invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
13328         leftX *= invLeftLen;
13329         leftY *= invLeftLen;
13330         leftZ *= invLeftLen;
13331         // up = dir x left
13332         double upX = dirY * leftZ - dirZ * leftY;
13333         double upY = dirZ * leftX - dirX * leftZ;
13334         double upZ = dirX * leftY - dirY * leftX;
13335         // set matrix elements
13336         _m00(leftX).
13337         _m01(leftY).
13338         _m02(leftZ).
13339         _m03(0.0).
13340         _m10(upX).
13341         _m11(upY).
13342         _m12(upZ).
13343         _m13(0.0).
13344         _m20(dirX).
13345         _m21(dirY).
13346         _m22(dirZ).
13347         _m23(0.0).
13348         _m30(objPos.x).
13349         _m31(objPos.y).
13350         _m32(objPos.z).
13351         _m33(1.0).
13352         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
13353         return this;
13354     }
13355 
13356     /**
13357      * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position <code>objPos</code> towards
13358      * a target position at <code>targetPos</code> using a shortest arc rotation by not preserving any <i>up</i> vector of the object.
13359      * <p>
13360      * This method can be used to create the complete model transformation for a given object, including the translation of the object to
13361      * its position <code>objPos</code>.
13362      * <p>
13363      * In order to specify an <i>up</i> vector which needs to be maintained when rotating the +Z axis of the object,
13364      * use {@link #billboardSpherical(ref Vector3d, Vector3d, Vector3d)}.
13365      * 
13366      * @see #billboardSpherical(ref Vector3d, Vector3d, Vector3d)
13367      * 
13368      * @param objPos
13369      *          the position of the object to rotate towards <code>targetPos</code>
13370      * @param targetPos
13371      *          the position of the target (for example the camera) towards which to rotate the object
13372      * @return this
13373      */
13374     ref public Matrix4d billboardSpherical(ref Vector3d objPos, Vector3d targetPos) return {
13375         double toDirX = targetPos.x - objPos.x;
13376         double toDirY = targetPos.y - objPos.y;
13377         double toDirZ = targetPos.z - objPos.z;
13378         double x = -toDirY;
13379         double y = toDirX;
13380         double w = Math.sqrt(toDirX * toDirX + toDirY * toDirY + toDirZ * toDirZ) + toDirZ;
13381         double invNorm = Math.invsqrt(x * x + y * y + w * w);
13382         x *= invNorm;
13383         y *= invNorm;
13384         w *= invNorm;
13385         double q00 = (x + x) * x;
13386         double q11 = (y + y) * y;
13387         double q01 = (x + x) * y;
13388         double q03 = (x + x) * w;
13389         double q13 = (y + y) * w;
13390         _m00(1.0 - q11).
13391         _m01(q01).
13392         _m02(-q13).
13393         _m03(0.0).
13394         _m10(q01).
13395         _m11(1.0 - q00).
13396         _m12(q03).
13397         _m13(0.0).
13398         _m20(q13).
13399         _m21(-q03).
13400         _m22(1.0 - q11 - q00).
13401         _m23(0.0).
13402         _m30(objPos.x).
13403         _m31(objPos.y).
13404         _m32(objPos.z).
13405         _m33(1.0).
13406         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
13407         return this;
13408     }
13409 
13410     public int hashCode() {
13411         immutable int prime = 31;
13412         int result = 1;
13413         long temp;
13414         temp = Math.doubleToLongBits(m00);
13415         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13416         temp = Math.doubleToLongBits(m01);
13417         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13418         temp = Math.doubleToLongBits(m02);
13419         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13420         temp = Math.doubleToLongBits(m03);
13421         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13422         temp = Math.doubleToLongBits(m10);
13423         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13424         temp = Math.doubleToLongBits(m11);
13425         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13426         temp = Math.doubleToLongBits(m12);
13427         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13428         temp = Math.doubleToLongBits(m13);
13429         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13430         temp = Math.doubleToLongBits(m20);
13431         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13432         temp = Math.doubleToLongBits(m21);
13433         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13434         temp = Math.doubleToLongBits(m22);
13435         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13436         temp = Math.doubleToLongBits(m23);
13437         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13438         temp = Math.doubleToLongBits(m30);
13439         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13440         temp = Math.doubleToLongBits(m31);
13441         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13442         temp = Math.doubleToLongBits(m32);
13443         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13444         temp = Math.doubleToLongBits(m33);
13445         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13446         return result;
13447     }
13448 
13449     public bool equals(Matrix4d m, double delta) {
13450         if (this == m)
13451             return true;
13452         if (!Math.equals(m00, m.m00, delta))
13453             return false;
13454         if (!Math.equals(m01, m.m01, delta))
13455             return false;
13456         if (!Math.equals(m02, m.m02, delta))
13457             return false;
13458         if (!Math.equals(m03, m.m03, delta))
13459             return false;
13460         if (!Math.equals(m10, m.m10, delta))
13461             return false;
13462         if (!Math.equals(m11, m.m11, delta))
13463             return false;
13464         if (!Math.equals(m12, m.m12, delta))
13465             return false;
13466         if (!Math.equals(m13, m.m13, delta))
13467             return false;
13468         if (!Math.equals(m20, m.m20, delta))
13469             return false;
13470         if (!Math.equals(m21, m.m21, delta))
13471             return false;
13472         if (!Math.equals(m22, m.m22, delta))
13473             return false;
13474         if (!Math.equals(m23, m.m23, delta))
13475             return false;
13476         if (!Math.equals(m30, m.m30, delta))
13477             return false;
13478         if (!Math.equals(m31, m.m31, delta))
13479             return false;
13480         if (!Math.equals(m32, m.m32, delta))
13481             return false;
13482         if (!Math.equals(m33, m.m33, delta))
13483             return false;
13484         return true;
13485     }
13486 
13487     public Matrix4d pick(double x, double y, double width, double height, int[] viewport, ref Matrix4d dest) {
13488         double sx = viewport[2] / width;
13489         double sy = viewport[3] / height;
13490         double tx = (viewport[2] + 2.0 * (viewport[0] - x)) / width;
13491         double ty = (viewport[3] + 2.0 * (viewport[1] - y)) / height;
13492         dest._m30(m00 * tx + m10 * ty + m30)
13493         ._m31(m01 * tx + m11 * ty + m31)
13494         ._m32(m02 * tx + m12 * ty + m32)
13495         ._m33(m03 * tx + m13 * ty + m33)
13496         ._m00(m00 * sx)
13497         ._m01(m01 * sx)
13498         ._m02(m02 * sx)
13499         ._m03(m03 * sx)
13500         ._m10(m10 * sy)
13501         ._m11(m11 * sy)
13502         ._m12(m12 * sy)
13503         ._m13(m13 * sy)
13504         ._properties(0);
13505         return dest;
13506     }
13507 
13508     /**
13509      * Apply a picking transformation to this matrix using the given window coordinates <code>(x, y)</code> as the pick center
13510      * and the given <code>(width, height)</code> as the size of the picking region in window coordinates.
13511      * 
13512      * @param x
13513      *          the x coordinate of the picking region center in window coordinates
13514      * @param y
13515      *          the y coordinate of the picking region center in window coordinates
13516      * @param width
13517      *          the width of the picking region in window coordinates
13518      * @param height
13519      *          the height of the picking region in window coordinates
13520      * @param viewport
13521      *          the viewport described by <code>[x, y, width, height]</code>
13522      * @return this
13523      */
13524     ref public Matrix4d pick(double x, double y, double width, double height, int[] viewport) return {
13525         pick(x, y, width, height, viewport, this);
13526         return this;
13527     }
13528 
13529     public bool isAffine() {
13530         return m03 == 0.0 && m13 == 0.0 && m23 == 0.0 && m33 == 1.0;
13531     }
13532 
13533     /**
13534      * Exchange the values of <code>this</code> matrix with the given <code>other</code> matrix.
13535      * 
13536      * @param other
13537      *          the other matrix to exchange the values with
13538      * @return this
13539      */
13540     ref public Matrix4d swap(ref Matrix4d other) return {
13541         double tmp;
13542         tmp = m00; m00 = other.m00; other.m00 = tmp;
13543         tmp = m01; m01 = other.m01; other.m01 = tmp;
13544         tmp = m02; m02 = other.m02; other.m02 = tmp;
13545         tmp = m03; m03 = other.m03; other.m03 = tmp;
13546         tmp = m10; m10 = other.m10; other.m10 = tmp;
13547         tmp = m11; m11 = other.m11; other.m11 = tmp;
13548         tmp = m12; m12 = other.m12; other.m12 = tmp;
13549         tmp = m13; m13 = other.m13; other.m13 = tmp;
13550         tmp = m20; m20 = other.m20; other.m20 = tmp;
13551         tmp = m21; m21 = other.m21; other.m21 = tmp;
13552         tmp = m22; m22 = other.m22; other.m22 = tmp;
13553         tmp = m23; m23 = other.m23; other.m23 = tmp;
13554         tmp = m30; m30 = other.m30; other.m30 = tmp;
13555         tmp = m31; m31 = other.m31; other.m31 = tmp;
13556         tmp = m32; m32 = other.m32; other.m32 = tmp;
13557         tmp = m33; m33 = other.m33; other.m33 = tmp;
13558         int props = properties;
13559         this.properties = other.properties;
13560         other.properties = props;
13561         return this;
13562     }
13563 
13564     public Matrix4d arcball(double radius, double centerX, double centerY, double centerZ, double angleX, double angleY, ref Matrix4d dest) {
13565         double m30 = m20 * -radius + this.m30;
13566         double m31 = m21 * -radius + this.m31;
13567         double m32 = m22 * -radius + this.m32;
13568         double m33 = m23 * -radius + this.m33;
13569         double sin = Math.sin(angleX);
13570         double cos = Math.cosFromSin(sin, angleX);
13571         double nm10 = m10 * cos + m20 * sin;
13572         double nm11 = m11 * cos + m21 * sin;
13573         double nm12 = m12 * cos + m22 * sin;
13574         double nm13 = m13 * cos + m23 * sin;
13575         double m20 = this.m20 * cos - m10 * sin;
13576         double m21 = this.m21 * cos - m11 * sin;
13577         double m22 = this.m22 * cos - m12 * sin;
13578         double m23 = this.m23 * cos - m13 * sin;
13579         sin = Math.sin(angleY);
13580         cos = Math.cosFromSin(sin, angleY);
13581         double nm00 = m00 * cos - m20 * sin;
13582         double nm01 = m01 * cos - m21 * sin;
13583         double nm02 = m02 * cos - m22 * sin;
13584         double nm03 = m03 * cos - m23 * sin;
13585         double nm20 = m00 * sin + m20 * cos;
13586         double nm21 = m01 * sin + m21 * cos;
13587         double nm22 = m02 * sin + m22 * cos;
13588         double nm23 = m03 * sin + m23 * cos;
13589         dest._m30(-nm00 * centerX - nm10 * centerY - nm20 * centerZ + m30)
13590         ._m31(-nm01 * centerX - nm11 * centerY - nm21 * centerZ + m31)
13591         ._m32(-nm02 * centerX - nm12 * centerY - nm22 * centerZ + m32)
13592         ._m33(-nm03 * centerX - nm13 * centerY - nm23 * centerZ + m33)
13593         ._m20(nm20)
13594         ._m21(nm21)
13595         ._m22(nm22)
13596         ._m23(nm23)
13597         ._m10(nm10)
13598         ._m11(nm11)
13599         ._m12(nm12)
13600         ._m13(nm13)
13601         ._m00(nm00)
13602         ._m01(nm01)
13603         ._m02(nm02)
13604         ._m03(nm03)
13605         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
13606         return dest;
13607     }
13608 
13609     public Matrix4d arcball(double radius, Vector3d center, double angleX, double angleY, ref Matrix4d dest) {
13610         return arcball(radius, center.x, center.y, center.z, angleX, angleY, dest);
13611     }
13612 
13613     /**
13614      * Apply an arcball view transformation to this matrix with the given <code>radius</code> and center <code>(centerX, centerY, centerZ)</code>
13615      * position of the arcball and the specified X and Y rotation angles.
13616      * <p>
13617      * This method is equivalent to calling: <code>translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-centerX, -centerY, -centerZ)</code>
13618      * 
13619      * @param radius
13620      *          the arcball radius
13621      * @param centerX
13622      *          the x coordinate of the center position of the arcball
13623      * @param centerY
13624      *          the y coordinate of the center position of the arcball
13625      * @param centerZ
13626      *          the z coordinate of the center position of the arcball
13627      * @param angleX
13628      *          the rotation angle around the X axis in radians
13629      * @param angleY
13630      *          the rotation angle around the Y axis in radians
13631      * @return this
13632      */
13633     ref public Matrix4d arcball(double radius, double centerX, double centerY, double centerZ, double angleX, double angleY) return {
13634         arcball(radius, centerX, centerY, centerZ, angleX, angleY, this);
13635         return this;
13636     }
13637 
13638     /**
13639      * Apply an arcball view transformation to this matrix with the given <code>radius</code> and <code>center</code>
13640      * position of the arcball and the specified X and Y rotation angles.
13641      * <p>
13642      * This method is equivalent to calling: <code>translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-center.x, -center.y, -center.z)</code>
13643      * 
13644      * @param radius
13645      *          the arcball radius
13646      * @param center
13647      *          the center position of the arcball
13648      * @param angleX
13649      *          the rotation angle around the X axis in radians
13650      * @param angleY
13651      *          the rotation angle around the Y axis in radians
13652      * @return this
13653      */
13654     ref public Matrix4d arcball(double radius, Vector3d center, double angleX, double angleY) return {
13655         arcball(radius, center.x, center.y, center.z, angleX, angleY, this);
13656         return this;
13657     }
13658 
13659     /**
13660      * Compute the axis-aligned bounding box of the frustum described by <code>this</code> matrix and store the minimum corner
13661      * coordinates in the given <code>min</code> and the maximum corner coordinates in the given <code>max</code> vector.
13662      * <p>
13663      * The matrix <code>this</code> is assumed to be the {@link #invert() inverse} of the origial view-projection matrix
13664      * for which to compute the axis-aligned bounding box in world-space.
13665      * <p>
13666      * The axis-aligned bounding box of the unit frustum is <code>(-1, -1, -1)</code>, <code>(1, 1, 1)</code>.
13667      * 
13668      * @param min
13669      *          will hold the minimum corner coordinates of the axis-aligned bounding box
13670      * @param max
13671      *          will hold the maximum corner coordinates of the axis-aligned bounding box
13672      * @return this
13673      */
13674     ref public Matrix4d frustumAabb(ref Vector3d min, Vector3d max) return {
13675         double minX = double.infinity;
13676         double minY = double.infinity;
13677         double minZ = double.infinity;
13678         double maxX = -double.infinity;
13679         double maxY = -double.infinity;
13680         double maxZ = -double.infinity;
13681         for (int t = 0; t < 8; t++) {
13682             double x = ((t & 1) << 1) - 1.0;
13683             double y = (((t >>> 1) & 1) << 1) - 1.0;
13684             double z = (((t >>> 2) & 1) << 1) - 1.0;
13685             double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33);
13686             double nx = (m00 * x + m10 * y + m20 * z + m30) * invW;
13687             double ny = (m01 * x + m11 * y + m21 * z + m31) * invW;
13688             double nz = (m02 * x + m12 * y + m22 * z + m32) * invW;
13689             minX = minX < nx ? minX : nx;
13690             minY = minY < ny ? minY : ny;
13691             minZ = minZ < nz ? minZ : nz;
13692             maxX = maxX > nx ? maxX : nx;
13693             maxY = maxY > ny ? maxY : ny;
13694             maxZ = maxZ > nz ? maxZ : nz;
13695         }
13696         min.x = minX;
13697         min.y = minY;
13698         min.z = minZ;
13699         max.x = maxX;
13700         max.y = maxY;
13701         max.z = maxZ;
13702         return this;
13703     }
13704 
13705     public Matrix4d projectedGridRange(Matrix4d projector, double sLower, double sUpper, ref Matrix4d dest) {
13706         // Compute intersection with frustum edges and plane
13707         double minX = double.infinity, minY = double.infinity;
13708         double maxX = -double.infinity, maxY = -double.infinity;
13709         bool intersection = false;
13710         for (int t = 0; t < 3 * 4; t++) {
13711             double c0X, c0Y, c0Z;
13712             double c1X, c1Y, c1Z;
13713             if (t < 4) {
13714                 // all x edges
13715                 c0X = -1; c1X = +1;
13716                 c0Y = c1Y = ((t & 1) << 1) - 1.0;
13717                 c0Z = c1Z = (((t >>> 1) & 1) << 1) - 1.0;
13718             } else if (t < 8) {
13719                 // all y edges
13720                 c0Y = -1; c1Y = +1;
13721                 c0X = c1X = ((t & 1) << 1) - 1.0;
13722                 c0Z = c1Z = (((t >>> 1) & 1) << 1) - 1.0;
13723             } else {
13724                 // all z edges
13725                 c0Z = -1; c1Z = +1;
13726                 c0X = c1X = ((t & 1) << 1) - 1.0;
13727                 c0Y = c1Y = (((t >>> 1) & 1) << 1) - 1.0;
13728             }
13729             // unproject corners
13730             double invW = 1.0 / (m03 * c0X + m13 * c0Y + m23 * c0Z + m33);
13731             double p0x = (m00 * c0X + m10 * c0Y + m20 * c0Z + m30) * invW;
13732             double p0y = (m01 * c0X + m11 * c0Y + m21 * c0Z + m31) * invW;
13733             double p0z = (m02 * c0X + m12 * c0Y + m22 * c0Z + m32) * invW;
13734             invW = 1.0 / (m03 * c1X + m13 * c1Y + m23 * c1Z + m33);
13735             double p1x = (m00 * c1X + m10 * c1Y + m20 * c1Z + m30) * invW;
13736             double p1y = (m01 * c1X + m11 * c1Y + m21 * c1Z + m31) * invW;
13737             double p1z = (m02 * c1X + m12 * c1Y + m22 * c1Z + m32) * invW;
13738             double dirX = p1x - p0x;
13739             double dirY = p1y - p0y;
13740             double dirZ = p1z - p0z;
13741             double invDenom = 1.0 / dirY;
13742             // test for intersection
13743             for (int s = 0; s < 2; s++) {
13744                 double isectT = -(p0y + (s == 0 ? sLower : sUpper)) * invDenom;
13745                 if (isectT >= 0.0 && isectT <= 1.0) {
13746                     intersection = true;
13747                     // project with projector matrix
13748                     double ix = p0x + isectT * dirX;
13749                     double iz = p0z + isectT * dirZ;
13750                     invW = 1.0 / (projector.m03 * ix + projector.m23 * iz + projector.m33);
13751                     double px = (projector.m00 * ix + projector.m20 * iz + projector.m30) * invW;
13752                     double py = (projector.m01 * ix + projector.m21 * iz + projector.m31) * invW;
13753                     minX = minX < px ? minX : px;
13754                     minY = minY < py ? minY : py;
13755                     maxX = maxX > px ? maxX : px;
13756                     maxY = maxY > py ? maxY : py;
13757                 }
13758             }
13759         }
13760         if (!intersection)
13761             return dest; // <- projected grid is not visible
13762         dest.set(maxX - minX, 0, 0, 0, 0, maxY - minY, 0, 0, 0, 0, 1, 0, minX, minY, 0, 1)
13763         ._properties(PROPERTY_AFFINE);
13764         return dest;
13765     }
13766 
13767     public Matrix4d perspectiveFrustumSlice(double near, double far, ref Matrix4d dest) {
13768         double invOldNear = (m23 + m22) / m32;
13769         double invNearFar = 1.0 / (near - far);
13770         dest._m00(m00 * invOldNear * near)
13771         ._m01(m01)
13772         ._m02(m02)
13773         ._m03(m03)
13774         ._m10(m10)
13775         ._m11(m11 * invOldNear * near)
13776         ._m12(m12)
13777         ._m13(m13)
13778         ._m20(m20)
13779         ._m21(m21)
13780         ._m22((far + near) * invNearFar)
13781         ._m23(m23)
13782         ._m30(m30)
13783         ._m31(m31)
13784         ._m32((far + far) * near * invNearFar)
13785         ._m33(m33)
13786         ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
13787         return dest;
13788     }
13789 
13790     public Matrix4d orthoCrop(Matrix4d view, ref Matrix4d dest) {
13791         // determine min/max world z and min/max orthographically view-projected x/y
13792         double minX = double.infinity, maxX = -double.infinity;
13793         double minY = double.infinity, maxY = -double.infinity;
13794         double minZ = double.infinity, maxZ = -double.infinity;
13795         for (int t = 0; t < 8; t++) {
13796             double x = ((t & 1) << 1) - 1.0;
13797             double y = (((t >>> 1) & 1) << 1) - 1.0;
13798             double z = (((t >>> 2) & 1) << 1) - 1.0;
13799             double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33);
13800             double wx = (m00 * x + m10 * y + m20 * z + m30) * invW;
13801             double wy = (m01 * x + m11 * y + m21 * z + m31) * invW;
13802             double wz = (m02 * x + m12 * y + m22 * z + m32) * invW;
13803             invW = 1.0 / (view.m03 * wx + view.m13 * wy + view.m23 * wz + view.m33);
13804             double vx = view.m00 * wx + view.m10 * wy + view.m20 * wz + view.m30;
13805             double vy = view.m01 * wx + view.m11 * wy + view.m21 * wz + view.m31;
13806             double vz = (view.m02 * wx + view.m12 * wy + view.m22 * wz + view.m32) * invW;
13807             minX = minX < vx ? minX : vx;
13808             maxX = maxX > vx ? maxX : vx;
13809             minY = minY < vy ? minY : vy;
13810             maxY = maxY > vy ? maxY : vy;
13811             minZ = minZ < vz ? minZ : vz;
13812             maxZ = maxZ > vz ? maxZ : vz;
13813         }
13814         // build crop projection matrix to fit 'this' frustum into view
13815         return dest.setOrtho(minX, maxX, minY, maxY, -maxZ, -minZ);
13816     }
13817 
13818     /**
13819      * Set <code>this</code> matrix to a perspective transformation that maps the trapezoid spanned by the four corner coordinates
13820      * <code>(p0x, p0y)</code>, <code>(p1x, p1y)</code>, <code>(p2x, p2y)</code> and <code>(p3x, p3y)</code> to the unit square <code>[(-1, -1)..(+1, +1)]</code>.
13821      * <p>
13822      * The corner coordinates are given in counter-clockwise order starting from the <i>left</i> corner on the smaller parallel side of the trapezoid
13823      * seen when looking at the trapezoid oriented with its shorter parallel edge at the bottom and its longer parallel edge at the top.
13824      * <p>
13825      * Reference: <a href="http://www.comp.nus.edu.sg/~tants/tsm/TSM_recipe.html">Trapezoidal Shadow Maps (TSM) - Recipe</a>
13826      * 
13827      * @param p0x
13828      *          the x coordinate of the left corner at the shorter edge of the trapezoid
13829      * @param p0y
13830      *          the y coordinate of the left corner at the shorter edge of the trapezoid
13831      * @param p1x
13832      *          the x coordinate of the right corner at the shorter edge of the trapezoid
13833      * @param p1y
13834      *          the y coordinate of the right corner at the shorter edge of the trapezoid
13835      * @param p2x
13836      *          the x coordinate of the right corner at the longer edge of the trapezoid
13837      * @param p2y
13838      *          the y coordinate of the right corner at the longer edge of the trapezoid
13839      * @param p3x
13840      *          the x coordinate of the left corner at the longer edge of the trapezoid
13841      * @param p3y
13842      *          the y coordinate of the left corner at the longer edge of the trapezoid
13843      * @return this
13844      */
13845     ref public Matrix4d trapezoidCrop(double p0x, double p0y, double p1x, double p1y, double p2x, double p2y, double p3x, double p3y) return {
13846         double aX = p1y - p0y, aY = p0x - p1x;
13847         double nm00 = aY;
13848         double nm10 = -aX;
13849         double nm30 = aX * p0y - aY * p0x;
13850         double nm01 = aX;
13851         double nm11 = aY;
13852         double nm31 = -(aX * p0x + aY * p0y);
13853         double c3x = nm00 * p3x + nm10 * p3y + nm30;
13854         double c3y = nm01 * p3x + nm11 * p3y + nm31;
13855         double s = -c3x / c3y;
13856         nm00 += s * nm01;
13857         nm10 += s * nm11;
13858         nm30 += s * nm31;
13859         double d1x = nm00 * p1x + nm10 * p1y + nm30;
13860         double d2x = nm00 * p2x + nm10 * p2y + nm30;
13861         double d = d1x * c3y / (d2x - d1x);
13862         nm31 += d;
13863         double sx = 2.0 / d2x;
13864         double sy = 1.0 / (c3y + d);
13865         double u = (sy + sy) * d / (1.0 - sy * d);
13866         double m03 = nm01 * sy;
13867         double m13 = nm11 * sy;
13868         double m33 = nm31 * sy;
13869         nm01 = (u + 1.0) * m03;
13870         nm11 = (u + 1.0) * m13;
13871         nm31 = (u + 1.0) * m33 - u;
13872         nm00 = sx * nm00 - m03;
13873         nm10 = sx * nm10 - m13;
13874         nm30 = sx * nm30 - m33;
13875         set(nm00, nm01, 0, m03,
13876             nm10, nm11, 0, m13,
13877               0,   0, 1,   0,
13878             nm30, nm31, 0, m33);
13879         properties = 0;
13880         return this;
13881     }
13882 
13883     ref public Matrix4d transformAab(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, ref Vector3d outMin, ref Vector3d outMax) return {
13884         double xax = m00 * minX, xay = m01 * minX, xaz = m02 * minX;
13885         double xbx = m00 * maxX, xby = m01 * maxX, xbz = m02 * maxX;
13886         double yax = m10 * minY, yay = m11 * minY, yaz = m12 * minY;
13887         double ybx = m10 * maxY, yby = m11 * maxY, ybz = m12 * maxY;
13888         double zax = m20 * minZ, zay = m21 * minZ, zaz = m22 * minZ;
13889         double zbx = m20 * maxZ, zby = m21 * maxZ, zbz = m22 * maxZ;
13890         double xminx, xminy, xminz, yminx, yminy, yminz, zminx, zminy, zminz;
13891         double xmaxx, xmaxy, xmaxz, ymaxx, ymaxy, ymaxz, zmaxx, zmaxy, zmaxz;
13892         if (xax < xbx) {
13893             xminx = xax;
13894             xmaxx = xbx;
13895         } else {
13896             xminx = xbx;
13897             xmaxx = xax;
13898         }
13899         if (xay < xby) {
13900             xminy = xay;
13901             xmaxy = xby;
13902         } else {
13903             xminy = xby;
13904             xmaxy = xay;
13905         }
13906         if (xaz < xbz) {
13907             xminz = xaz;
13908             xmaxz = xbz;
13909         } else {
13910             xminz = xbz;
13911             xmaxz = xaz;
13912         }
13913         if (yax < ybx) {
13914             yminx = yax;
13915             ymaxx = ybx;
13916         } else {
13917             yminx = ybx;
13918             ymaxx = yax;
13919         }
13920         if (yay < yby) {
13921             yminy = yay;
13922             ymaxy = yby;
13923         } else {
13924             yminy = yby;
13925             ymaxy = yay;
13926         }
13927         if (yaz < ybz) {
13928             yminz = yaz;
13929             ymaxz = ybz;
13930         } else {
13931             yminz = ybz;
13932             ymaxz = yaz;
13933         }
13934         if (zax < zbx) {
13935             zminx = zax;
13936             zmaxx = zbx;
13937         } else {
13938             zminx = zbx;
13939             zmaxx = zax;
13940         }
13941         if (zay < zby) {
13942             zminy = zay;
13943             zmaxy = zby;
13944         } else {
13945             zminy = zby;
13946             zmaxy = zay;
13947         }
13948         if (zaz < zbz) {
13949             zminz = zaz;
13950             zmaxz = zbz;
13951         } else {
13952             zminz = zbz;
13953             zmaxz = zaz;
13954         }
13955         outMin.x = xminx + yminx + zminx + m30;
13956         outMin.y = xminy + yminy + zminy + m31;
13957         outMin.z = xminz + yminz + zminz + m32;
13958         outMax.x = xmaxx + ymaxx + zmaxx + m30;
13959         outMax.y = xmaxy + ymaxy + zmaxy + m31;
13960         outMax.z = xmaxz + ymaxz + zmaxz + m32;
13961         return this;
13962     }
13963 
13964     ref public Matrix4d transformAab(ref Vector3d min, ref Vector3d max, ref Vector3d outMin, ref Vector3d outMax) return {
13965         return transformAab(min.x, min.y, min.z, max.x, max.y, max.z, outMin, outMax);
13966     }
13967 
13968     /**
13969      * Linearly interpolate <code>this</code> and <code>other</code> using the given interpolation factor <code>t</code>
13970      * and store the result in <code>this</code>.
13971      * <p>
13972      * If <code>t</code> is <code>0.0</code> then the result is <code>this</code>. If the interpolation factor is <code>1.0</code>
13973      * then the result is <code>other</code>.
13974      *
13975      * @param other
13976      *          the other matrix
13977      * @param t
13978      *          the interpolation factor between 0.0 and 1.0
13979      * @return this
13980      */
13981     ref public Matrix4d lerp(Matrix4d other, double t) return {
13982         lerp(other, t, this);
13983         return this;
13984     }
13985 
13986     public Matrix4d lerp(Matrix4d other, double t, ref Matrix4d dest) {
13987         dest._m00(Math.fma(other.m00 - m00, t, m00))
13988         ._m01(Math.fma(other.m01 - m01, t, m01))
13989         ._m02(Math.fma(other.m02 - m02, t, m02))
13990         ._m03(Math.fma(other.m03 - m03, t, m03))
13991         ._m10(Math.fma(other.m10 - m10, t, m10))
13992         ._m11(Math.fma(other.m11 - m11, t, m11))
13993         ._m12(Math.fma(other.m12 - m12, t, m12))
13994         ._m13(Math.fma(other.m13 - m13, t, m13))
13995         ._m20(Math.fma(other.m20 - m20, t, m20))
13996         ._m21(Math.fma(other.m21 - m21, t, m21))
13997         ._m22(Math.fma(other.m22 - m22, t, m22))
13998         ._m23(Math.fma(other.m23 - m23, t, m23))
13999         ._m30(Math.fma(other.m30 - m30, t, m30))
14000         ._m31(Math.fma(other.m31 - m31, t, m31))
14001         ._m32(Math.fma(other.m32 - m32, t, m32))
14002         ._m33(Math.fma(other.m33 - m33, t, m33))
14003         ._properties(properties & other.properties);
14004         return dest;
14005     }
14006 
14007     /**
14008      * Apply a model transformation to this matrix for a right-handed coordinate system, 
14009      * that aligns the local <code>+Z</code> axis with <code>direction</code>
14010      * and store the result in <code>dest</code>.
14011      * <p>
14012      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
14013      * then the new matrix will be <code>M * L</code>. So when transforming a
14014      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
14015      * the lookat transformation will be applied first!
14016      * <p>
14017      * In order to set the matrix to a rotation transformation without post-multiplying it,
14018      * use {@link #rotationTowards(ref Vector3d, Vector3d) rotationTowards()}.
14019      * <p>
14020      * This method is equivalent to calling: <code>mulAffine(new Matrix4d().lookAt(new Vector3d(), new Vector3d(dir).negate(), up).invertAffine(), dest)</code>
14021      * 
14022      * @see #rotateTowards(double, double, double, double, double, double, Matrix4d)
14023      * @see #rotationTowards(ref Vector3d, Vector3d)
14024      * 
14025      * @param direction
14026      *              the direction to rotate towards
14027      * @param up
14028      *              the up vector
14029      * @param dest
14030      *              will hold the result
14031      * @return dest
14032      */
14033     public Matrix4d rotateTowards(ref Vector3d direction, Vector3d up, ref Matrix4d dest) {
14034         return rotateTowards(direction.x, direction.y, direction.z, up.x, up.y, up.z, dest);
14035     }
14036 
14037     /**
14038      * Apply a model transformation to this matrix for a right-handed coordinate system, 
14039      * that aligns the local <code>+Z</code> axis with <code>direction</code>.
14040      * <p>
14041      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
14042      * then the new matrix will be <code>M * L</code>. So when transforming a
14043      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
14044      * the lookat transformation will be applied first!
14045      * <p>
14046      * In order to set the matrix to a rotation transformation without post-multiplying it,
14047      * use {@link #rotationTowards(ref Vector3d, Vector3d) rotationTowards()}.
14048      * <p>
14049      * This method is equivalent to calling: <code>mulAffine(new Matrix4d().lookAt(new Vector3d(), new Vector3d(dir).negate(), up).invertAffine())</code>
14050      * 
14051      * @see #rotateTowards(double, double, double, double, double, double)
14052      * @see #rotationTowards(ref Vector3d, Vector3d)
14053      * 
14054      * @param direction
14055      *              the direction to orient towards
14056      * @param up
14057      *              the up vector
14058      * @return this
14059      */
14060     ref public Matrix4d rotateTowards(ref Vector3d direction, Vector3d up) return {
14061         rotateTowards(direction.x, direction.y, direction.z, up.x, up.y, up.z, this);
14062         return this;
14063     }
14064 
14065     /**
14066      * Apply a model transformation to this matrix for a right-handed coordinate system, 
14067      * that aligns the local <code>+Z</code> axis with <code>(dirX, dirY, dirZ)</code>.
14068      * <p>
14069      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
14070      * then the new matrix will be <code>M * L</code>. So when transforming a
14071      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
14072      * the lookat transformation will be applied first!
14073      * <p>
14074      * In order to set the matrix to a rotation transformation without post-multiplying it,
14075      * use {@link #rotationTowards(double, double, double, double, double, double) rotationTowards()}.
14076      * <p>
14077      * This method is equivalent to calling: <code>mulAffine(new Matrix4d().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine())</code>
14078      * 
14079      * @see #rotateTowards(ref Vector3d, Vector3d)
14080      * @see #rotationTowards(double, double, double, double, double, double)
14081      * 
14082      * @param dirX
14083      *              the x-coordinate of the direction to rotate towards
14084      * @param dirY
14085      *              the y-coordinate of the direction to rotate towards
14086      * @param dirZ
14087      *              the z-coordinate of the direction to rotate towards
14088      * @param upX
14089      *              the x-coordinate of the up vector
14090      * @param upY
14091      *              the y-coordinate of the up vector
14092      * @param upZ
14093      *              the z-coordinate of the up vector
14094      * @return this
14095      */
14096     ref public Matrix4d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) return {
14097         rotateTowards(dirX, dirY, dirZ, upX, upY, upZ, this);
14098         return this;
14099     }
14100 
14101     /**
14102      * Apply a model transformation to this matrix for a right-handed coordinate system, 
14103      * that aligns the local <code>+Z</code> axis with <code>dir</code>
14104      * and store the result in <code>dest</code>.
14105      * <p>
14106      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
14107      * then the new matrix will be <code>M * L</code>. So when transforming a
14108      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
14109      * the lookat transformation will be applied first!
14110      * <p>
14111      * In order to set the matrix to a rotation transformation without post-multiplying it,
14112      * use {@link #rotationTowards(double, double, double, double, double, double) rotationTowards()}.
14113      * <p>
14114      * This method is equivalent to calling: <code>mulAffine(new Matrix4d().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine(), dest)</code>
14115      * 
14116      * @see #rotateTowards(ref Vector3d, Vector3d)
14117      * @see #rotationTowards(double, double, double, double, double, double)
14118      * 
14119      * @param dirX
14120      *              the x-coordinate of the direction to rotate towards
14121      * @param dirY
14122      *              the y-coordinate of the direction to rotate towards
14123      * @param dirZ
14124      *              the z-coordinate of the direction to rotate towards
14125      * @param upX
14126      *              the x-coordinate of the up vector
14127      * @param upY
14128      *              the y-coordinate of the up vector
14129      * @param upZ
14130      *              the z-coordinate of the up vector
14131      * @param dest
14132      *              will hold the result
14133      * @return dest
14134      */
14135     public Matrix4d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, ref Matrix4d dest) {
14136         // Normalize direction
14137         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
14138         double ndirX = dirX * invDirLength;
14139         double ndirY = dirY * invDirLength;
14140         double ndirZ = dirZ * invDirLength;
14141         // left = up x direction
14142         double leftX, leftY, leftZ;
14143         leftX = upY * ndirZ - upZ * ndirY;
14144         leftY = upZ * ndirX - upX * ndirZ;
14145         leftZ = upX * ndirY - upY * ndirX;
14146         // normalize left
14147         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
14148         leftX *= invLeftLength;
14149         leftY *= invLeftLength;
14150         leftZ *= invLeftLength;
14151         // up = direction x left
14152         double upnX = ndirY * leftZ - ndirZ * leftY;
14153         double upnY = ndirZ * leftX - ndirX * leftZ;
14154         double upnZ = ndirX * leftY - ndirY * leftX;
14155         double rm00 = leftX;
14156         double rm01 = leftY;
14157         double rm02 = leftZ;
14158         double rm10 = upnX;
14159         double rm11 = upnY;
14160         double rm12 = upnZ;
14161         double rm20 = ndirX;
14162         double rm21 = ndirY;
14163         double rm22 = ndirZ;
14164         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
14165         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
14166         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
14167         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
14168         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
14169         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
14170         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
14171         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
14172         dest._m30(m30)
14173         ._m31(m31)
14174         ._m32(m32)
14175         ._m33(m33)
14176         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
14177         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
14178         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
14179         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
14180         ._m00(nm00)
14181         ._m01(nm01)
14182         ._m02(nm02)
14183         ._m03(nm03)
14184         ._m10(nm10)
14185         ._m11(nm11)
14186         ._m12(nm12)
14187         ._m13(nm13)
14188         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
14189         return dest;
14190     }
14191 
14192     /**
14193      * Set this matrix to a model transformation for a right-handed coordinate system, 
14194      * that aligns the local <code>-z</code> axis with <code>dir</code>.
14195      * <p>
14196      * In order to apply the rotation transformation to a previous existing transformation,
14197      * use {@link #rotateTowards(double, double, double, double, double, double) rotateTowards}.
14198      * <p>
14199      * This method is equivalent to calling: <code>setLookAt(new Vector3d(), new Vector3d(dir).negate(), up).invertAffine()</code>
14200      * 
14201      * @see #rotationTowards(ref Vector3d, Vector3d)
14202      * @see #rotateTowards(double, double, double, double, double, double)
14203      * 
14204      * @param dir
14205      *              the direction to orient the local -z axis towards
14206      * @param up
14207      *              the up vector
14208      * @return this
14209      */
14210     ref public Matrix4d rotationTowards(ref Vector3d dir, Vector3d up) return {
14211         return rotationTowards(dir.x, dir.y, dir.z, up.x, up.y, up.z);
14212     }
14213 
14214     /**
14215      * Set this matrix to a model transformation for a right-handed coordinate system, 
14216      * that aligns the local <code>-z</code> axis with <code>dir</code>.
14217      * <p>
14218      * In order to apply the rotation transformation to a previous existing transformation,
14219      * use {@link #rotateTowards(double, double, double, double, double, double) rotateTowards}.
14220      * <p>
14221      * This method is equivalent to calling: <code>setLookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine()</code>
14222      * 
14223      * @see #rotateTowards(ref Vector3d, Vector3d)
14224      * @see #rotationTowards(double, double, double, double, double, double)
14225      * 
14226      * @param dirX
14227      *              the x-coordinate of the direction to rotate towards
14228      * @param dirY
14229      *              the y-coordinate of the direction to rotate towards
14230      * @param dirZ
14231      *              the z-coordinate of the direction to rotate towards
14232      * @param upX
14233      *              the x-coordinate of the up vector
14234      * @param upY
14235      *              the y-coordinate of the up vector
14236      * @param upZ
14237      *              the z-coordinate of the up vector
14238      * @return this
14239      */
14240     ref public Matrix4d rotationTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) return {
14241         // Normalize direction
14242         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
14243         double ndirX = dirX * invDirLength;
14244         double ndirY = dirY * invDirLength;
14245         double ndirZ = dirZ * invDirLength;
14246         // left = up x direction
14247         double leftX, leftY, leftZ;
14248         leftX = upY * ndirZ - upZ * ndirY;
14249         leftY = upZ * ndirX - upX * ndirZ;
14250         leftZ = upX * ndirY - upY * ndirX;
14251         // normalize left
14252         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
14253         leftX *= invLeftLength;
14254         leftY *= invLeftLength;
14255         leftZ *= invLeftLength;
14256         // up = direction x left
14257         double upnX = ndirY * leftZ - ndirZ * leftY;
14258         double upnY = ndirZ * leftX - ndirX * leftZ;
14259         double upnZ = ndirX * leftY - ndirY * leftX;
14260         if ((properties & PROPERTY_IDENTITY) == 0)
14261             this._identity();
14262         setm00(leftX);
14263         setm01(leftY);
14264         setm02(leftZ);
14265         setm10(upnX);
14266         setm11(upnY);
14267         setm12(upnZ);
14268         setm20(ndirX);
14269         setm21(ndirY);
14270         setm22(ndirZ);
14271         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
14272         return this;
14273     }
14274 
14275     /**
14276      * Set this matrix to a model transformation for a right-handed coordinate system, 
14277      * that translates to the given <code>pos</code> and aligns the local <code>-z</code>
14278      * axis with <code>dir</code>.
14279      * <p>
14280      * This method is equivalent to calling: <code>translation(pos).rotateTowards(dir, up)</code>
14281      * 
14282      * @see #translation(ref Vector3d)
14283      * @see #rotateTowards(ref Vector3d, Vector3d)
14284      *
14285      * @param pos
14286      *              the position to translate to
14287      * @param dir
14288      *              the direction to rotate towards
14289      * @param up
14290      *              the up vector
14291      * @return this
14292      */
14293     ref public Matrix4d translationRotateTowards(ref Vector3d pos, Vector3d dir, Vector3d up) return {
14294         return translationRotateTowards(pos.x, pos.y, pos.z, dir.x, dir.y, dir.z, up.x, up.y, up.z);
14295     }
14296 
14297     /**
14298      * Set this matrix to a model transformation for a right-handed coordinate system, 
14299      * that translates to the given <code>(posX, posY, posZ)</code> and aligns the local <code>-z</code>
14300      * axis with <code>(dirX, dirY, dirZ)</code>.
14301      * <p>
14302      * This method is equivalent to calling: <code>translation(posX, posY, posZ).rotateTowards(dirX, dirY, dirZ, upX, upY, upZ)</code>
14303      * 
14304      * @see #translation(double, double, double)
14305      * @see #rotateTowards(double, double, double, double, double, double)
14306      * 
14307      * @param posX
14308      *              the x-coordinate of the position to translate to
14309      * @param posY
14310      *              the y-coordinate of the position to translate to
14311      * @param posZ
14312      *              the z-coordinate of the position to translate to
14313      * @param dirX
14314      *              the x-coordinate of the direction to rotate towards
14315      * @param dirY
14316      *              the y-coordinate of the direction to rotate towards
14317      * @param dirZ
14318      *              the z-coordinate of the direction to rotate towards
14319      * @param upX
14320      *              the x-coordinate of the up vector
14321      * @param upY
14322      *              the y-coordinate of the up vector
14323      * @param upZ
14324      *              the z-coordinate of the up vector
14325      * @return this
14326      */
14327     ref public Matrix4d translationRotateTowards(double posX, double posY, double posZ, double dirX, double dirY, double dirZ, double upX, double upY, double upZ) return {
14328         // Normalize direction
14329         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
14330         double ndirX = dirX * invDirLength;
14331         double ndirY = dirY * invDirLength;
14332         double ndirZ = dirZ * invDirLength;
14333         // left = up x direction
14334         double leftX, leftY, leftZ;
14335         leftX = upY * ndirZ - upZ * ndirY;
14336         leftY = upZ * ndirX - upX * ndirZ;
14337         leftZ = upX * ndirY - upY * ndirX;
14338         // normalize left
14339         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
14340         leftX *= invLeftLength;
14341         leftY *= invLeftLength;
14342         leftZ *= invLeftLength;
14343         // up = direction x left
14344         double upnX = ndirY * leftZ - ndirZ * leftY;
14345         double upnY = ndirZ * leftX - ndirX * leftZ;
14346         double upnZ = ndirX * leftY - ndirY * leftX;
14347         setm00(leftX);
14348         setm01(leftY);
14349         setm02(leftZ);
14350         setm03(0.0);
14351         setm10(upnX);
14352         setm11(upnY);
14353         setm12(upnZ);
14354         setm13(0.0);
14355         setm20(ndirX);
14356         setm21(ndirY);
14357         setm22(ndirZ);
14358         setm23(0.0);
14359         setm30(posX);
14360         setm31(posY);
14361         setm32(posZ);
14362         setm33(1.0);
14363         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
14364         return this;
14365     }
14366 
14367     public Vector3d getEulerAnglesZYX(ref Vector3d dest) {
14368         dest.x = Math.atan2(m12, m22);
14369         dest.y = Math.atan2(-m02, Math.sqrt(1.0 - m02 * m02));
14370         dest.z = Math.atan2(m01, m00);
14371         return dest;
14372     }
14373 
14374     public Vector3d getEulerAnglesXYZ(ref Vector3d dest) {
14375         dest.x = Math.atan2(-m21, m22);
14376         dest.y = Math.atan2(m20, Math.sqrt(1.0 - m20 * m20));
14377         dest.z = Math.atan2(-m10, m00);
14378         return dest;
14379     }
14380 
14381     /**
14382      * Compute the extents of the coordinate system before this {@link #isAffine() affine} transformation was applied
14383      * and store the resulting corner coordinates in <code>corner</code> and the span vectors in
14384      * <code>xDir</code>, <code>yDir</code> and <code>zDir</code>.
14385      * <p>
14386      * That means, given the maximum extents of the coordinate system between <code>[-1..+1]</code> in all dimensions,
14387      * this method returns one corner and the length and direction of the three base axis vectors in the coordinate
14388      * system before this transformation is applied, which transforms into the corner coordinates <code>[-1, +1]</code>.
14389      * <p>
14390      * This method is equivalent to computing at least three adjacent corners using {@link #frustumCorner(int, Vector3d)}
14391      * and subtracting them to obtain the length and direction of the span vectors.
14392      * 
14393      * @param corner
14394      *          will hold one corner of the span (usually the corner {@link Matrix4d#CORNER_NXNYNZ})
14395      * @param xDir
14396      *          will hold the direction and length of the span along the positive X axis
14397      * @param yDir
14398      *          will hold the direction and length of the span along the positive Y axis
14399      * @param zDir
14400      *          will hold the direction and length of the span along the positive z axis
14401      * @return this
14402      */
14403     ref public Matrix4d affineSpan(ref Vector3d corner, ref Vector3d xDir, ref Vector3d yDir, ref Vector3d zDir) return {
14404         double a = m10 * m22, b = m10 * m21, c = m10 * m02, d = m10 * m01;
14405         double e = m11 * m22, f = m11 * m20, g = m11 * m02, h = m11 * m00;
14406         double i = m12 * m21, j = m12 * m20, k = m12 * m01, l = m12 * m00;
14407         double m = m20 * m02, n = m20 * m01, o = m21 * m02, p = m21 * m00;
14408         double q = m22 * m01, r = m22 * m00;
14409         double s = 1.0 / (m00 * m11 - m01 * m10) * m22 + (m02 * m10 - m00 * m12) * m21 + (m01 * m12 - m02 * m11) * m20;
14410         double nm00 = (e - i) * s, nm01 = (o - q) * s, nm02 = (k - g) * s;
14411         double nm10 = (j - a) * s, nm11 = (r - m) * s, nm12 = (c - l) * s;
14412         double nm20 = (b - f) * s, nm21 = (n - p) * s, nm22 = (h - d) * s;
14413         corner.x = -nm00 - nm10 - nm20 + (a * m31 - b * m32 + f * m32 - e * m30 + i * m30 - j * m31) * s;
14414         corner.y = -nm01 - nm11 - nm21 + (m * m31 - n * m32 + p * m32 - o * m30 + q * m30 - r * m31) * s;
14415         corner.z = -nm02 - nm12 - nm22 + (g * m30 - k * m30 + l * m31 - c * m31 + d * m32 - h * m32) * s;
14416         xDir.x = 2.0 * nm00; xDir.y = 2.0 * nm01; xDir.z = 2.0 * nm02;
14417         yDir.x = 2.0 * nm10; yDir.y = 2.0 * nm11; yDir.z = 2.0 * nm12;
14418         zDir.x = 2.0 * nm20; zDir.y = 2.0 * nm21; zDir.z = 2.0 * nm22;
14419         return this;
14420     }
14421 
14422     public bool testPoint(double x, double y, double z) {
14423         double nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30;
14424         double pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30;
14425         double nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31;
14426         double pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31;
14427         double nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32;
14428         double pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32;
14429         return nxX * x + nxY * y + nxZ * z + nxW >= 0 && pxX * x + pxY * y + pxZ * z + pxW >= 0 &&
14430                nyX * x + nyY * y + nyZ * z + nyW >= 0 && pyX * x + pyY * y + pyZ * z + pyW >= 0 &&
14431                nzX * x + nzY * y + nzZ * z + nzW >= 0 && pzX * x + pzY * y + pzZ * z + pzW >= 0;
14432     }
14433 
14434     public bool testSphere(double x, double y, double z, double r) {
14435         double invl;
14436         double nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30;
14437         invl = Math.invsqrt(nxX * nxX + nxY * nxY + nxZ * nxZ);
14438         nxX *= invl; nxY *= invl; nxZ *= invl; nxW *= invl;
14439         double pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30;
14440         invl = Math.invsqrt(pxX * pxX + pxY * pxY + pxZ * pxZ);
14441         pxX *= invl; pxY *= invl; pxZ *= invl; pxW *= invl;
14442         double nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31;
14443         invl = Math.invsqrt(nyX * nyX + nyY * nyY + nyZ * nyZ);
14444         nyX *= invl; nyY *= invl; nyZ *= invl; nyW *= invl;
14445         double pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31;
14446         invl = Math.invsqrt(pyX * pyX + pyY * pyY + pyZ * pyZ);
14447         pyX *= invl; pyY *= invl; pyZ *= invl; pyW *= invl;
14448         double nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32;
14449         invl = Math.invsqrt(nzX * nzX + nzY * nzY + nzZ * nzZ);
14450         nzX *= invl; nzY *= invl; nzZ *= invl; nzW *= invl;
14451         double pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32;
14452         invl = Math.invsqrt(pzX * pzX + pzY * pzY + pzZ * pzZ);
14453         pzX *= invl; pzY *= invl; pzZ *= invl; pzW *= invl;
14454         return nxX * x + nxY * y + nxZ * z + nxW >= -r && pxX * x + pxY * y + pxZ * z + pxW >= -r &&
14455                nyX * x + nyY * y + nyZ * z + nyW >= -r && pyX * x + pyY * y + pyZ * z + pyW >= -r &&
14456                nzX * x + nzY * y + nzZ * z + nzW >= -r && pzX * x + pzY * y + pzZ * z + pzW >= -r;
14457     }
14458 
14459     public bool testAab(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
14460         double nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30;
14461         double pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30;
14462         double nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31;
14463         double pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31;
14464         double nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32;
14465         double pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32;
14466         /*
14467          * This is an implementation of the "2.4 Basic intersection test" of the mentioned site.
14468          * It does not distinguish between partially inside and fully inside, though, so the test with the 'p' vertex is omitted.
14469          */
14470         return nxX * (nxX < 0 ? minX : maxX) + nxY * (nxY < 0 ? minY : maxY) + nxZ * (nxZ < 0 ? minZ : maxZ) >= -nxW &&
14471                pxX * (pxX < 0 ? minX : maxX) + pxY * (pxY < 0 ? minY : maxY) + pxZ * (pxZ < 0 ? minZ : maxZ) >= -pxW &&
14472                nyX * (nyX < 0 ? minX : maxX) + nyY * (nyY < 0 ? minY : maxY) + nyZ * (nyZ < 0 ? minZ : maxZ) >= -nyW &&
14473                pyX * (pyX < 0 ? minX : maxX) + pyY * (pyY < 0 ? minY : maxY) + pyZ * (pyZ < 0 ? minZ : maxZ) >= -pyW &&
14474                nzX * (nzX < 0 ? minX : maxX) + nzY * (nzY < 0 ? minY : maxY) + nzZ * (nzZ < 0 ? minZ : maxZ) >= -nzW &&
14475                pzX * (pzX < 0 ? minX : maxX) + pzY * (pzY < 0 ? minY : maxY) + pzZ * (pzZ < 0 ? minZ : maxZ) >= -pzW;
14476     }
14477 
14478     /**
14479      * Apply an oblique projection transformation to this matrix with the given values for <code>a</code> and
14480      * <code>b</code>.
14481      * <p>
14482      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the oblique transformation matrix,
14483      * then the new matrix will be <code>M * O</code>. So when transforming a
14484      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
14485      * oblique transformation will be applied first!
14486      * <p>
14487      * The oblique transformation is defined as:
14488      * <pre>
14489      * x' = x + a*z
14490      * y' = y + a*z
14491      * z' = z
14492      * </pre>
14493      * or in matrix form:
14494      * <pre>
14495      * 1 0 a 0
14496      * 0 1 b 0
14497      * 0 0 1 0
14498      * 0 0 0 1
14499      * </pre>
14500      * 
14501      * @param a
14502      *            the value for the z factor that applies to x
14503      * @param b
14504      *            the value for the z factor that applies to y
14505      * @return this
14506      */
14507     ref public Matrix4d obliqueZ(double a, double b) return {
14508         setm20(m00 * a + m10 * b + m20);
14509         setm21(m01 * a + m11 * b + m21);
14510         setm22(m02 * a + m12 * b + m22);
14511         this.properties &= PROPERTY_AFFINE;
14512         return this;
14513     }
14514 
14515     /**
14516      * Apply an oblique projection transformation to this matrix with the given values for <code>a</code> and
14517      * <code>b</code> and store the result in <code>dest</code>.
14518      * <p>
14519      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the oblique transformation matrix,
14520      * then the new matrix will be <code>M * O</code>. So when transforming a
14521      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
14522      * oblique transformation will be applied first!
14523      * <p>
14524      * The oblique transformation is defined as:
14525      * <pre>
14526      * x' = x + a*z
14527      * y' = y + a*z
14528      * z' = z
14529      * </pre>
14530      * or in matrix form:
14531      * <pre>
14532      * 1 0 a 0
14533      * 0 1 b 0
14534      * 0 0 1 0
14535      * 0 0 0 1
14536      * </pre>
14537      * 
14538      * @param a
14539      *            the value for the z factor that applies to x
14540      * @param b
14541      *            the value for the z factor that applies to y
14542      * @param dest
14543      *            will hold the result
14544      * @return dest
14545      */
14546     public Matrix4d obliqueZ(double a, double b, ref Matrix4d dest) {
14547         dest._m00(m00)
14548         ._m01(m01)
14549         ._m02(m02)
14550         ._m03(m03)
14551         ._m10(m10)
14552         ._m11(m11)
14553         ._m12(m12)
14554         ._m13(m13)
14555         ._m20(m00 * a + m10 * b + m20)
14556         ._m21(m01 * a + m11 * b + m21)
14557         ._m22(m02 * a + m12 * b + m22)
14558         ._m23(m23)
14559         ._m30(m30)
14560         ._m31(m31)
14561         ._m32(m32)
14562         ._m33(m33)
14563         ._properties(properties & PROPERTY_AFFINE);
14564         return dest;
14565     }
14566 
14567     /**
14568      * Create a view and projection matrix from a given <code>eye</code> position, a given bottom left corner position <code>p</code> of the near plane rectangle
14569      * and the extents of the near plane rectangle along its local <code>x</code> and <code>y</code> axes, and store the resulting matrices
14570      * in <code>projDest</code> and <code>viewDest</code>.
14571      * <p>
14572      * This method creates a view and perspective projection matrix assuming that there is a pinhole camera at position <code>eye</code>
14573      * projecting the scene onto the near plane defined by the rectangle.
14574      * <p>
14575      * All positions and lengths are in the same (world) unit.
14576      * 
14577      * @param eye
14578      *          the position of the camera
14579      * @param p
14580      *          the bottom left corner of the near plane rectangle (will map to the bottom left corner in window coordinates)
14581      * @param x
14582      *          the direction and length of the local "bottom/top" X axis/side of the near plane rectangle
14583      * @param y
14584      *          the direction and length of the local "left/right" Y axis/side of the near plane rectangle
14585      * @param nearFarDist
14586      *          the distance between the far and near plane (the near plane will be calculated by this method).
14587      *          If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
14588      *          If the special value {@link Double#NEGATIVE_INFINITY} is used, the near and far planes will be swapped and 
14589      *          the near clipping plane will be at positive infinity.
14590      *          If a negative value is used (except for {@link Double#NEGATIVE_INFINITY}) the near and far planes will be swapped
14591      * @param zeroToOne
14592      *          whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
14593      *          or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
14594      * @param projDest
14595      *          will hold the resulting projection matrix
14596      * @param viewDest
14597      *          will hold the resulting view matrix
14598      */
14599     public static void projViewFromRectangle(
14600             Vector3d eye, Vector3d p, Vector3d x, Vector3d y, double nearFarDist, bool zeroToOne,
14601             ref Matrix4d projDest, ref Matrix4d viewDest) {
14602         double zx = y.y * x.z - y.z * x.y, zy = y.z * x.x - y.x * x.z, zz = y.x * x.y - y.y * x.x;
14603         double zd = zx * (p.x - eye.x) + zy * (p.y - eye.y) + zz * (p.z - eye.z);
14604         double zs = zd >= 0 ? 1 : -1; zx *= zs; zy *= zs; zz *= zs; zd *= zs; 
14605         viewDest.setLookAt(eye.x, eye.y, eye.z, eye.x + zx, eye.y + zy, eye.z + zz, y.x, y.y, y.z);
14606         double px = viewDest.m00 * p.x + viewDest.m10 * p.y + viewDest.m20 * p.z + viewDest.m30;
14607         double py = viewDest.m01 * p.x + viewDest.m11 * p.y + viewDest.m21 * p.z + viewDest.m31;
14608         double tx = viewDest.m00 * x.x + viewDest.m10 * x.y + viewDest.m20 * x.z;
14609         double ty = viewDest.m01 * y.x + viewDest.m11 * y.y + viewDest.m21 * y.z;
14610         double len = Math.sqrt(zx * zx + zy * zy + zz * zz);
14611         double near = zd / len, far;
14612         if (Math.isInfinite(nearFarDist) && nearFarDist < 0.0) {
14613             far = near;
14614             near = double.infinity;
14615         } else if (Math.isInfinite(nearFarDist) && nearFarDist > 0.0) {
14616             far = double.infinity;
14617         } else if (nearFarDist < 0.0) {
14618             far = near;
14619             near = near + nearFarDist;
14620         } else {
14621             far = near + nearFarDist;
14622         }
14623         projDest.setFrustum(px, px + tx, py, py + ty, near, far, zeroToOne);
14624     }
14625 
14626     /**
14627      * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(ref Vector3d)})
14628      * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(ref Vector3d)}) and the
14629      * given vector <code>up</code>.
14630      * <p>
14631      * This effectively ensures that the resulting matrix will be equal to the one obtained from 
14632      * {@link #setLookAt(ref Vector3d, Vector3d, Vector3d)} called with the current 
14633      * local origin of this matrix (as obtained by {@link #originAffine(ref Vector3d)}), the sum of this position and the 
14634      * negated local Z axis as well as the given vector <code>up</code>.
14635      * <p>
14636      * This method must only be called on {@link #isAffine()} matrices.
14637      * 
14638      * @param up
14639      *            the up vector
14640      * @return this
14641      */
14642     ref public Matrix4d withLookAtUp(ref Vector3d up) return {
14643         withLookAtUp(up.x, up.y, up.z, this);
14644         return this;
14645     }
14646 
14647     public Matrix4d withLookAtUp(ref Vector3d up, ref Matrix4d dest) {
14648         return dest.withLookAtUp(up.x, up.y, up.z);
14649     }
14650 
14651     /**
14652      * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(ref Vector3d)})
14653      * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(ref Vector3d)}) and the
14654      * given vector <code>(upX, upY, upZ)</code>.
14655      * <p>
14656      * This effectively ensures that the resulting matrix will be equal to the one obtained from 
14657      * {@link #setLookAt(double, double, double, double, double, double, double, double, double)} called with the current 
14658      * local origin of this matrix (as obtained by {@link #originAffine(ref Vector3d)}), the sum of this position and the 
14659      * negated local Z axis as well as the given vector <code>(upX, upY, upZ)</code>.
14660      * <p>
14661      * This method must only be called on {@link #isAffine()} matrices.
14662      * 
14663      * @param upX
14664      *            the x coordinate of the up vector
14665      * @param upY
14666      *            the y coordinate of the up vector
14667      * @param upZ
14668      *            the z coordinate of the up vector
14669      * @return this
14670      */
14671     ref public Matrix4d withLookAtUp(double upX, double upY, double upZ) return {
14672         withLookAtUp(upX, upY, upZ, this);
14673         return this;
14674     }
14675 
14676     public Matrix4d withLookAtUp(double upX, double upY, double upZ, ref Matrix4d dest) {
14677         double y = (upY * m21 - upZ * m11) * m02 +
14678                    (upZ * m01 - upX * m21) * m12 +
14679                    (upX * m11 - upY * m01) * m22;
14680         double x = upX * m01 + upY * m11 + upZ * m21;
14681         if ((properties & PROPERTY_ORTHONORMAL) == 0)
14682             x *= Math.sqrt(m01 * m01 + m11 * m11 + m21 * m21);
14683         double invsqrt = Math.invsqrt(y * y + x * x);
14684         double c = x * invsqrt, s = y * invsqrt;
14685         double nm00 = c * m00 - s * m01, nm10 = c * m10 - s * m11, nm20 = c * m20 - s * m21, nm31 = s * m30 + c * m31;
14686         double nm01 = s * m00 + c * m01, nm11 = s * m10 + c * m11, nm21 = s * m20 + c * m21, nm30 = c * m30 - s * m31;
14687         dest._m00(nm00)._m10(nm10)._m20(nm20)._m30(nm30)
14688         ._m01(nm01)._m11(nm11)._m21(nm21)._m31(nm31);
14689         if (dest != this) {
14690             dest
14691             ._m02(m02)._m12(m12)._m22(m22)._m32(m32)
14692             ._m03(m03)._m13(m13)._m23(m23)._m33(m33);
14693         }
14694         dest._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
14695         return dest;
14696     }
14697 
14698     /**
14699      * Multiply <code>this</code> by the matrix
14700      * <pre>
14701      * 1 0 0 0
14702      * 0 0 1 0
14703      * 0 1 0 0
14704      * 0 0 0 1
14705      * </pre>
14706      * 
14707      * @return this
14708      */
14709     ref public Matrix4d mapXZY() return {
14710         mapXZY(this);
14711         return this;
14712     }
14713     public Matrix4d mapXZY(ref Matrix4d dest) {
14714         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14715         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14716     }
14717     /**
14718      * Multiply <code>this</code> by the matrix
14719      * <pre>
14720      * 1 0  0 0
14721      * 0 0 -1 0
14722      * 0 1  0 0
14723      * 0 0  0 1
14724      * </pre>
14725      * 
14726      * @return this
14727      */
14728     ref public Matrix4d mapXZnY() return {
14729         mapXZnY(this);
14730         return this;
14731     }
14732     public Matrix4d mapXZnY(ref Matrix4d dest) {
14733         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14734         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14735     }
14736     /**
14737      * Multiply <code>this</code> by the matrix
14738      * <pre>
14739      * 1  0  0 0
14740      * 0 -1  0 0
14741      * 0  0 -1 0
14742      * 0  0  0 1
14743      * </pre>
14744      * 
14745      * @return this
14746      */
14747     ref public Matrix4d mapXnYnZ() return {
14748         mapXnYnZ(this);
14749         return this;
14750     }
14751     public Matrix4d mapXnYnZ(ref Matrix4d dest) {
14752         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14753     }
14754     /**
14755      * Multiply <code>this</code> by the matrix
14756      * <pre>
14757      * 1  0 0 0
14758      * 0  0 1 0
14759      * 0 -1 0 0
14760      * 0  0 0 1
14761      * </pre>
14762      * 
14763      * @return this
14764      */
14765     ref public Matrix4d mapXnZY() return {
14766         mapXnZY(this);
14767         return this;
14768     }
14769     public Matrix4d mapXnZY(ref Matrix4d dest) {
14770         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14771         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14772     }
14773     /**
14774      * Multiply <code>this</code> by the matrix
14775      * <pre>
14776      * 1  0  0 0
14777      * 0  0 -1 0
14778      * 0 -1  0 0
14779      * 0  0  0 1
14780      * </pre>
14781      * 
14782      * @return this
14783      */
14784     ref public Matrix4d mapXnZnY() return {
14785         mapXnZnY(this);
14786         return this;
14787     }
14788     public Matrix4d mapXnZnY(ref Matrix4d dest) {
14789         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14790         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14791     }
14792     /**
14793      * Multiply <code>this</code> by the matrix
14794      * <pre>
14795      * 0 1 0 0
14796      * 1 0 0 0
14797      * 0 0 1 0
14798      * 0 0 0 1
14799      * </pre>
14800      * 
14801      * @return this
14802      */
14803     ref public Matrix4d mapYXZ() return {
14804         mapYXZ(this);
14805         return this;
14806     }
14807     public Matrix4d mapYXZ(ref Matrix4d dest) {
14808         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14809         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14810     }
14811     /**
14812      * Multiply <code>this</code> by the matrix
14813      * <pre>
14814      * 0 1  0 0
14815      * 1 0  0 0
14816      * 0 0 -1 0
14817      * 0 0  0 1
14818      * </pre>
14819      * 
14820      * @return this
14821      */
14822     ref public Matrix4d mapYXnZ() return {
14823         mapYXnZ(this);
14824         return this;
14825     }
14826     public Matrix4d mapYXnZ(ref Matrix4d dest) {
14827         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14828         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14829     }
14830     /**
14831      * Multiply <code>this</code> by the matrix
14832      * <pre>
14833      * 0 0 1 0
14834      * 1 0 0 0
14835      * 0 1 0 0
14836      * 0 0 0 1
14837      * </pre>
14838      * 
14839      * @return this
14840      */
14841     ref public Matrix4d mapYZX() return {
14842         mapYZX(this);
14843         return this;
14844     }
14845     public Matrix4d mapYZX(ref Matrix4d dest) {
14846         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14847         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14848     }
14849     /**
14850      * Multiply <code>this</code> by the matrix
14851      * <pre>
14852      * 0 0 -1 0
14853      * 1 0  0 0
14854      * 0 1  0 0
14855      * 0 0  0 1
14856      * </pre>
14857      * 
14858      * @return this
14859      */
14860     ref public Matrix4d mapYZnX() return {
14861         mapYZnX(this);
14862         return this;
14863     }
14864     public Matrix4d mapYZnX(ref Matrix4d dest) {
14865         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14866         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14867     }
14868     /**
14869      * Multiply <code>this</code> by the matrix
14870      * <pre>
14871      * 0 -1 0 0
14872      * 1  0 0 0
14873      * 0  0 1 0
14874      * 0  0 0 1
14875      * </pre>
14876      * 
14877      * @return this
14878      */
14879     ref public Matrix4d mapYnXZ() return {
14880         mapYnXZ(this);
14881         return this;
14882     }
14883     public Matrix4d mapYnXZ(ref Matrix4d dest) {
14884         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14885         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14886     }
14887     /**
14888      * Multiply <code>this</code> by the matrix
14889      * <pre>
14890      * 0 -1  0 0
14891      * 1  0  0 0
14892      * 0  0 -1 0
14893      * 0  0  0 1
14894      * </pre>
14895      * 
14896      * @return this
14897      */
14898     ref public Matrix4d mapYnXnZ() return {
14899         mapYnXnZ(this);
14900         return this;
14901     }
14902     public Matrix4d mapYnXnZ(ref Matrix4d dest) {
14903         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14904         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14905     }
14906     /**
14907      * Multiply <code>this</code> by the matrix
14908      * <pre>
14909      * 0  0 1 0
14910      * 1  0 0 0
14911      * 0 -1 0 0
14912      * 0  0 0 1
14913      * </pre>
14914      * 
14915      * @return this
14916      */
14917     ref public Matrix4d mapYnZX() return {
14918         mapYnZX(this);
14919         return this;
14920     }
14921     public Matrix4d mapYnZX(ref Matrix4d dest) {
14922         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14923         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14924     }
14925     /**
14926      * Multiply <code>this</code> by the matrix
14927      * <pre>
14928      * 0  0 -1 0
14929      * 1  0  0 0
14930      * 0 -1  0 0
14931      * 0  0  0 1
14932      * </pre>
14933      * 
14934      * @return this
14935      */
14936     ref public Matrix4d mapYnZnX() return {
14937         mapYnZnX(this);
14938         return this;
14939     }
14940     public Matrix4d mapYnZnX(ref Matrix4d dest) {
14941         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14942         return dest._m00(m10)._m01(m11)._m02(m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14943     }
14944     /**
14945      * Multiply <code>this</code> by the matrix
14946      * <pre>
14947      * 0 1 0 0
14948      * 0 0 1 0
14949      * 1 0 0 0
14950      * 0 0 0 1
14951      * </pre>
14952      * 
14953      * @return this
14954      */
14955     ref public Matrix4d mapZXY() return {
14956         mapZXY(this);
14957         return this;
14958     }
14959     public Matrix4d mapZXY(ref Matrix4d dest) {
14960         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14961         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14962         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14963     }
14964     /**
14965      * Multiply <code>this</code> by the matrix
14966      * <pre>
14967      * 0 1  0 0
14968      * 0 0 -1 0
14969      * 1 0  0 0
14970      * 0 0  0 1
14971      * </pre>
14972      * 
14973      * @return this
14974      */
14975     ref public Matrix4d mapZXnY() return {
14976         mapZXnY(this);
14977         return this;
14978     }
14979     public Matrix4d mapZXnY(ref Matrix4d dest) {
14980         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14981         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14982         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
14983     }
14984     /**
14985      * Multiply <code>this</code> by the matrix
14986      * <pre>
14987      * 0 0 1 0
14988      * 0 1 0 0
14989      * 1 0 0 0
14990      * 0 0 0 1
14991      * </pre>
14992      * 
14993      * @return this
14994      */
14995     ref public Matrix4d mapZYX() return {
14996         mapZYX(this);
14997         return this;
14998     }
14999     public Matrix4d mapZYX(ref Matrix4d dest) {
15000         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15001         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15002     }
15003     /**
15004      * Multiply <code>this</code> by the matrix
15005      * <pre>
15006      * 0 0 -1 0
15007      * 0 1  0 0
15008      * 1 0  0 0
15009      * 0 0  0 1
15010      * </pre>
15011      * 
15012      * @return this
15013      */
15014     ref public Matrix4d mapZYnX() return {
15015         mapZYnX(this);
15016         return this;
15017     }
15018     public Matrix4d mapZYnX(ref Matrix4d dest) {
15019         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15020         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15021     }
15022     /**
15023      * Multiply <code>this</code> by the matrix
15024      * <pre>
15025      * 0 -1 0 0
15026      * 0  0 1 0
15027      * 1  0 0 0
15028      * 0  0 0 1
15029      * </pre>
15030      * 
15031      * @return this
15032      */
15033     ref public Matrix4d mapZnXY() return {
15034         mapZnXY(this);
15035         return this;
15036     }
15037     public Matrix4d mapZnXY(ref Matrix4d dest) {
15038         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15039         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15040         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15041     }
15042     /**
15043      * Multiply <code>this</code> by the matrix
15044      * <pre>
15045      * 0 -1  0 0
15046      * 0  0 -1 0
15047      * 1  0  0 0
15048      * 0  0  0 1
15049      * </pre>
15050      * 
15051      * @return this
15052      */
15053     ref public Matrix4d mapZnXnY() return {
15054         mapZnXnY(this);
15055         return this;
15056     }
15057     public Matrix4d mapZnXnY(ref Matrix4d dest) {
15058         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15059         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15060         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15061     }
15062     /**
15063      * Multiply <code>this</code> by the matrix
15064      * <pre>
15065      * 0  0 1 0
15066      * 0 -1 0 0
15067      * 1  0 0 0
15068      * 0  0 0 1
15069      * </pre>
15070      * 
15071      * @return this
15072      */
15073     ref public Matrix4d mapZnYX() return {
15074         mapZnYX(this);
15075         return this;
15076     }
15077     public Matrix4d mapZnYX(ref Matrix4d dest) {
15078         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15079         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15080     }
15081     /**
15082      * Multiply <code>this</code> by the matrix
15083      * <pre>
15084      * 0  0 -1 0
15085      * 0 -1  0 0
15086      * 1  0  0 0
15087      * 0  0  0 1
15088      * </pre>
15089      * 
15090      * @return this
15091      */
15092     ref public Matrix4d mapZnYnX() return {
15093         mapZnYnX(this);
15094         return this;
15095     }
15096     public Matrix4d mapZnYnX(ref Matrix4d dest) {
15097         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15098         return dest._m00(m20)._m01(m21)._m02(m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15099     }
15100     /**
15101      * Multiply <code>this</code> by the matrix
15102      * <pre>
15103      * -1 0  0 0
15104      *  0 1  0 0
15105      *  0 0 -1 0
15106      *  0 0  0 1
15107      * </pre>
15108      * 
15109      * @return this
15110      */
15111     ref public Matrix4d mapnXYnZ() return {
15112         mapnXYnZ(this);
15113         return this;
15114     }
15115     public Matrix4d mapnXYnZ(ref Matrix4d dest) {
15116         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15117     }
15118     /**
15119      * Multiply <code>this</code> by the matrix
15120      * <pre>
15121      * -1 0 0 0
15122      *  0 0 1 0
15123      *  0 1 0 0
15124      *  0 0 0 1
15125      * </pre>
15126      * 
15127      * @return this
15128      */
15129     ref public Matrix4d mapnXZY() return {
15130         mapnXZY(this);
15131         return this;
15132     }
15133     public Matrix4d mapnXZY(ref Matrix4d dest) {
15134         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15135         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15136     }
15137     /**
15138      * Multiply <code>this</code> by the matrix
15139      * <pre>
15140      * -1 0  0 0
15141      *  0 0 -1 0
15142      *  0 1  0 0
15143      *  0 0  0 1
15144      * </pre>
15145      * 
15146      * @return this
15147      */
15148     ref public Matrix4d mapnXZnY() return {
15149         mapnXZnY(this);
15150         return this;
15151     }
15152     public Matrix4d mapnXZnY(ref Matrix4d dest) {
15153         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15154         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15155     }
15156     /**
15157      * Multiply <code>this</code> by the matrix
15158      * <pre>
15159      * -1  0 0 0
15160      *  0 -1 0 0
15161      *  0  0 1 0
15162      *  0  0 0 1
15163      * </pre>
15164      * 
15165      * @return this
15166      */
15167     ref public Matrix4d mapnXnYZ() return {
15168         mapnXnYZ(this);
15169         return this;
15170     }
15171     public Matrix4d mapnXnYZ(ref Matrix4d dest) {
15172         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15173     }
15174     /**
15175      * Multiply <code>this</code> by the matrix
15176      * <pre>
15177      * -1  0  0 0
15178      *  0 -1  0 0
15179      *  0  0 -1 0
15180      *  0  0  0 1
15181      * </pre>
15182      * 
15183      * @return this
15184      */
15185     ref public Matrix4d mapnXnYnZ() return {
15186         mapnXnYnZ(this);
15187         return this;
15188     }
15189     public Matrix4d mapnXnYnZ(ref Matrix4d dest) {
15190         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15191     }
15192     /**
15193      * Multiply <code>this</code> by the matrix
15194      * <pre>
15195      * -1  0 0 0
15196      *  0  0 1 0
15197      *  0 -1 0 0
15198      *  0  0 0 1
15199      * </pre>
15200      * 
15201      * @return this
15202      */
15203     ref public Matrix4d mapnXnZY() return {
15204         mapnXnZY(this);
15205         return this;
15206     }
15207     public Matrix4d mapnXnZY(ref Matrix4d dest) {
15208         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15209         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15210     }
15211     /**
15212      * Multiply <code>this</code> by the matrix
15213      * <pre>
15214      * -1  0  0 0
15215      *  0  0 -1 0
15216      *  0 -1  0 0
15217      *  0  0  0 1
15218      * </pre>
15219      * 
15220      * @return this
15221      */
15222     ref public Matrix4d mapnXnZnY() return {
15223         mapnXnZnY(this);
15224         return this;
15225     }
15226     public Matrix4d mapnXnZnY(ref Matrix4d dest) {
15227         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15228         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15229     }
15230     /**
15231      * Multiply <code>this</code> by the matrix
15232      * <pre>
15233      *  0 1 0 0
15234      * -1 0 0 0
15235      *  0 0 1 0
15236      *  0 0 0 1
15237      * </pre>
15238      * 
15239      * @return this
15240      */
15241     ref public Matrix4d mapnYXZ() return {
15242         mapnYXZ(this);
15243         return this;
15244     }
15245     public Matrix4d mapnYXZ(ref Matrix4d dest) {
15246         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15247         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15248     }
15249     /**
15250      * Multiply <code>this</code> by the matrix
15251      * <pre>
15252      *  0 1  0 0
15253      * -1 0  0 0
15254      *  0 0 -1 0
15255      *  0 0  0 1
15256      * </pre>
15257      * 
15258      * @return this
15259      */
15260     ref public Matrix4d mapnYXnZ() return {
15261         mapnYXnZ(this);
15262         return this;
15263     }
15264     public Matrix4d mapnYXnZ(ref Matrix4d dest) {
15265         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15266         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15267     }
15268     /**
15269      * Multiply <code>this</code> by the matrix
15270      * <pre>
15271      *  0 0 1 0
15272      * -1 0 0 0
15273      *  0 1 0 0
15274      *  0 0 0 1
15275      * </pre>
15276      * 
15277      * @return this
15278      */
15279     ref public Matrix4d mapnYZX() return {
15280         mapnYZX(this);
15281         return this;
15282     }
15283     public Matrix4d mapnYZX(ref Matrix4d dest) {
15284         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15285         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15286     }
15287     /**
15288      * Multiply <code>this</code> by the matrix
15289      * <pre>
15290      *  0 0 -1 0
15291      * -1 0  0 0
15292      *  0 1  0 0
15293      *  0 0  0 1
15294      * </pre>
15295      * 
15296      * @return this
15297      */
15298     ref public Matrix4d mapnYZnX() return {
15299         mapnYZnX(this);
15300         return this;
15301     }
15302     public Matrix4d mapnYZnX(ref Matrix4d dest) {
15303         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15304         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(m20)._m11(m21)._m12(m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15305     }
15306     /**
15307      * Multiply <code>this</code> by the matrix
15308      * <pre>
15309      *  0 -1 0 0
15310      * -1  0 0 0
15311      *  0  0 1 0
15312      *  0  0 0 1
15313      * </pre>
15314      * 
15315      * @return this
15316      */
15317     ref public Matrix4d mapnYnXZ() return {
15318         mapnYnXZ(this);
15319         return this;
15320     }
15321     public Matrix4d mapnYnXZ(ref Matrix4d dest) {
15322         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15323         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15324     }
15325     /**
15326      * Multiply <code>this</code> by the matrix
15327      * <pre>
15328      *  0 -1  0 0
15329      * -1  0  0 0
15330      *  0  0 -1 0
15331      *  0  0  0 1
15332      * </pre>
15333      * 
15334      * @return this
15335      */
15336     ref public Matrix4d mapnYnXnZ() return {
15337         mapnYnXnZ(this);
15338         return this;
15339     }
15340     public Matrix4d mapnYnXnZ(ref Matrix4d dest) {
15341         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15342         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15343     }
15344     /**
15345      * Multiply <code>this</code> by the matrix
15346      * <pre>
15347      *  0  0 1 0
15348      * -1  0 0 0
15349      *  0 -1 0 0
15350      *  0  0 0 1
15351      * </pre>
15352      * 
15353      * @return this
15354      */
15355     ref public Matrix4d mapnYnZX() return {
15356         mapnYnZX(this);
15357         return this;
15358     }
15359     public Matrix4d mapnYnZX(ref Matrix4d dest) {
15360         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15361         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15362     }
15363     /**
15364      * Multiply <code>this</code> by the matrix
15365      * <pre>
15366      *  0  0 -1 0
15367      * -1  0  0 0
15368      *  0 -1  0 0
15369      *  0  0  0 1
15370      * </pre>
15371      * 
15372      * @return this
15373      */
15374     ref public Matrix4d mapnYnZnX() return {
15375         mapnYnZnX(this);
15376         return this;
15377     }
15378     public Matrix4d mapnYnZnX(ref Matrix4d dest) {
15379         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15380         return dest._m00(-m10)._m01(-m11)._m02(-m12)._m03(m03)._m10(-m20)._m11(-m21)._m12(-m22)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15381     }
15382     /**
15383      * Multiply <code>this</code> by the matrix
15384      * <pre>
15385      *  0 1 0 0
15386      *  0 0 1 0
15387      * -1 0 0 0
15388      *  0 0 0 1
15389      * </pre>
15390      * 
15391      * @return this
15392      */
15393     ref public Matrix4d mapnZXY() return {
15394         mapnZXY(this);
15395         return this;
15396     }
15397     public Matrix4d mapnZXY(ref Matrix4d dest) {
15398         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15399         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15400         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15401     }
15402     /**
15403      * Multiply <code>this</code> by the matrix
15404      * <pre>
15405      *  0 1  0 0
15406      *  0 0 -1 0
15407      * -1 0  0 0
15408      *  0 0  0 1
15409      * </pre>
15410      * 
15411      * @return this
15412      */
15413     ref public Matrix4d mapnZXnY() return {
15414         mapnZXnY(this);
15415         return this;
15416     }
15417     public Matrix4d mapnZXnY(ref Matrix4d dest) {
15418         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15419         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15420         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m00)._m11(m01)._m12(m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15421     }
15422     /**
15423      * Multiply <code>this</code> by the matrix
15424      * <pre>
15425      *  0 0 1 0
15426      *  0 1 0 0
15427      * -1 0 0 0
15428      *  0 0 0 1
15429      * </pre>
15430      * 
15431      * @return this
15432      */
15433     ref public Matrix4d mapnZYX() return {
15434         mapnZYX(this);
15435         return this;
15436     }
15437     public Matrix4d mapnZYX(ref Matrix4d dest) {
15438         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15439         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15440     }
15441     /**
15442      * Multiply <code>this</code> by the matrix
15443      * <pre>
15444      *  0 0 -1 0
15445      *  0 1  0 0
15446      * -1 0  0 0
15447      *  0 0  0 1
15448      * </pre>
15449      * 
15450      * @return this
15451      */
15452     ref public Matrix4d mapnZYnX() return {
15453         mapnZYnX(this);
15454         return this;
15455     }
15456     public Matrix4d mapnZYnX(ref Matrix4d dest) {
15457         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15458         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15459     }
15460     /**
15461      * Multiply <code>this</code> by the matrix
15462      * <pre>
15463      *  0 -1 0 0
15464      *  0  0 1 0
15465      * -1  0 0 0
15466      *  0  0 0 1
15467      * </pre>
15468      * 
15469      * @return this
15470      */
15471     ref public Matrix4d mapnZnXY() return {
15472         mapnZnXY(this);
15473         return this;
15474     }
15475     public Matrix4d mapnZnXY(ref Matrix4d dest) {
15476         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15477         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15478         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(m10)._m21(m11)._m22(m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15479     }
15480     /**
15481      * Multiply <code>this</code> by the matrix
15482      * <pre>
15483      *  0 -1  0 0
15484      *  0  0 -1 0
15485      * -1  0  0 0
15486      *  0  0  0 1
15487      * </pre>
15488      * 
15489      * @return this
15490      */
15491     ref public Matrix4d mapnZnXnY() return {
15492         mapnZnXnY(this);
15493         return this;
15494     }
15495     public Matrix4d mapnZnXnY(ref Matrix4d dest) {
15496         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15497         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15498         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m00)._m11(-m01)._m12(-m02)._m13(m13)._m20(-m10)._m21(-m11)._m22(-m12)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15499     }
15500     /**
15501      * Multiply <code>this</code> by the matrix
15502      * <pre>
15503      *  0  0 1 0
15504      *  0 -1 0 0
15505      * -1  0 0 0
15506      *  0  0 0 1
15507      * </pre>
15508      * 
15509      * @return this
15510      */
15511     ref public Matrix4d mapnZnYX() return {
15512         mapnZnYX(this);
15513         return this;
15514     }
15515     public Matrix4d mapnZnYX(ref Matrix4d dest) {
15516         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15517         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m00)._m21(m01)._m22(m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15518     }
15519     /**
15520      * Multiply <code>this</code> by the matrix
15521      * <pre>
15522      *  0  0 -1 0
15523      *  0 -1  0 0
15524      * -1  0  0 0
15525      *  0  0  0 1
15526      * </pre>
15527      * 
15528      * @return this
15529      */
15530     ref public Matrix4d mapnZnYnX() return {
15531         mapnZnYnX(this);
15532         return this;
15533     }
15534     public Matrix4d mapnZnYnX(ref Matrix4d dest) {
15535         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15536         return dest._m00(-m20)._m01(-m21)._m02(-m22)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(-m00)._m21(-m01)._m22(-m02)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15537     }
15538 
15539     /**
15540      * Multiply <code>this</code> by the matrix
15541      * <pre>
15542      * -1 0 0 0
15543      *  0 1 0 0
15544      *  0 0 1 0
15545      *  0 0 0 1
15546      * </pre>
15547      * 
15548      * @return this
15549      */
15550     ref public Matrix4d negateX() return {
15551         return _m00(-m00)._m01(-m01)._m02(-m02)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15552     }
15553     public Matrix4d negateX(ref Matrix4d dest) {
15554         return dest._m00(-m00)._m01(-m01)._m02(-m02)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15555     }
15556 
15557     /**
15558      * Multiply <code>this</code> by the matrix
15559      * <pre>
15560      * 1  0 0 0
15561      * 0 -1 0 0
15562      * 0  0 1 0
15563      * 0  0 0 1
15564      * </pre>
15565      * 
15566      * @return this
15567      */
15568     ref public Matrix4d negateY() return {
15569         return _m10(-m10)._m11(-m11)._m12(-m12)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15570     }
15571     public Matrix4d negateY(ref Matrix4d dest) {
15572         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(-m10)._m11(-m11)._m12(-m12)._m13(m13)._m20(m20)._m21(m21)._m22(m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15573     }
15574 
15575     /**
15576      * Multiply <code>this</code> by the matrix
15577      * <pre>
15578      * 1 0  0 0
15579      * 0 1  0 0
15580      * 0 0 -1 0
15581      * 0 0  0 1
15582      * </pre>
15583      * 
15584      * @return this
15585      */
15586     ref public Matrix4d negateZ() return {
15587         return _m20(-m20)._m21(-m21)._m22(-m22)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15588     }
15589     public Matrix4d negateZ(ref Matrix4d dest) {
15590         return dest._m00(m00)._m01(m01)._m02(m02)._m03(m03)._m10(m10)._m11(m11)._m12(m12)._m13(m13)._m20(-m20)._m21(-m21)._m22(-m22)._m23(m23)._m30(m30)._m31(m31)._m32(m32)._m33(m33)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15591     }
15592 
15593     public bool isFinite() {
15594         return Math.isFinite(m00) && Math.isFinite(m01) && Math.isFinite(m02) && Math.isFinite(m03) &&
15595                Math.isFinite(m10) && Math.isFinite(m11) && Math.isFinite(m12) && Math.isFinite(m13) &&
15596                Math.isFinite(m20) && Math.isFinite(m21) && Math.isFinite(m22) && Math.isFinite(m23) &&
15597                Math.isFinite(m30) && Math.isFinite(m31) && Math.isFinite(m32) && Math.isFinite(m33);
15598     }
15599 
15600 }