Initial Contribution
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
new file mode 100644
index 0000000..441da38
--- /dev/null
+++ b/opengl/libagl/matrix.cpp
@@ -0,0 +1,1144 @@
+/* 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 = (c->clipPlanes.enable) ?
+            ogles_vertex_clipAllPerspective3DZ : ogles_vertex_perspective3DZ;
+    }
+    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;
+}