public static DistanceSet GetDistanceToCamera(ushort lensIndex, Bounds targetBounds, FramingParameters framingParameters) { var distanceSet = new DistanceSet(); float frustumHeight; //find vertical field of view for given lens float vFov = ElPresidente.Instance.lensFovData[lensIndex]._unityVFOV * Mathf.Deg2Rad; frustumHeight = getFrustumHeight(framingParameters.MinPercent, targetBounds); distanceSet.min = getDistanceForHeight(frustumHeight, vFov); frustumHeight = getFrustumHeight(framingParameters.MaxPercent, targetBounds); distanceSet.max = getDistanceForHeight(frustumHeight, vFov); frustumHeight = getFrustumHeight(framingParameters.TargetPercent, targetBounds); distanceSet.ideal = getDistanceForHeight(frustumHeight, vFov); return(distanceSet); }
private bool findCameraPositionByRadius(GameObject framingTarget, Bounds targetBounds, FramingParameters framingParameters, ushort lensIndex, float maxHorizontalSearchPercent, out CameraPositionAndLens result) { //if the badness is low, get on with life float BADNESS_THRESHOLD = 0.15f; result = new CameraPositionAndLens() { LensIndex = lensIndex, Badness = float.MaxValue, Position = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue) }; bool subjectVisible = false; //find min,max,ideal camera distance for this lens var distanceToCamera = DistanceSet.GetDistanceToCamera(lensIndex, targetBounds, framingParameters); //search and track best match float horizontalSearchSign = 1; float horizontalSearchStepSize = 5f * Mathf.Deg2Rad; ushort horizontalSearchIterations = 0; float horizontalSearchAngleCurrent = 0; //bool verticalSearchSign = true; only searching up atm float verticalSearchStepSize = 1.5f * Mathf.Deg2Rad; ushort verticalSearchIterations = 0; ushort verticalSearchIterationsMax = 10; float verticalSearchAngleInitial = (cameraAngle == null ? 0f : CameraActionFactory.angles[cameraAngle.AngleSetting]) * Mathf.Deg2Rad; RaycastHit hit; while (!subjectVisible)//search over the range about ideal position { horizontalSearchIterations++; //reset vertical angle to default/specified float verticalSearchAngleCurrent = verticalSearchAngleInitial; verticalSearchIterations = 0; while (!subjectVisible && verticalSearchIterations < verticalSearchIterationsMax) { //find normalized direction vector given "direction" and angle measure float subjectToCameraHeading = getBaseCameraHeading(direction, framingTarget); Vector3 subjectToCamera = get3DDirection(framingTarget, new Vector3(verticalSearchAngleCurrent, horizontalSearchAngleCurrent), subjectToCameraHeading); //raycast to check for LoS Debug.DrawRay(targetLookAtPoint, subjectToCamera, Color.magenta, 10); if (Physics.Raycast(targetLookAtPoint, subjectToCamera, out hit)) { Opacity opacity = Opacity.High; if (hit.collider.gameObject.name == "Terrain" && hit.point.y > 0.26f) //below a certain height, we expect ground. this only works while we have flatness { opacity = getTreeOpacity(hit.point); } //if we get a hit, we will have to put the camera between the hit object and the //subject. again we are ignoring the range of valid sizes. we should use //min/max as stdev guidelines for "goodness" distribution //find distance to hit point from subject var distToHit = (targetLookAtPoint - hit.point).magnitude; //if the object we hit is closer than our min camera distance for the framing, //we need to check if it is least bad option so far if (distToHit < distanceToCamera.ideal && opacity > Opacity.Medium) { float badness = Mathf.Abs(verticalSearchAngleCurrent - verticalSearchAngleInitial) + Mathf.Abs(horizontalSearchAngleCurrent) + Mathf.Abs((distanceToCamera.ideal - distToHit) / distanceToCamera.ideal); if (badness < result.Badness) //update result with new position { result.Badness = badness; //adjust position slightly off intercepted collider toward the subject var cameraToSubjectOffset = (targetLookAtPoint - hit.point).normalized * 0.1f; result.Position = hit.point + cameraToSubjectOffset; } } else { float badness = Mathf.Abs(verticalSearchAngleCurrent - verticalSearchAngleInitial) + getOpacityBadness(opacity); if (badness < result.Badness) //update result with new position { result.Badness = badness; result.Position = targetLookAtPoint + subjectToCamera * distanceToCamera.ideal; } } } else //if we get no hit, there is nothing that can occlude the camera position { //place camera at ideal distance along subjectToCamera vector float badness = verticalSearchAngleCurrent + horizontalSearchAngleCurrent; if (badness < result.Badness) //update result with new position { result.Badness = badness; result.Position = targetLookAtPoint + subjectToCamera * distanceToCamera.ideal; } } if (result.Badness < BADNESS_THRESHOLD) { subjectVisible = true; break; } //unless we find a very close match and break out of this loop, we will update the search angles verticalSearchAngleCurrent += verticalSearchStepSize; verticalSearchIterations++; } if (!subjectVisible)//search around the circle { horizontalSearchSign = -horizontalSearchSign; horizontalSearchAngleCurrent = horizontalSearchSign * horizontalSearchIterations * horizontalSearchStepSize; if (Mathf.Abs(horizontalSearchAngleCurrent) > 1.8 * maxHorizontalSearchPercent) //have we gone more than the allotted amount around the circle? { break; } } } return(subjectVisible); }