Update Mesh Edit Tools: T50680
This commit is contained in:
parent
f53017167b
commit
ba89ec341c
|
@ -63,6 +63,7 @@ if "bpy" in locals():
|
|||
importlib.reload(mesh_selection_topokit)
|
||||
importlib.reload(mesh_info_select)
|
||||
importlib.reload(mesh_extrude_and_reshape)
|
||||
importlib.reload(mesh_check)
|
||||
|
||||
else:
|
||||
from . import face_inset_fillet
|
||||
|
@ -83,6 +84,7 @@ else:
|
|||
from . import vfe_specials
|
||||
from . import mesh_help
|
||||
from . import mesh_extrude_and_reshape
|
||||
from . import mesh_check
|
||||
|
||||
from .mesh_select_tools import mesh_select_by_direction
|
||||
from .mesh_select_tools import mesh_select_by_edge_length
|
||||
|
@ -93,7 +95,11 @@ else:
|
|||
from .mesh_select_tools import mesh_selection_topokit
|
||||
from .mesh_select_tools import mesh_info_select
|
||||
|
||||
from . icons.icons import load_icons
|
||||
|
||||
import bpy
|
||||
import bmesh
|
||||
from bpy.props import EnumProperty
|
||||
from bpy.types import (
|
||||
Menu,
|
||||
Panel,
|
||||
|
@ -316,6 +322,11 @@ class EditToolsPanel(Panel):
|
|||
row.operator("mesh.extra_tools_help",
|
||||
icon="LAYER_USED").help_ids = "split_solidify"
|
||||
|
||||
row = layout.split(0.8, align=True)
|
||||
row.operator("mesh.add_faces_to_object", "Shape Extrude")
|
||||
row.operator("mesh.extra_tools_help",
|
||||
icon="LAYER_USED").help_ids = "pkhg_faces"
|
||||
|
||||
# Utils options
|
||||
box1 = self.layout.box()
|
||||
col = box1.column(align=True)
|
||||
|
@ -353,11 +364,64 @@ class EditToolsPanel(Panel):
|
|||
|
||||
row = layout.row(align=True)
|
||||
row.operator("mesh.select_vert_edge_face_index",
|
||||
icon="VERTEXSEL", text="Vert Index").select_type = 'VERT'
|
||||
row.operator("mesh.select_vert_edge_face_index",
|
||||
icon="EDGESEL", text="Edge Index").select_type = 'EDGE'
|
||||
row.operator("mesh.select_vert_edge_face_index",
|
||||
icon="FACESEL", text="Face Index").select_type = 'FACE'
|
||||
icon="VERTEXSEL", text="Select By Index").select_type = 'VERT'
|
||||
|
||||
layout = self.layout
|
||||
icons = load_icons()
|
||||
tris = icons.get("triangles")
|
||||
ngons = icons.get("ngons")
|
||||
|
||||
|
||||
mesh_check = context.window_manager.mesh_check
|
||||
|
||||
layout.prop(mesh_check, "mesh_check_use")
|
||||
|
||||
if mesh_check.mesh_check_use:
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row()
|
||||
row.operator("object.face_type_select", text="Tris", icon_value=tris.icon_id).face_type = 'tris'
|
||||
row.operator("object.face_type_select", text="Ngons",icon_value=ngons.icon_id).face_type = 'ngons'
|
||||
row = layout.row()
|
||||
row.prop(mesh_check, "display_faces", text="Display Faces")
|
||||
if mesh_check.display_faces:
|
||||
row = layout.row()
|
||||
row.prop(mesh_check, "edge_width")
|
||||
row = layout.row()
|
||||
row.prop(mesh_check, "custom_tri_color",text="Tris color" )
|
||||
row = layout.row()
|
||||
row.prop(mesh_check, "custom_ngons_color")
|
||||
row = layout.row()
|
||||
row.prop(mesh_check, "face_opacity")
|
||||
if bpy.context.object.mode == 'EDIT':
|
||||
obj = bpy.context.object
|
||||
me = obj.data
|
||||
bm = bmesh.from_edit_mesh(me)
|
||||
|
||||
info_str = ""
|
||||
tris = ngons = 0
|
||||
|
||||
for f in bm.faces:
|
||||
|
||||
v = len(f.verts)
|
||||
if v == 3:
|
||||
tris += 1
|
||||
elif v > 4:
|
||||
ngons += 1
|
||||
|
||||
bmesh.update_edit_mesh(me)
|
||||
info_str = " Ngons: %i Tris: %i" % (ngons, tris)
|
||||
|
||||
split = layout.split(percentage=0.1)
|
||||
split.separator()
|
||||
split.label(info_str, icon='MESH_DATA')
|
||||
|
||||
if context.mode == 'EDIT_MESH' and not context.space_data.use_occlude_geometry:
|
||||
split = layout.split(percentage=0.1)
|
||||
split.separator()
|
||||
split2 = split.split()
|
||||
row = split2.row()
|
||||
row.prop(mesh_check, "finer_lines_behind_use")
|
||||
|
||||
|
||||
# ********** Edit Multiselect **********
|
||||
|
@ -719,6 +783,7 @@ def register():
|
|||
mesh_pen_tool.register()
|
||||
vfe_specials.register()
|
||||
mesh_extrude_and_reshape.register()
|
||||
mesh_check.register()
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
# Register Scene Properties
|
||||
|
@ -742,6 +807,7 @@ def unregister():
|
|||
mesh_pen_tool.unregister()
|
||||
vfe_specials.unregister()
|
||||
mesh_extrude_and_reshape.unregister()
|
||||
mesh_check.unregister()
|
||||
|
||||
del bpy.types.Scene.mesh_extra_tools
|
||||
del bpy.types.Object.tkkey
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import os
|
||||
import bpy
|
||||
import bpy.utils.previews
|
||||
|
||||
mesh_check_icon_collections = {}
|
||||
mesh_check_icons_loaded = False
|
||||
|
||||
def load_icons():
|
||||
global mesh_check_icon_collections
|
||||
global mesh_check_icons_loaded
|
||||
|
||||
if mesh_check_icons_loaded: return mesh_check_icon_collections["main"]
|
||||
|
||||
custom_icons = bpy.utils.previews.new()
|
||||
|
||||
icons_dir = os.path.join(os.path.dirname(__file__))
|
||||
|
||||
custom_icons.load("ngons", os.path.join(icons_dir, "ngon.png"), 'IMAGE')
|
||||
custom_icons.load("triangles", os.path.join(icons_dir, "triangle.png"), 'IMAGE')
|
||||
|
||||
mesh_check_icon_collections["main"] = custom_icons
|
||||
mesh_check_icons_loaded = True
|
||||
|
||||
return mesh_check_icon_collections["main"]
|
||||
|
||||
def clear_icons():
|
||||
global mesh_check_icons_loaded
|
||||
for icon in mesh_check_icon_collections.values():
|
||||
bpy.utils.previews.remove(icon)
|
||||
mesh_check_icon_collections.clear()
|
||||
mesh_check_icons_loaded = False
|
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
|
@ -0,0 +1,448 @@
|
|||
bl_info = {"name": "Mesh Check BGL edition",
|
||||
"description": "Display the triangles and ngons of the mesh",
|
||||
"author": "Pistiwique",
|
||||
"version": (1, 0, 0),
|
||||
"blender": (2, 75, 0),
|
||||
"location": "3D View(s) -> Properties -> Shading",
|
||||
"category": "3D View"
|
||||
}
|
||||
|
||||
|
||||
|
||||
import bpy
|
||||
from bgl import (
|
||||
glBegin,
|
||||
glLineWidth,
|
||||
glColor3f,
|
||||
glColor4f,
|
||||
glVertex3f,
|
||||
glEnd,
|
||||
GL_LINES,
|
||||
glEnable,
|
||||
glDisable,
|
||||
GL_DEPTH_TEST,
|
||||
GL_BLEND,
|
||||
GL_POLYGON
|
||||
)
|
||||
|
||||
from mathutils.geometry import tessellate_polygon as tessellate
|
||||
import bmesh
|
||||
from mathutils import Vector
|
||||
|
||||
from bpy.props import EnumProperty, PointerProperty
|
||||
|
||||
mesh_check_handle = []
|
||||
draw_enabled = [False]
|
||||
edge_width = [1.0]
|
||||
face_opacity = [0.2]
|
||||
edges_tri_color = [(1.0, 1.0, 0.0, 1)]
|
||||
faces_tri_color = [(1.0, 1.0, 0.0, face_opacity[0])]
|
||||
edges_ngons_color = [(1.0, 0.0, 0.0, 1.0)]
|
||||
faces_ngons_color = [(1.0, 0.0, 0.0, face_opacity[0])]
|
||||
bm_old = [None]
|
||||
finer_lines = [False]
|
||||
|
||||
|
||||
def draw_poly(points):
|
||||
for i in range(len(points)):
|
||||
glVertex3f(points[i][0], points[i][1], points[i][2])
|
||||
|
||||
|
||||
def mesh_check_draw_callback():
|
||||
obj = bpy.context.object
|
||||
if obj and obj.type == 'MESH':
|
||||
if draw_enabled[0]:
|
||||
mesh = obj.data
|
||||
matrix_world = obj.matrix_world
|
||||
|
||||
glLineWidth(edge_width[0])
|
||||
|
||||
if bpy.context.mode == 'EDIT_MESH':
|
||||
|
||||
use_occlude = True
|
||||
|
||||
if bm_old[0] is None or not bm_old[0].is_valid:
|
||||
bm = bm_old[0] = bmesh.from_edit_mesh(mesh)
|
||||
|
||||
else:
|
||||
bm = bm_old[0]
|
||||
|
||||
no_depth = not bpy.context.space_data.use_occlude_geometry
|
||||
|
||||
if no_depth:
|
||||
glDisable(GL_DEPTH_TEST)
|
||||
|
||||
use_occlude = False
|
||||
|
||||
if finer_lines[0]:
|
||||
glLineWidth(edge_width[0]/4.0)
|
||||
use_occlude = True
|
||||
|
||||
for face in bm.faces:
|
||||
if len([verts for verts in face.verts]) == 3:
|
||||
faces = [matrix_world*vert.co for vert in face.verts]
|
||||
glColor4f(*faces_tri_color[0])
|
||||
glEnable(GL_BLEND)
|
||||
glBegin(GL_POLYGON)
|
||||
draw_poly(faces)
|
||||
glEnd()
|
||||
|
||||
for edge in face.edges:
|
||||
if edge.is_valid:
|
||||
edges = [matrix_world*vert.co for vert in edge.verts]
|
||||
glColor4f(*edges_tri_color[0])
|
||||
glBegin(GL_LINES)
|
||||
draw_poly(edges)
|
||||
glEnd()
|
||||
|
||||
elif len([verts for verts in face.verts]) > 4:
|
||||
new_faces = []
|
||||
faces = []
|
||||
coords = [v.co for v in face.verts]
|
||||
indices = [v.index for v in face.verts]
|
||||
for pol in tessellate([coords]):
|
||||
new_faces.append([indices[i] for i in pol])
|
||||
|
||||
for f in new_faces:
|
||||
faces.append([((matrix_world*bm.verts[i].co)[0]+face.normal.x*0.001,
|
||||
(matrix_world*bm.verts[i].co)[1]+face.normal.y*0.001,
|
||||
(matrix_world*bm.verts[i].co)[2]+face.normal.z*0.001)
|
||||
for i in f])
|
||||
|
||||
for f in faces:
|
||||
glColor4f(*faces_ngons_color[0])
|
||||
glEnable(GL_BLEND)
|
||||
glBegin(GL_POLYGON)
|
||||
draw_poly(f)
|
||||
glEnd()
|
||||
|
||||
for edge in face.edges:
|
||||
if edge.is_valid:
|
||||
edges = [matrix_world*vert.co for vert in edge.verts]
|
||||
glColor4f(*edges_ngons_color[0])
|
||||
glBegin(GL_LINES)
|
||||
draw_poly(edges)
|
||||
glEnd()
|
||||
|
||||
glDisable(GL_BLEND)
|
||||
glColor4f(0.0, 0.0, 0.0, 1.0)
|
||||
glLineWidth(edge_width[0])
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
|
||||
if use_occlude:
|
||||
|
||||
for face in bm.faces:
|
||||
if len([verts for verts in face.verts]) == 3:
|
||||
faces = []
|
||||
for vert in face.verts:
|
||||
vert_face = matrix_world*vert.co
|
||||
faces.append((vert_face[0]+face.normal.x*0.001, vert_face[1]+face.normal.y*0.001, vert_face[2]+face.normal.z*0.001))
|
||||
|
||||
glColor4f(*faces_tri_color[0])
|
||||
glEnable(GL_BLEND)
|
||||
glBegin(GL_POLYGON)
|
||||
draw_poly(faces)
|
||||
glEnd()
|
||||
|
||||
for edge in face.edges:
|
||||
if edge.is_valid:
|
||||
edges = []
|
||||
for vert in edge.verts:
|
||||
vert_edge = matrix_world*vert.co
|
||||
edges.append((vert_edge[0]+face.normal.x*0.001, vert_edge[1]+face.normal.y*0.001, vert_edge[2]+face.normal.z*0.001))
|
||||
glColor4f(*edges_tri_color[0])
|
||||
glBegin(GL_LINES)
|
||||
draw_poly(edges)
|
||||
glEnd()
|
||||
|
||||
elif len([verts for verts in face.verts]) > 4:
|
||||
new_faces = []
|
||||
faces = []
|
||||
coords = [v.co for v in face.verts]
|
||||
indices = [v.index for v in face.verts]
|
||||
for pol in tessellate([coords]):
|
||||
new_faces.append([indices[i] for i in pol])
|
||||
|
||||
for f in new_faces:
|
||||
faces.append([((matrix_world*bm.verts[i].co)[0]+face.normal.x*0.001,
|
||||
(matrix_world*bm.verts[i].co)[1]+face.normal.y*0.001,
|
||||
(matrix_world*bm.verts[i].co)[2]+face.normal.z*0.001)
|
||||
for i in f])
|
||||
|
||||
for f in faces:
|
||||
glColor4f(*faces_ngons_color[0])
|
||||
glEnable(GL_BLEND)
|
||||
glBegin(GL_POLYGON)
|
||||
draw_poly(f)
|
||||
glEnd()
|
||||
|
||||
for edge in face.edges:
|
||||
if edge.is_valid:
|
||||
edges = []
|
||||
for vert in edge.verts:
|
||||
vert_edge = matrix_world*vert.co
|
||||
edges.append((vert_edge[0]+face.normal.x*0.001, vert_edge[1]+face.normal.y*0.001, vert_edge[2]+face.normal.z*0.001))
|
||||
glColor4f(*edges_ngons_color[0])
|
||||
glBegin(GL_LINES)
|
||||
draw_poly(edges)
|
||||
glEnd()
|
||||
|
||||
|
||||
glDisable(GL_BLEND)
|
||||
glColor4f(0.0, 0.0, 0.0, 1.0)
|
||||
|
||||
|
||||
|
||||
def updateBGLData(self, context):
|
||||
if self.mesh_check_use and self.display_faces:
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
draw_enabled[0] = True
|
||||
edge_width[0] = self.edge_width
|
||||
finer_lines[0] = self.finer_lines_behind_use
|
||||
face_opacity[0] = self.face_opacity
|
||||
edges_tri_color[0] = (self.custom_tri_color[0],
|
||||
self.custom_tri_color[1],
|
||||
self.custom_tri_color[2],
|
||||
1)
|
||||
faces_tri_color[0] = (self.custom_tri_color[0],
|
||||
self.custom_tri_color[1],
|
||||
self.custom_tri_color[2],
|
||||
self.face_opacity)
|
||||
edges_ngons_color[0] = (self.custom_ngons_color[0],
|
||||
self.custom_ngons_color[1],
|
||||
self.custom_ngons_color[2],
|
||||
1)
|
||||
|
||||
faces_ngons_color[0] = (self.custom_ngons_color[0],
|
||||
self.custom_ngons_color[1],
|
||||
self.custom_ngons_color[2],
|
||||
self.face_opacity)
|
||||
|
||||
return
|
||||
|
||||
draw_enabled[0] = False
|
||||
|
||||
|
||||
class FaceTypeSelect(bpy.types.Operator):
|
||||
bl_idname = "object.face_type_select"
|
||||
bl_label = "Face type select"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
face_type = bpy.props.EnumProperty(
|
||||
items=(('tris', 'Tris', ""),
|
||||
('ngons', 'Ngons', "")),
|
||||
default='ngons'
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object is not None and context.active_object.type == 'MESH'
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
context.tool_settings.mesh_select_mode=(False, False, True)
|
||||
|
||||
if self.face_type == "tris":
|
||||
bpy.ops.mesh.select_face_by_sides(number=3, type='EQUAL')
|
||||
else:
|
||||
bpy.ops.mesh.select_face_by_sides(number=4, type='GREATER')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class MeshCheckCollectionGroup(bpy.types.PropertyGroup):
|
||||
mesh_check_use = bpy.props.BoolProperty(
|
||||
name="Mesh Check",
|
||||
description="Display Mesh Check options",
|
||||
default=False,
|
||||
update=updateBGLData
|
||||
)
|
||||
|
||||
|
||||
display_faces = bpy.props.BoolProperty(
|
||||
name="Display Faces",
|
||||
description="Use BGL to display ngons en tris of the mesh",
|
||||
default=False,
|
||||
update=updateBGLData
|
||||
)
|
||||
|
||||
edge_width = bpy.props.FloatProperty(
|
||||
name="Width",
|
||||
description="Edges width in pixels",
|
||||
min=1.0,
|
||||
max=10.0,
|
||||
default=3.0,
|
||||
subtype='PIXEL',
|
||||
update=updateBGLData)
|
||||
|
||||
finer_lines_behind_use = bpy.props.BoolProperty(
|
||||
name="Finer Lines behind",
|
||||
description="Display partially hidden edges finer in non-occlude mode",
|
||||
default=True,
|
||||
update=updateBGLData)
|
||||
|
||||
custom_tri_color = bpy.props.FloatVectorProperty(
|
||||
name="Tri Color",
|
||||
description="Custom color for the triangles",
|
||||
min=0.0,
|
||||
max=1.0,
|
||||
default=(1.0, 1.0, 0.0),
|
||||
size=3,
|
||||
subtype='COLOR',
|
||||
update=updateBGLData)
|
||||
|
||||
custom_ngons_color = bpy.props.FloatVectorProperty(
|
||||
name="Ngons Color",
|
||||
description="custom color for the ngons",
|
||||
min=0.0,
|
||||
max=1.0,
|
||||
default=(1.0, 0.0, 0.0),
|
||||
size=3,
|
||||
subtype='COLOR',
|
||||
update=updateBGLData)
|
||||
|
||||
face_opacity = bpy.props.FloatProperty(
|
||||
name="Face Opacity",
|
||||
description="Opacity of the color for the face",
|
||||
min=0.0,
|
||||
max=1.0,
|
||||
default=0.2,
|
||||
subtype='FACTOR',
|
||||
update=updateBGLData)
|
||||
|
||||
class DATA_OP_facetype_select(bpy.types.Operator):
|
||||
"""Select all faces of a certain type"""
|
||||
bl_idname = "data.facetype_select"
|
||||
bl_label = "Select by face type"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
select_mode = bpy.props.StringProperty()
|
||||
face_type = EnumProperty(name="Select faces:",
|
||||
items=(("3", "Triangles", "Faces made up of 3 vertices"),
|
||||
("5", "Ngons", "Faces made up of 5 and more vertices")),
|
||||
default="5")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object is not None and context.object.type == 'MESH'
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='DESELECT')
|
||||
if tuple(bpy.context.tool_settings.mesh_select_mode) == (True, False, False):
|
||||
self.select_mode = "VERT"
|
||||
elif tuple(bpy.context.tool_settings.mesh_select_mode) == (False, True, False):
|
||||
self.select_mode = "EDGE"
|
||||
elif tuple(bpy.context.tool_settings.mesh_select_mode) == (False, False, True):
|
||||
self.select_mode = "FACE"
|
||||
context.tool_settings.mesh_select_mode = (False, False, True)
|
||||
if self.face_type == "3":
|
||||
bpy.ops.mesh.select_face_by_sides(number=3, type='EQUAL')
|
||||
bpy.ops.mesh.select_mode(type=self.select_mode)
|
||||
else:
|
||||
bpy.ops.mesh.select_face_by_sides(number=4, type='GREATER')
|
||||
bpy.ops.mesh.select_mode(type=self.select_mode)
|
||||
return {'FINISHED'}
|
||||
|
||||
def displayMeshCheckPanel(self, context):
|
||||
layout = self.layout
|
||||
icons = load_icons()
|
||||
tris = icons.get("triangles")
|
||||
ngons = icons.get("ngons")
|
||||
|
||||
|
||||
mesh_check = context.window_manager.mesh_check
|
||||
|
||||
layout.prop(mesh_check, "mesh_check_use")
|
||||
|
||||
if mesh_check.mesh_check_use:
|
||||
split = layout.split(percentage=0.1)
|
||||
split.separator()
|
||||
split2 = split.split()
|
||||
row = split2.row(align=True)
|
||||
row.operator("object.face_type_select", text="Tris", icon_value=tris.icon_id).face_type = 'tris'
|
||||
row.operator("object.face_type_select", text="Ngons",icon_value=ngons.icon_id).face_type = 'ngons'
|
||||
split = layout.split(percentage=0.1)
|
||||
split.separator()
|
||||
split.prop(mesh_check, "display_faces", text="Display Faces")
|
||||
if mesh_check.display_faces:
|
||||
split = layout.split(percentage=0.1)
|
||||
split.separator()
|
||||
split2 = split.split()
|
||||
row = split2.row(align=True)
|
||||
row.prop(mesh_check, "edge_width")
|
||||
split = layout.split(percentage=0.1)
|
||||
split.separator()
|
||||
split2 = split.split()
|
||||
row = split2.row()
|
||||
row.prop(mesh_check, "custom_tri_color",text="Tris color" )
|
||||
split = layout.split(percentage=0.1)
|
||||
split.separator()
|
||||
split2 = split.split()
|
||||
row = split2.row()
|
||||
row.prop(mesh_check, "custom_ngons_color")
|
||||
split = layout.split(percentage=0.1)
|
||||
split.separator()
|
||||
split2 = split.split()
|
||||
row = split2.row()
|
||||
row.prop(mesh_check, "face_opacity")
|
||||
if bpy.context.object.mode == 'EDIT':
|
||||
obj = bpy.context.object
|
||||
me = obj.data
|
||||
bm = bmesh.from_edit_mesh(me)
|
||||
|
||||
info_str = ""
|
||||
tris = ngons = 0
|
||||
|
||||
for f in bm.faces:
|
||||
|
||||
v = len(f.verts)
|
||||
if v == 3:
|
||||
tris += 1
|
||||
elif v > 4:
|
||||
ngons += 1
|
||||
|
||||
bmesh.update_edit_mesh(me)
|
||||
info_str = " Ngons: %i Tris: %i" % (ngons, tris)
|
||||
|
||||
split = layout.split(percentage=0.1)
|
||||
split.separator()
|
||||
split.label(info_str, icon='MESH_DATA')
|
||||
|
||||
if context.mode == 'EDIT_MESH' and not context.space_data.use_occlude_geometry:
|
||||
split = layout.split(percentage=0.1)
|
||||
split.separator()
|
||||
split2 = split.split()
|
||||
row = split2.row()
|
||||
row.prop(mesh_check, "finer_lines_behind_use")
|
||||
|
||||
|
||||
classes = (
|
||||
FaceTypeSelect,
|
||||
MeshCheckCollectionGroup,
|
||||
DATA_OP_facetype_select
|
||||
)
|
||||
|
||||
def register():
|
||||
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
bpy.types.WindowManager.mesh_check = bpy.props.PointerProperty(
|
||||
type=MeshCheckCollectionGroup)
|
||||
|
||||
if mesh_check_handle:
|
||||
bpy.types.SpaceView3D.draw_handler_remove(mesh_check_handle[0], 'WINDOW')
|
||||
mesh_check_handle[:] = [bpy.types.SpaceView3D.draw_handler_add(mesh_check_draw_callback, (), 'WINDOW', 'POST_VIEW')]
|
||||
|
||||
|
||||
def unregister():
|
||||
|
||||
del bpy.types.WindowManager.mesh_check
|
||||
if mesh_check_handle:
|
||||
bpy.types.SpaceView3D.draw_handler_remove(mesh_check_handle[0], 'WINDOW')
|
||||
mesh_check_handle[:] = []
|
||||
for cls in classes:
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
|
@ -17,12 +17,15 @@
|
|||
# ##### END GPL LICENSE BLOCK #####
|
||||
#
|
||||
# Repeats extrusion + rotation + scale for one or more faces
|
||||
# Original code by liero
|
||||
# Update by Jimmy Hazevoet 03/2017 for Blender 2.79
|
||||
# normal rotation, probability, scaled offset, object origin, initial scale noise
|
||||
|
||||
|
||||
bl_info = {
|
||||
"name": "MExtrude Plus1",
|
||||
"author": "liero",
|
||||
"version": (1, 2, 9),
|
||||
"author": "liero, Jimmy Hazevoet",
|
||||
"version": (1, 3, 0),
|
||||
"blender": (2, 77, 0),
|
||||
"location": "View3D > Tool Shelf",
|
||||
"description": "Repeat extrusions from faces to create organic shapes",
|
||||
|
@ -36,25 +39,27 @@ import bpy
|
|||
import bmesh
|
||||
import random
|
||||
from bpy.types import Operator
|
||||
from random import gauss, choice
|
||||
from random import gauss
|
||||
from math import radians
|
||||
from mathutils import Euler
|
||||
from mathutils import Euler, Vector
|
||||
from bpy.props import (
|
||||
FloatProperty,
|
||||
IntProperty,
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
|
||||
|
||||
# added normal rot
|
||||
def nrot(self,n):
|
||||
return Euler((radians(self.nrotx) * n[0], \
|
||||
radians(self.nroty) * n[1], \
|
||||
radians(self.nrotz) * n[2]), 'XYZ')
|
||||
def gloc(self, r):
|
||||
return Vector((self.offx, self.offy, self.offz))
|
||||
|
||||
def vloc(self, r):
|
||||
random.seed(self.ran + r)
|
||||
return self.off * (1 + gauss(0, self.var1 / 3))
|
||||
|
||||
def nrot(self, n):
|
||||
return Euler((radians(self.nrotx) * n[0],
|
||||
radians(self.nroty) * n[1],
|
||||
radians(self.nrotz) * n[2]), 'XYZ')
|
||||
|
||||
def vrot(self, r):
|
||||
random.seed(self.ran + r)
|
||||
|
@ -62,10 +67,9 @@ def vrot(self, r):
|
|||
radians(self.roty) + gauss(0, self.var2 / 3),
|
||||
radians(self.rotz) + gauss(0, self.var2 / 3)), 'XYZ')
|
||||
|
||||
|
||||
def vsca(self, r):
|
||||
random.seed(self.ran + r)
|
||||
return self.sca * (1 + random.gauss(0, self.var3 / 3))
|
||||
return self.sca * (1 + gauss(0, self.var3 / 3))
|
||||
|
||||
|
||||
class MExtrude(Operator):
|
||||
|
@ -73,15 +77,36 @@ class MExtrude(Operator):
|
|||
bl_label = "Multi Extrude"
|
||||
bl_description = ("Extrude selected Faces with Rotation,\n"
|
||||
"Scaling, Variation, Randomization")
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_options = {"REGISTER", "UNDO", "PRESET"}
|
||||
|
||||
off = FloatProperty(
|
||||
name="Offset",
|
||||
soft_min=0.001, soft_max=2,
|
||||
min=-2, max=5,
|
||||
default=.5,
|
||||
default=1.0,
|
||||
description="Translation"
|
||||
)
|
||||
offx = FloatProperty(
|
||||
name="Loc X",
|
||||
soft_min=-2.0, soft_max=2.0,
|
||||
min=-5.0, max=5.0,
|
||||
default=0.0,
|
||||
description="Global translation X"
|
||||
)
|
||||
offy = FloatProperty(
|
||||
name="Loc Y",
|
||||
soft_min=-2.0, soft_max=2.0,
|
||||
min=-5.0, max=5.0,
|
||||
default=0.0,
|
||||
description="Global translation Y"
|
||||
)
|
||||
offz = FloatProperty(
|
||||
name="Loc Z",
|
||||
soft_min=-2.0, soft_max=2.0,
|
||||
min=-5.0, max=5.0,
|
||||
default=0.0,
|
||||
description="Global translation Z"
|
||||
)
|
||||
rotx = FloatProperty(
|
||||
name="Rot X",
|
||||
min=-85, max=85,
|
||||
|
@ -109,22 +134,21 @@ class MExtrude(Operator):
|
|||
min=-85, max=85,
|
||||
soft_min=-30, soft_max=30,
|
||||
default=0,
|
||||
description="X Rotation"
|
||||
description="Normal X Rotation"
|
||||
)
|
||||
nroty = FloatProperty(
|
||||
name="N Rot Y",
|
||||
min=-85, max=85,
|
||||
soft_min=-30,
|
||||
soft_max=30,
|
||||
soft_min=-30, soft_max=30,
|
||||
default=0,
|
||||
description="Y Rotation"
|
||||
description="Normal Y Rotation"
|
||||
)
|
||||
nrotz = FloatProperty(
|
||||
name="N Rot Z",
|
||||
min=-85, max=85,
|
||||
soft_min=-30, soft_max=30,
|
||||
default=-0,
|
||||
description="Z Rotation"
|
||||
description="Normal Z Rotation"
|
||||
)
|
||||
sca = FloatProperty(
|
||||
name="Scale",
|
||||
|
@ -161,15 +185,38 @@ class MExtrude(Operator):
|
|||
)
|
||||
num = IntProperty(
|
||||
name="Repeat",
|
||||
min=1, max=50,
|
||||
min=1, max=250,
|
||||
soft_max=100,
|
||||
default=5,
|
||||
description="Repetitions")
|
||||
description="Repetitions"
|
||||
)
|
||||
ran = IntProperty(
|
||||
name="Seed",
|
||||
min=-9999, max=9999,
|
||||
default=0,
|
||||
description="Seed to feed random values")
|
||||
description="Seed to feed random values"
|
||||
)
|
||||
opt1 = BoolProperty(
|
||||
name="Polygon coördinates",
|
||||
default=True,
|
||||
description="Polygon coördinates, Object coördinates"
|
||||
)
|
||||
opt2 = BoolProperty(
|
||||
name="Proportional offset",
|
||||
default=False,
|
||||
description="Scale * Offset"
|
||||
)
|
||||
opt3 = BoolProperty(
|
||||
name="Per step scale noise",
|
||||
default=False,
|
||||
description="Per step scale noise, Initial scale noise"
|
||||
)
|
||||
opt4 = BoolProperty(
|
||||
name="Per step rotation noise",
|
||||
default=False,
|
||||
description="Per step rotation noise, Initial rotation noise"
|
||||
)
|
||||
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
@ -178,16 +225,21 @@ class MExtrude(Operator):
|
|||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.label(text="Transformations:")
|
||||
col.prop(self, "off", slider=True)
|
||||
col.prop(self, "offx", slider=True)
|
||||
col.prop(self, "offy", slider=True)
|
||||
col.prop(self, "offz", slider=True)
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, "rotx", slider=True)
|
||||
col.prop(self, "roty", slider=True)
|
||||
col.prop(self, "rotz", slider=True)
|
||||
col.prop(self, 'nrotx', slider=True)
|
||||
col.prop(self, 'nroty', slider=True)
|
||||
col.prop(self, 'nrotz', slider=True)
|
||||
col = layout.column(align=True)
|
||||
col.prop(self, "sca", slider=True)
|
||||
|
||||
col = layout.column(align=True)
|
||||
|
@ -200,13 +252,18 @@ class MExtrude(Operator):
|
|||
col = layout.column(align=False)
|
||||
col.prop(self, 'num')
|
||||
|
||||
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.label(text="Extra settings:")
|
||||
col.prop(self, "opt1")
|
||||
col.prop(self, "opt2")
|
||||
col.prop(self, "opt3")
|
||||
col.prop(self, "opt4")
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.context.object
|
||||
om = obj.mode
|
||||
bpy.context.tool_settings.mesh_select_mode = [False, False, True]
|
||||
origin = Vector([0.0, 0.0, 0.0])
|
||||
|
||||
# bmesh operations
|
||||
bpy.ops.object.mode_set()
|
||||
|
@ -218,28 +275,50 @@ class MExtrude(Operator):
|
|||
|
||||
# faces loop
|
||||
for i, of in enumerate(sel):
|
||||
rot = vrot(self, i)
|
||||
off = vloc(self, i)
|
||||
nro = nrot(self, of.normal)
|
||||
off = vloc(self, i)
|
||||
loc = gloc(self, i)
|
||||
of.normal_update()
|
||||
|
||||
# extrusion loop
|
||||
for r in range( self.num ):
|
||||
# initial scale noise
|
||||
if self.opt3 is False:
|
||||
s = vsca(self, i)
|
||||
# initial rotation noise
|
||||
if self.opt4 is False:
|
||||
rot = vrot(self, i)
|
||||
|
||||
## random % skip some extrusions
|
||||
if self.var4 >= int(random.random()*100):
|
||||
# extrusion loop
|
||||
for r in range(self.num):
|
||||
|
||||
## random probability % for extrusions
|
||||
if self.var4 > int(random.random()*100):
|
||||
|
||||
nf = of.copy()
|
||||
nf.normal_update()
|
||||
no = nf.normal.copy()
|
||||
ce = nf.calc_center_bounds()
|
||||
s = vsca(self, i + r)
|
||||
|
||||
# face/obj coördinates
|
||||
if self.opt1 is True:
|
||||
ce = nf.calc_center_bounds()
|
||||
else:
|
||||
ce = origin
|
||||
|
||||
# per step scale noise
|
||||
if self.opt3 is True:
|
||||
s = vsca(self, i + r)
|
||||
# per step rotation noise
|
||||
if self.opt4 is True:
|
||||
rot = vrot(self, i + r)
|
||||
|
||||
# proportional, scale * offset
|
||||
if self.opt2 is True:
|
||||
off = s * off
|
||||
|
||||
for v in nf.verts:
|
||||
v.co -= ce
|
||||
v.co.rotate(nro)
|
||||
v.co.rotate(rot)
|
||||
v.co += ce + no * off
|
||||
v.co += ce + loc + no * off
|
||||
v.co = v.co.lerp(ce, 1 - s)
|
||||
|
||||
# extrude code from TrumanBlending
|
||||
|
@ -247,9 +326,9 @@ class MExtrude(Operator):
|
|||
sf = bm.faces.new((a.vert, a.link_loop_next.vert,
|
||||
b.link_loop_next.vert, b.vert))
|
||||
sf.normal_update()
|
||||
|
||||
bm.faces.remove(of)
|
||||
of = nf
|
||||
|
||||
after.append(of)
|
||||
|
||||
for v in bm.verts:
|
||||
|
@ -258,7 +337,10 @@ class MExtrude(Operator):
|
|||
e.select = False
|
||||
|
||||
for f in after:
|
||||
f.select = True
|
||||
if f not in sel:
|
||||
f.select = True
|
||||
else:
|
||||
f.select = False
|
||||
|
||||
bm.to_mesh(obj.data)
|
||||
obj.data.update()
|
||||
|
|
|
@ -461,33 +461,6 @@ class MESH_OT_add_faces_to_object(Operator):
|
|||
return {'FINISHED'}
|
||||
|
||||
|
||||
class VIEW3D_Faces_Panel(Panel):
|
||||
bl_label = "Face Extrude"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "TOOLS"
|
||||
bl_category = "Tools"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
result = False
|
||||
active_object = context.active_object
|
||||
if active_object:
|
||||
mesh_objects_name = [el.name for el in bpy.data.objects if el.type == "MESH"]
|
||||
if active_object.name in mesh_objects_name:
|
||||
if active_object.mode == "OBJECT":
|
||||
result = True
|
||||
return result
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
row = layout.split(0.8, align=True)
|
||||
row.operator("mesh.add_faces_to_object", "Selected Faces")
|
||||
row.operator("mesh.extra_tools_help",
|
||||
icon="LAYER_USED").help_ids = "pkhg_faces"
|
||||
|
||||
|
||||
def find_one_ring(sel_vertices):
|
||||
ring0 = sel_vertices.pop(0)
|
||||
to_delete = []
|
||||
|
|
Loading…
Reference in New Issue