#if GRIFFIN using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; #if UNITY_EDITOR using UnityEditor.SceneManagement; #endif using UnityEngine.Rendering; #if GRIFFIN_URP using UnityEngine.Rendering.Universal; #endif namespace Pinwheel.Griffin.SplineTool { [System.Serializable] [ExecuteInEditMode] public partial class GSplineCreator : MonoBehaviour { public const string SPLINE_LAYER = "POLARIS SPLINE"; public delegate void SplineChangedHandler(GSplineCreator sender); public static event SplineChangedHandler Editor_SplineChanged; [SerializeField] private int groupId; public int GroupId { get { return groupId; } set { groupId = value; } } [SerializeField] private bool enableTerrainMask; public bool EnableTerrainMask { get { return enableTerrainMask; } set { enableTerrainMask = value; } } [SerializeField] private Vector3 positionOffset; public Vector3 PositionOffset { get { return positionOffset; } set { positionOffset = value; } } [SerializeField] private Quaternion initialRotation; public Quaternion InitialRotation { get { return initialRotation; } set { initialRotation = value; } } [SerializeField] private Vector3 initialScale; public Vector3 InitialScale { get { return initialScale; } set { initialScale = value; } } [SerializeField] private int smoothness; public int Smoothness { get { return smoothness; } set { smoothness = Mathf.Max(2, value); } } [SerializeField] private float width; public float Width { get { return width; } set { width = Mathf.Max(0, value); } } [SerializeField] private float falloffWidth; public float FalloffWidth { get { return falloffWidth; } set { falloffWidth = Mathf.Max(0, value); } } [SerializeField] private GSpline spline; public GSpline Spline { get { if (spline == null) { spline = new GSpline(); } return spline; } set { spline = value; } } #if UNITY_EDITOR private List vertices; public List Editor_Vertices { get { if (vertices == null) vertices = new List(); return vertices; } set { vertices = value; } } #endif public void Reset() { PositionOffset = Vector3.up * 5; InitialRotation = Quaternion.identity; InitialScale = Vector3.one; Smoothness = 20; Width = 1; FalloffWidth = 1; } private void OnEnable() { UpdateMeshes(); } private void OnDisable() { CleanUp(); } public void UpdateMeshes() { List segments = Spline.Segments; for (int i = 0; i < segments.Count; ++i) { UpdateMesh(i); } } public void UpdateMesh(int sIndex) { List segments = Spline.Segments; if (sIndex < 0 || sIndex >= segments.Count) throw new System.IndexOutOfRangeException("segmentIndex is out of range"); GSplineSegment s = segments[sIndex]; List vertices = new List(); List colors = new List(); List triangles = new List(); float tStep = 1f / (Smoothness - 1); for (int tIndex = 0; tIndex < Smoothness - 1; ++tIndex) { float t0 = tIndex * tStep; Vector3 translation0 = Spline.EvaluatePosition(sIndex, t0); Quaternion rotation0 = Spline.EvaluateRotation(sIndex, t0); Vector3 scale0 = Spline.EvaluateScale(sIndex, t0); float t1 = (tIndex + 1) * tStep; Vector3 translation1 = Spline.EvaluatePosition(sIndex, t1); Quaternion rotation1 = Spline.EvaluateRotation(sIndex, t1); Vector3 scale1 = Spline.EvaluateScale(sIndex, t1); Matrix4x4 matrix0 = Matrix4x4.TRS(translation0, rotation0, scale0); Matrix4x4 matrix1 = Matrix4x4.TRS(translation1, rotation1, scale1); Vector3 bl, tl, tr, br; float halfWidth = Width * 0.5f; if (FalloffWidth > 0) { //Left falloff bl = matrix0.MultiplyPoint(new Vector3(-halfWidth - FalloffWidth, 0, 0)); tl = matrix1.MultiplyPoint(new Vector3(-halfWidth - FalloffWidth, 0, 0)); tr = matrix1.MultiplyPoint(new Vector3(-halfWidth, 0, 0)); br = matrix0.MultiplyPoint(new Vector3(-halfWidth, 0, 0)); vertices.Add(bl); vertices.Add(tl); vertices.Add(tr); colors.Add(Color.clear); colors.Add(Color.clear); colors.Add(Color.white); vertices.Add(bl); vertices.Add(tr); vertices.Add(br); colors.Add(Color.clear); colors.Add(Color.white); colors.Add(Color.white); } if (Width > 0) { //Center bl = matrix0.MultiplyPoint(new Vector3(-halfWidth, 0, 0)); tl = matrix1.MultiplyPoint(new Vector3(-halfWidth, 0, 0)); tr = matrix1.MultiplyPoint(new Vector3(halfWidth, 0, 0)); br = matrix0.MultiplyPoint(new Vector3(halfWidth, 0, 0)); vertices.Add(bl); vertices.Add(tl); vertices.Add(tr); colors.Add(Color.white); colors.Add(Color.white); colors.Add(Color.white); vertices.Add(bl); vertices.Add(tr); vertices.Add(br); colors.Add(Color.white); colors.Add(Color.white); colors.Add(Color.white); } if (FalloffWidth > 0) { //Right falloff bl = matrix0.MultiplyPoint(new Vector3(halfWidth, 0, 0)); tl = matrix1.MultiplyPoint(new Vector3(halfWidth, 0, 0)); tr = matrix1.MultiplyPoint(new Vector3(halfWidth + FalloffWidth, 0, 0)); br = matrix0.MultiplyPoint(new Vector3(halfWidth + FalloffWidth, 0, 0)); vertices.Add(bl); vertices.Add(tl); vertices.Add(tr); colors.Add(Color.white); colors.Add(Color.white); colors.Add(Color.clear); vertices.Add(bl); vertices.Add(tr); vertices.Add(br); colors.Add(Color.white); colors.Add(Color.clear); colors.Add(Color.clear); } } Mesh m = s.Mesh; m.Clear(); m.SetVertices(vertices); m.SetColors(colors); m.SetTriangles(GUtilities.GetIndicesArray(vertices.Count), 0); m.uv = null; m.normals = null; m.tangents = null; } public void UpdateMesh(IEnumerable indices) { IEnumerator i = indices.GetEnumerator(); while (i.MoveNext()) { UpdateMesh(i.Current); } } public void CleanUp() { Spline.Dispose(); } [System.Obsolete] public List GenerateVerticesWithFalloff() { List vertices = new List(); return vertices; } public IEnumerable SweepDirtyRect(GStylizedTerrain terrain) { if (terrain.TerrainData == null) return new List(); int gridSize = terrain.TerrainData.Geometry.ChunkGridSize; List uvRects = new List(); for (int x = 0; x < gridSize; ++x) { for (int z = 0; z < gridSize; ++z) { uvRects.Add(GCommon.GetUvRange(gridSize, x, z)); } } HashSet dirtyRects = new HashSet(); Vector3 terrainSize = new Vector3( terrain.TerrainData.Geometry.Width, terrain.TerrainData.Geometry.Height, terrain.TerrainData.Geometry.Length); float splineSize = Mathf.Max(1, Width + FalloffWidth * 2); Vector2 sweepRectSize = new Vector2( Mathf.InverseLerp(0, terrainSize.x, splineSize), Mathf.InverseLerp(0, terrainSize.z, splineSize)); Rect sweepRect = new Rect(); sweepRect.size = sweepRectSize; int segmentCount = Spline.Segments.Count; for (int sIndex = 0; sIndex < segmentCount; ++sIndex) { float tStep = 1f / (Smoothness - 1); for (int tIndex = 0; tIndex < Smoothness - 1; ++tIndex) { float t = tIndex * tStep; Vector3 worldPos = transform.TransformPoint(Spline.EvaluatePosition(sIndex, t)); Vector3 scale = transform.TransformVector(Spline.EvaluateScale(sIndex, t)); float maxScaleComponent = Mathf.Max(Mathf.Abs(scale.x), Mathf.Abs(scale.y), Mathf.Abs(scale.z)); Vector3 normalizedPos = terrain.WorldPointToNormalized(worldPos); sweepRect.center = new Vector2(normalizedPos.x, normalizedPos.z); sweepRect.size = sweepRectSize * maxScaleComponent; for (int rIndex = 0; rIndex < uvRects.Count; ++rIndex) { if (uvRects[rIndex].Overlaps(sweepRect)) { dirtyRects.Add(uvRects[rIndex]); } } } } return dirtyRects; } public bool OverlapTest(GStylizedTerrain terrain) { Rect terrainRect = terrain.Rect; float splineSize = Mathf.Max(1, Width + FalloffWidth * 2); Vector2 sweepRectSize = Vector2.one * splineSize; Rect sweepRect = new Rect(); sweepRect.size = sweepRectSize; int segmentCount = Spline.Segments.Count; for (int sIndex = 0; sIndex < segmentCount; ++sIndex) { float tStep = 1f / (Smoothness - 1); for (int tIndex = 0; tIndex < Smoothness - 1; ++tIndex) { float t = tIndex * tStep; Vector3 worldPos = Spline.EvaluatePosition(sIndex, t); Vector3 scale = Spline.EvaluateScale(sIndex, t); float maxScaleComponent = Mathf.Max(Mathf.Abs(scale.x), Mathf.Abs(scale.y), Mathf.Abs(scale.z)); sweepRect.center = new Vector2(worldPos.x, worldPos.z); sweepRect.size = sweepRectSize * maxScaleComponent; if (sweepRect.Overlaps(terrainRect)) { return true; } } } return false; } public static void MarkSplineChanged(GSplineCreator sender) { if (Editor_SplineChanged != null) { Editor_SplineChanged.Invoke(sender); } } public void DrawOnTexture(RenderTexture rt, Bounds worldBounds, Material mat) { Vector3 cameraWorldPos = new Vector3(worldBounds.center.x, worldBounds.max.y + worldBounds.size.y, worldBounds.center.z); GameObject cameraObject = new GameObject("Camera") { hideFlags = HideFlags.DontSave }; Camera cam = cameraObject.AddComponent(); cam.transform.position = cameraWorldPos; cam.transform.rotation = Quaternion.Euler(90, 0, 0); cam.transform.localScale = Vector3.one; cam.nearClipPlane = 0.01f; cam.farClipPlane = worldBounds.size.y * 2; cam.clearFlags = CameraClearFlags.Nothing; cam.cameraType = CameraType.Preview; cam.aspect = worldBounds.size.x / worldBounds.size.z; cam.orthographic = true; cam.orthographicSize = worldBounds.size.z * 0.5f; cam.useOcclusionCulling = false; cam.cullingMask = LayerMask.GetMask(SPLINE_LAYER); cam.enabled = false; cam.targetTexture = rt; Matrix4x4 worldToNormalized = Matrix4x4.TRS( worldBounds.min, Quaternion.identity, worldBounds.size).inverse; mat.SetMatrix("_WorldToNormalized", worldToNormalized); List segments = Spline.Segments; for (int i = 0; i < segments.Count; ++i) { Mesh m = segments[i].Mesh; Graphics.DrawMesh( m, transform.localToWorldMatrix, mat, LayerMask.NameToLayer(SPLINE_LAYER), cam, 0, null, ShadowCastingMode.Off, false, null, LightProbeUsage.Off, null); } cam.Render(); cam.targetTexture = null; GUtilities.DestroyGameobject(cameraObject); } public void DrawOnTexture(RenderTexture rt, Bounds worldBounds, Material mat, ScriptableRenderContext context) { #if GRIFFIN_URP Vector3 cameraWorldPos = new Vector3(worldBounds.center.x, worldBounds.max.y + worldBounds.size.y, worldBounds.center.z); GameObject cameraObject = new GameObject("Camera") { hideFlags = HideFlags.DontSave }; Camera cam = cameraObject.AddComponent(); cam.transform.position = cameraWorldPos; cam.transform.rotation = Quaternion.Euler(90, 0, 0); cam.transform.localScale = Vector3.one; cam.nearClipPlane = 0.01f; cam.farClipPlane = worldBounds.size.y * 2; cam.clearFlags = CameraClearFlags.Nothing; cam.cameraType = CameraType.Preview; cam.aspect = worldBounds.size.x / worldBounds.size.z; cam.orthographic = true; cam.orthographicSize = worldBounds.size.z * 0.5f; cam.useOcclusionCulling = false; cam.cullingMask = LayerMask.GetMask(SPLINE_LAYER); cam.enabled = false; cam.targetTexture = rt; Matrix4x4 worldToNormalized = Matrix4x4.TRS( worldBounds.min, Quaternion.identity, worldBounds.size).inverse; mat.SetMatrix("_WorldToNormalized", worldToNormalized); mat.SetPass(0); if (GCommon.CurrentRenderPipeline == GRenderPipelineType.Universal) { UniversalAdditionalCameraData urpCameraData = cam.GetUniversalAdditionalCameraData(); urpCameraData.renderType = CameraRenderType.Overlay; } List segments = Spline.Segments; for (int i = 0; i < segments.Count; ++i) { Mesh m = segments[i].Mesh; Graphics.DrawMesh( m, transform.localToWorldMatrix, mat, LayerMask.NameToLayer(SPLINE_LAYER), cam, 0, null, ShadowCastingMode.Off, false, null, LightProbeUsage.Off, null); } #if UNITY_2022_2_OR_NEWER RenderPipeline.SubmitRenderRequest(cam, new RenderPipeline.StandardRequest()); #else UniversalRenderPipeline.RenderSingleCamera(context, cam); #endif cam.targetTexture = null; GUtilities.DestroyGameobject(cameraObject); #endif } } } #endif