m3g/m3gcore11/src/m3g_material.c
author jakl.martin@cell-telecom.com
Mon, 06 Dec 2010 18:07:30 +0100
branchNewGraphicsArchitecture
changeset 218 99b3451c560e
parent 0 5d03bc08d59c
permissions -rw-r--r--
Fix for Bug 3890

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


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

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

#include "m3g_material.h"
#include "m3g_animationtrack.h"

#define ALL_TARGET_MASK (M3G_AMBIENT_BIT | M3G_DIFFUSE_BIT | M3G_EMISSIVE_BIT | M3G_SPECULAR_BIT)

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

/*!
 * \internal
 * \brief Applies default material to OpenGL.
 */
static void m3gApplyDefaultMaterial(void)
{
    glDisable(GL_COLOR_MATERIAL);
    glDisable(GL_LIGHTING);
}

/*!
 * \internal
 * \brief Applies material to OpenGL.
 *
 * \param material Material object
 * \param alphaFactor alpha factor as 1.16 fixed point
 */
static void m3gApplyMaterial(Material *material, M3Gint alphaFactor)
{
    if (material != NULL) {
    	M3Gfloat colors[4];

        /* NOTE We must set the ColorMaterial state *before* setting
         * any of the material colors, as they will not change if
         * tracking is enabled! */
        
        if (material->vertexColorTracking) {
            glEnable(GL_COLOR_MATERIAL);
        }
        else {
            glDisable(GL_COLOR_MATERIAL);

            /* Ambient and diffuse only need to be set when tracking
             * is disabled */
            
            m3gFloatColor(material->ambientColor, 1.0f, colors);
            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, colors);
            m3gFloatColor(material->diffuseColor, 1.0f, colors);
            if (alphaFactor < 0x10000) {
                colors[3] = m3gMul(colors[3],
                                   m3gMul((M3Gfloat) alphaFactor,
                                          1.f/65536.f));
            }
            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, colors);
        }
        m3gFloatColor(material->emissiveColor, 1.0f, colors);
        glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, colors);
        m3gFloatColor(material->specularColor, 1.0f, colors);
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, colors);
        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material->shininess);

        glEnable(GL_LIGHTING);
    }
    else {
        m3gApplyDefaultMaterial();
    }
    M3G_ASSERT_GL;
}

/*!
 * \internal
 * \brief Overloaded Object3D method.
 *
 * \param property      animation property
 * \retval M3G_TRUE     property supported
 * \retval M3G_FALSE    property not supported
 */
static M3Gbool m3gMaterialIsCompatible(M3Gint property)
{
    switch (property) {
    case M3G_ANIM_ALPHA:
    case M3G_ANIM_AMBIENT_COLOR:
    case M3G_ANIM_DIFFUSE_COLOR:
    case M3G_ANIM_EMISSIVE_COLOR:
    case M3G_ANIM_SHININESS:
    case M3G_ANIM_SPECULAR_COLOR:
        return M3G_TRUE;
    default:
        return m3gObjectIsCompatible(property);
    }
}

/*!
 * \internal
 * \brief Overloaded Object3D method.
 *
 * \param self          Material object
 * \param property      animation property
 * \param valueSize     size of value array
 * \param value         value array
 */
static void m3gMaterialUpdateProperty(Object *self,
                                      M3Gint property,
                                      M3Gint valueSize,
                                      const M3Gfloat *value)
{
    Material *material = (Material *)self;
    M3G_VALIDATE_OBJECT(material);
    M3G_ASSERT_PTR(value);

    switch (property) {
    case M3G_ANIM_ALPHA:
        M3G_ASSERT(valueSize >= 1);
        material->diffuseColor = (material->diffuseColor | M3G_ALPHA_MASK) &
            m3gAlpha1f(value[0]);
        break;
    case M3G_ANIM_AMBIENT_COLOR:
        M3G_ASSERT(valueSize >= 3);
        material->ambientColor = m3gColor3f(value[0], value[1], value[2]);
        break;
    case M3G_ANIM_DIFFUSE_COLOR:
        M3G_ASSERT(valueSize >= 3);
        material->diffuseColor = (material->diffuseColor | M3G_RGB_MASK)
            & m3gColor3f(value[0], value[1], value[2]);
        break;
    case M3G_ANIM_EMISSIVE_COLOR:
        M3G_ASSERT(valueSize >= 3);
        material->emissiveColor = m3gColor3f(value[0], value[1], value[2]) & M3G_RGB_MASK;
        break;
    case M3G_ANIM_SHININESS:
        M3G_ASSERT(valueSize >= 1);
        material->shininess = m3gClampFloat(value[0], 0.f, 128.f);
        break;
    case M3G_ANIM_SPECULAR_COLOR:
        M3G_ASSERT(valueSize >= 3);
        material->specularColor = m3gColor3f(value[0], value[1], value[2]);
        break;
    default:
        m3gObjectUpdateProperty(self, property, valueSize, value);
    }
}

/*!
 * \internal
 * \brief Overloaded Object3D method.
 *
 * \param originalObj original Material object
 * \param cloneObj pointer to cloned Material object
 * \param pairs array for all object-duplicate pairs
 * \param numPairs number of pairs
 */
static M3Gbool m3gMaterialDuplicate(const Object *originalObj,
                                    Object **cloneObj,
                                    Object **pairs,
                                    M3Gint *numPairs)
{
    const Material *original = (const Material *)originalObj;

    /* Create the clone object */
    
    Material *clone = (Material *)m3gCreateMaterial(originalObj->interface);
    if (!clone) {
        return M3G_FALSE;
    }
    *cloneObj = (Object *)clone;

    /* Duplicate base class and own data */
    
    if (m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs)) {
        clone->vertexColorTracking = original->vertexColorTracking;
        clone->ambientColor = original->ambientColor;
        clone->diffuseColor = original->diffuseColor;
        clone->emissiveColor = original->emissiveColor;
        clone->specularColor = original->specularColor;
        clone->shininess = original->shininess;
        return M3G_TRUE;
    }
    else {
        return M3G_FALSE;
    }
}

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

static const ObjectVFTable m3gvf_Material = {
    m3gObjectApplyAnimation,
    m3gMaterialIsCompatible,
    m3gMaterialUpdateProperty,
    m3gObjectDoGetReferences,
    m3gObjectFindID,
    m3gMaterialDuplicate,
    m3gDestroyObject
};


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

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

    {
        Material *material = m3gAllocZ(m3g, sizeof(Material));

        if (material != NULL) {
            m3gInitObject(&material->object, m3g, M3G_CLASS_MATERIAL);
            /* Default values are from the jsr-184 specification */
            material->vertexColorTracking = GL_FALSE;
            material->ambientColor = 0x00333333U;
            material->diffuseColor = 0xFFCCCCCCU;
            material->emissiveColor = 0x00000000U;
            material->specularColor = 0x00000000U;
            material->shininess = 0.0f;
        }

        return (M3GMaterial)material;
    }
}

/*!
 * \brief Set material color.
 *
 * \param hMaterial Material object
 * \param target    color target
 * \param ARGB      ARGB color
 */
M3G_API void m3gSetColor(M3GMaterial hMaterial, M3Genum target, M3Guint ARGB)
{
    Material *material = (Material *) hMaterial;
    M3G_VALIDATE_OBJECT(material);
    /* is invalid target in the mask OR no target */
    if (((target | ALL_TARGET_MASK) != ALL_TARGET_MASK) || ((target & ALL_TARGET_MASK) == 0)) {
        m3gRaiseError(M3G_INTERFACE(material), M3G_INVALID_VALUE);
        return;
    }
    if ((target & M3G_AMBIENT_BIT) != 0) {
        material->ambientColor = ARGB & M3G_RGB_MASK;
    }
    if ((target & M3G_DIFFUSE_BIT) != 0) {
        material->diffuseColor = ARGB;
    }
    if ((target & M3G_EMISSIVE_BIT) != 0) {
        material->emissiveColor = ARGB & M3G_RGB_MASK;
    }
    if ((target & M3G_SPECULAR_BIT) != 0) {
        material->specularColor = ARGB & M3G_RGB_MASK;
    }
}

/*!
 * \brief Get material color.
 *
 * \param hMaterial Material object
 * \param target    color target
 * \return          ARGB color
 */
M3G_API M3Guint m3gGetColor(M3GMaterial hMaterial, M3Genum target)
{
    Material *material = (Material *) hMaterial;
    M3G_VALIDATE_OBJECT(material);
    
    switch (target) {
    case M3G_AMBIENT_BIT:
        return material->ambientColor;
    case M3G_DIFFUSE_BIT:
        return material->diffuseColor;
    case M3G_EMISSIVE_BIT:
        return material->emissiveColor;
    case M3G_SPECULAR_BIT:
        return material->specularColor;
    default:
        m3gRaiseError(M3G_INTERFACE(material), M3G_INVALID_VALUE);
        break;
    }

    return 0; /* Error */
}

/*!
 * \brief Set material shininess.
 *
 * \param hMaterial Material object
 * \param shininess shininess
 */
M3G_API void m3gSetShininess(M3GMaterial hMaterial, M3Gfloat shininess)
{
    Material *material = (Material *) hMaterial;
    M3G_VALIDATE_OBJECT(material);
    if (!m3gInRangef(shininess, 0.0f, 128.f)) {
        m3gRaiseError(M3G_INTERFACE(material), M3G_INVALID_VALUE);
        return;
    }
    material->shininess = shininess;
}

/*!
 * \brief Get material shininess.
 *
 * \param hMaterial Material object
 * \return          shininess
 */
M3G_API GLfloat m3gGetShininess(M3GMaterial hMaterial)
{
    Material *material = (Material *) hMaterial;
    M3G_VALIDATE_OBJECT(material);
    return material->shininess;
}

/*!
 * \brief Set vertex color tracking enable.
 *
 * \param hMaterial Material object
 * \param enable color tracking enable
 */
M3G_API void m3gSetVertexColorTrackingEnable(M3GMaterial hMaterial,
                                             M3Gbool enable)
{
    Material *material = (Material *) hMaterial;
    M3G_VALIDATE_OBJECT(material);
    material->vertexColorTracking = (GLboolean) enable;
}

/*!
 * \brief Get vertex color tracking enable.
 *
 * \param hMaterial Material object
 * \retval M3G_TRUE color tracking enabled
 * \retval M3G_FALSE color tracking disabled
 */
M3G_API M3Gbool m3gIsVertexColorTrackingEnabled(M3GMaterial hMaterial)
{
    Material *material = (Material *) hMaterial;
    M3G_VALIDATE_OBJECT(material);
    return (M3Gbool) material->vertexColorTracking;
}