matrix.cpp revision edbf3b6af777b721cd2a1ef461947e51e88241e1
1/* libs/opengles/matrix.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include <stdlib.h>
19#include <stdio.h>
20
21#include "context.h"
22#include "fp.h"
23#include "state.h"
24#include "matrix.h"
25#include "vertex.h"
26#include "light.h"
27
28#if defined(__arm__) && defined(__thumb__)
29#warning "matrix.cpp should not be compiled in thumb on ARM."
30#endif
31
32#define I(_i, _j) ((_j)+ 4*(_i))
33
34namespace android {
35
36// ----------------------------------------------------------------------------
37
38static const GLfloat gIdentityf[16] = { 1,0,0,0,
39                                        0,1,0,0,
40                                        0,0,1,0,
41                                        0,0,0,1 };
42
43static const matrixx_t gIdentityx = {
44            {   0x10000,0,0,0,
45                0,0x10000,0,0,
46                0,0,0x10000,0,
47                0,0,0,0x10000
48            }
49        };
50
51static void point2__nop(transform_t const*, vec4_t* c, vec4_t const* o);
52static void point3__nop(transform_t const*, vec4_t* c, vec4_t const* o);
53static void point4__nop(transform_t const*, vec4_t* c, vec4_t const* o);
54static void normal__nop(transform_t const*, vec4_t* c, vec4_t const* o);
55static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o);
56static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o);
57static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o);
58static void normal__generic(transform_t const*, vec4_t* c, vec4_t const* o);
59
60// ----------------------------------------------------------------------------
61#if 0
62#pragma mark -
63#endif
64
65void ogles_init_matrix(ogles_context_t* c)
66{
67    c->transforms.modelview.init(OGLES_MODELVIEW_STACK_DEPTH);
68    c->transforms.projection.init(OGLES_PROJECTION_STACK_DEPTH);
69    for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++)
70        c->transforms.texture[i].init(OGLES_TEXTURE_STACK_DEPTH);
71
72    c->transforms.current = &c->transforms.modelview;
73    c->transforms.matrixMode = GL_MODELVIEW;
74    c->transforms.dirty =   transform_state_t::VIEWPORT |
75                            transform_state_t::MVUI |
76                            transform_state_t::MVIT |
77                            transform_state_t::MVP;
78    c->transforms.mvp.loadIdentity();
79    c->transforms.mvp4.loadIdentity();
80    c->transforms.mvit4.loadIdentity();
81    c->transforms.mvui.loadIdentity();
82    c->transforms.vpt.loadIdentity();
83    c->transforms.vpt.zNear = 0.0f;
84    c->transforms.vpt.zFar  = 1.0f;
85}
86
87void ogles_uninit_matrix(ogles_context_t* c)
88{
89    c->transforms.modelview.uninit();
90    c->transforms.projection.uninit();
91    for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++)
92        c->transforms.texture[i].uninit();
93}
94
95static void validate_perspective(ogles_context_t* c, vertex_t* v)
96{
97    const uint32_t enables = c->rasterizer.state.enables;
98    c->arrays.perspective = (c->clipPlanes.enable) ?
99        ogles_vertex_clipAllPerspective3D : ogles_vertex_perspective3D;
100    if (enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) {
101        c->arrays.perspective = ogles_vertex_perspective3DZ;
102        if (c->clipPlanes.enable || (enables&GGL_ENABLE_FOG))
103            c->arrays.perspective = ogles_vertex_clipAllPerspective3DZ;
104    }
105    if ((c->arrays.vertex.size != 4) &&
106        (c->transforms.mvp4.flags & transform_t::FLAGS_2D_PROJECTION)) {
107        c->arrays.perspective = ogles_vertex_perspective2D;
108    }
109    c->arrays.perspective(c, v);
110}
111
112void ogles_invalidate_perspective(ogles_context_t* c)
113{
114    c->arrays.perspective = validate_perspective;
115}
116
117void ogles_validate_transform_impl(ogles_context_t* c, uint32_t want)
118{
119    int dirty = c->transforms.dirty & want;
120
121    // Validate the modelview
122    if (dirty & transform_state_t::MODELVIEW) {
123        c->transforms.modelview.validate();
124    }
125
126    // Validate the projection stack (in fact, it's never needed)
127    if (dirty & transform_state_t::PROJECTION) {
128        c->transforms.projection.validate();
129    }
130
131    // Validate the viewport transformation
132    if (dirty & transform_state_t::VIEWPORT) {
133        vp_transform_t& vpt = c->transforms.vpt;
134        vpt.transform.matrix.load(vpt.matrix);
135        vpt.transform.picker();
136    }
137
138    // We need to update the mvp (used to transform each vertex)
139    if (dirty & transform_state_t::MVP) {
140        c->transforms.update_mvp();
141        // invalidate perspective (divide by W) and view volume clipping
142        ogles_invalidate_perspective(c);
143    }
144
145    // Validate the mvui (for normal transformation)
146    if (dirty & transform_state_t::MVUI) {
147        c->transforms.update_mvui();
148        ogles_invalidate_lighting_mvui(c);
149    }
150
151    // Validate the texture stack
152    if (dirty & transform_state_t::TEXTURE) {
153        for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++)
154            c->transforms.texture[i].validate();
155    }
156
157    // Validate the mvit4 (user-clip planes)
158    if (dirty & transform_state_t::MVIT) {
159        c->transforms.update_mvit();
160    }
161
162    c->transforms.dirty &= ~want;
163}
164
165// ----------------------------------------------------------------------------
166#if 0
167#pragma mark -
168#pragma mark transform_t
169#endif
170
171void transform_t::loadIdentity() {
172    matrix = gIdentityx;
173    flags = 0;
174    ops = OP_IDENTITY;
175    point2 = point2__nop;
176    point3 = point3__nop;
177    point4 = point4__nop;
178}
179
180
181static inline
182int notZero(GLfixed v) {
183    return abs(v) & ~0x3;
184}
185
186static inline
187int notOne(GLfixed v) {
188    return notZero(v - 0x10000);
189}
190
191void transform_t::picker()
192{
193    const GLfixed* const m = matrix.m;
194
195    // XXX: picker needs to be smarter
196    flags = 0;
197    ops = OP_ALL;
198    point2 = point2__generic;
199    point3 = point3__generic;
200    point4 = point4__generic;
201
202    // find out if this is a 2D projection
203    if (!(notZero(m[3]) | notZero(m[7]) | notZero(m[11]) | notOne(m[15]))) {
204        flags |= FLAGS_2D_PROJECTION;
205    }
206}
207
208void mvui_transform_t::picker()
209{
210    flags = 0;
211    ops = OP_ALL;
212    point3 = normal__generic;
213}
214
215void transform_t::dump(const char* what)
216{
217    GLfixed const * const m = matrix.m;
218    LOGD("%s:", what);
219    for (int i=0 ; i<4 ; i++)
220        LOGD("[%08x %08x %08x %08x] [%f %f %f %f]\n",
221            m[I(0,i)], m[I(1,i)], m[I(2,i)], m[I(3,i)],
222            fixedToFloat(m[I(0,i)]),
223            fixedToFloat(m[I(1,i)]),
224            fixedToFloat(m[I(2,i)]),
225            fixedToFloat(m[I(3,i)]));
226}
227
228// ----------------------------------------------------------------------------
229#if 0
230#pragma mark -
231#pragma mark matrixx_t
232#endif
233
234void matrixx_t::load(const matrixf_t& rhs) {
235    GLfixed* xp = m;
236    GLfloat const* fp = rhs.elements();
237    unsigned int i = 16;
238    do {
239        const GLfloat f = *fp++;
240        *xp++ = isZerof(f) ? 0 : gglFloatToFixed(f);
241    } while (--i);
242}
243
244// ----------------------------------------------------------------------------
245#if 0
246#pragma mark -
247#pragma mark matrixf_t
248#endif
249
250void matrixf_t::multiply(matrixf_t& r, const matrixf_t& lhs, const matrixf_t& rhs)
251{
252    GLfloat const* const m = lhs.m;
253    for (int i=0 ; i<4 ; i++) {
254        register const float rhs_i0 = rhs.m[ I(i,0) ];
255        register float ri0 = m[ I(0,0) ] * rhs_i0;
256        register float ri1 = m[ I(0,1) ] * rhs_i0;
257        register float ri2 = m[ I(0,2) ] * rhs_i0;
258        register float ri3 = m[ I(0,3) ] * rhs_i0;
259        for (int j=1 ; j<4 ; j++) {
260            register const float rhs_ij = rhs.m[ I(i,j) ];
261            ri0 += m[ I(j,0) ] * rhs_ij;
262            ri1 += m[ I(j,1) ] * rhs_ij;
263            ri2 += m[ I(j,2) ] * rhs_ij;
264            ri3 += m[ I(j,3) ] * rhs_ij;
265        }
266        r.m[ I(i,0) ] = ri0;
267        r.m[ I(i,1) ] = ri1;
268        r.m[ I(i,2) ] = ri2;
269        r.m[ I(i,3) ] = ri3;
270    }
271}
272
273void matrixf_t::dump(const char* what) {
274    LOGD("%s", what);
275    LOGD("[ %9f %9f %9f %9f ]", m[I(0,0)], m[I(1,0)], m[I(2,0)], m[I(3,0)]);
276    LOGD("[ %9f %9f %9f %9f ]", m[I(0,1)], m[I(1,1)], m[I(2,1)], m[I(3,1)]);
277    LOGD("[ %9f %9f %9f %9f ]", m[I(0,2)], m[I(1,2)], m[I(2,2)], m[I(3,2)]);
278    LOGD("[ %9f %9f %9f %9f ]", m[I(0,3)], m[I(1,3)], m[I(2,3)], m[I(3,3)]);
279}
280
281void matrixf_t::loadIdentity() {
282    memcpy(m, gIdentityf, sizeof(m));
283}
284
285void matrixf_t::set(const GLfixed* rhs) {
286    load(rhs);
287}
288
289void matrixf_t::set(const GLfloat* rhs) {
290    load(rhs);
291}
292
293void matrixf_t::load(const GLfixed* rhs) {
294    GLfloat* fp = m;
295    unsigned int i = 16;
296    do {
297        *fp++ = fixedToFloat(*rhs++);
298    } while (--i);
299}
300
301void matrixf_t::load(const GLfloat* rhs) {
302    memcpy(m, rhs, sizeof(m));
303}
304
305void matrixf_t::load(const matrixf_t& rhs) {
306    operator = (rhs);
307}
308
309void matrixf_t::multiply(const matrixf_t& rhs) {
310    matrixf_t r;
311    multiply(r, *this, rhs);
312    operator = (r);
313}
314
315void matrixf_t::translate(GLfloat x, GLfloat y, GLfloat z) {
316    for (int i=0 ; i<4 ; i++) {
317        m[12+i] += m[i]*x + m[4+i]*y + m[8+i]*z;
318    }
319}
320
321void matrixf_t::scale(GLfloat x, GLfloat y, GLfloat z) {
322    for (int i=0 ; i<4 ; i++) {
323        m[  i] *= x;
324        m[4+i] *= y;
325        m[8+i] *= z;
326    }
327}
328
329void matrixf_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z)
330{
331    matrixf_t rotation;
332    GLfloat* r = rotation.m;
333    GLfloat c, s;
334    r[3] = 0;   r[7] = 0;   r[11]= 0;
335    r[12]= 0;   r[13]= 0;   r[14]= 0;   r[15]= 1;
336    a *= GLfloat(M_PI / 180.0f);
337    sincosf(a, &s, &c);
338    if (isOnef(x) && isZerof(y) && isZerof(z)) {
339        r[5] = c;   r[10]= c;
340        r[6] = s;   r[9] = -s;
341        r[1] = 0;   r[2] = 0;
342        r[4] = 0;   r[8] = 0;
343        r[0] = 1;
344    } else if (isZerof(x) && isOnef(y) && isZerof(z)) {
345        r[0] = c;   r[10]= c;
346        r[8] = s;   r[2] = -s;
347        r[1] = 0;   r[4] = 0;
348        r[6] = 0;   r[9] = 0;
349        r[5] = 1;
350    } else if (isZerof(x) && isZerof(y) && isOnef(z)) {
351        r[0] = c;   r[5] = c;
352        r[1] = s;   r[4] = -s;
353        r[2] = 0;   r[6] = 0;
354        r[8] = 0;   r[9] = 0;
355        r[10]= 1;
356    } else {
357        const GLfloat len = sqrtf(x*x + y*y + z*z);
358        if (!isOnef(len)) {
359            const GLfloat recipLen = reciprocalf(len);
360            x *= recipLen;
361            y *= recipLen;
362            z *= recipLen;
363        }
364        const GLfloat nc = 1.0f - c;
365        const GLfloat xy = x * y;
366        const GLfloat yz = y * z;
367        const GLfloat zx = z * x;
368        const GLfloat xs = x * s;
369        const GLfloat ys = y * s;
370        const GLfloat zs = z * s;
371        r[ 0] = x*x*nc +  c;    r[ 4] =  xy*nc - zs;    r[ 8] =  zx*nc + ys;
372        r[ 1] =  xy*nc + zs;    r[ 5] = y*y*nc +  c;    r[ 9] =  yz*nc - xs;
373        r[ 2] =  zx*nc - ys;    r[ 6] =  yz*nc + xs;    r[10] = z*z*nc +  c;
374    }
375    multiply(rotation);
376}
377
378// ----------------------------------------------------------------------------
379#if 0
380#pragma mark -
381#pragma mark matrix_stack_t
382#endif
383
384void matrix_stack_t::init(int depth) {
385    stack = new matrixf_t[depth];
386    ops = new uint8_t[depth];
387    maxDepth = depth;
388    depth = 0;
389    dirty = 0;
390    loadIdentity();
391}
392
393void matrix_stack_t::uninit() {
394    delete [] stack;
395    delete [] ops;
396}
397
398void matrix_stack_t::loadIdentity() {
399    transform.loadIdentity();
400    stack[depth].loadIdentity();
401    ops[depth] = OP_IDENTITY;
402}
403
404void matrix_stack_t::load(const GLfixed* rhs)
405{
406    memcpy(transform.matrix.m, rhs, sizeof(transform.matrix.m));
407    stack[depth].load(rhs);
408    ops[depth] = OP_ALL;    // TODO: we should look at the matrix
409}
410
411void matrix_stack_t::load(const GLfloat* rhs)
412{
413    stack[depth].load(rhs);
414    ops[depth] = OP_ALL;    // TODO: we should look at the matrix
415}
416
417void matrix_stack_t::multiply(const matrixf_t& rhs)
418{
419    stack[depth].multiply(rhs);
420    ops[depth] = OP_ALL;    // TODO: we should look at the matrix
421}
422
423void matrix_stack_t::translate(GLfloat x, GLfloat y, GLfloat z)
424{
425    stack[depth].translate(x,y,z);
426    ops[depth] |= OP_TRANSLATE;
427}
428
429void matrix_stack_t::scale(GLfloat x, GLfloat y, GLfloat z)
430{
431    stack[depth].scale(x,y,z);
432    if (x==y && y==z) {
433        ops[depth] |= OP_UNIFORM_SCALE;
434    } else {
435        ops[depth] |= OP_SCALE;
436    }
437}
438
439void matrix_stack_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z)
440{
441    stack[depth].rotate(a,x,y,z);
442    ops[depth] |= OP_ROTATE;
443}
444
445void matrix_stack_t::validate()
446{
447    if (dirty & DO_FLOAT_TO_FIXED) {
448        transform.matrix.load(top());
449    }
450    if (dirty & DO_PICKER) {
451        transform.picker();
452    }
453    dirty = 0;
454}
455
456GLint matrix_stack_t::push()
457{
458    if (depth >= (maxDepth-1)) {
459        return GL_STACK_OVERFLOW;
460    }
461    stack[depth+1] = stack[depth];
462    ops[depth+1] = ops[depth];
463    depth++;
464    return 0;
465}
466
467GLint matrix_stack_t::pop()
468{
469    if (depth == 0) {
470        return GL_STACK_UNDERFLOW;
471    }
472    depth--;
473    return 0;
474}
475
476// ----------------------------------------------------------------------------
477#if 0
478#pragma mark -
479#pragma mark vp_transform_t
480#endif
481
482void vp_transform_t::loadIdentity() {
483    transform.loadIdentity();
484    matrix.loadIdentity();
485}
486
487// ----------------------------------------------------------------------------
488#if 0
489#pragma mark -
490#pragma mark transform_state_t
491#endif
492
493void transform_state_t::invalidate()
494{
495    switch (matrixMode) {
496    case GL_MODELVIEW:  dirty |= MODELVIEW  | MVP | MVUI | MVIT;    break;
497    case GL_PROJECTION: dirty |= PROJECTION | MVP;                  break;
498    case GL_TEXTURE:    dirty |= TEXTURE    | MVP;                  break;
499    }
500    current->dirty =    matrix_stack_t::DO_PICKER |
501                        matrix_stack_t::DO_FLOAT_TO_FIXED;
502}
503
504void transform_state_t::update_mvp()
505{
506    matrixf_t temp_mvp;
507    matrixf_t::multiply(temp_mvp, projection.top(), modelview.top());
508    mvp4.matrix.load(temp_mvp);
509    mvp4.picker();
510
511    if (mvp4.flags & transform_t::FLAGS_2D_PROJECTION) {
512        // the mvp matrix doesn't transform W, in this case we can
513        // premultiply it with the viewport transformation. In addition to
514        // being more efficient, this is also much more accurate and in fact
515        // is needed for 2D drawing with a resulting 1:1 mapping.
516        matrixf_t mvpv;
517        matrixf_t::multiply(mvpv, vpt.matrix, temp_mvp);
518        mvp.matrix.load(mvpv);
519        mvp.picker();
520    } else {
521        mvp = mvp4;
522    }
523}
524
525static inline
526GLfloat det22(GLfloat a, GLfloat b, GLfloat c, GLfloat d) {
527    return a*d - b*c;
528}
529
530static inline
531GLfloat ndet22(GLfloat a, GLfloat b, GLfloat c, GLfloat d) {
532    return b*c - a*d;
533}
534
535static __attribute__((noinline))
536void invert(GLfloat* inverse, const GLfloat* src)
537{
538    double t;
539    int i, j, k, swap;
540    GLfloat tmp[4][4];
541
542    memcpy(inverse, gIdentityf, sizeof(gIdentityf));
543    memcpy(tmp, src, sizeof(GLfloat)*16);
544
545    for (i = 0; i < 4; i++) {
546        // look for largest element in column
547        swap = i;
548        for (j = i + 1; j < 4; j++) {
549            if (fabs(tmp[j][i]) > fabs(tmp[i][i])) {
550                swap = j;
551            }
552        }
553
554        if (swap != i) {
555            /* swap rows. */
556            for (k = 0; k < 4; k++) {
557                t = tmp[i][k];
558                tmp[i][k] = tmp[swap][k];
559                tmp[swap][k] = t;
560
561                t = inverse[i*4+k];
562                inverse[i*4+k] = inverse[swap*4+k];
563                inverse[swap*4+k] = t;
564            }
565        }
566
567        t = 1.0f / tmp[i][i];
568        for (k = 0; k < 4; k++) {
569            tmp[i][k] *= t;
570            inverse[i*4+k] *= t;
571        }
572        for (j = 0; j < 4; j++) {
573            if (j != i) {
574                t = tmp[j][i];
575                for (k = 0; k < 4; k++) {
576                    tmp[j][k] -= tmp[i][k]*t;
577                    inverse[j*4+k] -= inverse[i*4+k]*t;
578                }
579            }
580        }
581    }
582}
583
584void transform_state_t::update_mvit()
585{
586    GLfloat r[16];
587    const GLfloat* const mv = modelview.top().elements();
588    invert(r, mv);
589    // convert to fixed-point and transpose
590    GLfixed* const x = mvit4.matrix.m;
591    for (int i=0 ; i<4 ; i++)
592        for (int j=0 ; j<4 ; j++)
593            x[I(i,j)] = gglFloatToFixed(r[I(j,i)]);
594    mvit4.picker();
595}
596
597void transform_state_t::update_mvui()
598{
599    const GLfloat* const mv = modelview.top().elements();
600
601    /*
602    When transforming normals, we can use the upper 3x3 matrix, see:
603    http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html
604    */
605
606    // Also note that:
607    //      l(obj) =  tr(M).l(eye) for infinite light
608    //      l(obj) = inv(M).l(eye) for local light
609
610    const uint32_t ops = modelview.top_ops() & ~OP_TRANSLATE;
611    if (ggl_likely((!(ops & ~OP_ROTATE)) ||
612        (rescaleNormals && modelview.isRigidBody()))) {
613        // if the modelview matrix is a rigid body transformation
614        // (translation, rotation, uniform scaling), then we can bypass
615        // the inverse by transposing the matrix.
616        GLfloat rescale = 1.0f;
617        if (rescaleNormals == GL_RESCALE_NORMAL) {
618            if (!(ops & ~OP_UNIFORM_SCALE)) {
619                rescale = reciprocalf(mv[I(0,0)]);
620            } else {
621                rescale = rsqrtf(
622                        sqrf(mv[I(2,0)]) + sqrf(mv[I(2,1)]) + sqrf(mv[I(2,2)]));
623            }
624        }
625        GLfixed* const x = mvui.matrix.m;
626        for (int i=0 ; i<3 ; i++) {
627            x[I(i,0)] = gglFloatToFixed(mv[I(0,i)] * rescale);
628            x[I(i,1)] = gglFloatToFixed(mv[I(1,i)] * rescale);
629            x[I(i,2)] = gglFloatToFixed(mv[I(2,i)] * rescale);
630        }
631        mvui.picker();
632        return;
633    }
634
635    GLfloat r[3][3];
636    r[0][0] = det22(mv[I(1,1)], mv[I(2,1)], mv[I(1,2)], mv[I(2,2)]);
637    r[0][1] =ndet22(mv[I(0,1)], mv[I(2,1)], mv[I(0,2)], mv[I(2,2)]);
638    r[0][2] = det22(mv[I(0,1)], mv[I(1,1)], mv[I(0,2)], mv[I(1,2)]);
639    r[1][0] =ndet22(mv[I(1,0)], mv[I(2,0)], mv[I(1,2)], mv[I(2,2)]);
640    r[1][1] = det22(mv[I(0,0)], mv[I(2,0)], mv[I(0,2)], mv[I(2,2)]);
641    r[1][2] =ndet22(mv[I(0,0)], mv[I(1,0)], mv[I(0,2)], mv[I(1,2)]);
642    r[2][0] = det22(mv[I(1,0)], mv[I(2,0)], mv[I(1,1)], mv[I(2,1)]);
643    r[2][1] =ndet22(mv[I(0,0)], mv[I(2,0)], mv[I(0,1)], mv[I(2,1)]);
644    r[2][2] = det22(mv[I(0,0)], mv[I(1,0)], mv[I(0,1)], mv[I(1,1)]);
645
646    GLfloat rdet;
647    if (rescaleNormals == GL_RESCALE_NORMAL) {
648        rdet = rsqrtf(sqrf(r[0][2]) + sqrf(r[1][2]) + sqrf(r[2][2]));
649    } else {
650        rdet = reciprocalf(
651            r[0][0]*mv[I(0,0)] + r[0][1]*mv[I(1,0)] + r[0][2]*mv[I(2,0)]);
652    }
653
654    GLfixed* const x = mvui.matrix.m;
655    for (int i=0 ; i<3 ; i++) {
656        x[I(i,0)] = gglFloatToFixed(r[i][0] * rdet);
657        x[I(i,1)] = gglFloatToFixed(r[i][1] * rdet);
658        x[I(i,2)] = gglFloatToFixed(r[i][2] * rdet);
659    }
660    mvui.picker();
661}
662
663
664// ----------------------------------------------------------------------------
665// transformation and matrices API
666// ----------------------------------------------------------------------------
667#if 0
668#pragma mark -
669#pragma mark transformation and matrices API
670#endif
671
672int ogles_surfaceport(ogles_context_t* c, GLint x, GLint y)
673{
674    c->viewport.surfaceport.x = x;
675    c->viewport.surfaceport.y = y;
676
677    ogles_viewport(c,
678            c->viewport.x,
679            c->viewport.y,
680            c->viewport.w,
681            c->viewport.h);
682
683    ogles_scissor(c,
684            c->viewport.scissor.x,
685            c->viewport.scissor.y,
686            c->viewport.scissor.w,
687            c->viewport.scissor.h);
688
689    return 0;
690}
691
692void ogles_scissor(ogles_context_t* c,
693        GLint x, GLint y, GLsizei w, GLsizei h)
694{
695    if ((w|h) < 0) {
696        ogles_error(c, GL_INVALID_VALUE);
697        return;
698    }
699    c->viewport.scissor.x = x;
700    c->viewport.scissor.y = y;
701    c->viewport.scissor.w = w;
702    c->viewport.scissor.h = h;
703
704    x += c->viewport.surfaceport.x;
705    y += c->viewport.surfaceport.y;
706
707    y = c->rasterizer.state.buffers.color.height - (y + h);
708    c->rasterizer.procs.scissor(c, x, y, w, h);
709}
710
711void ogles_viewport(ogles_context_t* c,
712        GLint x, GLint y, GLsizei w, GLsizei h)
713{
714    if ((w|h)<0) {
715        ogles_error(c, GL_INVALID_VALUE);
716        return;
717    }
718
719    c->viewport.x = x;
720    c->viewport.y = y;
721    c->viewport.w = w;
722    c->viewport.h = h;
723
724    x += c->viewport.surfaceport.x;
725    y += c->viewport.surfaceport.y;
726
727    GLint H = c->rasterizer.state.buffers.color.height;
728    GLfloat sx = div2f(w);
729    GLfloat ox = sx + x;
730    GLfloat sy = div2f(h);
731    GLfloat oy = sy - y + (H - h);
732
733    GLfloat near = c->transforms.vpt.zNear;
734    GLfloat far  = c->transforms.vpt.zFar;
735    GLfloat A = div2f(far - near);
736    GLfloat B = div2f(far + near);
737
738    // compute viewport matrix
739    GLfloat* const f = c->transforms.vpt.matrix.editElements();
740    f[0] = sx;  f[4] = 0;   f[ 8] = 0;  f[12] = ox;
741    f[1] = 0;   f[5] =-sy;  f[ 9] = 0;  f[13] = oy;
742    f[2] = 0;   f[6] = 0;   f[10] = A;  f[14] = B;
743    f[3] = 0;   f[7] = 0;   f[11] = 0;  f[15] = 1;
744    c->transforms.dirty |= transform_state_t::VIEWPORT;
745}
746
747// ----------------------------------------------------------------------------
748#if 0
749#pragma mark -
750#pragma mark matrix * vertex
751#endif
752
753void point2__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
754    const GLfixed* const m = mx->matrix.m;
755    const GLfixed rx = rhs->x;
756    const GLfixed ry = rhs->y;
757    lhs->x = mla2a(rx, m[ 0], ry, m[ 4], m[12]);
758    lhs->y = mla2a(rx, m[ 1], ry, m[ 5], m[13]);
759    lhs->z = mla2a(rx, m[ 2], ry, m[ 6], m[14]);
760    lhs->w = mla2a(rx, m[ 3], ry, m[ 7], m[15]);
761}
762
763void point3__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
764    const GLfixed* const m = mx->matrix.m;
765    const GLfixed rx = rhs->x;
766    const GLfixed ry = rhs->y;
767    const GLfixed rz = rhs->z;
768    lhs->x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]);
769    lhs->y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]);
770    lhs->z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]);
771    lhs->w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]);
772}
773
774void point4__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
775    const GLfixed* const m = mx->matrix.m;
776    const GLfixed rx = rhs->x;
777    const GLfixed ry = rhs->y;
778    const GLfixed rz = rhs->z;
779    const GLfixed rw = rhs->w;
780    lhs->x = mla4(rx, m[ 0], ry, m[ 4], rz, m[ 8], rw, m[12]);
781    lhs->y = mla4(rx, m[ 1], ry, m[ 5], rz, m[ 9], rw, m[13]);
782    lhs->z = mla4(rx, m[ 2], ry, m[ 6], rz, m[10], rw, m[14]);
783    lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]);
784}
785
786void normal__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
787    const GLfixed* const m = mx->matrix.m;
788    const GLfixed rx = rhs->x;
789    const GLfixed ry = rhs->y;
790    const GLfixed rz = rhs->z;
791    lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]);
792    lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]);
793    lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]);
794}
795
796
797void point2__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) {
798    lhs->z = 0;
799    lhs->w = 0x10000;
800    if (lhs != rhs) {
801        lhs->x = rhs->x;
802        lhs->y = rhs->y;
803    }
804}
805
806void point3__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) {
807    lhs->w = 0x10000;
808    if (lhs != rhs) {
809        lhs->x = rhs->x;
810        lhs->y = rhs->y;
811        lhs->z = rhs->z;
812    }
813}
814
815void point4__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) {
816    if (lhs != rhs)
817        *lhs = *rhs;
818}
819
820
821static void frustumf(
822            GLfloat left, GLfloat right,
823            GLfloat bottom, GLfloat top,
824            GLfloat zNear, GLfloat zFar,
825            ogles_context_t* c)
826    {
827    if (cmpf(left,right) ||
828        cmpf(top, bottom) ||
829        cmpf(zNear, zFar) ||
830        isZeroOrNegativef(zNear) ||
831        isZeroOrNegativef(zFar))
832    {
833        ogles_error(c, GL_INVALID_VALUE);
834        return;
835    }
836    const GLfloat r_width  = reciprocalf(right - left);
837    const GLfloat r_height = reciprocalf(top - bottom);
838    const GLfloat r_depth  = reciprocalf(zNear - zFar);
839    const GLfloat x = mul2f(zNear * r_width);
840    const GLfloat y = mul2f(zNear * r_height);
841    const GLfloat A = mul2f((right + left) * r_width);
842    const GLfloat B = (top + bottom) * r_height;
843    const GLfloat C = (zFar + zNear) * r_depth;
844    const GLfloat D = mul2f(zFar * zNear * r_depth);
845    GLfloat f[16];
846    f[ 0] = x;
847    f[ 5] = y;
848    f[ 8] = A;
849    f[ 9] = B;
850    f[10] = C;
851    f[14] = D;
852    f[11] = -1.0f;
853    f[ 1] = f[ 2] = f[ 3] =
854    f[ 4] = f[ 6] = f[ 7] =
855    f[12] = f[13] = f[15] = 0.0f;
856
857    matrixf_t rhs;
858    rhs.set(f);
859    c->transforms.current->multiply(rhs);
860    c->transforms.invalidate();
861}
862
863static void orthof(
864        GLfloat left, GLfloat right,
865        GLfloat bottom, GLfloat top,
866        GLfloat zNear, GLfloat zFar,
867        ogles_context_t* c)
868{
869    if (cmpf(left,right) ||
870        cmpf(top, bottom) ||
871        cmpf(zNear, zFar))
872    {
873        ogles_error(c, GL_INVALID_VALUE);
874        return;
875    }
876    const GLfloat r_width  = reciprocalf(right - left);
877    const GLfloat r_height = reciprocalf(top - bottom);
878    const GLfloat r_depth  = reciprocalf(zFar - zNear);
879    const GLfloat x =  mul2f(r_width);
880    const GLfloat y =  mul2f(r_height);
881    const GLfloat z = -mul2f(r_depth);
882    const GLfloat tx = -(right + left) * r_width;
883    const GLfloat ty = -(top + bottom) * r_height;
884    const GLfloat tz = -(zFar + zNear) * r_depth;
885    GLfloat f[16];
886    f[ 0] = x;
887    f[ 5] = y;
888    f[10] = z;
889    f[12] = tx;
890    f[13] = ty;
891    f[14] = tz;
892    f[15] = 1.0f;
893    f[ 1] = f[ 2] = f[ 3] =
894    f[ 4] = f[ 6] = f[ 7] =
895    f[ 8] = f[ 9] = f[11] = 0.0f;
896    matrixf_t rhs;
897    rhs.set(f);
898    c->transforms.current->multiply(rhs);
899    c->transforms.invalidate();
900}
901
902static void depthRangef(GLclampf zNear, GLclampf zFar, ogles_context_t* c)
903{
904    zNear = clampToZerof(zNear > 1 ? 1 : zNear);
905    zFar  = clampToZerof(zFar  > 1 ? 1 : zFar);
906    GLfloat* const f = c->transforms.vpt.matrix.editElements();
907    f[10] = div2f(zFar - zNear);
908    f[14] = div2f(zFar + zNear);
909    c->transforms.dirty |= transform_state_t::VIEWPORT;
910    c->transforms.vpt.zNear = zNear;
911    c->transforms.vpt.zFar  = zFar;
912}
913
914
915// ----------------------------------------------------------------------------
916}; // namespace android
917
918using namespace android;
919
920void glMatrixMode(GLenum mode)
921{
922    ogles_context_t* c = ogles_context_t::get();
923    matrix_stack_t* stack = 0;
924    switch (mode) {
925    case GL_MODELVIEW:
926        stack = &c->transforms.modelview;
927        break;
928    case GL_PROJECTION:
929        stack = &c->transforms.projection;
930        break;
931    case GL_TEXTURE:
932        stack = &c->transforms.texture[c->textures.active];
933        break;
934    default:
935        ogles_error(c, GL_INVALID_ENUM);
936        return;
937    }
938    c->transforms.matrixMode = mode;
939    c->transforms.current = stack;
940}
941
942void glLoadIdentity()
943{
944    ogles_context_t* c = ogles_context_t::get();
945    c->transforms.current->loadIdentity(); // also loads the GLfixed transform
946    c->transforms.invalidate();
947    c->transforms.current->dirty = 0;
948}
949
950void glLoadMatrixf(const GLfloat* m)
951{
952    ogles_context_t* c = ogles_context_t::get();
953    c->transforms.current->load(m);
954    c->transforms.invalidate();
955}
956
957void glLoadMatrixx(const GLfixed* m)
958{
959    ogles_context_t* c = ogles_context_t::get();
960    c->transforms.current->load(m); // also loads the GLfixed transform
961    c->transforms.invalidate();
962    c->transforms.current->dirty &= ~matrix_stack_t::DO_FLOAT_TO_FIXED;
963}
964
965void glMultMatrixf(const GLfloat* m)
966{
967    ogles_context_t* c = ogles_context_t::get();
968    matrixf_t rhs;
969    rhs.set(m);
970    c->transforms.current->multiply(rhs);
971    c->transforms.invalidate();
972}
973
974void glMultMatrixx(const GLfixed* m)
975{
976    ogles_context_t* c = ogles_context_t::get();
977    matrixf_t rhs;
978    rhs.set(m);
979    c->transforms.current->multiply(rhs);
980    c->transforms.invalidate();
981}
982
983void glPopMatrix()
984{
985    ogles_context_t* c = ogles_context_t::get();
986    GLint err = c->transforms.current->pop();
987    if (ggl_unlikely(err)) {
988        ogles_error(c, err);
989        return;
990    }
991    c->transforms.invalidate();
992}
993
994void glPushMatrix()
995{
996    ogles_context_t* c = ogles_context_t::get();
997    GLint err = c->transforms.current->push();
998    if (ggl_unlikely(err)) {
999        ogles_error(c, err);
1000        return;
1001    }
1002    c->transforms.invalidate();
1003}
1004
1005void glFrustumf(
1006        GLfloat left, GLfloat right,
1007        GLfloat bottom, GLfloat top,
1008        GLfloat zNear, GLfloat zFar)
1009{
1010    ogles_context_t* c = ogles_context_t::get();
1011    frustumf(left, right, bottom, top, zNear, zFar, c);
1012}
1013
1014void glFrustumx(
1015        GLfixed left, GLfixed right,
1016        GLfixed bottom, GLfixed top,
1017        GLfixed zNear, GLfixed zFar)
1018{
1019    ogles_context_t* c = ogles_context_t::get();
1020    frustumf( fixedToFloat(left), fixedToFloat(right),
1021              fixedToFloat(bottom), fixedToFloat(top),
1022              fixedToFloat(zNear), fixedToFloat(zFar),
1023              c);
1024}
1025
1026void glOrthof(
1027        GLfloat left, GLfloat right,
1028        GLfloat bottom, GLfloat top,
1029        GLfloat zNear, GLfloat zFar)
1030{
1031    ogles_context_t* c = ogles_context_t::get();
1032    orthof(left, right, bottom, top, zNear, zFar, c);
1033}
1034
1035void glOrthox(
1036        GLfixed left, GLfixed right,
1037        GLfixed bottom, GLfixed top,
1038        GLfixed zNear, GLfixed zFar)
1039{
1040    ogles_context_t* c = ogles_context_t::get();
1041    orthof( fixedToFloat(left), fixedToFloat(right),
1042            fixedToFloat(bottom), fixedToFloat(top),
1043            fixedToFloat(zNear), fixedToFloat(zFar),
1044            c);
1045}
1046
1047void glRotatef(GLfloat a, GLfloat x, GLfloat y, GLfloat z)
1048{
1049    ogles_context_t* c = ogles_context_t::get();
1050    c->transforms.current->rotate(a, x, y, z);
1051    c->transforms.invalidate();
1052}
1053
1054void glRotatex(GLfixed a, GLfixed x, GLfixed y, GLfixed z)
1055{
1056    ogles_context_t* c = ogles_context_t::get();
1057    c->transforms.current->rotate(
1058            fixedToFloat(a), fixedToFloat(x),
1059            fixedToFloat(y), fixedToFloat(z));
1060    c->transforms.invalidate();
1061}
1062
1063void glScalef(GLfloat x, GLfloat y, GLfloat z)
1064{
1065    ogles_context_t* c = ogles_context_t::get();
1066    c->transforms.current->scale(x, y, z);
1067    c->transforms.invalidate();
1068}
1069
1070void glScalex(GLfixed x, GLfixed y, GLfixed z)
1071{
1072    ogles_context_t* c = ogles_context_t::get();
1073    c->transforms.current->scale(
1074            fixedToFloat(x), fixedToFloat(y), fixedToFloat(z));
1075    c->transforms.invalidate();
1076}
1077
1078void glTranslatef(GLfloat x, GLfloat y, GLfloat z)
1079{
1080    ogles_context_t* c = ogles_context_t::get();
1081    c->transforms.current->translate(x, y, z);
1082    c->transforms.invalidate();
1083}
1084
1085void glTranslatex(GLfixed x, GLfixed y, GLfixed z)
1086{
1087    ogles_context_t* c = ogles_context_t::get();
1088    c->transforms.current->translate(
1089            fixedToFloat(x), fixedToFloat(y), fixedToFloat(z));
1090    c->transforms.invalidate();
1091}
1092
1093void glScissor(GLint x, GLint y, GLsizei w, GLsizei h)
1094{
1095    ogles_context_t* c = ogles_context_t::get();
1096    ogles_scissor(c, x, y, w, h);
1097}
1098
1099void glViewport(GLint x, GLint y, GLsizei w, GLsizei h)
1100{
1101    ogles_context_t* c = ogles_context_t::get();
1102    ogles_viewport(c, x, y, w, h);
1103}
1104
1105void glDepthRangef(GLclampf zNear, GLclampf zFar)
1106{
1107    ogles_context_t* c = ogles_context_t::get();
1108    depthRangef(zNear, zFar, c);
1109}
1110
1111void glDepthRangex(GLclampx zNear, GLclampx zFar)
1112{
1113    ogles_context_t* c = ogles_context_t::get();
1114    depthRangef(fixedToFloat(zNear), fixedToFloat(zFar), c);
1115}
1116
1117void glPolygonOffsetx(GLfixed factor, GLfixed units)
1118{
1119    ogles_context_t* c = ogles_context_t::get();
1120    c->polygonOffset.factor = factor;
1121    c->polygonOffset.units = units;
1122}
1123
1124void glPolygonOffset(GLfloat factor, GLfloat units)
1125{
1126    ogles_context_t* c = ogles_context_t::get();
1127    c->polygonOffset.factor = gglFloatToFixed(factor);
1128    c->polygonOffset.units = gglFloatToFixed(units);
1129}
1130
1131GLbitfield glQueryMatrixxOES(GLfixed* m, GLint* e)
1132{
1133    ogles_context_t* c = ogles_context_t::get();
1134    GLbitfield status = 0;
1135    GLfloat const* f = c->transforms.current->top().elements();
1136    for  (int i=0 ; i<16 ; i++) {
1137        if (isnan(f[i]) || isinf(f[i])) {
1138            status |= 1<<i;
1139            continue;
1140        }
1141        e[i] = exponent(f[i]) - 7;
1142        m[i] = mantissa(f[i]);
1143    }
1144    return status;
1145}
1146