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