2025-10-21 14:18:31 +09:00

259 lines
8.2 KiB
C#

// 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;
}
}
}