/* libs/opengles/light.cpp
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License"); 
** you may not use this file except in compliance with the License. 
** You may obtain a copy of the License at 
**
**     http://www.apache.org/licenses/LICENSE-2.0 
**
** Unless required by applicable law or agreed to in writing, software 
** distributed under the License is distributed on an "AS IS" BASIS, 
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
** See the License for the specific language governing permissions and 
** limitations under the License.
*/

#include <stdio.h>
#include "context.h"
#include "fp.h"
#include "light.h"
#include "state.h"
#include "matrix.h"


#if defined(__arm__) && defined(__thumb__)
#warning "light.cpp should not be compiled in thumb on ARM."
#endif

namespace android {

// ----------------------------------------------------------------------------

static void invalidate_lighting(ogles_context_t* c);
static void lightVertexValidate(ogles_context_t* c, vertex_t* v);
static void lightVertexNop(ogles_context_t* c, vertex_t* v);
static void lightVertex(ogles_context_t* c, vertex_t* v);
static void lightVertexMaterial(ogles_context_t* c, vertex_t* v);

static inline void vscale3(GLfixed* d, const GLfixed* m, GLfixed s);

static __attribute__((noinline))
void vnorm3(GLfixed* d, const GLfixed* a);

static inline void vsa3(GLfixed* d,
    const GLfixed* m, GLfixed s, const GLfixed* a);
static inline void vss3(GLfixed* d,
    const GLfixed* m, GLfixed s, const GLfixed* a);
static inline void vmla3(GLfixed* d,
    const GLfixed* m0, const GLfixed* m1, const GLfixed* a);
static inline void vmul3(GLfixed* d,
    const GLfixed* m0, const GLfixed* m1);

static GLfixed fog_linear(ogles_context_t* c, GLfixed z);
static GLfixed fog_exp(ogles_context_t* c, GLfixed z);
static GLfixed fog_exp2(ogles_context_t* c, GLfixed z);


// ----------------------------------------------------------------------------

static void init_white(vec4_t& c) {
    c.r = c.g = c.b = c.a = 0x10000;
}

void ogles_init_light(ogles_context_t* c)
{
    for (unsigned int i=0 ; i<OGLES_MAX_LIGHTS ; i++) {
        c->lighting.lights[i].ambient.a = 0x10000;
        c->lighting.lights[i].position.z = 0x10000;
        c->lighting.lights[i].spotDir.z = -0x10000;
        c->lighting.lights[i].spotCutoff = gglIntToFixed(180);
        c->lighting.lights[i].attenuation[0] = 0x10000;
    }
    init_white(c->lighting.lights[0].diffuse);
    init_white(c->lighting.lights[0].specular);

    c->lighting.front.ambient.r =
    c->lighting.front.ambient.g =
    c->lighting.front.ambient.b = gglFloatToFixed(0.2f);
    c->lighting.front.ambient.a = 0x10000;
    c->lighting.front.diffuse.r =
    c->lighting.front.diffuse.g =
    c->lighting.front.diffuse.b = gglFloatToFixed(0.8f);
    c->lighting.front.diffuse.a = 0x10000;
    c->lighting.front.specular.a = 0x10000;
    c->lighting.front.emission.a = 0x10000;

    c->lighting.lightModel.ambient.r =
    c->lighting.lightModel.ambient.g =
    c->lighting.lightModel.ambient.b = gglFloatToFixed(0.2f);
    c->lighting.lightModel.ambient.a = 0x10000;

    c->lighting.colorMaterial.face = GL_FRONT_AND_BACK;
    c->lighting.colorMaterial.mode = GL_AMBIENT_AND_DIFFUSE;

    c->fog.mode = GL_EXP;
    c->fog.fog = fog_exp;
    c->fog.density = 0x10000;
    c->fog.end = 0x10000;
    c->fog.invEndMinusStart = 0x10000;

    invalidate_lighting(c);
       
    c->rasterizer.procs.shadeModel(c, GL_SMOOTH);
    c->lighting.shadeModel = GL_SMOOTH;
}

void ogles_uninit_light(ogles_context_t* /*c*/)
{
}

static inline int32_t clampF(GLfixed f) CONST;
int32_t clampF(GLfixed f) {
    f = (f & ~(f>>31));
    if (f >= 0x10000)
        f = 0x10000;
    return f;
}

static GLfixed fog_linear(ogles_context_t* c, GLfixed z) {
    return clampF(gglMulx((c->fog.end - ((z<0)?-z:z)), c->fog.invEndMinusStart));
}

static GLfixed fog_exp(ogles_context_t* c, GLfixed z) {
    const float e = fixedToFloat(gglMulx(c->fog.density, ((z<0)?-z:z)));
    return clampF(gglFloatToFixed(fastexpf(-e)));
}

static GLfixed fog_exp2(ogles_context_t* c, GLfixed z) {
    const float e = fixedToFloat(gglMulx(c->fog.density, z));
    return clampF(gglFloatToFixed(fastexpf(-e*e)));
}

// ----------------------------------------------------------------------------
#if 0
#pragma mark -
#pragma mark math helpers
#endif

static inline
void vscale3(GLfixed* d, const GLfixed* m, GLfixed s) {
    d[0] = gglMulx(m[0], s);
    d[1] = gglMulx(m[1], s);
    d[2] = gglMulx(m[2], s);
}

static inline
void vsa3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) {
    d[0] = gglMulAddx(m[0], s, a[0]);
    d[1] = gglMulAddx(m[1], s, a[1]);
    d[2] = gglMulAddx(m[2], s, a[2]);
}

