os/graphics/m3g/m3gcore11/src/m3g_lightmanager.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /*
     2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
     3 * All rights reserved.
     4 * This component and the accompanying materials are made available
     5 * under the terms of the License "Eclipse Public License v1.0"
     6 * which accompanies this distribution, and is available
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
     8 *
     9 * Initial Contributors:
    10 * Nokia Corporation - initial contribution.
    11 *
    12 * Contributors:
    13 *
    14 * Description: Light manager implementation
    15 *
    16 */
    17 
    18 
    19 /*!
    20  * \internal
    21  * \file
    22  * \brief Light manager implementation
    23  */
    24 
    25 #ifndef M3G_CORE_INCLUDE
    26 #   error included by m3g_core.c; do not compile separately.
    27 #endif
    28 
    29 #include "m3g_lightmanager.h"
    30 #include "m3g_light.h"
    31 #include "m3g_rendercontext.h"
    32 #include "m3g_math.h"
    33 
    34 /*!
    35  * \internal
    36  * \brief Light array element
    37  */
    38 typedef struct
    39 {
    40     /*! \internal \brief eye space spot direction */
    41     Vec4 spotDir;
    42 
    43     /*! \internal \brief eye space position */
    44     Vec4 position;
    45     
    46     /*! \internal \brief reference to the Light node */
    47     Light *light;
    48 } LightRecord;
    49 
    50 /*----------------------------------------------------------------------
    51  * Private functions
    52  *--------------------------------------------------------------------*/
    53  
    54 /*!
    55  * \internal
    56  * \brief Update a single light record
    57  *
    58  * Light position and spot direction are transformed into world space,
    59  * i.e. omitting the viewing transformation which is applied by OpenGL
    60  * when rendering. Both transformations are special cases and
    61  * accomplished by just reading a part of the matrix.
    62  *
    63  * \note We need to transform the spotlight direction even if the
    64  * light is not a spotlight, since in immediate mode, it may change
    65  * into a spotlight later on!
    66  */
    67 static void m3gSetLightRecord(LightRecord *lrec,
    68                               Light *light,
    69                               const Matrix *tf)
    70 {
    71     Vec4 v;
    72     M3G_ASSIGN_REF(lrec->light, light);
    73 
    74     if (tf != NULL) {
    75         m3gGetMatrixColumn(tf, 3, &lrec->position);
    76         m3gGetMatrixColumn(tf, 2, &v);
    77         lrec->spotDir.x = -v.x;
    78         lrec->spotDir.y = -v.y;
    79         lrec->spotDir.z = -v.z;
    80         lrec->spotDir.w = 0.0f;
    81     }
    82     else {
    83         lrec->spotDir.x = lrec->spotDir.y = lrec->spotDir.w = 0.0f;
    84         lrec->spotDir.z = -1.0f;
    85         lrec->position.x = lrec->position.y = lrec->position.z = 0.0f;
    86         lrec->position.w = 1.0f;
    87     }
    88 }
    89 
    90 /*----------------------------------------------------------------------
    91  * Internal functions
    92  *--------------------------------------------------------------------*/
    93 
    94 /*!
    95  * \internal
    96  * \brief Clears all lights in the current pool of lights
    97  */
    98 static void m3gClearLights2(LightManager *mgr)
    99 {
   100     LightRecord *lrec;
   101     PointerArray *lights;
   102     int i, n;
   103     M3G_ASSERT_PTR(mgr);
   104 
   105     lights = &mgr->lights;
   106     n = m3gArraySize(lights);
   107     for (i = 0; i < n; ++i) {
   108         lrec = m3gGetArrayElement(lights, i);
   109         M3G_ASSIGN_REF(lrec->light, NULL);
   110     }
   111 
   112     mgr->numActive = 0;
   113 }
   114 
   115 /*!
   116  * \internal
   117  * \brief Destroys the light manager, freeing allocated resources
   118  */
   119 static void m3gDestroyLightManager(LightManager *mgr, Interface *m3g)
   120 {
   121     int i, n;
   122     M3G_ASSERT_PTR(mgr);
   123     M3G_VALIDATE_INTERFACE(m3g);
   124 
   125     /* First remove all light references */
   126     m3gClearLights2(mgr);
   127 
   128     /* Free the records currently in the light array */
   129     n = m3gArraySize(&mgr->lights);
   130     for (i = 0; i < n; ++i) {
   131         m3gFree(m3g, m3gGetArrayElement(&mgr->lights, i));
   132     }
   133     m3gDestroyArray(&mgr->lights, m3g);
   134 }
   135 
   136 /*!
   137  * \internal
   138  * \brief Appends a light at the end of the light manager array
   139  */
   140 static M3Gint m3gInsertLight(LightManager *mgr,
   141                              Light *light,
   142                              const Matrix *tf,
   143                              Interface *m3g)
   144 {
   145     LightRecord *lrec;
   146     PointerArray *lights;
   147     M3Gint idx;
   148     M3G_ASSERT_PTR(mgr);
   149     M3G_VALIDATE_INTERFACE(m3g);
   150 
   151     lights = &mgr->lights;
   152     
   153     /* Get the first unused light record, or add a new one */
   154     
   155     if (mgr->numActive < m3gArraySize(lights)) {
   156         lrec = m3gGetArrayElement(lights, mgr->numActive);
   157     }
   158     else {
   159         M3G_ASSERT(mgr->numActive == m3gArraySize(lights));
   160         lrec = m3gAllocZ(m3g, sizeof(LightRecord));
   161         if (lrec == NULL) {
   162             return -1;
   163         }
   164         if (m3gArrayAppend(lights, lrec, m3g) == -1) {
   165             return -1;
   166         }
   167     }
   168     idx = mgr->numActive++;
   169 
   170     m3gSetLightRecord(lrec, light, tf);
   171     return idx;
   172 }
   173 
   174 /*!
   175  * \internal
   176  * \brief
   177  */
   178 static M3Gsizei m3gLightArraySize(const LightManager *mgr)
   179 {
   180     M3G_ASSERT_PTR(mgr);
   181     return mgr->numActive;
   182 }
   183 
   184 /*!
   185  * \internal
   186  * \brief
   187  */
   188 static Light *m3gGetLightTransformInternal(const LightManager *mgr, M3Gint idx, M3GMatrix *transform)
   189 {
   190     M3Gfloat matrix[16];
   191     LightRecord *lrec;
   192     M3G_ASSERT_PTR(mgr);
   193     M3G_ASSERT(m3gInRange(idx, 0, mgr->numActive - 1));
   194 
   195     lrec = m3gGetArrayElement(&mgr->lights, idx);
   196 
   197     if (transform != NULL) {
   198         m3gZero(matrix, sizeof(matrix));
   199     
   200         matrix[0 * 4 + 0] = 1.f;
   201         matrix[1 * 4 + 1] = 1.f;
   202     
   203         matrix[2 * 4 + 0] = -lrec->spotDir.x;
   204         matrix[2 * 4 + 1] = -lrec->spotDir.y;
   205         matrix[2 * 4 + 2] = -lrec->spotDir.z;
   206     
   207         matrix[3 * 4 + 0] = lrec->position.x;
   208         matrix[3 * 4 + 1] = lrec->position.y;
   209         matrix[3 * 4 + 2] = lrec->position.z;
   210         matrix[3 * 4 + 3] = lrec->position.w;
   211     
   212         m3gSetMatrixColumns(transform, matrix);
   213     }
   214 
   215     return lrec->light;
   216 }
   217 
   218 /*!
   219  * \internal
   220  * \brief Replaces an existing light in the light array
   221  */
   222 static void m3gReplaceLight(LightManager *mgr,
   223                             M3Gint idx,
   224                             Light *light,
   225                             const Matrix *tf)
   226 {
   227     LightRecord *lrec;
   228     M3G_ASSERT_PTR(mgr);
   229     M3G_ASSERT(m3gInRange(idx, 0, mgr->numActive - 1));
   230 
   231     lrec = m3gGetArrayElement(&mgr->lights, idx);
   232     m3gSetLightRecord(lrec, light, tf);
   233 }
   234 
   235 /*!
   236  * \internal
   237  * \brief Selects a set of lights from the current light array for OpenGL
   238  *
   239  * Selects a maximum on \c maxNum lights from the array matching the
   240  * given scope, sets those into the current OpenGL context, and
   241  * disables the rest of the OpenGL lights GL_LIGHT0..GL_LIGHT7. A
   242  * maximum of 8 lights is ever used.
   243  *
   244  */
   245 static void m3gSelectGLLights(const LightManager *mgr,
   246                               M3Gsizei maxNum,
   247                               M3Guint scope,
   248                               M3Gfloat x, M3Gfloat y, M3Gfloat z)
   249 {
   250     const PointerArray *lights;
   251     int i, required, total;
   252     GLenum glIndex = GL_LIGHT0;
   253     M3G_ASSERT_PTR(mgr);
   254 
   255     M3G_UNREF(x);
   256     M3G_UNREF(y);
   257     M3G_UNREF(z);
   258 
   259     lights = &mgr->lights;
   260     required = m3gClampInt(maxNum, 0, 8);
   261     total = mgr->numActive;
   262 
   263     /* Select the first n lights that match the scope */
   264     
   265     for (i = 0; required > 0 && i < total; ++i) {
   266         const LightRecord *lrec;
   267         const Light *light;
   268         
   269         lrec = (const LightRecord *) m3gGetArrayElement(lights, i);
   270         M3G_ASSERT(lrec != NULL);
   271         light = lrec->light;
   272         
   273         if (light != NULL && (light->node.scope & scope) != 0) {
   274             m3gApplyLight(light, glIndex++, &lrec->position, &lrec->spotDir);
   275             --required;
   276         }
   277     }
   278 
   279     /* Disable the leftover lights */
   280     
   281     while (glIndex <= GL_LIGHT7) {
   282         glDisable(glIndex++);
   283     }
   284 }
   285 
   286 /*!
   287  * \internal
   288  * \brief Transforms all lights with the given matrix
   289  */
   290 static void m3gTransformLights(LightManager *mgr, const Matrix *mtx)
   291 {
   292     const PointerArray *lights;
   293     M3Gint i, n;
   294 
   295     lights = &mgr->lights;
   296     n = mgr->numActive;
   297     
   298     for (i = 0; i < n; ++i) {
   299         LightRecord *lrec = (LightRecord*) m3gGetArrayElement(lights, i);
   300         m3gTransformVec4(mtx, &lrec->position);
   301         m3gTransformVec4(mtx, &lrec->spotDir);
   302     }
   303 }
   304