├── AutomaticLOD.cs ├── MIT LICENSE ├── MathExtra.cs ├── PRTriangle.cs ├── PRVertex.cs ├── README.md └── ReductionData.cs /AutomaticLOD.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | namespace PolyReduction{ 6 | public class AutomaticLOD : MonoBehaviour { 7 | public Mesh meshToGenerate; 8 | private Vector3[] vertices; 9 | private int[] triangles; 10 | private Vector3[] normals; 11 | private PRVertex[] prVertices; 12 | private PRTriangle[] prTriangle; 13 | private int vertexNum = 0; 14 | private List reductionData = new List(); 15 | // Use this for initialization 16 | void Start () { 17 | 18 | Generate (); 19 | } 20 | void Generate() 21 | { 22 | if (meshToGenerate == null) 23 | meshToGenerate = GetComponent ().mesh; 24 | meshToGenerate = Object.Instantiate (meshToGenerate); 25 | 26 | vertices = meshToGenerate.vertices; 27 | triangles = meshToGenerate.triangles; 28 | normals = meshToGenerate.normals; 29 | vertexNum = vertices.Length; 30 | prVertices = new PRVertex[vertices.Length]; 31 | prTriangle = new PRTriangle[triangles.Length/3]; 32 | int i; 33 | int j; 34 | for (i = 0; i ().mesh = meshToGenerate; 72 | } 73 | public PRVertex MinimunCostEdge() 74 | { 75 | PRVertex t=prVertices[0]; 76 | for (int i = 0; i < prVertices.Length; i++) { 77 | if (prVertices [i].cost < t.cost) 78 | t = prVertices [i]; 79 | } 80 | return t; 81 | } 82 | public float ComputeEdgeCollapseCost(PRVertex u,PRVertex v) 83 | { 84 | float edgeLength = MathExtra.GetV3L (u.pos-v.pos); 85 | float curvature = 0f; 86 | List sides = new List (); 87 | for (int i = 0; i < u.face.Count; i++) { 88 | if (u.face [i].HasVertex (v)) 89 | sides.Add (u.face[i]); 90 | } 91 | for (int i = 0; i < u.face.Count; i++) { 92 | float mincurv = 1f; 93 | for (int j = 0; j < sides.Count; j++) { 94 | float dotprod = Vector3.Dot (u.face [i].normal, sides [j].normal); 95 | mincurv = Mathf.Min (mincurv,(1f-dotprod)*0.5f); 96 | } 97 | curvature = Mathf.Max (curvature,mincurv); 98 | } 99 | return edgeLength * curvature; 100 | } 101 | public void ComputeEdgeCostAtVertex(PRVertex v) 102 | { 103 | if (v.neighbor.Count == 0) { 104 | v.collapse = null; 105 | v.cost = 1000000f; 106 | return; 107 | } 108 | v.cost = 1000000f; 109 | v.collapse = null; 110 | for (int i = 0; i < v.neighbor.Count; i++) { 111 | float c; 112 | c = ComputeEdgeCollapseCost (v,v.neighbor[i]); 113 | if (c < v.cost) { 114 | v.collapse = v.neighbor [i]; 115 | v.cost = c; 116 | } 117 | 118 | } 119 | } 120 | public void Collapse(PRVertex u,PRVertex v) 121 | { 122 | if (v==null) { 123 | Debug.Log ("!!!");//prVertices [u.id] = null; 124 | return; 125 | } 126 | //Debug.Log (u.id.ToString()+" "+v.id.ToString()+" "+u.cost.ToString()); 127 | int i; 128 | List tmp = new List (); 129 | for (i = 0; i < u.neighbor.Count; i++) { 130 | tmp.Add(u.neighbor[i]); 131 | } 132 | ReductionData rd = new ReductionData (); 133 | rd.vertexU = u.id; 134 | rd.vertexV = v.id; 135 | v.neighbor.Remove (u); 136 | for (i = u.face.Count-1; i >= 0; i--) { 137 | u.face[i].ReplaceVertex(u,v); 138 | } 139 | for (int j = 0; j < u.face.Count; j++) { 140 | rd.triangleID.Add(u.face[j].id); 141 | } 142 | reductionData.Add (rd); 143 | ComputeEdgeCostAtVertex (v); 144 | //prVertices [u.id] = null; 145 | for (i = 0; i < tmp.Count; i++) { 146 | ComputeEdgeCostAtVertex(tmp[i]); 147 | } 148 | //prVertices [u.id] = null; 149 | u.cost = 10000000f; 150 | } 151 | public void ApplyData(ReductionData rd) 152 | { 153 | 154 | for (int i = 0; i < rd.triangleID.Count; i++) { 155 | if (triangles [rd.triangleID [i]] == rd.vertexV || triangles [rd.triangleID [i] + 1] == rd.vertexV || triangles [rd.triangleID [i] + 2] == rd.vertexV) { 156 | triangles [rd.triangleID [i]] = triangles [rd.triangleID [i]+1] = triangles [rd.triangleID [i]+2] = 0; 157 | } else { 158 | if (triangles [rd.triangleID [i]] == rd.vertexU) { 159 | triangles [rd.triangleID [i]] = rd.vertexV; 160 | continue; 161 | } 162 | if (triangles [rd.triangleID [i]+1] == rd.vertexU) { 163 | triangles [rd.triangleID [i]+1] = rd.vertexV; 164 | continue; 165 | } 166 | if (triangles [rd.triangleID [i]+2] == rd.vertexU) { 167 | triangles [rd.triangleID [i]+2] = rd.vertexV; 168 | continue; 169 | } 170 | } 171 | } 172 | } 173 | public void BackData(ReductionData rd) 174 | { 175 | 176 | } 177 | 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /MIT LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /MathExtra.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | public static class MathExtra { 5 | 6 | public static Vector3 Verlet(Vector3 lxt,Vector3 xt,Vector3 a,float d = 0.2f,float dt = 0.02f) 7 | { 8 | return xt + d * (xt - lxt) + a * dt*dt; 9 | } 10 | public static float GetDegree(float d) 11 | { 12 | while (d>180f) { 13 | d -= 360f; 14 | } 15 | while (d<-180f) { 16 | d += 360f; 17 | } 18 | return d; 19 | } 20 | 21 | 22 | public static float GetV2L(Vector2 v) 23 | { 24 | return MathExtra.fastSqrt(Mathf.Pow(v.x,2f)+Mathf.Pow(v.y,2f)); 25 | } 26 | public static float GetV3L(Vector3 v) 27 | { 28 | return MathExtra.fastSqrt(Mathf.Pow(v.x,2f)+Mathf.Pow(v.y,2f)+Mathf.Pow(v.z,2f)); 29 | } 30 | public static float fastSqrt(float x) 31 | { 32 | return 1f / MathExtra.InverseSqrtFast (x); 33 | } 34 | public static float InverseSqrtFast(float x) 35 | { 36 | //return OpenGLTK.MathInverseSqrtFast.InverseSqrtFast(x); 37 | unsafe 38 | { 39 | float xhalf = 0.5f * x; 40 | int i = *(int*)&x; // Read bits as integer. 41 | i = 0x5f375a86 - (i >> 1); // Make an initial guess for Newton-Raphson approximation 42 | x = *(float*)&i; // Convert bits back to float 43 | x = x * (1.5f - xhalf * x * x); // Perform left single Newton-Raphson step. 44 | return x; 45 | } 46 | } 47 | public static float Dot (Vector2 v1,Vector2 v2) 48 | { 49 | return v1.x * v2.x + v1.y * v2.y; 50 | } 51 | public static Vector2 Damping(float time,float swing,float damping,float hz){ 52 | Vector2 pos = Vector2.zero; 53 | pos.x = time; 54 | pos.y = swing * Mathf.Pow (damping, -time) * Mathf.Sin (hz * time); 55 | return pos; 56 | } 57 | public static float GetDegree(Vector2 dir) 58 | { 59 | if (dir.y == 0) { 60 | if (dir.x > 0) { 61 | return 0f; 62 | } else { 63 | return 180f; 64 | } 65 | } 66 | float oppo = dir.y; 67 | float hy = dir.magnitude; 68 | float degree = Mathf.Asin (oppo / hy) * Mathf.Rad2Deg; 69 | if (dir.x < 0 && dir.y > 0) 70 | return 180f - degree; 71 | if (dir.x <= 0 && dir.y < 0) 72 | return 180f - degree; 73 | if (dir.x > 0 && dir.y < 0) 74 | return 360f + degree; 75 | return degree; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /PRTriangle.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | namespace PolyReduction{ 4 | public class PRTriangle{ 5 | public int id; 6 | public PRVertex[] vertex = new PRVertex[3]; 7 | public Vector3 normal; 8 | public PRTriangle(int id,PRVertex v1,PRVertex v2,PRVertex v3) 9 | { 10 | this.id = id; 11 | vertex [0] = v1; 12 | vertex [1] = v2; 13 | vertex [2] = v3; 14 | ComputeNormal (); 15 | } 16 | public void ComputeNormal() 17 | { 18 | Vector3 p1 = vertex[1].pos-vertex[0].pos; 19 | Vector3 p2 = vertex[2].pos-vertex[0].pos; 20 | normal = Vector3.Cross (p1,p2).normalized; 21 | } 22 | public bool HasVertex(PRVertex v) 23 | { 24 | if (vertex [0] == v) 25 | return true; 26 | if (vertex [1] == v) 27 | return true; 28 | if (vertex [2] == v) 29 | return true; 30 | return false; 31 | } 32 | public void ReplaceVertex(PRVertex u,PRVertex v) 33 | { 34 | v.AddFace (this); 35 | if (vertex [0] == u) { 36 | vertex [0] = v; 37 | } 38 | if (vertex [1] == u) { 39 | vertex [1] = v; 40 | } 41 | if (vertex [2] == u) { 42 | vertex [2] = v; 43 | } 44 | for (int i = 0; i < 3; i++) { 45 | if (vertex [i].neighbor.Contains (u)) { 46 | vertex [i].neighbor.Remove (u); 47 | vertex [i].AddNeighbor(v); 48 | } 49 | v.AddNeighbor(vertex [i]); 50 | } 51 | 52 | ComputeNormal (); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /PRVertex.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | namespace PolyReduction{ 5 | public class PRVertex { 6 | public int id; 7 | public Vector3 pos; 8 | public List neighbor = new List(); 9 | public List face = new List(); 10 | public float cost; 11 | public PRVertex collapse; 12 | public PRVertex(int id,Vector3 pos) 13 | { 14 | this.id = id; 15 | this.pos = pos; 16 | } 17 | public void AddNeighbor(PRVertex v) 18 | { 19 | if ((!neighbor.Contains (v))&&v!=this) 20 | neighbor.Add (v); 21 | } 22 | public void AddFace(PRTriangle t) 23 | { 24 | if (!face.Contains (t)) 25 | face.Add (t); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Automic-LOD 2 | Automic poly reducer 3 | 4 | 高模运行时转低模 5 | 6 | http://dev.gameres.com/Program/Visual/3D/PolygonReduction.htm 中文 7 | 8 | http://dev.gameres.com/Program/Visual/3D/PolygonReduction.pdf english 9 | 10 | # 使用方法 11 | 12 | 将AutomaticLOD作为componnet添加到gameobject上 13 | 14 | 把想要减面的mesh拖到meshToGenerate变量中 15 | 16 | 运行游戏 17 | 18 | # 注意 19 | 20 | 此工程只是为了理解论文实现,仅适用于学习,还不具备实用性 21 | 22 | 目前的实现默认位置相同即为同一个顶点 23 | 24 | 如果存在相同位置有多个顶点的情况(unity默认几何体在边缘存在这种情况),还需特殊处理 25 | 26 | -------------------------------------------------------------------------------- /ReductionData.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | namespace PolyReduction{ 5 | public class ReductionData : ScriptableObject { 6 | public int vertexU; 7 | public int vertexV; 8 | public List triangleID = new List(); 9 | } 10 | } 11 | --------------------------------------------------------------------------------