NearPointReduction.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. // Magica Cloth.
  2. // Copyright (c) MagicaSoft, 2020-2022.
  3. // https://magicasoft.jp
  4. using System.Collections.Generic;
  5. using UnityEngine;
  6. namespace MagicaReductionMesh
  7. {
  8. /// <summary>
  9. /// 3次グリッドハッシュを利用した最近点リダクション
  10. /// </summary>
  11. public class NearPointReduction
  12. {
  13. protected MeshData meshData;
  14. /// <summary>
  15. /// 頂点データ
  16. /// </summary>
  17. public class Point
  18. {
  19. public MeshData.ShareVertex shareVertex;
  20. public Vector3 pos;
  21. public Vector3Int grid;
  22. /// <summary>
  23. /// 現在の最近点のポイント(null=なし)
  24. /// </summary>
  25. public Point nearPoint;
  26. /// <summary>
  27. /// 現在の最近点ポイントまでの距離
  28. /// </summary>
  29. public float nearDist;
  30. }
  31. /// <summary>
  32. /// 頂点データリスト
  33. /// </summary>
  34. List<Point> pointList = new List<Point>();
  35. /// <summary>
  36. /// 3次元グリッドマップ
  37. /// </summary>
  38. protected Dictionary<Vector3Int, List<Point>> gridMap = new Dictionary<Vector3Int, List<Point>>();
  39. /// <summary>
  40. /// グリッドサイズ
  41. /// </summary>
  42. protected float gridSize = 0.05f;
  43. /// <summary>
  44. /// 検索範囲
  45. /// </summary>
  46. float searchRadius;
  47. /// <summary>
  48. /// 最近点ペアの逆引き辞書(キー:最近点ポイント、データ:それを指すポイントのリスト)
  49. /// </summary>
  50. Dictionary<Point, List<Point>> nearPointDict = new Dictionary<Point, List<Point>>();
  51. //=========================================================================================
  52. public NearPointReduction(float radius = 0.05f)
  53. {
  54. searchRadius = radius;
  55. gridSize = radius * 2;
  56. }
  57. public int PointCount
  58. {
  59. get
  60. {
  61. return pointList.Count;
  62. }
  63. }
  64. //=========================================================================================
  65. /// <summary>
  66. /// リダクションデータをメッシュデータから構築する
  67. /// </summary>
  68. /// <param name="meshData"></param>
  69. public void Create(MeshData meshData)
  70. {
  71. this.meshData = meshData;
  72. foreach (var sv in meshData.shareVertexList)
  73. {
  74. AddPoint(sv, sv.wpos);
  75. }
  76. // すべてのの最近点を求める
  77. SearchNearPointAll();
  78. }
  79. /// <summary>
  80. /// リダクション実行
  81. /// </summary>
  82. public void Reduction()
  83. {
  84. Point p0 = null;
  85. var nlist = new List<Point>();
  86. while ((p0 = GetNearPointPair()) != null)
  87. {
  88. // p0にp1をマージする
  89. var p1 = p0.nearPoint;
  90. Debug.Assert(p1 != null);
  91. var sv0 = p0.shareVertex;
  92. var sv1 = p1.shareVertex;
  93. // この2つの頂点を最近点として参照しているリスト
  94. nlist.Clear();
  95. if (nearPointDict.ContainsKey(p0))
  96. {
  97. nlist.AddRange(nearPointDict[p0]);
  98. nearPointDict.Remove(p0);
  99. }
  100. if (nearPointDict.ContainsKey(p1))
  101. {
  102. nlist.AddRange(nearPointDict[p1]);
  103. nearPointDict.Remove(p1);
  104. }
  105. nlist.Add(p0); // p0も追加する
  106. // 最近点の参照を切る
  107. foreach (var np in nlist)
  108. {
  109. np.nearPoint = null;
  110. np.nearDist = 100000;
  111. }
  112. // p1を削除
  113. Remove(p1);
  114. p1 = null;
  115. // sv1にsv2をマージ
  116. meshData.CombineVertex(sv0, sv1);
  117. // p0を新しいグリッド位置に移動
  118. Move(p0, sv0.wpos);
  119. // p0/p1を指していたポイントに対して最近点を再計算する
  120. foreach (var np in nlist)
  121. {
  122. SearchNearPoint(np, searchRadius, null);
  123. }
  124. }
  125. }
  126. //=========================================================================================
  127. /// <summary>
  128. /// 頂点をグリッドに追加する
  129. /// </summary>
  130. /// <param name="pos"></param>
  131. Point AddPoint(MeshData.ShareVertex sv, Vector3 pos)
  132. {
  133. var p = new Point()
  134. {
  135. shareVertex = sv,
  136. pos = pos
  137. };
  138. pointList.Add(p);
  139. AddGrid(p);
  140. return p;
  141. }
  142. /// <summary>
  143. /// グリッドに追加
  144. /// </summary>
  145. /// <param name="p"></param>
  146. void AddGrid(Point p)
  147. {
  148. var grid = GetGridPos(p.pos);
  149. p.grid = grid;
  150. if (gridMap.ContainsKey(grid))
  151. gridMap[grid].Add(p);
  152. else
  153. {
  154. var plist = new List<Point>();
  155. plist.Add(p);
  156. gridMap.Add(grid, plist);
  157. }
  158. }
  159. /// <summary>
  160. /// グリッドから削除
  161. /// </summary>
  162. /// <param name="p"></param>
  163. void RemoveGrid(Point p)
  164. {
  165. var grid = p.grid;
  166. if (gridMap.ContainsKey(grid))
  167. {
  168. var plist = gridMap[grid];
  169. plist.Remove(p);
  170. if (plist.Count == 0)
  171. gridMap.Remove(grid);
  172. }
  173. else
  174. Debug.LogError("remove faild!");
  175. p.grid = Vector3Int.zero;
  176. }
  177. void Move(Point p, Vector3 newpos)
  178. {
  179. // グリッドから削除
  180. RemoveGrid(p);
  181. // 座標更新
  182. p.pos = newpos;
  183. // グリッド追加
  184. AddGrid(p);
  185. }
  186. /// <summary>
  187. /// 頂点をグリッドから削除する
  188. /// </summary>
  189. /// <param name="p"></param>
  190. void Remove(Point p)
  191. {
  192. // データ削除
  193. RemoveGrid(p);
  194. pointList.Remove(p);
  195. }
  196. //=========================================================================================
  197. /// <summary>
  198. /// 座標から3次元グリッド座標を割り出す
  199. /// </summary>
  200. /// <param name="pos"></param>
  201. /// <param name="gridSize"></param>
  202. /// <returns></returns>
  203. protected Vector3Int GetGridPos(Vector3 pos)
  204. {
  205. var v = pos / gridSize;
  206. return new Vector3Int((int)Mathf.Floor(v.x), (int)Mathf.Floor(v.y), (int)Mathf.Floor(v.z));
  207. }
  208. //=========================================================================================
  209. /// <summary>
  210. /// すべてのポイントの近接インデックスを算出しバッファに格納する
  211. /// </summary>
  212. void SearchNearPointAll()
  213. {
  214. nearPointDict.Clear();
  215. foreach (var plist in gridMap.Values)
  216. {
  217. foreach (var p in plist)
  218. {
  219. SearchNearPoint(p, searchRadius, null);
  220. }
  221. }
  222. }
  223. /// <summary>
  224. /// 指定インデックス1つの近接インデックスを算出しバッファに格納する
  225. /// </summary>
  226. /// <param name="id"></param>
  227. /// <param name="pos"></param>
  228. void SearchNearPoint(Point p, float radius, Point ignorePoint)
  229. {
  230. Point nearPoint = null;
  231. float nearDist = 100000;
  232. // 現在Pが登録している逆引き最近点辞書があるなら削除する
  233. if (p.nearPoint != null)
  234. {
  235. if (nearPointDict.ContainsKey(p.nearPoint))
  236. {
  237. nearPointDict[p.nearPoint].Remove(p);
  238. }
  239. }
  240. // 範囲内のグリッドを走査してもっとも近いポイントを算出する
  241. var center = p.grid;
  242. int size = (int)(radius / gridSize) + 1;
  243. var s = new Vector3Int(size, size, size);
  244. var sgrid = center - s;
  245. var egrid = center + s;
  246. Vector3Int grid = Vector3Int.zero;
  247. for (int x = sgrid.x; x <= egrid.x; x++)
  248. {
  249. grid.x = x;
  250. for (int y = sgrid.y; y <= egrid.y; y++)
  251. {
  252. grid.y = y;
  253. for (int z = sgrid.z; z <= egrid.z; z++)
  254. {
  255. grid.z = z;
  256. // このグリッドを検索する
  257. if (gridMap.ContainsKey(grid))
  258. {
  259. var plist = gridMap[grid];
  260. foreach (var wp in plist)
  261. {
  262. // 自身は弾く
  263. if (wp == p)
  264. continue;
  265. // 計算除外ポイントは弾く
  266. if (wp == ignorePoint)
  267. continue;
  268. // 距離判定
  269. float dist = Vector3.Distance(wp.pos, p.pos);
  270. if (dist < nearDist && dist <= radius)
  271. {
  272. nearPoint = wp;
  273. nearDist = dist;
  274. }
  275. }
  276. }
  277. }
  278. }
  279. }
  280. // 結果格納
  281. if (nearPoint != null)
  282. {
  283. p.nearPoint = nearPoint;
  284. p.nearDist = nearDist;
  285. // 逆引き辞書に登録
  286. if (nearPointDict.ContainsKey(nearPoint) == false)
  287. nearPointDict.Add(nearPoint, new List<Point>());
  288. nearPointDict[nearPoint].Add(p);
  289. }
  290. else
  291. {
  292. p.nearPoint = null;
  293. p.nearDist = 100000;
  294. }
  295. }
  296. /// <summary>
  297. /// 現時点で最も距離が近いポイントペアを返す
  298. /// </summary>
  299. /// <returns></returns>
  300. Point GetNearPointPair()
  301. {
  302. #if true
  303. float nearDist = 10000;
  304. Point nearPoint = null;
  305. // ※全検索
  306. foreach (var p in pointList)
  307. {
  308. if (p.nearPoint != null && p.nearDist < nearDist)
  309. {
  310. nearDist = p.nearDist;
  311. nearPoint = p;
  312. }
  313. }
  314. return nearPoint;
  315. #else
  316. if (pointList.Count == 0)
  317. return null;
  318. // 距離ソート
  319. pointList.Sort((a, b) => a.nearDist < b.nearDist ? -1 : 1);
  320. return pointList[0];
  321. #endif
  322. }
  323. }
  324. }