Ejemplo n.º 1
0
    protected override void OnStart()
    {
        EmotionSpectrum currentEmotion = ProceduralEngine.Instance.GetCurrentEmotion();
        float           expectation    = currentEmotion.Dot(new EmotionSpectrum(EmotionVector.GetCoreEmotion(CoreEmotion.Anticipation)));

        camera.RotationDampingTime = .1f + Mathf.Lerp(.4f, 0f, Mathf.Clamp01(expectation - 2f));
        camera.PositionDampingTime = .1f + Mathf.Lerp(.4f, 0f, Mathf.Clamp01(expectation - 2f));
        camera.SetNoiseParameters(Mathf.Clamp(expectation * .4f, 0f, .25f), .75f);

        Vector3 boundsAxis = mainInterestPoint.AssociatedItemBounds.size.normalized;

        List <KeyValuePair <Vector3, float> > possibleDirections = new List <KeyValuePair <Vector3, float> >();

        // Chance of following the frustum average
        possibleDirections.Add(new KeyValuePair <Vector3, float>(mainInterestAxis, .5f));

        // Chance of picking a dolly direction based on the item boundaries
        possibleDirections.Add(new KeyValuePair <Vector3, float>(mainInterestPoint.transform.right * boundsAxis.x, boundsAxis.x));
        possibleDirections.Add(new KeyValuePair <Vector3, float>(mainInterestPoint.transform.up * boundsAxis.y, boundsAxis.y));
        possibleDirections.Add(new KeyValuePair <Vector3, float>(mainInterestPoint.transform.forward * boundsAxis.z, boundsAxis.z));

        // Chance of doing a dolly in/out
        float inOutDirection = ProceduralEngine.Instance.EmotionEngine.GetStructureAtTime(ProceduralEngine.Instance.CurrentTimeNormalized) == StructureType.Decreasing ? -1f : 1f;

        possibleDirections.Add(new KeyValuePair <Vector3, float>(GetForward() * inOutDirection, .5f));

        movementDirection = ProceduralEngine.SelectRandomWeighted(possibleDirections, x => x.Value).Key.normalized;
        keepAttention     = ProceduralEngine.RandomRange(0f, 1f) > .5f; // TODO: associate this with the rotation smoothness/lag, and with emotions (e.g. sadness lags, expectation keeps)
    }
Ejemplo n.º 2
0
    protected void StartTransition(ShotInformation shot)
    {
        if (currentShot.valid && currentShot.strategy != null)
        {
            currentShot.strategy.StopStrategy();
        }

        if (shot.valid)
        {
            // Select the strategy when starting the transition
            shot.strategy       = ProceduralEngine.SelectRandomWeighted(shot.sampledStrategies, x => x.Value).Key;
            shot.selectedCamera = InstanceCamera();

            this.history.Add(shot);
            this.nextShot = new ShotInformation();
            this.nextShot.sampledStrategies = new List <KeyValuePair <ProceduralCameraStrategy, float> >();
            this.nextShotTries  = 0;
            this.currentCutTime = 0f;

            this.currentShot   = shot;
            this.currentCamera = shot.selectedCamera;
            this.currentCamera.InitializeCamera(currentShot.strategy, GetComponent <PostProcessingBehaviour>().profile);
            this.currentShot.strategy.StartStrategy(currentCamera);
        }
    }
Ejemplo n.º 3
0
    protected virtual bool FindCameraPosition(float minDistance, float maxDistance)
    {
        Vector3 p = mainInterestPoint.transform.position;

        int maxTries = 4;

        for (int i = 0; i < maxTries; ++i)
        {
            Vector3 pointScale = mainInterestPoint.transform.lossyScale * mainInterestPoint.size;
            Vector3 startPoint = p + Vector3.Scale(Random.onUnitSphere, pointScale);

            // If the interest point is very directional, consider that for the ray direction
            Vector3 biasedDirection = (Random.onUnitSphere + mainInterestPoint.transform.forward * mainInterestPoint.directionality * 6f + GetCameraDirectionBias()).normalized;

            Ray        ray = new Ray(startPoint, biasedDirection);
            RaycastHit hit;
            bool       intersect = Physics.Raycast(ray, out hit, maxDistance);

            bool firstPass = false;

            if (intersect)
            {
                if (hit.distance > minDistance)
                {
                    float d = Mathf.Clamp(hit.distance * .75f, minDistance, maxDistance);
                    CameraPosition = ray.origin + ray.direction * (ProceduralEngine.RandomRange(minDistance, d) + pointScale.magnitude * 2f); // Make sure we're outside the sphere
                    firstPass      = true;
                }
            }
            else
            {
                CameraPosition = ray.origin + ray.direction * ProceduralEngine.RandomRange(minDistance, maxDistance);
                firstPass      = true;
            }

            // Now we need to shoot from the other side... non convex meshes and all...
            if (firstPass)
            {
                ray       = new Ray(CameraPosition, -ray.direction);
                intersect = Physics.Raycast(ray, out hit, maxDistance);

                if (intersect)
                {
                    if (hit.distance > (CameraPosition - startPoint).magnitude)
                    {
                        return(true);
                    }
                }
                else
                {
                    // This case should be rare, but can happen.
                    return(true);
                }
            }
        }

        return(false);
    }
Ejemplo n.º 4
0
    protected virtual CompositionSettings ProposeComposition()
    {
        CompositionSettings c = new CompositionSettings();

        // Random rule of thirds. TODO: based on item's biggest axis, align on X or Y
        int column = Mathf.RoundToInt(ProceduralEngine.RandomRange(1f, 3f));

        c.screenTarget = new Vector2(column * .25f + ProceduralEngine.RandomRange(-.05f, .05f), ProceduralEngine.RandomRange(.4f, .6f)); // TODO: use frustum main axis
        c.deadZoneSize = 0.01f;
        c.fieldOfView  = ProceduralEngine.RandomRange(25f, 50f);                                                                         // TODO: Find a fov that adjusts to the interest point's size
        return(c);
    }
Ejemplo n.º 5
0
    protected override void OnStart()
    {
        orbitAmplitude      = Mathf.PI / 8f;
        endRadiusPercentage = ProceduralEngine.RandomRange(.8f, 1.2f); // A small zoom in/out

        direction = ProceduralEngine.RandomRange(0f, 1f) > .5f ? 1f : -1f;

        camera.RotationDampingTime = .5f;
        camera.PositionDampingTime = .4f;
        camera.SetNoiseParameters(ProceduralEngine.RandomRange(0f, .2f), .75f);

        OnUpdateStrategy();
    }
Ejemplo n.º 6
0
    protected override void OnStart()
    {
        base.OnStart();

        camera.RotationDampingTime = .4f;
        camera.PositionDampingTime = .4f;
        camera.SetNoiseParameters(ProceduralEngine.RandomRange(.3f, .6f), ProceduralEngine.RandomRange(.5f, 1f));
        keepAttention = false; // Just pan!

        // 2D angle
        float angle = ProceduralEngine.RandomRange(0f, 2f * Mathf.PI);

        movementDirection = (GetRight() * Mathf.Cos(angle) + GetUp() * Mathf.Sin(angle)).normalized;

        speed = ProceduralEngine.Instance.EmotionEngine.GetSmoothEnergy(ProceduralEngine.Instance.CurrentTimeNormalized) / ProceduralEngine.Instance.EmotionEngine.MaxEnergy;
        speed = Mathf.Lerp(10f, 20f, speed);
    }
Ejemplo n.º 7
0
    protected ProceduralCameraStrategy BuildCameraStrategy(InterestPoint point, EmotionEvent e, float shotDuration)
    {
        List <KeyValuePair <ProceduralCameraStrategy, float> > strategies = new List <KeyValuePair <ProceduralCameraStrategy, float> >();

        float normalizedEnergy = ProceduralEngine.Instance.EmotionEngine.GetSmoothEnergy(e.timestamp) / ProceduralEngine.Instance.EmotionEngine.MaxEnergy;

        float overviewWeight = 0.2f;
        float dollyWeight    = 2f + (1f - normalizedEnergy);
        float orbitWeight    = 1f + normalizedEnergy;

        TrackChunkData structureChunk = ProceduralEngine.Instance.EmotionEngine.GetCurrentStructureData(e.timestamp);
        StructureType  structure      = ProceduralEngine.Instance.EmotionEngine.GetStructureAtTime(e.timestamp);

        switch (structure)
        {
        case StructureType.None:
            break;

        case StructureType.Sustain:
            break;

        case StructureType.Increasing:
            overviewWeight += (1f - structureChunk.GetIntensity(e.timestamp)) * 200f;
            break;

        case StructureType.Decreasing:
            overviewWeight += (1f - structureChunk.GetIntensity(e.timestamp)) * 200f;
            break;
        }

        strategies.Add(new KeyValuePair <ProceduralCameraStrategy, float>(new OverviewCameraStrategy(), overviewWeight));
        strategies.Add(new KeyValuePair <ProceduralCameraStrategy, float>(new DollyCameraStrategy(), dollyWeight));
        strategies.Add(new KeyValuePair <ProceduralCameraStrategy, float>(new OrbitCameraStrategy(), orbitWeight));

        return(ProceduralEngine.SelectRandomWeighted(strategies, x => x.Value).Key);
    }
Ejemplo n.º 8
0
    /// <summary>
    /// This method doesn't say the specific cut, but it constraints
    /// the time for searching interesting events. It is mostly
    /// dependent on current emotion.
    /// </summary>
    public CutRange EvaluateCutRangeForEvent(EmotionEvent e)
    {
        CutRange        range = new CutRange();
        EmotionSpectrum emotionAtEventTime = emotionEngine.GetSpectrum(e.timestamp);
        CoreEmotion     coreEmotion        = EmotionEngine.FindMainEmotion(emotionAtEventTime);

        // In seconds
        switch (coreEmotion)
        {
        case CoreEmotion.Joy:
            range.minCutTime = ProceduralEngine.RandomRange(1f, 2f);
            range.maxCutTime = ProceduralEngine.RandomRange(7f, 8f);
            break;

        case CoreEmotion.Trust:
            range.minCutTime = ProceduralEngine.RandomRange(2f, 5f);
            range.maxCutTime = ProceduralEngine.RandomRange(7f, 10f);
            break;

        case CoreEmotion.Fear:
            range.minCutTime = ProceduralEngine.RandomRange(1f, 2f);
            range.maxCutTime = ProceduralEngine.RandomRange(4f, 6f);
            break;

        case CoreEmotion.Surprise:
            range.minCutTime = ProceduralEngine.RandomRange(1.5f, 2f);
            range.maxCutTime = ProceduralEngine.RandomRange(2f, 4f);
            break;

        case CoreEmotion.Sadness:
            range.minCutTime = ProceduralEngine.RandomRange(1f, 1.5f);
            range.maxCutTime = ProceduralEngine.RandomRange(2f, 4f);
            break;

        case CoreEmotion.Disgust:
            range.minCutTime = ProceduralEngine.RandomRange(1f, 2f);
            range.maxCutTime = ProceduralEngine.RandomRange(3f, 4f);
            break;

        case CoreEmotion.Anger:
            range.minCutTime = ProceduralEngine.RandomRange(.3f, 1f);
            range.maxCutTime = ProceduralEngine.RandomRange(1f, 3f);
            break;

        case CoreEmotion.Anticipation:
            range.minCutTime = ProceduralEngine.RandomRange(2f, 4f);
            range.maxCutTime = ProceduralEngine.RandomRange(4f, 5f);
            break;
        }

        switch (e.type)
        {
        case EmotionEvent.EmotionEventType.Start:
            // Longer cuts when showing for first time
            range.minCutTime *= e.chunkDelimitsSegment ? 1f : .75f;
            range.maxCutTime *= e.chunkDelimitsSegment ? 1f : .75f;
            break;

        case EmotionEvent.EmotionEventType.End:
            // Longer cuts when something disappears for good
            range.minCutTime *= e.chunkDelimitsSegment ? 1.5f : 1f;
            range.maxCutTime *= e.chunkDelimitsSegment ? 1.5f : 1f;
            break;

        case EmotionEvent.EmotionEventType.LocalMaximum:
            range.minCutTime *= 1f;
            range.maxCutTime *= 1f;
            break;

        case EmotionEvent.EmotionEventType.LocalMinimum:
            range.minCutTime *= 2f;
            range.maxCutTime *= 2f;
            break;
        }

        TrackChunkData structureData = emotionEngine.GetCurrentStructureData(e.timestamp);

        if (structureData != null)
        {
            // More intense -> shorter
            float normalizedStructuralIntensity = Mathf.Pow(structureData.GetIntensity(e.timestamp), 2f);
            range.minCutTime *= 1.35f - normalizedStructuralIntensity * .5f;
            range.maxCutTime *= 1.35f - normalizedStructuralIntensity * .5f;

            // TODO: decide if we need further modifications of cut time based on type.
            // Intensity curve should cover most I think
            StructureType currentStructure = emotionEngine.GetStructureAtTime(e.timestamp);

            switch (currentStructure)
            {
            case StructureType.None:
                break;

            case StructureType.Sustain:
                break;

            case StructureType.Increasing:
                break;

            case StructureType.Decreasing:
                break;
            }
        }

        range.minCutTime = Mathf.Max(0.01f, range.minCutTime);
        range.maxCutTime = Mathf.Max(0.02f, range.maxCutTime);

        float tmp = range.minCutTime;

        range.minCutTime = Mathf.Min(range.minCutTime, range.maxCutTime);
        range.maxCutTime = Mathf.Max(tmp, range.maxCutTime);

        // Normalize times
        range.minCutTime /= ProceduralEngine.Instance.Duration;
        range.maxCutTime /= ProceduralEngine.Instance.Duration;
        return(range);
    }
Ejemplo n.º 9
0
    /// <summary>
    /// This method has two main responsibilities:
    /// - Decide when to cut
    /// - Decide what shot to take
    /// It is tied to a specific event, so that the chaining of shots is possible
    /// </summary>
    protected ShotInformation TryFindCut(EmotionEvent startEvent)
    {
        ShotInformation shot = new ShotInformation();

        shot.valid             = false;
        shot.selectedCamera    = null;
        shot.type              = TransitionType.Cut; // TODO: for now...
        shot.strategy          = null;
        shot.interestPoint     = null;
        shot.sampledStrategies = new List <KeyValuePair <ProceduralCameraStrategy, float> >();
        shot.startEvent        = startEvent;

        // Make sure we don't lag
        float timestamp = Mathf.Max(startEvent.timestamp, ProceduralEngine.Instance.CurrentTimeNormalized);

        CutRange searchRange = EvaluateCutRangeForEvent(startEvent);
        float    minT        = timestamp + searchRange.minCutTime;
        float    maxT        = timestamp + searchRange.maxCutTime * (1f + nextShotTries * .1f); // Increase search range when it fails

        List <EmotionEventGroup> searchEvents = ProceduralEngine.Instance.EventDispatcher.GetFutureEventGroups(minT, maxT);

        if (searchEvents.Count == 0)
        {
            Debug.Log("Could not find event groups... " + minT + ", " + maxT);
            return(shot);
        }

        EmotionEventGroup selectedGroup = null;
        bool structural = false;

        foreach (EmotionEventGroup g in searchEvents)
        {
            if (g.ContainsStructuralEvent())
            {
                selectedGroup = g;
                structural    = true;
            }
        }

        if (selectedGroup == null)
        {
            selectedGroup = ProceduralEngine.SelectRandomWeighted(searchEvents, x => x.GetPriority());
        }

        // We found a subset of interesting events, now we can pick something in here
        if (selectedGroup != null && selectedGroup.events.Count > 0)
        {
            EmotionEvent selectedEvent;

            if (structural)
            {
                selectedEvent = selectedGroup.GetStructuralEvent();
            }
            else
            {
                selectedEvent = ProceduralEngine.SelectRandomWeighted(selectedGroup.events, x => GetEventPriority(x));
            }

            shot.duration = (selectedEvent.timestamp - timestamp);
            shot.selectedNextEventTrigger = selectedEvent;

            // Try cutting before, but not after
            float margin        = emotionEngine.BeatDurationNormalized * .5f;
            float fuzzyDuration = shot.duration - ProceduralEngine.RandomRange(0f, margin);

            if (fuzzyDuration > searchRange.minCutTime && fuzzyDuration < searchRange.maxCutTime)
            {
                shot.duration = fuzzyDuration;
            }

            shot.valid = true;
        }

        return(shot);
    }