1 module matrix_4d;
2 
3 import Math = math;
4 import MemUtil = mem_util;
5 
6 import matrix_3d;
7 import matrix_4x3d;
8 import matrix_3x2d;
9 
10 import vector_2d;
11 import vector_3d;
12 import vector_4d;
13 
14 import axis_angle_4d;
15 import quaternion_d;
16 
17 /*
18  * The MIT License
19  $!#@$@ Translated by jordan4ibanez
20  *
21  * Copyright (c) 2015-2021 Richard Greenlees
22  *
23  * Permission is hereby granted, free of charge, to any person obtaining a copy
24  * of this software and associated documentation files (the "Software"), to deal
25  * in the Software without restriction, including without limitation the rights
26  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27  * copies of the Software, and to permit persons to whom the Software is
28  * furnished to do so, subject to the following conditions:
29  *
30  * The above copyright notice and this permission notice shall be included in
31  * all copies or substantial portions of the Software.
32  *
33  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39  * THE SOFTWARE.
40  */
41 
42 /**
43  * Contains the definition of a 4x4 Matrix of doubles, and associated functions to transform
44  * it. The matrix is column-major to match OpenGL's interpretation, and it looks like this:
45  * <p>
46  *      m00  m10  m20  m30<br>
47  *      m01  m11  m21  m31<br>
48  *      m02  m12  m22  m32<br>
49  *      m03  m13  m23  m33<br>
50  * 
51  * @author Richard Greenlees
52  * @author Kai Burjack
53  */
54 
55 
56 
57 struct Matrix4d {
58 
59     double m00 = 1.0;
60     double m01 = 0.0;
61     double m02 = 0.0;
62     double m03 = 0.0;
63 
64     double m10 = 0.0;
65     double m11 = 1.0;
66     double m12 = 0.0;
67     double m13 = 0.0;
68 
69     double m20 = 0.0;
70     double m21 = 0.0;
71     double m22 = 1.0;
72     double m23 = 0.0;
73 
74     double m30 = 0.0;
75     double m31 = 0.0;
76     double m32 = 0.0;
77     double m33 = 1.0;
78 
79    /**
80 
81     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
82     * identifying the plane with equation <code>x=-1</code> when using the identity matrix.  
83     */
84     static immutable int PLANE_NX = 0;
85     /**
86     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
87     * identifying the plane with equation <code>x=1</code> when using the identity matrix.  
88     */
89     static immutable int PLANE_PX = 1;
90     /**
91     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
92     * identifying the plane with equation <code>y=-1</code> when using the identity matrix.  
93     */
94     static immutable int PLANE_NY = 2;
95     /**
96     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
97     * identifying the plane with equation <code>y=1</code> when using the identity matrix.  
98     */
99     static immutable int PLANE_PY = 3;
100     /**
101     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
102     * identifying the plane with equation <code>z=-1</code> when using the identity matrix.  
103     */
104     static immutable int PLANE_NZ = 4;
105     /**
106     * Argument to the first parameter of {@link #frustumPlane(int, Vector4d)}
107     * identifying the plane with equation <code>z=1</code> when using the identity matrix.  
108     */
109     static immutable int PLANE_PZ = 5;
110     /**
111     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
112     * identifying the corner <code>(-1, -1, -1)</code> when using the identity matrix.
113     */
114     static immutable int CORNER_NXNYNZ = 0;
115     /**
116     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
117     * identifying the corner <code>(1, -1, -1)</code> when using the identity matrix.
118     */
119     static immutable int CORNER_PXNYNZ = 1;
120     /**
121     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
122     * identifying the corner <code>(1, 1, -1)</code> when using the identity matrix.
123     */
124     static immutable int CORNER_PXPYNZ = 2;
125     /**
126     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
127     * identifying the corner <code>(-1, 1, -1)</code> when using the identity matrix.
128     */
129     static immutable int CORNER_NXPYNZ = 3;
130     /**
131     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
132     * identifying the corner <code>(1, -1, 1)</code> when using the identity matrix.
133     */
134     static immutable int CORNER_PXNYPZ = 4;
135     /**
136     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
137     * identifying the corner <code>(-1, -1, 1)</code> when using the identity matrix.
138     */
139     static immutable int CORNER_NXNYPZ = 5;
140     /**
141     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
142     * identifying the corner <code>(-1, 1, 1)</code> when using the identity matrix.
143     */
144     static immutable int CORNER_NXPYPZ = 6;
145     /**
146     * Argument to the first parameter of {@link #frustumCorner(int, Vector3d)}
147     * identifying the corner <code>(1, 1, 1)</code> when using the identity matrix.
148     */
149     static immutable int CORNER_PXPYPZ = 7;
150 
151     /**
152     * Bit returned by {@link #properties()} to indicate that the matrix represents a perspective transformation.
153     */
154     static immutable byte PROPERTY_PERSPECTIVE = 1<<0;
155     /**
156     * Bit returned by {@link #properties()} to indicate that the matrix represents an affine transformation.
157     */
158     static immutable byte PROPERTY_AFFINE = 1<<1;
159     /**
160     * Bit returned by {@link #properties()} to indicate that the matrix represents the identity transformation.
161     */
162     static immutable byte PROPERTY_IDENTITY = 1<<2;
163     /**
164     * Bit returned by {@link #properties()} to indicate that the matrix represents a pure translation transformation.
165     */
166     static immutable byte PROPERTY_TRANSLATION = 1<<3;
167     /**
168     * Bit returned by {@link #properties()} to indicate that the upper-left 3x3 submatrix represents an orthogonal
169     * matrix (i.e. orthonormal basis). For practical reasons, this property also always implies 
170     * {@link #PROPERTY_AFFINE} in this implementation.
171     */
172     static immutable byte PROPERTY_ORTHONORMAL = 1<<4;
173 
174 
175 
176     int properties = PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL;
177 
178     /**
179      * Create a new {@link Matrix4d} and make it a copy of the given matrix.
180      * 
181      * @param mat
182      *          the {@link Matrix4d} to copy the values from
183      */
184     this(Matrix4d mat) {
185         set(mat);
186     }
187 
188     /**
189      * Create a new {@link Matrix4d} and set its upper 4x3 submatrix to the given matrix <code>mat</code>
190      * and all other elements to identity.
191      * 
192      * @param mat
193      *          the {@link Matrix4x3d} to copy the values from
194      */
195     this(Matrix4x3d mat) {
196         set(mat);
197     }
198 
199     /**
200      * Create a new {@link Matrix4d} by setting its uppper left 3x3 submatrix to the values of the given {@link Matrix3d}
201      * and the rest to identity.
202      * 
203      * @param mat
204      *          the {@link Matrix3d}
205      */
206     this(Matrix3d mat) {
207         set(mat);
208     }
209 
210     /**
211      * Create a new 4x4 matrix using the supplied double values.
212      * <p>
213      * The matrix layout will be:<br><br>
214      *   m00, m10, m20, m30<br>
215      *   m01, m11, m21, m31<br>
216      *   m02, m12, m22, m32<br>
217      *   m03, m13, m23, m33
218      * 
219      * @param m00
220      *          the value of m00
221      * @param m01
222      *          the value of m01
223      * @param m02
224      *          the value of m02
225      * @param m03
226      *          the value of m03
227      * @param m10
228      *          the value of m10
229      * @param m11
230      *          the value of m11
231      * @param m12
232      *          the value of m12
233      * @param m13
234      *          the value of m13
235      * @param m20
236      *          the value of m20
237      * @param m21
238      *          the value of m21
239      * @param m22
240      *          the value of m22
241      * @param m23
242      *          the value of m23
243      * @param m30
244      *          the value of m30
245      * @param m31
246      *          the value of m31
247      * @param m32
248      *          the value of m32
249      * @param m33
250      *          the value of m33
251      */
252     this(double m00, double m01, double m02, double m03,
253                     double m10, double m11, double m12, double m13, 
254                     double m20, double m21, double m22, double m23, 
255                     double m30, double m31, double m32, double m33) {
256         setm00(m00);
257         setm01(m01);
258         setm02(m02);
259         setm03(m03);
260         setm10(m10);
261         setm11(m11);
262         setm12(m12);
263         setm13(m13);
264         setm20(m20);
265         setm21(m21);
266         setm22(m22);
267         setm23(m23);
268         setm30(m30);
269         setm31(m31);
270         setm32(m32);
271         setm33(m33);
272         determineProperties();
273     }
274 
275     /**
276      * Create a new {@link Matrix4d} and initialize its four columns using the supplied vectors.
277      * 
278      * @param col0
279      *          the first column
280      * @param col1
281      *          the second column
282      * @param col2
283      *          the third column
284      * @param col3
285      *          the fourth column
286      */
287     this(ref Vector4d col0, Vector4d col1, Vector4d col2, Vector4d col3) {
288         set(col0, col1, col2, col3);
289     }
290 
291     /**
292      * Assume the given properties about this matrix.
293      * <p>
294      * Use one or multiple of 0, {@link Matrix4d#PROPERTY_IDENTITY},
295      * {@link Matrix4d#PROPERTY_TRANSLATION}, {@link Matrix4d#PROPERTY_AFFINE},
296      * {@link Matrix4d#PROPERTY_PERSPECTIVE}, {@link Matrix4fc#PROPERTY_ORTHONORMAL}.
297      * 
298      * @param properties
299      *          bitset of the properties to assume about this matrix
300      * @return this
301      */
302     ref public Matrix4d assume(int properties) return {
303         this.properties = cast(byte) properties;
304         return this;
305     }
306 
307     /**
308      * Compute and set the matrix properties returned by {@link #properties()} based
309      * on the current matrix element values.
310      * 
311      * @return this
312      */
313     ref public Matrix4d determineProperties() return {
314         int __properties = 0;
315         if (m03 == 0.0 && m13 == 0.0) {
316             if (m23 == 0.0 && m33 == 1.0) {
317                 __properties |= PROPERTY_AFFINE;
318                 if (m00 == 1.0 && m01 == 0.0 && m02 == 0.0 && m10 == 0.0 && m11 == 1.0 && m12 == 0.0 && m20 == 0.0
319                         && m21 == 0.0 && m22 == 1.0) {
320                     __properties |= PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL;
321                     if (m30 == 0.0 && m31 == 0.0 && m32 == 0.0)
322                         __properties |= PROPERTY_IDENTITY;
323                 }
324                 /* 
325                  * We do not determine orthogonality, since it would require arbitrary epsilons
326                  * and is rather expensive (6 dot products) in the worst case.
327                  */
328             } else if (m01 == 0.0 && m02 == 0.0 && m10 == 0.0 && m12 == 0.0 && m20 == 0.0 && m21 == 0.0 && m30 == 0.0
329                     && m31 == 0.0 && m33 == 0.0) {
330                 __properties |= PROPERTY_PERSPECTIVE;
331             }
332         }
333         this.properties = __properties;
334         return this;
335     }
336 
337     public int getProperties() {
338         return this.properties;
339     }
340 
341     /**
342      * Set the value of the matrix element at column 0 and row 0.
343      * 
344      * @param m00
345      *          the new value
346      * @return this
347      */
348     ref public Matrix4d setm00(double m00) return {
349         this.m00 = m00;
350         properties &= ~PROPERTY_ORTHONORMAL;
351         if (m00 != 1.0)
352             properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
353         return this;
354     }
355     /**
356      * Set the value of the matrix element at column 0 and row 1.
357      * 
358      * @param m01
359      *          the new value
360      * @return this
361      */
362     ref public Matrix4d setm01(double m01) return {
363         this.m01 = m01;
364         properties &= ~PROPERTY_ORTHONORMAL;
365         if (m01 != 0.0)
366             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
367         return this;
368     }
369     /**
370      * Set the value of the matrix element at column 0 and row 2.
371      * 
372      * @param m02
373      *          the new value
374      * @return this
375      */
376     ref public Matrix4d setm02(double m02) return {
377         this.m02 = m02;
378         properties &= ~PROPERTY_ORTHONORMAL;
379         if (m02 != 0.0)
380             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
381         return this;
382     }
383     /**
384      * Set the value of the matrix element at column 0 and row 3.
385      * 
386      * @param m03
387      *          the new value
388      * @return this
389      */
390     ref public Matrix4d setm03(double m03) return {
391         this.m03 = m03;
392         if (m03 != 0.0)
393             properties = 0;
394         return this;
395     }
396     /**
397      * Set the value of the matrix element at column 1 and row 0.
398      * 
399      * @param m10
400      *          the new value
401      * @return this
402      */
403     ref public Matrix4d setm10(double m10) return {
404         this.m10 = m10;
405         properties &= ~PROPERTY_ORTHONORMAL;
406         if (m10 != 0.0)
407             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
408         return this;
409     }
410     /**
411      * Set the value of the matrix element at column 1 and row 1.
412      * 
413      * @param m11
414      *          the new value
415      * @return this
416      */
417     ref public Matrix4d setm11(double m11) return {
418         this.m11 = m11;
419         properties &= ~PROPERTY_ORTHONORMAL;
420         if (m11 != 1.0)
421             properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
422         return this;
423     }
424     /**
425      * Set the value of the matrix element at column 1 and row 2.
426      * 
427      * @param m12
428      *          the new value
429      * @return this
430      */
431     ref public Matrix4d setm12(double m12) return {
432         this.m12 = m12;
433         properties &= ~PROPERTY_ORTHONORMAL;
434         if (m12 != 0.0)
435             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
436         return this;
437     }
438     /**
439      * Set the value of the matrix element at column 1 and row 3.
440      * 
441      * @param m13
442      *          the new value
443      * @return this
444      */
445     ref public Matrix4d setm13(double m13) return {
446         this.m13 = m13;
447         if (m03 != 0.0)
448             properties = 0;
449         return this;
450     }
451     /**
452      * Set the value of the matrix element at column 2 and row 0.
453      * 
454      * @param m20
455      *          the new value
456      * @return this
457      */
458     ref public Matrix4d setm20(double m20) return {
459         this.m20 = m20;
460         properties &= ~PROPERTY_ORTHONORMAL;
461         if (m20 != 0.0)
462             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
463         return this;
464     }
465     /**
466      * Set the value of the matrix element at column 2 and row 1.
467      * 
468      * @param m21
469      *          the new value
470      * @return this
471      */
472     ref public Matrix4d setm21(double m21) return {
473         this.m21 = m21;
474         properties &= ~PROPERTY_ORTHONORMAL;
475         if (m21 != 0.0)
476             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION);
477         return this;
478     }
479     /**
480      * Set the value of the matrix element at column 2 and row 2.
481      * 
482      * @param m22
483      *          the new value
484      * @return this
485      */
486     ref public Matrix4d setm22(double m22) return {
487         this.m22 = m22;
488         properties &= ~PROPERTY_ORTHONORMAL;
489         if (m22 != 1.0)
490             properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
491         return this;
492     }
493     /**
494      * Set the value of the matrix element at column 2 and row 3.
495      * 
496      * @param m23
497      *          the new value
498      * @return this
499      */
500     ref public Matrix4d setm23(double m23) return {
501         this.m23 = m23;
502         if (m23 != 0.0)
503             properties &= ~(PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL);
504         return this;
505     }
506     /**
507      * Set the value of the matrix element at column 3 and row 0.
508      * 
509      * @param m30
510      *          the new value
511      * @return this
512      */
513     ref public Matrix4d setm30(double m30) return {
514         this.m30 = m30;
515         if (m30 != 0.0)
516             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE);
517         return this;
518     }
519     /**
520      * Set the value of the matrix element at column 3 and row 1.
521      * 
522      * @param m31
523      *          the new value
524      * @return this
525      */
526     ref public Matrix4d setm31(double m31) return {
527         this.m31 = m31;
528         if (m31 != 0.0)
529             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE);
530         return this;
531     }
532     /**
533      * Set the value of the matrix element at column 3 and row 2.
534      * 
535      * @param m32
536      *          the new value
537      * @return this
538      */
539     ref public Matrix4d setm32(double m32) return {
540         this.m32 = m32;
541         if (m32 != 0.0)
542             properties &= ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE);
543         return this;
544     }
545     /**
546      * Set the value of the matrix element at column 3 and row 3.
547      * 
548      * @param m33
549      *          the new value
550      * @return this
551      */
552     ref public Matrix4d setm33(double m33) return {
553         this.m33 = m33;
554         if (m33 != 0.0)
555             properties &= ~(PROPERTY_PERSPECTIVE);
556         if (m33 != 1.0)
557             properties &= ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL | PROPERTY_AFFINE);
558         return this;
559     }
560 
561     ref Matrix4d _properties(int properties) return {
562         this.properties = properties;
563         return this;
564     }
565 
566     /**
567      * Set the value of the matrix element at column 0 and row 0 without updating the properties of the matrix.
568      * 
569      * @param m00
570      *          the new value
571      * @return this
572      */
573     ref Matrix4d _m00(double m00) return {
574         setm00(m00);
575         return this;
576     }
577     /**
578      * Set the value of the matrix element at column 0 and row 1 without updating the properties of the matrix.
579      * 
580      * @param m01
581      *          the new value
582      * @return this
583      */
584     ref Matrix4d _m01(double m01) return {
585         setm01(m01);
586         return this;
587     }
588     /**
589      * Set the value of the matrix element at column 0 and row 2 without updating the properties of the matrix.
590      * 
591      * @param m02
592      *          the new value
593      * @return this
594      */
595     ref Matrix4d _m02(double m02) return {
596         setm02(m02);
597         return this;
598     }
599     /**
600      * Set the value of the matrix element at column 0 and row 3 without updating the properties of the matrix.
601      * 
602      * @param m03
603      *          the new value
604      * @return this
605      */
606     ref Matrix4d _m03(double m03) return {
607         setm03(m03);
608         return this;
609     }
610     /**
611      * Set the value of the matrix element at column 1 and row 0 without updating the properties of the matrix.
612      * 
613      * @param m10
614      *          the new value
615      * @return this
616      */
617     ref Matrix4d _m10(double m10) return {
618         setm10(m10);
619         return this;
620     }
621     /**
622      * Set the value of the matrix element at column 1 and row 1 without updating the properties of the matrix.
623      * 
624      * @param m11
625      *          the new value
626      * @return this
627      */
628     ref Matrix4d _m11(double m11) return {
629         setm11(m11);
630         return this;
631     }
632     /**
633      * Set the value of the matrix element at column 1 and row 2 without updating the properties of the matrix.
634      * 
635      * @param m12
636      *          the new value
637      * @return this
638      */
639     ref Matrix4d _m12(double m12) return {
640         setm12(m12);
641         return this;
642     }
643     /**
644      * Set the value of the matrix element at column 1 and row 3 without updating the properties of the matrix.
645      * 
646      * @param m13
647      *          the new value
648      * @return this
649      */
650     ref Matrix4d _m13(double m13) return {
651         setm13(m13);
652         return this;
653     }
654     /**
655      * Set the value of the matrix element at column 2 and row 0 without updating the properties of the matrix.
656      * 
657      * @param m20
658      *          the new value
659      * @return this
660      */
661     ref Matrix4d _m20(double m20) return {
662         setm20(m20);
663         return this;
664     }
665     /**
666      * Set the value of the matrix element at column 2 and row 1 without updating the properties of the matrix.
667      * 
668      * @param m21
669      *          the new value
670      * @return this
671      */
672     ref Matrix4d _m21(double m21) return {
673         setm21(m21);
674         return this;
675     }
676     /**
677      * Set the value of the matrix element at column 2 and row 2 without updating the properties of the matrix.
678      * 
679      * @param m22
680      *          the new value
681      * @return this
682      */
683     ref Matrix4d _m22(double m22) return {
684         setm22(m22);
685         return this;
686     }
687     /**
688      * Set the value of the matrix element at column 2 and row 3 without updating the properties of the matrix.
689      * 
690      * @param m23
691      *          the new value
692      * @return this
693      */
694     ref Matrix4d _m23(double m23) return {
695         setm23(m23);
696         return this;
697     }
698     /**
699      * Set the value of the matrix element at column 3 and row 0 without updating the properties of the matrix.
700      * 
701      * @param m30
702      *          the new value
703      * @return this
704      */
705     ref Matrix4d _m30(double m30) return {
706         setm30(m30);
707         return this;
708     }
709     /**
710      * Set the value of the matrix element at column 3 and row 1 without updating the properties of the matrix.
711      * 
712      * @param m31
713      *          the new value
714      * @return this
715      */
716     ref Matrix4d _m31(double m31) return {
717         setm31(m31);
718         return this;
719     }
720     /**
721      * Set the value of the matrix element at column 3 and row 2 without updating the properties of the matrix.
722      * 
723      * @param m32
724      *          the new value
725      * @return this
726      */
727     ref Matrix4d _m32(double m32) return {
728         setm32(m32);
729         return this;
730     }
731     /**
732      * Set the value of the matrix element at column 3 and row 3 without updating the properties of the matrix.
733      * 
734      * @param m33
735      *          the new value
736      * @return this
737      */
738     ref Matrix4d _m33(double m33) return {
739         setm33(m33);
740         return this;
741     }
742 
743     /**
744      * Reset this matrix to the identity.
745      * <p>
746      * Please note that if a call to {@link #identity()} is immediately followed by a call to:
747      * {@link #translate(double, double, double) translate}, 
748      * {@link #rotate(double, double, double, double) rotate},
749      * {@link #scale(double, double, double) scale},
750      * {@link #perspective(double, double, double, double) perspective},
751      * {@link #frustum(double, double, double, double, double, double) frustum},
752      * {@link #ortho(double, double, double, double, double, double) ortho},
753      * {@link #ortho2D(double, double, double, double) ortho2D},
754      * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt},
755      * {@link #lookAlong(double, double, double, double, double, double) lookAlong},
756      * or any of their overloads, then the call to {@link #identity()} can be omitted and the subsequent call replaced with:
757      * {@link #translation(double, double, double) translation},
758      * {@link #rotation(double, double, double, double) rotation},
759      * {@link #scaling(double, double, double) scaling},
760      * {@link #setPerspective(double, double, double, double) setPerspective},
761      * {@link #setFrustum(double, double, double, double, double, double) setFrustum},
762      * {@link #setOrtho(double, double, double, double, double, double) setOrtho},
763      * {@link #setOrtho2D(double, double, double, double) setOrtho2D},
764      * {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt},
765      * {@link #setLookAlong(double, double, double, double, double, double) setLookAlong},
766      * or any of their overloads.
767      * 
768      * @return this
769      */
770     ref public Matrix4d identity() return {
771         if ((properties & PROPERTY_IDENTITY) != 0)
772             return this;
773         _identity();
774         properties = PROPERTY_IDENTITY | PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL;
775         return this;
776     }
777     private void _identity() {
778         _m00(1.0).
779         _m10(0.0).
780         _m20(0.0).
781         _m30(0.0).
782         _m01(0.0).
783         _m11(1.0).
784         _m21(0.0).
785         _m31(0.0).
786         _m02(0.0).
787         _m12(0.0).
788         _m22(1.0).
789         _m32(0.0).
790         _m03(0.0).
791         _m13(0.0).
792         _m23(0.0).
793         _m33(1.0);
794     }
795 
796     /**
797      * Store the values of the given matrix <code>m</code> into <code>this</code> matrix.
798      * 
799      * @see #Matrix4d(Matrix4d)
800      * @see #get(Matrix4d)
801      * 
802      * @param m
803      *          the matrix to copy the values from
804      * @return this
805      */
806     ref public Matrix4d set(Matrix4d m) return {
807         return
808         _m00(m.m00).
809         _m01(m.m01).
810         _m02(m.m02).
811         _m03(m.m03).
812         _m10(m.m10).
813         _m11(m.m11).
814         _m12(m.m12).
815         _m13(m.m13).
816         _m20(m.m20).
817         _m21(m.m21).
818         _m22(m.m22).
819         _m23(m.m23).
820         _m30(m.m30).
821         _m31(m.m31).
822         _m32(m.m32).
823         _m33(m.m33).
824         _properties(m.properties);
825     }
826 
827 
828     /**
829      * Store the values of the transpose of the given matrix <code>m</code> into <code>this</code> matrix.
830      * 
831      * @param m
832      *          the matrix to copy the transposed values from
833      * @return this
834      */
835     ref public Matrix4d setTransposed(Matrix4d m) return {
836         if ((m.properties & PROPERTY_IDENTITY) != 0)
837             return this.identity();
838         return setTransposedInternal(m);
839     }
840     ref private Matrix4d setTransposedInternal(Matrix4d m) return {
841         double nm10 = m.m01, nm12 = m.m21, nm13 = m.m31;
842         double nm20 = m.m02, nm21 = m.m12, nm30 = m.m03;
843         double nm31 = m.m13, nm32 = m.m23;
844         return this
845         ._m00(m.m00)._m01(m.m10)._m02(m.m20)._m03(m.m30)
846         ._m10(nm10)._m11(m.m11)._m12(nm12)._m13(nm13)
847         ._m20(nm20)._m21(nm21)._m22(m.m22)._m23(m.m32)
848         ._m30(nm30)._m31(nm31)._m32(nm32)._m33(m.m33)
849         ._properties(m.properties & PROPERTY_IDENTITY);
850     }
851 
852     /**
853      * Store the values of the given matrix <code>m</code> into <code>this</code> matrix
854      * and set the other matrix elements to identity.
855      * 
856      * @see #Matrix4d(Matrix4x3d)
857      * 
858      * @param m
859      *          the matrix to copy the values from
860      * @return this
861      */
862     ref public Matrix4d set(Matrix4x3d m) return {
863         return
864         _m00(m.m00).
865         _m01(m.m01).
866         _m02(m.m02).
867         _m03(0.0).
868         _m10(m.m10).
869         _m11(m.m11).
870         _m12(m.m12).
871         _m13(0.0).
872         _m20(m.m20).
873         _m21(m.m21).
874         _m22(m.m22).
875         _m23(0.0).
876         _m30(m.m30).
877         _m31(m.m31).
878         _m32(m.m32).
879         _m33(1.0).
880         _properties(m.properties | PROPERTY_AFFINE);
881     }
882 
883    
884     /**
885      * Set the upper left 3x3 submatrix of this {@link Matrix4d} to the given {@link Matrix3d} 
886      * and the rest to identity.
887      * 
888      * @see #Matrix4d(Matrix3d)
889      * 
890      * @param mat
891      *          the {@link Matrix3d}
892      * @return this
893      */
894     ref public Matrix4d set(Matrix3d mat) return {
895         return
896         _m00(mat.m00).
897         _m01(mat.m01).
898         _m02(mat.m02).
899         _m03(0.0).
900         _m10(mat.m10).
901         _m11(mat.m11).
902         _m12(mat.m12).
903         _m13(0.0).
904         _m20(mat.m20).
905         _m21(mat.m21).
906         _m22(mat.m22).
907         _m23(0.0).
908         _m30(0.0).
909         _m31(0.0).
910         _m32(0.0).
911         _m33(1.0).
912         _properties(PROPERTY_AFFINE);
913     }
914 
915     /**
916      * Set the upper left 3x3 submatrix of this {@link Matrix4d} to that of the given {@link Matrix4d} 
917      * and don't change the other elements.
918      * 
919      * @param mat
920      *          the {@link Matrix4d}
921      * @return this
922      */
923     ref public Matrix4d set3x3(Matrix4d mat) return {
924         return
925         _m00(mat.m00).
926         _m01(mat.m01).
927         _m02(mat.m02).
928         _m10(mat.m10).
929         _m11(mat.m11).
930         _m12(mat.m12).
931         _m20(mat.m20).
932         _m21(mat.m21).
933         _m22(mat.m22).
934         _properties(properties & mat.properties & ~(PROPERTY_PERSPECTIVE));
935     }
936 
937     /**
938      * Set the upper 4x3 submatrix of this {@link Matrix4d} to the given {@link Matrix4x3d} 
939      * and don't change the other elements.
940      * 
941      * @see Matrix4x3d#get(Matrix4d)
942      * 
943      * @param mat
944      *          the {@link Matrix4x3d}
945      * @return this
946      */
947     ref public Matrix4d set4x3(Matrix4x3d mat) return {
948         return
949         _m00(mat.m00).
950         _m01(mat.m01).
951         _m02(mat.m02).
952         _m10(mat.m10).
953         _m11(mat.m11).
954         _m12(mat.m12).
955         _m20(mat.m20).
956         _m21(mat.m21).
957         _m22(mat.m22).
958         _m30(mat.m30).
959         _m31(mat.m31).
960         _m32(mat.m32).
961         _properties(properties & mat.properties & ~(PROPERTY_PERSPECTIVE));
962     }
963 
964 
965     /**
966      * Set the upper 4x3 submatrix of this {@link Matrix4d} to the upper 4x3 submatrix of the given {@link Matrix4d} 
967      * and don't change the other elements.
968      * 
969      * @param mat
970      *          the {@link Matrix4d}
971      * @return this
972      */
973     ref public Matrix4d set4x3(Matrix4d mat) return {
974         return
975         _m00(mat.m00).
976         _m01(mat.m01).
977         _m02(mat.m02).
978         _m10(mat.m10).
979         _m11(mat.m11).
980         _m12(mat.m12).
981         _m20(mat.m20).
982         _m21(mat.m21).
983         _m22(mat.m22).
984         _m30(mat.m30).
985         _m31(mat.m31).
986         _m32(mat.m32).
987         _properties(properties & mat.properties & ~(PROPERTY_PERSPECTIVE));
988     }
989 
990     /**
991      * Set this matrix to be equivalent to the rotation specified by the given {@link AxisAngle4d}.
992      * 
993      * @param axisAngle
994      *          the {@link AxisAngle4d}
995      * @return this
996      */
997     ref public Matrix4d set(ref AxisAngle4d axisAngle) return {
998         double x = axisAngle.x;
999         double y = axisAngle.y;
1000         double z = axisAngle.z;
1001         double angle = axisAngle.angle;
1002         double invLength = Math.invsqrt(x*x + y*y + z*z);
1003         x *= invLength;
1004         y *= invLength;
1005         z *= invLength;
1006         double s = Math.sin(angle);
1007         double c = Math.cosFromSin(s, angle);
1008         double omc = 1.0 - c;
1009         _m00(c + x*x*omc).
1010         _m11(c + y*y*omc).
1011         _m22(c + z*z*omc);
1012         double tmp1 = x*y*omc;
1013         double tmp2 = z*s;
1014         _m10(tmp1 - tmp2).
1015         _m01(tmp1 + tmp2);
1016         tmp1 = x*z*omc;
1017         tmp2 = y*s;
1018         _m20(tmp1 + tmp2).
1019         _m02(tmp1 - tmp2);
1020         tmp1 = y*z*omc;
1021         tmp2 = x*s;
1022         _m21(tmp1 - tmp2).
1023         _m12(tmp1 + tmp2).
1024         _m03(0.0).
1025         _m13(0.0).
1026         _m23(0.0).
1027         _m30(0.0).
1028         _m31(0.0).
1029         _m32(0.0).
1030         _m33(1.0).
1031         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
1032         return this;
1033     }
1034 
1035 
1036     /**
1037      * Set this matrix to be equivalent to the rotation - and possibly scaling - specified by the given {@link Quaterniond}.
1038      * <p>
1039      * This method is equivalent to calling: <code>rotation(q)</code>
1040      * <p>
1041      * Reference: <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/">http://www.euclideanspace.com/</a>
1042      * 
1043      * @see #rotation(ref Quaterniond)
1044      * 
1045      * @param q
1046      *          the {@link Quaterniond}
1047      * @return this
1048      */
1049     ref public Matrix4d set(ref Quaterniond q) return {
1050         return rotation(q);
1051     }
1052 
1053     /**
1054      * Multiply this matrix by the supplied <code>right</code> matrix.
1055      * <p>
1056      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1057      * then the new matrix will be <code>M * R</code>. So when transforming a
1058      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1059      * transformation of the right matrix will be applied first!
1060      * 
1061      * @param right
1062      *          the right operand of the multiplication
1063      * @return this
1064      */
1065     ref public Matrix4d mul(Matrix4d right) return {
1066         mul(right, this);
1067         return this;
1068     }
1069 
1070     public Matrix4d mul(Matrix4d right, ref Matrix4d dest) {
1071         if ((properties & PROPERTY_IDENTITY) != 0)
1072             return dest.set(right);
1073         else if ((right.properties & PROPERTY_IDENTITY) != 0)
1074             return dest.set(this);
1075         else if ((properties & PROPERTY_TRANSLATION) != 0 && (right.properties & PROPERTY_AFFINE) != 0)
1076             return mulTranslationAffine(right, dest);
1077         else if ((properties & PROPERTY_AFFINE) != 0 && (right.properties & PROPERTY_AFFINE) != 0)
1078             return mulAffine(right, dest);
1079         else if ((properties & PROPERTY_PERSPECTIVE) != 0 && (right.properties & PROPERTY_AFFINE) != 0)
1080             return mulPerspectiveAffine(right, dest);
1081         else if ((right.properties & PROPERTY_AFFINE) != 0)
1082             return mulAffineR(right, dest);
1083         return mul0(right, dest);
1084     }
1085 
1086     /**
1087      * Multiply this matrix by the supplied <code>right</code> matrix.
1088      * <p>
1089      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1090      * then the new matrix will be <code>M * R</code>. So when transforming a
1091      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1092      * transformation of the right matrix will be applied first!
1093      * <p>
1094      * This method neither assumes nor checks for any matrix properties of <code>this</code> or <code>right</code>
1095      * and will always perform a complete 4x4 matrix multiplication. This method should only be used whenever the
1096      * multiplied matrices do not have any properties for which there are optimized multiplication methods available.
1097      * 
1098      * @param right
1099      *          the right operand of the matrix multiplication
1100      * @return this
1101      */
1102     ref public Matrix4d mul0(Matrix4d right) return {
1103        mul0(right, this);
1104        return this;
1105     }
1106 
1107     public Matrix4d mul0(Matrix4d right, ref Matrix4d dest) {
1108         double nm00 = Math.fma(m00, right.m00, Math.fma(m10, right.m01, Math.fma(m20, right.m02, m30 * right.m03)));
1109         double nm01 = Math.fma(m01, right.m00, Math.fma(m11, right.m01, Math.fma(m21, right.m02, m31 * right.m03)));
1110         double nm02 = Math.fma(m02, right.m00, Math.fma(m12, right.m01, Math.fma(m22, right.m02, m32 * right.m03)));
1111         double nm03 = Math.fma(m03, right.m00, Math.fma(m13, right.m01, Math.fma(m23, right.m02, m33 * right.m03)));
1112         double nm10 = Math.fma(m00, right.m10, Math.fma(m10, right.m11, Math.fma(m20, right.m12, m30 * right.m13)));
1113         double nm11 = Math.fma(m01, right.m10, Math.fma(m11, right.m11, Math.fma(m21, right.m12, m31 * right.m13)));
1114         double nm12 = Math.fma(m02, right.m10, Math.fma(m12, right.m11, Math.fma(m22, right.m12, m32 * right.m13)));
1115         double nm13 = Math.fma(m03, right.m10, Math.fma(m13, right.m11, Math.fma(m23, right.m12, m33 * right.m13)));
1116         double nm20 = Math.fma(m00, right.m20, Math.fma(m10, right.m21, Math.fma(m20, right.m22, m30 * right.m23)));
1117         double nm21 = Math.fma(m01, right.m20, Math.fma(m11, right.m21, Math.fma(m21, right.m22, m31 * right.m23)));
1118         double nm22 = Math.fma(m02, right.m20, Math.fma(m12, right.m21, Math.fma(m22, right.m22, m32 * right.m23)));
1119         double nm23 = Math.fma(m03, right.m20, Math.fma(m13, right.m21, Math.fma(m23, right.m22, m33 * right.m23)));
1120         double nm30 = Math.fma(m00, right.m30, Math.fma(m10, right.m31, Math.fma(m20, right.m32, m30 * right.m33)));
1121         double nm31 = Math.fma(m01, right.m30, Math.fma(m11, right.m31, Math.fma(m21, right.m32, m31 * right.m33)));
1122         double nm32 = Math.fma(m02, right.m30, Math.fma(m12, right.m31, Math.fma(m22, right.m32, m32 * right.m33)));
1123         double nm33 = Math.fma(m03, right.m30, Math.fma(m13, right.m31, Math.fma(m23, right.m32, m33 * right.m33)));
1124         return dest
1125         ._m00(nm00)
1126         ._m01(nm01)
1127         ._m02(nm02)
1128         ._m03(nm03)
1129         ._m10(nm10)
1130         ._m11(nm11)
1131         ._m12(nm12)
1132         ._m13(nm13)
1133         ._m20(nm20)
1134         ._m21(nm21)
1135         ._m22(nm22)
1136         ._m23(nm23)
1137         ._m30(nm30)
1138         ._m31(nm31)
1139         ._m32(nm32)
1140         ._m33(nm33)
1141         ._properties(0);
1142     }
1143 
1144     /**
1145      * Multiply this matrix by the matrix with the supplied elements.
1146      * <p>
1147      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix whose 
1148      * elements are supplied via the parameters, then the new matrix will be <code>M * R</code>.
1149      * So when transforming a vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1150      * transformation of the right matrix will be applied first!
1151      *
1152      * @param r00
1153      *          the m00 element of the right matrix
1154      * @param r01
1155      *          the m01 element of the right matrix
1156      * @param r02
1157      *          the m02 element of the right matrix
1158      * @param r03
1159      *          the m03 element of the right matrix
1160      * @param r10
1161      *          the m10 element of the right matrix
1162      * @param r11
1163      *          the m11 element of the right matrix
1164      * @param r12
1165      *          the m12 element of the right matrix
1166      * @param r13
1167      *          the m13 element of the right matrix
1168      * @param r20
1169      *          the m20 element of the right matrix
1170      * @param r21
1171      *          the m21 element of the right matrix
1172      * @param r22
1173      *          the m22 element of the right matrix
1174      * @param r23
1175      *          the m23 element of the right matrix
1176      * @param r30
1177      *          the m30 element of the right matrix
1178      * @param r31
1179      *          the m31 element of the right matrix
1180      * @param r32
1181      *          the m32 element of the right matrix
1182      * @param r33
1183      *          the m33 element of the right matrix
1184      * @return this
1185      */
1186     ref public Matrix4d mul(
1187             double r00, double r01, double r02, double r03,
1188             double r10, double r11, double r12, double r13,
1189             double r20, double r21, double r22, double r23,
1190             double r30, double r31, double r32, double r33) return {
1191         mul(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, this);
1192         return this;
1193     }
1194 
1195     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, ref Matrix4d dest) {
1200         if ((properties & PROPERTY_IDENTITY) != 0)
1201             return dest.set(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33);
1202         else if ((properties & PROPERTY_AFFINE) != 0)
1203             return mulAffineL(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, dest);
1204         return mulGeneric(r00, r01, r02, r03, r10, r11, r12, r13, r20, r21, r22, r23, r30, r31, r32, r33, dest);
1205     }
1206     private Matrix4d mulAffineL(
1207             double r00, double r01, double r02, double r03,
1208             double r10, double r11, double r12, double r13,
1209             double r20, double r21, double r22, double r23,
1210             double r30, double r31, double r32, double r33, ref Matrix4d dest) {
1211         double nm00 = Math.fma(m00, r00, Math.fma(m10, r01, Math.fma(m20, r02, m30 * r03)));
1212         double nm01 = Math.fma(m01, r00, Math.fma(m11, r01, Math.fma(m21, r02, m31 * r03)));
1213         double nm02 = Math.fma(m02, r00, Math.fma(m12, r01, Math.fma(m22, r02, m32 * r03)));
1214         double nm03 = r03;
1215         double nm10 = Math.fma(m00, r10, Math.fma(m10, r11, Math.fma(m20, r12, m30 * r13)));
1216         double nm11 = Math.fma(m01, r10, Math.fma(m11, r11, Math.fma(m21, r12, m31 * r13)));
1217         double nm12 = Math.fma(m02, r10, Math.fma(m12, r11, Math.fma(m22, r12, m32 * r13)));
1218         double nm13 = r13;
1219         double nm20 = Math.fma(m00, r20, Math.fma(m10, r21, Math.fma(m20, r22, m30 * r23)));
1220         double nm21 = Math.fma(m01, r20, Math.fma(m11, r21, Math.fma(m21, r22, m31 * r23)));
1221         double nm22 = Math.fma(m02, r20, Math.fma(m12, r21, Math.fma(m22, r22, m32 * r23)));
1222         double nm23 = r23;
1223         double nm30 = Math.fma(m00, r30, Math.fma(m10, r31, Math.fma(m20, r32, m30 * r33)));
1224         double nm31 = Math.fma(m01, r30, Math.fma(m11, r31, Math.fma(m21, r32, m31 * r33)));
1225         double nm32 = Math.fma(m02, r30, Math.fma(m12, r31, Math.fma(m22, r32, m32 * r33)));
1226         double nm33 = r33;
1227         return dest
1228         ._m00(nm00)
1229         ._m01(nm01)
1230         ._m02(nm02)
1231         ._m03(nm03)
1232         ._m10(nm10)
1233         ._m11(nm11)
1234         ._m12(nm12)
1235         ._m13(nm13)
1236         ._m20(nm20)
1237         ._m21(nm21)
1238         ._m22(nm22)
1239         ._m23(nm23)
1240         ._m30(nm30)
1241         ._m31(nm31)
1242         ._m32(nm32)
1243         ._m33(nm33)
1244         ._properties(PROPERTY_AFFINE);
1245     }
1246     private Matrix4d mulGeneric(
1247             double r00, double r01, double r02, double r03,
1248             double r10, double r11, double r12, double r13,
1249             double r20, double r21, double r22, double r23,
1250             double r30, double r31, double r32, double r33, ref Matrix4d dest) {
1251         double nm00 = Math.fma(m00, r00, Math.fma(m10, r01, Math.fma(m20, r02, m30 * r03)));
1252         double nm01 = Math.fma(m01, r00, Math.fma(m11, r01, Math.fma(m21, r02, m31 * r03)));
1253         double nm02 = Math.fma(m02, r00, Math.fma(m12, r01, Math.fma(m22, r02, m32 * r03)));
1254         double nm03 = Math.fma(m03, r00, Math.fma(m13, r01, Math.fma(m23, r02, m33 * r03)));
1255         double nm10 = Math.fma(m00, r10, Math.fma(m10, r11, Math.fma(m20, r12, m30 * r13)));
1256         double nm11 = Math.fma(m01, r10, Math.fma(m11, r11, Math.fma(m21, r12, m31 * r13)));
1257         double nm12 = Math.fma(m02, r10, Math.fma(m12, r11, Math.fma(m22, r12, m32 * r13)));
1258         double nm13 = Math.fma(m03, r10, Math.fma(m13, r11, Math.fma(m23, r12, m33 * r13)));
1259         double nm20 = Math.fma(m00, r20, Math.fma(m10, r21, Math.fma(m20, r22, m30 * r23)));
1260         double nm21 = Math.fma(m01, r20, Math.fma(m11, r21, Math.fma(m21, r22, m31 * r23)));
1261         double nm22 = Math.fma(m02, r20, Math.fma(m12, r21, Math.fma(m22, r22, m32 * r23)));
1262         double nm23 = Math.fma(m03, r20, Math.fma(m13, r21, Math.fma(m23, r22, m33 * r23)));
1263         double nm30 = Math.fma(m00, r30, Math.fma(m10, r31, Math.fma(m20, r32, m30 * r33)));
1264         double nm31 = Math.fma(m01, r30, Math.fma(m11, r31, Math.fma(m21, r32, m31 * r33)));
1265         double nm32 = Math.fma(m02, r30, Math.fma(m12, r31, Math.fma(m22, r32, m32 * r33)));
1266         double nm33 = Math.fma(m03, r30, Math.fma(m13, r31, Math.fma(m23, r32, m33 * r33)));
1267         return dest
1268         ._m00(nm00)
1269         ._m01(nm01)
1270         ._m02(nm02)
1271         ._m03(nm03)
1272         ._m10(nm10)
1273         ._m11(nm11)
1274         ._m12(nm12)
1275         ._m13(nm13)
1276         ._m20(nm20)
1277         ._m21(nm21)
1278         ._m22(nm22)
1279         ._m23(nm23)
1280         ._m30(nm30)
1281         ._m31(nm31)
1282         ._m32(nm32)
1283         ._m33(nm33)
1284         ._properties(0);
1285     }
1286 
1287     /**
1288      * Multiply this matrix by the 3x3 matrix with the supplied elements expanded to a 4x4 matrix with 
1289      * all other matrix elements set to identity.
1290      * <p>
1291      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix whose 
1292      * elements are supplied via the parameters, then the new matrix will be <code>M * R</code>.
1293      * So when transforming a vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1294      * transformation of the right matrix will be applied first!
1295      *
1296      * @param r00
1297      *          the m00 element of the right matrix
1298      * @param r01
1299      *          the m01 element of the right matrix
1300      * @param r02
1301      *          the m02 element of the right matrix
1302      * @param r10
1303      *          the m10 element of the right matrix
1304      * @param r11
1305      *          the m11 element of the right matrix
1306      * @param r12
1307      *          the m12 element of the right matrix
1308      * @param r20
1309      *          the m20 element of the right matrix
1310      * @param r21
1311      *          the m21 element of the right matrix
1312      * @param r22
1313      *          the m22 element of the right matrix
1314      * @return this
1315      */
1316     ref public Matrix4d mul3x3(
1317             double r00, double r01, double r02,
1318             double r10, double r11, double r12,
1319             double r20, double r21, double r22) return {
1320         mul3x3(r00, r01, r02, r10, r11, r12, r20, r21, r22, this);
1321         return this;
1322     }
1323     public Matrix4d mul3x3(
1324             double r00, double r01, double r02,
1325             double r10, double r11, double r12,
1326             double r20, double r21, double r22, ref Matrix4d dest) {
1327         if ((properties & PROPERTY_IDENTITY) != 0)
1328             return dest.set(r00, r01, r02, 0, r10, r11, r12, 0, r20, r21, r22, 0, 0, 0, 0, 1);
1329         return mulGeneric3x3(r00, r01, r02, r10, r11, r12, r20, r21, r22, dest);
1330     }
1331     private Matrix4d mulGeneric3x3(
1332             double r00, double r01, double r02,
1333             double r10, double r11, double r12,
1334             double r20, double r21, double r22, ref Matrix4d dest) {
1335         double nm00 = Math.fma(m00, r00, Math.fma(m10, r01, m20 * r02));
1336         double nm01 = Math.fma(m01, r00, Math.fma(m11, r01, m21 * r02));
1337         double nm02 = Math.fma(m02, r00, Math.fma(m12, r01, m22 * r02));
1338         double nm03 = Math.fma(m03, r00, Math.fma(m13, r01, m23 * r02));
1339         double nm10 = Math.fma(m00, r10, Math.fma(m10, r11, m20 * r12));
1340         double nm11 = Math.fma(m01, r10, Math.fma(m11, r11, m21 * r12));
1341         double nm12 = Math.fma(m02, r10, Math.fma(m12, r11, m22 * r12));
1342         double nm13 = Math.fma(m03, r10, Math.fma(m13, r11, m23 * r12));
1343         double nm20 = Math.fma(m00, r20, Math.fma(m10, r21, m20 * r22));
1344         double nm21 = Math.fma(m01, r20, Math.fma(m11, r21, m21 * r22));
1345         double nm22 = Math.fma(m02, r20, Math.fma(m12, r21, m22 * r22));
1346         double nm23 = Math.fma(m03, r20, Math.fma(m13, r21, m23 * r22));
1347         return dest
1348         ._m00(nm00)
1349         ._m01(nm01)
1350         ._m02(nm02)
1351         ._m03(nm03)
1352         ._m10(nm10)
1353         ._m11(nm11)
1354         ._m12(nm12)
1355         ._m13(nm13)
1356         ._m20(nm20)
1357         ._m21(nm21)
1358         ._m22(nm22)
1359         ._m23(nm23)
1360         ._m30(m30)
1361         ._m31(m31)
1362         ._m32(m32)
1363         ._m33(m33)
1364         ._properties(this.properties & PROPERTY_AFFINE);
1365     }
1366 
1367     /**
1368      * Pre-multiply this matrix by the supplied <code>left</code> matrix and store the result in <code>this</code>.
1369      * <p>
1370      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the <code>left</code> matrix,
1371      * then the new matrix will be <code>L * M</code>. So when transforming a
1372      * vector <code>v</code> with the new matrix by using <code>L * M * v</code>, the
1373      * transformation of <code>this</code> matrix will be applied first!
1374      *
1375      * @param left
1376      *          the left operand of the matrix multiplication
1377      * @return this
1378      */
1379     ref public Matrix4d mulLocal(Matrix4d left) return {
1380        mulLocal(left, this);
1381        return this;
1382     }
1383 
1384     public Matrix4d mulLocal(Matrix4d left, ref Matrix4d dest) {
1385         if ((properties & PROPERTY_IDENTITY) != 0)
1386             return dest.set(left);
1387         else if ((left.properties & PROPERTY_IDENTITY) != 0)
1388             return dest.set(this);
1389         else if ((properties & PROPERTY_AFFINE) != 0 && (left.properties & PROPERTY_AFFINE) != 0)
1390             return mulLocalAffine(left, dest);
1391         return mulLocalGeneric(left, dest);
1392     }
1393     private Matrix4d mulLocalGeneric(Matrix4d left, ref Matrix4d dest) {
1394         double nm00 = Math.fma(left.m00, m00, Math.fma(left.m10, m01, Math.fma(left.m20, m02, left.m30 * m03)));
1395         double nm01 = Math.fma(left.m01, m00, Math.fma(left.m11, m01, Math.fma(left.m21, m02, left.m31 * m03)));
1396         double nm02 = Math.fma(left.m02, m00, Math.fma(left.m12, m01, Math.fma(left.m22, m02, left.m32 * m03)));
1397         double nm03 = Math.fma(left.m03, m00, Math.fma(left.m13, m01, Math.fma(left.m23, m02, left.m33 * m03)));
1398         double nm10 = Math.fma(left.m00, m10, Math.fma(left.m10, m11, Math.fma(left.m20, m12, left.m30 * m13)));
1399         double nm11 = Math.fma(left.m01, m10, Math.fma(left.m11, m11, Math.fma(left.m21, m12, left.m31 * m13)));
1400         double nm12 = Math.fma(left.m02, m10, Math.fma(left.m12, m11, Math.fma(left.m22, m12, left.m32 * m13)));
1401         double nm13 = Math.fma(left.m03, m10, Math.fma(left.m13, m11, Math.fma(left.m23, m12, left.m33 * m13)));
1402         double nm20 = Math.fma(left.m00, m20, Math.fma(left.m10, m21, Math.fma(left.m20, m22, left.m30 * m23)));
1403         double nm21 = Math.fma(left.m01, m20, Math.fma(left.m11, m21, Math.fma(left.m21, m22, left.m31 * m23)));
1404         double nm22 = Math.fma(left.m02, m20, Math.fma(left.m12, m21, Math.fma(left.m22, m22, left.m32 * m23)));
1405         double nm23 = Math.fma(left.m03, m20, Math.fma(left.m13, m21, Math.fma(left.m23, m22, left.m33 * m23)));
1406         double nm30 = Math.fma(left.m00, m30, Math.fma(left.m10, m31, Math.fma(left.m20, m32, left.m30 * m33)));
1407         double nm31 = Math.fma(left.m01, m30, Math.fma(left.m11, m31, Math.fma(left.m21, m32, left.m31 * m33)));
1408         double nm32 = Math.fma(left.m02, m30, Math.fma(left.m12, m31, Math.fma(left.m22, m32, left.m32 * m33)));
1409         double nm33 = Math.fma(left.m03, m30, Math.fma(left.m13, m31, Math.fma(left.m23, m32, left.m33 * m33)));
1410         return dest
1411         ._m00(nm00)
1412         ._m01(nm01)
1413         ._m02(nm02)
1414         ._m03(nm03)
1415         ._m10(nm10)
1416         ._m11(nm11)
1417         ._m12(nm12)
1418         ._m13(nm13)
1419         ._m20(nm20)
1420         ._m21(nm21)
1421         ._m22(nm22)
1422         ._m23(nm23)
1423         ._m30(nm30)
1424         ._m31(nm31)
1425         ._m32(nm32)
1426         ._m33(nm33)
1427         ._properties(0);
1428     }
1429 
1430     /**
1431      * 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>.
1432      * <p>
1433      * This method assumes that <code>this</code> matrix and the given <code>left</code> matrix both represent an {@link #isAffine() affine} transformation
1434      * (i.e. their last rows are equal to <code>(0, 0, 0, 1)</code>)
1435      * 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).
1436      * <p>
1437      * This method will not modify either the last row of <code>this</code> or the last row of <code>left</code>.
1438      * <p>
1439      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the <code>left</code> matrix,
1440      * then the new matrix will be <code>L * M</code>. So when transforming a
1441      * vector <code>v</code> with the new matrix by using <code>L * M * v</code>, the
1442      * transformation of <code>this</code> matrix will be applied first!
1443      *
1444      * @param left
1445      *          the left operand of the matrix multiplication (the last row is assumed to be <code>(0, 0, 0, 1)</code>)
1446      * @return this
1447      */
1448     ref public Matrix4d mulLocalAffine(Matrix4d left) return {
1449        mulLocalAffine(left, this);
1450        return this;
1451     }
1452 
1453     public Matrix4d mulLocalAffine(Matrix4d left, ref Matrix4d dest) {
1454         double nm00 = left.m00 * m00 + left.m10 * m01 + left.m20 * m02;
1455         double nm01 = left.m01 * m00 + left.m11 * m01 + left.m21 * m02;
1456         double nm02 = left.m02 * m00 + left.m12 * m01 + left.m22 * m02;
1457         double nm03 = left.m03;
1458         double nm10 = left.m00 * m10 + left.m10 * m11 + left.m20 * m12;
1459         double nm11 = left.m01 * m10 + left.m11 * m11 + left.m21 * m12;
1460         double nm12 = left.m02 * m10 + left.m12 * m11 + left.m22 * m12;
1461         double nm13 = left.m13;
1462         double nm20 = left.m00 * m20 + left.m10 * m21 + left.m20 * m22;
1463         double nm21 = left.m01 * m20 + left.m11 * m21 + left.m21 * m22;
1464         double nm22 = left.m02 * m20 + left.m12 * m21 + left.m22 * m22;
1465         double nm23 = left.m23;
1466         double nm30 = left.m00 * m30 + left.m10 * m31 + left.m20 * m32 + left.m30;
1467         double nm31 = left.m01 * m30 + left.m11 * m31 + left.m21 * m32 + left.m31;
1468         double nm32 = left.m02 * m30 + left.m12 * m31 + left.m22 * m32 + left.m32;
1469         double nm33 = left.m33;
1470         dest._m00(nm00)
1471         ._m01(nm01)
1472         ._m02(nm02)
1473         ._m03(nm03)
1474         ._m10(nm10)
1475         ._m11(nm11)
1476         ._m12(nm12)
1477         ._m13(nm13)
1478         ._m20(nm20)
1479         ._m21(nm21)
1480         ._m22(nm22)
1481         ._m23(nm23)
1482         ._m30(nm30)
1483         ._m31(nm31)
1484         ._m32(nm32)
1485         ._m33(nm33)
1486         ._properties(PROPERTY_AFFINE);
1487         return dest;
1488     }
1489 
1490     /**
1491      * Multiply this matrix by the supplied <code>right</code> matrix.
1492      * <p>
1493      * The last row of the <code>right</code> matrix is assumed to be <code>(0, 0, 0, 1)</code>.
1494      * <p>
1495      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1496      * then the new matrix will be <code>M * R</code>. So when transforming a
1497      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1498      * transformation of the right matrix will be applied first!
1499      *
1500      * @param right
1501      *          the right operand of the matrix multiplication
1502      * @return this
1503      */
1504     ref public Matrix4d mul(Matrix4x3d right) return {
1505         mul(right, this);
1506         return this;
1507     }
1508 
1509     public Matrix4d mul(Matrix4x3d right, ref Matrix4d dest) {
1510         if ((properties & PROPERTY_IDENTITY) != 0)
1511             return dest.set(right);
1512         else if ((right.properties & PROPERTY_IDENTITY) != 0)
1513             return dest.set(this);
1514         else if ((properties & PROPERTY_TRANSLATION) != 0)
1515             return mulTranslation(right, dest);
1516         else if ((properties & PROPERTY_AFFINE) != 0)
1517             return mulAffine(right, dest);
1518         else if ((properties & PROPERTY_PERSPECTIVE) != 0)
1519             return mulPerspectiveAffine(right, dest);
1520         return mulGeneric(right, dest);
1521     }
1522     private Matrix4d mulTranslation(Matrix4x3d right, ref Matrix4d dest) {
1523         return dest
1524         ._m00(right.m00)
1525         ._m01(right.m01)
1526         ._m02(right.m02)
1527         ._m03(m03)
1528         ._m10(right.m10)
1529         ._m11(right.m11)
1530         ._m12(right.m12)
1531         ._m13(m13)
1532         ._m20(right.m20)
1533         ._m21(right.m21)
1534         ._m22(right.m22)
1535         ._m23(m23)
1536         ._m30(right.m30 + m30)
1537         ._m31(right.m31 + m31)
1538         ._m32(right.m32 + m32)
1539         ._m33(m33)
1540         ._properties(PROPERTY_AFFINE | (right.properties & PROPERTY_ORTHONORMAL));
1541     }
1542     private Matrix4d mulAffine(Matrix4x3d right, ref Matrix4d dest) {
1543         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
1544         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
1545         double m20 = this.m20, m21 = this.m21, m22 = this.m22;
1546         double rm00 = right.m00, rm01 = right.m01, rm02 = right.m02;
1547         double rm10 = right.m10, rm11 = right.m11, rm12 = right.m12;
1548         double rm20 = right.m20, rm21 = right.m21, rm22 = right.m22;
1549         double rm30 = right.m30, rm31 = right.m31, rm32 = right.m32;
1550         return dest
1551         ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02)))
1552         ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02)))
1553         ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02)))
1554         ._m03(m03)
1555         ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12)))
1556         ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12)))
1557         ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12)))
1558         ._m13(m13)
1559         ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22)))
1560         ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22)))
1561         ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22)))
1562         ._m23(m23)
1563         ._m30(Math.fma(m00, rm30, Math.fma(m10, rm31, Math.fma(m20, rm32, m30))))
1564         ._m31(Math.fma(m01, rm30, Math.fma(m11, rm31, Math.fma(m21, rm32, m31))))
1565         ._m32(Math.fma(m02, rm30, Math.fma(m12, rm31, Math.fma(m22, rm32, m32))))
1566         ._m33(m33)
1567         ._properties(PROPERTY_AFFINE | (this.properties & right.properties & PROPERTY_ORTHONORMAL));
1568     }
1569     private Matrix4d mulGeneric(Matrix4x3d right, ref Matrix4d dest) {
1570         double nm00 = Math.fma(m00, right.m00, Math.fma(m10, right.m01, m20 * right.m02));
1571         double nm01 = Math.fma(m01, right.m00, Math.fma(m11, right.m01, m21 * right.m02));
1572         double nm02 = Math.fma(m02, right.m00, Math.fma(m12, right.m01, m22 * right.m02));
1573         double nm03 = Math.fma(m03, right.m00, Math.fma(m13, right.m01, m23 * right.m02));
1574         double nm10 = Math.fma(m00, right.m10, Math.fma(m10, right.m11, m20 * right.m12));
1575         double nm11 = Math.fma(m01, right.m10, Math.fma(m11, right.m11, m21 * right.m12));
1576         double nm12 = Math.fma(m02, right.m10, Math.fma(m12, right.m11, m22 * right.m12));
1577         double nm13 = Math.fma(m03, right.m10, Math.fma(m13, right.m11, m23 * right.m12));
1578         double nm20 = Math.fma(m00, right.m20, Math.fma(m10, right.m21, m20 * right.m22));
1579         double nm21 = Math.fma(m01, right.m20, Math.fma(m11, right.m21, m21 * right.m22));
1580         double nm22 = Math.fma(m02, right.m20, Math.fma(m12, right.m21, m22 * right.m22));
1581         double nm23 = Math.fma(m03, right.m20, Math.fma(m13, right.m21, m23 * right.m22));
1582         double nm30 = Math.fma(m00, right.m30, Math.fma(m10, right.m31, Math.fma(m20, right.m32, m30)));
1583         double nm31 = Math.fma(m01, right.m30, Math.fma(m11, right.m31, Math.fma(m21, right.m32, m31)));
1584         double nm32 = Math.fma(m02, right.m30, Math.fma(m12, right.m31, Math.fma(m22, right.m32, m32)));
1585         double nm33 = Math.fma(m03, right.m30, Math.fma(m13, right.m31, Math.fma(m23, right.m32, m33)));
1586         dest._m00(nm00)
1587         ._m01(nm01)
1588         ._m02(nm02)
1589         ._m03(nm03)
1590         ._m10(nm10)
1591         ._m11(nm11)
1592         ._m12(nm12)
1593         ._m13(nm13)
1594         ._m20(nm20)
1595         ._m21(nm21)
1596         ._m22(nm22)
1597         ._m23(nm23)
1598         ._m30(nm30)
1599         ._m31(nm31)
1600         ._m32(nm32)
1601         ._m33(nm33)
1602         ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
1603         return dest;
1604     }
1605     public Matrix4d mulPerspectiveAffine(Matrix4x3d view, ref Matrix4d dest) {
1606         double lm00 = m00, lm11 = m11, lm22 = m22, lm23 = m23;
1607         dest._m00(lm00 * view.m00)._m01(lm11 * view.m01)._m02(lm22 * view.m02)._m03(lm23 * view.m02).
1608         _m10(lm00 * view.m10)._m11(lm11 * view.m11)._m12(lm22 * view.m12)._m13(lm23 * view.m12).
1609         _m20(lm00 * view.m20)._m21(lm11 * view.m21)._m22(lm22 * view.m22)._m23(lm23 * view.m22).
1610         _m30(lm00 * view.m30)._m31(lm11 * view.m31)._m32(lm22 * view.m32 + m32)._m33(lm23 * view.m32)
1611         ._properties(0);
1612         return dest;
1613     }
1614 
1615 
1616     /**
1617      * Multiply this matrix by the supplied <code>right</code> matrix and store the result in <code>this</code>.
1618      * <p>
1619      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1620      * then the new matrix will be <code>M * R</code>. So when transforming a
1621      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1622      * transformation of the right matrix will be applied first!
1623      *
1624      * @param right
1625      *          the right operand of the matrix multiplication
1626      * @return this
1627      */
1628     ref public Matrix4d mul(Matrix3x2d right) return {
1629         mul(right, this);
1630         return this;
1631     }
1632 
1633     public Matrix4d mul(Matrix3x2d right, ref Matrix4d dest) {
1634         double nm00 = m00 * right.m00 + m10 * right.m01;
1635         double nm01 = m01 * right.m00 + m11 * right.m01;
1636         double nm02 = m02 * right.m00 + m12 * right.m01;
1637         double nm03 = m03 * right.m00 + m13 * right.m01;
1638         double nm10 = m00 * right.m10 + m10 * right.m11;
1639         double nm11 = m01 * right.m10 + m11 * right.m11;
1640         double nm12 = m02 * right.m10 + m12 * right.m11;
1641         double nm13 = m03 * right.m10 + m13 * right.m11;
1642         double nm30 = m00 * right.m20 + m10 * right.m21 + m30;
1643         double nm31 = m01 * right.m20 + m11 * right.m21 + m31;
1644         double nm32 = m02 * right.m20 + m12 * right.m21 + m32;
1645         double nm33 = m03 * right.m20 + m13 * right.m21 + m33;
1646         dest._m00(nm00)
1647         ._m01(nm01)
1648         ._m02(nm02)
1649         ._m03(nm03)
1650         ._m10(nm10)
1651         ._m11(nm11)
1652         ._m12(nm12)
1653         ._m13(nm13)
1654         ._m20(m20)
1655         ._m21(m21)
1656         ._m22(m22)
1657         ._m23(m23)
1658         ._m30(nm30)
1659         ._m31(nm31)
1660         ._m32(nm32)
1661         ._m33(nm33)
1662         ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
1663         return dest;
1664     }
1665 
1666 
1667 
1668 
1669     /**
1670      * Multiply <code>this</code> symmetric perspective projection matrix by the supplied {@link #isAffine() affine} <code>view</code> matrix.
1671      * <p>
1672      * If <code>P</code> is <code>this</code> matrix and <code>V</code> the <code>view</code> matrix,
1673      * then the new matrix will be <code>P * V</code>. So when transforming a
1674      * vector <code>v</code> with the new matrix by using <code>P * V * v</code>, the
1675      * transformation of the <code>view</code> matrix will be applied first!
1676      *
1677      * @param view
1678      *          the {@link #isAffine() affine} matrix to multiply <code>this</code> symmetric perspective projection matrix by
1679      * @return this
1680      */
1681     ref public Matrix4d mulPerspectiveAffine(Matrix4d view) return {
1682        mulPerspectiveAffine(view, this);
1683        return this;
1684     }
1685 
1686     public Matrix4d mulPerspectiveAffine(Matrix4d view, ref Matrix4d dest) {
1687         double nm00 = m00 * view.m00, nm01 = m11 * view.m01, nm02 = m22 * view.m02, nm03 = m23 * view.m02;
1688         double nm10 = m00 * view.m10, nm11 = m11 * view.m11, nm12 = m22 * view.m12, nm13 = m23 * view.m12;
1689         double nm20 = m00 * view.m20, nm21 = m11 * view.m21, nm22 = m22 * view.m22, nm23 = m23 * view.m22;
1690         double nm30 = m00 * view.m30, nm31 = m11 * view.m31, nm32 = m22 * view.m32 + m32, nm33 = m23 * view.m32;
1691         return dest
1692             ._m00(nm00)._m01(nm01)._m02(nm02)._m03(nm03)
1693             ._m10(nm10)._m11(nm11)._m12(nm12)._m13(nm13)
1694             ._m20(nm20)._m21(nm21)._m22(nm22)._m23(nm23)
1695             ._m30(nm30)._m31(nm31)._m32(nm32)._m33(nm33)
1696             ._properties(0);
1697     }
1698 
1699     /**
1700      * 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>.
1701      * <p>
1702      * 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>)
1703      * 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).
1704      * <p>
1705      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1706      * then the new matrix will be <code>M * R</code>. So when transforming a
1707      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1708      * transformation of the right matrix will be applied first!
1709      *
1710      * @param right
1711      *          the right operand of the matrix multiplication (the last row is assumed to be <code>(0, 0, 0, 1)</code>)
1712      * @return this
1713      */
1714     ref public Matrix4d mulAffineR(Matrix4d right) return {
1715        mulAffineR(right, this);
1716        return this;
1717     }
1718 
1719     public Matrix4d mulAffineR(Matrix4d right, ref Matrix4d dest) {
1720         double nm00 = Math.fma(m00, right.m00, Math.fma(m10, right.m01, m20 * right.m02));
1721         double nm01 = Math.fma(m01, right.m00, Math.fma(m11, right.m01, m21 * right.m02));
1722         double nm02 = Math.fma(m02, right.m00, Math.fma(m12, right.m01, m22 * right.m02));
1723         double nm03 = Math.fma(m03, right.m00, Math.fma(m13, right.m01, m23 * right.m02));
1724         double nm10 = Math.fma(m00, right.m10, Math.fma(m10, right.m11, m20 * right.m12));
1725         double nm11 = Math.fma(m01, right.m10, Math.fma(m11, right.m11, m21 * right.m12));
1726         double nm12 = Math.fma(m02, right.m10, Math.fma(m12, right.m11, m22 * right.m12));
1727         double nm13 = Math.fma(m03, right.m10, Math.fma(m13, right.m11, m23 * right.m12));
1728         double nm20 = Math.fma(m00, right.m20, Math.fma(m10, right.m21, m20 * right.m22));
1729         double nm21 = Math.fma(m01, right.m20, Math.fma(m11, right.m21, m21 * right.m22));
1730         double nm22 = Math.fma(m02, right.m20, Math.fma(m12, right.m21, m22 * right.m22));
1731         double nm23 = Math.fma(m03, right.m20, Math.fma(m13, right.m21, m23 * right.m22));
1732         double nm30 = Math.fma(m00, right.m30, Math.fma(m10, right.m31, Math.fma(m20, right.m32, m30)));
1733         double nm31 = Math.fma(m01, right.m30, Math.fma(m11, right.m31, Math.fma(m21, right.m32, m31)));
1734         double nm32 = Math.fma(m02, right.m30, Math.fma(m12, right.m31, Math.fma(m22, right.m32, m32)));
1735         double nm33 = Math.fma(m03, right.m30, Math.fma(m13, right.m31, Math.fma(m23, right.m32, m33)));
1736         dest._m00(nm00)
1737         ._m01(nm01)
1738         ._m02(nm02)
1739         ._m03(nm03)
1740         ._m10(nm10)
1741         ._m11(nm11)
1742         ._m12(nm12)
1743         ._m13(nm13)
1744         ._m20(nm20)
1745         ._m21(nm21)
1746         ._m22(nm22)
1747         ._m23(nm23)
1748         ._m30(nm30)
1749         ._m31(nm31)
1750         ._m32(nm32)
1751         ._m33(nm33)
1752         ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_PERSPECTIVE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
1753         return dest;
1754     }
1755 
1756     /**
1757      * 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>.
1758      * <p>
1759      * This method assumes that <code>this</code> matrix and the given <code>right</code> matrix both represent an {@link #isAffine() affine} transformation
1760      * (i.e. their last rows are equal to <code>(0, 0, 0, 1)</code>)
1761      * 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).
1762      * <p>
1763      * This method will not modify either the last row of <code>this</code> or the last row of <code>right</code>.
1764      * <p>
1765      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the <code>right</code> matrix,
1766      * then the new matrix will be <code>M * R</code>. So when transforming a
1767      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
1768      * transformation of the right matrix will be applied first!
1769      *
1770      * @param right
1771      *          the right operand of the matrix multiplication (the last row is assumed to be <code>(0, 0, 0, 1)</code>)
1772      * @return this
1773      */
1774     ref public Matrix4d mulAffine(Matrix4d right) return {
1775        mulAffine(right, this);
1776        return this;
1777     }
1778 
1779     public Matrix4d mulAffine(Matrix4d right, ref Matrix4d dest) {
1780         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
1781         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
1782         double m20 = this.m20, m21 = this.m21, m22 = this.m22;
1783         double rm00 = right.m00, rm01 = right.m01, rm02 = right.m02;
1784         double rm10 = right.m10, rm11 = right.m11, rm12 = right.m12;
1785         double rm20 = right.m20, rm21 = right.m21, rm22 = right.m22;
1786         double rm30 = right.m30, rm31 = right.m31, rm32 = right.m32;
1787         return dest
1788         ._m00(Math.fma(m00, rm00, Math.fma(m10, rm01, m20 * rm02)))
1789         ._m01(Math.fma(m01, rm00, Math.fma(m11, rm01, m21 * rm02)))
1790         ._m02(Math.fma(m02, rm00, Math.fma(m12, rm01, m22 * rm02)))
1791         ._m03(m03)
1792         ._m10(Math.fma(m00, rm10, Math.fma(m10, rm11, m20 * rm12)))
1793         ._m11(Math.fma(m01, rm10, Math.fma(m11, rm11, m21 * rm12)))
1794         ._m12(Math.fma(m02, rm10, Math.fma(m12, rm11, m22 * rm12)))
1795         ._m13(m13)
1796         ._m20(Math.fma(m00, rm20, Math.fma(m10, rm21, m20 * rm22)))
1797         ._m21(Math.fma(m01, rm20, Math.fma(m11, rm21, m21 * rm22)))
1798         ._m22(Math.fma(m02, rm20, Math.fma(m12, rm21, m22 * rm22)))
1799         ._m23(m23)
1800         ._m30(Math.fma(m00, rm30, Math.fma(m10, rm31, Math.fma(m20, rm32, m30))))
1801         ._m31(Math.fma(m01, rm30, Math.fma(m11, rm31, Math.fma(m21, rm32, m31))))
1802         ._m32(Math.fma(m02, rm30, Math.fma(m12, rm31, Math.fma(m22, rm32, m32))))
1803         ._m33(m33)
1804         ._properties(PROPERTY_AFFINE | (this.properties & right.properties & PROPERTY_ORTHONORMAL));
1805     }
1806 
1807     public Matrix4d mulTranslationAffine(Matrix4d right, ref Matrix4d dest) {
1808         return dest
1809         ._m00(right.m00)
1810         ._m01(right.m01)
1811         ._m02(right.m02)
1812         ._m03(m03)
1813         ._m10(right.m10)
1814         ._m11(right.m11)
1815         ._m12(right.m12)
1816         ._m13(m13)
1817         ._m20(right.m20)
1818         ._m21(right.m21)
1819         ._m22(right.m22)
1820         ._m23(m23)
1821         ._m30(right.m30 + m30)
1822         ._m31(right.m31 + m31)
1823         ._m32(right.m32 + m32)
1824         ._m33(m33)
1825         ._properties(PROPERTY_AFFINE | (right.properties & PROPERTY_ORTHONORMAL));
1826     }
1827 
1828     /**
1829      * Multiply <code>this</code> orthographic projection matrix by the supplied {@link #isAffine() affine} <code>view</code> matrix.
1830      * <p>
1831      * If <code>M</code> is <code>this</code> matrix and <code>V</code> the <code>view</code> matrix,
1832      * then the new matrix will be <code>M * V</code>. So when transforming a
1833      * vector <code>v</code> with the new matrix by using <code>M * V * v</code>, the
1834      * transformation of the <code>view</code> matrix will be applied first!
1835      *
1836      * @param view
1837      *          the affine matrix which to multiply <code>this</code> with
1838      * @return this
1839      */
1840     ref public Matrix4d mulOrthoAffine(Matrix4d view) return {
1841         mulOrthoAffine(view, this);
1842         return this;
1843     }
1844 
1845     public Matrix4d mulOrthoAffine(Matrix4d view, ref Matrix4d dest) {
1846         double nm00 = m00 * view.m00;
1847         double nm01 = m11 * view.m01;
1848         double nm02 = m22 * view.m02;
1849         double nm03 = 0.0;
1850         double nm10 = m00 * view.m10;
1851         double nm11 = m11 * view.m11;
1852         double nm12 = m22 * view.m12;
1853         double nm13 = 0.0;
1854         double nm20 = m00 * view.m20;
1855         double nm21 = m11 * view.m21;
1856         double nm22 = m22 * view.m22;
1857         double nm23 = 0.0;
1858         double nm30 = m00 * view.m30 + m30;
1859         double nm31 = m11 * view.m31 + m31;
1860         double nm32 = m22 * view.m32 + m32;
1861         double nm33 = 1.0;
1862         dest._m00(nm00)
1863         ._m01(nm01)
1864         ._m02(nm02)
1865         ._m03(nm03)
1866         ._m10(nm10)
1867         ._m11(nm11)
1868         ._m12(nm12)
1869         ._m13(nm13)
1870         ._m20(nm20)
1871         ._m21(nm21)
1872         ._m22(nm22)
1873         ._m23(nm23)
1874         ._m30(nm30)
1875         ._m31(nm31)
1876         ._m32(nm32)
1877         ._m33(nm33)
1878         ._properties(PROPERTY_AFFINE);
1879         return dest;
1880     }
1881 
1882     /**
1883      * Component-wise add the upper 4x3 submatrices of <code>this</code> and <code>other</code>
1884      * by first multiplying each component of <code>other</code>'s 4x3 submatrix by <code>otherFactor</code> and
1885      * adding that result to <code>this</code>.
1886      * <p>
1887      * The matrix <code>other</code> will not be changed.
1888      * 
1889      * @param other
1890      *          the other matrix
1891      * @param otherFactor
1892      *          the factor to multiply each of the other matrix's 4x3 components
1893      * @return this
1894      */
1895     ref public Matrix4d fma4x3(Matrix4d other, double otherFactor) return {
1896         fma4x3(other, otherFactor, this);
1897         return this;
1898     }
1899 
1900     public Matrix4d fma4x3(Matrix4d other, double otherFactor, ref Matrix4d dest) {
1901         dest._m00(Math.fma(other.m00, otherFactor, m00))
1902         ._m01(Math.fma(other.m01, otherFactor, m01))
1903         ._m02(Math.fma(other.m02, otherFactor, m02))
1904         ._m03(m03)
1905         ._m10(Math.fma(other.m10, otherFactor, m10))
1906         ._m11(Math.fma(other.m11, otherFactor, m11))
1907         ._m12(Math.fma(other.m12, otherFactor, m12))
1908         ._m13(m13)
1909         ._m20(Math.fma(other.m20, otherFactor, m20))
1910         ._m21(Math.fma(other.m21, otherFactor, m21))
1911         ._m22(Math.fma(other.m22, otherFactor, m22))
1912         ._m23(m23)
1913         ._m30(Math.fma(other.m30, otherFactor, m30))
1914         ._m31(Math.fma(other.m31, otherFactor, m31))
1915         ._m32(Math.fma(other.m32, otherFactor, m32))
1916         ._m33(m33)
1917         ._properties(0);
1918         return dest;
1919     }
1920 
1921     /**
1922      * Component-wise add <code>this</code> and <code>other</code>.
1923      * 
1924      * @param other
1925      *          the other addend
1926      * @return this
1927      */
1928     ref public Matrix4d add(Matrix4d other) return {
1929         add(other, this);
1930         return this;
1931     }
1932 
1933     public Matrix4d add(Matrix4d other, ref Matrix4d dest) {
1934         dest._m00(m00 + other.m00)
1935         ._m01(m01 + other.m01)
1936         ._m02(m02 + other.m02)
1937         ._m03(m03 + other.m03)
1938         ._m10(m10 + other.m10)
1939         ._m11(m11 + other.m11)
1940         ._m12(m12 + other.m12)
1941         ._m13(m13 + other.m13)
1942         ._m20(m20 + other.m20)
1943         ._m21(m21 + other.m21)
1944         ._m22(m22 + other.m22)
1945         ._m23(m23 + other.m23)
1946         ._m30(m30 + other.m30)
1947         ._m31(m31 + other.m31)
1948         ._m32(m32 + other.m32)
1949         ._m33(m33 + other.m33)
1950         ._properties(0);
1951         return dest;
1952     }
1953 
1954     /**
1955      * Component-wise subtract <code>subtrahend</code> from <code>this</code>.
1956      * 
1957      * @param subtrahend
1958      *          the subtrahend
1959      * @return this
1960      */
1961     ref public Matrix4d sub(Matrix4d subtrahend) return {
1962         sub(subtrahend, this);
1963         return this;
1964     }
1965 
1966     public Matrix4d sub(Matrix4d subtrahend, ref Matrix4d dest) {
1967         dest._m00(m00 - subtrahend.m00)
1968         ._m01(m01 - subtrahend.m01)
1969         ._m02(m02 - subtrahend.m02)
1970         ._m03(m03 - subtrahend.m03)
1971         ._m10(m10 - subtrahend.m10)
1972         ._m11(m11 - subtrahend.m11)
1973         ._m12(m12 - subtrahend.m12)
1974         ._m13(m13 - subtrahend.m13)
1975         ._m20(m20 - subtrahend.m20)
1976         ._m21(m21 - subtrahend.m21)
1977         ._m22(m22 - subtrahend.m22)
1978         ._m23(m23 - subtrahend.m23)
1979         ._m30(m30 - subtrahend.m30)
1980         ._m31(m31 - subtrahend.m31)
1981         ._m32(m32 - subtrahend.m32)
1982         ._m33(m33 - subtrahend.m33)
1983         ._properties(0);
1984         return dest;
1985     }
1986 
1987     /**
1988      * Component-wise multiply <code>this</code> by <code>other</code>.
1989      * 
1990      * @param other
1991      *          the other matrix
1992      * @return this
1993      */
1994     ref public Matrix4d mulComponentWise(Matrix4d other) return {
1995         mulComponentWise(other, this);
1996         return this;
1997     }
1998 
1999     public Matrix4d mulComponentWise(Matrix4d other, ref Matrix4d dest) {
2000         dest._m00(m00 * other.m00)
2001         ._m01(m01 * other.m01)
2002         ._m02(m02 * other.m02)
2003         ._m03(m03 * other.m03)
2004         ._m10(m10 * other.m10)
2005         ._m11(m11 * other.m11)
2006         ._m12(m12 * other.m12)
2007         ._m13(m13 * other.m13)
2008         ._m20(m20 * other.m20)
2009         ._m21(m21 * other.m21)
2010         ._m22(m22 * other.m22)
2011         ._m23(m23 * other.m23)
2012         ._m30(m30 * other.m30)
2013         ._m31(m31 * other.m31)
2014         ._m32(m32 * other.m32)
2015         ._m33(m33 * other.m33)
2016         ._properties(0);
2017         return dest;
2018     }
2019 
2020     /**
2021      * Component-wise add the upper 4x3 submatrices of <code>this</code> and <code>other</code>.
2022      * 
2023      * @param other
2024      *          the other addend
2025      * @return this
2026      */
2027     ref public Matrix4d add4x3(Matrix4d other) return {
2028         add4x3(other, this);
2029         return this;
2030     }
2031 
2032     public Matrix4d add4x3(Matrix4d other, ref Matrix4d dest) {
2033         dest._m00(m00 + other.m00)
2034         ._m01(m01 + other.m01)
2035         ._m02(m02 + other.m02)
2036         ._m03(m03)
2037         ._m10(m10 + other.m10)
2038         ._m11(m11 + other.m11)
2039         ._m12(m12 + other.m12)
2040         ._m13(m13)
2041         ._m20(m20 + other.m20)
2042         ._m21(m21 + other.m21)
2043         ._m22(m22 + other.m22)
2044         ._m23(m23)
2045         ._m30(m30 + other.m30)
2046         ._m31(m31 + other.m31)
2047         ._m32(m32 + other.m32)
2048         ._m33(m33)
2049         ._properties(0);
2050         return dest;
2051     }
2052 
2053     /**
2054      * Component-wise subtract the upper 4x3 submatrices of <code>subtrahend</code> from <code>this</code>.
2055      * 
2056      * @param subtrahend
2057      *          the subtrahend
2058      * @return this
2059      */
2060     ref public Matrix4d sub4x3(Matrix4d subtrahend) return {
2061         sub4x3(subtrahend, this);
2062         return this;
2063     }
2064 
2065     public Matrix4d sub4x3(Matrix4d subtrahend, ref Matrix4d dest) {
2066         dest._m00(m00 - subtrahend.m00)
2067         ._m01(m01 - subtrahend.m01)
2068         ._m02(m02 - subtrahend.m02)
2069         ._m03(m03)
2070         ._m10(m10 - subtrahend.m10)
2071         ._m11(m11 - subtrahend.m11)
2072         ._m12(m12 - subtrahend.m12)
2073         ._m13(m13)
2074         ._m20(m20 - subtrahend.m20)
2075         ._m21(m21 - subtrahend.m21)
2076         ._m22(m22 - subtrahend.m22)
2077         ._m23(m23)
2078         ._m30(m30 - subtrahend.m30)
2079         ._m31(m31 - subtrahend.m31)
2080         ._m32(m32 - subtrahend.m32)
2081         ._m33(m33)
2082         ._properties(0);
2083         return dest;
2084     }
2085 
2086     /**
2087      * Component-wise multiply the upper 4x3 submatrices of <code>this</code> by <code>other</code>.
2088      * 
2089      * @param other
2090      *          the other matrix
2091      * @return this
2092      */
2093     ref public Matrix4d mul4x3ComponentWise(Matrix4d other) return {
2094         mul4x3ComponentWise(other, this);
2095         return this;
2096     }
2097 
2098     public Matrix4d mul4x3ComponentWise(Matrix4d other, ref Matrix4d dest) {
2099         dest._m00(m00 * other.m00)
2100         ._m01(m01 * other.m01)
2101         ._m02(m02 * other.m02)
2102         ._m03(m03)
2103         ._m10(m10 * other.m10)
2104         ._m11(m11 * other.m11)
2105         ._m12(m12 * other.m12)
2106         ._m13(m13)
2107         ._m20(m20 * other.m20)
2108         ._m21(m21 * other.m21)
2109         ._m22(m22 * other.m22)
2110         ._m23(m23)
2111         ._m30(m30 * other.m30)
2112         ._m31(m31 * other.m31)
2113         ._m32(m32 * other.m32)
2114         ._m33(m33)
2115         ._properties(0);
2116         return dest;
2117     }
2118 
2119     /** Set the values within this matrix to the supplied double values. The matrix will look like this:<br><br>
2120      *  
2121      * m00, m10, m20, m30<br>
2122      * m01, m11, m21, m31<br>
2123      * m02, m12, m22, m32<br>
2124      * m03, m13, m23, m33
2125      *
2126      * @param m00
2127      *          the new value of m00
2128      * @param m01
2129      *          the new value of m01
2130      * @param m02
2131      *          the new value of m02
2132      * @param m03
2133      *          the new value of m03
2134      * @param m10
2135      *          the new value of m10
2136      * @param m11
2137      *          the new value of m11
2138      * @param m12
2139      *          the new value of m12
2140      * @param m13
2141      *          the new value of m13
2142      * @param m20
2143      *          the new value of m20
2144      * @param m21
2145      *          the new value of m21
2146      * @param m22
2147      *          the new value of m22
2148      * @param m23
2149      *          the new value of m23
2150      * @param m30
2151      *          the new value of m30
2152      * @param m31
2153      *          the new value of m31
2154      * @param m32
2155      *          the new value of m32
2156      * @param m33
2157      *          the new value of m33
2158      * @return this
2159      */
2160     ref public Matrix4d set(double m00, double m01, double m02,double m03,
2161                         double m10, double m11, double m12, double m13,
2162                         double m20, double m21, double m22, double m23, 
2163                         double m30, double m31, double m32, double m33) return {
2164         setm00(m00);
2165         setm10(m10);
2166         setm20(m20);
2167         setm30(m30);
2168         setm01(m01);
2169         setm11(m11);
2170         setm21(m21);
2171         setm31(m31);
2172         setm02(m02);
2173         setm12(m12);
2174         setm22(m22);
2175         setm32(m32);
2176         setm03(m03);
2177         setm13(m13);
2178         setm23(m23);
2179         setm33(m33);
2180         return determineProperties();
2181     }
2182 
2183     /**
2184      * Set the values in the matrix using a double array that contains the matrix elements in column-major order.
2185      * <p>
2186      * The results will look like this:<br><br>
2187      * 
2188      * 0, 4, 8, 12<br>
2189      * 1, 5, 9, 13<br>
2190      * 2, 6, 10, 14<br>
2191      * 3, 7, 11, 15<br>
2192      * 
2193      * @see #set(double[])
2194      * 
2195      * @param m
2196      *          the array to read the matrix values from
2197      * @param off
2198      *          the offset into the array
2199      * @return this
2200      */
2201     ref public Matrix4d set(double[] m, int off) return {
2202         return
2203         _m00(m[off+0]).
2204         _m01(m[off+1]).
2205         _m02(m[off+2]).
2206         _m03(m[off+3]).
2207         _m10(m[off+4]).
2208         _m11(m[off+5]).
2209         _m12(m[off+6]).
2210         _m13(m[off+7]).
2211         _m20(m[off+8]).
2212         _m21(m[off+9]).
2213         _m22(m[off+10]).
2214         _m23(m[off+11]).
2215         _m30(m[off+12]).
2216         _m31(m[off+13]).
2217         _m32(m[off+14]).
2218         _m33(m[off+15]).
2219         determineProperties();
2220     }
2221 
2222     /**
2223      * Set the values in the matrix using a double array that contains the matrix elements in column-major order.
2224      * <p>
2225      * The results will look like this:<br><br>
2226      * 
2227      * 0, 4, 8, 12<br>
2228      * 1, 5, 9, 13<br>
2229      * 2, 6, 10, 14<br>
2230      * 3, 7, 11, 15<br>
2231      * 
2232      * @see #set(double[], int)
2233      * 
2234      * @param m
2235      *          the array to read the matrix values from
2236      * @return this
2237      */
2238     ref public Matrix4d set(double[] m) return {
2239         return set(m, 0);
2240     }
2241 
2242     /**
2243      * Set the values in the matrix using a float array that contains the matrix elements in column-major order.
2244      * <p>
2245      * The results will look like this:<br><br>
2246      * 
2247      * 0, 4, 8, 12<br>
2248      * 1, 5, 9, 13<br>
2249      * 2, 6, 10, 14<br>
2250      * 3, 7, 11, 15<br>
2251      * 
2252      * @see #set(float[])
2253      * 
2254      * @param m
2255      *          the array to read the matrix values from
2256      * @param off
2257      *          the offset into the array
2258      * @return this
2259      */
2260     ref public Matrix4d set(float[] m, int off) return {
2261         return
2262         _m00(m[off+0]).
2263         _m01(m[off+1]).
2264         _m02(m[off+2]).
2265         _m03(m[off+3]).
2266         _m10(m[off+4]).
2267         _m11(m[off+5]).
2268         _m12(m[off+6]).
2269         _m13(m[off+7]).
2270         _m20(m[off+8]).
2271         _m21(m[off+9]).
2272         _m22(m[off+10]).
2273         _m23(m[off+11]).
2274         _m30(m[off+12]).
2275         _m31(m[off+13]).
2276         _m32(m[off+14]).
2277         _m33(m[off+15]).
2278         determineProperties();
2279     }
2280 
2281     /**
2282      * Set the values in the matrix using a float array that contains the matrix elements in column-major order.
2283      * <p>
2284      * The results will look like this:<br><br>
2285      * 
2286      * 0, 4, 8, 12<br>
2287      * 1, 5, 9, 13<br>
2288      * 2, 6, 10, 14<br>
2289      * 3, 7, 11, 15<br>
2290      * 
2291      * @see #set(float[], int)
2292      * 
2293      * @param m
2294      *          the array to read the matrix values from
2295      * @return this
2296      */
2297     ref public Matrix4d set(float[] m) return {
2298         return set(m, 0);
2299     }
2300 
2301 
2302     /**
2303      * Set the four columns of this matrix to the supplied vectors, respectively.
2304      * 
2305      * @param col0
2306      *          the first column
2307      * @param col1
2308      *          the second column
2309      * @param col2
2310      *          the third column
2311      * @param col3
2312      *          the fourth column
2313      * @return this
2314      */
2315     ref public Matrix4d set(ref Vector4d col0, Vector4d col1, Vector4d col2, Vector4d col3) return {
2316         return
2317         _m00(col0.x).
2318         _m01(col0.y).
2319         _m02(col0.z).
2320         _m03(col0.w).
2321         _m10(col1.x).
2322         _m11(col1.y).
2323         _m12(col1.z).
2324         _m13(col1.w).
2325         _m20(col2.x).
2326         _m21(col2.y).
2327         _m22(col2.z).
2328         _m23(col2.w).
2329         _m30(col3.x).
2330         _m31(col3.y).
2331         _m32(col3.z).
2332         _m33(col3.w).
2333         determineProperties();
2334     }
2335 
2336     public double determinant() {
2337         if ((properties & PROPERTY_AFFINE) != 0)
2338             return determinantAffine();
2339         return (m00 * m11 - m01 * m10) * (m22 * m33 - m23 * m32)
2340              + (m02 * m10 - m00 * m12) * (m21 * m33 - m23 * m31)
2341              + (m00 * m13 - m03 * m10) * (m21 * m32 - m22 * m31) 
2342              + (m01 * m12 - m02 * m11) * (m20 * m33 - m23 * m30)
2343              + (m03 * m11 - m01 * m13) * (m20 * m32 - m22 * m30) 
2344              + (m02 * m13 - m03 * m12) * (m20 * m31 - m21 * m30);
2345     }
2346 
2347     public double determinant3x3() {
2348         return (m00 * m11 - m01 * m10) * m22
2349              + (m02 * m10 - m00 * m12) * m21
2350              + (m01 * m12 - m02 * m11) * m20;
2351     }
2352 
2353     public double determinantAffine() {
2354         return (m00 * m11 - m01 * m10) * m22
2355              + (m02 * m10 - m00 * m12) * m21
2356              + (m01 * m12 - m02 * m11) * m20;
2357     }
2358 
2359     /**
2360      * Invert this matrix.
2361      * <p>
2362      * If <code>this</code> matrix represents an {@link #isAffine() affine} transformation, such as translation, rotation, scaling and shearing,
2363      * and thus its last row is equal to <code>(0, 0, 0, 1)</code>, then {@link #invertAffine()} can be used instead of this method.
2364      * 
2365      * @see #invertAffine()
2366      * 
2367      * @return this
2368      */
2369     ref public Matrix4d invert() return {
2370         invert(this);
2371         return this;
2372     }
2373 
2374     public Matrix4d invert(ref Matrix4d dest) {
2375         if ((properties & PROPERTY_IDENTITY) != 0)
2376             return dest.identity();
2377         else if ((properties & PROPERTY_TRANSLATION) != 0)
2378             return invertTranslation(dest);
2379         else if ((properties & PROPERTY_ORTHONORMAL) != 0)
2380             return invertOrthonormal(dest);
2381         else if ((properties & PROPERTY_AFFINE) != 0)
2382             return invertAffine(dest);
2383         else if ((properties & PROPERTY_PERSPECTIVE) != 0)
2384             return invertPerspective(dest);
2385         return invertGeneric(dest);
2386     }
2387     private Matrix4d invertTranslation(ref Matrix4d dest) {
2388         if (dest != this)
2389             dest.set(this);
2390         dest._m30(-m30)
2391         ._m31(-m31)
2392         ._m32(-m32)
2393         ._properties(PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL);
2394         return dest;
2395     }
2396     private Matrix4d invertOrthonormal(ref Matrix4d dest) {
2397         double nm30 = -(m00 * m30 + m01 * m31 + m02 * m32);
2398         double nm31 = -(m10 * m30 + m11 * m31 + m12 * m32);
2399         double nm32 = -(m20 * m30 + m21 * m31 + m22 * m32);
2400         double m01 = this.m01;
2401         double m02 = this.m02;
2402         double m12 = this.m12;
2403         dest._m00(m00)
2404         ._m01(m10)
2405         ._m02(m20)
2406         ._m03(0.0)
2407         ._m10(m01)
2408         ._m11(m11)
2409         ._m12(m21)
2410         ._m13(0.0)
2411         ._m20(m02)
2412         ._m21(m12)
2413         ._m22(m22)
2414         ._m23(0.0)
2415         ._m30(nm30)
2416         ._m31(nm31)
2417         ._m32(nm32)
2418         ._m33(1.0)
2419         ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
2420         return dest;
2421     }
2422     private Matrix4d invertGeneric(ref Matrix4d dest) {
2423         if (this != dest)
2424             return invertGenericNonThis(dest);
2425         return invertGenericThis(dest);
2426     }
2427     private Matrix4d invertGenericNonThis(ref Matrix4d dest) {
2428         double a = m00 * m11 - m01 * m10;
2429         double b = m00 * m12 - m02 * m10;
2430         double c = m00 * m13 - m03 * m10;
2431         double d = m01 * m12 - m02 * m11;
2432         double e = m01 * m13 - m03 * m11;
2433         double f = m02 * m13 - m03 * m12;
2434         double g = m20 * m31 - m21 * m30;
2435         double h = m20 * m32 - m22 * m30;
2436         double i = m20 * m33 - m23 * m30;
2437         double j = m21 * m32 - m22 * m31;
2438         double k = m21 * m33 - m23 * m31;
2439         double l = m22 * m33 - m23 * m32;
2440         double det = a * l - b * k + c * j + d * i - e * h + f * g;
2441         det = 1.0 / det;
2442         return dest
2443         ._m00(Math.fma( m11, l, Math.fma(-m12, k,  m13 * j)) * det)
2444         ._m01(Math.fma(-m01, l, Math.fma( m02, k, -m03 * j)) * det)
2445         ._m02(Math.fma( m31, f, Math.fma(-m32, e,  m33 * d)) * det)
2446         ._m03(Math.fma(-m21, f, Math.fma( m22, e, -m23 * d)) * det)
2447         ._m10(Math.fma(-m10, l, Math.fma( m12, i, -m13 * h)) * det)
2448         ._m11(Math.fma( m00, l, Math.fma(-m02, i,  m03 * h)) * det)
2449         ._m12(Math.fma(-m30, f, Math.fma( m32, c, -m33 * b)) * det)
2450         ._m13(Math.fma( m20, f, Math.fma(-m22, c,  m23 * b)) * det)
2451         ._m20(Math.fma( m10, k, Math.fma(-m11, i,  m13 * g)) * det)
2452         ._m21(Math.fma(-m00, k, Math.fma( m01, i, -m03 * g)) * det)
2453         ._m22(Math.fma( m30, e, Math.fma(-m31, c,  m33 * a)) * det)
2454         ._m23(Math.fma(-m20, e, Math.fma( m21, c, -m23 * a)) * det)
2455         ._m30(Math.fma(-m10, j, Math.fma( m11, h, -m12 * g)) * det)
2456         ._m31(Math.fma( m00, j, Math.fma(-m01, h,  m02 * g)) * det)
2457         ._m32(Math.fma(-m30, d, Math.fma( m31, b, -m32 * a)) * det)
2458         ._m33(Math.fma( m20, d, Math.fma(-m21, b,  m22 * a)) * det)
2459         ._properties(0);
2460     }
2461     private Matrix4d invertGenericThis(ref Matrix4d dest) {
2462         double a = m00 * m11 - m01 * m10;
2463         double b = m00 * m12 - m02 * m10;
2464         double c = m00 * m13 - m03 * m10;
2465         double d = m01 * m12 - m02 * m11;
2466         double e = m01 * m13 - m03 * m11;
2467         double f = m02 * m13 - m03 * m12;
2468         double g = m20 * m31 - m21 * m30;
2469         double h = m20 * m32 - m22 * m30;
2470         double i = m20 * m33 - m23 * m30;
2471         double j = m21 * m32 - m22 * m31;
2472         double k = m21 * m33 - m23 * m31;
2473         double l = m22 * m33 - m23 * m32;
2474         double det = a * l - b * k + c * j + d * i - e * h + f * g;
2475         det = 1.0 / det;
2476         double nm00 = Math.fma( m11, l, Math.fma(-m12, k,  m13 * j)) * det;
2477         double nm01 = Math.fma(-m01, l, Math.fma( m02, k, -m03 * j)) * det;
2478         double nm02 = Math.fma( m31, f, Math.fma(-m32, e,  m33 * d)) * det;
2479         double nm03 = Math.fma(-m21, f, Math.fma( m22, e, -m23 * d)) * det;
2480         double nm10 = Math.fma(-m10, l, Math.fma( m12, i, -m13 * h)) * det;
2481         double nm11 = Math.fma( m00, l, Math.fma(-m02, i,  m03 * h)) * det;
2482         double nm12 = Math.fma(-m30, f, Math.fma( m32, c, -m33 * b)) * det;
2483         double nm13 = Math.fma( m20, f, Math.fma(-m22, c,  m23 * b)) * det;
2484         double nm20 = Math.fma( m10, k, Math.fma(-m11, i,  m13 * g)) * det;
2485         double nm21 = Math.fma(-m00, k, Math.fma( m01, i, -m03 * g)) * det;
2486         double nm22 = Math.fma( m30, e, Math.fma(-m31, c,  m33 * a)) * det;
2487         double nm23 = Math.fma(-m20, e, Math.fma( m21, c, -m23 * a)) * det;
2488         double nm30 = Math.fma(-m10, j, Math.fma( m11, h, -m12 * g)) * det;
2489         double nm31 = Math.fma( m00, j, Math.fma(-m01, h,  m02 * g)) * det;
2490         double nm32 = Math.fma(-m30, d, Math.fma( m31, b, -m32 * a)) * det;
2491         double nm33 = Math.fma( m20, d, Math.fma(-m21, b,  m22 * a)) * det;
2492         return dest
2493         ._m00(nm00)
2494         ._m01(nm01)
2495         ._m02(nm02)
2496         ._m03(nm03)
2497         ._m10(nm10)
2498         ._m11(nm11)
2499         ._m12(nm12)
2500         ._m13(nm13)
2501         ._m20(nm20)
2502         ._m21(nm21)
2503         ._m22(nm22)
2504         ._m23(nm23)
2505         ._m30(nm30)
2506         ._m31(nm31)
2507         ._m32(nm32)
2508         ._m33(nm33)
2509         ._properties(0);
2510     }
2511 
2512     public Matrix4d invertPerspective(ref Matrix4d dest) {
2513         double a =  1.0 / (m00 * m11);
2514         double l = -1.0 / (m23 * m32);
2515         dest.set(m11 * a, 0, 0, 0,
2516                  0, m00 * a, 0, 0,
2517                  0, 0, 0, -m23 * l,
2518                  0, 0, -m32 * l, m22 * l);
2519         return dest;
2520     }
2521 
2522     /**
2523      * If <code>this</code> is a perspective projection matrix obtained via one of the {@link #perspective(double, double, double, double) perspective()} methods
2524      * or via {@link #setPerspective(double, double, double, double) setPerspective()}, that is, if <code>this</code> is a symmetrical perspective frustum transformation,
2525      * then this method builds the inverse of <code>this</code>.
2526      * <p>
2527      * 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()}.
2528      * 
2529      * @see #perspective(double, double, double, double)
2530      * 
2531      * @return this
2532      */
2533     ref public Matrix4d invertPerspective() return {
2534         invertPerspective(this);
2535         return this;
2536     }
2537 
2538     public Matrix4d invertFrustum(ref Matrix4d dest) {
2539         double invM00 = 1.0 / m00;
2540         double invM11 = 1.0 / m11;
2541         double invM23 = 1.0 / m23;
2542         double invM32 = 1.0 / m32;
2543         dest.set(invM00, 0, 0, 0,
2544                  0, invM11, 0, 0,
2545                  0, 0, 0, invM32,
2546                  -m20 * invM00 * invM23, -m21 * invM11 * invM23, invM23, -m22 * invM23 * invM32);
2547         return dest;
2548     }
2549 
2550     /**
2551      * 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
2552      * or via {@link #setFrustum(double, double, double, double, double, double) setFrustum()},
2553      * then this method builds the inverse of <code>this</code>.
2554      * <p>
2555      * This method can be used to quickly obtain the inverse of a perspective projection matrix.
2556      * <p>
2557      * If this matrix represents a symmetric perspective frustum transformation, as obtained via {@link #perspective(double, double, double, double) perspective()}, then
2558      * {@link #invertPerspective()} should be used instead.
2559      * 
2560      * @see #frustum(double, double, double, double, double, double)
2561      * @see #invertPerspective()
2562      * 
2563      * @return this
2564      */
2565     ref public Matrix4d invertFrustum() return {
2566         invertFrustum(this);
2567         return this;
2568     }
2569 
2570     public Matrix4d invertOrtho(ref Matrix4d dest) {
2571         double invM00 = 1.0 / m00;
2572         double invM11 = 1.0 / m11;
2573         double invM22 = 1.0 / m22;
2574         dest.set(invM00, 0, 0, 0,
2575                  0, invM11, 0, 0,
2576                  0, 0, invM22, 0,
2577                  -m30 * invM00, -m31 * invM11, -m32 * invM22, 1)
2578         ._properties(PROPERTY_AFFINE | (this.properties & PROPERTY_ORTHONORMAL));
2579         return dest;
2580     }
2581 
2582     /**
2583      * Invert <code>this</code> orthographic projection matrix.
2584      * <p>
2585      * This method can be used to quickly obtain the inverse of an orthographic projection matrix.
2586      * 
2587      * @return this
2588      */
2589     ref public Matrix4d invertOrtho() return {
2590         invertOrtho(this);
2591         return this;
2592     }
2593 
2594     public Matrix4d invertPerspectiveView(Matrix4d view, ref Matrix4d dest) {
2595         double a =  1.0 / (m00 * m11);
2596         double l = -1.0 / (m23 * m32);
2597         double pm00 =  m11 * a;
2598         double pm11 =  m00 * a;
2599         double pm23 = -m23 * l;
2600         double pm32 = -m32 * l;
2601         double pm33 =  m22 * l;
2602         double vm30 = -view.m00 * view.m30 - view.m01 * view.m31 - view.m02 * view.m32;
2603         double vm31 = -view.m10 * view.m30 - view.m11 * view.m31 - view.m12 * view.m32;
2604         double vm32 = -view.m20 * view.m30 - view.m21 * view.m31 - view.m22 * view.m32;
2605         double nm10 = view.m01 * pm11;
2606         double nm30 = view.m02 * pm32 + vm30 * pm33;
2607         double nm31 = view.m12 * pm32 + vm31 * pm33;
2608         double nm32 = view.m22 * pm32 + vm32 * pm33;
2609         return dest
2610         ._m00(view.m00 * pm00)
2611         ._m01(view.m10 * pm00)
2612         ._m02(view.m20 * pm00)
2613         ._m03(0.0)
2614         ._m10(nm10)
2615         ._m11(view.m11 * pm11)
2616         ._m12(view.m21 * pm11)
2617         ._m13(0.0)
2618         ._m20(vm30 * pm23)
2619         ._m21(vm31 * pm23)
2620         ._m22(vm32 * pm23)
2621         ._m23(pm23)
2622         ._m30(nm30)
2623         ._m31(nm31)
2624         ._m32(nm32)
2625         ._m33(pm33)
2626         ._properties(0);
2627     }
2628 
2629     public Matrix4d invertPerspectiveView(Matrix4x3d view, ref Matrix4d dest) {
2630         double a =  1.0 / (m00 * m11);
2631         double l = -1.0 / (m23 * m32);
2632         double pm00 =  m11 * a;
2633         double pm11 =  m00 * a;
2634         double pm23 = -m23 * l;
2635         double pm32 = -m32 * l;
2636         double pm33 =  m22 * l;
2637         double vm30 = -view.m00 * view.m30 - view.m01 * view.m31 - view.m02 * view.m32;
2638         double vm31 = -view.m10 * view.m30 - view.m11 * view.m31 - view.m12 * view.m32;
2639         double vm32 = -view.m20 * view.m30 - view.m21 * view.m31 - view.m22 * view.m32;
2640         return dest
2641         ._m00(view.m00 * pm00)
2642         ._m01(view.m10 * pm00)
2643         ._m02(view.m20 * pm00)
2644         ._m03(0.0)
2645         ._m10(view.m01 * pm11)
2646         ._m11(view.m11 * pm11)
2647         ._m12(view.m21 * pm11)
2648         ._m13(0.0)
2649         ._m20(vm30 * pm23)
2650         ._m21(vm31 * pm23)
2651         ._m22(vm32 * pm23)
2652         ._m23(pm23)
2653         ._m30(view.m02 * pm32 + vm30 * pm33)
2654         ._m31(view.m12 * pm32 + vm31 * pm33)
2655         ._m32(view.m22 * pm32 + vm32 * pm33)
2656         ._m33(pm33)
2657         ._properties(0);
2658     }
2659 
2660     public Matrix4d invertAffine(ref Matrix4d dest) {
2661         double m11m00 = m00 * m11, m10m01 = m01 * m10, m10m02 = m02 * m10;
2662         double m12m00 = m00 * m12, m12m01 = m01 * m12, m11m02 = m02 * m11;
2663         double s = 1.0 / ((m11m00 - m10m01) * m22 + (m10m02 - m12m00) * m21 + (m12m01 - m11m02) * m20);
2664         double m10m22 = m10 * m22, m10m21 = m10 * m21, m11m22 = m11 * m22;
2665         double m11m20 = m11 * m20, m12m21 = m12 * m21, m12m20 = m12 * m20;
2666         double m20m02 = m20 * m02, m20m01 = m20 * m01, m21m02 = m21 * m02;
2667         double m21m00 = m21 * m00, m22m01 = m22 * m01, m22m00 = m22 * m00;
2668         double nm00 = (m11m22 - m12m21) * s;
2669         double nm01 = (m21m02 - m22m01) * s;
2670         double nm02 = (m12m01 - m11m02) * s;
2671         double nm10 = (m12m20 - m10m22) * s;
2672         double nm11 = (m22m00 - m20m02) * s;
2673         double nm12 = (m10m02 - m12m00) * s;
2674         double nm20 = (m10m21 - m11m20) * s;
2675         double nm21 = (m20m01 - m21m00) * s;
2676         double nm22 = (m11m00 - m10m01) * s;
2677         double nm30 = (m10m22 * m31 - m10m21 * m32 + m11m20 * m32 - m11m22 * m30 + m12m21 * m30 - m12m20 * m31) * s;
2678         double nm31 = (m20m02 * m31 - m20m01 * m32 + m21m00 * m32 - m21m02 * m30 + m22m01 * m30 - m22m00 * m31) * s;
2679         double nm32 = (m11m02 * m30 - m12m01 * m30 + m12m00 * m31 - m10m02 * m31 + m10m01 * m32 - m11m00 * m32) * s;
2680         dest._m00(nm00)
2681         ._m01(nm01)
2682         ._m02(nm02)
2683         ._m03(0.0)
2684         ._m10(nm10)
2685         ._m11(nm11)
2686         ._m12(nm12)
2687         ._m13(0.0)
2688         ._m20(nm20)
2689         ._m21(nm21)
2690         ._m22(nm22)
2691         ._m23(0.0)
2692         ._m30(nm30)
2693         ._m31(nm31)
2694         ._m32(nm32)
2695         ._m33(1.0)
2696         ._properties(PROPERTY_AFFINE);
2697         return dest;
2698     }
2699 
2700     /**
2701      * 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>).
2702      * 
2703      * @return this
2704      */
2705     ref public Matrix4d invertAffine() return {
2706         invertAffine(this);
2707         return this;
2708     }
2709 
2710     /**
2711      * Transpose this matrix.
2712      * 
2713      * @return this
2714      */
2715     ref public Matrix4d transpose() return {
2716         transpose(this);
2717         return this;
2718     }
2719 
2720     public Matrix4d transpose(ref Matrix4d dest) {
2721         if ((properties & PROPERTY_IDENTITY) != 0)
2722             return dest.identity();
2723         else if (this != dest)
2724             return transposeNonThisGeneric(dest);
2725         return transposeThisGeneric(dest);
2726     }
2727     private Matrix4d transposeNonThisGeneric(ref Matrix4d dest) {
2728         return dest
2729         ._m00(m00)
2730         ._m01(m10)
2731         ._m02(m20)
2732         ._m03(m30)
2733         ._m10(m01)
2734         ._m11(m11)
2735         ._m12(m21)
2736         ._m13(m31)
2737         ._m20(m02)
2738         ._m21(m12)
2739         ._m22(m22)
2740         ._m23(m32)
2741         ._m30(m03)
2742         ._m31(m13)
2743         ._m32(m23)
2744         ._m33(m33)
2745         ._properties(0);
2746     }
2747     private Matrix4d transposeThisGeneric(ref Matrix4d dest) {
2748         double nm10 = m01;
2749         double nm20 = m02;
2750         double nm21 = m12;
2751         double nm30 = m03;
2752         double nm31 = m13;
2753         double nm32 = m23;
2754         return dest
2755         ._m01(m10)
2756         ._m02(m20)
2757         ._m03(m30)
2758         ._m10(nm10)
2759         ._m12(m21)
2760         ._m13(m31)
2761         ._m20(nm20)
2762         ._m21(nm21)
2763         ._m23(m32)
2764         ._m30(nm30)
2765         ._m31(nm31)
2766         ._m32(nm32)
2767         ._properties(0);
2768     }
2769 
2770     /**
2771      * Transpose only the upper left 3x3 submatrix of this matrix.
2772      * <p>
2773      * All other matrix elements are left unchanged.
2774      * 
2775      * @return this
2776      */
2777     ref public Matrix4d transpose3x3() return {
2778         transpose3x3(this);
2779         return this;
2780     }
2781 
2782     public Matrix4d transpose3x3(ref Matrix4d dest) {
2783         double nm10 = m01, nm20 = m02, nm21 = m12;
2784         return dest
2785         ._m00(m00)
2786         ._m01(m10)
2787         ._m02(m20)
2788         ._m10(nm10)
2789         ._m11(m11)
2790         ._m12(m21)
2791         ._m20(nm20)
2792         ._m21(nm21)
2793         ._m22(m22)
2794         ._properties(this.properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
2795     }
2796 
2797     public Matrix3d transpose3x3(ref Matrix3d dest) {
2798         return dest
2799         ._m00(m00)
2800         ._m01(m10)
2801         ._m02(m20)
2802         ._m10(m01)
2803         ._m11(m11)
2804         ._m12(m21)
2805         ._m20(m02)
2806         ._m21(m12)
2807         ._m22(m22);
2808     }
2809 
2810     /**
2811      * Set this matrix to be a simple translation matrix.
2812      * <p>
2813      * The resulting matrix can be multiplied against another transformation
2814      * matrix to obtain an additional translation.
2815      * 
2816      * @param x
2817      *          the offset to translate in x
2818      * @param y
2819      *          the offset to translate in y
2820      * @param z
2821      *          the offset to translate in z
2822      * @return this
2823      */
2824     ref public Matrix4d translation(double x, double y, double z) return {
2825         if ((properties & PROPERTY_IDENTITY) == 0)
2826             this._identity();
2827         return this.
2828         _m30(x).
2829         _m31(y).
2830         _m32(z).
2831         _m33(1.0).
2832         _properties(PROPERTY_AFFINE | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL);
2833     }
2834 
2835 
2836     /**
2837      * Set this matrix to be a simple translation matrix.
2838      * <p>
2839      * The resulting matrix can be multiplied against another transformation
2840      * matrix to obtain an additional translation.
2841      *
2842      * @param offset
2843      *              the offsets in x, y and z to translate
2844      * @return this
2845      */
2846     ref public Matrix4d translation(ref Vector3d offset) return {
2847         return translation(offset.x, offset.y, offset.z);
2848     }
2849 
2850     /**
2851      * Set only the translation components <code>(m30, m31, m32)</code> of this matrix to the given values <code>(x, y, z)</code>.
2852      * <p>
2853      * To build a translation matrix instead, use {@link #translation(double, double, double)}.
2854      * To apply a translation, use {@link #translate(double, double, double)}.
2855      * 
2856      * @see #translation(double, double, double)
2857      * @see #translate(double, double, double)
2858      * 
2859      * @param x
2860      *          the units to translate in x
2861      * @param y
2862      *          the units to translate in y
2863      * @param z
2864      *          the units to translate in z
2865      * @return this
2866      */
2867     ref public Matrix4d setTranslation(double x, double y, double z) return {
2868         _m30(x).
2869         _m31(y).
2870         _m32(z).
2871         properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY);
2872         return this;
2873     }
2874 
2875     /**
2876      * 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>.
2877      * <p>
2878      * To build a translation matrix instead, use {@link #translation(ref Vector3d)}.
2879      * To apply a translation, use {@link #translate(ref Vector3d)}.
2880      * 
2881      * @see #translation(ref Vector3d)
2882      * @see #translate(ref Vector3d)
2883      * 
2884      * @param xyz
2885      *          the units to translate in <code>(x, y, z)</code>
2886      * @return this
2887      */
2888     ref public Matrix4d setTranslation(ref Vector3d xyz) return {
2889         return setTranslation(xyz.x, xyz.y, xyz.z);
2890     }
2891 
2892     public Vector3d getTranslation(ref Vector3d dest) {
2893         dest.x = m30;
2894         dest.y = m31;
2895         dest.z = m32;
2896         return dest;
2897     }
2898 
2899     public Vector3d getScale(ref Vector3d dest) {
2900         dest.x = Math.sqrt(m00 * m00 + m01 * m01 + m02 * m02);
2901         dest.y = Math.sqrt(m10 * m10 + m11 * m11 + m12 * m12);
2902         dest.z = Math.sqrt(m20 * m20 + m21 * m21 + m22 * m22);
2903         return dest;
2904     }
2905 
2906     public Matrix4d get(ref Matrix4d dest) {
2907         return dest.set(this);
2908     }
2909 
2910     public Matrix4x3d get4x3(ref Matrix4x3d dest) {
2911         return dest.set(this);
2912     }
2913 
2914     public Matrix3d get3x3(ref Matrix3d dest) {
2915         return dest.set(this);
2916     }
2917 
2918     public Quaterniond getUnnormalizedRotation(ref Quaterniond dest) {
2919         return dest.setFromUnnormalized(this);
2920     }
2921 
2922     public Quaterniond getNormalizedRotation(ref Quaterniond dest) {
2923         return dest.setFromNormalized(this);
2924     }
2925 
2926     // Additional functionality for D (becomes a FloatBuffer)
2927     public float[16] getFloatArray() {
2928         float[16] dest;
2929         dest[0]  = cast(float)m00;
2930         dest[1]  = cast(float)m01;
2931         dest[2]  = cast(float)m02;
2932         dest[3]  = cast(float)m03;
2933         dest[4]  = cast(float)m10;
2934         dest[5]  = cast(float)m11;
2935         dest[6]  = cast(float)m12;
2936         dest[7]  = cast(float)m13;
2937         dest[8]  = cast(float)m20;
2938         dest[9]  = cast(float)m21;
2939         dest[10] = cast(float)m22;
2940         dest[11] = cast(float)m23;
2941         dest[12] = cast(float)m30;
2942         dest[13] = cast(float)m31;
2943         dest[14] = cast(float)m32;
2944         dest[15] = cast(float)m33;
2945         return dest;
2946     }
2947 
2948     // Additional functionality for D (becomes a FloatBuffer)
2949     public float[16] getFloatArray(ref float[16] dest, int offset) {
2950         dest[offset+0]  = cast(float)m00;
2951         dest[offset+1]  = cast(float)m01;
2952         dest[offset+2]  = cast(float)m02;
2953         dest[offset+3]  = cast(float)m03;
2954         dest[offset+4]  = cast(float)m10;
2955         dest[offset+5]  = cast(float)m11;
2956         dest[offset+6]  = cast(float)m12;
2957         dest[offset+7]  = cast(float)m13;
2958         dest[offset+8]  = cast(float)m20;
2959         dest[offset+9]  = cast(float)m21;
2960         dest[offset+10] = cast(float)m22;
2961         dest[offset+11] = cast(float)m23;
2962         dest[offset+12] = cast(float)m30;
2963         dest[offset+13] = cast(float)m31;
2964         dest[offset+14] = cast(float)m32;
2965         dest[offset+15] = cast(float)m33;
2966         return dest;
2967     }
2968 
2969     public double[] get(ref double[] dest, int offset) {
2970         dest[offset+0]  = m00;
2971         dest[offset+1]  = m01;
2972         dest[offset+2]  = m02;
2973         dest[offset+3]  = m03;
2974         dest[offset+4]  = m10;
2975         dest[offset+5]  = m11;
2976         dest[offset+6]  = m12;
2977         dest[offset+7]  = m13;
2978         dest[offset+8]  = m20;
2979         dest[offset+9]  = m21;
2980         dest[offset+10] = m22;
2981         dest[offset+11] = m23;
2982         dest[offset+12] = m30;
2983         dest[offset+13] = m31;
2984         dest[offset+14] = m32;
2985         dest[offset+15] = m33;
2986         return dest;
2987     }
2988 
2989     public double[] get(ref double[] dest) {
2990         return get(dest, 0);
2991     }
2992 
2993     /**
2994      * Set all the values within this matrix to 0.
2995      * 
2996      * @return this
2997      */
2998     ref public Matrix4d zero() return {
2999         return
3000         _m00(0.0).
3001         _m01(0.0).
3002         _m02(0.0).
3003         _m03(0.0).
3004         _m10(0.0).
3005         _m11(0.0).
3006         _m12(0.0).
3007         _m13(0.0).
3008         _m20(0.0).
3009         _m21(0.0).
3010         _m22(0.0).
3011         _m23(0.0).
3012         _m30(0.0).
3013         _m31(0.0).
3014         _m32(0.0).
3015         _m33(0.0).
3016         _properties(0);
3017     }
3018 
3019     /**
3020      * Set this matrix to be a simple scale matrix, which scales all axes uniformly by the given factor.
3021      * <p>
3022      * The resulting matrix can be multiplied against another transformation
3023      * matrix to obtain an additional scaling.
3024      * <p>
3025      * In order to post-multiply a scaling transformation directly to a
3026      * matrix, use {@link #scale(double) scale()} instead.
3027      * 
3028      * @see #scale(double)
3029      * 
3030      * @param factor
3031      *             the scale factor in x, y and z
3032      * @return this
3033      */
3034     ref public Matrix4d scaling(double factor) return {
3035         return scaling(factor, factor, factor);
3036     }
3037 
3038     /**
3039      * Set this matrix to be a simple scale matrix.
3040      * 
3041      * @param x
3042      *          the scale in x
3043      * @param y
3044      *          the scale in y
3045      * @param z
3046      *          the scale in z         
3047      * @return this
3048      */
3049     ref public Matrix4d scaling(double x, double y, double z) return {
3050         if ((properties & PROPERTY_IDENTITY) == 0)
3051             identity();
3052         bool one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z);
3053         _m00(x).
3054         _m11(y).
3055         _m22(z).
3056         properties = PROPERTY_AFFINE | (one ? PROPERTY_ORTHONORMAL : 0);
3057         return this;
3058     }
3059 
3060     /**
3061      * Set this matrix to be a simple scale matrix which scales the base axes by
3062      * <code>xyz.x</code>, <code>xyz.y</code> and <code>xyz.z</code>, respectively.
3063      * <p>
3064      * The resulting matrix can be multiplied against another transformation
3065      * matrix to obtain an additional scaling.
3066      * <p>
3067      * In order to post-multiply a scaling transformation directly to a
3068      * matrix use {@link #scale(ref Vector3d) scale()} instead.
3069      * 
3070      * @see #scale(ref Vector3d)
3071      * 
3072      * @param xyz
3073      *             the scale in x, y and z, respectively
3074      * @return this
3075      */
3076     ref public Matrix4d scaling(ref Vector3d xyz) return {
3077         return scaling(xyz.x, xyz.y, xyz.z);
3078     }
3079 
3080     /**
3081      * Set this matrix to a rotation matrix which rotates the given radians about a given axis.
3082      * <p>
3083      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3084      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3085      * When used with a left-handed coordinate system, the rotation is clockwise.
3086      * <p>
3087      * From <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">Wikipedia</a>
3088      * 
3089      * @param angle
3090      *          the angle in radians
3091      * @param x
3092      *          the x-coordinate of the axis to rotate about
3093      * @param y
3094      *          the y-coordinate of the axis to rotate about
3095      * @param z
3096      *          the z-coordinate of the axis to rotate about
3097      * @return this
3098      */
3099     ref public Matrix4d rotation(double angle, double x, double y, double z) return {
3100         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
3101             rotationX(x * angle);
3102         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
3103             rotationY(y * angle);
3104         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
3105             rotationZ(z * angle);
3106         else
3107             rotationInternal(angle, x, y, z);
3108         return this;
3109     }
3110     private Matrix4d rotationInternal(double angle, double x, double y, double z) {
3111         double sin = Math.sin(angle);
3112         double cos = Math.cosFromSin(sin, angle);
3113         double C = 1.0 - cos;
3114         double xy = x * y, xz = x * z, yz = y * z;
3115         if ((properties & PROPERTY_IDENTITY) == 0)
3116             this._identity();
3117         _m00(cos + x * x * C).
3118         _m10(xy * C - z * sin).
3119         _m20(xz * C + y * sin).
3120         _m01(xy * C + z * sin).
3121         _m11(cos + y * y * C).
3122         _m21(yz * C - x * sin).
3123         _m02(xz * C - y * sin).
3124         _m12(yz * C + x * sin).
3125         _m22(cos + z * z * C).
3126         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3127         return this;
3128     }
3129 
3130     /**
3131      * Set this matrix to a rotation transformation about the X axis.
3132      * <p>
3133      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3134      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3135      * When used with a left-handed coordinate system, the rotation is clockwise.
3136      * <p>
3137      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
3138      * 
3139      * @param ang
3140      *            the angle in radians
3141      * @return this
3142      */
3143     ref public Matrix4d rotationX(double ang) return {
3144         double sin, cos;
3145         sin = Math.sin(ang);
3146         cos = Math.cosFromSin(sin, ang);
3147         if ((properties & PROPERTY_IDENTITY) == 0)
3148             this._identity();
3149         _m11(cos).
3150         _m12(sin).
3151         _m21(-sin).
3152         _m22(cos).
3153         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3154         return this;
3155     }
3156 
3157     /**
3158      * Set this matrix to a rotation transformation about the Y axis.
3159      * <p>
3160      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3161      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3162      * When used with a left-handed coordinate system, the rotation is clockwise.
3163      * <p>
3164      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
3165      * 
3166      * @param ang
3167      *            the angle in radians
3168      * @return this
3169      */
3170     ref public Matrix4d rotationY(double ang) return {
3171         double sin, cos;
3172         sin = Math.sin(ang);
3173         cos = Math.cosFromSin(sin, ang);
3174         if ((properties & PROPERTY_IDENTITY) == 0)
3175             this._identity();
3176         _m00(cos).
3177         _m02(-sin).
3178         _m20(sin).
3179         _m22(cos).
3180         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3181         return this;
3182     }
3183 
3184     /**
3185      * Set this matrix to a rotation transformation about the Z axis.
3186      * <p>
3187      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3188      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3189      * When used with a left-handed coordinate system, the rotation is clockwise.
3190      * <p>
3191      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
3192      * 
3193      * @param ang
3194      *            the angle in radians
3195      * @return this
3196      */
3197     ref public Matrix4d rotationZ(double ang) return {
3198         double sin, cos;
3199         sin = Math.sin(ang);
3200         cos = Math.cosFromSin(sin, ang);
3201         if ((properties & PROPERTY_IDENTITY) == 0)
3202             this._identity();
3203         _m00(cos).
3204         _m01(sin).
3205         _m10(-sin).
3206         _m11(cos).
3207         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3208         return this;
3209     }
3210 
3211     /**
3212      * Set this matrix to a rotation transformation about the Z axis to align the local <code>+X</code> towards <code>(dirX, dirY)</code>.
3213      * <p>
3214      * The vector <code>(dirX, dirY)</code> must be a unit vector.
3215      * 
3216      * @param dirX
3217      *            the x component of the normalized direction
3218      * @param dirY
3219      *            the y component of the normalized direction
3220      * @return this
3221      */
3222     ref public Matrix4d rotationTowardsXY(double dirX, double dirY) return {
3223         if ((properties & PROPERTY_IDENTITY) == 0)
3224             this._identity();
3225         setm00(dirY);
3226         setm01(dirX);
3227         setm10(-dirX);
3228         setm11(dirY);
3229         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3230         return this;
3231     }
3232 
3233     /**
3234      * Set this matrix to a rotation of <code>angleX</code> radians about the X axis, followed by a rotation
3235      * of <code>angleY</code> radians about the Y axis and followed by a rotation of <code>angleZ</code> radians about the Z axis.
3236      * <p>
3237      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3238      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3239      * When used with a left-handed coordinate system, the rotation is clockwise.
3240      * <p>
3241      * This method is equivalent to calling: <code>rotationX(angleX).rotateY(angleY).rotateZ(angleZ)</code>
3242      * 
3243      * @param angleX
3244      *            the angle to rotate about X
3245      * @param angleY
3246      *            the angle to rotate about Y
3247      * @param angleZ
3248      *            the angle to rotate about Z
3249      * @return this
3250      */
3251     ref public Matrix4d rotationXYZ(double angleX, double angleY, double angleZ) return {
3252         double sinX = Math.sin(angleX);
3253         double cosX = Math.cosFromSin(sinX, angleX);
3254         double sinY = Math.sin(angleY);
3255         double cosY = Math.cosFromSin(sinY, angleY);
3256         double sinZ = Math.sin(angleZ);
3257         double cosZ = Math.cosFromSin(sinZ, angleZ);
3258         double m_sinX = -sinX;
3259         double m_sinY = -sinY;
3260         double m_sinZ = -sinZ;
3261         if ((properties & PROPERTY_IDENTITY) == 0)
3262             this._identity();
3263 
3264         // rotateX
3265         double nm11 = cosX;
3266         double nm12 = sinX;
3267         double nm21 = m_sinX;
3268         double nm22 = cosX;
3269         // rotateY
3270         double nm00 = cosY;
3271         double nm01 = nm21 * m_sinY;
3272         double nm02 = nm22 * m_sinY;
3273         _m20(sinY).
3274         _m21(nm21 * cosY).
3275         _m22(nm22 * cosY).
3276         // rotateZ
3277         _m00(nm00 * cosZ).
3278         _m01(nm01 * cosZ + nm11 * sinZ).
3279         _m02(nm02 * cosZ + nm12 * sinZ).
3280         _m10(nm00 * m_sinZ).
3281         _m11(nm01 * m_sinZ + nm11 * cosZ).
3282         _m12(nm02 * m_sinZ + nm12 * cosZ).
3283         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3284         return this;
3285     }
3286 
3287     /**
3288      * Set this matrix to a rotation of <code>angleZ</code> radians about the Z axis, followed by a rotation
3289      * of <code>angleY</code> radians about the Y axis and followed by a rotation of <code>angleX</code> radians about the X axis.
3290      * <p>
3291      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3292      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3293      * When used with a left-handed coordinate system, the rotation is clockwise.
3294      * <p>
3295      * This method is equivalent to calling: <code>rotationZ(angleZ).rotateY(angleY).rotateX(angleX)</code>
3296      * 
3297      * @param angleZ
3298      *            the angle to rotate about Z
3299      * @param angleY
3300      *            the angle to rotate about Y
3301      * @param angleX
3302      *            the angle to rotate about X
3303      * @return this
3304      */
3305     ref public Matrix4d rotationZYX(double angleZ, double angleY, double angleX) return {
3306         double sinX = Math.sin(angleX);
3307         double cosX = Math.cosFromSin(sinX, angleX);
3308         double sinY = Math.sin(angleY);
3309         double cosY = Math.cosFromSin(sinY, angleY);
3310         double sinZ = Math.sin(angleZ);
3311         double cosZ = Math.cosFromSin(sinZ, angleZ);
3312         double m_sinZ = -sinZ;
3313         double m_sinY = -sinY;
3314         double m_sinX = -sinX;
3315         if ((properties & PROPERTY_IDENTITY) == 0)
3316             this._identity();
3317 
3318         // rotateZ
3319         double nm00 = cosZ;
3320         double nm01 = sinZ;
3321         double nm10 = m_sinZ;
3322         double nm11 = cosZ;
3323         // rotateY
3324         double nm20 = nm00 * sinY;
3325         double nm21 = nm01 * sinY;
3326         double nm22 = cosY;
3327         _m00(nm00 * cosY).
3328         _m01(nm01 * cosY).
3329         _m02(m_sinY).
3330         // rotateX
3331         _m10(nm10 * cosX + nm20 * sinX).
3332         _m11(nm11 * cosX + nm21 * sinX).
3333         _m12(nm22 * sinX).
3334         _m20(nm10 * m_sinX + nm20 * cosX).
3335         _m21(nm11 * m_sinX + nm21 * cosX).
3336         _m22(nm22 * cosX).
3337         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3338         return this;
3339     }
3340 
3341     /**
3342      * Set this matrix to a rotation of <code>angleY</code> radians about the Y axis, followed by a rotation
3343      * of <code>angleX</code> radians about the X axis and followed by a rotation of <code>angleZ</code> radians about the Z axis.
3344      * <p>
3345      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3346      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3347      * When used with a left-handed coordinate system, the rotation is clockwise.
3348      * <p>
3349      * This method is equivalent to calling: <code>rotationY(angleY).rotateX(angleX).rotateZ(angleZ)</code>
3350      * 
3351      * @param angleY
3352      *            the angle to rotate about Y
3353      * @param angleX
3354      *            the angle to rotate about X
3355      * @param angleZ
3356      *            the angle to rotate about Z
3357      * @return this
3358      */
3359     ref public Matrix4d rotationYXZ(double angleY, double angleX, double angleZ) return {
3360         double sinX = Math.sin(angleX);
3361         double cosX = Math.cosFromSin(sinX, angleX);
3362         double sinY = Math.sin(angleY);
3363         double cosY = Math.cosFromSin(sinY, angleY);
3364         double sinZ = Math.sin(angleZ);
3365         double cosZ = Math.cosFromSin(sinZ, angleZ);
3366         double m_sinY = -sinY;
3367         double m_sinX = -sinX;
3368         double m_sinZ = -sinZ;
3369 
3370         // rotateY
3371         double nm00 = cosY;
3372         double nm02 = m_sinY;
3373         double nm20 = sinY;
3374         double nm22 = cosY;
3375         // rotateX
3376         double nm10 = nm20 * sinX;
3377         double nm11 = cosX;
3378         double nm12 = nm22 * sinX;
3379         _m20(nm20 * cosX).
3380         _m21(m_sinX).
3381         _m22(nm22 * cosX).
3382         _m23(0.0).
3383         // rotateZ
3384         _m00(nm00 * cosZ + nm10 * sinZ).
3385         _m01(nm11 * sinZ).
3386         _m02(nm02 * cosZ + nm12 * sinZ).
3387         _m03(0.0).
3388         _m10(nm00 * m_sinZ + nm10 * cosZ).
3389         _m11(nm11 * cosZ).
3390         _m12(nm02 * m_sinZ + nm12 * cosZ).
3391         _m13(0.0).
3392         // set last column to identity
3393         _m30(0.0).
3394         _m31(0.0).
3395         _m32(0.0).
3396         _m33(1.0).
3397         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
3398         return this;
3399     }
3400 
3401     /**
3402      * 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
3403      * of <code>angleY</code> radians about the Y axis and followed by a rotation of <code>angleZ</code> radians about the Z axis.
3404      * <p>
3405      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3406      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3407      * When used with a left-handed coordinate system, the rotation is clockwise.
3408      * 
3409      * @param angleX
3410      *            the angle to rotate about X
3411      * @param angleY
3412      *            the angle to rotate about Y
3413      * @param angleZ
3414      *            the angle to rotate about Z
3415      * @return this
3416      */
3417     ref public Matrix4d setRotationXYZ(double angleX, double angleY, double angleZ) return {
3418         double sinX = Math.sin(angleX);
3419         double cosX = Math.cosFromSin(sinX, angleX);
3420         double sinY = Math.sin(angleY);
3421         double cosY = Math.cosFromSin(sinY, angleY);
3422         double sinZ = Math.sin(angleZ);
3423         double cosZ = Math.cosFromSin(sinZ, angleZ);
3424         double m_sinX = -sinX;
3425         double m_sinY = -sinY;
3426         double m_sinZ = -sinZ;
3427 
3428         // rotateX
3429         double nm11 = cosX;
3430         double nm12 = sinX;
3431         double nm21 = m_sinX;
3432         double nm22 = cosX;
3433         // rotateY
3434         double nm00 = cosY;
3435         double nm01 = nm21 * m_sinY;
3436         double nm02 = nm22 * m_sinY;
3437         _m20(sinY).
3438         _m21(nm21 * cosY).
3439         _m22(nm22 * cosY).
3440         // rotateZ
3441         _m00(nm00 * cosZ).
3442         _m01(nm01 * cosZ + nm11 * sinZ).
3443         _m02(nm02 * cosZ + nm12 * sinZ).
3444         _m10(nm00 * m_sinZ).
3445         _m11(nm01 * m_sinZ + nm11 * cosZ).
3446         _m12(nm02 * m_sinZ + nm12 * cosZ).
3447         properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
3448         return this;
3449     }
3450 
3451     /**
3452      * 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
3453      * of <code>angleY</code> radians about the Y axis and followed by a rotation of <code>angleX</code> radians about the X axis.
3454      * <p>
3455      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3456      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3457      * When used with a left-handed coordinate system, the rotation is clockwise.
3458      * 
3459      * @param angleZ
3460      *            the angle to rotate about Z
3461      * @param angleY
3462      *            the angle to rotate about Y
3463      * @param angleX
3464      *            the angle to rotate about X
3465      * @return this
3466      */
3467     ref public Matrix4d setRotationZYX(double angleZ, double angleY, double angleX) return {
3468         double sinX = Math.sin(angleX);
3469         double cosX = Math.cosFromSin(sinX, angleX);
3470         double sinY = Math.sin(angleY);
3471         double cosY = Math.cosFromSin(sinY, angleY);
3472         double sinZ = Math.sin(angleZ);
3473         double cosZ = Math.cosFromSin(sinZ, angleZ);
3474         double m_sinZ = -sinZ;
3475         double m_sinY = -sinY;
3476         double m_sinX = -sinX;
3477 
3478         // rotateZ
3479         double nm00 = cosZ;
3480         double nm01 = sinZ;
3481         double nm10 = m_sinZ;
3482         double nm11 = cosZ;
3483         // rotateY
3484         double nm20 = nm00 * sinY;
3485         double nm21 = nm01 * sinY;
3486         double nm22 = cosY;
3487         _m00(nm00 * cosY).
3488         _m01(nm01 * cosY).
3489         _m02(m_sinY).
3490         // rotateX
3491         _m10(nm10 * cosX + nm20 * sinX).
3492         _m11(nm11 * cosX + nm21 * sinX).
3493         _m12(nm22 * sinX).
3494         _m20(nm10 * m_sinX + nm20 * cosX).
3495         _m21(nm11 * m_sinX + nm21 * cosX).
3496         _m22(nm22 * cosX).
3497         properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
3498         return this;
3499     }
3500 
3501     /**
3502      * 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
3503      * of <code>angleX</code> radians about the X axis and followed by a rotation of <code>angleZ</code> radians about the Z axis.
3504      * <p>
3505      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3506      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3507      * When used with a left-handed coordinate system, the rotation is clockwise.
3508      * 
3509      * @param angleY
3510      *            the angle to rotate about Y
3511      * @param angleX
3512      *            the angle to rotate about X
3513      * @param angleZ
3514      *            the angle to rotate about Z
3515      * @return this
3516      */
3517     ref public Matrix4d setRotationYXZ(double angleY, double angleX, double angleZ) return {
3518         double sinX = Math.sin(angleX);
3519         double cosX = Math.cosFromSin(sinX, angleX);
3520         double sinY = Math.sin(angleY);
3521         double cosY = Math.cosFromSin(sinY, angleY);
3522         double sinZ = Math.sin(angleZ);
3523         double cosZ = Math.cosFromSin(sinZ, angleZ);
3524         double m_sinY = -sinY;
3525         double m_sinX = -sinX;
3526         double m_sinZ = -sinZ;
3527 
3528         // rotateY
3529         double nm00 = cosY;
3530         double nm02 = m_sinY;
3531         double nm20 = sinY;
3532         double nm22 = cosY;
3533         // rotateX
3534         double nm10 = nm20 * sinX;
3535         double nm11 = cosX;
3536         double nm12 = nm22 * sinX;
3537         _m20(nm20 * cosX).
3538         _m21(m_sinX).
3539         _m22(nm22 * cosX).
3540         // rotateZ
3541         _m00(nm00 * cosZ + nm10 * sinZ).
3542         _m01(nm11 * sinZ).
3543         _m02(nm02 * cosZ + nm12 * sinZ).
3544         _m10(nm00 * m_sinZ + nm10 * cosZ).
3545         _m11(nm11 * cosZ).
3546         _m12(nm02 * m_sinZ + nm12 * cosZ).
3547         properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION);
3548         return this;
3549     }
3550 
3551     /**
3552      * Set this matrix to a rotation matrix which rotates the given radians about a given axis.
3553      * <p>
3554      * The axis described by the <code>axis</code> vector needs to be a unit vector.
3555      * <p>
3556      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
3557      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
3558      * When used with a left-handed coordinate system, the rotation is clockwise.
3559      * 
3560      * @param angle
3561      *          the angle in radians
3562      * @param axis
3563      *          the axis to rotate about
3564      * @return this
3565      */
3566     ref public Matrix4d rotation(double angle, Vector3d axis) return {
3567         return rotation(angle, axis.x, axis.y, axis.z);
3568     }
3569 
3570 
3571     public Vector4d transform(ref Vector4d v) {
3572         return v.mul(this);
3573     }
3574 
3575     public Vector4d transform(ref Vector4d v, ref Vector4d dest) {
3576         return v.mul(this, dest);
3577     }
3578 
3579     public Vector4d transform(double x, double y, double z, double w, ref Vector4d dest) {
3580         return dest.set(m00 * x + m10 * y + m20 * z + m30 * w,
3581                         m01 * x + m11 * y + m21 * z + m31 * w,
3582                         m02 * x + m12 * y + m22 * z + m32 * w,
3583                         m03 * x + m13 * y + m23 * z + m33 * w);
3584     }
3585 
3586     public Vector4d transformTranspose(ref Vector4d v) {
3587         return v.mulTranspose(this);
3588     }
3589     public Vector4d transformTranspose(ref Vector4d v, ref Vector4d dest) {
3590         return v.mulTranspose(this, dest);
3591     }
3592     public Vector4d transformTranspose(double x, double y, double z, double w, ref Vector4d dest) {
3593        return dest.set(x, y, z, w).mulTranspose(this);
3594     }
3595 
3596     public Vector4d transformProject(ref Vector4d v) {
3597         return v.mulProject(this);
3598     }
3599 
3600     public Vector4d transformProject(ref Vector4d v, ref Vector4d dest) {
3601         return v.mulProject(this, dest);
3602     }
3603 
3604     public Vector4d transformProject(double x, double y, double z, double w, ref Vector4d dest) {
3605         double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33 * w);
3606         return dest.set((m00 * x + m10 * y + m20 * z + m30 * w) * invW,
3607                         (m01 * x + m11 * y + m21 * z + m31 * w) * invW,
3608                         (m02 * x + m12 * y + m22 * z + m32 * w) * invW,
3609                         1.0);
3610     }
3611 
3612     public Vector3d transformProject(ref Vector3d v) {
3613         return v.mulProject(this);
3614     }
3615 
3616     public Vector3d transformProject(ref Vector3d v, ref Vector3d dest) {
3617         return v.mulProject(this, dest);
3618     }
3619 
3620     public Vector3d transformProject(double x, double y, double z, ref Vector3d dest) {
3621         double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33);
3622         return dest.set((m00 * x + m10 * y + m20 * z + m30) * invW,
3623                         (m01 * x + m11 * y + m21 * z + m31) * invW,
3624                         (m02 * x + m12 * y + m22 * z + m32) * invW);
3625     }
3626 
3627     public Vector3d transformProject(ref Vector4d v, ref Vector3d dest) {
3628         return v.mulProject(this, dest);
3629     }
3630 
3631     public Vector3d transformProject(double x, double y, double z, double w, ref Vector3d dest) {
3632         dest.x = x;
3633         dest.y = y;
3634         dest.z = z;
3635         return dest.mulProject(this, w, dest);
3636     }
3637 
3638     public Vector3d transformPosition(ref Vector3d dest) {
3639         return dest.set(m00 * dest.x + m10 * dest.y + m20 * dest.z + m30,
3640                         m01 * dest.x + m11 * dest.y + m21 * dest.z + m31,
3641                         m02 * dest.x + m12 * dest.y + m22 * dest.z + m32);
3642     }
3643 
3644     public Vector3d transformPosition(ref Vector3d v, ref Vector3d dest) {
3645         return transformPosition(v.x, v.y, v.z, dest);
3646     }
3647 
3648     public Vector3d transformPosition(double x, double y, double z, ref Vector3d dest) {
3649         return dest.set(m00 * x + m10 * y + m20 * z + m30,
3650                         m01 * x + m11 * y + m21 * z + m31,
3651                         m02 * x + m12 * y + m22 * z + m32);
3652     }
3653 
3654     public Vector3d transformDirection(ref Vector3d dest) {
3655         return dest.set(m00 * dest.x + m10 * dest.y + m20 * dest.z,
3656                         m01 * dest.x + m11 * dest.y + m21 * dest.z,
3657                         m02 * dest.x + m12 * dest.y + m22 * dest.z);
3658     }
3659 
3660     public Vector3d transformDirection(ref Vector3d v, ref Vector3d dest) {
3661         return dest.set(m00 * v.x + m10 * v.y + m20 * v.z,
3662                         m01 * v.x + m11 * v.y + m21 * v.z,
3663                         m02 * v.x + m12 * v.y + m22 * v.z);
3664     }
3665 
3666     public Vector3d transformDirection(double x, double y, double z, ref Vector3d dest) {
3667         return dest.set(m00 * x + m10 * y + m20 * z,
3668                         m01 * x + m11 * y + m21 * z,
3669                         m02 * x + m12 * y + m22 * z);
3670     }
3671 
3672     public Vector4d transformAffine(ref Vector4d dest) {
3673         return dest.mulAffine(this, dest);
3674     }
3675 
3676     public Vector4d transformAffine(ref Vector4d v, ref Vector4d dest) {
3677         return transformAffine(v.x, v.y, v.z, v.w, dest);
3678     }
3679 
3680     public Vector4d transformAffine(double x, double y, double z, double w, ref Vector4d dest) {
3681         double rx = m00 * x + m10 * y + m20 * z + m30 * w;
3682         double ry = m01 * x + m11 * y + m21 * z + m31 * w;
3683         double rz = m02 * x + m12 * y + m22 * z + m32 * w;
3684         dest.x = rx;
3685         dest.y = ry;
3686         dest.z = rz;
3687         dest.w = w;
3688         return dest;
3689     }
3690 
3691     /**
3692      * Set the upper left 3x3 submatrix of this {@link Matrix4d} to the given {@link Matrix3d} and don't change the other elements.
3693      * 
3694      * @param mat
3695      *          the 3x3 matrix
3696      * @return this
3697      */
3698     ref public Matrix4d set3x3(Matrix3d mat) return {
3699         return
3700         _m00(mat.m00).
3701         _m01(mat.m01).
3702         _m02(mat.m02).
3703         _m10(mat.m10).
3704         _m11(mat.m11).
3705         _m12(mat.m12).
3706         _m20(mat.m20).
3707         _m21(mat.m21).
3708         _m22(mat.m22).
3709         _properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
3710     }
3711 
3712     public Matrix4d scale(ref Vector3d xyz, ref Matrix4d dest) {
3713         return scale(xyz.x, xyz.y, xyz.z, dest);
3714     }
3715 
3716     /**
3717      * Apply scaling to this matrix by scaling the base axes by the given <code>xyz.x</code>,
3718      * <code>xyz.y</code> and <code>xyz.z</code> factors, respectively.
3719      * <p>
3720      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3721      * then the new matrix will be <code>M * S</code>. So when transforming a
3722      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
3723      * scaling will be applied first!
3724      * 
3725      * @param xyz
3726      *            the factors of the x, y and z component, respectively
3727      * @return this
3728      */
3729     ref public Matrix4d scale(ref Vector3d xyz) return {
3730         scale(xyz.x, xyz.y, xyz.z, this);
3731         return this;
3732     }
3733 
3734     public Matrix4d scale(double x, double y, double z, ref Matrix4d dest) {
3735         if ((properties & PROPERTY_IDENTITY) != 0)
3736             return dest.scaling(x, y, z);
3737         return scaleGeneric(x, y, z, dest);
3738     }
3739     private Matrix4d scaleGeneric(double x, double y, double z, ref Matrix4d dest) {
3740         bool one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z);
3741         dest._m00(m00 * x)
3742         ._m01(m01 * x)
3743         ._m02(m02 * x)
3744         ._m03(m03 * x)
3745         ._m10(m10 * y)
3746         ._m11(m11 * y)
3747         ._m12(m12 * y)
3748         ._m13(m13 * y)
3749         ._m20(m20 * z)
3750         ._m21(m21 * z)
3751         ._m22(m22 * z)
3752         ._m23(m23 * z)
3753         ._m30(m30)
3754         ._m31(m31)
3755         ._m32(m32)
3756         ._m33(m33)
3757         ._properties(properties
3758                 & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | (one ? 0 : PROPERTY_ORTHONORMAL)));
3759         return dest;
3760     }
3761 
3762     /**
3763      * Apply scaling to <code>this</code> matrix by scaling the base axes by the given x,
3764      * y and z factors.
3765      * <p>
3766      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3767      * then the new matrix will be <code>M * S</code>. So when transforming a
3768      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>
3769      * , the scaling will be applied first!
3770      * 
3771      * @param x
3772      *            the factor of the x component
3773      * @param y
3774      *            the factor of the y component
3775      * @param z
3776      *            the factor of the z component
3777      * @return this
3778      */
3779     ref public Matrix4d scale(double x, double y, double z) return {
3780         scale(x, y, z, this);
3781         return this;
3782     }
3783 
3784     public Matrix4d scale(double xyz, ref Matrix4d dest) {
3785         return scale(xyz, xyz, xyz, dest);
3786     }
3787 
3788     /**
3789      * Apply scaling to this matrix by uniformly scaling all base axes by the given xyz factor.
3790      * <p>
3791      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3792      * then the new matrix will be <code>M * S</code>. So when transforming a
3793      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>
3794      * , the scaling will be applied first!
3795      * 
3796      * @see #scale(double, double, double)
3797      * 
3798      * @param xyz
3799      *            the factor for all components
3800      * @return this
3801      */
3802     ref public Matrix4d scale(double xyz) return {
3803         return scale(xyz, xyz, xyz);
3804     }
3805 
3806     public Matrix4d scaleXY(double x, double y, ref Matrix4d dest) {
3807         return scale(x, y, 1.0, dest);
3808     }
3809 
3810     /**
3811      * Apply scaling to this matrix by scaling the X axis by <code>x</code> and the Y axis by <code>y</code>.
3812      * <p>
3813      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3814      * then the new matrix will be <code>M * S</code>. So when transforming a
3815      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
3816      * scaling will be applied first!
3817      * 
3818      * @param x
3819      *            the factor of the x component
3820      * @param y
3821      *            the factor of the y component
3822      * @return this
3823      */
3824     ref public Matrix4d scaleXY(double x, double y) return {
3825         return scale(x, y, 1.0);
3826     }
3827 
3828     public Matrix4d scaleAround(double sx, double sy, double sz, double ox, double oy, double oz, ref Matrix4d dest) {
3829         double nm30 = m00 * ox + m10 * oy + m20 * oz + m30;
3830         double nm31 = m01 * ox + m11 * oy + m21 * oz + m31;
3831         double nm32 = m02 * ox + m12 * oy + m22 * oz + m32;
3832         double nm33 = m03 * ox + m13 * oy + m23 * oz + m33;
3833         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
3834         return dest
3835         ._m00(m00 * sx)
3836         ._m01(m01 * sx)
3837         ._m02(m02 * sx)
3838         ._m03(m03 * sx)
3839         ._m10(m10 * sy)
3840         ._m11(m11 * sy)
3841         ._m12(m12 * sy)
3842         ._m13(m13 * sy)
3843         ._m20(m20 * sz)
3844         ._m21(m21 * sz)
3845         ._m22(m22 * sz)
3846         ._m23(m23 * sz)
3847         ._m30(-dest.m00 * ox - dest.m10 * oy - dest.m20 * oz + nm30)
3848         ._m31(-dest.m01 * ox - dest.m11 * oy - dest.m21 * oz + nm31)
3849         ._m32(-dest.m02 * ox - dest.m12 * oy - dest.m22 * oz + nm32)
3850         ._m33(-dest.m03 * ox - dest.m13 * oy - dest.m23 * oz + nm33)
3851         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION
3852             | (one ? 0 : PROPERTY_ORTHONORMAL)));
3853     }
3854 
3855     /**
3856      * Apply scaling to this matrix by scaling the base axes by the given sx,
3857      * sy and sz factors while using <code>(ox, oy, oz)</code> as the scaling origin.
3858      * <p>
3859      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3860      * then the new matrix will be <code>M * S</code>. So when transforming a
3861      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
3862      * scaling will be applied first!
3863      * <p>
3864      * This method is equivalent to calling: <code>translate(ox, oy, oz).scale(sx, sy, sz).translate(-ox, -oy, -oz)</code>
3865      * 
3866      * @param sx
3867      *            the scaling factor of the x component
3868      * @param sy
3869      *            the scaling factor of the y component
3870      * @param sz
3871      *            the scaling factor of the z component
3872      * @param ox
3873      *            the x coordinate of the scaling origin
3874      * @param oy
3875      *            the y coordinate of the scaling origin
3876      * @param oz
3877      *            the z coordinate of the scaling origin
3878      * @return this
3879      */
3880     ref public Matrix4d scaleAround(double sx, double sy, double sz, double ox, double oy, double oz) return {
3881         scaleAround(sx, sy, sz, ox, oy, oz, this);
3882         return this;
3883     }
3884 
3885     /**
3886      * Apply scaling to this matrix by scaling all three base axes by the given <code>factor</code>
3887      * while using <code>(ox, oy, oz)</code> as the scaling origin.
3888      * <p>
3889      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3890      * then the new matrix will be <code>M * S</code>. So when transforming a
3891      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
3892      * scaling will be applied first!
3893      * <p>
3894      * This method is equivalent to calling: <code>translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz)</code>
3895      * 
3896      * @param factor
3897      *            the scaling factor for all three axes
3898      * @param ox
3899      *            the x coordinate of the scaling origin
3900      * @param oy
3901      *            the y coordinate of the scaling origin
3902      * @param oz
3903      *            the z coordinate of the scaling origin
3904      * @return this
3905      */
3906     ref public Matrix4d scaleAround(double factor, double ox, double oy, double oz) return {
3907         scaleAround(factor, factor, factor, ox, oy, oz, this);
3908         return this;
3909     }
3910 
3911     public Matrix4d scaleAround(double factor, double ox, double oy, double oz, ref Matrix4d dest) {
3912         return scaleAround(factor, factor, factor, ox, oy, oz, dest);
3913     }
3914 
3915     public Matrix4d scaleLocal(double x, double y, double z, ref Matrix4d dest) {
3916         if ((properties & PROPERTY_IDENTITY) != 0)
3917             return dest.scaling(x, y, z);
3918         return scaleLocalGeneric(x, y, z, dest);
3919     }
3920     private Matrix4d scaleLocalGeneric(double x, double y, double z, ref Matrix4d dest) {
3921         double nm00 = x * m00;
3922         double nm01 = y * m01;
3923         double nm02 = z * m02;
3924         double nm10 = x * m10;
3925         double nm11 = y * m11;
3926         double nm12 = z * m12;
3927         double nm20 = x * m20;
3928         double nm21 = y * m21;
3929         double nm22 = z * m22;
3930         double nm30 = x * m30;
3931         double nm31 = y * m31;
3932         double nm32 = z * m32;
3933         bool one = Math.absEqualsOne(x) && Math.absEqualsOne(y) && Math.absEqualsOne(z);
3934         dest._m00(nm00)
3935         ._m01(nm01)
3936         ._m02(nm02)
3937         ._m03(m03)
3938         ._m10(nm10)
3939         ._m11(nm11)
3940         ._m12(nm12)
3941         ._m13(m13)
3942         ._m20(nm20)
3943         ._m21(nm21)
3944         ._m22(nm22)
3945         ._m23(m23)
3946         ._m30(nm30)
3947         ._m31(nm31)
3948         ._m32(nm32)
3949         ._m33(m33)
3950         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION
3951                 | (one ? 0 : PROPERTY_ORTHONORMAL)));
3952         return dest;
3953     }
3954 
3955     public Matrix4d scaleLocal(double xyz, ref Matrix4d dest) {
3956         return scaleLocal(xyz, xyz, xyz, dest);
3957     }
3958 
3959     /**
3960      * Pre-multiply scaling to this matrix by scaling the base axes by the given xyz factor.
3961      * <p>
3962      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3963      * then the new matrix will be <code>S * M</code>. So when transforming a
3964      * vector <code>v</code> with the new matrix by using <code>S * M * v</code>, the
3965      * scaling will be applied last!
3966      * 
3967      * @param xyz
3968      *            the factor of the x, y and z component
3969      * @return this
3970      */
3971     ref public Matrix4d scaleLocal(double xyz) return {
3972         scaleLocal(xyz, this);
3973         return this;
3974     }
3975 
3976     /**
3977      * Pre-multiply scaling to this matrix by scaling the base axes by the given x,
3978      * y and z factors.
3979      * <p>
3980      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
3981      * then the new matrix will be <code>S * M</code>. So when transforming a
3982      * vector <code>v</code> with the new matrix by using <code>S * M * v</code>, the
3983      * scaling will be applied last!
3984      * 
3985      * @param x
3986      *            the factor of the x component
3987      * @param y
3988      *            the factor of the y component
3989      * @param z
3990      *            the factor of the z component
3991      * @return this
3992      */
3993     ref public Matrix4d scaleLocal(double x, double y, double z) return {
3994         scaleLocal(x, y, z, this);
3995         return this;
3996     }
3997 
3998     public Matrix4d scaleAroundLocal(double sx, double sy, double sz, double ox, double oy, double oz, ref Matrix4d dest) {
3999         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
4000         dest._m00(sx * (m00 - ox * m03) + ox * m03)
4001         ._m01(sy * (m01 - oy * m03) + oy * m03)
4002         ._m02(sz * (m02 - oz * m03) + oz * m03)
4003         ._m03(m03)
4004         ._m10(sx * (m10 - ox * m13) + ox * m13)
4005         ._m11(sy * (m11 - oy * m13) + oy * m13)
4006         ._m12(sz * (m12 - oz * m13) + oz * m13)
4007         ._m13(m13)
4008         ._m20(sx * (m20 - ox * m23) + ox * m23)
4009         ._m21(sy * (m21 - oy * m23) + oy * m23)
4010         ._m22(sz * (m22 - oz * m23) + oz * m23)
4011         ._m23(m23)
4012         ._m30(sx * (m30 - ox * m33) + ox * m33)
4013         ._m31(sy * (m31 - oy * m33) + oy * m33)
4014         ._m32(sz * (m32 - oz * m33) + oz * m33)
4015         ._m33(m33)
4016         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION
4017                 | (one ? 0 : PROPERTY_ORTHONORMAL)));
4018         return dest;
4019     }
4020 
4021     /**
4022      * Pre-multiply scaling to this matrix by scaling the base axes by the given sx,
4023      * sy and sz factors while using <code>(ox, oy, oz)</code> as the scaling origin.
4024      * <p>
4025      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
4026      * then the new matrix will be <code>S * M</code>. So when transforming a
4027      * vector <code>v</code> with the new matrix by using <code>S * M * v</code>, the
4028      * scaling will be applied last!
4029      * <p>
4030      * 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>
4031      * 
4032      * @param sx
4033      *            the scaling factor of the x component
4034      * @param sy
4035      *            the scaling factor of the y component
4036      * @param sz
4037      *            the scaling factor of the z component
4038      * @param ox
4039      *            the x coordinate of the scaling origin
4040      * @param oy
4041      *            the y coordinate of the scaling origin
4042      * @param oz
4043      *            the z coordinate of the scaling origin
4044      * @return this
4045      */
4046     ref public Matrix4d scaleAroundLocal(double sx, double sy, double sz, double ox, double oy, double oz) return {
4047         scaleAroundLocal(sx, sy, sz, ox, oy, oz, this);
4048         return this;
4049     }
4050 
4051     /**
4052      * Pre-multiply scaling to this matrix by scaling all three base axes by the given <code>factor</code>
4053      * while using <code>(ox, oy, oz)</code> as the scaling origin.
4054      * <p>
4055      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the scaling matrix,
4056      * then the new matrix will be <code>S * M</code>. So when transforming a
4057      * vector <code>v</code> with the new matrix by using <code>S * M * v</code>, the
4058      * scaling will be applied last!
4059      * <p>
4060      * This method is equivalent to calling: <code>new Matrix4d().translate(ox, oy, oz).scale(factor).translate(-ox, -oy, -oz).mul(this, this)</code>
4061      * 
4062      * @param factor
4063      *            the scaling factor for all three axes
4064      * @param ox
4065      *            the x coordinate of the scaling origin
4066      * @param oy
4067      *            the y coordinate of the scaling origin
4068      * @param oz
4069      *            the z coordinate of the scaling origin
4070      * @return this
4071      */
4072     ref public Matrix4d scaleAroundLocal(double factor, double ox, double oy, double oz) return {
4073         scaleAroundLocal(factor, factor, factor, ox, oy, oz, this);
4074         return this;
4075     }
4076 
4077     public Matrix4d scaleAroundLocal(double factor, double ox, double oy, double oz, ref Matrix4d dest) {
4078         return scaleAroundLocal(factor, factor, factor, ox, oy, oz, dest);
4079     }
4080 
4081     public Matrix4d rotate(double ang, double x, double y, double z, ref Matrix4d dest) {
4082         if ((properties & PROPERTY_IDENTITY) != 0)
4083             return dest.rotation(ang, x, y, z);
4084         else if ((properties & PROPERTY_TRANSLATION) != 0)
4085             return rotateTranslation(ang, x, y, z, dest);
4086         else if ((properties & PROPERTY_AFFINE) != 0)
4087             return rotateAffine(ang, x, y, z, dest);
4088         return rotateGeneric(ang, x, y, z, dest);
4089     }
4090     private Matrix4d rotateGeneric(double ang, double x, double y, double z, ref Matrix4d dest) {
4091         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
4092             return rotateX(x * ang, dest);
4093         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
4094             return rotateY(y * ang, dest);
4095         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
4096             return rotateZ(z * ang, dest);
4097         return rotateGenericInternal(ang, x, y, z, dest);
4098     }
4099     private Matrix4d rotateGenericInternal(double ang, double x, double y, double z, ref Matrix4d dest) {
4100         double s = Math.sin(ang);
4101         double c = Math.cosFromSin(s, ang);
4102         double C = 1.0 - c;
4103         double xx = x * x, xy = x * y, xz = x * z;
4104         double yy = y * y, yz = y * z;
4105         double zz = z * z;
4106         double rm00 = xx * C + c;
4107         double rm01 = xy * C + z * s;
4108         double rm02 = xz * C - y * s;
4109         double rm10 = xy * C - z * s;
4110         double rm11 = yy * C + c;
4111         double rm12 = yz * C + x * s;
4112         double rm20 = xz * C + y * s;
4113         double rm21 = yz * C - x * s;
4114         double rm22 = zz * C + c;
4115         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
4116         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
4117         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
4118         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
4119         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
4120         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
4121         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
4122         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
4123         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
4124         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
4125         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
4126         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
4127         ._m00(nm00)
4128         ._m01(nm01)
4129         ._m02(nm02)
4130         ._m03(nm03)
4131         ._m10(nm10)
4132         ._m11(nm11)
4133         ._m12(nm12)
4134         ._m13(nm13)
4135         ._m30(m30)
4136         ._m31(m31)
4137         ._m32(m32)
4138         ._m33(m33)
4139         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4140         return dest;
4141     }
4142 
4143     /**
4144      * Apply rotation to this matrix by rotating the given amount of radians
4145      * about the given axis specified as x, y and z components.
4146      * <p>
4147      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4148      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4149      * When used with a left-handed coordinate system, the rotation is clockwise.
4150      * <p>
4151      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4152      * then the new matrix will be <code>M * R</code>. So when transforming a
4153      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>
4154      * , the rotation will be applied first!
4155      * <p>
4156      * In order to set the matrix to a rotation matrix without post-multiplying the rotation
4157      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4158      * 
4159      * @see #rotation(double, double, double, double)
4160      *  
4161      * @param ang
4162      *            the angle is in radians
4163      * @param x
4164      *            the x component of the axis
4165      * @param y
4166      *            the y component of the axis
4167      * @param z
4168      *            the z component of the axis
4169      * @return this
4170      */
4171     ref public Matrix4d rotate(double ang, double x, double y, double z) return {
4172         rotate(ang, x, y, z, this);
4173         return this;
4174     }
4175 
4176     /**
4177      * Apply rotation to this matrix, which is assumed to only contain a translation, by rotating the given amount of radians
4178      * about the specified <code>(x, y, z)</code> axis and store the result in <code>dest</code>.
4179      * <p>
4180      * This method assumes <code>this</code> to only contain a translation.
4181      * <p>
4182      * The axis described by the three components needs to be a unit vector.
4183      * <p>
4184      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4185      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4186      * When used with a left-handed coordinate system, the rotation is clockwise.
4187      * <p>
4188      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4189      * then the new matrix will be <code>M * R</code>. So when transforming a
4190      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
4191      * rotation will be applied first!
4192      * <p>
4193      * In order to set the matrix to a rotation matrix without post-multiplying the rotation
4194      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4195      * <p>
4196      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4197      * 
4198      * @see #rotation(double, double, double, double)
4199      * 
4200      * @param ang
4201      *            the angle in radians
4202      * @param x
4203      *            the x component of the axis
4204      * @param y
4205      *            the y component of the axis
4206      * @param z
4207      *            the z component of the axis
4208      * @param dest
4209      *            will hold the result
4210      * @return dest
4211      */
4212     public Matrix4d rotateTranslation(double ang, double x, double y, double z, ref Matrix4d dest) {
4213         double tx = m30, ty = m31, tz = m32;
4214         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
4215             return dest.rotationX(x * ang).setTranslation(tx, ty, tz);
4216         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
4217             return dest.rotationY(y * ang).setTranslation(tx, ty, tz);
4218         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
4219             return dest.rotationZ(z * ang).setTranslation(tx, ty, tz);
4220         return rotateTranslationInternal(ang, x, y, z, dest);
4221     }
4222     private Matrix4d rotateTranslationInternal(double ang, double x, double y, double z, ref Matrix4d dest) {
4223         double s = Math.sin(ang);
4224         double c = Math.cosFromSin(s, ang);
4225         double C = 1.0 - c;
4226         double xx = x * x, xy = x * y, xz = x * z;
4227         double yy = y * y, yz = y * z;
4228         double zz = z * z;
4229         double rm00 = xx * C + c;
4230         double rm01 = xy * C + z * s;
4231         double rm02 = xz * C - y * s;
4232         double rm10 = xy * C - z * s;
4233         double rm11 = yy * C + c;
4234         double rm12 = yz * C + x * s;
4235         double rm20 = xz * C + y * s;
4236         double rm21 = yz * C - x * s;
4237         double rm22 = zz * C + c;
4238         return dest
4239         ._m20(rm20)
4240         ._m21(rm21)
4241         ._m22(rm22)
4242         ._m23(0.0)
4243         ._m00(rm00)
4244         ._m01(rm01)
4245         ._m02(rm02)
4246         ._m03(0.0)
4247         ._m10(rm10)
4248         ._m11(rm11)
4249         ._m12(rm12)
4250         ._m13(0.0)
4251         ._m30(m30)
4252         ._m31(m31)
4253         ._m32(m32)
4254         ._m33(1.0)
4255         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4256     }
4257 
4258     /**
4259      * Apply rotation to this {@link #isAffine() affine} matrix by rotating the given amount of radians
4260      * about the specified <code>(x, y, z)</code> axis and store the result in <code>dest</code>.
4261      * <p>
4262      * This method assumes <code>this</code> to be {@link #isAffine() affine}.
4263      * <p>
4264      * The axis described by the three components needs to be a unit vector.
4265      * <p>
4266      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4267      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4268      * When used with a left-handed coordinate system, the rotation is clockwise.
4269      * <p>
4270      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4271      * then the new matrix will be <code>M * R</code>. So when transforming a
4272      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
4273      * rotation will be applied first!
4274      * <p>
4275      * In order to set the matrix to a rotation matrix without post-multiplying the rotation
4276      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4277      * <p>
4278      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4279      * 
4280      * @see #rotation(double, double, double, double)
4281      * 
4282      * @param ang
4283      *            the angle in radians
4284      * @param x
4285      *            the x component of the axis
4286      * @param y
4287      *            the y component of the axis
4288      * @param z
4289      *            the z component of the axis
4290      * @param dest
4291      *            will hold the result
4292      * @return dest
4293      */
4294     public Matrix4d rotateAffine(double ang, double x, double y, double z, ref Matrix4d dest) {
4295         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
4296             return rotateX(x * ang, dest);
4297         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
4298             return rotateY(y * ang, dest);
4299         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
4300             return rotateZ(z * ang, dest);
4301         return rotateAffineInternal(ang, x, y, z, dest);
4302     }
4303     private Matrix4d rotateAffineInternal(double ang, double x, double y, double z, ref Matrix4d dest) {
4304         double s = Math.sin(ang);
4305         double c = Math.cosFromSin(s, ang);
4306         double C = 1.0 - c;
4307         double xx = x * x, xy = x * y, xz = x * z;
4308         double yy = y * y, yz = y * z;
4309         double zz = z * z;
4310         double rm00 = xx * C + c;
4311         double rm01 = xy * C + z * s;
4312         double rm02 = xz * C - y * s;
4313         double rm10 = xy * C - z * s;
4314         double rm11 = yy * C + c;
4315         double rm12 = yz * C + x * s;
4316         double rm20 = xz * C + y * s;
4317         double rm21 = yz * C - x * s;
4318         double rm22 = zz * C + c;
4319         // add temporaries for dependent values
4320         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
4321         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
4322         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
4323         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
4324         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
4325         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
4326         // set non-dependent values directly
4327         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
4328         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
4329         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
4330         ._m23(0.0)
4331         // set other values
4332         ._m00(nm00)
4333         ._m01(nm01)
4334         ._m02(nm02)
4335         ._m03(0.0)
4336         ._m10(nm10)
4337         ._m11(nm11)
4338         ._m12(nm12)
4339         ._m13(0.0)
4340         ._m30(m30)
4341         ._m31(m31)
4342         ._m32(m32)
4343         ._m33(m33)
4344         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4345         return dest;
4346     }
4347 
4348     /**
4349      * Apply rotation to this {@link #isAffine() affine} matrix by rotating the given amount of radians
4350      * about the specified <code>(x, y, z)</code> axis.
4351      * <p>
4352      * This method assumes <code>this</code> to be {@link #isAffine() affine}.
4353      * <p>
4354      * The axis described by the three components needs to be a unit vector.
4355      * <p>
4356      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4357      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4358      * When used with a left-handed coordinate system, the rotation is clockwise.
4359      * <p>
4360      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4361      * then the new matrix will be <code>M * R</code>. So when transforming a
4362      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
4363      * rotation will be applied first!
4364      * <p>
4365      * In order to set the matrix to a rotation matrix without post-multiplying the rotation
4366      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4367      * <p>
4368      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4369      * 
4370      * @see #rotation(double, double, double, double)
4371      * 
4372      * @param ang
4373      *            the angle in radians
4374      * @param x
4375      *            the x component of the axis
4376      * @param y
4377      *            the y component of the axis
4378      * @param z
4379      *            the z component of the axis
4380      * @return this
4381      */
4382     ref public Matrix4d rotateAffine(double ang, double x, double y, double z) return {
4383         rotateAffine(ang, x, y, z, this);
4384         return this;
4385     }
4386 
4387     /**
4388      * Apply the rotation transformation of the given {@link Quaterniond} to this matrix while using <code>(ox, oy, oz)</code> as the rotation origin.
4389      * <p>
4390      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4391      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4392      * When used with a left-handed coordinate system, the rotation is clockwise.
4393      * <p>
4394      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
4395      * then the new matrix will be <code>M * Q</code>. So when transforming a
4396      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
4397      * the quaternion rotation will be applied first!
4398      * <p>
4399      * This method is equivalent to calling: <code>translate(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz)</code>
4400      * <p>
4401      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
4402      * 
4403      * @param quat
4404      *          the {@link Quaterniond}
4405      * @param ox
4406      *          the x coordinate of the rotation origin
4407      * @param oy
4408      *          the y coordinate of the rotation origin
4409      * @param oz
4410      *          the z coordinate of the rotation origin
4411      * @return this
4412      */
4413     ref public Matrix4d rotateAround(ref Quaterniond quat, double ox, double oy, double oz) return {
4414         rotateAround(quat, ox, oy, oz, this);
4415         return this;
4416     }
4417 
4418     public Matrix4d rotateAroundAffine(ref Quaterniond quat, double ox, double oy, double oz, ref Matrix4d dest) {
4419         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
4420         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
4421         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
4422         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
4423         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
4424         double rm00 = w2 + x2 - z2 - y2;
4425         double rm01 = dxy + dzw;
4426         double rm02 = dxz - dyw;
4427         double rm10 = -dzw + dxy;
4428         double rm11 = y2 - z2 + w2 - x2;
4429         double rm12 = dyz + dxw;
4430         double rm20 = dyw + dxz;
4431         double rm21 = dyz - dxw;
4432         double rm22 = z2 - y2 - x2 + w2;
4433         double tm30 = m00 * ox + m10 * oy + m20 * oz + m30;
4434         double tm31 = m01 * ox + m11 * oy + m21 * oz + m31;
4435         double tm32 = m02 * ox + m12 * oy + m22 * oz + m32;
4436         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
4437         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
4438         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
4439         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
4440         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
4441         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
4442         dest
4443         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
4444         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
4445         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
4446         ._m23(0.0)
4447         ._m00(nm00)
4448         ._m01(nm01)
4449         ._m02(nm02)
4450         ._m03(0.0)
4451         ._m10(nm10)
4452         ._m11(nm11)
4453         ._m12(nm12)
4454         ._m13(0.0)
4455         ._m30(-nm00 * ox - nm10 * oy - m20 * oz + tm30)
4456         ._m31(-nm01 * ox - nm11 * oy - m21 * oz + tm31)
4457         ._m32(-nm02 * ox - nm12 * oy - m22 * oz + tm32)
4458         ._m33(1.0)
4459         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4460         return dest;
4461     }
4462 
4463     public Matrix4d rotateAround(ref Quaterniond quat, double ox, double oy, double oz, ref Matrix4d dest) {
4464         if ((properties & PROPERTY_IDENTITY) != 0)
4465             return rotationAround(quat, ox, oy, oz);
4466         else if ((properties & PROPERTY_AFFINE) != 0)
4467             return rotateAroundAffine(quat, ox, oy, oz, this);
4468         return rotateAroundGeneric(quat, ox, oy, oz, this);
4469     }
4470     private Matrix4d rotateAroundGeneric(ref Quaterniond quat, double ox, double oy, double oz, ref Matrix4d dest) {
4471         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
4472         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
4473         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
4474         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
4475         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
4476         double rm00 = w2 + x2 - z2 - y2;
4477         double rm01 = dxy + dzw;
4478         double rm02 = dxz - dyw;
4479         double rm10 = -dzw + dxy;
4480         double rm11 = y2 - z2 + w2 - x2;
4481         double rm12 = dyz + dxw;
4482         double rm20 = dyw + dxz;
4483         double rm21 = dyz - dxw;
4484         double rm22 = z2 - y2 - x2 + w2;
4485         double tm30 = m00 * ox + m10 * oy + m20 * oz + m30;
4486         double tm31 = m01 * ox + m11 * oy + m21 * oz + m31;
4487         double tm32 = m02 * ox + m12 * oy + m22 * oz + m32;
4488         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
4489         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
4490         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
4491         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
4492         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
4493         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
4494         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
4495         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
4496         dest
4497         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
4498         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
4499         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
4500         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
4501         ._m00(nm00)
4502         ._m01(nm01)
4503         ._m02(nm02)
4504         ._m03(nm03)
4505         ._m10(nm10)
4506         ._m11(nm11)
4507         ._m12(nm12)
4508         ._m13(nm13)
4509         ._m30(-nm00 * ox - nm10 * oy - m20 * oz + tm30)
4510         ._m31(-nm01 * ox - nm11 * oy - m21 * oz + tm31)
4511         ._m32(-nm02 * ox - nm12 * oy - m22 * oz + tm32)
4512         ._m33(m33)
4513         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4514         return dest;
4515     }
4516 
4517     /**
4518      * 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.
4519      * <p>
4520      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4521      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4522      * When used with a left-handed coordinate system, the rotation is clockwise.
4523      * <p>
4524      * This method is equivalent to calling: <code>translation(ox, oy, oz).rotate(quat).translate(-ox, -oy, -oz)</code>
4525      * <p>
4526      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
4527      * 
4528      * @param quat
4529      *          the {@link Quaterniond}
4530      * @param ox
4531      *          the x coordinate of the rotation origin
4532      * @param oy
4533      *          the y coordinate of the rotation origin
4534      * @param oz
4535      *          the z coordinate of the rotation origin
4536      * @return this
4537      */
4538     ref public Matrix4d rotationAround(ref Quaterniond quat, double ox, double oy, double oz) return {
4539         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
4540         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
4541         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
4542         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
4543         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
4544         this._m20(dyw + dxz);
4545         this._m21(dyz - dxw);
4546         this._m22(z2 - y2 - x2 + w2);
4547         this._m23(0.0);
4548         this._m00(w2 + x2 - z2 - y2);
4549         this._m01(dxy + dzw);
4550         this._m02(dxz - dyw);
4551         this._m03(0.0);
4552         this._m10(-dzw + dxy);
4553         this._m11(y2 - z2 + w2 - x2);
4554         this._m12(dyz + dxw);
4555         this._m13(0.0);
4556         this._m30(-m00 * ox - m10 * oy - m20 * oz + ox);
4557         this._m31(-m01 * ox - m11 * oy - m21 * oz + oy);
4558         this._m32(-m02 * ox - m12 * oy - m22 * oz + oz);
4559         this._m33(1.0);
4560         this.properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
4561         return this;
4562     }
4563 
4564     /**
4565      * Pre-multiply a rotation to this matrix by rotating the given amount of radians
4566      * about the specified <code>(x, y, z)</code> axis and store the result in <code>dest</code>.
4567      * <p>
4568      * The axis described by the three components needs to be a unit vector.
4569      * <p>
4570      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4571      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4572      * When used with a left-handed coordinate system, the rotation is clockwise.
4573      * <p>
4574      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4575      * then the new matrix will be <code>R * M</code>. So when transforming a
4576      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
4577      * rotation will be applied last!
4578      * <p>
4579      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
4580      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4581      * <p>
4582      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4583      * 
4584      * @see #rotation(double, double, double, double)
4585      * 
4586      * @param ang
4587      *            the angle in radians
4588      * @param x
4589      *            the x component of the axis
4590      * @param y
4591      *            the y component of the axis
4592      * @param z
4593      *            the z component of the axis
4594      * @param dest
4595      *            will hold the result
4596      * @return dest
4597      */
4598     public Matrix4d rotateLocal(double ang, double x, double y, double z, ref Matrix4d dest) {
4599         if ((properties & PROPERTY_IDENTITY) != 0)
4600             return dest.rotation(ang, x, y, z);
4601         return rotateLocalGeneric(ang, x, y, z, dest);
4602     }
4603     private Matrix4d rotateLocalGeneric(double ang, double x, double y, double z, ref Matrix4d dest) {
4604         if (y == 0.0 && z == 0.0 && Math.absEqualsOne(x))
4605             return rotateLocalX(x * ang, dest);
4606         else if (x == 0.0 && z == 0.0 && Math.absEqualsOne(y))
4607             return rotateLocalY(y * ang, dest);
4608         else if (x == 0.0 && y == 0.0 && Math.absEqualsOne(z))
4609             return rotateLocalZ(z * ang, dest);
4610         return rotateLocalGenericInternal(ang, x, y, z, dest);
4611     }
4612     private Matrix4d rotateLocalGenericInternal(double ang, double x, double y, double z, ref Matrix4d dest) {
4613         double s = Math.sin(ang);
4614         double c = Math.cosFromSin(s, ang);
4615         double C = 1.0 - c;
4616         double xx = x * x, xy = x * y, xz = x * z;
4617         double yy = y * y, yz = y * z;
4618         double zz = z * z;
4619         double lm00 = xx * C + c;
4620         double lm01 = xy * C + z * s;
4621         double lm02 = xz * C - y * s;
4622         double lm10 = xy * C - z * s;
4623         double lm11 = yy * C + c;
4624         double lm12 = yz * C + x * s;
4625         double lm20 = xz * C + y * s;
4626         double lm21 = yz * C - x * s;
4627         double lm22 = zz * C + c;
4628         double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02;
4629         double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02;
4630         double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02;
4631         double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12;
4632         double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12;
4633         double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12;
4634         double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22;
4635         double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22;
4636         double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22;
4637         double nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32;
4638         double nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32;
4639         double nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32;
4640         dest._m00(nm00)
4641         ._m01(nm01)
4642         ._m02(nm02)
4643         ._m03(m03)
4644         ._m10(nm10)
4645         ._m11(nm11)
4646         ._m12(nm12)
4647         ._m13(m13)
4648         ._m20(nm20)
4649         ._m21(nm21)
4650         ._m22(nm22)
4651         ._m23(m23)
4652         ._m30(nm30)
4653         ._m31(nm31)
4654         ._m32(nm32)
4655         ._m33(m33)
4656         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4657         return dest;
4658     }
4659 
4660     /**
4661      * Pre-multiply a rotation to this matrix by rotating the given amount of radians
4662      * about the specified <code>(x, y, z)</code> axis.
4663      * <p>
4664      * The axis described by the three components needs to be a unit vector.
4665      * <p>
4666      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4667      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4668      * When used with a left-handed coordinate system, the rotation is clockwise.
4669      * <p>
4670      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
4671      * then the new matrix will be <code>R * M</code>. So when transforming a
4672      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
4673      * rotation will be applied last!
4674      * <p>
4675      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
4676      * transformation, use {@link #rotation(double, double, double, double) rotation()}.
4677      * <p>
4678      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
4679      * 
4680      * @see #rotation(double, double, double, double)
4681      * 
4682      * @param ang
4683      *            the angle in radians
4684      * @param x
4685      *            the x component of the axis
4686      * @param y
4687      *            the y component of the axis
4688      * @param z
4689      *            the z component of the axis
4690      * @return this
4691      */
4692     ref public Matrix4d rotateLocal(double ang, double x, double y, double z) return {
4693         rotateLocal(ang, x, y, z, this);
4694         return this;
4695     }
4696 
4697     public Matrix4d rotateAroundLocal(ref Quaterniond quat, double ox, double oy, double oz, ref Matrix4d dest) {
4698         double w2 = quat.w * quat.w;
4699         double x2 = quat.x * quat.x;
4700         double y2 = quat.y * quat.y;
4701         double z2 = quat.z * quat.z;
4702         double zw = quat.z * quat.w;
4703         double xy = quat.x * quat.y;
4704         double xz = quat.x * quat.z;
4705         double yw = quat.y * quat.w;
4706         double yz = quat.y * quat.z;
4707         double xw = quat.x * quat.w;
4708         double lm00 = w2 + x2 - z2 - y2;
4709         double lm01 = xy + zw + zw + xy;
4710         double lm02 = xz - yw + xz - yw;
4711         double lm10 = -zw + xy - zw + xy;
4712         double lm11 = y2 - z2 + w2 - x2;
4713         double lm12 = yz + yz + xw + xw;
4714         double lm20 = yw + xz + xz + yw;
4715         double lm21 = yz + yz - xw - xw;
4716         double lm22 = z2 - y2 - x2 + w2;
4717         double tm00 = m00 - ox * m03;
4718         double tm01 = m01 - oy * m03;
4719         double tm02 = m02 - oz * m03;
4720         double tm10 = m10 - ox * m13;
4721         double tm11 = m11 - oy * m13;
4722         double tm12 = m12 - oz * m13;
4723         double tm20 = m20 - ox * m23;
4724         double tm21 = m21 - oy * m23;
4725         double tm22 = m22 - oz * m23;
4726         double tm30 = m30 - ox * m33;
4727         double tm31 = m31 - oy * m33;
4728         double tm32 = m32 - oz * m33;
4729         dest._m00(lm00 * tm00 + lm10 * tm01 + lm20 * tm02 + ox * m03)
4730         ._m01(lm01 * tm00 + lm11 * tm01 + lm21 * tm02 + oy * m03)
4731         ._m02(lm02 * tm00 + lm12 * tm01 + lm22 * tm02 + oz * m03)
4732         ._m03(m03)
4733         ._m10(lm00 * tm10 + lm10 * tm11 + lm20 * tm12 + ox * m13)
4734         ._m11(lm01 * tm10 + lm11 * tm11 + lm21 * tm12 + oy * m13)
4735         ._m12(lm02 * tm10 + lm12 * tm11 + lm22 * tm12 + oz * m13)
4736         ._m13(m13)
4737         ._m20(lm00 * tm20 + lm10 * tm21 + lm20 * tm22 + ox * m23)
4738         ._m21(lm01 * tm20 + lm11 * tm21 + lm21 * tm22 + oy * m23)
4739         ._m22(lm02 * tm20 + lm12 * tm21 + lm22 * tm22 + oz * m23)
4740         ._m23(m23)
4741         ._m30(lm00 * tm30 + lm10 * tm31 + lm20 * tm32 + ox * m33)
4742         ._m31(lm01 * tm30 + lm11 * tm31 + lm21 * tm32 + oy * m33)
4743         ._m32(lm02 * tm30 + lm12 * tm31 + lm22 * tm32 + oz * m33)
4744         ._m33(m33)
4745         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
4746         return dest;
4747     }
4748 
4749     /**
4750      * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix while using <code>(ox, oy, oz)</code>
4751      * as the rotation origin.
4752      * <p>
4753      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
4754      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
4755      * When used with a left-handed coordinate system, the rotation is clockwise.
4756      * <p>
4757      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
4758      * then the new matrix will be <code>Q * M</code>. So when transforming a
4759      * vector <code>v</code> with the new matrix by using <code>Q * M * v</code>,
4760      * the quaternion rotation will be applied last!
4761      * <p>
4762      * This method is equivalent to calling: <code>translateLocal(-ox, -oy, -oz).rotateLocal(quat).translateLocal(ox, oy, oz)</code>
4763      * <p>
4764      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
4765      * 
4766      * @param quat
4767      *          the {@link Quaterniond}
4768      * @param ox
4769      *          the x coordinate of the rotation origin
4770      * @param oy
4771      *          the y coordinate of the rotation origin
4772      * @param oz
4773      *          the z coordinate of the rotation origin
4774      * @return this
4775      */
4776     ref public Matrix4d rotateAroundLocal(ref Quaterniond quat, double ox, double oy, double oz) return {
4777         rotateAroundLocal(quat, ox, oy, oz, this);
4778         return this;
4779     }
4780 
4781     /**
4782      * Apply a translation to this matrix by translating by the given number of
4783      * units in x, y and z.
4784      * <p>
4785      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4786      * matrix, then the new matrix will be <code>M * T</code>. So when
4787      * transforming a vector <code>v</code> with the new matrix by using
4788      * <code>M * T * v</code>, the translation will be applied first!
4789      * <p>
4790      * In order to set the matrix to a translation transformation without post-multiplying
4791      * it, use {@link #translation(ref Vector3d)}.
4792      * 
4793      * @see #translation(ref Vector3d)
4794      * 
4795      * @param offset
4796      *          the number of units in x, y and z by which to translate
4797      * @return this
4798      */
4799     ref public Matrix4d translate(ref Vector3d offset) return {
4800         return translate(offset.x, offset.y, offset.z);
4801     }
4802 
4803     /**
4804      * Apply a translation to this matrix by translating by the given number of
4805      * units in x, y and z and store the result in <code>dest</code>.
4806      * <p>
4807      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4808      * matrix, then the new matrix will be <code>M * T</code>. So when
4809      * transforming a vector <code>v</code> with the new matrix by using
4810      * <code>M * T * v</code>, the translation will be applied first!
4811      * <p>
4812      * In order to set the matrix to a translation transformation without post-multiplying
4813      * it, use {@link #translation(ref Vector3d)}.
4814      * 
4815      * @see #translation(ref Vector3d)
4816      * 
4817      * @param offset
4818      *          the number of units in x, y and z by which to translate
4819      * @param dest
4820      *          will hold the result
4821      * @return dest
4822      */
4823     public Matrix4d translate(ref Vector3d offset, ref Matrix4d dest) {
4824         return translate(offset.x, offset.y, offset.z, dest);
4825     }
4826 
4827 
4828 
4829     /**
4830      * Apply a translation to this matrix by translating by the given number of
4831      * units in x, y and z and store the result in <code>dest</code>.
4832      * <p>
4833      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4834      * matrix, then the new matrix will be <code>M * T</code>. So when
4835      * transforming a vector <code>v</code> with the new matrix by using
4836      * <code>M * T * v</code>, the translation will be applied first!
4837      * <p>
4838      * In order to set the matrix to a translation transformation without post-multiplying
4839      * it, use {@link #translation(double, double, double)}.
4840      * 
4841      * @see #translation(double, double, double)
4842      * 
4843      * @param x
4844      *          the offset to translate in x
4845      * @param y
4846      *          the offset to translate in y
4847      * @param z
4848      *          the offset to translate in z
4849      * @param dest
4850      *          will hold the result
4851      * @return dest
4852      */
4853     public Matrix4d translate(double x, double y, double z, ref Matrix4d dest) {
4854         if ((properties & PROPERTY_IDENTITY) != 0)
4855             return dest.translation(x, y, z);
4856         return translateGeneric(x, y, z, dest);
4857     }
4858     private Matrix4d translateGeneric(double x, double y, double z, ref Matrix4d dest) {
4859         dest._m00(m00)
4860         ._m01(m01)
4861         ._m02(m02)
4862         ._m03(m03)
4863         ._m10(m10)
4864         ._m11(m11)
4865         ._m12(m12)
4866         ._m13(m13)
4867         ._m20(m20)
4868         ._m21(m21)
4869         ._m22(m22)
4870         ._m23(m23)
4871         ._m30(Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))))
4872         ._m31(Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))))
4873         ._m32(Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))))
4874         ._m33(Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33))))
4875         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY));
4876         return dest;
4877     }
4878 
4879     /**
4880      * Apply a translation to this matrix by translating by the given number of
4881      * units in x, y and z.
4882      * <p>
4883      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4884      * matrix, then the new matrix will be <code>M * T</code>. So when
4885      * transforming a vector <code>v</code> with the new matrix by using
4886      * <code>M * T * v</code>, the translation will be applied first!
4887      * <p>
4888      * In order to set the matrix to a translation transformation without post-multiplying
4889      * it, use {@link #translation(double, double, double)}.
4890      * 
4891      * @see #translation(double, double, double)
4892      * 
4893      * @param x
4894      *          the offset to translate in x
4895      * @param y
4896      *          the offset to translate in y
4897      * @param z
4898      *          the offset to translate in z
4899      * @return this
4900      */
4901     ref public Matrix4d translate(double x, double y, double z) return {
4902         if ((properties & PROPERTY_IDENTITY) != 0)
4903             return translation(x, y, z);
4904         this._m30(Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))));
4905         this._m31(Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))));
4906         this._m32(Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))));
4907         this._m33(Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33))));
4908         this.properties &= ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY);
4909         return this;
4910     }
4911 
4912     /**
4913      * Pre-multiply a translation to this matrix by translating by the given number of
4914      * units in x, y and z.
4915      * <p>
4916      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4917      * matrix, then the new matrix will be <code>T * M</code>. So when
4918      * transforming a vector <code>v</code> with the new matrix by using
4919      * <code>T * M * v</code>, the translation will be applied last!
4920      * <p>
4921      * In order to set the matrix to a translation transformation without pre-multiplying
4922      * it, use {@link #translation(ref Vector3d)}.
4923      * 
4924      * @see #translation(ref Vector3d)
4925      * 
4926      * @param offset
4927      *          the number of units in x, y and z by which to translate
4928      * @return this
4929      */
4930     ref public Matrix4d translateLocal(ref Vector3d offset) return {
4931         return translateLocal(offset.x, offset.y, offset.z);
4932     }
4933 
4934     /**
4935      * Pre-multiply a translation to this matrix by translating by the given number of
4936      * units in x, y and z and store the result in <code>dest</code>.
4937      * <p>
4938      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4939      * matrix, then the new matrix will be <code>T * M</code>. So when
4940      * transforming a vector <code>v</code> with the new matrix by using
4941      * <code>T * M * v</code>, the translation will be applied last!
4942      * <p>
4943      * In order to set the matrix to a translation transformation without pre-multiplying
4944      * it, use {@link #translation(ref Vector3d)}.
4945      * 
4946      * @see #translation(ref Vector3d)
4947      * 
4948      * @param offset
4949      *          the number of units in x, y and z by which to translate
4950      * @param dest
4951      *          will hold the result
4952      * @return dest
4953      */
4954     public Matrix4d translateLocal(ref Vector3d offset, ref Matrix4d dest) {
4955         return translateLocal(offset.x, offset.y, offset.z, dest);
4956     }
4957 
4958     /**
4959      * Pre-multiply a translation to this matrix by translating by the given number of
4960      * units in x, y and z and store the result in <code>dest</code>.
4961      * <p>
4962      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
4963      * matrix, then the new matrix will be <code>T * M</code>. So when
4964      * transforming a vector <code>v</code> with the new matrix by using
4965      * <code>T * M * v</code>, the translation will be applied last!
4966      * <p>
4967      * In order to set the matrix to a translation transformation without pre-multiplying
4968      * it, use {@link #translation(double, double, double)}.
4969      * 
4970      * @see #translation(double, double, double)
4971      * 
4972      * @param x
4973      *          the offset to translate in x
4974      * @param y
4975      *          the offset to translate in y
4976      * @param z
4977      *          the offset to translate in z
4978      * @param dest
4979      *          will hold the result
4980      * @return dest
4981      */
4982     public Matrix4d translateLocal(double x, double y, double z, ref Matrix4d dest) {
4983         if ((properties & PROPERTY_IDENTITY) != 0)
4984             return dest.translation(x, y, z);
4985         return translateLocalGeneric(x, y, z, dest);
4986     }
4987     private Matrix4d translateLocalGeneric(double x, double y, double z, ref Matrix4d dest) {
4988         double nm00 = m00 + x * m03;
4989         double nm01 = m01 + y * m03;
4990         double nm02 = m02 + z * m03;
4991         double nm10 = m10 + x * m13;
4992         double nm11 = m11 + y * m13;
4993         double nm12 = m12 + z * m13;
4994         double nm20 = m20 + x * m23;
4995         double nm21 = m21 + y * m23;
4996         double nm22 = m22 + z * m23;
4997         double nm30 = m30 + x * m33;
4998         double nm31 = m31 + y * m33;
4999         double nm32 = m32 + z * m33;
5000         return dest
5001         ._m00(nm00)
5002         ._m01(nm01)
5003         ._m02(nm02)
5004         ._m03(m03)
5005         ._m10(nm10)
5006         ._m11(nm11)
5007         ._m12(nm12)
5008         ._m13(m13)
5009         ._m20(nm20)
5010         ._m21(nm21)
5011         ._m22(nm22)
5012         ._m23(m23)
5013         ._m30(nm30)
5014         ._m31(nm31)
5015         ._m32(nm32)
5016         ._m33(m33)
5017         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY));
5018     }
5019 
5020     /**
5021      * Pre-multiply a translation to this matrix by translating by the given number of
5022      * units in x, y and z.
5023      * <p>
5024      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the translation
5025      * matrix, then the new matrix will be <code>T * M</code>. So when
5026      * transforming a vector <code>v</code> with the new matrix by using
5027      * <code>T * M * v</code>, the translation will be applied last!
5028      * <p>
5029      * In order to set the matrix to a translation transformation without pre-multiplying
5030      * it, use {@link #translation(double, double, double)}.
5031      * 
5032      * @see #translation(double, double, double)
5033      * 
5034      * @param x
5035      *          the offset to translate in x
5036      * @param y
5037      *          the offset to translate in y
5038      * @param z
5039      *          the offset to translate in z
5040      * @return this
5041      */
5042     ref public Matrix4d translateLocal(double x, double y, double z) return {
5043         translateLocal(x, y, z, this);
5044         return this;
5045     }
5046 
5047     /**
5048      * Pre-multiply a rotation around the X axis to this matrix by rotating the given amount of radians
5049      * about the X axis and store the result in <code>dest</code>.
5050      * <p>
5051      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5052      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5053      * When used with a left-handed coordinate system, the rotation is clockwise.
5054      * <p>
5055      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5056      * then the new matrix will be <code>R * M</code>. So when transforming a
5057      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5058      * rotation will be applied last!
5059      * <p>
5060      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5061      * transformation, use {@link #rotationX(double) rotationX()}.
5062      * <p>
5063      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5064      * 
5065      * @see #rotationX(double)
5066      * 
5067      * @param ang
5068      *            the angle in radians to rotate about the X axis
5069      * @param dest
5070      *            will hold the result
5071      * @return dest
5072      */
5073     public Matrix4d rotateLocalX(double ang, ref Matrix4d dest) {
5074         double sin = Math.sin(ang);
5075         double cos = Math.cosFromSin(sin, ang);
5076         double nm02 = sin * m01 + cos * m02;
5077         double nm12 = sin * m11 + cos * m12;
5078         double nm22 = sin * m21 + cos * m22;
5079         double nm32 = sin * m31 + cos * m32;
5080         dest
5081         ._m00(m00)
5082         ._m01(cos * m01 - sin * m02)
5083         ._m02(nm02)
5084         ._m03(m03)
5085         ._m10(m10)
5086         ._m11(cos * m11 - sin * m12)
5087         ._m12(nm12)
5088         ._m13(m13)
5089         ._m20(m20)
5090         ._m21(cos * m21 - sin * m22)
5091         ._m22(nm22)
5092         ._m23(m23)
5093         ._m30(m30)
5094         ._m31(cos * m31 - sin * m32)
5095         ._m32(nm32)
5096         ._m33(m33)
5097         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5098         return dest;
5099     }
5100 
5101     /**
5102      * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the X axis.
5103      * <p>
5104      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5105      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5106      * When used with a left-handed coordinate system, the rotation is clockwise.
5107      * <p>
5108      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5109      * then the new matrix will be <code>R * M</code>. So when transforming a
5110      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5111      * rotation will be applied last!
5112      * <p>
5113      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5114      * transformation, use {@link #rotationX(double) rotationX()}.
5115      * <p>
5116      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5117      * 
5118      * @see #rotationX(double)
5119      * 
5120      * @param ang
5121      *            the angle in radians to rotate about the X axis
5122      * @return this
5123      */
5124     ref public Matrix4d rotateLocalX(double ang) return {
5125         rotateLocalX(ang, this);
5126         return this;
5127     }
5128 
5129     /**
5130      * Pre-multiply a rotation around the Y axis to this matrix by rotating the given amount of radians
5131      * about the Y axis and store the result in <code>dest</code>.
5132      * <p>
5133      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5134      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5135      * When used with a left-handed coordinate system, the rotation is clockwise.
5136      * <p>
5137      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5138      * then the new matrix will be <code>R * M</code>. So when transforming a
5139      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5140      * rotation will be applied last!
5141      * <p>
5142      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5143      * transformation, use {@link #rotationY(double) rotationY()}.
5144      * <p>
5145      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5146      * 
5147      * @see #rotationY(double)
5148      * 
5149      * @param ang
5150      *            the angle in radians to rotate about the Y axis
5151      * @param dest
5152      *            will hold the result
5153      * @return dest
5154      */
5155     public Matrix4d rotateLocalY(double ang, ref Matrix4d dest) {
5156         double sin = Math.sin(ang);
5157         double cos = Math.cosFromSin(sin, ang);
5158         double nm02 = -sin * m00 + cos * m02;
5159         double nm12 = -sin * m10 + cos * m12;
5160         double nm22 = -sin * m20 + cos * m22;
5161         double nm32 = -sin * m30 + cos * m32;
5162         dest
5163         ._m00(cos * m00 + sin * m02)
5164         ._m01(m01)
5165         ._m02(nm02)
5166         ._m03(m03)
5167         ._m10(cos * m10 + sin * m12)
5168         ._m11(m11)
5169         ._m12(nm12)
5170         ._m13(m13)
5171         ._m20(cos * m20 + sin * m22)
5172         ._m21(m21)
5173         ._m22(nm22)
5174         ._m23(m23)
5175         ._m30(cos * m30 + sin * m32)
5176         ._m31(m31)
5177         ._m32(nm32)
5178         ._m33(m33)
5179         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5180         return dest;
5181     }
5182 
5183     /**
5184      * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Y axis.
5185      * <p>
5186      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5187      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5188      * When used with a left-handed coordinate system, the rotation is clockwise.
5189      * <p>
5190      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5191      * then the new matrix will be <code>R * M</code>. So when transforming a
5192      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5193      * rotation will be applied last!
5194      * <p>
5195      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5196      * transformation, use {@link #rotationY(double) rotationY()}.
5197      * <p>
5198      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5199      * 
5200      * @see #rotationY(double)
5201      * 
5202      * @param ang
5203      *            the angle in radians to rotate about the Y axis
5204      * @return this
5205      */
5206     ref public Matrix4d rotateLocalY(double ang) return {
5207         rotateLocalY(ang, this);
5208         return this;
5209     }
5210 
5211     /**
5212      * Pre-multiply a rotation around the Z axis to this matrix by rotating the given amount of radians
5213      * about the Z axis and store the result in <code>dest</code>.
5214      * <p>
5215      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5216      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5217      * When used with a left-handed coordinate system, the rotation is clockwise.
5218      * <p>
5219      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5220      * then the new matrix will be <code>R * M</code>. So when transforming a
5221      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5222      * rotation will be applied last!
5223      * <p>
5224      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5225      * transformation, use {@link #rotationZ(double) rotationZ()}.
5226      * <p>
5227      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5228      * 
5229      * @see #rotationZ(double)
5230      * 
5231      * @param ang
5232      *            the angle in radians to rotate about the Z axis
5233      * @param dest
5234      *            will hold the result
5235      * @return dest
5236      */
5237     public Matrix4d rotateLocalZ(double ang, ref Matrix4d dest) {
5238         double sin = Math.sin(ang);
5239         double cos = Math.cosFromSin(sin, ang);
5240         double nm01 = sin * m00 + cos * m01;
5241         double nm11 = sin * m10 + cos * m11;
5242         double nm21 = sin * m20 + cos * m21;
5243         double nm31 = sin * m30 + cos * m31;
5244         dest
5245         ._m00(cos * m00 - sin * m01)
5246         ._m01(nm01)
5247         ._m02(m02)
5248         ._m03(m03)
5249         ._m10(cos * m10 - sin * m11)
5250         ._m11(nm11)
5251         ._m12(m12)
5252         ._m13(m13)
5253         ._m20(cos * m20 - sin * m21)
5254         ._m21(nm21)
5255         ._m22(m22)
5256         ._m23(m23)
5257         ._m30(cos * m30 - sin * m31)
5258         ._m31(nm31)
5259         ._m32(m32)
5260         ._m33(m33)
5261         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5262         return dest;
5263     }
5264 
5265     /**
5266      * Pre-multiply a rotation to this matrix by rotating the given amount of radians about the Z axis.
5267      * <p>
5268      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5269      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5270      * When used with a left-handed coordinate system, the rotation is clockwise.
5271      * <p>
5272      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5273      * then the new matrix will be <code>R * M</code>. So when transforming a
5274      * vector <code>v</code> with the new matrix by using <code>R * M * v</code>, the
5275      * rotation will be applied last!
5276      * <p>
5277      * In order to set the matrix to a rotation matrix without pre-multiplying the rotation
5278      * transformation, use {@link #rotationZ(double) rotationY()}.
5279      * <p>
5280      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle">http://en.wikipedia.org</a>
5281      * 
5282      * @see #rotationY(double)
5283      * 
5284      * @param ang
5285      *            the angle in radians to rotate about the Z axis
5286      * @return this
5287      */
5288     ref public Matrix4d rotateLocalZ(double ang) return {
5289         rotateLocalZ(ang, this);
5290         return this;
5291     }
5292 
5293     public Matrix4d rotateX(double ang, ref Matrix4d dest) {
5294         if ((properties & PROPERTY_IDENTITY) != 0)
5295             return dest.rotationX(ang);
5296         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5297             double x = m30, y = m31, z = m32;
5298             return dest.rotationX(ang).setTranslation(x, y, z);
5299         }
5300         return rotateXInternal(ang, dest);
5301     }
5302     private Matrix4d rotateXInternal(double ang, ref Matrix4d dest) {
5303         double sin, cos;
5304         sin = Math.sin(ang);
5305         cos = Math.cosFromSin(sin, ang);
5306         double rm11 = cos;
5307         double rm12 = sin;
5308         double rm21 = -sin;
5309         double rm22 = cos;
5310 
5311         // add temporaries for dependent values
5312         double nm10 = m10 * rm11 + m20 * rm12;
5313         double nm11 = m11 * rm11 + m21 * rm12;
5314         double nm12 = m12 * rm11 + m22 * rm12;
5315         double nm13 = m13 * rm11 + m23 * rm12;
5316         // set non-dependent values directly
5317         dest._m20(m10 * rm21 + m20 * rm22)
5318         ._m21(m11 * rm21 + m21 * rm22)
5319         ._m22(m12 * rm21 + m22 * rm22)
5320         ._m23(m13 * rm21 + m23 * rm22)
5321         // set other values
5322         ._m10(nm10)
5323         ._m11(nm11)
5324         ._m12(nm12)
5325         ._m13(nm13)
5326         ._m00(m00)
5327         ._m01(m01)
5328         ._m02(m02)
5329         ._m03(m03)
5330         ._m30(m30)
5331         ._m31(m31)
5332         ._m32(m32)
5333         ._m33(m33)
5334         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5335         return dest;
5336     }
5337 
5338     /**
5339      * Apply rotation about the X axis to this matrix by rotating the given amount of radians.
5340      * <p>
5341      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5342      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5343      * When used with a left-handed coordinate system, the rotation is clockwise.
5344      * <p>
5345      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5346      * then the new matrix will be <code>M * R</code>. So when transforming a
5347      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5348      * rotation will be applied first!
5349      * <p>
5350      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
5351      * 
5352      * @param ang
5353      *            the angle in radians
5354      * @return this
5355      */
5356     ref public Matrix4d rotateX(double ang) return {
5357         rotateX(ang, this);
5358         return this;
5359     }
5360 
5361     public Matrix4d rotateY(double ang, ref Matrix4d dest) {
5362         if ((properties & PROPERTY_IDENTITY) != 0)
5363             return dest.rotationY(ang);
5364         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5365             double x = m30, y = m31, z = m32;
5366             return dest.rotationY(ang).setTranslation(x, y, z);
5367         }
5368         return rotateYInternal(ang, dest);
5369     }
5370     private Matrix4d rotateYInternal(double ang, ref Matrix4d dest) {
5371         double sin, cos;
5372         sin = Math.sin(ang);
5373         cos = Math.cosFromSin(sin, ang);
5374         double rm00 = cos;
5375         double rm02 = -sin;
5376         double rm20 = sin;
5377         double rm22 = cos;
5378 
5379         // add temporaries for dependent values
5380         double nm00 = m00 * rm00 + m20 * rm02;
5381         double nm01 = m01 * rm00 + m21 * rm02;
5382         double nm02 = m02 * rm00 + m22 * rm02;
5383         double nm03 = m03 * rm00 + m23 * rm02;
5384         // set non-dependent values directly
5385         dest._m20(m00 * rm20 + m20 * rm22)
5386         ._m21(m01 * rm20 + m21 * rm22)
5387         ._m22(m02 * rm20 + m22 * rm22)
5388         ._m23(m03 * rm20 + m23 * rm22)
5389         // set other values
5390         ._m00(nm00)
5391         ._m01(nm01)
5392         ._m02(nm02)
5393         ._m03(nm03)
5394         ._m10(m10)
5395         ._m11(m11)
5396         ._m12(m12)
5397         ._m13(m13)
5398         ._m30(m30)
5399         ._m31(m31)
5400         ._m32(m32)
5401         ._m33(m33)
5402         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5403         return dest;
5404     }
5405 
5406     /**
5407      * Apply rotation about the Y axis to this matrix by rotating the given amount of radians.
5408      * <p>
5409      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5410      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5411      * When used with a left-handed coordinate system, the rotation is clockwise.
5412      * <p>
5413      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5414      * then the new matrix will be <code>M * R</code>. So when transforming a
5415      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5416      * rotation will be applied first!
5417      * <p>
5418      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
5419      * 
5420      * @param ang
5421      *            the angle in radians
5422      * @return this
5423      */
5424     ref public Matrix4d rotateY(double ang) return {
5425         rotateY(ang, this);
5426         return this;
5427     }
5428 
5429     public Matrix4d rotateZ(double ang, ref Matrix4d dest) {
5430         if ((properties & PROPERTY_IDENTITY) != 0)
5431             return dest.rotationZ(ang);
5432         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5433             double x = m30, y = m31, z = m32;
5434             return dest.rotationZ(ang).setTranslation(x, y, z);
5435         }
5436         return rotateZInternal(ang, dest);
5437     }
5438     private Matrix4d rotateZInternal(double ang, ref Matrix4d dest) {
5439         double sin = Math.sin(ang);
5440         double cos = Math.cosFromSin(sin, ang);
5441         return rotateTowardsXY(sin, cos, dest);
5442     }
5443 
5444     /**
5445      * Apply rotation about the Z axis to this matrix by rotating the given amount of radians.
5446      * <p>
5447      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5448      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5449      * When used with a left-handed coordinate system, the rotation is clockwise.
5450      * <p>
5451      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5452      * then the new matrix will be <code>M * R</code>. So when transforming a
5453      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5454      * rotation will be applied first!
5455      * <p>
5456      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">http://en.wikipedia.org</a>
5457      * 
5458      * @param ang
5459      *            the angle in radians
5460      * @return this
5461      */
5462     ref public Matrix4d rotateZ(double ang) return {
5463         rotateZ(ang, this);
5464         return this;
5465     }
5466 
5467     /**
5468      * Apply rotation about the Z axis to align the local <code>+X</code> towards <code>(dirX, dirY)</code>.
5469      * <p>
5470      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5471      * then the new matrix will be <code>M * R</code>. So when transforming a
5472      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5473      * rotation will be applied first!
5474      * <p>
5475      * The vector <code>(dirX, dirY)</code> must be a unit vector.
5476      * 
5477      * @param dirX
5478      *            the x component of the normalized direction
5479      * @param dirY
5480      *            the y component of the normalized direction
5481      * @return this
5482      */
5483     ref public Matrix4d rotateTowardsXY(double dirX, double dirY) return {
5484         rotateTowardsXY(dirX, dirY, this);
5485         return this;
5486     }
5487 
5488     public Matrix4d rotateTowardsXY(double dirX, double dirY, ref Matrix4d dest) {
5489         if ((properties & PROPERTY_IDENTITY) != 0)
5490             return dest.rotationTowardsXY(dirX, dirY);
5491         double rm00 = dirY;
5492         double rm01 = dirX;
5493         double rm10 = -dirX;
5494         double rm11 = dirY;
5495         double nm00 = m00 * rm00 + m10 * rm01;
5496         double nm01 = m01 * rm00 + m11 * rm01;
5497         double nm02 = m02 * rm00 + m12 * rm01;
5498         double nm03 = m03 * rm00 + m13 * rm01;
5499         dest._m10(m00 * rm10 + m10 * rm11)
5500         ._m11(m01 * rm10 + m11 * rm11)
5501         ._m12(m02 * rm10 + m12 * rm11)
5502         ._m13(m03 * rm10 + m13 * rm11)
5503         ._m00(nm00)
5504         ._m01(nm01)
5505         ._m02(nm02)
5506         ._m03(nm03)
5507         ._m20(m20)
5508         ._m21(m21)
5509         ._m22(m22)
5510         ._m23(m23)
5511         ._m30(m30)
5512         ._m31(m31)
5513         ._m32(m32)
5514         ._m33(m33)
5515         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5516         return dest;
5517     }
5518 
5519     /**
5520      * 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
5521      * followed by a rotation of <code>angles.z</code> radians about the Z axis.
5522      * <p>
5523      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5524      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5525      * When used with a left-handed coordinate system, the rotation is clockwise.
5526      * <p>
5527      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5528      * then the new matrix will be <code>M * R</code>. So when transforming a
5529      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5530      * rotation will be applied first!
5531      * <p>
5532      * This method is equivalent to calling: <code>rotateX(angles.x).rotateY(angles.y).rotateZ(angles.z)</code>
5533      * 
5534      * @param angles
5535      *            the Euler angles
5536      * @return this
5537      */
5538     ref public Matrix4d rotateXYZ(ref Vector3d angles) return {
5539         return rotateXYZ(angles.x, angles.y, angles.z);
5540     }
5541 
5542     /**
5543      * 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
5544      * followed by a rotation of <code>angleZ</code> radians about the Z axis.
5545      * <p>
5546      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5547      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5548      * When used with a left-handed coordinate system, the rotation is clockwise.
5549      * <p>
5550      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5551      * then the new matrix will be <code>M * R</code>. So when transforming a
5552      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5553      * rotation will be applied first!
5554      * <p>
5555      * This method is equivalent to calling: <code>rotateX(angleX).rotateY(angleY).rotateZ(angleZ)</code>
5556      * 
5557      * @param angleX
5558      *            the angle to rotate about X
5559      * @param angleY
5560      *            the angle to rotate about Y
5561      * @param angleZ
5562      *            the angle to rotate about Z
5563      * @return this
5564      */
5565     ref public Matrix4d rotateXYZ(double angleX, double angleY, double angleZ) return {
5566         rotateXYZ(angleX, angleY, angleZ, this);
5567         return this;
5568     }
5569 
5570     public Matrix4d rotateXYZ(double angleX, double angleY, double angleZ, ref Matrix4d dest) {
5571         if ((properties & PROPERTY_IDENTITY) != 0)
5572             return dest.rotationXYZ(angleX, angleY, angleZ);
5573         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5574             double tx = m30, ty = m31, tz = m32;
5575             return dest.rotationXYZ(angleX, angleY, angleZ).setTranslation(tx, ty, tz);
5576         } else if ((properties & PROPERTY_AFFINE) != 0)
5577             return dest.rotateAffineXYZ(angleX, angleY, angleZ);
5578         return rotateXYZInternal(angleX, angleY, angleZ, dest);
5579     }
5580     private Matrix4d rotateXYZInternal(double angleX, double angleY, double angleZ, ref Matrix4d dest) {
5581         double sinX = Math.sin(angleX);
5582         double cosX = Math.cosFromSin(sinX, angleX);
5583         double sinY = Math.sin(angleY);
5584         double cosY = Math.cosFromSin(sinY, angleY);
5585         double sinZ = Math.sin(angleZ);
5586         double cosZ = Math.cosFromSin(sinZ, angleZ);
5587         double m_sinX = -sinX;
5588         double m_sinY = -sinY;
5589         double m_sinZ = -sinZ;
5590 
5591         // rotateX
5592         double nm10 = m10 * cosX + m20 * sinX;
5593         double nm11 = m11 * cosX + m21 * sinX;
5594         double nm12 = m12 * cosX + m22 * sinX;
5595         double nm13 = m13 * cosX + m23 * sinX;
5596         double nm20 = m10 * m_sinX + m20 * cosX;
5597         double nm21 = m11 * m_sinX + m21 * cosX;
5598         double nm22 = m12 * m_sinX + m22 * cosX;
5599         double nm23 = m13 * m_sinX + m23 * cosX;
5600         // rotateY
5601         double nm00 = m00 * cosY + nm20 * m_sinY;
5602         double nm01 = m01 * cosY + nm21 * m_sinY;
5603         double nm02 = m02 * cosY + nm22 * m_sinY;
5604         double nm03 = m03 * cosY + nm23 * m_sinY;
5605         dest._m20(m00 * sinY + nm20 * cosY)
5606         ._m21(m01 * sinY + nm21 * cosY)
5607         ._m22(m02 * sinY + nm22 * cosY)
5608         ._m23(m03 * sinY + nm23 * cosY)
5609         // rotateZ
5610         ._m00(nm00 * cosZ + nm10 * sinZ)
5611         ._m01(nm01 * cosZ + nm11 * sinZ)
5612         ._m02(nm02 * cosZ + nm12 * sinZ)
5613         ._m03(nm03 * cosZ + nm13 * sinZ)
5614         ._m10(nm00 * m_sinZ + nm10 * cosZ)
5615         ._m11(nm01 * m_sinZ + nm11 * cosZ)
5616         ._m12(nm02 * m_sinZ + nm12 * cosZ)
5617         ._m13(nm03 * m_sinZ + nm13 * cosZ)
5618         // copy last column from 'this'
5619         ._m30(m30)
5620         ._m31(m31)
5621         ._m32(m32)
5622         ._m33(m33)
5623         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5624         return dest;
5625     }
5626 
5627     /**
5628      * 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
5629      * followed by a rotation of <code>angleZ</code> radians about the Z axis.
5630      * <p>
5631      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5632      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5633      * When used with a left-handed coordinate system, the rotation is clockwise.
5634      * <p>
5635      * 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>)
5636      * 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).
5637      * <p>
5638      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5639      * then the new matrix will be <code>M * R</code>. So when transforming a
5640      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5641      * rotation will be applied first!
5642      * <p>
5643      * This method is equivalent to calling: <code>rotateX(angleX).rotateY(angleY).rotateZ(angleZ)</code>
5644      * 
5645      * @param angleX
5646      *            the angle to rotate about X
5647      * @param angleY
5648      *            the angle to rotate about Y
5649      * @param angleZ
5650      *            the angle to rotate about Z
5651      * @return this
5652      */
5653     ref public Matrix4d rotateAffineXYZ(double angleX, double angleY, double angleZ) return {
5654         rotateAffineXYZ(angleX, angleY, angleZ, this);
5655         return this;
5656     }
5657 
5658     public Matrix4d rotateAffineXYZ(double angleX, double angleY, double angleZ, ref Matrix4d dest) {
5659         if ((properties & PROPERTY_IDENTITY) != 0)
5660             return dest.rotationXYZ(angleX, angleY, angleZ);
5661         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5662             double tx = m30, ty = m31, tz = m32;
5663             return dest.rotationXYZ(angleX, angleY, angleZ).setTranslation(tx, ty, tz);
5664         }
5665         return rotateAffineXYZInternal(angleX, angleY, angleZ, dest);
5666     }
5667     private Matrix4d rotateAffineXYZInternal(double angleX, double angleY, double angleZ, ref Matrix4d dest) {
5668         double sinX = Math.sin(angleX);
5669         double cosX = Math.cosFromSin(sinX, angleX);
5670         double sinY = Math.sin(angleY);
5671         double cosY = Math.cosFromSin(sinY, angleY);
5672         double sinZ = Math.sin(angleZ);
5673         double cosZ = Math.cosFromSin(sinZ, angleZ);
5674         double m_sinX = -sinX;
5675         double m_sinY = -sinY;
5676         double m_sinZ = -sinZ;
5677 
5678         // rotateX
5679         double nm10 = m10 * cosX + m20 * sinX;
5680         double nm11 = m11 * cosX + m21 * sinX;
5681         double nm12 = m12 * cosX + m22 * sinX;
5682         double nm20 = m10 * m_sinX + m20 * cosX;
5683         double nm21 = m11 * m_sinX + m21 * cosX;
5684         double nm22 = m12 * m_sinX + m22 * cosX;
5685         // rotateY
5686         double nm00 = m00 * cosY + nm20 * m_sinY;
5687         double nm01 = m01 * cosY + nm21 * m_sinY;
5688         double nm02 = m02 * cosY + nm22 * m_sinY;
5689         dest._m20(m00 * sinY + nm20 * cosY)
5690         ._m21(m01 * sinY + nm21 * cosY)
5691         ._m22(m02 * sinY + nm22 * cosY)
5692         ._m23(0.0)
5693         // rotateZ
5694         ._m00(nm00 * cosZ + nm10 * sinZ)
5695         ._m01(nm01 * cosZ + nm11 * sinZ)
5696         ._m02(nm02 * cosZ + nm12 * sinZ)
5697         ._m03(0.0)
5698         ._m10(nm00 * m_sinZ + nm10 * cosZ)
5699         ._m11(nm01 * m_sinZ + nm11 * cosZ)
5700         ._m12(nm02 * m_sinZ + nm12 * cosZ)
5701         ._m13(0.0)
5702         // copy last column from 'this'
5703         ._m30(m30)
5704         ._m31(m31)
5705         ._m32(m32)
5706         ._m33(m33)
5707         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5708         return dest;
5709     }
5710 
5711     /**
5712      * 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
5713      * followed by a rotation of <code>angles.x</code> radians about the X axis.
5714      * <p>
5715      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5716      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5717      * When used with a left-handed coordinate system, the rotation is clockwise.
5718      * <p>
5719      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5720      * then the new matrix will be <code>M * R</code>. So when transforming a
5721      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5722      * rotation will be applied first!
5723      * <p>
5724      * This method is equivalent to calling: <code>rotateZ(angles.z).rotateY(angles.y).rotateX(angles.x)</code>
5725      * 
5726      * @param angles
5727      *            the Euler angles
5728      * @return this
5729      */
5730     ref public Matrix4d rotateZYX(ref Vector3d angles) return {
5731         return rotateZYX(angles.z, angles.y, angles.x);
5732     }
5733 
5734     /**
5735      * 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
5736      * followed by a rotation of <code>angleX</code> radians about the X axis.
5737      * <p>
5738      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5739      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5740      * When used with a left-handed coordinate system, the rotation is clockwise.
5741      * <p>
5742      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5743      * then the new matrix will be <code>M * R</code>. So when transforming a
5744      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5745      * rotation will be applied first!
5746      * <p>
5747      * This method is equivalent to calling: <code>rotateZ(angleZ).rotateY(angleY).rotateX(angleX)</code>
5748      * 
5749      * @param angleZ
5750      *            the angle to rotate about Z
5751      * @param angleY
5752      *            the angle to rotate about Y
5753      * @param angleX
5754      *            the angle to rotate about X
5755      * @return this
5756      */
5757     ref public Matrix4d rotateZYX(double angleZ, double angleY, double angleX) return {
5758         rotateZYX(angleZ, angleY, angleX, this);
5759         return this;
5760     }
5761 
5762     public Matrix4d rotateZYX(double angleZ, double angleY, double angleX, ref Matrix4d dest) {
5763         if ((properties & PROPERTY_IDENTITY) != 0)
5764             return dest.rotationZYX(angleZ, angleY, angleX);
5765         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5766             double tx = m30, ty = m31, tz = m32;
5767             return dest.rotationZYX(angleZ, angleY, angleX).setTranslation(tx, ty, tz);
5768         } else if ((properties & PROPERTY_AFFINE) != 0)
5769             return dest.rotateAffineZYX(angleZ, angleY, angleX);
5770         return rotateZYXInternal(angleZ, angleY, angleX, dest);
5771     }
5772     private Matrix4d rotateZYXInternal(double angleZ, double angleY, double angleX, ref Matrix4d dest) {
5773         double sinX = Math.sin(angleX);
5774         double cosX = Math.cosFromSin(sinX, angleX);
5775         double sinY = Math.sin(angleY);
5776         double cosY = Math.cosFromSin(sinY, angleY);
5777         double sinZ = Math.sin(angleZ);
5778         double cosZ = Math.cosFromSin(sinZ, angleZ);
5779         double m_sinZ = -sinZ;
5780         double m_sinY = -sinY;
5781         double m_sinX = -sinX;
5782 
5783         // rotateZ
5784         double nm00 = m00 * cosZ + m10 * sinZ;
5785         double nm01 = m01 * cosZ + m11 * sinZ;
5786         double nm02 = m02 * cosZ + m12 * sinZ;
5787         double nm03 = m03 * cosZ + m13 * sinZ;
5788         double nm10 = m00 * m_sinZ + m10 * cosZ;
5789         double nm11 = m01 * m_sinZ + m11 * cosZ;
5790         double nm12 = m02 * m_sinZ + m12 * cosZ;
5791         double nm13 = m03 * m_sinZ + m13 * cosZ;
5792         // rotateY
5793         double nm20 = nm00 * sinY + m20 * cosY;
5794         double nm21 = nm01 * sinY + m21 * cosY;
5795         double nm22 = nm02 * sinY + m22 * cosY;
5796         double nm23 = nm03 * sinY + m23 * cosY;
5797         dest._m00(nm00 * cosY + m20 * m_sinY)
5798         ._m01(nm01 * cosY + m21 * m_sinY)
5799         ._m02(nm02 * cosY + m22 * m_sinY)
5800         ._m03(nm03 * cosY + m23 * m_sinY)
5801         // rotateX
5802         ._m10(nm10 * cosX + nm20 * sinX)
5803         ._m11(nm11 * cosX + nm21 * sinX)
5804         ._m12(nm12 * cosX + nm22 * sinX)
5805         ._m13(nm13 * cosX + nm23 * sinX)
5806         ._m20(nm10 * m_sinX + nm20 * cosX)
5807         ._m21(nm11 * m_sinX + nm21 * cosX)
5808         ._m22(nm12 * m_sinX + nm22 * cosX)
5809         ._m23(nm13 * m_sinX + nm23 * cosX)
5810         // copy last column from 'this'
5811         ._m30(m30)
5812         ._m31(m31)
5813         ._m32(m32)
5814         ._m33(m33)
5815         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5816         return dest;
5817     }
5818 
5819     /**
5820      * 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
5821      * followed by a rotation of <code>angleX</code> radians about the X axis.
5822      * <p>
5823      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5824      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5825      * When used with a left-handed coordinate system, the rotation is clockwise.
5826      * <p>
5827      * 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>)
5828      * 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).
5829      * <p>
5830      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5831      * then the new matrix will be <code>M * R</code>. So when transforming a
5832      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5833      * rotation will be applied first!
5834      * 
5835      * @param angleZ
5836      *            the angle to rotate about Z
5837      * @param angleY
5838      *            the angle to rotate about Y
5839      * @param angleX
5840      *            the angle to rotate about X
5841      * @return this
5842      */
5843     ref public Matrix4d rotateAffineZYX(double angleZ, double angleY, double angleX) return {
5844         rotateAffineZYX(angleZ, angleY, angleX, this);
5845         return this;
5846     }
5847 
5848     public Matrix4d rotateAffineZYX(double angleZ, double angleY, double angleX, ref Matrix4d dest) {
5849         double sinX = Math.sin(angleX);
5850         double cosX = Math.cosFromSin(sinX, angleX);
5851         double sinY = Math.sin(angleY);
5852         double cosY = Math.cosFromSin(sinY, angleY);
5853         double sinZ = Math.sin(angleZ);
5854         double cosZ = Math.cosFromSin(sinZ, angleZ);
5855         double m_sinZ = -sinZ;
5856         double m_sinY = -sinY;
5857         double m_sinX = -sinX;
5858 
5859         // rotateZ
5860         double nm00 = m00 * cosZ + m10 * sinZ;
5861         double nm01 = m01 * cosZ + m11 * sinZ;
5862         double nm02 = m02 * cosZ + m12 * sinZ;
5863         double nm10 = m00 * m_sinZ + m10 * cosZ;
5864         double nm11 = m01 * m_sinZ + m11 * cosZ;
5865         double nm12 = m02 * m_sinZ + m12 * cosZ;
5866         // rotateY
5867         double nm20 = nm00 * sinY + m20 * cosY;
5868         double nm21 = nm01 * sinY + m21 * cosY;
5869         double nm22 = nm02 * sinY + m22 * cosY;
5870         dest._m00(nm00 * cosY + m20 * m_sinY)
5871         ._m01(nm01 * cosY + m21 * m_sinY)
5872         ._m02(nm02 * cosY + m22 * m_sinY)
5873         ._m03(0.0)
5874         // rotateX
5875         ._m10(nm10 * cosX + nm20 * sinX)
5876         ._m11(nm11 * cosX + nm21 * sinX)
5877         ._m12(nm12 * cosX + nm22 * sinX)
5878         ._m13(0.0)
5879         ._m20(nm10 * m_sinX + nm20 * cosX)
5880         ._m21(nm11 * m_sinX + nm21 * cosX)
5881         ._m22(nm12 * m_sinX + nm22 * cosX)
5882         ._m23(0.0)
5883         // copy last column from 'this'
5884         ._m30(m30)
5885         ._m31(m31)
5886         ._m32(m32)
5887         ._m33(m33)
5888         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5889         return dest;
5890     }
5891 
5892     /**
5893      * 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
5894      * followed by a rotation of <code>angles.z</code> radians about the Z axis.
5895      * <p>
5896      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5897      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5898      * When used with a left-handed coordinate system, the rotation is clockwise.
5899      * <p>
5900      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5901      * then the new matrix will be <code>M * R</code>. So when transforming a
5902      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5903      * rotation will be applied first!
5904      * <p>
5905      * This method is equivalent to calling: <code>rotateY(angles.y).rotateX(angles.x).rotateZ(angles.z)</code>
5906      * 
5907      * @param angles
5908      *            the Euler angles
5909      * @return this
5910      */
5911     ref public Matrix4d rotateYXZ(ref Vector3d angles) return {
5912         return rotateYXZ(angles.y, angles.x, angles.z);
5913     }
5914 
5915     /**
5916      * 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
5917      * followed by a rotation of <code>angleZ</code> radians about the Z axis.
5918      * <p>
5919      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
5920      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
5921      * When used with a left-handed coordinate system, the rotation is clockwise.
5922      * <p>
5923      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
5924      * then the new matrix will be <code>M * R</code>. So when transforming a
5925      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
5926      * rotation will be applied first!
5927      * <p>
5928      * This method is equivalent to calling: <code>rotateY(angleY).rotateX(angleX).rotateZ(angleZ)</code>
5929      * 
5930      * @param angleY
5931      *            the angle to rotate about Y
5932      * @param angleX
5933      *            the angle to rotate about X
5934      * @param angleZ
5935      *            the angle to rotate about Z
5936      * @return this
5937      */
5938     ref public Matrix4d rotateYXZ(double angleY, double angleX, double angleZ) return {
5939         rotateYXZ(angleY, angleX, angleZ, this);
5940         return this;
5941     }
5942 
5943     public Matrix4d rotateYXZ(double angleY, double angleX, double angleZ, ref Matrix4d dest) {
5944         if ((properties & PROPERTY_IDENTITY) != 0)
5945             return dest.rotationYXZ(angleY, angleX, angleZ);
5946         else if ((properties & PROPERTY_TRANSLATION) != 0) {
5947             double tx = m30, ty = m31, tz = m32;
5948             return dest.rotationYXZ(angleY, angleX, angleZ).setTranslation(tx, ty, tz);
5949         } else if ((properties & PROPERTY_AFFINE) != 0)
5950             return dest.rotateAffineYXZ(angleY, angleX, angleZ);
5951         return rotateYXZInternal(angleY, angleX, angleZ, dest);
5952     }
5953     private Matrix4d rotateYXZInternal(double angleY, double angleX, double angleZ, ref Matrix4d dest) {
5954         double sinX = Math.sin(angleX);
5955         double cosX = Math.cosFromSin(sinX, angleX);
5956         double sinY = Math.sin(angleY);
5957         double cosY = Math.cosFromSin(sinY, angleY);
5958         double sinZ = Math.sin(angleZ);
5959         double cosZ = Math.cosFromSin(sinZ, angleZ);
5960         double m_sinY = -sinY;
5961         double m_sinX = -sinX;
5962         double m_sinZ = -sinZ;
5963 
5964         // rotateY
5965         double nm20 = m00 * sinY + m20 * cosY;
5966         double nm21 = m01 * sinY + m21 * cosY;
5967         double nm22 = m02 * sinY + m22 * cosY;
5968         double nm23 = m03 * sinY + m23 * cosY;
5969         double nm00 = m00 * cosY + m20 * m_sinY;
5970         double nm01 = m01 * cosY + m21 * m_sinY;
5971         double nm02 = m02 * cosY + m22 * m_sinY;
5972         double nm03 = m03 * cosY + m23 * m_sinY;
5973         // rotateX
5974         double nm10 = m10 * cosX + nm20 * sinX;
5975         double nm11 = m11 * cosX + nm21 * sinX;
5976         double nm12 = m12 * cosX + nm22 * sinX;
5977         double nm13 = m13 * cosX + nm23 * sinX;
5978         dest._m20(m10 * m_sinX + nm20 * cosX)
5979         ._m21(m11 * m_sinX + nm21 * cosX)
5980         ._m22(m12 * m_sinX + nm22 * cosX)
5981         ._m23(m13 * m_sinX + nm23 * cosX)
5982         // rotateZ
5983         ._m00(nm00 * cosZ + nm10 * sinZ)
5984         ._m01(nm01 * cosZ + nm11 * sinZ)
5985         ._m02(nm02 * cosZ + nm12 * sinZ)
5986         ._m03(nm03 * cosZ + nm13 * sinZ)
5987         ._m10(nm00 * m_sinZ + nm10 * cosZ)
5988         ._m11(nm01 * m_sinZ + nm11 * cosZ)
5989         ._m12(nm02 * m_sinZ + nm12 * cosZ)
5990         ._m13(nm03 * m_sinZ + nm13 * cosZ)
5991         // copy last column from 'this'
5992         ._m30(m30)
5993         ._m31(m31)
5994         ._m32(m32)
5995         ._m33(m33)
5996         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
5997         return dest;
5998     }
5999 
6000     /**
6001      * 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
6002      * followed by a rotation of <code>angleZ</code> radians about the Z axis.
6003      * <p>
6004      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6005      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6006      * When used with a left-handed coordinate system, the rotation is clockwise.
6007      * <p>
6008      * 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>)
6009      * 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).
6010      * <p>
6011      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the rotation matrix,
6012      * then the new matrix will be <code>M * R</code>. So when transforming a
6013      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
6014      * rotation will be applied first!
6015      * 
6016      * @param angleY
6017      *            the angle to rotate about Y
6018      * @param angleX
6019      *            the angle to rotate about X
6020      * @param angleZ
6021      *            the angle to rotate about Z
6022      * @return this
6023      */
6024     ref public Matrix4d rotateAffineYXZ(double angleY, double angleX, double angleZ) return {
6025         rotateAffineYXZ(angleY, angleX, angleZ, this);
6026         return this;
6027     }
6028 
6029     public Matrix4d rotateAffineYXZ(double angleY, double angleX, double angleZ, ref Matrix4d dest) {
6030         double sinX = Math.sin(angleX);
6031         double cosX = Math.cosFromSin(sinX, angleX);
6032         double sinY = Math.sin(angleY);
6033         double cosY = Math.cosFromSin(sinY, angleY);
6034         double sinZ = Math.sin(angleZ);
6035         double cosZ = Math.cosFromSin(sinZ, angleZ);
6036         double m_sinY = -sinY;
6037         double m_sinX = -sinX;
6038         double m_sinZ = -sinZ;
6039 
6040         // rotateY
6041         double nm20 = m00 * sinY + m20 * cosY;
6042         double nm21 = m01 * sinY + m21 * cosY;
6043         double nm22 = m02 * sinY + m22 * cosY;
6044         double nm00 = m00 * cosY + m20 * m_sinY;
6045         double nm01 = m01 * cosY + m21 * m_sinY;
6046         double nm02 = m02 * cosY + m22 * m_sinY;
6047         // rotateX
6048         double nm10 = m10 * cosX + nm20 * sinX;
6049         double nm11 = m11 * cosX + nm21 * sinX;
6050         double nm12 = m12 * cosX + nm22 * sinX;
6051         dest._m20(m10 * m_sinX + nm20 * cosX)
6052         ._m21(m11 * m_sinX + nm21 * cosX)
6053         ._m22(m12 * m_sinX + nm22 * cosX)
6054         ._m23(0.0)
6055         // rotateZ
6056         ._m00(nm00 * cosZ + nm10 * sinZ)
6057         ._m01(nm01 * cosZ + nm11 * sinZ)
6058         ._m02(nm02 * cosZ + nm12 * sinZ)
6059         ._m03(0.0)
6060         ._m10(nm00 * m_sinZ + nm10 * cosZ)
6061         ._m11(nm01 * m_sinZ + nm11 * cosZ)
6062         ._m12(nm02 * m_sinZ + nm12 * cosZ)
6063         ._m13(0.0)
6064         // copy last column from 'this'
6065         ._m30(m30)
6066         ._m31(m31)
6067         ._m32(m32)
6068         ._m33(m33)
6069         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
6070         return dest;
6071     }
6072 
6073     /**
6074      * Set this matrix to a rotation transformation using the given {@link AxisAngle4d}.
6075      * <p>
6076      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6077      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6078      * When used with a left-handed coordinate system, the rotation is clockwise.
6079      * <p>
6080      * The resulting matrix can be multiplied against another transformation
6081      * matrix to obtain an additional rotation.
6082      * <p>
6083      * In order to apply the rotation transformation to an existing transformation,
6084      * use {@link #rotate(ref AxisAngle4d) rotate()} instead.
6085      * <p>
6086      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
6087      *
6088      * @see #rotate(ref AxisAngle4d)
6089      * 
6090      * @param angleAxis
6091      *          the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized})
6092      * @return this
6093      */
6094     ref public Matrix4d rotation(ref AxisAngle4d angleAxis) return {
6095         return rotation(angleAxis.angle, angleAxis.x, angleAxis.y, angleAxis.z);
6096     }
6097 
6098     /**
6099      * Set this matrix to the rotation - and possibly scaling - transformation of the given {@link Quaterniond}.
6100      * <p>
6101      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6102      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6103      * When used with a left-handed coordinate system, the rotation is clockwise.
6104      * <p>
6105      * The resulting matrix can be multiplied against another transformation
6106      * matrix to obtain an additional rotation.
6107      * <p>
6108      * In order to apply the rotation transformation to an existing transformation,
6109      * use {@link #rotate(ref Quaterniond) rotate()} instead.
6110      * <p>
6111      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6112      * 
6113      * @see #rotate(ref Quaterniond)
6114      * 
6115      * @param quat
6116      *          the {@link Quaterniond}
6117      * @return this
6118      */
6119     ref public Matrix4d rotation(ref Quaterniond quat) return {
6120         double w2 = quat.w * quat.w;
6121         double x2 = quat.x * quat.x;
6122         double y2 = quat.y * quat.y;
6123         double z2 = quat.z * quat.z;
6124         double zw = quat.z * quat.w, dzw = zw + zw;
6125         double xy = quat.x * quat.y, dxy = xy + xy;
6126         double xz = quat.x * quat.z, dxz = xz + xz;
6127         double yw = quat.y * quat.w, dyw = yw + yw;
6128         double yz = quat.y * quat.z, dyz = yz + yz;
6129         double xw = quat.x * quat.w, dxw = xw + xw;
6130         if ((properties & PROPERTY_IDENTITY) == 0)
6131             this._identity();
6132         _m00(w2 + x2 - z2 - y2).
6133         _m01(dxy + dzw).
6134         _m02(dxz - dyw).
6135         _m10(-dzw + dxy).
6136         _m11(y2 - z2 + w2 - x2).
6137         _m12(dyz + dxw).
6138         _m20(dyw + dxz).
6139         _m21(dyz - dxw).
6140         _m22(z2 - y2 - x2 + w2).
6141         _properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
6142         return this;
6143     }
6144 
6145 
6146     /**
6147      * 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>,
6148      * <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
6149      * which scales the three axes x, y and z by <code>(sx, sy, sz)</code>.
6150      * <p>
6151      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6152      * at last the translation.
6153      * <p>
6154      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6155      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6156      * When used with a left-handed coordinate system, the rotation is clockwise.
6157      * <p>
6158      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz)</code>
6159      * 
6160      * @see #translation(double, double, double)
6161      * @see #rotate(ref Quaterniond)
6162      * @see #scale(double, double, double)
6163      * 
6164      * @param tx
6165      *          the number of units by which to translate the x-component
6166      * @param ty
6167      *          the number of units by which to translate the y-component
6168      * @param tz
6169      *          the number of units by which to translate the z-component
6170      * @param qx
6171      *          the x-coordinate of the vector part of the quaternion
6172      * @param qy
6173      *          the y-coordinate of the vector part of the quaternion
6174      * @param qz
6175      *          the z-coordinate of the vector part of the quaternion
6176      * @param qw
6177      *          the scalar part of the quaternion
6178      * @param sx
6179      *          the scaling factor for the x-axis
6180      * @param sy
6181      *          the scaling factor for the y-axis
6182      * @param sz
6183      *          the scaling factor for the z-axis
6184      * @return this
6185      */
6186     ref public Matrix4d translationRotateScale(double tx, double ty, double tz, 
6187                                            double qx, double qy, double qz, double qw, 
6188                                            double sx, double sy, double sz) return {
6189         double dqx = qx + qx, dqy = qy + qy, dqz = qz + qz;
6190         double q00 = dqx * qx;
6191         double q11 = dqy * qy;
6192         double q22 = dqz * qz;
6193         double q01 = dqx * qy;
6194         double q02 = dqx * qz;
6195         double q03 = dqx * qw;
6196         double q12 = dqy * qz;
6197         double q13 = dqy * qw;
6198         double q23 = dqz * qw;
6199         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
6200         _m00(sx - (q11 + q22) * sx).
6201         _m01((q01 + q23) * sx).
6202         _m02((q02 - q13) * sx).
6203         _m03(0.0).
6204         _m10((q01 - q23) * sy).
6205         _m11(sy - (q22 + q00) * sy).
6206         _m12((q12 + q03) * sy).
6207         _m13(0.0).
6208         _m20((q02 + q13) * sz).
6209         _m21((q12 - q03) * sz).
6210         _m22(sz - (q11 + q00) * sz).
6211         _m23(0.0).
6212         _m30(tx).
6213         _m31(ty).
6214         _m32(tz).
6215         _m33(1.0).
6216         properties = PROPERTY_AFFINE | (one ? PROPERTY_ORTHONORMAL : 0);
6217         return this;
6218     }
6219 
6220     /**
6221      * Set <code>this</code> matrix to <code>T * R * S</code>, where <code>T</code> is the given <code>translation</code>,
6222      * <code>R</code> is a rotation transformation specified by the given quaternion, and <code>S</code> is a scaling transformation
6223      * which scales the axes by <code>scale</code>.
6224      * <p>
6225      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6226      * at last the translation.
6227      * <p>
6228      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6229      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6230      * When used with a left-handed coordinate system, the rotation is clockwise.
6231      * <p>
6232      * This method is equivalent to calling: <code>translation(translation).rotate(quat).scale(scale)</code>
6233      * 
6234      * @see #translation(ref Vector3d)
6235      * @see #rotate(ref Quaterniond)
6236      * @see #scale(ref Vector3d)
6237      * 
6238      * @param translation
6239      *          the translation
6240      * @param quat
6241      *          the quaternion representing a rotation
6242      * @param scale
6243      *          the scaling factors
6244      * @return this
6245      */
6246     ref public Matrix4d translationRotateScale(ref Vector3d translation, 
6247                                            Quaterniond quat, 
6248                                            Vector3d scale) return {
6249         return translationRotateScale(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w, scale.x, scale.y, scale.z);
6250     }
6251 
6252     /**
6253      * 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>,
6254      * <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
6255      * which scales all three axes by <code>scale</code>.
6256      * <p>
6257      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6258      * at last the translation.
6259      * <p>
6260      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6261      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6262      * When used with a left-handed coordinate system, the rotation is clockwise.
6263      * <p>
6264      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat).scale(scale)</code>
6265      * 
6266      * @see #translation(double, double, double)
6267      * @see #rotate(ref Quaterniond)
6268      * @see #scale(double)
6269      * 
6270      * @param tx
6271      *          the number of units by which to translate the x-component
6272      * @param ty
6273      *          the number of units by which to translate the y-component
6274      * @param tz
6275      *          the number of units by which to translate the z-component
6276      * @param qx
6277      *          the x-coordinate of the vector part of the quaternion
6278      * @param qy
6279      *          the y-coordinate of the vector part of the quaternion
6280      * @param qz
6281      *          the z-coordinate of the vector part of the quaternion
6282      * @param qw
6283      *          the scalar part of the quaternion
6284      * @param scale
6285      *          the scaling factor for all three axes
6286      * @return this
6287      */
6288     ref public Matrix4d translationRotateScale(double tx, double ty, double tz, 
6289                                            double qx, double qy, double qz, double qw, 
6290                                            double scale) return {
6291         return translationRotateScale(tx, ty, tz, qx, qy, qz, qw, scale, scale, scale);
6292     }
6293 
6294     /**
6295      * Set <code>this</code> matrix to <code>T * R * S</code>, where <code>T</code> is the given <code>translation</code>,
6296      * <code>R</code> is a rotation transformation specified by the given quaternion, and <code>S</code> is a scaling transformation
6297      * which scales all three axes by <code>scale</code>.
6298      * <p>
6299      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6300      * at last the translation.
6301      * <p>
6302      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6303      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6304      * When used with a left-handed coordinate system, the rotation is clockwise.
6305      * <p>
6306      * This method is equivalent to calling: <code>translation(translation).rotate(quat).scale(scale)</code>
6307      * 
6308      * @see #translation(ref Vector3d)
6309      * @see #rotate(ref Quaterniond)
6310      * @see #scale(double)
6311      * 
6312      * @param translation
6313      *          the translation
6314      * @param quat
6315      *          the quaternion representing a rotation
6316      * @param scale
6317      *          the scaling factors
6318      * @return this
6319      */
6320     ref public Matrix4d translationRotateScale(ref Vector3d translation, 
6321                                            Quaterniond quat, 
6322                                            double scale) return {
6323         return translationRotateScale(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w, scale, scale, scale);
6324     }
6325 
6326 
6327     /**
6328      * 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>,
6329      * <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
6330      * which scales the three axes x, y and z by <code>(sx, sy, sz)</code>.
6331      * <p>
6332      * This method is equivalent to calling: <code>translationRotateScale(...).invert()</code>
6333      * 
6334      * @see #translationRotateScale(double, double, double, double, double, double, double, double, double, double)
6335      * @see #invert()
6336      * 
6337      * @param tx
6338      *          the number of units by which to translate the x-component
6339      * @param ty
6340      *          the number of units by which to translate the y-component
6341      * @param tz
6342      *          the number of units by which to translate the z-component
6343      * @param qx
6344      *          the x-coordinate of the vector part of the quaternion
6345      * @param qy
6346      *          the y-coordinate of the vector part of the quaternion
6347      * @param qz
6348      *          the z-coordinate of the vector part of the quaternion
6349      * @param qw
6350      *          the scalar part of the quaternion
6351      * @param sx
6352      *          the scaling factor for the x-axis
6353      * @param sy
6354      *          the scaling factor for the y-axis
6355      * @param sz
6356      *          the scaling factor for the z-axis
6357      * @return this
6358      */
6359     ref public Matrix4d translationRotateScaleInvert(double tx, double ty, double tz, 
6360                                                  double qx, double qy, double qz, double qw, 
6361                                                  double sx, double sy, double sz) return {
6362         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
6363         if (one)
6364             return translationRotateInvert(tx, ty, tz, qx, qy, qz, qw);
6365         double nqx = -qx, nqy = -qy, nqz = -qz;
6366         double dqx = nqx + nqx;
6367         double dqy = nqy + nqy;
6368         double dqz = nqz + nqz;
6369         double q00 = dqx * nqx;
6370         double q11 = dqy * nqy;
6371         double q22 = dqz * nqz;
6372         double q01 = dqx * nqy;
6373         double q02 = dqx * nqz;
6374         double q03 = dqx * qw;
6375         double q12 = dqy * nqz;
6376         double q13 = dqy * qw;
6377         double q23 = dqz * qw;
6378         double isx = 1/sx, isy = 1/sy, isz = 1/sz;
6379         _m00(isx * (1.0 - q11 - q22)).
6380         _m01(isy * (q01 + q23)).
6381         _m02(isz * (q02 - q13)).
6382         _m03(0.0).
6383         _m10(isx * (q01 - q23)).
6384         _m11(isy * (1.0 - q22 - q00)).
6385         _m12(isz * (q12 + q03)).
6386         _m13(0.0).
6387         _m20(isx * (q02 + q13)).
6388         _m21(isy * (q12 - q03)).
6389         _m22(isz * (1.0 - q11 - q00)).
6390         _m23(0.0).
6391         _m30(-m00 * tx - m10 * ty - m20 * tz).
6392         _m31(-m01 * tx - m11 * ty - m21 * tz).
6393         _m32(-m02 * tx - m12 * ty - m22 * tz).
6394         _m33(1.0).
6395         properties = PROPERTY_AFFINE;
6396         return this;
6397     }
6398 
6399     /**
6400      * Set <code>this</code> matrix to <code>(T * R * S)<sup>-1</sup></code>, where <code>T</code> is the given <code>translation</code>,
6401      * <code>R</code> is a rotation transformation specified by the given quaternion, and <code>S</code> is a scaling transformation
6402      * which scales the axes by <code>scale</code>.
6403      * <p>
6404      * This method is equivalent to calling: <code>translationRotateScale(...).invert()</code>
6405      * 
6406      * @see #translationRotateScale(ref Vector3d, Quaterniond, Vector3d)
6407      * @see #invert()
6408      * 
6409      * @param translation
6410      *          the translation
6411      * @param quat
6412      *          the quaternion representing a rotation
6413      * @param scale
6414      *          the scaling factors
6415      * @return this
6416      */
6417     ref public Matrix4d translationRotateScaleInvert(ref Vector3d translation, 
6418                                                  Quaterniond quat, 
6419                                                  Vector3d scale) return {
6420         return translationRotateScaleInvert(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w, scale.x, scale.y, scale.z);
6421     }
6422 
6423     
6424     /**
6425      * Set <code>this</code> matrix to <code>(T * R * S)<sup>-1</sup></code>, where <code>T</code> is the given <code>translation</code>,
6426      * <code>R</code> is a rotation transformation specified by the given quaternion, and <code>S</code> is a scaling transformation
6427      * which scales all three axes by <code>scale</code>.
6428      * <p>
6429      * This method is equivalent to calling: <code>translationRotateScale(...).invert()</code>
6430      * 
6431      * @see #translationRotateScale(ref Vector3d, Quaterniond, double)
6432      * @see #invert()
6433      * 
6434      * @param translation
6435      *          the translation
6436      * @param quat
6437      *          the quaternion representing a rotation
6438      * @param scale
6439      *          the scaling factors
6440      * @return this
6441      */
6442     ref public Matrix4d translationRotateScaleInvert(ref Vector3d translation, 
6443                                                  Quaterniond quat, 
6444                                                  double scale) return {
6445         return translationRotateScaleInvert(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w, scale, scale, scale);
6446     }
6447 
6448 
6449     /**
6450      * 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>,
6451      * <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
6452      * 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.
6453      * <p>
6454      * 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
6455      * at last the translation.
6456      * <p>
6457      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6458      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6459      * When used with a left-handed coordinate system, the rotation is clockwise.
6460      * <p>
6461      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat).scale(sx, sy, sz).mulAffine(m)</code>
6462      * 
6463      * @see #translation(double, double, double)
6464      * @see #rotate(ref Quaterniond)
6465      * @see #scale(double, double, double)
6466      * @see #mulAffine(Matrix4d)
6467      * 
6468      * @param tx
6469      *          the number of units by which to translate the x-component
6470      * @param ty
6471      *          the number of units by which to translate the y-component
6472      * @param tz
6473      *          the number of units by which to translate the z-component
6474      * @param qx
6475      *          the x-coordinate of the vector part of the quaternion
6476      * @param qy
6477      *          the y-coordinate of the vector part of the quaternion
6478      * @param qz
6479      *          the z-coordinate of the vector part of the quaternion
6480      * @param qw
6481      *          the scalar part of the quaternion
6482      * @param sx
6483      *          the scaling factor for the x-axis
6484      * @param sy
6485      *          the scaling factor for the y-axis
6486      * @param sz
6487      *          the scaling factor for the z-axis
6488      * @param m
6489      *          the {@link #isAffine() affine} matrix to multiply by
6490      * @return this
6491      */
6492     ref public Matrix4d translationRotateScaleMulAffine(double tx, double ty, double tz, 
6493                                                     double qx, double qy, double qz, double qw, 
6494                                                     double sx, double sy, double sz,
6495                                                     Matrix4d m) return {
6496         double w2 = qw * qw;
6497         double x2 = qx * qx;
6498         double y2 = qy * qy;
6499         double z2 = qz * qz;
6500         double zw = qz * qw;
6501         double xy = qx * qy;
6502         double xz = qx * qz;
6503         double yw = qy * qw;
6504         double yz = qy * qz;
6505         double xw = qx * qw;
6506         double nm00 = w2 + x2 - z2 - y2;
6507         double nm01 = xy + zw + zw + xy;
6508         double nm02 = xz - yw + xz - yw;
6509         double nm10 = -zw + xy - zw + xy;
6510         double nm11 = y2 - z2 + w2 - x2;
6511         double nm12 = yz + yz + xw + xw;
6512         double nm20 = yw + xz + xz + yw;
6513         double nm21 = yz + yz - xw - xw;
6514         double nm22 = z2 - y2 - x2 + w2;
6515         double m00 = nm00 * m.m00 + nm10 * m.m01 + nm20 * m.m02;
6516         double m01 = nm01 * m.m00 + nm11 * m.m01 + nm21 * m.m02;
6517         setm02(nm02 * m.m00 + nm12 * m.m01 + nm22 * m.m02);
6518         setm00(m00);
6519         setm01(m01);
6520         setm03(0.0);
6521         double m10 = nm00 * m.m10 + nm10 * m.m11 + nm20 * m.m12;
6522         double m11 = nm01 * m.m10 + nm11 * m.m11 + nm21 * m.m12;
6523         setm12(nm02 * m.m10 + nm12 * m.m11 + nm22 * m.m12);
6524         setm10(m10);
6525         setm11(m11);
6526         setm13(0.0);
6527         double m20 = nm00 * m.m20 + nm10 * m.m21 + nm20 * m.m22;
6528         double m21 = nm01 * m.m20 + nm11 * m.m21 + nm21 * m.m22;
6529         setm22(nm02 * m.m20 + nm12 * m.m21 + nm22 * m.m22);
6530         setm20(m20);
6531         setm21(m21);
6532         setm23(0.0);
6533         double m30 = nm00 * m.m30 + nm10 * m.m31 + nm20 * m.m32 + tx;
6534         double m31 = nm01 * m.m30 + nm11 * m.m31 + nm21 * m.m32 + ty;
6535         setm32(nm02 * m.m30 + nm12 * m.m31 + nm22 * m.m32 + tz);
6536         setm30(m30);
6537         setm31(m31);
6538         setm33(1.0);
6539         bool one = Math.absEqualsOne(sx) && Math.absEqualsOne(sy) && Math.absEqualsOne(sz);
6540         properties = PROPERTY_AFFINE | (one && (m.properties & PROPERTY_ORTHONORMAL) != 0 ? PROPERTY_ORTHONORMAL : 0);
6541         return this;
6542     }
6543 
6544    
6545 
6546     /**
6547      * 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
6548      * <code>R</code> is a rotation - and possibly scaling - transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>.
6549      * <p>
6550      * When transforming a vector by the resulting matrix the rotation - and possibly scaling - transformation will be applied first and then the translation.
6551      * <p>
6552      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6553      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6554      * When used with a left-handed coordinate system, the rotation is clockwise.
6555      * <p>
6556      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat)</code>
6557      * 
6558      * @see #translation(double, double, double)
6559      * @see #rotate(ref Quaterniond)
6560      * 
6561      * @param tx
6562      *          the number of units by which to translate the x-component
6563      * @param ty
6564      *          the number of units by which to translate the y-component
6565      * @param tz
6566      *          the number of units by which to translate the z-component
6567      * @param qx
6568      *          the x-coordinate of the vector part of the quaternion
6569      * @param qy
6570      *          the y-coordinate of the vector part of the quaternion
6571      * @param qz
6572      *          the z-coordinate of the vector part of the quaternion
6573      * @param qw
6574      *          the scalar part of the quaternion
6575      * @return this
6576      */
6577     ref public Matrix4d translationRotate(double tx, double ty, double tz, double qx, double qy, double qz, double qw) return {
6578         double w2 = qw * qw;
6579         double x2 = qx * qx;
6580         double y2 = qy * qy;
6581         double z2 = qz * qz;
6582         double zw = qz * qw;
6583         double xy = qx * qy;
6584         double xz = qx * qz;
6585         double yw = qy * qw;
6586         double yz = qy * qz;
6587         double xw = qx * qw;
6588         setm00(w2 + x2 - z2 - y2);
6589         setm01(xy + zw + zw + xy);
6590         setm02(xz - yw + xz - yw);
6591         setm10(-zw + xy - zw + xy);
6592         setm11(y2 - z2 + w2 - x2);
6593         setm12(yz + yz + xw + xw);
6594         setm20(yw + xz + xz + yw);
6595         setm21(yz + yz - xw - xw);
6596         setm22(z2 - y2 - x2 + w2);
6597         setm30(tx);
6598         setm31(ty);
6599         setm32(tz);
6600         setm33(1.0);
6601         this.properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
6602         return this;
6603     }
6604 
6605     /**
6606      * 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
6607      * <code>R</code> is a rotation - and possibly scaling - transformation specified by the given quaternion.
6608      * <p>
6609      * When transforming a vector by the resulting matrix the rotation - and possibly scaling - transformation will be applied first and then the translation.
6610      * <p>
6611      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6612      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6613      * When used with a left-handed coordinate system, the rotation is clockwise.
6614      * <p>
6615      * This method is equivalent to calling: <code>translation(tx, ty, tz).rotate(quat)</code>
6616      * 
6617      * @see #translation(double, double, double)
6618      * @see #rotate(ref Quaterniond)
6619      * 
6620      * @param tx
6621      *          the number of units by which to translate the x-component
6622      * @param ty
6623      *          the number of units by which to translate the y-component
6624      * @param tz
6625      *          the number of units by which to translate the z-component
6626      * @param quat
6627      *          the quaternion representing a rotation
6628      * @return this
6629      */
6630     ref public Matrix4d translationRotate(double tx, double ty, double tz, Quaterniond quat) return {
6631         return translationRotate(tx, ty, tz, quat.x, quat.y, quat.z, quat.w);
6632     }
6633 
6634     /**
6635      * Set <code>this</code> matrix to <code>T * R</code>, where <code>T</code> is the given <code>translation</code> and
6636      * <code>R</code> is a rotation transformation specified by the given quaternion.
6637      * <p>
6638      * When transforming a vector by the resulting matrix the scaling transformation will be applied first, then the rotation and
6639      * at last the translation.
6640      * <p>
6641      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6642      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6643      * When used with a left-handed coordinate system, the rotation is clockwise.
6644      * <p>
6645      * This method is equivalent to calling: <code>translation(translation).rotate(quat)</code>
6646      * 
6647      * @see #translation(ref Vector3d)
6648      * @see #rotate(ref Quaterniond)
6649      * 
6650      * @param translation
6651      *          the translation
6652      * @param quat
6653      *          the quaternion representing a rotation
6654      * @return this
6655      */
6656     ref public Matrix4d translationRotate(ref Vector3d translation, 
6657                                       Quaterniond quat) return {
6658         return translationRotate(translation.x, translation.y, translation.z, quat.x, quat.y, quat.z, quat.w);
6659     }
6660 
6661     /**
6662      * 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
6663      * <code>R</code> is a rotation transformation specified by the quaternion <code>(qx, qy, qz, qw)</code>.
6664      * <p>
6665      * This method is equivalent to calling: <code>translationRotate(...).invert()</code>
6666      * 
6667      * @see #translationRotate(double, double, double, double, double, double, double)
6668      * @see #invert()
6669      * 
6670      * @param tx
6671      *          the number of units by which to translate the x-component
6672      * @param ty
6673      *          the number of units by which to translate the y-component
6674      * @param tz
6675      *          the number of units by which to translate the z-component
6676      * @param qx
6677      *          the x-coordinate of the vector part of the quaternion
6678      * @param qy
6679      *          the y-coordinate of the vector part of the quaternion
6680      * @param qz
6681      *          the z-coordinate of the vector part of the quaternion
6682      * @param qw
6683      *          the scalar part of the quaternion
6684      * @return this
6685      */
6686     ref public Matrix4d translationRotateInvert(double tx, double ty, double tz, double qx, double qy, double qz, double qw) return {
6687         double nqx = -qx, nqy = -qy, nqz = -qz;
6688         double dqx = nqx + nqx;
6689         double dqy = nqy + nqy;
6690         double dqz = nqz + nqz;
6691         double q00 = dqx * nqx;
6692         double q11 = dqy * nqy;
6693         double q22 = dqz * nqz;
6694         double q01 = dqx * nqy;
6695         double q02 = dqx * nqz;
6696         double q03 = dqx * qw;
6697         double q12 = dqy * nqz;
6698         double q13 = dqy * qw;
6699         double q23 = dqz * qw;
6700         return this
6701         ._m00(1.0 - q11 - q22)
6702         ._m01(q01 + q23)
6703         ._m02(q02 - q13)
6704         ._m03(0.0)
6705         ._m10(q01 - q23)
6706         ._m11(1.0 - q22 - q00)
6707         ._m12(q12 + q03)
6708         ._m13(0.0)
6709         ._m20(q02 + q13)
6710         ._m21(q12 - q03)
6711         ._m22(1.0 - q11 - q00)
6712         ._m23(0.0)
6713         ._m30(-m00 * tx - m10 * ty - m20 * tz)
6714         ._m31(-m01 * tx - m11 * ty - m21 * tz)
6715         ._m32(-m02 * tx - m12 * ty - m22 * tz)
6716         ._m33(1.0)
6717         ._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
6718     }
6719 
6720     
6721 
6722     /**
6723      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix and store
6724      * the result in <code>dest</code>.
6725      * <p>
6726      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6727      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6728      * When used with a left-handed coordinate system, the rotation is clockwise.
6729      * <p>
6730      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6731      * then the new matrix will be <code>M * Q</code>. So when transforming a
6732      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6733      * the quaternion rotation will be applied first!
6734      * <p>
6735      * In order to set the matrix to a rotation transformation without post-multiplying,
6736      * use {@link #rotation(ref Quaterniond)}.
6737      * <p>
6738      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6739      * 
6740      * @see #rotation(ref Quaterniond)
6741      * 
6742      * @param quat
6743      *          the {@link Quaterniond}
6744      * @param dest
6745      *          will hold the result
6746      * @return dest
6747      */
6748     public Matrix4d rotate(ref Quaterniond quat, ref Matrix4d dest) {
6749         if ((properties & PROPERTY_IDENTITY) != 0)
6750             return dest.rotation(quat);
6751         else if ((properties & PROPERTY_TRANSLATION) != 0)
6752             return rotateTranslation(quat, dest);
6753         else if ((properties & PROPERTY_AFFINE) != 0)
6754             return rotateAffine(quat, dest);
6755         return rotateGeneric(quat, dest);
6756     }
6757     private Matrix4d rotateGeneric(ref Quaterniond quat, ref Matrix4d dest) {
6758         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
6759         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
6760         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
6761         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
6762         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
6763         double rm00 = w2 + x2 - z2 - y2;
6764         double rm01 = dxy + dzw;
6765         double rm02 = dxz - dyw;
6766         double rm10 = -dzw + dxy;
6767         double rm11 = y2 - z2 + w2 - x2;
6768         double rm12 = dyz + dxw;
6769         double rm20 = dyw + dxz;
6770         double rm21 = dyz - dxw;
6771         double rm22 = z2 - y2 - x2 + w2;
6772         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
6773         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
6774         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
6775         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
6776         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
6777         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
6778         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
6779         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
6780         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
6781         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
6782         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
6783         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
6784         ._m00(nm00)
6785         ._m01(nm01)
6786         ._m02(nm02)
6787         ._m03(nm03)
6788         ._m10(nm10)
6789         ._m11(nm11)
6790         ._m12(nm12)
6791         ._m13(nm13)
6792         ._m30(m30)
6793         ._m31(m31)
6794         ._m32(m32)
6795         ._m33(m33)
6796         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
6797         return dest;
6798     }
6799 
6800 
6801     /**
6802      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix.
6803      * <p>
6804      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6805      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6806      * When used with a left-handed coordinate system, the rotation is clockwise.
6807      * <p>
6808      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6809      * then the new matrix will be <code>M * Q</code>. So when transforming a
6810      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6811      * the quaternion rotation will be applied first!
6812      * <p>
6813      * In order to set the matrix to a rotation transformation without post-multiplying,
6814      * use {@link #rotation(ref Quaterniond)}.
6815      * <p>
6816      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6817      * 
6818      * @see #rotation(ref Quaterniond)
6819      * 
6820      * @param quat
6821      *          the {@link Quaterniond}
6822      * @return this
6823      */
6824     ref public Matrix4d rotate(ref Quaterniond quat) return {
6825         rotate(quat, this);
6826         return this;
6827     }
6828 
6829 
6830     /**
6831      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this {@link #isAffine() affine} matrix and store
6832      * the result in <code>dest</code>.
6833      * <p>
6834      * This method assumes <code>this</code> to be {@link #isAffine() affine}.
6835      * <p>
6836      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6837      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6838      * When used with a left-handed coordinate system, the rotation is clockwise.
6839      * <p>
6840      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6841      * then the new matrix will be <code>M * Q</code>. So when transforming a
6842      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6843      * the quaternion rotation will be applied first!
6844      * <p>
6845      * In order to set the matrix to a rotation transformation without post-multiplying,
6846      * use {@link #rotation(ref Quaterniond)}.
6847      * <p>
6848      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6849      * 
6850      * @see #rotation(ref Quaterniond)
6851      * 
6852      * @param quat
6853      *          the {@link Quaterniond}
6854      * @param dest
6855      *          will hold the result
6856      * @return dest
6857      */
6858     public Matrix4d rotateAffine(ref Quaterniond quat, ref Matrix4d dest) {
6859         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
6860         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
6861         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
6862         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
6863         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
6864         double rm00 = w2 + x2 - z2 - y2;
6865         double rm01 = dxy + dzw;
6866         double rm02 = dxz - dyw;
6867         double rm10 = -dzw + dxy;
6868         double rm11 = y2 - z2 + w2 - x2;
6869         double rm12 = dyz + dxw;
6870         double rm20 = dyw + dxz;
6871         double rm21 = dyz - dxw;
6872         double rm22 = z2 - y2 - x2 + w2;
6873         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
6874         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
6875         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
6876         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
6877         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
6878         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
6879         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
6880         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
6881         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
6882         ._m23(0.0)
6883         ._m00(nm00)
6884         ._m01(nm01)
6885         ._m02(nm02)
6886         ._m03(0.0)
6887         ._m10(nm10)
6888         ._m11(nm11)
6889         ._m12(nm12)
6890         ._m13(0.0)
6891         ._m30(m30)
6892         ._m31(m31)
6893         ._m32(m32)
6894         ._m33(m33)
6895         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
6896         return dest;
6897     }
6898 
6899     /**
6900      * Apply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix.
6901      * <p>
6902      * This method assumes <code>this</code> to be {@link #isAffine() affine}.
6903      * <p>
6904      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6905      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6906      * When used with a left-handed coordinate system, the rotation is clockwise.
6907      * <p>
6908      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6909      * then the new matrix will be <code>M * Q</code>. So when transforming a
6910      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6911      * the quaternion rotation will be applied first!
6912      * <p>
6913      * In order to set the matrix to a rotation transformation without post-multiplying,
6914      * use {@link #rotation(ref Quaterniond)}.
6915      * <p>
6916      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6917      * 
6918      * @see #rotation(ref Quaterniond)
6919      * 
6920      * @param quat
6921      *          the {@link Quaterniond}
6922      * @return this
6923      */
6924     ref public Matrix4d rotateAffine(ref Quaterniond quat) return {
6925         rotateAffine(quat, this);
6926         return this;
6927     }
6928 
6929     /**
6930      * 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
6931      * the result in <code>dest</code>.
6932      * <p>
6933      * This method assumes <code>this</code> to only contain a translation.
6934      * <p>
6935      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6936      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6937      * When used with a left-handed coordinate system, the rotation is clockwise.
6938      * <p>
6939      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
6940      * then the new matrix will be <code>M * Q</code>. So when transforming a
6941      * vector <code>v</code> with the new matrix by using <code>M * Q * v</code>,
6942      * the quaternion rotation will be applied first!
6943      * <p>
6944      * In order to set the matrix to a rotation transformation without post-multiplying,
6945      * use {@link #rotation(ref Quaterniond)}.
6946      * <p>
6947      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
6948      * 
6949      * @see #rotation(ref Quaterniond)
6950      * 
6951      * @param quat
6952      *          the {@link Quaterniond}
6953      * @param dest
6954      *          will hold the result
6955      * @return dest
6956      */
6957     public Matrix4d rotateTranslation(ref Quaterniond quat, ref Matrix4d dest) {
6958         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
6959         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
6960         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
6961         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
6962         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
6963         double rm00 = w2 + x2 - z2 - y2;
6964         double rm01 = dxy + dzw;
6965         double rm02 = dxz - dyw;
6966         double rm10 = -dzw + dxy;
6967         double rm11 = y2 - z2 + w2 - x2;
6968         double rm12 = dyz + dxw;
6969         double rm20 = dyw + dxz;
6970         double rm21 = dyz - dxw;
6971         double rm22 = z2 - y2 - x2 + w2;
6972         dest._m20(rm20)
6973         ._m21(rm21)
6974         ._m22(rm22)
6975         ._m23(0.0)
6976         ._m00(rm00)
6977         ._m01(rm01)
6978         ._m02(rm02)
6979         ._m03(0.0)
6980         ._m10(rm10)
6981         ._m11(rm11)
6982         ._m12(rm12)
6983         ._m13(0.0)
6984         ._m30(m30)
6985         ._m31(m31)
6986         ._m32(m32)
6987         ._m33(1.0)
6988         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
6989         return dest;
6990     }
6991 
6992 
6993     /**
6994      * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix and store
6995      * the result in <code>dest</code>.
6996      * <p>
6997      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
6998      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
6999      * When used with a left-handed coordinate system, the rotation is clockwise.
7000      * <p>
7001      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
7002      * then the new matrix will be <code>Q * M</code>. So when transforming a
7003      * vector <code>v</code> with the new matrix by using <code>Q * M * v</code>,
7004      * the quaternion rotation will be applied last!
7005      * <p>
7006      * In order to set the matrix to a rotation transformation without pre-multiplying,
7007      * use {@link #rotation(ref Quaterniond)}.
7008      * <p>
7009      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
7010      * 
7011      * @see #rotation(ref Quaterniond)
7012      * 
7013      * @param quat
7014      *          the {@link Quaterniond}
7015      * @param dest
7016      *          will hold the result
7017      * @return dest
7018      */
7019     public Matrix4d rotateLocal(ref Quaterniond quat, ref Matrix4d dest) {
7020         double w2 = quat.w * quat.w, x2 = quat.x * quat.x;
7021         double y2 = quat.y * quat.y, z2 = quat.z * quat.z;
7022         double zw = quat.z * quat.w, dzw = zw + zw, xy = quat.x * quat.y, dxy = xy + xy;
7023         double xz = quat.x * quat.z, dxz = xz + xz, yw = quat.y * quat.w, dyw = yw + yw;
7024         double yz = quat.y * quat.z, dyz = yz + yz, xw = quat.x * quat.w, dxw = xw + xw;
7025         double lm00 = w2 + x2 - z2 - y2;
7026         double lm01 = dxy + dzw;
7027         double lm02 = dxz - dyw;
7028         double lm10 = -dzw + dxy;
7029         double lm11 = y2 - z2 + w2 - x2;
7030         double lm12 = dyz + dxw;
7031         double lm20 = dyw + dxz;
7032         double lm21 = dyz - dxw;
7033         double lm22 = z2 - y2 - x2 + w2;
7034         double nm00 = lm00 * m00 + lm10 * m01 + lm20 * m02;
7035         double nm01 = lm01 * m00 + lm11 * m01 + lm21 * m02;
7036         double nm02 = lm02 * m00 + lm12 * m01 + lm22 * m02;
7037         double nm03 = m03;
7038         double nm10 = lm00 * m10 + lm10 * m11 + lm20 * m12;
7039         double nm11 = lm01 * m10 + lm11 * m11 + lm21 * m12;
7040         double nm12 = lm02 * m10 + lm12 * m11 + lm22 * m12;
7041         double nm13 = m13;
7042         double nm20 = lm00 * m20 + lm10 * m21 + lm20 * m22;
7043         double nm21 = lm01 * m20 + lm11 * m21 + lm21 * m22;
7044         double nm22 = lm02 * m20 + lm12 * m21 + lm22 * m22;
7045         double nm23 = m23;
7046         double nm30 = lm00 * m30 + lm10 * m31 + lm20 * m32;
7047         double nm31 = lm01 * m30 + lm11 * m31 + lm21 * m32;
7048         double nm32 = lm02 * m30 + lm12 * m31 + lm22 * m32;
7049         dest._m00(nm00)
7050         ._m01(nm01)
7051         ._m02(nm02)
7052         ._m03(nm03)
7053         ._m10(nm10)
7054         ._m11(nm11)
7055         ._m12(nm12)
7056         ._m13(nm13)
7057         ._m20(nm20)
7058         ._m21(nm21)
7059         ._m22(nm22)
7060         ._m23(nm23)
7061         ._m30(nm30)
7062         ._m31(nm31)
7063         ._m32(nm32)
7064         ._m33(m33)
7065         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
7066         return dest;
7067     }
7068 
7069     /**
7070      * Pre-multiply the rotation - and possibly scaling - transformation of the given {@link Quaterniond} to this matrix.
7071      * <p>
7072      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7073      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7074      * When used with a left-handed coordinate system, the rotation is clockwise.
7075      * <p>
7076      * If <code>M</code> is <code>this</code> matrix and <code>Q</code> the rotation matrix obtained from the given quaternion,
7077      * then the new matrix will be <code>Q * M</code>. So when transforming a
7078      * vector <code>v</code> with the new matrix by using <code>Q * M * v</code>,
7079      * the quaternion rotation will be applied last!
7080      * <p>
7081      * In order to set the matrix to a rotation transformation without pre-multiplying,
7082      * use {@link #rotation(ref Quaterniond)}.
7083      * <p>
7084      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Quaternion">http://en.wikipedia.org</a>
7085      * 
7086      * @see #rotation(ref Quaterniond)
7087      * 
7088      * @param quat
7089      *          the {@link Quaterniond}
7090      * @return this
7091      */
7092     ref public Matrix4d rotateLocal(ref Quaterniond quat) return {
7093         rotateLocal(quat, this);
7094         return this;
7095     }
7096 
7097 
7098     /**
7099      * Apply a rotation transformation, rotating about the given {@link AxisAngle4d}, to this matrix.
7100      * <p>
7101      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7102      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7103      * When used with a left-handed coordinate system, the rotation is clockwise.
7104      * <p>
7105      * If <code>M</code> is <code>this</code> matrix and <code>A</code> the rotation matrix obtained from the given {@link AxisAngle4d},
7106      * then the new matrix will be <code>M * A</code>. So when transforming a
7107      * vector <code>v</code> with the new matrix by using <code>M * A * v</code>,
7108      * the {@link AxisAngle4d} rotation will be applied first!
7109      * <p>
7110      * In order to set the matrix to a rotation transformation without post-multiplying,
7111      * use {@link #rotation(ref AxisAngle4d)}.
7112      * <p>
7113      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
7114      * 
7115      * @see #rotate(double, double, double, double)
7116      * @see #rotation(ref AxisAngle4d)
7117      * 
7118      * @param axisAngle
7119      *          the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized})
7120      * @return this
7121      */
7122     ref public Matrix4d rotate(ref AxisAngle4d axisAngle) return {
7123         return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z);
7124     }
7125 
7126     /**
7127      * Apply a rotation transformation, rotating about the given {@link AxisAngle4d} and store the result in <code>dest</code>.
7128      * <p>
7129      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7130      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7131      * When used with a left-handed coordinate system, the rotation is clockwise.
7132      * <p>
7133      * If <code>M</code> is <code>this</code> matrix and <code>A</code> the rotation matrix obtained from the given {@link AxisAngle4d},
7134      * then the new matrix will be <code>M * A</code>. So when transforming a
7135      * vector <code>v</code> with the new matrix by using <code>M * A * v</code>,
7136      * the {@link AxisAngle4d} rotation will be applied first!
7137      * <p>
7138      * In order to set the matrix to a rotation transformation without post-multiplying,
7139      * use {@link #rotation(ref AxisAngle4d)}.
7140      * <p>
7141      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
7142      * 
7143      * @see #rotate(double, double, double, double)
7144      * @see #rotation(ref AxisAngle4d)
7145      * 
7146      * @param axisAngle
7147      *          the {@link AxisAngle4d} (needs to be {@link AxisAngle4d#normalize() normalized})
7148      * @param dest
7149      *          will hold the result
7150      * @return dest
7151      */
7152     public Matrix4d rotate(ref AxisAngle4d axisAngle, ref Matrix4d dest) {
7153         return rotate(axisAngle.angle, axisAngle.x, axisAngle.y, axisAngle.z, dest);
7154     }
7155 
7156     /**
7157      * Apply a rotation transformation, rotating the given radians about the specified axis, to this matrix.
7158      * <p>
7159      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7160      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7161      * When used with a left-handed coordinate system, the rotation is clockwise.
7162      * <p>
7163      * If <code>M</code> is <code>this</code> matrix and <code>A</code> the rotation matrix obtained from the given angle and axis,
7164      * then the new matrix will be <code>M * A</code>. So when transforming a
7165      * vector <code>v</code> with the new matrix by using <code>M * A * v</code>,
7166      * the axis-angle rotation will be applied first!
7167      * <p>
7168      * In order to set the matrix to a rotation transformation without post-multiplying,
7169      * use {@link #rotation(double, Vector3d)}.
7170      * <p>
7171      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
7172      * 
7173      * @see #rotate(double, double, double, double)
7174      * @see #rotation(double, Vector3d)
7175      * 
7176      * @param angle
7177      *          the angle in radians
7178      * @param axis
7179      *          the rotation axis (needs to be {@link Vector3d#normalize() normalized})
7180      * @return this
7181      */
7182     ref public Matrix4d rotate(double angle, Vector3d axis) return {
7183         return rotate(angle, axis.x, axis.y, axis.z);
7184     }
7185 
7186     /**
7187      * Apply a rotation transformation, rotating the given radians about the specified axis and store the result in <code>dest</code>.
7188      * <p>
7189      * When used with a right-handed coordinate system, the produced rotation will rotate a vector 
7190      * counter-clockwise around the rotation axis, when viewing along the negative axis direction towards the origin.
7191      * When used with a left-handed coordinate system, the rotation is clockwise.
7192      * <p>
7193      * If <code>M</code> is <code>this</code> matrix and <code>A</code> the rotation matrix obtained from the given angle and axis,
7194      * then the new matrix will be <code>M * A</code>. So when transforming a
7195      * vector <code>v</code> with the new matrix by using <code>M * A * v</code>,
7196      * the axis-angle rotation will be applied first!
7197      * <p>
7198      * In order to set the matrix to a rotation transformation without post-multiplying,
7199      * use {@link #rotation(double, Vector3d)}.
7200      * <p>
7201      * Reference: <a href="http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle">http://en.wikipedia.org</a>
7202      * 
7203      * @see #rotate(double, double, double, double)
7204      * @see #rotation(double, Vector3d)
7205      * 
7206      * @param angle
7207      *          the angle in radians
7208      * @param axis
7209      *          the rotation axis (needs to be {@link Vector3d#normalize() normalized})
7210      * @param dest
7211      *          will hold the result
7212      * @return dest
7213      */
7214     public Matrix4d rotate(double angle, Vector3d axis, ref Matrix4d dest) {
7215         return rotate(angle, axis.x, axis.y, axis.z, dest);
7216     }
7217 
7218     public Vector4d getRow(int row, ref Vector4d dest) {
7219         switch (row) {
7220         case 0:
7221             dest.x = m00;
7222             dest.y = m10;
7223             dest.z = m20;
7224             dest.w = m30;
7225             break;
7226         case 1:
7227             dest.x = m01;
7228             dest.y = m11;
7229             dest.z = m21;
7230             dest.w = m31;
7231             break;
7232         case 2:
7233             dest.x = m02;
7234             dest.y = m12;
7235             dest.z = m22;
7236             dest.w = m32;
7237             break;
7238         case 3:
7239             dest.x = m03;
7240             dest.y = m13;
7241             dest.z = m23;
7242             dest.w = m33;
7243             break;
7244         default: {}
7245         }
7246         return dest;
7247     }
7248 
7249     public Vector3d getRow(int row, ref Vector3d dest){
7250         switch (row) {
7251         case 0:
7252             dest.x = m00;
7253             dest.y = m10;
7254             dest.z = m20;
7255             break;
7256         case 1:
7257             dest.x = m01;
7258             dest.y = m11;
7259             dest.z = m21;
7260             break;
7261         case 2:
7262             dest.x = m02;
7263             dest.y = m12;
7264             dest.z = m22;
7265             break;
7266         case 3:
7267             dest.x = m03;
7268             dest.y = m13;
7269             dest.z = m23;
7270             break;
7271         default: {}
7272         }
7273         return dest;
7274     }
7275 
7276     /**
7277      * Set the row at the given <code>row</code> index, starting with <code>0</code>.
7278      * 
7279      * @param row
7280      *          the row index in <code>[0..3]</code>
7281      * @param src
7282      *          the row components to set
7283      * @return this
7284      * @throws IndexOutOfBoundsException if <code>row</code> is not in <code>[0..3]</code>
7285      */
7286     ref public Matrix4d setRow(int row, Vector4d src) return {
7287         switch (row) {
7288         case 0:
7289             return _m00(src.x)._m10(src.y)._m20(src.z)._m30(src.w)._properties(0);
7290         case 1:
7291             return _m01(src.x)._m11(src.y)._m21(src.z)._m31(src.w)._properties(0);
7292         case 2:
7293             return _m02(src.x)._m12(src.y)._m22(src.z)._m32(src.w)._properties(0);
7294         case 3:
7295             return _m03(src.x)._m13(src.y)._m23(src.z)._m33(src.w)._properties(0);
7296         default:
7297             return this;
7298         }
7299     }
7300 
7301     public Vector4d getColumn(int column, ref Vector4d dest) {
7302         switch (column) {
7303         case 0:
7304             dest.x = m00;
7305             dest.y = m01;
7306             dest.z = m02;
7307             dest.w = m03;
7308             break;
7309         case 1:
7310             dest.x = m10;
7311             dest.y = m11;
7312             dest.z = m12;
7313             dest.w = m13;
7314             break;
7315         case 2:
7316             dest.x = m20;
7317             dest.y = m21;
7318             dest.z = m22;
7319             dest.w = m23;
7320             break;
7321         case 3:
7322             dest.x = m30;
7323             dest.y = m31;
7324             dest.z = m32;
7325             dest.w = m33;
7326             break;
7327         default: {}
7328         }
7329         return dest;
7330     }
7331 
7332     public Vector3d getColumn(int column, ref Vector3d dest) {
7333         switch (column) {
7334         case 0:
7335             dest.x = m00;
7336             dest.y = m01;
7337             dest.z = m02;
7338             break;
7339         case 1:
7340             dest.x = m10;
7341             dest.y = m11;
7342             dest.z = m12;
7343             break;
7344         case 2:
7345             dest.x = m20;
7346             dest.y = m21;
7347             dest.z = m22;
7348             break;
7349         case 3:
7350             dest.x = m30;
7351             dest.y = m31;
7352             dest.z = m32;
7353             break;
7354         default: {}
7355         }
7356         return dest;
7357     }
7358 
7359     /**
7360      * Set the column at the given <code>column</code> index, starting with <code>0</code>.
7361      * 
7362      * @param column
7363      *          the column index in <code>[0..3]</code>
7364      * @param src
7365      *          the column components to set
7366      * @return this
7367      * @throws IndexOutOfBoundsException if <code>column</code> is not in <code>[0..3]</code>
7368      */
7369     ref public Matrix4d setColumn(int column, Vector4d src) return {
7370         switch (column) {
7371         case 0:
7372             return _m00(src.x)._m01(src.y)._m02(src.z)._m03(src.w)._properties(0);
7373         case 1:
7374             return _m10(src.x)._m11(src.y)._m12(src.z)._m13(src.w)._properties(0);
7375         case 2:
7376             return _m20(src.x)._m21(src.y)._m22(src.z)._m23(src.w)._properties(0);
7377         case 3:
7378             return _m30(src.x)._m31(src.y)._m32(src.z)._m33(src.w)._properties(0);
7379         default:
7380             return this;
7381         }
7382     }
7383 
7384     public double get(int column, int row) {
7385         return MemUtil.get(this, column, row);
7386     }
7387 
7388     /**
7389      * Set the matrix element at the given column and row to the specified value.
7390      * 
7391      * @param column
7392      *          the colum index in <code>[0..3]</code>
7393      * @param row
7394      *          the row index in <code>[0..3]</code>
7395      * @param value
7396      *          the value
7397      * @return this
7398      */
7399     ref public Matrix4d set(int column, int row, double value) return {
7400         MemUtil.set(this, column, row, value);
7401         return this;
7402     }
7403 
7404     public double getRowColumn(int row, int column) {
7405         return MemUtil.get(this, column, row);
7406     }
7407 
7408     /**
7409      * Set the matrix element at the given row and column to the specified value.
7410      * 
7411      * @param row
7412      *          the row index in <code>[0..3]</code>
7413      * @param column
7414      *          the colum index in <code>[0..3]</code>
7415      * @param value
7416      *          the value
7417      * @return this
7418      */
7419     ref public Matrix4d setRowColumn(int row, int column, double value) return {
7420         MemUtil.set(this, column, row, value);
7421         return this;
7422     }
7423 
7424     /**
7425      * Compute a normal matrix from the upper left 3x3 submatrix of <code>this</code>
7426      * and store it into the upper left 3x3 submatrix of <code>this</code>.
7427      * All other values of <code>this</code> will be set to {@link #identity() identity}.
7428      * <p>
7429      * The normal matrix of <code>m</code> is the transpose of the inverse of <code>m</code>.
7430      * <p>
7431      * Please note that, if <code>this</code> is an orthogonal matrix or a matrix whose columns are orthogonal vectors, 
7432      * then this method <i>need not</i> be invoked, since in that case <code>this</code> itself is its normal matrix.
7433      * In that case, use {@link #set3x3(Matrix4d)} to set a given Matrix4f to only the upper left 3x3 submatrix
7434      * of this matrix.
7435      * 
7436      * @see #set3x3(Matrix4d)
7437      * 
7438      * @return this
7439      */
7440     ref public Matrix4d normal() return {
7441         normal(this);
7442         return this;
7443     }
7444 
7445     /**
7446      * Compute a normal matrix from the upper left 3x3 submatrix of <code>this</code>
7447      * and store it into the upper left 3x3 submatrix of <code>dest</code>.
7448      * All other values of <code>dest</code> will be set to {@link #identity() identity}.
7449      * <p>
7450      * The normal matrix of <code>m</code> is the transpose of the inverse of <code>m</code>.
7451      * <p>
7452      * Please note that, if <code>this</code> is an orthogonal matrix or a matrix whose columns are orthogonal vectors, 
7453      * then this method <i>need not</i> be invoked, since in that case <code>this</code> itself is its normal matrix.
7454      * In that case, use {@link #set3x3(Matrix4d)} to set a given Matrix4d to only the upper left 3x3 submatrix
7455      * of a given matrix.
7456      * 
7457      * @see #set3x3(Matrix4d)
7458      * 
7459      * @param dest
7460      *             will hold the result
7461      * @return dest
7462      */
7463     public Matrix4d normal(ref Matrix4d dest) {
7464         if ((properties & PROPERTY_IDENTITY) != 0)
7465             return dest.identity();
7466         else if ((properties & PROPERTY_ORTHONORMAL) != 0)
7467             return normalOrthonormal(dest);
7468         return normalGeneric(dest);
7469     }
7470     private Matrix4d normalOrthonormal(ref Matrix4d dest) {
7471         if (dest != this)
7472             dest.set(this);
7473         return dest._properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
7474     }
7475     private Matrix4d normalGeneric(ref Matrix4d dest) {
7476         double m00m11 = m00 * m11;
7477         double m01m10 = m01 * m10;
7478         double m02m10 = m02 * m10;
7479         double m00m12 = m00 * m12;
7480         double m01m12 = m01 * m12;
7481         double m02m11 = m02 * m11;
7482         double det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20;
7483         double s = 1.0 / det;
7484         /* Invert and transpose in one go */
7485         double nm00 = (m11 * m22 - m21 * m12) * s;
7486         double nm01 = (m20 * m12 - m10 * m22) * s;
7487         double nm02 = (m10 * m21 - m20 * m11) * s;
7488         double nm10 = (m21 * m02 - m01 * m22) * s;
7489         double nm11 = (m00 * m22 - m20 * m02) * s;
7490         double nm12 = (m20 * m01 - m00 * m21) * s;
7491         double nm20 = (m01m12 - m02m11) * s;
7492         double nm21 = (m02m10 - m00m12) * s;
7493         double nm22 = (m00m11 - m01m10) * s;
7494         return dest
7495         ._m00(nm00)
7496         ._m01(nm01)
7497         ._m02(nm02)
7498         ._m03(0.0)
7499         ._m10(nm10)
7500         ._m11(nm11)
7501         ._m12(nm12)
7502         ._m13(0.0)
7503         ._m20(nm20)
7504         ._m21(nm21)
7505         ._m22(nm22)
7506         ._m23(0.0)
7507         ._m30(0.0)
7508         ._m31(0.0)
7509         ._m32(0.0)
7510         ._m33(1.0)
7511         ._properties((properties | PROPERTY_AFFINE) & ~(PROPERTY_TRANSLATION | PROPERTY_PERSPECTIVE));
7512     }
7513 
7514     /**
7515      * Compute a normal matrix from the upper left 3x3 submatrix of <code>this</code>
7516      * and store it into <code>dest</code>.
7517      * <p>
7518      * The normal matrix of <code>m</code> is the transpose of the inverse of <code>m</code>.
7519      * <p>
7520      * Please note that, if <code>this</code> is an orthogonal matrix or a matrix whose columns are orthogonal vectors, 
7521      * then this method <i>need not</i> be invoked, since in that case <code>this</code> itself is its normal matrix.
7522      * In that case, use {@link Matrix3d#set(Matrix4d)} to set a given Matrix3d to only the upper left 3x3 submatrix
7523      * of this matrix.
7524      * 
7525      * @see Matrix3d#set(Matrix4d)
7526      * @see #get3x3(Matrix3d)
7527      * 
7528      * @param dest
7529      *             will hold the result
7530      * @return dest
7531      */
7532     public Matrix3d normal(ref Matrix3d dest) {
7533         if ((properties & PROPERTY_ORTHONORMAL) != 0)
7534             return normalOrthonormal(dest);
7535         return normalGeneric(dest);
7536     }
7537     private Matrix3d normalOrthonormal(ref Matrix3d dest) {
7538         dest.set(this);
7539         return dest;
7540     }
7541     private Matrix3d normalGeneric(ref Matrix3d dest) {
7542         double m00m11 = m00 * m11;
7543         double m01m10 = m01 * m10;
7544         double m02m10 = m02 * m10;
7545         double m00m12 = m00 * m12;
7546         double m01m12 = m01 * m12;
7547         double m02m11 = m02 * m11;
7548         double det = (m00m11 - m01m10) * m22 + (m02m10 - m00m12) * m21 + (m01m12 - m02m11) * m20;
7549         double s = 1.0 / det;
7550         /* Invert and transpose in one go */
7551         return dest._m00((m11 * m22 - m21 * m12) * s)
7552         ._m01((m20 * m12 - m10 * m22) * s)
7553         ._m02((m10 * m21 - m20 * m11) * s)
7554         ._m10((m21 * m02 - m01 * m22) * s)
7555         ._m11((m00 * m22 - m20 * m02) * s)
7556         ._m12((m20 * m01 - m00 * m21) * s)
7557         ._m20((m01m12 - m02m11) * s)
7558         ._m21((m02m10 - m00m12) * s)
7559         ._m22((m00m11 - m01m10) * s);
7560     }
7561 
7562     /**
7563      * Compute the cofactor matrix of the upper left 3x3 submatrix of <code>this</code>.
7564      * <p>
7565      * The cofactor matrix can be used instead of {@link #normal()} to transform normals
7566      * when the orientation of the normals with respect to the surface should be preserved.
7567      * 
7568      * @return this
7569      */
7570     ref public Matrix4d cofactor3x3() return {
7571         cofactor3x3(this);
7572         return this;
7573     }
7574 
7575     /**
7576      * Compute the cofactor matrix of the upper left 3x3 submatrix of <code>this</code>
7577      * and store it into <code>dest</code>.
7578      * <p>
7579      * The cofactor matrix can be used instead of {@link #normal(Matrix3d)} to transform normals
7580      * when the orientation of the normals with respect to the surface should be preserved.
7581      * 
7582      * @param dest
7583      *             will hold the result
7584      * @return dest
7585      */
7586     public Matrix3d cofactor3x3(ref Matrix3d dest) {
7587         return dest._m00(m11 * m22 - m21 * m12)
7588         ._m01(m20 * m12 - m10 * m22)
7589         ._m02(m10 * m21 - m20 * m11)
7590         ._m10(m21 * m02 - m01 * m22)
7591         ._m11(m00 * m22 - m20 * m02)
7592         ._m12(m20 * m01 - m00 * m21)
7593         ._m20(m01 * m12 - m02 * m11)
7594         ._m21(m02 * m10 - m00 * m12)
7595         ._m22(m00 * m11 - m01 * m10);
7596     }
7597 
7598     /**
7599      * Compute the cofactor matrix of the upper left 3x3 submatrix of <code>this</code>
7600      * and store it into <code>dest</code>.
7601      * All other values of <code>dest</code> will be set to {@link #identity() identity}.
7602      * <p>
7603      * The cofactor matrix can be used instead of {@link #normal(Matrix4d)} to transform normals
7604      * when the orientation of the normals with respect to the surface should be preserved.
7605      * 
7606      * @param dest
7607      *             will hold the result
7608      * @return dest
7609      */
7610     public Matrix4d cofactor3x3(ref Matrix4d dest) {
7611         double nm10 = m21 * m02 - m01 * m22;
7612         double nm11 = m00 * m22 - m20 * m02;
7613         double nm12 = m20 * m01 - m00 * m21;
7614         double nm20 = m01 * m12 - m11 * m02;
7615         double nm21 = m02 * m10 - m12 * m00;
7616         double nm22 = m00 * m11 - m10 * m01;
7617         return dest
7618         ._m00(m11 * m22 - m21 * m12)
7619         ._m01(m20 * m12 - m10 * m22)
7620         ._m02(m10 * m21 - m20 * m11)
7621         ._m03(0.0)
7622         ._m10(nm10)
7623         ._m11(nm11)
7624         ._m12(nm12)
7625         ._m13(0.0)
7626         ._m20(nm20)
7627         ._m21(nm21)
7628         ._m22(nm22)
7629         ._m23(0.0)
7630         ._m30(0.0)
7631         ._m31(0.0)
7632         ._m32(0.0)
7633         ._m33(1.0)
7634         ._properties((properties | PROPERTY_AFFINE) & ~(PROPERTY_TRANSLATION | PROPERTY_PERSPECTIVE));
7635     }
7636 
7637     /**
7638      * Normalize the upper left 3x3 submatrix of this matrix.
7639      * <p>
7640      * The resulting matrix will map unit vectors to unit vectors, though a pair of orthogonal input unit
7641      * vectors need not be mapped to a pair of orthogonal output vectors if the original matrix was not orthogonal itself
7642      * (i.e. had <i>skewing</i>).
7643      * 
7644      * @return this
7645      */
7646     ref public Matrix4d normalize3x3() return {
7647         normalize3x3(this);
7648         return this;
7649     }
7650 
7651     public Matrix4d normalize3x3(ref Matrix4d dest) {
7652         double invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02);
7653         double invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12);
7654         double invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22);
7655         dest._m00(m00 * invXlen)._m01(m01 * invXlen)._m02(m02 * invXlen)
7656             ._m10(m10 * invYlen)._m11(m11 * invYlen)._m12(m12 * invYlen)
7657             ._m20(m20 * invZlen)._m21(m21 * invZlen)._m22(m22 * invZlen)
7658             ._m30(m30)._m31(m31)._m32(m32)._m33(m33)
7659             ._properties(properties);
7660         return dest;
7661     }
7662 
7663     public Matrix3d normalize3x3(ref Matrix3d dest) {
7664         double invXlen = Math.invsqrt(m00 * m00 + m01 * m01 + m02 * m02);
7665         double invYlen = Math.invsqrt(m10 * m10 + m11 * m11 + m12 * m12);
7666         double invZlen = Math.invsqrt(m20 * m20 + m21 * m21 + m22 * m22);
7667         dest.m00 = (m00 * invXlen);
7668         dest.m01 = (m01 * invXlen);
7669         dest.m02 = (m02 * invXlen);
7670 
7671         dest.m10 = (m10 * invYlen);
7672         dest.m11 = (m11 * invYlen);
7673         dest.m12 = (m12 * invYlen);
7674 
7675         dest.m20 = (m20 * invZlen);
7676         dest.m21 = (m21 * invZlen);
7677         dest.m22 = (m22 * invZlen);
7678         return dest;
7679     }
7680 
7681     public Vector4d unproject(double winX, double winY, double winZ, int[] viewport, ref Vector4d dest) {
7682         double a = m00 * m11 - m01 * m10;
7683         double b = m00 * m12 - m02 * m10;
7684         double c = m00 * m13 - m03 * m10;
7685         double d = m01 * m12 - m02 * m11;
7686         double e = m01 * m13 - m03 * m11;
7687         double f = m02 * m13 - m03 * m12;
7688         double g = m20 * m31 - m21 * m30;
7689         double h = m20 * m32 - m22 * m30;
7690         double i = m20 * m33 - m23 * m30;
7691         double j = m21 * m32 - m22 * m31;
7692         double k = m21 * m33 - m23 * m31;
7693         double l = m22 * m33 - m23 * m32;
7694         double det = a * l - b * k + c * j + d * i - e * h + f * g;
7695         det = 1.0 / det;
7696         double im00 = ( m11 * l - m12 * k + m13 * j) * det;
7697         double im01 = (-m01 * l + m02 * k - m03 * j) * det;
7698         double im02 = ( m31 * f - m32 * e + m33 * d) * det;
7699         double im03 = (-m21 * f + m22 * e - m23 * d) * det;
7700         double im10 = (-m10 * l + m12 * i - m13 * h) * det;
7701         double im11 = ( m00 * l - m02 * i + m03 * h) * det;
7702         double im12 = (-m30 * f + m32 * c - m33 * b) * det;
7703         double im13 = ( m20 * f - m22 * c + m23 * b) * det;
7704         double im20 = ( m10 * k - m11 * i + m13 * g) * det;
7705         double im21 = (-m00 * k + m01 * i - m03 * g) * det;
7706         double im22 = ( m30 * e - m31 * c + m33 * a) * det;
7707         double im23 = (-m20 * e + m21 * c - m23 * a) * det;
7708         double im30 = (-m10 * j + m11 * h - m12 * g) * det;
7709         double im31 = ( m00 * j - m01 * h + m02 * g) * det;
7710         double im32 = (-m30 * d + m31 * b - m32 * a) * det;
7711         double im33 = ( m20 * d - m21 * b + m22 * a) * det;
7712         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7713         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7714         double ndcZ = winZ+winZ-1.0;
7715         double invW = 1.0 / (im03 * ndcX + im13 * ndcY + im23 * ndcZ + im33);
7716         dest.x = (im00 * ndcX + im10 * ndcY + im20 * ndcZ + im30) * invW;
7717         dest.y = (im01 * ndcX + im11 * ndcY + im21 * ndcZ + im31) * invW;
7718         dest.z = (im02 * ndcX + im12 * ndcY + im22 * ndcZ + im32) * invW;
7719         dest.w = 1.0;
7720         return dest;
7721     }
7722 
7723     public Vector3d unproject(double winX, double winY, double winZ, int[] viewport, ref Vector3d dest) {
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 ndcZ = winZ+winZ-1.0;
7757         double invW = 1.0 / (im03 * ndcX + im13 * ndcY + im23 * ndcZ + im33);
7758         dest.x = (im00 * ndcX + im10 * ndcY + im20 * ndcZ + im30) * invW;
7759         dest.y = (im01 * ndcX + im11 * ndcY + im21 * ndcZ + im31) * invW;
7760         dest.z = (im02 * ndcX + im12 * ndcY + im22 * ndcZ + im32) * invW;
7761         return dest;
7762     }
7763 
7764     public Vector4d unproject(ref Vector3d winCoords, int[] viewport, ref Vector4d dest) {
7765         return unproject(winCoords.x, winCoords.y, winCoords.z, viewport, dest);
7766     }
7767 
7768     public Vector3d unproject(ref Vector3d winCoords, int[] viewport, ref Vector3d dest) {
7769         return unproject(winCoords.x, winCoords.y, winCoords.z, viewport, dest);
7770     }
7771 
7772     ref public Matrix4d unprojectRay(double winX, double winY, int[] viewport, ref Vector3d originDest, ref Vector3d dirDest) return {
7773         double a = m00 * m11 - m01 * m10;
7774         double b = m00 * m12 - m02 * m10;
7775         double c = m00 * m13 - m03 * m10;
7776         double d = m01 * m12 - m02 * m11;
7777         double e = m01 * m13 - m03 * m11;
7778         double f = m02 * m13 - m03 * m12;
7779         double g = m20 * m31 - m21 * m30;
7780         double h = m20 * m32 - m22 * m30;
7781         double i = m20 * m33 - m23 * m30;
7782         double j = m21 * m32 - m22 * m31;
7783         double k = m21 * m33 - m23 * m31;
7784         double l = m22 * m33 - m23 * m32;
7785         double det = a * l - b * k + c * j + d * i - e * h + f * g;
7786         det = 1.0 / det;
7787         double im00 = ( m11 * l - m12 * k + m13 * j) * det;
7788         double im01 = (-m01 * l + m02 * k - m03 * j) * det;
7789         double im02 = ( m31 * f - m32 * e + m33 * d) * det;
7790         double im03 = (-m21 * f + m22 * e - m23 * d) * det;
7791         double im10 = (-m10 * l + m12 * i - m13 * h) * det;
7792         double im11 = ( m00 * l - m02 * i + m03 * h) * det;
7793         double im12 = (-m30 * f + m32 * c - m33 * b) * det;
7794         double im13 = ( m20 * f - m22 * c + m23 * b) * det;
7795         double im20 = ( m10 * k - m11 * i + m13 * g) * det;
7796         double im21 = (-m00 * k + m01 * i - m03 * g) * det;
7797         double im22 = ( m30 * e - m31 * c + m33 * a) * det;
7798         double im23 = (-m20 * e + m21 * c - m23 * a) * det;
7799         double im30 = (-m10 * j + m11 * h - m12 * g) * det;
7800         double im31 = ( m00 * j - m01 * h + m02 * g) * det;
7801         double im32 = (-m30 * d + m31 * b - m32 * a) * det;
7802         double im33 = ( m20 * d - m21 * b + m22 * a) * det;
7803         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7804         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7805         double px = im00 * ndcX + im10 * ndcY + im30;
7806         double py = im01 * ndcX + im11 * ndcY + im31;
7807         double pz = im02 * ndcX + im12 * ndcY + im32;
7808         double invNearW = 1.0 / (im03 * ndcX + im13 * ndcY - im23 + im33);
7809         double nearX = (px - im20) * invNearW;
7810         double nearY = (py - im21) * invNearW;
7811         double nearZ = (pz - im22) * invNearW;
7812         double invW0 = 1.0 / (im03 * ndcX + im13 * ndcY + im33);
7813         double x0 = px * invW0;
7814         double y0 = py * invW0;
7815         double z0 = pz * invW0;
7816         originDest.x = nearX; originDest.y = nearY; originDest.z = nearZ;
7817         dirDest.x = x0 - nearX; dirDest.y = y0 - nearY; dirDest.z = z0 - nearZ;
7818         return this;
7819     }
7820 
7821     public Matrix4d unprojectRay(ref Vector2d winCoords, int[] viewport, ref Vector3d originDest, ref Vector3d dirDest) {
7822         return unprojectRay(winCoords.x, winCoords.y, viewport, originDest, dirDest);
7823     }
7824 
7825     public Vector4d unprojectInv(ref Vector3d winCoords, int[] viewport, ref Vector4d dest) {
7826         return unprojectInv(winCoords.x, winCoords.y, winCoords.z, viewport, dest);
7827     }
7828 
7829     public Vector4d unprojectInv(double winX, double winY, double winZ, int[] viewport, ref Vector4d dest) {
7830         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7831         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7832         double ndcZ = winZ+winZ-1.0;
7833         double invW = 1.0 / (m03 * ndcX + m13 * ndcY + m23 * ndcZ + m33);
7834         dest.x = (m00 * ndcX + m10 * ndcY + m20 * ndcZ + m30) * invW;
7835         dest.y = (m01 * ndcX + m11 * ndcY + m21 * ndcZ + m31) * invW;
7836         dest.z = (m02 * ndcX + m12 * ndcY + m22 * ndcZ + m32) * invW;
7837         dest.w = 1.0;
7838         return dest;
7839     }
7840 
7841     public Vector3d unprojectInv(ref Vector3d winCoords, int[] viewport, ref Vector3d dest) {
7842         return unprojectInv(winCoords.x, winCoords.y, winCoords.z, viewport, dest);
7843     }
7844 
7845     public Vector3d unprojectInv(double winX, double winY, double winZ, int[] viewport, ref Vector3d dest) {
7846         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7847         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7848         double ndcZ = winZ+winZ-1.0;
7849         double invW = 1.0 / (m03 * ndcX + m13 * ndcY + m23 * ndcZ + m33);
7850         dest.x = (m00 * ndcX + m10 * ndcY + m20 * ndcZ + m30) * invW;
7851         dest.y = (m01 * ndcX + m11 * ndcY + m21 * ndcZ + m31) * invW;
7852         dest.z = (m02 * ndcX + m12 * ndcY + m22 * ndcZ + m32) * invW;
7853         return dest;
7854     }
7855 
7856     public Matrix4d unprojectInvRay(ref Vector2d winCoords, int[] viewport, ref Vector3d originDest, ref Vector3d dirDest) {
7857         return unprojectInvRay(winCoords.x, winCoords.y, viewport, originDest, dirDest);
7858     }
7859 
7860     ref public Matrix4d unprojectInvRay(double winX, double winY, int[] viewport, ref Vector3d originDest, ref Vector3d dirDest) return  {
7861         double ndcX = (winX-viewport[0])/viewport[2]*2.0-1.0;
7862         double ndcY = (winY-viewport[1])/viewport[3]*2.0-1.0;
7863         double px = m00 * ndcX + m10 * ndcY + m30;
7864         double py = m01 * ndcX + m11 * ndcY + m31;
7865         double pz = m02 * ndcX + m12 * ndcY + m32;
7866         double invNearW = 1.0 / (m03 * ndcX + m13 * ndcY - m23 + m33);
7867         double nearX = (px - m20) * invNearW;
7868         double nearY = (py - m21) * invNearW;
7869         double nearZ = (pz - m22) * invNearW;
7870         double invW0 = 1.0 / (m03 * ndcX + m13 * ndcY + m33);
7871         double x0 = px * invW0;
7872         double y0 = py * invW0;
7873         double z0 = pz * invW0;
7874         originDest.x = nearX; originDest.y = nearY; originDest.z = nearZ;
7875         dirDest.x = x0 - nearX; dirDest.y = y0 - nearY; dirDest.z = z0 - nearZ;
7876         return this;
7877     }
7878 
7879     public Vector4d project(double x, double y, double z, int[] viewport, ref Vector4d winCoordsDest) {
7880         double invW = 1.0 / Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33)));
7881         double nx = Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))) * invW;
7882         double ny = Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))) * invW;
7883         double nz = Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))) * invW;
7884         return winCoordsDest.set(Math.fma(Math.fma(nx, 0.5, 0.5), viewport[2], viewport[0]),
7885                                  Math.fma(Math.fma(ny, 0.5, 0.5), viewport[3], viewport[1]),
7886                                  Math.fma(0.5, nz, 0.5),
7887                                  1.0);
7888     }
7889 
7890     public Vector3d project(double x, double y, double z, int[] viewport, ref Vector3d winCoordsDest) {
7891         double invW = 1.0 / Math.fma(m03, x, Math.fma(m13, y, Math.fma(m23, z, m33)));
7892         double nx = Math.fma(m00, x, Math.fma(m10, y, Math.fma(m20, z, m30))) * invW;
7893         double ny = Math.fma(m01, x, Math.fma(m11, y, Math.fma(m21, z, m31))) * invW;
7894         double nz = Math.fma(m02, x, Math.fma(m12, y, Math.fma(m22, z, m32))) * invW;
7895         winCoordsDest.x = Math.fma(Math.fma(nx, 0.5, 0.5), viewport[2], viewport[0]);
7896         winCoordsDest.y = Math.fma(Math.fma(ny, 0.5, 0.5), viewport[3], viewport[1]);
7897         winCoordsDest.z = Math.fma(0.5, nz, 0.5);
7898         return winCoordsDest;
7899     }
7900 
7901     public Vector4d project(ref Vector3d position, int[] viewport, ref Vector4d dest) {
7902         return project(position.x, position.y, position.z, viewport, dest);
7903     }
7904 
7905     public Vector3d project(ref Vector3d position, int[] viewport, ref Vector3d dest) {
7906         return project(position.x, position.y, position.z, viewport, dest);
7907     }
7908 
7909     public Matrix4d reflect(double a, double b, double c, double d, ref Matrix4d dest) {
7910         if ((properties & PROPERTY_IDENTITY) != 0)
7911             return dest.reflection(a, b, c, d);
7912         if ((properties & PROPERTY_IDENTITY) != 0)
7913             return dest.reflection(a, b, c, d);
7914         else if ((properties & PROPERTY_AFFINE) != 0)
7915             return reflectAffine(a, b, c, d, dest);
7916         return reflectGeneric(a, b, c, d, dest);
7917     }
7918     private Matrix4d reflectAffine(double a, double b, double c, double d, ref Matrix4d dest) {
7919         double da = a + a, db = b + b, dc = c + c, dd = d + d;
7920         double rm00 = 1.0 - da * a;
7921         double rm01 = -da * b;
7922         double rm02 = -da * c;
7923         double rm10 = -db * a;
7924         double rm11 = 1.0 - db * b;
7925         double rm12 = -db * c;
7926         double rm20 = -dc * a;
7927         double rm21 = -dc * b;
7928         double rm22 = 1.0 - dc * c;
7929         double rm30 = -dd * a;
7930         double rm31 = -dd * b;
7931         double rm32 = -dd * c;
7932         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
7933         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
7934         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
7935         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
7936         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
7937         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
7938         // matrix multiplication
7939         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
7940         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
7941         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
7942         ._m33(m33)
7943         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
7944         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
7945         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
7946         ._m23(0.0)
7947         ._m00(nm00)
7948         ._m01(nm01)
7949         ._m02(nm02)
7950         ._m03(0.0)
7951         ._m10(nm10)
7952         ._m11(nm11)
7953         ._m12(nm12)
7954         ._m13(0.0)
7955         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
7956         return dest;
7957     }
7958     private Matrix4d reflectGeneric(double a, double b, double c, double d, ref Matrix4d dest) {
7959         double da = a + a, db = b + b, dc = c + c, dd = d + d;
7960         double rm00 = 1.0 - da * a;
7961         double rm01 = -da * b;
7962         double rm02 = -da * c;
7963         double rm10 = -db * a;
7964         double rm11 = 1.0 - db * b;
7965         double rm12 = -db * c;
7966         double rm20 = -dc * a;
7967         double rm21 = -dc * b;
7968         double rm22 = 1.0 - dc * c;
7969         double rm30 = -dd * a;
7970         double rm31 = -dd * b;
7971         double rm32 = -dd * c;
7972         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
7973         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
7974         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
7975         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
7976         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
7977         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
7978         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
7979         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
7980         // matrix multiplication
7981         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
7982         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
7983         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
7984         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
7985         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
7986         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
7987         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
7988         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
7989         ._m00(nm00)
7990         ._m01(nm01)
7991         ._m02(nm02)
7992         ._m03(nm03)
7993         ._m10(nm10)
7994         ._m11(nm11)
7995         ._m12(nm12)
7996         ._m13(nm13)
7997         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
7998         return dest;
7999     }
8000 
8001     /**
8002      * Apply a mirror/reflection transformation to this matrix that reflects about the given plane
8003      * specified via the equation <code>x*a + y*b + z*c + d = 0</code>.
8004      * <p>
8005      * The vector <code>(a, b, c)</code> must be a unit vector.
8006      * <p>
8007      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the reflection matrix,
8008      * then the new matrix will be <code>M * R</code>. So when transforming a
8009      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
8010      * reflection will be applied first!
8011      * <p>
8012      * Reference: <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb281733(v=vs.85).aspx">msdn.microsoft.com</a>
8013      * 
8014      * @param a
8015      *          the x factor in the plane equation
8016      * @param b
8017      *          the y factor in the plane equation
8018      * @param c
8019      *          the z factor in the plane equation
8020      * @param d
8021      *          the constant in the plane equation
8022      * @return this
8023      */
8024     ref public Matrix4d reflect(double a, double b, double c, double d) return {
8025         reflect(a, b, c, d, this);
8026         return this;
8027     }
8028 
8029     /**
8030      * Apply a mirror/reflection transformation to this matrix that reflects about the given plane
8031      * specified via the plane normal and a point on the plane.
8032      * <p>
8033      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the reflection matrix,
8034      * then the new matrix will be <code>M * R</code>. So when transforming a
8035      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
8036      * reflection will be applied first!
8037      * 
8038      * @param nx
8039      *          the x-coordinate of the plane normal
8040      * @param ny
8041      *          the y-coordinate of the plane normal
8042      * @param nz
8043      *          the z-coordinate of the plane normal
8044      * @param px
8045      *          the x-coordinate of a point on the plane
8046      * @param py
8047      *          the y-coordinate of a point on the plane
8048      * @param pz
8049      *          the z-coordinate of a point on the plane
8050      * @return this
8051      */
8052     ref public Matrix4d reflect(double nx, double ny, double nz, double px, double py, double pz) return {
8053         reflect(nx, ny, nz, px, py, pz, this);
8054         return this;
8055     }
8056 
8057     public Matrix4d reflect(double nx, double ny, double nz, double px, double py, double pz, ref Matrix4d dest) {
8058         double invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz);
8059         double nnx = nx * invLength;
8060         double nny = ny * invLength;
8061         double nnz = nz * invLength;
8062         /* See: http://mathworld.wolfram.com/Plane.html */
8063         return reflect(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz, dest);
8064     }
8065 
8066     /**
8067      * Apply a mirror/reflection transformation to this matrix that reflects about the given plane
8068      * specified via the plane normal and a point on the plane.
8069      * <p>
8070      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the reflection matrix,
8071      * then the new matrix will be <code>M * R</code>. So when transforming a
8072      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
8073      * reflection will be applied first!
8074      * 
8075      * @param normal
8076      *          the plane normal
8077      * @param point
8078      *          a point on the plane
8079      * @return this
8080      */
8081     ref public Matrix4d reflect(ref Vector3d normal, Vector3d point) return {
8082         return reflect(normal.x, normal.y, normal.z, point.x, point.y, point.z);
8083     }
8084 
8085     /**
8086      * Apply a mirror/reflection transformation to this matrix that reflects about a plane
8087      * specified via the plane orientation and a point on the plane.
8088      * <p>
8089      * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene.
8090      * It is assumed that the default mirror plane's normal is <code>(0, 0, 1)</code>. So, if the given {@link Quaterniond} is
8091      * the identity (does not apply any additional rotation), the reflection plane will be <code>z=0</code>, offset by the given <code>point</code>.
8092      * <p>
8093      * If <code>M</code> is <code>this</code> matrix and <code>R</code> the reflection matrix,
8094      * then the new matrix will be <code>M * R</code>. So when transforming a
8095      * vector <code>v</code> with the new matrix by using <code>M * R * v</code>, the
8096      * reflection will be applied first!
8097      * 
8098      * @param orientation
8099      *          the plane orientation relative to an implied normal vector of <code>(0, 0, 1)</code>
8100      * @param point
8101      *          a point on the plane
8102      * @return this
8103      */
8104     ref public Matrix4d reflect(ref Quaterniond orientation, Vector3d point) return {
8105         reflect(orientation, point, this);
8106         return this;
8107     }
8108 
8109     public Matrix4d reflect(ref Quaterniond orientation, Vector3d point, ref Matrix4d dest) {
8110         double num1 = orientation.x + orientation.x;
8111         double num2 = orientation.y + orientation.y;
8112         double num3 = orientation.z + orientation.z;
8113         double normalX = orientation.x * num3 + orientation.w * num2;
8114         double normalY = orientation.y * num3 - orientation.w * num1;
8115         double normalZ = 1.0 - (orientation.x * num1 + orientation.y * num2);
8116         return reflect(normalX, normalY, normalZ, point.x, point.y, point.z, dest);
8117     }
8118 
8119     public Matrix4d reflect(ref Vector3d normal, Vector3d point, ref Matrix4d dest) {
8120         return reflect(normal.x, normal.y, normal.z, point.x, point.y, point.z, dest);
8121     }
8122 
8123     /**
8124      * Set this matrix to a mirror/reflection transformation that reflects about the given plane
8125      * specified via the equation <code>x*a + y*b + z*c + d = 0</code>.
8126      * <p>
8127      * The vector <code>(a, b, c)</code> must be a unit vector.
8128      * <p>
8129      * Reference: <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb281733(v=vs.85).aspx">msdn.microsoft.com</a>
8130      * 
8131      * @param a
8132      *          the x factor in the plane equation
8133      * @param b
8134      *          the y factor in the plane equation
8135      * @param c
8136      *          the z factor in the plane equation
8137      * @param d
8138      *          the constant in the plane equation
8139      * @return this
8140      */
8141     ref public Matrix4d reflection(double a, double b, double c, double d) return {
8142         double da = a + a, db = b + b, dc = c + c, dd = d + d;
8143         _m00(1.0 - da * a).
8144         _m01(-da * b).
8145         _m02(-da * c).
8146         _m03(0.0).
8147         _m10(-db * a).
8148         _m11(1.0 - db * b).
8149         _m12(-db * c).
8150         _m13(0.0).
8151         _m20(-dc * a).
8152         _m21(-dc * b).
8153         _m22(1.0 - dc * c).
8154         _m23(0.0).
8155         _m30(-dd * a).
8156         _m31(-dd * b).
8157         _m32(-dd * c).
8158         _m33(1.0).
8159         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
8160         return this;
8161     }
8162 
8163     /**
8164      * Set this matrix to a mirror/reflection transformation that reflects about the given plane
8165      * specified via the plane normal and a point on the plane.
8166      * 
8167      * @param nx
8168      *          the x-coordinate of the plane normal
8169      * @param ny
8170      *          the y-coordinate of the plane normal
8171      * @param nz
8172      *          the z-coordinate of the plane normal
8173      * @param px
8174      *          the x-coordinate of a point on the plane
8175      * @param py
8176      *          the y-coordinate of a point on the plane
8177      * @param pz
8178      *          the z-coordinate of a point on the plane
8179      * @return this
8180      */
8181     ref public Matrix4d reflection(double nx, double ny, double nz, double px, double py, double pz) return {
8182         double invLength = Math.invsqrt(nx * nx + ny * ny + nz * nz);
8183         double nnx = nx * invLength;
8184         double nny = ny * invLength;
8185         double nnz = nz * invLength;
8186         /* See: http://mathworld.wolfram.com/Plane.html */
8187         return reflection(nnx, nny, nnz, -nnx * px - nny * py - nnz * pz);
8188     }
8189 
8190     /**
8191      * Set this matrix to a mirror/reflection transformation that reflects about the given plane
8192      * specified via the plane normal and a point on the plane.
8193      * 
8194      * @param normal
8195      *          the plane normal
8196      * @param point
8197      *          a point on the plane
8198      * @return this
8199      */
8200     ref public Matrix4d reflection(ref Vector3d normal, Vector3d point) return {
8201         return reflection(normal.x, normal.y, normal.z, point.x, point.y, point.z);
8202     }
8203 
8204     /**
8205      * Set this matrix to a mirror/reflection transformation that reflects about a plane
8206      * specified via the plane orientation and a point on the plane.
8207      * <p>
8208      * This method can be used to build a reflection transformation based on the orientation of a mirror object in the scene.
8209      * It is assumed that the default mirror plane's normal is <code>(0, 0, 1)</code>. So, if the given {@link Quaterniond} is
8210      * the identity (does not apply any additional rotation), the reflection plane will be <code>z=0</code>, offset by the given <code>point</code>.
8211      * 
8212      * @param orientation
8213      *          the plane orientation
8214      * @param point
8215      *          a point on the plane
8216      * @return this
8217      */
8218     ref public Matrix4d reflection(ref Quaterniond orientation, Vector3d point) return {
8219         double num1 = orientation.x + orientation.x;
8220         double num2 = orientation.y + orientation.y;
8221         double num3 = orientation.z + orientation.z;
8222         double normalX = orientation.x * num3 + orientation.w * num2;
8223         double normalY = orientation.y * num3 - orientation.w * num1;
8224         double normalZ = 1.0 - (orientation.x * num1 + orientation.y * num2);
8225         return reflection(normalX, normalY, normalZ, point.x, point.y, point.z);
8226     }
8227 
8228     /**
8229      * Apply an orthographic projection transformation for a right-handed coordinate system
8230      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
8231      * <p>
8232      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8233      * then the new matrix will be <code>M * O</code>. So when transforming a
8234      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8235      * orthographic projection transformation will be applied first!
8236      * <p>
8237      * In order to set the matrix to an orthographic projection without post-multiplying it,
8238      * use {@link #setOrtho(double, double, double, double, double, double, bool) setOrtho()}.
8239      * <p>
8240      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8241      * 
8242      * @see #setOrtho(double, double, double, double, double, double, bool)
8243      * 
8244      * @param left
8245      *            the distance from the center to the left frustum edge
8246      * @param right
8247      *            the distance from the center to the right frustum edge
8248      * @param bottom
8249      *            the distance from the center to the bottom frustum edge
8250      * @param top
8251      *            the distance from the center to the top frustum edge
8252      * @param zNear
8253      *            near clipping plane distance
8254      * @param zFar
8255      *            far clipping plane distance
8256      * @param zZeroToOne
8257      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8258      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8259      * @param dest
8260      *            will hold the result
8261      * @return dest
8262      */
8263     public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8264         if ((properties & PROPERTY_IDENTITY) != 0)
8265             return dest.setOrtho(left, right, bottom, top, zNear, zFar, zZeroToOne);
8266         return orthoGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest);
8267     }
8268     private Matrix4d orthoGeneric(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8269         // calculate right matrix elements
8270         double rm00 = 2.0 / (right - left);
8271         double rm11 = 2.0 / (top - bottom);
8272         double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zNear - zFar);
8273         double rm30 = (left + right) / (left - right);
8274         double rm31 = (top + bottom) / (bottom - top);
8275         double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar);
8276         // perform optimized multiplication
8277         // compute the last column first, because other columns do not depend on it
8278         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
8279         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
8280         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
8281         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
8282         ._m00(m00 * rm00)
8283         ._m01(m01 * rm00)
8284         ._m02(m02 * rm00)
8285         ._m03(m03 * rm00)
8286         ._m10(m10 * rm11)
8287         ._m11(m11 * rm11)
8288         ._m12(m12 * rm11)
8289         ._m13(m13 * rm11)
8290         ._m20(m20 * rm22)
8291         ._m21(m21 * rm22)
8292         ._m22(m22 * rm22)
8293         ._m23(m23 * rm22)
8294         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
8295         return dest;
8296     }
8297 
8298     /**
8299      * Apply an orthographic projection transformation for a right-handed coordinate system
8300      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
8301      * <p>
8302      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8303      * then the new matrix will be <code>M * O</code>. So when transforming a
8304      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8305      * orthographic projection transformation will be applied first!
8306      * <p>
8307      * In order to set the matrix to an orthographic projection without post-multiplying it,
8308      * use {@link #setOrtho(double, double, double, double, double, double) setOrtho()}.
8309      * <p>
8310      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8311      * 
8312      * @see #setOrtho(double, double, double, double, double, double)
8313      * 
8314      * @param left
8315      *            the distance from the center to the left frustum edge
8316      * @param right
8317      *            the distance from the center to the right frustum edge
8318      * @param bottom
8319      *            the distance from the center to the bottom frustum edge
8320      * @param top
8321      *            the distance from the center to the top frustum edge
8322      * @param zNear
8323      *            near clipping plane distance
8324      * @param zFar
8325      *            far clipping plane distance
8326      * @param dest
8327      *            will hold the result
8328      * @return dest
8329      */
8330     public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, ref Matrix4d dest) {
8331         return ortho(left, right, bottom, top, zNear, zFar, false, dest);
8332     }
8333 
8334     /**
8335      * Apply an orthographic projection transformation for a right-handed coordinate system
8336      * using the given NDC z range to this matrix.
8337      * <p>
8338      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8339      * then the new matrix will be <code>M * O</code>. So when transforming a
8340      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8341      * orthographic projection transformation will be applied first!
8342      * <p>
8343      * In order to set the matrix to an orthographic projection without post-multiplying it,
8344      * use {@link #setOrtho(double, double, double, double, double, double, bool) setOrtho()}.
8345      * <p>
8346      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8347      * 
8348      * @see #setOrtho(double, double, double, double, double, double, bool)
8349      * 
8350      * @param left
8351      *            the distance from the center to the left frustum edge
8352      * @param right
8353      *            the distance from the center to the right frustum edge
8354      * @param bottom
8355      *            the distance from the center to the bottom frustum edge
8356      * @param top
8357      *            the distance from the center to the top frustum edge
8358      * @param zNear
8359      *            near clipping plane distance
8360      * @param zFar
8361      *            far clipping plane distance
8362      * @param zZeroToOne
8363      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8364      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8365      * @return this
8366      */
8367     ref public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
8368         ortho(left, right, bottom, top, zNear, zFar, zZeroToOne, this);
8369         return this;
8370     }
8371 
8372     /**
8373      * Apply an orthographic projection transformation for a right-handed coordinate system
8374      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
8375      * <p>
8376      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8377      * then the new matrix will be <code>M * O</code>. So when transforming a
8378      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8379      * orthographic projection transformation will be applied first!
8380      * <p>
8381      * In order to set the matrix to an orthographic projection without post-multiplying it,
8382      * use {@link #setOrtho(double, double, double, double, double, double) setOrtho()}.
8383      * <p>
8384      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8385      * 
8386      * @see #setOrtho(double, double, double, double, double, double)
8387      * 
8388      * @param left
8389      *            the distance from the center to the left frustum edge
8390      * @param right
8391      *            the distance from the center to the right frustum edge
8392      * @param bottom
8393      *            the distance from the center to the bottom frustum edge
8394      * @param top
8395      *            the distance from the center to the top frustum edge
8396      * @param zNear
8397      *            near clipping plane distance
8398      * @param zFar
8399      *            far clipping plane distance
8400      * @return this
8401      */
8402     ref public Matrix4d ortho(double left, double right, double bottom, double top, double zNear, double zFar) return {
8403         return ortho(left, right, bottom, top, zNear, zFar, false);
8404     }
8405 
8406     /**
8407      * Apply an orthographic projection transformation for a left-handed coordiante system
8408      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
8409      * <p>
8410      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8411      * then the new matrix will be <code>M * O</code>. So when transforming a
8412      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8413      * orthographic projection transformation will be applied first!
8414      * <p>
8415      * In order to set the matrix to an orthographic projection without post-multiplying it,
8416      * use {@link #setOrthoLH(double, double, double, double, double, double, bool) setOrthoLH()}.
8417      * <p>
8418      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8419      * 
8420      * @see #setOrthoLH(double, double, double, double, double, double, bool)
8421      * 
8422      * @param left
8423      *            the distance from the center to the left frustum edge
8424      * @param right
8425      *            the distance from the center to the right frustum edge
8426      * @param bottom
8427      *            the distance from the center to the bottom frustum edge
8428      * @param top
8429      *            the distance from the center to the top frustum edge
8430      * @param zNear
8431      *            near clipping plane distance
8432      * @param zFar
8433      *            far clipping plane distance
8434      * @param zZeroToOne
8435      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8436      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8437      * @param dest
8438      *            will hold the result
8439      * @return dest
8440      */
8441     public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8442         if ((properties & PROPERTY_IDENTITY) != 0)
8443             return dest.setOrthoLH(left, right, bottom, top, zNear, zFar, zZeroToOne);
8444         return orthoLHGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest);
8445     }
8446     private Matrix4d orthoLHGeneric(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8447         // calculate right matrix elements
8448         double rm00 = 2.0 / (right - left);
8449         double rm11 = 2.0 / (top - bottom);
8450         double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zFar - zNear);
8451         double rm30 = (left + right) / (left - right);
8452         double rm31 = (top + bottom) / (bottom - top);
8453         double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar);
8454         // perform optimized multiplication
8455         // compute the last column first, because other columns do not depend on it
8456         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
8457         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
8458         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
8459         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
8460         ._m00(m00 * rm00)
8461         ._m01(m01 * rm00)
8462         ._m02(m02 * rm00)
8463         ._m03(m03 * rm00)
8464         ._m10(m10 * rm11)
8465         ._m11(m11 * rm11)
8466         ._m12(m12 * rm11)
8467         ._m13(m13 * rm11)
8468         ._m20(m20 * rm22)
8469         ._m21(m21 * rm22)
8470         ._m22(m22 * rm22)
8471         ._m23(m23 * rm22)
8472         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
8473         return dest;
8474     }
8475 
8476     /**
8477      * Apply an orthographic projection transformation for a left-handed coordiante system
8478      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
8479      * <p>
8480      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8481      * then the new matrix will be <code>M * O</code>. So when transforming a
8482      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8483      * orthographic projection transformation will be applied first!
8484      * <p>
8485      * In order to set the matrix to an orthographic projection without post-multiplying it,
8486      * use {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()}.
8487      * <p>
8488      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8489      * 
8490      * @see #setOrthoLH(double, double, double, double, double, double)
8491      * 
8492      * @param left
8493      *            the distance from the center to the left frustum edge
8494      * @param right
8495      *            the distance from the center to the right frustum edge
8496      * @param bottom
8497      *            the distance from the center to the bottom frustum edge
8498      * @param top
8499      *            the distance from the center to the top frustum edge
8500      * @param zNear
8501      *            near clipping plane distance
8502      * @param zFar
8503      *            far clipping plane distance
8504      * @param dest
8505      *            will hold the result
8506      * @return dest
8507      */
8508     public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, ref Matrix4d dest) {
8509         return orthoLH(left, right, bottom, top, zNear, zFar, false, dest);
8510     }
8511 
8512     /**
8513      * Apply an orthographic projection transformation for a left-handed coordiante system
8514      * using the given NDC z range to this matrix.
8515      * <p>
8516      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8517      * then the new matrix will be <code>M * O</code>. So when transforming a
8518      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8519      * orthographic projection transformation will be applied first!
8520      * <p>
8521      * In order to set the matrix to an orthographic projection without post-multiplying it,
8522      * use {@link #setOrthoLH(double, double, double, double, double, double, bool) setOrthoLH()}.
8523      * <p>
8524      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8525      * 
8526      * @see #setOrthoLH(double, double, double, double, double, double, bool)
8527      * 
8528      * @param left
8529      *            the distance from the center to the left frustum edge
8530      * @param right
8531      *            the distance from the center to the right frustum edge
8532      * @param bottom
8533      *            the distance from the center to the bottom frustum edge
8534      * @param top
8535      *            the distance from the center to the top frustum edge
8536      * @param zNear
8537      *            near clipping plane distance
8538      * @param zFar
8539      *            far clipping plane distance
8540      * @param zZeroToOne
8541      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8542      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8543      * @return this
8544      */
8545     ref public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
8546         orthoLH(left, right, bottom, top, zNear, zFar, zZeroToOne, this);
8547         return this;
8548     }
8549 
8550     /**
8551      * Apply an orthographic projection transformation for a left-handed coordiante system
8552      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
8553      * <p>
8554      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8555      * then the new matrix will be <code>M * O</code>. So when transforming a
8556      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8557      * orthographic projection transformation will be applied first!
8558      * <p>
8559      * In order to set the matrix to an orthographic projection without post-multiplying it,
8560      * use {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()}.
8561      * <p>
8562      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8563      * 
8564      * @see #setOrthoLH(double, double, double, double, double, double)
8565      * 
8566      * @param left
8567      *            the distance from the center to the left frustum edge
8568      * @param right
8569      *            the distance from the center to the right frustum edge
8570      * @param bottom
8571      *            the distance from the center to the bottom frustum edge
8572      * @param top
8573      *            the distance from the center to the top frustum edge
8574      * @param zNear
8575      *            near clipping plane distance
8576      * @param zFar
8577      *            far clipping plane distance
8578      * @return this
8579      */
8580     ref public Matrix4d orthoLH(double left, double right, double bottom, double top, double zNear, double zFar) return {
8581         return orthoLH(left, right, bottom, top, zNear, zFar, false);
8582     }
8583 
8584     /**
8585      * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system
8586      * using the given NDC z range.
8587      * <p>
8588      * In order to apply the orthographic projection to an already existing transformation,
8589      * use {@link #ortho(double, double, double, double, double, double, bool) ortho()}.
8590      * <p>
8591      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8592      * 
8593      * @see #ortho(double, double, double, double, double, double, bool)
8594      * 
8595      * @param left
8596      *            the distance from the center to the left frustum edge
8597      * @param right
8598      *            the distance from the center to the right frustum edge
8599      * @param bottom
8600      *            the distance from the center to the bottom frustum edge
8601      * @param top
8602      *            the distance from the center to the top frustum edge
8603      * @param zNear
8604      *            near clipping plane distance
8605      * @param zFar
8606      *            far clipping plane distance
8607      * @param zZeroToOne
8608      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8609      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8610      * @return this
8611      */
8612     ref public Matrix4d setOrtho(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
8613         if ((properties & PROPERTY_IDENTITY) == 0)
8614             _identity();
8615         _m00(2.0 / (right - left)).
8616         _m11(2.0 / (top - bottom)).
8617         _m22((zZeroToOne ? 1.0 : 2.0) / (zNear - zFar)).
8618         _m30((right + left) / (left - right)).
8619         _m31((top + bottom) / (bottom - top)).
8620         _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)).
8621         properties = PROPERTY_AFFINE;
8622         return this;
8623     }
8624 
8625     /**
8626      * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system
8627      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
8628      * <p>
8629      * In order to apply the orthographic projection to an already existing transformation,
8630      * use {@link #ortho(double, double, double, double, double, double) ortho()}.
8631      * <p>
8632      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8633      * 
8634      * @see #ortho(double, double, double, double, double, double)
8635      * 
8636      * @param left
8637      *            the distance from the center to the left frustum edge
8638      * @param right
8639      *            the distance from the center to the right frustum edge
8640      * @param bottom
8641      *            the distance from the center to the bottom frustum edge
8642      * @param top
8643      *            the distance from the center to the top frustum edge
8644      * @param zNear
8645      *            near clipping plane distance
8646      * @param zFar
8647      *            far clipping plane distance
8648      * @return this
8649      */
8650     ref public Matrix4d setOrtho(double left, double right, double bottom, double top, double zNear, double zFar) return {
8651         return setOrtho(left, right, bottom, top, zNear, zFar, false);
8652     }
8653 
8654     /**
8655      * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system
8656      * using the given NDC z range.
8657      * <p>
8658      * In order to apply the orthographic projection to an already existing transformation,
8659      * use {@link #orthoLH(double, double, double, double, double, double, bool) orthoLH()}.
8660      * <p>
8661      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8662      * 
8663      * @see #orthoLH(double, double, double, double, double, double, bool)
8664      * 
8665      * @param left
8666      *            the distance from the center to the left frustum edge
8667      * @param right
8668      *            the distance from the center to the right frustum edge
8669      * @param bottom
8670      *            the distance from the center to the bottom frustum edge
8671      * @param top
8672      *            the distance from the center to the top frustum edge
8673      * @param zNear
8674      *            near clipping plane distance
8675      * @param zFar
8676      *            far clipping plane distance
8677      * @param zZeroToOne
8678      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8679      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8680      * @return this
8681      */
8682     ref public Matrix4d setOrthoLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
8683         if ((properties & PROPERTY_IDENTITY) == 0)
8684             _identity();
8685         _m00(2.0 / (right - left)).
8686         _m11(2.0 / (top - bottom)).
8687         _m22((zZeroToOne ? 1.0 : 2.0) / (zFar - zNear)).
8688         _m30((right + left) / (left - right)).
8689         _m31((top + bottom) / (bottom - top)).
8690         _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)).
8691         properties = PROPERTY_AFFINE;
8692         return this;
8693     }
8694 
8695     /**
8696      * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system
8697      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
8698      * <p>
8699      * In order to apply the orthographic projection to an already existing transformation,
8700      * use {@link #orthoLH(double, double, double, double, double, double) orthoLH()}.
8701      * <p>
8702      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8703      * 
8704      * @see #orthoLH(double, double, double, double, double, double)
8705      * 
8706      * @param left
8707      *            the distance from the center to the left frustum edge
8708      * @param right
8709      *            the distance from the center to the right frustum edge
8710      * @param bottom
8711      *            the distance from the center to the bottom frustum edge
8712      * @param top
8713      *            the distance from the center to the top frustum edge
8714      * @param zNear
8715      *            near clipping plane distance
8716      * @param zFar
8717      *            far clipping plane distance
8718      * @return this
8719      */
8720     ref public Matrix4d setOrthoLH(double left, double right, double bottom, double top, double zNear, double zFar) return {
8721         return setOrthoLH(left, right, bottom, top, zNear, zFar, false);
8722     }
8723 
8724     /**
8725      * Apply a symmetric orthographic projection transformation for a right-handed coordinate system
8726      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
8727      * <p>
8728      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, bool, Matrix4d) ortho()} with
8729      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8730      * <p>
8731      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8732      * then the new matrix will be <code>M * O</code>. So when transforming a
8733      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8734      * orthographic projection transformation will be applied first!
8735      * <p>
8736      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8737      * use {@link #setOrthoSymmetric(double, double, double, double, bool) setOrthoSymmetric()}.
8738      * <p>
8739      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8740      * 
8741      * @see #setOrthoSymmetric(double, double, double, double, bool)
8742      * 
8743      * @param width
8744      *            the distance between the right and left frustum edges
8745      * @param height
8746      *            the distance between the top and bottom frustum edges
8747      * @param zNear
8748      *            near clipping plane distance
8749      * @param zFar
8750      *            far clipping plane distance
8751      * @param dest
8752      *            will hold the result
8753      * @param zZeroToOne
8754      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8755      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8756      * @return dest
8757      */
8758     public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8759         if ((properties & PROPERTY_IDENTITY) != 0)
8760             return dest.setOrthoSymmetric(width, height, zNear, zFar, zZeroToOne);
8761         return orthoSymmetricGeneric(width, height, zNear, zFar, zZeroToOne, dest);
8762     }
8763     private Matrix4d orthoSymmetricGeneric(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8764         // calculate right matrix elements
8765         double rm00 = 2.0 / width;
8766         double rm11 = 2.0 / height;
8767         double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zNear - zFar);
8768         double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar);
8769         // perform optimized multiplication
8770         // compute the last column first, because other columns do not depend on it
8771         dest._m30(m20 * rm32 + m30)
8772         ._m31(m21 * rm32 + m31)
8773         ._m32(m22 * rm32 + m32)
8774         ._m33(m23 * rm32 + m33)
8775         ._m00(m00 * rm00)
8776         ._m01(m01 * rm00)
8777         ._m02(m02 * rm00)
8778         ._m03(m03 * rm00)
8779         ._m10(m10 * rm11)
8780         ._m11(m11 * rm11)
8781         ._m12(m12 * rm11)
8782         ._m13(m13 * rm11)
8783         ._m20(m20 * rm22)
8784         ._m21(m21 * rm22)
8785         ._m22(m22 * rm22)
8786         ._m23(m23 * rm22)
8787         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
8788         return dest;
8789     }
8790 
8791     /**
8792      * Apply a symmetric orthographic projection transformation for a right-handed coordinate system
8793      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
8794      * <p>
8795      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4d) ortho()} with
8796      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8797      * <p>
8798      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8799      * then the new matrix will be <code>M * O</code>. So when transforming a
8800      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8801      * orthographic projection transformation will be applied first!
8802      * <p>
8803      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8804      * use {@link #setOrthoSymmetric(double, double, double, double) setOrthoSymmetric()}.
8805      * <p>
8806      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8807      * 
8808      * @see #setOrthoSymmetric(double, double, double, double)
8809      * 
8810      * @param width
8811      *            the distance between the right and left frustum edges
8812      * @param height
8813      *            the distance between the top and bottom frustum edges
8814      * @param zNear
8815      *            near clipping plane distance
8816      * @param zFar
8817      *            far clipping plane distance
8818      * @param dest
8819      *            will hold the result
8820      * @return dest
8821      */
8822     public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, ref Matrix4d dest) {
8823         return orthoSymmetric(width, height, zNear, zFar, false, dest);
8824     }
8825 
8826     /**
8827      * Apply a symmetric orthographic projection transformation for a right-handed coordinate system
8828      * using the given NDC z range to this matrix.
8829      * <p>
8830      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, bool) ortho()} with
8831      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8832      * <p>
8833      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8834      * then the new matrix will be <code>M * O</code>. So when transforming a
8835      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8836      * orthographic projection transformation will be applied first!
8837      * <p>
8838      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8839      * use {@link #setOrthoSymmetric(double, double, double, double, bool) setOrthoSymmetric()}.
8840      * <p>
8841      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8842      * 
8843      * @see #setOrthoSymmetric(double, double, double, double, bool)
8844      * 
8845      * @param width
8846      *            the distance between the right and left frustum edges
8847      * @param height
8848      *            the distance between the top and bottom frustum edges
8849      * @param zNear
8850      *            near clipping plane distance
8851      * @param zFar
8852      *            far clipping plane distance
8853      * @param zZeroToOne
8854      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8855      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8856      * @return this
8857      */
8858     ref public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
8859         orthoSymmetric(width, height, zNear, zFar, zZeroToOne, this);
8860         return this;
8861     }
8862 
8863     /**
8864      * Apply a symmetric orthographic projection transformation for a right-handed coordinate system
8865      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
8866      * <p>
8867      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double) ortho()} with
8868      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8869      * <p>
8870      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8871      * then the new matrix will be <code>M * O</code>. So when transforming a
8872      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8873      * orthographic projection transformation will be applied first!
8874      * <p>
8875      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8876      * use {@link #setOrthoSymmetric(double, double, double, double) setOrthoSymmetric()}.
8877      * <p>
8878      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8879      * 
8880      * @see #setOrthoSymmetric(double, double, double, double)
8881      * 
8882      * @param width
8883      *            the distance between the right and left frustum edges
8884      * @param height
8885      *            the distance between the top and bottom frustum edges
8886      * @param zNear
8887      *            near clipping plane distance
8888      * @param zFar
8889      *            far clipping plane distance
8890      * @return this
8891      */
8892     ref public Matrix4d orthoSymmetric(double width, double height, double zNear, double zFar) return {
8893         orthoSymmetric(width, height, zNear, zFar, false, this);
8894         return this;
8895     }
8896 
8897     /**
8898      * Apply a symmetric orthographic projection transformation for a left-handed coordinate system
8899      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
8900      * <p>
8901      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, bool, Matrix4d) orthoLH()} with
8902      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8903      * <p>
8904      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8905      * then the new matrix will be <code>M * O</code>. So when transforming a
8906      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8907      * orthographic projection transformation will be applied first!
8908      * <p>
8909      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8910      * use {@link #setOrthoSymmetricLH(double, double, double, double, bool) setOrthoSymmetricLH()}.
8911      * <p>
8912      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8913      * 
8914      * @see #setOrthoSymmetricLH(double, double, double, double, bool)
8915      * 
8916      * @param width
8917      *            the distance between the right and left frustum edges
8918      * @param height
8919      *            the distance between the top and bottom frustum edges
8920      * @param zNear
8921      *            near clipping plane distance
8922      * @param zFar
8923      *            far clipping plane distance
8924      * @param dest
8925      *            will hold the result
8926      * @param zZeroToOne
8927      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
8928      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
8929      * @return dest
8930      */
8931     public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8932         if ((properties & PROPERTY_IDENTITY) != 0)
8933             return dest.setOrthoSymmetricLH(width, height, zNear, zFar, zZeroToOne);
8934         return orthoSymmetricLHGeneric(width, height, zNear, zFar, zZeroToOne, dest);
8935     }
8936     private Matrix4d orthoSymmetricLHGeneric(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
8937         // calculate right matrix elements
8938         double rm00 = 2.0 / width;
8939         double rm11 = 2.0 / height;
8940         double rm22 = (zZeroToOne ? 1.0 : 2.0) / (zFar - zNear);
8941         double rm32 = (zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar);
8942         // perform optimized multiplication
8943         // compute the last column first, because other columns do not depend on it
8944         dest._m30(m20 * rm32 + m30)
8945         ._m31(m21 * rm32 + m31)
8946         ._m32(m22 * rm32 + m32)
8947         ._m33(m23 * rm32 + m33)
8948         ._m00(m00 * rm00)
8949         ._m01(m01 * rm00)
8950         ._m02(m02 * rm00)
8951         ._m03(m03 * rm00)
8952         ._m10(m10 * rm11)
8953         ._m11(m11 * rm11)
8954         ._m12(m12 * rm11)
8955         ._m13(m13 * rm11)
8956         ._m20(m20 * rm22)
8957         ._m21(m21 * rm22)
8958         ._m22(m22 * rm22)
8959         ._m23(m23 * rm22)
8960         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
8961         return dest;
8962     }
8963 
8964     /**
8965      * Apply a symmetric orthographic projection transformation for a left-handed coordinate system
8966      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
8967      * <p>
8968      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4d) orthoLH()} with
8969      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
8970      * <p>
8971      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
8972      * then the new matrix will be <code>M * O</code>. So when transforming a
8973      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
8974      * orthographic projection transformation will be applied first!
8975      * <p>
8976      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
8977      * use {@link #setOrthoSymmetricLH(double, double, double, double) setOrthoSymmetricLH()}.
8978      * <p>
8979      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
8980      * 
8981      * @see #setOrthoSymmetricLH(double, double, double, double)
8982      * 
8983      * @param width
8984      *            the distance between the right and left frustum edges
8985      * @param height
8986      *            the distance between the top and bottom frustum edges
8987      * @param zNear
8988      *            near clipping plane distance
8989      * @param zFar
8990      *            far clipping plane distance
8991      * @param dest
8992      *            will hold the result
8993      * @return dest
8994      */
8995     public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, ref Matrix4d dest) {
8996         return orthoSymmetricLH(width, height, zNear, zFar, false, dest);
8997     }
8998 
8999     /**
9000      * Apply a symmetric orthographic projection transformation for a left-handed coordinate system
9001      * using the given NDC z range to this matrix.
9002      * <p>
9003      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, bool) orthoLH()} with
9004      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9005      * <p>
9006      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9007      * then the new matrix will be <code>M * O</code>. So when transforming a
9008      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9009      * orthographic projection transformation will be applied first!
9010      * <p>
9011      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
9012      * use {@link #setOrthoSymmetricLH(double, double, double, double, bool) setOrthoSymmetricLH()}.
9013      * <p>
9014      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9015      * 
9016      * @see #setOrthoSymmetricLH(double, double, double, double, bool)
9017      * 
9018      * @param width
9019      *            the distance between the right and left frustum edges
9020      * @param height
9021      *            the distance between the top and bottom frustum edges
9022      * @param zNear
9023      *            near clipping plane distance
9024      * @param zFar
9025      *            far clipping plane distance
9026      * @param zZeroToOne
9027      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
9028      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
9029      * @return this
9030      */
9031     ref public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
9032         orthoSymmetricLH(width, height, zNear, zFar, zZeroToOne, this);
9033         return this;
9034     }
9035 
9036     /**
9037      * Apply a symmetric orthographic projection transformation for a left-handed coordinate system
9038      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
9039      * <p>
9040      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double) orthoLH()} with
9041      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9042      * <p>
9043      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9044      * then the new matrix will be <code>M * O</code>. So when transforming a
9045      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9046      * orthographic projection transformation will be applied first!
9047      * <p>
9048      * In order to set the matrix to a symmetric orthographic projection without post-multiplying it,
9049      * use {@link #setOrthoSymmetricLH(double, double, double, double) setOrthoSymmetricLH()}.
9050      * <p>
9051      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9052      * 
9053      * @see #setOrthoSymmetricLH(double, double, double, double)
9054      * 
9055      * @param width
9056      *            the distance between the right and left frustum edges
9057      * @param height
9058      *            the distance between the top and bottom frustum edges
9059      * @param zNear
9060      *            near clipping plane distance
9061      * @param zFar
9062      *            far clipping plane distance
9063      * @return this
9064      */
9065     ref public Matrix4d orthoSymmetricLH(double width, double height, double zNear, double zFar) return {
9066         orthoSymmetricLH(width, height, zNear, zFar, false, this);
9067         return this;
9068     }
9069 
9070     /**
9071      * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system
9072      * using the given NDC z range.
9073      * <p>
9074      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double, bool) setOrtho()} with
9075      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9076      * <p>
9077      * In order to apply the symmetric orthographic projection to an already existing transformation,
9078      * use {@link #orthoSymmetric(double, double, double, double, bool) orthoSymmetric()}.
9079      * <p>
9080      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9081      * 
9082      * @see #orthoSymmetric(double, double, double, double, bool)
9083      * 
9084      * @param width
9085      *            the distance between the right and left frustum edges
9086      * @param height
9087      *            the distance between the top and bottom frustum edges
9088      * @param zNear
9089      *            near clipping plane distance
9090      * @param zFar
9091      *            far clipping plane distance
9092      * @param zZeroToOne
9093      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
9094      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
9095      * @return this
9096      */
9097     ref public Matrix4d setOrthoSymmetric(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
9098         if ((properties & PROPERTY_IDENTITY) == 0)
9099             _identity();
9100         _m00(2.0 / width).
9101         _m11(2.0 / height).
9102         _m22((zZeroToOne ? 1.0 : 2.0) / (zNear - zFar)).
9103         _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)).
9104         properties = PROPERTY_AFFINE;
9105         return this;
9106     }
9107 
9108     /**
9109      * Set this matrix to be a symmetric orthographic projection transformation for a right-handed coordinate system
9110      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
9111      * <p>
9112      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrtho()} with
9113      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9114      * <p>
9115      * In order to apply the symmetric orthographic projection to an already existing transformation,
9116      * use {@link #orthoSymmetric(double, double, double, double) orthoSymmetric()}.
9117      * <p>
9118      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9119      * 
9120      * @see #orthoSymmetric(double, double, double, double)
9121      * 
9122      * @param width
9123      *            the distance between the right and left frustum edges
9124      * @param height
9125      *            the distance between the top and bottom frustum edges
9126      * @param zNear
9127      *            near clipping plane distance
9128      * @param zFar
9129      *            far clipping plane distance
9130      * @return this
9131      */
9132     ref public Matrix4d setOrthoSymmetric(double width, double height, double zNear, double zFar) return {
9133         return setOrthoSymmetric(width, height, zNear, zFar, false);
9134     }
9135 
9136     /**
9137      * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system using the given NDC z range.
9138      * <p>
9139      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double, bool) setOrtho()} with
9140      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9141      * <p>
9142      * In order to apply the symmetric orthographic projection to an already existing transformation,
9143      * use {@link #orthoSymmetricLH(double, double, double, double, bool) orthoSymmetricLH()}.
9144      * <p>
9145      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9146      * 
9147      * @see #orthoSymmetricLH(double, double, double, double, bool)
9148      * 
9149      * @param width
9150      *            the distance between the right and left frustum edges
9151      * @param height
9152      *            the distance between the top and bottom frustum edges
9153      * @param zNear
9154      *            near clipping plane distance
9155      * @param zFar
9156      *            far clipping plane distance
9157      * @param zZeroToOne
9158      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
9159      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
9160      * @return this
9161      */
9162     ref public Matrix4d setOrthoSymmetricLH(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
9163         if ((properties & PROPERTY_IDENTITY) == 0)
9164             _identity();
9165         _m00(2.0 / width).
9166         _m11(2.0 / height).
9167         _m22((zZeroToOne ? 1.0 : 2.0) / (zFar - zNear)).
9168         _m32((zZeroToOne ? zNear : (zFar + zNear)) / (zNear - zFar)).
9169         properties = PROPERTY_AFFINE;
9170         return this;
9171     }
9172 
9173     /**
9174      * Set this matrix to be a symmetric orthographic projection transformation for a left-handed coordinate system
9175      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
9176      * <p>
9177      * This method is equivalent to calling {@link #setOrthoLH(double, double, double, double, double, double) setOrthoLH()} with
9178      * <code>left=-width/2</code>, <code>right=+width/2</code>, <code>bottom=-height/2</code> and <code>top=+height/2</code>.
9179      * <p>
9180      * In order to apply the symmetric orthographic projection to an already existing transformation,
9181      * use {@link #orthoSymmetricLH(double, double, double, double) orthoSymmetricLH()}.
9182      * <p>
9183      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9184      * 
9185      * @see #orthoSymmetricLH(double, double, double, double)
9186      * 
9187      * @param width
9188      *            the distance between the right and left frustum edges
9189      * @param height
9190      *            the distance between the top and bottom frustum edges
9191      * @param zNear
9192      *            near clipping plane distance
9193      * @param zFar
9194      *            far clipping plane distance
9195      * @return this
9196      */
9197     ref public Matrix4d setOrthoSymmetricLH(double width, double height, double zNear, double zFar) return {
9198         return setOrthoSymmetricLH(width, height, zNear, zFar, false);
9199     }
9200 
9201     /**
9202      * Apply an orthographic projection transformation for a right-handed coordinate system
9203      * to this matrix and store the result in <code>dest</code>.
9204      * <p>
9205      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double, Matrix4d) ortho()} with
9206      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9207      * <p>
9208      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9209      * then the new matrix will be <code>M * O</code>. So when transforming a
9210      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9211      * orthographic projection transformation will be applied first!
9212      * <p>
9213      * In order to set the matrix to an orthographic projection without post-multiplying it,
9214      * use {@link #setOrtho2D(double, double, double, double) setOrtho()}.
9215      * <p>
9216      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9217      * 
9218      * @see #ortho(double, double, double, double, double, double, Matrix4d)
9219      * @see #setOrtho2D(double, double, double, double)
9220      * 
9221      * @param left
9222      *            the distance from the center to the left frustum edge
9223      * @param right
9224      *            the distance from the center to the right frustum edge
9225      * @param bottom
9226      *            the distance from the center to the bottom frustum edge
9227      * @param top
9228      *            the distance from the center to the top frustum edge
9229      * @param dest
9230      *            will hold the result
9231      * @return dest
9232      */
9233     public Matrix4d ortho2D(double left, double right, double bottom, double top, ref Matrix4d dest) {
9234         if ((properties & PROPERTY_IDENTITY) != 0)
9235             return dest.setOrtho2D(left, right, bottom, top);
9236         return ortho2DGeneric(left, right, bottom, top, dest);
9237     }
9238     private Matrix4d ortho2DGeneric(double left, double right, double bottom, double top, ref Matrix4d dest) {
9239         // calculate right matrix elements
9240         double rm00 = 2.0 / (right - left);
9241         double rm11 = 2.0 / (top - bottom);
9242         double rm30 = (right + left) / (left - right);
9243         double rm31 = (top + bottom) / (bottom - top);
9244         // perform optimized multiplication
9245         // compute the last column first, because other columns do not depend on it
9246         dest._m30(m00 * rm30 + m10 * rm31 + m30)
9247         ._m31(m01 * rm30 + m11 * rm31 + m31)
9248         ._m32(m02 * rm30 + m12 * rm31 + m32)
9249         ._m33(m03 * rm30 + m13 * rm31 + m33)
9250         ._m00(m00 * rm00)
9251         ._m01(m01 * rm00)
9252         ._m02(m02 * rm00)
9253         ._m03(m03 * rm00)
9254         ._m10(m10 * rm11)
9255         ._m11(m11 * rm11)
9256         ._m12(m12 * rm11)
9257         ._m13(m13 * rm11)
9258         ._m20(-m20)
9259         ._m21(-m21)
9260         ._m22(-m22)
9261         ._m23(-m23)
9262         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
9263         return dest;
9264     }
9265 
9266     /**
9267      * Apply an orthographic projection transformation for a right-handed coordinate system to this matrix.
9268      * <p>
9269      * This method is equivalent to calling {@link #ortho(double, double, double, double, double, double) ortho()} with
9270      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9271      * <p>
9272      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9273      * then the new matrix will be <code>M * O</code>. So when transforming a
9274      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9275      * orthographic projection transformation will be applied first!
9276      * <p>
9277      * In order to set the matrix to an orthographic projection without post-multiplying it,
9278      * use {@link #setOrtho2D(double, double, double, double) setOrtho2D()}.
9279      * <p>
9280      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9281      * 
9282      * @see #ortho(double, double, double, double, double, double)
9283      * @see #setOrtho2D(double, double, double, double)
9284      * 
9285      * @param left
9286      *            the distance from the center to the left frustum edge
9287      * @param right
9288      *            the distance from the center to the right frustum edge
9289      * @param bottom
9290      *            the distance from the center to the bottom frustum edge
9291      * @param top
9292      *            the distance from the center to the top frustum edge
9293      * @return this
9294      */
9295     ref public Matrix4d ortho2D(double left, double right, double bottom, double top) return {
9296         ortho2D(left, right, bottom, top, this);
9297         return this;
9298     }
9299 
9300     /**
9301      * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix and store the result in <code>dest</code>.
9302      * <p>
9303      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double, Matrix4d) orthoLH()} with
9304      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9305      * <p>
9306      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9307      * then the new matrix will be <code>M * O</code>. So when transforming a
9308      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9309      * orthographic projection transformation will be applied first!
9310      * <p>
9311      * In order to set the matrix to an orthographic projection without post-multiplying it,
9312      * use {@link #setOrtho2DLH(double, double, double, double) setOrthoLH()}.
9313      * <p>
9314      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9315      * 
9316      * @see #orthoLH(double, double, double, double, double, double, Matrix4d)
9317      * @see #setOrtho2DLH(double, double, double, double)
9318      * 
9319      * @param left
9320      *            the distance from the center to the left frustum edge
9321      * @param right
9322      *            the distance from the center to the right frustum edge
9323      * @param bottom
9324      *            the distance from the center to the bottom frustum edge
9325      * @param top
9326      *            the distance from the center to the top frustum edge
9327      * @param dest
9328      *            will hold the result
9329      * @return dest
9330      */
9331     public Matrix4d ortho2DLH(double left, double right, double bottom, double top, ref Matrix4d dest) {
9332         if ((properties & PROPERTY_IDENTITY) != 0)
9333             return dest.setOrtho2DLH(left, right, bottom, top);
9334         return ortho2DLHGeneric(left, right, bottom, top, dest);
9335     }
9336     private Matrix4d ortho2DLHGeneric(double left, double right, double bottom, double top, ref Matrix4d dest) {
9337         // calculate right matrix elements
9338         double rm00 = 2.0 / (right - left);
9339         double rm11 = 2.0 / (top - bottom);
9340         double rm30 = (right + left) / (left - right);
9341         double rm31 = (top + bottom) / (bottom - top);
9342         // perform optimized multiplication
9343         // compute the last column first, because other columns do not depend on it
9344         dest._m30(m00 * rm30 + m10 * rm31 + m30)
9345         ._m31(m01 * rm30 + m11 * rm31 + m31)
9346         ._m32(m02 * rm30 + m12 * rm31 + m32)
9347         ._m33(m03 * rm30 + m13 * rm31 + m33)
9348         ._m00(m00 * rm00)
9349         ._m01(m01 * rm00)
9350         ._m02(m02 * rm00)
9351         ._m03(m03 * rm00)
9352         ._m10(m10 * rm11)
9353         ._m11(m11 * rm11)
9354         ._m12(m12 * rm11)
9355         ._m13(m13 * rm11)
9356         ._m20(m20)
9357         ._m21(m21)
9358         ._m22(m22)
9359         ._m23(m23)
9360         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
9361         return dest;
9362     }
9363 
9364     /**
9365      * Apply an orthographic projection transformation for a left-handed coordinate system to this matrix.
9366      * <p>
9367      * This method is equivalent to calling {@link #orthoLH(double, double, double, double, double, double) orthoLH()} with
9368      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9369      * <p>
9370      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the orthographic projection matrix,
9371      * then the new matrix will be <code>M * O</code>. So when transforming a
9372      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
9373      * orthographic projection transformation will be applied first!
9374      * <p>
9375      * In order to set the matrix to an orthographic projection without post-multiplying it,
9376      * use {@link #setOrtho2DLH(double, double, double, double) setOrtho2DLH()}.
9377      * <p>
9378      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9379      * 
9380      * @see #orthoLH(double, double, double, double, double, double)
9381      * @see #setOrtho2DLH(double, double, double, double)
9382      * 
9383      * @param left
9384      *            the distance from the center to the left frustum edge
9385      * @param right
9386      *            the distance from the center to the right frustum edge
9387      * @param bottom
9388      *            the distance from the center to the bottom frustum edge
9389      * @param top
9390      *            the distance from the center to the top frustum edge
9391      * @return this
9392      */
9393     ref public Matrix4d ortho2DLH(double left, double right, double bottom, double top) return {
9394         ortho2DLH(left, right, bottom, top, this);
9395         return this;
9396     }
9397 
9398     /**
9399      * Set this matrix to be an orthographic projection transformation for a right-handed coordinate system.
9400      * <p>
9401      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrtho()} with
9402      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9403      * <p>
9404      * In order to apply the orthographic projection to an already existing transformation,
9405      * use {@link #ortho2D(double, double, double, double) ortho2D()}.
9406      * <p>
9407      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9408      * 
9409      * @see #setOrtho(double, double, double, double, double, double)
9410      * @see #ortho2D(double, double, double, double)
9411      * 
9412      * @param left
9413      *            the distance from the center to the left frustum edge
9414      * @param right
9415      *            the distance from the center to the right frustum edge
9416      * @param bottom
9417      *            the distance from the center to the bottom frustum edge
9418      * @param top
9419      *            the distance from the center to the top frustum edge
9420      * @return this
9421      */
9422     ref public Matrix4d setOrtho2D(double left, double right, double bottom, double top) return {
9423         if ((properties & PROPERTY_IDENTITY) == 0)
9424             _identity();
9425         _m00(2.0 / (right - left)).
9426         _m11(2.0 / (top - bottom)).
9427         _m22(-1.0).
9428         _m30((right + left) / (left - right)).
9429         _m31((top + bottom) / (bottom - top)).
9430         properties = PROPERTY_AFFINE;
9431         return this;
9432     }
9433 
9434     /**
9435      * Set this matrix to be an orthographic projection transformation for a left-handed coordinate system.
9436      * <p>
9437      * This method is equivalent to calling {@link #setOrtho(double, double, double, double, double, double) setOrthoLH()} with
9438      * <code>zNear=-1</code> and <code>zFar=+1</code>.
9439      * <p>
9440      * In order to apply the orthographic projection to an already existing transformation,
9441      * use {@link #ortho2DLH(double, double, double, double) ortho2DLH()}.
9442      * <p>
9443      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho">http://www.songho.ca</a>
9444      * 
9445      * @see #setOrthoLH(double, double, double, double, double, double)
9446      * @see #ortho2DLH(double, double, double, double)
9447      * 
9448      * @param left
9449      *            the distance from the center to the left frustum edge
9450      * @param right
9451      *            the distance from the center to the right frustum edge
9452      * @param bottom
9453      *            the distance from the center to the bottom frustum edge
9454      * @param top
9455      *            the distance from the center to the top frustum edge
9456      * @return this
9457      */
9458     ref public Matrix4d setOrtho2DLH(double left, double right, double bottom, double top) return {
9459         if ((properties & PROPERTY_IDENTITY) == 0)
9460             _identity();
9461         _m00(2.0 / (right - left)).
9462         _m11(2.0 / (top - bottom)).
9463         _m30((right + left) / (left - right)).
9464         _m31((top + bottom) / (bottom - top)).
9465         properties = PROPERTY_AFFINE;
9466         return this;
9467     }
9468 
9469     /**
9470      * Apply a rotation transformation to this matrix to make <code>-z</code> point along <code>dir</code>. 
9471      * <p>
9472      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookalong rotation matrix,
9473      * then the new matrix will be <code>M * L</code>. So when transforming a
9474      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>, the
9475      * lookalong rotation transformation will be applied first!
9476      * <p>
9477      * This is equivalent to calling
9478      * {@link #lookAt(ref Vector3d, Vector3d, Vector3d) lookAt}
9479      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9480      * <p>
9481      * In order to set the matrix to a lookalong transformation without post-multiplying it,
9482      * use {@link #setLookAlong(ref Vector3d, Vector3d) setLookAlong()}.
9483      * 
9484      * @see #lookAlong(double, double, double, double, double, double)
9485      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
9486      * @see #setLookAlong(ref Vector3d, Vector3d)
9487      * 
9488      * @param dir
9489      *            the direction in space to look along
9490      * @param up
9491      *            the direction of 'up'
9492      * @return this
9493      */
9494     ref public Matrix4d lookAlong(ref Vector3d dir, Vector3d up) return {
9495         lookAlong(dir.x, dir.y, dir.z, up.x, up.y, up.z, this);
9496         return this;
9497     }
9498 
9499     /**
9500      * Apply a rotation transformation to this matrix to make <code>-z</code> point along <code>dir</code>
9501      * and store the result in <code>dest</code>. 
9502      * <p>
9503      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookalong rotation matrix,
9504      * then the new matrix will be <code>M * L</code>. So when transforming a
9505      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>, the
9506      * lookalong rotation transformation will be applied first!
9507      * <p>
9508      * This is equivalent to calling
9509      * {@link #lookAt(ref Vector3d, Vector3d, Vector3d) lookAt}
9510      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9511      * <p>
9512      * In order to set the matrix to a lookalong transformation without post-multiplying it,
9513      * use {@link #setLookAlong(ref Vector3d, Vector3d) setLookAlong()}.
9514      * 
9515      * @see #lookAlong(double, double, double, double, double, double)
9516      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
9517      * @see #setLookAlong(ref Vector3d, Vector3d)
9518      * 
9519      * @param dir
9520      *            the direction in space to look along
9521      * @param up
9522      *            the direction of 'up'
9523      * @param dest
9524      *            will hold the result
9525      * @return dest
9526      */
9527     public Matrix4d lookAlong(ref Vector3d dir, Vector3d up, ref Matrix4d dest) {
9528         return lookAlong(dir.x, dir.y, dir.z, up.x, up.y, up.z, dest);
9529     }
9530 
9531     /**
9532      * Apply a rotation transformation to this matrix to make <code>-z</code> point along <code>dir</code>
9533      * and store the result in <code>dest</code>. 
9534      * <p>
9535      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookalong rotation matrix,
9536      * then the new matrix will be <code>M * L</code>. So when transforming a
9537      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>, the
9538      * lookalong rotation transformation will be applied first!
9539      * <p>
9540      * This is equivalent to calling
9541      * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt()}
9542      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9543      * <p>
9544      * In order to set the matrix to a lookalong transformation without post-multiplying it,
9545      * use {@link #setLookAlong(double, double, double, double, double, double) setLookAlong()}
9546      * 
9547      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9548      * @see #setLookAlong(double, double, double, double, double, double)
9549      * 
9550      * @param dirX
9551      *              the x-coordinate of the direction to look along
9552      * @param dirY
9553      *              the y-coordinate of the direction to look along
9554      * @param dirZ
9555      *              the z-coordinate of the direction to look along
9556      * @param upX
9557      *              the x-coordinate of the up vector
9558      * @param upY
9559      *              the y-coordinate of the up vector
9560      * @param upZ
9561      *              the z-coordinate of the up vector
9562      * @param dest
9563      *              will hold the result
9564      * @return dest
9565      */
9566     public Matrix4d lookAlong(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, ref Matrix4d dest) {
9567         if ((properties & PROPERTY_IDENTITY) != 0)
9568             return dest.setLookAlong(dirX, dirY, dirZ, upX, upY, upZ);
9569         return lookAlongGeneric(dirX, dirY, dirZ, upX, upY, upZ, dest);
9570     }
9571 
9572     private Matrix4d lookAlongGeneric(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, ref Matrix4d dest) {
9573         // Normalize direction
9574         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
9575         dirX *= -invDirLength;
9576         dirY *= -invDirLength;
9577         dirZ *= -invDirLength;
9578         // left = up x direction
9579         double leftX, leftY, leftZ;
9580         leftX = upY * dirZ - upZ * dirY;
9581         leftY = upZ * dirX - upX * dirZ;
9582         leftZ = upX * dirY - upY * dirX;
9583         // normalize left
9584         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
9585         leftX *= invLeftLength;
9586         leftY *= invLeftLength;
9587         leftZ *= invLeftLength;
9588         // up = direction x left
9589         double upnX = dirY * leftZ - dirZ * leftY;
9590         double upnY = dirZ * leftX - dirX * leftZ;
9591         double upnZ = dirX * leftY - dirY * leftX;
9592         // calculate right matrix elements
9593         double rm00 = leftX;
9594         double rm01 = upnX;
9595         double rm02 = dirX;
9596         double rm10 = leftY;
9597         double rm11 = upnY;
9598         double rm12 = dirY;
9599         double rm20 = leftZ;
9600         double rm21 = upnZ;
9601         double rm22 = dirZ;
9602         // perform optimized matrix multiplication
9603         // introduce temporaries for dependent results
9604         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
9605         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
9606         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
9607         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
9608         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
9609         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
9610         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
9611         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
9612         dest._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
9613         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
9614         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
9615         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
9616         // set the rest of the matrix elements
9617         ._m00(nm00)
9618         ._m01(nm01)
9619         ._m02(nm02)
9620         ._m03(nm03)
9621         ._m10(nm10)
9622         ._m11(nm11)
9623         ._m12(nm12)
9624         ._m13(nm13)
9625         ._m30(m30)
9626         ._m31(m31)
9627         ._m32(m32)
9628         ._m33(m33)
9629         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
9630         return dest;
9631     }
9632 
9633     /**
9634      * Apply a rotation transformation to this matrix to make <code>-z</code> point along <code>dir</code>. 
9635      * <p>
9636      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookalong rotation matrix,
9637      * then the new matrix will be <code>M * L</code>. So when transforming a
9638      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>, the
9639      * lookalong rotation transformation will be applied first!
9640      * <p>
9641      * This is equivalent to calling
9642      * {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt()}
9643      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9644      * <p>
9645      * In order to set the matrix to a lookalong transformation without post-multiplying it,
9646      * use {@link #setLookAlong(double, double, double, double, double, double) setLookAlong()}
9647      * 
9648      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9649      * @see #setLookAlong(double, double, double, double, double, double)
9650      * 
9651      * @param dirX
9652      *              the x-coordinate of the direction to look along
9653      * @param dirY
9654      *              the y-coordinate of the direction to look along
9655      * @param dirZ
9656      *              the z-coordinate of the direction to look along
9657      * @param upX
9658      *              the x-coordinate of the up vector
9659      * @param upY
9660      *              the y-coordinate of the up vector
9661      * @param upZ
9662      *              the z-coordinate of the up vector
9663      * @return this
9664      */
9665     ref public Matrix4d lookAlong(double dirX, double dirY, double dirZ,
9666                               double upX, double upY, double upZ) return {
9667         lookAlong(dirX, dirY, dirZ, upX, upY, upZ, this);
9668         return this;
9669     }
9670 
9671     /**
9672      * Set this matrix to a rotation transformation to make <code>-z</code>
9673      * point along <code>dir</code>.
9674      * <p>
9675      * This is equivalent to calling
9676      * {@link #setLookAt(ref Vector3d, Vector3d, Vector3d) setLookAt()} 
9677      * with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9678      * <p>
9679      * In order to apply the lookalong transformation to any previous existing transformation,
9680      * use {@link #lookAlong(ref Vector3d, Vector3d)}.
9681      * 
9682      * @see #setLookAlong(ref Vector3d, Vector3d)
9683      * @see #lookAlong(ref Vector3d, Vector3d)
9684      * 
9685      * @param dir
9686      *            the direction in space to look along
9687      * @param up
9688      *            the direction of 'up'
9689      * @return this
9690      */
9691     ref public Matrix4d setLookAlong(ref Vector3d dir, Vector3d up) return {
9692         return setLookAlong(dir.x, dir.y, dir.z, up.x, up.y, up.z);
9693     }
9694 
9695     /**
9696      * Set this matrix to a rotation transformation to make <code>-z</code>
9697      * point along <code>dir</code>.
9698      * <p>
9699      * This is equivalent to calling
9700      * {@link #setLookAt(double, double, double, double, double, double, double, double, double)
9701      * setLookAt()} with <code>eye = (0, 0, 0)</code> and <code>center = dir</code>.
9702      * <p>
9703      * In order to apply the lookalong transformation to any previous existing transformation,
9704      * use {@link #lookAlong(double, double, double, double, double, double) lookAlong()}
9705      * 
9706      * @see #setLookAlong(double, double, double, double, double, double)
9707      * @see #lookAlong(double, double, double, double, double, double)
9708      * 
9709      * @param dirX
9710      *              the x-coordinate of the direction to look along
9711      * @param dirY
9712      *              the y-coordinate of the direction to look along
9713      * @param dirZ
9714      *              the z-coordinate of the direction to look along
9715      * @param upX
9716      *              the x-coordinate of the up vector
9717      * @param upY
9718      *              the y-coordinate of the up vector
9719      * @param upZ
9720      *              the z-coordinate of the up vector
9721      * @return this
9722      */
9723     ref public Matrix4d setLookAlong(double dirX, double dirY, double dirZ,
9724                                  double upX, double upY, double upZ) return {
9725         // Normalize direction
9726         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
9727         dirX *= -invDirLength;
9728         dirY *= -invDirLength;
9729         dirZ *= -invDirLength;
9730         // left = up x direction
9731         double leftX, leftY, leftZ;
9732         leftX = upY * dirZ - upZ * dirY;
9733         leftY = upZ * dirX - upX * dirZ;
9734         leftZ = upX * dirY - upY * dirX;
9735         // normalize left
9736         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
9737         leftX *= invLeftLength;
9738         leftY *= invLeftLength;
9739         leftZ *= invLeftLength;
9740         // up = direction x left
9741         double upnX = dirY * leftZ - dirZ * leftY;
9742         double upnY = dirZ * leftX - dirX * leftZ;
9743         double upnZ = dirX * leftY - dirY * leftX;
9744         _m00(leftX).
9745         _m01(upnX).
9746         _m02(dirX).
9747         _m03(0.0).
9748         _m10(leftY).
9749         _m11(upnY).
9750         _m12(dirY).
9751         _m13(0.0).
9752         _m20(leftZ).
9753         _m21(upnZ).
9754         _m22(dirZ).
9755         _m23(0.0).
9756         _m30(0.0).
9757         _m31(0.0).
9758         _m32(0.0).
9759         _m33(1.0).
9760         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
9761         return this;
9762     }
9763 
9764     /**
9765      * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, that aligns
9766      * <code>-z</code> with <code>center - eye</code>.
9767      * <p>
9768      * In order to not make use of vectors to specify <code>eye</code>, <code>center</code> and <code>up</code> but use primitives,
9769      * like in the GLU function, use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}
9770      * instead.
9771      * <p>
9772      * In order to apply the lookat transformation to a previous existing transformation,
9773      * use {@link #lookAt(ref Vector3d, Vector3d, Vector3d) lookAt()}.
9774      * 
9775      * @see #setLookAt(double, double, double, double, double, double, double, double, double)
9776      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
9777      * 
9778      * @param eye
9779      *            the position of the camera
9780      * @param center
9781      *            the point in space to look at
9782      * @param up
9783      *            the direction of 'up'
9784      * @return this
9785      */
9786     ref public Matrix4d setLookAt(ref Vector3d eye, Vector3d center, Vector3d up) return {
9787         return setLookAt(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z);
9788     }
9789 
9790     /**
9791      * Set this matrix to be a "lookat" transformation for a right-handed coordinate system, 
9792      * that aligns <code>-z</code> with <code>center - eye</code>.
9793      * <p>
9794      * In order to apply the lookat transformation to a previous existing transformation,
9795      * use {@link #lookAt(double, double, double, double, double, double, double, double, double) lookAt}.
9796      * 
9797      * @see #setLookAt(ref Vector3d, Vector3d, Vector3d)
9798      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9799      * 
9800      * @param eyeX
9801      *              the x-coordinate of the eye/camera location
9802      * @param eyeY
9803      *              the y-coordinate of the eye/camera location
9804      * @param eyeZ
9805      *              the z-coordinate of the eye/camera location
9806      * @param centerX
9807      *              the x-coordinate of the point to look at
9808      * @param centerY
9809      *              the y-coordinate of the point to look at
9810      * @param centerZ
9811      *              the z-coordinate of the point to look at
9812      * @param upX
9813      *              the x-coordinate of the up vector
9814      * @param upY
9815      *              the y-coordinate of the up vector
9816      * @param upZ
9817      *              the z-coordinate of the up vector
9818      * @return this
9819      */
9820     ref public Matrix4d setLookAt(double eyeX, double eyeY, double eyeZ,
9821                               double centerX, double centerY, double centerZ,
9822                               double upX, double upY, double upZ) return {
9823         // Compute direction from position to lookAt
9824         double dirX, dirY, dirZ;
9825         dirX = eyeX - centerX;
9826         dirY = eyeY - centerY;
9827         dirZ = eyeZ - centerZ;
9828         // Normalize direction
9829         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
9830         dirX *= invDirLength;
9831         dirY *= invDirLength;
9832         dirZ *= invDirLength;
9833         // left = up x direction
9834         double leftX, leftY, leftZ;
9835         leftX = upY * dirZ - upZ * dirY;
9836         leftY = upZ * dirX - upX * dirZ;
9837         leftZ = upX * dirY - upY * dirX;
9838         // normalize left
9839         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
9840         leftX *= invLeftLength;
9841         leftY *= invLeftLength;
9842         leftZ *= invLeftLength;
9843         // up = direction x left
9844         double upnX = dirY * leftZ - dirZ * leftY;
9845         double upnY = dirZ * leftX - dirX * leftZ;
9846         double upnZ = dirX * leftY - dirY * leftX;
9847         return this.
9848         _m00(leftX).
9849         _m01(upnX).
9850         _m02(dirX).
9851         _m03(0.0).
9852         _m10(leftY).
9853         _m11(upnY).
9854         _m12(dirY).
9855         _m13(0.0).
9856         _m20(leftZ).
9857         _m21(upnZ).
9858         _m22(dirZ).
9859         _m23(0.0).
9860         _m30(-(leftX * eyeX + leftY * eyeY + leftZ * eyeZ)).
9861         _m31(-(upnX * eyeX + upnY * eyeY + upnZ * eyeZ)).
9862         _m32(-(dirX * eyeX + dirY * eyeY + dirZ * eyeZ)).
9863         _m33(1.0).
9864         _properties(PROPERTY_AFFINE | PROPERTY_ORTHONORMAL);
9865     }
9866 
9867     /**
9868      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
9869      * that aligns <code>-z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
9870      * <p>
9871      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
9872      * then the new matrix will be <code>M * L</code>. So when transforming a
9873      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
9874      * the lookat transformation will be applied first!
9875      * <p>
9876      * In order to set the matrix to a lookat transformation without post-multiplying it,
9877      * use {@link #setLookAt(ref Vector3d, Vector3d, Vector3d)}.
9878      * 
9879      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9880      * @see #setLookAlong(ref Vector3d, Vector3d)
9881      * 
9882      * @param eye
9883      *            the position of the camera
9884      * @param center
9885      *            the point in space to look at
9886      * @param up
9887      *            the direction of 'up'
9888      * @param dest
9889      *            will hold the result
9890      * @return dest
9891      */
9892     public Matrix4d lookAt(ref Vector3d eye, Vector3d center, Vector3d up, ref Matrix4d dest) {
9893         return lookAt(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z, dest);
9894     }
9895 
9896     /**
9897      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
9898      * that aligns <code>-z</code> with <code>center - eye</code>.
9899      * <p>
9900      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
9901      * then the new matrix will be <code>M * L</code>. So when transforming a
9902      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
9903      * the lookat transformation will be applied first!
9904      * <p>
9905      * In order to set the matrix to a lookat transformation without post-multiplying it,
9906      * use {@link #setLookAt(ref Vector3d, Vector3d, Vector3d)}.
9907      * 
9908      * @see #lookAt(double, double, double, double, double, double, double, double, double)
9909      * @see #setLookAlong(ref Vector3d, Vector3d)
9910      * 
9911      * @param eye
9912      *            the position of the camera
9913      * @param center
9914      *            the point in space to look at
9915      * @param up
9916      *            the direction of 'up'
9917      * @return this
9918      */
9919     ref public Matrix4d lookAt(ref Vector3d eye, Vector3d center, Vector3d up) return {
9920         lookAt(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z, this);
9921         return this;
9922     }
9923 
9924     /**
9925      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
9926      * that aligns <code>-z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
9927      * <p>
9928      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
9929      * then the new matrix will be <code>M * L</code>. So when transforming a
9930      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
9931      * the lookat transformation will be applied first!
9932      * <p>
9933      * In order to set the matrix to a lookat transformation without post-multiplying it,
9934      * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}.
9935      * 
9936      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
9937      * @see #setLookAt(double, double, double, double, double, double, double, double, double)
9938      * 
9939      * @param eyeX
9940      *              the x-coordinate of the eye/camera location
9941      * @param eyeY
9942      *              the y-coordinate of the eye/camera location
9943      * @param eyeZ
9944      *              the z-coordinate of the eye/camera location
9945      * @param centerX
9946      *              the x-coordinate of the point to look at
9947      * @param centerY
9948      *              the y-coordinate of the point to look at
9949      * @param centerZ
9950      *              the z-coordinate of the point to look at
9951      * @param upX
9952      *              the x-coordinate of the up vector
9953      * @param upY
9954      *              the y-coordinate of the up vector
9955      * @param upZ
9956      *              the z-coordinate of the up vector
9957      * @param dest
9958      *          will hold the result
9959      * @return dest
9960      */
9961     public Matrix4d lookAt(double eyeX, double eyeY, double eyeZ,
9962                            double centerX, double centerY, double centerZ,
9963                            double upX, double upY, double upZ, ref Matrix4d dest) {
9964         if ((properties & PROPERTY_IDENTITY) != 0)
9965             return dest.setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
9966         else if ((properties & PROPERTY_PERSPECTIVE) != 0)
9967             return lookAtPerspective(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest);
9968         return lookAtGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest);
9969     }
9970     private Matrix4d lookAtGeneric(double eyeX, double eyeY, double eyeZ,
9971                                    double centerX, double centerY, double centerZ,
9972                                    double upX, double upY, double upZ, ref Matrix4d dest) {
9973         // Compute direction from position to lookAt
9974         double dirX, dirY, dirZ;
9975         dirX = eyeX - centerX;
9976         dirY = eyeY - centerY;
9977         dirZ = eyeZ - centerZ;
9978         // Normalize direction
9979         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
9980         dirX *= invDirLength;
9981         dirY *= invDirLength;
9982         dirZ *= invDirLength;
9983         // left = up x direction
9984         double leftX, leftY, leftZ;
9985         leftX = upY * dirZ - upZ * dirY;
9986         leftY = upZ * dirX - upX * dirZ;
9987         leftZ = upX * dirY - upY * dirX;
9988         // normalize left
9989         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
9990         leftX *= invLeftLength;
9991         leftY *= invLeftLength;
9992         leftZ *= invLeftLength;
9993         // up = direction x left
9994         double upnX = dirY * leftZ - dirZ * leftY;
9995         double upnY = dirZ * leftX - dirX * leftZ;
9996         double upnZ = dirX * leftY - dirY * leftX;
9997         // calculate right matrix elements
9998         double rm00 = leftX;
9999         double rm01 = upnX;
10000         double rm02 = dirX;
10001         double rm10 = leftY;
10002         double rm11 = upnY;
10003         double rm12 = dirY;
10004         double rm20 = leftZ;
10005         double rm21 = upnZ;
10006         double rm22 = dirZ;
10007         double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ);
10008         double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ);
10009         double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ);
10010         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
10011         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
10012         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
10013         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
10014         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
10015         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
10016         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
10017         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
10018         // perform optimized matrix multiplication
10019         // compute last column first, because others do not depend on it
10020         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
10021         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
10022         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
10023         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
10024         // introduce temporaries for dependent results
10025         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
10026         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
10027         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
10028         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
10029         // set the rest of the matrix elements
10030         ._m00(nm00)
10031         ._m01(nm01)
10032         ._m02(nm02)
10033         ._m03(nm03)
10034         ._m10(nm10)
10035         ._m11(nm11)
10036         ._m12(nm12)
10037         ._m13(nm13)
10038         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
10039         return dest;
10040     }
10041 
10042     /**
10043      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
10044      * that aligns <code>-z</code> with <code>center - eye</code>.
10045      * <p>
10046      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10047      * then the new matrix will be <code>M * L</code>. So when transforming a
10048      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10049      * the lookat transformation will be applied first!
10050      * <p>
10051      * In order to set the matrix to a lookat transformation without post-multiplying it,
10052      * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}.
10053      * 
10054      * @see #lookAt(ref Vector3d, Vector3d, Vector3d)
10055      * @see #setLookAt(double, double, double, double, double, double, double, double, double)
10056      * 
10057      * @param eyeX
10058      *              the x-coordinate of the eye/camera location
10059      * @param eyeY
10060      *              the y-coordinate of the eye/camera location
10061      * @param eyeZ
10062      *              the z-coordinate of the eye/camera location
10063      * @param centerX
10064      *              the x-coordinate of the point to look at
10065      * @param centerY
10066      *              the y-coordinate of the point to look at
10067      * @param centerZ
10068      *              the z-coordinate of the point to look at
10069      * @param upX
10070      *              the x-coordinate of the up vector
10071      * @param upY
10072      *              the y-coordinate of the up vector
10073      * @param upZ
10074      *              the z-coordinate of the up vector
10075      * @return this
10076      */
10077     ref public Matrix4d lookAt(double eyeX, double eyeY, double eyeZ,
10078                            double centerX, double centerY, double centerZ,
10079                            double upX, double upY, double upZ) return {
10080         lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this);
10081         return this;
10082     }
10083 
10084     /**
10085      * Apply a "lookat" transformation to this matrix for a right-handed coordinate system, 
10086      * that aligns <code>-z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
10087      * <p>
10088      * This method assumes <code>this</code> to be a perspective transformation, obtained via
10089      * {@link #frustum(double, double, double, double, double, double) frustum()} or {@link #perspective(double, double, double, double) perspective()} or
10090      * one of their overloads.
10091      * <p>
10092      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10093      * then the new matrix will be <code>M * L</code>. So when transforming a
10094      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10095      * the lookat transformation will be applied first!
10096      * <p>
10097      * In order to set the matrix to a lookat transformation without post-multiplying it,
10098      * use {@link #setLookAt(double, double, double, double, double, double, double, double, double) setLookAt()}.
10099      * 
10100      * @see #setLookAt(double, double, double, double, double, double, double, double, double)
10101      * 
10102      * @param eyeX
10103      *              the x-coordinate of the eye/camera location
10104      * @param eyeY
10105      *              the y-coordinate of the eye/camera location
10106      * @param eyeZ
10107      *              the z-coordinate of the eye/camera location
10108      * @param centerX
10109      *              the x-coordinate of the point to look at
10110      * @param centerY
10111      *              the y-coordinate of the point to look at
10112      * @param centerZ
10113      *              the z-coordinate of the point to look at
10114      * @param upX
10115      *              the x-coordinate of the up vector
10116      * @param upY
10117      *              the y-coordinate of the up vector
10118      * @param upZ
10119      *              the z-coordinate of the up vector
10120      * @param dest
10121      *          will hold the result
10122      * @return dest
10123      */
10124     public Matrix4d lookAtPerspective(double eyeX, double eyeY, double eyeZ,
10125             double centerX, double centerY, double centerZ,
10126             double upX, double upY, double upZ, ref Matrix4d dest) {
10127         // Compute direction from position to lookAt
10128         double dirX, dirY, dirZ;
10129         dirX = eyeX - centerX;
10130         dirY = eyeY - centerY;
10131         dirZ = eyeZ - centerZ;
10132         // Normalize direction
10133         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
10134         dirX *= invDirLength;
10135         dirY *= invDirLength;
10136         dirZ *= invDirLength;
10137         // left = up x direction
10138         double leftX, leftY, leftZ;
10139         leftX = upY * dirZ - upZ * dirY;
10140         leftY = upZ * dirX - upX * dirZ;
10141         leftZ = upX * dirY - upY * dirX;
10142         // normalize left
10143         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
10144         leftX *= invLeftLength;
10145         leftY *= invLeftLength;
10146         leftZ *= invLeftLength;
10147         // up = direction x left
10148         double upnX = dirY * leftZ - dirZ * leftY;
10149         double upnY = dirZ * leftX - dirX * leftZ;
10150         double upnZ = dirX * leftY - dirY * leftX;
10151         double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ);
10152         double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ);
10153         double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ);
10154         double nm10 = m00 * leftY;
10155         double nm20 = m00 * leftZ;
10156         double nm21 = m11 * upnZ;
10157         double nm30 = m00 * rm30;
10158         double nm31 = m11 * rm31;
10159         double nm32 = m22 * rm32 + m32;
10160         double nm33 = m23 * rm32;
10161         return dest
10162         ._m00(m00 * leftX)
10163         ._m01(m11 * upnX)
10164         ._m02(m22 * dirX)
10165         ._m03(m23 * dirX)
10166         ._m10(nm10)
10167         ._m11(m11 * upnY)
10168         ._m12(m22 * dirY)
10169         ._m13(m23 * dirY)
10170         ._m20(nm20)
10171         ._m21(nm21)
10172         ._m22(m22 * dirZ)
10173         ._m23(m23 * dirZ)
10174         ._m30(nm30)
10175         ._m31(nm31)
10176         ._m32(nm32)
10177         ._m33(nm33)
10178         ._properties(0);
10179     }
10180 
10181     /**
10182      * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, that aligns
10183      * <code>+z</code> with <code>center - eye</code>.
10184      * <p>
10185      * In order to not make use of vectors to specify <code>eye</code>, <code>center</code> and <code>up</code> but use primitives,
10186      * like in the GLU function, use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}
10187      * instead.
10188      * <p>
10189      * In order to apply the lookat transformation to a previous existing transformation,
10190      * use {@link #lookAtLH(ref Vector3d, Vector3d, Vector3d) lookAt()}.
10191      * 
10192      * @see #setLookAtLH(double, double, double, double, double, double, double, double, double)
10193      * @see #lookAtLH(ref Vector3d, Vector3d, Vector3d)
10194      * 
10195      * @param eye
10196      *            the position of the camera
10197      * @param center
10198      *            the point in space to look at
10199      * @param up
10200      *            the direction of 'up'
10201      * @return this
10202      */
10203     ref public Matrix4d setLookAtLH(ref Vector3d eye, Vector3d center, Vector3d up) return {
10204         return setLookAtLH(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z);
10205     }
10206 
10207     /**
10208      * Set this matrix to be a "lookat" transformation for a left-handed coordinate system, 
10209      * that aligns <code>+z</code> with <code>center - eye</code>.
10210      * <p>
10211      * In order to apply the lookat transformation to a previous existing transformation,
10212      * use {@link #lookAtLH(double, double, double, double, double, double, double, double, double) lookAtLH}.
10213      * 
10214      * @see #setLookAtLH(ref Vector3d, Vector3d, Vector3d)
10215      * @see #lookAtLH(double, double, double, double, double, double, double, double, double)
10216      * 
10217      * @param eyeX
10218      *              the x-coordinate of the eye/camera location
10219      * @param eyeY
10220      *              the y-coordinate of the eye/camera location
10221      * @param eyeZ
10222      *              the z-coordinate of the eye/camera location
10223      * @param centerX
10224      *              the x-coordinate of the point to look at
10225      * @param centerY
10226      *              the y-coordinate of the point to look at
10227      * @param centerZ
10228      *              the z-coordinate of the point to look at
10229      * @param upX
10230      *              the x-coordinate of the up vector
10231      * @param upY
10232      *              the y-coordinate of the up vector
10233      * @param upZ
10234      *              the z-coordinate of the up vector
10235      * @return this
10236      */
10237     ref public Matrix4d setLookAtLH(double eyeX, double eyeY, double eyeZ,
10238                                 double centerX, double centerY, double centerZ,
10239                                 double upX, double upY, double upZ) return {
10240         // Compute direction from position to lookAt
10241         double dirX, dirY, dirZ;
10242         dirX = centerX - eyeX;
10243         dirY = centerY - eyeY;
10244         dirZ = centerZ - eyeZ;
10245         // Normalize direction
10246         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
10247         dirX *= invDirLength;
10248         dirY *= invDirLength;
10249         dirZ *= invDirLength;
10250         // left = up x direction
10251         double leftX, leftY, leftZ;
10252         leftX = upY * dirZ - upZ * dirY;
10253         leftY = upZ * dirX - upX * dirZ;
10254         leftZ = upX * dirY - upY * dirX;
10255         // normalize left
10256         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
10257         leftX *= invLeftLength;
10258         leftY *= invLeftLength;
10259         leftZ *= invLeftLength;
10260         // up = direction x left
10261         double upnX = dirY * leftZ - dirZ * leftY;
10262         double upnY = dirZ * leftX - dirX * leftZ;
10263         double upnZ = dirX * leftY - dirY * leftX;
10264         _m00(leftX).
10265         _m01(upnX).
10266         _m02(dirX).
10267         _m03(0.0).
10268         _m10(leftY).
10269         _m11(upnY).
10270         _m12(dirY).
10271         _m13(0.0).
10272         _m20(leftZ).
10273         _m21(upnZ).
10274         _m22(dirZ).
10275         _m23(0.0).
10276         _m30(-(leftX * eyeX + leftY * eyeY + leftZ * eyeZ)).
10277         _m31(-(upnX * eyeX + upnY * eyeY + upnZ * eyeZ)).
10278         _m32(-(dirX * eyeX + dirY * eyeY + dirZ * eyeZ)).
10279         _m33(1.0).
10280         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
10281         return this;
10282     }
10283 
10284     /**
10285      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10286      * that aligns <code>+z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
10287      * <p>
10288      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10289      * then the new matrix will be <code>M * L</code>. So when transforming a
10290      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10291      * the lookat transformation will be applied first!
10292      * <p>
10293      * In order to set the matrix to a lookat transformation without post-multiplying it,
10294      * use {@link #setLookAtLH(ref Vector3d, Vector3d, Vector3d)}.
10295      * 
10296      * @see #lookAtLH(double, double, double, double, double, double, double, double, double)
10297      * @see #setLookAtLH(ref Vector3d, Vector3d, Vector3d)
10298      * 
10299      * @param eye
10300      *            the position of the camera
10301      * @param center
10302      *            the point in space to look at
10303      * @param up
10304      *            the direction of 'up'
10305      * @param dest
10306      *            will hold the result
10307      * @return dest
10308      */
10309     public Matrix4d lookAtLH(ref Vector3d eye, Vector3d center, Vector3d up, ref Matrix4d dest) {
10310         return lookAtLH(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z, dest);
10311     }
10312 
10313     /**
10314      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10315      * that aligns <code>+z</code> with <code>center - eye</code>.
10316      * <p>
10317      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10318      * then the new matrix will be <code>M * L</code>. So when transforming a
10319      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10320      * the lookat transformation will be applied first!
10321      * <p>
10322      * In order to set the matrix to a lookat transformation without post-multiplying it,
10323      * use {@link #setLookAtLH(ref Vector3d, Vector3d, Vector3d)}.
10324      * 
10325      * @see #lookAtLH(double, double, double, double, double, double, double, double, double)
10326      * 
10327      * @param eye
10328      *            the position of the camera
10329      * @param center
10330      *            the point in space to look at
10331      * @param up
10332      *            the direction of 'up'
10333      * @return this
10334      */
10335     ref public Matrix4d lookAtLH(ref Vector3d eye, Vector3d center, Vector3d up) return {
10336         lookAtLH(eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z, this);
10337         return this;
10338     }
10339 
10340     /**
10341      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10342      * that aligns <code>+z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
10343      * <p>
10344      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10345      * then the new matrix will be <code>M * L</code>. So when transforming a
10346      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10347      * the lookat transformation will be applied first!
10348      * <p>
10349      * In order to set the matrix to a lookat transformation without post-multiplying it,
10350      * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}.
10351      * 
10352      * @see #lookAtLH(ref Vector3d, Vector3d, Vector3d)
10353      * @see #setLookAtLH(double, double, double, double, double, double, double, double, double)
10354      * 
10355      * @param eyeX
10356      *              the x-coordinate of the eye/camera location
10357      * @param eyeY
10358      *              the y-coordinate of the eye/camera location
10359      * @param eyeZ
10360      *              the z-coordinate of the eye/camera location
10361      * @param centerX
10362      *              the x-coordinate of the point to look at
10363      * @param centerY
10364      *              the y-coordinate of the point to look at
10365      * @param centerZ
10366      *              the z-coordinate of the point to look at
10367      * @param upX
10368      *              the x-coordinate of the up vector
10369      * @param upY
10370      *              the y-coordinate of the up vector
10371      * @param upZ
10372      *              the z-coordinate of the up vector
10373      * @param dest
10374      *          will hold the result
10375      * @return dest
10376      */
10377     public Matrix4d lookAtLH(double eyeX, double eyeY, double eyeZ,
10378                              double centerX, double centerY, double centerZ,
10379                              double upX, double upY, double upZ, ref Matrix4d dest) {
10380         if ((properties & PROPERTY_IDENTITY) != 0)
10381             return dest.setLookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
10382         else if ((properties & PROPERTY_PERSPECTIVE) != 0)
10383             return lookAtPerspectiveLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest);
10384         return lookAtLHGeneric(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, dest);
10385     }
10386     private Matrix4d lookAtLHGeneric(double eyeX, double eyeY, double eyeZ,
10387                                      double centerX, double centerY, double centerZ,
10388                                      double upX, double upY, double upZ, ref Matrix4d dest) {
10389         // Compute direction from position to lookAt
10390         double dirX, dirY, dirZ;
10391         dirX = centerX - eyeX;
10392         dirY = centerY - eyeY;
10393         dirZ = centerZ - eyeZ;
10394         // Normalize direction
10395         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
10396         dirX *= invDirLength;
10397         dirY *= invDirLength;
10398         dirZ *= invDirLength;
10399         // left = up x direction
10400         double leftX, leftY, leftZ;
10401         leftX = upY * dirZ - upZ * dirY;
10402         leftY = upZ * dirX - upX * dirZ;
10403         leftZ = upX * dirY - upY * dirX;
10404         // normalize left
10405         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
10406         leftX *= invLeftLength;
10407         leftY *= invLeftLength;
10408         leftZ *= invLeftLength;
10409         // up = direction x left
10410         double upnX = dirY * leftZ - dirZ * leftY;
10411         double upnY = dirZ * leftX - dirX * leftZ;
10412         double upnZ = dirX * leftY - dirY * leftX;
10413         // calculate right matrix elements
10414         double rm00 = leftX;
10415         double rm01 = upnX;
10416         double rm02 = dirX;
10417         double rm10 = leftY;
10418         double rm11 = upnY;
10419         double rm12 = dirY;
10420         double rm20 = leftZ;
10421         double rm21 = upnZ;
10422         double rm22 = dirZ;
10423         double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ);
10424         double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ);
10425         double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ);
10426         // introduce temporaries for dependent results
10427         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
10428         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
10429         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
10430         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
10431         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
10432         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
10433         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
10434         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
10435         // perform optimized matrix multiplication
10436         // compute last column first, because others do not depend on it
10437         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30)
10438         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31)
10439         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32)
10440         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33)
10441         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
10442         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
10443         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
10444         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
10445         // set the rest of the matrix elements
10446         ._m00(nm00)
10447         ._m01(nm01)
10448         ._m02(nm02)
10449         ._m03(nm03)
10450         ._m10(nm10)
10451         ._m11(nm11)
10452         ._m12(nm12)
10453         ._m13(nm13)
10454         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
10455         return dest;
10456     }
10457 
10458     /**
10459      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10460      * that aligns <code>+z</code> with <code>center - eye</code>.
10461      * <p>
10462      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10463      * then the new matrix will be <code>M * L</code>. So when transforming a
10464      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10465      * the lookat transformation will be applied first!
10466      * <p>
10467      * In order to set the matrix to a lookat transformation without post-multiplying it,
10468      * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}.
10469      * 
10470      * @see #lookAtLH(ref Vector3d, Vector3d, Vector3d)
10471      * @see #setLookAtLH(double, double, double, double, double, double, double, double, double)
10472      * 
10473      * @param eyeX
10474      *              the x-coordinate of the eye/camera location
10475      * @param eyeY
10476      *              the y-coordinate of the eye/camera location
10477      * @param eyeZ
10478      *              the z-coordinate of the eye/camera location
10479      * @param centerX
10480      *              the x-coordinate of the point to look at
10481      * @param centerY
10482      *              the y-coordinate of the point to look at
10483      * @param centerZ
10484      *              the z-coordinate of the point to look at
10485      * @param upX
10486      *              the x-coordinate of the up vector
10487      * @param upY
10488      *              the y-coordinate of the up vector
10489      * @param upZ
10490      *              the z-coordinate of the up vector
10491      * @return this
10492      */
10493     ref public Matrix4d lookAtLH(double eyeX, double eyeY, double eyeZ,
10494                              double centerX, double centerY, double centerZ,
10495                              double upX, double upY, double upZ) return {
10496         lookAtLH(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ, this);
10497         return this;
10498     }
10499 
10500     /**
10501      * Apply a "lookat" transformation to this matrix for a left-handed coordinate system, 
10502      * that aligns <code>+z</code> with <code>center - eye</code> and store the result in <code>dest</code>.
10503      * <p>
10504      * This method assumes <code>this</code> to be a perspective transformation, obtained via
10505      * {@link #frustumLH(double, double, double, double, double, double) frustumLH()} or {@link #perspectiveLH(double, double, double, double) perspectiveLH()} or
10506      * one of their overloads.
10507      * <p>
10508      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
10509      * then the new matrix will be <code>M * L</code>. So when transforming a
10510      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
10511      * the lookat transformation will be applied first!
10512      * <p>
10513      * In order to set the matrix to a lookat transformation without post-multiplying it,
10514      * use {@link #setLookAtLH(double, double, double, double, double, double, double, double, double) setLookAtLH()}.
10515      * 
10516      * @see #setLookAtLH(double, double, double, double, double, double, double, double, double)
10517      * 
10518      * @param eyeX
10519      *              the x-coordinate of the eye/camera location
10520      * @param eyeY
10521      *              the y-coordinate of the eye/camera location
10522      * @param eyeZ
10523      *              the z-coordinate of the eye/camera location
10524      * @param centerX
10525      *              the x-coordinate of the point to look at
10526      * @param centerY
10527      *              the y-coordinate of the point to look at
10528      * @param centerZ
10529      *              the z-coordinate of the point to look at
10530      * @param upX
10531      *              the x-coordinate of the up vector
10532      * @param upY
10533      *              the y-coordinate of the up vector
10534      * @param upZ
10535      *              the z-coordinate of the up vector
10536      * @param dest
10537      *          will hold the result
10538      * @return dest
10539      */
10540     public Matrix4d lookAtPerspectiveLH(double eyeX, double eyeY, double eyeZ,
10541             double centerX, double centerY, double centerZ,
10542             double upX, double upY, double upZ, ref Matrix4d dest) {
10543         // Compute direction from position to lookAt
10544         double dirX, dirY, dirZ;
10545         dirX = centerX - eyeX;
10546         dirY = centerY - eyeY;
10547         dirZ = centerZ - eyeZ;
10548         // Normalize direction
10549         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
10550         dirX *= invDirLength;
10551         dirY *= invDirLength;
10552         dirZ *= invDirLength;
10553         // left = up x direction
10554         double leftX, leftY, leftZ;
10555         leftX = upY * dirZ - upZ * dirY;
10556         leftY = upZ * dirX - upX * dirZ;
10557         leftZ = upX * dirY - upY * dirX;
10558         // normalize left
10559         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
10560         leftX *= invLeftLength;
10561         leftY *= invLeftLength;
10562         leftZ *= invLeftLength;
10563         // up = direction x left
10564         double upnX = dirY * leftZ - dirZ * leftY;
10565         double upnY = dirZ * leftX - dirX * leftZ;
10566         double upnZ = dirX * leftY - dirY * leftX;
10567 
10568         // calculate right matrix elements
10569         double rm00 = leftX;
10570         double rm01 = upnX;
10571         double rm02 = dirX;
10572         double rm10 = leftY;
10573         double rm11 = upnY;
10574         double rm12 = dirY;
10575         double rm20 = leftZ;
10576         double rm21 = upnZ;
10577         double rm22 = dirZ;
10578         double rm30 = -(leftX * eyeX + leftY * eyeY + leftZ * eyeZ);
10579         double rm31 = -(upnX * eyeX + upnY * eyeY + upnZ * eyeZ);
10580         double rm32 = -(dirX * eyeX + dirY * eyeY + dirZ * eyeZ);
10581 
10582         double nm00 = m00 * rm00;
10583         double nm01 = m11 * rm01;
10584         double nm02 = m22 * rm02;
10585         double nm03 = m23 * rm02;
10586         double nm10 = m00 * rm10;
10587         double nm11 = m11 * rm11;
10588         double nm12 = m22 * rm12;
10589         double nm13 = m23 * rm12;
10590         double nm20 = m00 * rm20;
10591         double nm21 = m11 * rm21;
10592         double nm22 = m22 * rm22;
10593         double nm23 = m23 * rm22;
10594         double nm30 = m00 * rm30;
10595         double nm31 = m11 * rm31;
10596         double nm32 = m22 * rm32 + m32;
10597         double nm33 = m23 * rm32;
10598         dest._m00(nm00)
10599         ._m01(nm01)
10600         ._m02(nm02)
10601         ._m03(nm03)
10602         ._m10(nm10)
10603         ._m11(nm11)
10604         ._m12(nm12)
10605         ._m13(nm13)
10606         ._m20(nm20)
10607         ._m21(nm21)
10608         ._m22(nm22)
10609         ._m23(nm23)
10610         ._m30(nm30)
10611         ._m31(nm31)
10612         ._m32(nm32)
10613         ._m33(nm33)
10614         ._properties(0);
10615 
10616         return dest;
10617     }
10618 
10619     /**
10620      * This method is equivalent to calling: <code>translate(w-1-2*x, h-1-2*y, 0).scale(w, h, 1)</code>
10621      * <p>
10622      * If <code>M</code> is <code>this</code> matrix and <code>T</code> the created transformation matrix,
10623      * then the new matrix will be <code>M * T</code>. So when transforming a
10624      * vector <code>v</code> with the new matrix by using <code>M * T * v</code>, the
10625      * created transformation will be applied first!
10626      * 
10627      * @param x
10628      *             the tile's x coordinate/index (should be in <code>[0..w)</code>)
10629      * @param y
10630      *             the tile's y coordinate/index (should be in <code>[0..h)</code>)
10631      * @param w
10632      *             the number of tiles along the x axis
10633      * @param h
10634      *             the number of tiles along the y axis
10635      * @return this
10636      */
10637     ref public Matrix4d tile(int x, int y, int w, int h) return {
10638         tile(x, y, w, h, this);
10639         return this;
10640     }
10641     public Matrix4d tile(int x, int y, int w, int h, ref Matrix4d dest) {
10642         float tx = w - 1 - (x<<1), ty = h - 1 - (y<<1);
10643         return dest
10644         ._m30(Math.fma(m00, tx, Math.fma(m10, ty, m30)))
10645         ._m31(Math.fma(m01, tx, Math.fma(m11, ty, m31)))
10646         ._m32(Math.fma(m02, tx, Math.fma(m12, ty, m32)))
10647         ._m33(Math.fma(m03, tx, Math.fma(m13, ty, m33)))
10648         ._m00(m00 * w)
10649         ._m01(m01 * w)
10650         ._m02(m02 * w)
10651         ._m03(m03 * w)
10652         ._m10(m10 * h)
10653         ._m11(m11 * h)
10654         ._m12(m12 * h)
10655         ._m13(m13 * h)
10656         ._m20(m20)
10657         ._m21(m21)
10658         ._m22(m22)
10659         ._m23(m23)
10660         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
10661     }
10662 
10663     /**
10664      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10665      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
10666      * <p>
10667      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10668      * then the new matrix will be <code>M * P</code>. So when transforming a
10669      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10670      * the perspective projection will be applied first!
10671      * <p>
10672      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10673      * use {@link #setPerspective(double, double, double, double, bool) setPerspective}.
10674      * 
10675      * @see #setPerspective(double, double, double, double, bool)
10676      * 
10677      * @param fovy
10678      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
10679      * @param aspect
10680      *            the aspect ratio (i.e. width / height; must be greater than zero)
10681      * @param zNear
10682      *            near clipping plane distance. This value must be greater than zero.
10683      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10684      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10685      * @param zFar
10686      *            far clipping plane distance. This value must be greater than zero.
10687      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10688      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10689      * @param dest
10690      *            will hold the result
10691      * @param zZeroToOne
10692      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
10693      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
10694      * @return dest
10695      */
10696     public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
10697         if ((properties & PROPERTY_IDENTITY) != 0)
10698             return dest.setPerspective(fovy, aspect, zNear, zFar, zZeroToOne);
10699         return perspectiveGeneric(fovy, aspect, zNear, zFar, zZeroToOne, dest);
10700     }
10701     private Matrix4d perspectiveGeneric(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
10702         double h = Math.tan(fovy * 0.5);
10703         // calculate right matrix elements
10704         double rm00 = 1.0 / (h * aspect);
10705         double rm11 = 1.0 / h;
10706         double rm22;
10707         double rm32;
10708         bool farInf = zFar > 0 && Math.isInfinite(zFar);
10709         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
10710         if (farInf) {
10711             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
10712             double e = 1E-6;
10713             rm22 = e - 1.0;
10714             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
10715         } else if (nearInf) {
10716             double e = 1E-6;
10717             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
10718             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
10719         } else {
10720             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar);
10721             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
10722         }
10723         // perform optimized matrix multiplication
10724         double nm20 = m20 * rm22 - m30;
10725         double nm21 = m21 * rm22 - m31;
10726         double nm22 = m22 * rm22 - m32;
10727         double nm23 = m23 * rm22 - m33;
10728         dest._m00(m00 * rm00)
10729         ._m01(m01 * rm00)
10730         ._m02(m02 * rm00)
10731         ._m03(m03 * rm00)
10732         ._m10(m10 * rm11)
10733         ._m11(m11 * rm11)
10734         ._m12(m12 * rm11)
10735         ._m13(m13 * rm11)
10736         ._m30(m20 * rm32)
10737         ._m31(m21 * rm32)
10738         ._m32(m22 * rm32)
10739         ._m33(m23 * rm32)
10740         ._m20(nm20)
10741         ._m21(nm21)
10742         ._m22(nm22)
10743         ._m23(nm23)
10744         ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
10745         return dest;
10746     }
10747 
10748     /**
10749      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10750      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
10751      * <p>
10752      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10753      * then the new matrix will be <code>M * P</code>. So when transforming a
10754      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10755      * the perspective projection will be applied first!
10756      * <p>
10757      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10758      * use {@link #setPerspective(double, double, double, double) setPerspective}.
10759      * 
10760      * @see #setPerspective(double, double, double, double)
10761      * 
10762      * @param fovy
10763      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
10764      * @param aspect
10765      *            the aspect ratio (i.e. width / height; must be greater than zero)
10766      * @param zNear
10767      *            near clipping plane distance. This value must be greater than zero.
10768      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10769      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10770      * @param zFar
10771      *            far clipping plane distance. This value must be greater than zero.
10772      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10773      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10774      * @param dest
10775      *            will hold the result
10776      * @return dest
10777      */
10778     public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, ref Matrix4d dest) {
10779         return perspective(fovy, aspect, zNear, zFar, false, dest);
10780     }
10781 
10782     /**
10783      * Apply a symmetric perspective projection frustum transformation using for a right-handed coordinate system
10784      * using the given NDC z range to this matrix.
10785      * <p>
10786      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10787      * then the new matrix will be <code>M * P</code>. So when transforming a
10788      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10789      * the perspective projection will be applied first!
10790      * <p>
10791      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10792      * use {@link #setPerspective(double, double, double, double, bool) setPerspective}.
10793      * 
10794      * @see #setPerspective(double, double, double, double, bool)
10795      * 
10796      * @param fovy
10797      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
10798      * @param aspect
10799      *            the aspect ratio (i.e. width / height; must be greater than zero)
10800      * @param zNear
10801      *            near clipping plane distance. This value must be greater than zero.
10802      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10803      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10804      * @param zFar
10805      *            far clipping plane distance. This value must be greater than zero.
10806      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10807      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10808      * @param zZeroToOne
10809      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
10810      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
10811      * @return this
10812      */
10813     ref public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne) return {
10814         perspective(fovy, aspect, zNear, zFar, zZeroToOne, this);
10815         return this;
10816     }
10817 
10818     /**
10819      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10820      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
10821      * <p>
10822      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10823      * then the new matrix will be <code>M * P</code>. So when transforming a
10824      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10825      * the perspective projection will be applied first!
10826      * <p>
10827      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10828      * use {@link #setPerspective(double, double, double, double) setPerspective}.
10829      * 
10830      * @see #setPerspective(double, double, double, double)
10831      * 
10832      * @param fovy
10833      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
10834      * @param aspect
10835      *            the aspect ratio (i.e. width / height; must be greater than zero)
10836      * @param zNear
10837      *            near clipping plane distance. This value must be greater than zero.
10838      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10839      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10840      * @param zFar
10841      *            far clipping plane distance. This value must be greater than zero.
10842      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10843      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10844      * @return this
10845      */    
10846     ref public Matrix4d perspective(double fovy, double aspect, double zNear, double zFar) return {
10847         perspective(fovy, aspect, zNear, zFar, this);
10848         return this;
10849     }
10850 
10851     /**
10852      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10853      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
10854      * <p>
10855      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10856      * then the new matrix will be <code>M * P</code>. So when transforming a
10857      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10858      * the perspective projection will be applied first!
10859      * <p>
10860      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10861      * use {@link #setPerspectiveRect(double, double, double, double, bool) setPerspectiveRect}.
10862      * 
10863      * @see #setPerspectiveRect(double, double, double, double, bool)
10864      * 
10865      * @param width
10866      *            the width of the near frustum plane
10867      * @param height
10868      *            the height of the near frustum plane
10869      * @param zNear
10870      *            near clipping plane distance. This value must be greater than zero.
10871      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10872      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10873      * @param zFar
10874      *            far clipping plane distance. This value must be greater than zero.
10875      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10876      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10877      * @param dest
10878      *            will hold the result
10879      * @param zZeroToOne
10880      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
10881      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
10882      * @return dest
10883      */
10884     public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
10885         if ((properties & PROPERTY_IDENTITY) != 0)
10886             return dest.setPerspectiveRect(width, height, zNear, zFar, zZeroToOne);
10887         return perspectiveRectGeneric(width, height, zNear, zFar, zZeroToOne, dest);
10888     }
10889     private Matrix4d perspectiveRectGeneric(double width, double height, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
10890         double rm00 = (zNear + zNear) / width;
10891         double rm11 = (zNear + zNear) / height;
10892         double rm22, rm32;
10893         bool farInf = zFar > 0 && Math.isInfinite(zFar);
10894         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
10895         if (farInf) {
10896             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
10897             double e = 1E-6f;
10898             rm22 = e - 1.0;
10899             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
10900         } else if (nearInf) {
10901             double e = 1E-6f;
10902             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
10903             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
10904         } else {
10905             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar);
10906             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
10907         }
10908         // perform optimized matrix multiplication
10909         double nm20 = m20 * rm22 - m30;
10910         double nm21 = m21 * rm22 - m31;
10911         double nm22 = m22 * rm22 - m32;
10912         double nm23 = m23 * rm22 - m33;
10913         dest._m00(m00 * rm00)
10914         ._m01(m01 * rm00)
10915         ._m02(m02 * rm00)
10916         ._m03(m03 * rm00)
10917         ._m10(m10 * rm11)
10918         ._m11(m11 * rm11)
10919         ._m12(m12 * rm11)
10920         ._m13(m13 * rm11)
10921         ._m30(m20 * rm32)
10922         ._m31(m21 * rm32)
10923         ._m32(m22 * rm32)
10924         ._m33(m23 * rm32)
10925         ._m20(nm20)
10926         ._m21(nm21)
10927         ._m22(nm22)
10928         ._m23(nm23)
10929         ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
10930         return dest;
10931     }
10932 
10933     /**
10934      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
10935      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
10936      * <p>
10937      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10938      * then the new matrix will be <code>M * P</code>. So when transforming a
10939      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10940      * the perspective projection will be applied first!
10941      * <p>
10942      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10943      * use {@link #setPerspectiveRect(double, double, double, double) setPerspectiveRect}.
10944      * 
10945      * @see #setPerspectiveRect(double, double, double, double)
10946      * 
10947      * @param width
10948      *            the width of the near frustum plane
10949      * @param height
10950      *            the height of the near frustum plane
10951      * @param zNear
10952      *            near clipping plane distance. This value must be greater than zero.
10953      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10954      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10955      * @param zFar
10956      *            far clipping plane distance. This value must be greater than zero.
10957      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10958      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10959      * @param dest
10960      *            will hold the result
10961      * @return dest
10962      */
10963     public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, ref Matrix4d dest) {
10964         return perspectiveRect(width, height, zNear, zFar, false, dest);
10965     }
10966 
10967     /**
10968      * Apply a symmetric perspective projection frustum transformation using for a right-handed coordinate system
10969      * using the given NDC z range to this matrix.
10970      * <p>
10971      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
10972      * then the new matrix will be <code>M * P</code>. So when transforming a
10973      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
10974      * the perspective projection will be applied first!
10975      * <p>
10976      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
10977      * use {@link #setPerspectiveRect(double, double, double, double, bool) setPerspectiveRect}.
10978      * 
10979      * @see #setPerspectiveRect(double, double, double, double, bool)
10980      * 
10981      * @param width
10982      *            the width of the near frustum plane
10983      * @param height
10984      *            the height of the near frustum plane
10985      * @param zNear
10986      *            near clipping plane distance. This value must be greater than zero.
10987      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
10988      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
10989      * @param zFar
10990      *            far clipping plane distance. This value must be greater than zero.
10991      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
10992      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
10993      * @param zZeroToOne
10994      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
10995      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
10996      * @return this
10997      */
10998     ref public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
10999         perspectiveRect(width, height, zNear, zFar, zZeroToOne, this);
11000         return this;
11001     }
11002 
11003     /**
11004      * Apply a symmetric perspective projection frustum transformation for a right-handed coordinate system
11005      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
11006      * <p>
11007      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11008      * then the new matrix will be <code>M * P</code>. So when transforming a
11009      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11010      * the perspective projection will be applied first!
11011      * <p>
11012      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11013      * use {@link #setPerspectiveRect(double, double, double, double) setPerspectiveRect}.
11014      * 
11015      * @see #setPerspectiveRect(double, double, double, double)
11016      * 
11017      * @param width
11018      *            the width of the near frustum plane
11019      * @param height
11020      *            the height of the near frustum plane
11021      * @param zNear
11022      *            near clipping plane distance. This value must be greater than zero.
11023      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11024      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11025      * @param zFar
11026      *            far clipping plane distance. This value must be greater than zero.
11027      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11028      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11029      * @return this
11030      */
11031     ref public Matrix4d perspectiveRect(double width, double height, double zNear, double zFar) return {
11032         perspectiveRect(width, height, zNear, zFar, this);
11033         return this;
11034     }
11035 
11036     /**
11037      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11038      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
11039      * <p>
11040      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11041      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11042      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11043      * is parallel to the XZ-plane.
11044      * <p>
11045      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11046      * then the new matrix will be <code>M * P</code>. So when transforming a
11047      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11048      * the perspective projection will be applied first!
11049      * <p>
11050      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11051      * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double, bool) setPerspectiveOffCenter}.
11052      * 
11053      * @see #setPerspectiveOffCenter(double, double, double, double, double, double, bool)
11054      * 
11055      * @param fovy
11056      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11057      * @param offAngleX
11058      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11059      * @param offAngleY
11060      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11061      * @param aspect
11062      *            the aspect ratio (i.e. width / height; must be greater than zero)
11063      * @param zNear
11064      *            near clipping plane distance. This value must be greater than zero.
11065      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11066      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11067      * @param zFar
11068      *            far clipping plane distance. This value must be greater than zero.
11069      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11070      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11071      * @param dest
11072      *            will hold the result
11073      * @param zZeroToOne
11074      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11075      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11076      * @return dest
11077      */
11078     public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11079         if ((properties & PROPERTY_IDENTITY) != 0)
11080             return dest.setPerspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne);
11081         return perspectiveOffCenterGeneric(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne, dest);
11082     }
11083     private Matrix4d perspectiveOffCenterGeneric(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11084         double h = Math.tan(fovy * 0.5);
11085         // calculate right matrix elements
11086         double xScale = 1.0 / (h * aspect);
11087         double yScale = 1.0 / h;
11088         double rm00 = xScale;
11089         double rm11 = yScale;
11090         double offX = Math.tan(offAngleX), offY = Math.tan(offAngleY);
11091         double rm20 = offX * xScale;
11092         double rm21 = offY * yScale;
11093         double rm22;
11094         double rm32;
11095         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11096         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11097         if (farInf) {
11098             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11099             double e = 1E-6;
11100             rm22 = e - 1.0;
11101             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
11102         } else if (nearInf) {
11103             double e = 1E-6;
11104             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
11105             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
11106         } else {
11107             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar);
11108             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
11109         }
11110         // perform optimized matrix multiplication
11111         double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 - m30;
11112         double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 - m31;
11113         double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 - m32;
11114         double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 - m33;
11115         dest._m00(m00 * rm00)
11116         ._m01(m01 * rm00)
11117         ._m02(m02 * rm00)
11118         ._m03(m03 * rm00)
11119         ._m10(m10 * rm11)
11120         ._m11(m11 * rm11)
11121         ._m12(m12 * rm11)
11122         ._m13(m13 * rm11)
11123         ._m30(m20 * rm32)
11124         ._m31(m21 * rm32)
11125         ._m32(m22 * rm32)
11126         ._m33(m23 * rm32)
11127         ._m20(nm20)
11128         ._m21(nm21)
11129         ._m22(nm22)
11130         ._m23(nm23)
11131         ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION
11132                 | PROPERTY_ORTHONORMAL | (rm20 == 0.0 && rm21 == 0.0 ? 0 : PROPERTY_PERSPECTIVE)));
11133         return dest;
11134     }
11135 
11136     /**
11137      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11138      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
11139      * <p>
11140      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11141      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11142      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11143      * is parallel to the XZ-plane.
11144      * <p>
11145      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11146      * then the new matrix will be <code>M * P</code>. So when transforming a
11147      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11148      * the perspective projection will be applied first!
11149      * <p>
11150      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11151      * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double) setPerspectiveOffCenter}.
11152      * 
11153      * @see #setPerspectiveOffCenter(double, double, double, double, double, double)
11154      * 
11155      * @param fovy
11156      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11157      * @param offAngleX
11158      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11159      * @param offAngleY
11160      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11161      * @param aspect
11162      *            the aspect ratio (i.e. width / height; must be greater than zero)
11163      * @param zNear
11164      *            near clipping plane distance. This value must be greater than zero.
11165      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11166      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11167      * @param zFar
11168      *            far clipping plane distance. This value must be greater than zero.
11169      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11170      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11171      * @param dest
11172      *            will hold the result
11173      * @return dest
11174      */
11175     public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, ref Matrix4d dest) {
11176         return perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, false, dest);
11177     }
11178 
11179     /**
11180      * Apply an asymmetric off-center perspective projection frustum transformation using for a right-handed coordinate system
11181      * using the given NDC z range to this matrix.
11182      * <p>
11183      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11184      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11185      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11186      * is parallel to the XZ-plane.
11187      * <p>
11188      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11189      * then the new matrix will be <code>M * P</code>. So when transforming a
11190      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11191      * the perspective projection will be applied first!
11192      * <p>
11193      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11194      * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double, bool) setPerspectiveOffCenter}.
11195      * 
11196      * @see #setPerspectiveOffCenter(double, double, double, double, double, double, bool)
11197      * 
11198      * @param fovy
11199      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11200      * @param offAngleX
11201      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11202      * @param offAngleY
11203      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11204      * @param aspect
11205      *            the aspect ratio (i.e. width / height; must be greater than zero)
11206      * @param zNear
11207      *            near clipping plane distance. This value must be greater than zero.
11208      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11209      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11210      * @param zFar
11211      *            far clipping plane distance. This value must be greater than zero.
11212      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11213      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11214      * @param zZeroToOne
11215      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11216      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11217      * @return this
11218      */
11219     ref public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar, bool zZeroToOne) return {
11220         perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, zZeroToOne, this);
11221         return this;
11222     }
11223 
11224     /**
11225      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11226      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
11227      * <p>
11228      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11229      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11230      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11231      * is parallel to the XZ-plane.
11232      * <p>
11233      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11234      * then the new matrix will be <code>M * P</code>. So when transforming a
11235      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11236      * the perspective projection will be applied first!
11237      * <p>
11238      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11239      * use {@link #setPerspectiveOffCenter(double, double, double, double, double, double) setPerspectiveOffCenter}.
11240      * 
11241      * @see #setPerspectiveOffCenter(double, double, double, double, double, double)
11242      * 
11243      * @param fovy
11244      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11245      * @param offAngleX
11246      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11247      * @param offAngleY
11248      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11249      * @param aspect
11250      *            the aspect ratio (i.e. width / height; must be greater than zero)
11251      * @param zNear
11252      *            near clipping plane distance. This value must be greater than zero.
11253      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11254      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11255      * @param zFar
11256      *            far clipping plane distance. This value must be greater than zero.
11257      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11258      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11259      * @return this
11260      */
11261     ref public Matrix4d perspectiveOffCenter(double fovy, double offAngleX, double offAngleY, double aspect, double zNear, double zFar) return {
11262         perspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, this);
11263         return this;
11264     }
11265 
11266     /**
11267      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11268      * using the given NDC z range to this matrix.
11269      * <p>
11270      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11271      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11272      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11273      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11274      * <p>
11275      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11276      * then the new matrix will be <code>M * P</code>. So when transforming a
11277      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11278      * the perspective projection will be applied first!
11279      * <p>
11280      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11281      * use {@link #setPerspectiveOffCenterFov(double, double, double, double, double, double, bool) setPerspectiveOffCenterFov}.
11282      * 
11283      * @see #setPerspectiveOffCenterFov(double, double, double, double, double, double, bool)
11284      * 
11285      * @param angleLeft
11286      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11287      *            For a symmetric frustum, this value is negative.
11288      * @param angleRight
11289      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11290      * @param angleDown
11291      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11292      *            For a symmetric frustum, this value is negative.
11293      * @param angleUp
11294      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11295      * @param zNear
11296      *            near clipping plane distance. This value must be greater than zero.
11297      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11298      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11299      * @param zFar
11300      *            far clipping plane distance. This value must be greater than zero.
11301      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11302      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11303      * @param zZeroToOne
11304      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11305      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11306      * @return this
11307      */
11308     ref public Matrix4d perspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne) return {
11309         perspectiveOffCenterFov(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, zZeroToOne, this);
11310         return this;
11311     }
11312     public Matrix4d perspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11313         return frustum(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, zZeroToOne, dest);
11314     }
11315 
11316     /**
11317      * Apply an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11318      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
11319      * <p>
11320      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11321      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11322      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11323      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11324      * <p>
11325      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11326      * then the new matrix will be <code>M * P</code>. So when transforming a
11327      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11328      * the perspective projection will be applied first!
11329      * <p>
11330      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11331      * use {@link #setPerspectiveOffCenterFov(double, double, double, double, double, double) setPerspectiveOffCenterFov}.
11332      * 
11333      * @see #setPerspectiveOffCenterFov(double, double, double, double, double, double)
11334      * 
11335      * @param angleLeft
11336      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11337      *            For a symmetric frustum, this value is negative.
11338      * @param angleRight
11339      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11340      * @param angleDown
11341      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11342      *            For a symmetric frustum, this value is negative.
11343      * @param angleUp
11344      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11345      * @param zNear
11346      *            near clipping plane distance. This value must be greater than zero.
11347      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11348      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11349      * @param zFar
11350      *            far clipping plane distance. This value must be greater than zero.
11351      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11352      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11353      * @return this
11354      */
11355     ref public Matrix4d perspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar) return {
11356         perspectiveOffCenterFov(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, this);
11357         return this;
11358     }
11359     public Matrix4d perspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, ref Matrix4d dest) {
11360         return frustum(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, dest);
11361     }
11362 
11363     /**
11364      * Apply an asymmetric off-center perspective projection frustum transformation for a left-handed coordinate system
11365      * using the given NDC z range to this matrix.
11366      * <p>
11367      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11368      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11369      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11370      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11371      * <p>
11372      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11373      * then the new matrix will be <code>M * P</code>. So when transforming a
11374      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11375      * the perspective projection will be applied first!
11376      * <p>
11377      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11378      * use {@link #setPerspectiveOffCenterFovLH(double, double, double, double, double, double, bool) setPerspectiveOffCenterFovLH}.
11379      * 
11380      * @see #setPerspectiveOffCenterFovLH(double, double, double, double, double, double, bool)
11381      * 
11382      * @param angleLeft
11383      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11384      *            For a symmetric frustum, this value is negative.
11385      * @param angleRight
11386      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11387      * @param angleDown
11388      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11389      *            For a symmetric frustum, this value is negative.
11390      * @param angleUp
11391      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11392      * @param zNear
11393      *            near clipping plane distance. This value must be greater than zero.
11394      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11395      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11396      * @param zFar
11397      *            far clipping plane distance. This value must be greater than zero.
11398      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11399      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11400      * @param zZeroToOne
11401      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11402      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11403      * @return this
11404      */
11405     ref public Matrix4d perspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne) return {
11406         perspectiveOffCenterFovLH(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, zZeroToOne, this);
11407         return this;
11408     }
11409     public Matrix4d perspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11410         return frustumLH(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, zZeroToOne, dest);
11411     }
11412 
11413     /**
11414      * Apply an asymmetric off-center perspective projection frustum transformation for a left-handed coordinate system
11415      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
11416      * <p>
11417      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11418      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11419      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11420      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11421      * <p>
11422      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11423      * then the new matrix will be <code>M * P</code>. So when transforming a
11424      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11425      * the perspective projection will be applied first!
11426      * <p>
11427      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11428      * use {@link #setPerspectiveOffCenterFovLH(double, double, double, double, double, double) setPerspectiveOffCenterFovLH}.
11429      * 
11430      * @see #setPerspectiveOffCenterFovLH(double, double, double, double, double, double)
11431      * 
11432      * @param angleLeft
11433      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11434      *            For a symmetric frustum, this value is negative.
11435      * @param angleRight
11436      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11437      * @param angleDown
11438      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11439      *            For a symmetric frustum, this value is negative.
11440      * @param angleUp
11441      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11442      * @param zNear
11443      *            near clipping plane distance. This value must be greater than zero.
11444      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11445      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11446      * @param zFar
11447      *            far clipping plane distance. This value must be greater than zero.
11448      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11449      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11450      * @return this
11451      */
11452     ref public Matrix4d perspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar) return {
11453         perspectiveOffCenterFovLH(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, this);
11454         return this;
11455     }
11456     public Matrix4d perspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, ref Matrix4d dest) {
11457         return frustumLH(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, dest);
11458     }
11459 
11460     /**
11461      * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system
11462      * using the given NDC z range.
11463      * <p>
11464      * In order to apply the perspective projection transformation to an existing transformation,
11465      * use {@link #perspective(double, double, double, double, bool) perspective()}.
11466      * 
11467      * @see #perspective(double, double, double, double, bool)
11468      * 
11469      * @param fovy
11470      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11471      * @param aspect
11472      *            the aspect ratio (i.e. width / height; must be greater than zero)
11473      * @param zNear
11474      *            near clipping plane distance. This value must be greater than zero.
11475      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11476      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11477      * @param zFar
11478      *            far clipping plane distance. This value must be greater than zero.
11479      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11480      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11481      * @param zZeroToOne
11482      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11483      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11484      * @return this
11485      */
11486     ref public Matrix4d setPerspective(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne) return {
11487         double h = Math.tan(fovy * 0.5);
11488         _m00(1.0 / (h * aspect)).
11489         _m01(0.0).
11490         _m02(0.0).
11491         _m03(0.0).
11492         _m10(0.0).
11493         _m11(1.0 / h).
11494         _m12(0.0).
11495         _m13(0.0).
11496         _m20(0.0).
11497         _m21(0.0);
11498         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11499         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11500         if (farInf) {
11501             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11502             double e = 1E-6;
11503             _m22(e - 1.0).
11504             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
11505         } else if (nearInf) {
11506             double e = 1E-6;
11507             _m22((zZeroToOne ? 0.0 : 1.0) - e).
11508             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
11509         } else {
11510             _m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)).
11511             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
11512         }
11513         _m23(-1.0).
11514         _m30(0.0).
11515         _m31(0.0).
11516         _m33(0.0).
11517         properties = PROPERTY_PERSPECTIVE;
11518         return this;
11519     }
11520 
11521     /**
11522      * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system
11523      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
11524      * <p>
11525      * In order to apply the perspective projection transformation to an existing transformation,
11526      * use {@link #perspective(double, double, double, double) perspective()}.
11527      * 
11528      * @see #perspective(double, double, double, double)
11529      * 
11530      * @param fovy
11531      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11532      * @param aspect
11533      *            the aspect ratio (i.e. width / height; must be greater than zero)
11534      * @param zNear
11535      *            near clipping plane distance. This value must be greater than zero.
11536      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11537      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11538      * @param zFar
11539      *            far clipping plane distance. This value must be greater than zero.
11540      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11541      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11542      * @return this
11543      */
11544     ref public Matrix4d setPerspective(double fovy, double aspect, double zNear, double zFar) return {
11545         return setPerspective(fovy, aspect, zNear, zFar, false);
11546     }
11547 
11548     /**
11549      * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system
11550      * using the given NDC z range.
11551      * <p>
11552      * In order to apply the perspective projection transformation to an existing transformation,
11553      * use {@link #perspectiveRect(double, double, double, double, bool) perspectiveRect()}.
11554      * 
11555      * @see #perspectiveRect(double, double, double, double, bool)
11556      * 
11557      * @param width
11558      *            the width of the near frustum plane
11559      * @param height
11560      *            the height of the near frustum plane
11561      * @param zNear
11562      *            near clipping plane distance. This value must be greater than zero.
11563      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11564      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11565      * @param zFar
11566      *            far clipping plane distance. This value must be greater than zero.
11567      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11568      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11569      * @param zZeroToOne
11570      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11571      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11572      * @return this
11573      */
11574     ref public Matrix4d setPerspectiveRect(double width, double height, double zNear, double zFar, bool zZeroToOne) return {
11575         this.zero();
11576         this._m00((zNear + zNear) / width);
11577         this._m11((zNear + zNear) / height);
11578         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11579         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11580         if (farInf) {
11581             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11582             double e = 1E-6;
11583             this._m22(e - 1.0);
11584             this._m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
11585         } else if (nearInf) {
11586             double e = 1E-6f;
11587             this._m22((zZeroToOne ? 0.0 : 1.0) - e);
11588             this._m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
11589         } else {
11590             this._m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar));
11591             this._m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
11592         }
11593         this._m23(-1.0);
11594         properties = PROPERTY_PERSPECTIVE;
11595         return this;
11596     }
11597 
11598     /**
11599      * Set this matrix to be a symmetric perspective projection frustum transformation for a right-handed coordinate system
11600      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
11601      * <p>
11602      * In order to apply the perspective projection transformation to an existing transformation,
11603      * use {@link #perspectiveRect(double, double, double, double) perspectiveRect()}.
11604      * 
11605      * @see #perspectiveRect(double, double, double, double)
11606      * 
11607      * @param width
11608      *            the width of the near frustum plane
11609      * @param height
11610      *            the height of the near frustum plane
11611      * @param zNear
11612      *            near clipping plane distance. This value must be greater than zero.
11613      *            If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11614      *            In that case, <code>zFar</code> may not also be {@link Float#POSITIVE_INFINITY}.
11615      * @param zFar
11616      *            far clipping plane distance. This value must be greater than zero.
11617      *            If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11618      *            In that case, <code>zNear</code> may not also be {@link Float#POSITIVE_INFINITY}.
11619      * @return this
11620      */
11621     ref public Matrix4d setPerspectiveRect(double width, double height, double zNear, double zFar) return {
11622         return setPerspectiveRect(width, height, zNear, zFar, false);
11623     }
11624 
11625     /**
11626      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed
11627      * coordinate system using OpenGL's NDC z range of <code>[-1..+1]</code>.
11628      * <p>
11629      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11630      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11631      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11632      * is parallel to the XZ-plane.
11633      * <p>
11634      * In order to apply the perspective projection transformation to an existing transformation,
11635      * use {@link #perspectiveOffCenter(double, double, double, double, double, double) perspectiveOffCenter()}.
11636      * 
11637      * @see #perspectiveOffCenter(double, double, double, double, double, double)
11638      * 
11639      * @param fovy
11640      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11641      * @param offAngleX
11642      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11643      * @param offAngleY
11644      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11645      * @param aspect
11646      *            the aspect ratio (i.e. width / height; must be greater than zero)
11647      * @param zNear
11648      *            near clipping plane distance. This value must be greater than zero.
11649      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11650      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11651      * @param zFar
11652      *            far clipping plane distance. This value must be greater than zero.
11653      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11654      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11655      * @return this
11656      */
11657     ref public Matrix4d setPerspectiveOffCenter(double fovy, double offAngleX, double offAngleY,
11658             double aspect, double zNear, double zFar) return {
11659         return setPerspectiveOffCenter(fovy, offAngleX, offAngleY, aspect, zNear, zFar, false);
11660     }
11661     /**
11662      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed
11663      * coordinate system using the given NDC z range.
11664      * <p>
11665      * The given angles <code>offAngleX</code> and <code>offAngleY</code> are the horizontal and vertical angles between
11666      * the line of sight and the line given by the center of the near and far frustum planes. So, when <code>offAngleY</code>
11667      * is just <code>fovy/2</code> then the projection frustum is rotated towards +Y and the bottom frustum plane 
11668      * is parallel to the XZ-plane.
11669      * <p>
11670      * In order to apply the perspective projection transformation to an existing transformation,
11671      * use {@link #perspectiveOffCenter(double, double, double, double, double, double) perspectiveOffCenter()}.
11672      * 
11673      * @see #perspectiveOffCenter(double, double, double, double, double, double)
11674      * 
11675      * @param fovy
11676      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11677      * @param offAngleX
11678      *            the horizontal angle between the line of sight and the line crossing the center of the near and far frustum planes
11679      * @param offAngleY
11680      *            the vertical angle between the line of sight and the line crossing the center of the near and far frustum planes
11681      * @param aspect
11682      *            the aspect ratio (i.e. width / height; must be greater than zero)
11683      * @param zNear
11684      *            near clipping plane distance. This value must be greater than zero.
11685      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11686      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11687      * @param zFar
11688      *            far clipping plane distance. This value must be greater than zero.
11689      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11690      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11691      * @param zZeroToOne
11692      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11693      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11694      * @return this
11695      */
11696     ref public Matrix4d setPerspectiveOffCenter(double fovy, double offAngleX, double offAngleY,
11697                                             double aspect, double zNear, double zFar, bool zZeroToOne) return {
11698         this.zero();
11699         double h = Math.tan(fovy * 0.5);
11700         double xScale = 1.0 / (h * aspect), yScale = 1.0 / h;
11701         _m00(xScale).
11702         _m11(yScale);
11703         double offX = Math.tan(offAngleX), offY = Math.tan(offAngleY);
11704         _m20(offX * xScale).
11705         _m21(offY * yScale);
11706         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11707         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11708         if (farInf) {
11709             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11710             double e = 1E-6;
11711             _m22(e - 1.0).
11712             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
11713         } else if (nearInf) {
11714             double e = 1E-6;
11715             _m22((zZeroToOne ? 0.0 : 1.0) - e).
11716             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
11717         } else {
11718             _m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)).
11719             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
11720         }
11721         _m23(-1.0).
11722         _m30(0.0).
11723         _m31(0.0).
11724         _m33(0.0).
11725         properties = offAngleX == 0.0 && offAngleY == 0.0 ? PROPERTY_PERSPECTIVE : 0;
11726         return this;
11727     }
11728 
11729     /**
11730      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate
11731      * system using OpenGL's NDC z range of <code>[-1..+1]</code>.
11732      * <p>
11733      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11734      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11735      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11736      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11737      * <p>
11738      * In order to apply the perspective projection transformation to an existing transformation,
11739      * use {@link #perspectiveOffCenterFov(double, double, double, double, double, double) perspectiveOffCenterFov()}.
11740      * 
11741      * @see #perspectiveOffCenterFov(double, double, double, double, double, double)
11742      * 
11743      * @param angleLeft
11744      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11745      *            For a symmetric frustum, this value is negative.
11746      * @param angleRight
11747      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11748      * @param angleDown
11749      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11750      *            For a symmetric frustum, this value is negative.
11751      * @param angleUp
11752      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11753      * @param zNear
11754      *            near clipping plane distance. This value must be greater than zero.
11755      *            If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11756      *            In that case, <code>zFar</code> may not also be {@link Float#POSITIVE_INFINITY}.
11757      * @param zFar
11758      *            far clipping plane distance. This value must be greater than zero.
11759      *            If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11760      *            In that case, <code>zNear</code> may not also be {@link Float#POSITIVE_INFINITY}.
11761      * @return this
11762      */
11763     ref public Matrix4d setPerspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar) return {
11764         return setPerspectiveOffCenterFov(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, false);
11765     }
11766     /**
11767      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a right-handed coordinate system
11768      * using the given NDC z range.
11769      * <p>
11770      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11771      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11772      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11773      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11774      * <p>
11775      * In order to apply the perspective projection transformation to an existing transformation,
11776      * use {@link #perspectiveOffCenterFov(double, double, double, double, double, double, bool) perspectiveOffCenterFov()}.
11777      * 
11778      * @see #perspectiveOffCenterFov(double, double, double, double, double, double, bool)
11779      * 
11780      * @param angleLeft
11781      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11782      *            For a symmetric frustum, this value is negative.
11783      * @param angleRight
11784      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11785      * @param angleDown
11786      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11787      *            For a symmetric frustum, this value is negative.
11788      * @param angleUp
11789      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11790      * @param zNear
11791      *            near clipping plane distance. This value must be greater than zero.
11792      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11793      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11794      * @param zFar
11795      *            far clipping plane distance. This value must be greater than zero.
11796      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11797      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11798      * @param zZeroToOne
11799      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11800      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11801      * @return this
11802      */
11803     ref public Matrix4d setPerspectiveOffCenterFov(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne) return {
11804         return setFrustum(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, zZeroToOne);
11805     }
11806 
11807     /**
11808      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a left-handed coordinate
11809      * system using OpenGL's NDC z range of <code>[-1..+1]</code>.
11810      * <p>
11811      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11812      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11813      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11814      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11815      * <p>
11816      * In order to apply the perspective projection transformation to an existing transformation,
11817      * use {@link #perspectiveOffCenterFovLH(double, double, double, double, double, double) perspectiveOffCenterFovLH()}.
11818      * 
11819      * @see #perspectiveOffCenterFovLH(double, double, double, double, double, double)
11820      * 
11821      * @param angleLeft
11822      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11823      *            For a symmetric frustum, this value is negative.
11824      * @param angleRight
11825      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11826      * @param angleDown
11827      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11828      *            For a symmetric frustum, this value is negative.
11829      * @param angleUp
11830      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11831      * @param zNear
11832      *            near clipping plane distance. This value must be greater than zero.
11833      *            If the special value {@link Float#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11834      *            In that case, <code>zFar</code> may not also be {@link Float#POSITIVE_INFINITY}.
11835      * @param zFar
11836      *            far clipping plane distance. This value must be greater than zero.
11837      *            If the special value {@link Float#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11838      *            In that case, <code>zNear</code> may not also be {@link Float#POSITIVE_INFINITY}.
11839      * @return this
11840      */
11841     ref public Matrix4d setPerspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar) return {
11842         return setPerspectiveOffCenterFovLH(angleLeft, angleRight, angleDown, angleUp, zNear, zFar, false);
11843     }
11844     /**
11845      * Set this matrix to be an asymmetric off-center perspective projection frustum transformation for a left-handed coordinate system
11846      * using the given NDC z range.
11847      * <p>
11848      * The given angles <code>angleLeft</code> and <code>angleRight</code> are the horizontal angles between
11849      * the left and right frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11850      * The angles <code>angleDown</code> and <code>angleUp</code> are the vertical angles between
11851      * the bottom and top frustum planes, respectively, and a line perpendicular to the near and far frustum planes.
11852      * <p>
11853      * In order to apply the perspective projection transformation to an existing transformation,
11854      * use {@link #perspectiveOffCenterFovLH(double, double, double, double, double, double, bool) perspectiveOffCenterFovLH()}.
11855      * 
11856      * @see #perspectiveOffCenterFovLH(double, double, double, double, double, double, bool)
11857      * 
11858      * @param angleLeft
11859      *            the horizontal angle between left frustum plane and a line perpendicular to the near/far frustum planes.
11860      *            For a symmetric frustum, this value is negative.
11861      * @param angleRight
11862      *            the horizontal angle between right frustum plane and a line perpendicular to the near/far frustum planes
11863      * @param angleDown
11864      *            the vertical angle between bottom frustum plane and a line perpendicular to the near/far frustum planes.
11865      *            For a symmetric frustum, this value is negative.
11866      * @param angleUp
11867      *            the vertical angle between top frustum plane and a line perpendicular to the near/far frustum planes
11868      * @param zNear
11869      *            near clipping plane distance. This value must be greater than zero.
11870      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11871      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11872      * @param zFar
11873      *            far clipping plane distance. This value must be greater than zero.
11874      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11875      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11876      * @param zZeroToOne
11877      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11878      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11879      * @return this
11880      */
11881     ref public Matrix4d setPerspectiveOffCenterFovLH(double angleLeft, double angleRight, double angleDown, double angleUp, double zNear, double zFar, bool zZeroToOne) return {
11882         return setFrustumLH(Math.tan(angleLeft)*zNear, Math.tan(angleRight)*zNear, Math.tan(angleDown)*zNear, Math.tan(angleUp)*zNear, zNear, zFar, zZeroToOne);
11883     }
11884 
11885     /**
11886      * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system
11887      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
11888      * <p>
11889      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11890      * then the new matrix will be <code>M * P</code>. So when transforming a
11891      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11892      * the perspective projection will be applied first!
11893      * <p>
11894      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11895      * use {@link #setPerspectiveLH(double, double, double, double, bool) setPerspectiveLH}.
11896      * 
11897      * @see #setPerspectiveLH(double, double, double, double, bool)
11898      * 
11899      * @param fovy
11900      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11901      * @param aspect
11902      *            the aspect ratio (i.e. width / height; must be greater than zero)
11903      * @param zNear
11904      *            near clipping plane distance. This value must be greater than zero.
11905      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11906      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11907      * @param zFar
11908      *            far clipping plane distance. This value must be greater than zero.
11909      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11910      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11911      * @param zZeroToOne
11912      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11913      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11914      * @param dest
11915      *            will hold the result
11916      * @return dest
11917      */
11918     public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11919         if ((properties & PROPERTY_IDENTITY) != 0)
11920             return dest.setPerspectiveLH(fovy, aspect, zNear, zFar, zZeroToOne);
11921         return perspectiveLHGeneric(fovy, aspect, zNear, zFar, zZeroToOne, dest);
11922     }
11923     private Matrix4d perspectiveLHGeneric(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
11924         double h = Math.tan(fovy * 0.5);
11925         // calculate right matrix elements
11926         double rm00 = 1.0 / (h * aspect);
11927         double rm11 = 1.0 / h;
11928         double rm22;
11929         double rm32;
11930         bool farInf = zFar > 0 && Math.isInfinite(zFar);
11931         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
11932         if (farInf) {
11933             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
11934             double e = 1E-6;
11935             rm22 = 1.0 - e;
11936             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
11937         } else if (nearInf) {
11938             double e = 1E-6;
11939             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
11940             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
11941         } else {
11942             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear);
11943             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
11944         }
11945         // perform optimized matrix multiplication
11946         double nm20 = m20 * rm22 + m30;
11947         double nm21 = m21 * rm22 + m31;
11948         double nm22 = m22 * rm22 + m32;
11949         double nm23 = m23 * rm22 + m33;
11950         dest._m00(m00 * rm00)
11951         ._m01(m01 * rm00)
11952         ._m02(m02 * rm00)
11953         ._m03(m03 * rm00)
11954         ._m10(m10 * rm11)
11955         ._m11(m11 * rm11)
11956         ._m12(m12 * rm11)
11957         ._m13(m13 * rm11)
11958         ._m30(m20 * rm32)
11959         ._m31(m21 * rm32)
11960         ._m32(m22 * rm32)
11961         ._m33(m23 * rm32)
11962         ._m20(nm20)
11963         ._m21(nm21)
11964         ._m22(nm22)
11965         ._m23(nm23)
11966         ._properties(properties & ~(PROPERTY_AFFINE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
11967         return dest;
11968     }
11969 
11970     /**
11971      * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system
11972      * using the given NDC z range to this matrix.
11973      * <p>
11974      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
11975      * then the new matrix will be <code>M * P</code>. So when transforming a
11976      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
11977      * the perspective projection will be applied first!
11978      * <p>
11979      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
11980      * use {@link #setPerspectiveLH(double, double, double, double, bool) setPerspectiveLH}.
11981      * 
11982      * @see #setPerspectiveLH(double, double, double, double, bool)
11983      * 
11984      * @param fovy
11985      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
11986      * @param aspect
11987      *            the aspect ratio (i.e. width / height; must be greater than zero)
11988      * @param zNear
11989      *            near clipping plane distance. This value must be greater than zero.
11990      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
11991      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
11992      * @param zFar
11993      *            far clipping plane distance. This value must be greater than zero.
11994      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
11995      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
11996      * @param zZeroToOne
11997      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
11998      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
11999      * @return this
12000      */
12001     ref public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne) return {
12002         perspectiveLH(fovy, aspect, zNear, zFar, zZeroToOne, this);
12003         return this;
12004     }
12005 
12006     /**
12007      * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system
12008      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
12009      * <p>
12010      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
12011      * then the new matrix will be <code>M * P</code>. So when transforming a
12012      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
12013      * the perspective projection will be applied first!
12014      * <p>
12015      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12016      * use {@link #setPerspectiveLH(double, double, double, double) setPerspectiveLH}.
12017      * 
12018      * @see #setPerspectiveLH(double, double, double, double)
12019      * 
12020      * @param fovy
12021      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
12022      * @param aspect
12023      *            the aspect ratio (i.e. width / height; must be greater than zero)
12024      * @param zNear
12025      *            near clipping plane distance. This value must be greater than zero.
12026      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12027      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12028      * @param zFar
12029      *            far clipping plane distance. This value must be greater than zero.
12030      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12031      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12032      * @param dest
12033      *            will hold the result
12034      * @return dest
12035      */
12036     public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar, ref Matrix4d dest) {
12037         return perspectiveLH(fovy, aspect, zNear, zFar, false, dest);
12038     }
12039 
12040     /**
12041      * Apply a symmetric perspective projection frustum transformation for a left-handed coordinate system
12042      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
12043      * <p>
12044      * If <code>M</code> is <code>this</code> matrix and <code>P</code> the perspective projection matrix,
12045      * then the new matrix will be <code>M * P</code>. So when transforming a
12046      * vector <code>v</code> with the new matrix by using <code>M * P * v</code>,
12047      * the perspective projection will be applied first!
12048      * <p>
12049      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12050      * use {@link #setPerspectiveLH(double, double, double, double) setPerspectiveLH}.
12051      * 
12052      * @see #setPerspectiveLH(double, double, double, double)
12053      * 
12054      * @param fovy
12055      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
12056      * @param aspect
12057      *            the aspect ratio (i.e. width / height; must be greater than zero)
12058      * @param zNear
12059      *            near clipping plane distance. This value must be greater than zero.
12060      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12061      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12062      * @param zFar
12063      *            far clipping plane distance. This value must be greater than zero.
12064      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12065      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12066      * @return this
12067      */
12068     ref public Matrix4d perspectiveLH(double fovy, double aspect, double zNear, double zFar) return {
12069         perspectiveLH(fovy, aspect, zNear, zFar, this);
12070         return this;
12071     }
12072 
12073     /**
12074      * Set this matrix to be a symmetric perspective projection frustum transformation for a left-handed coordinate system
12075      * using the given NDC z range of <code>[-1..+1]</code>.
12076      * <p>
12077      * In order to apply the perspective projection transformation to an existing transformation,
12078      * use {@link #perspectiveLH(double, double, double, double, bool) perspectiveLH()}.
12079      * 
12080      * @see #perspectiveLH(double, double, double, double, bool)
12081      * 
12082      * @param fovy
12083      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
12084      * @param aspect
12085      *            the aspect ratio (i.e. width / height; must be greater than zero)
12086      * @param zNear
12087      *            near clipping plane distance. This value must be greater than zero.
12088      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12089      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12090      * @param zFar
12091      *            far clipping plane distance. This value must be greater than zero.
12092      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12093      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12094      * @param zZeroToOne
12095      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12096      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12097      * @return this
12098      */
12099     ref public Matrix4d setPerspectiveLH(double fovy, double aspect, double zNear, double zFar, bool zZeroToOne) return {
12100         double h = Math.tan(fovy * 0.5);
12101         _m00(1.0 / (h * aspect)).
12102         _m01(0.0).
12103         _m02(0.0).
12104         _m03(0.0).
12105         _m10(0.0).
12106         _m11(1.0 / h).
12107         _m12(0.0).
12108         _m13(0.0).
12109         _m20(0.0).
12110         _m21(0.0);
12111         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12112         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12113         if (farInf) {
12114             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12115             double e = 1E-6;
12116             _m22(1.0 - e).
12117             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
12118         } else if (nearInf) {
12119             double e = 1E-6;
12120             _m22((zZeroToOne ? 0.0 : 1.0) - e).
12121             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
12122         } else {
12123             _m22((zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear)).
12124             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
12125         }
12126         _m23(1.0).
12127         _m30(0.0).
12128         _m31(0.0).
12129         _m33(0.0).
12130         properties = PROPERTY_PERSPECTIVE;
12131         return this;
12132     }
12133 
12134     /**
12135      * Set this matrix to be a symmetric perspective projection frustum transformation for a left-handed coordinate system
12136      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
12137      * <p>
12138      * In order to apply the perspective projection transformation to an existing transformation,
12139      * use {@link #perspectiveLH(double, double, double, double) perspectiveLH()}.
12140      * 
12141      * @see #perspectiveLH(double, double, double, double)
12142      * 
12143      * @param fovy
12144      *            the vertical field of view in radians (must be greater than zero and less than {@link Math#PI PI})
12145      * @param aspect
12146      *            the aspect ratio (i.e. width / height; must be greater than zero)
12147      * @param zNear
12148      *            near clipping plane distance. This value must be greater than zero.
12149      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12150      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12151      * @param zFar
12152      *            far clipping plane distance. This value must be greater than zero.
12153      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12154      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12155      * @return this
12156      */
12157     ref public Matrix4d setPerspectiveLH(double fovy, double aspect, double zNear, double zFar) return {
12158         return setPerspectiveLH(fovy, aspect, zNear, zFar, false);
12159     }
12160 
12161     /**
12162      * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12163      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
12164      * <p>
12165      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12166      * then the new matrix will be <code>M * F</code>. So when transforming a
12167      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12168      * the frustum transformation will be applied first!
12169      * <p>
12170      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12171      * use {@link #setFrustum(double, double, double, double, double, double, bool) setFrustum()}.
12172      * <p>
12173      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12174      * 
12175      * @see #setFrustum(double, double, double, double, double, double, bool)
12176      * 
12177      * @param left
12178      *            the distance along the x-axis to the left frustum edge
12179      * @param right
12180      *            the distance along the x-axis to the right frustum edge
12181      * @param bottom
12182      *            the distance along the y-axis to the bottom frustum edge
12183      * @param top
12184      *            the distance along the y-axis to the top frustum edge
12185      * @param zNear
12186      *            near clipping plane distance. This value must be greater than zero.
12187      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12188      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12189      * @param zFar
12190      *            far clipping plane distance. This value must be greater than zero.
12191      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12192      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12193      * @param zZeroToOne
12194      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12195      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12196      * @param dest
12197      *            will hold the result
12198      * @return dest
12199      */
12200     public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
12201         if ((properties & PROPERTY_IDENTITY) != 0)
12202             return dest.setFrustum(left, right, bottom, top, zNear, zFar, zZeroToOne);
12203         return frustumGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest);
12204     }
12205     private Matrix4d frustumGeneric(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
12206         // calculate right matrix elements
12207         double rm00 = (zNear + zNear) / (right - left);
12208         double rm11 = (zNear + zNear) / (top - bottom);
12209         double rm20 = (right + left) / (right - left);
12210         double rm21 = (top + bottom) / (top - bottom);
12211         double rm22;
12212         double rm32;
12213         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12214         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12215         if (farInf) {
12216             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12217             double e = 1E-6;
12218             rm22 = e - 1.0;
12219             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
12220         } else if (nearInf) {
12221             double e = 1E-6;
12222             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
12223             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
12224         } else {
12225             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar);
12226             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
12227         }
12228         // perform optimized matrix multiplication
12229         double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 - m30;
12230         double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 - m31;
12231         double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 - m32;
12232         double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 - m33;
12233         dest._m00(m00 * rm00)
12234         ._m01(m01 * rm00)
12235         ._m02(m02 * rm00)
12236         ._m03(m03 * rm00)
12237         ._m10(m10 * rm11)
12238         ._m11(m11 * rm11)
12239         ._m12(m12 * rm11)
12240         ._m13(m13 * rm11)
12241         ._m30(m20 * rm32)
12242         ._m31(m21 * rm32)
12243         ._m32(m22 * rm32)
12244         ._m33(m23 * rm32)
12245         ._m20(nm20)
12246         ._m21(nm21)
12247         ._m22(nm22)
12248         ._m23(nm23)
12249         ._properties(0);
12250         return dest;
12251     }
12252 
12253     /**
12254      * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12255      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
12256      * <p>
12257      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12258      * then the new matrix will be <code>M * F</code>. So when transforming a
12259      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12260      * the frustum transformation will be applied first!
12261      * <p>
12262      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12263      * use {@link #setFrustum(double, double, double, double, double, double) setFrustum()}.
12264      * <p>
12265      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12266      * 
12267      * @see #setFrustum(double, double, double, double, double, double)
12268      * 
12269      * @param left
12270      *            the distance along the x-axis to the left frustum edge
12271      * @param right
12272      *            the distance along the x-axis to the right frustum edge
12273      * @param bottom
12274      *            the distance along the y-axis to the bottom frustum edge
12275      * @param top
12276      *            the distance along the y-axis to the top frustum edge
12277      * @param zNear
12278      *            near clipping plane distance. This value must be greater than zero.
12279      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12280      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12281      * @param zFar
12282      *            far clipping plane distance. This value must be greater than zero.
12283      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12284      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12285      * @param dest
12286      *            will hold the result
12287      * @return dest
12288      */
12289     public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, ref Matrix4d dest) {
12290         return frustum(left, right, bottom, top, zNear, zFar, false, dest);
12291     }
12292 
12293     /**
12294      * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12295      * using the given NDC z range to this matrix.
12296      * <p>
12297      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12298      * then the new matrix will be <code>M * F</code>. So when transforming a
12299      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12300      * the frustum transformation will be applied first!
12301      * <p>
12302      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12303      * use {@link #setFrustum(double, double, double, double, double, double, bool) setFrustum()}.
12304      * <p>
12305      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12306      * 
12307      * @see #setFrustum(double, double, double, double, double, double, bool)
12308      * 
12309      * @param left
12310      *            the distance along the x-axis to the left frustum edge
12311      * @param right
12312      *            the distance along the x-axis to the right frustum edge
12313      * @param bottom
12314      *            the distance along the y-axis to the bottom frustum edge
12315      * @param top
12316      *            the distance along the y-axis to the top frustum edge
12317      * @param zNear
12318      *            near clipping plane distance. This value must be greater than zero.
12319      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12320      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12321      * @param zFar
12322      *            far clipping plane distance. This value must be greater than zero.
12323      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12324      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12325      * @param zZeroToOne
12326      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12327      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12328      * @return this
12329      */
12330     ref public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
12331         frustum(left, right, bottom, top, zNear, zFar, zZeroToOne, this);
12332         return this;
12333     }
12334 
12335     /**
12336      * Apply an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12337      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix.
12338      * <p>
12339      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12340      * then the new matrix will be <code>M * F</code>. So when transforming a
12341      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12342      * the frustum transformation will be applied first!
12343      * <p>
12344      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12345      * use {@link #setFrustum(double, double, double, double, double, double) setFrustum()}.
12346      * <p>
12347      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12348      * 
12349      * @see #setFrustum(double, double, double, double, double, double)
12350      * 
12351      * @param left
12352      *            the distance along the x-axis to the left frustum edge
12353      * @param right
12354      *            the distance along the x-axis to the right frustum edge
12355      * @param bottom
12356      *            the distance along the y-axis to the bottom frustum edge
12357      * @param top
12358      *            the distance along the y-axis to the top frustum edge
12359      * @param zNear
12360      *            near clipping plane distance. This value must be greater than zero.
12361      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12362      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12363      * @param zFar
12364      *            far clipping plane distance. This value must be greater than zero.
12365      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12366      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12367      * @return this
12368      */
12369     ref public Matrix4d frustum(double left, double right, double bottom, double top, double zNear, double zFar) return {
12370         frustum(left, right, bottom, top, zNear, zFar, this);
12371         return this;
12372     }
12373 
12374     /**
12375      * Set this matrix to be an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12376      * using the given NDC z range.
12377      * <p>
12378      * In order to apply the perspective frustum transformation to an existing transformation,
12379      * use {@link #frustum(double, double, double, double, double, double, bool) frustum()}.
12380      * <p>
12381      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12382      * 
12383      * @see #frustum(double, double, double, double, double, double, bool)
12384      * 
12385      * @param left
12386      *            the distance along the x-axis to the left frustum edge
12387      * @param right
12388      *            the distance along the x-axis to the right frustum edge
12389      * @param bottom
12390      *            the distance along the y-axis to the bottom frustum edge
12391      * @param top
12392      *            the distance along the y-axis to the top frustum edge
12393      * @param zNear
12394      *            near clipping plane distance. This value must be greater than zero.
12395      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12396      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12397      * @param zFar
12398      *            far clipping plane distance. This value must be greater than zero.
12399      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12400      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12401      * @param zZeroToOne
12402      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12403      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12404      * @return this
12405      */
12406     ref public Matrix4d setFrustum(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
12407         if ((properties & PROPERTY_IDENTITY) == 0)
12408             _identity();
12409         _m00((zNear + zNear) / (right - left)).
12410         _m11((zNear + zNear) / (top - bottom)).
12411         _m20((right + left) / (right - left)).
12412         _m21((top + bottom) / (top - bottom));
12413         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12414         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12415         if (farInf) {
12416             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12417             double e = 1E-6;
12418             _m22(e - 1.0).
12419             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
12420         } else if (nearInf) {
12421             double e = 1E-6;
12422             _m22((zZeroToOne ? 0.0 : 1.0) - e).
12423             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
12424         } else {
12425             _m22((zZeroToOne ? zFar : zFar + zNear) / (zNear - zFar)).
12426             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
12427         }
12428         _m23(-1.0).
12429         _m33(0.0).
12430         properties = this.m20 == 0.0 && this.m21 == 0.0 ? PROPERTY_PERSPECTIVE : 0;
12431         return this;
12432     }
12433 
12434     /**
12435      * Set this matrix to be an arbitrary perspective projection frustum transformation for a right-handed coordinate system
12436      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
12437      * <p>
12438      * In order to apply the perspective frustum transformation to an existing transformation,
12439      * use {@link #frustum(double, double, double, double, double, double) frustum()}.
12440      * <p>
12441      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12442      * 
12443      * @see #frustum(double, double, double, double, double, double)
12444      * 
12445      * @param left
12446      *            the distance along the x-axis to the left frustum edge
12447      * @param right
12448      *            the distance along the x-axis to the right frustum edge
12449      * @param bottom
12450      *            the distance along the y-axis to the bottom frustum edge
12451      * @param top
12452      *            the distance along the y-axis to the top frustum edge
12453      * @param zNear
12454      *            near clipping plane distance. This value must be greater than zero.
12455      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12456      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12457      * @param zFar
12458      *            far clipping plane distance. This value must be greater than zero.
12459      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12460      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12461      * @return this
12462      */
12463     ref public Matrix4d setFrustum(double left, double right, double bottom, double top, double zNear, double zFar) return {
12464         return setFrustum(left, right, bottom, top, zNear, zFar, false);
12465     }
12466 
12467     /**
12468      * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12469      * using the given NDC z range to this matrix and store the result in <code>dest</code>.
12470      * <p>
12471      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12472      * then the new matrix will be <code>M * F</code>. So when transforming a
12473      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12474      * the frustum transformation will be applied first!
12475      * <p>
12476      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12477      * use {@link #setFrustumLH(double, double, double, double, double, double, bool) setFrustumLH()}.
12478      * <p>
12479      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12480      * 
12481      * @see #setFrustumLH(double, double, double, double, double, double, bool)
12482      * 
12483      * @param left
12484      *            the distance along the x-axis to the left frustum edge
12485      * @param right
12486      *            the distance along the x-axis to the right frustum edge
12487      * @param bottom
12488      *            the distance along the y-axis to the bottom frustum edge
12489      * @param top
12490      *            the distance along the y-axis to the top frustum edge
12491      * @param zNear
12492      *            near clipping plane distance. This value must be greater than zero.
12493      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12494      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12495      * @param zFar
12496      *            far clipping plane distance. This value must be greater than zero.
12497      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12498      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12499      * @param zZeroToOne
12500      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12501      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12502      * @param dest
12503      *            will hold the result
12504      * @return dest
12505      */
12506     public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
12507         if ((properties & PROPERTY_IDENTITY) != 0)
12508             return dest.setFrustumLH(left, right, bottom, top, zNear, zFar, zZeroToOne);
12509         return frustumLHGeneric(left, right, bottom, top, zNear, zFar, zZeroToOne, dest);
12510     }
12511     private Matrix4d frustumLHGeneric(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne, ref Matrix4d dest) {
12512         // calculate right matrix elements
12513         double rm00 = (zNear + zNear) / (right - left);
12514         double rm11 = (zNear + zNear) / (top - bottom);
12515         double rm20 = (right + left) / (right - left);
12516         double rm21 = (top + bottom) / (top - bottom);
12517         double rm22;
12518         double rm32;
12519         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12520         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12521         if (farInf) {
12522             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12523             double e = 1E-6;
12524             rm22 = 1.0 - e;
12525             rm32 = (e - (zZeroToOne ? 1.0 : 2.0)) * zNear;
12526         } else if (nearInf) {
12527             double e = 1E-6;
12528             rm22 = (zZeroToOne ? 0.0 : 1.0) - e;
12529             rm32 = ((zZeroToOne ? 1.0 : 2.0) - e) * zFar;
12530         } else {
12531             rm22 = (zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear);
12532             rm32 = (zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar);
12533         }
12534         // perform optimized matrix multiplication
12535         double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 + m30;
12536         double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 + m31;
12537         double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 + m32;
12538         double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 + m33;
12539         dest._m00(m00 * rm00)
12540         ._m01(m01 * rm00)
12541         ._m02(m02 * rm00)
12542         ._m03(m03 * rm00)
12543         ._m10(m10 * rm11)
12544         ._m11(m11 * rm11)
12545         ._m12(m12 * rm11)
12546         ._m13(m13 * rm11)
12547         ._m30(m20 * rm32)
12548         ._m31(m21 * rm32)
12549         ._m32(m22 * rm32)
12550         ._m33(m23 * rm32)
12551         ._m20(nm20)
12552         ._m21(nm21)
12553         ._m22(nm22)
12554         ._m23(nm23)
12555         ._properties(0);
12556         return dest;
12557     }
12558 
12559     /**
12560      * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12561      * using the given NDC z range to this matrix.
12562      * <p>
12563      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12564      * then the new matrix will be <code>M * F</code>. So when transforming a
12565      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12566      * the frustum transformation will be applied first!
12567      * <p>
12568      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12569      * use {@link #setFrustumLH(double, double, double, double, double, double, bool) setFrustumLH()}.
12570      * <p>
12571      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12572      * 
12573      * @see #setFrustumLH(double, double, double, double, double, double, bool)
12574      * 
12575      * @param left
12576      *            the distance along the x-axis to the left frustum edge
12577      * @param right
12578      *            the distance along the x-axis to the right frustum edge
12579      * @param bottom
12580      *            the distance along the y-axis to the bottom frustum edge
12581      * @param top
12582      *            the distance along the y-axis to the top frustum edge
12583      * @param zNear
12584      *            near clipping plane distance. This value must be greater than zero.
12585      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12586      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12587      * @param zFar
12588      *            far clipping plane distance. This value must be greater than zero.
12589      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12590      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12591      * @param zZeroToOne
12592      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12593      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12594      * @return this
12595      */
12596     ref public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
12597         frustumLH(left, right, bottom, top, zNear, zFar, zZeroToOne, this);
12598         return this;
12599     }
12600 
12601     /**
12602      * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12603      * using OpenGL's NDC z range of <code>[-1..+1]</code> to this matrix and store the result in <code>dest</code>.
12604      * <p>
12605      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12606      * then the new matrix will be <code>M * F</code>. So when transforming a
12607      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12608      * the frustum transformation will be applied first!
12609      * <p>
12610      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12611      * use {@link #setFrustumLH(double, double, double, double, double, double) setFrustumLH()}.
12612      * <p>
12613      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12614      * 
12615      * @see #setFrustumLH(double, double, double, double, double, double)
12616      * 
12617      * @param left
12618      *            the distance along the x-axis to the left frustum edge
12619      * @param right
12620      *            the distance along the x-axis to the right frustum edge
12621      * @param bottom
12622      *            the distance along the y-axis to the bottom frustum edge
12623      * @param top
12624      *            the distance along the y-axis to the top frustum edge
12625      * @param zNear
12626      *            near clipping plane distance. This value must be greater than zero.
12627      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12628      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12629      * @param zFar
12630      *            far clipping plane distance. This value must be greater than zero.
12631      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12632      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12633      * @param dest
12634      *            will hold the result
12635      * @return dest
12636      */
12637     public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar, ref Matrix4d dest) {
12638         return frustumLH(left, right, bottom, top, zNear, zFar, false, dest);
12639     }
12640 
12641     /**
12642      * Apply an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12643      * using the given NDC z range to this matrix.
12644      * <p>
12645      * If <code>M</code> is <code>this</code> matrix and <code>F</code> the frustum matrix,
12646      * then the new matrix will be <code>M * F</code>. So when transforming a
12647      * vector <code>v</code> with the new matrix by using <code>M * F * v</code>,
12648      * the frustum transformation will be applied first!
12649      * <p>
12650      * In order to set the matrix to a perspective frustum transformation without post-multiplying,
12651      * use {@link #setFrustumLH(double, double, double, double, double, double) setFrustumLH()}.
12652      * <p>
12653      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12654      * 
12655      * @see #setFrustumLH(double, double, double, double, double, double)
12656      * 
12657      * @param left
12658      *            the distance along the x-axis to the left frustum edge
12659      * @param right
12660      *            the distance along the x-axis to the right frustum edge
12661      * @param bottom
12662      *            the distance along the y-axis to the bottom frustum edge
12663      * @param top
12664      *            the distance along the y-axis to the top frustum edge
12665      * @param zNear
12666      *            near clipping plane distance. This value must be greater than zero.
12667      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12668      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12669      * @param zFar
12670      *            far clipping plane distance. This value must be greater than zero.
12671      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12672      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12673      * @return this
12674      */
12675     ref public Matrix4d frustumLH(double left, double right, double bottom, double top, double zNear, double zFar) return {
12676         frustumLH(left, right, bottom, top, zNear, zFar, this);
12677         return this;
12678     }
12679 
12680     /**
12681      * Set this matrix to be an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12682      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
12683      * <p>
12684      * In order to apply the perspective frustum transformation to an existing transformation,
12685      * use {@link #frustumLH(double, double, double, double, double, double, bool) frustumLH()}.
12686      * <p>
12687      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12688      * 
12689      * @see #frustumLH(double, double, double, double, double, double, bool)
12690      * 
12691      * @param left
12692      *            the distance along the x-axis to the left frustum edge
12693      * @param right
12694      *            the distance along the x-axis to the right frustum edge
12695      * @param bottom
12696      *            the distance along the y-axis to the bottom frustum edge
12697      * @param top
12698      *            the distance along the y-axis to the top frustum edge
12699      * @param zNear
12700      *            near clipping plane distance. This value must be greater than zero.
12701      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12702      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12703      * @param zFar
12704      *            far clipping plane distance. This value must be greater than zero.
12705      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12706      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12707      * @param zZeroToOne
12708      *            whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
12709      *            or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
12710      * @return this
12711      */
12712     ref public Matrix4d setFrustumLH(double left, double right, double bottom, double top, double zNear, double zFar, bool zZeroToOne) return {
12713         if ((properties & PROPERTY_IDENTITY) == 0)
12714             _identity();
12715         _m00((zNear + zNear) / (right - left)).
12716         _m11((zNear + zNear) / (top - bottom)).
12717         _m20((right + left) / (right - left)).
12718         _m21((top + bottom) / (top - bottom));
12719         bool farInf = zFar > 0 && Math.isInfinite(zFar);
12720         bool nearInf = zNear > 0 && Math.isInfinite(zNear);
12721         if (farInf) {
12722             // See: "Infinite Projection Matrix" (http://www.terathon.com/gdc07_lengyel.pdf)
12723             double e = 1E-6;
12724             _m22(1.0 - e).
12725             _m32((e - (zZeroToOne ? 1.0 : 2.0)) * zNear);
12726         } else if (nearInf) {
12727             double e = 1E-6;
12728             _m22((zZeroToOne ? 0.0 : 1.0) - e).
12729             _m32(((zZeroToOne ? 1.0 : 2.0) - e) * zFar);
12730         } else {
12731             _m22((zZeroToOne ? zFar : zFar + zNear) / (zFar - zNear)).
12732             _m32((zZeroToOne ? zFar : zFar + zFar) * zNear / (zNear - zFar));
12733         }
12734         _m23(1.0).
12735         _m33(0.0).
12736         properties = this.m20 == 0.0 && this.m21 == 0.0 ? PROPERTY_PERSPECTIVE : 0;
12737         return this;
12738     }
12739 
12740     /**
12741      * Set this matrix to be an arbitrary perspective projection frustum transformation for a left-handed coordinate system
12742      * using OpenGL's NDC z range of <code>[-1..+1]</code>.
12743      * <p>
12744      * In order to apply the perspective frustum transformation to an existing transformation,
12745      * use {@link #frustumLH(double, double, double, double, double, double) frustumLH()}.
12746      * <p>
12747      * Reference: <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective">http://www.songho.ca</a>
12748      * 
12749      * @see #frustumLH(double, double, double, double, double, double)
12750      * 
12751      * @param left
12752      *            the distance along the x-axis to the left frustum edge
12753      * @param right
12754      *            the distance along the x-axis to the right frustum edge
12755      * @param bottom
12756      *            the distance along the y-axis to the bottom frustum edge
12757      * @param top
12758      *            the distance along the y-axis to the top frustum edge
12759      * @param zNear
12760      *            near clipping plane distance. This value must be greater than zero.
12761      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the near clipping plane will be at positive infinity.
12762      *            In that case, <code>zFar</code> may not also be {@link Double#POSITIVE_INFINITY}.
12763      * @param zFar
12764      *            far clipping plane distance. This value must be greater than zero.
12765      *            If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
12766      *            In that case, <code>zNear</code> may not also be {@link Double#POSITIVE_INFINITY}.
12767      * @return this
12768      */
12769     ref public Matrix4d setFrustumLH(double left, double right, double bottom, double top, double zNear, double zFar) return {
12770         return setFrustumLH(left, right, bottom, top, zNear, zFar, false);
12771     }
12772 
12773     /**
12774      * Set this matrix to represent a perspective projection equivalent to the given intrinsic camera calibration parameters.
12775      * The resulting matrix will be suited for a right-handed coordinate system using OpenGL's NDC z range of <code>[-1..+1]</code>.
12776      * <p>
12777      * See: <a href="https://en.wikipedia.org/wiki/Camera_resectioning#Intrinsic_parameters">https://en.wikipedia.org/</a>
12778      * <p>
12779      * Reference: <a href="http://ksimek.github.io/2013/06/03/calibrated_cameras_in_opengl/">http://ksimek.github.io/</a>
12780      * 
12781      * @param alphaX
12782      *          specifies the focal length and scale along the X axis
12783      * @param alphaY
12784      *          specifies the focal length and scale along the Y axis
12785      * @param gamma
12786      *          the skew coefficient between the X and Y axis (may be <code>0</code>)
12787      * @param u0
12788      *          the X coordinate of the principal point in image/sensor units
12789      * @param v0
12790      *          the Y coordinate of the principal point in image/sensor units
12791      * @param imgWidth
12792      *          the width of the sensor/image image/sensor units
12793      * @param imgHeight
12794      *          the height of the sensor/image image/sensor units
12795      * @param near
12796      *          the distance to the near plane
12797      * @param far
12798      *          the distance to the far plane
12799      * @return this
12800      */
12801     ref public Matrix4d setFromIntrinsic(double alphaX, double alphaY, double gamma, double u0, double v0, int imgWidth, int imgHeight, double near, double far) return {
12802         double l00 = 2.0 / imgWidth;
12803         double l11 = 2.0 / imgHeight;
12804         double l22 = 2.0 / (near - far);
12805         setm00(l00 * alphaX);
12806         setm01(0.0);
12807         setm02(0.0);
12808         setm03(0.0);
12809         setm10(l00 * gamma);
12810         setm11(l11 * alphaY);
12811         setm12(0.0);
12812         setm13(0.0);
12813         setm20(l00 * u0 - 1.0);
12814         setm21(l11 * v0 - 1.0);
12815         setm22(l22 * -(near + far) + (far + near) / (near - far));
12816         setm23(-1.0);
12817         setm30(0.0);
12818         setm31(0.0);
12819         setm32(l22 * -near * far);
12820         setm33(0.0);
12821         this.properties = PROPERTY_PERSPECTIVE;
12822         return this;
12823     }
12824 
12825     public Vector4d frustumPlane(int plane, ref Vector4d dest) {
12826         switch (plane) {
12827         case PLANE_NX:
12828             dest.set(m03 + m00, m13 + m10, m23 + m20, m33 + m30).normalize3();
12829             break;
12830         case PLANE_PX:
12831             dest.set(m03 - m00, m13 - m10, m23 - m20, m33 - m30).normalize3();
12832             break;
12833         case PLANE_NY:
12834             dest.set(m03 + m01, m13 + m11, m23 + m21, m33 + m31).normalize3();
12835             break;
12836         case PLANE_PY:
12837             dest.set(m03 - m01, m13 - m11, m23 - m21, m33 - m31).normalize3();
12838             break;
12839         case PLANE_NZ:
12840             dest.set(m03 + m02, m13 + m12, m23 + m22, m33 + m32).normalize3();
12841             break;
12842         case PLANE_PZ:
12843             dest.set(m03 - m02, m13 - m12, m23 - m22, m33 - m32).normalize3();
12844             break;
12845         default:
12846             // do nothing
12847         }
12848         return dest;
12849     }
12850 
12851     public Vector3d frustumCorner(int corner, ref Vector3d dest) {
12852         double d1, d2, d3;
12853         double n1x, n1y, n1z, n2x, n2y, n2z, n3x, n3y, n3z;
12854         switch (corner) {
12855         case CORNER_NXNYNZ: // left, bottom, near
12856             n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12857             n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom
12858             n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near
12859             break;
12860         case CORNER_PXNYNZ: // right, bottom, near
12861             n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right
12862             n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom
12863             n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near
12864             break;
12865         case CORNER_PXPYNZ: // right, top, near
12866             n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right
12867             n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top
12868             n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near
12869             break;
12870         case CORNER_NXPYNZ: // left, top, near
12871             n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12872             n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top
12873             n3x = m03 + m02; n3y = m13 + m12; n3z = m23 + m22; d3 = m33 + m32; // near
12874             break;
12875         case CORNER_PXNYPZ: // right, bottom, far
12876             n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right
12877             n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom
12878             n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far
12879             break;
12880         case CORNER_NXNYPZ: // left, bottom, far
12881             n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12882             n2x = m03 + m01; n2y = m13 + m11; n2z = m23 + m21; d2 = m33 + m31; // bottom
12883             n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far
12884             break;
12885         case CORNER_NXPYPZ: // left, top, far
12886             n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12887             n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top
12888             n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far
12889             break;
12890         case CORNER_PXPYPZ: // right, top, far
12891             n1x = m03 - m00; n1y = m13 - m10; n1z = m23 - m20; d1 = m33 - m30; // right
12892             n2x = m03 - m01; n2y = m13 - m11; n2z = m23 - m21; d2 = m33 - m31; // top
12893             n3x = m03 - m02; n3y = m13 - m12; n3z = m23 - m22; d3 = m33 - m32; // far
12894             break;
12895         default:
12896             // do nothing
12897         }
12898         double c23x, c23y, c23z;
12899         c23x = n2y * n3z - n2z * n3y;
12900         c23y = n2z * n3x - n2x * n3z;
12901         c23z = n2x * n3y - n2y * n3x;
12902         double c31x, c31y, c31z;
12903         c31x = n3y * n1z - n3z * n1y;
12904         c31y = n3z * n1x - n3x * n1z;
12905         c31z = n3x * n1y - n3y * n1x;
12906         double c12x, c12y, c12z;
12907         c12x = n1y * n2z - n1z * n2y;
12908         c12y = n1z * n2x - n1x * n2z;
12909         c12z = n1x * n2y - n1y * n2x;
12910         double invDot = 1.0 / (n1x * c23x + n1y * c23y + n1z * c23z);
12911         dest.x = (-c23x * d1 - c31x * d2 - c12x * d3) * invDot;
12912         dest.y = (-c23y * d1 - c31y * d2 - c12y * d3) * invDot;
12913         dest.z = (-c23z * d1 - c31z * d2 - c12z * d3) * invDot;
12914         return dest;
12915     }
12916 
12917     public Vector3d perspectiveOrigin(ref Vector3d dest) {
12918         /*
12919          * Simply compute the intersection point of the left, right and top frustum plane.
12920          */
12921         double d1, d2, d3;
12922         double n1x, n1y, n1z, n2x, n2y, n2z, n3x, n3y, n3z;
12923         n1x = m03 + m00; n1y = m13 + m10; n1z = m23 + m20; d1 = m33 + m30; // left
12924         n2x = m03 - m00; n2y = m13 - m10; n2z = m23 - m20; d2 = m33 - m30; // right
12925         n3x = m03 - m01; n3y = m13 - m11; n3z = m23 - m21; d3 = m33 - m31; // top
12926         double c23x, c23y, c23z;
12927         c23x = n2y * n3z - n2z * n3y;
12928         c23y = n2z * n3x - n2x * n3z;
12929         c23z = n2x * n3y - n2y * n3x;
12930         double c31x, c31y, c31z;
12931         c31x = n3y * n1z - n3z * n1y;
12932         c31y = n3z * n1x - n3x * n1z;
12933         c31z = n3x * n1y - n3y * n1x;
12934         double c12x, c12y, c12z;
12935         c12x = n1y * n2z - n1z * n2y;
12936         c12y = n1z * n2x - n1x * n2z;
12937         c12z = n1x * n2y - n1y * n2x;
12938         double invDot = 1.0 / (n1x * c23x + n1y * c23y + n1z * c23z);
12939         dest.x = (-c23x * d1 - c31x * d2 - c12x * d3) * invDot;
12940         dest.y = (-c23y * d1 - c31y * d2 - c12y * d3) * invDot;
12941         dest.z = (-c23z * d1 - c31z * d2 - c12z * d3) * invDot;
12942         return dest;
12943     }
12944 
12945     public Vector3d perspectiveInvOrigin(ref Vector3d dest) {
12946         double invW = 1.0 / m23;
12947         dest.x = m20 * invW;
12948         dest.y = m21 * invW;
12949         dest.z = m22 * invW;
12950         return dest;
12951     }
12952 
12953     public double perspectiveFov() {
12954         /*
12955          * Compute the angle between the bottom and top frustum plane normals.
12956          */
12957         double n1x, n1y, n1z, n2x, n2y, n2z;
12958         n1x = m03 + m01; n1y = m13 + m11; n1z = m23 + m21; // bottom
12959         n2x = m01 - m03; n2y = m11 - m13; n2z = m21 - m23; // top
12960         double n1len = Math.sqrt(n1x * n1x + n1y * n1y + n1z * n1z);
12961         double n2len = Math.sqrt(n2x * n2x + n2y * n2y + n2z * n2z);
12962         return Math.acos((n1x * n2x + n1y * n2y + n1z * n2z) / (n1len * n2len));
12963     }
12964 
12965     public double perspectiveNear() {
12966         return m32 / (m23 + m22);
12967     }
12968 
12969     public double perspectiveFar() {
12970         return m32 / (m22 - m23);
12971     }
12972 
12973     public Vector3d frustumRayDir(double x, double y, ref Vector3d dest) {
12974         /*
12975          * This method works by first obtaining the frustum plane normals,
12976          * then building the cross product to obtain the corner rays,
12977          * and finally bilinearly interpolating to obtain the desired direction.
12978          * The code below uses a condense form of doing all this making use 
12979          * of some mathematical identities to simplify the overall expression.
12980          */
12981         double a = m10 * m23, b = m13 * m21, c = m10 * m21, d = m11 * m23, e = m13 * m20, f = m11 * m20;
12982         double g = m03 * m20, h = m01 * m23, i = m01 * m20, j = m03 * m21, k = m00 * m23, l = m00 * m21;
12983         double m = m00 * m13, n = m03 * m11, o = m00 * m11, p = m01 * m13, q = m03 * m10, r = m01 * m10;
12984         double m1x, m1y, m1z;
12985         m1x = (d + e + f - a - b - c) * (1.0 - y) + (a - b - c + d - e + f) * y;
12986         m1y = (j + k + l - g - h - i) * (1.0 - y) + (g - h - i + j - k + l) * y;
12987         m1z = (p + q + r - m - n - o) * (1.0 - y) + (m - n - o + p - q + r) * y;
12988         double m2x, m2y, m2z;
12989         m2x = (b - c - d + e + f - a) * (1.0 - y) + (a + b - c - d - e + f) * y;
12990         m2y = (h - i - j + k + l - g) * (1.0 - y) + (g + h - i - j - k + l) * y;
12991         m2z = (n - o - p + q + r - m) * (1.0 - y) + (m + n - o - p - q + r) * y;
12992         dest.x = m1x * (1.0 - x) + m2x * x;
12993         dest.y = m1y * (1.0 - x) + m2y * x;
12994         dest.z = m1z * (1.0 - x) + m2z * x;
12995         return dest.normalize(dest);
12996     }
12997 
12998     public Vector3d positiveZ(ref Vector3d dir) {
12999         if ((properties & PROPERTY_ORTHONORMAL) != 0)
13000             return normalizedPositiveZ(dir);
13001         return positiveZGeneric(dir);
13002     }
13003     private Vector3d positiveZGeneric(ref Vector3d dir) {
13004         return dir.set(m10 * m21 - m11 * m20, m20 * m01 - m21 * m00, m00 * m11 - m01 * m10).normalize();
13005     }
13006 
13007     public Vector3d normalizedPositiveZ(ref Vector3d dir) {
13008         return dir.set(m02, m12, m22);
13009     }
13010 
13011     public Vector3d positiveX(ref Vector3d dir) {
13012         if ((properties & PROPERTY_ORTHONORMAL) != 0)
13013             return normalizedPositiveX(dir);
13014         return positiveXGeneric(dir);
13015     }
13016     private Vector3d positiveXGeneric(ref Vector3d dir) {
13017         return dir.set(m11 * m22 - m12 * m21, m02 * m21 - m01 * m22, m01 * m12 - m02 * m11).normalize();
13018     }
13019 
13020     public Vector3d normalizedPositiveX(ref Vector3d dir) {
13021         return dir.set(m00, m10, m20);
13022     }
13023 
13024     public Vector3d positiveY(ref Vector3d dir) {
13025         if ((properties & PROPERTY_ORTHONORMAL) != 0)
13026             return normalizedPositiveY(dir);
13027         return positiveYGeneric(dir);
13028     }
13029     private Vector3d positiveYGeneric(ref Vector3d dir) {
13030         return dir.set(m12 * m20 - m10 * m22, m00 * m22 - m02 * m20, m02 * m10 - m00 * m12).normalize();
13031     }
13032 
13033     public Vector3d normalizedPositiveY(ref Vector3d dir) {
13034         return dir.set(m01, m11, m21);
13035     }
13036 
13037     public Vector3d originAffine(ref Vector3d dest) {
13038         double a = m00 * m11 - m01 * m10;
13039         double b = m00 * m12 - m02 * m10;
13040         double d = m01 * m12 - m02 * m11;
13041         double g = m20 * m31 - m21 * m30;
13042         double h = m20 * m32 - m22 * m30;
13043         double j = m21 * m32 - m22 * m31;
13044         dest.x = -m10 * j + m11 * h - m12 * g;
13045         dest.y =  m00 * j - m01 * h + m02 * g;
13046         dest.z = -m30 * d + m31 * b - m32 * a;
13047         return dest;
13048     }
13049 
13050     public Vector3d origin(ref Vector3d dest) {
13051         if ((properties & PROPERTY_AFFINE) != 0)
13052             return originAffine(dest);
13053         return originGeneric(dest);
13054     }
13055     private Vector3d originGeneric(ref Vector3d dest) {
13056         double a = m00 * m11 - m01 * m10;
13057         double b = m00 * m12 - m02 * m10;
13058         double c = m00 * m13 - m03 * m10;
13059         double d = m01 * m12 - m02 * m11;
13060         double e = m01 * m13 - m03 * m11;
13061         double f = m02 * m13 - m03 * m12;
13062         double g = m20 * m31 - m21 * m30;
13063         double h = m20 * m32 - m22 * m30;
13064         double i = m20 * m33 - m23 * m30;
13065         double j = m21 * m32 - m22 * m31;
13066         double k = m21 * m33 - m23 * m31;
13067         double l = m22 * m33 - m23 * m32;
13068         double det = a * l - b * k + c * j + d * i - e * h + f * g;
13069         double invDet = 1.0 / det;
13070         double nm30 = (-m10 * j + m11 * h - m12 * g) * invDet;
13071         double nm31 = ( m00 * j - m01 * h + m02 * g) * invDet;
13072         double nm32 = (-m30 * d + m31 * b - m32 * a) * invDet;
13073         double nm33 = det / ( m20 * d - m21 * b + m22 * a);
13074         double x = nm30 * nm33;
13075         double y = nm31 * nm33;
13076         double z = nm32 * nm33;
13077         return dest.set(x, y, z);
13078     }
13079 
13080     /**
13081      * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation
13082      * <code>x*a + y*b + z*c + d = 0</code> as if casting a shadow from a given light position/direction <code>light</code>.
13083      * <p>
13084      * 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.
13085      * <p>
13086      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the shadow matrix,
13087      * then the new matrix will be <code>M * S</code>. So when transforming a
13088      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
13089      * shadow projection will be applied first!
13090      * <p>
13091      * Reference: <a href="ftp://ftp.sgi.com/opengl/contrib/blythe/advanced99/notes/node192.html">ftp.sgi.com</a>
13092      * 
13093      * @param light
13094      *          the light's vector
13095      * @param a
13096      *          the x factor in the plane equation
13097      * @param b
13098      *          the y factor in the plane equation
13099      * @param c
13100      *          the z factor in the plane equation
13101      * @param d
13102      *          the constant in the plane equation
13103      * @return this
13104      */
13105     ref public Matrix4d shadow(ref Vector4d light, double a, double b, double c, double d) return {
13106         shadow(light.x, light.y, light.z, light.w, a, b, c, d, this);
13107         return this;
13108     }
13109 
13110     public Matrix4d shadow(ref Vector4d light, double a, double b, double c, double d, ref Matrix4d dest) {
13111         return shadow(light.x, light.y, light.z, light.w, a, b, c, d, dest);
13112     }
13113 
13114     /**
13115      * Apply a projection transformation to this matrix that projects onto the plane specified via the general plane equation
13116      * <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>.
13117      * <p>
13118      * 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.
13119      * <p>
13120      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the shadow matrix,
13121      * then the new matrix will be <code>M * S</code>. So when transforming a
13122      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
13123      * shadow projection will be applied first!
13124      * <p>
13125      * Reference: <a href="ftp://ftp.sgi.com/opengl/contrib/blythe/advanced99/notes/node192.html">ftp.sgi.com</a>
13126      * 
13127      * @param lightX
13128      *          the x-component of the light's vector
13129      * @param lightY
13130      *          the y-component of the light's vector
13131      * @param lightZ
13132      *          the z-component of the light's vector
13133      * @param lightW
13134      *          the w-component of the light's vector
13135      * @param a
13136      *          the x factor in the plane equation
13137      * @param b
13138      *          the y factor in the plane equation
13139      * @param c
13140      *          the z factor in the plane equation
13141      * @param d
13142      *          the constant in the plane equation
13143      * @return this
13144      */
13145     ref public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, double a, double b, double c, double d) return {
13146         shadow(lightX, lightY, lightZ, lightW, a, b, c, d, this);
13147         return this;
13148     }
13149 
13150     public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, double a, double b, double c, double d, ref Matrix4d dest) {
13151         // normalize plane
13152         double invPlaneLen = Math.invsqrt(a*a + b*b + c*c);
13153         double an = a * invPlaneLen;
13154         double bn = b * invPlaneLen;
13155         double cn = c * invPlaneLen;
13156         double dn = d * invPlaneLen;
13157 
13158         double dot = an * lightX + bn * lightY + cn * lightZ + dn * lightW;
13159 
13160         // compute right matrix elements
13161         double rm00 = dot - an * lightX;
13162         double rm01 = -an * lightY;
13163         double rm02 = -an * lightZ;
13164         double rm03 = -an * lightW;
13165         double rm10 = -bn * lightX;
13166         double rm11 = dot - bn * lightY;
13167         double rm12 = -bn * lightZ;
13168         double rm13 = -bn * lightW;
13169         double rm20 = -cn * lightX;
13170         double rm21 = -cn * lightY;
13171         double rm22 = dot - cn * lightZ;
13172         double rm23 = -cn * lightW;
13173         double rm30 = -dn * lightX;
13174         double rm31 = -dn * lightY;
13175         double rm32 = -dn * lightZ;
13176         double rm33 = dot - dn * lightW;
13177 
13178         // matrix multiplication
13179         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02 + m30 * rm03;
13180         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02 + m31 * rm03;
13181         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02 + m32 * rm03;
13182         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02 + m33 * rm03;
13183         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12 + m30 * rm13;
13184         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12 + m31 * rm13;
13185         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12 + m32 * rm13;
13186         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12 + m33 * rm13;
13187         double nm20 = m00 * rm20 + m10 * rm21 + m20 * rm22 + m30 * rm23;
13188         double nm21 = m01 * rm20 + m11 * rm21 + m21 * rm22 + m31 * rm23;
13189         double nm22 = m02 * rm20 + m12 * rm21 + m22 * rm22 + m32 * rm23;
13190         double nm23 = m03 * rm20 + m13 * rm21 + m23 * rm22 + m33 * rm23;
13191         dest._m30(m00 * rm30 + m10 * rm31 + m20 * rm32 + m30 * rm33)
13192         ._m31(m01 * rm30 + m11 * rm31 + m21 * rm32 + m31 * rm33)
13193         ._m32(m02 * rm30 + m12 * rm31 + m22 * rm32 + m32 * rm33)
13194         ._m33(m03 * rm30 + m13 * rm31 + m23 * rm32 + m33 * rm33)
13195         ._m00(nm00)
13196         ._m01(nm01)
13197         ._m02(nm02)
13198         ._m03(nm03)
13199         ._m10(nm10)
13200         ._m11(nm11)
13201         ._m12(nm12)
13202         ._m13(nm13)
13203         ._m20(nm20)
13204         ._m21(nm21)
13205         ._m22(nm22)
13206         ._m23(nm23)
13207         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
13208         return dest;
13209     }
13210 
13211     public Matrix4d shadow(ref Vector4d light, Matrix4d planeTransform, ref Matrix4d dest) {
13212         // compute plane equation by transforming (y = 0)
13213         double a = planeTransform.m10;
13214         double b = planeTransform.m11;
13215         double c = planeTransform.m12;
13216         double d = -a * planeTransform.m30 - b * planeTransform.m31 - c * planeTransform.m32;
13217         return shadow(light.x, light.y, light.z, light.w, a, b, c, d, dest);
13218     }
13219 
13220     /**
13221      * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation
13222      * <code>y = 0</code> as if casting a shadow from a given light position/direction <code>light</code>.
13223      * <p>
13224      * Before the shadow projection is applied, the plane is transformed via the specified <code>planeTransformation</code>.
13225      * <p>
13226      * 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.
13227      * <p>
13228      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the shadow matrix,
13229      * then the new matrix will be <code>M * S</code>. So when transforming a
13230      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
13231      * shadow projection will be applied first!
13232      * 
13233      * @param light
13234      *          the light's vector
13235      * @param planeTransform
13236      *          the transformation to transform the implied plane <code>y = 0</code> before applying the projection
13237      * @return this
13238      */
13239     ref public Matrix4d shadow(ref Vector4d light, Matrix4d planeTransform) return {
13240         shadow(light, planeTransform, this);
13241         return this;
13242     }
13243 
13244     public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, Matrix4d planeTransform, ref Matrix4d dest) {
13245         // compute plane equation by transforming (y = 0)
13246         double a = planeTransform.m10;
13247         double b = planeTransform.m11;
13248         double c = planeTransform.m12;
13249         double d = -a * planeTransform.m30 - b * planeTransform.m31 - c * planeTransform.m32;
13250         return shadow(lightX, lightY, lightZ, lightW, a, b, c, d, dest);
13251     }
13252 
13253     /**
13254      * Apply a projection transformation to this matrix that projects onto the plane with the general plane equation
13255      * <code>y = 0</code> as if casting a shadow from a given light position/direction <code>(lightX, lightY, lightZ, lightW)</code>.
13256      * <p>
13257      * Before the shadow projection is applied, the plane is transformed via the specified <code>planeTransformation</code>.
13258      * <p>
13259      * 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.
13260      * <p>
13261      * If <code>M</code> is <code>this</code> matrix and <code>S</code> the shadow matrix,
13262      * then the new matrix will be <code>M * S</code>. So when transforming a
13263      * vector <code>v</code> with the new matrix by using <code>M * S * v</code>, the
13264      * shadow projection will be applied first!
13265      * 
13266      * @param lightX
13267      *          the x-component of the light vector
13268      * @param lightY
13269      *          the y-component of the light vector
13270      * @param lightZ
13271      *          the z-component of the light vector
13272      * @param lightW
13273      *          the w-component of the light vector
13274      * @param planeTransform
13275      *          the transformation to transform the implied plane <code>y = 0</code> before applying the projection
13276      * @return this
13277      */
13278     ref public Matrix4d shadow(double lightX, double lightY, double lightZ, double lightW, Matrix4d planeTransform) return {
13279         shadow(lightX, lightY, lightZ, lightW, planeTransform, this);
13280         return this;
13281     }
13282 
13283     /**
13284      * Set this matrix to a cylindrical billboard transformation that rotates the local +Z axis of a given object with position <code>objPos</code> towards
13285      * a target position at <code>targetPos</code> while constraining a cylindrical rotation around the given <code>up</code> vector.
13286      * <p>
13287      * This method can be used to create the complete model transformation for a given object, including the translation of the object to
13288      * its position <code>objPos</code>.
13289      * 
13290      * @param objPos
13291      *          the position of the object to rotate towards <code>targetPos</code>
13292      * @param targetPos
13293      *          the position of the target (for example the camera) towards which to rotate the object
13294      * @param up
13295      *          the rotation axis (must be {@link Vector3d#normalize() normalized})
13296      * @return this
13297      */
13298     ref public Matrix4d billboardCylindrical(ref Vector3d objPos, Vector3d targetPos, Vector3d up) return {
13299         double dirX = targetPos.x - objPos.x;
13300         double dirY = targetPos.y - objPos.y;
13301         double dirZ = targetPos.z - objPos.z;
13302         // left = up x dir
13303         double leftX = up.y * dirZ - up.z * dirY;
13304         double leftY = up.z * dirX - up.x * dirZ;
13305         double leftZ = up.x * dirY - up.y * dirX;
13306         // normalize left
13307         double invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
13308         leftX *= invLeftLen;
13309         leftY *= invLeftLen;
13310         leftZ *= invLeftLen;
13311         // recompute dir by constraining rotation around 'up'
13312         // dir = left x up
13313         dirX = leftY * up.z - leftZ * up.y;
13314         dirY = leftZ * up.x - leftX * up.z;
13315         dirZ = leftX * up.y - leftY * up.x;
13316         // normalize dir
13317         double invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
13318         dirX *= invDirLen;
13319         dirY *= invDirLen;
13320         dirZ *= invDirLen;
13321         // set matrix elements
13322         _m00(leftX).
13323         _m01(leftY).
13324         _m02(leftZ).
13325         _m03(0.0).
13326         _m10(up.x).
13327         _m11(up.y).
13328         _m12(up.z).
13329         _m13(0.0).
13330         _m20(dirX).
13331         _m21(dirY).
13332         _m22(dirZ).
13333         _m23(0.0).
13334         _m30(objPos.x).
13335         _m31(objPos.y).
13336         _m32(objPos.z).
13337         _m33(1.0).
13338         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
13339         return this;
13340     }
13341 
13342     /**
13343      * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position <code>objPos</code> towards
13344      * a target position at <code>targetPos</code>.
13345      * <p>
13346      * This method can be used to create the complete model transformation for a given object, including the translation of the object to
13347      * its position <code>objPos</code>.
13348      * <p>
13349      * If preserving an <i>up</i> vector is not necessary when rotating the +Z axis, then a shortest arc rotation can be obtained 
13350      * using {@link #billboardSpherical(ref Vector3d, Vector3d)}.
13351      * 
13352      * @see #billboardSpherical(ref Vector3d, Vector3d)
13353      * 
13354      * @param objPos
13355      *          the position of the object to rotate towards <code>targetPos</code>
13356      * @param targetPos
13357      *          the position of the target (for example the camera) towards which to rotate the object
13358      * @param up
13359      *          the up axis used to orient the object
13360      * @return this
13361      */
13362     ref public Matrix4d billboardSpherical(ref Vector3d objPos, Vector3d targetPos, Vector3d up) return {
13363         double dirX = targetPos.x - objPos.x;
13364         double dirY = targetPos.y - objPos.y;
13365         double dirZ = targetPos.z - objPos.z;
13366         // normalize dir
13367         double invDirLen = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
13368         dirX *= invDirLen;
13369         dirY *= invDirLen;
13370         dirZ *= invDirLen;
13371         // left = up x dir
13372         double leftX = up.y * dirZ - up.z * dirY;
13373         double leftY = up.z * dirX - up.x * dirZ;
13374         double leftZ = up.x * dirY - up.y * dirX;
13375         // normalize left
13376         double invLeftLen = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
13377         leftX *= invLeftLen;
13378         leftY *= invLeftLen;
13379         leftZ *= invLeftLen;
13380         // up = dir x left
13381         double upX = dirY * leftZ - dirZ * leftY;
13382         double upY = dirZ * leftX - dirX * leftZ;
13383         double upZ = dirX * leftY - dirY * leftX;
13384         // set matrix elements
13385         _m00(leftX).
13386         _m01(leftY).
13387         _m02(leftZ).
13388         _m03(0.0).
13389         _m10(upX).
13390         _m11(upY).
13391         _m12(upZ).
13392         _m13(0.0).
13393         _m20(dirX).
13394         _m21(dirY).
13395         _m22(dirZ).
13396         _m23(0.0).
13397         _m30(objPos.x).
13398         _m31(objPos.y).
13399         _m32(objPos.z).
13400         _m33(1.0).
13401         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
13402         return this;
13403     }
13404 
13405     /**
13406      * Set this matrix to a spherical billboard transformation that rotates the local +Z axis of a given object with position <code>objPos</code> towards
13407      * a target position at <code>targetPos</code> using a shortest arc rotation by not preserving any <i>up</i> vector of the object.
13408      * <p>
13409      * This method can be used to create the complete model transformation for a given object, including the translation of the object to
13410      * its position <code>objPos</code>.
13411      * <p>
13412      * In order to specify an <i>up</i> vector which needs to be maintained when rotating the +Z axis of the object,
13413      * use {@link #billboardSpherical(ref Vector3d, Vector3d, Vector3d)}.
13414      * 
13415      * @see #billboardSpherical(ref Vector3d, Vector3d, Vector3d)
13416      * 
13417      * @param objPos
13418      *          the position of the object to rotate towards <code>targetPos</code>
13419      * @param targetPos
13420      *          the position of the target (for example the camera) towards which to rotate the object
13421      * @return this
13422      */
13423     ref public Matrix4d billboardSpherical(ref Vector3d objPos, Vector3d targetPos) return {
13424         double toDirX = targetPos.x - objPos.x;
13425         double toDirY = targetPos.y - objPos.y;
13426         double toDirZ = targetPos.z - objPos.z;
13427         double x = -toDirY;
13428         double y = toDirX;
13429         double w = Math.sqrt(toDirX * toDirX + toDirY * toDirY + toDirZ * toDirZ) + toDirZ;
13430         double invNorm = Math.invsqrt(x * x + y * y + w * w);
13431         x *= invNorm;
13432         y *= invNorm;
13433         w *= invNorm;
13434         double q00 = (x + x) * x;
13435         double q11 = (y + y) * y;
13436         double q01 = (x + x) * y;
13437         double q03 = (x + x) * w;
13438         double q13 = (y + y) * w;
13439         _m00(1.0 - q11).
13440         _m01(q01).
13441         _m02(-q13).
13442         _m03(0.0).
13443         _m10(q01).
13444         _m11(1.0 - q00).
13445         _m12(q03).
13446         _m13(0.0).
13447         _m20(q13).
13448         _m21(-q03).
13449         _m22(1.0 - q11 - q00).
13450         _m23(0.0).
13451         _m30(objPos.x).
13452         _m31(objPos.y).
13453         _m32(objPos.z).
13454         _m33(1.0).
13455         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
13456         return this;
13457     }
13458 
13459     public int hashCode() {
13460         immutable int prime = 31;
13461         int result = 1;
13462         long temp;
13463         temp = Math.doubleToLongBits(m00);
13464         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13465         temp = Math.doubleToLongBits(m01);
13466         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13467         temp = Math.doubleToLongBits(m02);
13468         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13469         temp = Math.doubleToLongBits(m03);
13470         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13471         temp = Math.doubleToLongBits(m10);
13472         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13473         temp = Math.doubleToLongBits(m11);
13474         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13475         temp = Math.doubleToLongBits(m12);
13476         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13477         temp = Math.doubleToLongBits(m13);
13478         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13479         temp = Math.doubleToLongBits(m20);
13480         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13481         temp = Math.doubleToLongBits(m21);
13482         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13483         temp = Math.doubleToLongBits(m22);
13484         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13485         temp = Math.doubleToLongBits(m23);
13486         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13487         temp = Math.doubleToLongBits(m30);
13488         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13489         temp = Math.doubleToLongBits(m31);
13490         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13491         temp = Math.doubleToLongBits(m32);
13492         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13493         temp = Math.doubleToLongBits(m33);
13494         result = prime * result + cast(int) (temp ^ (temp >>> 32));
13495         return result;
13496     }
13497 
13498     public bool equals(Matrix4d m, double delta) {
13499         if (this == m)
13500             return true;
13501         if (!Math.equals(m00, m.m00, delta))
13502             return false;
13503         if (!Math.equals(m01, m.m01, delta))
13504             return false;
13505         if (!Math.equals(m02, m.m02, delta))
13506             return false;
13507         if (!Math.equals(m03, m.m03, delta))
13508             return false;
13509         if (!Math.equals(m10, m.m10, delta))
13510             return false;
13511         if (!Math.equals(m11, m.m11, delta))
13512             return false;
13513         if (!Math.equals(m12, m.m12, delta))
13514             return false;
13515         if (!Math.equals(m13, m.m13, delta))
13516             return false;
13517         if (!Math.equals(m20, m.m20, delta))
13518             return false;
13519         if (!Math.equals(m21, m.m21, delta))
13520             return false;
13521         if (!Math.equals(m22, m.m22, delta))
13522             return false;
13523         if (!Math.equals(m23, m.m23, delta))
13524             return false;
13525         if (!Math.equals(m30, m.m30, delta))
13526             return false;
13527         if (!Math.equals(m31, m.m31, delta))
13528             return false;
13529         if (!Math.equals(m32, m.m32, delta))
13530             return false;
13531         if (!Math.equals(m33, m.m33, delta))
13532             return false;
13533         return true;
13534     }
13535 
13536     public Matrix4d pick(double x, double y, double width, double height, int[] viewport, ref Matrix4d dest) {
13537         double sx = viewport[2] / width;
13538         double sy = viewport[3] / height;
13539         double tx = (viewport[2] + 2.0 * (viewport[0] - x)) / width;
13540         double ty = (viewport[3] + 2.0 * (viewport[1] - y)) / height;
13541         dest._m30(m00 * tx + m10 * ty + m30)
13542         ._m31(m01 * tx + m11 * ty + m31)
13543         ._m32(m02 * tx + m12 * ty + m32)
13544         ._m33(m03 * tx + m13 * ty + m33)
13545         ._m00(m00 * sx)
13546         ._m01(m01 * sx)
13547         ._m02(m02 * sx)
13548         ._m03(m03 * sx)
13549         ._m10(m10 * sy)
13550         ._m11(m11 * sy)
13551         ._m12(m12 * sy)
13552         ._m13(m13 * sy)
13553         ._properties(0);
13554         return dest;
13555     }
13556 
13557     /**
13558      * Apply a picking transformation to this matrix using the given window coordinates <code>(x, y)</code> as the pick center
13559      * and the given <code>(width, height)</code> as the size of the picking region in window coordinates.
13560      * 
13561      * @param x
13562      *          the x coordinate of the picking region center in window coordinates
13563      * @param y
13564      *          the y coordinate of the picking region center in window coordinates
13565      * @param width
13566      *          the width of the picking region in window coordinates
13567      * @param height
13568      *          the height of the picking region in window coordinates
13569      * @param viewport
13570      *          the viewport described by <code>[x, y, width, height]</code>
13571      * @return this
13572      */
13573     ref public Matrix4d pick(double x, double y, double width, double height, int[] viewport) return {
13574         pick(x, y, width, height, viewport, this);
13575         return this;
13576     }
13577 
13578     public bool isAffine() {
13579         return m03 == 0.0 && m13 == 0.0 && m23 == 0.0 && m33 == 1.0;
13580     }
13581 
13582     /**
13583      * Exchange the values of <code>this</code> matrix with the given <code>other</code> matrix.
13584      * 
13585      * @param other
13586      *          the other matrix to exchange the values with
13587      * @return this
13588      */
13589     ref public Matrix4d swap(ref Matrix4d other) return {
13590         double tmp;
13591         tmp = m00; m00 = other.m00; other.m00 = tmp;
13592         tmp = m01; m01 = other.m01; other.m01 = tmp;
13593         tmp = m02; m02 = other.m02; other.m02 = tmp;
13594         tmp = m03; m03 = other.m03; other.m03 = tmp;
13595         tmp = m10; m10 = other.m10; other.m10 = tmp;
13596         tmp = m11; m11 = other.m11; other.m11 = tmp;
13597         tmp = m12; m12 = other.m12; other.m12 = tmp;
13598         tmp = m13; m13 = other.m13; other.m13 = tmp;
13599         tmp = m20; m20 = other.m20; other.m20 = tmp;
13600         tmp = m21; m21 = other.m21; other.m21 = tmp;
13601         tmp = m22; m22 = other.m22; other.m22 = tmp;
13602         tmp = m23; m23 = other.m23; other.m23 = tmp;
13603         tmp = m30; m30 = other.m30; other.m30 = tmp;
13604         tmp = m31; m31 = other.m31; other.m31 = tmp;
13605         tmp = m32; m32 = other.m32; other.m32 = tmp;
13606         tmp = m33; m33 = other.m33; other.m33 = tmp;
13607         int props = properties;
13608         this.properties = other.properties;
13609         other.properties = props;
13610         return this;
13611     }
13612 
13613     public Matrix4d arcball(double radius, double centerX, double centerY, double centerZ, double angleX, double angleY, ref Matrix4d dest) {
13614         double m30 = m20 * -radius + this.m30;
13615         double m31 = m21 * -radius + this.m31;
13616         double m32 = m22 * -radius + this.m32;
13617         double m33 = m23 * -radius + this.m33;
13618         double sin = Math.sin(angleX);
13619         double cos = Math.cosFromSin(sin, angleX);
13620         double nm10 = m10 * cos + m20 * sin;
13621         double nm11 = m11 * cos + m21 * sin;
13622         double nm12 = m12 * cos + m22 * sin;
13623         double nm13 = m13 * cos + m23 * sin;
13624         double m20 = this.m20 * cos - m10 * sin;
13625         double m21 = this.m21 * cos - m11 * sin;
13626         double m22 = this.m22 * cos - m12 * sin;
13627         double m23 = this.m23 * cos - m13 * sin;
13628         sin = Math.sin(angleY);
13629         cos = Math.cosFromSin(sin, angleY);
13630         double nm00 = m00 * cos - m20 * sin;
13631         double nm01 = m01 * cos - m21 * sin;
13632         double nm02 = m02 * cos - m22 * sin;
13633         double nm03 = m03 * cos - m23 * sin;
13634         double nm20 = m00 * sin + m20 * cos;
13635         double nm21 = m01 * sin + m21 * cos;
13636         double nm22 = m02 * sin + m22 * cos;
13637         double nm23 = m03 * sin + m23 * cos;
13638         dest._m30(-nm00 * centerX - nm10 * centerY - nm20 * centerZ + m30)
13639         ._m31(-nm01 * centerX - nm11 * centerY - nm21 * centerZ + m31)
13640         ._m32(-nm02 * centerX - nm12 * centerY - nm22 * centerZ + m32)
13641         ._m33(-nm03 * centerX - nm13 * centerY - nm23 * centerZ + m33)
13642         ._m20(nm20)
13643         ._m21(nm21)
13644         ._m22(nm22)
13645         ._m23(nm23)
13646         ._m10(nm10)
13647         ._m11(nm11)
13648         ._m12(nm12)
13649         ._m13(nm13)
13650         ._m00(nm00)
13651         ._m01(nm01)
13652         ._m02(nm02)
13653         ._m03(nm03)
13654         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
13655         return dest;
13656     }
13657 
13658     public Matrix4d arcball(double radius, Vector3d center, double angleX, double angleY, ref Matrix4d dest) {
13659         return arcball(radius, center.x, center.y, center.z, angleX, angleY, dest);
13660     }
13661 
13662     /**
13663      * Apply an arcball view transformation to this matrix with the given <code>radius</code> and center <code>(centerX, centerY, centerZ)</code>
13664      * position of the arcball and the specified X and Y rotation angles.
13665      * <p>
13666      * This method is equivalent to calling: <code>translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-centerX, -centerY, -centerZ)</code>
13667      * 
13668      * @param radius
13669      *          the arcball radius
13670      * @param centerX
13671      *          the x coordinate of the center position of the arcball
13672      * @param centerY
13673      *          the y coordinate of the center position of the arcball
13674      * @param centerZ
13675      *          the z coordinate of the center position of the arcball
13676      * @param angleX
13677      *          the rotation angle around the X axis in radians
13678      * @param angleY
13679      *          the rotation angle around the Y axis in radians
13680      * @return this
13681      */
13682     ref public Matrix4d arcball(double radius, double centerX, double centerY, double centerZ, double angleX, double angleY) return {
13683         arcball(radius, centerX, centerY, centerZ, angleX, angleY, this);
13684         return this;
13685     }
13686 
13687     /**
13688      * Apply an arcball view transformation to this matrix with the given <code>radius</code> and <code>center</code>
13689      * position of the arcball and the specified X and Y rotation angles.
13690      * <p>
13691      * This method is equivalent to calling: <code>translate(0, 0, -radius).rotateX(angleX).rotateY(angleY).translate(-center.x, -center.y, -center.z)</code>
13692      * 
13693      * @param radius
13694      *          the arcball radius
13695      * @param center
13696      *          the center position of the arcball
13697      * @param angleX
13698      *          the rotation angle around the X axis in radians
13699      * @param angleY
13700      *          the rotation angle around the Y axis in radians
13701      * @return this
13702      */
13703     ref public Matrix4d arcball(double radius, Vector3d center, double angleX, double angleY) return {
13704         arcball(radius, center.x, center.y, center.z, angleX, angleY, this);
13705         return this;
13706     }
13707 
13708     /**
13709      * Compute the axis-aligned bounding box of the frustum described by <code>this</code> matrix and store the minimum corner
13710      * coordinates in the given <code>min</code> and the maximum corner coordinates in the given <code>max</code> vector.
13711      * <p>
13712      * The matrix <code>this</code> is assumed to be the {@link #invert() inverse} of the origial view-projection matrix
13713      * for which to compute the axis-aligned bounding box in world-space.
13714      * <p>
13715      * The axis-aligned bounding box of the unit frustum is <code>(-1, -1, -1)</code>, <code>(1, 1, 1)</code>.
13716      * 
13717      * @param min
13718      *          will hold the minimum corner coordinates of the axis-aligned bounding box
13719      * @param max
13720      *          will hold the maximum corner coordinates of the axis-aligned bounding box
13721      * @return this
13722      */
13723     ref public Matrix4d frustumAabb(ref Vector3d min, Vector3d max) return {
13724         double minX = double.infinity;
13725         double minY = double.infinity;
13726         double minZ = double.infinity;
13727         double maxX = -double.infinity;
13728         double maxY = -double.infinity;
13729         double maxZ = -double.infinity;
13730         for (int t = 0; t < 8; t++) {
13731             double x = ((t & 1) << 1) - 1.0;
13732             double y = (((t >>> 1) & 1) << 1) - 1.0;
13733             double z = (((t >>> 2) & 1) << 1) - 1.0;
13734             double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33);
13735             double nx = (m00 * x + m10 * y + m20 * z + m30) * invW;
13736             double ny = (m01 * x + m11 * y + m21 * z + m31) * invW;
13737             double nz = (m02 * x + m12 * y + m22 * z + m32) * invW;
13738             minX = minX < nx ? minX : nx;
13739             minY = minY < ny ? minY : ny;
13740             minZ = minZ < nz ? minZ : nz;
13741             maxX = maxX > nx ? maxX : nx;
13742             maxY = maxY > ny ? maxY : ny;
13743             maxZ = maxZ > nz ? maxZ : nz;
13744         }
13745         min.x = minX;
13746         min.y = minY;
13747         min.z = minZ;
13748         max.x = maxX;
13749         max.y = maxY;
13750         max.z = maxZ;
13751         return this;
13752     }
13753 
13754     public Matrix4d projectedGridRange(Matrix4d projector, double sLower, double sUpper, ref Matrix4d dest) {
13755         // Compute intersection with frustum edges and plane
13756         double minX = double.infinity, minY = double.infinity;
13757         double maxX = -double.infinity, maxY = -double.infinity;
13758         bool intersection = false;
13759         for (int t = 0; t < 3 * 4; t++) {
13760             double c0X, c0Y, c0Z;
13761             double c1X, c1Y, c1Z;
13762             if (t < 4) {
13763                 // all x edges
13764                 c0X = -1; c1X = +1;
13765                 c0Y = c1Y = ((t & 1) << 1) - 1.0;
13766                 c0Z = c1Z = (((t >>> 1) & 1) << 1) - 1.0;
13767             } else if (t < 8) {
13768                 // all y edges
13769                 c0Y = -1; c1Y = +1;
13770                 c0X = c1X = ((t & 1) << 1) - 1.0;
13771                 c0Z = c1Z = (((t >>> 1) & 1) << 1) - 1.0;
13772             } else {
13773                 // all z edges
13774                 c0Z = -1; c1Z = +1;
13775                 c0X = c1X = ((t & 1) << 1) - 1.0;
13776                 c0Y = c1Y = (((t >>> 1) & 1) << 1) - 1.0;
13777             }
13778             // unproject corners
13779             double invW = 1.0 / (m03 * c0X + m13 * c0Y + m23 * c0Z + m33);
13780             double p0x = (m00 * c0X + m10 * c0Y + m20 * c0Z + m30) * invW;
13781             double p0y = (m01 * c0X + m11 * c0Y + m21 * c0Z + m31) * invW;
13782             double p0z = (m02 * c0X + m12 * c0Y + m22 * c0Z + m32) * invW;
13783             invW = 1.0 / (m03 * c1X + m13 * c1Y + m23 * c1Z + m33);
13784             double p1x = (m00 * c1X + m10 * c1Y + m20 * c1Z + m30) * invW;
13785             double p1y = (m01 * c1X + m11 * c1Y + m21 * c1Z + m31) * invW;
13786             double p1z = (m02 * c1X + m12 * c1Y + m22 * c1Z + m32) * invW;
13787             double dirX = p1x - p0x;
13788             double dirY = p1y - p0y;
13789             double dirZ = p1z - p0z;
13790             double invDenom = 1.0 / dirY;
13791             // test for intersection
13792             for (int s = 0; s < 2; s++) {
13793                 double isectT = -(p0y + (s == 0 ? sLower : sUpper)) * invDenom;
13794                 if (isectT >= 0.0 && isectT <= 1.0) {
13795                     intersection = true;
13796                     // project with projector matrix
13797                     double ix = p0x + isectT * dirX;
13798                     double iz = p0z + isectT * dirZ;
13799                     invW = 1.0 / (projector.m03 * ix + projector.m23 * iz + projector.m33);
13800                     double px = (projector.m00 * ix + projector.m20 * iz + projector.m30) * invW;
13801                     double py = (projector.m01 * ix + projector.m21 * iz + projector.m31) * invW;
13802                     minX = minX < px ? minX : px;
13803                     minY = minY < py ? minY : py;
13804                     maxX = maxX > px ? maxX : px;
13805                     maxY = maxY > py ? maxY : py;
13806                 }
13807             }
13808         }
13809         if (!intersection)
13810             return dest; // <- projected grid is not visible
13811         dest.set(maxX - minX, 0, 0, 0, 0, maxY - minY, 0, 0, 0, 0, 1, 0, minX, minY, 0, 1)
13812         ._properties(PROPERTY_AFFINE);
13813         return dest;
13814     }
13815 
13816     public Matrix4d perspectiveFrustumSlice(double near, double far, ref Matrix4d dest) {
13817         double invOldNear = (m23 + m22) / m32;
13818         double invNearFar = 1.0 / (near - far);
13819         dest._m00(m00 * invOldNear * near)
13820         ._m01(m01)
13821         ._m02(m02)
13822         ._m03(m03)
13823         ._m10(m10)
13824         ._m11(m11 * invOldNear * near)
13825         ._m12(m12)
13826         ._m13(m13)
13827         ._m20(m20)
13828         ._m21(m21)
13829         ._m22((far + near) * invNearFar)
13830         ._m23(m23)
13831         ._m30(m30)
13832         ._m31(m31)
13833         ._m32((far + far) * near * invNearFar)
13834         ._m33(m33)
13835         ._properties(properties & ~(PROPERTY_IDENTITY | PROPERTY_TRANSLATION | PROPERTY_ORTHONORMAL));
13836         return dest;
13837     }
13838 
13839     public Matrix4d orthoCrop(Matrix4d view, ref Matrix4d dest) {
13840         // determine min/max world z and min/max orthographically view-projected x/y
13841         double minX = double.infinity, maxX = -double.infinity;
13842         double minY = double.infinity, maxY = -double.infinity;
13843         double minZ = double.infinity, maxZ = -double.infinity;
13844         for (int t = 0; t < 8; t++) {
13845             double x = ((t & 1) << 1) - 1.0;
13846             double y = (((t >>> 1) & 1) << 1) - 1.0;
13847             double z = (((t >>> 2) & 1) << 1) - 1.0;
13848             double invW = 1.0 / (m03 * x + m13 * y + m23 * z + m33);
13849             double wx = (m00 * x + m10 * y + m20 * z + m30) * invW;
13850             double wy = (m01 * x + m11 * y + m21 * z + m31) * invW;
13851             double wz = (m02 * x + m12 * y + m22 * z + m32) * invW;
13852             invW = 1.0 / (view.m03 * wx + view.m13 * wy + view.m23 * wz + view.m33);
13853             double vx = view.m00 * wx + view.m10 * wy + view.m20 * wz + view.m30;
13854             double vy = view.m01 * wx + view.m11 * wy + view.m21 * wz + view.m31;
13855             double vz = (view.m02 * wx + view.m12 * wy + view.m22 * wz + view.m32) * invW;
13856             minX = minX < vx ? minX : vx;
13857             maxX = maxX > vx ? maxX : vx;
13858             minY = minY < vy ? minY : vy;
13859             maxY = maxY > vy ? maxY : vy;
13860             minZ = minZ < vz ? minZ : vz;
13861             maxZ = maxZ > vz ? maxZ : vz;
13862         }
13863         // build crop projection matrix to fit 'this' frustum into view
13864         return dest.setOrtho(minX, maxX, minY, maxY, -maxZ, -minZ);
13865     }
13866 
13867     /**
13868      * Set <code>this</code> matrix to a perspective transformation that maps the trapezoid spanned by the four corner coordinates
13869      * <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>.
13870      * <p>
13871      * The corner coordinates are given in counter-clockwise order starting from the <i>left</i> corner on the smaller parallel side of the trapezoid
13872      * seen when looking at the trapezoid oriented with its shorter parallel edge at the bottom and its longer parallel edge at the top.
13873      * <p>
13874      * Reference: <a href="http://www.comp.nus.edu.sg/~tants/tsm/TSM_recipe.html">Trapezoidal Shadow Maps (TSM) - Recipe</a>
13875      * 
13876      * @param p0x
13877      *          the x coordinate of the left corner at the shorter edge of the trapezoid
13878      * @param p0y
13879      *          the y coordinate of the left corner at the shorter edge of the trapezoid
13880      * @param p1x
13881      *          the x coordinate of the right corner at the shorter edge of the trapezoid
13882      * @param p1y
13883      *          the y coordinate of the right corner at the shorter edge of the trapezoid
13884      * @param p2x
13885      *          the x coordinate of the right corner at the longer edge of the trapezoid
13886      * @param p2y
13887      *          the y coordinate of the right corner at the longer edge of the trapezoid
13888      * @param p3x
13889      *          the x coordinate of the left corner at the longer edge of the trapezoid
13890      * @param p3y
13891      *          the y coordinate of the left corner at the longer edge of the trapezoid
13892      * @return this
13893      */
13894     ref public Matrix4d trapezoidCrop(double p0x, double p0y, double p1x, double p1y, double p2x, double p2y, double p3x, double p3y) return {
13895         double aX = p1y - p0y, aY = p0x - p1x;
13896         double nm00 = aY;
13897         double nm10 = -aX;
13898         double nm30 = aX * p0y - aY * p0x;
13899         double nm01 = aX;
13900         double nm11 = aY;
13901         double nm31 = -(aX * p0x + aY * p0y);
13902         double c3x = nm00 * p3x + nm10 * p3y + nm30;
13903         double c3y = nm01 * p3x + nm11 * p3y + nm31;
13904         double s = -c3x / c3y;
13905         nm00 += s * nm01;
13906         nm10 += s * nm11;
13907         nm30 += s * nm31;
13908         double d1x = nm00 * p1x + nm10 * p1y + nm30;
13909         double d2x = nm00 * p2x + nm10 * p2y + nm30;
13910         double d = d1x * c3y / (d2x - d1x);
13911         nm31 += d;
13912         double sx = 2.0 / d2x;
13913         double sy = 1.0 / (c3y + d);
13914         double u = (sy + sy) * d / (1.0 - sy * d);
13915         double m03 = nm01 * sy;
13916         double m13 = nm11 * sy;
13917         double m33 = nm31 * sy;
13918         nm01 = (u + 1.0) * m03;
13919         nm11 = (u + 1.0) * m13;
13920         nm31 = (u + 1.0) * m33 - u;
13921         nm00 = sx * nm00 - m03;
13922         nm10 = sx * nm10 - m13;
13923         nm30 = sx * nm30 - m33;
13924         set(nm00, nm01, 0, m03,
13925             nm10, nm11, 0, m13,
13926               0,   0, 1,   0,
13927             nm30, nm31, 0, m33);
13928         properties = 0;
13929         return this;
13930     }
13931 
13932     ref public Matrix4d transformAab(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, ref Vector3d outMin, ref Vector3d outMax) return {
13933         double xax = m00 * minX, xay = m01 * minX, xaz = m02 * minX;
13934         double xbx = m00 * maxX, xby = m01 * maxX, xbz = m02 * maxX;
13935         double yax = m10 * minY, yay = m11 * minY, yaz = m12 * minY;
13936         double ybx = m10 * maxY, yby = m11 * maxY, ybz = m12 * maxY;
13937         double zax = m20 * minZ, zay = m21 * minZ, zaz = m22 * minZ;
13938         double zbx = m20 * maxZ, zby = m21 * maxZ, zbz = m22 * maxZ;
13939         double xminx, xminy, xminz, yminx, yminy, yminz, zminx, zminy, zminz;
13940         double xmaxx, xmaxy, xmaxz, ymaxx, ymaxy, ymaxz, zmaxx, zmaxy, zmaxz;
13941         if (xax < xbx) {
13942             xminx = xax;
13943             xmaxx = xbx;
13944         } else {
13945             xminx = xbx;
13946             xmaxx = xax;
13947         }
13948         if (xay < xby) {
13949             xminy = xay;
13950             xmaxy = xby;
13951         } else {
13952             xminy = xby;
13953             xmaxy = xay;
13954         }
13955         if (xaz < xbz) {
13956             xminz = xaz;
13957             xmaxz = xbz;
13958         } else {
13959             xminz = xbz;
13960             xmaxz = xaz;
13961         }
13962         if (yax < ybx) {
13963             yminx = yax;
13964             ymaxx = ybx;
13965         } else {
13966             yminx = ybx;
13967             ymaxx = yax;
13968         }
13969         if (yay < yby) {
13970             yminy = yay;
13971             ymaxy = yby;
13972         } else {
13973             yminy = yby;
13974             ymaxy = yay;
13975         }
13976         if (yaz < ybz) {
13977             yminz = yaz;
13978             ymaxz = ybz;
13979         } else {
13980             yminz = ybz;
13981             ymaxz = yaz;
13982         }
13983         if (zax < zbx) {
13984             zminx = zax;
13985             zmaxx = zbx;
13986         } else {
13987             zminx = zbx;
13988             zmaxx = zax;
13989         }
13990         if (zay < zby) {
13991             zminy = zay;
13992             zmaxy = zby;
13993         } else {
13994             zminy = zby;
13995             zmaxy = zay;
13996         }
13997         if (zaz < zbz) {
13998             zminz = zaz;
13999             zmaxz = zbz;
14000         } else {
14001             zminz = zbz;
14002             zmaxz = zaz;
14003         }
14004         outMin.x = xminx + yminx + zminx + m30;
14005         outMin.y = xminy + yminy + zminy + m31;
14006         outMin.z = xminz + yminz + zminz + m32;
14007         outMax.x = xmaxx + ymaxx + zmaxx + m30;
14008         outMax.y = xmaxy + ymaxy + zmaxy + m31;
14009         outMax.z = xmaxz + ymaxz + zmaxz + m32;
14010         return this;
14011     }
14012 
14013     ref public Matrix4d transformAab(ref Vector3d min, ref Vector3d max, ref Vector3d outMin, ref Vector3d outMax) return {
14014         return transformAab(min.x, min.y, min.z, max.x, max.y, max.z, outMin, outMax);
14015     }
14016 
14017     /**
14018      * Linearly interpolate <code>this</code> and <code>other</code> using the given interpolation factor <code>t</code>
14019      * and store the result in <code>this</code>.
14020      * <p>
14021      * 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>
14022      * then the result is <code>other</code>.
14023      *
14024      * @param other
14025      *          the other matrix
14026      * @param t
14027      *          the interpolation factor between 0.0 and 1.0
14028      * @return this
14029      */
14030     ref public Matrix4d lerp(Matrix4d other, double t) return {
14031         lerp(other, t, this);
14032         return this;
14033     }
14034 
14035     public Matrix4d lerp(Matrix4d other, double t, ref Matrix4d dest) {
14036         dest._m00(Math.fma(other.m00 - m00, t, m00))
14037         ._m01(Math.fma(other.m01 - m01, t, m01))
14038         ._m02(Math.fma(other.m02 - m02, t, m02))
14039         ._m03(Math.fma(other.m03 - m03, t, m03))
14040         ._m10(Math.fma(other.m10 - m10, t, m10))
14041         ._m11(Math.fma(other.m11 - m11, t, m11))
14042         ._m12(Math.fma(other.m12 - m12, t, m12))
14043         ._m13(Math.fma(other.m13 - m13, t, m13))
14044         ._m20(Math.fma(other.m20 - m20, t, m20))
14045         ._m21(Math.fma(other.m21 - m21, t, m21))
14046         ._m22(Math.fma(other.m22 - m22, t, m22))
14047         ._m23(Math.fma(other.m23 - m23, t, m23))
14048         ._m30(Math.fma(other.m30 - m30, t, m30))
14049         ._m31(Math.fma(other.m31 - m31, t, m31))
14050         ._m32(Math.fma(other.m32 - m32, t, m32))
14051         ._m33(Math.fma(other.m33 - m33, t, m33))
14052         ._properties(properties & other.properties);
14053         return dest;
14054     }
14055 
14056     /**
14057      * Apply a model transformation to this matrix for a right-handed coordinate system, 
14058      * that aligns the local <code>+Z</code> axis with <code>direction</code>
14059      * and store the result in <code>dest</code>.
14060      * <p>
14061      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
14062      * then the new matrix will be <code>M * L</code>. So when transforming a
14063      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
14064      * the lookat transformation will be applied first!
14065      * <p>
14066      * In order to set the matrix to a rotation transformation without post-multiplying it,
14067      * use {@link #rotationTowards(ref Vector3d, Vector3d) rotationTowards()}.
14068      * <p>
14069      * This method is equivalent to calling: <code>mulAffine(new Matrix4d().lookAt(new Vector3d(), new Vector3d(dir).negate(), up).invertAffine(), dest)</code>
14070      * 
14071      * @see #rotateTowards(double, double, double, double, double, double, Matrix4d)
14072      * @see #rotationTowards(ref Vector3d, Vector3d)
14073      * 
14074      * @param direction
14075      *              the direction to rotate towards
14076      * @param up
14077      *              the up vector
14078      * @param dest
14079      *              will hold the result
14080      * @return dest
14081      */
14082     public Matrix4d rotateTowards(ref Vector3d direction, Vector3d up, ref Matrix4d dest) {
14083         return rotateTowards(direction.x, direction.y, direction.z, up.x, up.y, up.z, dest);
14084     }
14085 
14086     /**
14087      * Apply a model transformation to this matrix for a right-handed coordinate system, 
14088      * that aligns the local <code>+Z</code> axis with <code>direction</code>.
14089      * <p>
14090      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
14091      * then the new matrix will be <code>M * L</code>. So when transforming a
14092      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
14093      * the lookat transformation will be applied first!
14094      * <p>
14095      * In order to set the matrix to a rotation transformation without post-multiplying it,
14096      * use {@link #rotationTowards(ref Vector3d, Vector3d) rotationTowards()}.
14097      * <p>
14098      * This method is equivalent to calling: <code>mulAffine(new Matrix4d().lookAt(new Vector3d(), new Vector3d(dir).negate(), up).invertAffine())</code>
14099      * 
14100      * @see #rotateTowards(double, double, double, double, double, double)
14101      * @see #rotationTowards(ref Vector3d, Vector3d)
14102      * 
14103      * @param direction
14104      *              the direction to orient towards
14105      * @param up
14106      *              the up vector
14107      * @return this
14108      */
14109     ref public Matrix4d rotateTowards(ref Vector3d direction, Vector3d up) return {
14110         rotateTowards(direction.x, direction.y, direction.z, up.x, up.y, up.z, this);
14111         return this;
14112     }
14113 
14114     /**
14115      * Apply a model transformation to this matrix for a right-handed coordinate system, 
14116      * that aligns the local <code>+Z</code> axis with <code>(dirX, dirY, dirZ)</code>.
14117      * <p>
14118      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
14119      * then the new matrix will be <code>M * L</code>. So when transforming a
14120      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
14121      * the lookat transformation will be applied first!
14122      * <p>
14123      * In order to set the matrix to a rotation transformation without post-multiplying it,
14124      * use {@link #rotationTowards(double, double, double, double, double, double) rotationTowards()}.
14125      * <p>
14126      * This method is equivalent to calling: <code>mulAffine(new Matrix4d().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine())</code>
14127      * 
14128      * @see #rotateTowards(ref Vector3d, Vector3d)
14129      * @see #rotationTowards(double, double, double, double, double, double)
14130      * 
14131      * @param dirX
14132      *              the x-coordinate of the direction to rotate towards
14133      * @param dirY
14134      *              the y-coordinate of the direction to rotate towards
14135      * @param dirZ
14136      *              the z-coordinate of the direction to rotate towards
14137      * @param upX
14138      *              the x-coordinate of the up vector
14139      * @param upY
14140      *              the y-coordinate of the up vector
14141      * @param upZ
14142      *              the z-coordinate of the up vector
14143      * @return this
14144      */
14145     ref public Matrix4d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) return {
14146         rotateTowards(dirX, dirY, dirZ, upX, upY, upZ, this);
14147         return this;
14148     }
14149 
14150     /**
14151      * Apply a model transformation to this matrix for a right-handed coordinate system, 
14152      * that aligns the local <code>+Z</code> axis with <code>dir</code>
14153      * and store the result in <code>dest</code>.
14154      * <p>
14155      * If <code>M</code> is <code>this</code> matrix and <code>L</code> the lookat matrix,
14156      * then the new matrix will be <code>M * L</code>. So when transforming a
14157      * vector <code>v</code> with the new matrix by using <code>M * L * v</code>,
14158      * the lookat transformation will be applied first!
14159      * <p>
14160      * In order to set the matrix to a rotation transformation without post-multiplying it,
14161      * use {@link #rotationTowards(double, double, double, double, double, double) rotationTowards()}.
14162      * <p>
14163      * This method is equivalent to calling: <code>mulAffine(new Matrix4d().lookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine(), dest)</code>
14164      * 
14165      * @see #rotateTowards(ref Vector3d, Vector3d)
14166      * @see #rotationTowards(double, double, double, double, double, double)
14167      * 
14168      * @param dirX
14169      *              the x-coordinate of the direction to rotate towards
14170      * @param dirY
14171      *              the y-coordinate of the direction to rotate towards
14172      * @param dirZ
14173      *              the z-coordinate of the direction to rotate towards
14174      * @param upX
14175      *              the x-coordinate of the up vector
14176      * @param upY
14177      *              the y-coordinate of the up vector
14178      * @param upZ
14179      *              the z-coordinate of the up vector
14180      * @param dest
14181      *              will hold the result
14182      * @return dest
14183      */
14184     public Matrix4d rotateTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ, ref Matrix4d dest) {
14185         // Normalize direction
14186         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
14187         double ndirX = dirX * invDirLength;
14188         double ndirY = dirY * invDirLength;
14189         double ndirZ = dirZ * invDirLength;
14190         // left = up x direction
14191         double leftX, leftY, leftZ;
14192         leftX = upY * ndirZ - upZ * ndirY;
14193         leftY = upZ * ndirX - upX * ndirZ;
14194         leftZ = upX * ndirY - upY * ndirX;
14195         // normalize left
14196         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
14197         leftX *= invLeftLength;
14198         leftY *= invLeftLength;
14199         leftZ *= invLeftLength;
14200         // up = direction x left
14201         double upnX = ndirY * leftZ - ndirZ * leftY;
14202         double upnY = ndirZ * leftX - ndirX * leftZ;
14203         double upnZ = ndirX * leftY - ndirY * leftX;
14204         double rm00 = leftX;
14205         double rm01 = leftY;
14206         double rm02 = leftZ;
14207         double rm10 = upnX;
14208         double rm11 = upnY;
14209         double rm12 = upnZ;
14210         double rm20 = ndirX;
14211         double rm21 = ndirY;
14212         double rm22 = ndirZ;
14213         double nm00 = m00 * rm00 + m10 * rm01 + m20 * rm02;
14214         double nm01 = m01 * rm00 + m11 * rm01 + m21 * rm02;
14215         double nm02 = m02 * rm00 + m12 * rm01 + m22 * rm02;
14216         double nm03 = m03 * rm00 + m13 * rm01 + m23 * rm02;
14217         double nm10 = m00 * rm10 + m10 * rm11 + m20 * rm12;
14218         double nm11 = m01 * rm10 + m11 * rm11 + m21 * rm12;
14219         double nm12 = m02 * rm10 + m12 * rm11 + m22 * rm12;
14220         double nm13 = m03 * rm10 + m13 * rm11 + m23 * rm12;
14221         dest._m30(m30)
14222         ._m31(m31)
14223         ._m32(m32)
14224         ._m33(m33)
14225         ._m20(m00 * rm20 + m10 * rm21 + m20 * rm22)
14226         ._m21(m01 * rm20 + m11 * rm21 + m21 * rm22)
14227         ._m22(m02 * rm20 + m12 * rm21 + m22 * rm22)
14228         ._m23(m03 * rm20 + m13 * rm21 + m23 * rm22)
14229         ._m00(nm00)
14230         ._m01(nm01)
14231         ._m02(nm02)
14232         ._m03(nm03)
14233         ._m10(nm10)
14234         ._m11(nm11)
14235         ._m12(nm12)
14236         ._m13(nm13)
14237         ._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
14238         return dest;
14239     }
14240 
14241     /**
14242      * Set this matrix to a model transformation for a right-handed coordinate system, 
14243      * that aligns the local <code>-z</code> axis with <code>dir</code>.
14244      * <p>
14245      * In order to apply the rotation transformation to a previous existing transformation,
14246      * use {@link #rotateTowards(double, double, double, double, double, double) rotateTowards}.
14247      * <p>
14248      * This method is equivalent to calling: <code>setLookAt(new Vector3d(), new Vector3d(dir).negate(), up).invertAffine()</code>
14249      * 
14250      * @see #rotationTowards(ref Vector3d, Vector3d)
14251      * @see #rotateTowards(double, double, double, double, double, double)
14252      * 
14253      * @param dir
14254      *              the direction to orient the local -z axis towards
14255      * @param up
14256      *              the up vector
14257      * @return this
14258      */
14259     ref public Matrix4d rotationTowards(ref Vector3d dir, Vector3d up) return {
14260         return rotationTowards(dir.x, dir.y, dir.z, up.x, up.y, up.z);
14261     }
14262 
14263     /**
14264      * Set this matrix to a model transformation for a right-handed coordinate system, 
14265      * that aligns the local <code>-z</code> axis with <code>dir</code>.
14266      * <p>
14267      * In order to apply the rotation transformation to a previous existing transformation,
14268      * use {@link #rotateTowards(double, double, double, double, double, double) rotateTowards}.
14269      * <p>
14270      * This method is equivalent to calling: <code>setLookAt(0, 0, 0, -dirX, -dirY, -dirZ, upX, upY, upZ).invertAffine()</code>
14271      * 
14272      * @see #rotateTowards(ref Vector3d, Vector3d)
14273      * @see #rotationTowards(double, double, double, double, double, double)
14274      * 
14275      * @param dirX
14276      *              the x-coordinate of the direction to rotate towards
14277      * @param dirY
14278      *              the y-coordinate of the direction to rotate towards
14279      * @param dirZ
14280      *              the z-coordinate of the direction to rotate towards
14281      * @param upX
14282      *              the x-coordinate of the up vector
14283      * @param upY
14284      *              the y-coordinate of the up vector
14285      * @param upZ
14286      *              the z-coordinate of the up vector
14287      * @return this
14288      */
14289     ref public Matrix4d rotationTowards(double dirX, double dirY, double dirZ, double upX, double upY, double upZ) return {
14290         // Normalize direction
14291         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
14292         double ndirX = dirX * invDirLength;
14293         double ndirY = dirY * invDirLength;
14294         double ndirZ = dirZ * invDirLength;
14295         // left = up x direction
14296         double leftX, leftY, leftZ;
14297         leftX = upY * ndirZ - upZ * ndirY;
14298         leftY = upZ * ndirX - upX * ndirZ;
14299         leftZ = upX * ndirY - upY * ndirX;
14300         // normalize left
14301         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
14302         leftX *= invLeftLength;
14303         leftY *= invLeftLength;
14304         leftZ *= invLeftLength;
14305         // up = direction x left
14306         double upnX = ndirY * leftZ - ndirZ * leftY;
14307         double upnY = ndirZ * leftX - ndirX * leftZ;
14308         double upnZ = ndirX * leftY - ndirY * leftX;
14309         if ((properties & PROPERTY_IDENTITY) == 0)
14310             this._identity();
14311         setm00(leftX);
14312         setm01(leftY);
14313         setm02(leftZ);
14314         setm10(upnX);
14315         setm11(upnY);
14316         setm12(upnZ);
14317         setm20(ndirX);
14318         setm21(ndirY);
14319         setm22(ndirZ);
14320         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
14321         return this;
14322     }
14323 
14324     /**
14325      * Set this matrix to a model transformation for a right-handed coordinate system, 
14326      * that translates to the given <code>pos</code> and aligns the local <code>-z</code>
14327      * axis with <code>dir</code>.
14328      * <p>
14329      * This method is equivalent to calling: <code>translation(pos).rotateTowards(dir, up)</code>
14330      * 
14331      * @see #translation(ref Vector3d)
14332      * @see #rotateTowards(ref Vector3d, Vector3d)
14333      *
14334      * @param pos
14335      *              the position to translate to
14336      * @param dir
14337      *              the direction to rotate towards
14338      * @param up
14339      *              the up vector
14340      * @return this
14341      */
14342     ref public Matrix4d translationRotateTowards(ref Vector3d pos, Vector3d dir, Vector3d up) return {
14343         return translationRotateTowards(pos.x, pos.y, pos.z, dir.x, dir.y, dir.z, up.x, up.y, up.z);
14344     }
14345 
14346     /**
14347      * Set this matrix to a model transformation for a right-handed coordinate system, 
14348      * that translates to the given <code>(posX, posY, posZ)</code> and aligns the local <code>-z</code>
14349      * axis with <code>(dirX, dirY, dirZ)</code>.
14350      * <p>
14351      * This method is equivalent to calling: <code>translation(posX, posY, posZ).rotateTowards(dirX, dirY, dirZ, upX, upY, upZ)</code>
14352      * 
14353      * @see #translation(double, double, double)
14354      * @see #rotateTowards(double, double, double, double, double, double)
14355      * 
14356      * @param posX
14357      *              the x-coordinate of the position to translate to
14358      * @param posY
14359      *              the y-coordinate of the position to translate to
14360      * @param posZ
14361      *              the z-coordinate of the position to translate to
14362      * @param dirX
14363      *              the x-coordinate of the direction to rotate towards
14364      * @param dirY
14365      *              the y-coordinate of the direction to rotate towards
14366      * @param dirZ
14367      *              the z-coordinate of the direction to rotate towards
14368      * @param upX
14369      *              the x-coordinate of the up vector
14370      * @param upY
14371      *              the y-coordinate of the up vector
14372      * @param upZ
14373      *              the z-coordinate of the up vector
14374      * @return this
14375      */
14376     ref public Matrix4d translationRotateTowards(double posX, double posY, double posZ, double dirX, double dirY, double dirZ, double upX, double upY, double upZ) return {
14377         // Normalize direction
14378         double invDirLength = Math.invsqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
14379         double ndirX = dirX * invDirLength;
14380         double ndirY = dirY * invDirLength;
14381         double ndirZ = dirZ * invDirLength;
14382         // left = up x direction
14383         double leftX, leftY, leftZ;
14384         leftX = upY * ndirZ - upZ * ndirY;
14385         leftY = upZ * ndirX - upX * ndirZ;
14386         leftZ = upX * ndirY - upY * ndirX;
14387         // normalize left
14388         double invLeftLength = Math.invsqrt(leftX * leftX + leftY * leftY + leftZ * leftZ);
14389         leftX *= invLeftLength;
14390         leftY *= invLeftLength;
14391         leftZ *= invLeftLength;
14392         // up = direction x left
14393         double upnX = ndirY * leftZ - ndirZ * leftY;
14394         double upnY = ndirZ * leftX - ndirX * leftZ;
14395         double upnZ = ndirX * leftY - ndirY * leftX;
14396         setm00(leftX);
14397         setm01(leftY);
14398         setm02(leftZ);
14399         setm03(0.0);
14400         setm10(upnX);
14401         setm11(upnY);
14402         setm12(upnZ);
14403         setm13(0.0);
14404         setm20(ndirX);
14405         setm21(ndirY);
14406         setm22(ndirZ);
14407         setm23(0.0);
14408         setm30(posX);
14409         setm31(posY);
14410         setm32(posZ);
14411         setm33(1.0);
14412         properties = PROPERTY_AFFINE | PROPERTY_ORTHONORMAL;
14413         return this;
14414     }
14415 
14416     public Vector3d getEulerAnglesZYX(ref Vector3d dest) {
14417         dest.x = Math.atan2(m12, m22);
14418         dest.y = Math.atan2(-m02, Math.sqrt(1.0 - m02 * m02));
14419         dest.z = Math.atan2(m01, m00);
14420         return dest;
14421     }
14422 
14423     public Vector3d getEulerAnglesXYZ(ref Vector3d dest) {
14424         dest.x = Math.atan2(-m21, m22);
14425         dest.y = Math.atan2(m20, Math.sqrt(1.0 - m20 * m20));
14426         dest.z = Math.atan2(-m10, m00);
14427         return dest;
14428     }
14429 
14430     /**
14431      * Compute the extents of the coordinate system before this {@link #isAffine() affine} transformation was applied
14432      * and store the resulting corner coordinates in <code>corner</code> and the span vectors in
14433      * <code>xDir</code>, <code>yDir</code> and <code>zDir</code>.
14434      * <p>
14435      * That means, given the maximum extents of the coordinate system between <code>[-1..+1]</code> in all dimensions,
14436      * this method returns one corner and the length and direction of the three base axis vectors in the coordinate
14437      * system before this transformation is applied, which transforms into the corner coordinates <code>[-1, +1]</code>.
14438      * <p>
14439      * This method is equivalent to computing at least three adjacent corners using {@link #frustumCorner(int, Vector3d)}
14440      * and subtracting them to obtain the length and direction of the span vectors.
14441      * 
14442      * @param corner
14443      *          will hold one corner of the span (usually the corner {@link Matrix4d#CORNER_NXNYNZ})
14444      * @param xDir
14445      *          will hold the direction and length of the span along the positive X axis
14446      * @param yDir
14447      *          will hold the direction and length of the span along the positive Y axis
14448      * @param zDir
14449      *          will hold the direction and length of the span along the positive z axis
14450      * @return this
14451      */
14452     ref public Matrix4d affineSpan(ref Vector3d corner, ref Vector3d xDir, ref Vector3d yDir, ref Vector3d zDir) return {
14453         double a = m10 * m22, b = m10 * m21, c = m10 * m02, d = m10 * m01;
14454         double e = m11 * m22, f = m11 * m20, g = m11 * m02, h = m11 * m00;
14455         double i = m12 * m21, j = m12 * m20, k = m12 * m01, l = m12 * m00;
14456         double m = m20 * m02, n = m20 * m01, o = m21 * m02, p = m21 * m00;
14457         double q = m22 * m01, r = m22 * m00;
14458         double s = 1.0 / (m00 * m11 - m01 * m10) * m22 + (m02 * m10 - m00 * m12) * m21 + (m01 * m12 - m02 * m11) * m20;
14459         double nm00 = (e - i) * s, nm01 = (o - q) * s, nm02 = (k - g) * s;
14460         double nm10 = (j - a) * s, nm11 = (r - m) * s, nm12 = (c - l) * s;
14461         double nm20 = (b - f) * s, nm21 = (n - p) * s, nm22 = (h - d) * s;
14462         corner.x = -nm00 - nm10 - nm20 + (a * m31 - b * m32 + f * m32 - e * m30 + i * m30 - j * m31) * s;
14463         corner.y = -nm01 - nm11 - nm21 + (m * m31 - n * m32 + p * m32 - o * m30 + q * m30 - r * m31) * s;
14464         corner.z = -nm02 - nm12 - nm22 + (g * m30 - k * m30 + l * m31 - c * m31 + d * m32 - h * m32) * s;
14465         xDir.x = 2.0 * nm00; xDir.y = 2.0 * nm01; xDir.z = 2.0 * nm02;
14466         yDir.x = 2.0 * nm10; yDir.y = 2.0 * nm11; yDir.z = 2.0 * nm12;
14467         zDir.x = 2.0 * nm20; zDir.y = 2.0 * nm21; zDir.z = 2.0 * nm22;
14468         return this;
14469     }
14470 
14471     public bool testPoint(double x, double y, double z) {
14472         double nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30;
14473         double pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30;
14474         double nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31;
14475         double pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31;
14476         double nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32;
14477         double pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32;
14478         return nxX * x + nxY * y + nxZ * z + nxW >= 0 && pxX * x + pxY * y + pxZ * z + pxW >= 0 &&
14479                nyX * x + nyY * y + nyZ * z + nyW >= 0 && pyX * x + pyY * y + pyZ * z + pyW >= 0 &&
14480                nzX * x + nzY * y + nzZ * z + nzW >= 0 && pzX * x + pzY * y + pzZ * z + pzW >= 0;
14481     }
14482 
14483     public bool testSphere(double x, double y, double z, double r) {
14484         double invl;
14485         double nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30;
14486         invl = Math.invsqrt(nxX * nxX + nxY * nxY + nxZ * nxZ);
14487         nxX *= invl; nxY *= invl; nxZ *= invl; nxW *= invl;
14488         double pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30;
14489         invl = Math.invsqrt(pxX * pxX + pxY * pxY + pxZ * pxZ);
14490         pxX *= invl; pxY *= invl; pxZ *= invl; pxW *= invl;
14491         double nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31;
14492         invl = Math.invsqrt(nyX * nyX + nyY * nyY + nyZ * nyZ);
14493         nyX *= invl; nyY *= invl; nyZ *= invl; nyW *= invl;
14494         double pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31;
14495         invl = Math.invsqrt(pyX * pyX + pyY * pyY + pyZ * pyZ);
14496         pyX *= invl; pyY *= invl; pyZ *= invl; pyW *= invl;
14497         double nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32;
14498         invl = Math.invsqrt(nzX * nzX + nzY * nzY + nzZ * nzZ);
14499         nzX *= invl; nzY *= invl; nzZ *= invl; nzW *= invl;
14500         double pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32;
14501         invl = Math.invsqrt(pzX * pzX + pzY * pzY + pzZ * pzZ);
14502         pzX *= invl; pzY *= invl; pzZ *= invl; pzW *= invl;
14503         return nxX * x + nxY * y + nxZ * z + nxW >= -r && pxX * x + pxY * y + pxZ * z + pxW >= -r &&
14504                nyX * x + nyY * y + nyZ * z + nyW >= -r && pyX * x + pyY * y + pyZ * z + pyW >= -r &&
14505                nzX * x + nzY * y + nzZ * z + nzW >= -r && pzX * x + pzY * y + pzZ * z + pzW >= -r;
14506     }
14507 
14508     public bool testAab(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
14509         double nxX = m03 + m00, nxY = m13 + m10, nxZ = m23 + m20, nxW = m33 + m30;
14510         double pxX = m03 - m00, pxY = m13 - m10, pxZ = m23 - m20, pxW = m33 - m30;
14511         double nyX = m03 + m01, nyY = m13 + m11, nyZ = m23 + m21, nyW = m33 + m31;
14512         double pyX = m03 - m01, pyY = m13 - m11, pyZ = m23 - m21, pyW = m33 - m31;
14513         double nzX = m03 + m02, nzY = m13 + m12, nzZ = m23 + m22, nzW = m33 + m32;
14514         double pzX = m03 - m02, pzY = m13 - m12, pzZ = m23 - m22, pzW = m33 - m32;
14515         /*
14516          * This is an implementation of the "2.4 Basic intersection test" of the mentioned site.
14517          * It does not distinguish between partially inside and fully inside, though, so the test with the 'p' vertex is omitted.
14518          */
14519         return nxX * (nxX < 0 ? minX : maxX) + nxY * (nxY < 0 ? minY : maxY) + nxZ * (nxZ < 0 ? minZ : maxZ) >= -nxW &&
14520                pxX * (pxX < 0 ? minX : maxX) + pxY * (pxY < 0 ? minY : maxY) + pxZ * (pxZ < 0 ? minZ : maxZ) >= -pxW &&
14521                nyX * (nyX < 0 ? minX : maxX) + nyY * (nyY < 0 ? minY : maxY) + nyZ * (nyZ < 0 ? minZ : maxZ) >= -nyW &&
14522                pyX * (pyX < 0 ? minX : maxX) + pyY * (pyY < 0 ? minY : maxY) + pyZ * (pyZ < 0 ? minZ : maxZ) >= -pyW &&
14523                nzX * (nzX < 0 ? minX : maxX) + nzY * (nzY < 0 ? minY : maxY) + nzZ * (nzZ < 0 ? minZ : maxZ) >= -nzW &&
14524                pzX * (pzX < 0 ? minX : maxX) + pzY * (pzY < 0 ? minY : maxY) + pzZ * (pzZ < 0 ? minZ : maxZ) >= -pzW;
14525     }
14526 
14527     /**
14528      * Apply an oblique projection transformation to this matrix with the given values for <code>a</code> and
14529      * <code>b</code>.
14530      * <p>
14531      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the oblique transformation matrix,
14532      * then the new matrix will be <code>M * O</code>. So when transforming a
14533      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
14534      * oblique transformation will be applied first!
14535      * <p>
14536      * The oblique transformation is defined as:
14537      * <pre>
14538      * x' = x + a*z
14539      * y' = y + a*z
14540      * z' = z
14541      * </pre>
14542      * or in matrix form:
14543      * <pre>
14544      * 1 0 a 0
14545      * 0 1 b 0
14546      * 0 0 1 0
14547      * 0 0 0 1
14548      * </pre>
14549      * 
14550      * @param a
14551      *            the value for the z factor that applies to x
14552      * @param b
14553      *            the value for the z factor that applies to y
14554      * @return this
14555      */
14556     ref public Matrix4d obliqueZ(double a, double b) return {
14557         setm20(m00 * a + m10 * b + m20);
14558         setm21(m01 * a + m11 * b + m21);
14559         setm22(m02 * a + m12 * b + m22);
14560         this.properties &= PROPERTY_AFFINE;
14561         return this;
14562     }
14563 
14564     /**
14565      * Apply an oblique projection transformation to this matrix with the given values for <code>a</code> and
14566      * <code>b</code> and store the result in <code>dest</code>.
14567      * <p>
14568      * If <code>M</code> is <code>this</code> matrix and <code>O</code> the oblique transformation matrix,
14569      * then the new matrix will be <code>M * O</code>. So when transforming a
14570      * vector <code>v</code> with the new matrix by using <code>M * O * v</code>, the
14571      * oblique transformation will be applied first!
14572      * <p>
14573      * The oblique transformation is defined as:
14574      * <pre>
14575      * x' = x + a*z
14576      * y' = y + a*z
14577      * z' = z
14578      * </pre>
14579      * or in matrix form:
14580      * <pre>
14581      * 1 0 a 0
14582      * 0 1 b 0
14583      * 0 0 1 0
14584      * 0 0 0 1
14585      * </pre>
14586      * 
14587      * @param a
14588      *            the value for the z factor that applies to x
14589      * @param b
14590      *            the value for the z factor that applies to y
14591      * @param dest
14592      *            will hold the result
14593      * @return dest
14594      */
14595     public Matrix4d obliqueZ(double a, double b, ref Matrix4d dest) {
14596         dest._m00(m00)
14597         ._m01(m01)
14598         ._m02(m02)
14599         ._m03(m03)
14600         ._m10(m10)
14601         ._m11(m11)
14602         ._m12(m12)
14603         ._m13(m13)
14604         ._m20(m00 * a + m10 * b + m20)
14605         ._m21(m01 * a + m11 * b + m21)
14606         ._m22(m02 * a + m12 * b + m22)
14607         ._m23(m23)
14608         ._m30(m30)
14609         ._m31(m31)
14610         ._m32(m32)
14611         ._m33(m33)
14612         ._properties(properties & PROPERTY_AFFINE);
14613         return dest;
14614     }
14615 
14616     /**
14617      * 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
14618      * and the extents of the near plane rectangle along its local <code>x</code> and <code>y</code> axes, and store the resulting matrices
14619      * in <code>projDest</code> and <code>viewDest</code>.
14620      * <p>
14621      * This method creates a view and perspective projection matrix assuming that there is a pinhole camera at position <code>eye</code>
14622      * projecting the scene onto the near plane defined by the rectangle.
14623      * <p>
14624      * All positions and lengths are in the same (world) unit.
14625      * 
14626      * @param eye
14627      *          the position of the camera
14628      * @param p
14629      *          the bottom left corner of the near plane rectangle (will map to the bottom left corner in window coordinates)
14630      * @param x
14631      *          the direction and length of the local "bottom/top" X axis/side of the near plane rectangle
14632      * @param y
14633      *          the direction and length of the local "left/right" Y axis/side of the near plane rectangle
14634      * @param nearFarDist
14635      *          the distance between the far and near plane (the near plane will be calculated by this method).
14636      *          If the special value {@link Double#POSITIVE_INFINITY} is used, the far clipping plane will be at positive infinity.
14637      *          If the special value {@link Double#NEGATIVE_INFINITY} is used, the near and far planes will be swapped and 
14638      *          the near clipping plane will be at positive infinity.
14639      *          If a negative value is used (except for {@link Double#NEGATIVE_INFINITY}) the near and far planes will be swapped
14640      * @param zeroToOne
14641      *          whether to use Vulkan's and Direct3D's NDC z range of <code>[0..+1]</code> when <code>true</code>
14642      *          or whether to use OpenGL's NDC z range of <code>[-1..+1]</code> when <code>false</code>
14643      * @param projDest
14644      *          will hold the resulting projection matrix
14645      * @param viewDest
14646      *          will hold the resulting view matrix
14647      */
14648     public static void projViewFromRectangle(
14649             Vector3d eye, Vector3d p, Vector3d x, Vector3d y, double nearFarDist, bool zeroToOne,
14650             ref Matrix4d projDest, ref Matrix4d viewDest) {
14651         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;
14652         double zd = zx * (p.x - eye.x) + zy * (p.y - eye.y) + zz * (p.z - eye.z);
14653         double zs = zd >= 0 ? 1 : -1; zx *= zs; zy *= zs; zz *= zs; zd *= zs; 
14654         viewDest.setLookAt(eye.x, eye.y, eye.z, eye.x + zx, eye.y + zy, eye.z + zz, y.x, y.y, y.z);
14655         double px = viewDest.m00 * p.x + viewDest.m10 * p.y + viewDest.m20 * p.z + viewDest.m30;
14656         double py = viewDest.m01 * p.x + viewDest.m11 * p.y + viewDest.m21 * p.z + viewDest.m31;
14657         double tx = viewDest.m00 * x.x + viewDest.m10 * x.y + viewDest.m20 * x.z;
14658         double ty = viewDest.m01 * y.x + viewDest.m11 * y.y + viewDest.m21 * y.z;
14659         double len = Math.sqrt(zx * zx + zy * zy + zz * zz);
14660         double near = zd / len, far;
14661         if (Math.isInfinite(nearFarDist) && nearFarDist < 0.0) {
14662             far = near;
14663             near = double.infinity;
14664         } else if (Math.isInfinite(nearFarDist) && nearFarDist > 0.0) {
14665             far = double.infinity;
14666         } else if (nearFarDist < 0.0) {
14667             far = near;
14668             near = near + nearFarDist;
14669         } else {
14670             far = near + nearFarDist;
14671         }
14672         projDest.setFrustum(px, px + tx, py, py + ty, near, far, zeroToOne);
14673     }
14674 
14675     /**
14676      * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(ref Vector3d)})
14677      * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(ref Vector3d)}) and the
14678      * given vector <code>up</code>.
14679      * <p>
14680      * This effectively ensures that the resulting matrix will be equal to the one obtained from 
14681      * {@link #setLookAt(ref Vector3d, Vector3d, Vector3d)} called with the current 
14682      * local origin of this matrix (as obtained by {@link #originAffine(ref Vector3d)}), the sum of this position and the 
14683      * negated local Z axis as well as the given vector <code>up</code>.
14684      * <p>
14685      * This method must only be called on {@link #isAffine()} matrices.
14686      * 
14687      * @param up
14688      *            the up vector
14689      * @return this
14690      */
14691     ref public Matrix4d withLookAtUp(ref Vector3d up) return {
14692         withLookAtUp(up.x, up.y, up.z, this);
14693         return this;
14694     }
14695 
14696     public Matrix4d withLookAtUp(ref Vector3d up, ref Matrix4d dest) {
14697         return dest.withLookAtUp(up.x, up.y, up.z);
14698     }
14699 
14700     /**
14701      * Apply a transformation to this matrix to ensure that the local Y axis (as obtained by {@link #positiveY(ref Vector3d)})
14702      * will be coplanar to the plane spanned by the local Z axis (as obtained by {@link #positiveZ(ref Vector3d)}) and the
14703      * given vector <code>(upX, upY, upZ)</code>.
14704      * <p>
14705      * This effectively ensures that the resulting matrix will be equal to the one obtained from 
14706      * {@link #setLookAt(double, double, double, double, double, double, double, double, double)} called with the current 
14707      * local origin of this matrix (as obtained by {@link #originAffine(ref Vector3d)}), the sum of this position and the 
14708      * negated local Z axis as well as the given vector <code>(upX, upY, upZ)</code>.
14709      * <p>
14710      * This method must only be called on {@link #isAffine()} matrices.
14711      * 
14712      * @param upX
14713      *            the x coordinate of the up vector
14714      * @param upY
14715      *            the y coordinate of the up vector
14716      * @param upZ
14717      *            the z coordinate of the up vector
14718      * @return this
14719      */
14720     ref public Matrix4d withLookAtUp(double upX, double upY, double upZ) return {
14721         withLookAtUp(upX, upY, upZ, this);
14722         return this;
14723     }
14724 
14725     public Matrix4d withLookAtUp(double upX, double upY, double upZ, ref Matrix4d dest) {
14726         double y = (upY * m21 - upZ * m11) * m02 +
14727                    (upZ * m01 - upX * m21) * m12 +
14728                    (upX * m11 - upY * m01) * m22;
14729         double x = upX * m01 + upY * m11 + upZ * m21;
14730         if ((properties & PROPERTY_ORTHONORMAL) == 0)
14731             x *= Math.sqrt(m01 * m01 + m11 * m11 + m21 * m21);
14732         double invsqrt = Math.invsqrt(y * y + x * x);
14733         double c = x * invsqrt, s = y * invsqrt;
14734         double nm00 = c * m00 - s * m01, nm10 = c * m10 - s * m11, nm20 = c * m20 - s * m21, nm31 = s * m30 + c * m31;
14735         double nm01 = s * m00 + c * m01, nm11 = s * m10 + c * m11, nm21 = s * m20 + c * m21, nm30 = c * m30 - s * m31;
14736         dest._m00(nm00)._m10(nm10)._m20(nm20)._m30(nm30)
14737         ._m01(nm01)._m11(nm11)._m21(nm21)._m31(nm31);
14738         if (dest != this) {
14739             dest
14740             ._m02(m02)._m12(m12)._m22(m22)._m32(m32)
14741             ._m03(m03)._m13(m13)._m23(m23)._m33(m33);
14742         }
14743         dest._properties(properties & ~(PROPERTY_PERSPECTIVE | PROPERTY_IDENTITY | PROPERTY_TRANSLATION));
14744         return dest;
14745     }
14746 
14747     /**
14748      * Multiply <code>this</code> by the matrix
14749      * <pre>
14750      * 1 0 0 0
14751      * 0 0 1 0
14752      * 0 1 0 0
14753      * 0 0 0 1
14754      * </pre>
14755      * 
14756      * @return this
14757      */
14758     ref public Matrix4d mapXZY() return {
14759         mapXZY(this);
14760         return this;
14761     }
14762     public Matrix4d mapXZY(ref Matrix4d dest) {
14763         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14764         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));
14765     }
14766     /**
14767      * Multiply <code>this</code> by the matrix
14768      * <pre>
14769      * 1 0  0 0
14770      * 0 0 -1 0
14771      * 0 1  0 0
14772      * 0 0  0 1
14773      * </pre>
14774      * 
14775      * @return this
14776      */
14777     ref public Matrix4d mapXZnY() return {
14778         mapXZnY(this);
14779         return this;
14780     }
14781     public Matrix4d mapXZnY(ref Matrix4d dest) {
14782         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14783         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));
14784     }
14785     /**
14786      * Multiply <code>this</code> by the matrix
14787      * <pre>
14788      * 1  0  0 0
14789      * 0 -1  0 0
14790      * 0  0 -1 0
14791      * 0  0  0 1
14792      * </pre>
14793      * 
14794      * @return this
14795      */
14796     ref public Matrix4d mapXnYnZ() return {
14797         mapXnYnZ(this);
14798         return this;
14799     }
14800     public Matrix4d mapXnYnZ(ref Matrix4d dest) {
14801         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));
14802     }
14803     /**
14804      * Multiply <code>this</code> by the matrix
14805      * <pre>
14806      * 1  0 0 0
14807      * 0  0 1 0
14808      * 0 -1 0 0
14809      * 0  0 0 1
14810      * </pre>
14811      * 
14812      * @return this
14813      */
14814     ref public Matrix4d mapXnZY() return {
14815         mapXnZY(this);
14816         return this;
14817     }
14818     public Matrix4d mapXnZY(ref Matrix4d dest) {
14819         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14820         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));
14821     }
14822     /**
14823      * Multiply <code>this</code> by the matrix
14824      * <pre>
14825      * 1  0  0 0
14826      * 0  0 -1 0
14827      * 0 -1  0 0
14828      * 0  0  0 1
14829      * </pre>
14830      * 
14831      * @return this
14832      */
14833     ref public Matrix4d mapXnZnY() return {
14834         mapXnZnY(this);
14835         return this;
14836     }
14837     public Matrix4d mapXnZnY(ref Matrix4d dest) {
14838         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
14839         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));
14840     }
14841     /**
14842      * Multiply <code>this</code> by the matrix
14843      * <pre>
14844      * 0 1 0 0
14845      * 1 0 0 0
14846      * 0 0 1 0
14847      * 0 0 0 1
14848      * </pre>
14849      * 
14850      * @return this
14851      */
14852     ref public Matrix4d mapYXZ() return {
14853         mapYXZ(this);
14854         return this;
14855     }
14856     public Matrix4d mapYXZ(ref Matrix4d dest) {
14857         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14858         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));
14859     }
14860     /**
14861      * Multiply <code>this</code> by the matrix
14862      * <pre>
14863      * 0 1  0 0
14864      * 1 0  0 0
14865      * 0 0 -1 0
14866      * 0 0  0 1
14867      * </pre>
14868      * 
14869      * @return this
14870      */
14871     ref public Matrix4d mapYXnZ() return {
14872         mapYXnZ(this);
14873         return this;
14874     }
14875     public Matrix4d mapYXnZ(ref Matrix4d dest) {
14876         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14877         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));
14878     }
14879     /**
14880      * Multiply <code>this</code> by the matrix
14881      * <pre>
14882      * 0 0 1 0
14883      * 1 0 0 0
14884      * 0 1 0 0
14885      * 0 0 0 1
14886      * </pre>
14887      * 
14888      * @return this
14889      */
14890     ref public Matrix4d mapYZX() return {
14891         mapYZX(this);
14892         return this;
14893     }
14894     public Matrix4d mapYZX(ref Matrix4d dest) {
14895         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14896         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));
14897     }
14898     /**
14899      * Multiply <code>this</code> by the matrix
14900      * <pre>
14901      * 0 0 -1 0
14902      * 1 0  0 0
14903      * 0 1  0 0
14904      * 0 0  0 1
14905      * </pre>
14906      * 
14907      * @return this
14908      */
14909     ref public Matrix4d mapYZnX() return {
14910         mapYZnX(this);
14911         return this;
14912     }
14913     public Matrix4d mapYZnX(ref Matrix4d dest) {
14914         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14915         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));
14916     }
14917     /**
14918      * Multiply <code>this</code> by the matrix
14919      * <pre>
14920      * 0 -1 0 0
14921      * 1  0 0 0
14922      * 0  0 1 0
14923      * 0  0 0 1
14924      * </pre>
14925      * 
14926      * @return this
14927      */
14928     ref public Matrix4d mapYnXZ() return {
14929         mapYnXZ(this);
14930         return this;
14931     }
14932     public Matrix4d mapYnXZ(ref Matrix4d dest) {
14933         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14934         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));
14935     }
14936     /**
14937      * Multiply <code>this</code> by the matrix
14938      * <pre>
14939      * 0 -1  0 0
14940      * 1  0  0 0
14941      * 0  0 -1 0
14942      * 0  0  0 1
14943      * </pre>
14944      * 
14945      * @return this
14946      */
14947     ref public Matrix4d mapYnXnZ() return {
14948         mapYnXnZ(this);
14949         return this;
14950     }
14951     public Matrix4d mapYnXnZ(ref Matrix4d dest) {
14952         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14953         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));
14954     }
14955     /**
14956      * Multiply <code>this</code> by the matrix
14957      * <pre>
14958      * 0  0 1 0
14959      * 1  0 0 0
14960      * 0 -1 0 0
14961      * 0  0 0 1
14962      * </pre>
14963      * 
14964      * @return this
14965      */
14966     ref public Matrix4d mapYnZX() return {
14967         mapYnZX(this);
14968         return this;
14969     }
14970     public Matrix4d mapYnZX(ref Matrix4d dest) {
14971         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14972         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));
14973     }
14974     /**
14975      * Multiply <code>this</code> by the matrix
14976      * <pre>
14977      * 0  0 -1 0
14978      * 1  0  0 0
14979      * 0 -1  0 0
14980      * 0  0  0 1
14981      * </pre>
14982      * 
14983      * @return this
14984      */
14985     ref public Matrix4d mapYnZnX() return {
14986         mapYnZnX(this);
14987         return this;
14988     }
14989     public Matrix4d mapYnZnX(ref Matrix4d dest) {
14990         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
14991         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));
14992     }
14993     /**
14994      * Multiply <code>this</code> by the matrix
14995      * <pre>
14996      * 0 1 0 0
14997      * 0 0 1 0
14998      * 1 0 0 0
14999      * 0 0 0 1
15000      * </pre>
15001      * 
15002      * @return this
15003      */
15004     ref public Matrix4d mapZXY() return {
15005         mapZXY(this);
15006         return this;
15007     }
15008     public Matrix4d mapZXY(ref Matrix4d dest) {
15009         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15010         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15011         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));
15012     }
15013     /**
15014      * Multiply <code>this</code> by the matrix
15015      * <pre>
15016      * 0 1  0 0
15017      * 0 0 -1 0
15018      * 1 0  0 0
15019      * 0 0  0 1
15020      * </pre>
15021      * 
15022      * @return this
15023      */
15024     ref public Matrix4d mapZXnY() return {
15025         mapZXnY(this);
15026         return this;
15027     }
15028     public Matrix4d mapZXnY(ref Matrix4d dest) {
15029         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15030         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15031         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));
15032     }
15033     /**
15034      * Multiply <code>this</code> by the matrix
15035      * <pre>
15036      * 0 0 1 0
15037      * 0 1 0 0
15038      * 1 0 0 0
15039      * 0 0 0 1
15040      * </pre>
15041      * 
15042      * @return this
15043      */
15044     ref public Matrix4d mapZYX() return {
15045         mapZYX(this);
15046         return this;
15047     }
15048     public Matrix4d mapZYX(ref Matrix4d dest) {
15049         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15050         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));
15051     }
15052     /**
15053      * Multiply <code>this</code> by the matrix
15054      * <pre>
15055      * 0 0 -1 0
15056      * 0 1  0 0
15057      * 1 0  0 0
15058      * 0 0  0 1
15059      * </pre>
15060      * 
15061      * @return this
15062      */
15063     ref public Matrix4d mapZYnX() return {
15064         mapZYnX(this);
15065         return this;
15066     }
15067     public Matrix4d mapZYnX(ref Matrix4d dest) {
15068         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15069         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));
15070     }
15071     /**
15072      * Multiply <code>this</code> by the matrix
15073      * <pre>
15074      * 0 -1 0 0
15075      * 0  0 1 0
15076      * 1  0 0 0
15077      * 0  0 0 1
15078      * </pre>
15079      * 
15080      * @return this
15081      */
15082     ref public Matrix4d mapZnXY() return {
15083         mapZnXY(this);
15084         return this;
15085     }
15086     public Matrix4d mapZnXY(ref Matrix4d dest) {
15087         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15088         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15089         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));
15090     }
15091     /**
15092      * Multiply <code>this</code> by the matrix
15093      * <pre>
15094      * 0 -1  0 0
15095      * 0  0 -1 0
15096      * 1  0  0 0
15097      * 0  0  0 1
15098      * </pre>
15099      * 
15100      * @return this
15101      */
15102     ref public Matrix4d mapZnXnY() return {
15103         mapZnXnY(this);
15104         return this;
15105     }
15106     public Matrix4d mapZnXnY(ref Matrix4d dest) {
15107         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15108         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15109         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));
15110     }
15111     /**
15112      * Multiply <code>this</code> by the matrix
15113      * <pre>
15114      * 0  0 1 0
15115      * 0 -1 0 0
15116      * 1  0 0 0
15117      * 0  0 0 1
15118      * </pre>
15119      * 
15120      * @return this
15121      */
15122     ref public Matrix4d mapZnYX() return {
15123         mapZnYX(this);
15124         return this;
15125     }
15126     public Matrix4d mapZnYX(ref Matrix4d dest) {
15127         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15128         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));
15129     }
15130     /**
15131      * Multiply <code>this</code> by the matrix
15132      * <pre>
15133      * 0  0 -1 0
15134      * 0 -1  0 0
15135      * 1  0  0 0
15136      * 0  0  0 1
15137      * </pre>
15138      * 
15139      * @return this
15140      */
15141     ref public Matrix4d mapZnYnX() return {
15142         mapZnYnX(this);
15143         return this;
15144     }
15145     public Matrix4d mapZnYnX(ref Matrix4d dest) {
15146         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15147         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));
15148     }
15149     /**
15150      * Multiply <code>this</code> by the matrix
15151      * <pre>
15152      * -1 0  0 0
15153      *  0 1  0 0
15154      *  0 0 -1 0
15155      *  0 0  0 1
15156      * </pre>
15157      * 
15158      * @return this
15159      */
15160     ref public Matrix4d mapnXYnZ() return {
15161         mapnXYnZ(this);
15162         return this;
15163     }
15164     public Matrix4d mapnXYnZ(ref Matrix4d dest) {
15165         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));
15166     }
15167     /**
15168      * Multiply <code>this</code> by the matrix
15169      * <pre>
15170      * -1 0 0 0
15171      *  0 0 1 0
15172      *  0 1 0 0
15173      *  0 0 0 1
15174      * </pre>
15175      * 
15176      * @return this
15177      */
15178     ref public Matrix4d mapnXZY() return {
15179         mapnXZY(this);
15180         return this;
15181     }
15182     public Matrix4d mapnXZY(ref Matrix4d dest) {
15183         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15184         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));
15185     }
15186     /**
15187      * Multiply <code>this</code> by the matrix
15188      * <pre>
15189      * -1 0  0 0
15190      *  0 0 -1 0
15191      *  0 1  0 0
15192      *  0 0  0 1
15193      * </pre>
15194      * 
15195      * @return this
15196      */
15197     ref public Matrix4d mapnXZnY() return {
15198         mapnXZnY(this);
15199         return this;
15200     }
15201     public Matrix4d mapnXZnY(ref Matrix4d dest) {
15202         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15203         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));
15204     }
15205     /**
15206      * Multiply <code>this</code> by the matrix
15207      * <pre>
15208      * -1  0 0 0
15209      *  0 -1 0 0
15210      *  0  0 1 0
15211      *  0  0 0 1
15212      * </pre>
15213      * 
15214      * @return this
15215      */
15216     ref public Matrix4d mapnXnYZ() return {
15217         mapnXnYZ(this);
15218         return this;
15219     }
15220     public Matrix4d mapnXnYZ(ref Matrix4d dest) {
15221         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));
15222     }
15223     /**
15224      * Multiply <code>this</code> by the matrix
15225      * <pre>
15226      * -1  0  0 0
15227      *  0 -1  0 0
15228      *  0  0 -1 0
15229      *  0  0  0 1
15230      * </pre>
15231      * 
15232      * @return this
15233      */
15234     ref public Matrix4d mapnXnYnZ() return {
15235         mapnXnYnZ(this);
15236         return this;
15237     }
15238     public Matrix4d mapnXnYnZ(ref Matrix4d dest) {
15239         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));
15240     }
15241     /**
15242      * Multiply <code>this</code> by the matrix
15243      * <pre>
15244      * -1  0 0 0
15245      *  0  0 1 0
15246      *  0 -1 0 0
15247      *  0  0 0 1
15248      * </pre>
15249      * 
15250      * @return this
15251      */
15252     ref public Matrix4d mapnXnZY() return {
15253         mapnXnZY(this);
15254         return this;
15255     }
15256     public Matrix4d mapnXnZY(ref Matrix4d dest) {
15257         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15258         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));
15259     }
15260     /**
15261      * Multiply <code>this</code> by the matrix
15262      * <pre>
15263      * -1  0  0 0
15264      *  0  0 -1 0
15265      *  0 -1  0 0
15266      *  0  0  0 1
15267      * </pre>
15268      * 
15269      * @return this
15270      */
15271     ref public Matrix4d mapnXnZnY() return {
15272         mapnXnZnY(this);
15273         return this;
15274     }
15275     public Matrix4d mapnXnZnY(ref Matrix4d dest) {
15276         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15277         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));
15278     }
15279     /**
15280      * Multiply <code>this</code> by the matrix
15281      * <pre>
15282      *  0 1 0 0
15283      * -1 0 0 0
15284      *  0 0 1 0
15285      *  0 0 0 1
15286      * </pre>
15287      * 
15288      * @return this
15289      */
15290     ref public Matrix4d mapnYXZ() return {
15291         mapnYXZ(this);
15292         return this;
15293     }
15294     public Matrix4d mapnYXZ(ref Matrix4d dest) {
15295         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15296         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));
15297     }
15298     /**
15299      * Multiply <code>this</code> by the matrix
15300      * <pre>
15301      *  0 1  0 0
15302      * -1 0  0 0
15303      *  0 0 -1 0
15304      *  0 0  0 1
15305      * </pre>
15306      * 
15307      * @return this
15308      */
15309     ref public Matrix4d mapnYXnZ() return {
15310         mapnYXnZ(this);
15311         return this;
15312     }
15313     public Matrix4d mapnYXnZ(ref Matrix4d dest) {
15314         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15315         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));
15316     }
15317     /**
15318      * Multiply <code>this</code> by the matrix
15319      * <pre>
15320      *  0 0 1 0
15321      * -1 0 0 0
15322      *  0 1 0 0
15323      *  0 0 0 1
15324      * </pre>
15325      * 
15326      * @return this
15327      */
15328     ref public Matrix4d mapnYZX() return {
15329         mapnYZX(this);
15330         return this;
15331     }
15332     public Matrix4d mapnYZX(ref Matrix4d dest) {
15333         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15334         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));
15335     }
15336     /**
15337      * Multiply <code>this</code> by the matrix
15338      * <pre>
15339      *  0 0 -1 0
15340      * -1 0  0 0
15341      *  0 1  0 0
15342      *  0 0  0 1
15343      * </pre>
15344      * 
15345      * @return this
15346      */
15347     ref public Matrix4d mapnYZnX() return {
15348         mapnYZnX(this);
15349         return this;
15350     }
15351     public Matrix4d mapnYZnX(ref Matrix4d dest) {
15352         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15353         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));
15354     }
15355     /**
15356      * Multiply <code>this</code> by the matrix
15357      * <pre>
15358      *  0 -1 0 0
15359      * -1  0 0 0
15360      *  0  0 1 0
15361      *  0  0 0 1
15362      * </pre>
15363      * 
15364      * @return this
15365      */
15366     ref public Matrix4d mapnYnXZ() return {
15367         mapnYnXZ(this);
15368         return this;
15369     }
15370     public Matrix4d mapnYnXZ(ref Matrix4d dest) {
15371         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15372         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));
15373     }
15374     /**
15375      * Multiply <code>this</code> by the matrix
15376      * <pre>
15377      *  0 -1  0 0
15378      * -1  0  0 0
15379      *  0  0 -1 0
15380      *  0  0  0 1
15381      * </pre>
15382      * 
15383      * @return this
15384      */
15385     ref public Matrix4d mapnYnXnZ() return {
15386         mapnYnXnZ(this);
15387         return this;
15388     }
15389     public Matrix4d mapnYnXnZ(ref Matrix4d dest) {
15390         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15391         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));
15392     }
15393     /**
15394      * Multiply <code>this</code> by the matrix
15395      * <pre>
15396      *  0  0 1 0
15397      * -1  0 0 0
15398      *  0 -1 0 0
15399      *  0  0 0 1
15400      * </pre>
15401      * 
15402      * @return this
15403      */
15404     ref public Matrix4d mapnYnZX() return {
15405         mapnYnZX(this);
15406         return this;
15407     }
15408     public Matrix4d mapnYnZX(ref Matrix4d dest) {
15409         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15410         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));
15411     }
15412     /**
15413      * Multiply <code>this</code> by the matrix
15414      * <pre>
15415      *  0  0 -1 0
15416      * -1  0  0 0
15417      *  0 -1  0 0
15418      *  0  0  0 1
15419      * </pre>
15420      * 
15421      * @return this
15422      */
15423     ref public Matrix4d mapnYnZnX() return {
15424         mapnYnZnX(this);
15425         return this;
15426     }
15427     public Matrix4d mapnYnZnX(ref Matrix4d dest) {
15428         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15429         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));
15430     }
15431     /**
15432      * Multiply <code>this</code> by the matrix
15433      * <pre>
15434      *  0 1 0 0
15435      *  0 0 1 0
15436      * -1 0 0 0
15437      *  0 0 0 1
15438      * </pre>
15439      * 
15440      * @return this
15441      */
15442     ref public Matrix4d mapnZXY() return {
15443         mapnZXY(this);
15444         return this;
15445     }
15446     public Matrix4d mapnZXY(ref Matrix4d dest) {
15447         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15448         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15449         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));
15450     }
15451     /**
15452      * Multiply <code>this</code> by the matrix
15453      * <pre>
15454      *  0 1  0 0
15455      *  0 0 -1 0
15456      * -1 0  0 0
15457      *  0 0  0 1
15458      * </pre>
15459      * 
15460      * @return this
15461      */
15462     ref public Matrix4d mapnZXnY() return {
15463         mapnZXnY(this);
15464         return this;
15465     }
15466     public Matrix4d mapnZXnY(ref Matrix4d dest) {
15467         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15468         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15469         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));
15470     }
15471     /**
15472      * Multiply <code>this</code> by the matrix
15473      * <pre>
15474      *  0 0 1 0
15475      *  0 1 0 0
15476      * -1 0 0 0
15477      *  0 0 0 1
15478      * </pre>
15479      * 
15480      * @return this
15481      */
15482     ref public Matrix4d mapnZYX() return {
15483         mapnZYX(this);
15484         return this;
15485     }
15486     public Matrix4d mapnZYX(ref Matrix4d dest) {
15487         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15488         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));
15489     }
15490     /**
15491      * Multiply <code>this</code> by the matrix
15492      * <pre>
15493      *  0 0 -1 0
15494      *  0 1  0 0
15495      * -1 0  0 0
15496      *  0 0  0 1
15497      * </pre>
15498      * 
15499      * @return this
15500      */
15501     ref public Matrix4d mapnZYnX() return {
15502         mapnZYnX(this);
15503         return this;
15504     }
15505     public Matrix4d mapnZYnX(ref Matrix4d dest) {
15506         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15507         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));
15508     }
15509     /**
15510      * Multiply <code>this</code> by the matrix
15511      * <pre>
15512      *  0 -1 0 0
15513      *  0  0 1 0
15514      * -1  0 0 0
15515      *  0  0 0 1
15516      * </pre>
15517      * 
15518      * @return this
15519      */
15520     ref public Matrix4d mapnZnXY() return {
15521         mapnZnXY(this);
15522         return this;
15523     }
15524     public Matrix4d mapnZnXY(ref Matrix4d dest) {
15525         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15526         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15527         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));
15528     }
15529     /**
15530      * Multiply <code>this</code> by the matrix
15531      * <pre>
15532      *  0 -1  0 0
15533      *  0  0 -1 0
15534      * -1  0  0 0
15535      *  0  0  0 1
15536      * </pre>
15537      * 
15538      * @return this
15539      */
15540     ref public Matrix4d mapnZnXnY() return {
15541         mapnZnXnY(this);
15542         return this;
15543     }
15544     public Matrix4d mapnZnXnY(ref Matrix4d dest) {
15545         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15546         double m10 = this.m10, m11 = this.m11, m12 = this.m12;
15547         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));
15548     }
15549     /**
15550      * Multiply <code>this</code> by the matrix
15551      * <pre>
15552      *  0  0 1 0
15553      *  0 -1 0 0
15554      * -1  0 0 0
15555      *  0  0 0 1
15556      * </pre>
15557      * 
15558      * @return this
15559      */
15560     ref public Matrix4d mapnZnYX() return {
15561         mapnZnYX(this);
15562         return this;
15563     }
15564     public Matrix4d mapnZnYX(ref Matrix4d dest) {
15565         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15566         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));
15567     }
15568     /**
15569      * Multiply <code>this</code> by the matrix
15570      * <pre>
15571      *  0  0 -1 0
15572      *  0 -1  0 0
15573      * -1  0  0 0
15574      *  0  0  0 1
15575      * </pre>
15576      * 
15577      * @return this
15578      */
15579     ref public Matrix4d mapnZnYnX() return {
15580         mapnZnYnX(this);
15581         return this;
15582     }
15583     public Matrix4d mapnZnYnX(ref Matrix4d dest) {
15584         double m00 = this.m00, m01 = this.m01, m02 = this.m02;
15585         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));
15586     }
15587 
15588     /**
15589      * Multiply <code>this</code> by the matrix
15590      * <pre>
15591      * -1 0 0 0
15592      *  0 1 0 0
15593      *  0 0 1 0
15594      *  0 0 0 1
15595      * </pre>
15596      * 
15597      * @return this
15598      */
15599     ref public Matrix4d negateX() return {
15600         return _m00(-m00)._m01(-m01)._m02(-m02)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15601     }
15602     public Matrix4d negateX(ref Matrix4d dest) {
15603         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));
15604     }
15605 
15606     /**
15607      * Multiply <code>this</code> by the matrix
15608      * <pre>
15609      * 1  0 0 0
15610      * 0 -1 0 0
15611      * 0  0 1 0
15612      * 0  0 0 1
15613      * </pre>
15614      * 
15615      * @return this
15616      */
15617     ref public Matrix4d negateY() return {
15618         return _m10(-m10)._m11(-m11)._m12(-m12)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15619     }
15620     public Matrix4d negateY(ref Matrix4d dest) {
15621         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));
15622     }
15623 
15624     /**
15625      * Multiply <code>this</code> by the matrix
15626      * <pre>
15627      * 1 0  0 0
15628      * 0 1  0 0
15629      * 0 0 -1 0
15630      * 0 0  0 1
15631      * </pre>
15632      * 
15633      * @return this
15634      */
15635     ref public Matrix4d negateZ() return {
15636         return _m20(-m20)._m21(-m21)._m22(-m22)._properties(properties & (PROPERTY_AFFINE | PROPERTY_ORTHONORMAL));
15637     }
15638     public Matrix4d negateZ(ref Matrix4d dest) {
15639         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));
15640     }
15641 
15642     public bool isFinite() {
15643         return Math.isFinite(m00) && Math.isFinite(m01) && Math.isFinite(m02) && Math.isFinite(m03) &&
15644                Math.isFinite(m10) && Math.isFinite(m11) && Math.isFinite(m12) && Math.isFinite(m13) &&
15645                Math.isFinite(m20) && Math.isFinite(m21) && Math.isFinite(m22) && Math.isFinite(m23) &&
15646                Math.isFinite(m30) && Math.isFinite(m31) && Math.isFinite(m32) && Math.isFinite(m33);
15647     }
15648 
15649 }