1/*
2 * Copyright (C) 2009-2012 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.renderscript;
18
19import java.lang.Math;
20
21
22/**
23 * Class for exposing the native RenderScript rs_matrix4x4 type back to the Android system.
24 *
25 **/
26public class Matrix4f {
27
28    /**
29    * Creates a new identity 4x4 matrix
30    */
31    public Matrix4f() {
32        mMat = new float[16];
33        loadIdentity();
34    }
35
36    /**
37    * Creates a new matrix and sets its values from the given
38    * parameter
39    *
40    * @param dataArray values to set the matrix to, must be 16
41    *                  floats long
42    */
43    public Matrix4f(float[] dataArray) {
44        mMat = new float[16];
45        System.arraycopy(dataArray, 0, mMat, 0, mMat.length);
46    }
47
48    /**
49    * Return a reference to the internal array representing matrix
50    * values. Modifying this array will also change the matrix
51    *
52    * @return internal array representing the matrix
53    */
54    public float[] getArray() {
55        return mMat;
56    }
57
58    /**
59    * Returns the value for a given row and column
60    *
61    * @param x column of the value to return
62    * @param y row of the value to return
63    *
64    * @return value in the yth row and xth column
65    */
66    public float get(int x, int y) {
67        return mMat[x*4 + y];
68    }
69
70    /**
71    * Sets the value for a given row and column
72    *
73    * @param x column of the value to set
74    * @param y row of the value to set
75    */
76    public void set(int x, int y, float v) {
77        mMat[x*4 + y] = v;
78    }
79
80    /**
81    * Sets the matrix values to identity
82    */
83    public void loadIdentity() {
84        mMat[0] = 1;
85        mMat[1] = 0;
86        mMat[2] = 0;
87        mMat[3] = 0;
88
89        mMat[4] = 0;
90        mMat[5] = 1;
91        mMat[6] = 0;
92        mMat[7] = 0;
93
94        mMat[8] = 0;
95        mMat[9] = 0;
96        mMat[10] = 1;
97        mMat[11] = 0;
98
99        mMat[12] = 0;
100        mMat[13] = 0;
101        mMat[14] = 0;
102        mMat[15] = 1;
103    }
104
105    /**
106    * Sets the values of the matrix to those of the parameter
107    *
108    * @param src matrix to load the values from
109    */
110    public void load(Matrix4f src) {
111        System.arraycopy(src.getArray(), 0, mMat, 0, mMat.length);
112    }
113
114    /**
115    * Sets the values of the matrix to those of the parameter
116    *
117    * @param src matrix to load the values from
118    * @hide
119    */
120    public void load(Matrix3f src) {
121        mMat[0] = src.mMat[0];
122        mMat[1] = src.mMat[1];
123        mMat[2] = src.mMat[2];
124        mMat[3] = 0;
125
126        mMat[4] = src.mMat[3];
127        mMat[5] = src.mMat[4];
128        mMat[6] = src.mMat[5];
129        mMat[7] = 0;
130
131        mMat[8] = src.mMat[6];
132        mMat[9] = src.mMat[7];
133        mMat[10] = src.mMat[8];
134        mMat[11] = 0;
135
136        mMat[12] = 0;
137        mMat[13] = 0;
138        mMat[14] = 0;
139        mMat[15] = 1;
140    }
141
142    /**
143    * Sets current values to be a rotation matrix of certain angle
144    * about a given axis
145    *
146    * @param rot angle of rotation
147    * @param x rotation axis x
148    * @param y rotation axis y
149    * @param z rotation axis z
150    */
151    public void loadRotate(float rot, float x, float y, float z) {
152        float c, s;
153        mMat[3] = 0;
154        mMat[7] = 0;
155        mMat[11]= 0;
156        mMat[12]= 0;
157        mMat[13]= 0;
158        mMat[14]= 0;
159        mMat[15]= 1;
160        rot *= (float)(java.lang.Math.PI / 180.0f);
161        c = (float)java.lang.Math.cos(rot);
162        s = (float)java.lang.Math.sin(rot);
163
164        float len = (float)java.lang.Math.sqrt(x*x + y*y + z*z);
165        if (!(len != 1)) {
166            float recipLen = 1.f / len;
167            x *= recipLen;
168            y *= recipLen;
169            z *= recipLen;
170        }
171        float nc = 1.0f - c;
172        float xy = x * y;
173        float yz = y * z;
174        float zx = z * x;
175        float xs = x * s;
176        float ys = y * s;
177        float zs = z * s;
178        mMat[ 0] = x*x*nc +  c;
179        mMat[ 4] =  xy*nc - zs;
180        mMat[ 8] =  zx*nc + ys;
181        mMat[ 1] =  xy*nc + zs;
182        mMat[ 5] = y*y*nc +  c;
183        mMat[ 9] =  yz*nc - xs;
184        mMat[ 2] =  zx*nc - ys;
185        mMat[ 6] =  yz*nc + xs;
186        mMat[10] = z*z*nc +  c;
187    }
188
189    /**
190    * Sets current values to be a scale matrix of given dimensions
191    *
192    * @param x scale component x
193    * @param y scale component y
194    * @param z scale component z
195    */
196    public void loadScale(float x, float y, float z) {
197        loadIdentity();
198        mMat[0] = x;
199        mMat[5] = y;
200        mMat[10] = z;
201    }
202
203    /**
204    * Sets current values to be a translation matrix of given
205    * dimensions
206    *
207    * @param x translation component x
208    * @param y translation component y
209    * @param z translation component z
210    */
211    public void loadTranslate(float x, float y, float z) {
212        loadIdentity();
213        mMat[12] = x;
214        mMat[13] = y;
215        mMat[14] = z;
216    }
217
218    /**
219    * Sets current values to be the result of multiplying two given
220    * matrices
221    *
222    * @param lhs left hand side matrix
223    * @param rhs right hand side matrix
224    */
225    public void loadMultiply(Matrix4f lhs, Matrix4f rhs) {
226        for (int i=0 ; i<4 ; i++) {
227            float ri0 = 0;
228            float ri1 = 0;
229            float ri2 = 0;
230            float ri3 = 0;
231            for (int j=0 ; j<4 ; j++) {
232                float rhs_ij = rhs.get(i,j);
233                ri0 += lhs.get(j,0) * rhs_ij;
234                ri1 += lhs.get(j,1) * rhs_ij;
235                ri2 += lhs.get(j,2) * rhs_ij;
236                ri3 += lhs.get(j,3) * rhs_ij;
237            }
238            set(i,0, ri0);
239            set(i,1, ri1);
240            set(i,2, ri2);
241            set(i,3, ri3);
242        }
243    }
244
245    /**
246    * Set current values to be an orthographic projection matrix
247    *
248    * @param l location of the left vertical clipping plane
249    * @param r location of the right vertical clipping plane
250    * @param b location of the bottom horizontal clipping plane
251    * @param t location of the top horizontal clipping plane
252    * @param n location of the near clipping plane
253    * @param f location of the far clipping plane
254    */
255    public void loadOrtho(float l, float r, float b, float t, float n, float f) {
256        loadIdentity();
257        mMat[0] = 2 / (r - l);
258        mMat[5] = 2 / (t - b);
259        mMat[10]= -2 / (f - n);
260        mMat[12]= -(r + l) / (r - l);
261        mMat[13]= -(t + b) / (t - b);
262        mMat[14]= -(f + n) / (f - n);
263    }
264
265    /**
266    * Set current values to be an orthographic projection matrix
267    * with the right and bottom clipping planes set to the given
268    * values. Left and top clipping planes are set to 0. Near and
269    * far are set to -1, 1 respectively
270    *
271    * @param w location of the right vertical clipping plane
272    * @param h location of the bottom horizontal clipping plane
273    *
274    */
275    public void loadOrthoWindow(int w, int h) {
276        loadOrtho(0,w, h,0, -1,1);
277    }
278
279    /**
280    * Sets current values to be a perspective projection matrix
281    *
282    * @param l location of the left vertical clipping plane
283    * @param r location of the right vertical clipping plane
284    * @param b location of the bottom horizontal clipping plane
285    * @param t location of the top horizontal clipping plane
286    * @param n location of the near clipping plane, must be positive
287    * @param f location of the far clipping plane, must be positive
288    *
289    */
290    public void loadFrustum(float l, float r, float b, float t, float n, float f) {
291        loadIdentity();
292        mMat[0] = 2 * n / (r - l);
293        mMat[5] = 2 * n / (t - b);
294        mMat[8] = (r + l) / (r - l);
295        mMat[9] = (t + b) / (t - b);
296        mMat[10]= -(f + n) / (f - n);
297        mMat[11]= -1;
298        mMat[14]= -2*f*n / (f - n);
299        mMat[15]= 0;
300    }
301
302    /**
303    * Sets current values to be a perspective projection matrix
304    *
305    * @param fovy vertical field of view angle in degrees
306    * @param aspect aspect ratio of the screen
307    * @param near near cliping plane, must be positive
308    * @param far far clipping plane, must be positive
309    */
310    public void loadPerspective(float fovy, float aspect, float near, float far) {
311        float top = near * (float)Math.tan((float) (fovy * Math.PI / 360.0f));
312        float bottom = -top;
313        float left = bottom * aspect;
314        float right = top * aspect;
315        loadFrustum(left, right, bottom, top, near, far);
316    }
317
318    /**
319    * Helper function to set the current values to a perspective
320    * projection matrix with aspect ratio defined by the parameters
321    * and (near, far), (bottom, top) mapping to (-1, 1) at z = 0
322    *
323    * @param w screen width
324    * @param h screen height
325    */
326    public void loadProjectionNormalized(int w, int h) {
327        // range -1,1 in the narrow axis at z = 0.
328        Matrix4f m1 = new Matrix4f();
329        Matrix4f m2 = new Matrix4f();
330
331        if(w > h) {
332            float aspect = ((float)w) / h;
333            m1.loadFrustum(-aspect,aspect,  -1,1,  1,100);
334        } else {
335            float aspect = ((float)h) / w;
336            m1.loadFrustum(-1,1, -aspect,aspect, 1,100);
337        }
338
339        m2.loadRotate(180, 0, 1, 0);
340        m1.loadMultiply(m1, m2);
341
342        m2.loadScale(-2, 2, 1);
343        m1.loadMultiply(m1, m2);
344
345        m2.loadTranslate(0, 0, 2);
346        m1.loadMultiply(m1, m2);
347
348        load(m1);
349    }
350
351    /**
352    * Post-multiplies the current matrix by a given parameter
353    *
354    * @param rhs right hand side to multiply by
355    */
356    public void multiply(Matrix4f rhs) {
357        Matrix4f tmp = new Matrix4f();
358        tmp.loadMultiply(this, rhs);
359        load(tmp);
360    }
361    /**
362    * Modifies the current matrix by post-multiplying it with a
363    * rotation matrix of certain angle about a given axis
364    *
365    * @param rot angle of rotation
366    * @param x rotation axis x
367    * @param y rotation axis y
368    * @param z rotation axis z
369    */
370    public void rotate(float rot, float x, float y, float z) {
371        Matrix4f tmp = new Matrix4f();
372        tmp.loadRotate(rot, x, y, z);
373        multiply(tmp);
374    }
375
376    /**
377    * Modifies the current matrix by post-multiplying it with a
378    * scale matrix of given dimensions
379    *
380    * @param x scale component x
381    * @param y scale component y
382    * @param z scale component z
383    */
384    public void scale(float x, float y, float z) {
385        Matrix4f tmp = new Matrix4f();
386        tmp.loadScale(x, y, z);
387        multiply(tmp);
388    }
389
390    /**
391    * Modifies the current matrix by post-multiplying it with a
392    * translation matrix of given dimensions
393    *
394    * @param x translation component x
395    * @param y translation component y
396    * @param z translation component z
397    */
398    public void translate(float x, float y, float z) {
399        Matrix4f tmp = new Matrix4f();
400        tmp.loadTranslate(x, y, z);
401        multiply(tmp);
402    }
403    private float computeCofactor(int i, int j) {
404        int c0 = (i+1) % 4;
405        int c1 = (i+2) % 4;
406        int c2 = (i+3) % 4;
407        int r0 = (j+1) % 4;
408        int r1 = (j+2) % 4;
409        int r2 = (j+3) % 4;
410
411        float minor = (mMat[c0 + 4*r0] * (mMat[c1 + 4*r1] * mMat[c2 + 4*r2] -
412                                            mMat[c1 + 4*r2] * mMat[c2 + 4*r1]))
413                     - (mMat[c0 + 4*r1] * (mMat[c1 + 4*r0] * mMat[c2 + 4*r2] -
414                                            mMat[c1 + 4*r2] * mMat[c2 + 4*r0]))
415                     + (mMat[c0 + 4*r2] * (mMat[c1 + 4*r0] * mMat[c2 + 4*r1] -
416                                            mMat[c1 + 4*r1] * mMat[c2 + 4*r0]));
417
418        float cofactor = ((i+j) & 1) != 0 ? -minor : minor;
419        return cofactor;
420    }
421
422    /**
423    * Sets the current matrix to its inverse
424    */
425    public boolean inverse() {
426
427        Matrix4f result = new Matrix4f();
428
429        for (int i = 0; i < 4; ++i) {
430            for (int j = 0; j < 4; ++j) {
431                result.mMat[4*i + j] = computeCofactor(i, j);
432            }
433        }
434
435        // Dot product of 0th column of source and 0th row of result
436        float det = mMat[0]*result.mMat[0] + mMat[4]*result.mMat[1] +
437                     mMat[8]*result.mMat[2] + mMat[12]*result.mMat[3];
438
439        if (Math.abs(det) < 1e-6) {
440            return false;
441        }
442
443        det = 1.0f / det;
444        for (int i = 0; i < 16; ++i) {
445            mMat[i] = result.mMat[i] * det;
446        }
447
448        return true;
449    }
450
451    /**
452    * Sets the current matrix to its inverse transpose
453    */
454    public boolean inverseTranspose() {
455
456        Matrix4f result = new Matrix4f();
457
458        for (int i = 0; i < 4; ++i) {
459            for (int j = 0; j < 4; ++j) {
460                result.mMat[4*j + i] = computeCofactor(i, j);
461            }
462        }
463
464        float det = mMat[0]*result.mMat[0] + mMat[4]*result.mMat[4] +
465                     mMat[8]*result.mMat[8] + mMat[12]*result.mMat[12];
466
467        if (Math.abs(det) < 1e-6) {
468            return false;
469        }
470
471        det = 1.0f / det;
472        for (int i = 0; i < 16; ++i) {
473            mMat[i] = result.mMat[i] * det;
474        }
475
476        return true;
477    }
478
479    /**
480    * Sets the current matrix to its transpose
481    */
482    public void transpose() {
483        for(int i = 0; i < 3; ++i) {
484            for(int j = i + 1; j < 4; ++j) {
485                float temp = mMat[i*4 + j];
486                mMat[i*4 + j] = mMat[j*4 + i];
487                mMat[j*4 + i] = temp;
488            }
489        }
490    }
491
492    final float[] mMat;
493}
494