Add water system package

This commit is contained in:
Vova
2023-12-06 20:29:45 +02:00
parent 627a90012a
commit 58d0f79bd1
140 changed files with 33167 additions and 3 deletions
@@ -0,0 +1,411 @@
// Buoyancy.cs
// by Alex Zhdankin
// Version 2.1
//
// http://forum.unity3d.com/threads/72974-Buoyancy-script
//
// Terms of use: do whatever you like
//
// Further tweaks by Andre McGrail
//
//
using System;
using System.Collections.Generic;
using UnityEngine;
using Unity.Collections;
using Unity.Mathematics;
namespace WaterSystem
{
public class BuoyantObject : MonoBehaviour
{
public BuoyancyType _buoyancyType; // type of buoyancy to calculate
public float density; // density of the object, this is calculated off it's volume and mass
public float volume; // volume of the object, this is calculated via it's colliders
public float voxelResolution = 0.51f; // voxel resolution, represents the half size of a voxel when creating the voxel representation
private Bounds _voxelBounds; // bounds of the voxels
public Vector3 centerOfMass = Vector3.zero; // Center Of Mass offset
public float waterLevelOffset = 0f;
private const float Dampner = 0.005f;
private const float WaterDensity = 1000;
private float _baseDrag; // reference to original drag
private float _baseAngularDrag; // reference to original angular drag
private int _guid; // GUID for the height system
private float3 _localArchimedesForce;
private Vector3[] _voxels; // voxel position
private NativeArray<float3> _samplePoints; // sample points for height calc
[NonSerialized] public float3[] Heights; // water height array(only size of 1 when simple or non-physical)
private float3[] _normals; // water normal array(only used when non-physical and size of 1 also when simple)
private float3[] _velocity; // voxel velocity for buoyancy
[SerializeField] Collider[] colliders; // colliders attatched ot this object
private Rigidbody _rb;
private DebugDrawing[] _debugInfo; // For drawing force gizmos
[NonSerialized] public float PercentSubmerged;
[ContextMenu("Initialize")]
private void Init()
{
_voxels = null;
switch (_buoyancyType)
{
case BuoyancyType.NonPhysical:
SetupVoxels();
SetupData();
break;
case BuoyancyType.NonPhysicalVoxel:
SetupColliders();
SetupVoxels();
SetupData();
break;
case BuoyancyType.Physical:
SetupVoxels();
SetupData();
SetupPhysical();
break;
case BuoyancyType.PhysicalVoxel:
SetupColliders();
SetupVoxels();
SetupData();
SetupPhysical();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private void SetupVoxels()
{
if (_buoyancyType == BuoyancyType.NonPhysicalVoxel || _buoyancyType == BuoyancyType.PhysicalVoxel)
{
SliceIntoVoxels();
}
else
{
_voxels = new Vector3[1];
_voxels[0] = centerOfMass;
}
}
private void SetupData()
{
_debugInfo = new DebugDrawing[_voxels.Length];
Heights = new float3[_voxels.Length];
_normals = new float3[_voxels.Length];
_samplePoints = new NativeArray<float3>(_voxels.Length, Allocator.Persistent);
}
private void OnEnable()
{
_guid = gameObject.GetInstanceID();
Init();
LocalToWorldConversion();
}
private void SetupColliders()
{
// The object must have a Collider
colliders = GetComponentsInChildren<Collider>();
if (colliders.Length != 0) return;
colliders = new Collider[1];
colliders[0] = gameObject.AddComponent<BoxCollider>();
Debug.LogError($"Buoyancy:Object \"{name}\" had no coll. BoxCollider has been added.");
}
private void Update()
{
#if STATIC_EVERYTHING
var dt = 0.0f;
#else
var dt = Time.deltaTime;
#endif
switch (_buoyancyType)
{
case BuoyancyType.NonPhysical:
{
var t = transform;
var vec = t.position;
vec.y = Heights[0].y + waterLevelOffset;
t.position = vec;
t.up = Vector3.Slerp(t.up, _normals[0], dt);
break;
}
case BuoyancyType.NonPhysicalVoxel:
// do the voxel non-physical
break;
case BuoyancyType.Physical:
LocalToWorldJob.CompleteJob(_guid);
GetVelocityPoints();
break;
case BuoyancyType.PhysicalVoxel:
LocalToWorldJob.CompleteJob(_guid);
GetVelocityPoints();
break;
default:
throw new ArgumentOutOfRangeException();
}
GerstnerWavesJobs.UpdateSamplePoints(ref _samplePoints, _guid);
GerstnerWavesJobs.GetData(_guid, ref Heights, ref _normals);
}
private void FixedUpdate()
{
var submergedAmount = 0f;
switch (_buoyancyType)
{
case BuoyancyType.PhysicalVoxel:
{
LocalToWorldJob.CompleteJob(_guid);
//Debug.Log("new pass: " + gameObject.name);
Physics.autoSyncTransforms = false;
for (var i = 0; i < _voxels.Length; i++)
BuoyancyForce(_samplePoints[i], _velocity[i], Heights[i].y + waterLevelOffset, ref submergedAmount, ref _debugInfo[i]);
Physics.SyncTransforms();
Physics.autoSyncTransforms = true;
UpdateDrag(submergedAmount);
break;
}
case BuoyancyType.Physical:
//LocalToWorldJob.CompleteJob(_guid);
BuoyancyForce(Vector3.zero, _velocity[0], Heights[0].y + waterLevelOffset, ref submergedAmount, ref _debugInfo[0]);
//UpdateDrag(submergedAmount);
break;
case BuoyancyType.NonPhysical:
break;
case BuoyancyType.NonPhysicalVoxel:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private void LateUpdate() { LocalToWorldConversion(); }
private void OnDestroy()
{
CleanUp();
}
void CleanUp()
{
if (_buoyancyType == BuoyancyType.Physical || _buoyancyType == BuoyancyType.PhysicalVoxel)
{
LocalToWorldJob.Cleanup(_guid);
}
else
{
_samplePoints.Dispose();
}
}
private void LocalToWorldConversion()
{
if (_buoyancyType != BuoyancyType.Physical && _buoyancyType != BuoyancyType.PhysicalVoxel) return;
var transformMatrix = transform.localToWorldMatrix;
LocalToWorldJob.ScheduleJob(_guid, transformMatrix);
}
private void BuoyancyForce(Vector3 position, float3 velocity, float waterHeight, ref float submergedAmount, ref DebugDrawing debug)
{
debug.Position = position;
debug.WaterHeight = waterHeight;
debug.Force = Vector3.zero;
if (!(position.y - voxelResolution < waterHeight)) return;
var k = math.clamp(waterHeight - (position.y - voxelResolution), 0f, 1f);
submergedAmount += k / _voxels.Length;
var localDampingForce = Dampner * _rb.mass * -velocity;
var force = localDampingForce + math.sqrt(k) * _localArchimedesForce;
_rb.AddForceAtPosition(force, position);
debug.Force = force; // For drawing force Gizmos
//Debug.Log(string.Format("Position: {0:f1} -- Force: {1:f2} -- Height: {2:f2}\nVelocity: {3:f2} -- Damp: {4:f2} -- Mass: {5:f1} -- K: {6:f2}", wp, force, waterLevel, velocity, localDampingForce, RB.mass, localArchimedesForce));
}
private void UpdateDrag(float submergedAmount)
{
PercentSubmerged = math.lerp(PercentSubmerged, submergedAmount, 0.25f);
_rb.drag = _baseDrag + _baseDrag * (PercentSubmerged * 10f);
_rb.angularDrag = _baseAngularDrag + PercentSubmerged * 0.5f;
}
private void GetVelocityPoints()
{
for (var i = 0; i < _voxels.Length; i++) { _velocity[i] = _rb.GetPointVelocity(_samplePoints[i]); }
}
private void SliceIntoVoxels()
{
var t = transform;
var rot = t.rotation;
var pos = t.position;
var size = t.localScale;
t.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
t.localScale = Vector3.one;
_voxels = null;
var points = new List<Vector3>();
var rawBounds = VoxelBounds();
_voxelBounds = rawBounds;
_voxelBounds.size = RoundVector(rawBounds.size, voxelResolution);
for (var ix = -_voxelBounds.extents.x; ix < _voxelBounds.extents.x; ix += voxelResolution)
{
for (var iy = -_voxelBounds.extents.y; iy < _voxelBounds.extents.y; iy += voxelResolution)
{
for (var iz = -_voxelBounds.extents.z; iz < _voxelBounds.extents.z; iz += voxelResolution)
{
var x = (voxelResolution * 0.5f) + ix;
var y = (voxelResolution * 0.5f) + iy;
var z = (voxelResolution * 0.5f) + iz;
var p = new Vector3(x, y, z) + _voxelBounds.center;
var inside = false;
foreach (var t1 in colliders)
{
if (PointIsInsideCollider(t1, p))
{
inside = true;
}
}
if(inside)
points.Add(p);
}
}
}
_voxels = points.ToArray();
t.SetPositionAndRotation(pos, rot);
t.localScale = size;
var voxelVolume = Mathf.Pow(voxelResolution, 3f) * _voxels.Length;
var rawVolume = rawBounds.size.x * rawBounds.size.y * rawBounds.size.z;
volume = Mathf.Min(rawVolume, voxelVolume);
density = gameObject.GetComponent<Rigidbody>().mass / volume;
}
private Bounds VoxelBounds()
{
var bounds = new Bounds();
foreach (var nextCollider in colliders)
{
bounds.Encapsulate(nextCollider.bounds);
}
return bounds;
}
private static Vector3 RoundVector(Vector3 vec, float rounding)
{
return new Vector3(Mathf.Ceil(vec.x / rounding) * rounding, Mathf.Ceil(vec.y / rounding) * rounding, Mathf.Ceil(vec.z / rounding) * rounding);
}
private bool PointIsInsideCollider(Collider c, Vector3 p)
{
var cp = Physics.ClosestPoint(p, c, Vector3.zero, Quaternion.identity);
return Vector3.Distance(cp, p) < 0.01f;
}
private void SetupPhysical()
{
if (!TryGetComponent(out _rb))
{
_rb = gameObject.AddComponent<Rigidbody>();
Debug.LogError($"Buoyancy:Object \"{name}\" had no Rigidbody. Rigidbody has been added.");
}
_rb.centerOfMass = centerOfMass + _voxelBounds.center;
_baseDrag = _rb.drag;
_baseAngularDrag = _rb.angularDrag;
_velocity = new float3[_voxels.Length];
var archimedesForceMagnitude = WaterDensity * Mathf.Abs(Physics.gravity.y) * volume;
_localArchimedesForce = new float3(0, archimedesForceMagnitude, 0) / _voxels.Length;
LocalToWorldJob.SetupJob(_guid, _voxels, ref _samplePoints);
}
private void OnDrawGizmosSelected()
{
const float gizmoSize = 0.05f;
var t = transform;
var matrix = Matrix4x4.TRS(t.position, t.rotation, t.lossyScale);
if (_voxels != null)
{
Gizmos.color = Color.yellow;
foreach (var p in _voxels)
{
Gizmos.DrawCube(p, new Vector3(gizmoSize, gizmoSize, gizmoSize));
}
}
Gizmos.matrix = matrix;
if (voxelResolution >= 0.1f)
{
Gizmos.DrawWireCube(_voxelBounds.center, _voxelBounds.size);
Vector3 center = _voxelBounds.center;
float y = center.y - _voxelBounds.extents.y;
for (float x = -_voxelBounds.extents.x; x < _voxelBounds.extents.x; x += voxelResolution)
{
Gizmos.DrawLine(new Vector3(x, y, -_voxelBounds.extents.z + center.z), new Vector3(x, y, _voxelBounds.extents.z + center.z));
}
for (float z = -_voxelBounds.extents.z; z < _voxelBounds.extents.z; z += voxelResolution)
{
Gizmos.DrawLine(new Vector3(-_voxelBounds.extents.x, y, z + center.z), new Vector3(_voxelBounds.extents.x, y, z + center.z));
}
}
else
_voxelBounds = VoxelBounds();
Gizmos.color = Color.red;
Gizmos.DrawSphere(_voxelBounds.center + centerOfMass, 0.2f);
Gizmos.matrix = Matrix4x4.identity;Gizmos.matrix = Matrix4x4.identity;
if (_debugInfo != null)
{
foreach (DebugDrawing debug in _debugInfo)
{
Gizmos.color = Color.cyan;
Gizmos.DrawCube(debug.Position, new Vector3(gizmoSize, gizmoSize, gizmoSize)); // drawCenter
var water = debug.Position;
water.y = debug.WaterHeight;
Gizmos.DrawLine(debug.Position, water); // draw the water line
Gizmos.DrawSphere(water, gizmoSize * 4f);
if(_buoyancyType == BuoyancyType.Physical || _buoyancyType == BuoyancyType.PhysicalVoxel)
{
Gizmos.color = Color.red;
Gizmos.DrawRay(debug.Position, debug.Force / _rb.mass); // draw force
}
}
}
}
private struct DebugDrawing
{
public Vector3 Force;
public Vector3 Position;
public float WaterHeight;
}
public enum BuoyancyType
{
NonPhysical,
NonPhysicalVoxel,
Physical,
PhysicalVoxel
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d5ac9593b59d4847815e4528b52a6d2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a28afa3e85133492baf302b1d3849fe9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,44 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e73e41b71c5f541679f1bb37dee026c1, type: 3}
m_Name: WaterResources
m_EditorClassIdentifier:
defaultFoamRamp: {fileID: 2800000, guid: 4098762bce32f44b9b7c56531d1a988e, type: 3}
defaultFoamMap: {fileID: 2800000, guid: ca439406e806ecf44a9586510be9477d, type: 3}
defaultSurfaceMap: {fileID: 2800000, guid: f395b7d64f44848c6a3cba1f1173fe90, type: 3}
defaultSeaMaterial: {fileID: 2100000, guid: 4849d4d15a9cb42eb9533ae935e2630b, type: 2}
defaultWaterMeshes:
- {fileID: 4300036, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300050, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300038, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300048, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300040, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300046, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300042, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300044, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300026, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300024, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300022, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300020, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300018, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300014, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300012, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300006, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300004, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300034, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300032, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300030, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300028, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300010, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300016, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300008, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
- {fileID: 4300002, guid: 074aa8ca109924e18baf19f3e26665b6, type: 3}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 40615c805d6e84ce7800418367985fe1
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,19 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace WaterSystem
{
/// <summary>
/// This scriptable object holds default resources for the water rendering
/// </summary>
[System.Serializable][CreateAssetMenu(fileName = "WaterResources", menuName = "WaterSystem/Resource", order = 0)]
public class WaterResources : ScriptableObject
{
public Texture2D defaultFoamRamp; // a default foam ramp for the basic foam setting
public Texture2D defaultFoamMap; // a default foam texture map
public Texture2D defaultSurfaceMap; // a default normal/caustic map
public Material defaultSeaMaterial;
public Mesh[] defaultWaterMeshes;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e73e41b71c5f541679f1bb37dee026c1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,26 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7ad544ee67a064d3eb407c8c47c67b8d, type: 3}
m_Name: WaterSettingsData
m_EditorClassIdentifier:
waterGeomType: 0
refType: 2
planarSettings:
m_ResolutionMultiplier: 1
m_ClipPlaneOffset: 0.24
m_ReflectLayers:
serializedVersion: 2
m_Bits: 4294967295
m_Shadows: 0
cubemapRefType: {fileID: 8900000, guid: a90d3c9b1c3ed4d36bffc02f6b335464, type: 3}
isInfinite: 0
originOffset: {x: 0, y: 0, z: 500, w: 500}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a4cacf74654e54b35b9eaea74fd20b4e
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,43 @@
using UnityEngine;
using UnityEngine.Rendering.Universal;
namespace WaterSystem.Data
{
/// <summary>
/// This scriptable object stores teh graphical/rendering settings for a water system
/// </summary>
[System.Serializable][CreateAssetMenu(fileName = "WaterSettingsData", menuName = "WaterSystem/Settings", order = 0)]
public class WaterSettingsData : ScriptableObject
{
public GeometryType waterGeomType; // The type of geometry, either vertex offset or tessellation
public ReflectionType refType = ReflectionType.PlanarReflection; // How the reflecitons are generated
// planar
public PlanarReflections.PlanarReflectionSettings planarSettings; // Planar reflection settings
// cubemap
public Cubemap cubemapRefType; // custom cubemap reference
public bool isInfinite; // Is the water infinite (shader incomplete)
public Vector4 originOffset = new Vector4(0f, 0f, 500f, 500f);
}
/// <summary>
/// The type of reflection source, custom cubemap, closest refelction probe, planar reflection
/// </summary>
[System.Serializable]
public enum ReflectionType
{
Cubemap,
ReflectionProbe,
PlanarReflection
}
/// <summary>
/// The type of geometry, either vertex offset or tessellation
/// </summary>
[System.Serializable]
public enum GeometryType
{
VertexOffset,
Tesselation
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7ad544ee67a064d3eb407c8c47c67b8d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,258 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b17284ebf5e80436eb6089f8c7459dab, type: 3}
m_Name: WaterSurfaceData
m_EditorClassIdentifier:
_waterMaxVisibility: 16.2
_absorptionRamp:
serializedVersion: 2
key0: {r: 1, g: 1, b: 1, a: 1}
key1: {r: 0.18396229, g: 0.89963174, b: 1, a: 1}
key2: {r: 0.021626905, g: 0.49048996, b: 0.509434, a: 0}
key3: {r: 0, g: 0.275, b: 0.44, a: 0}
key4: {r: 0, g: 0, b: 0, a: 0}
key5: {r: 0, g: 0, b: 0, a: 0}
key6: {r: 0, g: 0, b: 0, a: 0}
key7: {r: 0, g: 0, b: 0, a: 0}
ctime0: 0
ctime1: 6746
ctime2: 20817
ctime3: 43581
ctime4: 65535
ctime5: 0
ctime6: 0
ctime7: 0
atime0: 0
atime1: 65535
atime2: 0
atime3: 0
atime4: 0
atime5: 0
atime6: 0
atime7: 0
m_Mode: 0
m_NumColorKeys: 5
m_NumAlphaKeys: 2
_scatterRamp:
serializedVersion: 2
key0: {r: 0, g: 0, b: 0, a: 1}
key1: {r: 0.07843137, g: 0.24471705, b: 0.4117647, a: 1}
key2: {r: 0.12549019, g: 0.50673026, b: 0.58431375, a: 0}
key3: {r: 0.28888598, g: 0.45325178, b: 0.542, a: 0}
key4: {r: 0.19571698, g: 0.3647036, b: 0.45099998, a: 0}
key5: {r: 0, g: 0, b: 0, a: 0}
key6: {r: 0, g: 0, b: 0, a: 0}
key7: {r: 0, g: 0, b: 0, a: 0}
ctime0: 0
ctime1: 3277
ctime2: 24672
ctime3: 52043
ctime4: 63029
ctime5: 0
ctime6: 0
ctime7: 0
atime0: 0
atime1: 65535
atime2: 0
atime3: 0
atime4: 0
atime5: 0
atime6: 0
atime7: 0
m_Mode: 0
m_NumColorKeys: 4
m_NumAlphaKeys: 2
_waves:
- amplitude: 1
direction: -6.03
wavelength: 10
origin:
x: -146.4
y: 326.2
onmiDir: 1
- amplitude: 0.9
direction: 166
wavelength: 8
origin:
x: 0
y: 0
onmiDir: 0
- amplitude: 0.5
direction: 6.532788
wavelength: 7.455916
origin:
x: -63.14
y: 931.6
onmiDir: 1
- amplitude: 0.7
direction: 4.051435
wavelength: 4.6626315
origin:
x: -72.74
y: -16.01
onmiDir: 1
- amplitude: 0.5
direction: 180
wavelength: 2.6733496
origin:
x: -50.43
y: 0
onmiDir: 0
- amplitude: 0.5
direction: -151
wavelength: 2.9848163
origin:
x: 0
y: 0
onmiDir: 0
_customWaves: 0
randomSeed: 891
_basicWaveSettings:
numWaves: 6
amplitude: 0.6
direction: -176
wavelength: 8
_foamSettings:
foamType: 1
basicFoam:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0.16228558
outSlope: 0.16228558
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0.15316528
- serializedVersion: 3
time: 0.6247411
value: 0.39698213
inSlope: 1.2551872
outSlope: 1.2551872
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.28785437
- serializedVersion: 3
time: 0.8523668
value: 0.81129223
inSlope: 1.9929253
outSlope: 1.9929253
tangentMode: 0
weightedMode: 0
inWeight: 0.27977753
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 1
inSlope: 0.6611113
outSlope: 0.6611113
tangentMode: 0
weightedMode: 0
inWeight: 0.55429333
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
liteFoam:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0.2
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 0.4
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 0.7
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
mediumFoam:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0.4
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 0.7
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
denseFoam:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0.7
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
_init: 1
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 846f3dc3b5341447a86687211f741bc7
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,86 @@
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
namespace WaterSystem.Data
{
/// <summary>
/// This scriptable object contains setting for how the water looks visually
/// </summary>
[System.Serializable][CreateAssetMenu(fileName = "WaterSurfaceData", menuName = "WaterSystem/Surface Data", order = 0)]
public class WaterSurfaceData : ScriptableObject
{
public float _waterMaxVisibility = 40.0f;
public Gradient _absorptionRamp;
public Gradient _scatterRamp;
public List<Wave> _waves = new List<Wave>();
public bool _customWaves = false;
public int randomSeed = 3234;
public BasicWaves _basicWaveSettings = new BasicWaves(1.5f, 45.0f, 5.0f);
public FoamSettings _foamSettings = new FoamSettings();
[SerializeField]
public bool _init = false;
}
[System.Serializable]
public struct Wave
{
public float amplitude; // height of the wave in units(m)
public float direction; // direction the wave travels in degrees from Z+
public float wavelength; // distance between crest>crest
public float2 origin; // Omi directional point of origin
public float onmiDir; // Is omni?
public Wave(float amp, float dir, float length, float2 org, bool omni)
{
amplitude = amp;
direction = dir;
wavelength = length;
origin = org;
onmiDir = omni ? 1 : 0;
}
}
[System.Serializable]
public class BasicWaves
{
public int numWaves = 6;
public float amplitude;
public float direction;
public float wavelength;
public BasicWaves(float amp, float dir, float len)
{
numWaves = 6;
amplitude = amp;
direction = dir;
wavelength = len;
}
}
[System.Serializable]
public class FoamSettings
{
public int foamType; // 0=default, 1=simple, 3=custom
public AnimationCurve basicFoam;
public AnimationCurve liteFoam;
public AnimationCurve mediumFoam;
public AnimationCurve denseFoam;
// Foam curves
public FoamSettings()
{
foamType = 0;
basicFoam = new AnimationCurve(new Keyframe[2]{new Keyframe(0.25f, 0f),
new Keyframe(1f, 1f)});
liteFoam = new AnimationCurve(new Keyframe[3]{new Keyframe(0.2f, 0f),
new Keyframe(0.4f, 1f),
new Keyframe(0.7f, 0f)});
mediumFoam = new AnimationCurve(new Keyframe[3]{new Keyframe(0.4f, 0f),
new Keyframe(0.7f, 1f),
new Keyframe(1f, 0f)});
denseFoam = new AnimationCurve(new Keyframe[2]{new Keyframe(0.7f, 0f),
new Keyframe(1f, 1f)});
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b17284ebf5e80436eb6089f8c7459dab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,206 @@
using System.Collections.Generic;
using UnityEngine;
using Unity.Jobs;
using Unity.Burst;
using Unity.Mathematics;
using Unity.Collections;
using UnityEngine.Rendering.Universal;
using WaterSystem.Data;
namespace WaterSystem
{
/// <summary>
/// C# Jobs system version of the Gerstner waves implimentation
/// </summary>
public static class GerstnerWavesJobs
{
//General variables
public static bool Initialized;
private static bool _firstFrame = true;
private static bool _processing;
private static int _waveCount;
private static NativeArray<Wave> _waveData; // Wave data from the water system
//Details for Buoyant Objects
private static NativeArray<float3> _positions;
private static int _positionCount;
private static NativeArray<float3> _wavePos;
private static NativeArray<float3> _waveNormal;
private static JobHandle _waterHeightHandle;
static readonly Dictionary<int, int2> Registry = new Dictionary<int, int2>();
public static void Init()
{
if(Debug.isDebugBuild)
Debug.Log("Initializing Gerstner Waves Jobs");
//Wave data
_waveCount = Water.Instance._waves.Length;
_waveData = new NativeArray<Wave>(_waveCount, Allocator.Persistent);
for (var i = 0; i < _waveData.Length; i++)
{
_waveData[i] = Water.Instance._waves[i];
}
_positions = new NativeArray<float3>(4096, Allocator.Persistent);
_wavePos = new NativeArray<float3>(4096, Allocator.Persistent);
_waveNormal = new NativeArray<float3>(4096, Allocator.Persistent);
Initialized = true;
}
public static void Cleanup()
{
if(Debug.isDebugBuild)
Debug.Log("Cleaning up Gerstner Wave Jobs");
_waterHeightHandle.Complete();
//Cleanup native arrays
_waveData.Dispose();
_positions.Dispose();
_wavePos.Dispose();
_waveNormal.Dispose();
}
public static void UpdateSamplePoints(ref NativeArray<float3> samplePoints, int guid)
{
CompleteJobs();
if (Registry.TryGetValue(guid, out var offsets))
{
for (var i = offsets.x; i < offsets.y; i++) _positions[i] = samplePoints[i - offsets.x];
}
else
{
if (_positionCount + samplePoints.Length >= _positions.Length) return;
offsets = new int2(_positionCount, _positionCount + samplePoints.Length);
Registry.Add(guid, offsets);
_positionCount += samplePoints.Length;
}
}
public static void GetData(int guid, ref float3[] outPos, ref float3[] outNorm)
{
if (!Registry.TryGetValue(guid, out var offsets)) return;
_wavePos.Slice(offsets.x, offsets.y - offsets.x).CopyTo(outPos);
if(outNorm != null)
_waveNormal.Slice(offsets.x, offsets.y - offsets.x).CopyTo(outNorm);
}
// Height jobs for the next frame
public static void UpdateHeights()
{
if (_processing) return;
_processing = true;
#if STATIC_EVERYTHING
var t = 0.0f;
#else
var t = Time.time;
#endif
// Buoyant Object Job
var waterHeight = new HeightJob()
{
WaveData = _waveData,
Position = _positions,
OffsetLength = new int2(0, _positions.Length),
Time = t,
OutPosition = _wavePos,
OutNormal = _waveNormal
};
_waterHeightHandle = waterHeight.Schedule(_positionCount, 32);
JobHandle.ScheduleBatchedJobs();
_firstFrame = false;
}
private static void CompleteJobs()
{
if (_firstFrame || !_processing) return;
_waterHeightHandle.Complete();
_processing = false;
}
// Gerstner Height C# Job
[BurstCompile]
private struct HeightJob : IJobParallelFor
{
[ReadOnly]
public NativeArray<Wave> WaveData; // wave data stroed in vec4's like the shader version but packed into one
[ReadOnly]
public NativeArray<float3> Position;
[WriteOnly]
public NativeArray<float3> OutPosition;
[WriteOnly]
public NativeArray<float3> OutNormal;
[ReadOnly]
public float Time;
[ReadOnly]
public int2 OffsetLength;
// The code actually running on the job
public void Execute(int i)
{
if (i < OffsetLength.x || i >= OffsetLength.y - OffsetLength.x) return;
var waveCountMulti = 1f / WaveData.Length;
var wavePos = new float3(0f, 0f, 0f);
var waveNorm = new float3(0f, 0f, 0f);
for (var wave = 0; wave < WaveData.Length; wave++) // for each wave
{
// Wave data vars
var pos = Position[i].xz;
var amplitude = WaveData[wave].amplitude;
var direction = WaveData[wave].direction;
var wavelength = WaveData[wave].wavelength;
var omniPos = WaveData[wave].origin;
////////////////////////////////wave value calculations//////////////////////////
var w = 6.28318f / wavelength; // 2pi over wavelength(hardcoded)
var wSpeed = math.sqrt(9.8f * w); // frequency of the wave based off wavelength
const float peak = 0.8f; // peak value, 1 is the sharpest peaks
var qi = peak / (amplitude * w * WaveData.Length);
var windDir = new float2(0f, 0f);
direction = math.radians(direction); // convert the incoming degrees to radians
var windDirInput = new float2(math.sin(direction), math.cos(direction)) * (1 - WaveData[wave].onmiDir); // calculate wind direction - TODO - currently radians
var windOmniInput = (pos - omniPos) * WaveData[wave].onmiDir;
windDir += windDirInput;
windDir += windOmniInput;
windDir = math.normalize(windDir);
var dir = math.dot(windDir, pos - (omniPos * WaveData[wave].onmiDir));
////////////////////////////position output calculations/////////////////////////
var calc = dir * w + -Time * wSpeed; // the wave calculation
var cosCalc = math.cos(calc); // cosine version(used for horizontal undulation)
var sinCalc = math.sin(calc); // sin version(used for vertical undulation)
// calculate the offsets for the current point
wavePos.x += qi * amplitude * windDir.x * cosCalc;
wavePos.z += qi * amplitude * windDir.y * cosCalc;
wavePos.y += sinCalc * amplitude * waveCountMulti; // the height is divided by the number of waves
////////////////////////////normal output calculations/////////////////////////
var wa = w * amplitude;
// normal vector
var norm = new float3(-(windDir.xy * wa * cosCalc),
1 - (qi * wa * sinCalc));
waveNorm += (norm * waveCountMulti) * amplitude;
}
OutPosition[i] = wavePos;
OutNormal[i] = math.normalize(waveNorm.xzy);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e09f45dd7e088412e85c5924433ad4ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,87 @@
using System.Collections.Generic;
using UnityEngine;
using Unity.Collections;
using Unity.Jobs;
using Unity.Burst;
using Unity.Mathematics;
public static class LocalToWorldJob
{
private static readonly Dictionary<int, TransformLocalToWorld> Data = new Dictionary<int, TransformLocalToWorld>();
[BurstCompile]
struct LocalToWorldConvertJob : IJob
{
[WriteOnly] public NativeArray<float3> PositionsWorld;
[ReadOnly] public Matrix4x4 Matrix;
[ReadOnly] public NativeArray<float3> PositionsLocal;
// The code actually running on the job
public void Execute()
{
for (var i = 0; i < PositionsLocal.Length; i++)
{
var pos = float4.zero;
pos.xyz = PositionsLocal[i];
pos.w = 1f;
pos = Matrix * pos;
PositionsWorld[i] = pos.xyz;
}
}
}
public static void SetupJob(int guid, Vector3[] positions, ref NativeArray<float3> output)
{
var jobData = new TransformLocalToWorld
{
PositionsWorld = output,
PositionsLocal = new NativeArray<float3>(positions.Length, Allocator.Persistent)
};
for (var i = 0; i < positions.Length; i++)
jobData.PositionsLocal[i] = positions[i];
Data.Add(guid, jobData);
}
public static void ScheduleJob(int guid, Matrix4x4 localToWorld)
{
if (Data[guid].Processing)
return;
Data[guid].Job = new LocalToWorldConvertJob()
{
PositionsWorld = Data[guid].PositionsWorld,
PositionsLocal = Data[guid].PositionsLocal,
Matrix = localToWorld
};
Data[guid].Handle = Data[guid].Job.Schedule();
Data[guid].Processing = true;
JobHandle.ScheduleBatchedJobs();
}
public static void CompleteJob(int guid)
{
Data[guid].Handle.Complete();
Data[guid].Processing = false;
}
public static void Cleanup(int guid)
{
if (!Data.ContainsKey(guid)) return;
Data[guid].Handle.Complete();
Data[guid].PositionsWorld.Dispose();
Data[guid].PositionsLocal.Dispose();
Data.Remove(guid);
}
class TransformLocalToWorld
{
public NativeArray<float3> PositionsLocal;
public NativeArray<float3> PositionsWorld;
public JobHandle Handle;
public LocalToWorldConvertJob Job;
public bool Processing;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3813a158650064768b37058dc089214a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,43 @@
using UnityEngine;
using UnityEngine.Rendering;
namespace WaterSystem
{
/// <summary>
/// Camera script to align the water mesh with the camera in a quantized manner
/// </summary>
[ExecuteInEditMode]
public class MainCameraAlign : MonoBehaviour
{
public float quantizeValue = 6.25f;
public float forwards = 10f;
public float yOffset = -0.25f;
private void OnEnable()
{
RenderPipelineManager.beginCameraRendering += UpdatePosition;
}
private void OnDisable()
{
RenderPipelineManager.beginCameraRendering -= UpdatePosition;
}
private void UpdatePosition(ScriptableRenderContext src, Camera cam)
{
if (cam.cameraType == CameraType.Preview) return;
var newPos = cam.transform.TransformPoint(Vector3.forward * forwards);
newPos.y = yOffset;
newPos.x = QuantizeValue(newPos.x);
newPos.z = QuantizeValue(newPos.z);
transform.position = newPos;
}
private float QuantizeValue(float value)
{
return quantizeValue * (int) (value / quantizeValue);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a6b4e1e324e641ff86ca879104e20de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ceb62181994bc49eb8ac45fd7f5c4634
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,291 @@
using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Serialization;
using Unity.Mathematics;
namespace UnityEngine.Rendering.Universal
{
[ExecuteAlways]
public class PlanarReflections : MonoBehaviour
{
[Serializable]
public enum ResolutionMulltiplier
{
Full,
Half,
Third,
Quarter
}
[Serializable]
public class PlanarReflectionSettings
{
public ResolutionMulltiplier m_ResolutionMultiplier = ResolutionMulltiplier.Third;
public float m_ClipPlaneOffset = 0.07f;
public LayerMask m_ReflectLayers = -1;
public bool m_Shadows;
}
[SerializeField]
public PlanarReflectionSettings m_settings = new PlanarReflectionSettings();
public GameObject target;
[FormerlySerializedAs("camOffset")] public float m_planeOffset;
private static Camera _reflectionCamera;
private RenderTexture _reflectionTexture;
private readonly int _planarReflectionTextureId = Shader.PropertyToID("_PlanarReflectionTexture");
private int2 _oldReflectionTextureSize;
public static event Action<ScriptableRenderContext, Camera> BeginPlanarReflections;
private void OnEnable()
{
RenderPipelineManager.beginCameraRendering += ExecutePlanarReflections;
}
// Cleanup all the objects we possibly have created
private void OnDisable()
{
Cleanup();
}
private void OnDestroy()
{
Cleanup();
}
private void Cleanup()
{
RenderPipelineManager.beginCameraRendering -= ExecutePlanarReflections;
if(_reflectionCamera)
{
_reflectionCamera.targetTexture = null;
SafeDestroy(_reflectionCamera.gameObject);
}
if (_reflectionTexture)
{
RenderTexture.ReleaseTemporary(_reflectionTexture);
}
}
private static void SafeDestroy(Object obj)
{
if (Application.isEditor)
{
DestroyImmediate(obj);
}
else
{
Destroy(obj);
}
}
private void UpdateCamera(Camera src, Camera dest)
{
if (dest == null) return;
dest.CopyFrom(src);
dest.useOcclusionCulling = false;
if (dest.gameObject.TryGetComponent(out UniversalAdditionalCameraData camData))
{
camData.renderShadows = m_settings.m_Shadows; // turn off shadows for the reflection camera
}
}
private void UpdateReflectionCamera(Camera realCamera)
{
if (_reflectionCamera == null)
_reflectionCamera = CreateMirrorObjects();
// find out the reflection plane: position and normal in world space
Vector3 pos = Vector3.zero;
Vector3 normal = Vector3.up;
if (target != null)
{
pos = target.transform.position + Vector3.up * m_planeOffset;
normal = target.transform.up;
}
UpdateCamera(realCamera, _reflectionCamera);
// Render reflection
// Reflect camera around reflection plane
var d = -Vector3.Dot(normal, pos) - m_settings.m_ClipPlaneOffset;
var reflectionPlane = new Vector4(normal.x, normal.y, normal.z, d);
var reflection = Matrix4x4.identity;
reflection *= Matrix4x4.Scale(new Vector3(1, -1, 1));
CalculateReflectionMatrix(ref reflection, reflectionPlane);
var oldPosition = realCamera.transform.position - new Vector3(0, pos.y * 2, 0);
var newPosition = ReflectPosition(oldPosition);
_reflectionCamera.transform.forward = Vector3.Scale(realCamera.transform.forward, new Vector3(1, -1, 1));
_reflectionCamera.worldToCameraMatrix = realCamera.worldToCameraMatrix * reflection;
// Setup oblique projection matrix so that near plane is our reflection
// plane. This way we clip everything below/above it for free.
var clipPlane = CameraSpacePlane(_reflectionCamera, pos - Vector3.up * 0.1f, normal, 1.0f);
var projection = realCamera.CalculateObliqueMatrix(clipPlane);
_reflectionCamera.projectionMatrix = projection;
_reflectionCamera.cullingMask = m_settings.m_ReflectLayers; // never render water layer
_reflectionCamera.transform.position = newPosition;
}
// Calculates reflection matrix around the given plane
private static 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;
}
private static Vector3 ReflectPosition(Vector3 pos)
{
var newPos = new Vector3(pos.x, -pos.y, pos.z);
return newPos;
}
private float GetScaleValue()
{
switch(m_settings.m_ResolutionMultiplier)
{
case ResolutionMulltiplier.Full:
return 1f;
case ResolutionMulltiplier.Half:
return 0.5f;
case ResolutionMulltiplier.Third:
return 0.33f;
case ResolutionMulltiplier.Quarter:
return 0.25f;
default:
return 0.5f; // default to half res
}
}
// Compare two int2
private static bool Int2Compare(int2 a, int2 b)
{
return a.x == b.x && a.y == b.y;
}
// Given position/normal of the plane, calculates plane in camera space.
private Vector4 CameraSpacePlane(Camera cam, Vector3 pos, Vector3 normal, float sideSign)
{
var offsetPos = pos + normal * m_settings.m_ClipPlaneOffset;
var m = cam.worldToCameraMatrix;
var cameraPosition = m.MultiplyPoint(offsetPos);
var cameraNormal = m.MultiplyVector(normal).normalized * sideSign;
return new Vector4(cameraNormal.x, cameraNormal.y, cameraNormal.z, -Vector3.Dot(cameraPosition, cameraNormal));
}
private Camera CreateMirrorObjects()
{
var go = new GameObject("Planar Reflections",typeof(Camera));
var cameraData = go.AddComponent(typeof(UniversalAdditionalCameraData)) as UniversalAdditionalCameraData;
cameraData.requiresColorOption = CameraOverrideOption.Off;
cameraData.requiresDepthOption = CameraOverrideOption.Off;
cameraData.SetRenderer(1);
var t = transform;
var reflectionCamera = go.GetComponent<Camera>();
reflectionCamera.transform.SetPositionAndRotation(t.position, t.rotation);
reflectionCamera.depth = -10;
reflectionCamera.enabled = false;
go.hideFlags = HideFlags.HideAndDontSave;
return reflectionCamera;
}
private void PlanarReflectionTexture(Camera cam)
{
if (_reflectionTexture == null)
{
var res = ReflectionResolution(cam, UniversalRenderPipeline.asset.renderScale);
bool useHdr10 = RenderingUtils.SupportsRenderTextureFormat(RenderTextureFormat.RGB111110Float);
RenderTextureFormat hdrFormat = useHdr10 ? RenderTextureFormat.RGB111110Float : RenderTextureFormat.DefaultHDR;
_reflectionTexture = RenderTexture.GetTemporary(res.x, res.y, 16,
GraphicsFormatUtility.GetGraphicsFormat(hdrFormat, true));
}
_reflectionCamera.targetTexture = _reflectionTexture;
}
private int2 ReflectionResolution(Camera cam, float scale)
{
var x = (int)(cam.pixelWidth * scale * GetScaleValue());
var y = (int)(cam.pixelHeight * scale * GetScaleValue());
return new int2(x, y);
}
private void ExecutePlanarReflections(ScriptableRenderContext context, Camera camera)
{
// we dont want to render planar reflections in reflections or previews
if (camera.cameraType == CameraType.Reflection || camera.cameraType == CameraType.Preview)
return;
UpdateReflectionCamera(camera); // create reflected camera
PlanarReflectionTexture(camera); // create and assign RenderTexture
var data = new PlanarReflectionSettingData(); // save quality settings and lower them for the planar reflections
data.Set(); // set quality settings
Shader.EnableKeyword("_PLANAR_REFLECTION_CAMERA");
BeginPlanarReflections?.Invoke(context, _reflectionCamera); // callback Action for PlanarReflection
UniversalRenderPipeline.RenderSingleCamera(context, _reflectionCamera); // render planar reflections
data.Restore(); // restore the quality settings
Shader.SetGlobalTexture(_planarReflectionTextureId, _reflectionTexture); // Assign texture to water shader
Shader.DisableKeyword("_PLANAR_REFLECTION_CAMERA");
}
class PlanarReflectionSettingData
{
private readonly bool _fog;
private readonly int _maxLod;
private readonly float _lodBias;
public PlanarReflectionSettingData()
{
_fog = RenderSettings.fog;
_maxLod = QualitySettings.maximumLODLevel;
_lodBias = QualitySettings.lodBias;
}
public void Set()
{
GL.invertCulling = true;
RenderSettings.fog = false; // disable fog for now as it's incorrect with projection
QualitySettings.maximumLODLevel = 1;
QualitySettings.lodBias = _lodBias * 0.5f;
}
public void Restore()
{
GL.invertCulling = false;
RenderSettings.fog = _fog;
QualitySettings.maximumLODLevel = _maxLod;
QualitySettings.lodBias = _lodBias;
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3971d89a730cd5846a1944ab0228bf76
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,234 @@
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace WaterSystem
{
public class WaterSystemFeature : ScriptableRendererFeature
{
#region Water Effects Pass
class WaterFxPass : ScriptableRenderPass
{
private const string k_RenderWaterFXTag = "Render Water FX";
private ProfilingSampler m_WaterFX_Profile = new ProfilingSampler(k_RenderWaterFXTag);
private readonly ShaderTagId m_WaterFXShaderTag = new ShaderTagId("WaterFX");
private readonly Color m_ClearColor = new Color(0.0f, 0.5f, 0.5f, 0.5f); //r = foam mask, g = normal.x, b = normal.z, a = displacement
private FilteringSettings m_FilteringSettings;
private RTHandle m_WaterFX;
public WaterFxPass()
{
m_WaterFX=RTHandles.Alloc("_WaterFXMap", "_WaterFXMap");
// only wanting to render transparent objects
m_FilteringSettings = new FilteringSettings(RenderQueueRange.transparent);
}
// Calling Configure since we are wanting to render into a RenderTexture and control cleat
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
// no need for a depth buffer
cameraTextureDescriptor.depthBufferBits = 0;
// Half resolution
cameraTextureDescriptor.width /= 2;
cameraTextureDescriptor.height /= 2;
// default format TODO research usefulness of HDR format
cameraTextureDescriptor.colorFormat = RenderTextureFormat.Default;
// get a temp RT for rendering into
cmd.GetTemporaryRT(Shader.PropertyToID(m_WaterFX.name), cameraTextureDescriptor, FilterMode.Bilinear);
ConfigureTarget(m_WaterFX);
// clear the screen with a specific color for the packed data
ConfigureClear(ClearFlag.Color, m_ClearColor);
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
CommandBuffer cmd = CommandBufferPool.Get();
using (new ProfilingScope(cmd, m_WaterFX_Profile)) // makes sure we have profiling ability
{
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
// here we choose renderers based off the "WaterFX" shader pass and also sort back to front
var drawSettings = CreateDrawingSettings(m_WaterFXShaderTag, ref renderingData,
SortingCriteria.CommonTransparent);
// draw all the renderers matching the rules we setup
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref m_FilteringSettings);
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
public override void OnCameraCleanup(CommandBuffer cmd)
{
// since the texture is used within the single cameras use we need to cleanup the RT afterwards
cmd.ReleaseTemporaryRT(Shader.PropertyToID(m_WaterFX.name));
}
}
#endregion
#region Caustics Pass
class WaterCausticsPass : ScriptableRenderPass
{
private const string k_RenderWaterCausticsTag = "Render Water Caustics";
private ProfilingSampler m_WaterCaustics_Profile = new ProfilingSampler(k_RenderWaterCausticsTag);
public Material WaterCausticMaterial;
private static Mesh m_mesh;
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
var cam = renderingData.cameraData.camera;
// Stop the pass rendering in the preview or material missing
if (cam.cameraType == CameraType.Preview || !WaterCausticMaterial)
return;
CommandBuffer cmd = CommandBufferPool.Get();
using (new ProfilingScope(cmd, m_WaterCaustics_Profile))
{
var sunMatrix = RenderSettings.sun != null
? RenderSettings.sun.transform.localToWorldMatrix
: Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(-45f, 45f, 0f), Vector3.one);
WaterCausticMaterial.SetMatrix("_MainLightDir", sunMatrix);
// Create mesh if needed
if (!m_mesh)
m_mesh = GenerateCausticsMesh(1000f);
// Create the matrix to position the caustics mesh.
var position = cam.transform.position;
position.y = 0; // TODO should read a global 'water height' variable.
var matrix = Matrix4x4.TRS(position, Quaternion.identity, Vector3.one);
// Setup the CommandBuffer and draw the mesh with the caustic material and matrix
cmd.DrawMesh(m_mesh, matrix, WaterCausticMaterial, 0, 0);
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
}
#endregion
WaterFxPass m_WaterFxPass;
WaterCausticsPass m_CausticsPass;
public WaterSystemSettings settings = new WaterSystemSettings();
[HideInInspector][SerializeField] private Shader causticShader;
[HideInInspector][SerializeField] private Texture2D causticTexture;
private Material _causticMaterial;
private static readonly int SrcBlend = Shader.PropertyToID("_SrcBlend");
private static readonly int DstBlend = Shader.PropertyToID("_DstBlend");
private static readonly int Size = Shader.PropertyToID("_Size");
private static readonly int CausticTexture = Shader.PropertyToID("_CausticMap");
public override void Create()
{
// WaterFX Pass
m_WaterFxPass = new WaterFxPass {renderPassEvent = RenderPassEvent.BeforeRenderingOpaques};
// Caustic Pass
m_CausticsPass = new WaterCausticsPass();
causticShader = causticShader ? causticShader : Shader.Find("Hidden/BoatAttack/Caustics");
if (causticShader == null) return;
if (_causticMaterial)
{
DestroyImmediate(_causticMaterial);
}
_causticMaterial = CoreUtils.CreateEngineMaterial(causticShader);
_causticMaterial.SetFloat("_BlendDistance", settings.causticBlendDistance);
if (causticTexture == null)
{
Debug.Log("Caustics Texture missing, attempting to load.");
#if UNITY_EDITOR
causticTexture = UnityEditor.AssetDatabase.LoadAssetAtPath<Texture2D>("Packages/com.verasl.water-system/Textures/WaterSurface_single.tif");
#endif
}
_causticMaterial.SetTexture(CausticTexture, causticTexture);
switch (settings.debug)
{
case WaterSystemSettings.DebugMode.Caustics:
_causticMaterial.SetFloat(SrcBlend, 1f);
_causticMaterial.SetFloat(DstBlend, 0f);
_causticMaterial.EnableKeyword("_DEBUG");
m_CausticsPass.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
break;
case WaterSystemSettings.DebugMode.WaterEffects:
break;
case WaterSystemSettings.DebugMode.Disabled:
// Caustics
_causticMaterial.SetFloat(SrcBlend, 2f);
_causticMaterial.SetFloat(DstBlend, 0f);
_causticMaterial.DisableKeyword("_DEBUG");
m_CausticsPass.renderPassEvent = RenderPassEvent.AfterRenderingSkybox + 1;
// WaterEffects
break;
}
_causticMaterial.SetFloat(Size, settings.causticScale);
m_CausticsPass.WaterCausticMaterial = _causticMaterial;
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(m_WaterFxPass);
renderer.EnqueuePass(m_CausticsPass);
}
/// <summary>
/// This function Generates a flat quad for use with the caustics pass.
/// </summary>
/// <param name="size">The length of the quad.</param>
/// <returns></returns>
private static Mesh GenerateCausticsMesh(float size)
{
var m = new Mesh();
size *= 0.5f;
var verts = new[]
{
new Vector3(-size, 0f, -size),
new Vector3(size, 0f, -size),
new Vector3(-size, 0f, size),
new Vector3(size, 0f, size)
};
m.vertices = verts;
var tris = new[]
{
0, 2, 1,
2, 3, 1
};
m.triangles = tris;
return m;
}
[System.Serializable]
public class WaterSystemSettings
{
[Header("Caustics Settings")] [Range(0.1f, 1f)]
public float causticScale = 0.25f;
public float causticBlendDistance = 3f;
[Header("Advanced Settings")] public DebugMode debug = DebugMode.Disabled;
public enum DebugMode
{
Disabled,
WaterEffects,
Caustics
}
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: adbe377f4f72c497faee64bc3f0580b9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- causticShader: {fileID: 4800000, guid: 90939d2e4b62841d29c136c866715501, type: 3}
- causticTexture: {fileID: 2800000, guid: f395b7d64f44848c6a3cba1f1173fe90, type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,402 @@
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.Universal;
using WaterSystem.Data;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;
namespace WaterSystem
{
[ExecuteAlways]
public class Water : MonoBehaviour
{
// Singleton
private static Water _instance;
public static Water Instance
{
get
{
if (_instance == null)
_instance = (Water)FindObjectOfType(typeof(Water));
return _instance;
}
}
// Script references
private PlanarReflections _planarReflections;
private bool _useComputeBuffer;
public bool computeOverride;
[SerializeField] RenderTexture _depthTex;
public Texture bakedDepthTex;
private Camera _depthCam;
private Texture2D _rampTexture;
[SerializeField]
public Wave[] _waves;
[SerializeField]
private ComputeBuffer waveBuffer;
private float _maxWaveHeight;
private float _waveHeight;
[SerializeField]
public WaterSettingsData settingsData;
[SerializeField]
public WaterSurfaceData surfaceData;
[SerializeField]
private WaterResources resources;
private static readonly int CameraRoll = Shader.PropertyToID("_CameraRoll");
private static readonly int InvViewProjection = Shader.PropertyToID("_InvViewProjection");
private static readonly int WaterDepthMap = Shader.PropertyToID("_WaterDepthMap");
private static readonly int FoamMap = Shader.PropertyToID("_FoamMap");
private static readonly int SurfaceMap = Shader.PropertyToID("_SurfaceMap");
private static readonly int WaveHeight = Shader.PropertyToID("_WaveHeight");
private static readonly int MaxWaveHeight = Shader.PropertyToID("_MaxWaveHeight");
private static readonly int MaxDepth = Shader.PropertyToID("_MaxDepth");
private static readonly int WaveCount = Shader.PropertyToID("_WaveCount");
private static readonly int CubemapTexture = Shader.PropertyToID("_CubemapTexture");
private static readonly int WaveDataBuffer = Shader.PropertyToID("_WaveDataBuffer");
private static readonly int WaveData = Shader.PropertyToID("waveData");
private static readonly int AbsorptionScatteringRamp = Shader.PropertyToID("_AbsorptionScatteringRamp");
private static readonly int DepthCamZParams = Shader.PropertyToID("_VeraslWater_DepthCamParams");
private void OnEnable()
{
if (!computeOverride)
_useComputeBuffer = SystemInfo.supportsComputeShaders &&
Application.platform != RuntimePlatform.WebGLPlayer &&
Application.platform != RuntimePlatform.Android;
else
_useComputeBuffer = false;
Init();
RenderPipelineManager.beginCameraRendering += BeginCameraRendering;
if(resources == null)
{
resources = Resources.Load("WaterResources") as WaterResources;
}
}
private void OnDisable() {
Cleanup();
}
private void OnApplicationQuit()
{
GerstnerWavesJobs.Cleanup();
}
void Cleanup()
{
RenderPipelineManager.beginCameraRendering -= BeginCameraRendering;
if (_depthCam)
{
_depthCam.targetTexture = null;
SafeDestroy(_depthCam.gameObject);
}
if (_depthTex)
{
SafeDestroy(_depthTex);
}
waveBuffer?.Dispose();
}
private void BeginCameraRendering(ScriptableRenderContext src, Camera cam)
{
if (cam.cameraType == CameraType.Preview) return;
var roll = cam.transform.localEulerAngles.z;
Shader.SetGlobalFloat(CameraRoll, roll);
Shader.SetGlobalMatrix(InvViewProjection,
(GL.GetGPUProjectionMatrix(cam.projectionMatrix, false) * cam.worldToCameraMatrix).inverse);
// Water matrix
const float quantizeValue = 6.25f;
const float forwards = 10f;
const float yOffset = -0.25f;
var newPos = cam.transform.TransformPoint(Vector3.forward * forwards);
newPos.y = yOffset;
newPos.x = quantizeValue * (int) (newPos.x / quantizeValue);
newPos.z = quantizeValue * (int) (newPos.z / quantizeValue);
var matrix = Matrix4x4.TRS(newPos + transform.position, Quaternion.identity, transform.localScale); // transform.localToWorldMatrix;
foreach (var mesh in resources.defaultWaterMeshes)
{
Graphics.DrawMesh(mesh,
matrix,
resources.defaultSeaMaterial,
gameObject.layer,
cam,
0,
null,
ShadowCastingMode.Off,
true,
null,
LightProbeUsage.Off,
null);
}
}
private static void SafeDestroy(Object o)
{
if(Application.isPlaying)
Destroy(o);
else
DestroyImmediate(o);
}
public void Init()
{
SetWaves();
GenerateColorRamp();
if (bakedDepthTex)
{
Shader.SetGlobalTexture(WaterDepthMap, bakedDepthTex);
}
if (!gameObject.TryGetComponent(out _planarReflections))
{
_planarReflections = gameObject.AddComponent<PlanarReflections>();
}
_planarReflections.hideFlags = HideFlags.HideAndDontSave | HideFlags.HideInInspector;
_planarReflections.m_settings = settingsData.planarSettings;
_planarReflections.enabled = settingsData.refType == ReflectionType.PlanarReflection;
if(resources == null)
{
resources = Resources.Load("WaterResources") as WaterResources;
}
if(Application.platform != RuntimePlatform.WebGLPlayer) // TODO - bug with Opengl depth
CaptureDepthMap();
}
private void LateUpdate()
{
GerstnerWavesJobs.UpdateHeights();
}
public void FragWaveNormals(bool toggle)
{
var mat = GetComponent<Renderer>().sharedMaterial;
if (toggle)
mat.EnableKeyword("GERSTNER_WAVES");
else
mat.DisableKeyword("GERSTNER_WAVES");
}
private void SetWaves()
{
SetupWaves(surfaceData._customWaves);
// set default resources
Shader.SetGlobalTexture(FoamMap, resources.defaultFoamMap);
Shader.SetGlobalTexture(SurfaceMap, resources.defaultSurfaceMap);
_maxWaveHeight = 0f;
foreach (var w in _waves)
{
_maxWaveHeight += w.amplitude;
}
_maxWaveHeight /= _waves.Length;
_waveHeight = transform.position.y;
Shader.SetGlobalFloat(WaveHeight, _waveHeight);
Shader.SetGlobalFloat(MaxWaveHeight, _maxWaveHeight);
Shader.SetGlobalFloat(MaxDepth, surfaceData._waterMaxVisibility);
switch(settingsData.refType)
{
case ReflectionType.Cubemap:
Shader.EnableKeyword("_REFLECTION_CUBEMAP");
Shader.DisableKeyword("_REFLECTION_PROBES");
Shader.DisableKeyword("_REFLECTION_PLANARREFLECTION");
Shader.SetGlobalTexture(CubemapTexture, settingsData.cubemapRefType);
break;
case ReflectionType.ReflectionProbe:
Shader.DisableKeyword("_REFLECTION_CUBEMAP");
Shader.EnableKeyword("_REFLECTION_PROBES");
Shader.DisableKeyword("_REFLECTION_PLANARREFLECTION");
break;
case ReflectionType.PlanarReflection:
Shader.DisableKeyword("_REFLECTION_CUBEMAP");
Shader.DisableKeyword("_REFLECTION_PROBES");
Shader.EnableKeyword("_REFLECTION_PLANARREFLECTION");
break;
default:
throw new ArgumentOutOfRangeException();
}
Shader.SetGlobalInt(WaveCount, _waves.Length);
//GPU side
if (_useComputeBuffer)
{
Shader.EnableKeyword("USE_STRUCTURED_BUFFER");
waveBuffer?.Dispose();
waveBuffer = new ComputeBuffer(10, (sizeof(float) * 6));
waveBuffer.SetData(_waves);
Shader.SetGlobalBuffer(WaveDataBuffer, waveBuffer);
}
else
{
Shader.DisableKeyword("USE_STRUCTURED_BUFFER");
Shader.SetGlobalVectorArray(WaveData, GetWaveData());
}
//CPU side
if(GerstnerWavesJobs.Initialized == false && Application.isPlaying)
GerstnerWavesJobs.Init();
}
private Vector4[] GetWaveData()
{
var waveData = new Vector4[20];
for (var i = 0; i < _waves.Length; i++)
{
waveData[i] = new Vector4(_waves[i].amplitude, _waves[i].direction, _waves[i].wavelength, _waves[i].onmiDir);
waveData[i+10] = new Vector4(_waves[i].origin.x, _waves[i].origin.y, 0, 0);
}
return waveData;
}
private void SetupWaves(bool custom)
{
if(!custom)
{
//create basic waves based off basic wave settings
var backupSeed = Random.state;
Random.InitState(surfaceData.randomSeed);
var basicWaves = surfaceData._basicWaveSettings;
var a = basicWaves.amplitude;
var d = basicWaves.direction;
var l = basicWaves.wavelength;
var numWave = basicWaves.numWaves;
_waves = new Wave[numWave];
var r = 1f / numWave;
for (var i = 0; i < numWave; i++)
{
var p = Mathf.Lerp(0.5f, 1.5f, i * r);
var amp = a * p * Random.Range(0.8f, 1.2f);
var dir = d + Random.Range(-90f, 90f);
var len = l * p * Random.Range(0.6f, 1.4f);
_waves[i] = new Wave(amp, dir, len, Vector2.zero, false);
Random.InitState(surfaceData.randomSeed + i + 1);
}
Random.state = backupSeed;
}
else
{
_waves = surfaceData._waves.ToArray();
}
}
private void GenerateColorRamp()
{
if(_rampTexture == null)
_rampTexture = new Texture2D(128, 4, GraphicsFormat.R8G8B8A8_SRGB, TextureCreationFlags.None);
_rampTexture.wrapMode = TextureWrapMode.Clamp;
var defaultFoamRamp = resources.defaultFoamRamp;
var cols = new Color[512];
for (var i = 0; i < 128; i++)
{
cols[i] = surfaceData._absorptionRamp.Evaluate(i / 128f);
}
for (var i = 0; i < 128; i++)
{
cols[i + 128] = surfaceData._scatterRamp.Evaluate(i / 128f);
}
for (var i = 0; i < 128; i++)
{
switch(surfaceData._foamSettings.foamType)
{
case 0: // default
cols[i + 256] = defaultFoamRamp.GetPixelBilinear(i / 128f , 0.5f);
break;
case 1: // simple
cols[i + 256] = defaultFoamRamp.GetPixelBilinear(surfaceData._foamSettings.basicFoam.Evaluate(i / 128f) , 0.5f);
break;
case 2: // custom
cols[i + 256] = Color.black;
break;
}
}
_rampTexture.SetPixels(cols);
_rampTexture.Apply();
Shader.SetGlobalTexture(AbsorptionScatteringRamp, _rampTexture);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////Shoreline Depth Texture/////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[ContextMenu("Capture Depth")]
public void CaptureDepthMap()
{
//Generate the camera
if(_depthCam == null)
{
var go =
new GameObject("depthCamera") {hideFlags = HideFlags.HideAndDontSave}; //create the cameraObject
_depthCam = go.AddComponent<Camera>();
}
var additionalCamData = _depthCam.GetUniversalAdditionalCameraData();
additionalCamData.renderShadows = false;
additionalCamData.requiresColorOption = CameraOverrideOption.Off;
additionalCamData.requiresDepthOption = CameraOverrideOption.Off;
var t = _depthCam.transform;
var depthExtra = 4.0f;
t.position = Vector3.up * (transform.position.y + depthExtra);//center the camera on this water plane height
t.up = Vector3.forward;//face the camera down
_depthCam.enabled = true;
_depthCam.orthographic = true;
_depthCam.orthographicSize = 250;//hardcoded = 1k area - TODO
_depthCam.nearClipPlane =0.01f;
_depthCam.farClipPlane = surfaceData._waterMaxVisibility + depthExtra;
_depthCam.allowHDR = false;
_depthCam.allowMSAA = false;
_depthCam.cullingMask = (1 << 10);
//Generate RT
if (!_depthTex)
_depthTex = new RenderTexture(1024, 1024, 24, RenderTextureFormat.Depth, RenderTextureReadWrite.Linear);
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES2 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3)
{
_depthTex.filterMode = FilterMode.Point;
}
_depthTex.wrapMode = TextureWrapMode.Clamp;
_depthTex.name = "WaterDepthMap";
//do depth capture
_depthCam.targetTexture = _depthTex;
_depthCam.Render();
Shader.SetGlobalTexture(WaterDepthMap, _depthTex);
// set depth bufferParams for depth cam(since it doesnt exist and only temporary)
var _params = new Vector4(t.position.y, 250, 0, 0);
//Vector4 zParams = new Vector4(1-f/n, f/n, (1-f/n)/f, (f/n)/f);//2015
Shader.SetGlobalVector(DepthCamZParams, _params);
/* #if UNITY_EDITOR
Texture2D tex2D = new Texture2D(1024, 1024, TextureFormat.Alpha8, false);
Graphics.CopyTexture(_depthTex, tex2D);
byte[] image = tex2D.EncodeToPNG();
System.IO.File.WriteAllBytes(Application.dataPath + "/WaterDepth.png", image);
#endif*/
_depthCam.enabled = false;
_depthCam.targetTexture = null;
}
[Serializable]
public enum DebugMode { none, stationary, screen };
}
}
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: b6edb8448c9b94f018953ca2a6fa5891
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _rampTexture: {instanceID: 0}
- settingsData: {fileID: 11400000, guid: a4cacf74654e54b35b9eaea74fd20b4e, type: 2}
- surfaceData: {fileID: 11400000, guid: 846f3dc3b5341447a86687211f741bc7, type: 2}
- resources: {fileID: 11400000, guid: 40615c805d6e84ce7800418367985fe1, type: 2}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,19 @@
{
"name": "WaterSystem",
"references": [
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:15fc0a57446b3144c949da3e2b9737a9",
"GUID:df380645f10b7bc4b97d4f5eb6303d95",
"GUID:2665a8d13d1b3f18800f46e256720795",
"GUID:9e24947de15b9834991c9d8411ea37cf"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: eee6090a63a1e48e6857869e0cbe74dc
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: