/* camera.c */ #include #include #include #include "camera.h" #include "error.h" #include "vector.h" #include "Boolean.h" #include "pools.h" CAMERA Camera; /* the one and only virtual camera */ #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", (float)Camera->fov); fprintf(out, "\nhres = %d, vres = %d", Camera->hres, Camera->vres); fprintf(out, "\nbackground color: "); RGBPrint(out, Camera->background); fprintf(out, "\n"); } /* 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) { float n; Camera->eyep = *eyep; Camera->lookp = *lookp; Camera->updir = *updir; Camera->fov = fov; Camera->hres = hres; Camera->vres = vres; Camera->background = *background; Camera->changed = TRUE; /* compute viewing direction ==> Z axis of eye coordinate system */ VECTORSUBTRACT(*lookp, *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, *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 (hres < vres) { Camera->hfov = fov; Camera->vfov = atan(tan(fov * M_PI/180.)*(float)vres/(float)hres) * 180./M_PI; } else { Camera->vfov = fov; Camera->hfov = atan(tan(fov * M_PI/180.)*(float)hres/(float)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; 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 *CameraMoveForward(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 *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; }