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