static inline
void vss3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) {
    d[0] = gglMulSubx(m[0], s, a[0]);
    d[1] = gglMulSubx(m[1], s, a[1]);
    d[2] = gglMulSubx(m[2], s, a[2]);
}

static inline
void vmla3(GLfixed* d,
        const GLfixed* m0, const GLfixed* m1, const GLfixed* a)
{
    d[0] = gglMulAddx(m0[0], m1[0], a[0]);
    d[1] = gglMulAddx(m0[1], m1[1], a[1]);
    d[2] = gglMulAddx(m0[2], m1[2], a[2]);
}

static inline
void vmul3(GLfixed* d, const GLfixed* m0, const GLfixed* m1) {
    d[0] = gglMulx(m0[0], m1[0]);
    d[1] = gglMulx(m0[1], m1[1]);
    d[2] = gglMulx(m0[2], m1[2]);
}

void vnorm3(GLfixed* d, const GLfixed* a)
{
    // we must take care of overflows when normalizing a vector
    GLfixed n;
    int32_t x = a[0];   x = x>=0 ? x : -x;
    int32_t y = a[1];   y = y>=0 ? y : -y;
    int32_t z = a[2];   z = z>=0 ? z : -z;
    if (ggl_likely(x<=0x6800 && y<=0x6800 && z<= 0x6800)) {
        // in this case this will all fit on 32 bits
        n = x*x + y*y + z*z;
        n = gglSqrtRecipx(n);
        n <<= 8;
    } else {
        // here norm^2 is at least 0x7EC00000 (>>32 == 0.495117)
        n = vsquare3(x, y, z);
        n = gglSqrtRecipx(n);
    }
    vscale3(d, a, n);
}

// ----------------------------------------------------------------------------
#if 0
#pragma mark -
#pragma mark lighting equations
#endif

static inline void light_picker(ogles_context_t* c)
{
    if (ggl_likely(!c->lighting.enable)) {
        c->lighting.lightVertex = lightVertexNop;
        return;
    }
    if (c->lighting.colorMaterial.enable) {
        c->lighting.lightVertex = lightVertexMaterial;
    } else {
        c->lighting.lightVertex = lightVertex;
    }
}

static inline void validate_light_mvi(ogles_context_t* c)
{
    uint32_t en = c->lighting.enabledLights;
    // Vector from object to viewer, in eye coordinates
    while (en) {
        const int i = 31 - gglClz(en);
        en &= ~(1<<i);
        light_t& l = c->lighting.lights[i];
#if OBJECT_SPACE_LIGHTING
        c->transforms.mvui.point4(&c->transforms.mvui,
                &l.objPosition, &l.position);
#else
        l.objPosition = l.position;
#endif
        vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
    }
    const vec4_t eyeViewer = {{{ 0, 0, 0x10000, 0 }}};
#if OBJECT_SPACE_LIGHTING
    c->transforms.mvui.point3(&c->transforms.mvui,
            &c->lighting.objViewer, &eyeViewer);
    vnorm3(c->lighting.objViewer.v, c->lighting.objViewer.v);
#else
    c->lighting.objViewer = eyeViewer;
#endif
}

