/* * 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: rmcamera.c,v 1.9 2005/02/19 16:22:50 wes Exp $ * Version: $Name: OpenRM-1-6-0-RC5 $ * $Revision: 1.9 $ * $Log: rmcamera.c,v $ * Revision 1.9 2005/02/19 16:22:50 wes * Distro sync and consolidation. * * Revision 1.8 2005/01/23 17:00:22 wes * Copyright updated to 2005. * * Revision 1.7 2004/04/09 14:48:03 wes * Fix some compile warnings about unused variables. * * Revision 1.6 2004/03/30 14:13:31 wes * Fixed declarations and man page docs for several routines. * * Revision 1.5 2004/02/13 00:46:31 wes * Updated inline documentation. * * Revision 1.4 2004/01/16 16:43:24 wes * Updated copyright line for 2004. * * Revision 1.3 2003/06/13 03:08:39 wes * Minor documentation tweak. * * 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.7 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.6 2002/08/17 15:07:46 wes * rmCamera3DComputeViewFromGeometry() - updated computation of near/far * clip planes to allow more front-to-back maneuvering space. * * Revision 1.5 2002/04/30 19:28:55 wes * Updated copyright dates. * * Revision 1.4 2001/03/31 17:12:38 wes * v1.4.0-alpha-2 checkin. * * Revision 1.3 2000/08/23 23:24:52 wes * Updated documentation. * * 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:47 wes * Initial entry - pre-RM120 release, source base for OpenRM 1.2. * */ #include #include "rmprivat.h" /* default camera parameters */ extern float RM_DEFAULT_2DCAMERA_XMIN, RM_DEFAULT_2DCAMERA_YMIN, RM_DEFAULT_2DCAMERA_XMAX, RM_DEFAULT_2DCAMERA_YMAX; extern float RM_DEFAULT_2DCAMERA_ASPECT; extern float RM_DEFAULT_3DCAMERA_HITHER, RM_DEFAULT_3DCAMERA_YON, RM_DEFAULT_3DCAMERA_ASPECT, RM_DEFAULT_3DCAMERA_FOV; extern RMenum RM_DEFAULT_3DCAMERA_PROJECTION; extern RMvertex3D RM_DEFAULT_3DCAMERA_EYE, RM_DEFAULT_3DCAMERA_LOOKAT, RM_DEFAULT_3DCAMERA_UP; /* PRIVATE declarations */ static void private_rmCamera2DComputeMatrix (const RMcamera2D *c, RMmatrix *m); /* * ---------------------------------------------------- * @Name rmCamera2DNew @pstart RMcamera2D * rmCamera2DNew (void) @pend @astart No arguments. @aend @dstart A default 2D camera is created and a handle to the requested RMcamera2D is returned. If the 2D camera cannot be created, then a NULL pointer is returned. @dend * ---------------------------------------------------- */ RMcamera2D * rmCamera2DNew (void) { RMcamera2D *c; if ((c = (RMcamera2D *)malloc(sizeof(RMcamera2D))) == NULL) return(NULL); /* defaults */ rmCamera2DSetExtents(c, RM_DEFAULT_2DCAMERA_XMIN, RM_DEFAULT_2DCAMERA_YMIN, RM_DEFAULT_2DCAMERA_XMAX, RM_DEFAULT_2DCAMERA_YMAX); rmCamera2DSetAspectRatio(c, RM_DEFAULT_2DCAMERA_ASPECT); return(c); } /* * ---------------------------------------------------- * @Name rmCamera2DCopy @pstart RMenum rmCamera2DCopy (RMcamera2D *dst, const RMcamera2D *src) @pend @astart RMcamera2D *dst - a handle to an RMcamera2D object to be modified (output). const RMcamera2D *src - a handle to an RMcamera2D object that will be copied (input). @aend @dstart Copies all parameters and attributes from one RMcamera2D object to another. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmCamera2DCopy (RMcamera2D *dst, const RMcamera2D *src) { if ((RM_ASSERT(dst, "rmCamera2DCopy error: the dst RMcamera2D pointer is NULL") == RM_WHACKED) || (RM_ASSERT(src, "rmCamera2DCopy error: the src RMcamera2D pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); memcpy((void *)dst, (void *)src, sizeof(RMcamera2D)); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera2DDelete @pstart void rmCamera2DDelete (RMcamera2D *toDelete) @pend @astart RMcamera2D *toDelete - a handle to RMcamera2D object to delete (modified). @aend @dstart Free the RMcamera2D resources pointed to by c. It is assumed that c points to a valid RMcamera2D previously created with rmCamera2DNew(). @dend * ---------------------------------------------------- */ void rmCamera2DDelete (RMcamera2D *c) { if (c != NULL) free(c); } /* * ---------------------------------------------------- * @Name rmDefaultCamera2D @pstart RMenum rmDefaultCamera2D (RMcamera2D *toModify) @pend @astart RMcamera2D *toModify - a handle to a caller supplied RMcamera2D object that will be modified by this routine (modified). @aend @dstart This routine assigns a set of default parameters to an RMcamera2D object. All existing settings of the input RMcamera2D object will be overwritten. Returns RM_CHILL upon success, or RM_WHACKED upon failure. The new values written by this routine are: 1. 2D extents set to: (-1,-1)..(1,1) 2. Aspect ratio set to 1.0. @dend * ---------------------------------------------------- */ RMenum rmDefaultCamera2D (RMcamera2D *c) { if (RM_ASSERT(c, "rmDefaultCamera2D error: the input RMcamera2D object is NULL") == RM_WHACKED) return(RM_WHACKED); rmCamera2DSetExtents(c, RM_DEFAULT_2DCAMERA_XMIN, RM_DEFAULT_2DCAMERA_YMIN, RM_DEFAULT_2DCAMERA_XMAX, RM_DEFAULT_2DCAMERA_YMAX); rmCamera2DSetAspectRatio(c, RM_DEFAULT_2DCAMERA_ASPECT); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera2DSetAspectRatio @pstart RMenum rmCamera2DSetAspectRatio (RMcamera2D *toModify, float newAspect) @pend @astart RMcamera2D *toModify - a handle to the RMcamera2D object that will be modified by this routine. float newAspect - a floating point value specifying the new aspect ratio for the RMcamera2D object. @aend @dstart Use this routine to set the aspect ratio of a 2D camera. Returns RM_CHILL upon success, or RM_WHACKED upon failure. The aspect ratio is a fraction that is the width of the field of view divided by the height. It is used to control the stretch and squish of rendering. For example, consider rendering into a window whose width is 400 pixels and height is 300 pixels and using a logically square viewport (0,0..1,1). If the camera aspect ratio is set to 1.0, the resulting image will appear to be stretched along the horizontal axis because the square viewport is being mapped to a rectangular window. In this example, we would want to set the aspect ratio to 400/300 to avoid stretching. The aspect ratio is applied at the stage when we compute the view transformation matrix. @dend * ---------------------------------------------------- */ RMenum rmCamera2DSetAspectRatio (RMcamera2D *toModify, float newAspect) { if (RM_ASSERT(toModify, "rmCamera2DSetAspectRatio error: the input RMcamera2D object is NULL") == RM_WHACKED) return(RM_WHACKED); toModify->aspect_ratio = newAspect; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera2DResetAspectRatio @pstart RMenum rmCamera2DResetAspectRatio (RMcamera2D *toModify, const float *vp, int windowWidth, int windowWeight) @pend @astart RMcamera2D *toModify - a handle to the RMcamera2D that will be modified by this routine. const float *vp - an array of floating point values of length 4. This is a viewport specification, ordered {xmin, ymin, xmax, ymax}. The viewport coordinates are in the range 0..1, ranging from the lower left corner of the display window (0,0) to the upper right corner (1,1). int windowWidth, windowWeight - integer values specifying the size in pixels of the display window. These values are used in computing a new aspect ratio for the RMcamera2D object. @aend @dstart Given viewport and window geometry, compute an aspect ratio that will cause pixels to "remain square." In other words, an aspect ratio will be computed so that there is no stretch or squish in the rendered image: a square in world coordinates will remain square in the rendered image, regardless of viewport or window geometry. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmCamera2DResetAspectRatio (RMcamera2D *toModify, const float vp[4], int windowWidth, int windowHeight) { float sx, sy, new_aspect; if ((RM_ASSERT(toModify, "rmCamera2DResetAspectRatio error: the input RMcamera2D error is NULL") == RM_WHACKED) || (RM_ASSERT(vp, "rmCamera2DResetAspectRatio error: the input viewport is NULL")) == RM_WHACKED) return(RM_WHACKED); sx = vp[2] - vp[0]; sy = vp[3] - vp[1]; new_aspect = ((float)(windowWidth) * sx) / ((float)(windowHeight) * sy); rmCamera2DSetAspectRatio(toModify, new_aspect); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera2DGetAspectRatio @pstart RMenum rmCamera2DGetAspectRatio (const RMcamera2D *toQuery, float *retValue) @pend @astart const RMcamera2D *toQuery - a handle to the RMcamera2D object that will be queried by this routine. float *retValue - a handle to a caller-supplied float. The RMcamera2D's aspect ratio attribute will be copied into this memory location. @aend @dstart Use this routine to query the aspect ratio of an RMcamera2D object. RM_CHILL is returned upon success, otherwise RM_WHACKED is returned. The aspect ratio is a fraction that is the width of the field of view divided by the height. It is used to control the stretch and squish of rendering. For example, consider rendering into a window whose width is 400 pixels and height is 300 pixels and using a logically square viewport (0,0..1,1). If the camera aspect ratio is set to 1.0, the resulting image will appear to be stretched along the horizontal axis because the square viewport is being mapped to a rectangular window. In this example, we would want to set the aspect ratio to 400/300 to avoid stretching. The aspect ratio is applied at the stage when we compute the view transformation matrix. @dend * ---------------------------------------------------- */ RMenum rmCamera2DGetAspectRatio (const RMcamera2D *toQuery, float *retValue) { if ((RM_ASSERT(toQuery, "rmCamera2DGetAspectRatio error: the input RMcamera2D object is NULL") == RM_WHACKED) || (RM_ASSERT(retValue, "rmCamera2DGetAspectRatio error: the return float pointer is NULL.") == RM_WHACKED)) return(RM_WHACKED); *retValue = toQuery->aspect_ratio; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera2DSetExtents @pstart RMenum rmCamera2DSetExtents (RMcamera2D *toModify, float xmin, float ymin, float xmax, float ymax) @pend @astart RMcamera2D *toModify - a handle to an RMcamera2D object that will be modified by this routine. float xmin, ymin, xmax, ymax - floating point values that specify the new x/y extents "seen" by the 2D camera. These values are specified in world coordinates. @aend @dstart Use this routine to specify the extents for a 2D camera. Returns RM_CHILL upon success, or RM_WHACKED upon failure. A 2D camera is composed of two parameters in OpenRM: an extents and an aspect ratio. The extents defines a 2D region in world coordinates that will be seen by the 2D camera, these extents are specified with a minimum and maximum coordinate. The aspect ratio defines the relationship between the camera width and height and controls the stretch and squish of rendering, something that is useful if your window is not square. @dend * ---------------------------------------------------- */ RMenum rmCamera2DSetExtents (RMcamera2D *c, float xmin, float ymin, float xmax, float ymax) { if (RM_ASSERT(c, "rmCamera2DSetExtents error: the input RMcamera2D object is NULL") == RM_WHACKED) return(RM_WHACKED); c->xmin = xmin; c->ymin = ymin; c->xmax = xmax; c->ymax = ymax; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera2DGetExtents @pstart RMenum rmCamera2DGetExtents (const RMcamera2D *toQuery, float *xmin, float *ymin, float *xmax, float *ymax) @pend @astart const RMcamera2D *toQuery - a handle to an RMcamera2D object that will be queried by this routine. float *xmin, *ymin, *xmax, *ymax - pointers to caller-supplied floats. The 2D cameras extents parameters will be written into these memory locations. It is permissible to specify NULL for any or all of these parameters - the corresponding 2D camera extents parameter will not be reported for NULL return parameters. @aend @dstart Use this routine to query the extents of a 2D camera. Returns RM_CHILL upon success, or RM_WHACKED upon failure. A 2D camera is composed of two parameters in OpenRM: an extents and an aspect ratio. The extents defines a 2D region in world coordinates that will be seen by the 2D camera, these extents are specified with a minimum and maximum coordinate. The aspect ratio defines the relationship between the camera width and height and controls the stretch and squish of rendering, something that is useful if your window is not square. @dend * ---------------------------------------------------- */ RMenum rmCamera2DGetExtents (const RMcamera2D *c, float *xmin, float *ymin, float *xmax, float *ymax) { if (RM_ASSERT(c, "rmCamera2DGetExtents error: the input RMcamera2D object is NULL") == RM_WHACKED) return(RM_WHACKED); if (xmin != NULL) *xmin = c->xmin; if (ymin != NULL) *ymin = c->ymin; if (xmax != NULL) *xmax = c->xmax; if (ymax != NULL) *ymax = c->ymax; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera2DComputeViewMatrix @pstart RMenum rmCamera2DComputeViewMatrix (const RMcamera2D *camera, RMmatrix *matrixReturn) @pend @astart const RMcamera2D *camera - a handle to an RMcamera2D object (input). RMmatrix *matrixReturn - a handle to a caller-supplied RMmatrix object (result). @aend @dstart Computes the view matrix defined by the 2D input camera, copying the result into caller supplied memory. Upon success, RM_CHILL is returned. Upon failure, RM_WHACKED is returned, and matrixReturn remains undisturbed. Note that the 2D camera does not produce a projection matrix, unlike the 3D camera. @dend * ---------------------------------------------------- */ RMenum rmCamera2DComputeViewMatrix (const RMcamera2D *c, RMmatrix *m) { if ((RM_ASSERT(c, "rmCamera2DComputeViewMatrix error: the input RMcamera2D object is NULL") == RM_WHACKED) || (RM_ASSERT(m, "rmCamera2DComputeViewMatrix error: the return view RMmatrix object is NULL") == RM_WHACKED)) return(RM_WHACKED); private_rmCamera2DComputeMatrix(c, m); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera2DComputeViewFromGeometry @pstart RMenum rmCamera2DComputeViewFromGeometry (RMcamera2D *toModify, const RMnode *source) @pend @astart RMcamera2D *toModify - a handle to an RMcamera2D object that will be modified by this routine. const RMnode *source - a handle to an RMnode. @aend @dstart Use this routine to automatically compute 2D view parameters from the 2D bounding box of scene graph node. Returns RM_CHILL upon success, or RM_WHACKED upon failure. The 2D camera's parameters will be set to reflect the min/max extents of the input RMnode's 2D bounding box. The aspect ratio of the 2D camera remains unmodified. @dend * ---------------------------------------------------- */ RMenum rmCamera2DComputeViewFromGeometry (RMcamera2D *c, const RMnode *r) { RMvertex3D bmin, bmax; if ((RM_ASSERT(c, "rmCamera2DComputeViewFromGeometry error: the input RMcamera2D object is NULL") == RM_WHACKED) || (RM_ASSERT(r, "rmCamera2DComputeViewFromGeometry error: the input RMnode is NULL") == RM_WHACKED)) return(RM_WHACKED); rmNodeGetBoundingBox (r, &bmin, &bmax); rmCamera2DSetExtents(c, bmin.x, bmin.y, bmax.x, bmax.y); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DNew @pstart RMcamera3D * rmCamera3DNew (void) @pend @astart No arguments. @aend @dstart This routine creates a new RMcamera3D object, initializes it to some default values, and returns a handle to the caller. If there is an error (malloc failure), an error message is displayed with rmError and NULL is returned. For initialization, rmDefaultCamera3D is applied to the newly created RMcamera3D object so that all fields are initialized. See the description of rmDefaultCamera3D for particulars about the default values. @dend * ---------------------------------------------------- */ RMcamera3D * rmCamera3DNew (void) { RMcamera3D *c; if ((c = (RMcamera3D *)malloc(sizeof(RMcamera3D))) == NULL) { rmError("rmCamera3DNew() error: unable to malloc memory for a new RMcamera3D struct. "); return(NULL); } /* defaults */ rmDefaultCamera3D(c); return(c); } /* * ---------------------------------------------------- * @Name rmCamera3DCopy @pstart RMenum rmCamera3DCopy (RMcamera3D *dst, const RMcamera3D *src) @pend @astart RMcamera3D *dst - a handle to an RMcamera3D object to be modified (output). const RMcamera3D *src - a handle to an RMcamera3D object that will be copied (input). @aend @dstart Copies all parameters and attributes from one RMcamera3D object to another. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmCamera3DCopy (RMcamera3D *dst, const RMcamera3D *src) { if ((RM_ASSERT(dst, "rmCamera3DCopy error: the dst RMcamera3D pointer is NULL") == RM_WHACKED) || (RM_ASSERT(src, "rmCamera3DCopy error: the src RMcamera3D pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); memcpy((void *)dst, (void *)src, sizeof(RMcamera3D)); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DDelete @pstart void rmCamera3DDelete (RMcamera3D *toDelete) @pend @astart RMcamera3D *toDelete - a handle to RMcamera3D object to delete (modified). @aend @dstart Free the RMcamera3D resources pointed to by c. It is assumed that c points to a valid RMcamera3D previously created with rmCamera3DNew(). @dend * ---------------------------------------------------- */ void rmCamera3DDelete (RMcamera3D *c) { if (c != NULL) free((void *)c); } /* * ---------------------------------------------------- * @Name rmDefaultCamera3D @pstart RMenum rmDefaultCamera3D (RMcamera3D *toModify) @pend @astart RMcamera3D *toModify - a handle to an RMcamera3D object that will be modified by this routine. @aend @dstart Assigns some default parameters to an RMcamera3D object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. The parameters set by this routine are: 1. Eye point set to (10,10,50). (rmCamera3DSetEye) 2. Lookat point is the origin, (0,0,0). (rmCamera3DSetAt) 3. Up vector is (0,1,0) (rmCamera3DSetUp) 4. Field of view set to 45 degrees (rmCamera3DSetFOV) 5. Monoscopic camera (rmCamera3DSetStereo) 6. Perspective projection (rmCamera3DSetProjection) 7. Front and back clipping planes set to 2.0 and 40.0, respectively. (rmCamera3DSetHither, rmCamera3DSetYon) Returns RM_CHILL upon success, or RM_WHACKED if the input RMcamera3D object is NULL. @dend * ---------------------------------------------------- */ RMenum rmDefaultCamera3D (RMcamera3D *c) { if (RM_ASSERT(c, "rmDefaultCamera3D error: the input RMcamera3D object is NULL") == RM_WHACKED) return(RM_WHACKED); /* * assign some default values: * the camera's default position has the * - eye at (10,10,50) * - lookat point is the origin (0,0,0) * - up vector is +y axis * - hither is 2 and yon is 40 * - perspective projection * - monoscopic */ rmCamera3DSetFOV(c, RM_DEFAULT_3DCAMERA_FOV); rmCamera3DSetAspectRatio(c, RM_DEFAULT_3DCAMERA_ASPECT); rmCamera3DSetEye(c, &RM_DEFAULT_3DCAMERA_EYE); rmCamera3DSetAt(c, &RM_DEFAULT_3DCAMERA_LOOKAT); rmCamera3DSetUpVector(c, &RM_DEFAULT_3DCAMERA_UP); rmCamera3DSetHither(c, RM_DEFAULT_3DCAMERA_HITHER); rmCamera3DSetYon(c, RM_DEFAULT_3DCAMERA_YON); rmCamera3DSetProjection(c, RM_DEFAULT_3DCAMERA_PROJECTION); rmCamera3DSetStereo(c, RM_FALSE); rmCamera3DSetEyeSeparation (c, 0.0F); rmCamera3DSetFocalDistance (c, 1.0F); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DSetAspectRatio @pstart RMenum rmCamera3DSetAspectRatio (RMcamera3D *toModify, const float newAspect) @pend @astart RMcamera3D *toModify - a handle to an RMcamera3D object (modified). const float newAspect - a floating point value used to specify the new aspect ratio for the RMcamera3D object. @aend @dstart Use this routine to modify the aspect ratio attribute of an RMcamera3D object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmCamera3DSetAspectRatio (RMcamera3D *toModify, const float newAspect) { if (RM_ASSERT(toModify, "rmCamera3DSetAspectRatio error: the input RMcamera3D object is NULL") == RM_WHACKED) return(RM_WHACKED); toModify->aspect = newAspect; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DResetAspectRatio @pstart RMenum rmCamera3DResetAspectRatio (RMcamera3D *toModify, const float *vp, int windowWidth, int windowHeight) @pend @astart RMcamera3D *toModify - a handle to the RMcamera3D object that will be modified by this routine. const float *vp - an array of floating point values of length 4. This is a viewport specification, ordered {xmin, ymin, xmax, ymax}. The viewport coordinates are in the range 0..1, ranging from the lower left corner of the display window (0,0) to the upper right corner (1,1). int windowWidth, windowWeight - integer values specifying the size in pixels of the display window. These values are used in computing a new aspect ratio for the RMcamera3D object. @aend @dstart Given viewport and window geometry, compute an aspect ratio that will cause pixels to "remain square." In other words, an aspect ratio will be computed so that there is no stretch or squish in the rendered image: a square in world coordinates will remain square in the rendered image, regardless of viewport or window geometry. All other camera parameters are ignored and remain unaffected. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmCamera3DResetAspectRatio (RMcamera3D *toModify, const float *vp, int windowWidth, int windowHeight) { float sx, sy; float new_aspect; if ((RM_ASSERT(toModify, "rmCamera3DResetAspectRatio error: input RMcamera3D object is NULL.") == RM_WHACKED) || (RM_ASSERT(vp, "rmCamera3DResetAspectRatio error: input viewport is NULL.") == RM_WHACKED)) return(RM_WHACKED); sx = vp[2] - vp[0]; sy = vp[3] - vp[1]; new_aspect = ((float)windowWidth * sx) / ((float)windowHeight * sy); rmCamera3DSetAspectRatio(toModify, new_aspect); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DGetAspectRatio @pstart float rmCamera3DGetAspectRatio (const RMcamera3D *toQuery) @pend @astart const RMcamera3D *toQuery - a handle to the RMcamera3D object that will be queried by this routine. @aend @dstart Use this routine to query the aspect ratio of an RMcamera3D object. RM_CHILL is returned upon success, otherwise RM_WHACKED is returned. The aspect ratio is a fraction that is the width of the field of view divided by the height. It is used to control the stretch and squish of rendering. For example, consider rendering into a window whose width is 400 pixels and height is 300 pixels and using a logically square viewport (0,0..1,1). If the camera aspect ratio is set to 1.0, the resulting image will appear to be stretched along the horizontal axis because the square viewport is being mapped to a rectangular window. In this example, we would want to set the aspect ratio to 400/300 to avoid stretching. The aspect ratio is applied at the stage when we compute the view transformation matrix. @dend * ---------------------------------------------------- */ float rmCamera3DGetAspectRatio (const RMcamera3D *toQuery) { if (RM_ASSERT(toQuery, "rmCamera3DGetAspectRatio error: input RMcamera3D pointer is NULL. returning 1.0") == RM_WHACKED) return(1.0F); return(toQuery->aspect); } /* * ---------------------------------------------------- * @Name rmCamera3DSetProjection @pstart RMenum rmCamera3DSetProjection (RMcamera3D *toModify, RMenum newVal) @pend @astart RMcamera3D *toModify - a handle to an RMcamera3D object that will be modified by this routine (modified). RMenum newVal - an RMenum value specifying the new projection method for the RMcamera3D object. Should be either RM_PROJECTION_PERSPECTIVE or RM_PROJECTION_ORTHOGRAPHIC. @aend @dstart Use this routine to assign a projection method to a 3D camera. Returns RM_CHILL upon success, or RM_WHACKED upon failure. Perspective projections are used to achieve a "perspective foreshortening" effect. This is how humans see in the real world: objects that are further away appear smaller than similarly-sized objects that are closer. In contrast, in orthographics projections size does not diminish as a function of distance to the viewer. This type of projection is often used in CAD systems because it does not distort angles. @dend * ---------------------------------------------------- */ RMenum rmCamera3DSetProjection (RMcamera3D *toModify, RMenum newVal) { if (RM_ASSERT(toModify, "rmCamera3DSetProjection error: input camera is NULL") == RM_WHACKED) return(RM_WHACKED); if ((newVal != RM_PROJECTION_PERSPECTIVE) && (newVal != RM_PROJECTION_ORTHOGRAPHIC)) return(RM_WHACKED); toModify->projection = newVal; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DGetProjection @pstart RMenum rmCamera3DGetProjection (const RMcamera3D *toQuery) @pend @astart const RMcamera3D *toQuery - a handle to an RMcamera3D object that will be queried by this routine (input). @aend @dstart Use this routine to obtain the projection method attribute of the RMcamera3D object. If successful, either RM_PROJECTION_PERSPECTIVE or RM_PROJECTION_ORTHOGRAPHIC will be returned to the caller. Otherwise, upon failure, RM_WHACKED will be returned. @dend * ---------------------------------------------------- */ RMenum rmCamera3DGetProjection (const RMcamera3D *toQuery) { if (RM_ASSERT(toQuery, "rmCamera3DGetProjection error: input camera is NULL") == RM_WHACKED) return(RM_WHACKED); return(toQuery->projection); } /* * ---------------------------------------------------- * @Name rmCamera3DSetEye @pstart RMenum rmCamera3DSetEye (RMcamera3D *toModify, const RMvertex3D *newEye) @pend @astart RMcamera3D *toModify - a handle to the RMcamera3D object that will be modified by this routine. const RMvertex3D *newEye - a handle to an RMvertex3D object. @aend @dstart Use this routine to specify a new eye point for a 3D camera. The eye point is the location of the viewer, and is specified in world coordinates. The new eye point is copied from the caller-supplied RMvertex3D object into the RMcamera3D object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmCamera3DSetEye (RMcamera3D *toModify, const RMvertex3D *newEye) { if ((RM_ASSERT(toModify, "rmCamera3DSetEye error: input camera is null.") == RM_WHACKED) || (RM_ASSERT(newEye, "rmCamera3DSetEye error: input RMvertex3D pointer is null.") == RM_WHACKED)) return(RM_WHACKED); VCOPY(newEye, &(toModify->eye)); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DGetEye @pstart RMenum rmCamera3DGetEye (const RMcamera3D *toQuery, RMvertex3D *returnEye) @pend @astart const RMcamera3D *toQuery - a handle to an RMcamera3D object to be queried (input). RMvertex3D *returnEye - a handle to a caller-supplied RMvertex3D object (modified). @aend @dstart Use this routine to obtain the 3D eye position of an RMcamera3D object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmCamera3DGetEye (const RMcamera3D *toQuery, RMvertex3D *returnEye) { if ((RM_ASSERT(toQuery, "rmCamera3DGetEye error: input camera is null.") == RM_WHACKED) || (RM_ASSERT(returnEye, "rmCamera3DGetEye error: input RMvertex3D pointer is null.") == RM_WHACKED)) return(RM_WHACKED); VCOPY(&(toQuery->eye), returnEye); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DSetAt @pstart RMenum rmCamera3DSetAt (RMcamera3D *toModify, const RMvertex3D *newAt) @pend @astart RMcamera3D *toModify - a handle to an RMcamera3D object (modified). const RMvertex3D *newAt - a handle to an RMvertex3D object containing the new look-at point (input). @aend @dstart Use this routine to set the look-at point of a 3D camera. The contents of the caller-supplied newAt RMvertex3D object is copied into the RMcamera3D object's look-at point attribute. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmCamera3DSetAt (RMcamera3D *toModify, const RMvertex3D *newAt) { if ((RM_ASSERT(toModify, "rmCamera3DSetAt error: input camera is null.") == RM_WHACKED) || (RM_ASSERT(newAt, "rmCamera3DSetAt error: input RMvertex3D pointer is null.") == RM_WHACKED)) return(RM_WHACKED); VCOPY(newAt, &(toModify->at)); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DGetAt @pstart RMenum rmCamera3DGetAt (const RMcamera3D *toQuery, RMvertex3D *returnAt) @pend @astart const RMcamera3D *toQuery - a handle to an RMcamera3D object to be queried (input). RMvertex3D *returnAt - a handle to an RMvertex3D object that will contain the RMcamera3D's look-at point upon return (modified). @aend @dstart Use this routine to obtain the look-at point of a 3D camera. The RMcamera3D's look-at point is copied into the caller-supplied object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmCamera3DGetAt (const RMcamera3D *toQuery, RMvertex3D *returnAt) { if ((RM_ASSERT(toQuery, "rmCamera3DGetAt error: input camera is null.") == RM_WHACKED) || (RM_ASSERT(returnAt, "rmCamera3DGetAt error: input RMvertex3D pointer is null.") == RM_WHACKED)) return(RM_WHACKED); VCOPY(&(toQuery->at), returnAt); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DSetUpVector @pstart RMenum rmCamera3DSetUpVector (RMcamera3D *toModify, const RMvertex3D *newUpVector) @pend @astart RMcamera3D *toModify - a handle to the RMcamera3D object to be modified (result). const RMvertex3D *newUpVector - a handle to an RMvertex3D object supplying the new up vector (input). @aend @dstart Use this routine to set the Up vector attribute of an RMcamera3D object. The contents of the caller-supplied newUpVector is copied to the RMcamera3D object's up vector attribute. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmCamera3DSetUpVector (RMcamera3D *toModify, const RMvertex3D *newUpVector) { if ((RM_ASSERT(toModify, "rmCamera3DSetUpVector error: input RMcamera3D pointer is NULL.") == RM_WHACKED) || (RM_ASSERT(newUpVector, "rmCamera3DSetUpVector error: input RMvertex3D pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); VCOPY(newUpVector, &(toModify->up)); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DGetUpVector @pstart RMenum rmCamera3DGetUpVector (const RMcamera3D *toQuery, RMvertex3D *returnUpVector) @pend @astart const RMcamera3D *toQuery - a handle to the RMcamera3D object to query (input). RMvertex3D *returnUpVector - a handle to an RMvertex3D object supplied by the caller (modified). @aend @dstart Use this routine to query the Up vector attribute of an RMcamera3D object. The RMcamera3D object's up vector is copied into the caller-supplied memory. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmCamera3DGetUpVector (const RMcamera3D *toQuery, RMvertex3D *returnUpVector) { if ((RM_ASSERT(toQuery, "rmCamera3DGetUpVector error: input RMcamera3D pointer is NULL.") == RM_WHACKED) || (RM_ASSERT(returnUpVector, "rmCamera3DGetUpVector error: input RMvertex3D pointer is NULL") == RM_WHACKED)) return(RM_WHACKED); VCOPY(&(toQuery->up), returnUpVector); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DSetFOV @pstart RMenum rmCamera3DSetFOV (RMcamera3D *toModify, float newFOV) @pend @astart RMcamera3D *toModify - a handle to an RMcamera3D object that will be modified by this routine (modified). float newFOV - a floating point value, the new FOV parameter (input). @aend @dstart Use this routine to set the FOV parameter at a 3D camera object. If successful, RM_CHILL is returned, otherwise, RM_WHACKED is returned. FOV in OpenRM refers to the horizontal field of view, not the vertical field of view. Increasing the FOV in a 3D camera will cause more of the scene to be visible along the horizontal axis. @dend * ---------------------------------------------------- */ RMenum rmCamera3DSetFOV (RMcamera3D *toModify, float newFOV) { if (RM_ASSERT(toModify, "rmCamera3DSetFOV error: input camera is NULL.") == RM_WHACKED) return(RM_WHACKED); toModify->fov = newFOV; /* range check?? */ return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DGetFOV @pstart float rmCamera3DGetFOV (const RMcamera3D *toQuery) @pend @astart const RMcamera3D *toQuery - a handle to an RMcamera3D object that will be queried (input). @aend @dstart Use this routine to obtain the FOV parameter from a 3D camera object. A floating point value is returned to the caller. FOV in OpenRM refers to the horizontal field of view, not the vertical field of view. Increasing the FOV in a 3D camera will cause more of the scene to be visible along the horizontal axis. @dend * ---------------------------------------------------- */ float rmCamera3DGetFOV (const RMcamera3D *toQuery) { if (RM_ASSERT(toQuery, "rmCamera3DGetFOV error: input camera is NULL. Returning 0.0F.") == RM_WHACKED) return(0.0F); return(toQuery->fov); } /* * ---------------------------------------------------- * @Name rmCamera3DSetHither @pstart RMenum rmCamera3DSetHither (RMcamera3D *toModify, float newHither) @pend @astart RMcamera3D *toModify - a handle to the RMcamera3D object (modified). float newHither - a floating point value containing the new front clip plane value (input). @aend @dstart Used to set the value of the front clip plane attribute of the RMcamera3D object. The front clip plane value is a distance, in world coordinates, from the eye point to the front clip plane. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmCamera3DSetHither (RMcamera3D *toModify, float newHither) { if (RM_ASSERT(toModify, "rmCamera3DSetHither error: input camera is NULL") == RM_WHACKED) return(RM_WHACKED); toModify->hither = newHither; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DGetHither @pstart float rmCamera3DGetHither (const RMcamera3D *toQuery) @pend @astart const RMcamera3D *toQuery - a handle to the RMcamera3D object to be queried (input). @aend @dstart Returns to the caller the value of the front clip plane attribute of the RMcamera3D object. The front clip plane value is a distance, in world coordinates, from the eye point to the front clip plane. @dend * ---------------------------------------------------- */ float rmCamera3DGetHither (const RMcamera3D *toQuery) { if (RM_ASSERT(toQuery, "rmCamera3DGetHither error: input camera is NULL, returning 0.0F") == RM_WHACKED) return(0.0F); return(toQuery->hither); } /* * ---------------------------------------------------- * @Name rmCamera3DSetYon @pstart RMenum rmCamera3DSetYon (RMcamera3D *toModify, float newYon) @pend @astart RMcamera3D *toModify - a handle to the RMcamera3D object (modified). float newYon - a floating point value containing the new back clip plane value (input). @aend @dstart Used to set the value of the back clip plane attribute of the RMcamera3D object. The back clip plane value is a distance, in world coordinates, from the eye point to the back clip plane. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmCamera3DSetYon(RMcamera3D *toModify, float newYon) { if (RM_ASSERT(toModify,"rmCamera3DSetYon error: input RMcamera3D pointer is NULL") == RM_WHACKED) return(RM_WHACKED); toModify->yon = newYon; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DGetYon @pstart float rmCamera3DGetYon(const RMcamera3D *toQuery) @pend @astart const RMcamera3D *toQuery - a handle to the RMcamera3D object to be queried (input). @aend @dstart Returns to the caller the value of the clip plane attribute of the RMcamera3D object. The back clip plane value is a distance, in world coordinates, from the eye point to the back clip plane. @dend * ---------------------------------------------------- */ float rmCamera3DGetYon (const RMcamera3D *toQuery) { if (RM_ASSERT(toQuery, "rmCamera3DGetYon error: input RMcamera3D pointer is NULL. Returning 0.0F") == RM_WHACKED) return(0.0F); return(toQuery->yon); } /* * ---------------------------------------------------- * @Name rmCamera3DSetStereo @pstart RMenum rmCamera3DSetStereo (RMcamera3D *toModify, RMenum newBoolValue) @pend @astart RMcamera3D *toModify - a handle to an RMcamera3D object (modified). RMenum newBoolValue - a boolean value, either RM_TRUE or RM_FALSE (input). @aend @dstart Use this routine to set the stereo attribute of a 3D camera. Returns RM_CHILL upon success or RM_WHACKED upon failure. A stereo 3D camera in OpenRM defines a binocular viewing geometry. The binocular viewing geometry is a function of eye separation (rmCamera3DSetEyeSeparation) and focal length (rmCamera3DSetFocalDistance). When the 3D camera is set to be a stereo camera with this routine, the confluence of multipass rendering control and stereo 3D camera parameters are used to internally compute left- and right-eye views used for rendering. If the 3D camera's "is stereo" attribute is set to RM_FALSE, a monocular view will be used during a stereo multipass rendering. @dend * ---------------------------------------------------- */ RMenum rmCamera3DSetStereo (RMcamera3D *c, RMenum rm_bool) { if (RM_ASSERT(c, "rmCamera3DSetStereo error: input RMcamera3D object is NULL. ") == RM_WHACKED) return(RM_WHACKED); if ((rm_bool != RM_TRUE) && (rm_bool != RM_FALSE)) return(RM_WHACKED); else c->isStereo = rm_bool; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DGetStereo @pstart RMenum rmCamera3DGetStereo (const RMcamera3D *toQuery) @pend @astart const RMcamera3D *toQuery - a handle to an RMcamera3D object that will be queried by this routine (input). @aend @dstart Use this routine to obtain the stereo attribute of a 3D camera. Returns RM_WHACKED upon failure, otherwise returns RM_TRUE or RM_FALSE. A stereo 3D camera in OpenRM defines a binocular viewing geometry. The binocular viewing geometry is a function of eye separation (rmCamera3DSetEyeSeparation) and focal length (rmCamera3DSetFocalDistance). When the 3D camera is set to be a stereo camera with this routine, the confluence of multipass rendering control and stereo 3D camera parameters are used to internally compute left- and right-eye views used for rendering. If the 3D camera's "is stereo" attribute is set to RM_FALSE, a monocular view will be used during a stereo multipass rendering. @dend * ---------------------------------------------------- */ RMenum rmCamera3DGetStereo (const RMcamera3D *c) { if (RM_ASSERT(c, "rmCamera3DGetStereo error: input RMcamera3D object is NULL. Returning RM_WHACKED.") == RM_WHACKED) return(RM_WHACKED); return(c->isStereo); } /* * ---------------------------------------------------- * @Name rmCamera3DSetEyeSeparation @pstart RMenum rmCamera3DSetEyeSeparation (RMcamera3D *toModify, float newVal) @pend @astart RMcamera3D *toModify - a handle to an RMcamera3D object (modified). float newVal - a floating point value specifying the interocular displacement in degrees (input). @aend @dstart Use this routine to modify the interocular separation parameter of a stereo 3D camera. Returns RM_WHACKED upon failure, otherwise returns RM_CHILL. A stereo 3D camera in OpenRM defines a binocular viewing geometry. The binocular viewing geometry is a function of eye separation (rmCamera3DSetEyeSeparation) and focal length (rmCamera3DSetFocalDistance). The stereo camera's binocular geometry, specifically the interocular distance, is computed using the 3D camera's eye point, the look-at point, the focal length and the interocular distance. The "real" look at point of a 3D stereo camera is computed as the product of the focal length with the (unitized) eye/look-at vector. The left and right eyes both look at the "real" stereo look-at point (not the one specified by rmCamera3DSetAt). The apex of the triangle formed by the left and right eyes and the "real" stereo look at point will have an angular measure of X degrees, where X is specified as the parameter to the routine rmCamera3DSetEyeSeparation. We have found that values in the range 2.5-3.5 degrees work well for most viewers. @dend * ---------------------------------------------------- */ RMenum rmCamera3DSetEyeSeparation (RMcamera3D *c, float newval) { if (RM_ASSERT(c, "rmCamera3DSetEyeSeparation error: input RMcamera3D points is NULL. ") == RM_WHACKED) return(RM_WHACKED); c->degrees_eye_separation = newval; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DGetEyeSeparation @pstart RMenum rmCamera3DGetEyeSeparation (RMcamera3D *toQuery) @pend @astart const RMcamera3D *toQuery - a handle to an RMcamera3D object that will be queried by this routine (input). @aend @dstart Use this routine to query the interocular separation parameter of a stereo 3D camera. Returns a floating point value representing the 3D stereo camera's interocular distance parameter. A stereo 3D camera in OpenRM defines a binocular viewing geometry. The binocular viewing geometry is a function of eye separation (rmCamera3DSetEyeSeparation) and focal length (rmCamera3DSetFocalDistance). The stereo camera's binocular geometry, specifically the interocular distance, is computed using the 3D camera's eye point, the look-at point, the focal length and the interocular distance. The "real" look at point of a 3D stereo camera is computed as the product of the focal length with the (unitized) eye/look-at vector. The left and right eyes both look at the "real" stereo look-at point (not the one specified by rmCamera3DSetAt). The apex of the triangle formed by the left and right eyes and the "real" stereo look at point will have an angular measure of X degrees, where X is specified as the parameter to the routine rmCamera3DSetEyeSeparation. We have found that values in the range 2.5-3.5 degrees work well for most viewers. @dend * ---------------------------------------------------- */ float rmCamera3DGetEyeSeparation (const RMcamera3D *toQuery) { if (RM_ASSERT(toQuery, "rmCamera3DGetEyeSeparation error: input RMcamera3D points is NULL. Returning 1.0F.") == RM_WHACKED) return(1.0F); return(toQuery->degrees_eye_separation); } /* * ---------------------------------------------------- * @Name rmCamera3DSetFocalDistance @pstart RMenum rmCamera3DSetFocalDistance (RMcamera3D *toModify, float newVal) @pend @astart RMcamera3D *toModify - a handle to an RMcamera3D object (modified). float newVal - a floating point value specifying new focal distance length (input). @aend @dstart Use this routine to modify the focal distance parameter of a stereo 3D camera. Returns RM_WHACKED upon failure, otherwise returns RM_CHILL. A stereo 3D camera in OpenRM defines a binocular viewing geometry. The binocular viewing geometry is a function of eye separation (rmCamera3DSetEyeSeparation) and focal length (rmCamera3DSetFocalDistance). In a binocular view model, the left and right eyes look at a point somewhere in space. This point is computed from the 3D stereo camera parameters: eye point (rmCamera3DSetEye), the look-at point (rmCamera3DSetAt) and the focal distance (rmCamera3DSetFocalDistance). The point is computed as the eye point plus the focal distance times the eye-at vector. A value of 1.0 for the focal distance places the stereo look-at point at the look-at point specified by rmCamera3DSetAt. A value of, say, 0.707, places the stereo look at point in front of the 3D camera's look-at point, and a value of, say, 1.414, places the stereo look-at point somewhere behind the 3D camera's look at point. Manipulation of the focal distance and the interocular separation parameters can have a profound effect upon the perceived stereo view. @dend * ---------------------------------------------------- */ RMenum rmCamera3DSetFocalDistance (RMcamera3D *toModify, float newval) { if (RM_ASSERT(toModify, "rmCamera3DSetFocalDistance error: input RMcamera3D is NULL") == RM_WHACKED) return(RM_WHACKED); toModify->focalDistance = newval; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DGetFocalDistance @pstart RMenum rmCamera3DGetFocalDistance (const RMcamera3D *toQuery) @pend @astart const RMcamera3D *toQuery - a handle to an RMcamera3D object that will be queried by this routine (input). @aend @dstart Use this routine to query the focal distance parameter of a stereo 3D camera. A floating point value is returned by this routine. A stereo 3D camera in OpenRM defines a binocular viewing geometry. The binocular viewing geometry is a function of eye separation (rmCamera3DSetEyeSeparation) and focal length (rmCamera3DSetFocalDistance). In a binocular view model, the left and right eyes look at a point somewhere in space. This point is computed from the 3D stereo camera parameters: eye point (rmCamera3DSetEye), the look-at point (rmCamera3DSetAt) and the focal distance (rmCamera3DSetFocalDistance). The point is computed as the eye point plus the focal distance times the eye-at vector. A value of 1.0 for the focal distance places the stereo look-at point at the look-at point specified by rmCamera3DSetAt. A value of, say, 0.707, places the stereo look at point in front of the 3D camera's look-at point, and a value of, say, 1.414, places the stereo look-at point somewhere behind the 3D camera's look at point. Manipulation of the focal distance and the interocular separation parameters can have a profound effect upon the perceived stereo view. @dend * ---------------------------------------------------- */ float rmCamera3DGetFocalDistance (const RMcamera3D *c) { if (RM_ASSERT(c, "rmCamera3DGetFocalDistance error: input RMcamera3D is NULL. Returning 1.0F") == RM_WHACKED) return(1.0F); return(c->focalDistance); } /* * ---------------------------------------------------- * @Name rmCamera3DComputeViewMatrix @pstart RMenum rmCamera3DComputeViewMatrix (const RMcamera3D *source, RMmatrix *viewReturn, RMmatrix *projectionReturn) @pend @astart const RMcamera3D *source - a handle to an RMcamera3D object (input). RMmatrix *viewReturn - a handle to an RMmatrix object (result). RMmatrix *projectionReturn - a handle to an RMmatrix object (result). @aend @dstart Computes a view and projection matrix from an RMcamera3D object, returning the computed matrices in the "viewReturn" and "projectionReturn" parameters, respectively. RM_CHILL is returned upon success, or RM_WHACKED upon failure. Failure can occur if either of the input parameters is NULL, or if there is some type of fatal numerical error caused by bogus 3D camera parameters. These latter problems are detected and reported via rmError(). The view matrix represents the affine transformation that transforms from world coordinates into "eye coordinates," assuming a monocular view. The same matrix is returned regardless of whether or not the input camera is a stereo or mono camera. The projection matrix represents the transformation from eye space coordinates to NDC coordinates. If the camera uses perspective projection, that transformation will be present in the projectionReturn matrix. @dend * ---------------------------------------------------- */ RMenum rmCamera3DComputeViewMatrix (const RMcamera3D *c, RMmatrix *m, RMmatrix *p) { if ((RM_ASSERT(c, "rmCamera3DComputeViewMatrix error: the input RMcamera3D object is NULL") == RM_WHACKED) || (RM_ASSERT(m, "rmCamera3DComputeViewMatrix error: the return view RMmatrix object is NULL") == RM_WHACKED) || (RM_ASSERT(p, "rmCamera3DComputeViewMatrix error: the return projection RMmatrix object is NULL") == RM_WHACKED)) return(RM_WHACKED); private_rmComputeViewMatrix(c, m, p); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmCamera3DComputeViewFromGeometry @pstart RMenum rmCamera3DComputeViewFromGeometry (RMcamera3D *toModify, const RMnode *source, int windowWidth, int windowHeight) @pend @astart RMcamera3D *toModify - a handle to the RMcamera3D object (modified). const RMnode *source - a handle to an RMnode. the bounding box of this RMnode will be used in computing 3D camera parameters (input). int windowWidth, windowHeight - int values specifying the width and height in pixels of the display window. These values are used in computing an aspect ratio for the camera (input). @aend @dstart This routine computes and assigns 3D camera parameters such that a volume defined by the bounding box attribute of the input RMnode will be visible, regardless of box orientation. The scale matrix of the RMnode will be used to enlarge or shrink the bounding box parameter of the RMnode for the purposes of computing new camera parameters. The 3D camera must be a valid RMcamera3D object allocated by the caller. This routine merely computes new values for an existing camera using the bounding box of an RMnode. It neither creates nor destroys an RMcamera3D. The 3D camera's FOV parameter is used as part of the computation, and will not be overwritten by this routine. All stereo parameters, if any, are ignored and remain unmodified by this routine. The new 3D camera parameters are computed thus: 1. The 3D camera's look-at point will be the center of the RMnode's bounding box. 2. The 3D camera's new eye position is the same as the new look-at point, but offset along the Z axis a distance D. The distance D is computed as boxsize/tan(FOV/2) where boxsize is the distance from the bounding box's min vertex to the box's max vertex. The basic idea is to move the eye point away from the look-at point a distance which is sufficient for the entire box to be visible, given the requested FOV. 3. The near and far clip planes are set to a distance F which is computed as D-boxsize*1.4 and D+boxsize*1.4, respectively, where D is the same D computed in (2). Again, the point is to have the entire bounding box within the view frustum regardless of orientation. Note: 8/16/02 - changed computation of near/far to D-boxsize*2.0, D+boxsize*5.0, respectively. This gives more room to maneuver before the object gets clipped. 4. The aspect ratio for the 3D camera is set using the input values windowWidth and WindowHeight. @dend * ---------------------------------------------------- */ RMenum rmCamera3DComputeViewFromGeometry (RMcamera3D *c, /* input and output */ const RMnode *r, /* input, has geometry & stuff */ int w, /* image width, for aspect ratio */ int h) /* image height, for aspect ratio */ { float new_hither, new_yon; float vp[] = {0.0, 0.0, 1.0, 1.0}; double d, d2, boxsize; RMvertex3D new_at; RMvertex3D new_eye; RMvertex3D new_up; RMvertex3D bmin, bmax; const RMnode *root; RMmatrix scale, C, minusC; extern double sqrt(double); if ((RM_ASSERT(c, "rmCamera3DComputeViewFromGeometry error: the input RMcamera3D object is NULL") == RM_WHACKED) || (RM_ASSERT(r, "rmCamera3DComputeViewFromGeometry error: the input RMnode is NULL") == RM_WHACKED)) return(RM_WHACKED); root = r; rmNodeGetBoundingBox(root, &bmin, &bmax); /* get the scale matrix */ if (rmNodeGetScaleMatrix(r, &scale) == RM_WHACKED) rmMatrixIdentity(&scale); /* multiply the bounding box with the scale matrix */ { RMvertex3D center; RMmatrix composite; rmNodeGetCenter(r, ¢er); rmMatrixIdentity(&C); rmMatrixIdentity(&minusC); C.m[3][0] = center.x; C.m[3][1] = center.y; C.m[3][2] = center.z; minusC.m[3][0] = -1.0 * center.x; minusC.m[3][1] = -1.0 * center.y; minusC.m[3][2] = -1.0 * center.z; rmMatrixIdentity(&composite); rmMatrixMultiply(&minusC, &composite, &composite); rmMatrixMultiply(&composite, &scale, &composite); rmMatrixMultiply(&composite, &C, &composite); rmPointMatrixTransform(&bmin, &composite, &bmin); rmPointMatrixTransform(&bmax, &composite, &bmax); } /* we're going to assume that the bounding boxes are all sync'ed up. */ new_up.x = new_up.z = 0.0; new_up.y = 1.0; /* the new "at" point will be a point at the x/y center of the front face of the bounding box. */ new_at.x = bmin.x + ((bmax.x - bmin.x) * 0.5); new_at.y = bmin.y + ((bmax.y - bmin.y) * 0.5); /* new_at.z = bmax.z; */ new_at.z = bmin.z + ((bmax.z - bmin.z) * 0.5); /* compute the length of the diagonal of the box */ d = ((bmax.x - bmin.x) * (bmax.x - bmin.x)) + ((bmax.y - bmin.y) * (bmax.y - bmin.y)) + ((bmax.z - bmin.z) * (bmax.z - bmin.z)); boxsize = sqrt(d); boxsize *= 0.5; /* the new eye point will lie some distance along the +z axis * away from the new at point. "some distance" is defined as a * distance which is "far enough" away so that the front face of * the bounding box will lie within FOV degrees of view. */ d = boxsize; d2 = RM_DEGREES_TO_RADIANS(c->fov); d2 *= 0.5; d2 = tan(d2); d = boxsize / d2; d2 = d; /* the choices for hither and yon are somewhat arbitrary. what we * want is a choice of hither/yon that will permit the entire * object to be displayed w/o clipping regardless of orientation. so, * we need to look at the maximum dimension of the box. also, such * a goal may not be achievable under some conditions. for example, when * the requested fov is > 90, then the eye starts getting closer to * the box rather than further away from it. oh well... * * hither/yon are a function of the boxsize. these are set so that * the entire bbox will always be visible. possible error conditions * include the case of d being < boxsize. */ if (d < boxsize * 2.0) fprintf(stderr," error: we're about to set the front clip plane behind the eye. \n"); new_hither = d - (boxsize * 2.0); new_yon = d + (boxsize * 5.0F); rmCamera3DResetAspectRatio(c, vp, w, h); new_eye.x = new_at.x; new_eye.y = new_at.y; new_eye.z = new_at.z + (d2 * 1.1); rmCamera3DSetEye(c, &new_eye); rmCamera3DSetAt(c, &new_at); rmCamera3DSetUpVector(c, &new_up); rmCamera3DSetHither(c, new_hither); rmCamera3DSetYon(c, new_yon); return(RM_CHILL); } /* PRIVATE */ static void private_rmCamera2DComputeMatrix (const RMcamera2D *c, RMmatrix *m) { float x, y, z; float tx, ty, tz; float cwidth, center, cxmax, cxmin; double left ,right, top, bottom; RMmatrix P; rmMatrixIdentity(&P); cwidth = c->xmax - c->xmin; center = ((c->xmax - c->xmin) * 0.5F) + c->xmin; cxmax = center + (cwidth * 0.5F) * c->aspect_ratio; cxmin = center - (cwidth * 0.5F) * c->aspect_ratio; left = cxmin; right = cxmax; bottom = c->ymin; top = c->ymax; x = 2.0 / (right - left); y = 2.0 / (top - bottom); z = 1.0; tx = -(right + left) / (right -left); ty = -(top + bottom) / (top - bottom); tz = 0.; P.m[3][0] = tx; P.m[3][1] = ty; P.m[3][2] = tz; P.m[0][0] = x; P.m[1][1] = y; P.m[2][2] = z; *m = P; } /* PRIVATE */ void private_rmComputeViewMatrix (const RMcamera3D *c, RMmatrix *m, RMmatrix *proj) { /* compute both the view and projection matrices */ RMmatrix R, T, TR, P; RMvertex3D z, y, x; /* Make the translation matrix */ rmMatrixIdentity(&T); T.m[3][0] = -1.0 * c->eye.x; T.m[3][1] = -1.0 * c->eye.y; T.m[3][2] = -1.0 * c->eye.z; /* Make rotation matrix */ rmMatrixIdentity(&R); /* Z vector */ rmVertex3DDiff(&(c->eye), &(c->at),&z); rmVertex3DNormalize(&z); y = c->up; rmVertex3DNormalize(&y); /* X vector = Y cross Z */ rmVertex3DCross(&y, &z, &x); /* Recompute Y = Z cross X */ rmVertex3DCross(&z, &x, &y); /* cross product gives area of parallelogram, which is < 1.0 for non-perpendicular unit-length vectors; so normalize x, y here */ /* this might be transposed */ R.m[0][0] = x.x; R.m[1][0] = x.y; R.m[2][0] = x.z; R.m[0][1] = y.x; R.m[1][1] = y.y; R.m[2][1] = y.z; R.m[0][2] = z.x; R.m[1][2] = z.y; R.m[2][2] = z.z; /* concatenate the translational and rotational pieces together */ rmMatrixMultiply(&T, &R, &TR); /* build the projection matrix */ rmMatrixIdentity(&P); if (rmCamera3DGetProjection(c) == RM_PROJECTION_PERSPECTIVE) { double fovy, aspect, znear, zfar; double xmin, xmax, ymin, ymax; double x, y, a, b, C, d; double left, right, bottom, top, nearval, farval; fovy = c->fov; aspect = c->aspect; znear = c->hither; zfar = c->yon; ymax = znear * tan(fovy * RM_PI / 360.0); ymin = -ymax; xmin = ymin * aspect; xmax = ymax * aspect; left = xmin; right = xmax; bottom = ymin; top = ymax; nearval = znear; farval = zfar; x = (2.0 * nearval) / (right - left); y = (2.0 * nearval) / (top - bottom); a = (right + left) / (right - left); b = (top + bottom) / (top - bottom); C = -(farval + nearval) / ( farval - nearval); d = -(2.0 * farval * nearval) / (nearval - farval); /* error? */ P.m[0][0] = x; P.m[2][0] = a; P.m[1][1] = y; P.m[2][1] = b; P.m[2][2] = C; P.m[2][3] = -1.0; P.m[3][2] = -d; P.m[3][3] = 1.0; /* should be 0, but OGL puts a 1 there */ } else /* RM_PROJECTION_ORTHOGONAL */ { float x, y, z; float tx, ty, tz; double width, height; double sfov, mag; double left, right, top, bottom, hither, yon; RMvertex3D eye_at; /* set the width of the view volume to be proportional to * the field-of-view angle. we do this a little differently * than OpenGL, using the FOV to specify the horizontal field * of view, rather than the vertical field of view. */ sfov = sin((double)c->fov); eye_at.x = c->eye.x - c->at.x; eye_at.y = c->eye.y - c->at.y; eye_at.z = c->eye.z - c->at.z; mag = rmVertex3DMag(&eye_at); width = mag * sfov; height = width / c->aspect; left = -width; right = width; bottom = -height; top = height; hither = c->hither; yon = c->yon; x = 2.0 / (right - left); y = 2.0 / (top - bottom); z = -2.0 / (yon - hither); tx = -(right + left) / (right - left); ty = -(top + bottom) / (top - bottom); tz = -(yon + hither) / (yon - hither); P.m[3][0] = tx; P.m[3][1] = ty; P.m[3][2] = tz; P.m[0][0] = x; P.m[1][1] = y; P.m[2][2] = z; } *proj = P; *m = TR; } /* EOF */