/* camera.c */ #include #include #include #include "camera.h" #include "error.h" #include "vector.h" #include "Boolean.h" #include "pools.h" #include "options.h" #include "defaults.h" CAMERA Camera; /* the one and only virtual camera */ CAMERA AlternateCamera; /* The second one and only camera (jp) */ #define NEWCAMERA() (CAMERA *)Alloc(sizeof(CAMERA)) #define DISPOSECAMERA(ptr) Free((char *)ptr, sizeof(CAMERA)) CAMERA *CameraCreate(void) { CAMERA *cam; cam = NEWCAMERA(); *cam = Camera; return cam; } void CameraDestroy(CAMERA *cam) { DISPOSECAMERA(cam); } /* a stack of virtual camera positions, used for temporary saving the camera and * later restoring */ static CAMERA CamStack[MAXCAMSTACK], *CamStackPtr=CamStack; void CameraPrint(FILE *out, CAMERA *Camera) { fprintf(out, "eyepoint: "); VectorPrint(out, Camera->eyep); fprintf(out, "\nlooking to: "); VectorPrint(out, Camera->lookp); fprintf(out, "\nupdir: "); VectorPrint(out, Camera->updir); fprintf(out, "\nfov = %f", (double)(Camera->fov)); fprintf(out, "\nhres = %d, vres = %d", Camera->hres, Camera->vres); fprintf(out, "\npixh = %g, pixv = %g", Camera->pixh, Camera->pixv); fprintf(out, "\nbackground color: "); RGBPrint(out, Camera->background); fprintf(out, "\n"); } void CameraDefaults(void) { VECTOR eyep = DEFAULT_EYEP, lookp = DEFAULT_LOOKP, updir = DEFAULT_UPDIR; RGB backgroundcolor = DEFAULT_BACKGROUND_COLOR; CameraSet(&Camera, &eyep, &lookp, &updir, DEFAULT_FOV, 600, 600, &backgroundcolor); AlternateCamera = Camera; } static void _setEyepOption(void *val) { VECTOR *v = (VECTOR *)val; CameraSetEyep(&Camera, v->x, v->y, v->z); } static void _setLookpOption(void *val) { VECTOR *v = (VECTOR *)val; CameraSetLookp(&Camera, v->x, v->y, v->z); } static void _setUpdirOption(void *val) { VECTOR *v = (VECTOR *)val; CameraSetUpdir(&Camera, v->x, v->y, v->z); } static void _setFovOption(void *val) { float *v = (float *)val; CameraSetFov(&Camera, *v); } /* And once more the same for the alternate camera */ static void _setAEyepOption(void *val) { VECTOR *v = (VECTOR *)val; CameraSetEyep(&AlternateCamera, v->x, v->y, v->z); } static void _setALookpOption(void *val) { VECTOR *v = (VECTOR *)val; CameraSetLookp(&AlternateCamera, v->x, v->y, v->z); } static void _setAUpdirOption(void *val) { VECTOR *v = (VECTOR *)val; CameraSetUpdir(&AlternateCamera, v->x, v->y, v->z); } static void _setAFovOption(void *val) { float *v = (float *)val; CameraSetFov(&AlternateCamera, *v); } static CMDLINEOPTDESC cameraOptions[] = { {"-eyepoint", 4, TVECTOR, &Camera.eyep, _setEyepOption, "-eyepoint \t: viewing position"}, {"-center", 4, TVECTOR, &Camera.lookp, _setLookpOption, "-center \t: point looked at"}, {"-updir", 3, TVECTOR, &Camera.updir, _setUpdirOption, "-updir \t: direction pointing up"}, {"-fov", 4, Tfloat, &Camera.fov, _setFovOption, "-fov \t: field of view angle"}, {"-aeyepoint", 4, TVECTOR, &AlternateCamera.eyep, _setAEyepOption, "-aeyepoint \t: alternate viewing position"}, {"-acenter", 4, TVECTOR, &AlternateCamera.lookp, _setALookpOption, "-acenter \t: alternate point looked at"}, {"-aupdir", 3, TVECTOR, &AlternateCamera.updir, _setAUpdirOption, "-aupdir \t: alternate direction pointing up"}, {"-afov", 4, Tfloat, &AlternateCamera.fov, _setAFovOption, "-afov \t: alternate field of view angle"}, {NULL, 0, TYPELESS, NULL, DEFAULT_ACTION, NULL } }; void ParseCameraOptions(int *argc, char **argv) { ParseOptions(cameraOptions, argc, argv); } void PrintCameraOptions(FILE *fp) { fprintf(fp, "\nCamera options:\n"); PrintOptions(fp, cameraOptions); } /* Sets virtual camera position, focus point, up-direction, field of view * (in degrees), horizontal and vertical window resolution and window * background. Returns (CAMERA *)NULL if eyepoint and focus point coincide or * viewing direction is equal to the up-direction. */ CAMERA *CameraSet(CAMERA *Camera, VECTOR *eyep, VECTOR *lookp, VECTOR *updir, float fov, int hres, int vres, RGB *background) { Camera->eyep = *eyep; Camera->lookp = *lookp; Camera->updir = *updir; Camera->fov = fov; Camera->hres = hres; Camera->vres = vres; Camera->background = *background; Camera->changed = TRUE; CameraComplete(Camera); return Camera; } extern int CameraCompare(CAMERA *cam1, CAMERA *cam2) { int result; result = VECTOREQUAL(cam1->eyep, cam2->eyep, EPSILON); if(result) { result = VECTOREQUAL(cam1->lookp, cam2->lookp, EPSILON); if(result) { result = VECTOREQUAL(cam1->updir, cam2->updir, EPSILON); if(result) result = FLOATEQUAL(cam1->fov, cam2->fov, EPSILON); } } return result; } void CameraComputeClippingPlanes(CAMERA *Camera) { float x = Camera->tanhfov * Camera->viewdist; /* half the width of the virtual screen in 3D space */ float y = Camera->tanvfov * Camera->viewdist; /* half the height of the virtual screen */ VECTOR vscrn[4]; int i; VECTORCOMB3(Camera->lookp, x, Camera->X, -y, Camera->Y, vscrn[0]); /* upper right corner: Y axis points down! */ VECTORCOMB3(Camera->lookp, x, Camera->X, y, Camera->Y, vscrn[1]); /* lower right */ VECTORCOMB3(Camera->lookp, -x, Camera->X, y, Camera->Y, vscrn[2]); /* lower left */ VECTORCOMB3(Camera->lookp, -x, Camera->X, -y, Camera->Y, vscrn[3]); /* upper left */ for (i=0; i<4; i++) { VECTORTRIPLECROSSPRODUCT(vscrn[(i+1)%4], Camera->eyep, vscrn[i], Camera->viewplane[i].norm); VECTORNORMALIZE(Camera->viewplane[i].norm); Camera->viewplane[i].d = -VECTORDOTPRODUCT(Camera->viewplane[i].norm, Camera->eyep); } } CAMERA *CameraComplete(CAMERA *Camera) { float n; /* compute viewing direction ==> Z axis of eye coordinate system */ VECTORSUBTRACT(Camera->lookp, Camera->eyep, Camera->Z); /* distance from virtual camera position to focus point */ Camera->viewdist = VECTORNORM(Camera->Z); if (Camera->viewdist < EPSILON) { Error("SetCamera", "eyepoint and look-point coincide"); return NULL; } VECTORSCALEINVERSE(Camera->viewdist, Camera->Z, Camera->Z); /* Camera->X is a direction pointing to the right in the window */ VECTORCROSSPRODUCT(Camera->Z, Camera->updir, Camera->X); n = VECTORNORM(Camera->X); if (n < EPSILON) { Error("SetCamera", "up-direction and viewing direction coincide"); return NULL; } VECTORSCALEINVERSE(n, Camera->X, Camera->X); /* Camera->Y is a direction pointing down in the window */ VECTORCROSSPRODUCT(Camera->Z, Camera->X, Camera->Y); VECTORNORMALIZE(Camera->Y); /* compute horizontal and vertical field of view angle from the specified one */ if (Camera->hres < Camera->vres) { Camera->hfov = Camera->fov; Camera->vfov = atan(tan(Camera->fov * M_PI/180.) * (float)Camera->vres/(float)Camera->hres) * 180./M_PI; } else { Camera->vfov = Camera->fov; Camera->hfov = atan(tan(Camera->fov * M_PI/180.) * (float)Camera->hres/(float)Camera->vres) * 180./M_PI; } /* default near and far clipping plane distance, will be set to a more reasonable * value when setting the camera for rendering. */ Camera->near = EPSILON; Camera->far = 2. * Camera->viewdist; /* Compute some extra frequently used quantities */ Camera->tanhfov = tan(Camera->hfov * M_PI / 180.0); Camera->tanvfov = tan(Camera->vfov * M_PI / 180.0); Camera->pixh = 2.0 * Camera->tanhfov / (float)(Camera->hres); Camera->pixv = 2.0 * Camera->tanvfov / (float)(Camera->vres); /* Camera->pixh = 2.0 * Camera->tanhfov / (float)(Camera->hres - 1); Camera->pixv = 2.0 * Camera->tanvfov / (float)(Camera->vres - 1); */ CameraComputeClippingPlanes(Camera); return Camera; } /* only sets virtual camera position in 3D space */ CAMERA *CameraSetEyep(CAMERA *cam, float x, float y, float z) { VECTOR neweyep; VECTORSET(neweyep, x, y, z); return CameraSet(cam, &neweyep, &cam->lookp, &cam->updir, cam->fov, cam->hres, cam->vres, &cam->background); } CAMERA *CameraSetLookp(CAMERA *cam, float x, float y, float z) { VECTOR newlookp; VECTORSET(newlookp, x, y, z); return CameraSet(cam, &cam->eyep, &newlookp, &cam->updir, cam->fov, cam->hres, cam->vres, &cam->background); } CAMERA *CameraSetUpdir(CAMERA *cam, float x, float y, float z) { VECTOR newupdir; VECTORSET(newupdir, x, y, z); return CameraSet(cam, &cam->eyep, &cam->lookp, &newupdir, cam->fov, cam->hres, cam->vres, &cam->background); } CAMERA *CameraSetFov(CAMERA *cam, float fov) { return CameraSet(cam, &cam->eyep, &cam->lookp, &cam->updir, fov, cam->hres, cam->vres, &cam->background); } CAMERA *CameraMoveHorizontal(CAMERA *cam, float step) { VECTOR neweyep, newlookp, dir; VECTORORTHOCOMP(cam->Z, cam->updir, dir); VECTORNORMALIZE(dir); VECTORSUMSCALED(cam->eyep, step, dir, neweyep); VECTORSUMSCALED(cam->lookp, step, dir, newlookp); return CameraSet(cam, &neweyep, &newlookp, &cam->updir, cam->fov, cam->hres, cam->vres, &cam->background); } CAMERA *CameraMoveForward(CAMERA *cam, float step) { VECTOR neweyep, newlookp, dir; dir = cam->Z; VECTORSUMSCALED(cam->eyep, step, dir, neweyep); VECTORSUMSCALED(cam->lookp, step, dir, newlookp); return CameraSet(cam, &neweyep, &newlookp, &cam->updir, cam->fov, cam->hres, cam->vres, &cam->background); } CAMERA *CameraMoveRight(CAMERA *cam, float step) { VECTOR neweyep, newlookp; VECTORSUMSCALED(cam->eyep, step, cam->X, neweyep); VECTORSUMSCALED(cam->lookp, step, cam->X, newlookp); return CameraSet(cam, &neweyep, &newlookp, &cam->updir, cam->fov, cam->hres, cam->vres, &cam->background); } CAMERA *CameraMoveUp(CAMERA *cam, float step) { VECTOR neweyep, newlookp; VECTORSUMSCALED(cam->eyep, step, cam->Y, neweyep); VECTORSUMSCALED(cam->lookp, step, cam->Y, newlookp); return CameraSet(cam, &neweyep, &newlookp, &cam->updir, cam->fov, cam->hres, cam->vres, &cam->background); } CAMERA *CameraTurnRight(CAMERA *cam, float angle) { VECTOR newlookp; float z=cam->viewdist*cos(angle), x=cam->viewdist*sin(angle); VECTORCOMB3(cam->eyep, z, cam->Z, x, cam->X, newlookp); return CameraSet(cam, &cam->eyep, &newlookp, &cam->updir, cam->fov, cam->hres, cam->vres, &cam->background); } CAMERA *CameraTurnUp(CAMERA *cam, float angle) { VECTOR newlookp; float z=cam->viewdist*cos(angle), y=-cam->viewdist*sin(angle); VECTORCOMB3(cam->eyep, z, cam->Z, y, cam->Y, newlookp); return CameraSet(cam, &cam->eyep, &newlookp, &cam->updir, cam->fov, cam->hres, cam->vres, &cam->background); } CAMERA *CameraTilt(CAMERA *cam, float angle) { VECTOR newupdir; float x, y, z, r; r=VECTORDOTPRODUCT(cam->updir, cam->Y); x=-r*sin(angle); y=r*cos(angle); z=VECTORDOTPRODUCT(cam->updir, cam->Z); VECTORCOORD(x, cam->X, y, cam->Y, z, cam->Z, newupdir); return CameraSet(cam, &cam->eyep, &cam->lookp, &newupdir, cam->fov, cam->hres, cam->vres, &cam->background); } CAMERA *CameraZoom(CAMERA *cam, float amount) { cam->fov /= amount; cam->hfov /= amount; cam->vfov /= amount; cam->changed = TRUE; return cam; } /* save camera position on the camera stack */ void CameraPush(CAMERA *cam) { if (CamStackPtr - CamStack >= MAXCAMSTACK) Error("PushCamera", "Camera Stack depth exceeded"); else *CamStackPtr++ = *cam; } /* restore camera position from the stack */ CAMERA *CameraPop(CAMERA *cam) { CAMERA poppedcam; if (CamStackPtr - CamStack <= 0) Error("PopCamera", "Camera Stack empty"); else { poppedcam = *--CamStackPtr; cam = CameraSet(cam, &poppedcam.eyep, &poppedcam.lookp, &poppedcam.updir, poppedcam.fov, cam->hres, cam->vres, &poppedcam.background); } return cam; } /* returns pointer to the next saved camera. If previous==NULL, the first saved * camera is returned. In subsequent calls, the previous camera returned * by this function should be passed as the parameter. If all saved cameras * have been iterated over, NULL is returned. */ CAMERA *NextSavedCamera(CAMERA *previous) { CAMERA *cam = previous ? previous : CamStackPtr; cam--; return (cam < CamStack) ? (CAMERA *)NULL : cam; }