static inline void validate_light(ogles_context_t* c)
{
    // if colorMaterial is enabled, we get the color from the vertex
    if (!c->lighting.colorMaterial.enable) {
        material_t& material = c->lighting.front;
        uint32_t en = c->lighting.enabledLights;
        while (en) {
            const int i = 31 - gglClz(en);
            en &= ~(1<<i);
            light_t& l = c->lighting.lights[i];
            vmul3(l.implicitAmbient.v,  material.ambient.v,  l.ambient.v);
            vmul3(l.implicitDiffuse.v,  material.diffuse.v,  l.diffuse.v);
            vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v);
            
            // this is just a flag to tell if we have a specular component
            l.implicitSpecular.v[3] =
                    l.implicitSpecular.r |
                    l.implicitSpecular.g |
                    l.implicitSpecular.b;
            
            l.rConstAttenuation = (l.attenuation[1] | l.attenuation[2])==0;
            if (l.rConstAttenuation)
                l.rConstAttenuation = gglRecipFast(l.attenuation[0]);
        }
        // emission and ambient for the whole scene
        vmla3(  c->lighting.implicitSceneEmissionAndAmbient.v,
                c->lighting.lightModel.ambient.v,
                material.ambient.v, 
                material.emission.v);
        c->lighting.implicitSceneEmissionAndAmbient.a = material.diffuse.a;
    }
    validate_light_mvi(c);
}

void invalidate_lighting(ogles_context_t* c)
{
    // TODO: pick lightVertexValidate or lightVertexValidateMVI
    // instead of systematically the heavier lightVertexValidate()
    c->lighting.lightVertex = lightVertexValidate;
}

void ogles_invalidate_lighting_mvui(ogles_context_t* c)
{
    invalidate_lighting(c);
}

void lightVertexNop(ogles_context_t*, vertex_t* /*v*/)
{
    // we should never end-up here
}

void lightVertexValidateMVI(ogles_context_t* c, vertex_t* v)
{
    validate_light_mvi(c);
    light_picker(c);
    c->lighting.lightVertex(c, v);
}

void lightVertexValidate(ogles_context_t* c, vertex_t* v)
{
    validate_light(c);
    light_picker(c);
    c->lighting.lightVertex(c, v);
}

void lightVertexMaterial(ogles_context_t* c, vertex_t* v)
{
    // fetch the material color
    const GLvoid* cp = c->arrays.color.element(
            v->index & vertex_cache_t::INDEX_MASK);
    c->arrays.color.fetch(c, v->color.v, cp);

    // acquire the color-material from the vertex
    material_t& material = c->lighting.front;
    material.ambient =
    material.diffuse = v->color;
    // implicit arguments need to be computed per/vertex
    uint32_t en = c->lighting.enabledLights;
    while (en) {
        const int i = 31 - gglClz(en);
        en &= ~(1<<i);
        light_t& l = c->lighting.lights[i];
        vmul3(l.implicitAmbient.v,  material.ambient.v,  l.ambient.v);
        vmul3(l.implicitDiffuse.v,  material.diffuse.v,  l.diffuse.v);
        vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v);
        // this is just a flag to tell if we have a specular component
        l.implicitSpecular.v[3] =
                l.implicitSpecular.r |
                l.implicitSpecular.g |
                l.implicitSpecular.b;
    }
    // emission and ambient for the whole scene
    vmla3(  c->lighting.implicitSceneEmissionAndAmbient.v,
            c->lighting.lightModel.ambient.v,
            material.ambient.v, 
            material.emission.v);
    c->lighting.implicitSceneEmissionAndAmbient.a = material.diffuse.a;

    // now we can light our vertex as usual
    lightVertex(c, v);
}

