1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.opengl;
18
19/**
20 * Matrix math utilities. These methods operate on OpenGL ES format
21 * matrices and vectors stored in float arrays.
22 * <p>
23 * Matrices are 4 x 4 column-vector matrices stored in column-major
24 * order:
25 * <pre>
26 *  m[offset +  0] m[offset +  4] m[offset +  8] m[offset + 12]
27 *  m[offset +  1] m[offset +  5] m[offset +  9] m[offset + 13]
28 *  m[offset +  2] m[offset +  6] m[offset + 10] m[offset + 14]
29 *  m[offset +  3] m[offset +  7] m[offset + 11] m[offset + 15]</pre>
30 *
31 * Vectors are 4 x 1 column vectors stored in order:
32 * <pre>
33 * v[offset + 0]
34 * v[offset + 1]
35 * v[offset + 2]
36 * v[offset + 3]</pre>
37 */
38public class Matrix {
39
40    /** Temporary memory for operations that need temporary matrix data. */
41    private final static float[] sTemp = new float[32];
42
43    /**
44     * @deprecated All methods are static, do not instantiate this class.
45     */
46    @Deprecated
47    public Matrix() {}
48
49    /**
50     * Multiplies two 4x4 matrices together and stores the result in a third 4x4
51     * matrix. In matrix notation: result = lhs x rhs. Due to the way
52     * matrix multiplication works, the result matrix will have the same
53     * effect as first multiplying by the rhs matrix, then multiplying by
54     * the lhs matrix. This is the opposite of what you might expect.
55     * <p>
56     * The same float array may be passed for result, lhs, and/or rhs. However,
57     * the result element values are undefined if the result elements overlap
58     * either the lhs or rhs elements.
59     *
60     * @param result The float array that holds the result.
61     * @param resultOffset The offset into the result array where the result is
62     *        stored.
63     * @param lhs The float array that holds the left-hand-side matrix.
64     * @param lhsOffset The offset into the lhs array where the lhs is stored
65     * @param rhs The float array that holds the right-hand-side matrix.
66     * @param rhsOffset The offset into the rhs array where the rhs is stored.
67     *
68     * @throws IllegalArgumentException if result, lhs, or rhs are null, or if
69     * resultOffset + 16 > result.length or lhsOffset + 16 > lhs.length or
70     * rhsOffset + 16 > rhs.length.
71     */
72    public static native void multiplyMM(float[] result, int resultOffset,
73            float[] lhs, int lhsOffset, float[] rhs, int rhsOffset);
74
75    /**
76     * Multiplies a 4 element vector by a 4x4 matrix and stores the result in a
77     * 4-element column vector. In matrix notation: result = lhs x rhs
78     * <p>
79     * The same float array may be passed for resultVec, lhsMat, and/or rhsVec.
80     * However, the resultVec element values are undefined if the resultVec
81     * elements overlap either the lhsMat or rhsVec elements.
82     *
83     * @param resultVec The float array that holds the result vector.
84     * @param resultVecOffset The offset into the result array where the result
85     *        vector is stored.
86     * @param lhsMat The float array that holds the left-hand-side matrix.
87     * @param lhsMatOffset The offset into the lhs array where the lhs is stored
88     * @param rhsVec The float array that holds the right-hand-side vector.
89     * @param rhsVecOffset The offset into the rhs vector where the rhs vector
90     *        is stored.
91     *
92     * @throws IllegalArgumentException if resultVec, lhsMat,
93     * or rhsVec are null, or if resultVecOffset + 4 > resultVec.length
94     * or lhsMatOffset + 16 > lhsMat.length or
95     * rhsVecOffset + 4 > rhsVec.length.
96     */
97    public static native void multiplyMV(float[] resultVec,
98            int resultVecOffset, float[] lhsMat, int lhsMatOffset,
99            float[] rhsVec, int rhsVecOffset);
100
101    /**
102     * Transposes a 4 x 4 matrix.
103     * <p>
104     * mTrans and m must not overlap.
105     *
106     * @param mTrans the array that holds the output transposed matrix
107     * @param mTransOffset an offset into mTrans where the transposed matrix is
108     *        stored.
109     * @param m the input array
110     * @param mOffset an offset into m where the input matrix is stored.
111     */
112    public static void transposeM(float[] mTrans, int mTransOffset, float[] m,
113            int mOffset) {
114        for (int i = 0; i < 4; i++) {
115            int mBase = i * 4 + mOffset;
116            mTrans[i + mTransOffset] = m[mBase];
117            mTrans[i + 4 + mTransOffset] = m[mBase + 1];
118            mTrans[i + 8 + mTransOffset] = m[mBase + 2];
119            mTrans[i + 12 + mTransOffset] = m[mBase + 3];
120        }
121    }
122
123    /**
124     * Inverts a 4 x 4 matrix.
125     * <p>
126     * mInv and m must not overlap.
127     *
128     * @param mInv the array that holds the output inverted matrix
129     * @param mInvOffset an offset into mInv where the inverted matrix is
130     *        stored.
131     * @param m the input array
132     * @param mOffset an offset into m where the input matrix is stored.
133     * @return true if the matrix could be inverted, false if it could not.
134     */
135    public static boolean invertM(float[] mInv, int mInvOffset, float[] m,
136            int mOffset) {
137        // Invert a 4 x 4 matrix using Cramer's Rule
138
139        // transpose matrix
140        final float src0  = m[mOffset +  0];
141        final float src4  = m[mOffset +  1];
142        final float src8  = m[mOffset +  2];
143        final float src12 = m[mOffset +  3];
144
145        final float src1  = m[mOffset +  4];
146        final float src5  = m[mOffset +  5];
147        final float src9  = m[mOffset +  6];
148        final float src13 = m[mOffset +  7];
149
150        final float src2  = m[mOffset +  8];
151        final float src6  = m[mOffset +  9];
152        final float src10 = m[mOffset + 10];
153        final float src14 = m[mOffset + 11];
154
155        final float src3  = m[mOffset + 12];
156        final float src7  = m[mOffset + 13];
157        final float src11 = m[mOffset + 14];
158        final float src15 = m[mOffset + 15];
159
160        // calculate pairs for first 8 elements (cofactors)
161        final float atmp0  = src10 * src15;
162        final float atmp1  = src11 * src14;
163        final float atmp2  = src9  * src15;
164        final float atmp3  = src11 * src13;
165        final float atmp4  = src9  * src14;
166        final float atmp5  = src10 * src13;
167        final float atmp6  = src8  * src15;
168        final float atmp7  = src11 * src12;
169        final float atmp8  = src8  * src14;
170        final float atmp9  = src10 * src12;
171        final float atmp10 = src8  * src13;
172        final float atmp11 = src9  * src12;
173
174        // calculate first 8 elements (cofactors)
175        final float dst0  = (atmp0 * src5 + atmp3 * src6 + atmp4  * src7)
176                          - (atmp1 * src5 + atmp2 * src6 + atmp5  * src7);
177        final float dst1  = (atmp1 * src4 + atmp6 * src6 + atmp9  * src7)
178                          - (atmp0 * src4 + atmp7 * src6 + atmp8  * src7);
179        final float dst2  = (atmp2 * src4 + atmp7 * src5 + atmp10 * src7)
180                          - (atmp3 * src4 + atmp6 * src5 + atmp11 * src7);
181        final float dst3  = (atmp5 * src4 + atmp8 * src5 + atmp11 * src6)
182                          - (atmp4 * src4 + atmp9 * src5 + atmp10 * src6);
183        final float dst4  = (atmp1 * src1 + atmp2 * src2 + atmp5  * src3)
184                          - (atmp0 * src1 + atmp3 * src2 + atmp4  * src3);
185        final float dst5  = (atmp0 * src0 + atmp7 * src2 + atmp8  * src3)
186                          - (atmp1 * src0 + atmp6 * src2 + atmp9  * src3);
187        final float dst6  = (atmp3 * src0 + atmp6 * src1 + atmp11 * src3)
188                          - (atmp2 * src0 + atmp7 * src1 + atmp10 * src3);
189        final float dst7  = (atmp4 * src0 + atmp9 * src1 + atmp10 * src2)
190                          - (atmp5 * src0 + atmp8 * src1 + atmp11 * src2);
191
192        // calculate pairs for second 8 elements (cofactors)
193        final float btmp0  = src2 * src7;
194        final float btmp1  = src3 * src6;
195        final float btmp2  = src1 * src7;
196        final float btmp3  = src3 * src5;
197        final float btmp4  = src1 * src6;
198        final float btmp5  = src2 * src5;
199        final float btmp6  = src0 * src7;
200        final float btmp7  = src3 * src4;
201        final float btmp8  = src0 * src6;
202        final float btmp9  = src2 * src4;
203        final float btmp10 = src0 * src5;
204        final float btmp11 = src1 * src4;
205
206        // calculate second 8 elements (cofactors)
207        final float dst8  = (btmp0  * src13 + btmp3  * src14 + btmp4  * src15)
208                          - (btmp1  * src13 + btmp2  * src14 + btmp5  * src15);
209        final float dst9  = (btmp1  * src12 + btmp6  * src14 + btmp9  * src15)
210                          - (btmp0  * src12 + btmp7  * src14 + btmp8  * src15);
211        final float dst10 = (btmp2  * src12 + btmp7  * src13 + btmp10 * src15)
212                          - (btmp3  * src12 + btmp6  * src13 + btmp11 * src15);
213        final float dst11 = (btmp5  * src12 + btmp8  * src13 + btmp11 * src14)
214                          - (btmp4  * src12 + btmp9  * src13 + btmp10 * src14);
215        final float dst12 = (btmp2  * src10 + btmp5  * src11 + btmp1  * src9 )
216                          - (btmp4  * src11 + btmp0  * src9  + btmp3  * src10);
217        final float dst13 = (btmp8  * src11 + btmp0  * src8  + btmp7  * src10)
218                          - (btmp6  * src10 + btmp9  * src11 + btmp1  * src8 );
219        final float dst14 = (btmp6  * src9  + btmp11 * src11 + btmp3  * src8 )
220                          - (btmp10 * src11 + btmp2  * src8  + btmp7  * src9 );
221        final float dst15 = (btmp10 * src10 + btmp4  * src8  + btmp9  * src9 )
222                          - (btmp8  * src9  + btmp11 * src10 + btmp5  * src8 );
223
224        // calculate determinant
225        final float det =
226                src0 * dst0 + src1 * dst1 + src2 * dst2 + src3 * dst3;
227
228        if (det == 0.0f) {
229            return false;
230        }
231
232        // calculate matrix inverse
233        final float invdet = 1.0f / det;
234        mInv[     mInvOffset] = dst0  * invdet;
235        mInv[ 1 + mInvOffset] = dst1  * invdet;
236        mInv[ 2 + mInvOffset] = dst2  * invdet;
237        mInv[ 3 + mInvOffset] = dst3  * invdet;
238
239        mInv[ 4 + mInvOffset] = dst4  * invdet;
240        mInv[ 5 + mInvOffset] = dst5  * invdet;
241        mInv[ 6 + mInvOffset] = dst6  * invdet;
242        mInv[ 7 + mInvOffset] = dst7  * invdet;
243
244        mInv[ 8 + mInvOffset] = dst8  * invdet;
245        mInv[ 9 + mInvOffset] = dst9  * invdet;
246        mInv[10 + mInvOffset] = dst10 * invdet;
247        mInv[11 + mInvOffset] = dst11 * invdet;
248
249        mInv[12 + mInvOffset] = dst12 * invdet;
250        mInv[13 + mInvOffset] = dst13 * invdet;
251        mInv[14 + mInvOffset] = dst14 * invdet;
252        mInv[15 + mInvOffset] = dst15 * invdet;
253
254        return true;
255    }
256
257    /**
258     * Computes an orthographic projection matrix.
259     *
260     * @param m returns the result
261     * @param mOffset
262     * @param left
263     * @param right
264     * @param bottom
265     * @param top
266     * @param near
267     * @param far
268     */
269    public static void orthoM(float[] m, int mOffset,
270        float left, float right, float bottom, float top,
271        float near, float far) {
272        if (left == right) {
273            throw new IllegalArgumentException("left == right");
274        }
275        if (bottom == top) {
276            throw new IllegalArgumentException("bottom == top");
277        }
278        if (near == far) {
279            throw new IllegalArgumentException("near == far");
280        }
281
282        final float r_width  = 1.0f / (right - left);
283        final float r_height = 1.0f / (top - bottom);
284        final float r_depth  = 1.0f / (far - near);
285        final float x =  2.0f * (r_width);
286        final float y =  2.0f * (r_height);
287        final float z = -2.0f * (r_depth);
288        final float tx = -(right + left) * r_width;
289        final float ty = -(top + bottom) * r_height;
290        final float tz = -(far + near) * r_depth;
291        m[mOffset + 0] = x;
292        m[mOffset + 5] = y;
293        m[mOffset +10] = z;
294        m[mOffset +12] = tx;
295        m[mOffset +13] = ty;
296        m[mOffset +14] = tz;
297        m[mOffset +15] = 1.0f;
298        m[mOffset + 1] = 0.0f;
299        m[mOffset + 2] = 0.0f;
300        m[mOffset + 3] = 0.0f;
301        m[mOffset + 4] = 0.0f;
302        m[mOffset + 6] = 0.0f;
303        m[mOffset + 7] = 0.0f;
304        m[mOffset + 8] = 0.0f;
305        m[mOffset + 9] = 0.0f;
306        m[mOffset + 11] = 0.0f;
307    }
308
309
310    /**
311     * Defines a projection matrix in terms of six clip planes.
312     *
313     * @param m the float array that holds the output perspective matrix
314     * @param offset the offset into float array m where the perspective
315     *        matrix data is written
316     * @param left
317     * @param right
318     * @param bottom
319     * @param top
320     * @param near
321     * @param far
322     */
323    public static void frustumM(float[] m, int offset,
324            float left, float right, float bottom, float top,
325            float near, float far) {
326        if (left == right) {
327            throw new IllegalArgumentException("left == right");
328        }
329        if (top == bottom) {
330            throw new IllegalArgumentException("top == bottom");
331        }
332        if (near == far) {
333            throw new IllegalArgumentException("near == far");
334        }
335        if (near <= 0.0f) {
336            throw new IllegalArgumentException("near <= 0.0f");
337        }
338        if (far <= 0.0f) {
339            throw new IllegalArgumentException("far <= 0.0f");
340        }
341        final float r_width  = 1.0f / (right - left);
342        final float r_height = 1.0f / (top - bottom);
343        final float r_depth  = 1.0f / (near - far);
344        final float x = 2.0f * (near * r_width);
345        final float y = 2.0f * (near * r_height);
346        final float A = (right + left) * r_width;
347        final float B = (top + bottom) * r_height;
348        final float C = (far + near) * r_depth;
349        final float D = 2.0f * (far * near * r_depth);
350        m[offset + 0] = x;
351        m[offset + 5] = y;
352        m[offset + 8] = A;
353        m[offset +  9] = B;
354        m[offset + 10] = C;
355        m[offset + 14] = D;
356        m[offset + 11] = -1.0f;
357        m[offset +  1] = 0.0f;
358        m[offset +  2] = 0.0f;
359        m[offset +  3] = 0.0f;
360        m[offset +  4] = 0.0f;
361        m[offset +  6] = 0.0f;
362        m[offset +  7] = 0.0f;
363        m[offset + 12] = 0.0f;
364        m[offset + 13] = 0.0f;
365        m[offset + 15] = 0.0f;
366    }
367
368    /**
369     * Defines a projection matrix in terms of a field of view angle, an
370     * aspect ratio, and z clip planes.
371     *
372     * @param m the float array that holds the perspective matrix
373     * @param offset the offset into float array m where the perspective
374     *        matrix data is written
375     * @param fovy field of view in y direction, in degrees
376     * @param aspect width to height aspect ratio of the viewport
377     * @param zNear
378     * @param zFar
379     */
380    public static void perspectiveM(float[] m, int offset,
381          float fovy, float aspect, float zNear, float zFar) {
382        float f = 1.0f / (float) Math.tan(fovy * (Math.PI / 360.0));
383        float rangeReciprocal = 1.0f / (zNear - zFar);
384
385        m[offset + 0] = f / aspect;
386        m[offset + 1] = 0.0f;
387        m[offset + 2] = 0.0f;
388        m[offset + 3] = 0.0f;
389
390        m[offset + 4] = 0.0f;
391        m[offset + 5] = f;
392        m[offset + 6] = 0.0f;
393        m[offset + 7] = 0.0f;
394
395        m[offset + 8] = 0.0f;
396        m[offset + 9] = 0.0f;
397        m[offset + 10] = (zFar + zNear) * rangeReciprocal;
398        m[offset + 11] = -1.0f;
399
400        m[offset + 12] = 0.0f;
401        m[offset + 13] = 0.0f;
402        m[offset + 14] = 2.0f * zFar * zNear * rangeReciprocal;
403        m[offset + 15] = 0.0f;
404    }
405
406    /**
407     * Computes the length of a vector.
408     *
409     * @param x x coordinate of a vector
410     * @param y y coordinate of a vector
411     * @param z z coordinate of a vector
412     * @return the length of a vector
413     */
414    public static float length(float x, float y, float z) {
415        return (float) Math.sqrt(x * x + y * y + z * z);
416    }
417
418    /**
419     * Sets matrix m to the identity matrix.
420     *
421     * @param sm returns the result
422     * @param smOffset index into sm where the result matrix starts
423     */
424    public static void setIdentityM(float[] sm, int smOffset) {
425        for (int i=0 ; i<16 ; i++) {
426            sm[smOffset + i] = 0;
427        }
428        for(int i = 0; i < 16; i += 5) {
429            sm[smOffset + i] = 1.0f;
430        }
431    }
432
433    /**
434     * Scales matrix m by x, y, and z, putting the result in sm.
435     * <p>
436     * m and sm must not overlap.
437     *
438     * @param sm returns the result
439     * @param smOffset index into sm where the result matrix starts
440     * @param m source matrix
441     * @param mOffset index into m where the source matrix starts
442     * @param x scale factor x
443     * @param y scale factor y
444     * @param z scale factor z
445     */
446    public static void scaleM(float[] sm, int smOffset,
447            float[] m, int mOffset,
448            float x, float y, float z) {
449        for (int i=0 ; i<4 ; i++) {
450            int smi = smOffset + i;
451            int mi = mOffset + i;
452            sm[     smi] = m[     mi] * x;
453            sm[ 4 + smi] = m[ 4 + mi] * y;
454            sm[ 8 + smi] = m[ 8 + mi] * z;
455            sm[12 + smi] = m[12 + mi];
456        }
457    }
458
459    /**
460     * Scales matrix m in place by sx, sy, and sz.
461     *
462     * @param m matrix to scale
463     * @param mOffset index into m where the matrix starts
464     * @param x scale factor x
465     * @param y scale factor y
466     * @param z scale factor z
467     */
468    public static void scaleM(float[] m, int mOffset,
469            float x, float y, float z) {
470        for (int i=0 ; i<4 ; i++) {
471            int mi = mOffset + i;
472            m[     mi] *= x;
473            m[ 4 + mi] *= y;
474            m[ 8 + mi] *= z;
475        }
476    }
477
478    /**
479     * Translates matrix m by x, y, and z, putting the result in tm.
480     * <p>
481     * m and tm must not overlap.
482     *
483     * @param tm returns the result
484     * @param tmOffset index into sm where the result matrix starts
485     * @param m source matrix
486     * @param mOffset index into m where the source matrix starts
487     * @param x translation factor x
488     * @param y translation factor y
489     * @param z translation factor z
490     */
491    public static void translateM(float[] tm, int tmOffset,
492            float[] m, int mOffset,
493            float x, float y, float z) {
494        for (int i=0 ; i<12 ; i++) {
495            tm[tmOffset + i] = m[mOffset + i];
496        }
497        for (int i=0 ; i<4 ; i++) {
498            int tmi = tmOffset + i;
499            int mi = mOffset + i;
500            tm[12 + tmi] = m[mi] * x + m[4 + mi] * y + m[8 + mi] * z +
501                m[12 + mi];
502        }
503    }
504
505    /**
506     * Translates matrix m by x, y, and z in place.
507     *
508     * @param m matrix
509     * @param mOffset index into m where the matrix starts
510     * @param x translation factor x
511     * @param y translation factor y
512     * @param z translation factor z
513     */
514    public static void translateM(
515            float[] m, int mOffset,
516            float x, float y, float z) {
517        for (int i=0 ; i<4 ; i++) {
518            int mi = mOffset + i;
519            m[12 + mi] += m[mi] * x + m[4 + mi] * y + m[8 + mi] * z;
520        }
521    }
522
523    /**
524     * Rotates matrix m by angle a (in degrees) around the axis (x, y, z).
525     * <p>
526     * m and rm must not overlap.
527     *
528     * @param rm returns the result
529     * @param rmOffset index into rm where the result matrix starts
530     * @param m source matrix
531     * @param mOffset index into m where the source matrix starts
532     * @param a angle to rotate in degrees
533     * @param x X axis component
534     * @param y Y axis component
535     * @param z Z axis component
536     */
537    public static void rotateM(float[] rm, int rmOffset,
538            float[] m, int mOffset,
539            float a, float x, float y, float z) {
540        synchronized(sTemp) {
541            setRotateM(sTemp, 0, a, x, y, z);
542            multiplyMM(rm, rmOffset, m, mOffset, sTemp, 0);
543        }
544    }
545
546    /**
547     * Rotates matrix m in place by angle a (in degrees)
548     * around the axis (x, y, z).
549     *
550     * @param m source matrix
551     * @param mOffset index into m where the matrix starts
552     * @param a angle to rotate in degrees
553     * @param x X axis component
554     * @param y Y axis component
555     * @param z Z axis component
556     */
557    public static void rotateM(float[] m, int mOffset,
558            float a, float x, float y, float z) {
559        synchronized(sTemp) {
560            setRotateM(sTemp, 0, a, x, y, z);
561            multiplyMM(sTemp, 16, m, mOffset, sTemp, 0);
562            System.arraycopy(sTemp, 16, m, mOffset, 16);
563        }
564    }
565
566    /**
567     * Creates a matrix for rotation by angle a (in degrees)
568     * around the axis (x, y, z).
569     * <p>
570     * An optimized path will be used for rotation about a major axis
571     * (e.g. x=1.0f y=0.0f z=0.0f).
572     *
573     * @param rm returns the result
574     * @param rmOffset index into rm where the result matrix starts
575     * @param a angle to rotate in degrees
576     * @param x X axis component
577     * @param y Y axis component
578     * @param z Z axis component
579     */
580    public static void setRotateM(float[] rm, int rmOffset,
581            float a, float x, float y, float z) {
582        rm[rmOffset + 3] = 0;
583        rm[rmOffset + 7] = 0;
584        rm[rmOffset + 11]= 0;
585        rm[rmOffset + 12]= 0;
586        rm[rmOffset + 13]= 0;
587        rm[rmOffset + 14]= 0;
588        rm[rmOffset + 15]= 1;
589        a *= (float) (Math.PI / 180.0f);
590        float s = (float) Math.sin(a);
591        float c = (float) Math.cos(a);
592        if (1.0f == x && 0.0f == y && 0.0f == z) {
593            rm[rmOffset + 5] = c;   rm[rmOffset + 10]= c;
594            rm[rmOffset + 6] = s;   rm[rmOffset + 9] = -s;
595            rm[rmOffset + 1] = 0;   rm[rmOffset + 2] = 0;
596            rm[rmOffset + 4] = 0;   rm[rmOffset + 8] = 0;
597            rm[rmOffset + 0] = 1;
598        } else if (0.0f == x && 1.0f == y && 0.0f == z) {
599            rm[rmOffset + 0] = c;   rm[rmOffset + 10]= c;
600            rm[rmOffset + 8] = s;   rm[rmOffset + 2] = -s;
601            rm[rmOffset + 1] = 0;   rm[rmOffset + 4] = 0;
602            rm[rmOffset + 6] = 0;   rm[rmOffset + 9] = 0;
603            rm[rmOffset + 5] = 1;
604        } else if (0.0f == x && 0.0f == y && 1.0f == z) {
605            rm[rmOffset + 0] = c;   rm[rmOffset + 5] = c;
606            rm[rmOffset + 1] = s;   rm[rmOffset + 4] = -s;
607            rm[rmOffset + 2] = 0;   rm[rmOffset + 6] = 0;
608            rm[rmOffset + 8] = 0;   rm[rmOffset + 9] = 0;
609            rm[rmOffset + 10]= 1;
610        } else {
611            float len = length(x, y, z);
612            if (1.0f != len) {
613                float recipLen = 1.0f / len;
614                x *= recipLen;
615                y *= recipLen;
616                z *= recipLen;
617            }
618            float nc = 1.0f - c;
619            float xy = x * y;
620            float yz = y * z;
621            float zx = z * x;
622            float xs = x * s;
623            float ys = y * s;
624            float zs = z * s;
625            rm[rmOffset +  0] = x*x*nc +  c;
626            rm[rmOffset +  4] =  xy*nc - zs;
627            rm[rmOffset +  8] =  zx*nc + ys;
628            rm[rmOffset +  1] =  xy*nc + zs;
629            rm[rmOffset +  5] = y*y*nc +  c;
630            rm[rmOffset +  9] =  yz*nc - xs;
631            rm[rmOffset +  2] =  zx*nc - ys;
632            rm[rmOffset +  6] =  yz*nc + xs;
633            rm[rmOffset + 10] = z*z*nc +  c;
634        }
635    }
636
637    /**
638     * Converts Euler angles to a rotation matrix.
639     *
640     * @param rm returns the result
641     * @param rmOffset index into rm where the result matrix starts
642     * @param x angle of rotation, in degrees
643     * @param y angle of rotation, in degrees
644     * @param z angle of rotation, in degrees
645     */
646    public static void setRotateEulerM(float[] rm, int rmOffset,
647            float x, float y, float z) {
648        x *= (float) (Math.PI / 180.0f);
649        y *= (float) (Math.PI / 180.0f);
650        z *= (float) (Math.PI / 180.0f);
651        float cx = (float) Math.cos(x);
652        float sx = (float) Math.sin(x);
653        float cy = (float) Math.cos(y);
654        float sy = (float) Math.sin(y);
655        float cz = (float) Math.cos(z);
656        float sz = (float) Math.sin(z);
657        float cxsy = cx * sy;
658        float sxsy = sx * sy;
659
660        rm[rmOffset + 0]  =   cy * cz;
661        rm[rmOffset + 1]  =  -cy * sz;
662        rm[rmOffset + 2]  =   sy;
663        rm[rmOffset + 3]  =  0.0f;
664
665        rm[rmOffset + 4]  =  cxsy * cz + cx * sz;
666        rm[rmOffset + 5]  = -cxsy * sz + cx * cz;
667        rm[rmOffset + 6]  =  -sx * cy;
668        rm[rmOffset + 7]  =  0.0f;
669
670        rm[rmOffset + 8]  = -sxsy * cz + sx * sz;
671        rm[rmOffset + 9]  =  sxsy * sz + sx * cz;
672        rm[rmOffset + 10] =  cx * cy;
673        rm[rmOffset + 11] =  0.0f;
674
675        rm[rmOffset + 12] =  0.0f;
676        rm[rmOffset + 13] =  0.0f;
677        rm[rmOffset + 14] =  0.0f;
678        rm[rmOffset + 15] =  1.0f;
679    }
680
681    /**
682     * Defines a viewing transformation in terms of an eye point, a center of
683     * view, and an up vector.
684     *
685     * @param rm returns the result
686     * @param rmOffset index into rm where the result matrix starts
687     * @param eyeX eye point X
688     * @param eyeY eye point Y
689     * @param eyeZ eye point Z
690     * @param centerX center of view X
691     * @param centerY center of view Y
692     * @param centerZ center of view Z
693     * @param upX up vector X
694     * @param upY up vector Y
695     * @param upZ up vector Z
696     */
697    public static void setLookAtM(float[] rm, int rmOffset,
698            float eyeX, float eyeY, float eyeZ,
699            float centerX, float centerY, float centerZ, float upX, float upY,
700            float upZ) {
701
702        // See the OpenGL GLUT documentation for gluLookAt for a description
703        // of the algorithm. We implement it in a straightforward way:
704
705        float fx = centerX - eyeX;
706        float fy = centerY - eyeY;
707        float fz = centerZ - eyeZ;
708
709        // Normalize f
710        float rlf = 1.0f / Matrix.length(fx, fy, fz);
711        fx *= rlf;
712        fy *= rlf;
713        fz *= rlf;
714
715        // compute s = f x up (x means "cross product")
716        float sx = fy * upZ - fz * upY;
717        float sy = fz * upX - fx * upZ;
718        float sz = fx * upY - fy * upX;
719
720        // and normalize s
721        float rls = 1.0f / Matrix.length(sx, sy, sz);
722        sx *= rls;
723        sy *= rls;
724        sz *= rls;
725
726        // compute u = s x f
727        float ux = sy * fz - sz * fy;
728        float uy = sz * fx - sx * fz;
729        float uz = sx * fy - sy * fx;
730
731        rm[rmOffset + 0] = sx;
732        rm[rmOffset + 1] = ux;
733        rm[rmOffset + 2] = -fx;
734        rm[rmOffset + 3] = 0.0f;
735
736        rm[rmOffset + 4] = sy;
737        rm[rmOffset + 5] = uy;
738        rm[rmOffset + 6] = -fy;
739        rm[rmOffset + 7] = 0.0f;
740
741        rm[rmOffset + 8] = sz;
742        rm[rmOffset + 9] = uz;
743        rm[rmOffset + 10] = -fz;
744        rm[rmOffset + 11] = 0.0f;
745
746        rm[rmOffset + 12] = 0.0f;
747        rm[rmOffset + 13] = 0.0f;
748        rm[rmOffset + 14] = 0.0f;
749        rm[rmOffset + 15] = 1.0f;
750
751        translateM(rm, rmOffset, -eyeX, -eyeY, -eyeZ);
752    }
753}
754