mesh_utils.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. # ##### BEGIN GPL LICENSE BLOCK #####
  2. #
  3. # This program is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License
  5. # as published by the Free Software Foundation; either version 2
  6. # of the License, or (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software Foundation,
  15. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. #
  17. # ##### END GPL LICENSE BLOCK #####
  18. # <pep8-80 compliant>
  19. __all__ = (
  20. "mesh_linked_uv_islands",
  21. "mesh_linked_triangles",
  22. "edge_face_count_dict",
  23. "edge_face_count",
  24. "edge_loops_from_edges",
  25. "ngon_tessellate",
  26. "triangle_random_points",
  27. )
  28. def mesh_linked_uv_islands(mesh):
  29. """
  30. Splits the mesh into connected polygons, use this for separating cubes from
  31. other mesh elements within 1 mesh datablock.
  32. :arg mesh: the mesh used to group with.
  33. :type mesh: :class:`bpy.types.Mesh`
  34. :return: lists of lists containing polygon indices
  35. :rtype: list
  36. """
  37. uv_loops = [luv.uv[:] for luv in mesh.uv_layers.active.data]
  38. poly_loops = [poly.loop_indices for poly in mesh.polygons]
  39. luv_hash = {}
  40. luv_hash_get = luv_hash.get
  41. luv_hash_ls = [None] * len(uv_loops)
  42. for pi, poly_indices in enumerate(poly_loops):
  43. for li in poly_indices:
  44. uv = uv_loops[li]
  45. uv_hub = luv_hash_get(uv)
  46. if uv_hub is None:
  47. uv_hub = luv_hash[uv] = [pi]
  48. else:
  49. uv_hub.append(pi)
  50. luv_hash_ls[li] = uv_hub
  51. poly_islands = []
  52. # 0 = none, 1 = added, 2 = searched
  53. poly_tag = [0] * len(poly_loops)
  54. while True:
  55. poly_index = -1
  56. for i in range(len(poly_loops)):
  57. if poly_tag[i] == 0:
  58. poly_index = i
  59. break
  60. if poly_index != -1:
  61. island = [poly_index]
  62. poly_tag[poly_index] = 1
  63. poly_islands.append(island)
  64. else:
  65. break # we're done
  66. added = True
  67. while added:
  68. added = False
  69. for poly_index in island[:]:
  70. if poly_tag[poly_index] == 1:
  71. for li in poly_loops[poly_index]:
  72. for poly_index_shared in luv_hash_ls[li]:
  73. if poly_tag[poly_index_shared] == 0:
  74. added = True
  75. poly_tag[poly_index_shared] = 1
  76. island.append(poly_index_shared)
  77. poly_tag[poly_index] = 2
  78. return poly_islands
  79. def mesh_linked_triangles(mesh):
  80. """
  81. Splits the mesh into connected triangles, use this for separating cubes from
  82. other mesh elements within 1 mesh datablock.
  83. :arg mesh: the mesh used to group with.
  84. :type mesh: :class:`bpy.types.Mesh`
  85. :return: lists of lists containing triangles.
  86. :rtype: list
  87. """
  88. # Build vert face connectivity
  89. vert_tris = [[] for i in range(len(mesh.vertices))]
  90. for t in mesh.loop_triangles:
  91. for v in t.vertices:
  92. vert_tris[v].append(t)
  93. # sort triangles into connectivity groups
  94. tri_groups = [[t] for t in mesh.loop_triangles]
  95. # map old, new tri location
  96. tri_mapping = list(range(len(mesh.loop_triangles)))
  97. # Now clump triangles iteratively
  98. ok = True
  99. while ok:
  100. ok = False
  101. for t in mesh.loop_triangles:
  102. mapped_index = tri_mapping[t.index]
  103. mapped_group = tri_groups[mapped_index]
  104. for v in t.vertices:
  105. for nxt_t in vert_tris[v]:
  106. if nxt_t != t:
  107. nxt_mapped_index = tri_mapping[nxt_t.index]
  108. # We are not a part of the same group
  109. if mapped_index != nxt_mapped_index:
  110. ok = True
  111. # Assign mapping to this group so they
  112. # all map to this group
  113. for grp_t in tri_groups[nxt_mapped_index]:
  114. tri_mapping[grp_t.index] = mapped_index
  115. # Move triangles into this group
  116. mapped_group.extend(tri_groups[nxt_mapped_index])
  117. # remove reference to the list
  118. tri_groups[nxt_mapped_index] = None
  119. # return all tri groups that are not null
  120. # this is all the triangles that are connected in their own lists.
  121. return [tg for tg in tri_groups if tg]
  122. def edge_face_count_dict(mesh):
  123. """
  124. :return: dict of edge keys with their value set to the number of
  125. faces using each edge.
  126. :rtype: dict
  127. """
  128. face_edge_count = {}
  129. loops = mesh.loops
  130. edges = mesh.edges
  131. for poly in mesh.polygons:
  132. for i in poly.loop_indices:
  133. key = edges[loops[i].edge_index].key
  134. try:
  135. face_edge_count[key] += 1
  136. except:
  137. face_edge_count[key] = 1
  138. return face_edge_count
  139. def edge_face_count(mesh):
  140. """
  141. :return: list face users for each item in mesh.edges.
  142. :rtype: list
  143. """
  144. edge_face_count = edge_face_count_dict(mesh)
  145. get = dict.get
  146. return [get(edge_face_count, ed.key, 0) for ed in mesh.edges]
  147. def edge_loops_from_edges(mesh, edges=None):
  148. """
  149. Edge loops defined by edges
  150. Takes me.edges or a list of edges and returns the edge loops
  151. return a list of vertex indices.
  152. [ [1, 6, 7, 2], ...]
  153. closed loops have matching start and end values.
  154. """
  155. line_polys = []
  156. # Get edges not used by a face
  157. if edges is None:
  158. edges = mesh.edges
  159. if not hasattr(edges, "pop"):
  160. edges = edges[:]
  161. while edges:
  162. current_edge = edges.pop()
  163. vert_end, vert_start = current_edge.vertices[:]
  164. line_poly = [vert_start, vert_end]
  165. ok = True
  166. while ok:
  167. ok = False
  168. # for i, ed in enumerate(edges):
  169. i = len(edges)
  170. while i:
  171. i -= 1
  172. ed = edges[i]
  173. v1, v2 = ed.vertices
  174. if v1 == vert_end:
  175. line_poly.append(v2)
  176. vert_end = line_poly[-1]
  177. ok = 1
  178. del edges[i]
  179. # break
  180. elif v2 == vert_end:
  181. line_poly.append(v1)
  182. vert_end = line_poly[-1]
  183. ok = 1
  184. del edges[i]
  185. # break
  186. elif v1 == vert_start:
  187. line_poly.insert(0, v2)
  188. vert_start = line_poly[0]
  189. ok = 1
  190. del edges[i]
  191. # break
  192. elif v2 == vert_start:
  193. line_poly.insert(0, v1)
  194. vert_start = line_poly[0]
  195. ok = 1
  196. del edges[i]
  197. # break
  198. line_polys.append(line_poly)
  199. return line_polys
  200. def ngon_tessellate(from_data, indices, fix_loops=True, debug_print=True):
  201. """
  202. Takes a polyline of indices (fgon) and returns a list of face
  203. index lists. Designed to be used for importers that need indices for an
  204. fgon to create from existing verts.
  205. :arg from_data: either a mesh, or a list/tuple of vectors.
  206. :type from_data: list or :class:`bpy.types.Mesh`
  207. :arg indices: a list of indices to use this list
  208. is the ordered closed polyline
  209. to fill, and can be a subset of the data given.
  210. :type indices: list
  211. :arg fix_loops: If this is enabled polylines
  212. that use loops to make multiple
  213. polylines are delt with correctly.
  214. :type fix_loops: bool
  215. """
  216. from mathutils.geometry import tessellate_polygon
  217. from mathutils import Vector
  218. vector_to_tuple = Vector.to_tuple
  219. if not indices:
  220. return []
  221. def mlen(co):
  222. # manhatten length of a vector, faster then length
  223. return abs(co[0]) + abs(co[1]) + abs(co[2])
  224. def vert_treplet(v, i):
  225. return v, vector_to_tuple(v, 6), i, mlen(v)
  226. def ed_key_mlen(v1, v2):
  227. if v1[3] > v2[3]:
  228. return v2[1], v1[1]
  229. else:
  230. return v1[1], v2[1]
  231. if not fix_loops:
  232. """
  233. Normal single concave loop filling
  234. """
  235. if type(from_data) in {tuple, list}:
  236. verts = [Vector(from_data[i]) for ii, i in enumerate(indices)]
  237. else:
  238. verts = [from_data.vertices[i].co for ii, i in enumerate(indices)]
  239. # same as reversed(range(1, len(verts))):
  240. for i in range(len(verts) - 1, 0, -1):
  241. if verts[i][1] == verts[i - 1][0]:
  242. verts.pop(i - 1)
  243. fill = tessellate_polygon([verts])
  244. else:
  245. """
  246. Separate this loop into multiple loops be finding edges that are
  247. used twice. This is used by lightwave LWO files a lot
  248. """
  249. if type(from_data) in {tuple, list}:
  250. verts = [vert_treplet(Vector(from_data[i]), ii)
  251. for ii, i in enumerate(indices)]
  252. else:
  253. verts = [vert_treplet(from_data.vertices[i].co, ii)
  254. for ii, i in enumerate(indices)]
  255. edges = [(i, i - 1) for i in range(len(verts))]
  256. if edges:
  257. edges[0] = (0, len(verts) - 1)
  258. if not verts:
  259. return []
  260. edges_used = set()
  261. edges_doubles = set()
  262. # We need to check if any edges are used twice location based.
  263. for ed in edges:
  264. edkey = ed_key_mlen(verts[ed[0]], verts[ed[1]])
  265. if edkey in edges_used:
  266. edges_doubles.add(edkey)
  267. else:
  268. edges_used.add(edkey)
  269. # Store a list of unconnected loop segments split by double edges.
  270. # will join later
  271. loop_segments = []
  272. v_prev = verts[0]
  273. context_loop = [v_prev]
  274. loop_segments = [context_loop]
  275. for v in verts:
  276. if v != v_prev:
  277. # Are we crossing an edge we removed?
  278. if ed_key_mlen(v, v_prev) in edges_doubles:
  279. context_loop = [v]
  280. loop_segments.append(context_loop)
  281. else:
  282. if context_loop and context_loop[-1][1] == v[1]:
  283. pass
  284. else:
  285. context_loop.append(v)
  286. v_prev = v
  287. # Now join loop segments
  288. def join_seg(s1, s2):
  289. if s2[-1][1] == s1[0][1]:
  290. s1, s2 = s2, s1
  291. elif s1[-1][1] == s2[0][1]:
  292. pass
  293. else:
  294. return False
  295. # If were stuill here s1 and s2 are 2 segments in the same polyline
  296. s1.pop() # remove the last vert from s1
  297. s1.extend(s2) # add segment 2 to segment 1
  298. if s1[0][1] == s1[-1][1]: # remove endpoints double
  299. s1.pop()
  300. del s2[:] # Empty this segment s2 so we don't use it again.
  301. return True
  302. joining_segments = True
  303. while joining_segments:
  304. joining_segments = False
  305. segcount = len(loop_segments)
  306. for j in range(segcount - 1, -1, -1): # reversed(range(segcount)):
  307. seg_j = loop_segments[j]
  308. if seg_j:
  309. for k in range(j - 1, -1, -1): # reversed(range(j)):
  310. if not seg_j:
  311. break
  312. seg_k = loop_segments[k]
  313. if seg_k and join_seg(seg_j, seg_k):
  314. joining_segments = True
  315. loop_list = loop_segments
  316. for verts in loop_list:
  317. while verts and verts[0][1] == verts[-1][1]:
  318. verts.pop()
  319. loop_list = [verts for verts in loop_list if len(verts) > 2]
  320. # DONE DEALING WITH LOOP FIXING
  321. # vert mapping
  322. vert_map = [None] * len(indices)
  323. ii = 0
  324. for verts in loop_list:
  325. if len(verts) > 2:
  326. for i, vert in enumerate(verts):
  327. vert_map[i + ii] = vert[2]
  328. ii += len(verts)
  329. fill = tessellate_polygon([[v[0] for v in loop] for loop in loop_list])
  330. # draw_loops(loop_list)
  331. #raise Exception("done loop")
  332. # map to original indices
  333. fill = [[vert_map[i] for i in reversed(f)] for f in fill]
  334. if not fill:
  335. if debug_print:
  336. print('Warning Cannot scanfill, fallback on a triangle fan.')
  337. fill = [[0, i - 1, i] for i in range(2, len(indices))]
  338. else:
  339. # Use real scanfill.
  340. # See if its flipped the wrong way.
  341. flip = None
  342. for fi in fill:
  343. if flip is not None:
  344. break
  345. for i, vi in enumerate(fi):
  346. if vi == 0 and fi[i - 1] == 1:
  347. flip = False
  348. break
  349. elif vi == 1 and fi[i - 1] == 0:
  350. flip = True
  351. break
  352. if not flip:
  353. for i, fi in enumerate(fill):
  354. fill[i] = tuple([ii for ii in reversed(fi)])
  355. return fill
  356. def triangle_random_points(num_points, loop_triangles):
  357. """
  358. Generates a list of random points over mesh loop triangles.
  359. :arg num_points: the number of random points to generate on each triangle.
  360. :type int:
  361. :arg loop_triangles: list of the triangles to generate points on.
  362. :type loop_triangles: :class:`bpy.types.MeshLoopTriangle`, sequence
  363. :return: list of random points over all triangles.
  364. :rtype: list
  365. """
  366. from random import random
  367. # For each triangle, generate the required number of random points
  368. sampled_points = [None] * (num_points * len(loop_triangles))
  369. for i, lt in enumerate(loop_triangles):
  370. # Get triangle vertex coordinates
  371. verts = lt.id_data.vertices
  372. ltv = lt.vertices[:]
  373. tv = (verts[ltv[0]].co, verts[ltv[1]].co, verts[ltv[2]].co)
  374. for k in range(num_points):
  375. u1 = random()
  376. u2 = random()
  377. u_tot = u1 + u2
  378. if u_tot > 1:
  379. u1 = 1.0 - u1
  380. u2 = 1.0 - u2
  381. side1 = tv[1] - tv[0]
  382. side2 = tv[2] - tv[0]
  383. p = tv[0] + u1 * side1 + u2 * side2
  384. sampled_points[num_points * i + k] = p
  385. return sampled_points