(Update) Clean Projet
This commit is contained in:
@@ -0,0 +1,391 @@
|
||||
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
|
||||
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
|
||||
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
|
||||
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
|
||||
|
||||
using System;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace StylizedWater3
|
||||
{
|
||||
/// <summary>
|
||||
/// Samples the water height at 4 points around the Transform, and snaps the Y-position to the average.
|
||||
/// From these 4 points a normal direction can also be derived, which is used to orient the transform's rotation
|
||||
/// - This script is a prime example of how the Height Query System may be used.
|
||||
/// </summary>
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Stylized Water 3/Align Transform To Water")]
|
||||
public class AlignToWater : MonoBehaviour
|
||||
{
|
||||
//Because there two completely different methods of sampling the water's height, this Interface class provides a way to specify which is to be used.
|
||||
//In the case of the CPU-method, it contains everything else needed for such a query (eg. water level and material)
|
||||
public HeightQuerySystem.Interface heightInterface = new HeightQuerySystem.Interface();
|
||||
|
||||
public Vector2 surfaceSize = new Vector2(0.5f, 0.5f);
|
||||
|
||||
[Tooltip("Assign an optional transform to follow on the XZ axis.\n\nThis may be used if you want to use this component to read the water height at another transform's position.")]
|
||||
public Transform followTarget;
|
||||
public float heightOffset;
|
||||
[Range(0f, 8f)]
|
||||
[Tooltip("Controls how strongly the transform should rotate to align with the wave curvature")]
|
||||
public float rollAmount = 0.1f;
|
||||
[Tooltip("Add a Y-axis rotation to the transform. Note that this technically results in an incorrect alignment.")]
|
||||
[Range(0f, 360f)]
|
||||
public float rotation = 0f;
|
||||
|
||||
[Tooltip("Smoothly blend towards the newly calculated position and rotation. May be used to combat jittering")]
|
||||
[Min(0f)]
|
||||
public float smoothing = 0.1f;
|
||||
|
||||
private Vector3 normal;
|
||||
private float height;
|
||||
public enum HeightValue
|
||||
{
|
||||
Average,
|
||||
Maximum
|
||||
}
|
||||
public HeightValue heightValue;
|
||||
|
||||
//A sampler defines:
|
||||
// • An array of world-space input positions to sample the height at.
|
||||
// • A float array (of equal size) that stores the output height values.
|
||||
// Samplers must always be initialized by specifying how many sample points are needed.
|
||||
private HeightQuerySystem.Sampler heightSampler;
|
||||
//A request ask the Height Query System to return the water height at every one of the sampler's positions from the GPU
|
||||
//It contains a callback event that needs to be subscribed to, to let you know when the request is completed
|
||||
private HeightQuerySystem.AsyncRequest heightRequest;
|
||||
|
||||
#pragma warning disable 108,114 //New keyword
|
||||
public Rigidbody rigidbody;
|
||||
#pragma warning restore 108,114
|
||||
|
||||
private Vector3 m_targetNormal;
|
||||
private float m_targetHeight;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public static bool EnableInEditor
|
||||
{
|
||||
get { return UnityEditor.EditorPrefs.GetBool("SW3_BUOYANCY_EDITOR_ENABLED", true); }
|
||||
set { UnityEditor.EditorPrefs.SetBool("SW3_BUOYANCY_EDITOR_ENABLED", value); }
|
||||
}
|
||||
#endif
|
||||
|
||||
private void Start()
|
||||
{
|
||||
rigidbody = GetComponent<Rigidbody>();
|
||||
|
||||
if (rigidbody && rigidbody.useGravity)
|
||||
{
|
||||
Debug.LogWarning($"[Align Transform To Water] Disabled gravity on RigidBody \"{rigidbody.name}\". Otherwise its position can't be set without it struggling back");
|
||||
rigidbody.useGravity = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if(Application.isPlaying == false) UnityEditor.SceneView.duringSceneGui += DuringSceneViewUpdate;
|
||||
#endif
|
||||
|
||||
//Sampler uses 4 sampling points, one for each corner of the rectangle/plane
|
||||
//In the context of a physics-based buoyancy system a sampler will use more than 4 sampling points
|
||||
heightSampler = new HeightQuerySystem.Sampler();
|
||||
heightSampler.SetSampleCount(4);
|
||||
|
||||
//Only when using the GPU-readback method does the HeightQuerySystem come into play
|
||||
if (heightInterface.method == HeightQuerySystem.Interface.Method.GPU)
|
||||
{
|
||||
//Create a new request with the sampler created above.
|
||||
//Do this just once! It'll keep going until you call "Dispose()" on it.
|
||||
//Using the object's hashcode ensures the request gets a unique ID
|
||||
heightRequest = new HeightQuerySystem.AsyncRequest(this.GetHashCode(), heightSampler, this.name);
|
||||
|
||||
//Issue the request so the system starts populating the "heightSampler" with data
|
||||
heightRequest.Issue();
|
||||
|
||||
//True by default. This checks if the returned water height values are valid. If not, the value remains the same as last frame
|
||||
//This avoids objects that are not above any water surface, or fall outside the camera frustum, from falling down to -1000 heights
|
||||
heightRequest.invalidateMisses = true;
|
||||
|
||||
//You may also call .Withdraw() if you wish to only temporarily remove the request from the system. Then call .Issue() again when needed.
|
||||
//This is also necessary when looking to call heightSampler.SetSampleCount() again.
|
||||
//Doing so avoids reallocating its resources but only temporarily takes it out of the running.
|
||||
|
||||
//Subscribe to the event that indicates that data was retrieved from the GPU
|
||||
//Important to note that this may be a few (rendering) frames later than when the request was issued (at least 1)
|
||||
heightRequest.onCompleted += OnHeightRequestComplete;
|
||||
}
|
||||
|
||||
prevHeight = this.transform.position.y;
|
||||
height = prevHeight;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void DuringSceneViewUpdate(UnityEditor.SceneView sceneView)
|
||||
{
|
||||
FixedUpdate();
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnHeightRequestComplete()
|
||||
{
|
||||
//Debug.Log("Height request returned");
|
||||
|
||||
//Use the data. It pre-presents the water geometry's height value at each of the requested sample positions
|
||||
float xNeg = heightSampler.heightValues[0];
|
||||
float xPos = heightSampler.heightValues[1];
|
||||
float zNeg = heightSampler.heightValues[2];
|
||||
float zPos = heightSampler.heightValues[3];
|
||||
|
||||
float newHeight = 0f;
|
||||
|
||||
if (heightValue == HeightValue.Average)
|
||||
{
|
||||
newHeight = xNeg + xPos + zNeg + zPos;
|
||||
newHeight /= 4f;
|
||||
}
|
||||
if (heightValue == HeightValue.Maximum)
|
||||
{
|
||||
newHeight = Mathf.Max(Mathf.Max(xNeg, xPos), Mathf.Max(zNeg, zPos));
|
||||
}
|
||||
|
||||
newHeight += heightOffset;
|
||||
|
||||
if (float.IsNaN(newHeight))
|
||||
{
|
||||
#if SWS_DEV
|
||||
Debug.LogError("Height is NaN");
|
||||
#endif
|
||||
|
||||
//May occur during the first run
|
||||
newHeight = 0f;
|
||||
}
|
||||
|
||||
if (heightInterface.method == HeightQuerySystem.Interface.Method.GPU)
|
||||
{
|
||||
//If all the samples were taking at a point where no water was visible, the height values would be -1000f.
|
||||
//Avoid setting an invalid height to prevent objects from sinking way down, instead keep them at the last valid height
|
||||
if (HeightQuerySystem.EqualsVoid(newHeight))
|
||||
{
|
||||
UpdateSamplePositions();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
height = newHeight;
|
||||
|
||||
//Using 4 samples in a plus-shape pattern, a normal can be derived from the height differences
|
||||
normal = HeightQuerySystem.DeriveNormal(
|
||||
xNeg, xPos,
|
||||
zNeg, zPos,
|
||||
rollAmount);
|
||||
|
||||
//Note: In a physics-based buoyancy system, calculating the normal will not be necessary. The varying upward forces will naturally align an object to the water.
|
||||
//Reading back the water surface normals directly will not be possible, since no such information is available (requires a deferred rendering water system)
|
||||
|
||||
//Only updating the sampling positions here, for the next query. There would be no point in doing so for frames where the height request may not be returned
|
||||
UpdateSamplePositions();
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
//Auto-assign water object if there is only one
|
||||
if (heightInterface.waterObject == null && WaterObject.Instances.Count > 0)
|
||||
{
|
||||
heightInterface.waterObject = WaterObject.Instances[0];
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.EditorUtility.SetDirty(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
AutoCalculateSurfaceSizeFromMeshes();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if(Application.isPlaying == false) UnityEditor.SceneView.duringSceneGui -= DuringSceneViewUpdate;
|
||||
#endif
|
||||
|
||||
if (heightRequest != null)
|
||||
{
|
||||
//Unsubscribe
|
||||
heightRequest.onCompleted -= OnHeightRequestComplete;
|
||||
|
||||
//Important! Remove the request from the system so that it de-allocates the memory it uses.
|
||||
heightRequest.Dispose();
|
||||
heightRequest = null;
|
||||
}
|
||||
|
||||
//Also dispose the height sampler. This stores two arrays that need to be de-allocated
|
||||
//Note: When using the GPU method, disposing 'heightRequest' will already do this!
|
||||
heightSampler.Dispose();
|
||||
heightSampler = null;
|
||||
}
|
||||
|
||||
public void FixedUpdate()
|
||||
{
|
||||
if (!this.enabled) return;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!EnableInEditor && Application.isPlaying == false) return;
|
||||
#endif
|
||||
|
||||
//When using the CPU (wave pattern replication) method
|
||||
if (heightInterface.method == HeightQuerySystem.Interface.Method.CPU)
|
||||
{
|
||||
//The water object contains the material, and may be used to define a water level
|
||||
heightInterface.GetWaterObject(this.transform.position);
|
||||
|
||||
//A reference to a Water Object is required, which in turn contains a material
|
||||
if (heightInterface.HasMissingReferences()) return;
|
||||
|
||||
//This function reproduces the water shader's wave vertex animation.
|
||||
Gerstner.ComputeHeight(heightSampler, heightInterface);
|
||||
|
||||
//At this point the 'heightSampler' has new height values, so can go right ahead and use them
|
||||
OnHeightRequestComplete();
|
||||
|
||||
ApplyTransform();
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyTransform();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Translates the corners of the bounds to the 4 sampling points (plus-shaped pattern)
|
||||
//From local-space to world-space.
|
||||
private void UpdateSamplePositions()
|
||||
{
|
||||
//Note: Should the number of sample positions change in realtime, that creates an issue where resources have to be destroyed and recreated. Avoid doing so!
|
||||
//Should it be needed, do so by:
|
||||
//• heightRequest.Withdraw(); //Remove the active request for data
|
||||
//• heightSampler.SetSampleCount(count). //This will automatically reallocate the arrays if the count was changed.
|
||||
//• heightRequest.Issue(); //Re-issue the request with the new number of samples
|
||||
|
||||
// ┤
|
||||
heightSampler.SetSamplePosition(0, ConvertToWorldSpace(new Vector3(-surfaceSize.x * 0.5f, 0, 0)));
|
||||
// ├
|
||||
heightSampler.SetSamplePosition(1, ConvertToWorldSpace(new Vector3(surfaceSize.x * 0.5f, 0, 0)));
|
||||
// ┬
|
||||
heightSampler.SetSamplePosition(2, ConvertToWorldSpace(new Vector3(0, 0, -surfaceSize.y * 0.5f)));
|
||||
// ┴
|
||||
heightSampler.SetSamplePosition(3, ConvertToWorldSpace(new Vector3(0, 0, surfaceSize.y * 0.5f)));
|
||||
}
|
||||
|
||||
private Vector3 prevNormal = new Vector3(0, 1, 0);
|
||||
private float prevHeight;
|
||||
|
||||
private void ApplyTransform()
|
||||
{
|
||||
//Smooth transition to new normal of this frame, particularly reduces jittering on rigid bodies
|
||||
if (smoothing > 0)
|
||||
{
|
||||
m_targetHeight = Mathf.Lerp(prevHeight, height, Time.deltaTime / smoothing);
|
||||
prevHeight = m_targetHeight;
|
||||
|
||||
m_targetNormal = Vector3.Lerp(prevNormal, normal, Time.smoothDeltaTime / smoothing);
|
||||
prevNormal = m_targetNormal;
|
||||
}
|
||||
else
|
||||
{
|
||||
prevHeight = height;
|
||||
prevNormal = normal;
|
||||
|
||||
m_targetHeight = height;
|
||||
m_targetNormal = normal;
|
||||
}
|
||||
|
||||
var position = this.transform.position;
|
||||
if (followTarget)
|
||||
{
|
||||
position.x = followTarget.position.x;
|
||||
position.z = followTarget.position.z;
|
||||
}
|
||||
|
||||
position.y = m_targetHeight;
|
||||
|
||||
//Setting the normal of a transform directly overrides any and all external rotations
|
||||
//If the component is attached an object with a mesh, move the mesh into a child object and rotate that instead
|
||||
var newRotation = Quaternion.FromToRotation(Vector3.up, m_targetNormal);
|
||||
|
||||
newRotation *= quaternion.RotateY(rotation * Mathf.Deg2Rad);
|
||||
if (Application.isPlaying && rigidbody)
|
||||
{
|
||||
if (rollAmount > 0)
|
||||
{
|
||||
rigidbody.Move(position, newRotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
rigidbody.MovePosition(position);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rollAmount > 0)
|
||||
{
|
||||
this.transform.SetPositionAndRotation(position, newRotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.transform.position = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 ConvertToWorldSpace(Vector3 position)
|
||||
{
|
||||
return this.transform.TransformPoint(position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Automatically grab a starting point for the area size, based on the attached mesh(es)
|
||||
/// </summary>
|
||||
public void AutoCalculateSurfaceSizeFromMeshes()
|
||||
{
|
||||
MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
|
||||
|
||||
int meshCount = meshFilters.Length;
|
||||
|
||||
if (meshCount > 0)
|
||||
{
|
||||
surfaceSize.x = 0f;
|
||||
surfaceSize.y = 0f;
|
||||
|
||||
Bounds bounds = new Bounds();
|
||||
Vector3 minSum = Vector3.one * Mathf.Infinity;
|
||||
Vector3 maxSum = Vector3.one * Mathf.NegativeInfinity;
|
||||
|
||||
for (int i = 0; i < meshCount; i++)
|
||||
{
|
||||
minSum = Vector3.Min(minSum, meshFilters[i].sharedMesh.bounds.min);
|
||||
maxSum = Vector3.Max(maxSum, meshFilters[i].sharedMesh.bounds.max);
|
||||
}
|
||||
bounds.SetMinMax(minSum, maxSum);
|
||||
|
||||
surfaceSize.x = bounds.size.x;
|
||||
surfaceSize.y = bounds.size.z;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (heightSampler != null && heightSampler.IsCreated())
|
||||
{
|
||||
foreach (Vector3 p in heightSampler.positions)
|
||||
{
|
||||
Gizmos.DrawWireCube(p - (Vector3.up * heightOffset), Vector3.one * 0.2f);
|
||||
}
|
||||
}
|
||||
|
||||
Gizmos.DrawLine(this.transform.position, this.transform.position + (m_targetNormal * 2f));
|
||||
|
||||
Gizmos.matrix = this.transform.localToWorldMatrix;
|
||||
Gizmos.DrawWireCube(Vector3.zero - (Vector3.up * heightOffset), new Vector3(surfaceSize.x, 0f, surfaceSize.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95144392e6eede14cafa2d339e5c1ee9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 148978298399363526, guid: 0000000000000000d000000000000000, type: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 287769
|
||||
packageName: Stylized Water 3
|
||||
packageVersion: 3.2.6
|
||||
assetPath: Assets/Stylized Water 3/Runtime/Components/AlignToWater.cs
|
||||
uploadId: 895866
|
||||
@@ -0,0 +1,259 @@
|
||||
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
|
||||
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
|
||||
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
|
||||
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace StylizedWater3
|
||||
{
|
||||
[ExecuteAlways]
|
||||
[AddComponentMenu("")] //Hide, only to be used with the prefab
|
||||
public class OceanFollowBehaviour : MonoBehaviour
|
||||
{
|
||||
public static OceanFollowBehaviour Instance;
|
||||
|
||||
const int LODCount = 7;
|
||||
private static readonly float[] gridSizes = new[]
|
||||
{
|
||||
0.5f, //LOD0
|
||||
01f, //LOD1
|
||||
02f, //LOD2
|
||||
04f, //LOD3
|
||||
08f, //LOD4
|
||||
16f, //LOD5
|
||||
32f, //LOD6
|
||||
32f //LOD7
|
||||
};
|
||||
|
||||
private const float EXPECTED_MAX_WAVE_HEIGHT = 15f;
|
||||
|
||||
public Material material;
|
||||
|
||||
[Space]
|
||||
|
||||
[Tooltip("Enable to executing the camera following behaviour when outside of Play mode")]
|
||||
public bool enableInEditMode = true;
|
||||
[Tooltip("Assign a specific transform to follow on the XZ axis." +
|
||||
"\n\nIf left empty, the camera currently rendering will be targeted.")]
|
||||
public Transform followTarget;
|
||||
|
||||
[Space]
|
||||
|
||||
public static bool ShowWireFrame = true;
|
||||
|
||||
[Serializable]
|
||||
public class LOD
|
||||
{
|
||||
public List<GameObject> gameObjects = new List<GameObject>();
|
||||
public float gridSize = 1f;
|
||||
}
|
||||
[SerializeField]
|
||||
//[HideInInspector]
|
||||
private LOD[] lods;
|
||||
|
||||
//To ensure that each LOD renders behind the one before it, move each one down a tiny bit
|
||||
private const float heightOffset = 0.01f;
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
//BuildLODs();
|
||||
}
|
||||
|
||||
#if SWS_DEV
|
||||
[ContextMenu("Build LODs")]
|
||||
#endif
|
||||
//Populates the LOD list and sets the gridsize for each
|
||||
void BuildLODs()
|
||||
{
|
||||
lods = new LOD[LODCount+1];
|
||||
for (int i = 0; i < lods.Length; i++)
|
||||
{
|
||||
lods[i] = new LOD();
|
||||
lods[i].gridSize = gridSizes[i];
|
||||
}
|
||||
|
||||
MeshRenderer[] childs = this.gameObject.GetComponentsInChildren<MeshRenderer>(true);
|
||||
for (int i = 0; i < childs.Length; i++)
|
||||
{
|
||||
childs[i].gameObject.layer = WaterObject.WaterLayer;
|
||||
|
||||
string objName = childs[i].name;
|
||||
int lodIndex = 0;
|
||||
|
||||
if(objName.EndsWith("0")) lodIndex = 0;
|
||||
if(objName.EndsWith("1")) lodIndex = 1;
|
||||
if(objName.EndsWith("2")) lodIndex = 2;
|
||||
if(objName.EndsWith("3")) lodIndex = 3;
|
||||
if(objName.EndsWith("4")) lodIndex = 4;
|
||||
if(objName.EndsWith("5")) lodIndex = 5;
|
||||
if(objName.EndsWith("6")) lodIndex = 6;
|
||||
if(objName.EndsWith("7")) lodIndex = 7;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
childs[i].scaleInLightmap = Mathf.Lerp(1, 0.01f, (float)lodIndex / LODCount);
|
||||
#endif
|
||||
|
||||
lods[lodIndex].gameObjects.Add(childs[i].gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 targetPosition;
|
||||
private void SetPosition(Transform target)
|
||||
{
|
||||
float height = this.transform.position.y;
|
||||
|
||||
for (int i = 0; i < lods.Length; i++)
|
||||
{
|
||||
for (int j = 0; j < lods[i].gameObjects.Count; j++)
|
||||
{
|
||||
targetPosition = WaterGrid.SnapToGrid(target.position, this.transform.lossyScale.x * lods[i].gridSize);
|
||||
//targetPosition = target.position; //No snapping
|
||||
|
||||
//Progressively lower the height of each LOD a small amount, this helps ensure transparency sorting will be correct.
|
||||
targetPosition.y = height - (heightOffset * i);
|
||||
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
if (lods[i].gameObjects[j] == false) throw new Exception("[Ocean] A child GameObject was deleted, these should not be touched!");
|
||||
#endif
|
||||
|
||||
lods[i].gameObjects[j].transform.position = targetPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyMaterial()
|
||||
{
|
||||
for (int i = 0; i < lods.Length; i++)
|
||||
{
|
||||
foreach (var lod in lods[i].gameObjects)
|
||||
{
|
||||
MeshRenderer r = lod.GetComponent<MeshRenderer>();
|
||||
|
||||
if (r)
|
||||
{
|
||||
r.sharedMaterial = material;
|
||||
PadBounds(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Pad the bounds so that meshes don't get unintentionally culled when using high waves
|
||||
private void PadBounds()
|
||||
{
|
||||
for (int i = 0; i < lods.Length; i++)
|
||||
{
|
||||
foreach (var lod in lods[i].gameObjects)
|
||||
{
|
||||
MeshRenderer r = lod.GetComponent<MeshRenderer>();
|
||||
|
||||
if (r)
|
||||
{
|
||||
r.sharedMaterial = material;
|
||||
|
||||
PadBounds(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PadBounds(MeshRenderer m_renderer)
|
||||
{
|
||||
Bounds bounds = m_renderer.localBounds;
|
||||
bounds.Expand(Vector3.up * (EXPECTED_MAX_WAVE_HEIGHT * 0.5f));
|
||||
|
||||
m_renderer.localBounds = bounds;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
PadBounds();
|
||||
|
||||
#if URP
|
||||
RenderPipelineManager.beginCameraRendering += OnCameraRender;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (followTarget)
|
||||
{
|
||||
SetPosition(followTarget);
|
||||
}
|
||||
}
|
||||
|
||||
#if URP
|
||||
private void OnCameraRender(ScriptableRenderContext context, Camera targetCamera)
|
||||
{
|
||||
if (targetCamera.cameraType == CameraType.Preview) return;
|
||||
|
||||
//Component set up to follow a specific target
|
||||
if (followTarget != null) return;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
//Skip if disabled in scene-view
|
||||
if (targetCamera.cameraType == CameraType.SceneView && (enableInEditMode == false || Application.isPlaying)) return;
|
||||
#endif
|
||||
|
||||
SetPosition(targetCamera.transform);
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Instance = null;
|
||||
|
||||
#if URP
|
||||
RenderPipelineManager.beginCameraRendering -= OnCameraRender;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (!ShowWireFrame) return;
|
||||
|
||||
MeshFilter[] meshes = GetComponentsInChildren<MeshFilter>();
|
||||
|
||||
Gizmos.color = new Color(0,0,0,0.25f);
|
||||
for (int i = 0; i < meshes.Length; i++)
|
||||
{
|
||||
Gizmos.matrix = meshes[i].transform.localToWorldMatrix;
|
||||
Gizmos.DrawWireMesh(meshes[i].sharedMesh);
|
||||
}
|
||||
|
||||
/*
|
||||
Gizmos.color = new Color(1,1,0,0.25f);
|
||||
Gizmos.matrix = Matrix4x4.identity;
|
||||
for (int i = 0; i < lods.Length; i++)
|
||||
{
|
||||
foreach (var lod in lods[i].gameObjects)
|
||||
{
|
||||
MeshRenderer r = lod.GetComponent<MeshRenderer>();
|
||||
Gizmos.DrawWireCube(r.bounds.center, r.bounds.size);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public bool InvalidSetup()
|
||||
{
|
||||
if(lods == null) return true;
|
||||
if(lods.Length == 0) return true;
|
||||
|
||||
for (int i = 0; i < lods.Length; i++)
|
||||
{
|
||||
foreach (var lod in lods[i].gameObjects)
|
||||
{
|
||||
if(!lod.gameObject) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59d166d8583159b44ae4a88bf823b554
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- material: {fileID: 2100000, guid: d9bee4d86b9f55b46a4288109fb2b181, type: 2}
|
||||
- followTarget: {instanceID: 0}
|
||||
executionOrder: 0
|
||||
icon: {fileID: -7633815037825259808, guid: 0000000000000000d000000000000000, type: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 287769
|
||||
packageName: Stylized Water 3
|
||||
packageVersion: 3.2.6
|
||||
assetPath: Assets/Stylized Water 3/Runtime/Components/OceanFollowBehaviour.cs
|
||||
uploadId: 895866
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace StylizedWater3
|
||||
{
|
||||
/// <summary>
|
||||
/// Emulates the particle system "Rate over Distance" emission behaviour, but with accurate support for RigidBody's
|
||||
/// </summary>
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Effects/Particle Trail Emitter")]
|
||||
public class ParticleTrailEmitter : MonoBehaviour
|
||||
{
|
||||
#pragma warning disable 108,114 //New keyword
|
||||
[Tooltip("The Emission module on this particle system should have its Rate Over Distance value set 0.")]
|
||||
public ParticleSystem particleSystem;
|
||||
private ParticleSystem.EmissionModule emissionModule;
|
||||
#pragma warning restore 108,114
|
||||
|
||||
[Tooltip("If this particle system is parented under a RigidBody, then assign it here for correct positional tracking")]
|
||||
public Rigidbody rigidBody;
|
||||
|
||||
[Space]
|
||||
|
||||
public float spawnRatePerUnit = 1f;
|
||||
|
||||
private float distanceAccumulation = 0f;
|
||||
private Vector3 previousPosition;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
particleSystem = GetComponent<ParticleSystem>();
|
||||
|
||||
if (particleSystem)
|
||||
{
|
||||
emissionModule = particleSystem.emission;
|
||||
|
||||
if (emissionModule.rateOverDistance.constant > 0f)
|
||||
{
|
||||
ParticleSystem.MinMaxCurve rateOverDistance = emissionModule.rateOverDistance;
|
||||
rateOverDistance.constant = 0f;
|
||||
emissionModule.rateOverDistance = rateOverDistance;
|
||||
|
||||
Debug.LogWarning($"The Rate Over Distance has been set to 0 on the particle system \"{particleSystem.name}\". This is because the Particle Trail Emitter component will be responsible for emission");
|
||||
}
|
||||
}
|
||||
rigidBody = GetComponentInParent<Rigidbody>();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
previousPosition = this.transform.position;
|
||||
|
||||
if (particleSystem)
|
||||
{
|
||||
ParticleSystem.MainModule main = particleSystem.main;
|
||||
main.playOnAwake = false;
|
||||
}
|
||||
}
|
||||
|
||||
private float GetDistance()
|
||||
{
|
||||
float distanceThisFrame = 0;
|
||||
|
||||
if(rigidBody)
|
||||
{
|
||||
//Distance = speed * deltaTime
|
||||
float movementSpeed = Mathf.Max(rigidBody.linearVelocity.magnitude, rigidBody.angularVelocity.magnitude);
|
||||
distanceThisFrame = movementSpeed * Time.deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
distanceThisFrame = Vector3.Distance(transform.position, previousPosition);
|
||||
previousPosition = this.transform.position;
|
||||
}
|
||||
|
||||
return distanceThisFrame;
|
||||
}
|
||||
|
||||
public void FixedUpdate()
|
||||
{
|
||||
if (!particleSystem || !enabled) return;
|
||||
|
||||
emissionModule = particleSystem.emission;
|
||||
if (emissionModule.enabled == false) return;
|
||||
|
||||
float distance = GetDistance();
|
||||
|
||||
//Teleportation safeguard
|
||||
if (distance > 50f) return;
|
||||
|
||||
distanceAccumulation += distance;
|
||||
|
||||
var particlesToEmit = Mathf.CeilToInt(distanceAccumulation * spawnRatePerUnit);
|
||||
|
||||
if (particlesToEmit > 0)
|
||||
{
|
||||
particleSystem.Emit(particlesToEmit);
|
||||
|
||||
distanceAccumulation -= particlesToEmit / spawnRatePerUnit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[CustomEditor(typeof(ParticleTrailEmitter))]
|
||||
class ParticleTrailEmitterInspector : Editor
|
||||
{
|
||||
private ParticleTrailEmitter component;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
component = (ParticleTrailEmitter)target;
|
||||
}
|
||||
|
||||
private void OnSceneGUI()
|
||||
{
|
||||
component.FixedUpdate();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6febe12fcf8d2e149842da43eb923880
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 287769
|
||||
packageName: Stylized Water 3
|
||||
packageVersion: 3.2.6
|
||||
assetPath: Assets/Stylized Water 3/Runtime/Components/ParticleTrailEmitter.cs
|
||||
uploadId: 895866
|
||||
@@ -0,0 +1,165 @@
|
||||
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
|
||||
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
|
||||
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
|
||||
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace StylizedWater3
|
||||
{
|
||||
[ExecuteAlways]
|
||||
[AddComponentMenu("Stylized Water 3/Water Custom Time")]
|
||||
public class SetCustomWaterTime : MonoBehaviour
|
||||
{
|
||||
public enum Mode
|
||||
{
|
||||
None,
|
||||
Interval,
|
||||
Time,
|
||||
EditorTime,
|
||||
Speed,
|
||||
[InspectorName("System Time (UTC)")]
|
||||
SystemTime,
|
||||
Custom
|
||||
}
|
||||
|
||||
public Mode mode = Mode.Custom;
|
||||
|
||||
//Parameters for different modes
|
||||
[Min(0.02f)]
|
||||
public float interval = 0.2f;
|
||||
public float speed = 0f;
|
||||
[Min(0f)]
|
||||
public float customTime = 0f;
|
||||
|
||||
private float elapsedTime;
|
||||
private DateTime _startTime;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
RenderPipelineManager.beginContextRendering += OnBeginFrame;
|
||||
|
||||
_startTime = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 0, 0, 0);
|
||||
}
|
||||
|
||||
private void OnBeginFrame(ScriptableRenderContext context, List<Camera> cams)
|
||||
{
|
||||
SetTime();
|
||||
}
|
||||
|
||||
private void SetTime()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (mode == Mode.EditorTime)
|
||||
{
|
||||
WaterObject.CustomTime = (float)UnityEditor.EditorApplication.timeSinceStartup;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mode == Mode.None)
|
||||
{
|
||||
ResetTime();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == Mode.Interval)
|
||||
{
|
||||
elapsedTime += Time.deltaTime;
|
||||
|
||||
if (elapsedTime >= interval)
|
||||
{
|
||||
elapsedTime = 0;
|
||||
|
||||
WaterObject.CustomTime = Time.time;
|
||||
}
|
||||
}
|
||||
else if (mode == Mode.Time)
|
||||
{
|
||||
WaterObject.CustomTime = Time.time;
|
||||
}
|
||||
else if (mode == Mode.Speed)
|
||||
{
|
||||
elapsedTime += Time.deltaTime * speed;
|
||||
WaterObject.CustomTime = elapsedTime;
|
||||
}
|
||||
else if (mode == Mode.SystemTime)
|
||||
{
|
||||
WaterObject.CustomTime = (float)(DateTime.UtcNow - _startTime).TotalMilliseconds * 0.001f;
|
||||
}
|
||||
else if (mode == Mode.Custom)
|
||||
{
|
||||
WaterObject.CustomTime = customTime;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetTime()
|
||||
{
|
||||
elapsedTime = 0f;
|
||||
//Revert to using normal time
|
||||
WaterObject.CustomTime = -1;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
RenderPipelineManager.beginContextRendering -= OnBeginFrame;
|
||||
|
||||
ResetTime();
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[CustomEditor(typeof(SetCustomWaterTime))]
|
||||
public class SetCustomWaterTimeEditor : Editor
|
||||
{
|
||||
private SerializedProperty mode;
|
||||
|
||||
private SerializedProperty interval;
|
||||
private SerializedProperty speed;
|
||||
private SerializedProperty customTime;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
mode = serializedObject.FindProperty("mode");
|
||||
interval = serializedObject.FindProperty("interval");
|
||||
speed = serializedObject.FindProperty("speed");
|
||||
customTime = serializedObject.FindProperty("customTime");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.PropertyField(mode);
|
||||
|
||||
if (mode.intValue == (int)SetCustomWaterTime.Mode.Interval)
|
||||
{
|
||||
EditorGUILayout.PropertyField(interval);
|
||||
}
|
||||
else if (mode.intValue == (int)SetCustomWaterTime.Mode.Speed)
|
||||
{
|
||||
EditorGUILayout.PropertyField(speed);
|
||||
}
|
||||
else if (mode.intValue == (int)SetCustomWaterTime.Mode.Custom)
|
||||
{
|
||||
EditorGUILayout.PropertyField(customTime);
|
||||
}
|
||||
|
||||
if(WaterObject.CustomTime > 0 && mode.intValue != (int)SetCustomWaterTime.Mode.Custom) EditorGUILayout.HelpBox($"Time: {WaterObject.CustomTime}", MessageType.None);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33cfa047dbc543ea9a51492ad11fce6e
|
||||
timeCreated: 1719917866
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 287769
|
||||
packageName: Stylized Water 3
|
||||
packageVersion: 3.2.6
|
||||
assetPath: Assets/Stylized Water 3/Runtime/Components/SetCustomWaterTime.cs
|
||||
uploadId: 895866
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace StylizedWater3
|
||||
{
|
||||
[ExecuteAlways]
|
||||
[AddComponentMenu("Stylized Water 3/Global Wave Origin Offset")]
|
||||
public class SetGlobalWaveOriginOffset : MonoBehaviour
|
||||
{
|
||||
private readonly int _GlobalWaveOriginOffset = Shader.PropertyToID("_GlobalWaveOriginOffset");
|
||||
|
||||
private void Update()
|
||||
{
|
||||
Shader.SetGlobalVector(_GlobalWaveOriginOffset, this.transform.position);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Shader.SetGlobalVector(_GlobalWaveOriginOffset, Vector3.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3290db5725a149f09da1024eb11ed2e1
|
||||
timeCreated: 1747397807
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 287769
|
||||
packageName: Stylized Water 3
|
||||
packageVersion: 3.2.6
|
||||
assetPath: Assets/Stylized Water 3/Runtime/Components/SetGlobalWaveOriginOffset.cs
|
||||
uploadId: 895866
|
||||
@@ -0,0 +1,28 @@
|
||||
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
|
||||
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
|
||||
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
|
||||
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace StylizedWater3
|
||||
{
|
||||
[ExecuteAlways]
|
||||
[AddComponentMenu("Stylized Water 3/Water Position Offset")]
|
||||
public class SetWaterPositionOffset : MonoBehaviour
|
||||
{
|
||||
public bool negate;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
//Note: in a floating origin system, apply the value after offsetting all the transforms!
|
||||
//Otherwise the water geometry gets shifted in one frame, and this offset is applied the next. This induces a jitter.
|
||||
StylizedWater3.WaterObject.PositionOffset = negate ? -this.transform.position : this.transform.position;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
StylizedWater3.WaterObject.PositionOffset = Vector3.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff38353f302f4942bdccd7cb32b19d37
|
||||
timeCreated: 1728977343
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 287769
|
||||
packageName: Stylized Water 3
|
||||
packageVersion: 3.2.6
|
||||
assetPath: Assets/Stylized Water 3/Runtime/Components/SetWaterPositionOffset.cs
|
||||
uploadId: 895866
|
||||
@@ -0,0 +1,238 @@
|
||||
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
|
||||
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
|
||||
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
|
||||
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace StylizedWater3
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Stylized Water 3/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(0)]
|
||||
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 CalculateTileSize()
|
||||
{
|
||||
rowsColumns = Mathf.Max(rowsColumns, 0);
|
||||
float m_scale = scale * this.transform.lossyScale.x;
|
||||
tileSize = Mathf.Max(1f, m_scale / rowsColumns);
|
||||
}
|
||||
|
||||
private void RecreateMesh()
|
||||
{
|
||||
CalculateTileSize();
|
||||
|
||||
float m_vertexDistance = vertexDistance * this.transform.lossyScale.x;
|
||||
|
||||
//Value should never be larger than an individual tile
|
||||
m_vertexDistance = Mathf.Min(m_vertexDistance, tileSize);
|
||||
|
||||
mesh = WaterMesh.Create(WaterMesh.Shape.Rectangle, tileSize, m_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.matrix = this.transform.localToWorldMatrix;
|
||||
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.localPosition);
|
||||
}
|
||||
}
|
||||
|
||||
if (DisplayGrid)
|
||||
{
|
||||
if (tileSize <= 0) CalculateTileSize();
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bfcfc08716cec9c4ea8d75126995f02e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- material: {fileID: 2100000, guid: e8333e151973f1c4188ff534979c823b, type: 2}
|
||||
- followTarget: {instanceID: 0}
|
||||
executionOrder: 0
|
||||
icon: {fileID: 5243786984396574768, guid: 0000000000000000d000000000000000, type: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 287769
|
||||
packageName: Stylized Water 3
|
||||
packageVersion: 3.2.6
|
||||
assetPath: Assets/Stylized Water 3/Runtime/Components/WaterGrid.cs
|
||||
uploadId: 895866
|
||||
@@ -0,0 +1,205 @@
|
||||
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
|
||||
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
|
||||
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
|
||||
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace StylizedWater3
|
||||
{
|
||||
/// <summary>
|
||||
/// Attached to every mesh using the Stylized Water 3 shader
|
||||
/// Provides a generic way of identifying water objects and accessing their properties
|
||||
/// </summary>
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Stylized Water 3/Water Object")]
|
||||
[DisallowMultipleComponent]
|
||||
public class WaterObject : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// The default Water layer index in Unity
|
||||
/// </summary>
|
||||
public const int WaterLayer = 4;
|
||||
|
||||
/// <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 height sampling as well).
|
||||
/// This is typically used for network synchronized waves or cutscenes.
|
||||
/// To revert to using normal <see cref="Time.time"/>, pass in a value lower than <c>0</c>.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public static float CustomTime
|
||||
{
|
||||
set
|
||||
{
|
||||
m_customTimeValue = value;
|
||||
Shader.SetGlobalFloat(CustomTimeID, m_customTimeValue);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 079d48f7ea5dc054a8b2cd9151030ee5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 22661e393f063dd4085f7fe57377fbc7, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 287769
|
||||
packageName: Stylized Water 3
|
||||
packageVersion: 3.2.6
|
||||
assetPath: Assets/Stylized Water 3/Runtime/Components/WaterObject.cs
|
||||
uploadId: 895866
|
||||
Reference in New Issue
Block a user