备份CatanBuilding瘦身独立工程

This commit is contained in:
JSD\13999
2026-05-26 16:15:54 +08:00
commit 2d0e6a61b7
12001 changed files with 2431925 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
//Stylized Water 2
//Staggart Creations (http://staggart.xyz)
//Copyright protected under Unity Asset Store EULA
using System.Collections.Generic;
using UnityEngine;
namespace StylizedWater2
{
[ExecuteInEditMode]
[AddComponentMenu("Stylized Water 2/Align Transform To Waves")]
public class AlignToWaves : MonoBehaviour
{
[Tooltip("This reference is required to grab the wave distance and height values")]
public WaterObject waterObject;
[Tooltip("Automatically find the Water Object below of above the Transform's position. This is slower than assigning a specific Water Object directly.")]
public bool autoFind;
[Tooltip("Only enable if the material's wave parameters are being changed in realtime, this has some performance overhead.\n\nIn edit-mode, the wave parameters are always fetched, so changes are directly visible")]
public bool dynamicMaterial;
public enum WaterLevelSource
{
FixedValue,
WaterObject
}
[Tooltip("Configure what should be used to set the base water level. Relative wave height is added to this value")]
public WaterLevelSource waterLevelSource = WaterLevelSource.WaterObject;
public float waterLevel;
[Tooltip("You can assign a child mesh object here. When assigned, the sample points will rotate/scale with the transform, instead of transform the component is attached to.")]
public Transform childTransform;
public float heightOffset;
[Min(0)]
[Tooltip("Controls how strongly the transform should rotate to align with the wave curvature")]
public float rollAmount = 0.1f;
public List<Vector3> samples = new List<Vector3>();
private Vector3 normal;
private float height;
private float m_waterLevel = 0f;
/// <summary>
/// Global toggle to disable the animations. This is used to temporarily disable all instances when editing a prefab, or sample positions in the editor
/// </summary>
public static bool Disable;
#if UNITY_EDITOR
public static bool EnableInEditor
{
get { return UnityEditor.EditorPrefs.GetBool("SWS2_BUOYANCY_EDITOR_ENABLED", true); }
set { UnityEditor.EditorPrefs.SetBool("SWS2_BUOYANCY_EDITOR_ENABLED", value); }
}
#endif
#if UNITY_EDITOR
private void OnEnable()
{
UnityEditor.EditorApplication.update += FixedUpdate;
}
private void Reset()
{
//Auto-assign water object if there is only one
if (waterObject == null && WaterObject.Instances.Count > 0)
{
waterObject = WaterObject.Instances[0];
UnityEditor.EditorUtility.SetDirty(this);
}
}
private void OnDisable()
{
UnityEditor.EditorApplication.update -= FixedUpdate;
}
#endif
public void FixedUpdate()
{
if (!this || !this.enabled || Disable) return;
#if UNITY_EDITOR
if (!EnableInEditor && Application.isPlaying == false) return;
#endif
if(autoFind) waterObject = WaterObject.Find(this.transform.position, false);
if (!waterObject || !waterObject.material) return;
m_waterLevel = waterObject && waterLevelSource == WaterLevelSource.WaterObject? waterObject.transform.position.y : waterLevel;
normal = Vector3.up;
height = 0f;
if (samples.Count == 0)
{
height = Buoyancy.SampleWaves(this.transform.position, waterObject.material, m_waterLevel, rollAmount, dynamicMaterial, out normal);
}
else
{
Vector3 avgNormal = Vector3.zero;
for (int i = 0; i < samples.Count; i++)
{
height += Buoyancy.SampleWaves(ConvertToWorldSpace(samples[i]), waterObject.material, m_waterLevel, rollAmount, dynamicMaterial, out normal);
avgNormal += normal;
}
height /= samples.Count;
normal = (avgNormal / samples.Count).normalized;
}
height += heightOffset;
ApplyTransform();
}
private void ApplyTransform()
{
if(rollAmount > 0) this.transform.up = normal;
var position = this.transform.position;
this.transform.position = new Vector3(position.x, height, position.z);
}
public Vector3 ConvertToWorldSpace(Vector3 position)
{
if (childTransform) return childTransform.TransformPoint(position);
return this.transform.TransformPoint(position);
}
public Vector3 ConvertToLocalSpace(Vector3 position)
{
if (childTransform) return childTransform.InverseTransformPoint(position);
return this.transform.InverseTransformPoint(position);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 75215afeb99f1fc48aacad291a415f32
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 148978298399363526, guid: 0000000000000000d000000000000000, type: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,297 @@
//Stylized Water 2
//Staggart Creations (http://staggart.xyz)
//Copyright protected under Unity Asset Store EULA
//#undef MATHEMATICS
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
#if MATHEMATICS
using Unity.Mathematics;
using static Unity.Mathematics.math;
using Vector4 = Unity.Mathematics.float4;
using Vector3 = Unity.Mathematics.float3;
using Vector2 = Unity.Mathematics.float2;
#endif
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace StylizedWater2
{
public static partial class Buoyancy
{
private static WaveParameters waveParameters = new WaveParameters();
private static Material lastMaterial;
private static readonly int TimeParametersID = Shader.PropertyToID("_TimeParameters");
private static void GetMaterialParameters(Material mat)
{
waveParameters.Update(mat);
}
//Returns the same value as _TimeParameters.x
private static float _TimeParameters
{
get
{
if (WaterObject.CustomTime >= 0) return WaterObject.CustomTime;
#if UNITY_EDITOR
return Application.isPlaying ? Time.time : Shader.GetGlobalVector(TimeParametersID).x;
#else
return Time.time;
#endif
}
}
[Obsolete("Set the static 'WaterObject.CustomTime' parameter instead.", false)]
public static void SetCustomTime(float value)
{
WaterObject.CustomTime = value;
}
private static Vector4 sine;
private static Vector4 cosine;
private static Vector4 dotABCD;
private static Vector4 AB;
private static Vector4 CD;
private static Vector4 direction1;
private static Vector4 direction2;
private static Vector4 TIME;
private static Vector2 planarPosition;
private static Vector4 amp = new Vector4(0.3f, 0.35f, 0.25f, 0.25f);
private static Vector4 freq = new Vector4(1.3f, 1.35f, 1.25f, 1.25f);
private static Vector4 speed = new Vector4(1.2f, 1.375f, 1.1f, 1);
private static Vector4 dir1 = new Vector4(0.3f, 0.85f, 0.85f, 0.25f);
private static Vector4 dir2 = new Vector4(0.1f, 0.9f, -0.5f, -0.5f);
private static Vector4 steepness = new Vector4(12f,12f,12f,12f);
//Real frequency value per wave layer
private static Vector4 frequency;
//Output
private static Vector3 offsets;
/// <summary>
/// Returns a position in world-space, where a ray cast from the origin in the direction hits the (flat) water level height
/// </summary>
/// <param name="origin"></param>
/// <param name="direction"></param>
/// <param name="waterLevel">Water level height in world-space</param>
/// <returns></returns>
public static Vector3 FindWaterLevelIntersection(Vector3 origin, Vector3 direction, float waterLevel)
{
#if MATHEMATICS
float upDot = dot(direction, UnityEngine.Vector3.up);
float angle = (Mathf.Acos(upDot) * 180f) / Mathf.PI;
float depth = waterLevel - origin.y;
//Distance from origin to water level along direction
float hypotenuse = depth / cos(Mathf.Deg2Rad * angle);
return origin + (direction * hypotenuse);
#else
return Vector3.zero;
#endif
}
/// <summary>
/// Faux-raycast against the water surface
/// </summary>
/// <param name="waterObject">Water object component, used to get the water material and level (height)</param>
/// <param name="origin">Ray origin</param>
/// <param name="direction">Ray direction</param>
/// <param name="dynamicMaterial">If true, the material's wave parameters will be re-fetched with every function call</param>
/// <param name="hit">Reference to a RaycastHit, hit point and normal will be set</param>
public static void Raycast(WaterObject waterObject, Vector3 origin, Vector3 direction, bool dynamicMaterial, out RaycastHit hit)
{
Raycast(waterObject.material, waterObject.transform.position.y, origin, direction, dynamicMaterial, out hit);
}
private static RaycastHit hit = new RaycastHit();
/// <summary>
/// Faux-raycast against the water surface
/// </summary>
/// <param name="waterMat">Material using StylizedWater2 shader</param>
/// <param name="waterLevel">Height of the reference water plane.</param>
/// <param name="origin">Ray origin</param>
/// <param name="direction">Ray direction</param>
/// <param name="dynamicMaterial">If true, the material's wave parameters will be re-fetched with every function call</param>
/// <param name="hit">Reference to a RaycastHit, hit point and normal will be set</param>
public static void Raycast(Material waterMat, float waterLevel, Vector3 origin, Vector3 direction, bool dynamicMaterial, out RaycastHit hit)
{
Vector3 samplePos = FindWaterLevelIntersection(origin, direction, waterLevel);
float waveHeight = SampleWaves(samplePos, waterMat, waterLevel, 1f, dynamicMaterial, out var normal);
samplePos.y = waveHeight;
hit = Buoyancy.hit;
hit.normal = normal;
hit.point = samplePos;
}
/// <summary>
/// Given a position in world-space, returns the wave height and normal
/// </summary>
/// <param name="position">Sample position in world-space</param>
/// <param name="waterObject">Water object component, used to get the water material and level (height)</param>
/// <param name="rollStrength">Multiplier for the the normal strength</param>
/// <param name="dynamicMaterial">If true, the material's wave parameters will be re-fetched with every function call</param>
/// <param name="normal">Output upwards normal vector, perpendicular to the wave</param>
/// <returns>Wave height, in world-space.</returns>
public static float SampleWaves(UnityEngine.Vector3 position, WaterObject waterObject, float rollStrength, bool dynamicMaterial, out UnityEngine.Vector3 normal)
{
return SampleWaves(position, waterObject.material, waterObject.transform.position.y, rollStrength, dynamicMaterial, out normal);
}
private static void RecalculateParameters()
{
#if MATHEMATICS
direction1 = dir1 * waveParameters.direction;
direction2 = dir2 * waveParameters.direction;
frequency = freq * (1-waveParameters.distance) * 3f;
AB.x = steepness.x * waveParameters.steepness * direction1.x * amp.x;
AB.y = steepness.x * waveParameters.steepness * direction1.y * amp.x;
AB.z = steepness.x * waveParameters.steepness * direction1.z * amp.y;
AB.w = steepness.x * waveParameters.steepness * direction1.w * amp.y;
CD.x = steepness.z * waveParameters.steepness * direction2.x * amp.z;
CD.y = steepness.z * waveParameters.steepness * direction2.y * amp.z;
CD.z = steepness.w * waveParameters.steepness * direction2.z * amp.w;
CD.w = steepness.w * waveParameters.steepness * direction2.w * amp.w;
#endif
}
private static void SampleWaves(UnityEngine.Vector3 position, Material waterMat, float waterLevel, float rollStrength, bool dynamicMaterial, out UnityEngine.Vector3 offset, out UnityEngine.Vector3 normal)
{
Profiler.BeginSample("Buoyancy sampling");
#if MATHEMATICS
//If not desired to re-fetch the material properties every call, at least fetch them if the input material changed (since this is a static function)
//In edit-mode, always do this as materials are most likely modified then
if(!dynamicMaterial && Application.isPlaying)
{
//Fetch the material's wave parameters, so the exact calculations can be mirrored
if (lastMaterial == null || lastMaterial.Equals(waterMat) == false)
{
#if SWS_DEV
Debug.Log("SampleWaves: water material changed, re-fetching parameters");
#endif
GetMaterialParameters(waterMat);
lastMaterial = waterMat;
}
}
else
{
GetMaterialParameters(waterMat);
}
TIME = (_TimeParameters * -waveParameters.animationSpeed * waveParameters.speed * speed);
RecalculateParameters();
offsets = Vector3.zero;
planarPosition.x = position.x - WaterObject.PositionOffset.x;
planarPosition.y = position.z - WaterObject.PositionOffset.z;
for (int i = 0; i <= waveParameters.count; i++)
{
var t = 1f+((float)i / (float)waveParameters.count);
frequency *= t;
#if MATHEMATICS
dotABCD.x = dot(direction1.xy, planarPosition) * frequency.x;
dotABCD.y = dot(direction1.zw, planarPosition) * frequency.y;
dotABCD.z = dot(direction2.xy, planarPosition) * frequency.z;
dotABCD.w = dot(direction2.zw, planarPosition) * frequency.w;
#endif
sine.x = sin(dotABCD.x + TIME.x);
sine.y = sin(dotABCD.y + TIME.y);
sine.z = sin(dotABCD.z + TIME.z);
sine.w = sin(dotABCD.w + TIME.w);
cosine.x = cos(dotABCD.x + TIME.x);
cosine.y = cos(dotABCD.y + TIME.y);
cosine.z = cos(dotABCD.z + TIME.z);
cosine.w = cos(dotABCD.w + TIME.w);
offsets.x += dot(cosine, new Vector4(AB.x, AB.z, CD.x, CD.z));
offsets.y += dot(sine, amp);
offsets.z += dot(cosine, new Vector4(AB.y, AB.w, CD.y, CD.w));
}
rollStrength *= lerp(0.001f, 0.1f, waveParameters.steepness);
normal.x = -offsets.x * rollStrength * waveParameters.height;
normal.y = 2f;
normal.z = -offsets.z * rollStrength * waveParameters.height;
normal = normalize(normal);
//Average height
offsets.y /= waveParameters.count;
offsets.y = (offsets.y* waveParameters.height) + waterLevel;
offset = offsets;
#else
offset = Vector3.zero;
normal = Vector3.zero;
#endif
Profiler.EndSample();
}
private static UnityEngine.Vector3 m_offset;
/// <summary>
/// Given a position in world-space, returns the wave height and normal
/// </summary>
/// <param name="position">Sample position in world-space</param>
/// <param name="waterMat">Material using StylizedWater2 shader</param>
/// <param name="waterLevel">Height of the reference water plane.</param>
/// <param name="rollStrength">Multiplier for the the normal strength</param>
/// <param name="dynamicMaterial">If true, the material's wave parameters will be re-fetched with every function call</param>
/// <param name="normal">Output upwards normal vector, perpendicular to the wave</param>
/// <returns>Wave height, in world-space.</returns>
public static float SampleWaves(UnityEngine.Vector3 position, Material waterMat, float waterLevel, float rollStrength, bool dynamicMaterial, out UnityEngine.Vector3 normal)
{
SampleWaves(position, waterMat, waterLevel, rollStrength, dynamicMaterial, out m_offset, out normal);
return m_offset.y;
}
/// <summary>
/// Checks if the position is below the maximum possible wave height. Can be used as a fast broad-phase check, before actually using the more expensive SampleWaves function
/// </summary>
/// <param name="position"></param>
/// <param name="waterObject"></param>
/// <returns></returns>
public static bool CanTouchWater(Vector3 position, WaterObject waterObject)
{
if (!waterObject) return false;
return position.y < (waterObject.transform.position.y + WaveParameters.GetMaxWaveHeight(waterObject.material));
}
/// <summary>
/// Checks if the position is below the maximum possible wave height. Can be used as a fast broad-phase check, before actually using the more expensive SampleWaves function
/// </summary>
public static bool CanTouchWater(Vector3 position, Material waterMaterial, float waterLevel)
{
return position.y < (waterLevel + WaveParameters.GetMaxWaveHeight(waterMaterial));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c7991692bea09cb4f9aae01639d1d1e6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,649 @@
//Stylized Water 2
//Staggart Creations (http://staggart.xyz)
//Copyright protected under Unity Asset Store EULA
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using UnityEngine;
using UnityEngine.Rendering;
using Debug = UnityEngine.Debug;
#if URP
using UnityEngine.Rendering.Universal;
#if !UNITY_2021_2_OR_NEWER
using UniversalRendererData = UnityEngine.Rendering.Universal.ForwardRendererData;
#endif
using ScriptableRendererFeature = UnityEngine.Rendering.Universal.ScriptableRendererFeature;
#endif
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace StylizedWater2
{
//Stay awesome Unity, locking everything behind internal UI code just makes things convoluted.
public static class PipelineUtilities
{
private const string renderDataListFieldName = "m_RendererDataList";
private const string renderFeaturesListFieldName = "m_RendererFeatures";
private const string defaultRendererIndexFieldName = "m_DefaultRendererIndex";
#if URP
public static ScriptableRendererData[] GetRenderDataList(UniversalRenderPipelineAsset asset)
{
FieldInfo renderDataListField = typeof(UniversalRenderPipelineAsset).GetField(renderDataListFieldName, BindingFlags.NonPublic | BindingFlags.Instance);
if (renderDataListField != null)
{
return (ScriptableRendererData[])renderDataListField.GetValue(asset);
}
throw new Exception("Reflection failed on field \"m_RendererDataList\" from class \"UniversalRenderPipelineAsset\". URP API likely changed");
}
public static void RefreshRendererList()
{
if (UniversalRenderPipeline.asset == null)
{
Debug.LogError("No pipeline is active, do not display UI that uses this function if it isn't!");
}
ScriptableRendererData[] m_rendererDataList = GetRenderDataList(UniversalRenderPipeline.asset);
//Display names
_rendererDisplayList = new GUIContent[m_rendererDataList.Length+1];
int defaultIndex = GetDefaultRendererIndex(UniversalRenderPipeline.asset);
_rendererDisplayList[0] = new GUIContent($"Default ({(m_rendererDataList[defaultIndex].name)})");
for (int i = 1; i < _rendererDisplayList.Length; i++)
{
if (m_rendererDataList[i - 1] != null)
{
_rendererDisplayList[i] = new GUIContent($"{(i - 1).ToString()}: {(m_rendererDataList[i - 1]).name}");
}
else
{
_rendererDisplayList[i] = new GUIContent("(Missing)");
}
}
//Indices
_rendererIndexList = new int[m_rendererDataList.Length+1];
for (int i = 0; i < _rendererIndexList.Length; i++)
{
_rendererIndexList[i] = i-1;
}
}
private static GUIContent[] _rendererDisplayList;
public static GUIContent[] rendererDisplayList
{
get
{
if (_rendererDisplayList == null) RefreshRendererList();
return _rendererDisplayList;
}
}
private static int[] _rendererIndexList;
public static int[] rendererIndexList
{
get
{
if (_rendererIndexList == null) RefreshRendererList();
return _rendererIndexList;
}
}
/// <summary>
/// Given a renderer index, validates if there is actually a renderer at the index. Otherwise returns the index of the default renderer.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public static int ValidateRenderer(int index)
{
if (UniversalRenderPipeline.asset)
{
int defaultRendererIndex = GetDefaultRendererIndex(UniversalRenderPipeline.asset);
ScriptableRendererData[] m_rendererDataList = GetRenderDataList(UniversalRenderPipeline.asset);
//-1 is used to indicate the default renderer
if (index == -1) index = defaultRendererIndex;
//Check if any renderer exists at the current index
if (!(index < m_rendererDataList.Length && m_rendererDataList[index] != null))
{
Debug.LogWarning($"Renderer at <b>index {index.ToString()}</b> is missing, falling back to Default Renderer. <b>{m_rendererDataList[defaultRendererIndex].name}</b>", UniversalRenderPipeline.asset);
return defaultRendererIndex;
}
else
{
//Valid
return index;
}
}
else
{
Debug.LogError("No Universal Render Pipeline is currently active.");
return 0;
}
}
/// <summary>
/// Checks if a ForwardRenderer has been assigned to the pipeline asset
/// </summary>
/// <param name="renderer"></param>
public static bool IsRendererAdded(ScriptableRendererData renderer)
{
if (UniversalRenderPipeline.asset)
{
ScriptableRendererData[] m_rendererDataList = GetRenderDataList(UniversalRenderPipeline.asset);
bool isPresent = false;
for (int i = 0; i < m_rendererDataList.Length; i++)
{
if (m_rendererDataList[i] == renderer) isPresent = true;
}
return isPresent;
}
else
{
Debug.LogError("No Universal Render Pipeline is currently active.");
return false;
}
}
/// <summary>
/// Adds a ForwardRenderer to the pipeline asset in use
/// </summary>
/// <param name="renderer"></param>
private static int AddRendererToPipeline(ScriptableRendererData renderer)
{
if (renderer == null) return -1;
if (UniversalRenderPipeline.asset)
{
ScriptableRendererData[] m_rendererDataList = GetRenderDataList(UniversalRenderPipeline.asset);
List<ScriptableRendererData> rendererDataList = new List<ScriptableRendererData>();
for (int i = 0; i < m_rendererDataList.Length; i++)
{
rendererDataList.Add(m_rendererDataList[i]);
}
rendererDataList.Add(renderer);
int index = rendererDataList.Count-1;
typeof(UniversalRenderPipelineAsset).GetField(renderDataListFieldName, BindingFlags.NonPublic | BindingFlags.Instance).SetValue(UniversalRenderPipeline.asset, rendererDataList.ToArray());
#if UNITY_EDITOR
EditorUtility.SetDirty(UniversalRenderPipeline.asset);
#endif
RefreshRendererList();
return index;
}
else
{
Debug.LogError("No Universal Render Pipeline is currently active.");
}
return -1;
}
private static int GetDefaultRendererIndex(UniversalRenderPipelineAsset asset)
{
FieldInfo fieldInfo = typeof(UniversalRenderPipelineAsset).GetField(defaultRendererIndexFieldName, BindingFlags.NonPublic | BindingFlags.Instance);
if (fieldInfo == null) {throw new Exception($"Reflection failed on the field named \"{defaultRendererIndexFieldName}\". It may have changed in the current Unity version");}
return (int)fieldInfo.GetValue(asset);
}
/// <summary>
/// Gets the renderer from the current pipeline asset that's marked as default
/// </summary>
/// <returns></returns>
public static ScriptableRendererData GetDefaultRenderer(UniversalRenderPipelineAsset asset = null)
{
if (asset == null) asset = UniversalRenderPipeline.asset;
if (asset)
{
ScriptableRendererData[] rendererDataList = GetRenderDataList(asset);
int defaultRendererIndex = GetDefaultRendererIndex(asset);
return rendererDataList[defaultRendererIndex];
}
throw new Exception("No Universal Render Pipeline is currently active.");
}
/// <summary>
/// Editor only! Checks if the given render feature is missing on any renderers. Displays a pop up if that is the case, with the option to add it
/// </summary>
/// <param name="name">Descriptive name for the render feature</param>
/// <typeparam name="T">Render feature type</typeparam>
[Conditional("UNITY_EDITOR")]
public static void ValidateRenderFeatureSetup<T>(string name)
{
if (Application.isPlaying == false)
{
if (RenderFeatureMissing<T>(out ScriptableRendererData[] renderers))
{
#if UNITY_EDITOR
string[] rendererNames = new string[renderers.Length];
for (int i = 0; i < rendererNames.Length; i++)
{
rendererNames[i] = "• " + renderers[i].name;
}
if (EditorUtility.DisplayDialog($"Stylized Water 2",
$"The {name} render feature hasn't been added to the following renderers:\n\n" +
System.String.Join(System.Environment.NewLine, rendererNames) +
$"\n\nThis is required for rendering to take effect", "Setup", "Ignore"))
{
SetupRenderFeature<T>(name:$"Stylized Water 2: {name}");
}
#endif
}
}
}
/// <summary>
/// Retrieves the given render feature from the default renderer
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static ScriptableRendererFeature GetRenderFeature<T>(ScriptableRendererData renderer = null)
{
if(renderer == null) renderer = GetDefaultRenderer();
foreach (ScriptableRendererFeature feature in renderer.rendererFeatures)
{
if (feature && feature.GetType() == typeof(T)) return feature;
}
return null;
}
/// <summary>
/// Checks if a ScriptableRendererFeature is added to the default renderer
/// </summary>
/// <param name="addIfMissing"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static bool RenderFeatureAdded<T>(ScriptableRendererData renderer = null)
{
if(renderer == null) renderer = GetDefaultRenderer();
foreach (ScriptableRendererFeature feature in renderer.rendererFeatures)
{
if(feature == null) continue;
if (feature.GetType() == typeof(T))
{
return true;
}
}
return false;
}
/// <summary>
/// Checks if the given render feature is missing on any configured renderers
/// </summary>
/// <param name="renderers"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static bool RenderFeatureMissing<T>(out ScriptableRendererData[] renderers)
{
List<ScriptableRendererData> unconfigured = new List<ScriptableRendererData>();
foreach (var asset in GraphicsSettings.allConfiguredRenderPipelines)
{
ScriptableRendererData renderer = GetDefaultRenderer((UniversalRenderPipelineAsset)asset);
if(RenderFeatureAdded<T>(renderer) == false)
{
unconfigured.Add(renderer);
}
}
renderers = unconfigured.ToArray();
return unconfigured.Count > 0;
}
/// <summary>
/// Adds a render feature of a given type to all default renderers
/// </summary>
/// <param name="name"></param>
/// <typeparam name="T"></typeparam>
public static void SetupRenderFeature<T>(string name = "")
{
foreach (var asset in GraphicsSettings.allConfiguredRenderPipelines)
{
ScriptableRendererData renderer = GetDefaultRenderer((UniversalRenderPipelineAsset)asset);
if(RenderFeatureAdded<T>(renderer) == false) AddRenderFeature<T>(renderer, name);
}
}
/// <summary>
/// Adds a ScriptableRendererFeature to the renderer (default is none is supplied)
/// </summary>
/// <param name="renderer"></param>
/// <typeparam name="T"></typeparam>
public static ScriptableRendererFeature AddRenderFeature<T>(ScriptableRendererData renderer = null, string name = "")
{
if (renderer == null) renderer = GetDefaultRenderer();
ScriptableRendererFeature feature = (ScriptableRendererFeature)ScriptableRendererFeature.CreateInstance(typeof(T).ToString());
feature.name = name == string.Empty ? typeof(T).ToString() : name;
//Add component https://github.com/Unity-Technologies/Graphics/blob/d0473769091ff202422ad13b7b764c7b6a7ef0be/com.unity.render-pipelines.universal/Editor/ScriptableRendererDataEditor.cs#L180
#if UNITY_EDITOR
AssetDatabase.AddObjectToAsset(feature, renderer);
AssetDatabase.TryGetGUIDAndLocalFileIdentifier(feature, out var guid, out long localId);
#endif
//Get feature list
FieldInfo renderFeaturesInfo = typeof(ScriptableRendererData).GetField(renderFeaturesListFieldName, BindingFlags.Instance | BindingFlags.NonPublic);
List<ScriptableRendererFeature> m_RendererFeatures = (List<ScriptableRendererFeature>)renderFeaturesInfo.GetValue(renderer);
//Modify and set list
m_RendererFeatures.Add(feature);
renderFeaturesInfo.SetValue(renderer, m_RendererFeatures);
//Onvalidate will call ValidateRendererFeatures and update m_RendererPassMap
MethodInfo validateInfo = typeof(ScriptableRendererData).GetMethod("OnValidate", BindingFlags.Instance | BindingFlags.NonPublic);
validateInfo.Invoke(renderer, null);
#if UNITY_EDITOR
EditorUtility.SetDirty(renderer);
AssetDatabase.SaveAssets();
#endif
Debug.Log("<b>" + feature.name + "</b> was added to the <i>" + renderer.name + "</i> renderer");
return feature;
}
public static bool IsRenderFeatureEnabled<T>(ScriptableRendererData forwardRenderer = null, bool autoEnable = false)
{
if (!UniversalRenderPipeline.asset) return true;
#if UNITY_2020_1_OR_NEWER //Older version doesn't have the isActive property
if (forwardRenderer == null) forwardRenderer = GetDefaultRenderer();
FieldInfo renderFeaturesInfo = typeof(ScriptableRendererData).GetField(renderFeaturesListFieldName, BindingFlags.Instance | BindingFlags.NonPublic);
List<ScriptableRendererFeature> m_RendererFeatures = (List<ScriptableRendererFeature>)renderFeaturesInfo.GetValue(forwardRenderer);
foreach (ScriptableRendererFeature feature in m_RendererFeatures)
{
if (feature && feature.GetType() == typeof(T))
{
if (feature.isActive == false && autoEnable)
{
feature.SetActive(true);
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(forwardRenderer);
#endif
}
return feature.isActive;
}
}
#endif
//Fallback, if it is not even in the list
return true;
}
public static void ToggleRenderFeature<T>(bool state)
{
#if UNITY_2020_1_OR_NEWER
ScriptableRendererData forwardRenderer = GetDefaultRenderer();
foreach (ScriptableRendererFeature feature in forwardRenderer.rendererFeatures)
{
if (feature && feature.GetType() == typeof(T)) feature.SetActive(state);
}
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(forwardRenderer);
#endif
#endif
}
public static void CreateAndAssignNewRenderer(out int index, out string path)
{
ScriptableRendererData defaultRenderer = GetDefaultRenderer();
path = string.Empty;
#if UNITY_EDITOR
//Save next to default renderer
path = AssetDatabase.GetAssetPath(defaultRenderer);
path = path.Replace(defaultRenderer.name + ".asset", string.Empty);
#endif
ScriptableRendererData r = CreateEmptyRenderer("Planar Reflections Renderer", path);
#if UNITY_EDITOR
path = AssetDatabase.GetAssetPath(r);
#endif
index = AddRendererToPipeline(r);
//Debug.Log("Created new renderer with index " + index);
}
/// <summary>
/// Create an empty renderer, without any render features, but otherwise suitable for camera rendering
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static UniversalRendererData CreateEmptyRenderer(string name = "", string folder = "")
{
ScriptableRendererData defaultRenderer = GetDefaultRenderer();
UniversalRendererData rendererData = ScriptableObject.CreateInstance<UniversalRendererData>();
#if UNITY_EDITOR
//Save asset to disk, and load
if (folder != string.Empty)
{
string path = $"{folder}{name}.asset";
AssetDatabase.CreateAsset(rendererData, path);
AssetDatabase.ImportAsset(path);
rendererData = AssetDatabase.LoadAssetAtPath<UniversalRendererData>(path);
}
#endif
UniversalRendererData r = (UniversalRendererData)defaultRenderer;
#if UNITY_EDITOR
//Copy all fields. This should include the shader references, and post processing + XR data. Failing to do so results in nullrefs on these resources when using the renderer.
EditorUtility.CopySerialized(r, rendererData);
#endif
//After copying, apply these unique changes
rendererData.name = name; //Name must match file name
rendererData.rendererFeatures.Clear();
/* CopySerialized function accounts for any public fields
rendererData.shaders = r.shaders;
rendererData.postProcessData = r.postProcessData;
#if UNITY_2021_2_OR_NEWER
rendererData.debugShaders = r.debugShaders;
rendererData.xrSystemData = r.xrSystemData;
#endif
*/
return rendererData;
}
public static void RemoveRendererFromPipeline(ScriptableRendererData renderer)
{
if (renderer == null) return;
if (UniversalRenderPipeline.asset)
{
BindingFlags bindings = BindingFlags.NonPublic | BindingFlags.Instance;
ScriptableRendererData[] m_rendererDataList = GetRenderDataList(UniversalRenderPipeline.asset);
List<ScriptableRendererData> rendererDataList = new List<ScriptableRendererData>(m_rendererDataList);
if (rendererDataList.Contains(renderer))
{
rendererDataList.Remove(renderer);
typeof(UniversalRenderPipelineAsset).GetField(renderDataListFieldName, bindings).SetValue(UniversalRenderPipeline.asset, rendererDataList.ToArray());
#if UNITY_EDITOR
EditorUtility.SetDirty(UniversalRenderPipeline.asset);
AssetDatabase.SaveAssets();
#endif
}
}
else
{
Debug.LogError("No Universal Render Pipeline is currently active.");
}
}
/// <summary>
/// Sets the renderer index of the related forward renderer
/// </summary>
/// <param name="camData"></param>
/// <param name="renderer"></param>
public static void AssignRendererToCamera(UniversalAdditionalCameraData camData, ScriptableRendererData renderer)
{
if (UniversalRenderPipeline.asset)
{
if (renderer)
{
ScriptableRendererData[] rendererDataList = GetRenderDataList(UniversalRenderPipeline.asset);
for (int i = 0; i < rendererDataList.Length; i++)
{
if (rendererDataList[i] == renderer) camData.SetRenderer(i);
}
}
}
else
{
Debug.LogError("No Universal Render Pipeline is currently active.");
}
}
public static bool IsDepthTextureOptionDisabledAnywhere()
{
bool state = false;
for (int i = 0; i < GraphicsSettings.allConfiguredRenderPipelines.Length; i++)
{
if(GraphicsSettings.allConfiguredRenderPipelines[i].GetType() != typeof(UniversalRenderPipelineAsset)) continue;
UniversalRenderPipelineAsset pipeline = (UniversalRenderPipelineAsset)GraphicsSettings.allConfiguredRenderPipelines[i];
state |= (pipeline.supportsCameraDepthTexture == false);
}
return state;
}
public static void SetDepthTextureOnAllAssets(bool state)
{
for (int i = 0; i < GraphicsSettings.allConfiguredRenderPipelines.Length; i++)
{
if(GraphicsSettings.allConfiguredRenderPipelines[i].GetType() != typeof(UniversalRenderPipelineAsset)) continue;
UniversalRenderPipelineAsset pipeline = (UniversalRenderPipelineAsset)GraphicsSettings.allConfiguredRenderPipelines[i];
#if UNITY_EDITOR
if(pipeline.supportsCameraDepthTexture != state) EditorUtility.SetDirty(pipeline);
#endif
pipeline.supportsCameraDepthTexture = state;
}
}
public static bool IsOpaqueTextureOptionDisabledAnywhere()
{
bool state = false;
for (int i = 0; i < GraphicsSettings.allConfiguredRenderPipelines.Length; i++)
{
if(GraphicsSettings.allConfiguredRenderPipelines[i].GetType() != typeof(UniversalRenderPipelineAsset)) continue;
UniversalRenderPipelineAsset pipeline = (UniversalRenderPipelineAsset)GraphicsSettings.allConfiguredRenderPipelines[i];
if (pipeline.supportsCameraOpaqueTexture == false) return true;
}
return state;
}
public static void SetOpaqueTextureOnAllAssets(bool state)
{
for (int i = 0; i < GraphicsSettings.allConfiguredRenderPipelines.Length; i++)
{
if(GraphicsSettings.allConfiguredRenderPipelines[i].GetType() != typeof(UniversalRenderPipelineAsset)) continue;
UniversalRenderPipelineAsset pipeline = (UniversalRenderPipelineAsset)GraphicsSettings.allConfiguredRenderPipelines[i];
#if UNITY_EDITOR
if(pipeline.supportsCameraOpaqueTexture != state) EditorUtility.SetDirty(pipeline);
#endif
pipeline.supportsCameraOpaqueTexture = state;
}
}
public static bool TransparentShadowsEnabled()
{
if (!UniversalRenderPipeline.asset) return false;
UniversalRendererData main = (UniversalRendererData)GetDefaultRenderer();
return main ? main.shadowTransparentReceive : false;
}
public static bool IsDepthAfterTransparents()
{
#if UNITY_2022_2_OR_NEWER
if (!UniversalRenderPipeline.asset) return false;
UniversalRendererData main = (UniversalRendererData)GetDefaultRenderer();
return main.copyDepthMode == CopyDepthMode.AfterTransparents;
#else
return true;
#endif
}
public static bool VREnabled()
{
#if UNITY_2023_2_OR_NEWER
return XRSRPSettings.enabled;
#else
return XRGraphics.enabled;
#endif
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 004552901932ac14d889d7b07cc104f2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7fa4596443764ff6967bf4a4fedfe497
timeCreated: 1701077187

View File

@@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
#if URP
using UnityEngine.Rendering.Universal;
namespace StylizedWater2
{
public class DisplacementPrePass : ScriptableRenderPass
{
private const string profilerTag = "Water Displacement Prepass";
private static readonly ProfilingSampler profilerSampler = new ProfilingSampler(profilerTag);
public const string KEYWORD = "WATER_DISPLACEMENT_PASS";
[Serializable]
public class Settings
{
public bool enable;
public float range = 500f;
[Range(0.1f, 4f)]
public float cellSize = 0.25f;
}
//Render pass
FilteringSettings m_FilteringSettings;
RenderStateBlock m_RenderStateBlock;
private readonly List<ShaderTagId> m_ShaderTagIdList = new List<ShaderTagId>()
{
new ShaderTagId("DepthOnly")
};
public DisplacementPrePass()
{
m_FilteringSettings = new FilteringSettings(RenderQueueRange.all, LayerMask.GetMask("Water"));
m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
}
private static readonly Quaternion viewRotation = Quaternion.Euler(new Vector3(90f, 0f, 0f));
private static readonly Vector3 viewScale = new Vector3(1, 1, -1);
private static Rect viewportRect;
private const string BufferName = "_WaterDisplacementBuffer";
private static readonly int _WaterDisplacementBuffer = Shader.PropertyToID(BufferName);
private const string CoordsName = "_WaterDisplacementCoords";
private static readonly int _WaterDisplacementCoords = Shader.PropertyToID(CoordsName);
private RTHandle renderTarget;
private static Vector3 centerPosition;
private static Vector4 rendererCoords;
private static Matrix4x4 projection { set; get; }
private static Matrix4x4 view { set; get; }
private int resolution;
private int m_resolution;
private float orthoSize;
private Settings settings;
public void Setup(Settings settings)
{
this.settings = settings;
resolution = Mathf.CeilToInt(settings.range / settings.cellSize);
resolution = Mathf.Clamp(resolution, 16, 2048);
orthoSize = 0.5f * settings.range;
}
//Important to snap the projection to the nearest texel. Otherwise pixel swimming is introduced when moving, due to bilinear filtering
private static Vector3 StabilizeProjection(Vector3 pos, float texelSize)
{
float Snap(float coord, float cellSize) => Mathf.FloorToInt(coord / cellSize) * (cellSize) + (cellSize * 0.5f);
return new Vector3(Snap(pos.x, texelSize), Snap(pos.y, texelSize), Snap(pos.z, texelSize));
}
private void SetupProjection(CommandBuffer cmd, Camera camera)
{
centerPosition = camera.transform.position;
centerPosition += camera.transform.forward * settings.range * 0.5f;
centerPosition = StabilizeProjection(centerPosition, (settings.range) / resolution);
//var frustumHeight = 2.0f * renderRange * Mathf.Tan(camera.fieldOfView * 0.5f * Mathf.Deg2Rad); //Still clips, plus doesn't support orthographc
var frustumHeight = settings.range;
centerPosition += (Vector3.up * frustumHeight * 0.5f);
projection = Matrix4x4.Ortho(-orthoSize, orthoSize, -orthoSize, orthoSize, 0.03f, frustumHeight * 2f);
view = Matrix4x4.TRS(centerPosition, viewRotation, viewScale).inverse;
cmd.SetViewProjectionMatrices(view, projection);
//RenderingUtils.SetViewAndProjectionMatrices(cmd, view, projection, true);
viewportRect.width = resolution;
viewportRect.height = resolution;
cmd.SetViewport(viewportRect);
cmd.SetGlobalMatrix("UNITY_MATRIX_V", view);
//Position/scale of projection. Converted to a UV in the shader
rendererCoords.x = centerPosition.x - orthoSize;
rendererCoords.y = centerPosition.z - orthoSize;
rendererCoords.z = settings.range;
rendererCoords.w = 1f; //Enable in shader
cmd.SetGlobalVector(_WaterDisplacementCoords, rendererCoords);
}
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
if (resolution != m_resolution || renderTarget == null)
{
RTHandles.Release(renderTarget);
renderTarget = RTHandles.Alloc(resolution, resolution, 1, DepthBits.None,
UnityEngine.Experimental.Rendering.GraphicsFormat.R16_SFloat,
filterMode: FilterMode.Bilinear,
wrapMode: TextureWrapMode.Clamp,
useMipMap: false,
name: BufferName);
}
m_resolution = resolution;
cmd.SetGlobalTexture(_WaterDisplacementBuffer, renderTarget);
cmd.EnableShaderKeyword(KEYWORD);
ConfigureTarget(renderTarget);
ConfigureClear(ClearFlag.Color, Color.clear);
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
CommandBuffer cmd = CommandBufferPool.Get();
DrawingSettings drawingSettings = CreateDrawingSettings(m_ShaderTagIdList, ref renderingData, SortingCriteria.RenderQueue | SortingCriteria.SortingLayer | SortingCriteria.CommonTransparent);
drawingSettings.perObjectData = PerObjectData.None;
using (new ProfilingScope(cmd, profilerSampler))
{
ref CameraData cameraData = ref renderingData.cameraData;
SetupProjection(cmd, cameraData.camera);
//Execute current commands first
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
#if UNITY_2023_1_OR_NEWER
rendererListParams.cullingResults = renderingData.cullResults;
rendererListParams.drawSettings = drawingSettings;
rendererListParams.filteringSettings = m_FilteringSettings;
rendererList = context.CreateRendererList(ref rendererListParams);
cmd.DrawRendererList(rendererList);
#else
context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref m_FilteringSettings, ref m_RenderStateBlock);
#endif
//Restore
//Disabled, because this pass renders before the camera is initialized anyway
//cmd.SetViewProjectionMatrices(cameraData.camera.worldToCameraMatrix, cameraData.camera.projectionMatrix);
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
public void Dispose()
{
Shader.SetGlobalVector(_WaterDisplacementCoords, Vector4.zero);
RTHandles.Release(renderTarget);
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c08ec7d2563d4bea89aa3509298f8bf4
timeCreated: 1701077223

View File

@@ -0,0 +1,575 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
#if URP
using UnityEngine.Rendering.Universal;
#endif
namespace StylizedWater2
{
[ExecuteInEditMode]
[AddComponentMenu("Stylized Water 2/Planar Reflection Renderer")]
[HelpURL("https://staggart.xyz/unity/stylized-water-2/sws-2-docs/?section=planar-reflections")]
public class PlanarReflectionRenderer : MonoBehaviour
{
#if URP
public static List<PlanarReflectionRenderer> Instances = new List<PlanarReflectionRenderer>();
public Dictionary<Camera, Camera> reflectionCameras = new Dictionary<Camera, Camera>();
//Rendering
[Tooltip("If enabled, the reflection plane will be based on this transform's up vector (green arrow).\n\nOtherwise the world's upwards direction is assumed")]
public bool rotatable = false;
[Tooltip("Set the layers that should be rendered into the reflection. The \"Water\" layer is always excluded")]
public LayerMask cullingMask = -1;
[Tooltip("The renderer used by the reflection camera. It's recommend to create a separate renderer, so any custom render features aren't executed for the reflection")]
public int rendererIndex = -1;
[Min(0f)]
public float offset = 0.05f;
[Tooltip("When disabled, the skybox reflection comes from a Reflection Probe. This has the benefit of being omni-directional rather than flat/planar. Enabled this to render the skybox into the planar reflection anyway." +
"\n\nNote that enabling this will override Screen Space Reflections completely!")]
public bool includeSkybox;
[Tooltip("Render Unity's default fog in the reflection. Note that this doesn't strictly work correctly on large triangles, as it is incompatible with oblique camera projections.")]
public bool enableFog;
//Quality
public bool renderShadows;
[Tooltip("Objects beyond this range aren't rendered into the reflection. Note that this may causes popping for large/tall objects.")]
public float renderRange = 500f;
[Range(0.25f, 1f)]
[Tooltip("A multiplier for the rendering resolution, based on the current screen resolution. The render scale, as configured in the pipeline settings is multiplied over this.")]
public float renderScale = 0.75f;
[Range(0, 4)]
[Tooltip("Do not render LOD objects lower than this value. Example: With a value of 1, LOD0 for LOD Groups will not be used")]
public int maximumLODLevel = 0;
[SerializeField]
public List<WaterObject> waterObjects = new List<WaterObject>();
[Tooltip("If enabled, the center of the rendering bounds (that wraps around the water objects) moves with the Transform position" +
"\n\nYou must however ensure you are only moving on the XZ axis")]
public bool moveWithTransform;
[HideInInspector]
public Bounds bounds = new Bounds();
private float m_renderScale = 1f;
private float m_renderRange;
/// <summary>
/// Reflections will only render if this is true. Value can be set through the static SetQuality function
/// </summary>
public static bool AllowReflections { get; private set; } = true;
private static readonly int _PlanarReflectionsEnabledID = Shader.PropertyToID("_PlanarReflectionsEnabled");
private static readonly int _PlanarReflectionID = Shader.PropertyToID("_PlanarReflection");
#if UNITY_2023_1_OR_NEWER
private UniversalRenderPipeline.SingleCameraRequest requestData;
#endif
[NonSerialized]
public bool isRendering;
private Camera m_reflectionCamera;
private static UniversalAdditionalCameraData m_cameraData;
private void Reset()
{
this.gameObject.name = "Planar Reflection Renderer";
}
private void OnEnable()
{
InitializeValues();
Instances.Add(this);
EnableReflections();
}
private void OnDisable()
{
Instances.Remove(this);
DisableReflections();
}
public void InitializeValues()
{
m_renderScale = renderScale;
m_renderRange = renderRange;
}
/// <summary>
/// Assigns all Water Objects in the WaterObject.Instances list and enables reflection for them
/// </summary>
public void ApplyToAllWaterInstances()
{
waterObjects = new List<WaterObject>(WaterObject.Instances);
RecalculateBounds();
EnableMaterialReflectionSampling();
}
/// <summary>
/// Toggle reflections or set the render scale for all reflection renderers. This can be tied into performance scaling or graphics settings in menus
/// </summary>
/// <param name="enableReflections">Toggles rendering of reflections, and toggles it on all the assigned water objects</param>
/// <param name="renderScale">A multiplier for the current screen resolution. Note that the render scale configured in URP is also taken into account</param>
/// <param name="renderRange">Objects beyond this range aren't rendered into the reflection</param>
public static void SetQuality(bool enableReflections, float renderScale = -1f, float renderRange = -1f, int maxLodLevel = -1)
{
AllowReflections = enableReflections;
foreach (PlanarReflectionRenderer renderer in Instances)
{
if (renderScale > 0) renderer.renderScale = renderScale;
if (renderRange > 0) renderer.renderRange = renderRange;
if (maxLodLevel >= 0) renderer.maximumLODLevel = maxLodLevel;
renderer.InitializeValues();
if (enableReflections) renderer.EnableReflections();
if (!enableReflections) renderer.DisableReflections();
}
}
public void EnableReflections()
{
if (!AllowReflections || PipelineUtilities.VREnabled()) return;
RenderPipelineManager.beginCameraRendering += OnWillRenderCamera;
ToggleMaterialReflectionSampling(true);
}
public void DisableReflections()
{
RenderPipelineManager.beginCameraRendering -= OnWillRenderCamera;
ToggleMaterialReflectionSampling(false);
//Clear cameras
foreach (var kvp in reflectionCameras)
{
if (kvp.Value == null) continue;
if (kvp.Value)
{
RenderTexture.ReleaseTemporary(kvp.Value.targetTexture);
DestroyImmediate(kvp.Value.gameObject);
}
}
reflectionCameras.Clear();
}
private void OnDrawGizmosSelected()
{
Gizmos.color = bounds.size.y > 0.01f ? Color.yellow : Color.white;
Gizmos.DrawWireCube(bounds.center, bounds.size);
}
public Bounds CalculateBounds()
{
Bounds m_bounds = new Bounds(Vector3.zero, Vector3.zero);
if (waterObjects == null) return m_bounds;
if (waterObjects.Count == 0) return m_bounds;
Vector3 minSum = Vector3.one * Mathf.Infinity;
Vector3 maxSum = Vector3.one * Mathf.NegativeInfinity;
for (int i = 0; i < waterObjects.Count; i++)
{
if (!waterObjects[i]) continue;
minSum = Vector3.Min(waterObjects[i].meshRenderer.bounds.min, minSum);
maxSum = Vector3.Max(waterObjects[i].meshRenderer.bounds.max, maxSum);
}
m_bounds.SetMinMax(minSum, maxSum);
//Flatten to center
m_bounds.size = new Vector3(m_bounds.size.x, 0f, m_bounds.size.z);
return m_bounds;
}
public void RecalculateBounds()
{
bounds = CalculateBounds();
}
public static bool InvalidContext(Camera camera)
{
#if UNITY_EDITOR
//Avoid the "Screen position outside of frustrum" error
if (camera.orthographic && Vector3.Dot(Vector3.up, camera.transform.up) > 0.9999f) return true;
#if UNITY_2021_2_OR_NEWER
//Causes an internal error in URP's rendering code in the CopyColorPass
if (camera.cameraType == CameraType.SceneView && UnityEditor.SceneView.lastActiveSceneView && UnityEditor.SceneView.lastActiveSceneView.isUsingSceneFiltering) return true;
#endif
#endif
//Skip for any special use camera's (except scene view camera)
//Note: Scene camera still rendering even if window not focused!
return (camera.cameraType != CameraType.SceneView && (camera.cameraType == CameraType.Reflection || camera.cameraType == CameraType.Preview || camera.hideFlags != HideFlags.None));
}
private void OnWillRenderCamera(ScriptableRenderContext context, Camera camera)
{
if (InvalidContext(camera))
{
isRendering = false;
return;
}
isRendering = WaterObjectsVisible(camera);
if (isRendering == false) return;
if (moveWithTransform) bounds.center = this.transform.position;
m_cameraData = camera.GetComponent<UniversalAdditionalCameraData>();
if (m_cameraData && m_cameraData.renderType == CameraRenderType.Overlay) return;
reflectionCameras.TryGetValue(camera, out m_reflectionCamera);
if (m_reflectionCamera == null) CreateReflectionCamera(camera);
//It's possible it is destroyed at this point when disabling reflections
if (!m_reflectionCamera) return;
UnityEngine.Profiling.Profiler.BeginSample("Planar Water Reflections", camera);
//Render scale changed
if (Math.Abs(renderScale - m_renderScale) > 0.02f)
{
RenderTexture.ReleaseTemporary(m_reflectionCamera.targetTexture);
CreateRenderTexture(m_reflectionCamera, camera);
m_renderScale = renderScale;
}
UpdateWaterProperties(m_reflectionCamera);
UpdateCameraProperties(camera, m_reflectionCamera);
UpdatePerspective(camera, m_reflectionCamera);
bool fogEnabled = RenderSettings.fog && !enableFog;
//Fog is based on clip-space z-distance and doesn't work with oblique projections
if (fogEnabled) RenderSettings.fog = false;
int maxLODLevel = QualitySettings.maximumLODLevel;
QualitySettings.maximumLODLevel = maximumLODLevel;
GL.invertCulling = true;
#pragma warning disable 0618
#if UNITY_2023_1_OR_NEWER
/*
requestData = new UniversalRenderPipeline.SingleCameraRequest();
requestData.destination = reflectionCamera.targetTexture;
requestData.slice = -1;
//Throws the 'Recursive rendering is not supported in SRP (are you calling Camera.Render from within a render pipeline?).' error.
if (RenderPipeline.SupportsRenderRequest(m_reflectionCamera, requestData))
{
RenderPipeline.SubmitRenderRequest(m_reflectionCamera, requestData);
}
*/
//Instead, Unity will whine about using an obsolete API.
UniversalRenderPipeline.RenderSingleCamera(context, m_reflectionCamera);
//So now what?
#else
UniversalRenderPipeline.RenderSingleCamera(context, m_reflectionCamera);
#endif
#pragma warning restore 0618
if (fogEnabled) RenderSettings.fog = true;
QualitySettings.maximumLODLevel = maxLODLevel;
GL.invertCulling = false;
UnityEngine.Profiling.Profiler.EndSample();
}
private float GetRenderScale()
{
return Mathf.Clamp(renderScale * UniversalRenderPipeline.asset.renderScale, 0.25f, 1f);
}
/// <summary>
/// Should the renderer index be changed at runtime, this function must be called to update any reflection cameras
/// </summary>
/// <param name="index"></param>
public void SetRendererIndex(int index)
{
index = PipelineUtilities.ValidateRenderer(index);
foreach (var kvp in reflectionCameras)
{
if (kvp.Value == null) continue;
m_cameraData = kvp.Value.GetComponent<UniversalAdditionalCameraData>();
m_cameraData.SetRenderer(index);
}
}
public void ToggleShadows(bool state)
{
foreach (var kvp in reflectionCameras)
{
if (kvp.Value == null) continue;
m_cameraData = kvp.Value.GetComponent<UniversalAdditionalCameraData>();
m_cameraData.renderShadows = state;
}
}
/// <summary>
/// Add the WaterObject, and recalculates the rendering bounds.
/// </summary>
/// <param name="waterObject"></param>
public void AddWaterObject(WaterObject waterObject)
{
ToggleMaterialReflectionSampling(waterObject, true);
waterObjects.Add(waterObject);
RecalculateBounds();
}
/// <summary>
/// Remove the WaterObject, and recalculates the rendering bounds.
/// </summary>
/// <param name="waterObject"></param>
public void RemoveWaterObject(WaterObject waterObject)
{
ToggleMaterialReflectionSampling(waterObject, false);
waterObjects.Remove(waterObject);
RecalculateBounds();
}
/// <summary>
/// Enables planar reflections on the MeshRenderers of the assigned water objects
/// </summary>
public void EnableMaterialReflectionSampling()
{
ToggleMaterialReflectionSampling(AllowReflections);
}
/// <summary>
/// Toggles the sampling of the planar reflections texture in the water shader.
/// </summary>
/// <param name="state"></param>
public void ToggleMaterialReflectionSampling(bool state)
{
if (waterObjects == null) return;
for (int i = 0; i < waterObjects.Count; i++)
{
if (waterObjects[i] == null) continue;
ToggleMaterialReflectionSampling(waterObjects[i], state);
}
}
private void ToggleMaterialReflectionSampling(WaterObject waterObject, bool state)
{
waterObject.props.SetFloat(_PlanarReflectionsEnabledID, state ? 1f : 0f);
waterObject.ApplyInstancedProperties();
}
private void CreateReflectionCamera(Camera source)
{
//Object creation
GameObject go = new GameObject($"{source.name} Planar Reflection");
go.hideFlags = HideFlags.DontSave | HideFlags.HideInHierarchy;
Camera newCamera = go.AddComponent<Camera>();
newCamera.hideFlags = HideFlags.DontSave;
//For the scene-view camera this also copies unwanted properties. Such as the camera type and background color!
newCamera.CopyFrom(source);
//Always exclude water layer
newCamera.cullingMask = ~(1 << 4) & cullingMask;
//Must always be set to Game, otherwise shadows render anyway
newCamera.cameraType = CameraType.Game;
newCamera.depth = source.depth-1f;
newCamera.rect = new Rect(0,0,1,1);
newCamera.enabled = false;
newCamera.clearFlags = includeSkybox ? CameraClearFlags.Skybox : CameraClearFlags.Depth;
//Required to maintain the alpha channel for the scene view
newCamera.backgroundColor = Color.clear;
newCamera.useOcclusionCulling = false;
//Component required for the UniversalRenderPipeline.RenderSingleCamera call
UniversalAdditionalCameraData data = newCamera.gameObject.AddComponent<UniversalAdditionalCameraData>();
data.requiresDepthTexture = false;
data.requiresColorTexture = false;
data.renderShadows = renderShadows;
rendererIndex = PipelineUtilities.ValidateRenderer(rendererIndex);
data.SetRenderer(rendererIndex);
CreateRenderTexture(newCamera, source);
reflectionCameras[source] = newCamera;
}
private void CreateRenderTexture(Camera targetCamera, Camera source)
{
//Note: Do not use RenderTextureFormat.Default or HDR, as these may be without an alpha channel on some platforms
RenderTextureFormat colorFormat = UniversalRenderPipeline.asset.supportsHDR && SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf) ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB32;
float scale = GetRenderScale();
RenderTextureDescriptor rtDsc = new RenderTextureDescriptor(
(int)((float)source.scaledPixelWidth * scale),
(int)((float)source.scaledPixelHeight * scale),
colorFormat);
rtDsc.depthBufferBits = 16;
targetCamera.targetTexture = RenderTexture.GetTemporary(rtDsc);
targetCamera.targetTexture.name = $"{source.name}_Reflection {rtDsc.width}x{rtDsc.height}";
}
private static readonly Plane[] frustrumPlanes = new Plane[6];
public bool WaterObjectsVisible(Camera targetCamera)
{
GeometryUtility.CalculateFrustumPlanes(targetCamera.projectionMatrix * targetCamera.worldToCameraMatrix, frustrumPlanes);
return GeometryUtility.TestPlanesAABB(frustrumPlanes, bounds);
}
//Assigns the render target of the current reflection camera
private void UpdateWaterProperties(Camera cam)
{
for (int i = 0; i < waterObjects.Count; i++)
{
if (waterObjects[i] == null) continue;
waterObjects[i].props.SetTexture(_PlanarReflectionID, cam.targetTexture);
waterObjects[i].ApplyInstancedProperties();
}
}
private static Vector4 reflectionPlane;
private static Matrix4x4 reflectionBase;
private static Vector3 oldCamPos;
private static Matrix4x4 worldToCamera;
private static Matrix4x4 viewMatrix;
private static Matrix4x4 projectionMatrix;
private static Vector4 clipPlane;
private static readonly float[] layerCullDistances = new float[32];
private void UpdateCameraProperties(Camera source, Camera reflectionCam)
{
reflectionCam.fieldOfView = source.fieldOfView;
reflectionCam.orthographic = source.orthographic;
reflectionCam.orthographicSize = source.orthographicSize;
reflectionCam.useOcclusionCulling = source.useOcclusionCulling;
}
private void UpdatePerspective(Camera source, Camera reflectionCam)
{
if (!source || !reflectionCam) return;
Vector3 normal = rotatable ? this.transform.up : Vector3.up;
Vector3 position = bounds.center + (normal * offset);
var d = -Vector3.Dot(normal, position);
reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);
reflectionBase = Matrix4x4.identity;
reflectionBase *= Matrix4x4.Scale(new Vector3(1, -1, 1));
// View
CalculateReflectionMatrix(ref reflectionBase, reflectionPlane);
oldCamPos = source.transform.position - new Vector3(0, position.y * 2, 0);
reflectionCam.transform.forward = Vector3.Scale(source.transform.forward, new Vector3(1, -1, 1));
worldToCamera = source.worldToCameraMatrix;
viewMatrix = worldToCamera * reflectionBase;
//Reflect position
oldCamPos.y = -oldCamPos.y;
reflectionCam.transform.position = oldCamPos;
clipPlane = CameraSpacePlane(reflectionCam.worldToCameraMatrix, position - normal * 0.1f, normal, 1.0f);
projectionMatrix = source.CalculateObliqueMatrix(clipPlane);
//Settings
reflectionCam.cullingMask = ~(1 << 4) & cullingMask;;
m_reflectionCamera.clearFlags = includeSkybox ? CameraClearFlags.Skybox : CameraClearFlags.Depth;
#if !UNITY_2023_3_OR_NEWER
//Only re-apply on value change
if (m_renderRange != renderRange)
{
m_renderRange = renderRange;
for (int i = 0; i < layerCullDistances.Length; i++)
{
layerCullDistances[i] = renderRange;
}
}
reflectionCam.layerCullDistances = layerCullDistances;
reflectionCam.layerCullSpherical = true;
#endif
reflectionCam.projectionMatrix = projectionMatrix;
reflectionCam.worldToCameraMatrix = viewMatrix;
}
// Calculates reflection matrix around the given plane
private void CalculateReflectionMatrix(ref Matrix4x4 reflectionMat, Vector4 plane)
{
reflectionMat.m00 = (1F - 2F * plane[0] * plane[0]);
reflectionMat.m01 = (-2F * plane[0] * plane[1]);
reflectionMat.m02 = (-2F * plane[0] * plane[2]);
reflectionMat.m03 = (-2F * plane[3] * plane[0]);
reflectionMat.m10 = (-2F * plane[1] * plane[0]);
reflectionMat.m11 = (1F - 2F * plane[1] * plane[1]);
reflectionMat.m12 = (-2F * plane[1] * plane[2]);
reflectionMat.m13 = (-2F * plane[3] * plane[1]);
reflectionMat.m20 = (-2F * plane[2] * plane[0]);
reflectionMat.m21 = (-2F * plane[2] * plane[1]);
reflectionMat.m22 = (1F - 2F * plane[2] * plane[2]);
reflectionMat.m23 = (-2F * plane[3] * plane[2]);
reflectionMat.m30 = 0F;
reflectionMat.m31 = 0F;
reflectionMat.m32 = 0F;
reflectionMat.m33 = 1F;
}
// Given position/normal of the plane, calculates plane in camera space.
private Vector4 CameraSpacePlane(Matrix4x4 worldToCameraMatrix, Vector3 pos, Vector3 normal, float sideSign)
{
var offsetPos = pos + normal * offset;
var cameraPosition = worldToCameraMatrix.MultiplyPoint(offsetPos);
var cameraNormal = worldToCameraMatrix.MultiplyVector(normal).normalized * sideSign;
return new Vector4(cameraNormal.x, cameraNormal.y, cameraNormal.z,
-Vector3.Dot(cameraPosition, cameraNormal));
}
public RenderTexture TryGetReflectionTexture(Camera targetCamera)
{
if (targetCamera)
{
reflectionCameras.TryGetValue(targetCamera, out m_reflectionCamera);
if (m_reflectionCamera)
{
return m_reflectionCamera.targetTexture;
}
}
return null;
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 569d0b097a6f78843bff90729cbe4ef1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: -1426863774865177168, guid: 0000000000000000d000000000000000, type: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,110 @@
using UnityEngine;
using UnityEngine.Rendering;
#if URP
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering.Universal.Internal;
namespace StylizedWater2
{
public class SetupConstants : ScriptableRenderPass
{
private static readonly int _EnableDirectionalCaustics = Shader.PropertyToID("_EnableDirectionalCaustics");
private static readonly int CausticsProjection = Shader.PropertyToID("CausticsProjection");
private static readonly int _WaterSSREnabled = Shader.PropertyToID("_WaterSSREnabled");
private static readonly int _WaterDisplacementPrePassAvailable = Shader.PropertyToID("_WaterDisplacementPrePassAvailable");
private bool m_directionalCaustics;
private static VisibleLight mainLight;
private Matrix4x4 causticsProjection;
public SetupConstants()
{
//Force a unit scale, otherwise affects the projection tiling of the caustics
causticsProjection = Matrix4x4.Scale(Vector3.one);
}
private StylizedWaterRenderFeature settings;
public void Setup(StylizedWaterRenderFeature renderFeature)
{
this.settings = renderFeature;
m_directionalCaustics = settings.directionalCaustics;
}
#if UNITY_2020_2_OR_NEWER
private ScriptableRenderPassInput requirements;
#endif
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
#if UNITY_2020_2_OR_NEWER
//Inform the render pipeline which pre-passes are required
requirements = ScriptableRenderPassInput.None;
//Only when using advanced shading, so don't forcibly enable
//if(m_directionalCaustics) requirements = ScriptableRenderPassInput.Depth;
if (settings.screenSpaceReflectionSettings.enable)
{
requirements |= ScriptableRenderPassInput.Color | ScriptableRenderPassInput.Depth;
}
if(settings.displacementPrePassSettings.enable) cmd.EnableShaderKeyword(DisplacementPrePass.KEYWORD);
else cmd.DisableShaderKeyword(DisplacementPrePass.KEYWORD);
cmd.SetGlobalInt(_WaterSSREnabled, settings.screenSpaceReflectionSettings.enable ? 1 : 0);
cmd.SetGlobalInt(_WaterDisplacementPrePassAvailable, settings.displacementPrePassSettings.enable ? 1 : 0);
ConfigureInput(requirements);
#endif
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
CommandBuffer cmd = CommandBufferPool.Get();
if (m_directionalCaustics)
{
//When no lights are visible, main light will be set to -1.
if (renderingData.lightData.mainLightIndex > -1)
{
mainLight = renderingData.lightData.visibleLights[renderingData.lightData.mainLightIndex];
if (mainLight.lightType == LightType.Directional)
{
causticsProjection = Matrix4x4.Rotate(mainLight.light.transform.rotation);
cmd.SetGlobalMatrix(CausticsProjection, causticsProjection.inverse);
}
#if UNITY_2021_2_OR_NEWER
//Sets up the required View- -> Clip-space matrices
NormalReconstruction.SetupProperties(cmd, renderingData.cameraData);
#endif
}
else
{
m_directionalCaustics = false;
}
}
cmd.SetGlobalInt(_EnableDirectionalCaustics, m_directionalCaustics ? 1 : 0);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
public override void OnCameraCleanup(CommandBuffer cmd)
{
cmd.SetGlobalInt(_EnableDirectionalCaustics, 0);
cmd.SetGlobalInt(_WaterSSREnabled, 0);
}
public void Dispose()
{
Shader.SetGlobalInt(_WaterDisplacementPrePassAvailable, 0);
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fb6834bcc3854bbca9142a4bc547b968
timeCreated: 1701078225

View File

@@ -0,0 +1,65 @@
#if URP
using System;
using UnityEngine;
using UnityEngine.Rendering.Universal;
namespace StylizedWater2
{
#if UNITY_2021_1_OR_NEWER
[DisallowMultipleRendererFeature("Stylized Water 2")]
#endif
public class StylizedWaterRenderFeature : ScriptableRendererFeature
{
public static StylizedWaterRenderFeature GetDefault()
{
return (StylizedWaterRenderFeature)PipelineUtilities.GetRenderFeature<StylizedWaterRenderFeature>();
}
[Serializable]
public class ScreenSpaceReflectionSettings
{
public bool enable;
}
public ScreenSpaceReflectionSettings screenSpaceReflectionSettings = new ScreenSpaceReflectionSettings();
[Tooltip("Project caustics from the main directional light.")]
public bool directionalCaustics;
public DisplacementPrePass.Settings displacementPrePassSettings = new DisplacementPrePass.Settings();
private SetupConstants constantsSetup;
private DisplacementPrePass displacementPass;
public override void Create()
{
constantsSetup = new SetupConstants
{
renderPassEvent = RenderPassEvent.BeforeRendering
};
displacementPass = new DisplacementPrePass
{
renderPassEvent = RenderPassEvent.BeforeRendering
};
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
constantsSetup.Setup(this);
renderer.EnqueuePass(constantsSetup);
if (displacementPrePassSettings.enable)
{
displacementPass.Setup(displacementPrePassSettings);
renderer.EnqueuePass(displacementPass);
}
}
private void OnDestroy()
{
displacementPass.Dispose();
constantsSetup.Dispose();
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2086e3e143e14a2abcd3b0dbc77a10ac
timeCreated: 1701077140

View File

@@ -0,0 +1,149 @@
using System;
using UnityEngine;
using StylizedWater2;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace NWH.DWP2.WaterData
{
#if NWH_DWP2
public class StylizedWaterDataProvider : WaterDataProvider
{
[Tooltip("This reference is required to grab the wave distance and height values")]
public Material waterMat;
public enum WaterLevelSource
{
Value,
Mesh
}
[Tooltip("Configure what should be used to set the base water level. Relative wave height is added to this value")]
public WaterLevelSource waterLevelSource = WaterLevelSource.Value;
[Tooltip("This reference is required to get the base water height. Relative wave height is added to this")]
public MeshRenderer waterPlane;
public float waterLevel;
[Tooltip("Enable if the wave settings are being changed at runtime. Incurs some overhead")]
public bool dynamicMaterial;
private float m_waterLevel = 0f;
private Vector3[] _normals;
private int _prevArraySize;
private void Reset()
{
MeshRenderer r = GetComponent<MeshRenderer>();
if (r)
{
waterPlane = r;
waterMat = r.sharedMaterial;
}
waterLevel = this.transform.position.y;
}
private void OnValidate()
{
if (!waterMat && waterPlane) waterMat = waterPlane.sharedMaterial;
}
public override bool SupportsWaterHeightQueries()
{
return true;
}
public override bool SupportsWaterNormalQueries()
{
return true;
}
public override bool SupportsWaterFlowQueries()
{
return false;
}
public override void GetWaterHeights(NWH.DWP2.WaterObjects.WaterObject waterObject, ref Vector3[] points, ref float[] waterHeights)
{
var n = points.Length;
m_waterLevel = waterPlane && waterLevelSource == WaterLevelSource.Mesh ? waterPlane.transform.position.y : waterLevel;
// Resize array if data size changed
if (n != _prevArraySize)
{
_normals = new Vector3[n];
waterHeights = new float[n];
_prevArraySize = n;
}
for (int i = 0; i < points.Length; i++)
{
waterHeights[i] = Buoyancy.SampleWaves(points[i], waterMat, m_waterLevel, 0f, dynamicMaterial, out _normals[i]);
}
}
public override void GetWaterNormals(NWH.DWP2.WaterObjects.WaterObject waterObject, ref Vector3[] points, ref Vector3[] waterNormals)
{
waterNormals = _normals; // Already queried in GetWaterHeights
}
public override float GetWaterHeightSingle(NWH.DWP2.WaterObjects.WaterObject waterObject, Vector3 point)
{
return Buoyancy.SampleWaves(point, waterMat, m_waterLevel, 0f, dynamicMaterial, out _);
}
}
#if UNITY_EDITOR
[CustomEditor(typeof(StylizedWaterDataProvider))]
public class StylizedWaterDataProviderInspector : Editor
{
SerializedProperty waterMat;
SerializedProperty dynamicMaterial;
SerializedProperty waterLevelSource;
SerializedProperty waterPlane;
SerializedProperty waterLevel;
private void OnEnable()
{
waterMat = serializedObject.FindProperty("waterMat");
dynamicMaterial = serializedObject.FindProperty("dynamicMaterial");
waterLevelSource = serializedObject.FindProperty("waterLevelSource");
waterPlane = serializedObject.FindProperty("waterPlane");
waterLevel = serializedObject.FindProperty("waterLevel");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(waterMat);
if (waterMat.objectReferenceValue == null)
{
EditorGUILayout.HelpBox("A water material must be assigned!", MessageType.Error);
}
EditorGUILayout.PropertyField(dynamicMaterial);
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.PrefixLabel("Water level source");
waterLevelSource.intValue = GUILayout.Toolbar(waterLevelSource.intValue, new GUIContent[] { new GUIContent("Fixed Value"), new GUIContent("Mesh Object") });
}
if (waterLevelSource.intValue == (int)StylizedWaterDataProvider.WaterLevelSource.Value) EditorGUILayout.PropertyField(waterLevel);
if (waterLevelSource.intValue == (int)StylizedWaterDataProvider.WaterLevelSource.Mesh) EditorGUILayout.PropertyField(waterPlane);
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
}
}
}
#endif
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1f6878d8c33f6d147918f802eec4ddbf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 63c76fd48f9ad734da6fed8533f240a1, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace StylizedWater2
{
[ExecuteInEditMode]
[AddComponentMenu("Stylized Water 2/Water Grid")]
public class WaterGrid : MonoBehaviour
{
[Tooltip("Material used on the tile meshes")]
public Material material;
[Tooltip("When not in play-mode, the water will follow the scene-view camera position.")]
public bool followSceneCamera = false;
[Tooltip("If enabled, the object with the \"MainCamera\" tag will be assigned as the follow target when entering play mode")]
public bool autoAssignCamera;
[Tooltip("The grid will follow this Transform's position on the XZ axis. Ideally set to the camera's transform.")]
public Transform followTarget;
[Tooltip("Scale of the entire grid in the length and width")]
public float scale = 500f;
[Range(0.15f, 10f)]
[Tooltip("Distance between vertices, rather higher than lower")]
public float vertexDistance = 2f;
[Min(1)]
public int rowsColumns = 4;
[HideInInspector]
public int m_rowsColumns = 4;
[SerializeField]
[HideInInspector]
private Mesh mesh;
[SerializeField]
[HideInInspector]
private List<WaterObject> objects = new List<WaterObject>();
[NonSerialized]
private float tileSize;
[NonSerialized]
private WaterObject m_waterObject = null;
[NonSerialized]
private Transform actualFollowTarget;
[NonSerialized]
private Vector3 targetPosition;
#if UNITY_EDITOR
public static bool DisplayGrid = true;
public static bool DisplayWireframe;
#endif
private void Reset()
{
Recreate();
}
private void Start()
{
if (autoAssignCamera) followTarget = Camera.main ? Camera.main.transform : followTarget;
}
private void OnEnable()
{
#if UNITY_EDITOR
UnityEditor.SceneView.duringSceneGui += OnSceneGUI;
#endif
m_rowsColumns = rowsColumns;
//Mesh is serialized with the scene, if component is used as a prefab, regenerate it
if (mesh == null)
{
RecreateMesh();
ReassignMesh();
}
}
#if UNITY_EDITOR
private void OnDisable()
{
UnityEditor.SceneView.duringSceneGui -= OnSceneGUI;
}
#endif
void Update()
{
if (Application.isPlaying) actualFollowTarget = followTarget;
if (actualFollowTarget)
{
targetPosition = actualFollowTarget.transform.position;
targetPosition = SnapToGrid(targetPosition, vertexDistance);
targetPosition.y = this.transform.position.y;
this.transform.position = targetPosition;
}
}
public void Recreate()
{
RecreateMesh();
bool requireRecreate = (m_rowsColumns != rowsColumns) || objects.Count < (rowsColumns * rowsColumns);
if (requireRecreate) m_rowsColumns = rowsColumns;
//Only destroy/recreate objects if grid subdivision has changed
if (requireRecreate && objects.Count > 0)
{
foreach (WaterObject obj in objects)
{
if (obj) DestroyImmediate(obj.gameObject);
}
objects.Clear();
}
int index = 0;
for (int x = 0; x < rowsColumns; x++)
{
for (int z = 0; z < rowsColumns; z++)
{
if (requireRecreate)
{
m_waterObject = WaterObject.New(material, mesh);
objects.Add(m_waterObject);
m_waterObject.transform.parent = this.transform;
m_waterObject.name = "WaterTile_x" + x + "z" + z;
}
else
{
m_waterObject = objects[index];
m_waterObject.AssignMesh(mesh);
m_waterObject.AssignMaterial(material);
}
m_waterObject.transform.localPosition = GridLocalCenterPosition(x, z);
m_waterObject.transform.localScale = Vector3.one;
index++;
}
}
}
private void RecreateMesh()
{
rowsColumns = Mathf.Max(rowsColumns, 1);
tileSize = Mathf.Max(1f, scale / rowsColumns);
mesh = WaterMesh.Create(WaterMesh.Shape.Rectangle, tileSize, vertexDistance, tileSize);
}
private void ReassignMesh()
{
foreach (WaterObject obj in objects)
{
obj.AssignMesh(mesh);
}
}
private Vector3 GridLocalCenterPosition(int x, int z)
{
return new Vector3(x * tileSize - ((tileSize * (rowsColumns)) * 0.5f) + (tileSize * 0.5f), 0f,
z * tileSize - ((tileSize * (rowsColumns)) * 0.5f) + (tileSize * 0.5f));
}
public static Vector3 SnapToGrid(Vector3 position, float cellSize)
{
return new Vector3(SnapToGrid(position.x, cellSize), SnapToGrid(position.y, cellSize), SnapToGrid(position.z, cellSize));
}
private static float SnapToGrid(float position, float cellSize)
{
return Mathf.FloorToInt(position / cellSize) * (cellSize) + (cellSize * 0.5f);
}
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
if (DisplayWireframe)
{
Gizmos.color = new Color(0, 0, 0, 0.5f);
foreach (WaterObject waterObject in objects)
{
if(waterObject.meshFilter.sharedMesh) Gizmos.DrawWireMesh(waterObject.meshFilter.sharedMesh, waterObject.transform.position);
}
}
if (DisplayGrid)
{
Gizmos.color = new Color(1f, 0.25f, 0.25f, 0.5f);
Gizmos.matrix = this.transform.localToWorldMatrix;
for (int x = 0; x < rowsColumns; x++)
{
for (int z = 0; z < rowsColumns; z++)
{
Vector3 pos = GridLocalCenterPosition(x, z);
Gizmos.DrawWireCube(pos, new Vector3(tileSize, 0f, tileSize));
}
}
}
}
private void OnSceneGUI(UnityEditor.SceneView sceneView)
{
if (followSceneCamera)
{
actualFollowTarget = sceneView.camera.transform;
Update();
}
else
{
actualFollowTarget = null;
}
}
#endif
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 6265ced51cdf0b94bb311b507ea5c6b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- material: {fileID: 2100000, guid: fbb04271505a76f40b984e38071e86f3, type: 2}
- followTarget: {instanceID: 0}
executionOrder: 0
icon: {fileID: 5243786984396574768, guid: 0000000000000000d000000000000000, type: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,241 @@
//Stylized Water 2
//Staggart Creations (http://staggart.xyz)
//Copyright protected under Unity Asset Store EULA
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using Random = System.Random;
namespace StylizedWater2
{
[Serializable]
public class WaterMesh
{
public enum Shape
{
Rectangle,
Disk
}
public Shape shape;
[FormerlySerializedAs("size")]
[Range(10, 1000)]
public float scale = 100f;
[Tooltip("Distance between vertices")]
[Range(0.15f, 10f)]
public float vertexDistance = 1f;
public float UVTiling = 1f;
[Tooltip("Shifts the vertices in a random direction. Definitely use this when using flat shading")]
[Range(0f, 1f)]
public float noise;
[Min(0)]
[Tooltip("The surface is normally flat, yet vertex displacement on the GPU such as waves can give the surface artificial height." +
"\n\nThis can cause a Mesh Renderer to be prematurely culled, despite still actually being visible." +
"\n\nThis value adds an artificial amount of height to the generate mesh's bounds, to avoid this from happening.")]
public float boundsPadding = 4f;
/// <summary>
/// Generated output mesh. Empty by default, use the Rebuild() function to generate one from the current settings.
/// </summary>
public Mesh mesh;
public Mesh Rebuild()
{
switch (shape)
{
case Shape.Rectangle: mesh = CreatePlane();
break;
case Shape.Disk: mesh = CreateCircle();
break;
}
return mesh;
}
public static Mesh Create(Shape shape, float size, float vertexDistance, float uvTiling = 1f, float noise = 0f)
{
WaterMesh waterMesh = new WaterMesh();
waterMesh.shape = shape;
waterMesh.scale = size;
waterMesh.vertexDistance = vertexDistance;
waterMesh.UVTiling = uvTiling;
waterMesh.noise = noise;
return waterMesh.Rebuild();
}
// Get the index of point number 'x' in circle number 'c'
private int GetPointIndex(int c, int x)
{
if (c < 0) return 0;
x = x % ((c + 1) * 6);
return (3 * c * (c + 1) + x + 1);
}
private Mesh CreateCircle()
{
Mesh m = new Mesh();
m.name = "WaterDisk";
int subdivisions = Mathf.FloorToInt(scale / vertexDistance);
float distance = 1f / subdivisions;
var vertices = new List<Vector3>();
var uvs = new List<Vector2>();
var uvs2 = new List<Vector2>();
vertices.Add(Vector3.zero); //Center
var tris = new List<int>();
// First pass => build vertices
for (int loop = 0; loop < subdivisions; loop++)
{
float angleStep = (Mathf.PI * 2f) / ((loop + 1) * 6);
for (int point = 0; point < (loop + 1) * 6; ++point)
{
Vector3 vPos = new Vector3(
Mathf.Sin(angleStep * point) ,
0f,
Mathf.Cos(angleStep * point));
UnityEngine.Random.InitState(loop + point);
vPos.x += UnityEngine.Random.Range(-noise * 0.01f, noise * 0.01f);
vPos.z -= UnityEngine.Random.Range(noise * 0.01f, -noise * 0.01f);
vertices.Add(vPos * (scale * 0.5f) * distance * (loop + 1));
}
}
//Planar mapping
for (int i = 0; i < vertices.Count; i++)
{
uvs.Add(new Vector2(0.5f + (vertices[i].x) * UVTiling,0.5f + (vertices[i].z) * UVTiling));
//Lightmap UV's
uvs2.Add(new Vector2(0.5f + (vertices[i].x / scale),0.5f + (vertices[i].z / scale)));
}
// Second pass => connect vertices into triangles
for (int circ = 0; circ < subdivisions; ++circ)
{
for (int point = 0, other = 0; point < (circ + 1) * 6; ++point)
{
if (point % (circ + 1) != 0)
{
// Create 2 triangles
tris.Add(GetPointIndex(circ - 1, other + 1));
tris.Add(GetPointIndex(circ - 1, other));
tris.Add(GetPointIndex(circ, point));
tris.Add(GetPointIndex(circ, point));
tris.Add(GetPointIndex(circ, point + 1));
tris.Add(GetPointIndex(circ - 1, other + 1));
++other;
}
else
{
// Create 1 inverse triangle
tris.Add(GetPointIndex(circ, point));
tris.Add(GetPointIndex(circ, point + 1));
tris.Add(GetPointIndex(circ - 1, other));
// Do not move to the next point in the smaller circle
}
}
}
// Create the mesh
m.SetVertices(vertices);
m.SetTriangles(tris, 0);
m.RecalculateNormals();
m.RecalculateTangents();
m.SetUVs(0, uvs);
m.SetUVs(1, uvs2);
m.colors = new Color[vertices.Count];
m.bounds = new Bounds(Vector3.zero, new Vector3(scale, boundsPadding, scale));
return m;
}
private Mesh CreatePlane()
{
Mesh m = new Mesh();
m.name = "WaterPlane";
scale = Mathf.Max(1f, scale);
int subdivisions = Mathf.FloorToInt(scale / vertexDistance);
int xCount = subdivisions + 1;
int zCount = subdivisions + 1;
int numTriangles = subdivisions * subdivisions * 6;
int numVertices = xCount * zCount;
Vector3[] vertices = new Vector3[numVertices];
Vector2[] uvs = new Vector2[numVertices];
Vector2[] uvs2 = new Vector2[numVertices];
int[] triangles = new int[numTriangles];
Vector4[] tangents = new Vector4[numVertices];
Vector3[] normals = new Vector3[numVertices];
Vector4 tangent = new Vector4(1f, 0f, 0f, -1f);
int index = 0;
float scaleX = scale / subdivisions;
float scaleY = scale / subdivisions;
float noiseScale = vertexDistance * 0.5f;
for (int z = 0; z < zCount; z++)
{
for (int x = 0; x < xCount; x++)
{
vertices[index] = new Vector3(x * scaleX - (scale * 0.5f), 0f, z * scaleY - (scale * 0.5f));
UnityEngine.Random.InitState(index);
vertices[index].x += UnityEngine.Random.Range(-noise * noiseScale, noise * noiseScale);
vertices[index].z -= UnityEngine.Random.Range(noise * noiseScale, -noise * noiseScale);
tangents[index] = tangent;
uvs[index] = new Vector2(0.5f + (vertices[index].x) * UVTiling, 0.5f + (vertices[index].z) * UVTiling);
//Lightmap UV's
uvs2[index] = new Vector2(0.5f + vertices[index].x / scale, 0.5f + vertices[index].z / scale);
normals[index] = Vector3.up;
index++;
}
}
index = 0;
for (int z = 0; z < subdivisions; z++)
{
for (int x = 0; x < subdivisions; x++)
{
triangles[index] = (z * xCount) + x;
triangles[index + 1] = ((z + 1) * xCount) + x;
triangles[index + 2] = (z * xCount) + x + 1;
triangles[index + 3] = ((z + 1) * xCount) + x;
triangles[index + 4] = ((z + 1) * xCount) + x + 1;
triangles[index + 5] = (z * xCount) + x + 1;
index += 6;
}
}
m.vertices = vertices;
m.triangles = triangles;
m.uv = uvs;
m.uv2 = uvs2;
m.tangents = tangents;
m.normals = normals;
m.colors = new Color[vertices.Length];
m.bounds = new Bounds(Vector3.zero, new Vector3(scale, boundsPadding, scale));
return m;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3f4d8d3a5060ac547a2664e9e46d2c4a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,203 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace StylizedWater2
{
/// <summary>
/// Attached to every mesh using the Stylized Water 2 shader
/// Provides a generic way of identifying water objects and accessing their properties
/// </summary>
[ExecuteInEditMode]
[AddComponentMenu("Stylized Water 2/Water Object")]
[DisallowMultipleComponent]
public class WaterObject : MonoBehaviour
{
/// <summary>
/// Collection of all available WaterObject instances. Instances (un)register themselves in the OnEnable/OnDisable functions.
/// </summary>
public static readonly List<WaterObject> Instances = new List<WaterObject>();
public Material material;
public MeshFilter meshFilter;
public MeshRenderer meshRenderer;
private static Vector3 s_PositionOffset;
private static readonly int _WaterPositionOffset = Shader.PropertyToID("_WaterPositionOffset");
/// <summary>
/// For use with floating-point origin systems. In the shader, the world-position (used for UV coordinates) will be offset by this value.
/// Buoyancy calculations will also be offset to stay in sync.
/// </summary>
public static Vector3 PositionOffset
{
set
{
s_PositionOffset = value;
Shader.SetGlobalVector(_WaterPositionOffset, s_PositionOffset);
}
internal get => s_PositionOffset;
}
private static float m_customTimeValue = -1f;
private static readonly int CustomTimeID = Shader.PropertyToID("_CustomTime");
/// <summary>
/// Pass in any time value, any kind of animations will use this as a time index, including wave animations (and thus buoyancy calculations as well).
/// This is typically used for network synchronized waves or cutscenes.
/// To revert to using normal time, pass in a value lower than 0.
/// </summary>
/// <param name="value"></param>
public static float CustomTime
{
set
{
m_customTimeValue = value;
Shader.SetGlobalFloat(CustomTimeID, m_customTimeValue);
}
internal get => m_customTimeValue;
}
private MaterialPropertyBlock _props;
public MaterialPropertyBlock props
{
get
{
//Fetch when required, execution order makes it unreliable otherwise
if (_props == null)
{
CreatePropertyBlock(meshRenderer);
}
return _props;
}
private set => _props = value;
}
private void CreatePropertyBlock(Renderer sourceRenderer)
{
_props = new MaterialPropertyBlock();
sourceRenderer.GetPropertyBlock(_props);
}
private void Reset()
{
meshRenderer = GetComponent<MeshRenderer>();
CreatePropertyBlock(meshRenderer);
meshFilter = GetComponent<MeshFilter>();
}
private void OnEnable()
{
Instances.Add(this);
}
private void OnDisable()
{
Instances.Remove(this);
}
private void OnValidate()
{
if (!meshRenderer) meshRenderer = GetComponent<MeshRenderer>();
if (!meshFilter) meshFilter = GetComponent<MeshFilter>();
FetchWaterMaterial();
}
/// <summary>
/// Grabs the material from the attached Mesh Renderer
/// </summary>
public Material FetchWaterMaterial()
{
if (meshRenderer)
{
material = meshRenderer.sharedMaterial;
return material;
}
return null;
}
/// <summary>
/// Applies to changes made to the Material Property Blocks ('props' property)
/// </summary>
public void ApplyInstancedProperties()
{
if(props != null) meshRenderer.SetPropertyBlock(props);
}
/// <summary>
/// Checks if the position is below the maximum possible wave height. Can be used as a fast broad-phase check, before actually using the more expensive SampleWaves function
/// </summary>
/// <param name="position"></param>
public bool CanTouch(Vector3 position)
{
return Buoyancy.CanTouchWater(position, this);
}
public void AssignMesh(Mesh mesh)
{
if (meshFilter) meshFilter.sharedMesh = mesh;
}
public void AssignMaterial(Material newMaterial)
{
if (meshRenderer) meshRenderer.sharedMaterial = newMaterial;
material = newMaterial;
}
/// <summary>
/// Creates a new GameObject with a MeshFilter, MeshRenderer and WaterObject component
/// </summary>
/// <param name="waterMaterial">If assigned, this material is automatically added to the MeshRenderer</param>
/// <returns></returns>
public static WaterObject New(Material waterMaterial = null, Mesh mesh = null)
{
GameObject go = new GameObject("Water Object", typeof(MeshFilter), typeof(MeshRenderer), typeof(WaterObject));
go.layer = LayerMask.NameToLayer("Water");
#if UNITY_EDITOR
UnityEditor.Undo.RegisterCreatedObjectUndo(go, "Created Water Object");
#endif
WaterObject waterObject = go.GetComponent<WaterObject>();
waterObject.meshRenderer = waterObject.gameObject.GetComponent<MeshRenderer>();
waterObject.meshFilter = waterObject.gameObject.GetComponent<MeshFilter>();
waterObject.meshFilter.sharedMesh = mesh;
waterObject.meshRenderer.sharedMaterial = waterMaterial;
waterObject.meshRenderer.shadowCastingMode = ShadowCastingMode.Off;
waterObject.material = waterMaterial;
return waterObject;
}
/// <summary>
/// Attempt to find the WaterObject above or below the position. Checks against the bounds of ALL Water Object meshes by raycasting on the XZ plane
/// </summary>
/// <param name="position">Position in world-space (height is not relevant)</param>
/// <param name="rotationSupport">Unless this is true, water rotated on the Y-axis will yield incorrect results (but is faster)</param>
/// <returns></returns>
public static WaterObject Find(Vector3 position, bool rotationSupport)
{
Ray ray = new Ray(position + (Vector3.up * 1000f), Vector3.down);
foreach (WaterObject obj in Instances)
{
if (rotationSupport)
{
//Local space
ray.origin = obj.transform.InverseTransformPoint(ray.origin);
if (obj.meshFilter.sharedMesh.bounds.IntersectRay(ray)) return obj;
}
else
{
//Axis-aligned bounds
if (obj.meshRenderer.bounds.IntersectRay(ray)) return obj;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 52480f6baf9b7b0428c75481cb3d5a6d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 83548767ebe1791409159648329da97d, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,85 @@
using System;
using UnityEngine;
namespace StylizedWater2
{
/// <summary>
/// Helper class to retrieve and store a water material's wave settings
/// </summary>
[Serializable]
public class WaveParameters
{
private const string WavesKeyword = "_WAVES";
private static int _Direction = Shader.PropertyToID("_Direction");
private static int _Speed = Shader.PropertyToID("_Speed"
);
private static int _WaveDistance = Shader.PropertyToID("_WaveDistance");
private static int _WaveSpeed = Shader.PropertyToID("_WaveSpeed");
private static int _WaveHeight = Shader.PropertyToID("_WaveHeight");
private static int _WaveSteepness = Shader.PropertyToID("_WaveSteepness");
private static int _WaveCount = Shader.PropertyToID("_WaveCount");
private static int _WaveDirection = Shader.PropertyToID("_WaveDirection");
public Vector2 animationDirection;
public float animationSpeed;
public int count;
public float distance;
public float speed;
public float height;
public float steepness;
public Vector4 direction;
public static bool WavesEnabled(Material waterMat)
{
if (!waterMat) return false;
return waterMat.IsKeywordEnabled(WavesKeyword);
}
public static float GetMaxWaveHeight(Material mat)
{
return mat.GetFloat(_WaveHeight);
}
public void Update(Material waterMat)
{
animationDirection = waterMat.GetVector(_Direction);
animationSpeed = waterMat.GetFloat(_Speed);
speed = waterMat.GetFloat(_WaveSpeed);
distance = waterMat.GetFloat(_WaveDistance);
steepness = waterMat.GetFloat(_WaveSteepness);
height = waterMat.GetFloat(_WaveHeight);
count = waterMat.GetInt(_WaveCount);
direction = waterMat.GetVector(_WaveDirection);
}
public void SetAsGlobal()
{
Shader.SetGlobalVector(_Direction, animationDirection);
Shader.SetGlobalFloat(_Speed, animationSpeed);
Shader.SetGlobalFloat(_WaveSpeed, speed);
Shader.SetGlobalFloat(_WaveDistance, distance);
Shader.SetGlobalFloat(_WaveSteepness, steepness);
Shader.SetGlobalFloat(_WaveHeight, height);
Shader.SetGlobalFloat(_WaveCount, count);
Shader.SetGlobalVector(_WaveDirection, direction);
}
public void Apply(Material mat)
{
mat.SetVector(_Direction, animationDirection);
mat.SetFloat(_Speed, animationSpeed);
mat.SetFloat(_WaveSpeed, speed);
mat.SetFloat(_WaveDistance, distance);
mat.SetFloat(_WaveSteepness, steepness);
mat.SetFloat(_WaveHeight, height);
mat.SetInt(_WaveCount, count);
mat.SetVector(_WaveDirection, direction);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 99230946af6b43d59e5588b131421f2d
timeCreated: 1616150513

View File

@@ -0,0 +1,42 @@
{
"name": "sc.stylizedwater2.runtime",
"rootNamespace": "",
"references": [
"GUID:15fc0a57446b3144c949da3e2b9737a9",
"GUID:df380645f10b7bc4b97d4f5eb6303d95",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:75ecb28acc33857438e533566abcb3be",
"GUID:f06555f75b070af458a003d92f9efb00",
"GUID:9cb5eaf8df574e829047543e7b48b611"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.mathematics",
"expression": "1.1.0",
"define": "MATHEMATICS"
},
{
"name": "com.unity.render-pipelines.universal",
"expression": "10.3.2",
"define": "URP"
},
{
"name": "com.unity.visualeffectgraph",
"expression": "10.3.2",
"define": "VFX_GRAPH"
},
{
"name": "com.unity.splines",
"expression": "2.0.0",
"define": "SPLINES"
}
],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 93226a8cd37f67d4a996a525146f6f09
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: