This is camera.c in view mode; [Download] [Up]
/* Copyright (c) 1992 The Geometry Center; University of Minnesota 1300 South Second Street; Minneapolis, MN 55454, USA; This file is part of geomview/OOGL. geomview/OOGL is free software; you can redistribute it and/or modify it only under the terms given in the file COPYING, which you should have received along with this file. This and other related software may be obtained via anonymous ftp from geom.umn.edu; email: software@geom.umn.edu. */ static char *copyright = "Copyright (C) 1992 The Geometry Center"; /* Authors: Charlie Gunn, Stuart Levy, Tamara Munzner, Mark Phillips */ #include <math.h> #include "geomclass.h" #include "cameraP.h" #include "mg.h" Camera * _CamSet(Camera *cam, int attr, va_list *a_list); #define SETFLAG(flag, bit, value) \ if (value) flag |= bit; \ else flag &= ~bit #define GETFLAG(flag, bit) ( (flag & bit) != 0 ) static float GetHalfField( register Camera *cam ); static void SetHalfField( register Camera *cam, float halffield ); Camera * CamCreate(int a1, ...) { register Camera *thiscam; va_list a_list; thiscam = OOGLNewE(Camera, "CamCreate: unable to allocate camera\n"); if (thiscam == NULL) return(NULL); RefInit((Ref *)thiscam, CAMMAGIC); CamDefault(thiscam); thiscam->changed = 0; va_start( a_list, a1 ); _CamSet(thiscam, a1, &a_list); va_end(a_list); return thiscam; } void CamDefault(Camera *cam) { cam->flag = CAMF_PERSP; cam->frameaspect = 4.0/3.0; cam->focus = 3.0; cam->stereo_sep = 0.5; cam->stereo_angle = .08; cam->c2whandle = NULL; cam->w2chandle = NULL; cam->sterhandle[0] = NULL; cam->sterhandle[1] = NULL; CamStereoCompute(cam); cam->whicheye = 0; /* only applies to stereo */ cam->space = TM_EUCLIDEAN; CamReset( cam ); } Camera * CamSet(Camera *cam, int a1, ...) { va_list a_list; va_start(a_list, a1); return ( _CamSet(cam, a1, &a_list) ); } Camera * _CamSet(Camera *cam, int attr, register va_list *alist) { Transform * tt; int sethalffield = 0, setaspect = 0, setstereogeom = 0; double halffield, v; Handle *h; int bit, unbit; char **ablock = NULL; #define NEXT(type) OOGL_VA_ARG(type,alist,ablock) while (attr != CAM_END) { bit = unbit = 0; switch(attr) { case CAM_ABLOCK: ablock = NEXT(char**); break; case CAM_C2W: tt = NEXT(Transform *); bit = CAMF_NEWC2W, unbit = CAMF_W2C; TmCopy(tt, cam->camtoworld); TmInvert( cam->camtoworld, cam->worldtocam); break; case CAM_W2C: tt = NEXT(Transform *); bit = CAMF_W2C, unbit = CAMF_NEWC2W; TmCopy(tt, cam->worldtocam); TmInvert(cam->worldtocam, cam->camtoworld); break; case CAM_FOV: v = NEXT(double) / 2; bit = CAMF_FOV; if(cam->flag & CAMF_PERSP) { if(v >= 180/2) v = 120/2; v = tan( RADIANS(v) ); } halffield = v; sethalffield = 1; break; case CAM_HALFYFIELD: cam->halfyfield = NEXT(double); if(cam->flag & CAMF_PERSP) cam->halfyfield *= cam->focus; bit = CAMF_FOV; break; case CAM_HALFFIELD: halffield = NEXT(double); sethalffield = 1; bit = CAMF_FOV; break; case CAM_ASPECT: if((v = NEXT(double)) > 0.) { if (!sethalffield) halffield = GetHalfField(cam); cam->frameaspect = v; bit = CAMF_ASPECT; setaspect = 1; } break; case CAM_FOCUS: if((v = NEXT(double)) > 0) { if(cam->flag & CAMF_PERSP) cam->halfyfield *= v / cam->focus; cam->focus = v; bit = CAMF_FOCUS; } break; case CAM_NEAR: cam->near = NEXT(double); bit = CAMF_NEAR; break; case CAM_FAR: cam->far = NEXT(double); bit = CAMF_FAR; break; case CAM_STEREOSEP: cam->stereo_sep = NEXT(double); bit = CAMF_STEREOGEOM, unbit = CAMF_STEREOXFORM; setstereogeom = 1; break; case CAM_STEREOANGLE: cam->stereo_angle = NEXT(double); bit = CAMF_STEREOGEOM, unbit = CAMF_STEREOXFORM; setstereogeom = 1; break; case CAM_STEREOEYE: cam->whicheye = NEXT(int); bit = CAMF_EYE; break; case CAM_PERSPECTIVE: bit = CAMF_PERSP; goto flagbit; case CAM_STEREO: bit = CAMF_STEREO; goto flagbit; flagbit: SETFLAG(cam->flag, bit, NEXT(int)); break; case CAM_STEREYES: bcopy(NEXT(float *), cam->stereyes, 2*sizeof(Transform)); bit = CAMF_STEREOXFORM, unbit = CAMF_STEREOGEOM; break; case CAM_STERHANDLES: bcopy(NEXT(Handle **), cam->sterhandle, 2*sizeof(Handle *)); bit = CAMF_STEREOXFORM, unbit = CAMF_STEREOGEOM; break; case CAM_C2WHANDLE: h = NEXT(Handle *); if(cam->c2whandle && cam->c2whandle != h) HandlePDelete(&cam->c2whandle); cam->c2whandle = h; HandleRegister(&cam->c2whandle, (Ref *)cam, cam->camtoworld, CamTransUpdate); bit = CAMF_NEWC2W, unbit = CAMF_W2C; break; case CAM_W2CHANDLE: h = NEXT(Handle *); HandlePDelete(&cam->w2chandle); cam->w2chandle = h; HandleRegister(&cam->w2chandle, (Ref *)cam, cam->worldtocam, CamTransUpdate); bit = CAMF_W2C, unbit = CAMF_NEWC2W; break; case CAM_SPACE: { int space = NEXT(int); if ( space != TM_EUCLIDEAN && space != TM_HYPERBOLIC && space != TM_SPHERICAL) { OOGLError(0,"illegal space value %1d\n", space); } else { cam->space = space; bit = CAMF_SPACE; } } break; default: OOGLError (0, "CamSet: Undefined attribute: %d", attr); return NULL; } cam->changed &= ~unbit; cam->changed |= bit; attr = NEXT(int); } /* (sethalffield) means we have a new halffield value, stored in local var "halffield". (setaspect) means we have a new aspect ratio, and the halffield must be updated in accordance with this. In this case, "halffield" holds either the original halffield value, if a new one hasn't been explicitly set with CAM_HALFFIELD or CAM_FOV, or the new value, if it was explicitly set. All of these cases are dealt with by the following call to SetHalfField. */ if (setaspect || sethalffield) SetHalfField(cam, halffield); /* following works since the only way to change stereo parameters is by using this routine */ if (setstereogeom) CamStereoCompute(cam); return cam; #undef NEXT } /*----------------------------------------------------------------------- * Function: CamGet * Description: query a camera * Args: *cam: the camera to query * attr: the attribute to query * value: attr's value is written here * Returns: 1: attr is valid and value has been written * 0: attr is valid but currently does not * have a value * -1: invalid attr * Author: mbp * Date: Thu Aug 8 10:09:10 1991 * Notes: At present, there are no camera attr's that might * not be set, so 0 is never returned. This might change * in the future. */ int CamGet(register Camera *cam, int attr, void *value) { #define VALUE(type) ((type*)value) switch (attr) { case CAM_PERSPECTIVE: *VALUE(int) = GETFLAG(cam->flag, CAMF_PERSP); break; case CAM_STEREO: *VALUE(int) = GETFLAG(cam->flag, CAMF_STEREO); break; case CAM_C2W: /* camtoworld is always up to date, so just copy */ TmCopy( cam->camtoworld, VALUE(Transform) ); break; case CAM_W2C: /* worldtocam is not always up to date, so update if necessary ... */ if (cam->flag & CAMF_NEWC2W ) { TmInvert( cam->camtoworld, cam->worldtocam ); cam->flag &= ~CAMF_NEWC2W; } /* ... then copy */ TmCopy( cam->worldtocam, VALUE(Transform) ); break; case CAM_FOV: *VALUE(float) = 2 * ( (cam->flag & CAMF_PERSP) ? DEGREES( atan( (double)(GetHalfField(cam)) ) ) : GetHalfField(cam)); break; case CAM_HALFYFIELD: *VALUE(float) = (cam->flag & CAMF_PERSP) ? cam->halfyfield / cam->focus : cam->halfyfield; break; case CAM_HALFFIELD: *VALUE(float) = GetHalfField(cam); break; case CAM_ASPECT: *VALUE(float) = cam->frameaspect; break; case CAM_FOCUS: *VALUE(float) = cam->focus; break; case CAM_NEAR: *VALUE(float) = cam->near; break; case CAM_FAR: *VALUE(float) = cam->far; break; case CAM_STEREOSEP: *VALUE(float) = cam->stereo_sep; break; case CAM_STEREOANGLE: *VALUE(float) = cam->stereo_angle; break; case CAM_STEREOEYE: *VALUE(int) = cam->whicheye; break; case CAM_C2WHANDLE: *VALUE(Handle *) = cam->c2whandle; break; case CAM_W2CHANDLE: *VALUE(Handle *) = cam->w2chandle; break; case CAM_STEREYES: bcopy(cam->stereyes, value, 2*sizeof(Transform)); break; case CAM_STERHANDLES: bcopy(cam->sterhandle, value, 2*sizeof(Handle *)); break; case CAM_SPACE: *VALUE(int) = cam->space; break; default: return -1; break; } return 1; #undef VALUE } void CamDelete( register Camera *cam ) { if(cam && RefDecr((Ref *)cam) <= 0) { cam->magic = -1; /* Invalidate */ if(cam->c2whandle) HandlePDelete( &cam->c2whandle ); if(cam->w2chandle) HandlePDelete( &cam->w2chandle ); if(cam->sterhandle[0]) HandlePDelete( &cam->sterhandle[0] ); if(cam->sterhandle[1]) HandlePDelete( &cam->sterhandle[1] ); OOGLFree(cam); } } Camera * CamCopy( Camera *src, register Camera *dst ) { if(src == NULL) return NULL; if(dst == NULL) dst = OOGLNewE(Camera, "CamCopy Camera"); else HandleDelete(dst->handle); *dst = *src; dst->ref_count = 1; dst->handle = NULL; return dst; } void CamReset( register Camera *cam ) { Transform T; int persp; CamGet(cam, CAM_PERSPECTIVE, &persp); switch (cam->space) { case TM_EUCLIDEAN: CamSet( cam, CAM_NEAR, .07, CAM_FAR, 100.0, CAM_FOCUS, 3.0, CAM_FOV, persp ? 40.0 : 2.2, CAM_END); break; case TM_HYPERBOLIC: CamSet( cam, CAM_NEAR, .07, CAM_FAR, 100.0, CAM_FOCUS, 2.5, CAM_FOV, persp ? 40.0 : 2.2, CAM_END); break; case TM_SPHERICAL: CamSet( cam, CAM_NEAR, .05, CAM_FAR, -.05, CAM_FOCUS, 0.5, CAM_FOV, persp ? 90.0 : 2.2, CAM_END); break; } TmSpaceTranslate( T, 0.0, 0.0, cam->focus, cam->space ); CamSet(cam, CAM_C2W, T, CAM_END); } /* * Return camera's projection transform in proj. * See CamView below for the range of the projection. */ void CamViewProjection( register Camera *cam, register Transform proj ) { float y; float x; y = cam->halfyfield; if(cam->flag & CAMF_PERSP) y *= cam->near / cam->focus; x = cam->frameaspect * y; if(cam->flag & CAMF_PERSP) { TmPerspective( proj, -x, x, -y, y, cam->near, cam->far ); } else { TmOrthographic( proj, -x, x, -y, y, cam->near, cam->far ); } if (cam->flag & CAMF_STEREO) TmConcat( cam->stereyes[cam->whicheye], proj, proj ); } /* * Computes complete transformation from world -> projected coordinates * and leaves it in T. * Projected coordinates map the visible world into -1 <= {X,Y,Z} <= 1, * with Z = -1 at the near plane and Z = +1 at the far plane. */ void CamView( register Camera *cam, Transform T ) { Transform t; CamViewProjection( cam, t ); if(cam->flag & CAMF_NEWC2W) { TmInvert( cam->camtoworld, cam->worldtocam ); cam->flag &= ~CAMF_NEWC2W; } TmConcat( cam->worldtocam, t, T ); } void CamRotateX( register Camera *cam, float angle ) { CtmRotateX( cam->camtoworld, angle ); cam->flag |= CAMF_NEWC2W; } void CamRotateY( register Camera *cam, float angle ) { CtmRotateY( cam->camtoworld, angle ); cam->flag |= CAMF_NEWC2W; } void CamRotateZ( register Camera *cam, float angle ) { CtmRotateZ( cam->camtoworld, angle ); cam->flag |= CAMF_NEWC2W; } /* translate the camera, using the camera's notion of what space it is in */ void CamTranslate( register Camera *cam, float tx, float ty, float tz ) { Transform T; TmSpaceTranslate( T, tx, ty, tz, cam->space ); TmConcat(T, cam->camtoworld, cam->camtoworld); cam->flag |= CAMF_NEWC2W; } /* CamScale is a noop if the camera is not in Euclidean space */ void CamScale( register Camera *cam, float sx, float sy, float sz ) { if (cam->space == TM_EUCLIDEAN) { CtmScale( cam->camtoworld, sx, sy, sz ); cam->flag |= CAMF_NEWC2W; } } void CamAlignZ( register Camera *cam, float x, float y, float z ) { Point axis; axis.x = x; axis.y = y; axis.z = z; CtmAlignZ( cam->camtoworld, &axis ); cam->flag |= CAMF_NEWC2W; } /* * Apply T to camera as seen by world (== T^-1 to world, as seen by camera) */ void CamTransform( register Camera *cam, Transform T ) { TmConcat(T, cam->camtoworld, cam->camtoworld); cam->flag |= CAMF_NEWC2W; } static void CamStereoCompute( register Camera *cam ) { float tanconv = tan(cam->stereo_angle); TmTranslate( cam->stereyes[CAM_RIGHT], cam->stereo_sep, 0., 0. ); TmTranslate( cam->stereyes[CAM_LEFT], -cam->stereo_sep, 0., 0. ); cam->stereyes[CAM_RIGHT][Z][X] = -tanconv; cam->stereyes[CAM_LEFT][Z][X] = tanconv; } /*----------------------------------------------------------------------- * Function: SetHalfField * Description: set camera's "halffield" value * Args: *cam: the camera * halffield: the halffied value to set to * Returns: nothing * Author: mbp * Date: Wed Aug 21 14:26:43 1991 * Notes: This procedure modifies the halfyfield member of cam * in such a way as to guarantee that the min half-width * of the view window is halffield. This depends on the * camera's current aspect ratio. */ static void SetHalfField( register Camera *cam, float halffield ) { cam->halfyfield = (cam->frameaspect < 1 && cam->frameaspect > 0) ? halffield / cam->frameaspect : halffield; if(cam->flag & CAMF_PERSP) cam->halfyfield *= cam->focus; } /*----------------------------------------------------------------------- * Function: GetHalfField * Description: return camera's "halffield" value * Args: *cam: the camera * Returns: the halffield value * Author: mbp * Date: Wed Aug 21 14:29:31 1991 * Notes: the "halffield" is the min half-width of the view * window. If the aspect ratio is >= 1, this is * the vertical half-width (halfyfield). If the aspect * ratio is < 1, this is the horizontal half-width. */ static float GetHalfField( register Camera *cam ) { float v = cam->halfyfield; if(cam->frameaspect < 1) v *= cam->frameaspect; if(cam->flag & CAMF_PERSP) v /= cam->focus; return v; } /* * Merge one Camera's changed values into another Camera */ Camera * CamMerge(register Camera *src, register Camera *dst) { int chg; float fov; if(src == NULL) return dst; if(dst == NULL) return NULL; chg = src->changed; if(chg & CAMF_NEWC2W) CamSet(dst, CAM_C2WHANDLE, src->c2whandle, CAM_C2W, src->camtoworld, CAM_END); if(chg & CAMF_STEREOGEOM) CamSet(dst, CAM_STEREOSEP, src->stereo_sep, CAM_STEREOANGLE, src->stereo_angle, CAM_END); if(chg & CAMF_STEREOXFORM) CamSet(dst, CAM_STEREYES, src->stereyes, CAM_STERHANDLES, src->sterhandle, CAM_END); if(chg & CAMF_W2C) CamSet(dst, CAM_W2CHANDLE, src->w2chandle, CAM_W2C, src->worldtocam, CAM_END); CamGet(src, CAM_FOV, &fov); if(chg & CAMF_FOCUS) CamSet(dst, CAM_FOCUS, src->focus, CAM_END); if(chg & CAMF_PERSP) CamSet(dst,CAM_PERSPECTIVE,src->flag&CAMF_PERSP,CAM_END); if(chg & CAMF_FOV) CamSet(dst, CAM_FOV, fov, CAM_END); if(chg & CAMF_ASPECT) CamSet(dst, CAM_ASPECT, src->frameaspect, CAM_END); if(chg & CAMF_NEAR) dst->near = src->near; if(chg & CAMF_FAR) dst->far = src->far; if(chg & CAMF_EYE) dst->whicheye = src->whicheye; if(chg & CAMF_STEREO) CamSet(dst,CAM_STEREO,src->flag&CAMF_STEREO,CAM_END); if(chg & CAMF_SPACE) dst->space = src->space; return dst; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.