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