blob: 5ae341b7abcea945cc9fb69f6af10deb997ddcb1 [file] [log] [blame]
Aurimas Liutikasdc3f8852024-07-11 10:07:48 -07001/*
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 androidx.annotation.NonNull;
20
21/**
22 * Matrix math utilities. These methods operate on OpenGL ES format
23 * matrices and vectors stored in float arrays.
24 * <p>
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]</pre>
32 *
33 * Vectors are 4 x 1 column vectors stored in order:
34 * <pre>
35 * v[offset + 0]
36 * v[offset + 1]
37 * v[offset + 2]
38 * v[offset + 3]</pre>
39 */
40public class Matrix {
41
42 /** Temporary memory for operations that need temporary matrix data. */
43 private static final ThreadLocal<float[]> ThreadTmp = new ThreadLocal() {
44 @Override protected float[] initialValue() {
45 return new float[32];
46 }
47 };
48
49 /**
50 * @deprecated All methods are static, do not instantiate this class.
51 */
52 @Deprecated
53 public Matrix() {}
54
55 private static boolean overlap(
56 float[] a, int aStart, int aLength, float[] b, int bStart, int bLength) {
57 if (a != b) {
58 return false;
59 }
60
61 if (aStart == bStart) {
62 return true;
63 }
64
65 int aEnd = aStart + aLength;
66 int bEnd = bStart + bLength;
67
68 if (aEnd == bEnd) {
69 return true;
70 }
71
72 if (aStart < bStart && bStart < aEnd) {
73 return true;
74 }
75 if (aStart < bEnd && bEnd < aEnd) {
76 return true;
77 }
78
79 if (bStart < aStart && aStart < bEnd) {
80 return true;
81 }
82 if (bStart < aEnd && aEnd < bEnd) {
83 return true;
84 }
85
86 return false;
87 }
88
89 /**
90 * Multiplies two 4x4 matrices together and stores the result in a third 4x4
91 * matrix. In matrix notation: result = lhs x rhs. Due to the way
92 * matrix multiplication works, the result matrix will have the same
93 * effect as first multiplying by the rhs matrix, then multiplying by
94 * the lhs matrix. This is the opposite of what you might expect.
95 * <p>
96 * The same float array may be passed for result, lhs, and/or rhs. This
97 * operation is expected to do the correct thing if the result elements
98 * overlap with either of the lhs or rhs elements.
99 *
100 * @param result The float array that holds the result.
101 * @param resultOffset The offset into the result array where the result is
102 * stored.
103 * @param lhs The float array that holds the left-hand-side matrix.
104 * @param lhsOffset The offset into the lhs array where the lhs is stored
105 * @param rhs The float array that holds the right-hand-side matrix.
106 * @param rhsOffset The offset into the rhs array where the rhs is stored.
107 *
108 * @throws IllegalArgumentException under any of the following conditions:
109 * result, lhs, or rhs are null;
110 * resultOffset + 16 > result.length
111 * or lhsOffset + 16 > lhs.length
112 * or rhsOffset + 16 > rhs.length;
113 * resultOffset < 0 or lhsOffset < 0 or rhsOffset < 0
114 */
115 public static void multiplyMM(float[] result, int resultOffset,
116 float[] lhs, int lhsOffset, float[] rhs, int rhsOffset) {
117 // error checking
118 if (result == null) {
119 throw new IllegalArgumentException("result == null");
120 }
121 if (lhs == null) {
122 throw new IllegalArgumentException("lhs == null");
123 }
124 if (rhs == null) {
125 throw new IllegalArgumentException("rhs == null");
126 }
127 if (resultOffset < 0) {
128 throw new IllegalArgumentException("resultOffset < 0");
129 }
130 if (lhsOffset < 0) {
131 throw new IllegalArgumentException("lhsOffset < 0");
132 }
133 if (rhsOffset < 0) {
134 throw new IllegalArgumentException("rhsOffset < 0");
135 }
136 if (result.length < resultOffset + 16) {
137 throw new IllegalArgumentException("result.length < resultOffset + 16");
138 }
139 if (lhs.length < lhsOffset + 16) {
140 throw new IllegalArgumentException("lhs.length < lhsOffset + 16");
141 }
142 if (rhs.length < rhsOffset + 16) {
143 throw new IllegalArgumentException("rhs.length < rhsOffset + 16");
144 }
145
146 // Check for overlap between rhs and result or lhs and result
147 if ( overlap(result, resultOffset, 16, lhs, lhsOffset, 16)
148 || overlap(result, resultOffset, 16, rhs, rhsOffset, 16) ) {
149 float[] tmp = ThreadTmp.get();
150 for (int i=0; i<4; i++) {
151 final float rhs_i0 = rhs[ 4*i + 0 + rhsOffset ];
152 float ri0 = lhs[ 0 + lhsOffset ] * rhs_i0;
153 float ri1 = lhs[ 1 + lhsOffset ] * rhs_i0;
154 float ri2 = lhs[ 2 + lhsOffset ] * rhs_i0;
155 float ri3 = lhs[ 3 + lhsOffset ] * rhs_i0;
156 for (int j=1; j<4; j++) {
157 final float rhs_ij = rhs[ 4*i + j + rhsOffset];
158 ri0 += lhs[ 4*j + 0 + lhsOffset ] * rhs_ij;
159 ri1 += lhs[ 4*j + 1 + lhsOffset ] * rhs_ij;
160 ri2 += lhs[ 4*j + 2 + lhsOffset ] * rhs_ij;
161 ri3 += lhs[ 4*j + 3 + lhsOffset ] * rhs_ij;
162 }
163 tmp[ 4*i + 0 ] = ri0;
164 tmp[ 4*i + 1 ] = ri1;
165 tmp[ 4*i + 2 ] = ri2;
166 tmp[ 4*i + 3 ] = ri3;
167 }
168
169 // copy from tmp to result
170 for (int i=0; i < 16; i++) {
171 result[ i + resultOffset ] = tmp[ i ];
172 }
173
174 } else {
175 for (int i=0; i<4; i++) {
176 final float rhs_i0 = rhs[ 4*i + 0 + rhsOffset ];
177 float ri0 = lhs[ 0 + lhsOffset ] * rhs_i0;
178 float ri1 = lhs[ 1 + lhsOffset ] * rhs_i0;
179 float ri2 = lhs[ 2 + lhsOffset ] * rhs_i0;
180 float ri3 = lhs[ 3 + lhsOffset ] * rhs_i0;
181 for (int j=1; j<4; j++) {
182 final float rhs_ij = rhs[ 4*i + j + rhsOffset];
183 ri0 += lhs[ 4*j + 0 + lhsOffset ] * rhs_ij;
184 ri1 += lhs[ 4*j + 1 + lhsOffset ] * rhs_ij;
185 ri2 += lhs[ 4*j + 2 + lhsOffset ] * rhs_ij;
186 ri3 += lhs[ 4*j + 3 + lhsOffset ] * rhs_ij;
187 }
188 result[ 4*i + 0 + resultOffset ] = ri0;
189 result[ 4*i + 1 + resultOffset ] = ri1;
190 result[ 4*i + 2 + resultOffset ] = ri2;
191 result[ 4*i + 3 + resultOffset ] = ri3;
192 }
193 }
194 }
195
196 /**
197 * Multiplies a 4 element vector by a 4x4 matrix and stores the result in a
198 * 4-element column vector. In matrix notation: result = lhs x rhs
199 * <p>
200 * The same float array may be passed for resultVec, lhsMat, and/or rhsVec.
201 * This operation is expected to do the correct thing if the result elements
202 * overlap with either of the lhs or rhs elements.
203 *
204 * @param resultVec The float array that holds the result vector.
205 * @param resultVecOffset The offset into the result array where the result
206 * vector is stored.
207 * @param lhsMat The float array that holds the left-hand-side matrix.
208 * @param lhsMatOffset The offset into the lhs array where the lhs is stored
209 * @param rhsVec The float array that holds the right-hand-side vector.
210 * @param rhsVecOffset The offset into the rhs vector where the rhs vector
211 * is stored.
212 *
213 * @throws IllegalArgumentException under any of the following conditions:
214 * resultVec, lhsMat, or rhsVec are null;
215 * resultVecOffset + 4 > resultVec.length
216 * or lhsMatOffset + 16 > lhsMat.length
217 * or rhsVecOffset + 4 > rhsVec.length;
218 * resultVecOffset < 0 or lhsMatOffset < 0 or rhsVecOffset < 0
219 */
220 public static void multiplyMV(float[] resultVec,
221 int resultVecOffset, float[] lhsMat, int lhsMatOffset,
222 float[] rhsVec, int rhsVecOffset) {
223 // error checking
224 if (resultVec == null) {
225 throw new IllegalArgumentException("resultVec == null");
226 }
227 if (lhsMat == null) {
228 throw new IllegalArgumentException("lhsMat == null");
229 }
230 if (rhsVec == null) {
231 throw new IllegalArgumentException("rhsVec == null");
232 }
233 if (resultVecOffset < 0) {
234 throw new IllegalArgumentException("resultVecOffset < 0");
235 }
236 if (lhsMatOffset < 0) {
237 throw new IllegalArgumentException("lhsMatOffset < 0");
238 }
239 if (rhsVecOffset < 0) {
240 throw new IllegalArgumentException("rhsVecOffset < 0");
241 }
242 if (resultVec.length < resultVecOffset + 4) {
243 throw new IllegalArgumentException("resultVec.length < resultVecOffset + 4");
244 }
245 if (lhsMat.length < lhsMatOffset + 16) {
246 throw new IllegalArgumentException("lhsMat.length < lhsMatOffset + 16");
247 }
248 if (rhsVec.length < rhsVecOffset + 4) {
249 throw new IllegalArgumentException("rhsVec.length < rhsVecOffset + 4");
250 }
251
252 float tmp0 = lhsMat[0 + 4 * 0 + lhsMatOffset] * rhsVec[0 + rhsVecOffset] +
253 lhsMat[0 + 4 * 1 + lhsMatOffset] * rhsVec[1 + rhsVecOffset] +
254 lhsMat[0 + 4 * 2 + lhsMatOffset] * rhsVec[2 + rhsVecOffset] +
255 lhsMat[0 + 4 * 3 + lhsMatOffset] * rhsVec[3 + rhsVecOffset];
256 float tmp1 = lhsMat[1 + 4 * 0 + lhsMatOffset] * rhsVec[0 + rhsVecOffset] +
257 lhsMat[1 + 4 * 1 + lhsMatOffset] * rhsVec[1 + rhsVecOffset] +
258 lhsMat[1 + 4 * 2 + lhsMatOffset] * rhsVec[2 + rhsVecOffset] +
259 lhsMat[1 + 4 * 3 + lhsMatOffset] * rhsVec[3 + rhsVecOffset];
260 float tmp2 = lhsMat[2 + 4 * 0 + lhsMatOffset] * rhsVec[0 + rhsVecOffset] +
261 lhsMat[2 + 4 * 1 + lhsMatOffset] * rhsVec[1 + rhsVecOffset] +
262 lhsMat[2 + 4 * 2 + lhsMatOffset] * rhsVec[2 + rhsVecOffset] +
263 lhsMat[2 + 4 * 3 + lhsMatOffset] * rhsVec[3 + rhsVecOffset];
264 float tmp3 = lhsMat[3 + 4 * 0 + lhsMatOffset] * rhsVec[0 + rhsVecOffset] +
265 lhsMat[3 + 4 * 1 + lhsMatOffset] * rhsVec[1 + rhsVecOffset] +
266 lhsMat[3 + 4 * 2 + lhsMatOffset] * rhsVec[2 + rhsVecOffset] +
267 lhsMat[3 + 4 * 3 + lhsMatOffset] * rhsVec[3 + rhsVecOffset];
268
269 resultVec[ 0 + resultVecOffset ] = tmp0;
270 resultVec[ 1 + resultVecOffset ] = tmp1;
271 resultVec[ 2 + resultVecOffset ] = tmp2;
272 resultVec[ 3 + resultVecOffset ] = tmp3;
273 }
274
275 /**
276 * Transposes a 4 x 4 matrix.
277 * <p>
278 * mTrans and m must not overlap.
279 *
280 * @param mTrans the array that holds the output transposed matrix
281 * @param mTransOffset an offset into mTrans where the transposed matrix is
282 * stored.
283 * @param m the input array
284 * @param mOffset an offset into m where the input matrix is stored.
285 */
286 public static void transposeM(float[] mTrans, int mTransOffset, float[] m,
287 int mOffset) {
288 for (int i = 0; i < 4; i++) {
289 int mBase = i * 4 + mOffset;
290 mTrans[i + mTransOffset] = m[mBase];
291 mTrans[i + 4 + mTransOffset] = m[mBase + 1];
292 mTrans[i + 8 + mTransOffset] = m[mBase + 2];
293 mTrans[i + 12 + mTransOffset] = m[mBase + 3];
294 }
295 }
296
297 /**
298 * Inverts a 4 x 4 matrix.
299 * <p>
300 * mInv and m must not overlap.
301 *
302 * @param mInv the array that holds the output inverted matrix
303 * @param mInvOffset an offset into mInv where the inverted matrix is
304 * stored.
305 * @param m the input array
306 * @param mOffset an offset into m where the input matrix is stored.
307 * @return true if the matrix could be inverted, false if it could not.
308 */
309 public static boolean invertM(float[] mInv, int mInvOffset, float[] m,
310 int mOffset) {
311 // Invert a 4 x 4 matrix using Cramer's Rule
312
313 // transpose matrix
314 final float src0 = m[mOffset + 0];
315 final float src4 = m[mOffset + 1];
316 final float src8 = m[mOffset + 2];
317 final float src12 = m[mOffset + 3];
318
319 final float src1 = m[mOffset + 4];
320 final float src5 = m[mOffset + 5];
321 final float src9 = m[mOffset + 6];
322 final float src13 = m[mOffset + 7];
323
324 final float src2 = m[mOffset + 8];
325 final float src6 = m[mOffset + 9];
326 final float src10 = m[mOffset + 10];
327 final float src14 = m[mOffset + 11];
328
329 final float src3 = m[mOffset + 12];
330 final float src7 = m[mOffset + 13];
331 final float src11 = m[mOffset + 14];
332 final float src15 = m[mOffset + 15];
333
334 // calculate pairs for first 8 elements (cofactors)
335 final float atmp0 = src10 * src15;
336 final float atmp1 = src11 * src14;
337 final float atmp2 = src9 * src15;
338 final float atmp3 = src11 * src13;
339 final float atmp4 = src9 * src14;
340 final float atmp5 = src10 * src13;
341 final float atmp6 = src8 * src15;
342 final float atmp7 = src11 * src12;
343 final float atmp8 = src8 * src14;
344 final float atmp9 = src10 * src12;
345 final float atmp10 = src8 * src13;
346 final float atmp11 = src9 * src12;
347
348 // calculate first 8 elements (cofactors)
349 final float dst0 = (atmp0 * src5 + atmp3 * src6 + atmp4 * src7)
350 - (atmp1 * src5 + atmp2 * src6 + atmp5 * src7);
351 final float dst1 = (atmp1 * src4 + atmp6 * src6 + atmp9 * src7)
352 - (atmp0 * src4 + atmp7 * src6 + atmp8 * src7);
353 final float dst2 = (atmp2 * src4 + atmp7 * src5 + atmp10 * src7)
354 - (atmp3 * src4 + atmp6 * src5 + atmp11 * src7);
355 final float dst3 = (atmp5 * src4 + atmp8 * src5 + atmp11 * src6)
356 - (atmp4 * src4 + atmp9 * src5 + atmp10 * src6);
357 final float dst4 = (atmp1 * src1 + atmp2 * src2 + atmp5 * src3)
358 - (atmp0 * src1 + atmp3 * src2 + atmp4 * src3);
359 final float dst5 = (atmp0 * src0 + atmp7 * src2 + atmp8 * src3)
360 - (atmp1 * src0 + atmp6 * src2 + atmp9 * src3);
361 final float dst6 = (atmp3 * src0 + atmp6 * src1 + atmp11 * src3)
362 - (atmp2 * src0 + atmp7 * src1 + atmp10 * src3);
363 final float dst7 = (atmp4 * src0 + atmp9 * src1 + atmp10 * src2)
364 - (atmp5 * src0 + atmp8 * src1 + atmp11 * src2);
365
366 // calculate pairs for second 8 elements (cofactors)
367 final float btmp0 = src2 * src7;
368 final float btmp1 = src3 * src6;
369 final float btmp2 = src1 * src7;
370 final float btmp3 = src3 * src5;
371 final float btmp4 = src1 * src6;
372 final float btmp5 = src2 * src5;
373 final float btmp6 = src0 * src7;
374 final float btmp7 = src3 * src4;
375 final float btmp8 = src0 * src6;
376 final float btmp9 = src2 * src4;
377 final float btmp10 = src0 * src5;
378 final float btmp11 = src1 * src4;
379
380 // calculate second 8 elements (cofactors)
381 final float dst8 = (btmp0 * src13 + btmp3 * src14 + btmp4 * src15)
382 - (btmp1 * src13 + btmp2 * src14 + btmp5 * src15);
383 final float dst9 = (btmp1 * src12 + btmp6 * src14 + btmp9 * src15)
384 - (btmp0 * src12 + btmp7 * src14 + btmp8 * src15);
385 final float dst10 = (btmp2 * src12 + btmp7 * src13 + btmp10 * src15)
386 - (btmp3 * src12 + btmp6 * src13 + btmp11 * src15);
387 final float dst11 = (btmp5 * src12 + btmp8 * src13 + btmp11 * src14)
388 - (btmp4 * src12 + btmp9 * src13 + btmp10 * src14);
389 final float dst12 = (btmp2 * src10 + btmp5 * src11 + btmp1 * src9 )
390 - (btmp4 * src11 + btmp0 * src9 + btmp3 * src10);
391 final float dst13 = (btmp8 * src11 + btmp0 * src8 + btmp7 * src10)
392 - (btmp6 * src10 + btmp9 * src11 + btmp1 * src8 );
393 final float dst14 = (btmp6 * src9 + btmp11 * src11 + btmp3 * src8 )
394 - (btmp10 * src11 + btmp2 * src8 + btmp7 * src9 );
395 final float dst15 = (btmp10 * src10 + btmp4 * src8 + btmp9 * src9 )
396 - (btmp8 * src9 + btmp11 * src10 + btmp5 * src8 );
397
398 // calculate determinant
399 final float det =
400 src0 * dst0 + src1 * dst1 + src2 * dst2 + src3 * dst3;
401
402 if (det == 0.0f) {
403 return false;
404 }
405
406 // calculate matrix inverse
407 final float invdet = 1.0f / det;
408 mInv[ mInvOffset] = dst0 * invdet;
409 mInv[ 1 + mInvOffset] = dst1 * invdet;
410 mInv[ 2 + mInvOffset] = dst2 * invdet;
411 mInv[ 3 + mInvOffset] = dst3 * invdet;
412
413 mInv[ 4 + mInvOffset] = dst4 * invdet;
414 mInv[ 5 + mInvOffset] = dst5 * invdet;
415 mInv[ 6 + mInvOffset] = dst6 * invdet;
416 mInv[ 7 + mInvOffset] = dst7 * invdet;
417
418 mInv[ 8 + mInvOffset] = dst8 * invdet;
419 mInv[ 9 + mInvOffset] = dst9 * invdet;
420 mInv[10 + mInvOffset] = dst10 * invdet;
421 mInv[11 + mInvOffset] = dst11 * invdet;
422
423 mInv[12 + mInvOffset] = dst12 * invdet;
424 mInv[13 + mInvOffset] = dst13 * invdet;
425 mInv[14 + mInvOffset] = dst14 * invdet;
426 mInv[15 + mInvOffset] = dst15 * invdet;
427
428 return true;
429 }
430
431 /**
432 * Computes an orthographic projection matrix.
433 *
434 * @param m returns the result
435 * @param mOffset
436 * @param left
437 * @param right
438 * @param bottom
439 * @param top
440 * @param near
441 * @param far
442 */
443 public static void orthoM(float[] m, int mOffset,
444 float left, float right, float bottom, float top,
445 float near, float far) {
446 if (left == right) {
447 throw new IllegalArgumentException("left == right");
448 }
449 if (bottom == top) {
450 throw new IllegalArgumentException("bottom == top");
451 }
452 if (near == far) {
453 throw new IllegalArgumentException("near == far");
454 }
455
456 final float r_width = 1.0f / (right - left);
457 final float r_height = 1.0f / (top - bottom);
458 final float r_depth = 1.0f / (far - near);
459 final float x = 2.0f * (r_width);
460 final float y = 2.0f * (r_height);
461 final float z = -2.0f * (r_depth);
462 final float tx = -(right + left) * r_width;
463 final float ty = -(top + bottom) * r_height;
464 final float tz = -(far + near) * r_depth;
465 m[mOffset + 0] = x;
466 m[mOffset + 5] = y;
467 m[mOffset +10] = z;
468 m[mOffset +12] = tx;
469 m[mOffset +13] = ty;
470 m[mOffset +14] = tz;
471 m[mOffset +15] = 1.0f;
472 m[mOffset + 1] = 0.0f;
473 m[mOffset + 2] = 0.0f;
474 m[mOffset + 3] = 0.0f;
475 m[mOffset + 4] = 0.0f;
476 m[mOffset + 6] = 0.0f;
477 m[mOffset + 7] = 0.0f;
478 m[mOffset + 8] = 0.0f;
479 m[mOffset + 9] = 0.0f;
480 m[mOffset + 11] = 0.0f;
481 }
482
483
484 /**
485 * Defines a projection matrix in terms of six clip planes.
486 *
487 * @param m the float array that holds the output perspective matrix
488 * @param offset the offset into float array m where the perspective
489 * matrix data is written
490 * @param left
491 * @param right
492 * @param bottom
493 * @param top
494 * @param near
495 * @param far
496 */
497 public static void frustumM(float[] m, int offset,
498 float left, float right, float bottom, float top,
499 float near, float far) {
500 if (left == right) {
501 throw new IllegalArgumentException("left == right");
502 }
503 if (top == bottom) {
504 throw new IllegalArgumentException("top == bottom");
505 }
506 if (near == far) {
507 throw new IllegalArgumentException("near == far");
508 }
509 if (near <= 0.0f) {
510 throw new IllegalArgumentException("near <= 0.0f");
511 }
512 if (far <= 0.0f) {
513 throw new IllegalArgumentException("far <= 0.0f");
514 }
515 final float r_width = 1.0f / (right - left);
516 final float r_height = 1.0f / (top - bottom);
517 final float r_depth = 1.0f / (near - far);
518 final float x = 2.0f * (near * r_width);
519 final float y = 2.0f * (near * r_height);
520 final float A = (right + left) * r_width;
521 final float B = (top + bottom) * r_height;
522 final float C = (far + near) * r_depth;
523 final float D = 2.0f * (far * near * r_depth);
524 m[offset + 0] = x;
525 m[offset + 5] = y;
526 m[offset + 8] = A;
527 m[offset + 9] = B;
528 m[offset + 10] = C;
529 m[offset + 14] = D;
530 m[offset + 11] = -1.0f;
531 m[offset + 1] = 0.0f;
532 m[offset + 2] = 0.0f;
533 m[offset + 3] = 0.0f;
534 m[offset + 4] = 0.0f;
535 m[offset + 6] = 0.0f;
536 m[offset + 7] = 0.0f;
537 m[offset + 12] = 0.0f;
538 m[offset + 13] = 0.0f;
539 m[offset + 15] = 0.0f;
540 }
541
542 /**
543 * Defines a projection matrix in terms of a field of view angle, an
544 * aspect ratio, and z clip planes.
545 *
546 * @param m the float array that holds the perspective matrix
547 * @param offset the offset into float array m where the perspective
548 * matrix data is written
549 * @param fovy field of view in y direction, in degrees
550 * @param aspect width to height aspect ratio of the viewport
551 * @param zNear
552 * @param zFar
553 */
554 public static void perspectiveM(float[] m, int offset,
555 float fovy, float aspect, float zNear, float zFar) {
556 float f = 1.0f / (float) Math.tan(fovy * (Math.PI / 360.0));
557 float rangeReciprocal = 1.0f / (zNear - zFar);
558
559 m[offset + 0] = f / aspect;
560 m[offset + 1] = 0.0f;
561 m[offset + 2] = 0.0f;
562 m[offset + 3] = 0.0f;
563
564 m[offset + 4] = 0.0f;
565 m[offset + 5] = f;
566 m[offset + 6] = 0.0f;
567 m[offset + 7] = 0.0f;
568
569 m[offset + 8] = 0.0f;
570 m[offset + 9] = 0.0f;
571 m[offset + 10] = (zFar + zNear) * rangeReciprocal;
572 m[offset + 11] = -1.0f;
573
574 m[offset + 12] = 0.0f;
575 m[offset + 13] = 0.0f;
576 m[offset + 14] = 2.0f * zFar * zNear * rangeReciprocal;
577 m[offset + 15] = 0.0f;
578 }
579
580 /**
581 * Computes the length of a vector.
582 *
583 * @param x x coordinate of a vector
584 * @param y y coordinate of a vector
585 * @param z z coordinate of a vector
586 * @return the length of a vector
587 */
588 public static float length(float x, float y, float z) {
589 return (float) Math.sqrt(x * x + y * y + z * z);
590 }
591
592 /**
593 * Sets matrix m to the identity matrix.
594 *
595 * @param sm returns the result
596 * @param smOffset index into sm where the result matrix starts
597 */
598 public static void setIdentityM(float[] sm, int smOffset) {
599 for (int i=0 ; i<16 ; i++) {
600 sm[smOffset + i] = 0;
601 }
602 for(int i = 0; i < 16; i += 5) {
603 sm[smOffset + i] = 1.0f;
604 }
605 }
606
607 /**
608 * Scales matrix m by x, y, and z, putting the result in sm.
609 * <p>
610 * m and sm must not overlap.
611 *
612 * @param sm returns the result
613 * @param smOffset index into sm where the result matrix starts
614 * @param m source matrix
615 * @param mOffset index into m where the source matrix starts
616 * @param x scale factor x
617 * @param y scale factor y
618 * @param z scale factor z
619 */
620 public static void scaleM(float[] sm, int smOffset,
621 float[] m, int mOffset,
622 float x, float y, float z) {
623 for (int i=0 ; i<4 ; i++) {
624 int smi = smOffset + i;
625 int mi = mOffset + i;
626 sm[ smi] = m[ mi] * x;
627 sm[ 4 + smi] = m[ 4 + mi] * y;
628 sm[ 8 + smi] = m[ 8 + mi] * z;
629 sm[12 + smi] = m[12 + mi];
630 }
631 }
632
633 /**
634 * Scales matrix m in place by sx, sy, and sz.
635 *
636 * @param m matrix to scale
637 * @param mOffset index into m where the matrix starts
638 * @param x scale factor x
639 * @param y scale factor y
640 * @param z scale factor z
641 */
642 public static void scaleM(float[] m, int mOffset,
643 float x, float y, float z) {
644 for (int i=0 ; i<4 ; i++) {
645 int mi = mOffset + i;
646 m[ mi] *= x;
647 m[ 4 + mi] *= y;
648 m[ 8 + mi] *= z;
649 }
650 }
651
652 /**
653 * Translates matrix m by x, y, and z, putting the result in tm.
654 * <p>
655 * m and tm must not overlap.
656 *
657 * @param tm returns the result
658 * @param tmOffset index into sm where the result matrix starts
659 * @param m source matrix
660 * @param mOffset index into m where the source matrix starts
661 * @param x translation factor x
662 * @param y translation factor y
663 * @param z translation factor z
664 */
665 public static void translateM(float[] tm, int tmOffset,
666 float[] m, int mOffset,
667 float x, float y, float z) {
668 for (int i=0 ; i<12 ; i++) {
669 tm[tmOffset + i] = m[mOffset + i];
670 }
671 for (int i=0 ; i<4 ; i++) {
672 int tmi = tmOffset + i;
673 int mi = mOffset + i;
674 tm[12 + tmi] = m[mi] * x + m[4 + mi] * y + m[8 + mi] * z +
675 m[12 + mi];
676 }
677 }
678
679 /**
680 * Translates matrix m by x, y, and z in place.
681 *
682 * @param m matrix
683 * @param mOffset index into m where the matrix starts
684 * @param x translation factor x
685 * @param y translation factor y
686 * @param z translation factor z
687 */
688 public static void translateM(
689 float[] m, int mOffset,
690 float x, float y, float z) {
691 for (int i=0 ; i<4 ; i++) {
692 int mi = mOffset + i;
693 m[12 + mi] += m[mi] * x + m[4 + mi] * y + m[8 + mi] * z;
694 }
695 }
696
697 /**
698 * Rotates matrix m by angle a (in degrees) around the axis (x, y, z).
699 * <p>
700 * m and rm must not overlap.
701 *
702 * @param rm returns the result
703 * @param rmOffset index into rm where the result matrix starts
704 * @param m source matrix
705 * @param mOffset index into m where the source matrix starts
706 * @param a angle to rotate in degrees
707 * @param x X axis component
708 * @param y Y axis component
709 * @param z Z axis component
710 */
711 public static void rotateM(float[] rm, int rmOffset,
712 float[] m, int mOffset,
713 float a, float x, float y, float z) {
714 float[] tmp = ThreadTmp.get();
715 setRotateM(tmp, 16, a, x, y, z);
716 multiplyMM(rm, rmOffset, m, mOffset, tmp, 16);
717 }
718
719 /**
720 * Rotates matrix m in place by angle a (in degrees)
721 * around the axis (x, y, z).
722 *
723 * @param m source matrix
724 * @param mOffset index into m where the matrix starts
725 * @param a angle to rotate in degrees
726 * @param x X axis component
727 * @param y Y axis component
728 * @param z Z axis component
729 */
730 public static void rotateM(float[] m, int mOffset,
731 float a, float x, float y, float z) {
732 rotateM(m, mOffset, m, mOffset, a, x, y, z);
733 }
734
735 /**
736 * Creates a matrix for rotation by angle a (in degrees)
737 * around the axis (x, y, z).
738 * <p>
739 * An optimized path will be used for rotation about a major axis
740 * (e.g. x=1.0f y=0.0f z=0.0f).
741 *
742 * @param rm returns the result
743 * @param rmOffset index into rm where the result matrix starts
744 * @param a angle to rotate in degrees
745 * @param x X axis component
746 * @param y Y axis component
747 * @param z Z axis component
748 */
749 public static void setRotateM(float[] rm, int rmOffset,
750 float a, float x, float y, float z) {
751 rm[rmOffset + 3] = 0;
752 rm[rmOffset + 7] = 0;
753 rm[rmOffset + 11]= 0;
754 rm[rmOffset + 12]= 0;
755 rm[rmOffset + 13]= 0;
756 rm[rmOffset + 14]= 0;
757 rm[rmOffset + 15]= 1;
758 a *= (float) (Math.PI / 180.0f);
759 float s = (float) Math.sin(a);
760 float c = (float) Math.cos(a);
761 if (1.0f == x && 0.0f == y && 0.0f == z) {
762 rm[rmOffset + 5] = c; rm[rmOffset + 10]= c;
763 rm[rmOffset + 6] = s; rm[rmOffset + 9] = -s;
764 rm[rmOffset + 1] = 0; rm[rmOffset + 2] = 0;
765 rm[rmOffset + 4] = 0; rm[rmOffset + 8] = 0;
766 rm[rmOffset + 0] = 1;
767 } else if (0.0f == x && 1.0f == y && 0.0f == z) {
768 rm[rmOffset + 0] = c; rm[rmOffset + 10]= c;
769 rm[rmOffset + 8] = s; rm[rmOffset + 2] = -s;
770 rm[rmOffset + 1] = 0; rm[rmOffset + 4] = 0;
771 rm[rmOffset + 6] = 0; rm[rmOffset + 9] = 0;
772 rm[rmOffset + 5] = 1;
773 } else if (0.0f == x && 0.0f == y && 1.0f == z) {
774 rm[rmOffset + 0] = c; rm[rmOffset + 5] = c;
775 rm[rmOffset + 1] = s; rm[rmOffset + 4] = -s;
776 rm[rmOffset + 2] = 0; rm[rmOffset + 6] = 0;
777 rm[rmOffset + 8] = 0; rm[rmOffset + 9] = 0;
778 rm[rmOffset + 10]= 1;
779 } else {
780 float len = length(x, y, z);
781 if (1.0f != len) {
782 float recipLen = 1.0f / len;
783 x *= recipLen;
784 y *= recipLen;
785 z *= recipLen;
786 }
787 float nc = 1.0f - c;
788 float xy = x * y;
789 float yz = y * z;
790 float zx = z * x;
791 float xs = x * s;
792 float ys = y * s;
793 float zs = z * s;
794 rm[rmOffset + 0] = x*x*nc + c;
795 rm[rmOffset + 4] = xy*nc - zs;
796 rm[rmOffset + 8] = zx*nc + ys;
797 rm[rmOffset + 1] = xy*nc + zs;
798 rm[rmOffset + 5] = y*y*nc + c;
799 rm[rmOffset + 9] = yz*nc - xs;
800 rm[rmOffset + 2] = zx*nc - ys;
801 rm[rmOffset + 6] = yz*nc + xs;
802 rm[rmOffset + 10] = z*z*nc + c;
803 }
804 }
805
806 /**
807 * Converts Euler angles to a rotation matrix.
808 *
809 * @param rm returns the result
810 * @param rmOffset index into rm where the result matrix starts
811 * @param x angle of rotation, in degrees
812 * @param y is broken, do not use
813 * @param z angle of rotation, in degrees
814 *
815 * @deprecated This method is incorrect around the y axis. This method is
816 * deprecated and replaced (below) by setRotateEulerM2 which
817 * behaves correctly
818 */
819 @Deprecated
820 public static void setRotateEulerM(float[] rm, int rmOffset,
821 float x, float y, float z) {
822 x *= (float) (Math.PI / 180.0f);
823 y *= (float) (Math.PI / 180.0f);
824 z *= (float) (Math.PI / 180.0f);
825 float cx = (float) Math.cos(x);
826 float sx = (float) Math.sin(x);
827 float cy = (float) Math.cos(y);
828 float sy = (float) Math.sin(y);
829 float cz = (float) Math.cos(z);
830 float sz = (float) Math.sin(z);
831 float cxsy = cx * sy;
832 float sxsy = sx * sy;
833
834 rm[rmOffset + 0] = cy * cz;
835 rm[rmOffset + 1] = -cy * sz;
836 rm[rmOffset + 2] = sy;
837 rm[rmOffset + 3] = 0.0f;
838
839 rm[rmOffset + 4] = cxsy * cz + cx * sz;
840 rm[rmOffset + 5] = -cxsy * sz + cx * cz;
841 rm[rmOffset + 6] = -sx * cy;
842 rm[rmOffset + 7] = 0.0f;
843
844 rm[rmOffset + 8] = -sxsy * cz + sx * sz;
845 rm[rmOffset + 9] = sxsy * sz + sx * cz;
846 rm[rmOffset + 10] = cx * cy;
847 rm[rmOffset + 11] = 0.0f;
848
849 rm[rmOffset + 12] = 0.0f;
850 rm[rmOffset + 13] = 0.0f;
851 rm[rmOffset + 14] = 0.0f;
852 rm[rmOffset + 15] = 1.0f;
853 }
854
855 /**
856 * Converts Euler angles to a rotation matrix.
857 *
858 * @param rm returns the result
859 * @param rmOffset index into rm where the result matrix starts
860 * @param x angle of rotation, in degrees
861 * @param y angle of rotation, in degrees
862 * @param z angle of rotation, in degrees
863 *
864 * @throws IllegalArgumentException if rm is null;
865 * or if rmOffset + 16 > rm.length;
866 * rmOffset < 0
867 */
868 public static void setRotateEulerM2(@NonNull float[] rm, int rmOffset,
869 float x, float y, float z) {
870 if (rm == null) {
871 throw new IllegalArgumentException("rm == null");
872 }
873 if (rmOffset < 0) {
874 throw new IllegalArgumentException("rmOffset < 0");
875 }
876 if (rm.length < rmOffset + 16) {
877 throw new IllegalArgumentException("rm.length < rmOffset + 16");
878 }
879
880 x *= (float) (Math.PI / 180.0f);
881 y *= (float) (Math.PI / 180.0f);
882 z *= (float) (Math.PI / 180.0f);
883 float cx = (float) Math.cos(x);
884 float sx = (float) Math.sin(x);
885 float cy = (float) Math.cos(y);
886 float sy = (float) Math.sin(y);
887 float cz = (float) Math.cos(z);
888 float sz = (float) Math.sin(z);
889 float cxsy = cx * sy;
890 float sxsy = sx * sy;
891
892 rm[rmOffset + 0] = cy * cz;
893 rm[rmOffset + 1] = -cy * sz;
894 rm[rmOffset + 2] = sy;
895 rm[rmOffset + 3] = 0.0f;
896
897 rm[rmOffset + 4] = sxsy * cz + cx * sz;
898 rm[rmOffset + 5] = -sxsy * sz + cx * cz;
899 rm[rmOffset + 6] = -sx * cy;
900 rm[rmOffset + 7] = 0.0f;
901
902 rm[rmOffset + 8] = -cxsy * cz + sx * sz;
903 rm[rmOffset + 9] = cxsy * sz + sx * cz;
904 rm[rmOffset + 10] = cx * cy;
905 rm[rmOffset + 11] = 0.0f;
906
907 rm[rmOffset + 12] = 0.0f;
908 rm[rmOffset + 13] = 0.0f;
909 rm[rmOffset + 14] = 0.0f;
910 rm[rmOffset + 15] = 1.0f;
911 }
912
913 /**
914 * Defines a viewing transformation in terms of an eye point, a center of
915 * view, and an up vector.
916 *
917 * @param rm returns the result
918 * @param rmOffset index into rm where the result matrix starts
919 * @param eyeX eye point X
920 * @param eyeY eye point Y
921 * @param eyeZ eye point Z
922 * @param centerX center of view X
923 * @param centerY center of view Y
924 * @param centerZ center of view Z
925 * @param upX up vector X
926 * @param upY up vector Y
927 * @param upZ up vector Z
928 */
929 public static void setLookAtM(float[] rm, int rmOffset,
930 float eyeX, float eyeY, float eyeZ,
931 float centerX, float centerY, float centerZ, float upX, float upY,
932 float upZ) {
933
934 // See the OpenGL GLUT documentation for gluLookAt for a description
935 // of the algorithm. We implement it in a straightforward way:
936
937 float fx = centerX - eyeX;
938 float fy = centerY - eyeY;
939 float fz = centerZ - eyeZ;
940
941 // Normalize f
942 float rlf = 1.0f / Matrix.length(fx, fy, fz);
943 fx *= rlf;
944 fy *= rlf;
945 fz *= rlf;
946
947 // compute s = f x up (x means "cross product")
948 float sx = fy * upZ - fz * upY;
949 float sy = fz * upX - fx * upZ;
950 float sz = fx * upY - fy * upX;
951
952 // and normalize s
953 float rls = 1.0f / Matrix.length(sx, sy, sz);
954 sx *= rls;
955 sy *= rls;
956 sz *= rls;
957
958 // compute u = s x f
959 float ux = sy * fz - sz * fy;
960 float uy = sz * fx - sx * fz;
961 float uz = sx * fy - sy * fx;
962
963 rm[rmOffset + 0] = sx;
964 rm[rmOffset + 1] = ux;
965 rm[rmOffset + 2] = -fx;
966 rm[rmOffset + 3] = 0.0f;
967
968 rm[rmOffset + 4] = sy;
969 rm[rmOffset + 5] = uy;
970 rm[rmOffset + 6] = -fy;
971 rm[rmOffset + 7] = 0.0f;
972
973 rm[rmOffset + 8] = sz;
974 rm[rmOffset + 9] = uz;
975 rm[rmOffset + 10] = -fz;
976 rm[rmOffset + 11] = 0.0f;
977
978 rm[rmOffset + 12] = 0.0f;
979 rm[rmOffset + 13] = 0.0f;
980 rm[rmOffset + 14] = 0.0f;
981 rm[rmOffset + 15] = 1.0f;
982
983 translateM(rm, rmOffset, -eyeX, -eyeY, -eyeZ);
984 }
985}