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