Matrix.java revision 355c20cb9276148fd9b7074c5199aedeb497406e
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
19import javax.microedition.khronos.opengles.GL10;
20
21/**
22 * Matrix math utilities. These methods operate on OpenGL ES format
23 * matrices and vectors stored in float arrays.
24 *
25 * Matrices are 4 x 4 column-vector matrices stored in column-major
26 * order:
27 * <pre>
28 *  m[offset +  0] m[offset +  4] m[offset +  8] m[offset + 12]
29 *  m[offset +  1] m[offset +  5] m[offset +  9] m[offset + 13]
30 *  m[offset +  2] m[offset +  6] m[offset + 10] m[offset + 14]
31 *  m[offset +  3] m[offset +  7] m[offset + 11] m[offset + 15]
32 * </pre>
33 *
34 * Vectors are 4 row x 1 column column-vectors stored in order:
35 * <pre>
36 * v[offset + 0]
37 * v[offset + 1]
38 * v[offset + 2]
39 * v[offset + 3]
40 * </pre>
41 *
42 */
43public class Matrix {
44    /**
45     * Multiply two 4x4 matrices together and store the result in a third 4x4
46     * matrix. In matrix notation: result = lhs x rhs. Due to the way
47     * matrix multiplication works, the result matrix will have the same
48     * effect as first multiplying by the rhs matrix, then multiplying by
49     * the lhs matrix. This is the opposite of what you might expect.
50     *
51     * The same float array may be passed for result, lhs, and/or rhs. However,
52     * the result element values are undefined if the result elements overlap
53     * either the lhs or rhs elements.
54     *
55     * @param result The float array that holds the result.
56     * @param resultOffset The offset into the result array where the result is
57     *        stored.
58     * @param lhs The float array that holds the left-hand-side matrix.
59     * @param lhsOffset The offset into the lhs array where the lhs is stored
60     * @param rhs The float array that holds the right-hand-side matrix.
61     * @param rhsOffset The offset into the rhs array where the rhs is stored.
62     *
63     * @throws IllegalArgumentException if result, lhs, or rhs are null, or if
64     * resultOffset + 16 > result.length or lhsOffset + 16 > lhs.length or
65     * rhsOffset + 16 > rhs.length.
66     */
67    public static native void multiplyMM(float[] result, int resultOffset,
68            float[] lhs, int lhsOffset, float[] rhs, int rhsOffset);
69
70    /**
71     * Multiply a 4 element vector by a 4x4 matrix and store the result in a 4
72     * element column vector. In matrix notation: result = lhs x rhs
73     *
74     * The same float array may be passed for resultVec, lhsMat, and/or rhsVec.
75     * However, the resultVec element values are undefined if the resultVec
76     * elements overlap either the lhsMat or rhsVec elements.
77     *
78     * @param resultVec The float array that holds the result vector.
79     * @param resultVecOffset The offset into the result array where the result
80     *        vector is stored.
81     * @param lhsMat The float array that holds the left-hand-side matrix.
82     * @param lhsMatOffset The offset into the lhs array where the lhs is stored
83     * @param rhsVec The float array that holds the right-hand-side vector.
84     * @param rhsVecOffset The offset into the rhs vector where the rhs vector
85     *        is stored.
86     *
87     * @throws IllegalArgumentException if resultVec, lhsMat,
88     * or rhsVec are null, or if resultVecOffset + 4 > resultVec.length
89     * or lhsMatOffset + 16 > lhsMat.length or
90     * rhsVecOffset + 4 > rhsVec.length.
91     */
92    public static native void multiplyMV(float[] resultVec,
93            int resultVecOffset, float[] lhsMat, int lhsMatOffset,
94            float[] rhsVec, int rhsVecOffset);
95
96    /**
97     * Transposes a 4 x 4 matrix.
98     *
99     * @param mTrans the array that holds the output inverted matrix
100     * @param mTransOffset an offset into mInv where the inverted matrix is
101     *        stored.
102     * @param m the input array
103     * @param mOffset an offset into m where the matrix is stored.
104     */
105    public static void transposeM(float[] mTrans, int mTransOffset, float[] m,
106            int mOffset) {
107        for (int i = 0; i < 4; i++) {
108            int mBase = i * 4 + mOffset;
109            mTrans[i + mTransOffset] = m[mBase];
110            mTrans[i + 4 + mTransOffset] = m[mBase + 1];
111            mTrans[i + 8 + mTransOffset] = m[mBase + 2];
112            mTrans[i + 12 + mTransOffset] = m[mBase + 3];
113        }
114    }
115
116    /**
117     * Inverts a 4 x 4 matrix.
118     *
119     * @param mInv the array that holds the output inverted matrix
120     * @param mInvOffset an offset into mInv where the inverted matrix is
121     *        stored.
122     * @param m the input array
123     * @param mOffset an offset into m where the matrix is stored.
124     * @return true if the matrix could be inverted, false if it could not.
125     */
126    public static boolean invertM(float[] mInv, int mInvOffset, float[] m,
127            int mOffset) {
128        // Invert a 4 x 4 matrix using Cramer's Rule
129
130        // array of transpose source matrix
131        float[] src = new float[16];
132
133        // transpose matrix
134        transposeM(src, 0, m, mOffset);
135
136        // temp array for pairs
137        float[] tmp = new float[12];
138
139        // calculate pairs for first 8 elements (cofactors)
140        tmp[0] = src[10] * src[15];
141        tmp[1] = src[11] * src[14];
142        tmp[2] = src[9] * src[15];
143        tmp[3] = src[11] * src[13];
144        tmp[4] = src[9] * src[14];
145        tmp[5] = src[10] * src[13];
146        tmp[6] = src[8] * src[15];
147        tmp[7] = src[11] * src[12];
148        tmp[8] = src[8] * src[14];
149        tmp[9] = src[10] * src[12];
150        tmp[10] = src[8] * src[13];
151        tmp[11] = src[9] * src[12];
152
153        // Holds the destination matrix while we're building it up.
154        float[] dst = new float[16];
155
156        // calculate first 8 elements (cofactors)
157        dst[0] = tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7];
158        dst[0] -= tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7];
159        dst[1] = tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7];
160        dst[1] -= tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7];
161        dst[2] = tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7];
162        dst[2] -= tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7];
163        dst[3] = tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6];
164        dst[3] -= tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6];
165        dst[4] = tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3];
166        dst[4] -= tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3];
167        dst[5] = tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3];
168        dst[5] -= tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3];
169        dst[6] = tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3];
170        dst[6] -= tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3];
171        dst[7] = tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2];
172        dst[7] -= tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2];
173
174        // calculate pairs for second 8 elements (cofactors)
175        tmp[0] = src[2] * src[7];
176        tmp[1] = src[3] * src[6];
177        tmp[2] = src[1] * src[7];
178        tmp[3] = src[3] * src[5];
179        tmp[4] = src[1] * src[6];
180        tmp[5] = src[2] * src[5];
181        tmp[6] = src[0] * src[7];
182        tmp[7] = src[3] * src[4];
183        tmp[8] = src[0] * src[6];
184        tmp[9] = src[2] * src[4];
185        tmp[10] = src[0] * src[5];
186        tmp[11] = src[1] * src[4];
187
188        // calculate second 8 elements (cofactors)
189        dst[8] = tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15];
190        dst[8] -= tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15];
191        dst[9] = tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15];
192        dst[9] -= tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15];
193        dst[10] = tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15];
194        dst[10] -= tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15];
195        dst[11] = tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14];
196        dst[11] -= tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14];
197        dst[12] = tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9];
198        dst[12] -= tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10];
199        dst[13] = tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10];
200        dst[13] -= tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8];
201        dst[14] = tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8];
202        dst[14] -= tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9];
203        dst[15] = tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9];
204        dst[15] -= tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8];
205
206        // calculate determinant
207        float det =
208                src[0] * dst[0] + src[1] * dst[1] + src[2] * dst[2] + src[3]
209                        * dst[3];
210
211        if (det == 0.0f) {
212
213        }
214
215        // calculate matrix inverse
216        det = 1 / det;
217        for (int j = 0; j < 16; j++)
218            mInv[j + mInvOffset] = dst[j] * det;
219
220        return true;
221    }
222
223    /**
224     * Computes an orthographic projection matrix.
225     *
226     * @param m returns the result
227     * @param mOffset
228     * @param left
229     * @param right
230     * @param bottom
231     * @param top
232     * @param near
233     * @param far
234     */
235    public static void orthoM(float[] m, int mOffset,
236        float left, float right, float bottom, float top,
237        float near, float far) {
238        if (left == right) {
239            throw new IllegalArgumentException("left == right");
240        }
241        if (bottom == top) {
242            throw new IllegalArgumentException("bottom == top");
243        }
244        if (near == far) {
245            throw new IllegalArgumentException("near == far");
246        }
247
248        final float r_width  = 1.0f / (right - left);
249        final float r_height = 1.0f / (top - bottom);
250        final float r_depth  = 1.0f / (far - near);
251        final float x =  2.0f * (r_width);
252        final float y =  2.0f * (r_height);
253        final float z = -2.0f * (r_depth);
254        final float tx = -(right + left) * r_width;
255        final float ty = -(top + bottom) * r_height;
256        final float tz = -(far + near) * r_depth;
257        m[mOffset + 0] = x;
258        m[mOffset + 5] = y;
259        m[mOffset +10] = z;
260        m[mOffset +12] = tx;
261        m[mOffset +13] = ty;
262        m[mOffset +14] = tz;
263        m[mOffset +15] = 1.0f;
264        m[mOffset + 1] = 0.0f;
265        m[mOffset + 2] = 0.0f;
266        m[mOffset + 3] = 0.0f;
267        m[mOffset + 4] = 0.0f;
268        m[mOffset + 6] = 0.0f;
269        m[mOffset + 7] = 0.0f;
270        m[mOffset + 8] = 0.0f;
271        m[mOffset + 9] = 0.0f;
272        m[mOffset + 11] = 0.0f;
273    }
274
275
276    /**
277     * Define a projection matrix in terms of six clip planes
278     * @param m the float array that holds the perspective matrix
279     * @param offset the offset into float array m where the perspective
280     * matrix data is written
281     * @param left
282     * @param right
283     * @param bottom
284     * @param top
285     * @param near
286     * @param far
287     */
288
289    public static void frustumM(float[] m, int offset,
290            float left, float right, float bottom, float top,
291            float near, float far) {
292        if (left == right) {
293            throw new IllegalArgumentException("left == right");
294        }
295        if (top == bottom) {
296            throw new IllegalArgumentException("top == bottom");
297        }
298        if (near == far) {
299            throw new IllegalArgumentException("near == far");
300        }
301        if (near <= 0.0f) {
302            throw new IllegalArgumentException("near <= 0.0f");
303        }
304        if (far <= 0.0f) {
305            throw new IllegalArgumentException("far <= 0.0f");
306        }
307        final float r_width  = 1.0f / (right - left);
308        final float r_height = 1.0f / (top - bottom);
309        final float r_depth  = 1.0f / (near - far);
310        final float x = 2.0f * (near * r_width);
311        final float y = 2.0f * (near * r_height);
312        final float A = 2.0f * ((right + left) * r_width);
313        final float B = (top + bottom) * r_height;
314        final float C = (far + near) * r_depth;
315        final float D = 2.0f * (far * near * r_depth);
316        m[offset + 0] = x;
317        m[offset + 5] = y;
318        m[offset + 8] = A;
319        m[offset +  9] = B;
320        m[offset + 10] = C;
321        m[offset + 14] = D;
322        m[offset + 11] = -1.0f;
323        m[offset +  1] = 0.0f;
324        m[offset +  2] = 0.0f;
325        m[offset +  3] = 0.0f;
326        m[offset +  4] = 0.0f;
327        m[offset +  6] = 0.0f;
328        m[offset +  7] = 0.0f;
329        m[offset + 12] = 0.0f;
330        m[offset + 13] = 0.0f;
331        m[offset + 15] = 0.0f;
332    }
333
334    /**
335     * Computes the length of a vector
336     *
337     * @param x x coordinate of a vector
338     * @param y y coordinate of a vector
339     * @param z z coordinate of a vector
340     * @return the length of a vector
341     */
342    public static float length(float x, float y, float z) {
343        return (float) Math.sqrt(x * x + y * y + z * z);
344    }
345
346    /**
347     * Sets matrix m to the identity matrix.
348     * @param sm returns the result
349     * @param smOffset index into sm where the result matrix starts
350     */
351    public static void setIdentityM(float[] sm, int smOffset) {
352        for (int i=0 ; i<16 ; i++) {
353            sm[smOffset + i] = 0;
354        }
355        for(int i = 0; i < 16; i += 5) {
356            sm[smOffset + i] = 1.0f;
357        }
358    }
359
360    /**
361     * Scales matrix  m by x, y, and z, putting the result in sm
362     * @param sm returns the result
363     * @param smOffset index into sm where the result matrix starts
364     * @param m source matrix
365     * @param mOffset index into m where the source matrix starts
366     * @param x scale factor x
367     * @param y scale factor y
368     * @param z scale factor z
369     */
370    public static void scaleM(float[] sm, int smOffset,
371            float[] m, int mOffset,
372            float x, float y, float z) {
373        for (int i=0 ; i<4 ; i++) {
374            int smi = smOffset + i;
375            int mi = mOffset + i;
376            sm[     smi] = m[     mi] * x;
377            sm[ 4 + smi] = m[ 4 + mi] * y;
378            sm[ 8 + smi] = m[ 8 + mi] * z;
379            sm[12 + smi] = m[12 + mi];
380        }
381    }
382
383    /**
384     * Scales matrix m in place by sx, sy, and sz
385     * @param m matrix to scale
386     * @param mOffset index into m where the matrix starts
387     * @param x scale factor x
388     * @param y scale factor y
389     * @param z scale factor z
390     */
391    public static void scaleM(float[] m, int mOffset,
392            float x, float y, float z) {
393        for (int i=0 ; i<4 ; i++) {
394            int mi = mOffset + i;
395            m[     mi] *= x;
396            m[ 4 + mi] *= y;
397            m[ 8 + mi] *= z;
398        }
399    }
400
401    /**
402     * Translates matrix m by x, y, and z, putting the result in tm
403     * @param tm returns the result
404     * @param tmOffset index into sm where the result matrix starts
405     * @param m source matrix
406     * @param mOffset index into m where the source matrix starts
407     * @param x translation factor x
408     * @param y translation factor y
409     * @param z translation factor z
410     */
411    public static void translateM(float[] tm, int tmOffset,
412            float[] m, int mOffset,
413            float x, float y, float z) {
414        for (int i=0 ; i<12 ; i++) {
415            tm[tmOffset + i] = m[mOffset + i];
416        }
417        for (int i=0 ; i<4 ; i++) {
418            int tmi = tmOffset + i;
419            int mi = mOffset + i;
420            tm[12 + tmi] = m[mi] * x + m[4 + mi] * y + m[8 + mi] * z +
421                m[12 + mi];
422        }
423    }
424
425    /**
426     * Translates matrix m by x, y, and z in place.
427     * @param m matrix
428     * @param mOffset index into m where the matrix starts
429     * @param x translation factor x
430     * @param y translation factor y
431     * @param z translation factor z
432     */
433    public static void translateM(
434            float[] m, int mOffset,
435            float x, float y, float z) {
436        for (int i=0 ; i<4 ; i++) {
437            int mi = mOffset + i;
438            m[12 + mi] += m[mi] * x + m[4 + mi] * y + m[8 + mi] * z;
439        }
440    }
441
442    /**
443     * Rotates matrix m by angle a (in degrees) around the axis (x, y, z)
444     * @param rm returns the result
445     * @param rmOffset index into rm where the result matrix starts
446     * @param m source matrix
447     * @param mOffset index into m where the source matrix starts
448     * @param a angle to rotate in degrees
449     * @param x scale factor x
450     * @param y scale factor y
451     * @param z scale factor z
452     */
453    public static void rotateM(float[] rm, int rmOffset,
454            float[] m, int mOffset,
455            float a, float x, float y, float z) {
456        float[] r = new float[16];
457        setRotateM(r, 0, a, x, y, z);
458        multiplyMM(rm, rmOffset, m, mOffset, r, 0);
459    }
460
461    /**
462     * Rotates matrix m in place by angle a (in degrees)
463     * around the axis (x, y, z)
464     * @param m source matrix
465     * @param mOffset index into m where the matrix starts
466     * @param a angle to rotate in degrees
467     * @param x scale factor x
468     * @param y scale factor y
469     * @param z scale factor z
470     */
471    public static void rotateM(float[] m, int mOffset,
472            float a, float x, float y, float z) {
473        float[] temp = new float[32];
474        setRotateM(temp, 0, a, x, y, z);
475        multiplyMM(temp, 16, m, mOffset, temp, 0);
476        System.arraycopy(temp, 16, m, mOffset, 16);
477    }
478
479    /**
480     * Rotates matrix m by angle a (in degrees) around the axis (x, y, z)
481     * @param rm returns the result
482     * @param rmOffset index into rm where the result matrix starts
483     * @param a angle to rotate in degrees
484     * @param x scale factor x
485     * @param y scale factor y
486     * @param z scale factor z
487     */
488    public static void setRotateM(float[] rm, int rmOffset,
489            float a, float x, float y, float z) {
490        rm[rmOffset + 3] = 0;
491        rm[rmOffset + 7] = 0;
492        rm[rmOffset + 11]= 0;
493        rm[rmOffset + 12]= 0;
494        rm[rmOffset + 13]= 0;
495        rm[rmOffset + 14]= 0;
496        rm[rmOffset + 15]= 1;
497        a *= (float) (Math.PI / 180.0f);
498        float s = (float) Math.sin(a);
499        float c = (float) Math.cos(a);
500        if (1.0f == x && 0.0f == y && 0.0f == z) {
501            rm[rmOffset + 5] = c;   rm[rmOffset + 10]= c;
502            rm[rmOffset + 6] = s;   rm[rmOffset + 9] = -s;
503            rm[rmOffset + 1] = 0;   rm[rmOffset + 2] = 0;
504            rm[rmOffset + 4] = 0;   rm[rmOffset + 8] = 0;
505            rm[rmOffset + 0] = 1;
506        } else if (0.0f == x && 1.0f == y && 0.0f == z) {
507            rm[rmOffset + 0] = c;   rm[rmOffset + 10]= c;
508            rm[rmOffset + 8] = s;   rm[rmOffset + 2] = -s;
509            rm[rmOffset + 1] = 0;   rm[rmOffset + 4] = 0;
510            rm[rmOffset + 6] = 0;   rm[rmOffset + 9] = 0;
511            rm[rmOffset + 5] = 1;
512        } else if (0.0f == x && 0.0f == y && 1.0f == z) {
513            rm[rmOffset + 0] = c;   rm[rmOffset + 5] = c;
514            rm[rmOffset + 1] = s;   rm[rmOffset + 4] = -s;
515            rm[rmOffset + 2] = 0;   rm[rmOffset + 6] = 0;
516            rm[rmOffset + 8] = 0;   rm[rmOffset + 9] = 0;
517            rm[rmOffset + 10]= 1;
518        } else {
519            float len = length(x, y, z);
520            if (1.0f != len) {
521                float recipLen = 1.0f / len;
522                x *= recipLen;
523                y *= recipLen;
524                z *= recipLen;
525            }
526            float nc = 1.0f - c;
527            float xy = x * y;
528            float yz = y * z;
529            float zx = z * x;
530            float xs = x * s;
531            float ys = y * s;
532            float zs = z * s;
533            rm[rmOffset +  0] = x*x*nc +  c;
534            rm[rmOffset +  4] =  xy*nc - zs;
535            rm[rmOffset +  8] =  zx*nc + ys;
536            rm[rmOffset +  1] =  xy*nc + zs;
537            rm[rmOffset +  5] = y*y*nc +  c;
538            rm[rmOffset +  9] =  yz*nc - xs;
539            rm[rmOffset +  2] =  zx*nc - ys;
540            rm[rmOffset +  6] =  yz*nc + xs;
541            rm[rmOffset + 10] = z*z*nc +  c;
542        }
543    }
544
545    /**
546     * Converts Euler angles to a rotation matrix
547     * @param rm returns the result
548     * @param rmOffset index into rm where the result matrix starts
549     * @param x angle of rotation, in degrees
550     * @param y angle of rotation, in degrees
551     * @param z angle of rotation, in degrees
552     */
553    public static void setRotateEulerM(float[] rm, int rmOffset,
554            float x, float y, float z) {
555        x *= (float) (Math.PI / 180.0f);
556        y *= (float) (Math.PI / 180.0f);
557        z *= (float) (Math.PI / 180.0f);
558        float cx = (float) Math.cos(x);
559        float sx = (float) Math.sin(x);
560        float cy = (float) Math.cos(y);
561        float sy = (float) Math.sin(y);
562        float cz = (float) Math.cos(z);
563        float sz = (float) Math.sin(z);
564        float cxsy = cx * sy;
565        float sxsy = sx * sy;
566
567        rm[rmOffset + 0]  =   cy * cz;
568        rm[rmOffset + 1]  =  -cy * sz;
569        rm[rmOffset + 2]  =   sy;
570        rm[rmOffset + 3]  =  0.0f;
571
572        rm[rmOffset + 4]  =  cxsy * cz + cx * sz;
573        rm[rmOffset + 5]  = -cxsy * sz + cx * cz;
574        rm[rmOffset + 6]  =  -sx * cy;
575        rm[rmOffset + 7]  =  0.0f;
576
577        rm[rmOffset + 8]  = -sxsy * cz + sx * sz;
578        rm[rmOffset + 9]  =  sxsy * sz + sx * cz;
579        rm[rmOffset + 10] =  cx * cy;
580        rm[rmOffset + 11] =  0.0f;
581
582        rm[rmOffset + 12] =  0.0f;
583        rm[rmOffset + 13] =  0.0f;
584        rm[rmOffset + 14] =  0.0f;
585        rm[rmOffset + 15] =  1.0f;
586    }
587
588    /**
589     * Define a viewing transformation in terms of an eye point, a center of
590     * view, and an up vector.
591     *
592     * @param rm returns the result
593     * @param rmOffset index into rm where the result matrix starts
594     * @param eyeX eye point X
595     * @param eyeY eye point Y
596     * @param eyeZ eye point Z
597     * @param centerX center of view X
598     * @param centerY center of view Y
599     * @param centerZ center of view Z
600     * @param upX up vector X
601     * @param upY up vector Y
602     * @param upZ up vector Z
603     */
604    public static void setLookAtM(float[] rm, int rmOffset,
605            float eyeX, float eyeY, float eyeZ,
606            float centerX, float centerY, float centerZ, float upX, float upY,
607            float upZ) {
608
609        // See the OpenGL GLUT documentation for gluLookAt for a description
610        // of the algorithm. We implement it in a straightforward way:
611
612        float fx = centerX - eyeX;
613        float fy = centerY - eyeY;
614        float fz = centerZ - eyeZ;
615
616        // Normalize f
617        float rlf = 1.0f / Matrix.length(fx, fy, fz);
618        fx *= rlf;
619        fy *= rlf;
620        fz *= rlf;
621
622        // compute s = f x up (x means "cross product")
623        float sx = fy * upZ - fz * upY;
624        float sy = fz * upX - fx * upZ;
625        float sz = fx * upY - fy * upX;
626
627        // and normalize s
628        float rls = 1.0f / Matrix.length(sx, sy, sz);
629        sx *= rls;
630        sy *= rls;
631        sz *= rls;
632
633        // compute u = s x f
634        float ux = sy * fz - sz * fy;
635        float uy = sz * fx - sx * fz;
636        float uz = sx * fy - sy * fx;
637
638        rm[rmOffset + 0] = sx;
639        rm[rmOffset + 1] = ux;
640        rm[rmOffset + 2] = -fx;
641        rm[rmOffset + 3] = 0.0f;
642
643        rm[rmOffset + 4] = sy;
644        rm[rmOffset + 5] = uy;
645        rm[rmOffset + 6] = -fy;
646        rm[rmOffset + 7] = 0.0f;
647
648        rm[rmOffset + 8] = sz;
649        rm[rmOffset + 9] = uz;
650        rm[rmOffset + 10] = -fz;
651        rm[rmOffset + 11] = 0.0f;
652
653        rm[rmOffset + 12] = 0.0f;
654        rm[rmOffset + 13] = 0.0f;
655        rm[rmOffset + 14] = 0.0f;
656        rm[rmOffset + 15] = 1.0f;
657
658        translateM(rm, rmOffset, -eyeX, -eyeY, -eyeZ);
659    }
660}
661