/*============================================================================= Name : CameraCommand.c Purpose : Handles Camera commands such as focus and cancel focus, by keeping a "camera stack" Created 7/3/1997 by gshaw Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #include #include #include "types.h" #include "fastmath.h" #include "debug.h" #include "memory.h" #include "linkedlist.h" #include "camera.h" #include "utility.h" #include "prim2d.h" #include "statscript.h" #include "universe.h" #include "shipselect.h" #include "cameracommand.h" #include "proximitysensor.h" #include "sensors.h" #include "main.h" #include "alliance.h" #include "tutor.h" #include "singlePlayer.h" #include "task.h" #ifdef gshaw #define DEBUG_CAMERACOMMAND 0 #else #define DEBUG_CAMERACOMMAND 0 #endif extern udword utyNFrameTicks; /*============================================================================= Defines: =============================================================================*/ #define TOLERANCE 30 #define FOCUS_OFF_SCREEN 1 #define FOCUS_NEAR_BORDER 2 #define FOCUS_NEAR_BIGBORDER 4 #define FOCUS_NO_SHIPS 8 /*============================================================================= Tweakables: =============================================================================*/ udword MAX_CAMERA_STACKS = 20; real32 CAMERA_DEFAULT_DISTANCE = 4000.0f; real32 CAMERA_FAR_DISTANCE = 6000.0f; real32 CAMERA_FOCUSFAR_DISTANCE = 6000.0f; real32 CAMERA_MOTHERSHIPFOCUS_DISTANCE = 6000.0f; real32 CAMERA_MOTHERSHIPFOCUS_ANGLE = PI; real32 CAMERA_MOTHERSHIPFOCUS_DECLINATION = -0.5f; real32 GO_CAMERA_DELAY_TIME = 5.0f; sdword CAMERA_MAX_ZOOM_OUT_RATE = 40; sdword CAMERA_SCREEN_BORDER = 25; sdword CAMERA_NEAR_BORDER = 50; sdword CAMERA_NEAR_BIGBORDER = 150; real32 CLOSE_ON_ANGLE_SPEED = 10.0f; real32 CAMERA_ELASTIC_DISTANCE = 10000.0f; real32 CAMERA_ELASTIC_PULL_FACTOR = 1000.0f; real32 CAMERA_OUTBY_SCALE_ZOOM = 25.0f; real32 CAMERA_MIN_CLOSEFAST_RATE = 1.0f; real32 CAM_CUBIC_EVAL_TWEAK = 1.6f; real32 CAM_CUBIC_EVAL_ANGLE_TWEAK = 2.0f; //Falko: tweak camTrackDistSpeed[0] and camTrackPosSpeed - larger denominator real32 camTrackAngSpeed[2] = {1.0f/20.0f, 1.0f/10.0f}; real32 camTrackDistSpeed[2] = {1.0f/66.6666f, 1.0f/14.0f}; real32 camTrackDistBase[2] = {10.0f, 10.0f}; real32 camTrackDistThreshold = 1.0f/50.0f; real32 camTrackPosSpeed = 1.0f/50.0f; real32 camTrackPosThreshold = 1.0f/50.0f; real32 camTrackPosBase = 3.0f; #define MaxCameraDistanceFromPlayer 15000.0f static float MaxCamDistSquared = MaxCameraDistanceFromPlayer * MaxCameraDistanceFromPlayer; static long CollapseInterval = 8; // Happens every 8 frames static real32 CollapseTimeDelay = 7.0f; // Seconds before an invalid focus dies static real32 CollapseTimeStart = 0.0f; // Start Time when focus became invalid scriptEntry CameraCommandTweaks[] = { makeEntry(CAMERA_DEFAULT_DISTANCE,scriptSetReal32CB), makeEntry(CAMERA_FAR_DISTANCE,scriptSetReal32CB), makeEntry(CAMERA_FOCUSFAR_DISTANCE,scriptSetReal32CB), makeEntry(CAMERA_MOTHERSHIPFOCUS_DISTANCE,scriptSetReal32CB), makeEntry(CAMERA_MOTHERSHIPFOCUS_ANGLE,scriptSetAngCB), makeEntry(CAMERA_MOTHERSHIPFOCUS_DECLINATION,scriptSetAngCB), makeEntry(GO_CAMERA_DELAY_TIME,scriptSetReal32CB), makeEntry(CAMERA_MAX_ZOOM_OUT_RATE,scriptSetSdwordCB), makeEntry(CAMERA_SCREEN_BORDER,scriptSetSdwordCB), makeEntry(CAMERA_NEAR_BORDER,scriptSetSdwordCB), makeEntry(CAMERA_NEAR_BIGBORDER,scriptSetSdwordCB), makeEntry(CLOSE_ON_ANGLE_SPEED,scriptSetReal32CB), makeEntry(CAMERA_ELASTIC_DISTANCE,scriptSetReal32CB), makeEntry(CAMERA_ELASTIC_PULL_FACTOR,scriptSetReal32CB), makeEntry(CAMERA_OUTBY_SCALE_ZOOM,scriptSetReal32CB), makeEntry(CAMERA_MIN_CLOSEFAST_RATE,scriptSetReal32CB), makeEntry(MAX_CAMERA_STACKS,scriptSetUdwordCB), {"CAM_TRACK_ANG_AUTO", scriptSetReal32CB, &camTrackAngSpeed[0]}, {"CAM_TRACK_ANG_USER", scriptSetReal32CB, &camTrackAngSpeed[1]}, {"CAM_TRACK_DIST_AUTO", scriptSetReal32CB, &camTrackDistSpeed[0]}, {"CAM_TRACK_DIST_USER", scriptSetReal32CB, &camTrackDistSpeed[1]}, {"CAM_TRACK_DIST_THRESHOLD", scriptSetReal32CB, &camTrackDistThreshold}, {"CAM_EXP_DIST_AUTO", scriptSetReal32CB, &camTrackDistBase[0]}, {"CAM_EXP_DIST_USER", scriptSetReal32CB, &camTrackDistBase[1]}, {"CAM_TRACK_POS", scriptSetReal32CB, &camTrackPosSpeed}, {"CAM_TRACK_THRESHOLD", scriptSetReal32CB, &camTrackPosThreshold}, {"CAM_EXP_POS", scriptSetReal32CB, &camTrackPosBase}, makeEntry(CAM_CUBIC_EVAL_TWEAK,scriptSetReal32CB), makeEntry(CAM_CUBIC_EVAL_ANGLE_TWEAK,scriptSetReal32CB), makeEntry(CollapseTimeDelay,scriptSetReal32CB), endEntry }; /*============================================================================= Private Functions: =============================================================================*/ // This is the stuff for the camera collapse functions bool ccCameraTimeoutOverride = FALSE; bool ccFocusIsLegalToFocusOn(CameraStackEntry *entry) { struct Player *pPlayer; FocusCommand *pFocus; Node *pShipNode; Ship *pShip; long i; vector Vect, LookAt; pPlayer = universe.curPlayerPtr; LookAt = entry->remembercam.lookatpoint; pFocus = &entry->focus; for(i=0; inumShips; i++) { if(pFocus->ShipPtr[i]->objtype == OBJ_ShipType && allianceIsShipAlly(pFocus->ShipPtr[i], pPlayer)) { vecSub(Vect, LookAt, pFocus->ShipPtr[i]->posinfo.position); if( vecMagnitudeSquared(Vect) < MaxCamDistSquared ) { return TRUE; } } } pShipNode = universe.ShipList.head; while (pShipNode != NULL) { pShip = (Ship *)listGetStructOfNode(pShipNode); if(pShip->objtype == OBJ_ShipType && allianceIsShipAlly(pShip, pPlayer)) { vecSub(Vect, LookAt, pShip->posinfo.position); if( vecMagnitudeSquared(Vect) < MaxCamDistSquared ) { return TRUE; } } pShipNode = pShipNode->next; } return(FALSE); } bool ccFocusIsLegal(CameraStackEntry *entry) { if (ccFocusIsLegalToFocusOn(entry)) { CollapseTimeStart = taskTimeElapsed; return(TRUE); } if(!ccCameraTimeoutOverride && (taskTimeElapsed - CollapseTimeStart) > CollapseTimeDelay) return FALSE; else return TRUE; } void ccCollapseFocusStack(CameraCommand *cameracommand) { CameraStackEntry *entry; static long CollapseCount = 0; CollapseCount++; if(CollapseCount < CollapseInterval) { return; } CollapseCount = 0; entry = currentCameraStackEntry(cameracommand); if(!ccFocusIsLegal(entry)) { if(ccFocusOnFleet(cameracommand)) { // the FleetFocus worked, so delete the empty focus cameracommand->dontUseVelocityPredInChase = TRUE; } else { // if not, pop to sensors, then focus on the mothership smFocusOnMothershipOnClose = TRUE; smSensorsBegin(NULL, NULL); } } } typedef struct { double x,y,z; } DVect; void ccGetShipCollCenter(Ship *ship, vector *pos, real32 *rad) { Ship *master, *slave; Node *slavenode; vector center, distvec; float dist; if(bitTest(ship->flags, SOF_Slaveable) && bitTest(ship->slaveinfo->flags, SF_MASTER)) { //object is a master...so calculate Biggey selection circle based on all slave circles master = ship; slavenode = master->slaveinfo->slaves.head; center = master->collInfo.collPosition; while(slavenode != NULL) { slave = (Ship *) listGetStructOfNode(slavenode); vecAddTo(center, slave->collInfo.collPosition); slavenode = slavenode->next; } center.x /= (real32 )(master->slaveinfo->slaves.num+1); center.y /= (real32 )(master->slaveinfo->slaves.num+1); center.z /= (real32 )(master->slaveinfo->slaves.num+1); vecSub(distvec, center, master->collInfo.collPosition); dist = fsqrt(vecMagnitudeSquared(distvec)); *pos = center; *rad = dist + (master->staticinfo->staticheader.staticCollInfo.collspheresize); } else { *pos = ship->collInfo.collPosition; *rad = ship->staticinfo->staticheader.staticCollInfo.collspheresize; } } static const double TAN_FOVConstant = 1.303225; static const double INV_TAN_FOVConstant = 0.767327207; udword NewSetFocusPoint(CameraStackEntry *curentry, real32 *targetDistance) { udword numShipsFocusOn = curentry->focus.numShips; udword i; Ship *ship; real32 minx,maxx,miny,maxy,minz,maxz, radius, closezoom, minimumZoom; vector position; double MaxDist, Rad, LookCamLen, LookObjLen, invLookCam, invLookObj; double ObjZ, ObjRad, NewDist; DVect LookToObj, LookToCam, nLookToObj, nLookToCam; closezoom = curentry->remembercam.closestZoom; curentry->remembercam.oldlookatpoint = curentry->remembercam.lookatpoint; if (numShipsFocusOn == 0) { if(targetDistance) *targetDistance = closezoom; return FOCUS_NO_SHIPS; } ship = curentry->focus.ShipPtr[0]; ccGetShipCollCenter(ship, &position, &radius); minx = maxx = position.x; miny = maxy = position.y; minz = maxz = position.z; for (i=1;ifocus.ShipPtr[i]; ccGetShipCollCenter(ship, &position, &radius); if (position.x < minx) minx = position.x; else if (position.x > maxx) maxx = position.x; if (position.y < miny) miny = position.y; else if (position.y > maxy) maxy = position.y; if (position.z < minz) minz = position.z; else if (position.z > maxz) maxz = position.z; } curentry->remembercam.lookatpoint.x = (minx + maxx) * 0.5f; curentry->remembercam.lookatpoint.y = (miny + maxy) * 0.5f; curentry->remembercam.lookatpoint.z = (minz + maxz) * 0.5f; vecSub(LookToCam, curentry->remembercam.eyeposition, curentry->remembercam.lookatpoint); LookCamLen = sqrt(vecMagnitudeSquared(LookToCam)); invLookCam = 1.0 / LookCamLen; vecScalarMultiply(nLookToCam, LookToCam, invLookCam); vecNegate(nLookToCam); MaxDist = 0.0; for(i=0; ifocus.ShipPtr[i]; ccGetShipCollCenter(ship, &position, &radius); vecSub(LookToObj, curentry->remembercam.lookatpoint, position); LookObjLen = sqrt(vecMagnitudeSquared(LookToObj)); if (LookObjLen > 0.0) { invLookObj = 1.0 / LookObjLen; vecScalarMultiply(nLookToObj, LookToObj, invLookObj); Rad = vecDotProduct(nLookToObj, nLookToCam); ObjZ = Rad * LookObjLen; ObjRad = sqrt(1.0 - (Rad*Rad)) * LookObjLen; } else { Rad = ObjZ = ObjRad = 0.0; } if (ship->objtype == OBJ_ShipType) { minimumZoom = ship->staticinfo->minimumZoomDistance; } else if (ship->objtype == OBJ_DerelictType) { DerelictStaticInfo* derelictstaticinfo = (DerelictStaticInfo*)ship->staticinfo; minimumZoom = derelictstaticinfo->minimumZoomDistance; } else { minimumZoom = 0.0f; } if (minimumZoom < 0.10f) { ObjRad += radius; } else { ObjRad += minimumZoom * INV_TAN_FOVConstant; } NewDist = ObjRad * TAN_FOVConstant; if(NewDist < closezoom) NewDist = closezoom; NewDist += ObjZ; if (NewDist > MaxDist) { MaxDist = NewDist; } } if(targetDistance) *targetDistance = MaxDist; useSlowWheelZoomIn = FALSE; return 0; } /*----------------------------------------------------------------------------- Name : CloseOnAngle Description : gradually changes tracking to desired, at rate rate. Takes into account that angles can range from 0..2PI, and that 0 == 2PI. Inputs : tracking, desired, rate Outputs : tracking Return : returns 1 if tracking has locked on. ----------------------------------------------------------------------------*/ udword CloseOnAngle(real32 *tracking,real32 desired,real32 rate) { real32 diff = desired - *tracking; real32 absdiff = abs(diff); if (absdiff > PI) // at most we will be PI radians off, so if we are "more" than { // PI radians off we are going the wrong way. if (desired > PI) { desired -= TWOPI; } else { desired += TWOPI; } diff = desired - *tracking; absdiff = abs(diff); } if (abs(diff) < rate) { *tracking = desired; return 1; } if (diff > 0.0f) { *tracking += rate; } else { *tracking -= rate; } if (*tracking < 0.0f) { *tracking += TWOPI; } else if (*tracking > TWOPI) { *tracking -= TWOPI; } return 0; } /*----------------------------------------------------------------------------- Name : CloseOn Description : gradually changes tracking to desired, at rate rate. Inputs : tracking, desired, rate Outputs : tracking Return : returns 1 if tracking has locked on. ----------------------------------------------------------------------------*/ udword CloseOn(real32 *tracking,real32 desired,real32 rate) { real32 diff = desired - *tracking; if (abs(diff) < rate) { *tracking = desired; return 1; } if (diff > 0.0f) { *tracking += rate; } else { *tracking -= rate; } return 0; } /*----------------------------------------------------------------------------- Name : CloseOnFast Description : Changes tracking to desired, at a rate proportional to the difference between tracking and desired. This routine guarantees to close on at least with a rate of minrate. Inputs : tracking, desired, minrate Outputs : tracking Return : returns TRUE if tracking has locked on. ----------------------------------------------------------------------------*/ udword CloseOnFast(real32 *tracking,real32 desired,real32 minrate) { real32 diff = desired - *tracking; real32 dist = abs(diff); real32 rate = dist / 8.0f; if (rate <= minrate) { rate = minrate; } if (dist < rate) { return 1; } if (diff > 0.0f) { *tracking += rate; } else { *tracking -= rate; } return 0; } #if 0 void EyePositionFollowsLookatpoint(Camera *camera) { vector eyeToLookat; real32 distmag; real32 scale; vecSub(eyeToLookat,camera->lookatpoint,camera->eyeposition); distmag = fsqrt(vecMagnitudeSquared(eyeToLookat)); if (distmag >= CAMERA_ELASTIC_DISTANCE) { // have camera "pulled" in this directio scale = CAMERA_ELASTIC_PULL_FACTOR / distmag; vecMultiplyByScalar(eyeToLookat,scale); vecAddTo(camera->eyeposition,eyeToLookat); } } #endif /*---------------------------------------------------------------------------- EvalCubic: This is the Magic Function™ - It's basically a cubic spline smoothing function, which uses a current position and speed (*y0 and *m0) as well as a target value and a step size (y1 and t) to smoothly interpolate from the current value of *y0 to the desired target at y1. Inputs: y0 - pointer to the current position value m0 - pointer to the current speed value (initialize this to 0, then let EvalCubic handle it) y1 - Target or destination value t - Step interval (0 < t < 1.0) Outputs: y0 - *y0 is overwritten by the new position value m0 - *m0 is overwritten by the new speed value -----------------------------------------------------------------------------*/ void EvalCubic( float *y0, float *m0, float y1, float t ) { float a,b,c,d,e; c = *m0; d = *y0; e = y1-d; a = c-e-e; // b = e+e+e-c-c; b = e+e - (c * CAM_CUBIC_EVAL_TWEAK); // Different curve prediction *y0 = ((((a*t+b)*t)+c)*t)+d; *m0 = ((((a+a+a)*t)+(b+b))*t)+c; } /*---------------------------------------------------------------------------- EvalCubicAngle - Same as EvalCube, but handles *y0 & y1 in radians between 0 and 2PI. Performs the interpolation along the shortest angular path between *y0 and y1. Inputs: 0.0 < (*y0, y1) < 2PI 0.0 < t < 1.0 Outputs: *y0 is modified to reflect the new position, and is corrected to be between 0.0 to 2PI *m0 is the updated speed value -----------------------------------------------------------------------------*/ void EvalCubicAngle( float *y0, float *m0, float y1, float t ) { float a,b,c,d,e; c = *m0; d = *y0; e = y1-d; if(e < -PI) e += TWOPI; else if(e > PI) e += -TWOPI; a = c-e-e; // b = e+e+e-c-c; b = e+e+e- (c * CAM_CUBIC_EVAL_ANGLE_TWEAK); y1 = ((((a*t+b)*t)+c)*t)+d; if(y1 < 0.0f) y1 += TWOPI; else if(y1 > TWOPI) y1 -= TWOPI; *y0 = y1; *m0 = ((((a+a+a)*t)+(b+b))*t)+c; } /*----------------------------------------------------------------------------- Name : CubicLogUnsigned Description : Performs value tracking in log-base-n space rather than linearly. Inputs : targ - value to track threshold - how close to target we are to track step - how many tracks to do in total logBase - base of the logarithm space in which tracking is done Outputs : pos - updated position speed - updated speed Return : void ----------------------------------------------------------------------------*/ void CubicLogUnsigned(real32 *pos, real32 *speed, real32 targ, real32 threshold, real32 step, real32 logBase) { real32 fakeDist, fakeTarg; real32 posSign, tSign; if (*pos > targ) { fakeDist = *pos - targ - threshold; //logarithm is relative to target tSign = 1.0f; } else { fakeDist = *pos - targ + threshold; //logarithm is relative to target tSign = -1.0f; } if (fakeDist < 0.0f) { //handle negative posSign = -1.0f; fakeDist = -fakeDist; } else { posSign = 1.0f; if (fakeDist == 0.0f) { //handle zero fakeDist = REALlySmall; } } fakeDist = log(fakeDist) / log(logBase); //compute values in log-base-n space fakeTarg = log(threshold) / log(logBase); //roughly zero in logarithm space EvalCubic(&fakeDist, speed, fakeTarg, step); //update value *pos = pow(logBase, fakeDist) * posSign + targ + threshold * tSign;//convert back to linear space } /*----------------------------------------------------------------------------- Name : CameraChase Description : causes the actualcamera to track the currently set camera. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void CameraChase(CameraCommand *cameracommand,real32 zoomfactor) { Camera *desired = ¤tCameraStackEntry(cameracommand)->remembercam; vector targetvelocity; udword lockedon = 1; bool dontUseVelocityPredInChase = FALSE; long Frames, IndZoom, IndAng; static real32 oldTime = 0.0f; vector deLookVector; static float angSpeed = 0.0f, decSpeed = 0.0f; static float xSpeed = 0.0f, ySpeed = 0.0f; static float zSpeed = 0.0f, distSpeed = 0.0f; static float eyeXSpeed = 0.0f, eyeYSpeed = 0.0f, eyeZSpeed = 0.0f; if (cameracommand->dontUseVelocityPredInChase == TRUE) { dontUseVelocityPredInChase = TRUE; cameracommand->dontUseVelocityPredInChase = FALSE; } if (pilotView) { if (cameracommand->ccMode & CCMODE_PILOT_SHIP) { FocusCommand *focus = &cameracommand->currentCameraStack->focus; if (focus->numShips == 1) { cameraSetEyePositionBasedOnShip(&cameracommand->actualcamera,focus->ShipPtr[0]); return; } } vecSet(cameracommand->actualcamera.upvector,0.0f,0.0f,1.0f); } if(cameracommand->transition == CAMERA_FOCUSORIENT || cameracommand->transition == CAMERA_FOCUSMOVE || cameracommand->transition == CAMERA_FOCUSPOP) { Frames = utyNFrameTicks; if(Frames > 64) Frames = 64; IndZoom = (cameracommand->UserControlled & CAM_USER_ZOOMED) >> 1; IndAng = (cameracommand->UserControlled & CAM_USER_MOVED); if (!dontUseVelocityPredInChase) { vecSub(targetvelocity, desired->lookatpoint, desired->oldlookatpoint); vecAddTo(cameracommand->actualcamera.lookatpoint, targetvelocity); vecAddTo(cameracommand->actualcamera.eyeposition, targetvelocity); } while(Frames) { EvalCubicAngle(&cameracommand->actualcamera.angle, &angSpeed, desired->angle, camTrackAngSpeed[IndAng]); EvalCubic(&cameracommand->actualcamera.declination, &decSpeed, desired->declination, camTrackAngSpeed[IndAng]); CubicLogUnsigned(&cameracommand->actualcamera.lookatpoint.x, &xSpeed, desired->lookatpoint.x, camTrackPosThreshold, camTrackPosSpeed, camTrackPosBase); CubicLogUnsigned(&cameracommand->actualcamera.lookatpoint.y, &ySpeed, desired->lookatpoint.y, camTrackPosThreshold, camTrackPosSpeed, camTrackPosBase); CubicLogUnsigned(&cameracommand->actualcamera.lookatpoint.z, &zSpeed, desired->lookatpoint.z, camTrackPosThreshold, camTrackPosSpeed, camTrackPosBase); vecSub(deLookVector, cameracommand->actualcamera.eyeposition, cameracommand->actualcamera.lookatpoint); vecNormalize(&deLookVector); //compute desired eye position vecMultiplyByScalar(deLookVector, desired->distance); vecAddTo(deLookVector, desired->lookatpoint); CubicLogUnsigned(&cameracommand->actualcamera.eyeposition.x, &eyeXSpeed, deLookVector.x, camTrackPosThreshold, camTrackPosSpeed, camTrackPosBase); CubicLogUnsigned(&cameracommand->actualcamera.eyeposition.y, &eyeYSpeed, deLookVector.y, camTrackPosThreshold, camTrackPosSpeed, camTrackPosBase); CubicLogUnsigned(&cameracommand->actualcamera.eyeposition.z, &eyeZSpeed, deLookVector.z, camTrackPosThreshold, camTrackPosSpeed, camTrackPosBase); vecSub(deLookVector, cameracommand->actualcamera.eyeposition, cameracommand->actualcamera.lookatpoint); cameracommand->actualcamera.distance = fsqrt(vecMagnitudeSquared(deLookVector)); Frames--; } if (cameracommand->ccMode & CCMODE_FOCUS_CHANGES_LOOKATPOINT_ONLY) ; else if (!(cameracommand->ccMode & CCMODE_FOCUS_CHANGES_NOTHING)) cameraSetEyePosition(&cameracommand->actualcamera); } else if (cameracommand->transition == CAMERA_SET) { cameracommand->actualcamera.oldlookatpoint = cameracommand->actualcamera.lookatpoint; cameracommand->actualcamera.lookatpoint = desired->lookatpoint; cameracommand->actualcamera.distance = desired->distance; cameracommand->actualcamera.angle = desired->angle; cameracommand->actualcamera.declination = desired->declination; cameracommand->actualcamera.eyeposition = desired->eyeposition; } } /*----------------------------------------------------------------------------- Name : GetDistanceAngleDeclination Description : Sets the distance, angle and declination given the distvec vector which is a vector pointing from the lookatpoint to the eyepoint. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void GetDistanceAngleDeclination(Camera *camera,vector *distvec) { real32 value; // calculate distance camera->distance = sqrt(vecMagnitudeSquared(*distvec)); // now calculate angle and declination to face target camera->angle = (real32)atan2(distvec->y,distvec->x); value = -distvec->z/camera->distance; dbgAssert( abs(value) <= 1.01f ); // USE 1.01 SO ROUND OFF ERRORS DON'T CRASH US if(value < -1.0f) value = -1.0f; else if(value > 1.0f) value = 1.0f; camera->declination = (PI/2.0f) - (real32)acos(value); } void FocusOnNewEntry(CameraCommand *cameracommand,CameraStackEntry *entry) { Node *deletenode; Node *nextnode; CameraStackEntry *curentry = currentCameraStackEntry(cameracommand); dbgAssert(curentry); listAddNodeAfter(&curentry->stacklink,&entry->stacklink,entry); // Delete all nodes after new entry deletenode = entry->stacklink.next; while (deletenode != NULL) { nextnode = deletenode->next; listDeleteNode(deletenode); deletenode = nextnode; } cameracommand->currentCameraStack = entry; if (cameracommand->camerastack.num >= MAX_CAMERA_STACKS) { // remove head of stack listDeleteNode(cameracommand->camerastack.head); } #ifdef gshaw dbgMessagef("\nStack Depth: %d",cameracommand->camerastack.num); #endif } /*============================================================================= Public Functions: =============================================================================*/ /*----------------------------------------------------------------------------- Name : ccInit Description : Initializes the camera command object Inputs : sphereCameraCommandIsIn Outputs : Return : ----------------------------------------------------------------------------*/ void ccInit(CameraCommand *cameracommand) { CameraStackEntry *firstentry; listInit(&cameracommand->camerastack); cameracommand->ViewEntireMissphere = FALSE; cameracommand->dontUseVelocityPredInChase = FALSE; cameraInit(&cameracommand->actualcamera,CAMERA_FAR_DISTANCE); firstentry = memAlloc(sizeof(CameraStackEntry),"CamStackEntry",NonVolatile); firstentry->focus.numShips = 0; firstentry->remembercam = cameracommand->actualcamera; listAddNode(&cameracommand->camerastack,&firstentry->stacklink,firstentry); cameracommand->currentCameraStack = firstentry; // cameracommand->transition = CAMERA_SET; cameracommand->transition = CAMERA_FOCUSORIENT; cameracommand->action = CAMACTION_FOCUS; cameracommand->transTimeStamp = 0.0f; // cameracommand->zoomInCloseAsPossible = FALSE; cameracommand->zoomInCloseAsPossible = TRUE; cameracommand->UserControlled = 0; cameracommand->lockCameraTimeStamp = 0.0f; cameracommand->ccMode = 0; cameracommand->dontFocusOnMe = firstentry; } /*----------------------------------------------------------------------------- Name : ccSetModeFlag Description : sets a cameracommand mode flag Inputs : ccModeFlag Outputs : Return : ----------------------------------------------------------------------------*/ void ccSetModeFlag(CameraCommand *cameracommand,udword ccModeFlag) { cameracommand->ccMode |= ccModeFlag; } /*----------------------------------------------------------------------------- Name : ccClearModeFlag Description : clears a cameracommand mode flag Inputs : ccModeFlag Outputs : Return : ----------------------------------------------------------------------------*/ void ccClearModeFlag(CameraCommand *cameracommand,udword ccModeFlag) { bitClear(cameracommand->ccMode,ccModeFlag); } /*----------------------------------------------------------------------------- Name : ccFocusOnMyMothership Description : changes focus point to point to current players MotherShip Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ccFocusOnMyMothership(CameraCommand *cameracommand) { FocusCommand focus; CameraStackEntry *entry; if (universe.curPlayerPtr->PlayerMothership != NULL) { focus.ShipPtr[0] = universe.curPlayerPtr->PlayerMothership; focus.numShips = 1; ccFocus(cameracommand, &focus); cameracommand->zoomInCloseAsPossible = FALSE; entry = currentCameraStackEntry(cameracommand); entry->remembercam.distance = CAMERA_FAR_DISTANCE; cameraSetEyePosition(&entry->remembercam); } } real32 GetObjAngle(SpaceObjRot *robj) { vector heading; real32 angle; matGetVectFromMatrixCol3(heading,robj->rotinfo.coordsys); angle = (real32)atan2(heading.y,heading.x); return angle; } /*----------------------------------------------------------------------------- Name : ccFocusOnPlayersMissionSphere Description : changes focus point to point to current players mission sphere Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ccFocusOnPlayersMothership(CameraCommand *cameracommand,uword playerindex) { FocusCommand focus; CameraStackEntry *entry; // find players mission sphere Ship *mothership = universe.players[playerindex].PlayerMothership; real32 angle, declination, distance; vector distVector; if (mothership == NULL) { return; } // New Version ---------------------------- focus.ShipPtr[0] = mothership; focus.numShips = 1; ccFocus(cameracommand, &focus); cameracommand->zoomInCloseAsPossible = FALSE; entry = currentCameraStackEntry(cameracommand); if (spFindCameraAttitude(&entry->remembercam.eyeposition)) { vecSub(distVector, entry->remembercam.eyeposition, entry->remembercam.lookatpoint); GetDistanceAngleDeclination(&entry->remembercam, &distVector); distance = entry->remembercam.distance; angle = entry->remembercam.angle; declination = entry->remembercam.declination; if (!singlePlayerGame) { angle += GetObjAngle((SpaceObjRot *)mothership); } } else { angle = GetObjAngle((SpaceObjRot *)mothership) + CAMERA_MOTHERSHIPFOCUS_ANGLE; distance = CAMERA_MOTHERSHIPFOCUS_DISTANCE; declination = CAMERA_MOTHERSHIPFOCUS_DECLINATION; } if (angle < 0) { angle += TWOPI; } else if (angle > TWOPI) { angle -= TWOPI; } entry->remembercam.distance = distance; entry->remembercam.angle = angle; entry->remembercam.declination = declination; cameraSetEyePosition(&entry->remembercam); cameracommand->actualcamera = entry->remembercam; // New Version ---------------------------- // Old Version ------------------------------- // entry = currentCameraStackEntry(cameracommand); // dbgAssert(entry); // cameraChangeLookatpoint(&cameracommand->actualcamera,&mothership->posinfo.position); // entry->remembercam = cameracommand->actualcamera; // cameracommand->UserControlled = 0; // Old Version ------------------------------- } /*----------------------------------------------------------------------------- Name : ccClose Description : Closes the camera command object Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ccClose(CameraCommand *cameracommand) { listDeleteAll(&cameracommand->camerastack); } /*----------------------------------------------------------------------------- Name : ccReset Description : Resets the camera command object Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ccReset(CameraCommand *cameracommand) { ccClose(cameracommand); ccInit(cameracommand); } /*----------------------------------------------------------------------------- Name : ccFocusGeneral Description : gives the camera command object the command to focus on a group of ships Inputs : focuscom, command which tells what ships to focus on bCloseUp - Zoom in Close = TRUE / FALSE Outputs : Return : ----------------------------------------------------------------------------*/ void ccFocusGeneral(CameraCommand *cameracommand,FocusCommand *focuscom, bool bCloseUp) { CameraStackEntry *oldentry; CameraStackEntry *entry; udword excesssize; vector distvec; vector lookatdiffvec; real32 lookatdiffdist, newDist, MinDist; if((tutorial==TUTORIAL_ONLY) && !tutEnable.bFocus) { return; } if (focuscom->numShips < 1) { return; } //dbgAssert(focuscom->numShips >= 1); ccFocusCullRadiusMean(focuscom, RENDER_VIEWABLE_DISTANCE_SQR, NULL); if (focuscom->numShips == 0) { //if all ships focussed out (should never happen) return; } //Check for Proximity Sensor(s) in focus selection proxInFocusSelection(focuscom); oldentry = currentCameraStackEntry(cameracommand); if (SelectionsAreEquivalent((SelectCommand *)&oldentry->focus,(SelectCommand *)focuscom)) { #if DEBUG_CAMERACOMMAND dbgMessage("\nAutozooming in..."); #endif cameracommand->zoomInCloseAsPossible = TRUE; cameracommand->action = CAMACTION_FOCUS; cameracommand->UserControlled = 0; if (cameracommand->lockCameraTimeStamp != 0.0f) // cancel any freeze camera action { cameracommand->action = CAMACTION_FOCUS; cameracommand->lockCameraTimeStamp = 0.0f; cameracommand->transition = CAMERA_FOCUSORIENT; cameracommand->dontUseVelocityPredInChase = TRUE; } return; } excesssize = (focuscom->numShips-1)*sizeof(ShipPtr); entry = memAlloc(sizeof(CameraStackEntry)+excesssize,"CamStackEntry",NonVolatile); entry->remembercam = oldentry->remembercam; // start with previous camera and change it memcpy(&entry->focus,focuscom,sizeof(FocusCommand)+excesssize); // add code here to focus on new object NewSetFocusPoint(entry,&MinDist); vecSub(distvec, cameracommand->actualcamera.eyeposition, entry->remembercam.lookatpoint); // calculate distance newDist = sqrt(vecMagnitudeSquared(distvec)); entry->remembercam.distance = oldentry->remembercam.distance; if(entry->remembercam.distance < MinDist) entry->remembercam.distance = MinDist; vecSub(lookatdiffvec,entry->remembercam.lookatpoint,oldentry->remembercam.lookatpoint); lookatdiffdist = vecMagnitudeSquared(lookatdiffvec); if (lookatdiffdist >= RENDER_VIEWABLE_DISTANCE_SQR) { // now calculate angle and declination to face target entry->remembercam.angle = (real32)atan2(distvec.y,distvec.x); if (entry->remembercam.angle < 0) { entry->remembercam.angle += TWOPI; } entry->remembercam.declination = (real32)acos(distvec.z/newDist) - (PI/2.0f); } cameraSetEyePosition(&entry->remembercam); cameracommand->ViewEntireMissphere = FALSE; FocusOnNewEntry(cameracommand,entry); cameracommand->action = CAMACTION_FOCUS; cameracommand->transition = CAMERA_FOCUSORIENT; cameracommand->transTimeStamp = taskTimeElapsed; cameracommand->zoomInCloseAsPossible = bCloseUp; cameracommand->UserControlled = 0; cameracommand->lockCameraTimeStamp = 0.0f; if(oldentry->focus.numShips == 0) listDeleteNode(&oldentry->stacklink); } // General case of ccFocus : Don't zoom in close to focus target void ccFocus(CameraCommand *cameracommand, FocusCommand *focuscom) { ccFocusGeneral(cameracommand, focuscom, FALSE); } // Special case of ccFocus : Zoom in close to target when using ALT-Focus void ccFocusClose(CameraCommand *cameracommand, FocusCommand *focuscom) { ccFocusGeneral(cameracommand, focuscom, TRUE); } #if 0 /*----------------------------------------------------------------------------- Name : ccFocusSideways Description : gives the camera command object the command to focus on a group of ships, with a given angle and declination of camera Inputs : focuscom, command which tells what ships to focus on Outputs : Return : ----------------------------------------------------------------------------*/ void ccFocusSideways(CameraCommand *cameracommand,FocusCommand *focuscom) { CameraStackEntry *entry; ccFocus(cameracommand,focuscom); entry = currentCameraStackEntry(cameracommand); if ((entry->remembercam.angle > DEG_TO_RAD(90.0f)) || (entry->remembercam.angle < DEG_TO_RAD(-90.0f))) { entry->remembercam.angle = DEG_TO_RAD(180.0f); } else { entry->remembercam.angle = 0.0f; } entry->remembercam.declination = 0.0f; } #endif /*----------------------------------------------------------------------------- Name : ccChangeAngleDeclination Description : changes the angle and declination of camera Inputs : angle, declination Outputs : Return : ----------------------------------------------------------------------------*/ void ccChangeAngleDeclination(CameraCommand *cameracommand,real32 angle,real32 declination) { CameraStackEntry *entry; entry = currentCameraStackEntry(cameracommand); entry->remembercam.angle = angle; entry->remembercam.declination = declination; cameracommand->actualcamera.angle = angle; cameracommand->actualcamera.declination = declination; } /*----------------------------------------------------------------------------- Name : ccFocusFar Description : gives the camera command object the command to focus on a group of ships, from a far distance Inputs : focuscom, command which tells what ships to focus on currentCamera - camera to start with Outputs : Return : ----------------------------------------------------------------------------*/ void ccFocusFar(CameraCommand *cameracommand,FocusCommand *focuscom, Camera *currentCamera) { CameraStackEntry *oldentry; CameraStackEntry *entry; udword excesssize; if (focuscom->numShips < 1) { return; } //dbgAssert(focuscom->numShips >= 1); oldentry = currentCameraStackEntry(cameracommand); excesssize = (focuscom->numShips-1)*sizeof(ShipPtr); entry = memAlloc(sizeof(CameraStackEntry)+excesssize,"CamStackEntry",NonVolatile); // cameraCopyPositionInfo(&entry->remembercam, currentCamera); entry->remembercam = *currentCamera; // start with current camera and change it memcpy(&entry->focus,focuscom,sizeof(FocusCommand)+excesssize); // add code here to focus on new object NewSetFocusPoint(entry,NULL); // calculate distance entry->remembercam.distance = CAMERA_FAR_DISTANCE; cameraSetEyePosition(&entry->remembercam); cameracommand->ViewEntireMissphere = FALSE; FocusOnNewEntry(cameracommand,entry); cameracommand->action = CAMACTION_FOCUSFAR; cameracommand->transition = CAMERA_FOCUSORIENT; cameracommand->transTimeStamp = taskTimeElapsed; cameracommand->zoomInCloseAsPossible = FALSE; cameracommand->UserControlled = 0; cameracommand->lockCameraTimeStamp = 0.0f; } /*----------------------------------------------------------------------------- Name : ccFocusExact Description : copies focus of focustocopy to cameracommand Inputs : cameracommand, focustocopy Outputs : Return : TRUE if successful, FALSE if not able to do the operation because focustocopy wasn't focused on any ships. ----------------------------------------------------------------------------*/ bool ccFocusExact(CameraCommand *cameracommand,CameraCommand *focustocopy) { FocusCommand *currentfocus = currentFocus(focustocopy); if (currentfocus->numShips < 1) { return FALSE; } ccFocus(cameracommand,currentfocus); cameraCopyPositionInfo(¤tCameraStackEntry(cameracommand)->remembercam,¤tCameraStackEntry(focustocopy)->remembercam); cameraCopyPositionInfo(&cameracommand->actualcamera,&focustocopy->actualcamera); cameracommand->transition = focustocopy->transition; cameracommand->action = focustocopy->action; cameracommand->transTimeStamp = focustocopy->transTimeStamp; cameracommand->UserControlled = focustocopy->UserControlled; cameracommand->zoomInCloseAsPossible = focustocopy->zoomInCloseAsPossible; cameracommand->lockCameraTimeStamp = focustocopy->lockCameraTimeStamp; cameracommand->dontUseVelocityPredInChase = focustocopy->dontUseVelocityPredInChase; return TRUE; } /*----------------------------------------------------------------------------- Name : ccCopyCamera Description : Copies cameraToCopy into the cameracommand current camera Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ccCopyCamera(CameraCommand *cameracommand,Camera *cameraToCopy) { vector distvec; CameraStackEntry *curentry = currentCameraStackEntry(cameracommand); vecSub(distvec, cameraToCopy->eyeposition, cameraToCopy->lookatpoint); GetDistanceAngleDeclination(cameraToCopy,&distvec); // if (cameraToCopy->distance < 1000.0f) // { // cameraToCopy->distance = 1000.0f; // cameraSetEyePosition(cameraToCopy); // } cameraCopyPositionInfo(&curentry->remembercam,cameraToCopy); cameracommand->actualcamera = curentry->remembercam; } /*----------------------------------------------------------------------------- Name : ccCancelFocus Description : Cancel's focus (pops off current focused items of the camera stack, and reverts to the old one) Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ccCancelFocus(CameraCommand *cameracommand) { Node *node, *prevNode; CameraStackEntry *entry, *oldentry; if((tutorial==TUTORIAL_ONLY) && !tutEnable.bFocusCancel) return; oldentry = currentCameraStackEntry(cameracommand); node = oldentry->stacklink.prev; if (node == NULL) { return; } entry = (CameraStackEntry *)listGetStructOfNode(node); while (!ccFocusIsLegalToFocusOn(entry) && node != NULL && entry != cameracommand->dontFocusOnMe) { prevNode = node->prev; listDeleteNode(node); node = prevNode; if (node == NULL) { return; } entry = (CameraStackEntry *)listGetStructOfNode(node); } if (entry == cameracommand->dontFocusOnMe) { return; } cameracommand->ViewEntireMissphere = FALSE; ccFocusCullRadiusMean(&entry->focus, RENDER_VIEWABLE_DISTANCE_SQR, NULL); entry->remembercam.angle = cameracommand->actualcamera.angle; entry->remembercam.declination = cameracommand->actualcamera.declination; NewSetFocusPoint(entry,NULL); cameraSetEyePosition(&entry->remembercam); cameracommand->currentCameraStack = entry; cameracommand->action = CAMACTION_CANCELFOCUS; cameracommand->transition = CAMERA_FOCUSORIENT; cameracommand->transTimeStamp = taskTimeElapsed; cameracommand->zoomInCloseAsPossible = FALSE; cameracommand->UserControlled = 0; cameracommand->lockCameraTimeStamp = 0.0f; if(oldentry->focus.numShips == 0) { listDeleteNode(&oldentry->stacklink); } } /*----------------------------------------------------------------------------- Name : ccForwardFocus Description : Move's forward on the focus stack, similar to hitting the forward button on internet explorer. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ccForwardFocus(CameraCommand *cameracommand) { Node *next, *beyondNext; CameraStackEntry *entry, *oldentry; cameracommand->ViewEntireMissphere = FALSE; oldentry = currentCameraStackEntry(cameracommand); next = oldentry->stacklink.next; if (next == NULL) { return; } entry = (CameraStackEntry *)listGetStructOfNode(next); while (next != NULL && !ccFocusIsLegalToFocusOn(entry)) { beyondNext = next->next; listDeleteNode(next); next = beyondNext; if (next == NULL) { return; } entry = (CameraStackEntry *)listGetStructOfNode(next); } ccFocusCullRadiusMean(&entry->focus, RENDER_VIEWABLE_DISTANCE_SQR, NULL); entry->remembercam.angle = cameracommand->actualcamera.angle; entry->remembercam.declination = cameracommand->actualcamera.declination; NewSetFocusPoint(entry,NULL); cameraSetEyePosition(&entry->remembercam); cameracommand->currentCameraStack = entry; cameracommand->action = CAMACTION_FORWARDFOCUS; cameracommand->transition = CAMERA_FOCUSORIENT; cameracommand->transTimeStamp = taskTimeElapsed; cameracommand->zoomInCloseAsPossible = TRUE; cameracommand->UserControlled = 0; cameracommand->lockCameraTimeStamp = 0.0f; if(oldentry->focus.numShips == 0) listDeleteNode(&oldentry->stacklink); } /*----------------------------------------------------------------------------- Name : ccFocusOnFleet Description : focuses on fleet Inputs : Outputs : Return : TRUE if there were ships to focus on, FALSE otherwise ----------------------------------------------------------------------------*/ bool ccFocusOnFleet(CameraCommand *cameracommand) { uword numShips = 0; uword i; CameraStackEntry *entry, *oldentry; Node *objnode = universe.RenderList.head; Ship *ship; oldentry = currentCameraStackEntry(cameracommand); // count number of ships to focus on (current player's fleet) while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); if (ship->objtype == OBJ_ShipType && !(ship->flags & SOF_Dead)) { if (ship->playerowner == universe.curPlayerPtr) { numShips++; } } objnode = objnode->next; } if (numShips == 0) { return FALSE; } entry = memAlloc(sizeofCameraStackEntry(numShips),"CamStackEntry",NonVolatile); // put ships of current player's fleet into focus entry->focus.numShips = numShips; i = 0; objnode = universe.RenderList.head; while (objnode != NULL) { ship = (Ship *)listGetStructOfNode(objnode); if (ship->objtype == OBJ_ShipType && !(ship->flags & SOF_Dead)) { if (ship->playerowner == universe.curPlayerPtr) { entry->focus.ShipPtr[i++] = ship; } } objnode = objnode->next; } ccFocusCullRadiusMean(&entry->focus, RENDER_VIEWABLE_DISTANCE_SQR, NULL); entry->remembercam = cameracommand->actualcamera; entry->remembercam.distance = CAMERA_FAR_DISTANCE; NewSetFocusPoint(entry,NULL); cameraSetEyePosition(&entry->remembercam); FocusOnNewEntry(cameracommand,entry); if(oldentry->focus.numShips == 0) listDeleteNode(&oldentry->stacklink); return TRUE; } void ccFocusOnFleetSmooth(CameraCommand *cameracommand) { if (ccFocusOnFleet(cameracommand)) { cameracommand->action = CAMACTION_VIEWMISSPHERE; cameracommand->transition = CAMERA_FOCUSORIENT; cameracommand->transTimeStamp = taskTimeElapsed; cameracommand->zoomInCloseAsPossible = FALSE; cameracommand->UserControlled = 0; cameracommand->lockCameraTimeStamp = 0.0f; } } /*----------------------------------------------------------------------------- Name : ccViewToggleMissionSphere Description : toggles between far-out mission sphere view and regular view Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ccViewToggleMissionSphere(CameraCommand *cameracommand) { if (cameracommand->ViewEntireMissphere) { ccCancelFocus(cameracommand); } else { if (ccFocusOnFleet(cameracommand)) { cameracommand->ViewEntireMissphere = TRUE; cameracommand->action = CAMACTION_VIEWMISSPHERE; cameracommand->transition = CAMERA_FOCUSORIENT; cameracommand->transTimeStamp = taskTimeElapsed; cameracommand->zoomInCloseAsPossible = FALSE; cameracommand->lockCameraTimeStamp = 0.0f; } else { return; } } } /*----------------------------------------------------------------------------- Name : ccFreezeLookAtPoint Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ccFreezeLookAtPoint(CameraCommand *cameracommand,SelectCommand *selectcom) { #if 0 CameraStackEntry *entry = currentCameraStackEntry(cameracommand); if (SelectionsAreEquivalent(selectcom,&entry->focus)) { cameracommand->lockCameraTimeStamp = taskTimeElapsed; } #endif } /*----------------------------------------------------------------------------- Name : ccRemoveTheseShips Description : call this routine to remove references to theseships from cameracommand Inputs : theseships Outputs : Return : ----------------------------------------------------------------------------*/ void ccRemoveTheseShips(CameraCommand *cameracommand,SelectCommand *theseships) { Node *curnode = cameracommand->camerastack.head; Node *nextNode; CameraStackEntry *stackentry; sdword oldFocusShips; CameraStackEntry *curentry = currentCameraStackEntry(cameracommand); oldFocusShips = curentry->focus.numShips; while (curnode != NULL) { nextNode = curnode->next; stackentry = (CameraStackEntry *)listGetStructOfNode(curnode); MakeShipsNotIncludeTheseShips(&stackentry->focus,theseships); if(!smGhostMode && (stackentry->focus.numShips == 0) && (stackentry != curentry)) listDeleteNode(curnode); curnode = nextNode; } if(curentry->focus.numShips != oldFocusShips) cameracommand->dontUseVelocityPredInChase = TRUE; } /*----------------------------------------------------------------------------- Name : ccRemoveShip Description : call this routine to remove references to ship from cameracommand Inputs : ship Outputs : Return : ----------------------------------------------------------------------------*/ void ccRemoveShip(CameraCommand *cameracommand,Ship *ship) { Node *curnode = cameracommand->camerastack.head; Node *nextNode; CameraStackEntry *stackentry; sdword oldFocusShips; CameraStackEntry *curentry = currentCameraStackEntry(cameracommand); oldFocusShips = curentry->focus.numShips; while (curnode != NULL) { nextNode = curnode->next; stackentry = (CameraStackEntry *)listGetStructOfNode(curnode); clRemoveShipFromSelection(&stackentry->focus,ship); if(!smGhostMode && (stackentry->focus.numShips == 0) && (stackentry != curentry)) { listDeleteNode(curnode); } else { cameracommand->zoomInCloseAsPossible = FALSE; } curnode = nextNode; } if(curentry->focus.numShips != oldFocusShips) cameracommand->dontUseVelocityPredInChase = TRUE; } /*----------------------------------------------------------------------------- Name : ccControl Description : does all control for the Camera Command. This routine should be called everytime you want the user to be able to control the camera. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ccControl(CameraCommand *cameracommand) { udword focusOnScreen; real32 zoomfactor = 0.0f; CameraStackEntry *curentry = currentCameraStackEntry(cameracommand); float targetDistance; sdword UserAction; if (cameracommand->actualcamera.ignoreZoom) return; if (cameracommand->lockCameraTimeStamp == 0.0f) focusOnScreen = NewSetFocusPoint(curentry, &targetDistance); else { if ((taskTimeElapsed - cameracommand->lockCameraTimeStamp) > GO_CAMERA_DELAY_TIME) { cameracommand->lockCameraTimeStamp = 0.0f; cameracommand->transition = CAMERA_FOCUSORIENT; cameracommand->dontUseVelocityPredInChase = TRUE; focusOnScreen = NewSetFocusPoint(curentry, &targetDistance); } else focusOnScreen = FOCUS_NO_SHIPS; } // if(curentry->remembercam.distance > targetDistance) // zoomInAllowed = TRUE; // else // zoomInAllowed = FALSE; if ((cameracommand->ccMode & CCMODE_LOCK_OUT_USER_INPUT) == 0) { UserAction = cameraControl(&curentry->remembercam, TRUE); if(UserAction) { cameracommand->UserControlled = UserAction; if(UserAction & CAM_USER_ZOOMED) cameracommand->zoomInCloseAsPossible = FALSE; if(curentry->remembercam.distance < targetDistance) curentry->remembercam.distance = targetDistance; } } if (cameracommand->ccMode & CCMODE_FOCUS_CHANGES_LOOKATPOINT_ONLY) ; else if (!(cameracommand->ccMode & CCMODE_FOCUS_CHANGES_NOTHING)) cameraSetEyePosition(&curentry->remembercam); if(cameracommand->zoomInCloseAsPossible) { if (cameracommand->action == CAMACTION_FOCUSFAR) { curentry->remembercam.distance = CAMERA_FOCUSFAR_DISTANCE; cameracommand->zoomInCloseAsPossible = FALSE; } else curentry->remembercam.distance = targetDistance; } if(!smGhostMode && (!(tutorial==TUTORIAL_ONLY) || tutEnable.bGameRunning)) ccCollapseFocusStack(cameracommand); CameraChase(cameracommand,zoomfactor); } void ccLockOnTargetNow(CameraCommand *cameracommand) { real32 targetDistance; CameraStackEntry *curentry = currentCameraStackEntry(cameracommand); Camera *desired = &curentry->remembercam; NewSetFocusPoint(curentry, &targetDistance); if (cameracommand->ccMode & CCMODE_FOCUS_CHANGES_LOOKATPOINT_ONLY) ; else if (!(cameracommand->ccMode & CCMODE_FOCUS_CHANGES_NOTHING)) cameraSetEyePosition(&curentry->remembercam); if(cameracommand->zoomInCloseAsPossible) { if (cameracommand->action == CAMACTION_FOCUSFAR) { curentry->remembercam.distance = CAMERA_FOCUSFAR_DISTANCE; cameracommand->zoomInCloseAsPossible = FALSE; } else curentry->remembercam.distance = targetDistance; } cameracommand->actualcamera.lookatpoint = desired->lookatpoint; cameracommand->actualcamera.oldlookatpoint = cameracommand->actualcamera.lookatpoint; cameracommand->actualcamera.distance = desired->distance; cameracommand->actualcamera.angle = desired->angle; cameracommand->actualcamera.declination = desired->declination; cameracommand->actualcamera.eyeposition = desired->eyeposition; } /*----------------------------------------------------------------------------- Name : ccFocusCullRadius Description : Cull a selection for focussing based on an arbitrary centre point Inputs : in - selection to cull raduisSqr - Square of the radius from mean that is acceptable. centre - centre point to cull about Outputs : out - where to cull the selection to Return : ----------------------------------------------------------------------------*/ sdword ccFocusCullRadiusGeneral(FocusCommand *out, FocusCommand *in, real32 radiusSqr, vector *centre) { sdword index; real32 dx, dy, dz, distanceSqr; for (out->numShips = index = 0; index < in->numShips; index++) { dx = abs(in->ShipPtr[index]->posinfo.position.x - centre->x); dy = abs(in->ShipPtr[index]->posinfo.position.y - centre->y); dz = abs(in->ShipPtr[index]->posinfo.position.z - centre->z); distanceSqr = dx * dx + dy * dy + dz * dz; //compute distance from centre if (distanceSqr <= radiusSqr) { //if this ship close enough to the mean out->ShipPtr[out->numShips] = in->ShipPtr[index]; out->numShips++; //add this ship to selection } } return(out->numShips); } /*----------------------------------------------------------------------------- Name : ccFocusCullRadiusMean Description : Cull out any objects in the specified selection that are more than radius from the mean position. Inputs : selection - selection to cull radius - Square of the radius from mean that is acceptable. Outputs : centre - where to store the calculated centre point. May be NULL. Return : Number of objects remaining in selection ----------------------------------------------------------------------------*/ sdword ccFocusCullRadiusMean(FocusCommand *selection, real32 radiusSqr, vector *centreDest) { sdword index, iOuter; vector centre = {0.0f, 0.0f, 0.0f}; MaxSelection localSelection; sdword maxFocussed, iMaxFocussed; if (selection->numShips == 0) { //nothing to focus on return 0; } //get average position for (index = 0; index < selection->numShips; index++) { vecAddTo(centre, selection->ShipPtr[index]->posinfo.position); } vecMultiplyByScalar(centre, (1.0f / (real32)index)); //now go through the list and cull out any objects farther from the origin //than a specified amount ccFocusCullRadiusGeneral((FocusCommand *)&localSelection, selection, radiusSqr, ¢re); //using the mean didn't work; try to centre around any given ship if (localSelection.numShips == 0) { //if everything was culled out maxFocussed = 0; for (iOuter = 0; iOuter < selection->numShips; iOuter++) { centre = selection->ShipPtr[iOuter]->posinfo.position; ccFocusCullRadiusGeneral((FocusCommand *)&localSelection, selection, radiusSqr, ¢re); if (localSelection.numShips > maxFocussed) { //keep track of the best selection maxFocussed = localSelection.numShips; iMaxFocussed = iOuter; } } dbgAssert(maxFocussed > 0); //one selection combo should have worked centre = selection->ShipPtr[iMaxFocussed]->posinfo.position;//use the best selection we found ccFocusCullRadiusGeneral((FocusCommand *)&localSelection, selection, radiusSqr, ¢re); } selSelectionCopy((MaxAnySelection *)selection, (MaxAnySelection *)&localSelection); if (centreDest != NULL) { //store dest centre point *centreDest = centre; } return(selection->numShips); }