/* * Copyright (c) VMware, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include "piglit-matrix.h" #define DEG_TO_RAD(D) ((D) * M_PI / 180.0) /** * Create a scaling matrix. */ void piglit_identity_matrix(float mat[16]) { mat[0] = 1.0f; mat[1] = 0.0f; mat[2] = 0.0f; mat[3] = 0.0f; mat[4] = 0.0f; mat[5] = 1.0f; mat[6] = 0.0f; mat[7] = 0.0f; mat[8] = 0.0f; mat[9] = 0.0f; mat[10] = 1.0f; mat[11] = 0.0f; mat[12] = 0.0f; mat[13] = 0.0f; mat[14] = 0.0f; mat[15] = 1.0f; } /** * Create a scaling matrix. */ void piglit_scale_matrix(float mat[16], float sx, float sy, float sz) { mat[0] = sx; mat[1] = 0.0f; mat[2] = 0.0f; mat[3] = 0.0f; mat[4] = 0.0f; mat[5] = sy; mat[6] = 0.0f; mat[7] = 0.0f; mat[8] = 0.0f; mat[9] = 0.0f; mat[10] = sz; mat[11] = 0.0f; mat[12] = 0.0f; mat[13] = 0.0f; mat[14] = 0.0f; mat[15] = 1.0f; } /** * Create a translation matrix. */ void piglit_translation_matrix(float mat[16], float tx, float ty, float tz) { mat[0] = 0.0f; mat[1] = 0.0f; mat[2] = 0.0f; mat[3] = 0.0f; mat[4] = 0.0f; mat[5] = 0.0f; mat[6] = 0.0f; mat[7] = 0.0f; mat[8] = 0.0f; mat[9] = 0.0f; mat[10] = 0.0f; mat[11] = 0.0f; mat[12] = tx; mat[13] = ty; mat[14] = tz; mat[15] = 1.0f; } void piglit_rotation_matrix(float mat[16], float angle, float x, float y, float z) { /* Implementation borrowed from Mesa */ float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c, s, c; float m[16]; bool optimized = false; s = (float) sin(DEG_TO_RAD(angle)); c = (float) cos(DEG_TO_RAD(angle)); piglit_identity_matrix(m); #define M(row,col) m[col*4+row] if (x == 0.0F) { if (y == 0.0F) { if (z != 0.0F) { optimized = true; /* rotate only around z-axis */ M(0,0) = c; M(1,1) = c; if (z < 0.0F) { M(0,1) = s; M(1,0) = -s; } else { M(0,1) = -s; M(1,0) = s; } } } else if (z == 0.0F) { optimized = true; /* rotate only around y-axis */ M(0,0) = c; M(2,2) = c; if (y < 0.0F) { M(0,2) = -s; M(2,0) = s; } else { M(0,2) = s; M(2,0) = -s; } } } else if (y == 0.0F) { if (z == 0.0F) { optimized = true; /* rotate only around x-axis */ M(1,1) = c; M(2,2) = c; if (x < 0.0F) { M(1,2) = s; M(2,1) = -s; } else { M(1,2) = -s; M(2,1) = s; } } } if (!optimized) { const float mag = sqrtf(x * x + y * y + z * z); if (mag <= 1.0e-4) { /* no rotation, leave mat as-is */ return; } x /= mag; y /= mag; z /= mag; /* * Arbitrary axis rotation matrix. * * This is composed of 5 matrices, Rz, Ry, T, Ry', Rz', multiplied * like so: Rz * Ry * T * Ry' * Rz'. T is the final rotation * (which is about the X-axis), and the two composite transforms * Ry' * Rz' and Rz * Ry are (respectively) the rotations necessary * from the arbitrary axis to the X-axis then back. They are * all elementary rotations. * * Rz' is a rotation about the Z-axis, to bring the axis vector * into the x-z plane. Then Ry' is applied, rotating about the * Y-axis to bring the axis vector parallel with the X-axis. The * rotation about the X-axis is then performed. Ry and Rz are * simply the respective inverse transforms to bring the arbitrary * axis back to its original orientation. The first transforms * Rz' and Ry' are considered inverses, since the data from the * arbitrary axis gives you info on how to get to it, not how * to get away from it, and an inverse must be applied. * * The basic calculation used is to recognize that the arbitrary * axis vector (x, y, z), since it is of unit length, actually * represents the sines and cosines of the angles to rotate the * X-axis to the same orientation, with theta being the angle about * Z and phi the angle about Y (in the order described above) * as follows: * * cos ( theta ) = x / sqrt ( 1 - z^2 ) * sin ( theta ) = y / sqrt ( 1 - z^2 ) * * cos ( phi ) = sqrt ( 1 - z^2 ) * sin ( phi ) = z * * Note that cos ( phi ) can further be inserted to the above * formulas: * * cos ( theta ) = x / cos ( phi ) * sin ( theta ) = y / sin ( phi ) * * ...etc. Because of those relations and the standard trigonometric * relations, it is pssible to reduce the transforms down to what * is used below. It may be that any primary axis chosen will give the * same results (modulo a sign convention) using thie method. * * Particularly nice is to notice that all divisions that might * have caused trouble when parallel to certain planes or * axis go away with care paid to reducing the expressions. * After checking, it does perform correctly under all cases, since * in all the cases of division where the denominator would have * been zero, the numerator would have been zero as well, giving * the expected result. */ xx = x * x; yy = y * y; zz = z * z; xy = x * y; yz = y * z; zx = z * x; xs = x * s; ys = y * s; zs = z * s; one_c = 1.0F - c; /* We already hold the identity-matrix so we can skip some statements */ M(0,0) = (one_c * xx) + c; M(0,1) = (one_c * xy) - zs; M(0,2) = (one_c * zx) + ys; /* M(0,3) = 0.0F; */ M(1,0) = (one_c * xy) + zs; M(1,1) = (one_c * yy) + c; M(1,2) = (one_c * yz) - xs; /* M(1,3) = 0.0F; */ M(2,0) = (one_c * zx) - ys; M(2,1) = (one_c * yz) + xs; M(2,2) = (one_c * zz) + c; /* M(2,3) = 0.0F; */ /* M(3,0) = 0.0F; M(3,1) = 0.0F; M(3,2) = 0.0F; M(3,3) = 1.0F; */ } #undef M } void piglit_ortho_matrix(float mat[16], float left, float right, float bottom, float top, float nearval, float farval) { #define M(row,col) mat[col*4+row] M(0,0) = 2.0F / (right-left); M(0,1) = 0.0F; M(0,2) = 0.0F; M(0,3) = -(right+left) / (right-left); M(1,0) = 0.0F; M(1,1) = 2.0F / (top-bottom); M(1,2) = 0.0F; M(1,3) = -(top+bottom) / (top-bottom); M(2,0) = 0.0F; M(2,1) = 0.0F; M(2,2) = -2.0F / (farval-nearval); M(2,3) = -(farval+nearval) / (farval-nearval); M(3,0) = 0.0F; M(3,1) = 0.0F; M(3,2) = 0.0F; M(3,3) = 1.0F; #undef M } void piglit_frustum_matrix(float mat[16], float left, float right, float bottom, float top, float nearval, float farval) { float x, y, a, b, c, d; x = (2.0F*nearval) / (right-left); y = (2.0F*nearval) / (top-bottom); a = (right+left) / (right-left); b = (top+bottom) / (top-bottom); c = -(farval+nearval) / ( farval-nearval); d = -(2.0F*farval*nearval) / (farval-nearval); /* error? */ #define M(row,col) mat[col*4+row] M(0,0) = x; M(0,1) = 0.0F; M(0,2) = a; M(0,3) = 0.0F; M(1,0) = 0.0F; M(1,1) = y; M(1,2) = b; M(1,3) = 0.0F; M(2,0) = 0.0F; M(2,1) = 0.0F; M(2,2) = c; M(2,3) = d; M(3,0) = 0.0F; M(3,1) = 0.0F; M(3,2) = -1.0F; M(3,3) = 0.0F; #undef M } void piglit_matrix_mul_matrix(float product[16], const float a[16], const float b[16]) { #define ELEM(MAT, ROW, COL) MAT[(COL) * 4 + (ROW)] float tmp[16]; int i, j; for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { ELEM(tmp, i, j) = (ELEM(a, i, 0) * ELEM(b, 0, j) + ELEM(a, i, 1) * ELEM(b, 1, j) + ELEM(a, i, 2) * ELEM(b, 2, j) + ELEM(a, i, 3) * ELEM(b, 3, j)); } } for (i = 0; i < 16; i++) { product[i] = tmp[i]; } #undef ELEM } /** * Compute "out = mat * in" where in and out are column vectors * Typically used to transform homogeneous coordinates by a matrix. */ void piglit_matrix_mul_vector(float out[4], const float mat[16], const float in[4]) { const float in0 = in[0], in1 = in[1], in2 = in[2], in3 = in[3]; #define M(row,col) mat[row + col*4] out[0] = M(0,0) * in0 + M(0,1) * in1 + M(0,2) * in2 + M(0,3) * in3; out[1] = M(1,0) * in0 + M(1,1) * in1 + M(1,2) * in2 + M(1,3) * in3; out[2] = M(2,0) * in0 + M(2,1) * in1 + M(2,2) * in2 + M(2,3) * in3; out[3] = M(3,0) * in0 + M(3,1) * in1 + M(3,2) * in2 + M(3,3) * in3; #undef M } /** * Transfrom NDC coordinate to window coordinate using a viewport. */ void piglit_ndc_to_window(float win[3], const float ndc[4], int vp_left, int vp_bottom, int vp_width, int vp_height) { float x = ndc[0] * 0.5 + 0.5; float y = ndc[1] * 0.5 + 0.5; float z = ndc[2] * 0.5 + 0.5; win[0] = vp_left + x * vp_width; win[1] = vp_bottom + y * vp_height; win[2] = z; } /** * Transform an object coordinate to a window coordinate using a * modelview matrix, projection matrix and viewport. * \return true for success, false if coordinate is clipped away */ bool piglit_project_to_window(float win[3], const float obj[4], const float modelview[16], const float projection[16], int vp_left, int vp_bottom, int vp_width, int vp_height) { float eye[4], clip[4], ndc[4]; /* eye coord = modelview * object */ piglit_matrix_mul_vector(eye, modelview, obj); /* clip coord = projection * eye */ piglit_matrix_mul_vector(clip, projection, eye); /* view volume clipping */ if ( clip[0] > clip[3] || -clip[0] > clip[3] || clip[1] > clip[3] || -clip[1] > clip[3] || clip[2] > clip[3] || -clip[2] > clip[3]) { /* clipped */ return false; } /* ndc = clip / clip.w (divide by w) */ ndc[0] = clip[0] / clip[3]; ndc[1] = clip[1] / clip[3]; ndc[2] = clip[2] / clip[3]; ndc[3] = clip[3]; /* window = viewport_map(ndc) */ piglit_ndc_to_window(win, ndc, vp_left, vp_bottom, vp_width, vp_height); return true; } void piglit_print_matrix(const float mat[16]) { printf("%f %f %f %f\n", mat[0], mat[4], mat[8], mat[12]); printf("%f %f %f %f\n", mat[1], mat[5], mat[9], mat[13]); printf("%f %f %f %f\n", mat[2], mat[6], mat[10], mat[14]); printf("%f %f %f %f\n", mat[3], mat[7], mat[11], mat[15]); } /** * Invert matrix using cramer's rule. * This assumes that the matrix is non-singular (or non-near-singular). */ void piglit_matrix_inverse(float inv[16], const float m[16]) { inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]; inv[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]; inv[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]; inv[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]; inv[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]; inv[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]; inv[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]; inv[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]; inv[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6]; inv[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6]; inv[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5]; inv[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5]; inv[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6]; inv[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6]; inv[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5]; inv[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]; float det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]; assert(fabsf(det) > 1e-10f); for (int i = 0; i < 16; i++) inv[i] = inv[i] / det; } void piglit_matrix_transpose(float out[16], const float m[16]) { for (int i = 0; i < 4; ++i) for (int j = 0; j < 4; ++j) out[i+4*j] = m[4*i+j]; } /* * XXX to do items: * We could add a simple set of matrix stack functions. * Could add scale/translate/rotate functions that accumulate onto * the incoming matrix, similar to glScale, glTranslate, etc. */