Exemplo n.º 1
0
        public override bool Init()
        {
            if (initialized)
            {
                return(true);
            }
            Extensions.RenderColliders();
            //don't throw null refs in the debug statement if framing isn't there.  it's not required
            string framingDescriptor = string.Empty;

            if (existsFraming())
            {
                framingDescriptor = framings[0].ToString();
            }

            Extensions.Log("init shot fragment start[{0}] end[{1}] lens[{2}] fStop[{3}] framing[{4}] direction[{5}] angle[{6}] focus[{7}]",
                           startTick, endTick, lensName, fStopName, framingDescriptor, direction, cameraAngle, focusTarget);

            if (!findCamera())
            {
                return(false);
            }
            savePreviousCameraState();

            //ground parameters
            tempCameraPosition    = new Vector3Nullable(null, null, null);
            tempCameraOrientation = new Vector3Nullable(null, null, null);

            //subject position if something is framed
            GameObject framingTarget = null;

            if (existsFraming())
            {
                if (getActorByName(framings[0].FramingTarget, out framingTarget))
                {
                    targetLookAtPoint = findTargetLookAtPoint(framingTarget);
                }
                else
                {
                    Debug.LogError(string.Format("could not find actor [{0}]",
                                                 framings[0].FramingTarget).AppendTimestamps());
                }
            }

            //height
            if (existsFraming()) //default to even height with point of interest on framed target
            {
                tempCameraPosition.Y = targetLookAtPoint.y;
            }
            else
            {
                tempCameraPosition.Y = 1; //in the absence of all information just put the camera not at 0 height
            }

            //lens
            ushort tempLens;

            if (!string.IsNullOrEmpty(lensName) &&
                CameraActionFactory.lenses.TryGetValue(lensName, out tempLens))
            {
                tempLensIndex = tempLens;
            }

            //F Stop
            ushort tempFStop;

            if (!string.IsNullOrEmpty(fStopName) &&
                CameraActionFactory.fStops.TryGetValue(fStopName, out tempFStop))
            {
                tempFStopIndex = tempFStop;
            }

            //framing
            if (existsFraming() && framingTarget)
            {
                Bounds targetBounds = framingTarget.GetComponent <BoxCollider>().bounds;
                targetBounds.BuildDebugBox(5, Color.cyan);

                Extensions.Log("framing target[{0}] bounds[{1},{2}]", framingTarget.name,
                               targetBounds.min.y, targetBounds.max.y);

                FramingParameters framingParameters = FramingParameters.FramingTable[framings[0].FramingType];

                //default our aperture to one appropriate to the framing if it's not set
                if (!tempFStopIndex.HasValue &&
                    CameraActionFactory.fStops.TryGetValue(framingParameters.DefaultFStop, out tempFStop))
                {
                    tempFStopIndex = tempFStop;
                }

                if (tempLensIndex.HasValue && tempCameraPosition.X.HasValue && tempCameraPosition.Z.HasValue)
                {
                    //case is here for completeness.  rotation needs to be done for all combinations of lens and anchor specification, so it goes after all the conditionals
                }
                else if (!tempLensIndex.HasValue && tempCameraPosition.X.HasValue && tempCameraPosition.Z.HasValue)//direction still doesn't matter since we can't move in the x,z plane
                {
                    //naively guessing and checking
                    Quaternion savedCameraRotation = cameraBody.NodalCamera.transform.rotation;
                    //point the camera at the thing
                    cameraBody.NodalCamera.transform.rotation = Quaternion.LookRotation(targetBounds.center - cameraBody.NodalCamera.transform.position);
                    float targetFov = 0;
                    //need to keep from stepping up and down over some boundary
                    bool incremented = false;
                    bool decremented = false;
                    while (!(incremented && decremented))  //if we haven't set a value and we haven't stepped both up and down.
                    {
                        //find where on the screen the extents are.  using viewport space so this will be in %. z is world units away from camera
                        Vector3 bMax = cameraBody.NodalCamera.WorldToViewportPoint(targetBounds.max);
                        Vector3 bMin = cameraBody.NodalCamera.WorldToViewportPoint(targetBounds.min);

                        float FovStepSize = 2.5f;//consider making step size a function of current size to increase granularity at low fov.  2.5 is big enough to jump 100-180 oddly

                        if (bMax.y - bMin.y > framingParameters.MaxPercent && bMax.y - bMin.y < framingParameters.MinPercent)
                        {
                            break;//we found our answer in cameraBody.NodalCamera.fov
                        }
                        else if (bMax.y - bMin.y < framingParameters.MinPercent)
                        {
                            cameraBody.NodalCamera.fieldOfView -= FovStepSize;
                            decremented = true;
                        }
                        else //(bMax.y - bMin.y >= fp.MaxPercent)
                        {
                            cameraBody.NodalCamera.fieldOfView += FovStepSize;
                            incremented = true;
                        }

                        //force matrix recalculations on the camera after adjusting fov
                        cameraBody.NodalCamera.ResetProjectionMatrix();
                        cameraBody.NodalCamera.ResetWorldToCameraMatrix();
                    }
                    //reset camera position...we should only be moving the rig
                    targetFov = cameraBody.NodalCamera.fieldOfView;
                    cameraBody.NodalCamera.transform.rotation = savedCameraRotation;
                    tempLensIndex = (ushort)ElPresidente.Instance.GetLensIndex(targetFov);
                }
                else if (tempLensIndex.HasValue &&                                           //direction matters here.
                         (!tempCameraPosition.X.HasValue || !tempCameraPosition.Z.HasValue)) //also assuming we get x,z in a pair.  if only one is provided, it is invalid and will be ignored
                {
                    var cpl = new CameraPositionAndLens();
                    if (!findCameraPositionByRadius(framingTarget, targetBounds, framingParameters, tempLensIndex.Value, 0.5f, out cpl))
                    {
                        Extensions.Log("failed to find satisficing position for camera to frame [{0}] [{1}] with lens [{2}]. using least bad alternative",
                                       framings[0].FramingTarget, framings[0].FramingType.ToString(), ElPresidente.Instance.lensFovData[tempLensIndex.Value]._focalLength);
                    }
                    tempCameraPosition.X = cpl.Position.x;
                    tempCameraPosition.Y = cpl.Position.y;
                    tempCameraPosition.Z = cpl.Position.z;
                }
                else //we are calculating everything by framing and direction.
                {
                    var cpl     = new CameraPositionAndLens();
                    var bestCPL = new CameraPositionAndLens()
                    {
                        Badness = float.MaxValue
                    };
                    ushort defaultLensIndex = CameraActionFactory.lenses[framingParameters.DefaultFocalLength];
                    ushort bestLensIndex    = defaultLensIndex;
                    tempLensIndex = defaultLensIndex;
                    const float BADNESS_WEIGHT_LENS_CHANGE = 0.75f;
                    const float BADNESS_THRESHOLD          = 3.1f;
                    //x,z does not have value
                    //pick a typical lens for this type of shot
                    //ushort defaultLensIndex = CameraActionFactory.lenses[framingParameters.DefaultFocalLength];
                    //tempLensIndex = defaultLensIndex;

                    //see if we can find a camera location for this lens
                    //allow less than some % of a circle variance from ideal viewing.  if we don't find an answer, change the lens

                    bool   sign       = true;
                    short  iterations = 0;
                    ushort maxLensChangeIterations = 6;
                    while (!findCameraPositionByRadius(framingTarget, targetBounds, framingParameters, tempLensIndex.Value, 0.35f, out cpl))
                    {
                        //add lens change badness to result of camera position find
                        cpl.Badness += BADNESS_WEIGHT_LENS_CHANGE * Mathf.Abs(tempLensIndex.Value - defaultLensIndex);
                        if (cpl.Badness < bestCPL.Badness)
                        {
                            bestCPL       = cpl;
                            bestLensIndex = tempLensIndex.Value;
                            if (bestCPL.Badness < BADNESS_THRESHOLD)
                            {
                                break;
                            }
                        }

                        iterations++;
                        if (iterations > maxLensChangeIterations)
                        {
                            Extensions.Log("exceeded max lens change iterations[{0}] solving framing[{1}] on target[{2}]",
                                           maxLensChangeIterations, framingParameters.Type, framingTarget);
                            break; //framing is just not working out.  we will return a shot that's not so good and get on with things
                        }
                        int offset = sign ? iterations : -iterations;
                        if (tempLensIndex + offset < 0)
                        {
                            //should never get here since the smallest we specify is 27mm and we will cap at +-3 lenses
                        }
                        else if (tempLensIndex + offset > CameraActionFactory.lenses.Values.Max <ushort>())
                        {
                            //explore on the other side of our start lens until we hit our max iterations
                            iterations++;
                            offset = sign ? -iterations : iterations;
                        }
                        tempLensIndex = (ushort)(tempLensIndex + offset);
                    }
                    tempCameraPosition.X = bestCPL.Position.x;
                    tempCameraPosition.Y = bestCPL.Position.y;
                    tempCameraPosition.Z = bestCPL.Position.z;
                    tempLensIndex        = bestLensIndex;
                }
                tempCameraOrientation.Y = Quaternion.LookRotation(framingTarget.transform.position - tempCameraPosition.Merge(previousCameraPosition)).eulerAngles.y;
            }

            //this destroys the ability to angle with respect to anything but the framing target if specified
            //this does not seem terribly harmful. subject is attached to angle mostly because we wanted to not have
            //to specify a framing (if we used an absolute anchor for camera positioning)
            tiltCameraAtSubject(cameraAngle, framingTarget);

            //focus has to go after all possible x,y,z settings to get the correct distance to subject
            Vector3 focusPosition;

            if (calculateFocusPosition(focusTarget, out focusPosition))
            {
                tempFocusDistance = Vector3.Distance(tempCameraPosition.Merge(previousCameraPosition), focusPosition);
            }
            else if (framingTarget != null)//we didn't specify what to focus on, but we framed something.  let's focus on that by default
            {
                tempFocusDistance = Vector3.Distance(tempCameraPosition.Merge(previousCameraPosition), targetLookAtPoint);
            }

            //sort out what wins where and assign to final camera properties
            //start with previous camera properties in case nothing fills them in
            newCameraPosition    = tempCameraPosition.Merge(previousCameraPosition);
            newCameraOrientation = Quaternion.Euler(tempCameraOrientation.Merge(previousCameraOrientation.eulerAngles));
            newLensIndex         = tempLensIndex.HasValue ? tempLensIndex.Value : previousLensIndex;
            newFStopIndex        = tempFStopIndex.HasValue ? tempFStopIndex.Value : previousFStopIndex;
            newfocusDistance     = tempFocusDistance.HasValue ? tempFocusDistance.Value : previousFocusDistance;

            Skip();

            initialized = true;
            return(initialized);
        }
Exemplo n.º 2
0
        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);
        }