void lightVertex(ogles_context_t* c, vertex_t* v)
{
    // emission and ambient for the whole scene
    vec4_t r = c->lighting.implicitSceneEmissionAndAmbient;
    const vec4_t objViewer = c->lighting.objViewer;

    uint32_t en = c->lighting.enabledLights;
    if (ggl_likely(en)) {
        // since we do the lighting in object-space, we don't need to
        // transform each normal. However, we might still have to normalize
        // it if GL_NORMALIZE is enabled.
        vec4_t n;
        c->arrays.normal.fetch(c, n.v,
            c->arrays.normal.element(v->index & vertex_cache_t::INDEX_MASK));

#if !OBJECT_SPACE_LIGHTING
        c->transforms.mvui.point3(&c->transforms.mvui, &n, &n);
#endif

        // TODO: right now we handle GL_RESCALE_NORMALS as if it were
        // GL_NORMALIZE. We could optimize this by  scaling mvui 
        // appropriately instead.
        if (c->transforms.rescaleNormals)
            vnorm3(n.v, n.v);

        const material_t& material = c->lighting.front;
        const int twoSide = c->lighting.lightModel.twoSide;

        while (en) {
            const int i = 31 - gglClz(en);
            en &= ~(1<<i);
            const light_t& l = c->lighting.lights[i];
            
            vec4_t d, t;
            GLfixed s;
            GLfixed sqDist = 0x10000;

            // compute vertex-to-light vector
            if (ggl_unlikely(l.position.w)) {
                // lightPos/1.0 - vertex/vertex.w == lightPos*vertex.w - vertex
#if !OBJECT_SPACE_LIGHTING
                vec4_t o;
                const transform_t& mv = c->transforms.modelview.transform;
                mv.point4(&mv, &o, &v->obj);
                vss3(d.v, l.objPosition.v, o.w, o.v);
#else
                vss3(d.v, l.objPosition.v, v->obj.w, v->obj.v);
#endif
                sqDist = dot3(d.v, d.v);
                vscale3(d.v, d.v, gglSqrtRecipx(sqDist));
            } else {
                // TODO: avoid copy here
                d = l.normalizedObjPosition;
            }

            // ambient & diffuse
            s = dot3(n.v, d.v);
            s = (s<0) ? (twoSide?(-s):0) : s;
            vsa3(t.v, l.implicitDiffuse.v, s, l.implicitAmbient.v);

            // specular
            if (ggl_unlikely(s && l.implicitSpecular.v[3])) {
                vec4_t h;
                h.x = d.x + objViewer.x;
                h.y = d.y + objViewer.y;
                h.z = d.z + objViewer.z;
                vnorm3(h.v, h.v);
                s = dot3(n.v, h.v);
                s = (s<0) ? (twoSide?(-s):0) : s;
                if (s > 0) {
                    s = gglPowx(s, material.shininess);
                    vsa3(t.v, l.implicitSpecular.v, s, t.v);
                }
            }

            // spot
            if (ggl_unlikely(l.spotCutoff != gglIntToFixed(180))) {
                GLfixed spotAtt = -dot3(l.normalizedSpotDir.v, d.v);
                if (spotAtt >= l.spotCutoffCosine) {
                    vscale3(t.v, t.v, gglPowx(spotAtt, l.spotExp));
                }
            }

            // attenuation
            if (ggl_unlikely(l.position.w)) {
                if (l.rConstAttenuation) {
                    s = l.rConstAttenuation;
                } else {
                    s = gglMulAddx(sqDist, l.attenuation[2], l.attenuation[0]);
                    if (l.attenuation[1])
                        s = gglMulAddx(gglSqrtx(sqDist), l.attenuation[1], s);
                    s = gglRecipFast(s);
                }
                vscale3(t.v, t.v, s);
            }

            r.r += t.r;
            r.g += t.g;
            r.b += t.b;
        }
    }
    v->color.r = gglClampx(r.r);
    v->color.g = gglClampx(r.g);
    v->color.b = gglClampx(r.b);
    v->color.a = gglClampx(r.a);
    v->flags |= vertex_t::LIT;
}

static void lightModelx(GLenum pname, GLfixed param, ogles_context_t* c)
{
    if (ggl_unlikely(pname != GL_LIGHT_MODEL_TWO_SIDE)) {
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }
    c->lighting.lightModel.twoSide = param ? GL_TRUE : GL_FALSE;
    invalidate_lighting(c);
}

static void lightx(GLenum i, GLenum pname, GLfixed param, ogles_context_t* c)
{
    if (ggl_unlikely(uint32_t(i-GL_LIGHT0) >= OGLES_MAX_LIGHTS)) {
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }

    light_t& light = c->lighting.lights[i-GL_LIGHT0];
    switch (pname) {
    case GL_SPOT_EXPONENT:
        if (GGLfixed(param) >= gglIntToFixed(128)) {
            ogles_error(c, GL_INVALID_VALUE);
            return;
        }
        light.spotExp = param;
        break;
    case GL_SPOT_CUTOFF:
        if (param!=gglIntToFixed(180) && GGLfixed(param)>=gglIntToFixed(90)) {
            ogles_error(c, GL_INVALID_VALUE);
            return;
        }
        light.spotCutoff = param;
        light.spotCutoffCosine = 
                gglFloatToFixed(cosinef((M_PI/(180.0f*65536.0f))*param));
        break;
    case GL_CONSTANT_ATTENUATION:
        if (param < 0) {
            ogles_error(c, GL_INVALID_VALUE);
            return;
        }
        light.attenuation[0] = param;
        break;
    case GL_LINEAR_ATTENUATION:
        if (param < 0) {
            ogles_error(c, GL_INVALID_VALUE);
            return;
        }
        light.attenuation[1] = param;
        break;
    case GL_QUADRATIC_ATTENUATION:
        if (param < 0) {
            ogles_error(c, GL_INVALID_VALUE);
            return;
        }
        light.attenuation[2] = param;
        break;
    default:
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }
    invalidate_lighting(c);
}

static void lightxv(GLenum i, GLenum pname, const GLfixed *params, ogles_context_t* c)
{
    if (ggl_unlikely(uint32_t(i-GL_LIGHT0) >= OGLES_MAX_LIGHTS)) {
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }

    GLfixed* what;
    light_t& light = c->lighting.lights[i-GL_LIGHT0];
    switch (pname) {
    case GL_AMBIENT:
        what = light.ambient.v;
        break;
    case GL_DIFFUSE:
        what = light.diffuse.v;
        break;
    case GL_SPECULAR:
        what = light.specular.v;
        break;
    case GL_POSITION: {
        ogles_validate_transform(c, transform_state_t::MODELVIEW);
        transform_t& mv = c->transforms.modelview.transform;
        mv.point4(&mv, &light.position, reinterpret_cast<vec4_t const*>(params));
        invalidate_lighting(c);
        return;
    }
    case GL_SPOT_DIRECTION: {
#if OBJECT_SPACE_LIGHTING
        ogles_validate_transform(c, transform_state_t::MVUI);
        transform_t& mvui = c->transforms.mvui;
        mvui.point3(&mvui, &light.spotDir, reinterpret_cast<vec4_t const*>(params));
#else
        light.spotDir = *reinterpret_cast<vec4_t const*>(params);
#endif
        vnorm3(light.normalizedSpotDir.v, light.spotDir.v);
        invalidate_lighting(c);
        return;
    }
    default:
        lightx(i, pname, params[0], c);
        return;
    }
    what[0] = params[0];
    what[1] = params[1];
    what[2] = params[2];
    what[3] = params[3];
    invalidate_lighting(c);
}

static void materialx(GLenum face, GLenum pname, GLfixed param, ogles_context_t* c)
{
    if (ggl_unlikely(face != GL_FRONT_AND_BACK)) {
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }
    if (ggl_unlikely(pname != GL_SHININESS)) {
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }
    c->lighting.front.shininess = param;
    invalidate_lighting(c);
}

static void fogx(GLenum pname, GLfixed param, ogles_context_t* c)
{
    switch (pname) {
    case GL_FOG_DENSITY:
        if (param >= 0) {
            c->fog.density = param;
            break;
        }
        ogles_error(c, GL_INVALID_VALUE);
        break;
    case GL_FOG_START:
        c->fog.start = param;
        c->fog.invEndMinusStart = gglRecip(c->fog.end - c->fog.start);
        break;
    case GL_FOG_END:
        c->fog.end = param;
        c->fog.invEndMinusStart = gglRecip(c->fog.end - c->fog.start);
        break;
    case GL_FOG_MODE:
        switch (param) {
        case GL_LINEAR:
            c->fog.mode = param;
            c->fog.fog = fog_linear;
            break;
        case GL_EXP:
            c->fog.mode = param;
            c->fog.fog = fog_exp;
            break;
        case GL_EXP2:
            c->fog.mode = param;
            c->fog.fog = fog_exp2;
            break;
        default:
            ogles_error(c, GL_INVALID_ENUM);
            break;
        }
        break;
    default:
        ogles_error(c, GL_INVALID_ENUM);
        break;
    }
}

// ----------------------------------------------------------------------------
}; // namespace android
// ----------------------------------------------------------------------------

using namespace android;

#if 0
#pragma mark -
#pragma mark lighting APIs
#endif

void glShadeModel(GLenum mode)
{
    ogles_context_t* c = ogles_context_t::get();
    if (ggl_unlikely(mode != GL_SMOOTH && mode != GL_FLAT)) {
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }
    c->lighting.shadeModel = mode;
}

void glLightModelf(GLenum pname, GLfloat param)
{
    ogles_context_t* c = ogles_context_t::get();
    lightModelx(pname, gglFloatToFixed(param), c);
}

void glLightModelx(GLenum pname, GLfixed param)
{
    ogles_context_t* c = ogles_context_t::get();
    lightModelx(pname, param, c);
}

void glLightModelfv(GLenum pname, const GLfloat *params)
{
    ogles_context_t* c = ogles_context_t::get();
    if (pname == GL_LIGHT_MODEL_TWO_SIDE) {
        lightModelx(pname, gglFloatToFixed(params[0]), c);
        return;
    }

    if (ggl_unlikely(pname != GL_LIGHT_MODEL_AMBIENT)) {
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }

    c->lighting.lightModel.ambient.r = gglFloatToFixed(params[0]);
    c->lighting.lightModel.ambient.g = gglFloatToFixed(params[1]);
    c->lighting.lightModel.ambient.b = gglFloatToFixed(params[2]);
    c->lighting.lightModel.ambient.a = gglFloatToFixed(params[3]);
    invalidate_lighting(c);
}

void glLightModelxv(GLenum pname, const GLfixed *params)
{
    ogles_context_t* c = ogles_context_t::get();
    if (pname == GL_LIGHT_MODEL_TWO_SIDE) {
        lightModelx(pname, params[0], c);
        return;
    }

    if (ggl_unlikely(pname != GL_LIGHT_MODEL_AMBIENT)) {
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }

    c->lighting.lightModel.ambient.r = params[0];
    c->lighting.lightModel.ambient.g = params[1];
    c->lighting.lightModel.ambient.b = params[2];
    c->lighting.lightModel.ambient.a = params[3];
    invalidate_lighting(c);
}

// ----------------------------------------------------------------------------
#if 0
#pragma mark -
#endif

void glLightf(GLenum i, GLenum pname, GLfloat param)
{
    ogles_context_t* c = ogles_context_t::get();
    lightx(i, pname, gglFloatToFixed(param), c);
}

void glLightx(GLenum i, GLenum pname, GLfixed param)
{
    ogles_context_t* c = ogles_context_t::get();
    lightx(i, pname, param, c);
}

void glLightfv(GLenum i, GLenum pname, const GLfloat *params)
{
    ogles_context_t* c = ogles_context_t::get();
    switch (pname) {
    case GL_SPOT_EXPONENT:
    case GL_SPOT_CUTOFF:
    case GL_CONSTANT_ATTENUATION:
    case GL_LINEAR_ATTENUATION:
    case GL_QUADRATIC_ATTENUATION:
        lightx(i, pname, gglFloatToFixed(params[0]), c);
        return;
    }

    GLfixed paramsx[4];
    paramsx[0] = gglFloatToFixed(params[0]);
    paramsx[1] = gglFloatToFixed(params[1]);
    paramsx[2] = gglFloatToFixed(params[2]);
    if (pname != GL_SPOT_DIRECTION)
        paramsx[3] = gglFloatToFixed(params[3]);

    lightxv(i, pname, paramsx, c);
}

void glLightxv(GLenum i, GLenum pname, const GLfixed *params)
{
    ogles_context_t* c = ogles_context_t::get();
    lightxv(i, pname, params, c);
}

// ----------------------------------------------------------------------------
#if 0
#pragma mark -
#endif

void glMaterialf(GLenum face, GLenum pname, GLfloat param)
{
    ogles_context_t* c = ogles_context_t::get();
    materialx(face, pname, gglFloatToFixed(param), c);
}

