diff -r 000000000000 -r bde4ae8d615e os/graphics/m3g/m3gcore11/src/m3g_lightmanager.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/graphics/m3g/m3gcore11/src/m3g_lightmanager.c Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,304 @@ +/* +* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Light manager implementation +* +*/ + + +/*! + * \internal + * \file + * \brief Light manager implementation + */ + +#ifndef M3G_CORE_INCLUDE +# error included by m3g_core.c; do not compile separately. +#endif + +#include "m3g_lightmanager.h" +#include "m3g_light.h" +#include "m3g_rendercontext.h" +#include "m3g_math.h" + +/*! + * \internal + * \brief Light array element + */ +typedef struct +{ + /*! \internal \brief eye space spot direction */ + Vec4 spotDir; + + /*! \internal \brief eye space position */ + Vec4 position; + + /*! \internal \brief reference to the Light node */ + Light *light; +} LightRecord; + +/*---------------------------------------------------------------------- + * Private functions + *--------------------------------------------------------------------*/ + +/*! + * \internal + * \brief Update a single light record + * + * Light position and spot direction are transformed into world space, + * i.e. omitting the viewing transformation which is applied by OpenGL + * when rendering. Both transformations are special cases and + * accomplished by just reading a part of the matrix. + * + * \note We need to transform the spotlight direction even if the + * light is not a spotlight, since in immediate mode, it may change + * into a spotlight later on! + */ +static void m3gSetLightRecord(LightRecord *lrec, + Light *light, + const Matrix *tf) +{ + Vec4 v; + M3G_ASSIGN_REF(lrec->light, light); + + if (tf != NULL) { + m3gGetMatrixColumn(tf, 3, &lrec->position); + m3gGetMatrixColumn(tf, 2, &v); + lrec->spotDir.x = -v.x; + lrec->spotDir.y = -v.y; + lrec->spotDir.z = -v.z; + lrec->spotDir.w = 0.0f; + } + else { + lrec->spotDir.x = lrec->spotDir.y = lrec->spotDir.w = 0.0f; + lrec->spotDir.z = -1.0f; + lrec->position.x = lrec->position.y = lrec->position.z = 0.0f; + lrec->position.w = 1.0f; + } +} + +/*---------------------------------------------------------------------- + * Internal functions + *--------------------------------------------------------------------*/ + +/*! + * \internal + * \brief Clears all lights in the current pool of lights + */ +static void m3gClearLights2(LightManager *mgr) +{ + LightRecord *lrec; + PointerArray *lights; + int i, n; + M3G_ASSERT_PTR(mgr); + + lights = &mgr->lights; + n = m3gArraySize(lights); + for (i = 0; i < n; ++i) { + lrec = m3gGetArrayElement(lights, i); + M3G_ASSIGN_REF(lrec->light, NULL); + } + + mgr->numActive = 0; +} + +/*! + * \internal + * \brief Destroys the light manager, freeing allocated resources + */ +static void m3gDestroyLightManager(LightManager *mgr, Interface *m3g) +{ + int i, n; + M3G_ASSERT_PTR(mgr); + M3G_VALIDATE_INTERFACE(m3g); + + /* First remove all light references */ + m3gClearLights2(mgr); + + /* Free the records currently in the light array */ + n = m3gArraySize(&mgr->lights); + for (i = 0; i < n; ++i) { + m3gFree(m3g, m3gGetArrayElement(&mgr->lights, i)); + } + m3gDestroyArray(&mgr->lights, m3g); +} + +/*! + * \internal + * \brief Appends a light at the end of the light manager array + */ +static M3Gint m3gInsertLight(LightManager *mgr, + Light *light, + const Matrix *tf, + Interface *m3g) +{ + LightRecord *lrec; + PointerArray *lights; + M3Gint idx; + M3G_ASSERT_PTR(mgr); + M3G_VALIDATE_INTERFACE(m3g); + + lights = &mgr->lights; + + /* Get the first unused light record, or add a new one */ + + if (mgr->numActive < m3gArraySize(lights)) { + lrec = m3gGetArrayElement(lights, mgr->numActive); + } + else { + M3G_ASSERT(mgr->numActive == m3gArraySize(lights)); + lrec = m3gAllocZ(m3g, sizeof(LightRecord)); + if (lrec == NULL) { + return -1; + } + if (m3gArrayAppend(lights, lrec, m3g) == -1) { + return -1; + } + } + idx = mgr->numActive++; + + m3gSetLightRecord(lrec, light, tf); + return idx; +} + +/*! + * \internal + * \brief + */ +static M3Gsizei m3gLightArraySize(const LightManager *mgr) +{ + M3G_ASSERT_PTR(mgr); + return mgr->numActive; +} + +/*! + * \internal + * \brief + */ +static Light *m3gGetLightTransformInternal(const LightManager *mgr, M3Gint idx, M3GMatrix *transform) +{ + M3Gfloat matrix[16]; + LightRecord *lrec; + M3G_ASSERT_PTR(mgr); + M3G_ASSERT(m3gInRange(idx, 0, mgr->numActive - 1)); + + lrec = m3gGetArrayElement(&mgr->lights, idx); + + if (transform != NULL) { + m3gZero(matrix, sizeof(matrix)); + + matrix[0 * 4 + 0] = 1.f; + matrix[1 * 4 + 1] = 1.f; + + matrix[2 * 4 + 0] = -lrec->spotDir.x; + matrix[2 * 4 + 1] = -lrec->spotDir.y; + matrix[2 * 4 + 2] = -lrec->spotDir.z; + + matrix[3 * 4 + 0] = lrec->position.x; + matrix[3 * 4 + 1] = lrec->position.y; + matrix[3 * 4 + 2] = lrec->position.z; + matrix[3 * 4 + 3] = lrec->position.w; + + m3gSetMatrixColumns(transform, matrix); + } + + return lrec->light; +} + +/*! + * \internal + * \brief Replaces an existing light in the light array + */ +static void m3gReplaceLight(LightManager *mgr, + M3Gint idx, + Light *light, + const Matrix *tf) +{ + LightRecord *lrec; + M3G_ASSERT_PTR(mgr); + M3G_ASSERT(m3gInRange(idx, 0, mgr->numActive - 1)); + + lrec = m3gGetArrayElement(&mgr->lights, idx); + m3gSetLightRecord(lrec, light, tf); +} + +/*! + * \internal + * \brief Selects a set of lights from the current light array for OpenGL + * + * Selects a maximum on \c maxNum lights from the array matching the + * given scope, sets those into the current OpenGL context, and + * disables the rest of the OpenGL lights GL_LIGHT0..GL_LIGHT7. A + * maximum of 8 lights is ever used. + * + */ +static void m3gSelectGLLights(const LightManager *mgr, + M3Gsizei maxNum, + M3Guint scope, + M3Gfloat x, M3Gfloat y, M3Gfloat z) +{ + const PointerArray *lights; + int i, required, total; + GLenum glIndex = GL_LIGHT0; + M3G_ASSERT_PTR(mgr); + + M3G_UNREF(x); + M3G_UNREF(y); + M3G_UNREF(z); + + lights = &mgr->lights; + required = m3gClampInt(maxNum, 0, 8); + total = mgr->numActive; + + /* Select the first n lights that match the scope */ + + for (i = 0; required > 0 && i < total; ++i) { + const LightRecord *lrec; + const Light *light; + + lrec = (const LightRecord *) m3gGetArrayElement(lights, i); + M3G_ASSERT(lrec != NULL); + light = lrec->light; + + if (light != NULL && (light->node.scope & scope) != 0) { + m3gApplyLight(light, glIndex++, &lrec->position, &lrec->spotDir); + --required; + } + } + + /* Disable the leftover lights */ + + while (glIndex <= GL_LIGHT7) { + glDisable(glIndex++); + } +} + +/*! + * \internal + * \brief Transforms all lights with the given matrix + */ +static void m3gTransformLights(LightManager *mgr, const Matrix *mtx) +{ + const PointerArray *lights; + M3Gint i, n; + + lights = &mgr->lights; + n = mgr->numActive; + + for (i = 0; i < n; ++i) { + LightRecord *lrec = (LightRecord*) m3gGetArrayElement(lights, i); + m3gTransformVec4(mtx, &lrec->position); + m3gTransformVec4(mtx, &lrec->spotDir); + } +} +