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