Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'release/scripts/startup/bl_operators')
-rw-r--r--release/scripts/startup/bl_operators/__init__.py1
-rw-r--r--release/scripts/startup/bl_operators/gpencil_mesh_bake.py5
-rw-r--r--release/scripts/startup/bl_operators/object_quick_effects.py52
-rw-r--r--release/scripts/startup/bl_operators/screen_play_rendered_anim.py6
-rw-r--r--release/scripts/startup/bl_operators/sequencer.py4
-rw-r--r--release/scripts/startup/bl_operators/userpref.py5
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_follow_active.py7
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_lightmap.py2
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_smart_project.py1053
-rw-r--r--release/scripts/startup/bl_operators/view3d.py28
-rw-r--r--release/scripts/startup/bl_operators/wm.py27
11 files changed, 111 insertions, 1079 deletions
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/gpencil_mesh_bake.py b/release/scripts/startup/bl_operators/gpencil_mesh_bake.py
index ae75fa0e4d9..ec7ff970537 100644
--- a/release/scripts/startup/bl_operators/gpencil_mesh_bake.py
+++ b/release/scripts/startup/bl_operators/gpencil_mesh_bake.py
@@ -148,11 +148,6 @@ class GPENCIL_OT_mesh_bake(Operator):
return {'FINISHED'}
def invoke(self, context, _event):
- scene = context.scene
- self.frame_start = scene.frame_start
- self.frame_end = scene.frame_end
- self.frame_target = scene.frame_start
-
wm = context.window_manager
return wm.invoke_props_dialog(self)
diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py
index 311631ac65f..d0344b88be8 100644
--- a/release/scripts/startup/bl_operators/object_quick_effects.py
+++ b/release/scripts/startup/bl_operators/object_quick_effects.py
@@ -561,9 +561,61 @@ class QuickLiquid(Operator):
return {'FINISHED'}
+class QuickParticles(Operator):
+ """Use active object as particle emitter"""
+ bl_idname = "object.quick_particles"
+ bl_label = "Quick Particles"
+
+ @classmethod
+ def poll(cls, context):
+ if not context.preferences.experimental.use_new_particle_system:
+ return False
+ if context.mode != 'OBJECT':
+ return False
+ if context.active_object is None:
+ return False
+ if context.active_object.type != 'MESH':
+ return False
+ return True
+
+ def execute(self, context):
+ pointcloud = bpy.data.pointclouds.new("Particles")
+ pointcloud_object = bpy.data.objects.new("Particles", pointcloud)
+ modifier = pointcloud_object.modifiers.new("Simulation", 'SIMULATION')
+ simulation = bpy.data.simulations.new("Particle Simulation")
+ tree = simulation.node_tree
+
+ default_name = "Particles"
+ particle_simulation_node = tree.nodes.new('SimulationNodeParticleSimulation')
+ particle_simulation_node.name = default_name
+ emitter_node = tree.nodes.new('SimulationNodeParticleMeshEmitter')
+ emitter_node.location.x -= 200
+ emitter_node.location.y += 50
+ emitter_node.inputs["Object"].default_value = context.active_object
+ force_node = tree.nodes.new('SimulationNodeForce')
+ force_node.location.x -= 200
+ force_node.location.y -= 100
+ force_node.inputs["Force"].default_value = (0, 0, -1)
+
+ tree.links.new(particle_simulation_node.inputs["Emitters"], emitter_node.outputs["Emitter"])
+ tree.links.new(particle_simulation_node.inputs["Forces"], force_node.outputs["Force"])
+
+ modifier.simulation = simulation
+ modifier.data_path = default_name
+
+ for obj in context.selected_objects:
+ obj.select_set(False)
+
+ context.collection.objects.link(pointcloud_object)
+ pointcloud_object.select_set(True)
+ context.view_layer.objects.active = pointcloud_object
+ pointcloud_object.show_bounds = True
+ return {'FINISHED'}
+
classes = (
QuickExplode,
QuickFur,
QuickSmoke,
QuickLiquid,
+ QuickParticles,
)
diff --git a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py
index 255852d3b26..6c29c07c62e 100644
--- a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py
+++ b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py
@@ -143,10 +143,10 @@ class PlayRenderedAnim(Operator):
]
cmd.extend(opts)
elif preset == 'FRAMECYCLER':
- opts = [file, f"{scene.frame_start:d}-{scene.frame_end:d}"]
+ opts = [file, "%d-%d" % (scene.frame_start, scene.frame_end)]
cmd.extend(opts)
elif preset == 'RV':
- opts = ["-fps", str(rd.fps), "-play", f"[ {file:s} ]"]
+ opts = ["-fps", str(rd.fps), "-play", "[ %s ]" % file]
cmd.extend(opts)
elif preset == 'MPLAYER':
opts = []
@@ -156,7 +156,7 @@ class PlayRenderedAnim(Operator):
opts += [
("mf://" + file.replace("#", "?")),
"-mf",
- f"fps={fps_final:4f}"
+ "fps=%.4f" % fps_final,
]
opts += ["-loop", "0", "-really-quiet", "-fs"]
diff --git a/release/scripts/startup/bl_operators/sequencer.py b/release/scripts/startup/bl_operators/sequencer.py
index 9ffb23287f8..a332f938afd 100644
--- a/release/scripts/startup/bl_operators/sequencer.py
+++ b/release/scripts/startup/bl_operators/sequencer.py
@@ -164,7 +164,7 @@ class SequencerFadesClear(Operator):
if curve:
fcurves.remove(curve)
setattr(sequence, animated_property, 1.0)
- sequence.invalidate('COMPOSITE')
+ sequence.invalidate_cache('COMPOSITE')
return {'FINISHED'}
@@ -233,7 +233,7 @@ class SequencerFadesAdd(Operator):
self.fade_animation_clear(fade_fcurve, fades)
self.fade_animation_create(fade_fcurve, fades)
faded_sequences.append(sequence)
- sequence.invalidate('COMPOSITE')
+ sequence.invalidate_cache('COMPOSITE')
sequence_string = "sequence" if len(faded_sequences) == 1 else "sequences"
self.report({'INFO'}, "Added fade animation to %d %s." % (len(faded_sequences), sequence_string))
diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py
index 2e14df1920f..e92f493960a 100644
--- a/release/scripts/startup/bl_operators/userpref.py
+++ b/release/scripts/startup/bl_operators/userpref.py
@@ -119,8 +119,11 @@ class PREFERENCES_OT_copy_prev(Operator):
# Find config folder from previous version.
import os
version = bpy.app.version
+ version_new = ((version[0] * 100) + version[1])
version_old = ((version[0] * 100) + version[1]) - 1
- while version_old % 10 > 0:
+ # Ensure we only try to copy files from a point release.
+ # The check below ensures the second numbers match.
+ while (version_new % 100) // 10 == (version_old % 100) // 10:
version_split = version_old // 100, version_old % 100
if os.path.isdir(cls._old_version_path(version_split)):
return version_split
diff --git a/release/scripts/startup/bl_operators/uvcalc_follow_active.py b/release/scripts/startup/bl_operators/uvcalc_follow_active.py
index 6f441238606..4343717f264 100644
--- a/release/scripts/startup/bl_operators/uvcalc_follow_active.py
+++ b/release/scripts/startup/bl_operators/uvcalc_follow_active.py
@@ -160,7 +160,12 @@ def extend(obj, EXTEND_MODE):
l_b_uv = [l[uv_act].uv for l in l_b]
if EXTEND_MODE == 'LENGTH_AVERAGE':
- fac = edge_lengths[l_b[2].edge.index][0] / edge_lengths[l_a[1].edge.index][0]
+ d1 = edge_lengths[l_a[1].edge.index][0]
+ d2 = edge_lengths[l_b[2].edge.index][0]
+ try:
+ fac = d2 / d1
+ except ZeroDivisionError:
+ fac = 1.0
elif EXTEND_MODE == 'LENGTH':
a0, b0, c0 = l_a[3].vert.co, l_a[0].vert.co, l_b[3].vert.co
a1, b1, c1 = l_a[2].vert.co, l_a[1].vert.co, l_b[2].vert.co
diff --git a/release/scripts/startup/bl_operators/uvcalc_lightmap.py b/release/scripts/startup/bl_operators/uvcalc_lightmap.py
index 2befb7c73e2..7af5cd5ee5f 100644
--- a/release/scripts/startup/bl_operators/uvcalc_lightmap.py
+++ b/release/scripts/startup/bl_operators/uvcalc_lightmap.py
@@ -613,7 +613,7 @@ class LightMapPack(Operator):
# Image & UVs...
PREF_PACK_IN_ONE: BoolProperty(
- name="Share Tex Space",
+ name="Share Texture Space",
description=(
"Objects Share texture space, map all objects "
"into 1 uvmap"
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) * 5)
-
- # print 'USER_STEP_QUALITY', USER_STEP_QUALITY
- # print 'USER_FREE_SPACE_TO_TEST_QUALITY', USER_FREE_SPACE_TO_TEST_QUALITY
-
- removedCount = 0
-
- areaIslandIdx = 0
- ctrl = Window.Qual.CTRL
- BREAK = False
- while areaIslandIdx < len(decoratedIslandListAreaSort) and not BREAK:
- sourceIsland = decoratedIslandListAreaSort[areaIslandIdx]
- # Already packed?
- if not sourceIsland[0]:
- areaIslandIdx += 1
- else:
- efficIslandIdx = 0
- while efficIslandIdx < len(decoratedIslandListEfficSort) and not BREAK:
-
- if Window.GetKeyQualifiers() & ctrl:
- BREAK = True
- break
-
- # Now we have 2 islands, if the efficiency of the islands lowers there's an
- # increasing likely hood that we can fit merge into the bigger UV island.
- # this ensures a tight fit.
-
- # Just use figures we have about user/unused area to see if they might fit.
-
- targetIsland = decoratedIslandListEfficSort[efficIslandIdx]
-
- if sourceIsland[0] == targetIsland[0] or\
- not targetIsland[0] or\
- not sourceIsland[0]:
- pass
- else:
-
- #~ ([island, totFaceArea, efficiency, islandArea, w,h])
- # Wasted space on target is greater then UV bounding island area.
-
- #~ if targetIsland[3] > (sourceIsland[2]) and\ #
- # ~ print USER_FREE_SPACE_TO_TEST_QUALITY
- if targetIsland[2] > (sourceIsland[1] * USER_FREE_SPACE_TO_TEST_QUALITY) and\
- targetIsland[4] > sourceIsland[4] and\
- targetIsland[5] > sourceIsland[5]:
-
- # DEBUG # print '%.10f %.10f' % (targetIsland[3], sourceIsland[1])
-
- # These enough spare space lets move the box until it fits
-
- # How many times does the source fit into the target x/y
- blockTestXUnit = targetIsland[4] / sourceIsland[4]
- blockTestYUnit = targetIsland[5] / sourceIsland[5]
-
- boxLeft = 0
-
- # Distance we can move between whilst staying inside the targets bounds.
- testWidth = targetIsland[4] - sourceIsland[4]
- testHeight = targetIsland[5] - sourceIsland[5]
-
- # Increment we move each test. x/y
- xIncrement = (testWidth / (blockTestXUnit * ((USER_STEP_QUALITY / 50) + 0.1)))
- yIncrement = (testHeight / (blockTestYUnit * ((USER_STEP_QUALITY / 50) + 0.1)))
-
- # Make sure were not moving less then a 3rg of our width/height
- if xIncrement < sourceIsland[4] / 3:
- xIncrement = sourceIsland[4]
- if yIncrement < sourceIsland[5] / 3:
- yIncrement = sourceIsland[5]
-
- boxLeft = 0 # Start 1 back so we can jump into the loop.
- boxBottom = 0 # -yIncrement
-
- # ~ testcount= 0
-
- while boxBottom <= testHeight:
- # Should we use this? - not needed for now.
- # ~ if Window.GetKeyQualifiers() & ctrl:
- # ~ BREAK= True
- # ~ break
-
- # testcount+=1
- # print 'Testing intersect'
- Intersect = islandIntersectUvIsland(
- sourceIsland, targetIsland, Vector((boxLeft, boxBottom)))
- # print 'Done', Intersect
- if Intersect == 1: # Line intersect, don't bother with this any more
- pass
-
- if Intersect == 2: # Source inside target
- """
- We have an intersection, if we are inside the target
- then move us 1 whole width across,
- Its possible this is a bad idea since 2 skinny Angular faces
- could join without 1 whole move, but its a lot more optimal to speed this up
- since we have already tested for it.
-
- It gives about 10% speedup with minimal errors.
- """
- # Move the test along its width + SMALL_NUM
- #boxLeft += sourceIsland[4] + SMALL_NUM
- boxLeft += sourceIsland[4]
- elif Intersect == 0: # No intersection?? Place it.
- # Progress
- removedCount += 1
-# XXX Window.DrawProgressBar(0.0, 'Merged: %i islands, Ctrl to finish early.' % removedCount)
-
- # Move faces into new island and offset
- targetIsland[0].extend(sourceIsland[0])
- offset = Vector((boxLeft, boxBottom))
-
- for f in sourceIsland[0]:
- for uv in f.uv:
- uv += offset
-
- del sourceIsland[0][:] # Empty
-
- # Move edge loop into new and offset.
- # targetIsland[6].extend(sourceIsland[6])
- # while sourceIsland[6]:
- targetIsland[6].extend([(
- (e[0] + offset, e[1] + offset, e[2])
- ) for e in sourceIsland[6]])
-
- del sourceIsland[6][:] # Empty
-
- # Sort by edge length, reverse so biggest are first.
-
- try:
- targetIsland[6].sort(key=lambda A: A[2])
- except:
- targetIsland[6].sort(lambda B, A: cmp(A[2], B[2]))
-
- targetIsland[7].extend(sourceIsland[7])
- offset = Vector((boxLeft, boxBottom, 0.0))
- for p in sourceIsland[7]:
- p += offset
-
- del sourceIsland[7][:]
-
- # Decrement the efficiency
- targetIsland[1] += sourceIsland[1] # Increment totFaceArea
- targetIsland[2] -= sourceIsland[1] # Decrement efficiency
- # IF we ever used these again, should set to 0, eg
- sourceIsland[2] = 0 # No area if anyone wants to know
-
- break
-
- # INCREMENT NEXT LOCATION
- if boxLeft > testWidth:
- boxBottom += yIncrement
- boxLeft = 0.0
- else:
- boxLeft += xIncrement
- # print testcount
-
- efficIslandIdx += 1
- areaIslandIdx += 1
-
- # Remove empty islands
- i = len(islandList)
- while i:
- i -= 1
- if not islandList[i]:
- del islandList[i] # Can increment islands removed here.
-
-# Takes groups of faces. assumes face groups are UV groups.
-
-
-def getUvIslands(faceGroups, me):
-
- # Get seams so we don't cross over seams
- edge_seams = {} # should be a set
- for ed in me.edges:
- if ed.use_seam:
- edge_seams[ed.key] = None # dummy var- use sets!
- # Done finding seams
-
- islandList = []
-
-# XXX Window.DrawProgressBar(0.0, 'Splitting %d projection groups into UV islands:' % len(faceGroups))
- # print '\tSplitting %d projection groups into UV islands:' % len(faceGroups),
- # Find grouped faces
-
- faceGroupIdx = len(faceGroups)
-
- while faceGroupIdx:
- faceGroupIdx -= 1
- faces = faceGroups[faceGroupIdx]
-
- if not faces:
- continue
-
- # Build edge dict
- edge_users = {}
-
- for i, f in enumerate(faces):
- for ed_key in f.edge_keys:
- if ed_key in edge_seams: # DELIMIT SEAMS! ;)
- edge_users[ed_key] = [] # so as not to raise an error
- else:
- try:
- edge_users[ed_key].append(i)
- except:
- edge_users[ed_key] = [i]
-
- # Modes
- # 0 - face not yet touched.
- # 1 - added to island list, and need to search
- # 2 - touched and searched - don't touch again.
- face_modes = [0] * len(faces) # initialize zero - untested.
-
- face_modes[0] = 1 # start the search with face 1
-
- newIsland = []
-
- newIsland.append(faces[0])
-
- ok = True
- while ok:
-
- ok = True
- while ok:
- ok = False
- for i in range(len(faces)):
- if face_modes[i] == 1: # search
- for ed_key in faces[i].edge_keys:
- for ii in edge_users[ed_key]:
- if i != ii and face_modes[ii] == 0:
- face_modes[ii] = ok = 1 # mark as searched
- newIsland.append(faces[ii])
-
- # mark as searched, don't look again.
- face_modes[i] = 2
-
- islandList.append(newIsland)
-
- ok = False
- for i in range(len(faces)):
- if face_modes[i] == 0:
- newIsland = []
- newIsland.append(faces[i])
-
- face_modes[i] = ok = 1
- break
- # if not ok will stop looping
-
-# XXX Window.DrawProgressBar(0.1, 'Optimizing Rotation for %i UV Islands' % len(islandList))
-
- for island in islandList:
- optiRotateUvIsland(island)
-
- return islandList
-
-
-def packIslands(islandList):
- if USER_FILL_HOLES:
- # XXX Window.DrawProgressBar(0.1, 'Merging Islands (Ctrl: skip merge)...')
- mergeUvIslands(islandList) # Modify in place
-
- # Now we have UV islands, we need to pack them.
-
- # Make a synchronized list with the islands
- # so we can box pack the islands.
- packBoxes = []
-
- # Keep a list of X/Y offset so we can save time by writing the
- # uv's and packed data in one pass.
- islandOffsetList = []
-
- islandIdx = 0
-
- while islandIdx < len(islandList):
- minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
-
- w, h = maxx - minx, maxy - miny
-
- if USER_ISLAND_MARGIN:
- minx -= USER_ISLAND_MARGIN * w / 2
- miny -= USER_ISLAND_MARGIN * h / 2
- maxx += USER_ISLAND_MARGIN * w / 2
- maxy += USER_ISLAND_MARGIN * h / 2
-
- # recalc width and height
- w, h = maxx - minx, maxy - miny
-
- if w < SMALL_NUM:
- w = SMALL_NUM
- if h < SMALL_NUM:
- h = SMALL_NUM
-
- """Save the offset to be applied later,
- we could apply to the UVs now and align them to the bottom left hand area
- of the UV coords like the box packer imagines they are
- but, its quicker just to remember their offset and
- apply the packing and offset in 1 pass """
- islandOffsetList.append((minx, miny))
-
- # Add to boxList. use the island idx for the BOX id.
- packBoxes.append([0, 0, w, h])
- islandIdx += 1
-
- # Now we have a list of boxes to pack that syncs
- # with the islands.
-
- # print '\tPacking UV Islands...'
-# XXX Window.DrawProgressBar(0.7, "Packing %i UV Islands..." % len(packBoxes) )
-
- # time1 = time.time()
- packWidth, packHeight = geometry.box_pack_2d(packBoxes)
-
- # print 'Box Packing Time:', time.time() - time1
-
- # if len(packedLs) != len(islandList):
- # raise ValueError("Packed boxes differs from original length")
-
- # print '\tWriting Packed Data to faces'
-# XXX Window.DrawProgressBar(0.8, "Writing Packed Data to faces")
-
- # Sort by ID, so there in sync again
- islandIdx = len(islandList)
- # Having these here avoids divide by 0
- if islandIdx:
-
- if USER_STRETCH_ASPECT:
- # Maximize to uv area?? Will write a normalize function.
- xfactor = 1.0 / packWidth
- yfactor = 1.0 / packHeight
- else:
- # Keep proportions.
- xfactor = yfactor = 1.0 / max(packWidth, packHeight)
-
- while islandIdx:
- islandIdx -= 1
- # Write the packed values to the UV's
-
- xoffset = packBoxes[islandIdx][0] - islandOffsetList[islandIdx][0]
- yoffset = packBoxes[islandIdx][1] - islandOffsetList[islandIdx][1]
-
- for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box
- for uv in f.uv:
- uv.x = (uv.x + xoffset) * xfactor
- uv.y = (uv.y + yoffset) * yfactor
-
-
-def VectoQuat(vec):
- vec = vec.normalized()
- return vec.to_track_quat('Z', 'X' if abs(vec.x) > 0.5 else 'Y').inverted()
-
-
-class thickface:
- __slost__ = "v", "uv", "no", "area", "edge_keys"
-
- def __init__(self, face, uv_layer, mesh_verts):
- self.v = [mesh_verts[i] for i in face.vertices]
- self.uv = [uv_layer[i].uv for i in face.loop_indices]
-
- self.no = face.normal.copy()
- self.area = face.area
- self.edge_keys = face.edge_keys
-
-
-def main_consts():
- from math import radians
-
- global ROTMAT_2D_POS_90D
- global ROTMAT_2D_POS_45D
- global RotMatStepRotation
-
- ROTMAT_2D_POS_90D = Matrix.Rotation(radians(90.0), 2)
- ROTMAT_2D_POS_45D = Matrix.Rotation(radians(45.0), 2)
-
- RotMatStepRotation = []
- rot_angle = 22.5 # 45.0/2
- while rot_angle > 0.1:
- RotMatStepRotation.append([
- Matrix.Rotation(radians(+rot_angle), 2),
- Matrix.Rotation(radians(-rot_angle), 2),
- ])
-
- rot_angle = rot_angle / 2.0
-
-
-global ob
-ob = None
-
-
-def main(context,
- island_margin,
- projection_limit,
- user_area_weight,
- use_aspect,
- stretch_to_bounds,
- ):
- global USER_FILL_HOLES
- global USER_FILL_HOLES_QUALITY
- global USER_STRETCH_ASPECT
- global USER_ISLAND_MARGIN
-
- from math import cos
- import time
-
- global dict_matrix
- dict_matrix = {}
-
- # Constants:
- # Takes a list of faces that make up a UV island and rotate
- # until they optimally fit inside a square.
- global ROTMAT_2D_POS_90D
- global ROTMAT_2D_POS_45D
- global RotMatStepRotation
- main_consts()
-
- # Create the variables.
- USER_PROJECTION_LIMIT = projection_limit
- USER_ONLY_SELECTED_FACES = True
- USER_SHARE_SPACE = 1 # Only for hole filling.
- USER_STRETCH_ASPECT = stretch_to_bounds
- USER_ISLAND_MARGIN = island_margin # Only for hole filling.
- USER_FILL_HOLES = 0
- USER_FILL_HOLES_QUALITY = 50 # Only for hole filling.
- USER_VIEW_INIT = 0 # Only for hole filling.
-
- is_editmode = (context.mode == 'EDIT_MESH')
- if is_editmode:
- obList = context.objects_in_mode_unique_data
- else:
- obList = [
- ob for ob in context.selected_editable_objects
- if ob.type == 'MESH' and ob.data.library is None
- ]
-
- if not is_editmode:
- USER_ONLY_SELECTED_FACES = False
-
- if not obList:
- raise Exception("error, no selected mesh objects")
-
- # Convert from being button types
- USER_PROJECTION_LIMIT_CONVERTED = cos(USER_PROJECTION_LIMIT * DEG_TO_RAD)
- USER_PROJECTION_LIMIT_HALF_CONVERTED = cos((USER_PROJECTION_LIMIT / 2) * DEG_TO_RAD)
-
- # Toggle Edit mode
- if is_editmode:
- bpy.ops.object.mode_set(mode='OBJECT')
- # Assume face select mode! an annoying hack to toggle face select mode because Mesh doesn't like faceSelectMode.
-
- if USER_SHARE_SPACE:
- # Sort by data name so we get consistent results
- obList.sort(key=lambda ob: ob.data.name)
- collected_islandList = []
-
- time1 = time.time()
-
- # Tag as False so we don't operate on the same mesh twice.
- for me in bpy.data.meshes:
- me.tag = False
-
- for ob in obList:
- me = ob.data
-
- if me.tag or me.library:
- continue
-
- # Tag as used
- me.tag = True
-
- if not me.uv_layers: # Mesh has no UV Coords, don't bother.
- me.uv_layers.new()
-
- uv_layer = me.uv_layers.active.data
- me_verts = list(me.vertices)
-
- if USER_ONLY_SELECTED_FACES:
- meshFaces = [thickface(f, uv_layer, me_verts) for i, f in enumerate(me.polygons) if f.select]
- else:
- meshFaces = [thickface(f, uv_layer, me_verts) for i, f in enumerate(me.polygons)]
-
- # =======
- # Generate a projection list from face normals, this is meant to be smart :)
-
- # make a list of face props that are in sync with meshFaces
- # Make a Face List that is sorted by area.
- # meshFaces = []
-
- # meshFaces.sort( lambda a, b: cmp(b.area , a.area) ) # Biggest first.
- meshFaces.sort(key=lambda a: -a.area)
-
- # remove all zero area faces
- while meshFaces and meshFaces[-1].area <= SMALL_NUM:
- # Set their UV's to 0,0
- for uv in meshFaces[-1].uv:
- uv.zero()
- meshFaces.pop()
-
- if not meshFaces:
- continue
-
- # Smallest first is slightly more efficient,
- # but if the user cancels early then its better we work on the larger data.
-
- # Generate Projection Vecs
- # 0d is 1.0
- # 180 IS -0.59846
-
- # Initialize projectVecs
- if USER_VIEW_INIT:
- # Generate Projection
-
- # We add to this along the way
- projectVecs = [Vector(Window.GetViewVector()) @ ob.matrix_world.inverted().to_3x3()]
- else:
- projectVecs = []
-
- newProjectVec = meshFaces[0].no
- newProjectMeshFaces = [] # Popping stuffs it up.
-
- # Pretend that the most unique angle is ages away to start the loop off
- mostUniqueAngle = -1.0
-
- # This is popped
- tempMeshFaces = meshFaces[:]
-
- # This while only gathers projection vecs, faces are assigned later on.
- while 1:
- # If there's none there then start with the largest face
-
- # add all the faces that are close.
- for fIdx in range(len(tempMeshFaces) - 1, -1, -1):
- # Use half the angle limit so we don't overweight faces towards this
- # normal and hog all the faces.
- if newProjectVec.dot(tempMeshFaces[fIdx].no) > USER_PROJECTION_LIMIT_HALF_CONVERTED:
- newProjectMeshFaces.append(tempMeshFaces.pop(fIdx))
-
- # Add the average of all these faces normals as a projectionVec
- averageVec = Vector((0.0, 0.0, 0.0))
- if user_area_weight == 0.0:
- for fprop in newProjectMeshFaces:
- averageVec += fprop.no
- elif user_area_weight == 1.0:
- for fprop in newProjectMeshFaces:
- averageVec += fprop.no * fprop.area
- else:
- for fprop in newProjectMeshFaces:
- averageVec += fprop.no * ((fprop.area * user_area_weight) + (1.0 - user_area_weight))
-
- if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0: # Avoid NAN
- projectVecs.append(averageVec.normalized())
-
- # Get the next vec!
- # Pick the face that's most different to all existing angles :)
- mostUniqueAngle = 1.0 # 1.0 is 0d. no difference.
- mostUniqueIndex = 0 # dummy
-
- for fIdx in range(len(tempMeshFaces) - 1, -1, -1):
- angleDifference = -1.0 # 180d difference.
-
- # Get the closest vec angle we are to.
- for p in projectVecs:
- temp_angle_diff = p.dot(tempMeshFaces[fIdx].no)
-
- if angleDifference < temp_angle_diff:
- angleDifference = temp_angle_diff
-
- if angleDifference < mostUniqueAngle:
- # We have a new most different angle
- mostUniqueIndex = fIdx
- mostUniqueAngle = angleDifference
-
- if mostUniqueAngle < USER_PROJECTION_LIMIT_CONVERTED:
- # print 'adding', mostUniqueAngle, USER_PROJECTION_LIMIT, len(newProjectMeshFaces)
- # Now weight the vector to all its faces, will give a more direct projection
- # if the face its self was not representative of the normal from surrounding faces.
-
- newProjectVec = tempMeshFaces[mostUniqueIndex].no
- newProjectMeshFaces = [tempMeshFaces.pop(mostUniqueIndex)]
-
- else:
- if len(projectVecs) >= 1: # Must have at least 2 projections
- break
-
- # If there are only zero area faces then its possible
- # there are no projectionVecs
- if not len(projectVecs):
- Draw.PupMenu('error, no projection vecs where generated, 0 area faces can cause this.')
- return
-
- faceProjectionGroupList = [[] for i in range(len(projectVecs))]
-
- # MAP and Arrange # We know there are 3 or 4 faces here
-
- for fIdx in range(len(meshFaces) - 1, -1, -1):
- fvec = meshFaces[fIdx].no
- i = len(projectVecs)
-
- # Initialize first
- bestAng = fvec.dot(projectVecs[0])
- bestAngIdx = 0
-
- # Cycle through the remaining, first already done
- while i - 1:
- i -= 1
-
- newAng = fvec.dot(projectVecs[i])
- if newAng > bestAng: # Reverse logic for dotvecs
- bestAng = newAng
- bestAngIdx = i
-
- # Store the area for later use.
- faceProjectionGroupList[bestAngIdx].append(meshFaces[fIdx])
-
- # Cull faceProjectionGroupList,
-
- # Now faceProjectionGroupList is full of faces that face match the project Vecs list
- for i in range(len(projectVecs)):
- # Account for projectVecs having no faces.
- if not faceProjectionGroupList[i]:
- continue
-
- # Make a projection matrix from a unit length vector.
- MatQuat = VectoQuat(projectVecs[i])
-
- # Get the faces UV's from the projected vertex.
- for f in faceProjectionGroupList[i]:
- f_uv = f.uv
- for j, v in enumerate(f.v):
- f_uv[j][:] = (MatQuat @ v.co).xy
-
- if USER_SHARE_SPACE:
- # Should we collect and pack later?
- islandList = getUvIslands(faceProjectionGroupList, me)
- collected_islandList.extend(islandList)
-
- else:
- # Should we pack the islands for this 1 object?
- islandList = getUvIslands(faceProjectionGroupList, me)
- packIslands(islandList)
-
- # update the mesh here if we need to.
-
- # We want to pack all in 1 go, so pack now
- if USER_SHARE_SPACE:
- packIslands(collected_islandList)
-
- print("Smart Projection time: %.2f" % (time.time() - time1))
-
- # aspect correction is only done in edit mode - and only smart unwrap supports currently
- if is_editmode:
- bpy.ops.object.mode_set(mode='EDIT')
-
- if use_aspect:
- import bmesh
- aspect = context.scene.uvedit_aspect(context.active_object)
- if aspect[0] > aspect[1]:
- aspect[0] = aspect[1] / aspect[0]
- aspect[1] = 1.0
- else:
- aspect[1] = aspect[0] / aspect[1]
- aspect[0] = 1.0
-
- bm = bmesh.from_edit_mesh(me)
-
- uv_act = bm.loops.layers.uv.active
-
- faces = [f for f in bm.faces if f.select]
-
- for f in faces:
- for l in f.loops:
- l[uv_act].uv[0] *= aspect[0]
- l[uv_act].uv[1] *= aspect[1]
-
- dict_matrix.clear()
-
-
-from bpy.props import FloatProperty, BoolProperty
-
-
-class SmartProject(Operator):
- """This script projection unwraps the selected faces of a mesh """ \
- """(it operates on all selected mesh objects, and can be used """ \
- """to unwrap selected faces, or all faces)"""
- bl_idname = "uv.smart_project"
- bl_label = "Smart UV Project"
- bl_options = {'REGISTER', 'UNDO'}
-
- angle_limit: FloatProperty(
- name="Angle Limit",
- description="Lower for more projection groups, higher for less distortion",
- min=1.0, max=89.0,
- default=66.0,
- )
- island_margin: FloatProperty(
- name="Island Margin",
- description="Margin to reduce bleed from adjacent islands",
- min=0.0, max=1.0,
- default=0.0,
- )
- user_area_weight: FloatProperty(
- name="Area Weight",
- description="Weight projections vector by faces with larger areas",
- min=0.0, max=1.0,
- default=0.0,
- )
- use_aspect: BoolProperty(
- name="Correct Aspect",
- description="Map UVs taking image aspect ratio into account",
- default=True,
- )
- stretch_to_bounds: BoolProperty(
- name="Stretch to UV Bounds",
- description="Stretch the final output to texture bounds",
- default=True,
- )
-
- @classmethod
- def poll(cls, context):
- return context.active_object is not None
-
- def execute(self, context):
- main(context,
- self.island_margin,
- self.angle_limit,
- self.user_area_weight,
- self.use_aspect,
- self.stretch_to_bounds,
- )
- return {'FINISHED'}
-
- def invoke(self, context, _event):
- wm = context.window_manager
- return wm.invoke_props_dialog(self)
-
-
-classes = (
- SmartProject,
-)
diff --git a/release/scripts/startup/bl_operators/view3d.py b/release/scripts/startup/bl_operators/view3d.py
index a8e04eb2f24..02bfebbdc0c 100644
--- a/release/scripts/startup/bl_operators/view3d.py
+++ b/release/scripts/startup/bl_operators/view3d.py
@@ -159,6 +159,33 @@ class VIEW3D_OT_edit_mesh_extrude_shrink_fatten(Operator):
return self.execute(context)
+class VIEW3D_OT_edit_mesh_extrude_manifold_normal(Operator):
+ """Extrude manifold region along normals"""
+ bl_label = "Extrude Manifold Along Normals"
+ bl_idname = "view3d.edit_mesh_extrude_manifold_normal"
+
+ @classmethod
+ def poll(cls, context):
+ obj = context.active_object
+ return (obj is not None and obj.mode == 'EDIT')
+
+ def execute(self, context):
+ bpy.ops.mesh.extrude_manifold(
+ 'INVOKE_REGION_WIN',
+ MESH_OT_extrude_region={
+ "use_dissolve_ortho_edges": True,
+ },
+ TRANSFORM_OT_translate={
+ "orient_type": 'NORMAL',
+ "constraint_axis": (False, False, True),
+ },
+ )
+ return {'FINISHED'}
+
+ def invoke(self, context, _event):
+ return self.execute(context)
+
+
class VIEW3D_OT_transform_gizmo_set(Operator):
"""Set the current transform gizmo"""
bl_label = "Transform Gizmo Set"
@@ -208,5 +235,6 @@ classes = (
VIEW3D_OT_edit_mesh_extrude_individual_move,
VIEW3D_OT_edit_mesh_extrude_move,
VIEW3D_OT_edit_mesh_extrude_shrink_fatten,
+ VIEW3D_OT_edit_mesh_extrude_manifold_normal,
VIEW3D_OT_transform_gizmo_set,
)
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index a543ea6685c..570b4663f1d 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -1450,6 +1450,10 @@ class WM_OT_properties_edit(Operator):
)
layout = self.layout
+
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
layout.prop(self, "property")
layout.prop(self, "value")
@@ -1460,19 +1464,18 @@ class WM_OT_properties_edit(Operator):
row.enabled = proptype in {int, float, str}
row.prop(self, "default")
- row = layout.row(align=True)
- row.prop(self, "min")
- row.prop(self, "max")
+ col = layout.column(align=True)
+ col.prop(self, "min")
+ col.prop(self, "max")
- row = layout.row()
- row.prop(self, "use_soft_limits")
- if bpy.app.use_override_library:
- row.prop(self, "is_overridable_library")
-
- row = layout.row(align=True)
- row.enabled = self.use_soft_limits
- row.prop(self, "soft_min", text="Soft Min")
- row.prop(self, "soft_max", text="Soft Max")
+ col = layout.column()
+ col.prop(self, "is_overridable_library")
+ col.prop(self, "use_soft_limits")
+
+ col = layout.column(align=True)
+ col.enabled = self.use_soft_limits
+ col.prop(self, "soft_min", text="Soft Min")
+ col.prop(self, "soft_max", text="Max")
layout.prop(self, "description")
if is_array and proptype == float: