kingBook

导航

unity 凸包算法

https://www.pianshen.com/article/763090525/
https://blog.csdn.net/qq_29242649/article/details/105772034
一:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 凸包算法
/// </summary>
public class ConvexHullAlgorithm{

	public static readonly float Epsilon = 1e-7f;
	
	private struct VertexData{
		public int index;
		public Vector3 vertex;
	}

	/// <summary>
	/// 执行算法(将修改 indices 参数,将以相对平面坐标系(从平面上方看向平面)的逆时针顺序输出凸包索引列表)
	/// </summary>
	/// <param name="indices">引用的索引列表</param>
	/// <param name="vertices">顶点列表</param>
	/// <param name="planeNormal">坐标系平面(从平面上方看向平面)</param>
	public static void Execute(ref List<int> indices,Vector3[] vertices,Vector3 planeNormal){
		Quaternion rotation=Quaternion.FromToRotation(planeNormal,Vector3.back);
		
		int indexCount=indices.Count;
		List<int> result=new List<int>();

		List<VertexData> vertexDatas=new List<VertexData>(indexCount);

		for(int i=0;i<indexCount;i++){
			int index=indices[i];
			Vector3 vertex=vertices[index];
			//旋转至切割平面坐标系(从平面上方看向平面)
			vertex=rotation*vertex;
			vertex.z=0f;

			VertexData vertexData=new VertexData();
			vertexData.index=index;
			vertexData.vertex=vertex;
			vertexDatas.Add(vertexData);
		}

		//找到y值最小的点作为p0,如果有多个则选择最左边的那个
		float minY=float.MaxValue;
		int p0Index=-1;
		for(int i=0;i<indexCount;i++){
			VertexData vertexData=vertexDatas[i];
			if(vertexData.vertex.y<minY){
				minY=vertexData.vertex.y;
				p0Index=i;
			}else if(vertexData.vertex.y==minY){
				if(p0Index>-1){
					if(vertexData.vertex.x<vertexDatas[p0Index].vertex.x){
						p0Index=i;
					}
				}
			}
		}
		VertexData p0=vertexDatas[p0Index];

		List<VertexData> resultVertexDatas=new List<VertexData>();
		resultVertexDatas.Add(p0);
		vertexDatas.RemoveAt(p0Index);//移除p0

		//计算测试点相对于p0的幅角,并按小到大排序
		vertexDatas.Sort((VertexData a,VertexData b)=>{ 
			Vector3 oa=a.vertex-p0.vertex;
			Vector3 ob=b.vertex-p0.vertex;
			float angleA=Mathf.Atan2(oa.y,oa.x);
			float angleB=Mathf.Atan2(ob.y,ob.x);
			if(Mathf.Abs(angleA-angleB)<1e-4f){
				return (int)Mathf.Sign(oa.magnitude-ob.magnitude);
			}
			return (int)Mathf.Sign(angleA-angleB);

		});
		
		resultVertexDatas.Add(vertexDatas[0]);
		resultVertexDatas.Add(vertexDatas[1]);
		//此时 resultVertexDatas 有 p0,p1,p2;而 vertexDatas 移除了p0,vertexDatas[0]为 p1

		for(int i=2,len=vertexDatas.Count;i<len;i++){
			VertexData baseVertex=resultVertexDatas[resultVertexDatas.Count-2];//从p1开始

			Vector3 v1=vertexDatas[i-1].vertex-baseVertex.vertex;//i等于2时,是 p2-p1
			Vector3 v2=vertexDatas[i].vertex-baseVertex.vertex;//i等于2时,是 p3-p1
			v1.z=v2.z=0;
			//cross需要判断小数精度,否则三角化时可能出现非凸多边形
			if(Vector3.Cross(v1,v2).z<Epsilon){
			//if(Vector3.Cross(v1,v2).z<0){
				resultVertexDatas.RemoveAt(resultVertexDatas.Count-1);
				while(resultVertexDatas.Count>=2){
					VertexData baseVertex2=resultVertexDatas[resultVertexDatas.Count-2];
					Vector3 v12=resultVertexDatas[resultVertexDatas.Count-1].vertex-baseVertex2.vertex;
					Vector3 v22=vertexDatas[i].vertex-baseVertex2.vertex;
					v12.z=v22.z=0;
					//cross需要判断小数精度,否则三角化时可能出现非凸多边形
					if(Vector3.Cross(v12,v22).z<Epsilon){
					//if(Vector3.Cross(v12,v22).z<0){
						resultVertexDatas.RemoveAt(resultVertexDatas.Count-1);
					}else{
						break;
					}
				}
			}
			resultVertexDatas.Add(vertexDatas[i]);
		}

		indices.Clear();

		for(int i=0,len=resultVertexDatas.Count;i<len;i++){
			indices.Add(resultVertexDatas[i].index);
		}
		
	}
}

posted on 2021-02-22 15:32  kingBook  阅读(18)  评论(0编辑  收藏