ui基本完毕,修了一大把的bug
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
/// <summary>
|
||||
/// Defines an interface for an object that is sorted by bin number
|
||||
/// </summary>
|
||||
public interface IBinSortable
|
||||
{
|
||||
int bin { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Methods for sorting objects on an ordered grid by bin number.
|
||||
///
|
||||
/// The grid ordering is shown by example below. Even rows (row 0 = bottom row) are ordered
|
||||
/// right-to-left while odd rows are ordered left-to-right.
|
||||
/// _____ _____ _____
|
||||
/// | | | |
|
||||
/// | 6 | 7 | 8 |
|
||||
/// |_____|_____|_____|
|
||||
/// | | | |
|
||||
/// | 5 | 4 | 3 |
|
||||
/// |_____|_____|_____|
|
||||
/// | | | |
|
||||
/// | 0 | 1 | 2 |
|
||||
/// |_____|_____|_____|
|
||||
///
|
||||
/// </summary>
|
||||
public class BinSort
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the bin number for the set of grid coordinates
|
||||
/// </summary>
|
||||
/// <param name="i">Grid row</param>
|
||||
/// <param name="j">Grid column</param>
|
||||
/// <param name="n">Grid size</param>
|
||||
/// <returns></returns>
|
||||
internal static int GetBinNumber(int i, int j, int n)
|
||||
{
|
||||
return (i % 2 == 0) ? (i * n) + j : (i + 1) * n - j - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a counting sort of the input points based on their bin number. Only
|
||||
/// sorts the elements in the index range [0, count]. If binCount is <= 1, no sorting
|
||||
/// is performed. If lastIndex > input.Length, the entire input array is sorted.
|
||||
/// </summary>
|
||||
/// <param name="input">The input array to sort</param>
|
||||
/// <param name="lastIndex">The index of the last element in `input` to sort. Only the
|
||||
/// elements [0, lastIndex) are sorted.</param>
|
||||
/// <param name="binCount">Number of bins</param>
|
||||
internal static T[] Sort<T>(T[] input, int lastIndex, int binCount) where T: IBinSortable
|
||||
{
|
||||
int[] count = new int[binCount];
|
||||
T[] output = new T[input.Length];
|
||||
|
||||
#region Validation
|
||||
// Need at least two bins to sort
|
||||
if (binCount <= 1)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
// If lastIndex is out of range, default to sorting the entire input array
|
||||
if (lastIndex > input.Length)
|
||||
{
|
||||
lastIndex = input.Length;
|
||||
}
|
||||
#endregion
|
||||
|
||||
// Only sort the first [0, count] points, don't want to sort super-triangle vertices
|
||||
for (int i = 0; i < lastIndex; i++)
|
||||
{
|
||||
int j = input[i].bin;
|
||||
count[j] += 1;
|
||||
}
|
||||
|
||||
for (int i = 1; i < binCount; i++)
|
||||
{
|
||||
count[i] += count[i - 1];
|
||||
}
|
||||
|
||||
for (int i = lastIndex - 1; i >= 0; i--)
|
||||
{
|
||||
int j = input[i].bin;
|
||||
count[j] -= 1;
|
||||
output[count[j]] = input[i];
|
||||
}
|
||||
|
||||
// Copy over the rest of the un-sorted points
|
||||
for (int i = lastIndex; i < output.Length; i++)
|
||||
{
|
||||
output[i] = input[i];
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa6520366fb0c0d418e02cb5b731ad3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,128 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public static class MathUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns true if the quad specified by the two diagonals a1->a2 and b1->b2 is convex
|
||||
/// Quad is convex if a1->a2 and b1->b2 intersect each other
|
||||
/// </summary>
|
||||
/// <param name="a1">Start point of diagonal A</param>
|
||||
/// <param name="a2">End point of diagonal A</param>
|
||||
/// <param name="b1">Start point of diagonal B</param>
|
||||
/// <param name="b2">End point of diagonal B</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsQuadConvex(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2)
|
||||
{
|
||||
return LinesIntersectInternal(a1, a2, b1, b2, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true lines a1->a2 and b1->b2 is intersect
|
||||
/// </summary>
|
||||
/// <param name="a1">Start point of line A</param>
|
||||
/// <param name="a2">End point of line A</param>
|
||||
/// <param name="b1">Start point of line B</param>
|
||||
/// <param name="b2">End point of line B</param>
|
||||
/// <returns></returns>
|
||||
public static bool LinesIntersect(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2)
|
||||
{
|
||||
return LinesIntersectInternal(a1, a2, b1, b2, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true lines a1->a2 and b1->b2 is intersect
|
||||
/// </summary>
|
||||
/// <param name="a1">Start point of line A</param>
|
||||
/// <param name="a2">End point of line A</param>
|
||||
/// <param name="b1">Start point of line B</param>
|
||||
/// <param name="b2">End point of line B</param>
|
||||
/// <returns></returns>
|
||||
private static bool LinesIntersectInternal(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2, bool includeSharedEndpoints)
|
||||
{
|
||||
Vector2 a12 = new Vector2(a2.x - a1.x, a2.y - a1.y);
|
||||
Vector2 b12 = new Vector2(b2.x - b1.x, b2.y - b1.y);
|
||||
|
||||
// If any of the vertices are shared between the two diagonals,
|
||||
// the quad collapses into a triangle and is convex by default.
|
||||
if (a1 == b1 || a1 == b2 || a2 == b1 || a2 == b2)
|
||||
{
|
||||
return includeSharedEndpoints;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compute cross product between each point and the opposite diagonal
|
||||
// Look at sign of the Z component to see which side of line point is on
|
||||
float a1xb = (a1.x - b1.x) * b12.y - (a1.y - b1.y) * b12.x;
|
||||
float a2xb = (a2.x - b1.x) * b12.y - (a2.y - b1.y) * b12.x;
|
||||
float b1xa = (b1.x - a1.x) * a12.y - (b1.y - a1.y) * a12.x;
|
||||
float b2xa = (b2.x - a1.x) * a12.y - (b2.y - a1.y) * a12.x;
|
||||
|
||||
// Check that the points for each diagonal lie on opposite sides of the other
|
||||
// diagonal. Quad is also convex if a1/a2 lie on b1->b2 (and vice versa) since
|
||||
// the shape collapses into a triangle (hence >= instead of >)
|
||||
return ((a1xb >= 0 && a2xb <= 0) || (a1xb <= 0 && a2xb >= 0)) &&
|
||||
((b1xa >= 0 && b2xa <= 0) || (b1xa <= 0 && b2xa >= 0));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the intersection between the line segment a->b and the plane defined by the specified normal and origin point. If an intersection point exists, it is returned via the out parameter `intersection`. The parameter `s` is defined below and is used to properly interpolate normals/uvs for intersection vertices.
|
||||
/// </summary>
|
||||
/// <param name="a">Start point of line</param>
|
||||
/// <param name="b">End point of line</param>
|
||||
/// <param name="n">Plane normal</param>
|
||||
/// <param name="p0">Plane origin</param>
|
||||
/// <param name="x">If intersection exists, intersection point return as out parameter.</param>
|
||||
/// <param name="s">Returns the parameterization of the intersection where x = a + (b - a) * s</param>
|
||||
/// <returns></returns>
|
||||
public static bool LinePlaneIntersection(Vector3 a,
|
||||
Vector3 b,
|
||||
Vector3 n,
|
||||
Vector3 p0,
|
||||
out Vector3 x,
|
||||
out float s)
|
||||
{
|
||||
// Initialize out params
|
||||
s = 0;
|
||||
x = Vector3.zero;
|
||||
|
||||
// Handle degenerate cases
|
||||
if (a == b)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (n == Vector3.zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// `s` is the parameter for the line segment a -> b where 0.0 <= s <= 1.0
|
||||
s = Vector3.Dot(p0 - a, n) / Vector3.Dot(b - a, n);
|
||||
|
||||
if (s >= 0 && s <= 1)
|
||||
{
|
||||
x = a + (b - a) * s;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true of the point `p` is on the left side of the directed line segment `i` -> `j`
|
||||
/// Use for checking if a point is inside of a triangle. Since triangle vertices oriented
|
||||
/// CCW, a point on the left side of a triangle edge is "inside" that edge of the triangle.
|
||||
/// </summary>
|
||||
/// <param name="p">Index of test point in `points` array</param>
|
||||
/// <param name="i">Index of first vertex of the edge in the `points` array</param>
|
||||
/// /// <param name="j">Index of second vertex of the edge in the `points` array</param>
|
||||
/// <returns>True if the point `p` is on the left side of the line `i`->`j`</returns>
|
||||
public static bool IsPointOnRightSideOfLine(Vector2 a, Vector2 b, Vector2 c)
|
||||
{
|
||||
// The <= is essential; if it is <, the whole thing falls apart
|
||||
return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) <= 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d37a4c4b4b021b4dafc4db72c4a6fc2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,239 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Collections;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
public static class MeshUtils
|
||||
{
|
||||
// Description of vertex attributes for the island mesh
|
||||
private static VertexAttributeDescriptor[] layout = new[]
|
||||
{
|
||||
new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3),
|
||||
new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3),
|
||||
new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Identifies all disconnected sets of geometry contained within the mesh.
|
||||
/// Each set of geometry is split into a separate meshes.
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh to search</param>
|
||||
/// <returns>Returns an array of all disconnected meshes found.</returns>
|
||||
public static Mesh[] FindDisconnectedMeshes(Mesh mesh)
|
||||
{
|
||||
// Each disconnected set of geometry is referred to as an "island"
|
||||
List<Mesh> islands = new List<Mesh>();
|
||||
|
||||
#region Preliminaries
|
||||
|
||||
// Extract mesh data
|
||||
var vertices = mesh.vertices;
|
||||
var triangles = mesh.triangles;
|
||||
var normals = mesh.normals;
|
||||
var uvs = mesh.uv;
|
||||
|
||||
// For each triangle, find the corresponding sub-mesh index. (Mesh.triangles contains
|
||||
// the triangles for all sub-meshes)
|
||||
int[] triangleSubMesh = new int[triangles.Length / 3];
|
||||
int subMeshIndex = 0;
|
||||
int subMeshSize = mesh.GetTriangles(subMeshIndex).Length / 3;
|
||||
for (int i = 0; i < triangles.Length / 3; i++)
|
||||
{
|
||||
if (i >= subMeshSize)
|
||||
{
|
||||
subMeshIndex++;
|
||||
subMeshSize += mesh.GetTriangles(subMeshIndex).Length / 3;
|
||||
}
|
||||
triangleSubMesh[i] = subMeshIndex;
|
||||
}
|
||||
|
||||
// Identify coincident vertices
|
||||
List<int>[] coincidentVertices = new List<int>[vertices.Length];
|
||||
for(int i = 0; i < vertices.Length; i++)
|
||||
{
|
||||
coincidentVertices[i] = new List<int>();
|
||||
}
|
||||
for(int i = 0; i < vertices.Length; i++)
|
||||
{
|
||||
Vector3 v_i = vertices[i];
|
||||
for (int k = i + 1; k < vertices.Length; k++)
|
||||
{
|
||||
Vector3 v_k = vertices[k];
|
||||
if (v_i == v_k)
|
||||
{
|
||||
coincidentVertices[k].Add(i);
|
||||
coincidentVertices[i].Add(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the triangles the each vertex belongs to. Need to do this for each submesh
|
||||
List<int>[] vertexTriangles = new List<int>[vertices.Length];
|
||||
|
||||
for (int i = 0; i < vertices.Length; i++)
|
||||
{
|
||||
vertexTriangles[i] = new List<int>();
|
||||
}
|
||||
|
||||
int v1, v2, v3;
|
||||
for (int i = 0; i < triangles.Length; i += 3)
|
||||
{
|
||||
// Index of the triangle
|
||||
int t = i / 3;
|
||||
|
||||
v1 = triangles[i];
|
||||
v2 = triangles[i + 1];
|
||||
v3 = triangles[i + 2];
|
||||
|
||||
vertexTriangles[v1].Add(t);
|
||||
vertexTriangles[v2].Add(t);
|
||||
vertexTriangles[v3].Add(t);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// Search the mesh geometry and identify all islands
|
||||
// 1) Start by finding a vertex that has not yet been visited
|
||||
// 2) Insert the vertex into a queue, begin a breadth-first search
|
||||
// 3) Dequeue the next vertex 'v'
|
||||
// 4) Find all triangles that 'v' is connected to. Add each triangle to a list
|
||||
// 5) Enqueue the vertices for each connected triangle if they haven't been visited yet
|
||||
// 6) Enqueue all vertices coincident with 'v' if they haven't been visited yet
|
||||
// 7) Repeat Steps 3-6 until the queue is empty
|
||||
// 8) Take the list of triangles and use the existing mesh data to create a new island mesh
|
||||
// 9) Go back to Step 1, continue until all vertices have been visited.
|
||||
|
||||
bool[] visitedVertices = new bool[vertices.Length];
|
||||
bool[] visitedTriangles = new bool[triangles.Length];
|
||||
Queue<int> frontier = new Queue<int>();
|
||||
|
||||
// Vertex data for the island mesh. Only initialize once and keep track of pointer to last element to minimize GC
|
||||
NativeArray<MeshVertex> islandVertices = new NativeArray<MeshVertex>(vertices.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
|
||||
|
||||
// Array containing triangle data for the island mesh. Need to keep track of triangles for each sub-mesh separately
|
||||
int[][] islandTriangles = new int[mesh.subMeshCount][];
|
||||
for (int i = 0; i < mesh.subMeshCount; i++)
|
||||
{
|
||||
islandTriangles[i] = new int[triangles.Length];
|
||||
}
|
||||
|
||||
// Counters to keep track of how many vertices
|
||||
int vertexCount = 0;
|
||||
int totalIndexCount = 0;
|
||||
int[] subMeshIndexCounts = new int[mesh.subMeshCount];
|
||||
|
||||
for (int i = 0; i < vertices.Length; i++)
|
||||
{
|
||||
if (visitedVertices[i]) continue;
|
||||
|
||||
// Reset the vertex/triangle counts
|
||||
vertexCount = 0;
|
||||
totalIndexCount = 0;
|
||||
for(int j = 0; j < mesh.subMeshCount; j++)
|
||||
{
|
||||
subMeshIndexCounts[j] = 0;
|
||||
}
|
||||
|
||||
// Search the mesh geometry starting at vertex 'i'. Search is performed by looking up
|
||||
// the triangles that contain each vertex, adding their vertices, etc. until all
|
||||
// triangles have been visited.
|
||||
frontier.Enqueue(i);
|
||||
|
||||
// Index map between source mesh vertex array and the sub mesh vertex arrays
|
||||
int[] vertexMap = new int[vertices.Length];
|
||||
// Initialize map to '-1' to serve as "unmapped" value
|
||||
for(int j = 0; j < vertices.Length; j++)
|
||||
{
|
||||
vertexMap[j] = -1;
|
||||
}
|
||||
|
||||
while (frontier.Count > 0)
|
||||
{
|
||||
int k = frontier.Dequeue();
|
||||
|
||||
// Ignore vertex if we've already visited it
|
||||
if (visitedVertices[k])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
visitedVertices[k] = true;
|
||||
}
|
||||
|
||||
// Add this vertex array for the island mesh
|
||||
// Map between the original vertex index to the vertex's new index in the island
|
||||
// mesh vertex array. This will be used to update the indices for the triangles later
|
||||
vertexMap[k] = vertexCount;
|
||||
islandVertices[vertexCount++] = new MeshVertex(vertices[k], normals[k], uvs[k]);
|
||||
|
||||
// Get the list of all triangles that this vertex is a part of
|
||||
foreach(int t in vertexTriangles[k])
|
||||
{
|
||||
// If triangle is already included, skip it
|
||||
if (!visitedTriangles[t])
|
||||
{
|
||||
visitedTriangles[t] = true;
|
||||
|
||||
// Loop through each vertex of the triangle and add the non-visited ones
|
||||
// to the search frontier
|
||||
for (int m = t * 3; m < t * 3 + 3; m++)
|
||||
{
|
||||
int v = triangles[m];
|
||||
subMeshIndex = triangleSubMesh[t];
|
||||
islandTriangles[subMeshIndex][subMeshIndexCounts[subMeshIndex]++] = v;
|
||||
totalIndexCount++;
|
||||
|
||||
frontier.Enqueue(v);
|
||||
|
||||
// If this vertex is coincident with other vertices, add those to the search frontier
|
||||
foreach(int cv in coincidentVertices[v])
|
||||
{
|
||||
frontier.Enqueue(cv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the island contains at least one triangle, create a new mesh
|
||||
if (vertexCount > 0)
|
||||
{
|
||||
Mesh island = new Mesh();
|
||||
|
||||
island.SetIndexBufferParams(totalIndexCount, IndexFormat.UInt32);
|
||||
island.SetVertexBufferParams(vertexCount, layout);
|
||||
island.SetVertexBufferData(islandVertices, 0, 0, vertexCount);
|
||||
|
||||
// Set the triangles for each submesh
|
||||
island.subMeshCount = mesh.subMeshCount;
|
||||
int indexStart = 0;
|
||||
for (subMeshIndex = 0; subMeshIndex < mesh.subMeshCount; subMeshIndex++)
|
||||
{
|
||||
var subMeshIndexBuffer = islandTriangles[subMeshIndex];
|
||||
var subMeshIndexCount = subMeshIndexCounts[subMeshIndex];
|
||||
|
||||
// Map vertex indexes from the original mesh to the island mesh
|
||||
for(int k = 0; k < subMeshIndexCount; k++)
|
||||
{
|
||||
int originalIndex = subMeshIndexBuffer[k];
|
||||
subMeshIndexBuffer[k] = vertexMap[originalIndex];
|
||||
}
|
||||
|
||||
// Set the index data for this sub mesh
|
||||
island.SetIndexBufferData(subMeshIndexBuffer, 0, indexStart, (int)subMeshIndexCount);
|
||||
island.SetSubMesh(subMeshIndex, new SubMeshDescriptor(indexStart, subMeshIndexCount));
|
||||
|
||||
indexStart += subMeshIndexCount;
|
||||
}
|
||||
|
||||
island.RecalculateBounds();
|
||||
|
||||
islands.Add(island);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through rest of triangles
|
||||
return islands.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe28a9177c0175f479eb9155cc4aa3a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
public static class Vector3Extensions
|
||||
{
|
||||
//
|
||||
// that the normal is pointing to
|
||||
// - p: The point being checked
|
||||
// - n: The normal of the plane
|
||||
// - o: The origin of the plane
|
||||
/// <summary>
|
||||
/// Returns true if the point is either on or above the plane. "Above" is the side of the place in the direction of the normal.
|
||||
/// </summary>
|
||||
/// <param name="p">The test point</param>
|
||||
/// <param name="n">The plane normal</param>
|
||||
/// <param name="o">The plane origin</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsAbovePlane(this Vector3 p, Vector3 n, Vector3 o)
|
||||
{
|
||||
return (n.x * (p.x - o.x) + n.y * (p.y - o.y) + n.z * (p.z - o.z)) >= 0;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0cb4a40ec03ec24485fca5a7cd3ad16
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user