m3g/m3gcore11/src/m3g_light.c
author William Roberts <williamr@symbian.org>
Fri, 23 Jul 2010 11:46:39 +0100
changeset 126 0ee22b620a47
parent 0 5d03bc08d59c
permissions -rw-r--r--
Use buildrom ABI_DIR\BUILD_DIR conventions in the minigui .oby files

/*
* 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 implementation
*
*/


/*!
 * \internal
 * \file
 * \brief Light implementation
 */

#ifndef M3G_CORE_INCLUDE
#   error included by m3g_core.c; do not compile separately.
#endif

#include "m3g_light.h"
#include "m3g_animationtrack.h"

/*----------------------------------------------------------------------
 * Internal functions
 *--------------------------------------------------------------------*/

/*!
 * \internal
 * \brief Overloaded Node method.
 *
 * Insert light to render queue.
 *
 * \param self Light object
 * \param toCamera transform to camera
 * \param alphaFactor total alpha factor
 * \param caller caller node
 * \param renderQueue RenderQueue
 *
 * \retval M3G_TRUE continue render setup
 * \retval M3G_FALSE abort render setup
 */
static M3Gbool m3gLightSetupRender(Node *self,
                                   const Node *caller,
                                   SetupRenderState *s,
                                   RenderQueue *renderQueue)
{
    M3G_UNREF(caller);

    if (renderQueue->lightManager != NULL) {
        Light *light = (Light *)self;
        
        if (self->enableBits & NODE_RENDER_BIT) {
            if (m3gInsertLight(renderQueue->lightManager,
                               light, &s->toCamera, M3G_INTERFACE(light)) == -1)
                return M3G_FALSE;
        }
    }

    return M3G_TRUE;
}

/*!
 * \internal
 * \brief Overloaded Object3D method.
 *
 * \param property      animation property
 * \retval M3G_TRUE     property supported
 * \retval M3G_FALSE    property not supported
 */
static M3Gbool m3gLightIsCompatible(M3Gint property)
{
    switch (property) {
    case M3G_ANIM_COLOR:
    case M3G_ANIM_INTENSITY:
    case M3G_ANIM_SPOT_ANGLE:
    case M3G_ANIM_SPOT_EXPONENT:
        return M3G_TRUE;
    default:
        return m3gNodeIsCompatible(property);
    }
}

/*!
 * \internal
 * \brief Overloaded Object3D method.
 *
 * \param self          Light object
 * \param property      animation property
 * \param valueSize     size of value array
 * \param value         value array
 */
static void m3gLightUpdateProperty(Object *self,
                                   M3Gint property,
                                   M3Gint valueSize,
                                   const M3Gfloat *value)
{
    Light *light = (Light *)self;
    M3G_VALIDATE_OBJECT(light);
    M3G_ASSERT_PTR(value);
    
    switch (property) {
    case M3G_ANIM_COLOR:
        M3G_ASSERT(valueSize >= 3);
        light->color = m3gColor3f(value[0], value[1], value[2]);
        break;
    case M3G_ANIM_INTENSITY:
        M3G_ASSERT(valueSize >= 1);
        light->intensity = value[0];
        break;
    case M3G_ANIM_SPOT_ANGLE:
        M3G_ASSERT(valueSize >= 1);
        light->spotAngle = m3gClampFloat(value[0], 0.f, 90.f);
        break;
    case M3G_ANIM_SPOT_EXPONENT:
        M3G_ASSERT(valueSize >= 1);
        light->spotExponent = m3gClampFloat(value[0], 0.f, 128.f);
        break;
    default:
        m3gNodeUpdateProperty(self, property, valueSize, value);
    }
}

/*!
 * \internal
 * \brief Overloaded Object3D method.
 *
 * \param originalObj original Light object
 * \param cloneObj pointer to cloned Light object
 * \param pairs array for all object-duplicate pairs
 * \param numPairs number of pairs
 */
static M3Gbool m3gLightDuplicate(const Object *originalObj,
                                 Object **cloneObj,
                                 Object **pairs,
                                 M3Gint *numPairs)
{
    const Light *original = (const Light *) originalObj;
    Light *clone;
    M3G_ASSERT(*cloneObj == NULL); /* no derived classes for light */
    
    /* Create the clone object and exit on failure */
    
    clone = (Light *)m3gCreateLight(originalObj->interface);
    if (!clone) {
        return M3G_FALSE;
    }
    *cloneObj = (Object *)clone;

    /* Duplicate base class data */
    
    if (!m3gNodeDuplicate(originalObj, cloneObj, pairs, numPairs)) {
        return M3G_FALSE;
    }

    /* Duplicate own data */
    
	clone->constantAttenuation = original->constantAttenuation;
	clone->linearAttenuation = original->linearAttenuation;
	clone->quadraticAttenuation = original->quadraticAttenuation;
	clone->intensity = original->intensity;
	clone->color = original->color;
	clone->mode = original->mode;
    clone->spotAngle = original->spotAngle;
	clone->spotExponent = original->spotExponent;
    
    return M3G_TRUE;
}

/*!
 * \internal
 * \brief Initializes a Light object. See specification
 * for default values.
 *
 * \param m3g           M3G interface
 * \param light         Light object
 */
