| /* libs/opengles/matrix.cpp |
| ** |
| ** Copyright 2006, The Android Open Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| |
| #include "context.h" |
| #include "fp.h" |
| #include "state.h" |
| #include "matrix.h" |
| #include "vertex.h" |
| #include "light.h" |
| |
| #if defined(__arm__) && defined(__thumb__) |
| #warning "matrix.cpp should not be compiled in thumb on ARM." |
| #endif |
| |
| #define I(_i, _j) ((_j)+ 4*(_i)) |
| |
| namespace android { |
| |
| // ---------------------------------------------------------------------------- |
| |
| static const GLfloat gIdentityf[16] = { 1,0,0,0, |
| 0,1,0,0, |
| 0,0,1,0, |
| 0,0,0,1 }; |
| |
| static const matrixx_t gIdentityx = { |
| { 0x10000,0,0,0, |
| 0,0x10000,0,0, |
| 0,0,0x10000,0, |
| 0,0,0,0x10000 |
| } |
| }; |
| |
| static void point2__nop(transform_t const*, vec4_t* c, vec4_t const* o); |
| static void point3__nop(transform_t const*, vec4_t* c, vec4_t const* o); |
| static void point4__nop(transform_t const*, vec4_t* c, vec4_t const* o); |
| static void normal__nop(transform_t const*, vec4_t* c, vec4_t const* o); |
| static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o); |
| static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o); |
| static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o); |
| static void normal__generic(transform_t const*, vec4_t* c, vec4_t const* o); |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #endif |
| |
| void ogles_init_matrix(ogles_context_t* c) |
| { |
| c->transforms.modelview.init(OGLES_MODELVIEW_STACK_DEPTH); |
| c->transforms.projection.init(OGLES_PROJECTION_STACK_DEPTH); |
| for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) |
| c->transforms.texture[i].init(OGLES_TEXTURE_STACK_DEPTH); |
| |
| c->transforms.current = &c->transforms.modelview; |
| c->transforms.matrixMode = GL_MODELVIEW; |
| c->transforms.dirty = transform_state_t::VIEWPORT | |
| transform_state_t::MVUI | |
| transform_state_t::MVIT | |
| transform_state_t::MVP; |
| c->transforms.mvp.loadIdentity(); |
| c->transforms.mvp4.loadIdentity(); |
| c->transforms.mvit4.loadIdentity(); |
| c->transforms.mvui.loadIdentity(); |
| c->transforms.vpt.loadIdentity(); |
| c->transforms.vpt.zNear = 0.0f; |
| c->transforms.vpt.zFar = 1.0f; |
| } |
| |
| void ogles_uninit_matrix(ogles_context_t* c) |
| { |
| c->transforms.modelview.uninit(); |
| c->transforms.projection.uninit(); |
| for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) |
| c->transforms.texture[i].uninit(); |
| } |
| |
| static void validate_perspective(ogles_context_t* c, vertex_t* v) |
| { |
| const uint32_t enables = c->rasterizer.state.enables; |
| c->arrays.perspective = (c->clipPlanes.enable) ? |
| ogles_vertex_clipAllPerspective3D : ogles_vertex_perspective3D; |
| if (enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) { |
| c->arrays.perspective = ogles_vertex_perspective3DZ; |
| if (c->clipPlanes.enable || (enables&GGL_ENABLE_FOG)) |
| c->arrays.perspective = ogles_vertex_clipAllPerspective3DZ; |
| } |
| if ((c->arrays.vertex.size != 4) && |
| (c->transforms.mvp4.flags & transform_t::FLAGS_2D_PROJECTION)) { |
| c->arrays.perspective = ogles_vertex_perspective2D; |
| } |
| c->arrays.perspective(c, v); |
| } |
| |
| void ogles_invalidate_perspective(ogles_context_t* c) |
| { |
| c->arrays.perspective = validate_perspective; |
| } |
| |
| void ogles_validate_transform_impl(ogles_context_t* c, uint32_t want) |
| { |
| int dirty = c->transforms.dirty & want; |
| |
| // Validate the modelview |
| if (dirty & transform_state_t::MODELVIEW) { |
| c->transforms.modelview.validate(); |
| } |
| |
| // Validate the projection stack (in fact, it's never needed) |
| if (dirty & transform_state_t::PROJECTION) { |
| c->transforms.projection.validate(); |
| } |
| |
| // Validate the viewport transformation |
| if (dirty & transform_state_t::VIEWPORT) { |
| vp_transform_t& vpt = c->transforms.vpt; |
| vpt.transform.matrix.load(vpt.matrix); |
| vpt.transform.picker(); |
| } |
| |
| // We need to update the mvp (used to transform each vertex) |
| if (dirty & transform_state_t::MVP) { |
| c->transforms.update_mvp(); |
| // invalidate perspective (divide by W) and view volume clipping |
| ogles_invalidate_perspective(c); |
| } |
| |
| // Validate the mvui (for normal transformation) |
| if (dirty & transform_state_t::MVUI) { |
| c->transforms.update_mvui(); |
| ogles_invalidate_lighting_mvui(c); |
| } |
| |
| // Validate the texture stack |
| if (dirty & transform_state_t::TEXTURE) { |
| for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) |
| c->transforms.texture[i].validate(); |
| } |
| |
| // Validate the mvit4 (user-clip planes) |
| if (dirty & transform_state_t::MVIT) { |
| c->transforms.update_mvit(); |
| } |
| |
| c->transforms.dirty &= ~want; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark transform_t |
| #endif |
| |
| void transform_t::loadIdentity() { |
| matrix = gIdentityx; |
| flags = 0; |
| ops = OP_IDENTITY; |
| point2 = point2__nop; |
| point3 = point3__nop; |
| point4 = point4__nop; |
| } |
| |
| |
| static inline |
| int notZero(GLfixed v) { |
| return abs(v) & ~0x3; |
| } |
| |
| static inline |
| int notOne(GLfixed v) { |
| return notZero(v - 0x10000); |
| } |
| |
| void transform_t::picker() |
| { |
| const GLfixed* const m = matrix.m; |
| |
| // XXX: picker needs to be smarter |
| flags = 0; |
| ops = OP_ALL; |
| point2 = point2__generic; |
| point3 = point3__generic; |
| point4 = point4__generic; |
| |
| // find out if this is a 2D projection |
| if (!(notZero(m[3]) | notZero(m[7]) | notZero(m[11]) | notOne(m[15]))) { |
| flags |= FLAGS_2D_PROJECTION; |
| } |
| } |
| |
| void mvui_transform_t::picker() |
| { |
| flags = 0; |
| ops = OP_ALL; |
| point3 = normal__generic; |
| } |
| |
| void transform_t::dump(const char* what) |
| { |
| GLfixed const * const m = matrix.m; |
| LOGD("%s:", what); |
| for (int i=0 ; i<4 ; i++) |
| LOGD("[%08x %08x %08x %08x] [%f %f %f %f]\n", |
| m[I(0,i)], m[I(1,i)], m[I(2,i)], m[I(3,i)], |
| fixedToFloat(m[I(0,i)]), |
| fixedToFloat(m[I(1,i)]), |
| fixedToFloat(m[I(2,i)]), |
| fixedToFloat(m[I(3,i)])); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark matrixx_t |
| #endif |
| |
| void matrixx_t::load(const matrixf_t& rhs) { |
| GLfixed* xp = m; |
| GLfloat const* fp = rhs.elements(); |
| unsigned int i = 16; |
| do { |
| const GLfloat f = *fp++; |
| *xp++ = isZerof(f) ? 0 : gglFloatToFixed(f); |
| } while (--i); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark matrixf_t |
| #endif |
| |
| void matrixf_t::multiply(matrixf_t& r, const matrixf_t& lhs, const matrixf_t& rhs) |
| { |
| GLfloat const* const m = lhs.m; |
| for (int i=0 ; i<4 ; i++) { |
| register const float rhs_i0 = rhs.m[ I(i,0) ]; |
| register float ri0 = m[ I(0,0) ] * rhs_i0; |
| register float ri1 = m[ I(0,1) ] * rhs_i0; |
| register float ri2 = m[ I(0,2) ] * rhs_i0; |
| register float ri3 = m[ I(0,3) ] * rhs_i0; |
| for (int j=1 ; j<4 ; j++) { |
| register const float rhs_ij = rhs.m[ I(i,j) ]; |
| ri0 += m[ I(j,0) ] * rhs_ij; |
| ri1 += m[ I(j,1) ] * rhs_ij; |
| ri2 += m[ I(j,2) ] * rhs_ij; |
| ri3 += m[ I(j,3) ] * rhs_ij; |
| } |
| r.m[ I(i,0) ] = ri0; |
| r.m[ I(i,1) ] = ri1; |
| r.m[ I(i,2) ] = ri2; |
| r.m[ I(i,3) ] = ri3; |
| } |
| } |
| |
| void matrixf_t::dump(const char* what) { |
| LOGD("%s", what); |
| LOGD("[ %9f %9f %9f %9f ]", m[I(0,0)], m[I(1,0)], m[I(2,0)], m[I(3,0)]); |
| LOGD("[ %9f %9f %9f %9f ]", m[I(0,1)], m[I(1,1)], m[I(2,1)], m[I(3,1)]); |
| LOGD("[ %9f %9f %9f %9f ]", m[I(0,2)], m[I(1,2)], m[I(2,2)], m[I(3,2)]); |
| LOGD("[ %9f %9f %9f %9f ]", m[I(0,3)], m[I(1,3)], m[I(2,3)], m[I(3,3)]); |
| } |
| |
| void matrixf_t::loadIdentity() { |
| memcpy(m, gIdentityf, sizeof(m)); |
| } |
| |
| void matrixf_t::set(const GLfixed* rhs) { |
| load(rhs); |
| } |
| |
| void matrixf_t::set(const GLfloat* rhs) { |
| load(rhs); |
| } |
| |
| void matrixf_t::load(const GLfixed* rhs) { |
| GLfloat* fp = m; |
| unsigned int i = 16; |
| do { |
| *fp++ = fixedToFloat(*rhs++); |
| } while (--i); |
| } |
| |
| void matrixf_t::load(const GLfloat* rhs) { |
| memcpy(m, rhs, sizeof(m)); |
| } |
| |
| void matrixf_t::load(const matrixf_t& rhs) { |
| operator = (rhs); |
| } |
| |
| void matrixf_t::multiply(const matrixf_t& rhs) { |
| matrixf_t r; |
| multiply(r, *this, rhs); |
| operator = (r); |
| } |
| |
| void matrixf_t::translate(GLfloat x, GLfloat y, GLfloat z) { |
| for (int i=0 ; i<4 ; i++) { |
| m[12+i] += m[i]*x + m[4+i]*y + m[8+i]*z; |
| } |
| } |
| |
| void matrixf_t::scale(GLfloat x, GLfloat y, GLfloat z) { |
| for (int i=0 ; i<4 ; i++) { |
| m[ i] *= x; |
| m[4+i] *= y; |
| m[8+i] *= z; |
| } |
| } |
| |
| void matrixf_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z) |
| { |
| matrixf_t rotation; |
| GLfloat* r = rotation.m; |
| GLfloat c, s; |
| r[3] = 0; r[7] = 0; r[11]= 0; |
| r[12]= 0; r[13]= 0; r[14]= 0; r[15]= 1; |
| a *= GLfloat(M_PI / 180.0f); |
| sincosf(a, &s, &c); |
| if (isOnef(x) && isZerof(y) && isZerof(z)) { |
| r[5] = c; r[10]= c; |
| r[6] = s; r[9] = -s; |
| r[1] = 0; r[2] = 0; |
| r[4] = 0; r[8] = 0; |
| r[0] = 1; |
| } else if (isZerof(x) && isOnef(y) && isZerof(z)) { |
| r[0] = c; r[10]= c; |
| r[8] = s; r[2] = -s; |
| r[1] = 0; r[4] = 0; |
| r[6] = 0; r[9] = 0; |
| r[5] = 1; |
| } else if (isZerof(x) && isZerof(y) && isOnef(z)) { |
| r[0] = c; r[5] = c; |
| r[1] = s; r[4] = -s; |
| r[2] = 0; r[6] = 0; |
| r[8] = 0; r[9] = 0; |
| r[10]= 1; |
| } else { |
| const GLfloat len = sqrtf(x*x + y*y + z*z); |
| if (!isOnef(len)) { |
| const GLfloat recipLen = reciprocalf(len); |
| x *= recipLen; |
| y *= recipLen; |
| z *= recipLen; |
| } |
| const GLfloat nc = 1.0f - c; |
| const GLfloat xy = x * y; |
| const GLfloat yz = y * z; |
| const GLfloat zx = z * x; |
| const GLfloat xs = x * s; |
| const GLfloat ys = y * s; |
| const GLfloat zs = z * s; |
| r[ 0] = x*x*nc + c; r[ 4] = xy*nc - zs; r[ 8] = zx*nc + ys; |
| r[ 1] = xy*nc + zs; r[ 5] = y*y*nc + c; r[ 9] = yz*nc - xs; |
| r[ 2] = zx*nc - ys; r[ 6] = yz*nc + xs; r[10] = z*z*nc + c; |
| } |
| multiply(rotation); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark matrix_stack_t |
| #endif |
| |
| void matrix_stack_t::init(int depth) { |
| stack = new matrixf_t[depth]; |
| ops = new uint8_t[depth]; |
| maxDepth = depth; |
| depth = 0; |
| dirty = 0; |
| loadIdentity(); |
| } |
| |
| void matrix_stack_t::uninit() { |
| delete [] stack; |
| delete [] ops; |
| } |
| |
| void matrix_stack_t::loadIdentity() { |
| transform.loadIdentity(); |
| stack[depth].loadIdentity(); |
| ops[depth] = OP_IDENTITY; |
| } |
| |
| void matrix_stack_t::load(const GLfixed* rhs) |
| { |
| memcpy(transform.matrix.m, rhs, sizeof(transform.matrix.m)); |
| stack[depth].load(rhs); |
| ops[depth] = OP_ALL; // TODO: we should look at the matrix |
| } |
| |
| void matrix_stack_t::load(const GLfloat* rhs) |
| { |
| stack[depth].load(rhs); |
| ops[depth] = OP_ALL; // TODO: we should look at the matrix |
| } |
| |
| void matrix_stack_t::multiply(const matrixf_t& rhs) |
| { |
| stack[depth].multiply(rhs); |
| ops[depth] = OP_ALL; // TODO: we should look at the matrix |
| } |
| |
| void matrix_stack_t::translate(GLfloat x, GLfloat y, GLfloat z) |
| { |
| stack[depth].translate(x,y,z); |
| ops[depth] |= OP_TRANSLATE; |
| } |
| |
| void matrix_stack_t::scale(GLfloat x, GLfloat y, GLfloat z) |
| { |
| stack[depth].scale(x,y,z); |
| if (x==y && y==z) { |
| ops[depth] |= OP_UNIFORM_SCALE; |
| } else { |
| ops[depth] |= OP_SCALE; |
| } |
| } |
| |
| void matrix_stack_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z) |
| { |
| stack[depth].rotate(a,x,y,z); |
| ops[depth] |= OP_ROTATE; |
| } |
| |
| void matrix_stack_t::validate() |
| { |
| if (dirty & DO_FLOAT_TO_FIXED) { |
| transform.matrix.load(top()); |
| } |
| if (dirty & DO_PICKER) { |
| transform.picker(); |
| } |
| dirty = 0; |
| } |
| |
| GLint matrix_stack_t::push() |
| { |
| if (depth >= (maxDepth-1)) { |
| return GL_STACK_OVERFLOW; |
| } |
| stack[depth+1] = stack[depth]; |
| ops[depth+1] = ops[depth]; |
| depth++; |
| return 0; |
| } |
| |
| GLint matrix_stack_t::pop() |
| { |
| if (depth == 0) { |
| return GL_STACK_UNDERFLOW; |
| } |
| depth--; |
| return 0; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark vp_transform_t |
| #endif |
| |
| void vp_transform_t::loadIdentity() { |
| transform.loadIdentity(); |
| matrix.loadIdentity(); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark transform_state_t |
| #endif |
| |
| void transform_state_t::invalidate() |
| { |
| switch (matrixMode) { |
| case GL_MODELVIEW: dirty |= MODELVIEW | MVP | MVUI | MVIT; break; |
| case GL_PROJECTION: dirty |= PROJECTION | MVP; break; |
| case GL_TEXTURE: dirty |= TEXTURE | MVP; break; |
| } |
| current->dirty = matrix_stack_t::DO_PICKER | |
| matrix_stack_t::DO_FLOAT_TO_FIXED; |
| } |
| |
| void transform_state_t::update_mvp() |
| { |
| matrixf_t temp_mvp; |
| matrixf_t::multiply(temp_mvp, projection.top(), modelview.top()); |
| mvp4.matrix.load(temp_mvp); |
| mvp4.picker(); |
| |
| if (mvp4.flags & transform_t::FLAGS_2D_PROJECTION) { |
| // the mvp matrix doesn't transform W, in this case we can |
| // premultiply it with the viewport transformation. In addition to |
| // being more efficient, this is also much more accurate and in fact |
| // is needed for 2D drawing with a resulting 1:1 mapping. |
| matrixf_t mvpv; |
| matrixf_t::multiply(mvpv, vpt.matrix, temp_mvp); |
| mvp.matrix.load(mvpv); |
| mvp.picker(); |
| } else { |
| mvp = mvp4; |
| } |
| } |
| |
| static inline |
| GLfloat det22(GLfloat a, GLfloat b, GLfloat c, GLfloat d) { |
| return a*d - b*c; |
| } |
| |
| static inline |
| GLfloat ndet22(GLfloat a, GLfloat b, GLfloat c, GLfloat d) { |
| return b*c - a*d; |
| } |
| |
| static __attribute__((noinline)) |
| void invert(GLfloat* inverse, const GLfloat* src) |
| { |
| double t; |
| int i, j, k, swap; |
| GLfloat tmp[4][4]; |
| |
| memcpy(inverse, gIdentityf, sizeof(gIdentityf)); |
| memcpy(tmp, src, sizeof(GLfloat)*16); |
| |
| for (i = 0; i < 4; i++) { |
| // look for largest element in column |
| swap = i; |
| for (j = i + 1; j < 4; j++) { |
| if (fabs(tmp[j][i]) > fabs(tmp[i][i])) { |
| swap = j; |
| } |
| } |
| |
| if (swap != i) { |
| /* swap rows. */ |
| for (k = 0; k < 4; k++) { |
| t = tmp[i][k]; |
| tmp[i][k] = tmp[swap][k]; |
| tmp[swap][k] = t; |
| |
| t = inverse[i*4+k]; |
| inverse[i*4+k] = inverse[swap*4+k]; |
| inverse[swap*4+k] = t; |
| } |
| } |
| |
| t = 1.0f / tmp[i][i]; |
| for (k = 0; k < 4; k++) { |
| tmp[i][k] *= t; |
| inverse[i*4+k] *= t; |
| } |
| for (j = 0; j < 4; j++) { |
| if (j != i) { |
| t = tmp[j][i]; |
| for (k = 0; k < 4; k++) { |
| tmp[j][k] -= tmp[i][k]*t; |
| inverse[j*4+k] -= inverse[i*4+k]*t; |
| } |
| } |
| } |
| } |
| } |
| |
| void transform_state_t::update_mvit() |
| { |
| GLfloat r[16]; |
| const GLfloat* const mv = modelview.top().elements(); |
| invert(r, mv); |
| // convert to fixed-point and transpose |
| GLfixed* const x = mvit4.matrix.m; |
| for (int i=0 ; i<4 ; i++) |
| for (int j=0 ; j<4 ; j++) |
| x[I(i,j)] = gglFloatToFixed(r[I(j,i)]); |
| mvit4.picker(); |
| } |
| |
| void transform_state_t::update_mvui() |
| { |
| const GLfloat* const mv = modelview.top().elements(); |
| |
| /* |
| When transforming normals, we can use the upper 3x3 matrix, see: |
| http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html |
| */ |
| |
| // Also note that: |
| // l(obj) = tr(M).l(eye) for infinite light |
| // l(obj) = inv(M).l(eye) for local light |
| |
| const uint32_t ops = modelview.top_ops() & ~OP_TRANSLATE; |
| if (ggl_likely((!(ops & ~OP_ROTATE)) || |
| (rescaleNormals && modelview.isRigidBody()))) { |
| // if the modelview matrix is a rigid body transformation |
| // (translation, rotation, uniform scaling), then we can bypass |
| // the inverse by transposing the matrix. |
| GLfloat rescale = 1.0f; |
| if (rescaleNormals == GL_RESCALE_NORMAL) { |
| if (!(ops & ~OP_UNIFORM_SCALE)) { |
| rescale = reciprocalf(mv[I(0,0)]); |
| } else { |
| rescale = rsqrtf( |
| sqrf(mv[I(2,0)]) + sqrf(mv[I(2,1)]) + sqrf(mv[I(2,2)])); |
| } |
| } |
| GLfixed* const x = mvui.matrix.m; |
| for (int i=0 ; i<3 ; i++) { |
| x[I(i,0)] = gglFloatToFixed(mv[I(0,i)] * rescale); |
| x[I(i,1)] = gglFloatToFixed(mv[I(1,i)] * rescale); |
| x[I(i,2)] = gglFloatToFixed(mv[I(2,i)] * rescale); |
| } |
| mvui.picker(); |
| return; |
| } |
| |
| GLfloat r[3][3]; |
| r[0][0] = det22(mv[I(1,1)], mv[I(2,1)], mv[I(1,2)], mv[I(2,2)]); |
| r[0][1] =ndet22(mv[I(0,1)], mv[I(2,1)], mv[I(0,2)], mv[I(2,2)]); |
| r[0][2] = det22(mv[I(0,1)], mv[I(1,1)], mv[I(0,2)], mv[I(1,2)]); |
| r[1][0] =ndet22(mv[I(1,0)], mv[I(2,0)], mv[I(1,2)], mv[I(2,2)]); |
| r[1][1] = det22(mv[I(0,0)], mv[I(2,0)], mv[I(0,2)], mv[I(2,2)]); |
| r[1][2] =ndet22(mv[I(0,0)], mv[I(1,0)], mv[I(0,2)], mv[I(1,2)]); |
| r[2][0] = det22(mv[I(1,0)], mv[I(2,0)], mv[I(1,1)], mv[I(2,1)]); |
| r[2][1] =ndet22(mv[I(0,0)], mv[I(2,0)], mv[I(0,1)], mv[I(2,1)]); |
| r[2][2] = det22(mv[I(0,0)], mv[I(1,0)], mv[I(0,1)], mv[I(1,1)]); |
| |
| GLfloat rdet; |
| if (rescaleNormals == GL_RESCALE_NORMAL) { |
| rdet = rsqrtf(sqrf(r[0][2]) + sqrf(r[1][2]) + sqrf(r[2][2])); |
| } else { |
| rdet = reciprocalf( |
| r[0][0]*mv[I(0,0)] + r[0][1]*mv[I(1,0)] + r[0][2]*mv[I(2,0)]); |
| } |
| |
| GLfixed* const x = mvui.matrix.m; |
| for (int i=0 ; i<3 ; i++) { |
| x[I(i,0)] = gglFloatToFixed(r[i][0] * rdet); |
| x[I(i,1)] = gglFloatToFixed(r[i][1] * rdet); |
| x[I(i,2)] = gglFloatToFixed(r[i][2] * rdet); |
| } |
| mvui.picker(); |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // transformation and matrices API |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark transformation and matrices API |
| #endif |
| |
| int ogles_surfaceport(ogles_context_t* c, GLint x, GLint y) |
| { |
| c->viewport.surfaceport.x = x; |
| c->viewport.surfaceport.y = y; |
| |
| ogles_viewport(c, |
| c->viewport.x, |
| c->viewport.y, |
| c->viewport.w, |
| c->viewport.h); |
| |
| ogles_scissor(c, |
| c->viewport.scissor.x, |
| c->viewport.scissor.y, |
| c->viewport.scissor.w, |
| c->viewport.scissor.h); |
| |
| return 0; |
| } |
| |
| void ogles_scissor(ogles_context_t* c, |
| GLint x, GLint y, GLsizei w, GLsizei h) |
| { |
| if ((w|h) < 0) { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| c->viewport.scissor.x = x; |
| c->viewport.scissor.y = y; |
| c->viewport.scissor.w = w; |
| c->viewport.scissor.h = h; |
| |
| x += c->viewport.surfaceport.x; |
| y += c->viewport.surfaceport.y; |
| |
| y = c->rasterizer.state.buffers.color.height - (y + h); |
| c->rasterizer.procs.scissor(c, x, y, w, h); |
| } |
| |
| void ogles_viewport(ogles_context_t* c, |
| GLint x, GLint y, GLsizei w, GLsizei h) |
| { |
| if ((w|h)<0) { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| |
| c->viewport.x = x; |
| c->viewport.y = y; |
| c->viewport.w = w; |
| c->viewport.h = h; |
| |
| x += c->viewport.surfaceport.x; |
| y += c->viewport.surfaceport.y; |
| |
| GLint H = c->rasterizer.state.buffers.color.height; |
| GLfloat sx = div2f(w); |
| GLfloat ox = sx + x; |
| GLfloat sy = div2f(h); |
| GLfloat oy = sy - y + (H - h); |
| |
| GLfloat near = c->transforms.vpt.zNear; |
| GLfloat far = c->transforms.vpt.zFar; |
| GLfloat A = div2f(far - near); |
| GLfloat B = div2f(far + near); |
| |
| // compute viewport matrix |
| GLfloat* const f = c->transforms.vpt.matrix.editElements(); |
| f[0] = sx; f[4] = 0; f[ 8] = 0; f[12] = ox; |
| f[1] = 0; f[5] =-sy; f[ 9] = 0; f[13] = oy; |
| f[2] = 0; f[6] = 0; f[10] = A; f[14] = B; |
| f[3] = 0; f[7] = 0; f[11] = 0; f[15] = 1; |
| c->transforms.dirty |= transform_state_t::VIEWPORT; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark matrix * vertex |
| #endif |
| |
| void point2__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { |
| const GLfixed* const m = mx->matrix.m; |
| const GLfixed rx = rhs->x; |
| const GLfixed ry = rhs->y; |
| lhs->x = mla2a(rx, m[ 0], ry, m[ 4], m[12]); |
| lhs->y = mla2a(rx, m[ 1], ry, m[ 5], m[13]); |
| lhs->z = mla2a(rx, m[ 2], ry, m[ 6], m[14]); |
| lhs->w = mla2a(rx, m[ 3], ry, m[ 7], m[15]); |
| } |
| |
| void point3__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { |
| const GLfixed* const m = mx->matrix.m; |
| const GLfixed rx = rhs->x; |
| const GLfixed ry = rhs->y; |
| const GLfixed rz = rhs->z; |
| lhs->x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); |
| lhs->y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]); |
| lhs->z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]); |
| lhs->w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]); |
| } |
| |
| void point4__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { |
| const GLfixed* const m = mx->matrix.m; |
| const GLfixed rx = rhs->x; |
| const GLfixed ry = rhs->y; |
| const GLfixed rz = rhs->z; |
| const GLfixed rw = rhs->w; |
| lhs->x = mla4(rx, m[ 0], ry, m[ 4], rz, m[ 8], rw, m[12]); |
| lhs->y = mla4(rx, m[ 1], ry, m[ 5], rz, m[ 9], rw, m[13]); |
| lhs->z = mla4(rx, m[ 2], ry, m[ 6], rz, m[10], rw, m[14]); |
| lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]); |
| } |
| |
| void normal__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { |
| const GLfixed* const m = mx->matrix.m; |
| const GLfixed rx = rhs->x; |
| const GLfixed ry = rhs->y; |
| const GLfixed rz = rhs->z; |
| lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]); |
| lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]); |
| lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]); |
| } |
| |
| |
| void point2__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) { |
| lhs->z = 0; |
| lhs->w = 0x10000; |
| if (lhs != rhs) { |
| lhs->x = rhs->x; |
| lhs->y = rhs->y; |
| } |
| } |
| |
| void point3__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) { |
| lhs->w = 0x10000; |
| if (lhs != rhs) { |
| lhs->x = rhs->x; |
| lhs->y = rhs->y; |
| lhs->z = rhs->z; |
| } |
| } |
| |
| void point4__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) { |
| if (lhs != rhs) |
| *lhs = *rhs; |
| } |
| |
| |
| static void frustumf( |
| GLfloat left, GLfloat right, |
| GLfloat bottom, GLfloat top, |
| GLfloat zNear, GLfloat zFar, |
| ogles_context_t* c) |
| { |
| if (cmpf(left,right) || |
| cmpf(top, bottom) || |
| cmpf(zNear, zFar) || |
| isZeroOrNegativef(zNear) || |
| isZeroOrNegativef(zFar)) |
| { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| const GLfloat r_width = reciprocalf(right - left); |
| const GLfloat r_height = reciprocalf(top - bottom); |
| const GLfloat r_depth = reciprocalf(zNear - zFar); |
| const GLfloat x = mul2f(zNear * r_width); |
| const GLfloat y = mul2f(zNear * r_height); |
| const GLfloat A = mul2f((right + left) * r_width); |
| const GLfloat B = (top + bottom) * r_height; |
| const GLfloat C = (zFar + zNear) * r_depth; |
| const GLfloat D = mul2f(zFar * zNear * r_depth); |
| GLfloat f[16]; |
| f[ 0] = x; |
| f[ 5] = y; |
| f[ 8] = A; |
| f[ 9] = B; |
| f[10] = C; |
| f[14] = D; |
| f[11] = -1.0f; |
| f[ 1] = f[ 2] = f[ 3] = |
| f[ 4] = f[ 6] = f[ 7] = |
| f[12] = f[13] = f[15] = 0.0f; |
| |
| matrixf_t rhs; |
| rhs.set(f); |
| c->transforms.current->multiply(rhs); |
| c->transforms.invalidate(); |
| } |
| |
| static void orthof( |
| GLfloat left, GLfloat right, |
| GLfloat bottom, GLfloat top, |
| GLfloat zNear, GLfloat zFar, |
| ogles_context_t* c) |
| { |
| if (cmpf(left,right) || |
| cmpf(top, bottom) || |
| cmpf(zNear, zFar)) |
| { |
| ogles_error(c, GL_INVALID_VALUE); |
| return; |
| } |
| const GLfloat r_width = reciprocalf(right - left); |
| const GLfloat r_height = reciprocalf(top - bottom); |
| const GLfloat r_depth = reciprocalf(zFar - zNear); |
| const GLfloat x = mul2f(r_width); |
| const GLfloat y = mul2f(r_height); |
| const GLfloat z = -mul2f(r_depth); |
| const GLfloat tx = -(right + left) * r_width; |
| const GLfloat ty = -(top + bottom) * r_height; |
| const GLfloat tz = -(zFar + zNear) * r_depth; |
| GLfloat f[16]; |
| f[ 0] = x; |
| f[ 5] = y; |
| f[10] = z; |
| f[12] = tx; |
| f[13] = ty; |
| f[14] = tz; |
| f[15] = 1.0f; |
| f[ 1] = f[ 2] = f[ 3] = |
| f[ 4] = f[ 6] = f[ 7] = |
| f[ 8] = f[ 9] = f[11] = 0.0f; |
| matrixf_t rhs; |
| rhs.set(f); |
| c->transforms.current->multiply(rhs); |
| c->transforms.invalidate(); |
| } |
| |
| static void depthRangef(GLclampf zNear, GLclampf zFar, ogles_context_t* c) |
| { |
| zNear = clampToZerof(zNear > 1 ? 1 : zNear); |
| zFar = clampToZerof(zFar > 1 ? 1 : zFar); |
| GLfloat* const f = c->transforms.vpt.matrix.editElements(); |
| f[10] = div2f(zFar - zNear); |
| f[14] = div2f(zFar + zNear); |
| c->transforms.dirty |= transform_state_t::VIEWPORT; |
| c->transforms.vpt.zNear = zNear; |
| c->transforms.vpt.zFar = zFar; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| }; // namespace android |
| |
| using namespace android; |
| |
| void glMatrixMode(GLenum mode) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| matrix_stack_t* stack = 0; |
| switch (mode) { |
| case GL_MODELVIEW: |
| stack = &c->transforms.modelview; |
| break; |
| case GL_PROJECTION: |
| stack = &c->transforms.projection; |
| break; |
| case GL_TEXTURE: |
| stack = &c->transforms.texture[c->textures.active]; |
| break; |
| default: |
| ogles_error(c, GL_INVALID_ENUM); |
| return; |
| } |
| c->transforms.matrixMode = mode; |
| c->transforms.current = stack; |
| } |
| |
| void glLoadIdentity() |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| c->transforms.current->loadIdentity(); // also loads the GLfixed transform |
| c->transforms.invalidate(); |
| c->transforms.current->dirty = 0; |
| } |
| |
| void glLoadMatrixf(const GLfloat* m) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| c->transforms.current->load(m); |
| c->transforms.invalidate(); |
| } |
| |
| void glLoadMatrixx(const GLfixed* m) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| c->transforms.current->load(m); // also loads the GLfixed transform |
| c->transforms.invalidate(); |
| c->transforms.current->dirty &= ~matrix_stack_t::DO_FLOAT_TO_FIXED; |
| } |
| |
| void glMultMatrixf(const GLfloat* m) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| matrixf_t rhs; |
| rhs.set(m); |
| c->transforms.current->multiply(rhs); |
| c->transforms.invalidate(); |
| } |
| |
| void glMultMatrixx(const GLfixed* m) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| matrixf_t rhs; |
| rhs.set(m); |
| c->transforms.current->multiply(rhs); |
| c->transforms.invalidate(); |
| } |
| |
| void glPopMatrix() |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| GLint err = c->transforms.current->pop(); |
| if (ggl_unlikely(err)) { |
| ogles_error(c, err); |
| return; |
| } |
| c->transforms.invalidate(); |
| } |
| |
| void glPushMatrix() |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| GLint err = c->transforms.current->push(); |
| if (ggl_unlikely(err)) { |
| ogles_error(c, err); |
| return; |
| } |
| c->transforms.invalidate(); |
| } |
| |
| void glFrustumf( |
| GLfloat left, GLfloat right, |
| GLfloat bottom, GLfloat top, |
| GLfloat zNear, GLfloat zFar) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| frustumf(left, right, bottom, top, zNear, zFar, c); |
| } |
| |
| void glFrustumx( |
| GLfixed left, GLfixed right, |
| GLfixed bottom, GLfixed top, |
| GLfixed zNear, GLfixed zFar) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| frustumf( fixedToFloat(left), fixedToFloat(right), |
| fixedToFloat(bottom), fixedToFloat(top), |
| fixedToFloat(zNear), fixedToFloat(zFar), |
| c); |
| } |
| |
| void glOrthof( |
| GLfloat left, GLfloat right, |
| GLfloat bottom, GLfloat top, |
| GLfloat zNear, GLfloat zFar) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| orthof(left, right, bottom, top, zNear, zFar, c); |
| } |
| |
| void glOrthox( |
| GLfixed left, GLfixed right, |
| GLfixed bottom, GLfixed top, |
| GLfixed zNear, GLfixed zFar) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| orthof( fixedToFloat(left), fixedToFloat(right), |
| fixedToFloat(bottom), fixedToFloat(top), |
| fixedToFloat(zNear), fixedToFloat(zFar), |
| c); |
| } |
| |
| void glRotatef(GLfloat a, GLfloat x, GLfloat y, GLfloat z) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| c->transforms.current->rotate(a, x, y, z); |
| c->transforms.invalidate(); |
| } |
| |
| void glRotatex(GLfixed a, GLfixed x, GLfixed y, GLfixed z) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| c->transforms.current->rotate( |
| fixedToFloat(a), fixedToFloat(x), |
| fixedToFloat(y), fixedToFloat(z)); |
| c->transforms.invalidate(); |
| } |
| |
| void glScalef(GLfloat x, GLfloat y, GLfloat z) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| c->transforms.current->scale(x, y, z); |
| c->transforms.invalidate(); |
| } |
| |
| void glScalex(GLfixed x, GLfixed y, GLfixed z) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| c->transforms.current->scale( |
| fixedToFloat(x), fixedToFloat(y), fixedToFloat(z)); |
| c->transforms.invalidate(); |
| } |
| |
| void glTranslatef(GLfloat x, GLfloat y, GLfloat z) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| c->transforms.current->translate(x, y, z); |
| c->transforms.invalidate(); |
| } |
| |
| void glTranslatex(GLfixed x, GLfixed y, GLfixed z) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| c->transforms.current->translate( |
| fixedToFloat(x), fixedToFloat(y), fixedToFloat(z)); |
| c->transforms.invalidate(); |
| } |
| |
| void glScissor(GLint x, GLint y, GLsizei w, GLsizei h) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| ogles_scissor(c, x, y, w, h); |
| } |
| |
| void glViewport(GLint x, GLint y, GLsizei w, GLsizei h) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| ogles_viewport(c, x, y, w, h); |
| } |
| |
| void glDepthRangef(GLclampf zNear, GLclampf zFar) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| depthRangef(zNear, zFar, c); |
| } |
| |
| void glDepthRangex(GLclampx zNear, GLclampx zFar) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| depthRangef(fixedToFloat(zNear), fixedToFloat(zFar), c); |
| } |
| |
| void glPolygonOffsetx(GLfixed factor, GLfixed units) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| c->polygonOffset.factor = factor; |
| c->polygonOffset.units = units; |
| } |
| |
| void glPolygonOffset(GLfloat factor, GLfloat units) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| c->polygonOffset.factor = gglFloatToFixed(factor); |
| c->polygonOffset.units = gglFloatToFixed(units); |
| } |
| |
| GLbitfield glQueryMatrixxOES(GLfixed* m, GLint* e) |
| { |
| ogles_context_t* c = ogles_context_t::get(); |
| GLbitfield status = 0; |
| GLfloat const* f = c->transforms.current->top().elements(); |
| for (int i=0 ; i<16 ; i++) { |
| if (isnan(f[i]) || isinf(f[i])) { |
| status |= 1<<i; |
| continue; |
| } |
| e[i] = exponent(f[i]) - 7; |
| m[i] = mantissa(f[i]); |
| } |
| return status; |
| } |