[Bf-blender-cvs] [850234c1b10] master: UV: port smart project from Python to C
Campbell Barton
noreply at git.blender.org
Sun Jul 26 13:30:31 CEST 2020
Commit: 850234c1b10a828678f1b91001f2731db807f7e2
Author: Campbell Barton
Date: Sun Jul 26 20:46:24 2020 +1000
Branches: master
https://developer.blender.org/rB850234c1b10a828678f1b91001f2731db807f7e2
UV: port smart project from Python to C
Use C for faster operation on high poly models,
in my tests this gave ~27x speedup.
D8311 by @andreasterrius with edits.
===================================================================
M release/scripts/startup/bl_operators/__init__.py
D release/scripts/startup/bl_operators/uvcalc_smart_project.py
M source/blender/editors/uvedit/uvedit_intern.h
M source/blender/editors/uvedit/uvedit_ops.c
M source/blender/editors/uvedit/uvedit_unwrap_ops.c
===================================================================
diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py
index c39a7afcff9..c927cc184a3 100644
--- a/release/scripts/startup/bl_operators/__init__.py
+++ b/release/scripts/startup/bl_operators/__init__.py
@@ -46,7 +46,6 @@ _modules = [
"userpref",
"uvcalc_follow_active",
"uvcalc_lightmap",
- "uvcalc_smart_project",
"vertexpaint_dirt",
"view3d",
"gpencil_mesh_bake",
diff --git a/release/scripts/startup/bl_operators/uvcalc_smart_project.py b/release/scripts/startup/bl_operators/uvcalc_smart_project.py
deleted file mode 100644
index 1f56cbe6d57..00000000000
--- a/release/scripts/startup/bl_operators/uvcalc_smart_project.py
+++ /dev/null
@@ -1,1053 +0,0 @@
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-# TODO <pep8 compliant>
-
-from mathutils import (
- Matrix,
- Vector,
- geometry,
-)
-import bpy
-from bpy.types import Operator
-
-DEG_TO_RAD = 0.017453292519943295 # pi/180.0
-# see bugs:
-# - T31598 (when too small).
-# - T48086 (when too big).
-SMALL_NUM = 1e-12
-
-
-global USER_FILL_HOLES
-global USER_FILL_HOLES_QUALITY
-USER_FILL_HOLES = None
-USER_FILL_HOLES_QUALITY = None
-
-
-def pointInTri2D(v, v1, v2, v3):
- key = v1.x, v1.y, v2.x, v2.y, v3.x, v3.y
-
- # Commented because its slower to do the bounds check, we should really cache the bounds info for each face.
- '''
- # BOUNDS CHECK
- xmin= 1000000
- ymin= 1000000
-
- xmax= -1000000
- ymax= -1000000
-
- for i in (0,2,4):
- x= key[i]
- y= key[i+1]
-
- if xmax<x: xmax= x
- if ymax<y: ymax= y
- if xmin>x: xmin= x
- if ymin>y: ymin= y
-
- x= v.x
- y= v.y
-
- if x<xmin or x>xmax or y < ymin or y > ymax:
- return False
- # Done with bounds check
- '''
- try:
- mtx = dict_matrix[key]
- if not mtx:
- return False
- except:
- side1 = v2 - v1
- side2 = v3 - v1
-
- nor = side1.cross(side2)
-
- mtx = Matrix((side1, side2, nor))
-
- # Zero area 2d tri, even tho we throw away zero area faces
- # the projection UV can result in a zero area UV.
- if not mtx.determinant():
- dict_matrix[key] = None
- return False
-
- mtx.invert()
-
- dict_matrix[key] = mtx
-
- uvw = (v - v1) @ mtx
- return 0 <= uvw[0] and 0 <= uvw[1] and uvw[0] + uvw[1] <= 1
-
-
-def boundsIsland(faces):
- minx = maxx = faces[0].uv[0][0] # Set initial bounds.
- miny = maxy = faces[0].uv[0][1]
- # print len(faces), minx, maxx, miny , maxy
- for f in faces:
- for uv in f.uv:
- x = uv.x
- y = uv.y
- if x < minx:
- minx = x
- if y < miny:
- miny = y
- if x > maxx:
- maxx = x
- if y > maxy:
- maxy = y
-
- return minx, miny, maxx, maxy
-
-
-"""
-def boundsEdgeLoop(edges):
- minx = maxx = edges[0][0] # Set initial bounds.
- miny = maxy = edges[0][1]
- # print len(faces), minx, maxx, miny , maxy
- for ed in edges:
- for pt in ed:
- x= pt[0]
- y= pt[1]
- if x<minx: x= minx
- if y<miny: y= miny
- if x>maxx: x= maxx
- if y>maxy: y= maxy
-
- return minx, miny, maxx, maxy
-"""
-
-# Turns the islands into a list of unpordered edges (Non internal)
-# Only for UV's
-# only returns outline edges for intersection tests. and unique points.
-
-
-def island2Edge(island):
-
- # Vert index edges
- edges = {}
-
- unique_points = {}
-
- for f in island:
- f_uvkey = list(map(tuple, f.uv))
-
- for vIdx in range(len(f_uvkey)):
- unique_points[f_uvkey[vIdx]] = f.uv[vIdx]
-
- if f.v[vIdx].index > f.v[vIdx - 1].index:
- i1 = vIdx - 1
- i2 = vIdx
- else:
- i1 = vIdx
- i2 = vIdx - 1
-
- try:
- edges[f_uvkey[i1], f_uvkey[i2]] *= 0 # sets any edge with more than 1 user to 0 are not returned.
- except:
- edges[f_uvkey[i1], f_uvkey[i2]] = (f.uv[i1] - f.uv[i2]).length
-
- # If 2 are the same then they will be together, but full [a,b] order is not correct.
-
- # Sort by length
- length_sorted_edges = [(Vector(key[0]), Vector(key[1]), value) for key, value in edges.items() if value != 0]
-
- length_sorted_edges.sort(key=lambda a: -a[2]) # largest first
-
- # Its okay to leave the length in there.
- # for e in length_sorted_edges:
- # e.pop(2)
-
- # return edges and unique points
- return length_sorted_edges, [v.to_3d() for v in unique_points.values()]
-
-
-def pointInIsland(pt, island):
- vec1, vec2, vec3 = Vector(), Vector(), Vector()
- for f in island:
- vec1.x, vec1.y = f.uv[0]
- vec2.x, vec2.y = f.uv[1]
- vec3.x, vec3.y = f.uv[2]
-
- if pointInTri2D(pt, vec1, vec2, vec3):
- return True
-
- if len(f.v) == 4:
- vec1.x, vec1.y = f.uv[0]
- vec2.x, vec2.y = f.uv[2]
- vec3.x, vec3.y = f.uv[3]
- if pointInTri2D(pt, vec1, vec2, vec3):
- return True
- return False
-
-
-# box is (left,bottom, right, top)
-def islandIntersectUvIsland(source, target, SourceOffset):
- # Is 1 point in the box, inside the vertLoops
- edgeLoopsSource = source[6] # Pretend this is offset
- edgeLoopsTarget = target[6]
-
- # Edge intersect test
- for ed in edgeLoopsSource:
- for seg in edgeLoopsTarget:
- i = geometry.intersect_line_line_2d(seg[0],
- seg[1],
- SourceOffset + ed[0],
- SourceOffset + ed[1],
- )
- if i:
- return 1 # LINE INTERSECTION
-
- # 1 test for source being totally inside target
- SourceOffset.resize_3d()
- for pv in source[7]:
- if pointInIsland(pv + SourceOffset, target[0]):
- return 2 # SOURCE INSIDE TARGET
-
- # 2 test for a part of the target being totally inside the source.
- for pv in target[7]:
- if pointInIsland(pv - SourceOffset, source[0]):
- return 3 # PART OF TARGET INSIDE SOURCE.
-
- return 0 # NO INTERSECTION
-
-
-def rotate_uvs(uv_points, angle):
-
- if angle != 0.0:
- mat = Matrix.Rotation(angle, 2)
- for uv in uv_points:
- uv[:] = mat @ uv
-
-
-def optiRotateUvIsland(faces):
- uv_points = [uv for f in faces for uv in f.uv]
- angle = geometry.box_fit_2d(uv_points)
-
- if angle != 0.0:
- rotate_uvs(uv_points, angle)
-
- # orient them vertically (could be an option)
- minx, miny, maxx, maxy = boundsIsland(faces)
- w, h = maxx - minx, maxy - miny
- # use epsilon so we don't randomly rotate (almost) perfect squares.
- if h + 0.00001 < w:
- from math import pi
- angle = pi / 2.0
- rotate_uvs(uv_points, angle)
-
-
-# Takes an island list and tries to find concave, hollow areas to pack smaller islands into.
-def mergeUvIslands(islandList):
- global USER_FILL_HOLES
- global USER_FILL_HOLES_QUALITY
-
- # Pack islands to bottom LHS
- # Sync with island
-
- # islandTotFaceArea = [] # A list of floats, each island area
- # islandArea = [] # a list of tuples ( area, w,h)
-
- decoratedIslandList = []
-
- islandIdx = len(islandList)
- while islandIdx:
- islandIdx -= 1
- minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
- w, h = maxx - minx, maxy - miny
-
- totFaceArea = 0
- offset = Vector((minx, miny))
- for f in islandList[islandIdx]:
- for uv in f.uv:
- uv -= offset
-
- totFaceArea += f.area
-
- islandBoundsArea = w * h
- efficiency = abs(islandBoundsArea - totFaceArea)
-
- # UV Edge list used for intersections as well as unique points.
- edges, uniqueEdgePoints = island2Edge(islandList[islandIdx])
-
- decoratedIslandList.append([
- islandList[islandIdx],
- totFaceArea,
- efficiency,
- islandBoundsArea,
- w,
- h,
- edges,
- uniqueEdgePoints,
- ])
-
- # Sort by island bounding box area, smallest face area first.
- # no.. chance that to most simple edge loop first.
- decoratedIslandListAreaSort = decoratedIslandList[:]
-
- decoratedIslandListAreaSort.sort(key=lambda A: A[3])
-
- # sort by efficiency, Least Efficient first.
- decoratedIslandListEfficSort = decoratedIslandList[:]
- # decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2]))
-
- decoratedIslandListEfficSort.sort(key=lambda A: -A[2])
-
- # ================================================== THESE CAN BE TWEAKED.
- # This is a quality value for the number of tests.
- # from 1 to 4, generic quality value is from 1 to 100
- USER_STEP_QUALITY = ((USER_FILL_HOLES_QUALITY - 1) / 25.0) + 1
-
- # If 100 will test as long as there is enough free space.
- # this is rarely enough, and testing takes a while, so lower quality speeds this up.
-
- # 1 means they have the same quality
- USER_FREE_SPACE_TO_TEST_QUALITY = 1 + (((100 - USER_FILL_HOLES_QUALITY) / 100.0)
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list