static void m3gInitLight(Interface *m3g, Light *light)
{
	/* Light is derived from node */
	m3gInitNode(m3g, &light->node, M3G_CLASS_LIGHT);

	light->constantAttenuation = 1.0f;
	light->intensity = 1.0f;
	light->color = 0x00ffffff;  /* Full white */
	light->mode = M3G_DIRECTIONAL;
	light->spotAngle = 45.0f;
}

/*!
 * \internal
 * \brief Applies this light to the current OpenGL context.
 *
 * \param self          Light object
 * \param glLight       OpenGL light index
 * \param pos           light position
 * \param spotDir       light direction
 */
static void m3gApplyLight(const Light *self,
                          GLenum glLight,
                          const Vec4 *pos,
                          const Vec4 *spotDir)
{
	static const M3Gfloat BLACK[] = { 0.0f, 0.0f, 0.0f, 0.0f };
	M3Gfloat light[4];
    
    M3G_ASSERT(m3gInRange(glLight, GL_LIGHT0, GL_LIGHT7));
    glEnable(glLight);
    
	m3gFloatColor(self->color, self->intensity, light);

    /* Set light position */
    
    if (self->mode == M3G_DIRECTIONAL) {
        GLfloat temp[4];
        temp[0] = -spotDir->x;
        temp[1] = -spotDir->y;
        temp[2] = -spotDir->z;
        temp[3] = 0.0f;
        glLightfv(glLight, GL_POSITION, temp);
    }
    else {
        glLightfv(glLight, GL_POSITION, &pos->x);
        if (self->mode == M3G_SPOT) {
            glLightfv(glLight, GL_SPOT_DIRECTION, &spotDir->x);
        }
    }

    /* Set ambient, diffuse, and specular contributions */
    
	if (self->mode == M3G_AMBIENT) {
		glLightfv(glLight, GL_AMBIENT, light);
		glLightfv(glLight, GL_DIFFUSE, BLACK);
		glLightfv(glLight, GL_SPECULAR, BLACK);
	}
	else {
		glLightfv(glLight, GL_AMBIENT, BLACK);
		glLightfv(glLight, GL_DIFFUSE, light);
		glLightfv(glLight, GL_SPECULAR, light);
	}

    /* Set spot parameters */
    
	if (self->mode == M3G_SPOT) {
		glLightf(glLight, GL_SPOT_EXPONENT, self->spotExponent);
		glLightf(glLight, GL_SPOT_CUTOFF, self->spotAngle);
	}
	else {
		glLightf(glLight, GL_SPOT_CUTOFF, 180.0f);
	}

    /* Set attenuation */
    
	if (self->mode == M3G_OMNI || self->mode == M3G_SPOT) {
		glLightf(glLight, GL_CONSTANT_ATTENUATION,  self->constantAttenuation);
		glLightf(glLight, GL_LINEAR_ATTENUATION,    self->linearAttenuation);
		glLightf(glLight, GL_QUADRATIC_ATTENUATION, self->quadraticAttenuation);
	}
	else if (self->mode == M3G_AMBIENT) {
		glLightf(glLight, GL_CONSTANT_ATTENUATION,  1.0f);
		glLightf(glLight, GL_LINEAR_ATTENUATION,    0.0f);
		glLightf(glLight, GL_QUADRATIC_ATTENUATION, 0.0f);
	}
}

/*----------------------------------------------------------------------
 * Virtual function table
 *--------------------------------------------------------------------*/

static const NodeVFTable m3gvf_Light = {
    {
        {
            m3gObjectApplyAnimation,
            m3gLightIsCompatible,
            m3gLightUpdateProperty,
            m3gObjectDoGetReferences,
            m3gObjectFindID,
            m3gLightDuplicate,
            m3gDestroyNode
        }
    },
    m3gNodeAlign,
    NULL, /* pure virtual DoRender */
    m3gNodeGetBBox,
    m3gNodeRayIntersect,
    m3gLightSetupRender,
    m3gNodeUpdateDuplicateReferences,
    m3gNodeValidate
};


/*----------------------------------------------------------------------
 * Public API functions
 *--------------------------------------------------------------------*/

/*!
 * \brief Creates a Light object.
 *
 * \param interface     M3G interface
 * \retval Light new Light object
 * \retval NULL Light creating failed
 */
M3G_API M3GLight m3gCreateLight(M3GInterface interface)
{
    Interface *m3g = (Interface *) interface;
    M3G_VALIDATE_INTERFACE(m3g);

	{
		Light *light =  m3gAllocZ(m3g, sizeof(Light));

        if (light != NULL) {
    		m3gInitLight(m3g, light);
        }

		return (M3GLight) light;
	}
}

/*!
 * \brief Set light intensity.
 *
 * \param handle        Light object
 * \param intensity     light intensity
 */
M3G_API void m3gSetIntensity(M3GLight handle, M3Gfloat intensity)
{
	Light *light = (Light *) handle;
	M3G_VALIDATE_OBJECT(light);
	
	light->intensity = intensity;
}

