/* * Copyright (C) 1997-2005, R3vis Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA, or visit http://www.gnu.org/copyleft/lgpl.html. * * Original Contributor: * Wes Bethel, R3vis Corporation, Marin County, California * Additional Contributor(s): * * The OpenRM project is located at http://openrm.sourceforge.net/. */ /* * $Id: rmlites.c,v 1.8 2005/08/06 15:30:39 wes Exp $ * Version: $Name: OpenRM-1-6-0-RC5 $ * $Revision: 1.8 $ * $Log: rmlites.c,v $ * Revision 1.8 2005/08/06 15:30:39 wes * Reverted to RM_FALSE for localviewer in rmDefaultLighting. Turning off * localviewer results in about a 50% increase in frame rate in one of * our test cases. * * Revision 1.7 2005/06/26 18:51:23 wes * In rmDefaultLighting, changed default setting of localview to RM_TRUE. * * Revision 1.6 2005/02/19 16:22:50 wes * Distro sync and consolidation. * * Revision 1.5 2005/01/23 17:04:03 wes * Copyright update to 2005. * * Revision 1.4 2004/03/10 01:45:10 wes * Enabled use of global default spotlight exponent when a new light * is created with rmLightNew. * * Revision 1.3 2004/01/16 16:45:12 wes * Updated copyright line for 2004. * * Revision 1.2 2003/02/02 02:07:15 wes * Updated copyright to 2003. * * Revision 1.1.1.1 2003/01/28 02:15:23 wes * Manual rebuild of rm150 repository. * * Revision 1.6 2003/01/16 22:21:17 wes * Updated all source files to reflect new organization of header files: * all header files formerly located in include/rmaux, include/rmi, include/rmv * are now located in include/rm. * * Revision 1.5 2002/04/30 19:32:22 wes * Updated copyright dates. * * Revision 1.4 2001/05/26 14:35:10 wes * Doc mods. * * Revision 1.3 2001/03/31 17:12:38 wes * v1.4.0-alpha-2 checkin. * * Revision 1.2 2000/04/20 16:29:47 wes * Documentation additions/enhancements, some code rearragement. * * Revision 1.1.1.1 2000/02/28 21:29:40 wes * OpenRM 1.2 Checkin * * Revision 1.1.1.1 2000/02/28 17:18:48 wes * Initial entry - pre-RM120 release, source base for OpenRM 1.2. * */ #include #include "rmprivat.h" /* * ---------------------------------------------------- * @Name rmLightNew @pstart RMlight * rmLightNew (void) @pend @astart No arguments. @aend @dstart Creates a new RMlight object, returning a handle to the new object to the caller. The RMlight object is initialized with these values: 1. Light type set to RM_LIGHT_DIRECTIONAL (see rmLightSetType). 2. Light direction set to {0.0, 0.0, 1.0} (see rmLightSetXYZ). 3. Light ambient, diffuse and specular colors set to {0, 0, 0}, {0.75, 0.75, 0.75} and {1.0, 1.0, 1.0} respectively (see rmLightSetColor). 4. Spotlight direction, and cutoff set to {0,0,-1} and 90 degrees, respectively (see rmLightSetSpotDirection and rmLightSetSpotCutoff). Note that these parameters have no meaning with a light source that is not a spotlight. 5. Light attenuation coefficients consist of a constant, linear and quadratic terms. These are initialized to 1.0, 0.0 and 0.0, respectively (see rmLightSetAttenuation). 6. The light is enabled (see rmLightSetEnable) with RM_TRUE. Notes: The process of lighting in RM consists of a number of interrelated factors. One is the presence of light sources in the scene as scene parameters assigned to RMnodes. Another is the specification of a light model, another scene parameter that specifies whether or not lighting is two-sided (illumination of both front- and back-facing polygons by ignoring the sign of the dot product of the surface normal with the direction to/of the light source). Third, objects to be illuminated must have 3D surface normals. The absence of any of these three components will cause lighting/shading to "not work." Note that setting attributes in an RMlight does not change them in the scene graph. They must be assigned as scene parameters to an RMnode before they will be used in rendering (see rmNodeSetSceneLight). @dend * ---------------------------------------------------- */ RMlight * rmLightNew (void) { extern float RM_DEFAULT_LIGHT_SPOT_CUTOFF; extern float RM_DEFAULT_LIGHT_CONSTANT_ATTENUATION; extern float RM_DEFAULT_LIGHT_LINEAR_ATTENUATION; extern float RM_DEFAULT_LIGHT_QUADRATIC_ATTENUATION; extern float RM_DEFAULT_LIGHT_SPOT_EXPONENT; extern RMenum RM_DEFAULT_LIGHT_ENABLE; extern RMenum RM_DEFAULT_LIGHT_TYPE; extern RMvertex3D RM_DEFAULT_LIGHT_XYZ; extern RMcolor4D RM_DEFAULT_LIGHT_AMBIENT_COLOR; extern RMcolor4D RM_DEFAULT_LIGHT_DIFFUSE_COLOR; extern RMcolor4D RM_DEFAULT_LIGHT_SPECULAR_COLOR; extern RMvertex3D RM_DEFAULT_LIGHT_SPOT_DIRECTION; RMlight *t = (RMlight *)malloc(sizeof(RMlight)); if (t == NULL) return(NULL); memset(t, 0, sizeof(RMlight)); /* assign default values */ rmLightSetType(t, RM_DEFAULT_LIGHT_TYPE); rmLightSetXYZ(t, &RM_DEFAULT_LIGHT_XYZ); rmLightSetColor(t, &RM_DEFAULT_LIGHT_AMBIENT_COLOR, &RM_DEFAULT_LIGHT_DIFFUSE_COLOR, &RM_DEFAULT_LIGHT_SPECULAR_COLOR); rmLightSetSpotExponent(t, RM_DEFAULT_LIGHT_SPOT_EXPONENT); rmLightSetSpotDirection(t, &RM_DEFAULT_LIGHT_SPOT_DIRECTION); rmLightSetSpotCutoff(t, RM_DEFAULT_LIGHT_SPOT_CUTOFF); rmLightSetAttenuation (t, RM_DEFAULT_LIGHT_CONSTANT_ATTENUATION, RM_DEFAULT_LIGHT_LINEAR_ATTENUATION, RM_DEFAULT_LIGHT_QUADRATIC_ATTENUATION); rmLightSetEnable (t, RM_DEFAULT_LIGHT_ENABLE); return(t); } /* * ---------------------------------------------------- * @Name rmLightDelete @pstart void rmLightDelete (RMlight *toDelete) @pend @astart RMlight *toDelete - a handle to an RMlight object. @aend @dstart Frees resources associated with an RMlight object. This routine is the opposite of rmLightNew(). @dend * ---------------------------------------------------- */ void rmLightDelete (RMlight *t) { if (RM_ASSERT(t, "rmLightDelete() error: the input RMlight is NULL.") == RM_WHACKED) return; free((void *)t); } /* * ---------------------------------------------------- * @Name rmLightSetType @pstart RMenum rmLightSetType (RMlight *toModify, RMenum newType) @pend @astart RMlight *toModify - a handle to an RMlight object (modified). RMenum newType - an RMenum value specifying the new light type for the RMlight object. Must be one of RM_LIGHT_POINT, RM_LIGHT_DIRECTIONAL, or RM_LIGHT_SPOT (input). @aend @dstart RM light sources may be directional, spot or point light sources, and this routine is used to set the type of an RMlight object to one of those light source types. Returns RM_CHILL upon success, or RM_WHACKED upon failure. RMlight objects represent light sources. Of the parameters in an RMlight object, some are used regardless of light source type, while others have meaning only for certain types of lights. The ambient, diffuse and specular color attributes are used by all light source types (see rmLightSetColor). Also, the light attenuation coefficients are used by all light source types (see rmLightSetAttenuation). All light sources have a 3D coordinate associated with them, set with rmLightSetXYZ. The position of Point light sources (RM_LIGHT_POINT) and spotlight sources (RM_LIGHT_SPOT) is specified with rmLightSetXYZ. Directional light sources have their orientation vector specified with rmLightSetXYZ. Note that setting attributes in an RMlight does not change them in the scene graph. They must be assigned as scene parameters to an RMnode before they will be used in rendering (see rmNodeSetSceneLight). @dend * ---------------------------------------------------- */ RMenum rmLightSetType (RMlight *t, RMenum newType) { if (RM_ASSERT(t, "rmLightSetType() error: the input RMlight is NULL") == RM_WHACKED) return(RM_WHACKED); if ((newType == RM_LIGHT_POINT) || (newType == RM_LIGHT_DIRECTIONAL) || (newType == RM_LIGHT_SPOT)) { t->ltype = newType; } else { rmError("rmLightSetType() error: the newType enumerator is not one of RM_LIGHT_POINT, RM_LIGHT_DIRECTIONAL or RM_LIGHT_SPOT"); return(RM_WHACKED); } return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightGetType @pstart RMenum rmLightGetType (const RMlight *toQuery) @pend @astart const RMlight *toQuery - a handle to an RMlight object (input). @aend @dstart Upon success, returns to the caller the RMlight light source type; RM_LIGHT_POINT, RM_LIGHT_SPOT or RM_LIGHT_DIRECTIONAL. Otherwise, returns RM_WHACKED. @dend * ---------------------------------------------------- */ RMenum rmLightGetType (const RMlight *t) { if (RM_ASSERT(t, "rmLightGetType() error: the input RMlight is NULL") == RM_WHACKED) return(RM_WHACKED); return(t->ltype); } /* * ---------------------------------------------------- * @Name rmLightSetXYZ @pstart RMenum rmLightSetXYZ (RMlight *toModify, const RMvertex3D *newXYZ) @pend @astart RMlight *toModify - a handle to an RMlight object (modified). const RMvertex3D *newXYZ - a handle to an RMvertex3D object (input). @aend @dstart This routine is used to set the XYZ attribute of a light source object, controlling the position of RM_LIGHT_POINT and RM_LIGHT_SPOT sources, or the direction of RM_LIGHT_DIRECTIONAL objects. Copies from the caller-supplied RMvertex3D object into the RMlight object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. Note that setting attributes in an RMlight does not change them in the scene graph. They must be assigned as scene parameters to an RMnode before they will be used in rendering (see rmNodeSetSceneLight). @dend * ---------------------------------------------------- */ RMenum rmLightSetXYZ (RMlight *t, const RMvertex3D *newXYZ) { if ((RM_ASSERT(t, "rmLightSetXYZ() error: the input RMlight is NULL") == RM_WHACKED) || (RM_ASSERT(newXYZ, "rmLightSetXYZ() error: the input newXYZ pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); t->lightXYZ = *newXYZ; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightGetXYZ @pstart RMenum rmLightGetXYZ (const RMlight *toQuery, RMvertex3D *retXYZ) @pend @astart const RMlight *toQuery - a handle to an RMlight object (input). RMvertex3D *retXYZ - a handle to an RMvertex3D object (modified). @aend @dstart Upon success, copies the light source's XYZ position/orientation attribute from the RMlight object into the caller-supplied memory, and returns RM_CHILL. Upon failure, returns RM_WHACKED. @dend * ---------------------------------------------------- */ RMenum rmLightGetXYZ (const RMlight *t, RMvertex3D *retXYZ) { if ((RM_ASSERT(t, "rmLightGetXYZ() error: the input RMlight is NULL") == RM_WHACKED) || (RM_ASSERT(retXYZ, "rmLightGetXYZ() error: the input retXYZ pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); *retXYZ = t->lightXYZ; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightSetColor @pstart RMenum rmLightSetColor (RMlight *toModify, const RMcolor4D *newAmbientColor, const RMcolor4D *newDiffuseColor, const RMcolor4D *newSpecularColor) @pend @astart RMlight *toModify - a handle to an RMlight object (modified). const RMcolor4D *newAmbientColor, *newDiffuseColor, *newSpecularColor - handles to RMcolor4D objects specifying the RGBA colors for the light source ambient, diffuse and specular light terms. Use of NULL for some or all terms is acceptable (input). @aend @dstart In RM (and OpenGL), the final shade computed at a vertex is a function of the vertex color, the current material properties and the light source(s). OpenGL uses a Phong-type shading model at each vertex that computes the dot product of the surface normal at the vertex (specified by the application) with the direction to each light source (directional light sources require less work at this stage than positional light sources). This dot product to weight the diffuse and specular reflectance terms of the shading equation. The Phong shading equation has terms for three types of light reflectance: ambient, diffuse and specular. It is possible for applications to specify the ambient, diffuse and specular terms (colors) for both the light sources as well as the objects in the scene to implement custom scenes like a yellow light shining on a green ball. This routine is used to set the ambient, diffuse and specular color terms for a specific light source. Note that the "global ambient light" level is set in the RMlightModel object, not in the RMlight object (see rmLightModelNew). It is possible for applications to selectively modify a particular reflectence color term - by specifying NULL for one of the input color vectors, that term in the RMlight object is not modified. In other words, only those non-NULL color terms are modified in the RMlight object. Upon success, this routine copies from the caller-supplied RMcolor4D objects into the RMlight object and returns RM_CHILL. Otherwise, RM_WHACKED is returned. Note that setting attributes in an RMlight does not change them in the scene graph. They must be assigned as scene parameters to an RMnode before they will be used in rendering (see rmNodeSetSceneLight). @dend * ---------------------------------------------------- */ RMenum rmLightSetColor (RMlight *t, const RMcolor4D *newAmbientColor, const RMcolor4D *newDiffuseColor, const RMcolor4D *newSpecularColor) { if (RM_ASSERT(t, "rmLightSetAmbientColor() error: the input RMlight is NULL") == RM_WHACKED) return(RM_WHACKED); if (newAmbientColor != NULL) t->ambientLightColor = *newAmbientColor; if (newDiffuseColor != NULL) t->diffuseLightColor = *newDiffuseColor; if (newSpecularColor != NULL) t->specularLightColor = *newSpecularColor; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightGetColor @pstart RMenum rmLightGetColor (const RMlight *toQuery, RMcolor4D *retAmbientColor, RMcolor4D *retDiffuseColor, RMcolor4D *retSpecularColor) @pend @astart const RMlight *toQuery - a handle to an RMlight object (input). RMcolor4D *retAmbientColor, *retDiffuseColor, *retSpecularColor - handles to RMcolor4D objects (modified). Use of NULL for some or all terms is acceptable (input). @aend @dstart Use this routine to obtain one or more light source color vectors from an RMlight object. Upon success, one or more color vectors is copied from the RMlight object into caller-supplied memory, and RM_CHILL is returned. Otherwise, RM_WHACKED is returned. @dend * ---------------------------------------------------- */ RMenum rmLightGetColor (const RMlight *t, RMcolor4D *retAmbientColor, RMcolor4D *retDiffuseColor, RMcolor4D *retSpecularColor) { if (RM_ASSERT(t, "rmLightGetColor() error: the input RMlight is NULL") == RM_WHACKED) return(RM_WHACKED); if (retAmbientColor != NULL) *retAmbientColor = t->ambientLightColor; if (retDiffuseColor != NULL) *retDiffuseColor = t->diffuseLightColor; if (retSpecularColor != NULL) *retSpecularColor = t->specularLightColor; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightSetAttenuation @pstart RMenum rmLightSetAttenuation (RMlight *toModify, float newConstantAttenuation, float newLinearAttenuation, float newQuadraticAttenuation) @pend @astart RMlight *toModify - a handle to an RMlight object (modified). float newConstantAttenuation, newLinearAttenuation, newQuadraticAttenuation - floating point values specifying the coefficients for constant, linear and quadratic light attenuation, or decay, for an RMlight source (input). @aend @dstart In the real world, light intensity decays as a function of distance from the light source. The further away from the light source, the weaker the light. The rate at which light decays is largely a function of the medium through which the light energy passes. In a vaccuum, like space, light decays slowly. In another medium, like water, light decays more rapidly as the light energy is absorbed by the participating medium. Applications have some measure of control over how light energy decays as a function of distance from the light source through the manipulation of "light attenuation factors." The equation used to compute attenuation of light intensity at some point in space is: L = 1 / (Kc + Kl * d + Kq * d^2) where d is the distance to the light source. By default, that constant term (Kc) is set to 1.0, and the linear (Kl) and quadratic (Kq) terms are set to zero; light energy does not decay at all. Most OpenGL implementations are optimized for this condition. Upon success, the three attenuation coefficients are copied into the RMlight object, and RM_CHILL is returned. Otherwise, RM_WHACKED is returned. Note that setting attributes in an RMlight does not change them in the scene graph. They must be assigned as scene parameters to an RMnode before they will be used in rendering (see rmNodeSetSceneLight). @dend * ---------------------------------------------------- */ RMenum rmLightSetAttenuation (RMlight *t, float newConstantAttenuation, float newLinearAttenuation, float newQuadraticAttenuation) { if (RM_ASSERT(t, "rmLightSetAttenuation() error: the input RMlight is NULL") == RM_WHACKED) return(RM_WHACKED); t->constantAttenuation = newConstantAttenuation; t->linearAttenuation = newLinearAttenuation; t->quadraticAttenuation = newQuadraticAttenuation; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightGetAttenuation @pstart RMenum rmLightGetAttenuation (const RMlight *toQuery, float *retConstantAttenuation, float *retLinearAttenuation, float *retQuadraticAttenuation) @pend @astart const RMlight *toQuery - a handle to an RMlight object (input). float *retConstantAttenuation, *retLinearAttenuation, *retQuadraticAttenuation - handle to caller-supplied floats (modified). @aend @dstart Upon success, copies the three light attenuation factors into caller-supplied memory and returns RM_CHILL. Otherwise, RM_WHACKED is returned. Note that all three input parameters must be non-NULL, otherwise an error condition is produced. See rmLightSetAttenuation for more details about these attributes. @dend * ---------------------------------------------------- */ RMenum rmLightGetAttenuation (const RMlight *t, float *retConstantAttenuation, float *retLinearAttenuation, float *retQuadraticAttenuation) { if ((RM_ASSERT(t, "rmLightGetAttenuation() error: the input RMlight is NULL") == RM_WHACKED) || (RM_ASSERT(retConstantAttenuation, "rmLightGetAttenuation() error: the retConstantAttenuation pointer is NULL") == RM_WHACKED) || (RM_ASSERT(retLinearAttenuation, "rmLightGetAttenuation() error: the retLinearAttenuation pointer is NULL") == RM_WHACKED) || (RM_ASSERT(retQuadraticAttenuation, "rmLightGetAttenuation() error: the retQuadraticAttenuation pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); *retConstantAttenuation = t->constantAttenuation; *retLinearAttenuation = t->linearAttenuation; *retQuadraticAttenuation = t->quadraticAttenuation; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightSetEnable @pstart RMenum rmLightSetEnable (RMlight *toModify, RMenum newValue) @pend @astart RMlight *toModify - a handle to an RMlight object (modified). RMenum newValue - an RMenum value, must be either RM_TRUE or RM_FALSE. @aend @dstart Light sources may be individually turned on or off by using this routine. To turn a light on, use RM_TRUE, and to turn it off, use RM_FALSE. Upon success, the newValue attribute is copied into the RMlight object, and RM_CHILL is returned, otherwise RM_WHACKED is returned. Note that setting attributes in an RMlight does not change them in the scene graph. They must be assigned as scene parameters to an RMnode before they will be used in rendering (see rmNodeSetSceneLight). @dend * ---------------------------------------------------- */ RMenum rmLightSetEnable (RMlight *t, RMenum newValue) { if (RM_ASSERT(t, "rmLightSetEnable() error: the input RMlight pointer is NULL.") == RM_WHACKED) return(RM_WHACKED); if (newValue == RM_TRUE || newValue == RM_FALSE) t->enabled = newValue; else { rmError("rmLightSetEnable() error: the input newValue enumerator is niether RM_TRUE nor RM_FALSE."); return(RM_WHACKED); } return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightGetEnable @pstart RMenum rmLightGetEnable (const RMlight *toQuery) @pend @astart const RMlight *toQuery - a handle to an RMlight object (input). @aend @dstart Upon success, returns to the caller either RM_TRUE or RM_FALSE, indicating that the light source is either turned on or turned off, respectively. Otherwise, returns RM_WHACKED. @dend * ---------------------------------------------------- */ RMenum rmLightGetEnable (const RMlight *t) { if (RM_ASSERT(t, "rmLightGetEnable() error: the input RMlight object is NULL.") == RM_WHACKED) return(RM_WHACKED); return(t->enabled); } /* * ---------------------------------------------------- * @Name rmLightSetSpotDirection @pstart RMenum rmLightSetSpotDirection (RMlight *toModify, const RMvertex3D *newSpotDirection) @pend @astart RMlight *toModify - a handle to an RMlight object (modified). const RMvertex3D *newSpotDirection - a handle to an RMvertex3D object (input). @aend @dstart Spotlight geometry is specified with both a position (rmLightSetXYZ) and a direction (rmLightSetSpotDirection). This routine is used to set the 3D orientation, or direction, of the spotlight light source. Upon success, this routine will copy the 3D vector from the caller-supplied RMvertex3D object into the RMlight object and return RM_CHILL. Upon failure, RM_WHACKED is returned. Note that setting attributes in an RMlight does not change them in the scene graph. They must be assigned as scene parameters to an RMnode before they will be used in rendering (see rmNodeSetSceneLight). @dend * ---------------------------------------------------- */ RMenum rmLightSetSpotDirection (RMlight *t, const RMvertex3D *newSpotDirection) { if ((RM_ASSERT(t, "rmLightSetSpotDirection() error: the input RMlight is NULL") == RM_WHACKED) || (RM_ASSERT(newSpotDirection, "rmLightSetSpotDirection() error: the input newSpotDirection pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); t->spotDirection = *newSpotDirection; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightGetSpotDirection @pstart RMenum rmLightGetSpotDirection (const RMlight *toModify, RMvertex3D *retDirection) @pend @astart const RMlight *toModify - a handle to an RMlight object (input). RMvertex3D *retDirection - a handle to an RMvertex3D object (modified, return). @aend @dstart Spotlights are specified with both a position (rmLightSetXYZ) and a direction (rmLightSetSpotDirection). Use this routine to obtain the direction vector of a spotlight source. Upon success, this routine copies the spotlight direction vector from the RMlight object into the caller-supplied memory and returns RM_CHILL. Upon failure, RM_WHACKED is returned. @dend * ---------------------------------------------------- */ RMenum rmLightGetSpotDirection (const RMlight *t, RMvertex3D *retDirection) { if ((RM_ASSERT(t, "rmLightGetSpotDirection() error: the input RMlight is NULL") == RM_WHACKED) || (RM_ASSERT(retDirection, "rmLightGetSpotDirection() error: the input retDirection pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); *retDirection = t->spotDirection; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightSetSpotCutoff @pstart RMenum rmLightSetSpotCutoff (RMlight *toModify, float newValue) @pend @astart RMlight *toModify - a handle to an RMlight object (modified). float newValue - a floating point value in the range [0..90] (or the special value of 180) specifying the spread angle of the spotlight (input). @aend @dstart The spotlight light source consists of a number of parameters. Spotlight position is it's location in 3-space (rmLightSetXYZ). The direction in which the spotlight is pointed is it's orientation (rmLightSetSpotDirection). The light color consists of ambient, diffuse and specular colors (rmLightSetColor). The rate at which light decays as a function of distance from the light source position is specified with constant, linear and quadratic coefficients (rmLightSetAttenuation). The spread angle of the spotlight is a measure of the half-angle, in degrees, of the apex of a cone formed by the spotlight, with the light positioned at the apex of the cone and oriented towards the base of the cone (rmLightSetSpotCutoff). Finally, the rate at which light decays, or falls off, as a function of distance from the vector formed by the center of the spotlight cone (rmLightSetSpotExponent). This routine is used to set the value that controls the spread angle of the spotlight light source. The value must be in the range 0 to 90, or the special value 180. This value specifies the half-angle of the cone apex formed by the illumination volume of the spotlight source. The spotlight source is positioned at the apex of this cone, and shines in the direction of the base of the cone. The greater this value, the larger the illumination volume. A value of 45 (degrees) specifies an illumination volume with a cone apex of 90 degrees. The special value of 180.0 specifies a spotlight source that shines in all directions. Upon success, the spotlight cutoff attribute of the RMlight object is modified, and RM_CHILL is returned to the caller. Otherwise, RM_WHACKED is returned. Note that setting attributes in an RMlight does not change them in the scene graph. They must be assigned as scene parameters to an RMnode before they will be used in rendering (see rmNodeSetSceneLight). @dend * ---------------------------------------------------- */ RMenum rmLightSetSpotCutoff (RMlight *t, float newValue) { if (RM_ASSERT(t, "rmLightSetSpotCutoff() error: the input RMlight is NULL") == RM_WHACKED) return(RM_WHACKED); t->spotCutoff = newValue; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightGetSpotCutoff @pstart RMenum rmLightGetSpotCutoff (const RMlight *toQuery, float *retValue) @pend @astart const RMlight *toQuery - a handle to an RMlight object (input). float *retValue - a handle to a caller-supplied float (modified). @aend @dstart Use this routine to obtain the spotlight cutoff value from an RMlight object. Upon success, the spotlight cutoff value is copied into the caller-supplied memory and RM_CHILL is returned. Otherwise, RM_WHACKED is returned. See rmLightSetSpotCutoff for more details about the meaning of the spotlight cutoff attribute. @dend * ---------------------------------------------------- */ RMenum rmLightGetSpotCutoff (const RMlight *t, float *retValue) { if ((RM_ASSERT(t, "rmLightGetSpotCutoff() error: the input RMlight is NULL") == RM_WHACKED) || (RM_ASSERT(retValue, "rmLightSetSpotCutoff() error: the return float pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); *retValue = t->spotCutoff; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightSetSpotExponent @pstart RMenum rmLightSetSpotExponent (RMlight *toModify, float newValue) @pend @astart RMlight *toModify - a handle to an RMlight object (modified). float newValue - a non-negative floating point value (input). @aend @dstart The spotlight light source consists of a number of parameters. Spotlight position is it's location in 3-space (rmLightSetXYZ). The direction in which the spotlight is pointed is it's orientation (rmLightSetSpotDirection). The light color consists of ambient, diffuse and specular colors (rmLightSetColor). The rate at which light decays as a function of distance from the light source position is specified with constant, linear and quadratic coefficients (rmLightSetAttenuation). The spread angle of the spotlight is a measure of the half-angle, in degrees, of the apex of a cone formed by the spotlight, with the light positioned at the apex of the cone and oriented towards the base of the cone (rmLightSetSpotCutoff). Finally, the rate at which light decays, or falls off, as a function of distance from the vector formed by the center of the spotlight cone (rmLightSetSpotExponent). This routine is used to set the spotlight exponent value, or the rate at which light decays as a function of distance from the center of the illumination cone. By default, this parameter is set to zero, indicating uniform light distribution (no decay) within the illumination cone. Since this attribute specifies an exponent, light decay falls off more rapidly as the value for this parameter increases. Linear decay (inverse distance) is specified using a value of 1.0 for this parameter; a value of 2.0 specifies that light falls off as the inverse of the square of the distance to the center of the illumination cone. Upon success, this routine copies the caller-supplied newValue floating point value into the RMlight object's spotlight exponent attribute and returns RM_CHILL, otherwise RM_WHACKED is returned. Note that setting attributes in an RMlight does not change them in the scene graph. They must be assigned as scene parameters to an RMnode before they will be used in rendering (see rmNodeSetSceneLight). @dend * ---------------------------------------------------- */ RMenum rmLightSetSpotExponent (RMlight *t, float newValue) { if (RM_ASSERT(t, "rmLightSetSpotExponent() error: the input RMlight is NULL") == RM_WHACKED) return(RM_WHACKED); t->spotExponent = newValue; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightGetSpotExponent @pstart RMenum rmLightGetSpotExponent (const RMlight *toQuery, float *retValue) @pend @astart const RMlight *toQuery - a handle to an RMlight object (input). float *retValue - a handle to a caller-supplied float (modified). @aend @dstart Use this routine to obtain the spotlight exponent attribute of an RMlight object. Upon success, the spotlight exponent attribute of the RMlight object is copied into caller-supplied memory, and RM_CHILL is returned. Otherwise, RM_WHACKED is returned. See rmLightSetSpotExponent for more details about the meaning and use of this attribute. @dend * ---------------------------------------------------- */ RMenum rmLightGetSpotExponent (const RMlight *t, float *retValue) { if ((RM_ASSERT(t, "rmLightGetSpotExponent() error: the input RMlight is NULL") == RM_WHACKED) || (RM_ASSERT(retValue, "rmLightSetSpotExponent() error: the return float pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); *retValue = t->spotExponent; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightModelNew @pstart RMlightModel * rmLightModelNew (void) @pend @astart No arguments. @aend @dstart Creates a new RMlightModel object, initializes it with default values, and returns to handle to the caller. NULL is returned upon failure. Lighting in RM (and OpenGL) is a function of light sources and the lighting environment. The RMlight objects contain information about the specific light sources, such as their position, light decay properties, color and so forth. The RMlightModel object controls global lighting parameters, including global ambient illumination, two-sided illumination control and selection of one of two algorithms used to compute specular reflection values. Global ambient illumination is the amount of light that is present everywhere in the scene, and represents the combination of overall composite luminance with the degree to which light is scattered. By default, the ambient color assigned to the RMlightModel object is {0.,0.,0.}, indicating the absence of global ambient light. During shading, the global ambient term is applied to all vertices, regardless of the amount of ambient light specified in an RMlight object. This term can be thought of as another independent light source in the scene (see rmLightModelSetAmbient). Two-sided illumination is used to create the effect of a bi-directional light source. By default, two-sided lighting is turned off (see rmLightModelSetTwoSided). Specular reflections take into account the surface normal, the direction to the light source from a vertex, and the direction to the viewer from the vertex. When the "local viewer" attribute is set to RM_TRUE, the direction to the viewer from each vertex is calculated during shading. This results in more realistic shading, but at added computational expense. When set to RM_FALSE, the position of the viewer is fixed at (0,0,0) in eye coordinates (not world coordinates) for the purposes of shading. Note: OpenGL 1.2 adds a new term to the lighting model that can be used to control the computation specular reflection color. This feature has not yet been integrated into RM. Applications need to create both a light source and a light model in order for lighting to work properly. @dend * ---------------------------------------------------- */ RMlightModel * rmLightModelNew (void) { RMlightModel *t; t = (RMlightModel *)malloc(sizeof(RMlightModel)); if (t == NULL) { rmError("rmLightModelNew() error: unable to malloc a new RMlightModel object. "); return(NULL); } memset(t, 0, sizeof(RMlightModel)); /* default ambient is (0,0,0,0) */ t->twoSideEnable = RM_FALSE; t->localViewerEnable = RM_FALSE; return(t); } /* * ---------------------------------------------------- * @Name rmLightModelDelete @pstart RMenum rmLightModelDelete (RMlightModel *toDelete) @pend @astart RMlightModel *toDelete - a handle to an RMlightModel object to be deleted (modified). @aend @dstart Releases resources associated with an RMlightModel object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. This routine is the opposite of rmLightModelNew. @dend * ---------------------------------------------------- */ RMenum rmLightModelDelete (RMlightModel *toDelete) { if (RM_ASSERT(toDelete, "rmLightModelDelete() error: the input RMlightModel pointer is NULL") == RM_WHACKED) return(RM_WHACKED); free((void *)toDelete); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightModelSetAmbient @pstart RMenum rmLightModelSetAmbient (RMlightModel *toModify, const RMcolor4D *newAmbientColor) @pend @astart RMlightModel *toModify - a handle to an RMlightModel object (modified). const RMcolor4D *newAmbientColor - a handle to an RMcolor4D object (input). @aend @dstart Upon success, copies the contents of the RMcolor4D object to the RMlightModel object, and returns RM_CHILL. Otherwise, RM_WHACKED is returned. Color attributes are specified with floating point values in the range 0..1, where 0 is "off" and 1 is "on." The RMcolor4D object represents red, green, blue and alpha. See rmLightModelNew for more information about the global ambient light model attribute. Note that setting attributes in an RMlightModel does not change them in the scene graph. They must be assigned as scene parameters to an RMnode before they will be used in rendering (see rmNodeSetSceneLightModel). @dend * ---------------------------------------------------- */ RMenum rmLightModelSetAmbient (RMlightModel *toModify, const RMcolor4D *newAmbientColor) { if ((RM_ASSERT(toModify, "rmLightModelSetAmbient() error: the input RMlightModel pointer is NULL") == RM_WHACKED) || (RM_ASSERT(newAmbientColor, "rmLightModelSetAmbient() error: the input newAmbientColor pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); toModify->globalAmbient = *newAmbientColor; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightModelGetAmbient @pstart RMenum rmLightModelGetAmbient (const RMlightModel *toQuery, RMcolor4D *retAmbientColor) @pend @astart const RMlightModel *toQuery - a handle to an RMlightModel object (input). RMcolor4D *retAmbientColor - a handle to an RMcolor4D object (modified). @aend @dstart Upon success, this routine will copy the global ambient light attribute from the RMlightModel object into the caller-supplied RMcolor4D object, and return RM_CHILL. Otherwise, RM_WHACKED is returned. See rmLightModelNew for more information about the global ambient light model attribute. @dend * ---------------------------------------------------- */ RMenum rmLightModelGetAmbient (const RMlightModel *toQuery, RMcolor4D *retAmbientColor) { if ((RM_ASSERT(toQuery, "rmLightModelGetAmbient() error: the input RMlightModel pointer is NULL") == RM_WHACKED) || (RM_ASSERT(retAmbientColor, "rmLightModelGetAmbient() error: the input retAmbientColor pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); *retAmbientColor = toQuery->globalAmbient; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightModelSetTwoSided @pstart RMenum rmLightModelSetTwoSided (RMlightModel *toModify, RMenum newValue) @pend @astart RMlightModel *toModify - a handle to an RMlightModel object (modified). RMenum newValue - an RMenum value, should be either RM_TRUE or RM_FALSE (input). @aend @dstart This routine sets the two-sided lighting attribute of an RMlightModel object. When RM_TRUE is specified, two-sided lighting is activitated for this light model. When set to RM_FALSE, two-sided lighting is disabled, and single-sided lighting takes effect. Returns RM_CHILL upon success, or RM_WHACKED upon failure. Note that setting attributes in an RMlightModel does not change them in the scene graph. They must be assigned as scene parameters to an RMnode before they will be used in rendering (see rmNodeSetSceneLightModel). @dend * ---------------------------------------------------- */ RMenum rmLightModelSetTwoSided (RMlightModel *toModify, RMenum newValue) { if (RM_ASSERT(toModify, "rmLightModelSetTwoSided() error: the input RMlightModel pointer is NULL") == RM_WHACKED) return(RM_WHACKED); if (newValue == RM_TRUE || newValue == RM_FALSE) toModify->twoSideEnable = newValue; else { rmWarning("rmLightModelTwoSided warning: the input RMenum is neither RM_TRUE nor RM_FALSE"); return(RM_WHACKED); } return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightModelGetTwoSided @pstart RMenum rmLightModelGetTwoSided (const RMlightModel *toQuery) @pend @astart const RMlightModel *toQuery - a handle to an RMlightModel object (input). @aend @dstart Upon success, returns the setting of the two-sided lighting attribute of an RMlightModel object, either RM_TRUE or RM_FALSE. Otherwise, RM_WHACKED is returned. @dend * ---------------------------------------------------- */ RMenum rmLightModelGetTwoSided (const RMlightModel *toQuery) { if (RM_ASSERT(toQuery, "rmLightModelGetTwoSided() error: the input RMlightModel pointer is NULL") == RM_WHACKED) return(RM_WHACKED); return((RMenum)toQuery->twoSideEnable); } /* * ---------------------------------------------------- * @Name rmLightModelSetLocalViewer @pstart RMenum rmLightModelSetLocalViewer (RMlightModel *toModify, RMenum newValue) @pend @astart RMlightModel *toModify - a handle to an RMlightModel object (modified). RMenum newValue - an RMenum value, must be either RM_TRUE or RM_FALSE (input). @aend @dstart This routine will set the "local viewer" attribute of the RMlightModel object to the value specified by the caller and return RM_CHILL upon success. Otherwise, RM_WHACKED is returned. See rmLightModelNew for more information about the "local viewer" attribute. Note that setting attributes in an RMlightModel does not change them in the scene graph. They must be assigned as scene parameters to an RMnode before they will be used in rendering (see rmNodeSetSceneLightModel). @dend * ---------------------------------------------------- */ RMenum rmLightModelSetLocalViewer (RMlightModel *toModify, RMenum newValue) { if (RM_ASSERT(toModify, "rmLightModelSetLocalViewer() error: the input RMlightModel pointer is NULL") == RM_WHACKED) return(RM_WHACKED); if (newValue == RM_TRUE || newValue == RM_FALSE) toModify->localViewerEnable = newValue; else { rmError(" rmLightModelSetLocalViewer() error: the input enumerator is neither RM_TRUE nor RM_FALSE "); return(RM_WHACKED); } return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmLightModelGetLocalViewer @pstart RMenum rmLightModelGetLocalViewer (const RMlightModel *toQuery) @pend @astart const RMlightModel *toQuery - a handle to an RMlightModel object (input). @aend @dstart Upon success, returns the "local viewer" attribute of the RMlightModel object. Otherwise, RM_WHACKED is returned. See rmLightModelNew for more information about the "local viewer" attribute of the RMlightModel object. @dend * ---------------------------------------------------- */ RMenum rmLightModelGetLocalViewer (const RMlightModel *toQuery) { if (RM_ASSERT(toQuery, "rmLightModelGetLocalViewer() error: the input RMlightModel pointer is NULL") == RM_WHACKED) return(RM_WHACKED); return((RMenum)toQuery->localViewerEnable); } /* * ---------------------------------------------------- * @Name rmDefaultLighting @pstart RMenum rmDefaultLighting (RMnode *toModify) @pend @astart RMnode *toModify - a handle to an RMnode (modified). @aend @dstart This convenience routine is used to create a default set of lights and a default lighting model, and to assign them as scene parameters to an RMnode. The new lights and lighting model have effect over all children of the specified RMnode. The default lighting configuration consists of: 1. One directional light source at 0,10,3 (overhead and slightly behind the viewer) with no ambient term, diffuse set to (0.7,0.7,0.7,1.0) and specular set to (0.5,0.5,0.5,1.0). A second light source is available by uncommenting one line of code inside this routine. 2. A light model with global ambient light set to (0.2,0.2,0.2,1.0), two-sided lighting disabled and disabling of "local viewer" specular reflection calculations. The net result is a lighting model that works reasonably well for most scenes both in terms of performance as well as illumination characteristics. While this convenience function is useful, developers are encouraged to examine this routine because it is a good example of how to create and modify light sources and light models, as well as assign them as scene parameters to an RMnode. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmDefaultLighting (RMnode *n) { /* light source zero is a from-the-top directional light */ RMlight *l0, *l1; RMcolor4D diffuse = {0.7, 0.7, 0.7, 1.0}; RMcolor4D specular = {0.5, 0.5, 0.5, 1.0}; RMcolor4D diffuse2 = {0.3, 0.3, 0.5, 1.0}; RMvertex3D pos2 = {0.0, -3.0, 1.0}; RMvertex3D position = {0.0, 10.0, 3.0}; l0 = rmLightNew(); if (l0 == NULL) return(RM_WHACKED); rmLightSetType(l0, RM_LIGHT_DIRECTIONAL); rmLightSetColor(l0, NULL, &diffuse, &specular); rmLightSetXYZ (l0, &position); /* * light source one is a from-the-bottom directional light with * reduced emphasis on diffuse and specular. the more dim * diffuse light is kind of blue. */ l1 = rmLightNew(); if (l1 == NULL) return(RM_WHACKED); rmLightSetType(l1, RM_LIGHT_DIRECTIONAL); rmLightSetColor(l1, NULL, &diffuse2, &specular); rmLightSetXYZ(l1, &pos2); rmNodeSetSceneLight(n, RM_LIGHT0, l0); /* * uncomment the following line to add the second light source * into the scene. we leave it out by default for performance reasons, * but having it readily available for experimentation has proven useful. * * rmNodeSetSceneLight(n, RM_LIGHT1, l1); */ /* * when the lights are added as scene parameters, RM makes a copy * of them, and we don't need our RMlight objects anymore, so we * need to delete them. */ rmLightDelete(l0); rmLightDelete(l1); /* set up the light model/lighting environment */ { RMcolor4D defAmbient = {0.2, 0.2, 0.2, 1.0}; RMlightModel *lm = rmLightModelNew(); if (lm == NULL) return(RM_WHACKED); rmLightModelSetAmbient(lm, &defAmbient); rmLightModelSetTwoSided (lm, RM_FALSE); /* rmLightModelSetLocalViewer(lm, RM_TRUE); */ rmLightModelSetLocalViewer(lm, RM_FALSE); rmNodeSetSceneLightModel(n, lm); rmLightModelDelete (lm); } return(RM_CHILL); } /* EOF */