void glMaterialx(GLenum face, GLenum pname, GLfixed param)
{
    ogles_context_t* c = ogles_context_t::get();
    materialx(face, pname, param, c);
}

void glMaterialfv(
    GLenum face, GLenum pname, const GLfloat *params)
{
    ogles_context_t* c = ogles_context_t::get();
    if (ggl_unlikely(face != GL_FRONT_AND_BACK)) {
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }
    GLfixed* what=0;
    GLfixed* other=0;
    switch (pname) {
    case GL_AMBIENT:    what = c->lighting.front.ambient.v; break;
    case GL_DIFFUSE:    what = c->lighting.front.diffuse.v; break;
    case GL_SPECULAR:   what = c->lighting.front.specular.v; break;
    case GL_EMISSION:   what = c->lighting.front.emission.v; break;
    case GL_AMBIENT_AND_DIFFUSE:
        what  = c->lighting.front.ambient.v;
        other = c->lighting.front.diffuse.v;
        break;
    case GL_SHININESS:
        c->lighting.front.shininess = gglFloatToFixed(params[0]);
        invalidate_lighting(c);
        return;
    default:
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }
    what[0] = gglFloatToFixed(params[0]);
    what[1] = gglFloatToFixed(params[1]);
    what[2] = gglFloatToFixed(params[2]);
    what[3] = gglFloatToFixed(params[3]);
    if (other) {
        other[0] = what[0];
        other[1] = what[1];
        other[2] = what[2];
        other[3] = what[3];
    }
    invalidate_lighting(c);
}

void glMaterialxv(
    GLenum face, GLenum pname, const GLfixed *params)
{
    ogles_context_t* c = ogles_context_t::get();
    if (ggl_unlikely(face != GL_FRONT_AND_BACK)) {
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }
    GLfixed* what=0;
    GLfixed* other=0;
    switch (pname) {
    case GL_AMBIENT:    what = c->lighting.front.ambient.v; break;
    case GL_DIFFUSE:    what = c->lighting.front.diffuse.v; break;
    case GL_SPECULAR:   what = c->lighting.front.specular.v; break;
    case GL_EMISSION:   what = c->lighting.front.emission.v; break;
    case GL_AMBIENT_AND_DIFFUSE:
        what  = c->lighting.front.ambient.v;
        other = c->lighting.front.diffuse.v;
        break;
    case GL_SHININESS:
        c->lighting.front.shininess = gglFloatToFixed(params[0]);
        invalidate_lighting(c);
        return;
    default:
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }
    what[0] = params[0];
    what[1] = params[1];
    what[2] = params[2];
    what[3] = params[3];
    if (other) {
        other[0] = what[0];
        other[1] = what[1];
        other[2] = what[2];
        other[3] = what[3];
    }
    invalidate_lighting(c);
}

// ----------------------------------------------------------------------------
#if 0
#pragma mark -
#pragma mark fog
#endif

void glFogf(GLenum pname, GLfloat param) {
    ogles_context_t* c = ogles_context_t::get();
    GLfixed paramx = (GLfixed)param;
    if (pname != GL_FOG_MODE)
        paramx = gglFloatToFixed(param);
    fogx(pname, paramx, c);
}

void glFogx(GLenum pname, GLfixed param) {
    ogles_context_t* c = ogles_context_t::get();
    fogx(pname, param, c);
}

void glFogfv(GLenum pname, const GLfloat *params)
{
    ogles_context_t* c = ogles_context_t::get();
    if (pname != GL_FOG_COLOR) {
        GLfixed paramx = (GLfixed)params[0];
        if (pname != GL_FOG_MODE)
            paramx = gglFloatToFixed(params[0]);
        fogx(pname, paramx, c);
        return;
    }
    GLfixed paramsx[4];
    paramsx[0] = gglFloatToFixed(params[0]);
    paramsx[1] = gglFloatToFixed(params[1]);
    paramsx[2] = gglFloatToFixed(params[2]);
    paramsx[3] = gglFloatToFixed(params[3]);
    c->rasterizer.procs.fogColor3xv(c, paramsx);
}

void glFogxv(GLenum pname, const GLfixed *params)
{
    ogles_context_t* c = ogles_context_t::get();
    if (pname != GL_FOG_COLOR) {
        fogx(pname, params[0], c);
        return;
    }
    c->rasterizer.procs.fogColor3xv(c, params);
}