/*!
 * \brief Set light color as RGB.
 *
 * \param handle        Light object
 * \param rgb           light color as RGB
 */
M3G_API void m3gSetLightColor(M3GLight handle, M3Guint rgb)
{
	Light *light = (Light *) handle;
	M3G_VALIDATE_OBJECT(light);

	light->color = rgb & M3G_RGB_MASK;
}

/*!
 * \brief Set light mode.
 *
 * \param handle        Light object
 * \param mode          light mode
 */
M3G_API void m3gSetLightMode(M3GLight handle, M3Gint mode)
{
	Light *light = (Light *) handle;
	M3G_VALIDATE_OBJECT(light);

	if (mode < M3G_AMBIENT || mode > M3G_SPOT) {
    	m3gRaiseError(M3G_INTERFACE(light), M3G_INVALID_VALUE);
        return;
    }

	light->mode = mode;
}

/*!
 * \brief Set light spot angle.
 *
 * \param handle        Light object
 * \param angle         spot angle
 */
M3G_API void m3gSetSpotAngle(M3GLight handle, M3Gfloat angle)
{
	Light *light = (Light *) handle;
	M3G_VALIDATE_OBJECT(light);

	if (angle < 0.0f || angle > 90.f) {
    	m3gRaiseError(M3G_INTERFACE(light), M3G_INVALID_VALUE);
        return;
	}

	light->spotAngle = angle;
}

/*!
 * \brief Set light spot exponent.
 *
 * \param handle        Light object
 * \param exponent      spot exponent
 */
M3G_API void m3gSetSpotExponent(M3GLight handle, M3Gfloat exponent)
{
	Light *light = (Light *) handle;
	M3G_VALIDATE_OBJECT(light);

    if (exponent < 0.0f || exponent > 128.0f) {
    	m3gRaiseError(M3G_INTERFACE(light), M3G_INVALID_VALUE);
        return;
    }

    light->spotExponent = exponent;
}

/*!
 * \brief Set light attenuation factors.
 *
 * \param handle        Light object
 * \param constant      constant attenuation
 * \param linear        linear attenuation
 * \param quadratic     quadratic attenuation
 */
M3G_API void m3gSetAttenuation(M3GLight handle,
                               M3Gfloat constant,
                               M3Gfloat linear,
                               M3Gfloat quadratic)
{
	Light *light = (Light *) handle;
	M3G_VALIDATE_OBJECT(light);

    if (constant < 0.0f || linear < 0.0f || quadratic < 0.0f
        || (constant == 0.0f && linear == 0.0f && quadratic == 0.0f)) {
    	m3gRaiseError(M3G_INTERFACE(light), M3G_INVALID_VALUE);
        return;
    }

    light->constantAttenuation  = constant;
    light->linearAttenuation    = linear;
    light->quadraticAttenuation = quadratic;
}

/*!
 * \brief Get light intensity.
 *
 * \param handle        Light object
 * \return              light intensity
 */
M3G_API M3Gfloat m3gGetIntensity(M3GLight handle)
{
	Light *light = (Light *) handle;
	M3G_VALIDATE_OBJECT(light);

    return light->intensity;
}

/*!
 * \brief Get light color as RGB.
 *
 * \param handle        Light object
 * \return              light color as RGB
 */
M3G_API M3Guint m3gGetLightColor(M3GLight handle)
{
	Light *light = (Light *) handle;
	M3G_VALIDATE_OBJECT(light);

    return light->color;
}

/*!
 * \brief Get light mode.
 *
 * \param handle        Light object
 * \return              light mode
 */
M3G_API M3Gint m3gGetLightMode(M3GLight handle)
{
	Light *light = (Light *) handle;
	M3G_VALIDATE_OBJECT(light);

    return light->mode;
}

/*!
 * \brief Get light spot angle.
 *
 * \param handle        Light object
 * \return              light spot angle
 */
M3G_API M3Gfloat m3gGetSpotAngle(M3GLight handle)
{
	Light *light = (Light *) handle;
	M3G_VALIDATE_OBJECT(light);

    return light->spotAngle;
}

/*!
 * \brief Get light spot exponent.
 *
 * \param handle        Light object
 * \return              light spot exponent
 */
M3G_API M3Gfloat m3gGetSpotExponent(M3GLight handle)
{
	Light *light = (Light *) handle;
	M3G_VALIDATE_OBJECT(light);

    return light->spotExponent;
}

/*!
 * \brief Get light attenuation factor.
 *
 * \param handle        Light object
 * \param type          which factor to return
 *                      \arg M3G_GET_CONSTANT
 *                      \arg M3G_GET_LINEAR
 *                      \arg M3G_GET_QUADRATIC
 * \return              light attenuation factor
 */
M3G_API M3Gfloat m3gGetAttenuation(M3GLight handle, M3Gint type)
{
	Light *light = (Light *) handle;
	M3G_VALIDATE_OBJECT(light);

	switch(type) {
	case M3G_GET_CONSTANT:
	    return light->constantAttenuation;
	case M3G_GET_LINEAR:
    	return light->linearAttenuation;
    case M3G_GET_QUADRATIC:
	default:
	    return light->quadraticAttenuation;
	}
}