123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using Unity.Collections;
- using UnityEngine;
- using UnityEngine.U2D;
- using System;
-
- namespace UnityEngine.Rendering.Universal
- {
- [Serializable]
- internal class ShadowMesh2D : ShadowShape2D
- {
- internal const int k_CapsuleCapSegments = 8;
- internal const float k_TrimEdgeUninitialized = -1;
-
- public enum EdgeProcessing
- {
- None,
- Clipping,
- }
-
- [SerializeField] Mesh m_Mesh;
- [SerializeField] Bounds m_LocalBounds;
- [SerializeField] EdgeProcessing m_EdgeProcessing = EdgeProcessing.Clipping;
- [SerializeField] float m_TrimEdge = k_TrimEdgeUninitialized;
- [SerializeField] bool m_FlipX;
- [SerializeField] bool m_FlipY;
- [SerializeField] float m_InitialTrim = 0;
-
- public Mesh mesh { get => m_Mesh; }
- public BoundingSphere boundingSphere { get => m_BoundingSphere; }
- internal BoundingSphere m_BoundingSphere; // update to world space
- public EdgeProcessing edgeProcessing { get { return m_EdgeProcessing; } set { m_EdgeProcessing = value; } }
- public float trimEdge { get { return m_TrimEdge; } set { m_TrimEdge = value; } }
-
- static internal void DuplicateShadowMesh(Mesh source, out Mesh dest)
- {
- // This is not gc tested as this generates garbage
- dest = new Mesh();
- dest.Clear();
-
- if (source != null)
- {
- dest.vertices = source.vertices;
- dest.tangents = source.tangents;
- dest.triangles = source.triangles;
- dest.bounds = source.bounds;
- }
- }
-
- internal void CopyFrom(ShadowMesh2D source)
- {
- // This is not gc tested as this generates garbage (calls DuplicateShadowMesh)
- DuplicateShadowMesh(source.m_Mesh, out m_Mesh);
- m_TrimEdge = source.trimEdge;
- m_LocalBounds = source.m_LocalBounds;
- m_EdgeProcessing = source.edgeProcessing;
- }
-
- internal void AddCircle(Vector3 center, float r, NativeArray<Vector3> generatedVertices, NativeArray<int> generatedIndices, bool reverseWindingOrder, ref int vertexWritePos, ref int indexWritePos)
- {
- float direction = reverseWindingOrder ? 1 : -1;
-
- // Special case a full circle
- float segments = 2 * k_CapsuleCapSegments;
- float angle;
- int startWritePos = vertexWritePos;
- for (int i = 0; i < segments; i++)
- {
- angle = direction * (2 * Mathf.PI * (float)i / (float)segments);
- float x = r * Mathf.Cos(angle) + center.x;
- float y = r * Mathf.Sin(angle) + center.y;
- generatedIndices[indexWritePos++] = vertexWritePos;
- generatedIndices[indexWritePos++] = i + 1 < segments ? vertexWritePos + 1 : startWritePos;
- generatedVertices[vertexWritePos++] = new Vector3(x, y, 0);
- }
- }
-
- internal void AddCapsuleCap(Vector3 center, float r, Vector3 otherCenter, NativeArray<Vector3> generatedVertices, NativeArray<int> generatedIndices, bool reverseWindingOrder, ref int vertexWritePos, ref int indexWritePos)
- {
- float startAngle;
- float endAngle;
-
- // Special case a full circle
- float segments = k_CapsuleCapSegments;
- Vector3 otherCenterDir = (otherCenter - center).normalized;
- float absCenterAngle = Mathf.Acos(Vector3.Dot(otherCenterDir, new Vector3(1, 0, 0)));
- float angleSign = Vector3.Dot(otherCenterDir, new Vector3(0, 1, 0)) < 0 ? -1f : 1f;
- float centerAngle = absCenterAngle * angleSign;
-
- // This is hard coded for a half circle
- if (reverseWindingOrder)
- {
- float HalfPI = 0.5f * Mathf.PI;
- startAngle = centerAngle + HalfPI;
- endAngle = startAngle + Mathf.PI;
- }
- else
- {
- float ThreeHalfsPI = 1.5f * Mathf.PI;
- startAngle = centerAngle + ThreeHalfsPI;
- endAngle = startAngle - Mathf.PI;
- }
-
- float deltaAngle = endAngle - startAngle;
- float angle;
-
- for (int i = 0; i < segments; i++)
- {
- angle = (deltaAngle * (float)i / (float)segments) + startAngle;
- float x = r * Mathf.Cos(angle) + center.x;
- float y = r * Mathf.Sin(angle) + center.y;
- generatedIndices[indexWritePos++] = vertexWritePos;
- generatedIndices[indexWritePos++] = vertexWritePos + 1;
- generatedVertices[vertexWritePos++] = new Vector3(x, y, 0);
- }
- angle = deltaAngle + startAngle;
- generatedVertices[vertexWritePos++] = new Vector3(r * Mathf.Cos(angle) + center.x, r * Mathf.Sin(angle) + center.y, 0);
- }
-
- internal void AddCapsule(Vector3 pt0, Vector3 pt1, float r0, float r1, NativeArray<Vector3> generatedVertices, NativeArray<int> generatedIndices, bool reverseWindingOrder, ref int vertexWritePos, ref int indexWritePos)
- {
- // Add Straight Segments
- Vector3 delta = (pt1 - pt0).normalized;
- Vector3 relOffset0 = new Vector3(delta.y, -delta.x, 0);
- Vector3 relOffset1 = new Vector3(-delta.y, delta.x, 0);
-
- if (pt1.x < pt0.x)
- {
- Vector3 temp = pt0;
- pt0 = pt1;
- pt1 = temp;
- }
-
- int circle0Start = vertexWritePos;
-
- // Add circles
- AddCapsuleCap(pt0, r0, pt1, generatedVertices, generatedIndices, reverseWindingOrder, ref vertexWritePos, ref indexWritePos);
- generatedIndices[indexWritePos++] = vertexWritePos - 1;
- generatedIndices[indexWritePos++] = vertexWritePos;
- AddCapsuleCap(pt1, r1, pt0, generatedVertices, generatedIndices, reverseWindingOrder, ref vertexWritePos, ref indexWritePos);
- generatedIndices[indexWritePos++] = vertexWritePos - 1;
- generatedIndices[indexWritePos++] = circle0Start;
- }
-
- internal int AddShape(NativeArray<Vector3> vertices, NativeArray<int> indices, int indicesProcessed, NativeArray<Vector3> generatedVertices, NativeArray<int> generatedIndices, ref int vertexWritePos, ref int indexWritePos)
- {
- int indexToProcess = indicesProcessed;
- int prevIndex = indices[indexToProcess];
- int startIndex = indices[indexToProcess];
- int startWriteIndex = vertexWritePos;
-
- generatedVertices[vertexWritePos++] = vertices[prevIndex];
-
- bool continueProcessing = true;
- while (indexToProcess < indices.Length && continueProcessing)
- {
- int index0 = indices[indexToProcess++];
- int index1 = indices[indexToProcess++];
-
- generatedIndices[indexWritePos++] = vertexWritePos - 1;
-
- if (index1 != startIndex)
- {
- generatedIndices[indexWritePos++] = vertexWritePos;
- generatedVertices[vertexWritePos++] = vertices[index1];
- continueProcessing = index0 == prevIndex;
- }
- else
- {
- generatedIndices[indexWritePos++] = startWriteIndex;
- continueProcessing = false;
- }
-
- prevIndex = index1;
- }
-
- return indexToProcess;
- }
-
- public override void SetShape(NativeArray<Vector3> vertices, NativeArray<int> indices, NativeArray<float> radii, Matrix4x4 transform, ShadowShape2D.WindingOrder windingOrder = ShadowShape2D.WindingOrder.Clockwise, bool allowTriming = true, bool createInteriorGeometry = false)
- {
- if (m_TrimEdge == k_TrimEdgeUninitialized)
- m_TrimEdge = m_InitialTrim;
-
- if (m_Mesh == null)
- m_Mesh = new Mesh();
-
- if (indices.Length == 0)
- {
- m_Mesh.Clear();
- return;
- }
-
- bool reverseWindingOrder = windingOrder == ShadowShape2D.WindingOrder.CounterClockwise;
-
-
- int circleCount = 0;
- int capsuleCount = 0;
- for (int i = 0; i < indices.Length; i += 2)
- {
- int index0 = indices[i];
- int index1 = indices[i + 1];
-
- if (radii[index0] > 0 || radii[index1] > 0)
- {
- if (index0 == index1)
- circleCount++;
- else
- capsuleCount++;
- }
- }
-
- int capsuleStraightSegments = capsuleCount * 2;
- int capsuleCapSegments = capsuleCount * k_CapsuleCapSegments; // This can be refined later
- int circleSegments = circleCount * 2 * k_CapsuleCapSegments;
-
- int lineCount = (indices.Length >> 1) - (capsuleCount + circleCount);
- int indexCount = 2 * (lineCount + capsuleStraightSegments + (2 * capsuleCapSegments) + circleSegments);
- int vertexCount = indexCount; // Keep this simple for now
-
- NativeArray<Vector3> generatedVertices = new NativeArray<Vector3>(vertexCount, Allocator.Temp);
- NativeArray<int> generatedIndices = new NativeArray<int>(indexCount, Allocator.Temp);
-
- int vertexWritePos = 0;
- int indexWritePos = 0;
- int indicesProcessed = 0;
- while (indicesProcessed < indices.Length)
- {
- int v0 = indices[indicesProcessed];
- int v1 = indices[indicesProcessed + 1];
-
- float r0 = radii[v0];
- float r1 = radii[v1];
-
- if (radii[v0] > 0 || radii[v1] > 0)
- {
- Vector3 pt0 = vertices[v0];
- Vector3 pt1 = vertices[v1];
-
- if (vertices[v0].x == vertices[v1].x && vertices[v0].y == vertices[v1].y)
- AddCircle(pt0, r0, generatedVertices, generatedIndices, reverseWindingOrder, ref vertexWritePos, ref indexWritePos);
- else
- AddCapsule(pt0, pt1, r0, r1, generatedVertices, generatedIndices, reverseWindingOrder, ref vertexWritePos, ref indexWritePos);
-
- indicesProcessed += 2;
- }
- else
- {
- // Will add edges or polygons
- indicesProcessed = AddShape(vertices, indices, indicesProcessed, generatedVertices, generatedIndices, ref vertexWritePos, ref indexWritePos);
- }
- }
-
- for (int i = 0; i < generatedVertices.Length; i++)
- generatedVertices[i] = transform.MultiplyPoint(generatedVertices[i]);
-
- NativeArray<ShadowEdge> calculatedEdges;
- NativeArray<int> calculatedStartingEdges;
- NativeArray<bool> calculatedIsClosedArray;
-
- ShadowUtility.CalculateEdgesFromLines(ref generatedIndices, out calculatedEdges, out calculatedStartingEdges, out calculatedIsClosedArray);
-
- if (reverseWindingOrder)
- ShadowUtility.ReverseWindingOrder(ref calculatedStartingEdges, ref calculatedEdges);
-
- if (m_EdgeProcessing == EdgeProcessing.Clipping)
- {
- NativeArray<Vector3> clippedVertices;
- NativeArray<ShadowEdge> clippedEdges;
- NativeArray<int> clippedStartingIndices;
-
- ShadowUtility.ClipEdges(ref generatedVertices, ref calculatedEdges, ref calculatedStartingEdges, ref calculatedIsClosedArray, trimEdge, out clippedVertices, out clippedEdges, out clippedStartingIndices);
-
- if (clippedStartingIndices.Length > 0)
- m_LocalBounds = ShadowUtility.GenerateShadowMesh(m_Mesh, clippedVertices, clippedEdges, clippedStartingIndices, calculatedIsClosedArray, true, createInteriorGeometry, ShadowShape2D.OutlineTopology.Lines);
- else
- {
- m_LocalBounds = new Bounds();
- m_Mesh.Clear();
- }
-
- clippedVertices.Dispose();
- clippedEdges.Dispose();
- clippedStartingIndices.Dispose();
- }
- else
- {
- m_LocalBounds = ShadowUtility.GenerateShadowMesh(m_Mesh, generatedVertices, calculatedEdges, calculatedStartingEdges, calculatedIsClosedArray, true, createInteriorGeometry, ShadowShape2D.OutlineTopology.Lines);
- }
-
- generatedVertices.Dispose();
- generatedIndices.Dispose();
- calculatedEdges.Dispose();
- calculatedIsClosedArray.Dispose();
- calculatedStartingEdges.Dispose();
-
- }
-
-
- bool AreDegenerateVertices(NativeArray<Vector3> vertices)
- {
- if (vertices == null || vertices.Length == 0)
- return true;
-
- // This should is a trade off between perfomance and accuracy. This may need to be refined later if we find cases where this is not good enough.
- int prevIndex = vertices.Length - 1;
- for (int i=0;i< vertices.Length; i++)
- {
- if (vertices[prevIndex].x != vertices[i].x || vertices[prevIndex].y != vertices[i].y)
- return false;
-
- prevIndex = i;
- }
-
- return true;
- }
-
- public override void SetShape(NativeArray<Vector3> vertices, NativeArray<int> indices, ShadowShape2D.OutlineTopology outlineTopology, ShadowShape2D.WindingOrder windingOrder = ShadowShape2D.WindingOrder.Clockwise, bool allowTrimming = true, bool createInteriorGeometry = false)
- {
- if (AreDegenerateVertices(vertices))
- return;
-
- if (m_TrimEdge == k_TrimEdgeUninitialized)
- m_TrimEdge = m_InitialTrim;
-
-
- bool disposeVertices = false;
- NativeArray<ShadowEdge> edges;
- NativeArray<int> shapeStartingIndices;
- NativeArray<bool> shapeIsClosedArray;
-
- if (m_Mesh == null)
- m_Mesh = new Mesh();
-
- if (indices.Length == 0)
- {
- m_Mesh.Clear();
- return;
- }
-
- if (outlineTopology == ShadowShape2D.OutlineTopology.Triangles)
- {
- NativeArray<Vector3> newVertices;
- ShadowUtility.CalculateEdgesFromTriangles(ref vertices, ref indices, true, out newVertices, out edges, out shapeStartingIndices, out shapeIsClosedArray);
-
- disposeVertices = true;
- vertices = newVertices;
- }
- else // if (outlineTopology == ShadowShape2D.OutlineTopology.Lines)
- {
- ShadowUtility.CalculateEdgesFromLines(ref indices, out edges, out shapeStartingIndices, out shapeIsClosedArray);
- }
-
- if (windingOrder == ShadowShape2D.WindingOrder.CounterClockwise)
- ShadowUtility.ReverseWindingOrder(ref shapeStartingIndices, ref edges);
-
- // It would be better if we don't have to rerun SetShape after a trimEdge change.
- if (m_EdgeProcessing == EdgeProcessing.Clipping && allowTrimming)
- {
- NativeArray<Vector3> clippedVertices;
- NativeArray<ShadowEdge> clippedEdges;
- NativeArray<int> clippedStartingIndices;
-
- ShadowUtility.ClipEdges(ref vertices, ref edges, ref shapeStartingIndices, ref shapeIsClosedArray, trimEdge, out clippedVertices, out clippedEdges, out clippedStartingIndices);
-
- m_LocalBounds = ShadowUtility.GenerateShadowMesh(m_Mesh, clippedVertices, clippedEdges, clippedStartingIndices, shapeIsClosedArray, allowTrimming, createInteriorGeometry, outlineTopology);
-
- clippedVertices.Dispose();
- clippedEdges.Dispose();
- clippedStartingIndices.Dispose();
- }
- else
- {
- m_LocalBounds = ShadowUtility.GenerateShadowMesh(m_Mesh, vertices, edges, shapeStartingIndices, shapeIsClosedArray, allowTrimming, createInteriorGeometry, outlineTopology);
- }
-
- if(disposeVertices)
- vertices.Dispose();
-
- edges.Dispose();
- shapeStartingIndices.Dispose();
- shapeIsClosedArray.Dispose();
- }
-
- public void SetShapeWithLines(NativeArray<Vector3> vertices, NativeArray<int> indices, bool allowTrimming)
- {
- SetShape(vertices, indices, ShadowShape2D.OutlineTopology.Lines, allowTrimming: allowTrimming);
- }
-
- public override void SetFlip(bool flipX, bool flipY)
- {
- m_FlipX = flipX;
- m_FlipY = flipY;
- }
-
- public override void GetFlip(out bool flipX, out bool flipY)
- {
- flipX = m_FlipX;
- flipY = m_FlipY;
-
- }
-
- public override void SetDefaultTrim(float trim)
- {
- m_InitialTrim = trim;
- }
-
- public void UpdateBoundingSphere(Transform transform)
- {
- var maxBound = transform.TransformPoint(m_LocalBounds.max);
- var minBound = transform.TransformPoint(m_LocalBounds.min);
- var center = 0.5f * (maxBound + minBound);
- var radius = Vector3.Magnitude(maxBound - center);
-
- m_BoundingSphere = new BoundingSphere(center, radius);
- }
- }
- }
|