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: Appearance implementation sl@0: * sl@0: */ sl@0: sl@0: sl@0: /*! sl@0: * \internal sl@0: * \file sl@0: * \brief Appearance 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_appearance.h" sl@0: #include "m3g_vertexbuffer.h" sl@0: sl@0: /*---------------------------------------------------------------------- sl@0: * Private functions sl@0: *--------------------------------------------------------------------*/ sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Applies default appearance values to OpenGL. sl@0: */ sl@0: static void m3gApplyAppearanceDefaults(RenderContext *ctx) sl@0: { sl@0: int i; sl@0: m3gApplyCompositingMode(NULL, ctx); sl@0: m3gApplyPolygonMode(NULL); sl@0: m3gApplyMaterial(NULL, 0x10000); sl@0: m3gApplyFog(NULL); sl@0: sl@0: for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) { sl@0: glActiveTexture(GL_TEXTURE0 + i); sl@0: glDisable(GL_TEXTURE_2D); sl@0: } sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Generate a hash number for a pointer sl@0: */ sl@0: static M3Guint m3gGenPointerHash(const void *ptr) sl@0: { sl@0: M3Guint p = ((M3Guint) ptr) >> 2; sl@0: M3Guint key = p ^ (p >> 5) ^ (p >> 10) ^ (p >> 15) ^ (p >> 20) ^ (p >> 25); sl@0: return key; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Generate a quick hash bit pattern for the textures of this sl@0: * appearance object sl@0: */ sl@0: static M3Guint m3gGen12BitTextureHash(const Appearance *app) sl@0: { sl@0: M3Guint key = 0; sl@0: sl@0: int i; sl@0: sl@0: for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) { sl@0: const Texture *tex = app->texture[i]; sl@0: if (tex) { sl@0: key ^= (m3gGenPointerHash(m3gGetTextureImage((M3GTexture)tex)) >> i) << 6; sl@0: key ^= (m3gGenPointerHash(tex) >> i) & 0x3Fu; sl@0: } sl@0: } sl@0: return key & ((1u<<12)-1); sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Generate the sorting key for render queue sl@0: * sl@0: * Sort key is a combination of user settable layer and sl@0: * blending mode. Blended objects are always drawn last. sl@0: * sl@0: * \param appearance Appearance object sl@0: * \return sort key sl@0: */ sl@0: static void m3gRegenerateSortKey(Appearance *appearance) sl@0: { sl@0: M3Guint key = 0; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: sl@0: /*------------------------------------------------------------ sl@0: * First do the mandatory sorting by layer index and blending sl@0: * state; this currently uses the top eight bits, 31..24 sl@0: *-----------------------------------------------------------*/ sl@0: sl@0: key = (appearance->layer - M3G_APPEARANCE_MIN_LAYER) sl@0: << (33 - M3G_APPEARANCE_HARD_SORT_BITS); sl@0: sl@0: /* NOTE the blending state bit is not set here, but dynamically in sl@0: * m3gGetAppearanceSortKey; this way we do not need to implement sl@0: * signaling from CompositingMode to Appearance when the blending sl@0: * state changes */ sl@0: sl@0: /*----------------------------------------------------------------- sl@0: * The rest of the bits, 23..0, affect performance only; ideally, sl@0: * these should be sorted so that the more expensive state is in the sl@0: * higher bits, but this is largely dependent on the hardware sl@0: *----------------------------------------------------------------*/ sl@0: sl@0: /* Texturing changes are often expensive in graphics hardware, so sl@0: * we put a hash of the texture objects into the top twelve sl@0: * bits sl@0: * sl@0: * NOTE we do not currently update this if a texture image sl@0: * changes, but that shouldn't happen too often and only has sl@0: * relatively minor performance implications sl@0: */ sl@0: sl@0: key |= m3gGen12BitTextureHash(appearance) << 12; sl@0: sl@0: /* Use the rest of the bits for the various components; depth sl@0: * function changes are another potentially costly operation, so sl@0: * put that next */ sl@0: sl@0: key |= (m3gGenPointerHash(appearance->compositingMode) & 0x0Fu) << 8; sl@0: key |= (m3gGenPointerHash(appearance->material) & 0x07u) << 5; sl@0: key |= (m3gGenPointerHash(appearance->polygonMode) & 0x07u) << 2; sl@0: key |= (m3gGenPointerHash(appearance->fog) & 0x03u); sl@0: sl@0: /* Store the final value */ sl@0: appearance->sortKey = key; sl@0: } sl@0: sl@0: /*---------------------------------------------------------------------- sl@0: * Internal functions sl@0: *--------------------------------------------------------------------*/ sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Destroys this Appearance object. sl@0: * sl@0: * \param obj Appearance object sl@0: */ sl@0: static void m3gDestroyAppearance(Object *obj) sl@0: { sl@0: int i; sl@0: Appearance *appearance = (Appearance *) obj; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: sl@0: M3G_ASSIGN_REF(appearance->compositingMode, NULL); sl@0: M3G_ASSIGN_REF(appearance->fog, NULL); sl@0: M3G_ASSIGN_REF(appearance->material, NULL); sl@0: M3G_ASSIGN_REF(appearance->polygonMode, NULL); sl@0: for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) { sl@0: M3G_ASSIGN_REF(appearance->texture[i], NULL); sl@0: } sl@0: sl@0: m3gDestroyObject(obj); sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Applies Apperance settings to current OpenGL state sl@0: * sl@0: * \note m3gReleaseTextures must be called when no longer using this, sl@0: * to properly reset texture usage counters and unmap the texture sl@0: * images. sl@0: * sl@0: * \param appearance Appearance object sl@0: * \param alphaFactor alpha factor as 1.16 fixed point sl@0: */ sl@0: static void m3gApplyAppearance(const Appearance *appearance, sl@0: RenderContext *ctx, sl@0: M3Gint alphaFactor) sl@0: { sl@0: M3G_ASSERT_GL; sl@0: sl@0: if (appearance != NULL) { sl@0: int i; sl@0: sl@0: # if defined(M3G_NGL_TEXTURE_API) sl@0: m3gLockMemory(M3G_INTERFACE(appearance)); /* for textures */ sl@0: # endif sl@0: sl@0: m3gApplyCompositingMode(appearance->compositingMode, ctx); sl@0: m3gApplyPolygonMode(appearance->polygonMode); sl@0: m3gApplyMaterial(appearance->material, alphaFactor); sl@0: m3gApplyFog(appearance->fog); sl@0: sl@0: for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) { sl@0: Texture *tex = appearance->texture[i]; sl@0: glActiveTexture(GL_TEXTURE0 + i); sl@0: if (tex != NULL) { sl@0: glEnable(GL_TEXTURE_2D); sl@0: m3gBindTexture(appearance->texture[i]); sl@0: } sl@0: else { sl@0: glDisable(GL_TEXTURE_2D); sl@0: } sl@0: } sl@0: } sl@0: else { sl@0: m3gApplyAppearanceDefaults(ctx); sl@0: } sl@0: sl@0: M3G_ASSERT_GL; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Release the textures bound for this appearance sl@0: */ sl@0: static void m3gReleaseTextures(const Appearance *appearance) sl@0: { sl@0: if (appearance != NULL) { sl@0: int i; sl@0: sl@0: for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) { sl@0: Texture *tex = appearance->texture[i]; sl@0: if (tex != NULL) { sl@0: m3gReleaseTexture(tex); sl@0: } sl@0: } sl@0: sl@0: # if defined(M3G_NGL_TEXTURE_API) sl@0: m3gUnlockMemory(M3G_INTERFACE(appearance)); sl@0: # endif sl@0: } sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Overloaded Object3D method. sl@0: * sl@0: * \param self Appearance object sl@0: * \param time current world time sl@0: * \return minimum validity sl@0: */ sl@0: static M3Gint m3gAppearanceApplyAnimation(Object *self, M3Gint time) { sl@0: M3Gint i, validity, minValidity = 0x7fffffff; sl@0: Appearance *appearance = (Appearance *)self; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: sl@0: if (appearance->compositingMode != NULL) { sl@0: validity = M3G_VFUNC(Object, appearance->compositingMode, applyAnimation)((Object *)appearance->compositingMode, time); sl@0: minValidity = (validity < minValidity ? validity : minValidity); sl@0: } sl@0: if (appearance->fog != NULL) { sl@0: validity = M3G_VFUNC(Object, appearance->fog, applyAnimation)((Object *)appearance->fog, time); sl@0: minValidity = (validity < minValidity ? validity : minValidity); sl@0: } sl@0: if (appearance->material != NULL) { sl@0: validity = M3G_VFUNC(Object, appearance->material, applyAnimation)((Object *)appearance->material, time); sl@0: minValidity = (validity < minValidity ? validity : minValidity); sl@0: } sl@0: for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) { sl@0: if (appearance->texture[i] != NULL) { sl@0: validity = M3G_VFUNC(Object, appearance->texture[i], applyAnimation)((Object *)appearance->texture[i], time); sl@0: minValidity = (validity < minValidity ? validity : minValidity); sl@0: } sl@0: } sl@0: sl@0: /* no animations can target an Appearance directly, so we need sl@0: not call super.applyAnimation() here. */ sl@0: return minValidity; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Overloaded Object3D method. sl@0: */ sl@0: static Object *m3gAppearanceFindID(Object *self, M3Gint userID) sl@0: { sl@0: int i; sl@0: Appearance *app = (Appearance *)self; sl@0: Object *found = m3gObjectFindID(self, userID); sl@0: sl@0: if (!found && app->compositingMode) { sl@0: found = m3gFindID((Object*) app->compositingMode, userID); sl@0: } sl@0: if (!found && app->polygonMode) { sl@0: found = m3gFindID((Object*) app->polygonMode, userID); sl@0: } sl@0: if (!found && app->fog) { sl@0: found = m3gFindID((Object*) app->fog, userID); sl@0: } sl@0: if (!found && app->material) { sl@0: found = m3gFindID((Object*) app->material, userID); sl@0: } sl@0: for (i = 0; !found && i < M3G_NUM_TEXTURE_UNITS; ++i) { sl@0: if (app->texture[i]) { sl@0: found = m3gFindID((Object*) app->texture[i], userID); sl@0: } sl@0: } sl@0: return found; sl@0: } sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Overloaded Object3D method. sl@0: * sl@0: * \param self Appearance object sl@0: * \param references array of reference objects sl@0: * \return number of references sl@0: */ sl@0: static M3Gint m3gAppearanceDoGetReferences(Object *self, Object **references) sl@0: { sl@0: Appearance *app = (Appearance *)self; sl@0: M3Gint i, num = m3gObjectDoGetReferences(self, references); sl@0: if (app->compositingMode != NULL) { sl@0: if (references != NULL) sl@0: references[num] = (Object *)app->compositingMode; sl@0: num++; sl@0: } sl@0: if (app->polygonMode != NULL) { sl@0: if (references != NULL) sl@0: references[num] = (Object *)app->polygonMode; sl@0: num++; sl@0: } sl@0: if (app->fog != NULL) { sl@0: if (references != NULL) sl@0: references[num] = (Object *)app->fog; sl@0: num++; sl@0: } sl@0: if (app->material != NULL) { sl@0: if (references != NULL) sl@0: references[num] = (Object *)app->material; sl@0: num++; sl@0: } sl@0: for (i = 0; i < M3G_NUM_TEXTURE_UNITS; i++) { sl@0: if (app->texture[i] != NULL) { sl@0: if (references != NULL) sl@0: references[num] = (Object *)app->texture[i]; sl@0: num++; sl@0: } sl@0: } sl@0: return num; sl@0: } sl@0: sl@0: sl@0: /*! sl@0: * \internal sl@0: * \brief Overloaded Object3D method. sl@0: * sl@0: * \param originalObj original Appearance object sl@0: * \param cloneObj pointer to cloned Appearance object sl@0: * \param pairs array for all object-duplicate pairs sl@0: * \param numPairs number of pairs sl@0: */ sl@0: static M3Gbool m3gAppearanceDuplicate(const Object *originalObj, sl@0: Object **cloneObj, sl@0: Object **pairs, sl@0: M3Gint *numPairs) sl@0: { sl@0: M3Gint i; sl@0: Appearance *original = (Appearance *)originalObj; sl@0: Appearance *clone = (Appearance *)m3gCreateAppearance(originalObj->interface); sl@0: *cloneObj = (Object *)clone; sl@0: if (*cloneObj == NULL) { sl@0: return M3G_FALSE; sl@0: } sl@0: sl@0: if (m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs)) { sl@0: clone->layer = original->layer; sl@0: sl@0: M3G_ASSIGN_REF(clone->compositingMode, original->compositingMode); sl@0: M3G_ASSIGN_REF(clone->fog, original->fog); sl@0: M3G_ASSIGN_REF(clone->polygonMode, original->polygonMode); sl@0: M3G_ASSIGN_REF(clone->material, original->material); sl@0: for (i = 0; i < M3G_NUM_TEXTURE_UNITS; i++) { sl@0: M3G_ASSIGN_REF(clone->texture[i], original->texture[i]); sl@0: } sl@0: sl@0: m3gRegenerateSortKey(clone); sl@0: sl@0: return M3G_TRUE; sl@0: } sl@0: else { sl@0: return M3G_FALSE; sl@0: } sl@0: } sl@0: sl@0: /*---------------------------------------------------------------------- sl@0: * Virtual function table sl@0: *--------------------------------------------------------------------*/ sl@0: sl@0: static const ObjectVFTable m3gvf_Appearance = { sl@0: m3gAppearanceApplyAnimation, sl@0: m3gObjectIsCompatible, sl@0: m3gObjectUpdateProperty, sl@0: m3gAppearanceDoGetReferences, sl@0: m3gAppearanceFindID, sl@0: m3gAppearanceDuplicate, sl@0: m3gDestroyAppearance sl@0: }; sl@0: sl@0: sl@0: /*---------------------------------------------------------------------- sl@0: * Public API functions sl@0: *--------------------------------------------------------------------*/ sl@0: sl@0: /*! sl@0: * \brief Creates a new Appearance with default values sl@0: * sl@0: * \param hInterface M3G interface sl@0: * \retval Appearance new Appearance object sl@0: * \retval NULL Appearance creating failed sl@0: */ sl@0: /*@access M3GInterface@*/ sl@0: /*@access M3GAppearance@*/ sl@0: M3G_API M3GAppearance m3gCreateAppearance(M3GInterface hInterface) sl@0: { sl@0: Interface *m3g = (Interface *) hInterface; sl@0: M3G_VALIDATE_INTERFACE(m3g); sl@0: { sl@0: Appearance *appearance = m3gAllocZ(m3g, sizeof(Appearance)); sl@0: sl@0: if (appearance != NULL) { sl@0: m3gInitObject(&appearance->object, m3g, M3G_CLASS_APPEARANCE); sl@0: m3gRegenerateSortKey(appearance); sl@0: } sl@0: sl@0: return (M3GAppearance) appearance; sl@0: } sl@0: } sl@0: sl@0: /*! sl@0: * \brief Get compositing mode sl@0: * sl@0: * \param hAppearance Appearance object sl@0: * \return CompositingMode object sl@0: */ sl@0: M3G_API M3GCompositingMode m3gGetCompositingMode(M3GAppearance hAppearance) sl@0: { sl@0: const Appearance *appearance = (const Appearance *) hAppearance; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: return (M3GCompositingMode)(appearance->compositingMode); sl@0: } sl@0: sl@0: /*! sl@0: * \brief Get fog sl@0: * sl@0: * \param hAppearance Appearance object sl@0: * \return Fog object sl@0: */ sl@0: M3G_API M3GFog m3gGetFog(M3GAppearance hAppearance) sl@0: { sl@0: const Appearance *appearance = (const Appearance *) hAppearance; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: return (M3GFog)(appearance->fog); sl@0: } sl@0: sl@0: /*! sl@0: * \brief Get material sl@0: * sl@0: * \param hAppearance Appearance object sl@0: * \return Material object sl@0: */ sl@0: M3G_API M3GMaterial m3gGetMaterial(M3GAppearance hAppearance) sl@0: { sl@0: const Appearance *appearance = (const Appearance *) hAppearance; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: return (M3GMaterial)(appearance->material); sl@0: } sl@0: sl@0: /*! sl@0: * \brief Get polygon mode sl@0: * sl@0: * \param hAppearance Appearance object sl@0: * \return PolygonMode object sl@0: */ sl@0: M3G_API M3GPolygonMode m3gGetPolygonMode(M3GAppearance hAppearance) sl@0: { sl@0: const Appearance *appearance = (const Appearance *) hAppearance; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: return (M3GPolygonMode)(appearance->polygonMode); sl@0: } sl@0: sl@0: /*! sl@0: * \brief Get texture sl@0: * sl@0: * \param hAppearance Appearance object sl@0: * \param unit texturing unit sl@0: * \return Texture2D object sl@0: */ sl@0: M3G_API M3GTexture m3gGetTexture(M3GAppearance hAppearance, M3Gint unit) sl@0: { sl@0: const Appearance *appearance = (const Appearance *) hAppearance; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: if (!m3gInRange(unit, 0, M3G_NUM_TEXTURE_UNITS - 1)) { sl@0: m3gRaiseError(M3G_INTERFACE(appearance), M3G_INVALID_INDEX); sl@0: return (M3GTexture) NULL; sl@0: } sl@0: return (M3GTexture)(appearance->texture[unit]); sl@0: } sl@0: sl@0: /*! sl@0: * \brief Get layer sl@0: * sl@0: * \param hAppearance Appearance object sl@0: * \return layer number sl@0: */ sl@0: M3G_API M3Gint m3gGetLayer(M3GAppearance hAppearance) sl@0: { sl@0: const Appearance *appearance = (const Appearance *) hAppearance; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: return appearance->layer; sl@0: } sl@0: sl@0: /*! sl@0: * \brief Set compositing mode sl@0: * sl@0: * \param hAppearance Appearance object sl@0: * \param hMode CompositingMode object sl@0: */ sl@0: M3G_API void m3gSetCompositingMode(M3GAppearance hAppearance, sl@0: M3GCompositingMode hMode) sl@0: { sl@0: Appearance *appearance = (Appearance *) hAppearance; sl@0: CompositingMode *mode = (CompositingMode *) hMode; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: sl@0: M3G_ASSIGN_REF(appearance->compositingMode, mode); sl@0: sl@0: m3gRegenerateSortKey(appearance); sl@0: } sl@0: sl@0: /*! sl@0: * \brief Set polygon mode sl@0: * sl@0: * \param hAppearance Appearance object sl@0: * \param hMode PolygonMode object sl@0: */ sl@0: M3G_API void m3gSetPolygonMode(M3GAppearance hAppearance, sl@0: M3GPolygonMode hMode) sl@0: { sl@0: Appearance *appearance = (Appearance *) hAppearance; sl@0: PolygonMode *mode = (PolygonMode *) hMode; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: sl@0: M3G_ASSIGN_REF(appearance->polygonMode, mode); sl@0: sl@0: m3gRegenerateSortKey(appearance); sl@0: } sl@0: sl@0: /*! sl@0: * \brief Set layer sl@0: * sl@0: * \param hAppearance Appearance object sl@0: * \param layer layer number sl@0: */ sl@0: M3G_API void m3gSetLayer(M3GAppearance hAppearance, M3Gint layer) sl@0: { sl@0: Appearance *appearance = (Appearance *) hAppearance; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: sl@0: /* Check for errors */ sl@0: if (!m3gInRange(layer, M3G_APPEARANCE_MIN_LAYER, M3G_APPEARANCE_MAX_LAYER)) { sl@0: m3gRaiseError(M3G_INTERFACE(appearance), M3G_INVALID_INDEX); sl@0: return; sl@0: } sl@0: sl@0: appearance->layer = (M3Gshort) layer; sl@0: sl@0: m3gRegenerateSortKey(appearance); sl@0: } sl@0: sl@0: /*! sl@0: * \brief Set material sl@0: * sl@0: * \param hAppearance Appearance object sl@0: * \param hMaterial Material object sl@0: */ sl@0: M3G_API void m3gSetMaterial(M3GAppearance hAppearance, sl@0: M3GMaterial hMaterial) sl@0: { sl@0: Appearance *appearance = (Appearance *) hAppearance; sl@0: Material *material = (Material *) hMaterial; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: sl@0: M3G_ASSIGN_REF(appearance->material, material); sl@0: sl@0: if (material != NULL) { sl@0: appearance->vertexMask |= (M3Gushort)M3G_NORMAL_BIT; sl@0: } sl@0: else { sl@0: appearance->vertexMask &= ~(M3Gushort)M3G_NORMAL_BIT; sl@0: } sl@0: sl@0: m3gRegenerateSortKey(appearance); sl@0: } sl@0: sl@0: /*! sl@0: * \brief Set texture sl@0: * sl@0: * \param hAppearance Appearance object sl@0: * \param unit texturing unit sl@0: * \param hTexture Texture2D object sl@0: */ sl@0: M3G_API void m3gSetTexture(M3GAppearance hAppearance, sl@0: M3Gint unit, M3GTexture hTexture) sl@0: { sl@0: Appearance *appearance = (Appearance *) hAppearance; sl@0: Texture *texture = (Texture *) hTexture; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: sl@0: if (!m3gInRange(unit, 0, M3G_NUM_TEXTURE_UNITS - 1)) { sl@0: m3gRaiseError(M3G_INTERFACE(appearance), M3G_INVALID_INDEX); sl@0: return; sl@0: } sl@0: sl@0: M3G_ASSIGN_REF(appearance->texture[unit], texture); sl@0: sl@0: if (texture != NULL) { sl@0: appearance->vertexMask |= (M3Gushort) (M3G_TEXCOORD0_BIT << unit); sl@0: } sl@0: else { sl@0: appearance->vertexMask &= (M3Gushort) ~(M3G_TEXCOORD0_BIT << unit); sl@0: } sl@0: sl@0: m3gRegenerateSortKey(appearance); sl@0: } sl@0: sl@0: /*! sl@0: * \brief Set fog sl@0: * sl@0: * \param hAppearance Appearance object sl@0: * \param hFog Fog object sl@0: */ sl@0: M3G_API void m3gSetFog(M3GAppearance hAppearance, M3GFog hFog) sl@0: { sl@0: Appearance *appearance = (Appearance *) hAppearance; sl@0: M3G_VALIDATE_OBJECT(appearance); sl@0: sl@0: M3G_ASSIGN_REF(appearance->fog, hFog); sl@0: sl@0: m3gRegenerateSortKey(appearance); sl@0: } sl@0: