/// <summary> /// Creates and returns a new <see cref="VolumeStack"/> to use when you need to store /// the result of the Volume blending pass in a separate stack. /// </summary> /// <returns></returns> /// <seealso cref="VolumeStack"/> /// <seealso cref="Update(VolumeStack,Transform,LayerMask)"/> public VolumeStack CreateStack() { var stack = new VolumeStack(); stack.Reload(baseComponentTypes); return(stack); }
// Go through all listed components and lerp overridden values in the global state void OverrideData(VolumeStack stack, List <VolumeComponent> components, float interpFactor) { foreach (var component in components) { if (!component.active) { continue; } var state = stack.GetComponent(component.GetType()); component.Override(state, interpFactor); } }
// Faster version of OverrideData to force replace values in the global state void ReplaceData(VolumeStack stack, List <VolumeComponent> components) { foreach (var component in components) { var target = stack.GetComponent(component.GetType()); int count = component.parameters.Count; for (int i = 0; i < count; i++) { target.parameters[i].SetValue(component.parameters[i]); } } }
public void CheckStack(VolumeStack stack) { // The editor doesn't reload the domain when exiting play mode but still kills every // object created while in play mode, like stacks' component states var components = stack.components; if (components == null) { stack.Reload(baseComponentTypes); return; } foreach (var kvp in components) { if (kvp.Key == null || kvp.Value == null) { stack.Reload(baseComponentTypes); return; } } }
/// <summary> /// Destroy a Volume Stack /// </summary> /// <param name="stack">Volume Stack that needs to be destroyed.</param> public void DestroyStack(VolumeStack stack) { stack.Dispose(); }
/// <summary> /// Updates the Volume manager and stores the result in a custom <see cref="VolumeStack"/>. /// </summary> /// <param name="stack">The stack to store the blending result into.</param> /// <param name="trigger">A reference Transform to consider for positional Volume blending. /// </param> /// <param name="layerMask">The LayerMask that Unity uses to filter Volumes that it should consider /// for blending.</param> /// <seealso cref="VolumeStack"/> public void Update(VolumeStack stack, Transform trigger, LayerMask layerMask) { Assert.IsNotNull(stack); CheckBaseTypes(); CheckStack(stack); // Start by resetting the global state to default values ReplaceData(stack, m_ComponentsDefaultState); bool onlyGlobal = trigger == null; var triggerPos = onlyGlobal ? Vector3.zero : trigger.position; // Sort the cached volume list(s) for the given layer mask if needed and return it var volumes = GrabVolumes(layerMask); Camera camera = null; // Behavior should be fine even if camera is null if (!onlyGlobal) { trigger.TryGetComponent <Camera>(out camera); } #if UNITY_EDITOR // requested or prefab isolation mode. bool needIsolation = needIsolationFilteredByRenderer || (UnityEditor.SceneManagement.StageUtility.GetCurrentStageHandle() != UnityEditor.SceneManagement.StageUtility.GetMainStageHandle()); #endif // Traverse all volumes foreach (var volume in volumes) { #if UNITY_EDITOR // Skip volumes that aren't in the scene currently displayed in the scene view if (needIsolation && !IsVolumeRenderedByCamera(volume, camera)) { continue; } #endif // Skip disabled volumes and volumes without any data or weight if (!volume.enabled || volume.profileRef == null || volume.weight <= 0f) { continue; } // Global volumes always have influence if (volume.isGlobal) { OverrideData(stack, volume.profileRef.components, Mathf.Clamp01(volume.weight)); continue; } if (onlyGlobal) { continue; } // If volume isn't global and has no collider, skip it as it's useless var colliders = m_TempColliders; volume.GetComponents(colliders); if (colliders.Count == 0) { continue; } // Find closest distance to volume, 0 means it's inside it float closestDistanceSqr = float.PositiveInfinity; foreach (var collider in colliders) { if (!collider.enabled) { continue; } var closestPoint = collider.ClosestPoint(triggerPos); var d = (closestPoint - triggerPos).sqrMagnitude; if (d < closestDistanceSqr) { closestDistanceSqr = d; } } colliders.Clear(); float blendDistSqr = volume.blendDistance * volume.blendDistance; // Volume has no influence, ignore it // Note: Volume doesn't do anything when `closestDistanceSqr = blendDistSqr` but we // can't use a >= comparison as blendDistSqr could be set to 0 in which case // volume would have total influence if (closestDistanceSqr > blendDistSqr) { continue; } // Volume has influence float interpFactor = 1f; if (blendDistSqr > 0f) { interpFactor = 1f - (closestDistanceSqr / blendDistSqr); } // No need to clamp01 the interpolation factor as it'll always be in [0;1[ range OverrideData(stack, volume.profileRef.components, interpFactor * Mathf.Clamp01(volume.weight)); } }
// Update a specific stack - can be used to manage your own stack and store it for later use public void Update(VolumeStack stack, Transform trigger, LayerMask layerMask) { Assert.IsNotNull(stack); CheckBaseTypes(); CheckStack(stack); // Start by resetting the global state to default values ReplaceData(stack, m_ComponentsDefaultState); bool onlyGlobal = trigger == null; var triggerPos = onlyGlobal ? Vector3.zero : trigger.position; // Sort the cached volume list(s) for the given layer mask if needed and return it var volumes = GrabVolumes(layerMask); // Traverse all volumes foreach (var volume in volumes) { // Skip disabled volumes and volumes without any data or weight if (!volume.enabled || volume.profileRef == null || volume.weight <= 0f) { continue; } // Global volumes always have influence if (volume.isGlobal) { OverrideData(stack, volume.profileRef.components, Mathf.Clamp01(volume.weight)); continue; } if (onlyGlobal) { continue; } // If volume isn't global and has no collider, skip it as it's useless var colliders = m_TempColliders; volume.GetComponents(colliders); if (colliders.Count == 0) { continue; } // Find closest distance to volume, 0 means it's inside it float closestDistanceSqr = float.PositiveInfinity; foreach (var collider in colliders) { if (!collider.enabled) { continue; } var closestPoint = collider.ClosestPoint(triggerPos); var d = (closestPoint - triggerPos).sqrMagnitude; if (d < closestDistanceSqr) { closestDistanceSqr = d; } } colliders.Clear(); float blendDistSqr = volume.blendDistance * volume.blendDistance; // Volume has no influence, ignore it // Note: Volume doesn't do anything when `closestDistanceSqr = blendDistSqr` but we // can't use a >= comparison as blendDistSqr could be set to 0 in which case // volume would have total influence if (closestDistanceSqr > blendDistSqr) { continue; } // Volume has influence float interpFactor = 1f; if (blendDistSqr > 0f) { interpFactor = 1f - (closestDistanceSqr / blendDistSqr); } // No need to clamp01 the interpolation factor as it'll always be in [0;1[ range OverrideData(stack, volume.profileRef.components, interpFactor * Mathf.Clamp01(volume.weight)); } }