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