반응형
절차적 메시로 정다각형을 만들면 아래와 같은 결과를 얻는다.
그런데 N각형의 삼각형은 N - 2개면 충분한다.
위의 경우는 코드를 쉽게 적용하기 위해 N개의 삼각형을 만들었다.
따라서 오목 다각형의 삼각분할 알고리즘을 정다각형에도 적용해보자.
절차적 메시를 적용한 다각형의 삼각분할 알고리즘을 반영하여 ProceduralRegular.cs를 수정하자.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class ProceduralRegular : MonoBehaviour
{
public int polygon = 3;
public float size = 1.0f;
public Vector3 offset = new Vector3(0, 0, 0);
Mesh mesh;
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
Dictionary<Vector3, int> dic = new Dictionary<Vector3, int>();
void OnValidate()
{
if (mesh == null) return;
if (size > 0 || offset.magnitude > 0 || polygon >= 3)
{
triangluation(size, polygon);
createProceduralMesh();
}
}
void Start()
{
mesh = GetComponent<MeshFilter>().mesh;
triangluation(size, polygon);
createProceduralMesh();
}
//void setMeshData(float size, int polygon) { ... }
float CCWby2D(Vector3 a, Vector3 b, Vector3 c)
{
Vector3 p = b - a;
Vector3 q = c - b;
return Vector3.Cross(p, q).y;
}
float getAreaOfTriangle(Vector3 dot1, Vector3 dot2, Vector3 dot3)
{
Vector3 a = dot2 - dot1;
Vector3 b = dot3 - dot1;
Vector3 cross = Vector3.Cross(a, b);
return cross.magnitude / 2.0f;
}
bool checkTriangleInPoint(Vector3 dot1, Vector3 dot2, Vector3 dot3, Vector3 checkPoint)
{
float area = getAreaOfTriangle(dot1, dot2, dot3);
float dot12 = getAreaOfTriangle(dot1, dot2, checkPoint);
float dot23 = getAreaOfTriangle(dot2, dot3, checkPoint);
float dot31 = getAreaOfTriangle(dot3, dot1, checkPoint);
return (dot12 + dot23 + dot31) <= area + 0.1f /* 오차 허용 */;
}
bool CrossCheckAll(List<Vector3> list, int index)
{
Vector3 a = list[index];
Vector3 b = list[index + 1];
Vector3 c = list[index + 2];
for (int i = index + 3; i < list.Count; i++)
{
if (checkTriangleInPoint(a, b, c, list[i]) == true) return true;
}
return false;
}
void triangluation(float size, int polygon)
{
vertices.Clear();
triangles.Clear();
dic.Clear();
List<Vector3> dotList = new List<Vector3>();
dotList.Clear();
for (int i = 1; i <= polygon; i++)
{
float angle = -i * (Mathf.PI * 2.0f) / polygon;
dotList.Add((new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * size) + offset);
}
if (polygon > dotList.Count - 2) polygon = dotList.Count - 2;
for (int i = 0; i < polygon; i++)
{
List<Vector3> copy = new List<Vector3>(dotList);
for (int k = 0; k < copy.Count - 2; k++)
{
bool ccw = (CCWby2D(copy[k], copy[k + 1], copy[k + 2]) > 0);
bool cross = CrossCheckAll(copy, k);
if (ccw == true && cross == false)
{
/* triangle[0]은 부모의 LineRenderer */
//makeTriangle(lineForTriangles[i + 1], copy[k], copy[k + 1], copy[k + 2]);
for (int c = 0; c < 3; c++)
{
if (dic.ContainsKey(copy[k + c])) continue;
dic[copy[k + c]] = vertices.Count;
vertices.Add(copy[k + c]);
}
for (int c = 0; c < 3; c++)
triangles.Add(dic[copy[k + c]]);
copy.RemoveAt(k + 1);
dotList = new List<Vector3>(copy);
break;
}
}
}
}
void createProceduralMesh()
{
mesh.Clear();
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray();
mesh.RecalculateNormals();
Destroy(this.GetComponent<MeshCollider>());
this.gameObject.AddComponent<MeshCollider>();
}
}
setMeshData를 지우고 triangluation 함수가 역할을 대신하였다.
저대로 사용해도 상관없지만, 정다각형이라는 조건때문에 아래의 bool값은 더 이상 필요가 없다.
bool ccw = (CCWby2D(copy[k], copy[k + 1], copy[k + 2]) > 0);
bool cross = CrossCheckAll(copy, k);
ccw, cross를 체크할 필요가 없어졌으므로 관련 함수를 모두 지우자.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class ProceduralRegular : MonoBehaviour
{
public int polygon = 3;
public float size = 1.0f;
public Vector3 offset = new Vector3(0, 0, 0);
Mesh mesh;
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
Dictionary<Vector3, int> dic = new Dictionary<Vector3, int>();
void OnValidate()
{
if (mesh == null) return;
if (size > 0 || offset.magnitude > 0 || polygon >= 3)
{
triangluation(size, polygon);
createProceduralMesh();
}
}
void Start()
{
mesh = GetComponent<MeshFilter>().mesh;
triangluation(size, polygon);
createProceduralMesh();
}
void triangluation(float size, int polygon)
{
vertices.Clear();
triangles.Clear();
dic.Clear();
List<Vector3> dotList = new List<Vector3>();
dotList.Clear();
for (int i = 1; i <= polygon; i++)
{
float angle = -i * (Mathf.PI * 2.0f) / polygon;
dotList.Add((new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * size) + offset);
}
if (polygon > dotList.Count - 2) polygon = dotList.Count - 2;
for (int i = 0; i < polygon; i++)
{
List<Vector3> copy = new List<Vector3>(dotList);
for (int k = 0; k < copy.Count - 2; k++)
{
for (int c = 0; c < 3; c++)
{
if (dic.ContainsKey(copy[k + c])) continue;
dic[copy[k + c]] = vertices.Count;
vertices.Add(copy[k + c]);
}
for (int c = 0; c < 3; c++)
triangles.Add(dic[copy[k + c]]);
copy.RemoveAt(k + 1);
dotList = new List<Vector3>(copy);
}
}
}
void createProceduralMesh()
{
mesh.Clear();
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray();
mesh.RecalculateNormals();
Destroy(this.GetComponent<MeshCollider>());
this.gameObject.AddComponent<MeshCollider>();
}
}
마지막으로 오목 다각형처럼 점 3개를 차례대로 이동하면서 삼각형을 만들 필요가 없으므로,
기준이 되는 점만 정하면 삼각형은 모두 정해진다.
따라서 triangluation은 아래와 같이 바꿀 수 있다.
void triangluation(float size, int polygon)
{
vertices.Clear();
triangles.Clear();
dic.Clear();
for (int i = 0; i < polygon; i++)
{
float angle = -i * (Mathf.PI * 2.0f) / polygon;
vertices.Add((new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * size) + offset);
}
for (int i = 0; i < polygon - 2; i++)
{
triangles.Add(0);
triangles.Add(i + 1);
triangles.Add(i + 2);
}
}
게임을 실행하면 이전과 마찬가지로 정N각형이 만들어진다.
셰이딩 모드를 Shaded Wireframe으로 변경하면 N - 2개의 삼각형으로 나누어진 mesh도 같이 볼 수 있다.
Mesh Render를 off하면 콜라이더만 볼 수 있다.
전체 코드는 다음과 같다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class ProceduralRegular : MonoBehaviour
{
public int polygon = 3;
public float size = 1.0f;
public Vector3 offset = new Vector3(0, 0, 0);
Mesh mesh;
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
Dictionary<Vector3, int> dic = new Dictionary<Vector3, int>();
void OnValidate()
{
if (mesh == null) return;
if (size > 0 || offset.magnitude > 0 || polygon >= 3)
{
triangluation(size, polygon);
createProceduralMesh();
}
}
void Start()
{
mesh = GetComponent<MeshFilter>().mesh;
triangluation(size, polygon);
createProceduralMesh();
}
void triangluation(float size, int polygon)
{
vertices.Clear();
triangles.Clear();
dic.Clear();
for (int i = 0; i < polygon; i++)
{
float angle = -i * (Mathf.PI * 2.0f) / polygon;
vertices.Add((new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * size) + offset);
}
for (int i = 0; i < polygon - 2; i++)
{
triangles.Add(0);
triangles.Add(i + 1);
triangles.Add(i + 2);
}
}
void createProceduralMesh()
{
mesh.Clear();
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray();
mesh.RecalculateNormals();
Destroy(this.GetComponent<MeshCollider>());
this.gameObject.AddComponent<MeshCollider>();
}
}
위의 실행결과는 아래의 unitypackage에서 확인 가능하다.
Unity Plus:
Unity Pro:
Unity 프리미엄 학습:
반응형
댓글