diff options
38 files changed, 1548 insertions, 396 deletions
diff --git a/add_mesh_BoltFactory/Boltfactory.py b/add_mesh_BoltFactory/Boltfactory.py index b3d3b108..acfe5886 100644 --- a/add_mesh_BoltFactory/Boltfactory.py +++ b/add_mesh_BoltFactory/Boltfactory.py @@ -60,6 +60,7 @@ class add_mesh_bolt(Operator, AddObjectHelper): ) # Head Types Model_Type_List = [('bf_Head_Hex', 'HEX', 'Hex Head'), + ('bf_Head_12Pnt', '12 POINT', '12 Point Head'), ('bf_Head_Cap', 'CAP', 'Cap Head'), ('bf_Head_Dome', 'DOME', 'Dome Head'), ('bf_Head_Pan', 'PAN', 'Pan Head'), @@ -73,6 +74,7 @@ class add_mesh_bolt(Operator, AddObjectHelper): # Bit Types Bit_Type_List = [('bf_Bit_None', 'NONE', 'No Bit Type'), ('bf_Bit_Allen', 'ALLEN', 'Allen Bit Type'), + ('bf_Bit_Torx', 'TORX', 'Torx Bit Type'), ('bf_Bit_Philips', 'PHILLIPS', 'Phillips Bit Type')] bf_Bit_Type: EnumProperty( attr='bf_Bit_Type', @@ -82,7 +84,8 @@ class add_mesh_bolt(Operator, AddObjectHelper): ) # Nut Types Nut_Type_List = [('bf_Nut_Hex', 'HEX', 'Hex Nut'), - ('bf_Nut_Lock', 'LOCK', 'Lock Nut')] + ('bf_Nut_Lock', 'LOCK', 'Lock Nut'), + ('bf_Nut_12Pnt', '12 POINT', '12 Point Nut')] bf_Nut_Type: EnumProperty( attr='bf_Nut_Type', name='Nut Type', @@ -129,6 +132,30 @@ class add_mesh_bolt(Operator, AddObjectHelper): description='Flat Distance of the Allen Bit', unit='LENGTH', ) + # Torx Size Types + Torx_Size_Type_List = [('bf_Torx_T10', 'T10', 'T10'), + ('bf_Torx_T20', 'T20', 'T20'), + ('bf_Torx_T25', 'T25', 'T25'), + ('bf_Torx_T30', 'T30', 'T30'), + ('bf_Torx_T40', 'T40', 'T40'), + ('bf_Torx_T50', 'T50', 'T50'), + ('bf_Torx_T55', 'T55', 'T55'), + ] + + bf_Torx_Size_Type: EnumProperty( + attr='bf_Torx_Size_Type', + name='Torx Size', + description='Size of the Torx Bit', + items=Torx_Size_Type_List, default='bf_Torx_T20' + ) + bf_Torx_Bit_Depth: FloatProperty( + attr='bf_Torx_Bit_Depth', + name='Bit Depth', default=1.5, + min=0, soft_min=0, + max=MAX_INPUT_NUMBER, + description='Depth of the Torx Bit', + unit='LENGTH', + ) bf_Hex_Head_Height: FloatProperty( attr='bf_Hex_Head_Height', name='Head Height', default=2, @@ -144,6 +171,29 @@ class add_mesh_bolt(Operator, AddObjectHelper): description='Flat Distance of the Hex Head', unit='LENGTH', ) + bf_12_Point_Head_Height: FloatProperty( + attr='bf_12_Point_Head_Height', + name='Head Height', default=3.0, + min=0, soft_min=0, max=MAX_INPUT_NUMBER, + description='Height of the 12 Point Head', + unit='LENGTH', + ) + bf_12_Point_Head_Flat_Distance: FloatProperty( + attr='bf_12_Point_Head_Flat_Distance', + name='Flat Dist', default=3.0, + min=0.001, soft_min=0, #limit to 0.001 to avoid calculation error + max=MAX_INPUT_NUMBER, + description='Flat Distance of the 12 Point Head', + unit='LENGTH', + ) + bf_12_Point_Head_Flange_Dia: FloatProperty( + attr='bf_12_Point_Head_Flange_Dia', + name='12 Point Head Flange Dia', default=5.5, + min=0, soft_min=0, + max=MAX_INPUT_NUMBER, + description='Flange diameter of the 12 point Head', + unit='LENGTH', + ) bf_CounterSink_Head_Dia: FloatProperty( attr='bf_CounterSink_Head_Dia', name='Head Dia', default=6.300000190734863, @@ -265,7 +315,31 @@ class add_mesh_bolt(Operator, AddObjectHelper): description='Flat distance of the Hex Nut', unit='LENGTH', ) - + bf_12_Point_Nut_Height: FloatProperty( + attr='bf_12_Point_Nut_Height', + name='12 Point Nut Height', default=2.4000000953674316, + min=0, soft_min=0, + max=MAX_INPUT_NUMBER, + description='Height of the 12 Point Nut', + unit='LENGTH', + ) + + bf_12_Point_Nut_Flat_Distance: FloatProperty( + attr='bf_12_Point_Nut_Flat_Distance', + name='12 Point Nut Flat Dist', default=3.0, + min=0.001, soft_min=0, #limit to 0.001 to avoid calculation error + max=MAX_INPUT_NUMBER, + description='Flat distance of the 12 point Nut', + unit='LENGTH', + ) + bf_12_Point_Nut_Flange_Dia: FloatProperty( + attr='bf_12_Point_Nut_Flange_Dia', + name='12 Point Nut Flange Dia', default=5.5, + min=0, soft_min=0, + max=MAX_INPUT_NUMBER, + description='Flange diameter of the 12 point Nut', + unit='LENGTH', + ) def draw(self, context): layout = self.layout col = layout.column() @@ -282,6 +356,9 @@ class add_mesh_bolt(Operator, AddObjectHelper): elif self.bf_Bit_Type == 'bf_Bit_Allen': col.prop(self, 'bf_Allen_Bit_Depth') col.prop(self, 'bf_Allen_Bit_Flat_Distance') + elif self.bf_Bit_Type == 'bf_Bit_Torx': + col.prop(self, 'bf_Torx_Bit_Depth') + col.prop(self, 'bf_Torx_Size_Type') elif self.bf_Bit_Type == 'bf_Bit_Philips': col.prop(self, 'bf_Phillips_Bit_Depth') col.prop(self, 'bf_Philips_Bit_Dia') @@ -292,6 +369,10 @@ class add_mesh_bolt(Operator, AddObjectHelper): if self.bf_Head_Type == 'bf_Head_Hex': col.prop(self, 'bf_Hex_Head_Height') col.prop(self, 'bf_Hex_Head_Flat_Distance') + elif self.bf_Head_Type == 'bf_Head_12Pnt': + col.prop(self, 'bf_12_Point_Head_Height') + col.prop(self, 'bf_12_Point_Head_Flat_Distance') + col.prop(self, 'bf_12_Point_Head_Flange_Dia') elif self.bf_Head_Type == 'bf_Head_Cap': col.prop(self, 'bf_Cap_Head_Height') col.prop(self, 'bf_Cap_Head_Dia') @@ -311,8 +392,16 @@ class add_mesh_bolt(Operator, AddObjectHelper): # Nut if self.bf_Model_Type == 'bf_Model_Nut': col.prop(self, 'bf_Nut_Type') - col.prop(self, 'bf_Hex_Nut_Height') - col.prop(self, 'bf_Hex_Nut_Flat_Distance') + if self.bf_Nut_Type == "bf_Nut_12Pnt": + col.prop(self, 'bf_12_Point_Nut_Height') + col.prop(self, 'bf_12_Point_Nut_Flat_Distance') + col.prop(self, 'bf_12_Point_Nut_Flange_Dia') + else: + col.prop(self, 'bf_Hex_Nut_Height') + col.prop(self, 'bf_Hex_Nut_Flat_Distance') + + + # Thread col.label(text='Thread') if self.bf_Model_Type == 'bf_Model_Bolt': @@ -434,6 +523,8 @@ def BoltParameters(): "bf_Phillips_Bit_Depth", "bf_Allen_Bit_Depth", "bf_Allen_Bit_Flat_Distance", + "bf_Torx_Bit_Depth", + "bf_Torx_Size_Type", "bf_Hex_Head_Height", "bf_Hex_Head_Flat_Distance", "bf_CounterSink_Head_Dia", diff --git a/add_mesh_BoltFactory/createMesh.py b/add_mesh_BoltFactory/createMesh.py index ccdd4372..96284012 100644 --- a/add_mesh_BoltFactory/createMesh.py +++ b/add_mesh_BoltFactory/createMesh.py @@ -20,10 +20,11 @@ import bpy from mathutils import ( Matrix, Vector, + geometry, ) from math import ( sin, cos, - tan, radians, + tan, radians,atan,degrees ) from random import triangular from bpy_extras.object_utils import AddObjectHelper, object_data_add @@ -66,7 +67,7 @@ def unpack_face_list(list_of_tuples): """ Remove Doubles takes a list on Verts and a list of Faces and removes the doubles, much like Blender does in edit mode. -It doesn’t have the range function but it will round the corrdinates +It doesn't have the range function but it will round the corrdinates and remove verts that are very close together. The function is useful because you can perform a "Remove Doubles" with out having to enter Edit Mode. Having to enter edit mode has the @@ -285,6 +286,29 @@ def Fill_Ring_Face(OFFSET, NUM, FACE_DOWN=0): Face[2] = TempFace[2] return Ret +# Returns a list of faces that makes up a fill pattern around the last vert +def Fill_Fan_Face(OFFSET, NUM, FACE_DOWN=0): + Ret = [] + Face = [NUM-1,0,1] + TempFace = [0, 0, 0] + A = 0 + #B = 1 unsed + C = 2 + if NUM < 3: + return None + for _i in range(NUM - 2): + TempFace[0] = Face[A] + TempFace[1] = Face[C] + TempFace[2] = Face[C]+1 + if FACE_DOWN: + Ret.append([OFFSET + Face[2], OFFSET + Face[1], OFFSET + Face[0]]) + else: + Ret.append([OFFSET + Face[2], OFFSET + Face[1], OFFSET + Face[0]]) + + Face[0] = TempFace[0] + Face[1] = TempFace[1] + Face[2] = TempFace[2] + return Ret # #################################################################### # Create Allen Bit @@ -378,7 +402,175 @@ def Create_Allen_Bit(FLAT_DISTANCE, HEIGHT): faces.extend(M_Faces) return verts, faces, OUTTER_RADIUS * 2.0 +# #################################################################### +# Create Torx Bit +# #################################################################### + +def Torx_Bit_Size_To_Point_Distance(Bit_Size): + if Bit_Size == 'bf_Torx_T10': + return 2.83 + elif Bit_Size == 'bf_Torx_T20': + return 3.94 + elif Bit_Size == 'bf_Torx_T25': + return 4.52 + elif Bit_Size == 'bf_Torx_T30': + return 5.61 + elif Bit_Size == 'bf_Torx_T40': + return 6.75 + elif Bit_Size == 'bf_Torx_T50': + return 8.94 + elif Bit_Size == 'bf_Torx_T55': + return 8.94 + else: + return 2.83 #default to M3 + +def Torx_Fill(OFFSET, FLIP=0): + faces = [] + Lookup = [[0,10,11], + [0,11, 12], + [0,12,1], + + [1, 12, 13], + [1, 13, 14], + [1, 14, 15], + [1, 15, 2], + + [2, 15, 16], + [2, 16, 17], + [2, 17, 18], + [2, 18, 19], + [2, 19, 3], + + [3, 19, 20], + [3, 20, 21], + [3, 21, 22], + [3, 22, 23], + [3, 23, 24], + [3, 24, 25], + [3, 25, 4], + + + [4, 25, 26], + [4, 26, 27], + [4, 27, 28], + [4, 28, 29], + [4, 29, 30], + [4, 30, 31], + [4, 31, 5], + + [5, 31, 32], + [5, 32, 33], + [5, 33, 34], + [5, 34, 35], + [5, 35, 36], + [5, 36, 6], + + [6, 36, 37], + [6, 37, 38], + [6, 38, 39], + [6, 39, 7], + + [7, 39, 40], + [7, 40, 41], + [7, 41, 42], + [7, 42, 43], + [7, 43, 8], + + [8, 43, 44], + [8, 44, 45], + [8, 45, 46], + [8, 46, 47], + [8, 47, 48], + [8, 48, 49], + [8, 49, 50], + [8, 50, 51], + [8, 51, 52], + [8, 52, 9], + ] + for i in Lookup: + if FLIP: + faces.append([OFFSET + i[2], OFFSET + i[1], OFFSET + i[0]]) + else: + faces.append([OFFSET + i[0], OFFSET + i[1], OFFSET + i[2]]) + + return faces + + + +def Create_Torx_Bit(Point_Distance, HEIGHT): + verts = [] + faces = [] + + POINT_RADIUS = Point_Distance * 0.5 + OUTTER_RADIUS = POINT_RADIUS * 1.05 + + POINT_1_Y = POINT_RADIUS * 0.816592592592593 + POINT_2_X = POINT_RADIUS * 0.511111111111111 + POINT_2_Y = POINT_RADIUS * 0.885274074074074 + POINT_3_X = POINT_RADIUS * 0.7072 + POINT_3_Y = POINT_RADIUS * 0.408296296296296 + POINT_4_X = POINT_RADIUS * 1.02222222222222 + SMALL_RADIUS = POINT_RADIUS * 0.183407407407407 + BIG_RADIUS = POINT_RADIUS * 0.333333333333333 +# Values for T40 # POINT_1_Y = 2.756 +# POINT_2_X = 1.725 +# POINT_2_Y = 2.9878 +# POINT_3_X = 2.3868 +# POINT_3_Y = 1.378 +# POINT_4_X = 3.45 +# +# SMALL_RADIUS = 0.619 +# BIG_RADIUS = 1.125 + + def Do_Curve(Curve_Height): + for i in range(0, 90, 10): + x = sin(radians(i)) * SMALL_RADIUS + y = cos(radians(i)) * SMALL_RADIUS + verts.append([x, POINT_1_Y + y, Curve_Height]) + + for i in range(260, 150, -10): + x = sin(radians(i)) * BIG_RADIUS + y = cos(radians(i)) * BIG_RADIUS + verts.append([POINT_2_X + x, POINT_2_Y + y, Curve_Height]) + + for i in range(340, 150 + 360, 10): + x = sin(radians(i%360)) * SMALL_RADIUS + y = cos(radians(i%360)) * SMALL_RADIUS + verts.append([POINT_3_X + x, POINT_3_Y + y, Curve_Height]) + + for i in range(320, 260, -10): + x = sin(radians(i)) * BIG_RADIUS + y = cos(radians(i)) * BIG_RADIUS + verts.append([POINT_4_X + x, y, Curve_Height]) + + FaceStart_Outside = len(verts) + + for i in range(0, 100, 10): + x = sin(radians(i)) * OUTTER_RADIUS + y = cos(radians(i)) * OUTTER_RADIUS + verts.append([x, y, 0]) + FaceStart_Top_Curve= len(verts) + Do_Curve(0) + faces.extend(Torx_Fill(FaceStart_Outside, 0)) + + FaceStart_Bottom_Curve= len(verts) + Do_Curve(0 - HEIGHT) + + faces.extend(Build_Face_List_Quads(FaceStart_Top_Curve,42 ,1 , True)) + + verts.append([0,0,0 - HEIGHT]) # add center point for fill Fan + faces.extend(Fill_Fan_Face(FaceStart_Bottom_Curve, 44)) + + M_Verts, M_Faces = Mirror_Verts_Faces(verts, faces, 'x') + verts.extend(M_Verts) + faces.extend(M_Faces) + + M_Verts, M_Faces = Mirror_Verts_Faces(verts, faces, 'y') + verts.extend(M_Verts) + faces.extend(M_Faces) + + return verts, faces, OUTTER_RADIUS * 2.0 # #################################################################### # Create Phillips Bit @@ -881,6 +1073,280 @@ def Create_Hex_Head(FLAT, HOLE_DIA, SHANK_DIA, HEIGHT): return Spin_Verts, Spin_Faces, 0 - (-HEIGHT) + +def Create_12_Point(FLAT, HOLE_DIA, SHANK_DIA, HEIGHT,FLANGE_DIA): + FLANGE_HEIGHT = (1.89/8.0)*HEIGHT + FLAT_HEIGHT = (4.18/8.0)*HEIGHT +# FLANGE_DIA = (13.27/8.0)*FLAT + + FLANGE_RADIUS = FLANGE_DIA * 0.5 + FLANGE_TAPPER_HEIGHT = HEIGHT - FLANGE_HEIGHT - FLAT_HEIGHT + +# HOLE_DIA = 0.0 + + verts = [] + faces = [] + HOLE_RADIUS = HOLE_DIA / 2 + Half_Flat = FLAT / 2 + TopBevelRadius = Half_Flat - (Half_Flat * (0.05 / 8)) +# Undercut_Height = (Half_Flat * (0.05 / 8)) +# Shank_Bevel = (Half_Flat * (0.05 / 8)) +# Flat_Height = HEIGHT - Undercut_Height - Shank_Bevel + # Undercut_Height = 5 + SHANK_RADIUS = SHANK_DIA / 2 + Row = 0 + + verts.append([0.0, 0.0, 0.0]) + +# print("HOLE_RADIUS" + str(HOLE_RADIUS)) +# print("TopBevelRadius" + str(TopBevelRadius)) + + FaceStart = len(verts) + + # inner hole + x = sin(radians(0)) * HOLE_RADIUS + y = cos(radians(0)) * HOLE_RADIUS + verts.append([x, y, 0.0]) + + x = sin(radians(5)) * HOLE_RADIUS + y = cos(radians(5)) * HOLE_RADIUS + verts.append([x, y, 0.0]) + + x = sin(radians(10)) * HOLE_RADIUS + y = cos(radians(10)) * HOLE_RADIUS + verts.append([x, y, 0.0]) + + x = sin(radians(15)) * HOLE_RADIUS + y = cos(radians(15)) * HOLE_RADIUS + verts.append([x, y, 0.0]) + + x = sin(radians(20)) * HOLE_RADIUS + y = cos(radians(20)) * HOLE_RADIUS + verts.append([x, y, 0.0]) + + x = sin(radians(25)) * HOLE_RADIUS + y = cos(radians(25)) * HOLE_RADIUS + verts.append([x, y, 0.0]) + + x = sin(radians(30)) * HOLE_RADIUS + y = cos(radians(30)) * HOLE_RADIUS + verts.append([x, y, 0.0]) + + Row += 1 + + + + # bevel + x = sin(radians(0)) * TopBevelRadius + y = cos(radians(0)) * TopBevelRadius + vec1 = Vector([x, y, 0.0]) + verts.append([x, y, 0.0]) + + x = sin(radians(5)) * TopBevelRadius + y = cos(radians(5)) * TopBevelRadius + vec2 = Vector([x, y, 0.0]) + verts.append([x, y, 0.0]) + + x = sin(radians(10)) * TopBevelRadius + y = cos(radians(10)) * TopBevelRadius + vec3 = Vector([x, y, 0.0]) + verts.append([x, y, 0.0]) + + x = sin(radians(15)) * TopBevelRadius + y = cos(radians(15)) * TopBevelRadius + vec4 = Vector([x, y, 0.0]) + verts.append([x, y, 0.0]) + + x = sin(radians(20)) * TopBevelRadius + y = cos(radians(20)) * TopBevelRadius + vec5 = Vector([x, y, 0.0]) + verts.append([x, y, 0.0]) + + x = sin(radians(25)) * TopBevelRadius + y = cos(radians(25)) * TopBevelRadius + vec6 = Vector([x, y, 0.0]) + verts.append([x, y, 0.0]) + + x = sin(radians(30)) * TopBevelRadius + y = cos(radians(30)) * TopBevelRadius + vec7 = Vector([x, y, 0.0]) + verts.append([x, y, 0.0]) + + Row += 1 + + + #45Deg bevel on the top + + #First we work out how far up the Y axis the vert is + v_origin = Vector([0.0,0.0,0.0]) # center of the model + v_15Deg_Point = Vector([tan(radians(15)) * Half_Flat,Half_Flat,0.0]) #Is a know point to work back from + + x = tan(radians(0)) * Half_Flat + Point_Distance =(tan(radians(30)) * v_15Deg_Point.x)+Half_Flat + dvec = vec1 - Vector([x, Point_Distance, 0.0]) + verts.append([x, Point_Distance, -dvec.length]) + v_0_Deg_Top_Point = Vector([x, Point_Distance, -dvec.length]) + + v_0_Deg_Point = Vector([x, Point_Distance,0.0]) + + v_5Deg_Line = Vector([tan(radians(5)) * Half_Flat, Half_Flat, 0.0]) + v_5Deg_Line.length *= 2 # extende out the line on a 5 deg angle + + #We cross 2 lines. One from the origin to the 0 Deg point + #and the second is from the orign extended out past the first line + # This gives the cross point of the + v_Cross = geometry.intersect_line_line_2d(v_0_Deg_Point,v_15Deg_Point,v_origin,v_5Deg_Line) + dvec = vec2 - Vector([v_Cross.x,v_Cross.y,0.0]) + verts.append([v_Cross.x,v_Cross.y,-dvec.length]) + v_5_Deg_Top_Point = Vector([v_Cross.x,v_Cross.y,-dvec.length]) + + v_10Deg_Line = Vector([tan(radians(10)) * Half_Flat, Half_Flat, 0.0]) + v_10Deg_Line.length *= 2 # extende out the line + + v_Cross = geometry.intersect_line_line_2d(v_0_Deg_Point,v_15Deg_Point,v_origin,v_10Deg_Line) + dvec = vec3 - Vector([v_Cross.x,v_Cross.y,0.0]) + verts.append([v_Cross.x,v_Cross.y,-dvec.length]) + v_10_Deg_Top_Point = Vector([v_Cross.x,v_Cross.y,-dvec.length]) + + #The remain points are stright forward because y is all the same y height (Half_Flat) + x = tan(radians(15)) * Half_Flat + dvec = vec4 - Vector([x, Half_Flat, 0.0]) + Lowest_Point = -dvec.length + verts.append([x, Half_Flat, -dvec.length]) + v_15_Deg_Top_Point = Vector([x, Half_Flat, -dvec.length]) + + x = tan(radians(20)) * Half_Flat + dvec = vec5 - Vector([x, Half_Flat, 0.0]) + Lowest_Point = -dvec.length + verts.append([x, Half_Flat, -dvec.length]) + v_20_Deg_Top_Point = Vector([x, Half_Flat, -dvec.length]) + + x = tan(radians(25)) * Half_Flat + dvec = vec6 - Vector([x, Half_Flat, 0.0]) + Lowest_Point = -dvec.length + verts.append([x, Half_Flat, -dvec.length]) + v_25_Deg_Top_Point = Vector([x, Half_Flat, -dvec.length]) + + x = tan(radians(30)) * Half_Flat + dvec = vec7 - Vector([x, Half_Flat, 0.0]) + Lowest_Point = -dvec.length + verts.append([x, Half_Flat, -dvec.length]) + v_30_Deg_Top_Point = Vector([x, Half_Flat, -dvec.length]) + Row += 1 + + + #Down Bits +# print ("Point_Distance") +# print (Point_Distance) + + + + Flange_Adjacent = FLANGE_RADIUS - Point_Distance + if (Flange_Adjacent == 0.0): + Flange_Adjacent = 0.000001 + Flange_Opposite = FLANGE_TAPPER_HEIGHT + +# print ("Flange_Opposite") +# print (Flange_Opposite) +# print ("Flange_Adjacent") +# print (Flange_Adjacent) + + FLANGE_ANGLE_RAD = atan(Flange_Opposite/Flange_Adjacent ) +# FLANGE_ANGLE_RAD = radians(45) +# print("FLANGE_ANGLE_RAD") +# print (degrees (FLANGE_ANGLE_RAD)) + + + v_Extended_Flange_Edge = Vector([0.0,0.0,-HEIGHT + FLANGE_HEIGHT + (tan(FLANGE_ANGLE_RAD)* FLANGE_RADIUS) ]) +# print("v_Extended_Flange_Edge") +# print (v_Extended_Flange_Edge) + + #0deg + v_Flange_Edge = Vector([sin(radians(0)) * FLANGE_RADIUS,cos(radians(0)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT ]) + v_Cross = geometry.intersect_line_line(v_0_Deg_Top_Point,Vector([v_0_Deg_Top_Point.x,v_0_Deg_Top_Point.y,-HEIGHT]),v_Flange_Edge,v_Extended_Flange_Edge) + verts.append(v_Cross[0]) + + #5deg + v_Flange_Edge = Vector([sin(radians(5)) * FLANGE_RADIUS,cos(radians(5)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT ]) + v_Cross = geometry.intersect_line_line(v_5_Deg_Top_Point,Vector([v_5_Deg_Top_Point.x,v_5_Deg_Top_Point.y,-HEIGHT]),v_Flange_Edge,v_Extended_Flange_Edge) + verts.append(v_Cross[0]) + + #10deg + v_Flange_Edge = Vector([sin(radians(10)) * FLANGE_RADIUS,cos(radians(10)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT ]) + v_Cross = geometry.intersect_line_line(v_10_Deg_Top_Point,Vector([v_10_Deg_Top_Point.x,v_10_Deg_Top_Point.y,-HEIGHT]),v_Flange_Edge,v_Extended_Flange_Edge) + verts.append(v_Cross[0]) + + #15deg + v_Flange_Edge = Vector([sin(radians(15)) * FLANGE_RADIUS,cos(radians(15)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT ]) + v_Cross = geometry.intersect_line_line(v_15_Deg_Top_Point,Vector([v_15_Deg_Top_Point.x,v_15_Deg_Top_Point.y,-HEIGHT]),v_Flange_Edge,v_Extended_Flange_Edge) + verts.append(v_Cross[0]) + + + #20deg + v_Flange_Edge = Vector([sin(radians(20)) * FLANGE_RADIUS,cos(radians(20)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT ]) + v_Cross = geometry.intersect_line_line(v_20_Deg_Top_Point,Vector([v_20_Deg_Top_Point.x,v_20_Deg_Top_Point.y,-HEIGHT]),v_Flange_Edge,v_Extended_Flange_Edge) + verts.append(v_Cross[0]) + + #25deg + v_Flange_Edge = Vector([sin(radians(25)) * FLANGE_RADIUS,cos(radians(25)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT ]) + v_Cross = geometry.intersect_line_line(v_25_Deg_Top_Point,Vector([v_25_Deg_Top_Point.x,v_25_Deg_Top_Point.y,-HEIGHT]),v_Flange_Edge,v_Extended_Flange_Edge) + verts.append(v_Cross[0]) + + + #30deg + v_Flange_Edge = Vector([sin(radians(30)) * FLANGE_RADIUS,cos(radians(30)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT ]) + v_Cross = geometry.intersect_line_line(v_30_Deg_Top_Point,Vector([v_30_Deg_Top_Point.x,v_30_Deg_Top_Point.y,-HEIGHT]),v_Flange_Edge,v_Extended_Flange_Edge) + verts.append(v_Cross[0]) + + Row += 1 + + + + verts.append([sin(radians(0)) * FLANGE_RADIUS,cos(radians(0)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT ]) + verts.append([sin(radians(5)) * FLANGE_RADIUS,cos(radians(5)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT]) + verts.append([sin(radians(10)) * FLANGE_RADIUS,cos(radians(10)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT]) + verts.append([sin(radians(15)) * FLANGE_RADIUS,cos(radians(15)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT]) + verts.append([sin(radians(20)) * FLANGE_RADIUS,cos(radians(20)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT]) + verts.append([sin(radians(25)) * FLANGE_RADIUS,cos(radians(25)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT]) + verts.append([sin(radians(30)) * FLANGE_RADIUS,cos(radians(30)) * FLANGE_RADIUS,-HEIGHT + FLANGE_HEIGHT]) + + Row += 1 + + verts.append([sin(radians(0)) * FLANGE_RADIUS,cos(radians(0)) * FLANGE_RADIUS,-HEIGHT]) + verts.append([sin(radians(5)) * FLANGE_RADIUS,cos(radians(5)) * FLANGE_RADIUS,-HEIGHT]) + verts.append([sin(radians(10)) * FLANGE_RADIUS,cos(radians(10)) * FLANGE_RADIUS,-HEIGHT]) + verts.append([sin(radians(15)) * FLANGE_RADIUS,cos(radians(15)) * FLANGE_RADIUS,-HEIGHT]) + verts.append([sin(radians(20)) * FLANGE_RADIUS,cos(radians(20)) * FLANGE_RADIUS,-HEIGHT]) + verts.append([sin(radians(25)) * FLANGE_RADIUS,cos(radians(25)) * FLANGE_RADIUS,-HEIGHT]) + verts.append([sin(radians(30)) * FLANGE_RADIUS,cos(radians(30)) * FLANGE_RADIUS,-HEIGHT]) + + Row += 1 + + + verts.append([sin(radians(0)) * SHANK_RADIUS,cos(radians(0)) * SHANK_RADIUS,-HEIGHT]) + verts.append([sin(radians(0)) * SHANK_RADIUS,cos(radians(0)) * SHANK_RADIUS,-HEIGHT]) + verts.append([sin(radians(10)) * SHANK_RADIUS,cos(radians(10)) * SHANK_RADIUS,-HEIGHT]) + verts.append([sin(radians(10)) * SHANK_RADIUS,cos(radians(10)) * SHANK_RADIUS,-HEIGHT]) + verts.append([sin(radians(20)) * SHANK_RADIUS,cos(radians(20)) * SHANK_RADIUS,-HEIGHT]) + verts.append([sin(radians(20)) * SHANK_RADIUS,cos(radians(20)) * SHANK_RADIUS,-HEIGHT]) + verts.append([sin(radians(30)) * SHANK_RADIUS,cos(radians(30)) * SHANK_RADIUS,-HEIGHT]) + + Row += 1 + + + faces.extend(Build_Face_List_Quads(FaceStart, 6, Row - 1)) + + Spin_Verts, Spin_Faces = SpinDup(verts, faces, 360,12, 'z') + + return Spin_Verts, Spin_Faces, 0 - (-HEIGHT) + + +def Create_12_Point_Head(FLAT, HOLE_DIA, SHANK_DIA, HEIGHT,FLANGE_DIA): + #TODO add under head radius + return Create_12_Point(FLAT, HOLE_DIA, SHANK_DIA, HEIGHT,FLANGE_DIA) + + + # #################################################################### # Create External Thread # #################################################################### @@ -1569,6 +2035,11 @@ def add_Nylon_Part(OUTSIDE_RADIUS, Z_LOCATION, DIV_COUNT): return sVerts, faces, 0 - Lowest_Z_Vert +def add_12_Point_Nut(FLAT, HOLE_DIA, HEIGHT,FLANGE_DIA): + return Create_12_Point(FLAT, HOLE_DIA,HOLE_DIA, HEIGHT,FLANGE_DIA) + + + # #################################################################### # Create Internal Thread # #################################################################### @@ -1815,12 +2286,18 @@ def Nut_Mesh(props, context): Head_Verts = [] Head_Faces = [] - New_Nut_Height = 5 Face_Start = len(verts) + + + if props.bf_Nut_Type == 'bf_Nut_12Pnt': + Nut_Height = props.bf_12_Point_Nut_Height + else: + Nut_Height = props.bf_Hex_Nut_Height + Thread_Verts, Thread_Faces, New_Nut_Height = Create_Internal_Thread( props.bf_Minor_Dia, props.bf_Major_Dia, - props.bf_Pitch, props.bf_Hex_Nut_Height, + props.bf_Pitch, Nut_Height, props.bf_Crest_Percent, props.bf_Root_Percent, 1, props.bf_Div_Count ) @@ -1828,7 +2305,16 @@ def Nut_Mesh(props, context): faces.extend(Copy_Faces(Thread_Faces, Face_Start)) Face_Start = len(verts) - Head_Verts, Head_Faces, Lock_Nut_Rad = add_Hex_Nut( + + if props.bf_Nut_Type == 'bf_Nut_12Pnt': + Head_Verts, Head_Faces, Lock_Nut_Rad = add_12_Point_Nut( + props.bf_12_Point_Nut_Flat_Distance, + props.bf_Major_Dia, New_Nut_Height, + #Limit the size of the Flange to avoid calculation error + max(props.bf_12_Point_Nut_Flange_Dia,props.bf_12_Point_Nut_Flat_Distance) + ) + else: + Head_Verts, Head_Faces, Lock_Nut_Rad = add_Hex_Nut( props.bf_Hex_Nut_Flat_Distance, props.bf_Major_Dia, New_Nut_Height ) @@ -1892,6 +2378,13 @@ def Bolt_Mesh(props, context): props.bf_Allen_Bit_Depth ) + if props.bf_Bit_Type == 'bf_Bit_Torx': + Bit_Verts, Bit_Faces, Bit_Dia = Create_Torx_Bit( + Torx_Bit_Size_To_Point_Distance(props.bf_Torx_Size_Type), + props.bf_Torx_Bit_Depth + ) + + if props.bf_Bit_Type == 'bf_Bit_Philips': Bit_Verts, Bit_Faces, Bit_Dia = Create_Phillips_Bit( props.bf_Philips_Bit_Dia, @@ -1904,6 +2397,14 @@ def Bolt_Mesh(props, context): props.bf_Hex_Head_Flat_Distance, Bit_Dia, props.bf_Shank_Dia, props.bf_Hex_Head_Height ) + + elif props.bf_Head_Type == 'bf_Head_12Pnt': + Head_Verts, Head_Faces, Head_Height = Create_12_Point_Head( + props.bf_12_Point_Head_Flat_Distance, Bit_Dia, + props.bf_Shank_Dia, props.bf_12_Point_Head_Height, + #Limit the size of the Flange to avoid calculation error + max(props.bf_12_Point_Head_Flange_Dia,props.bf_12_Point_Head_Flat_Distance) + ) elif props.bf_Head_Type == 'bf_Head_Cap': Head_Verts, Head_Faces, Head_Height = Create_Cap_Head( Bit_Dia, props.bf_Cap_Head_Dia, @@ -1933,14 +2434,7 @@ def Bolt_Mesh(props, context): props.bf_CounterSink_Head_Dia * (0.09 / 6.31), props.bf_Div_Count ) - """ - Head_Verts, Head_Faces, Head_Height = Create_CounterSink_Head( - Bit_Dia, props.bf_CounterSink_Head_Dia, - props.bf_Shank_Dia, - props.bf_CounterSink_Head_Dia, - props.bf_CounterSink_Head_Dia * (1.0 / 19.0) - ) - """ + Face_Start = len(verts) verts.extend(Move_Verts_Up_Z(Bit_Verts, Head_Height)) faces.extend(Copy_Faces(Bit_Faces, Face_Start)) @@ -1958,7 +2452,7 @@ def Bolt_Mesh(props, context): props.bf_Root_Percent, props.bf_Div_Count ) - verts.extend(Move_Verts_Up_Z(Thread_Verts, 00)) + verts.extend(Move_Verts_Up_Z(Thread_Verts, 0)) faces.extend(Copy_Faces(Thread_Faces, Face_Start)) return Move_Verts_Up_Z(verts, Thread_Height), faces diff --git a/add_mesh_extra_objects/Wallfactory.py b/add_mesh_extra_objects/Wallfactory.py index a4e87574..ede91fc3 100644 --- a/add_mesh_extra_objects/Wallfactory.py +++ b/add_mesh_extra_objects/Wallfactory.py @@ -665,6 +665,10 @@ class add_mesh_wallb(Operator, object_utils.AddObjectHelper): if not self.ConstructTog: return {'FINISHED'} + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False + # enter the settings for the wall dimensions (area) # start can't be zero - min/max don't matter [if max less than end] but zero don't workie. # start can't exceed end. @@ -917,6 +921,12 @@ class add_mesh_wallb(Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def WallParameters(): diff --git a/add_mesh_extra_objects/__init__.py b/add_mesh_extra_objects/__init__.py index 4f00c450..14bde07d 100644 --- a/add_mesh_extra_objects/__init__.py +++ b/add_mesh_extra_objects/__init__.py @@ -249,6 +249,9 @@ def Extras_contex_menu(self, context): obj = context.object layout = self.layout + if obj == None: + return + if 'Gear' in obj.data.keys(): props = layout.operator("mesh.primitive_gear", text="Change Gear") props.change = True diff --git a/add_mesh_extra_objects/add_mesh_beam_builder.py b/add_mesh_extra_objects/add_mesh_beam_builder.py index ceda4ffc..38944c58 100644 --- a/add_mesh_extra_objects/add_mesh_beam_builder.py +++ b/add_mesh_extra_objects/add_mesh_beam_builder.py @@ -730,11 +730,6 @@ class addBeam(Operator, object_utils.AddObjectHelper): default=0, description="Angle beam edges" ) - Cursor: BoolProperty( - name="Use 3D Cursor", - default=False, - description="Draw the beam where the 3D Cursor is" - ) def draw(self, context): layout = self.layout @@ -742,7 +737,6 @@ class addBeam(Operator, object_utils.AddObjectHelper): box = layout.box() split = box.split(factor=0.85, align=True) split.prop(self, "Type", text="") - split.prop(self, "Cursor", text="") box.prop(self, "beamZ") box.prop(self, "beamX") @@ -760,6 +754,10 @@ class addBeam(Operator, object_utils.AddObjectHelper): box.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False + if bpy.context.mode == "OBJECT": if context.selected_objects != [] and context.active_object and \ @@ -781,21 +779,11 @@ class addBeam(Operator, object_utils.AddObjectHelper): bpy.ops.transform.rotate(value=1.570796, constraint_axis=[False, True, False]) bpy.ops.object.transform_apply(location=False, rotation=True, scale=False) - if self.Cursor: - if beamObj.select_get() is True: - # we also have to check if we're considered to be in 3D View (view3d) - if bpy.ops.view3d.snap_selected_to_cursor.poll(): - bpy.ops.view3d.snap_selected_to_cursor() - else: - self.Cursor = False - obj.data["Beam"] = True obj.data["change"] = False for prm in BeamParameters(): obj.data[prm] = getattr(self, prm) - return {'FINISHED'} - if bpy.context.mode == "EDIT_MESH": active_object = context.active_object name_active_object = active_object.name @@ -808,6 +796,12 @@ class addBeam(Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def BeamParameters(): @@ -818,6 +812,5 @@ def BeamParameters(): "beamY", "beamW", "edgeA", - "Cursor", ] return BeamParameters diff --git a/add_mesh_extra_objects/add_mesh_gears.py b/add_mesh_extra_objects/add_mesh_gears.py index 44128330..9170952a 100644 --- a/add_mesh_extra_objects/add_mesh_gears.py +++ b/add_mesh_extra_objects/add_mesh_gears.py @@ -683,6 +683,9 @@ class AddGear(Operator, object_utils.AddObjectHelper): return context.scene is not None def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False if bpy.context.mode == "OBJECT": if context.selected_objects != [] and context.active_object and \ @@ -738,6 +741,12 @@ class AddGear(Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def invoke(self, context, event): @@ -894,6 +903,9 @@ class AddWormGear(Operator, object_utils.AddObjectHelper): box.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False if bpy.context.mode == "OBJECT": if context.selected_objects != [] and context.active_object and \ @@ -950,6 +962,12 @@ class AddWormGear(Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def WormGearParameters(): diff --git a/add_mesh_extra_objects/add_mesh_gemstones.py b/add_mesh_extra_objects/add_mesh_gemstones.py index 5829794b..c47e4131 100644 --- a/add_mesh_extra_objects/add_mesh_gemstones.py +++ b/add_mesh_extra_objects/add_mesh_gemstones.py @@ -278,6 +278,9 @@ class AddDiamond(Operator, object_utils.AddObjectHelper): box.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False if bpy.context.mode == "OBJECT": if context.selected_objects != [] and context.active_object and \ @@ -333,6 +336,12 @@ class AddDiamond(Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def DiamondParameters(): @@ -417,6 +426,9 @@ class AddGem(Operator, object_utils.AddObjectHelper): box.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False if bpy.context.mode == "OBJECT": if context.selected_objects != [] and context.active_object and \ @@ -472,6 +484,12 @@ class AddGem(Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def GemParameters(): diff --git a/add_mesh_extra_objects/add_mesh_honeycomb.py b/add_mesh_extra_objects/add_mesh_honeycomb.py index ef7d34db..73dea1d6 100644 --- a/add_mesh_extra_objects/add_mesh_honeycomb.py +++ b/add_mesh_extra_objects/add_mesh_honeycomb.py @@ -272,6 +272,10 @@ class add_mesh_honeycomb(bpy.types.Operator, object_utils.AddObjectHelper): return context.scene is not None def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False + if bpy.context.mode == "OBJECT": if context.selected_objects != [] and context.active_object and \ ('HoneyComb' in context.active_object.data.keys()) and (self.change == True): @@ -314,6 +318,12 @@ class add_mesh_honeycomb(bpy.types.Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def HoneyCombParameters(): diff --git a/add_mesh_extra_objects/add_mesh_pipe_joint.py b/add_mesh_extra_objects/add_mesh_pipe_joint.py index 00902ad5..65876136 100644 --- a/add_mesh_extra_objects/add_mesh_pipe_joint.py +++ b/add_mesh_extra_objects/add_mesh_pipe_joint.py @@ -169,6 +169,10 @@ class AddElbowJoint(Operator, object_utils.AddObjectHelper): box.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False + radius = self.radius div = self.div @@ -259,6 +263,12 @@ class AddElbowJoint(Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} @@ -360,6 +370,10 @@ class AddTeeJoint(Operator, object_utils.AddObjectHelper): box.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False + radius = self.radius div = self.div @@ -514,6 +528,12 @@ class AddTeeJoint(Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def WyeJointParameters(): @@ -622,6 +642,10 @@ class AddWyeJoint(Operator, object_utils.AddObjectHelper): box.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False + radius = self.radius div = self.div @@ -786,6 +810,12 @@ class AddWyeJoint(Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} @@ -911,6 +941,10 @@ class AddCrossJoint(Operator, object_utils.AddObjectHelper): box.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False + radius = self.radius div = self.div @@ -1122,6 +1156,12 @@ class AddCrossJoint(Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} @@ -1198,6 +1238,10 @@ class AddNJoint(Operator, object_utils.AddObjectHelper): box.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False + radius = self.radius div = self.div number = self.number @@ -1353,4 +1397,10 @@ class AddNJoint(Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} diff --git a/add_mesh_extra_objects/add_mesh_pyramid.py b/add_mesh_extra_objects/add_mesh_pyramid.py index cfc086ff..c70988b7 100644 --- a/add_mesh_extra_objects/add_mesh_pyramid.py +++ b/add_mesh_extra_objects/add_mesh_pyramid.py @@ -162,6 +162,9 @@ class AddPyramid(bpy.types.Operator, object_utils.AddObjectHelper): col.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False if bpy.context.mode == "OBJECT": if context.selected_objects != [] and context.active_object and \ @@ -195,6 +198,12 @@ class AddPyramid(bpy.types.Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def PyramidParameters(): diff --git a/add_mesh_extra_objects/add_mesh_rocks/rockgen.py b/add_mesh_extra_objects/add_mesh_rocks/rockgen.py index 26f15d97..7279d904 100644 --- a/add_mesh_extra_objects/add_mesh_rocks/rockgen.py +++ b/add_mesh_extra_objects/add_mesh_rocks/rockgen.py @@ -174,6 +174,9 @@ def createMeshObject(context, verts, edges, faces, name): # Update mesh geometry after adding stuff. mesh.update() + if bpy.context.mode == "EDIT_MESH": + bpy.ops.object.mode_set(mode='OBJECT') + return object_utils.object_data_add(context, mesh, operator=None) @@ -749,7 +752,7 @@ def generateRocks(context, scaleX, skewX, scaleY, skewY, scaleZ, skewZ, scale_fac, detail, display_detail, deform, rough, smooth_fac, smooth_it, numOfRocks=1, userSeed=1.0, - scaleDisplace=False, randomSeed=True): + scaleDisplace=False, randomSeed=True, use_enter_edit_mode=False): global LASTROCK sigmaX = 0 sigmaY = 0 @@ -823,6 +826,8 @@ def generateRocks(context, scaleX, skewX, scaleY, skewY, scaleZ, skewZ, else: muZ = scaleZ + rocks = [] + for i in range(numOfRocks): # todo: enable different random values for each (x,y,z) corrdinate for # each vertex. This will add additional randomness to the shape of the @@ -928,13 +933,20 @@ def generateRocks(context, scaleX, skewX, scaleY, skewY, scaleZ, skewZ, bpy.ops.mesh.normals_make_consistent() bpy.ops.object.editmode_toggle() + if use_enter_edit_mode: + for m in rock.modifiers: + m.show_in_editmode = True + m.show_on_cage = True + # Store the last value of i: shift = i + rocks.append(rock) + # Add the shift to LASTROCK: LASTROCK += shift + 1 - return + return rocks # Much of the code below is more-or-less imitation of other addons and as such @@ -1087,12 +1099,10 @@ class OBJECT_OT_add_mesh_rock(bpy.types.Operator): box.prop(self, 'user_seed') box.prop(self, 'preset_values') - @classmethod - def poll(cls, context): - return context.mode == 'OBJECT' - # return (context.object is not None and context.object.mode == 'OBJECT') - def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False # The following "if" block loads preset values: if self.lastPreset != int(self.preset_values): @@ -1120,7 +1130,7 @@ class OBJECT_OT_add_mesh_rock(bpy.types.Operator): # *** Eliminated "deform_Var" and "rough_Var" so the script is not # as complex to use. May add in again as advanced features. *** if self.use_generate: - generateRocks(context, + rocks = generateRocks(context, self.scale_X, self.skew_X, self.scale_Y, @@ -1137,10 +1147,19 @@ class OBJECT_OT_add_mesh_rock(bpy.types.Operator): self.num_of_rocks, self.user_seed, self.use_scale_dis, - self.use_random_seed) + self.use_random_seed, + use_enter_edit_mode) - return {'FINISHED'} + for rock in rocks: + rock.select_set(True) + + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + + return {'FINISHED'} # Register: def menu_func_rocks(self, context): diff --git a/add_mesh_extra_objects/add_mesh_round_brilliant.py b/add_mesh_extra_objects/add_mesh_round_brilliant.py index cd796a53..49232151 100644 --- a/add_mesh_extra_objects/add_mesh_round_brilliant.py +++ b/add_mesh_extra_objects/add_mesh_round_brilliant.py @@ -422,6 +422,9 @@ class MESH_OT_primitive_brilliant_add(Operator, object_utils.AddObjectHelper): # call mesh/object generator function with user inputs def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False if bpy.context.mode == "OBJECT": if context.selected_objects != [] and context.active_object and \ @@ -466,6 +469,12 @@ class MESH_OT_primitive_brilliant_add(Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def BrilliantParameters(): diff --git a/add_mesh_extra_objects/add_mesh_round_cube.py b/add_mesh_extra_objects/add_mesh_round_cube.py index 3be67b5b..3c261908 100644 --- a/add_mesh_extra_objects/add_mesh_round_cube.py +++ b/add_mesh_extra_objects/add_mesh_round_cube.py @@ -388,6 +388,10 @@ class AddRoundCube(Operator, object_utils.AddObjectHelper): ) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False + if self.arc_div <= 0 and self.lin_div <= 0: self.report({'ERROR'}, "Either Arc Divisions or Linear Divisions must be greater than zero") @@ -441,6 +445,12 @@ class AddRoundCube(Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def check(self, context): diff --git a/add_mesh_extra_objects/add_mesh_star.py b/add_mesh_extra_objects/add_mesh_star.py index 105648e3..30573864 100644 --- a/add_mesh_extra_objects/add_mesh_star.py +++ b/add_mesh_extra_objects/add_mesh_star.py @@ -202,6 +202,10 @@ class AddStar(bpy.types.Operator, object_utils.AddObjectHelper): col.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False + if bpy.context.mode == "OBJECT": if context.selected_objects != [] and context.active_object and \ ('Star' in context.active_object.data.keys()) and (self.change == True): @@ -256,6 +260,12 @@ class AddStar(bpy.types.Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def StarParameters(): diff --git a/add_mesh_extra_objects/add_mesh_supertoroid.py b/add_mesh_extra_objects/add_mesh_supertoroid.py index 6aec60b5..af63ca2a 100644 --- a/add_mesh_extra_objects/add_mesh_supertoroid.py +++ b/add_mesh_extra_objects/add_mesh_supertoroid.py @@ -199,6 +199,10 @@ class add_supertoroid(bpy.types.Operator, object_utils.AddObjectHelper): col.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False + props = self.properties # check how the radii properties must be used @@ -274,6 +278,12 @@ class add_supertoroid(bpy.types.Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def SuperToroidParameters(): diff --git a/add_mesh_extra_objects/add_mesh_teapot.py b/add_mesh_extra_objects/add_mesh_teapot.py index c583bb7a..e4ddbecb 100644 --- a/add_mesh_extra_objects/add_mesh_teapot.py +++ b/add_mesh_extra_objects/add_mesh_teapot.py @@ -9,9 +9,10 @@ import mathutils import io import operator import functools +from bpy_extras import object_utils -class AddTeapot(bpy.types.Operator): +class AddTeapot(bpy.types.Operator, object_utils.AddObjectHelper): bl_idname = "mesh.primitive_teapot_add" bl_label = "Add Teapot" bl_description = "Construct a teapot or teaspoon mesh" @@ -31,16 +32,42 @@ class AddTeapot(bpy.types.Operator): default='1', ) + def draw(self, context): + layout = self.layout + + box = layout.box() + box.prop(self, 'resolution') + + box = layout.box() + box.prop(self, 'objecttype') + + # generic transform props + box = layout.box() + box.prop(self, 'align', expand=True) + box.prop(self, 'location', expand=True) + box.prop(self, 'rotation', expand=True) + def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False + cmode = bpy.context.mode verts, faces = make_teapot(self.objecttype, self.resolution) # Actually create the mesh object from this geometry data. - obj = create_mesh_object(context, verts, [], faces, "Teapot") + obj = create_mesh_object(self, context, verts, [], faces, "Teapot") bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.remove_doubles() if cmode != "EDIT_MESH": bpy.ops.object.mode_set(mode=cmode) + + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} @@ -56,7 +83,7 @@ def create_mesh_face_hack(faces): faces[:] = faces_copy -def create_mesh_object(context, verts, edges, faces, name): +def create_mesh_object(self, context, verts, edges, faces, name): create_mesh_face_hack(faces) @@ -66,8 +93,8 @@ def create_mesh_object(context, verts, edges, faces, name): mesh.from_pydata(verts, edges, faces) # Update mesh geometry after adding stuff. mesh.update() - from bpy_extras import object_utils - return object_utils.object_data_add(context, mesh, operator=None) + + return object_utils.object_data_add(context, mesh, operator=self) # ========================== diff --git a/add_mesh_extra_objects/add_mesh_torusknot.py b/add_mesh_extra_objects/add_mesh_torusknot.py index 73a0b80f..ac666289 100644 --- a/add_mesh_extra_objects/add_mesh_torusknot.py +++ b/add_mesh_extra_objects/add_mesh_torusknot.py @@ -138,6 +138,10 @@ class AddTorusKnot(bpy.types.Operator, object_utils.AddObjectHelper): col.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False + if bpy.context.mode == "OBJECT": if context.selected_objects != [] and context.active_object and \ ('TorusKnot' in context.active_object.data.keys()) and (self.change == True): @@ -177,6 +181,12 @@ class AddTorusKnot(bpy.types.Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def TorusKnotParameters(): diff --git a/add_mesh_extra_objects/add_mesh_twisted_torus.py b/add_mesh_extra_objects/add_mesh_twisted_torus.py index 8bc10169..ba6b4fc1 100644 --- a/add_mesh_extra_objects/add_mesh_twisted_torus.py +++ b/add_mesh_extra_objects/add_mesh_twisted_torus.py @@ -225,6 +225,9 @@ class AddTwistedTorus(bpy.types.Operator, object_utils.AddObjectHelper): col.prop(self, 'rotation', expand=True) def execute(self, context): + # turn off 'Enter Edit Mode' + use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode + bpy.context.preferences.edit.use_enter_edit_mode = False if self.use_abso is True: extra_helper = (self.abso_major_rad - self.abso_minor_rad) * 0.5 @@ -288,6 +291,12 @@ class AddTwistedTorus(bpy.types.Operator, object_utils.AddObjectHelper): context.active_object.name = name_active_object bpy.ops.object.mode_set(mode='EDIT') + if use_enter_edit_mode: + bpy.ops.object.mode_set(mode = 'EDIT') + + # restore pre operator state + bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode + return {'FINISHED'} def TwistedTorusParameters(): diff --git a/ant_landscape/ant_functions.py b/ant_landscape/ant_functions.py index b4e1e1f5..3c3c2c71 100644 --- a/ant_landscape/ant_functions.py +++ b/ant_landscape/ant_functions.py @@ -137,7 +137,7 @@ class AntLandscapeRefresh(bpy.types.Operator): @classmethod def poll(cls, context): ob = bpy.context.active_object - return (ob.ant_landscape and not ob.ant_landscape['sphere_mesh']) + return (ob.ant_landscape and not ob.ant_landscape.sphere_mesh) def execute(self, context): diff --git a/archipack/archipack_manipulator.py b/archipack/archipack_manipulator.py index 0eba8aba..fb454f69 100644 --- a/archipack/archipack_manipulator.py +++ b/archipack/archipack_manipulator.py @@ -118,9 +118,11 @@ class ArchipackActiveManip: Check for manipulable validity to disable modal when required """ + o = bpy.data.objects.get(self.object_name) return ( self.manipulable is None or - bpy.data.objects.find(self.object_name) < 0 + o is None or + not o.select_get() ) def exit(self): @@ -167,7 +169,6 @@ def check_stack(key): # print("check_stack : key.dirty %s" % (key)) remove_manipulable(key) return True - return False diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py index 40ca5fc4..b935768d 100644 --- a/io_scene_fbx/__init__.py +++ b/io_scene_fbx/__init__.py @@ -95,7 +95,7 @@ class ImportFBX(bpy.types.Operator, ImportHelper): default=1.0, ) bake_space_transform: BoolProperty( - name="!EXPERIMENTAL! Apply Transform", + name="Apply Transform", description="Bake space transform into object data, avoids getting unwanted rotations to objects when " "target space is not aligned with Blender's space " "(WARNING! experimental option, use at own risks, known broken with armatures/animations)", @@ -103,7 +103,7 @@ class ImportFBX(bpy.types.Operator, ImportHelper): ) use_custom_normals: BoolProperty( - name="Import Normals", + name="Custom Normals", description="Import custom normals, if available (otherwise Blender will recompute them)", default=True, ) @@ -138,13 +138,13 @@ class ImportFBX(bpy.types.Operator, ImportHelper): ) use_subsurf: BoolProperty( - name="Import Subdivision Surface", + name="Subdivision Data", description="Import FBX subdivision information as subdivision surface modifiers", default=False, ) use_custom_props: BoolProperty( - name="Import User Properties", + name="Custom Properties", description="Import user properties as custom properties", default=True, ) @@ -273,7 +273,9 @@ class FBX_PT_import_transform(bpy.types.Panel): layout.prop(operator, "global_scale") layout.prop(operator, "decal_offset") - layout.prop(operator, "bake_space_transform") + row = layout.row() + row.prop(operator, "bake_space_transform") + row.label(text="", icon='ERROR') layout.prop(operator, "use_prepost_rot") @@ -425,7 +427,7 @@ class ExportFBX(bpy.types.Operator, ExportHelper): "but many other applications do not handle the same way)", ) bake_space_transform: BoolProperty( - name="!EXPERIMENTAL! Apply Transform", + name="Apply Transform", description="Bake space transform into object data, avoids getting unwanted rotations to objects when " "target space is not aligned with Blender's space " "(WARNING! experimental option, use at own risks, known broken with armatures/animations)", @@ -688,7 +690,7 @@ class FBX_PT_export_include(bpy.types.Panel): sfile = context.space_data operator = sfile.active_operator - sublayout = layout.column() + sublayout = layout.column(heading="Limit to") sublayout.enabled = (operator.batch_mode == 'OFF') sublayout.prop(operator, "use_selection") sublayout.prop(operator, "use_active_collection") @@ -725,7 +727,9 @@ class FBX_PT_export_transform(bpy.types.Panel): layout.prop(operator, "axis_up") layout.prop(operator, "apply_unit_scale") - layout.prop(operator, "bake_space_transform") + row = layout.row() + row.prop(operator, "bake_space_transform") + row.label(text="", icon='ERROR') class FBX_PT_export_geometry(bpy.types.Panel): diff --git a/io_scene_obj/__init__.py b/io_scene_obj/__init__.py index 997deee2..e9fc8259 100644 --- a/io_scene_obj/__init__.py +++ b/io_scene_obj/__init__.py @@ -234,97 +234,6 @@ class OBJ_PT_import_geometry(bpy.types.Panel): col.prop(operator, "use_groups_as_vgroups") -class OBJ_PT_export_include(bpy.types.Panel): - bl_space_type = 'FILE_BROWSER' - bl_region_type = 'TOOL_PROPS' - bl_label = "Include" - bl_parent_id = "FILE_PT_operator" - - @classmethod - def poll(cls, context): - sfile = context.space_data - operator = sfile.active_operator - - return operator.bl_idname == "EXPORT_SCENE_OT_obj" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - sfile = context.space_data - operator = sfile.active_operator - - layout.prop(operator, 'use_selection') - layout.prop(operator, 'use_blen_objects') - layout.prop(operator, 'group_by_object') - layout.prop(operator, 'group_by_material') - layout.prop(operator, 'use_animation') - - -class OBJ_PT_export_transform(bpy.types.Panel): - bl_space_type = 'FILE_BROWSER' - bl_region_type = 'TOOL_PROPS' - bl_label = "Transform" - bl_parent_id = "FILE_PT_operator" - - @classmethod - def poll(cls, context): - sfile = context.space_data - operator = sfile.active_operator - - return operator.bl_idname == "EXPORT_SCENE_OT_obj" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - sfile = context.space_data - operator = sfile.active_operator - - layout.prop(operator, 'global_scale') - layout.prop(operator, 'path_mode') - layout.prop(operator, 'axis_forward') - layout.prop(operator, 'axis_up') - - -class OBJ_PT_export_geometry(bpy.types.Panel): - bl_space_type = 'FILE_BROWSER' - bl_region_type = 'TOOL_PROPS' - bl_label = "Geometry" - bl_parent_id = "FILE_PT_operator" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - sfile = context.space_data - operator = sfile.active_operator - - return operator.bl_idname == "EXPORT_SCENE_OT_obj" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - sfile = context.space_data - operator = sfile.active_operator - - layout.prop(operator, 'use_mesh_modifiers') - # Property definition disabled, not working in 2.8 currently. - # layout.prop(operator, 'use_mesh_modifiers_render') - layout.prop(operator, 'use_smooth_groups') - layout.prop(operator, 'use_smooth_groups_bitflags') - layout.prop(operator, 'use_normals') - layout.prop(operator, 'use_uvs') - layout.prop(operator, 'use_materials') - layout.prop(operator, 'use_triangles') - layout.prop(operator, 'use_nurbs', text="Curves as NURBS") - layout.prop(operator, 'use_vertex_groups') - layout.prop(operator, 'keep_vertex_order') - - @orientation_helper(axis_forward='-Z', axis_up='Y') class ExportOBJ(bpy.types.Operator, ExportHelper): """Save a Wavefront OBJ File""" @@ -415,18 +324,18 @@ class ExportOBJ(bpy.types.Operator, ExportHelper): # grouping group use_blen_objects: BoolProperty( - name="Objects as OBJ Objects", - description="", + name="OBJ Objects", + description="Export Blender objects as OBJ objects", default=True, ) group_by_object: BoolProperty( - name="Objects as OBJ Groups ", - description="", + name="OBJ Groups", + description="Export Blender objects as OBJ groups", default=False, ) group_by_material: BoolProperty( name="Material Groups", - description="", + description="Generate an OBJ group for each part of a geometry using a different material", default=False, ) keep_vertex_order: BoolProperty( @@ -468,6 +377,103 @@ class ExportOBJ(bpy.types.Operator, ExportHelper): pass +class OBJ_PT_export_include(bpy.types.Panel): + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOL_PROPS' + bl_label = "Include" + bl_parent_id = "FILE_PT_operator" + + @classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + + return operator.bl_idname == "EXPORT_SCENE_OT_obj" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + sfile = context.space_data + operator = sfile.active_operator + + col = layout.column(heading="Limit to") + col.prop(operator, 'use_selection') + + col = layout.column(heading="Objects as", align=True) + col.prop(operator, 'use_blen_objects') + col.prop(operator, 'group_by_object') + col.prop(operator, 'group_by_material') + + layout.separator() + + layout.prop(operator, 'use_animation') + + +class OBJ_PT_export_transform(bpy.types.Panel): + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOL_PROPS' + bl_label = "Transform" + bl_parent_id = "FILE_PT_operator" + + @classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + + return operator.bl_idname == "EXPORT_SCENE_OT_obj" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + sfile = context.space_data + operator = sfile.active_operator + + layout.prop(operator, 'global_scale') + layout.prop(operator, 'path_mode') + layout.prop(operator, 'axis_forward') + layout.prop(operator, 'axis_up') + + +class OBJ_PT_export_geometry(bpy.types.Panel): + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOL_PROPS' + bl_label = "Geometry" + bl_parent_id = "FILE_PT_operator" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + + return operator.bl_idname == "EXPORT_SCENE_OT_obj" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + sfile = context.space_data + operator = sfile.active_operator + + layout.prop(operator, 'use_mesh_modifiers') + # Property definition disabled, not working in 2.8 currently. + # layout.prop(operator, 'use_mesh_modifiers_render') + layout.prop(operator, 'use_smooth_groups') + layout.prop(operator, 'use_smooth_groups_bitflags') + layout.prop(operator, 'use_normals') + layout.prop(operator, 'use_uvs') + layout.prop(operator, 'use_materials') + layout.prop(operator, 'use_triangles') + layout.prop(operator, 'use_nurbs', text="Curves as NURBS") + layout.prop(operator, 'use_vertex_groups') + layout.prop(operator, 'keep_vertex_order') + + def menu_func_import(self, context): self.layout.operator(ImportOBJ.bl_idname, text="Wavefront (.obj)") diff --git a/node_wrangler.py b/node_wrangler.py index e26da88b..e9ba5e74 100644 --- a/node_wrangler.py +++ b/node_wrangler.py @@ -19,8 +19,8 @@ bl_info = { "name": "Node Wrangler", "author": "Bartek Skorupa, Greg Zaal, Sebastian Koenig, Christian Brinkmann, Florian Meyer", - "version": (3, 36), - "blender": (2, 80, 0), + "version": (3, 37), + "blender": (2, 83, 0), "location": "Node Editor Toolbar or Shift-W", "description": "Various tools to enhance and speed up node-based workflow", "warning": "", @@ -596,9 +596,10 @@ draw_color_sets = { ) } +viewer_socket_name = "tmp_viewer" def is_visible_socket(socket): - return not socket.hide and socket.enabled + return not socket.hide and socket.enabled and socket.type != 'CUSTOM' def nice_hotkey_name(punc): # convert the ugly string name into the actual character @@ -1030,10 +1031,9 @@ def draw_callback_nodeoutline(self, context, mode): bgl.glDisable(bgl.GL_BLEND) bgl.glDisable(bgl.GL_LINE_SMOOTH) - -def get_nodes_links(context): +def get_active_tree(context): tree = context.space_data.node_tree - + path = [] # Get nodes from currently edited tree. # If user is editing a group, space_data.node_tree is still the base level (outside group). # context.active_node is in the group though, so if space_data.node_tree.nodes.active is not @@ -1042,9 +1042,71 @@ def get_nodes_links(context): if tree.nodes.active: while tree.nodes.active != context.active_node: tree = tree.nodes.active.node_tree + path.append(tree) + return tree, path +def get_nodes_links(context): + tree, path = get_active_tree(context) return tree.nodes, tree.links +def is_viewer_socket(socket): + # checks if a internal socket is a valid viewer socket + return socket.name == viewer_socket_name and socket.NWViewerSocket + +def get_internal_socket(socket): + #get the internal socket from a socket inside or outside the group + node = socket.node + if node.type == 'GROUP_OUTPUT': + source_iterator = node.inputs + iterator = node.id_data.outputs + elif node.type == 'GROUP_INPUT': + source_iterator = node.outputs + iterator = node.id_data.inputs + elif hasattr(node, "node_tree"): + if socket.is_output: + source_iterator = node.outputs + iterator = node.node_tree.outputs + else: + source_iterator = node.inputs + iterator = node.node_tree.inputs + else: + return None + + for i, s in enumerate(source_iterator): + if s == socket: + break + return iterator[i] + +def is_viewer_link(link, output_node): + if "Emission Viewer" in link.to_node.name or link.to_node == output_node and link.to_socket == output_node.inputs[0]: + return True + if link.to_node.type == 'GROUP_OUTPUT': + socket = get_internal_socket(link.to_socket) + if is_viewer_socket(socket): + return True + return False + +def get_group_output_node(tree): + for node in tree.nodes: + if node.type == 'GROUP_OUTPUT' and node.is_active_output == True: + return node + +def get_output_location(tree): + # get right-most location + sorted_by_xloc = (sorted(tree.nodes, key=lambda x: x.location.x)) + max_xloc_node = sorted_by_xloc[-1] + if max_xloc_node.name == 'Emission Viewer': + max_xloc_node = sorted_by_xloc[-2] + + # get average y location + sum_yloc = 0 + for node in tree.nodes: + sum_yloc += node.location.y + + loc_x = max_xloc_node.location.x + max_xloc_node.dimensions.x + 80 + loc_y = sum_yloc / len(tree.nodes) + return loc_x, loc_y + # Principled prefs class NWPrincipledPreferences(bpy.types.PropertyGroup): base_color: StringProperty( @@ -1624,13 +1686,17 @@ class NWAddAttrNode(Operator, NWBase): nodes.active.attribute_name = self.attr_name return {'FINISHED'} - class NWEmissionViewer(Operator, NWBase): bl_idname = "node.nw_emission_viewer" bl_label = "Emission Viewer" bl_description = "Connect active node to Emission Shader for shadeless previews" bl_options = {'REGISTER', 'UNDO'} + def __init__(self): + self.shader_output_type = "" + self.shader_output_ident = "" + self.shader_viewer_ident = "" + @classmethod def poll(cls, context): if nw_check(context): @@ -1643,67 +1709,172 @@ class NWEmissionViewer(Operator, NWBase): return True return False - def invoke(self, context, event): - space = context.space_data - shader_type = space.shader_type + def ensure_viewer_socket(self, node, socket_type, connect_socket=None): + #check if a viewer output already exists in a node group otherwise create + if hasattr(node, "node_tree"): + index = None + if len(node.node_tree.outputs): + free_socket = None + for i, socket in enumerate(node.node_tree.outputs): + if is_viewer_socket(socket) and is_visible_socket(node.outputs[i]) and socket.type == socket_type: + #if viewer output is already used but leads to the same socket we can still use it + is_used = self.is_socket_used_other_mats(socket) + if is_used: + if connect_socket == None: + continue + groupout = get_group_output_node(node.node_tree) + groupout_input = groupout.inputs[i] + links = groupout_input.links + if connect_socket not in [link.from_socket for link in links]: + continue + index=i + break + if not free_socket: + free_socket = i + if not index and free_socket: + index = free_socket + + if not index: + #create viewer socket + node.node_tree.outputs.new(socket_type, viewer_socket_name) + index = len(node.node_tree.outputs) - 1 + node.node_tree.outputs[index].NWViewerSocket = True + return index + + def init_shader_variables(self, space, shader_type): if shader_type == 'OBJECT': if space.id not in [light for light in bpy.data.lights]: # cannot use bpy.data.lights directly as iterable - shader_output_type = "OUTPUT_MATERIAL" - shader_output_ident = "ShaderNodeOutputMaterial" - shader_viewer_ident = "ShaderNodeEmission" + self.shader_output_type = "OUTPUT_MATERIAL" + self.shader_output_ident = "ShaderNodeOutputMaterial" + self.shader_viewer_ident = "ShaderNodeEmission" else: - shader_output_type = "OUTPUT_LIGHT" - shader_output_ident = "ShaderNodeOutputLight" - shader_viewer_ident = "ShaderNodeEmission" + self.shader_output_type = "OUTPUT_LIGHT" + self.shader_output_ident = "ShaderNodeOutputLight" + self.shader_viewer_ident = "ShaderNodeEmission" elif shader_type == 'WORLD': - shader_output_type = "OUTPUT_WORLD" - shader_output_ident = "ShaderNodeOutputWorld" - shader_viewer_ident = "ShaderNodeBackground" + self.shader_output_type = "OUTPUT_WORLD" + self.shader_output_ident = "ShaderNodeOutputWorld" + self.shader_viewer_ident = "ShaderNodeBackground" + + def get_shader_output_node(self, tree): + for node in tree.nodes: + if node.type == self.shader_output_type and node.is_active_output == True: + return node + + @classmethod + def ensure_group_output(cls, tree): + #check if a group output node exists otherwise create + groupout = get_group_output_node(tree) + if not groupout: + groupout = tree.nodes.new('NodeGroupOutput') + loc_x, loc_y = get_output_location(tree) + groupout.location.x = loc_x + groupout.location.y = loc_y + groupout.select = False + return groupout + + @classmethod + def search_sockets(cls, node, sockets, index=None): + #recursevley scan nodes for viewer sockets and store in list + for i, input_socket in enumerate(node.inputs): + if index and i != index: + continue + if len(input_socket.links): + link = input_socket.links[0] + next_node = link.from_node + external_socket = link.from_socket + if hasattr(next_node, "node_tree"): + for socket_index, s in enumerate(next_node.outputs): + if s == external_socket: + break + socket = next_node.node_tree.outputs[socket_index] + if is_viewer_socket(socket) and socket not in sockets: + sockets.append(socket) + #continue search inside of node group but restrict socket to where we came from + groupout = get_group_output_node(next_node.node_tree) + cls.search_sockets(groupout, sockets, index=socket_index) + + @classmethod + def scan_nodes(cls, tree, sockets): + # get all viewer sockets in a material tree + for node in tree.nodes: + if hasattr(node, "node_tree"): + for socket in node.node_tree.outputs: + if is_viewer_socket(socket) and (socket not in sockets): + sockets.append(socket) + cls.scan_nodes(node.node_tree, sockets) + + def link_leads_to_used_socket(self, link): + #return True if link leads to a socket that is already used in this material + socket = get_internal_socket(link.to_socket) + return (socket and self.is_socket_used_active_mat(socket)) + + def is_socket_used_active_mat(self, socket): + #ensure used sockets in active material is calculated and check given socket + if not hasattr(self, "used_viewer_sockets_active_mat"): + self.used_viewer_sockets_active_mat = [] + materialout = self.get_shader_output_node(bpy.context.space_data.node_tree) + if materialout: + emission = self.get_viewer_node(materialout) + self.search_sockets((emission if emission else materialout), self.used_viewer_sockets_active_mat) + return socket in self.used_viewer_sockets_active_mat + + def is_socket_used_other_mats(self, socket): + #ensure used sockets in other materials are calculated and check given socket + if not hasattr(self, "used_viewer_sockets_other_mats"): + self.used_viewer_sockets_other_mats = [] + for mat in bpy.data.materials: + if mat.node_tree == bpy.context.space_data.node_tree or not hasattr(mat.node_tree, "nodes"): + continue + # get viewer node + materialout = self.get_shader_output_node(mat.node_tree) + if materialout: + emission = self.get_viewer_node(materialout) + self.search_sockets((emission if emission else materialout), self.used_viewer_sockets_other_mats) + return socket in self.used_viewer_sockets_other_mats + + @staticmethod + def get_viewer_node(materialout): + input_socket = materialout.inputs[0] + if len(input_socket.links) > 0: + node = input_socket.links[0].from_node + if node.type == 'EMISSION' and node.name == "Emission Viewer": + return node + + def invoke(self, context, event): + space = context.space_data + shader_type = space.shader_type + self.init_shader_variables(space, shader_type) shader_types = [x[1] for x in shaders_shader_nodes_props] mlocx = event.mouse_region_x mlocy = event.mouse_region_y select_node = bpy.ops.node.select(mouse_x=mlocx, mouse_y=mlocy, extend=False) if 'FINISHED' in select_node: # only run if mouse click is on a node - nodes, links = get_nodes_links(context) - in_group = context.active_node != space.node_tree.nodes.active + active_tree, path_to_tree = get_active_tree(context) + nodes, links = active_tree.nodes, active_tree.links + base_node_tree = space.node_tree active = nodes.active output_types = [x[1] for x in shaders_output_nodes_props] valid = False if active: - if (active.name != "Emission Viewer") and (active.type not in output_types) and not in_group: + if (active.name != "Emission Viewer") and (active.type not in output_types): for out in active.outputs: if is_visible_socket(out): valid = True break if valid: - # get material_output node, store selection, deselect all + # get material_output node materialout = None # placeholder node - selection = [] - for node in nodes: - if node.type == shader_output_type: - materialout = node - if node.select: - selection.append(node.name) - node.select = False - if not materialout: - # get right-most location - sorted_by_xloc = (sorted(nodes, key=lambda x: x.location.x)) - max_xloc_node = sorted_by_xloc[-1] - if max_xloc_node.name == 'Emission Viewer': - max_xloc_node = sorted_by_xloc[-2] - - # get average y location - sum_yloc = 0 - for node in nodes: - sum_yloc += node.location.y + delete_sockets = [] - new_locx = max_xloc_node.location.x + max_xloc_node.dimensions.x + 80 - new_locy = sum_yloc / len(nodes) + #scan through all nodes in tree including nodes inside of groups to find viewer sockets + self.scan_nodes(base_node_tree, delete_sockets) - materialout = nodes.new(shader_output_ident) - materialout.location.x = new_locx - materialout.location.y = new_locy + materialout = self.get_shader_output_node(base_node_tree) + if not materialout: + materialout = base_node_tree.nodes.new(self.shader_output_ident) + materialout.location = get_output_location(base_node_tree) materialout.select = False # Analyze outputs, add "Emission Viewer" if needed, make links out_i = None @@ -1715,24 +1886,28 @@ class NWEmissionViewer(Operator, NWBase): out_i = valid_outputs[0] # Start index of node's outputs for i, valid_i in enumerate(valid_outputs): for out_link in active.outputs[valid_i].links: - if "Emission Viewer" in out_link.to_node.name or (out_link.to_node == materialout and out_link.to_socket == materialout.inputs[0]): - if i < len(valid_outputs) - 1: - out_i = valid_outputs[i + 1] - else: - out_i = valid_outputs[0] + if is_viewer_link(out_link, materialout): + if nodes == base_node_tree.nodes or self.link_leads_to_used_socket(out_link): + if i < len(valid_outputs) - 1: + out_i = valid_outputs[i + 1] + else: + out_i = valid_outputs[0] + make_links = [] # store sockets for new links + delete_nodes = [] # store unused nodes to delete in the end if active.outputs: # If output type not 'SHADER' - "Emission Viewer" needed if active.outputs[out_i].type != 'SHADER': + socket_type = 'NodeSocketColor' # get Emission Viewer node emission_exists = False - emission_placeholder = nodes[0] - for node in nodes: + emission_placeholder = base_node_tree.nodes[0] + for node in base_node_tree.nodes: if "Emission Viewer" in node.name: emission_exists = True emission_placeholder = node if not emission_exists: - emission = nodes.new(shader_viewer_ident) + emission = base_node_tree.nodes.new(self.shader_viewer_ident) emission.hide = True emission.location = [materialout.location.x, (materialout.location.y + 40)] emission.label = "Viewer" @@ -1742,7 +1917,7 @@ class NWEmissionViewer(Operator, NWBase): emission.select = False else: emission = emission_placeholder - make_links.append((active.outputs[out_i], emission.inputs[0])) + output_socket = emission.inputs[0] # If Viewer is connected to output by user, don't change those connections (patch by gandalf3) if emission.outputs[0].links.__len__() > 0: @@ -1762,19 +1937,47 @@ class NWEmissionViewer(Operator, NWBase): else: # Output type is 'SHADER', no Viewer needed. Delete Viewer if exists. - make_links.append((active.outputs[out_i], materialout.inputs[1 if active.outputs[out_i].name == "Volume" else 0])) - for node in nodes: + socket_type = 'NodeSocketShader' + materialout_index = 1 if active.outputs[out_i].name == "Volume" else 0 + make_links.append((active.outputs[out_i], materialout.inputs[materialout_index])) + output_socket = materialout.inputs[materialout_index] + for node in base_node_tree.nodes: if node.name == 'Emission Viewer': - node.select = True - bpy.ops.node.delete() + delete_nodes.append((base_node_tree, node)) for li_from, li_to in make_links: - links.new(li_from, li_to) - # Restore selection + base_node_tree.links.new(li_from, li_to) + + # Crate links through node groups until we reach the active node + tree = base_node_tree + link_end = output_socket + while tree.nodes.active != active: + node = tree.nodes.active + index = self.ensure_viewer_socket(node, socket_type, connect_socket=active.outputs[out_i] if node.node_tree.nodes.active == active else None) + link_start = node.outputs[index] + node_socket = node.node_tree.outputs[index] + if node_socket in delete_sockets: + delete_sockets.remove(node_socket) + tree.links.new(link_start, link_end) + # Iterate + link_end = self.ensure_group_output(node.node_tree).inputs[index] + tree = tree.nodes.active.node_tree + tree.links.new(active.outputs[out_i], link_end) + + # Delete sockets + for socket in delete_sockets: + if not self.is_socket_used_other_mats(socket): + tree = socket.id_data + tree.outputs.remove(socket) + + # Delete nodes + for tree, node in delete_nodes: + tree.nodes.remove(node) + nodes.active = active - for node in nodes: - if node.name in selection: - node.select = True + active.select = True + force_update(context) + return {'FINISHED'} else: return {'CANCELLED'} @@ -4847,6 +5050,11 @@ def register(): name="Source Socket!", default=0, description="An internal property used to store the source socket in a Lazy Connect operation") + bpy.types.NodeSocketInterface.NWViewerSocket = BoolProperty( + name="NW Socket", + default=False, + description="An internal property used to determine if a socket is generated by the addon" + ) for cls in classes: register_class(cls) @@ -4882,6 +5090,7 @@ def unregister(): del bpy.types.Scene.NWLazySource del bpy.types.Scene.NWLazyTarget del bpy.types.Scene.NWSourceSocket + del bpy.types.NodeSocketInterface.NWViewerSocket # keymaps for km, kmi in addon_keymaps: diff --git a/object_fracture_cell/__init__.py b/object_fracture_cell/__init__.py index 02e156b4..cb8cc561 100644 --- a/object_fracture_cell/__init__.py +++ b/object_fracture_cell/__init__.py @@ -35,13 +35,13 @@ bl_info = { import bpy from bpy.props import ( - StringProperty, - BoolProperty, - IntProperty, - FloatProperty, - FloatVectorProperty, - EnumProperty, - ) + StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + FloatVectorProperty, + EnumProperty, +) from bpy.types import Operator @@ -80,18 +80,22 @@ def main_object(context, obj, level, **kw): obj.display_type = 'WIRE' objects = fracture_cell_setup.cell_fracture_objects(context, obj, **kw_copy) - objects = fracture_cell_setup.cell_fracture_boolean(context, obj, objects, - use_island_split=use_island_split, - use_interior_hide=(use_interior_vgroup or use_sharp_edges), - use_debug_bool=use_debug_bool, - use_debug_redraw=kw_copy["use_debug_redraw"], - level=level, - ) + objects = fracture_cell_setup.cell_fracture_boolean( + context, obj, objects, + use_island_split=use_island_split, + use_interior_hide=(use_interior_vgroup or use_sharp_edges), + use_debug_bool=use_debug_bool, + use_debug_redraw=kw_copy["use_debug_redraw"], + level=level, + ) # must apply after boolean. if use_recenter: - bpy.ops.object.origin_set({"selected_editable_objects": objects}, - type='ORIGIN_GEOMETRY', center='MEDIAN') + bpy.ops.object.origin_set( + {"selected_editable_objects": objects}, + type='ORIGIN_GEOMETRY', + center='MEDIAN', + ) #---------- # Recursion @@ -105,9 +109,10 @@ def main_object(context, obj, level, **kw): if recursion_chance_select == 'RANDOM': random.shuffle(objects_recurse_input) elif recursion_chance_select in {'SIZE_MIN', 'SIZE_MAX'}: - objects_recurse_input.sort(key=lambda ob_pair: - (Vector(ob_pair[1].bound_box[0]) - - Vector(ob_pair[1].bound_box[6])).length_squared) + objects_recurse_input.sort(key=lambda ob_pair: ( + Vector(ob_pair[1].bound_box[0]) - + Vector(ob_pair[1].bound_box[6]) + ).length_squared) if recursion_chance_select == 'SIZE_MAX': objects_recurse_input.reverse() elif recursion_chance_select in {'CURSOR_MIN', 'CURSOR_MAX'}: @@ -142,11 +147,12 @@ def main_object(context, obj, level, **kw): if level == 0: # import pdb; pdb.set_trace() if use_interior_vgroup or use_sharp_edges: - fracture_cell_setup.cell_fracture_interior_handle(objects, - use_interior_vgroup=use_interior_vgroup, - use_sharp_edges=use_sharp_edges, - use_sharp_edges_apply=use_sharp_edges_apply, - ) + fracture_cell_setup.cell_fracture_interior_handle( + objects, + use_interior_vgroup=use_interior_vgroup, + use_sharp_edges=use_sharp_edges, + use_sharp_edges_apply=use_sharp_edges_apply, + ) #-------------- # Scene Options @@ -247,168 +253,178 @@ class FractureCell(Operator): # ------------------------------------------------------------------------- # Source Options source: EnumProperty( - name="Source", - items=(('VERT_OWN', "Own Verts", "Use own vertices"), - ('VERT_CHILD', "Child Verts", "Use child object vertices"), - ('PARTICLE_OWN', "Own Particles", ("All particle systems of the " - "source object")), - ('PARTICLE_CHILD', "Child Particles", ("All particle systems of the " - "child objects")), - ('PENCIL', "Annotation Pencil", "Annotation Grease Pencil."), - ), - options={'ENUM_FLAG'}, - default={'PARTICLE_OWN'}, - ) + name="Source", + items=( + ('VERT_OWN', "Own Verts", "Use own vertices"), + ('VERT_CHILD', "Child Verts", "Use child object vertices"), + ('PARTICLE_OWN', "Own Particles", ( + "All particle systems of the " + "source object" + )), + ('PARTICLE_CHILD', "Child Particles", ( + "All particle systems of the " + "child objects" + )), + ('PENCIL', "Annotation Pencil", "Annotation Grease Pencil."), + ), + options={'ENUM_FLAG'}, + default={'PARTICLE_OWN'}, + ) source_limit: IntProperty( - name="Source Limit", - description="Limit the number of input points, 0 for unlimited", - min=0, max=5000, - default=100, - ) + name="Source Limit", + description="Limit the number of input points, 0 for unlimited", + min=0, max=5000, + default=100, + ) source_noise: FloatProperty( - name="Noise", - description="Randomize point distribution", - min=0.0, max=1.0, - default=0.0, - ) + name="Noise", + description="Randomize point distribution", + min=0.0, max=1.0, + default=0.0, + ) cell_scale: FloatVectorProperty( - name="Scale", - description="Scale Cell Shape", - size=3, - min=0.0, max=1.0, - default=(1.0, 1.0, 1.0), - ) + name="Scale", + description="Scale Cell Shape", + size=3, + min=0.0, max=1.0, + default=(1.0, 1.0, 1.0), + ) # ------------------------------------------------------------------------- # Recursion recursion: IntProperty( - name="Recursion", - description="Break shards recursively", - min=0, max=5000, - default=0, - ) + name="Recursion", + description="Break shards recursively", + min=0, max=5000, + default=0, + ) recursion_source_limit: IntProperty( - name="Source Limit", - description="Limit the number of input points, 0 for unlimited (applies to recursion only)", - min=0, max=5000, - default=8, - ) + name="Source Limit", + description="Limit the number of input points, 0 for unlimited (applies to recursion only)", + min=0, max=5000, + default=8, + ) recursion_clamp: IntProperty( - name="Clamp Recursion", - description="Finish recursion when this number of objects is reached (prevents recursing for extended periods of time), zero disables", - min=0, max=10000, - default=250, - ) + name="Clamp Recursion", + description=( + "Finish recursion when this number of objects is reached " + "(prevents recursing for extended periods of time), zero disables" + ), + min=0, max=10000, + default=250, + ) recursion_chance: FloatProperty( - name="Random Factor", - description="Likelihood of recursion", - min=0.0, max=1.0, - default=0.25, - ) + name="Random Factor", + description="Likelihood of recursion", + min=0.0, max=1.0, + default=0.25, + ) recursion_chance_select: EnumProperty( - name="Recurse Over", - items=(('RANDOM', "Random", ""), - ('SIZE_MIN', "Small", "Recursively subdivide smaller objects"), - ('SIZE_MAX', "Big", "Recursively subdivide bigger objects"), - ('CURSOR_MIN', "Cursor Close", "Recursively subdivide objects closer to the cursor"), - ('CURSOR_MAX', "Cursor Far", "Recursively subdivide objects farther from the cursor"), - ), - default='SIZE_MIN', - ) + name="Recurse Over", + items=( + ('RANDOM', "Random", ""), + ('SIZE_MIN', "Small", "Recursively subdivide smaller objects"), + ('SIZE_MAX', "Big", "Recursively subdivide bigger objects"), + ('CURSOR_MIN', "Cursor Close", "Recursively subdivide objects closer to the cursor"), + ('CURSOR_MAX', "Cursor Far", "Recursively subdivide objects farther from the cursor"), + ), + default='SIZE_MIN', + ) # ------------------------------------------------------------------------- # Mesh Data Options use_smooth_faces: BoolProperty( - name="Smooth Interior", - description="Smooth Faces of inner side", - default=False, - ) + name="Smooth Interior", + description="Smooth Faces of inner side", + default=False, + ) use_sharp_edges: BoolProperty( - name="Sharp Edges", - description="Set sharp edges when disabled", - default=True, - ) + name="Sharp Edges", + description="Set sharp edges when disabled", + default=True, + ) use_sharp_edges_apply: BoolProperty( - name="Apply Split Edge", - description="Split sharp hard edges", - default=True, - ) + name="Apply Split Edge", + description="Split sharp hard edges", + default=True, + ) use_data_match: BoolProperty( - name="Match Data", - description="Match original mesh materials and data layers", - default=True, - ) + name="Match Data", + description="Match original mesh materials and data layers", + default=True, + ) use_island_split: BoolProperty( - name="Split Islands", - description="Split disconnected meshes", - default=True, - ) + name="Split Islands", + description="Split disconnected meshes", + default=True, + ) margin: FloatProperty( - name="Margin", - description="Gaps for the fracture (gives more stable physics)", - min=0.0, max=1.0, - default=0.001, - ) + name="Margin", + description="Gaps for the fracture (gives more stable physics)", + min=0.0, max=1.0, + default=0.001, + ) material_index: IntProperty( - name="Material", - description="Material index for interior faces", - default=0, - ) + name="Material", + description="Material index for interior faces", + default=0, + ) use_interior_vgroup: BoolProperty( - name="Interior VGroup", - description="Create a vertex group for interior verts", - default=False, - ) + name="Interior VGroup", + description="Create a vertex group for interior verts", + default=False, + ) # ------------------------------------------------------------------------- # Physics Options mass_mode: EnumProperty( - name="Mass Mode", - items=(('VOLUME', "Volume", "Objects get part of specified mass based on their volume"), - ('UNIFORM', "Uniform", "All objects get the specified mass"), - ), - default='VOLUME', - ) + name="Mass Mode", + items=( + ('VOLUME', "Volume", "Objects get part of specified mass based on their volume"), + ('UNIFORM', "Uniform", "All objects get the specified mass"), + ), + default='VOLUME', + ) mass: FloatProperty( - name="Mass", - description="Mass to give created objects", - min=0.001, max=1000.0, - default=1.0, - ) + name="Mass", + description="Mass to give created objects", + min=0.001, max=1000.0, + default=1.0, + ) # ------------------------------------------------------------------------- # Object Options use_recenter: BoolProperty( - name="Recenter", - description="Recalculate the center points after splitting", - default=True, - ) + name="Recenter", + description="Recalculate the center points after splitting", + default=True, + ) use_remove_original: BoolProperty( - name="Remove Original", - description="Removes the parents used to create the shatter", - default=True, - ) + name="Remove Original", + description="Removes the parents used to create the shatter", + default=True, + ) # ------------------------------------------------------------------------- # Scene Options @@ -417,30 +433,32 @@ class FractureCell(Operator): # are setup in the scene. collection_name: StringProperty( - name="Collection", - description="Create objects in a collection " - "(use existing or create new)", - ) + name="Collection", + description=( + "Create objects in a collection " + "(use existing or create new)" + ), + ) # ------------------------------------------------------------------------- # Debug use_debug_points: BoolProperty( - name="Debug Points", - description="Create mesh data showing the points used for fracture", - default=False, - ) + name="Debug Points", + description="Create mesh data showing the points used for fracture", + default=False, + ) use_debug_redraw: BoolProperty( - name="Show Progress Realtime", - description="Redraw as fracture is done", - default=True, - ) + name="Show Progress Realtime", + description="Redraw as fracture is done", + default=True, + ) use_debug_bool: BoolProperty( - name="Debug Boolean", - description="Skip applying the boolean modifier", - default=False, - ) + name="Debug Boolean", + description="Skip applying the boolean modifier", + default=False, + ) def execute(self, context): keywords = self.as_keywords() # ignore=("blah",) diff --git a/oscurart_tools/render/material_overrides.py b/oscurart_tools/render/material_overrides.py index 8dbed0c1..4c3e936e 100644 --- a/oscurart_tools/render/material_overrides.py +++ b/oscurart_tools/render/material_overrides.py @@ -22,6 +22,13 @@ def ApplyOverrides(dummy): if ob.type == "MESH": if not ob.hide_viewport and not ob.hide_render: obDict.append([ob,[mat for mat in ob.data.materials]]) + if ob.type == "EMPTY": + if not ob.instance_collection == None: + for iob in ob.instance_collection.all_objects: + if iob.type == "MESH": + if not iob.hide_viewport and not iob.hide_render: + obDict.append([iob,[mat for mat in iob.data.materials]]) + for override in bpy.context.scene.ovlist: @@ -42,6 +49,13 @@ def ApplyOverrides(dummy): if not ob.hide_viewport and not ob.hide_render: for i,mat in enumerate(ob.data.materials): ob.data.materials[i] = bpy.data.materials[matClean] + if ob.type == "EMPTY": + if not ob.instance_collection == None: + for iob in ob.instance_collection.all_objects: + if iob.type == "MESH": + if not iob.hide_viewport and not iob.hide_render: + for i,mat in enumerate(iob.data.materials): + iob.data.materials[i] = bpy.data.materials[matClean] @persistent diff --git a/presets/operator/mesh.bolt_add/default.py b/presets/operator/mesh.bolt_add/default.py index 5c1e33dd..4bd250aa 100644 --- a/presets/operator/mesh.bolt_add/default.py +++ b/presets/operator/mesh.bolt_add/default.py @@ -10,8 +10,13 @@ op.bf_Shank_Dia = 3.0 op.bf_Phillips_Bit_Depth = 1.1431535482406616 op.bf_Allen_Bit_Depth = 1.5 op.bf_Allen_Bit_Flat_Distance = 2.5 +op.bf_Torx_Bit_Depth = 1.5 +op.bf_Torx_Size_Type = 'bf_Torx_T10' op.bf_Hex_Head_Height = 2.0 op.bf_Hex_Head_Flat_Distance = 5.5 +op.bf_12_Point_Head_Height = 3.0 +op.bf_12_Point_Head_Flat_Distance = 3.0 +op.bf_12_Point_Head_Flange_Dia = 5.72 op.bf_CounterSink_Head_Dia = 6.300000190734863 op.bf_Cap_Head_Height = 3.0 op.bf_Cap_Head_Dia = 5.5 @@ -27,3 +32,6 @@ op.bf_Root_Percent = 10 op.bf_Div_Count = 36 op.bf_Hex_Nut_Height = 2.4000000953674316 op.bf_Hex_Nut_Flat_Distance = 5.5 +op.bf_12_Point_Nut_Height = 3.0 +op.bf_12_Point_Nut_Flat_Distance = 3.0 +op.bf_12_Point_Nut_Flange_Dia = 5.72 diff --git a/presets/operator/mesh.bolt_add/m10.py b/presets/operator/mesh.bolt_add/m10.py index b92d009e..2f8b5866 100644 --- a/presets/operator/mesh.bolt_add/m10.py +++ b/presets/operator/mesh.bolt_add/m10.py @@ -10,8 +10,13 @@ op.bf_Shank_Dia = 10.0 op.bf_Phillips_Bit_Depth = 4.082691192626953 op.bf_Allen_Bit_Depth = 5.0 op.bf_Allen_Bit_Flat_Distance = 8.0 +op.bf_Torx_Bit_Depth = 5.0 +op.bf_Torx_Size_Type = 'bf_Torx_T50' op.bf_Hex_Head_Height = 6.400000095367432 op.bf_Hex_Head_Flat_Distance = 17.0 +op.bf_12_Point_Head_Height = 10.0 +op.bf_12_Point_Head_Flat_Distance = 10.0 +op.bf_12_Point_Head_Flange_Dia = 16.27 op.bf_CounterSink_Head_Dia = 20.0 op.bf_Cap_Head_Height = 10.0 op.bf_Cap_Head_Dia = 16.0 @@ -27,3 +32,6 @@ op.bf_Root_Percent = 10 op.bf_Div_Count = 36 op.bf_Hex_Nut_Height = 8.0 op.bf_Hex_Nut_Flat_Distance = 17.0 +op.bf_12_Point_Nut_Height = 10.0 +op.bf_12_Point_Nut_Flat_Distance = 10.0 +op.bf_12_Point_Nut_Flange_Dia = 16.27 diff --git a/presets/operator/mesh.bolt_add/m12.py b/presets/operator/mesh.bolt_add/m12.py index cbec7589..a5040619 100644 --- a/presets/operator/mesh.bolt_add/m12.py +++ b/presets/operator/mesh.bolt_add/m12.py @@ -10,8 +10,13 @@ op.bf_Shank_Dia = 12.0 op.bf_Phillips_Bit_Depth = 4.899229526519775 op.bf_Allen_Bit_Depth = 6.0 op.bf_Allen_Bit_Flat_Distance = 10.0 +op.bf_Torx_Bit_Depth = 6.0 +op.bf_Torx_Size_Type = 'bf_Torx_T55' op.bf_Hex_Head_Height = 7.5 op.bf_Hex_Head_Flat_Distance = 19.0 +op.bf_12_Point_Head_Height = 12.0 +op.bf_12_Point_Head_Flat_Distance = 12.0 +op.bf_12_Point_Head_Flange_Dia = 18.27 op.bf_CounterSink_Head_Dia = 22.0 op.bf_Cap_Head_Height = 12.0 op.bf_Cap_Head_Dia = 18.5 @@ -27,3 +32,6 @@ op.bf_Root_Percent = 10 op.bf_Div_Count = 36 op.bf_Hex_Nut_Height = 10.0 op.bf_Hex_Nut_Flat_Distance = 19.0 +op.bf_12_Point_Nut_Height = 12.0 +op.bf_12_Point_Nut_Flat_Distance = 12.0 +op.bf_12_Point_Nut_Flange_Dia = 18.27 diff --git a/presets/operator/mesh.bolt_add/m3.py b/presets/operator/mesh.bolt_add/m3.py index 5c1e33dd..4bd250aa 100644 --- a/presets/operator/mesh.bolt_add/m3.py +++ b/presets/operator/mesh.bolt_add/m3.py @@ -10,8 +10,13 @@ op.bf_Shank_Dia = 3.0 op.bf_Phillips_Bit_Depth = 1.1431535482406616 op.bf_Allen_Bit_Depth = 1.5 op.bf_Allen_Bit_Flat_Distance = 2.5 +op.bf_Torx_Bit_Depth = 1.5 +op.bf_Torx_Size_Type = 'bf_Torx_T10' op.bf_Hex_Head_Height = 2.0 op.bf_Hex_Head_Flat_Distance = 5.5 +op.bf_12_Point_Head_Height = 3.0 +op.bf_12_Point_Head_Flat_Distance = 3.0 +op.bf_12_Point_Head_Flange_Dia = 5.72 op.bf_CounterSink_Head_Dia = 6.300000190734863 op.bf_Cap_Head_Height = 3.0 op.bf_Cap_Head_Dia = 5.5 @@ -27,3 +32,6 @@ op.bf_Root_Percent = 10 op.bf_Div_Count = 36 op.bf_Hex_Nut_Height = 2.4000000953674316 op.bf_Hex_Nut_Flat_Distance = 5.5 +op.bf_12_Point_Nut_Height = 3.0 +op.bf_12_Point_Nut_Flat_Distance = 3.0 +op.bf_12_Point_Nut_Flange_Dia = 5.72 diff --git a/presets/operator/mesh.bolt_add/m4.py b/presets/operator/mesh.bolt_add/m4.py index a4349c5a..b5595d00 100644 --- a/presets/operator/mesh.bolt_add/m4.py +++ b/presets/operator/mesh.bolt_add/m4.py @@ -10,8 +10,13 @@ op.bf_Shank_Dia = 4.0 op.bf_Phillips_Bit_Depth = 1.6330764293670654 op.bf_Allen_Bit_Depth = 2.0 op.bf_Allen_Bit_Flat_Distance = 3.0 +op.bf_Torx_Bit_Depth = 2.0 +op.bf_Torx_Size_Type = 'bf_Torx_T20' op.bf_Hex_Head_Height = 2.799999952316284 op.bf_Hex_Head_Flat_Distance = 7.0 +op.bf_12_Point_Head_Height = 4.0 +op.bf_12_Point_Head_Flat_Distance = 4.0 +op.bf_12_Point_Head_Flange_Dia = 7.22 op.bf_CounterSink_Head_Dia = 9.399999618530273 op.bf_Cap_Head_Height = 4.0 op.bf_Cap_Head_Dia = 7.0 @@ -27,3 +32,6 @@ op.bf_Root_Percent = 10 op.bf_Div_Count = 36 op.bf_Hex_Nut_Height = 3.200000047683716 op.bf_Hex_Nut_Flat_Distance = 7.0 +op.bf_12_Point_Nut_Height = 4.0 +op.bf_12_Point_Nut_Flat_Distance = 4.0 +op.bf_12_Point_Nut_Flange_Dia = 7.22 diff --git a/presets/operator/mesh.bolt_add/m5.py b/presets/operator/mesh.bolt_add/m5.py index 7f53703c..d4485119 100644 --- a/presets/operator/mesh.bolt_add/m5.py +++ b/presets/operator/mesh.bolt_add/m5.py @@ -10,8 +10,13 @@ op.bf_Shank_Dia = 5.0 op.bf_Phillips_Bit_Depth = 1.9392783641815186 op.bf_Allen_Bit_Depth = 2.5 op.bf_Allen_Bit_Flat_Distance = 4.0 +op.bf_Torx_Bit_Depth = 2.5 +op.bf_Torx_Size_Type = 'bf_Torx_T25' op.bf_Hex_Head_Height = 3.5 op.bf_Hex_Head_Flat_Distance = 8.0 +op.bf_12_Point_Head_Height = 5.0 +op.bf_12_Point_Head_Flat_Distance = 5.0 +op.bf_12_Point_Head_Flange_Dia = 8.72 op.bf_CounterSink_Head_Dia = 10.399999618530273 op.bf_Cap_Head_Height = 5.0 op.bf_Cap_Head_Dia = 8.5 @@ -27,3 +32,6 @@ op.bf_Root_Percent = 10 op.bf_Div_Count = 36 op.bf_Hex_Nut_Height = 4.0 op.bf_Hex_Nut_Flat_Distance = 8.0 +op.bf_12_Point_Nut_Height = 5.0 +op.bf_12_Point_Nut_Flat_Distance = 5.0 +op.bf_12_Point_Nut_Flange_Dia = 8.72 diff --git a/presets/operator/mesh.bolt_add/m6.py b/presets/operator/mesh.bolt_add/m6.py index 225776be..01a8408d 100644 --- a/presets/operator/mesh.bolt_add/m6.py +++ b/presets/operator/mesh.bolt_add/m6.py @@ -10,8 +10,13 @@ op.bf_Shank_Dia = 6.0 op.bf_Phillips_Bit_Depth = 2.4496147632598877 op.bf_Allen_Bit_Depth = 3.0 op.bf_Allen_Bit_Flat_Distance = 5.0 +op.bf_Torx_Bit_Depth = 3.0 +op.bf_Torx_Size_Type = 'bf_Torx_T30' op.bf_Hex_Head_Height = 4.0 op.bf_Hex_Head_Flat_Distance = 10.0 +op.bf_12_Point_Head_Height = 6.0 +op.bf_12_Point_Head_Flat_Distance = 6.0 +op.bf_12_Point_Head_Flange_Dia = 10.22 op.bf_CounterSink_Head_Dia = 12.600000381469727 op.bf_Cap_Head_Height = 6.0 op.bf_Cap_Head_Dia = 10.0 @@ -27,3 +32,6 @@ op.bf_Root_Percent = 10 op.bf_Div_Count = 36 op.bf_Hex_Nut_Height = 5.0 op.bf_Hex_Nut_Flat_Distance = 10.0 +op.bf_12_Point_Nut_Height = 6.0 +op.bf_12_Point_Nut_Flat_Distance = 6.0 +op.bf_12_Point_Nut_Flange_Dia = 10.22 diff --git a/presets/operator/mesh.bolt_add/m8.py b/presets/operator/mesh.bolt_add/m8.py index 547cc719..a7493131 100644 --- a/presets/operator/mesh.bolt_add/m8.py +++ b/presets/operator/mesh.bolt_add/m8.py @@ -10,8 +10,13 @@ op.bf_Shank_Dia = 8.0 op.bf_Phillips_Bit_Depth = 3.266152858734131 op.bf_Allen_Bit_Depth = 4.0 op.bf_Allen_Bit_Flat_Distance = 6.0 +op.bf_Torx_Bit_Depth = 4.0 +op.bf_Torx_Size_Type = 'bf_Torx_T40' op.bf_Hex_Head_Height = 5.300000190734863 op.bf_Hex_Head_Flat_Distance = 13.0 +op.bf_12_Point_Head_Height = 8.0 +op.bf_12_Point_Head_Flat_Distance = 8.0 +op.bf_12_Point_Head_Flange_Dia = 13.27 op.bf_CounterSink_Head_Dia = 17.299999237060547 op.bf_Cap_Head_Height = 8.0 op.bf_Cap_Head_Dia = 13.5 @@ -27,3 +32,6 @@ op.bf_Root_Percent = 10 op.bf_Div_Count = 36 op.bf_Hex_Nut_Height = 6.5 op.bf_Hex_Nut_Flat_Distance = 13.0 +op.bf_12_Point_Nut_Height = 8.0 +op.bf_12_Point_Nut_Flat_Distance = 8.0 +op.bf_12_Point_Nut_Flange_Dia = 13.27 diff --git a/rigify/rigs/chain_rigs.py b/rigify/rigs/chain_rigs.py index fc070eb1..525626f0 100644 --- a/rigify/rigs/chain_rigs.py +++ b/rigify/rigs/chain_rigs.py @@ -36,9 +36,11 @@ class SimpleChainRig(BaseRig): def find_org_bones(self, bone): return [bone.name] + connected_children_names(self.obj, bone.name) + min_chain_length = 2 + def initialize(self): - if len(self.bones.org) <= 1: - self.raise_error("Input to rig type must be a chain of 2 or more bones.") + if len(self.bones.org) < self.min_chain_length: + self.raise_error("Input to rig type must be a chain of {} or more bones.", self.min_chain_length) def parent_bones(self): self.rig_parent_bone = self.get_bone_parent(self.bones.org[0]) diff --git a/rigify/rigs/spines/spine_rigs.py b/rigify/rigs/spines/spine_rigs.py index 070a6bd3..d23676d5 100644 --- a/rigify/rigs/spines/spine_rigs.py +++ b/rigify/rigs/spines/spine_rigs.py @@ -40,10 +40,10 @@ class BaseSpineRig(TweakChainRig): """ bbone_segments = 8 + min_chain_length = 3 def initialize(self): - if len(self.bones.org) < 3: - self.raise_error("Input to rig type must be a chain of 3 or more bones.") + super().initialize() self.use_torso_pivot = self.params.make_custom_pivot self.length = sum([self.get_bone(b).length for b in self.bones.org]) diff --git a/rigify/rigs/spines/super_head.py b/rigify/rigs/spines/super_head.py index 15f011f7..b1ba9ee0 100644 --- a/rigify/rigs/spines/super_head.py +++ b/rigify/rigs/spines/super_head.py @@ -40,11 +40,13 @@ class Rig(BaseHeadTailRig): """ use_connect_reverse = False + min_chain_length = 1 def initialize(self): super().initialize() self.long_neck = len(self.bones.org) > 3 + self.has_neck = len(self.bones.org) > 1 #################################################### # BONES @@ -78,8 +80,11 @@ class Rig(BaseHeadTailRig): orgs = self.bones.org ctrl = self.bones.ctrl - ctrl.neck = self.make_neck_control_bone(orgs[0], 'neck', orgs[-1]) + if self.has_neck: + ctrl.neck = self.make_neck_control_bone(orgs[0], 'neck', orgs[-1]) + ctrl.head = self.make_head_control_bone(orgs[-1], 'head') + if self.long_neck: ctrl.neck_bend = self.make_neck_bend_control_bone(orgs[0], 'neck_bend', ctrl.neck) @@ -118,14 +123,16 @@ class Rig(BaseHeadTailRig): def parent_control_chain(self): ctrl = self.bones.ctrl mch = self.bones.mch - self.set_bone_parent(ctrl.neck, mch.rot_neck) + if self.has_neck: + self.set_bone_parent(ctrl.neck, mch.rot_neck) self.set_bone_parent(ctrl.head, mch.rot_head) if self.long_neck: self.set_bone_parent(ctrl.neck_bend, mch.stretch) @stage.configure_bones def configure_control_chain(self): - self.configure_control_bone(0, self.bones.ctrl.neck, self.bones.org[0]) + if self.has_neck: + self.configure_control_bone(0, self.bones.ctrl.neck, self.bones.org[0]) self.configure_control_bone(2, self.bones.ctrl.head, self.bones.org[-1]) if self.long_neck: self.configure_control_bone(1, self.bones.ctrl.neck_bend, self.bones.org[0]) @@ -133,7 +140,8 @@ class Rig(BaseHeadTailRig): @stage.generate_widgets def make_control_widgets(self): ctrl = self.bones.ctrl - self.make_neck_widget(ctrl.neck) + if self.has_neck: + self.make_neck_widget(ctrl.neck) self.make_head_widget(ctrl.head) if self.long_neck: self.make_neck_bend_widget(ctrl.neck_bend) @@ -178,9 +186,10 @@ class Rig(BaseHeadTailRig): orgs = self.bones.org mch = self.bones.mch - mch.rot_neck = self.make_mch_follow_bone(orgs[0], 'neck', 0.5, copy_scale=True) + if self.has_neck: + mch.rot_neck = self.make_mch_follow_bone(orgs[0], 'neck', 0.5, copy_scale=True) + mch.stretch = self.make_mch_stretch_bone(orgs[0], 'STR-neck', orgs[-1]) mch.rot_head = self.make_mch_follow_bone(orgs[-1], 'head', 0.0, copy_scale=True) - mch.stretch = self.make_mch_stretch_bone(orgs[0], 'STR-neck', orgs[-1]) def make_mch_stretch_bone(self, org, name, org_head): name = self.copy_bone(org, make_derived_name(name, 'mch'), parent=False) @@ -189,13 +198,17 @@ class Rig(BaseHeadTailRig): @stage.parent_bones def parent_mch_control_bones(self): - self.set_bone_parent(self.bones.mch.rot_neck, self.rig_parent_bone) - self.set_bone_parent(self.bones.mch.rot_head, self.bones.ctrl.neck) - self.set_bone_parent(self.bones.mch.stretch, self.bones.ctrl.neck) + if self.has_neck: + self.set_bone_parent(self.bones.mch.rot_neck, self.rig_parent_bone) + self.set_bone_parent(self.bones.mch.rot_head, self.bones.ctrl.neck) + self.set_bone_parent(self.bones.mch.stretch, self.bones.ctrl.neck) + else: + self.set_bone_parent(self.bones.mch.rot_head, self.rig_parent_bone) @stage.rig_bones def rig_mch_control_bones(self): - self.rig_mch_stretch_bone(self.bones.mch.stretch, self.bones.ctrl.head) + if self.has_neck: + self.rig_mch_stretch_bone(self.bones.mch.stretch, self.bones.ctrl.head) def rig_mch_stretch_bone(self, mch, head): self.make_constraint(mch, 'DAMPED_TRACK', head) @@ -299,14 +312,19 @@ class Rig(BaseHeadTailRig): def make_tweak_chain(self): orgs = self.bones.org self.bones.ctrl.tweak = map_list(self.make_tweak_bone, count(0), orgs[0:-1]) + if not self.has_neck: + self.check_connect_tweak(orgs[0]) @stage.parent_bones def parent_tweak_chain(self): ctrl = self.bones.ctrl mch = self.bones.mch - for args in zip(ctrl.tweak, [ctrl.neck, *mch.chain]): - self.set_bone_parent(*args) + if self.has_neck: + for args in zip(ctrl.tweak, [ctrl.neck, *mch.chain]): + self.set_bone_parent(*args) + elif self.connected_tweak: + self.set_bone_parent(self.connected_tweak, ctrl.head) @stage.rig_bones def generate_neck_tweak_widget(self): @@ -333,7 +351,11 @@ class Rig(BaseHeadTailRig): @stage.rig_bones def rig_org_chain(self): - tweaks = self.bones.ctrl.tweak + [self.bones.ctrl.head] + if self.has_neck: + tweaks = self.bones.ctrl.tweak + [self.bones.ctrl.head] + else: + tweaks = [self.connected_tweak or self.bones.ctrl.head] + for args in zip(count(0), self.bones.org, tweaks, tweaks[1:] + [None]): self.rig_org_bone(*args) diff --git a/space_view3d_pie_menus/pie_proportional_menu.py b/space_view3d_pie_menus/pie_proportional_menu.py index 10575a55..009bf8a8 100644 --- a/space_view3d_pie_menus/pie_proportional_menu.py +++ b/space_view3d_pie_menus/pie_proportional_menu.py @@ -19,7 +19,7 @@ # <pep8 compliant> bl_info = { - "name": "Hotkey: 'O'", + "name": "Hotkey: 'Shift O'", "description": "Proportional Object/Edit Tools", "author": "pitiwazou, meta-androcto", "version": (0, 1, 1), diff --git a/viewport_vr_preview.py b/viewport_vr_preview.py index 1f8bbb64..ac191b56 100644 --- a/viewport_vr_preview.py +++ b/viewport_vr_preview.py @@ -33,7 +33,7 @@ from bpy.app.handlers import persistent bl_info = { "name": "VR Scene Inspection", "author": "Julian Eisel (Severin)", - "version": (0, 1, 0), + "version": (0, 2, 0), "blender": (2, 83, 8), "location": "3D View > Sidebar > VR", "description": ("View the viewport with virtual reality glasses " @@ -136,11 +136,16 @@ def xr_landmark_camera_object_poll(self, object): def xr_landmark_active_update(self, context): + wm = context.window_manager + xr_landmark_active_type_update(self, context) xr_landmark_active_camera_update(self, context) xr_landmark_active_base_pose_location_update(self, context) xr_landmark_active_base_pose_angle_update(self, context) + if wm.xr_session_state: + wm.xr_session_state.reset_to_base_pose(context) + class VRLandmark(bpy.types.PropertyGroup): name: bpy.props.StringProperty( @@ -214,7 +219,9 @@ class VIEW3D_UL_vr_landmarks(bpy.types.UIList): layout.prop(landmark, "name", text="") - icon = 'SOLO_ON' if (index == landmark_active_idx) else 'SOLO_OFF' + icon = ( + 'RADIOBUT_ON' if (index == landmark_active_idx) else 'RADIOBUT_OFF' + ) props = layout.operator( "view3d.vr_landmark_activate", text="", icon=icon) props.index = index @@ -269,10 +276,9 @@ class VIEW3D_PT_vr_session_view(bpy.types.Panel): layout.use_property_split = True layout.use_property_decorate = False # No animation. - layout.prop(session_settings, "show_floor", text="Floor") - layout.prop(session_settings, "show_annotation", text="Annotations") - - layout.separator() + col = layout.column(align=True, heading="Show") + col.prop(session_settings, "show_floor", text="Floor") + col.prop(session_settings, "show_annotation", text="Annotations") col = layout.column(align=True) col.prop(session_settings, "clip_start", text="Clip Start") @@ -289,8 +295,7 @@ class VIEW3D_PT_vr_session(bpy.types.Panel): layout = self.layout session_settings = context.window_manager.xr_session_settings - layout.use_property_split = True - layout.use_property_decorate = False # No animation. + layout.use_property_split = False is_session_running = bpy.types.XrSessionState.is_running(context) @@ -381,6 +386,13 @@ class VIEW3D_PT_vr_viewport_feedback(bpy.types.Panel): layout = self.layout view3d = context.space_data + col = layout.column(align=True) + col.label(icon='ERROR', text="Note:") + col.label(text="Settings here may have a significant") + col.label(text="performance impact!") + + layout.separator() + layout.prop(view3d.shading, "vr_show_virtual_camera") layout.prop(view3d, "mirror_xr_session") |