Update object_carver to 2.8 thanks @clarkx

This commit is contained in:
Brendon Murphy 2019-05-01 15:07:43 +10:00
parent 89568c1a42
commit 175202efb2
Notes: blender-bot 2023-02-14 19:17:40 +01:00
Referenced by issue #63476, Update Carver addon 2.8
7 changed files with 3693 additions and 3469 deletions

File diff suppressed because it is too large Load Diff

308
object_carver/__init__.py Normal file
View File

@ -0,0 +1,308 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Carver",
"author": "Pixivore, Cedric LEPILLER, Ted Milker, Clarkx",
"description": "Multiple tools to carve or to create objects",
"version": (1, 2, 0),
"blender": (2, 80, 0),
"location": "3D View",
"warning": "",
"wiki_url": "",
"support": 'COMMUNITY',
"category": "Object"
}
import bpy
import imp
from bpy.props import (
BoolProperty,
StringProperty,
IntProperty
)
from bpy.types import (AddonPreferences, WorkSpaceTool)
from bpy.utils.toolsystem import ToolDef
from . import carver_utils
imp.reload(carver_utils)
from . import carver_profils
imp.reload(carver_profils)
from . import carver_draw
imp.reload(carver_draw)
from . import carver_operator
imp.reload(carver_operator)
# TODO : Create an icon for Carver MT
# Add an icon in the toolbar
# class CarverTool(WorkSpaceTool):
# bl_space_type='VIEW_3D'
# bl_context_mode='OBJECT'
# bl_idname = "carver.operator"
# bl_label = "Carver"
# bl_description = (
# "Multiple tools to carve \n"
# "or to create objects"
# )
#
# #Icons : \blender-2.80\2.80\datafiles\icons
# #Todo: Create a new icon for Carver
# bl_icon = "ops.mesh.knife_tool"
# bl_widget = None
# bl_keymap = (
# ("carver.operator", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
# )
#
# def draw_settings(context, layout, tool):
# layout.prop(tool.operator_properties, "carver")
class CarverPreferences(AddonPreferences):
bl_idname = __name__
Enable_Tab_01: BoolProperty(
name="Info",
description="Some general information and settings about the add-on",
default=False
)
Enable_Tab_02: BoolProperty(
name="Hotkeys",
description="List of the shortcuts used during carving",
default=False
)
Key_Create: StringProperty(
name="Object creation",
description="Object creation",
maxlen=1,
default="C"
)
Key_Update: StringProperty(
name="Auto Bevel Update",
description="Auto Bevel Update",
maxlen=1,
default="A",
)
Key_Bool: StringProperty(
name="Boolean type",
description="Boolean operation type",
maxlen=1,
default="T",
)
Key_Brush: StringProperty(
name="Brush Mode",
description="Brush Mode",
maxlen=1,
default="B",
)
Key_Help: StringProperty(
name="Help display",
description="Help display",
maxlen=1,
default="H",
)
Key_Instant: StringProperty(
name="Instantiate",
description="Instantiate object",
maxlen=1,
default="I",
)
Key_Close: StringProperty(
name="Close polygonal shape",
description="Close polygonal shape",
maxlen=1,
default="X",
)
Key_Apply: StringProperty(
name="Apply operation",
description="Apply operation",
maxlen=1,
default="Q",
)
Key_Scale: StringProperty(
name="Scale object",
description="Scale object",
maxlen=1,
default="S",
)
Key_Gapy: StringProperty(
name="Gap rows",
description="Scale gap between columns",
maxlen=1,
default="J",
)
Key_Gapx: StringProperty(
name="Gap columns",
description="Scale gap between columns",
maxlen=1,
default="U",
)
Key_Depth: StringProperty(
name="Depth",
description="Cursor depth or solidify pattern",
maxlen=1,
default="D",
)
Key_BrushDepth: StringProperty(
name="Brush Depth",
description="Brush depth",
maxlen=1,
default="C",
)
Key_Subadd: StringProperty(
name="Add subdivision",
description="Add subdivision",
maxlen=1,
default="X",
)
Key_Subrem: StringProperty(
name="Remove subdivision",
description="Remove subdivision",
maxlen=1,
default="W",
)
Key_Randrot: StringProperty(
name="Random rotation",
description="Random rotation",
maxlen=1,
default="R",
)
ProfilePrefix: StringProperty(
name="Profile prefix",
description="Prefix to look for profiles with",
default="Carver_Profile-",
)
LineWidth: IntProperty(
name="Line Width",
description="Thickness of the drawing lines",
default=1,
)
Key_Snap: StringProperty(
name="Grid Snap",
description="Grid Snap",
maxlen=1,
default="G",
)
def draw(self, context):
scene = context.scene
layout = self.layout
icon_1 = "TRIA_RIGHT" if not self.Enable_Tab_01 else "TRIA_DOWN"
box = layout.box()
box.prop(self, "Enable_Tab_01", text="Info and Settings", emboss=False, icon=icon_1)
if self.Enable_Tab_01:
box.label(text="Carver Operator:", icon="LAYER_ACTIVE")
box.label(text="Select a Mesh Object and press [CTRL]+[SHIFT]+[X] to carve",
icon="LAYER_USED")
box.label(text="To finish carving press [ESC] or [RIGHT CLICK]",
icon="LAYER_USED")
box.prop(self, "ProfilePrefix", text="Profile prefix")
row = box.row(align=True)
row.label(text="Line width:")
row.prop(self, "LineWidth", text="")
icon_2 = "TRIA_RIGHT" if not self.Enable_Tab_02 else "TRIA_DOWN"
box = layout.box()
box.prop(self, "Enable_Tab_02", text="Keys", emboss=False, icon=icon_2)
if self.Enable_Tab_02:
split = box.split(align=True)
box = split.box()
col = box.column(align=True)
col.label(text="Object Creation:")
col.prop(self, "Key_Create", text="")
col.label(text="Auto bevel update:")
col.prop(self, "Key_Update", text="")
col.label(text="Boolean operation type:")
col.prop(self, "Key_Bool", text="")
col.label(text="Brush Depth:")
col.prop(self, "Key_BrushDepth", text="")
box = split.box()
col = box.column(align=True)
col.label(text="Brush Mode:")
col.prop(self, "Key_Brush", text="")
col.label(text="Help display:")
col.prop(self, "Key_Help", text="")
col.label(text="Instantiate object:")
col.prop(self, "Key_Instant", text="")
col.label(text="Random rotation:")
col.prop(self, "Key_Randrot", text="")
box = split.box()
col = box.column(align=True)
col.label(text="Close polygonal shape:")
col.prop(self, "Key_Close", text="")
col.label(text="Apply operation:")
col.prop(self, "Key_Apply", text="")
col.label(text="Scale object:")
col.prop(self, "Key_Scale", text="")
col.label(text="Subdiv add:")
col.prop(self, "Key_Subadd", text="")
box = split.box()
col = box.column(align=True)
col.label(text="Gap rows:")
col.prop(self, "Key_Gapy", text="")
col.label(text="Gap columns:")
col.prop(self, "Key_Gapx", text="")
col.label(text="Depth / Solidify:")
col.prop(self, "Key_Depth", text="")
col.label(text="Subdiv Remove:")
col.prop(self, "Key_Subrem", text="")
box = split.box()
col = box.column(align=True)
col.label(text="Grid Snap:")
col.prop(self, "Key_Snap", text="")
addon_keymaps = []
def register():
print("Registered Carver")
bpy.utils.register_class(CarverPreferences)
# Todo : Add an icon in the toolbat
# bpy.utils.register_tool(CarverTool, separator=True, group=True)
carver_operator.register()
# add keymap entry
kcfg = bpy.context.window_manager.keyconfigs.addon
if kcfg:
km = kcfg.keymaps.new(name='3D View', space_type='VIEW_3D')
kmi = km.keymap_items.new("carver.operator", 'X', 'PRESS', shift=True, ctrl=True)
addon_keymaps.append((km, kmi))
def unregister():
# Todo : Add an icon in the toolbat
# bpy.utils.unregister_tool(CarverTool)
carver_operator.unregister()
bpy.utils.unregister_class(CarverPreferences)
print("Unregistered Carver")
# remove keymap entry
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
if __name__ == "__main__":
register()

View File

@ -0,0 +1,494 @@
import bpy
import bgl
import blf
import bpy_extras
import numpy as np
import gpu
from gpu_extras.batch import batch_for_shader
from math import(
cos,
sin,
ceil,
floor,
)
from bpy_extras.view3d_utils import (
region_2d_to_location_3d,
location_3d_to_region_2d,
)
from .carver_utils import (
draw_circle,
draw_shader,
objDiagonal,
mini_grid,
)
from mathutils import (
Color,
Euler,
Vector,
Quaternion,
)
def get_text_info(self, context, help_txt):
""" Return the dimensions of each part of the text """
#Extract the longest first option in sublist
max_option = max(list(blf.dimensions(0, row[0])[0] for row in help_txt))
#Extract the longest key in sublist
max_key = max(list(blf.dimensions(0, row[1])[0] for row in help_txt))
#Space between option and key with a comma separator (" : ")
comma = blf.dimensions(0, "_:_")[0]
#Get a default height for all the letters
line_height = (blf.dimensions(0, "gM")[1] * 1.45)
#Get the total height of the text
bloc_height = 0
for row in help_txt:
bloc_height += line_height
return(help_txt, bloc_height, max_option, max_key, comma)
def draw_string(self, color1, color2, left, bottom, text, max_option, divide = 1):
""" Draw the text like 'option : key' or just 'option' """
font_id = 0
blf.enable(font_id,blf.SHADOW)
blf.shadow(font_id, 0, 0.0, 0.0, 0.0, 1.0)
blf.shadow_offset(font_id,2,-2)
line_height = (blf.dimensions(font_id, "gM")[1] * 1.45)
y_offset = 5
# Test if the text is a list formated like : ('option', 'key')
if isinstance(text,list):
for string in text:
blf.position(font_id, (left), (bottom + y_offset), 0)
blf.color(font_id, *color1)
blf.draw(font_id, string[0])
blf.position(font_id, (left + max_option), (bottom + y_offset), 0)
blf.draw(font_id, " : ")
blf.color(font_id, *color2)
blf.position(font_id, (left + max_option + 15), (bottom + y_offset), 0)
blf.draw(font_id, string[1])
y_offset += line_height
else:
# The text is formated like : ('option')
blf.position(font_id, left, (bottom + y_offset), 0)
blf.color(font_id, *color1)
blf.draw(font_id, text)
y_offset += line_height
blf.disable(font_id,blf.SHADOW)
# Opengl draw on screen
def draw_callback_px(self, context):
font_id = 0
region = context.region
UIColor = (0.992, 0.5518, 0.0, 1.0)
# Cut Type
RECTANGLE = 0
LINE = 1
CIRCLE = 2
self.carver_prefs = context.preferences.addons[__package__].preferences
# Color
color1 = (1.0, 1.0, 1.0, 1.0)
color2 = UIColor
#The mouse is outside the active region
if not self.in_view_3d:
color1 = color2 = (1.0, 0.2, 0.1, 1.0)
# Primitives type
PrimitiveType = "Rectangle"
if self.CutType == CIRCLE:
PrimitiveType = "Circle"
if self.CutType == LINE:
PrimitiveType = "Line"
# Width screen
overlap = context.preferences.system.use_region_overlap
t_panel_width = 0
if overlap:
for region in context.area.regions:
if region.type == 'TOOLS':
t_panel_width = region.width
# Initial position
region_width = int(region.width / 2.0)
y_txt = 10
# Draw the center command from bottom to top
# Get the size of the text
text_size = 18 if region.width >= 850 else 12
blf.size(0, int(round(text_size * bpy.context.preferences.view.ui_scale, 0)), 72)
# Help Display
if (self.ObjectMode is False) and (self.ProfileMode is False):
# Depth Cursor
TypeStr = "Cursor Depth [" + self.carver_prefs.Key_Depth + "]"
BoolStr = "(ON)" if self.snapCursor else "(OFF)"
help_txt = [[TypeStr, BoolStr]]
# Close poygonal shape
if self.CreateMode and self.CutType == LINE:
TypeStr = "Close [" + self.carver_prefs.Key_Close + "]"
BoolStr = "(ON)" if self.Closed else "(OFF)"
help_txt += [[TypeStr, BoolStr]]
if self.CreateMode is False:
# Apply Booleans
TypeStr = "Apply Operations [" + self.carver_prefs.Key_Apply + "]"
BoolStr = "(OFF)" if self.dont_apply_boolean else "(ON)"
help_txt += [[TypeStr, BoolStr]]
#Auto update for bevel
TypeStr = "Bevel Update [" + self.carver_prefs.Key_Update + "]"
BoolStr = "(ON)" if self.Auto_BevelUpdate else "(OFF)"
help_txt += [[TypeStr, BoolStr]]
# Circle subdivisions
if self.CutType == CIRCLE:
TypeStr = "Subdivisions [" + self.carver_prefs.Key_Subrem + "][" + self.carver_prefs.Key_Subadd + "]"
BoolStr = str((int(360 / self.stepAngle[self.step])))
help_txt += [[TypeStr, BoolStr]]
if self.CreateMode:
help_txt += [["Type [Space]", PrimitiveType]]
else:
help_txt += [["Cut Type [Space]", PrimitiveType]]
else:
# Instantiate
TypeStr = "Instantiate [" + self.carver_prefs.Key_Instant + "]"
BoolStr = "(ON)" if self.Instantiate else "(OFF)"
help_txt = [[TypeStr, BoolStr]]
# Random rotation
if self.alt:
TypeStr = "Random Rotation [" + self.carver_prefs.Key_Randrot + "]"
BoolStr = "(ON)" if self.RandomRotation else "(OFF)"
help_txt += [[TypeStr, BoolStr]]
# Thickness
if self.BrushSolidify:
TypeStr = "Thickness [" + self.carver_prefs.Key_Depth + "]"
if self.ProfileMode:
BoolStr = str(round(self.ProfileBrush.modifiers["CT_SOLIDIFY"].thickness, 2))
if self.ObjectMode:
BoolStr = str(round(self.ObjectBrush.modifiers["CT_SOLIDIFY"].thickness, 2))
help_txt += [[TypeStr, BoolStr]]
# Brush depth
if (self.ObjectMode):
TypeStr = "Carve Depth [" + self.carver_prefs.Key_Depth + "]"
BoolStr = str(round(self.ObjectBrush.data.vertices[0].co.z, 2))
help_txt += [[TypeStr, BoolStr]]
TypeStr = "Brush Depth [" + self.carver_prefs.Key_BrushDepth + "]"
BoolStr = str(round(self.BrushDepthOffset, 2))
help_txt += [[TypeStr, BoolStr]]
help_txt, bloc_height, max_option, max_key, comma = get_text_info(self, context, help_txt)
xCmd = region_width - (max_option + max_key + comma) / 2
draw_string(self, color1, color2, xCmd, y_txt, help_txt, max_option, divide = 2)
# Separator (Line)
LineWidth = (max_option + max_key + comma) / 2
if region.width >= 850:
LineWidth = 140
LineWidth = (max_option + max_key + comma)
coords = [(int(region_width - LineWidth/2), y_txt + bloc_height + 8), \
(int(region_width + LineWidth/2), y_txt + bloc_height + 8)]
draw_shader(self, UIColor, 1, 'LINES', coords, self.carver_prefs.LineWidth)
# Command Display
if self.CreateMode and ((self.ObjectMode is False) and (self.ProfileMode is False)):
BooleanMode = "Create"
else:
if self.ObjectMode or self.ProfileMode:
BooleanType = "Difference) [T]" if self.BoolOps == self.difference else "Union) [T]"
BooleanMode = \
"Object Brush (" + BooleanType if self.ObjectMode else "Profil Brush (" + BooleanType
else:
BooleanMode = \
"Difference" if (self.shift is False) and (self.ForceRebool is False) else "Rebool"
# Display boolean mode
text_size = 40 if region.width >= 850 else 20
blf.size(0, int(round(text_size * bpy.context.preferences.view.ui_scale, 0)), 72)
draw_string(self, color2, color2, region_width - (blf.dimensions(0, BooleanMode)[0]) / 2, \
y_txt + bloc_height + 16, BooleanMode, 0, divide = 2)
if region.width >= 850:
if self.AskHelp is False:
# "H for Help" text
blf.size(0, int(round(13 * bpy.context.preferences.view.ui_scale, 0)), 72)
help_txt = "[" + self.carver_prefs.Key_Help + "] for help"
txt_width = blf.dimensions(0, help_txt)[0]
txt_height = (blf.dimensions(0, "gM")[1] * 1.45)
# Draw a rectangle and put the text "H for Help"
xrect = 40
yrect = 40
rect_vertices = [(xrect - 5, yrect - 5), (xrect + txt_width + 5, yrect - 5), \
(xrect + txt_width + 5, yrect + txt_height + 5), (xrect - 5, yrect + txt_height + 5)]
draw_shader(self, (0.0, 0.0, 0.0), 0.3, 'TRI_FAN', rect_vertices, self.carver_prefs.LineWidth)
draw_string(self, color1, color2, xrect, yrect, help_txt, 0)
else:
#Draw the help text
xHelp = 30 + t_panel_width
yHelp = 10
if self.ObjectMode or self.ProfileMode:
if self.ProfileMode:
help_txt = [["Object Mode", self.carver_prefs.Key_Brush]]
else:
help_txt = [["Cut Mode", self.carver_prefs.Key_Brush]]
else:
help_txt =[
["Profil Brush", self.carver_prefs.Key_Brush],\
["Move Cursor", "Ctrl + LMB"]
]
if (self.ObjectMode is False) and (self.ProfileMode is False):
if self.CreateMode is False:
help_txt +=[
["Create geometry", self.carver_prefs.Key_Create],\
]
else:
help_txt +=[
["Cut", self.carver_prefs.Key_Create],\
]
if self.CutMode == RECTANGLE:
help_txt +=[
["Dimension", "MouseMove"],\
["Move all", "Alt"],\
["Validate", "LMB"],\
["Rebool", "Shift"]
]
elif self.CutMode == CIRCLE:
help_txt +=[
["Rotation and Radius", "MouseMove"],\
["Move all", "Alt"],\
["Subdivision", self.carver_prefs.Key_Subrem + " " + self.carver_prefs.Key_Subadd],\
["Incremental rotation", "Ctrl"],\
["Rebool", "Shift"]
]
elif self.CutMode == LINE:
help_txt +=[
["Dimension", "MouseMove"],\
["Move all", "Alt"],\
["Validate", "Space"],\
["Rebool", "Shift"],\
["Snap", "Ctrl"],\
["Scale Snap", "WheelMouse"],\
]
else:
# ObjectMode
help_txt +=[
["Difference", "Space"],\
["Rebool", "Shift + Space"],\
["Duplicate", "Alt + Space"],\
["Scale", self.carver_prefs.Key_Scale],\
["Rotation", "LMB + Move"],\
["Step Angle", "CTRL + LMB + Move"],\
]
if self.ProfileMode:
help_txt +=[["Previous or Next Profile", self.carver_prefs.Key_Subadd + " " + self.carver_prefs.Key_Subrem]]
help_txt +=[
["Create / Delete rows", chr(8597)],\
["Create / Delete cols", chr(8596)],\
["Gap for rows or columns", self.carver_prefs.Key_Gapy + " " + self.carver_prefs.Key_Gapx]
]
blf.size(0, int(round(15 * bpy.context.preferences.view.ui_scale, 0)), 72)
help_txt, bloc_height, max_option, max_key, comma = get_text_info(self, context, help_txt)
draw_string(self, color1, color2, xHelp, yHelp, help_txt, max_option)
if self.ProfileMode:
xrect = region.width - t_panel_width - 80
yrect = 80
coords = [(xrect, yrect), (xrect+60, yrect), (xrect+60, yrect-60), (xrect, yrect-60)]
# Draw rectangle background in the lower right
draw_shader(self, (0.0, 0.0, 0.0), 0.3, 'TRI_FAN', coords, size=self.carver_prefs.LineWidth)
# Use numpy to get the vertices and indices of the profile object to draw
WidthProfil = 50
location = Vector((region.width - t_panel_width - WidthProfil, 50, 0))
ProfilScale = 20.0
coords = []
mesh = bpy.data.meshes[self.Profils[self.nProfil][0]]
mesh.calc_loop_triangles()
vertices = np.empty((len(mesh.vertices), 3), 'f')
indices = np.empty((len(mesh.loop_triangles), 3), 'i')
mesh.vertices.foreach_get("co", np.reshape(vertices, len(mesh.vertices) * 3))
mesh.loop_triangles.foreach_get("vertices", np.reshape(indices, len(mesh.loop_triangles) * 3))
for idx, vals in enumerate(vertices):
coords.append([
vals[0] * ProfilScale + location.x,
vals[1] * ProfilScale + location.y,
vals[2] * ProfilScale + location.z
])
#Draw the silhouette of the mesh
draw_shader(self, UIColor, 0.5, 'TRIS', coords, size=self.carver_prefs.LineWidth, indices=indices)
if self.CutMode:
if len(self.mouse_path) > 1:
x0 = self.mouse_path[0][0]
y0 = self.mouse_path[0][1]
x1 = self.mouse_path[1][0]
y1 = self.mouse_path[1][1]
# Cut rectangle
if self.CutType == RECTANGLE:
coords = [
(x0 + self.xpos, y0 + self.ypos), (x1 + self.xpos, y0 + self.ypos), \
(x1 + self.xpos, y1 + self.ypos), (x0 + self.xpos, y1 + self.ypos)
]
indices = ((0, 1, 2), (2, 0, 3))
self.rectangle_coord = coords
draw_shader(self, UIColor, 1, 'LINE_LOOP', coords, size=self.carver_prefs.LineWidth)
#Draw points
draw_shader(self, UIColor, 1, 'POINTS', coords, size=3)
if self.shift or self.CreateMode:
draw_shader(self, UIColor, 0.5, 'TRIS', coords, size=self.carver_prefs.LineWidth, indices=indices)
# Draw grid (based on the overlay options) to show the incremental snapping
if self.ctrl:
mini_grid(self, context, UIColor)
# Cut Line
elif self.CutType == LINE:
coords = []
indices = []
top_grid = False
for idx, vals in enumerate(self.mouse_path):
coords.append([vals[0] + self.xpos, vals[1] + self.ypos])
indices.append([idx])
# Draw lines
if self.Closed:
draw_shader(self, UIColor, 1.0, 'LINE_LOOP', coords, size=self.carver_prefs.LineWidth)
else:
draw_shader(self, UIColor, 1.0, 'LINE_STRIP', coords, size=self.carver_prefs.LineWidth)
# Draw points
draw_shader(self, UIColor, 1.0, 'POINTS', coords, size=3)
# Draw polygon
if (self.shift) or (self.CreateMode and self.Closed):
draw_shader(self, UIColor, 0.5, 'TRI_FAN', coords, size=self.carver_prefs.LineWidth)
# Draw grid (based on the overlay options) to show the incremental snapping
if self.ctrl:
mini_grid(self, context, UIColor)
# Circle Cut
elif self.CutType == CIRCLE:
# Create a circle using a tri fan
tris_coords, indices = draw_circle(self, x0, y0)
# Remove the vertex in the center to get the outer line of the circle
line_coords = tris_coords[1:]
draw_shader(self, UIColor, 1.0, 'LINE_LOOP', line_coords, size=self.carver_prefs.LineWidth)
if self.shift or self.CreateMode:
draw_shader(self, UIColor, 0.5, 'TRIS', tris_coords, size=self.carver_prefs.LineWidth, indices=indices)
if (self.ObjectMode or self.ProfileMode) and len(self.CurrentSelection) > 0:
if self.ShowCursor:
region = context.region
rv3d = context.space_data.region_3d
if self.ObjectMode:
ob = self.ObjectBrush
if self.ProfileMode:
ob = self.ProfileBrush
mat = ob.matrix_world
# 50% alpha, 2 pixel width line
bgl.glEnable(bgl.GL_BLEND)
bbox = [mat @ Vector(b) for b in ob.bound_box]
objBBDiagonal = objDiagonal(self.CurrentSelection[0])
if self.shift:
gl_size = 4
UIColor = (0.5, 1.0, 0.0, 1.0)
else:
gl_size = 2
UIColor = (1.0, 0.8, 0.0, 1.0)
line_coords = []
idx = 0
CRadius = ((bbox[7] - bbox[0]).length) / 2
for i in range(int(len(self.CLR_C) / 3)):
vector3d = (self.CLR_C[idx * 3] * CRadius + self.CurLoc.x, \
self.CLR_C[idx * 3 + 1] * CRadius + self.CurLoc.y, \
self.CLR_C[idx * 3 + 2] * CRadius + self.CurLoc.z)
vector2d = bpy_extras.view3d_utils.location_3d_to_region_2d(region, rv3d, vector3d)
if vector2d is not None:
line_coords.append((vector2d[0], vector2d[1]))
idx += 1
if len(line_coords) > 0 :
draw_shader(self, UIColor, 1.0, 'LINE_LOOP', line_coords, size=gl_size)
# Object display
if self.quat_rot is not None:
ob.location = self.CurLoc
v = Vector()
v.x = v.y = 0.0
v.z = self.BrushDepthOffset
ob.location += self.quat_rot @ v
e = Euler()
e.x = 0.0
e.y = 0.0
e.z = self.aRotZ / 25.0
qe = e.to_quaternion()
qRot = self.quat_rot @ qe
ob.rotation_mode = 'QUATERNION'
ob.rotation_quaternion = qRot
ob.rotation_mode = 'XYZ'
if self.ProfileMode:
if self.ProfileBrush is not None:
self.ProfileBrush.location = self.CurLoc
self.ProfileBrush.rotation_mode = 'QUATERNION'
self.ProfileBrush.rotation_quaternion = qRot
self.ProfileBrush.rotation_mode = 'XYZ'
# Opengl defaults
bgl.glLineWidth(1)
bgl.glDisable(bgl.GL_BLEND)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,194 @@
import bpy
from bpy.props import (
BoolProperty,
IntProperty,
PointerProperty,
StringProperty,
)
class CarverPrefs(bpy.types.AddonPreferences):
bl_idname = __name__
Enable_Tab_01: BoolProperty(
name="Info",
description="Some general information and settings about the add-on",
default=False
)
Enable_Tab_02: BoolProperty(
name="Hotkeys",
description="List of the shortcuts used during carving",
default=False
)
bpy.types.Scene.Key_Create: StringProperty(
name="Object creation",
description="Object creation",
maxlen=1,
default="C"
)
bpy.types.Scene.Key_Update: StringProperty(
name="Auto Bevel Update",
description="Auto Bevel Update",
maxlen=1,
default="A",
)
bpy.types.Scene.Key_Bool: StringProperty(
name="Boolean type",
description="Boolean operation type",
maxlen=1,
default="T",
)
bpy.types.Scene.Key_Brush: StringProperty(
name="Brush Mode",
description="Brush Mode",
maxlen=1,
default="B",
)
bpy.types.Scene.Key_Help: StringProperty(
name="Help display",
description="Help display",
maxlen=1,
default="H",
)
bpy.types.Scene.Key_Instant: StringProperty(
name="Instantiate",
description="Instantiate object",
maxlen=1,
default="I",
)
bpy.types.Scene.Key_Close: StringProperty(
name="Close polygonal shape",
description="Close polygonal shape",
maxlen=1,
default="X",
)
bpy.types.Scene.Key_Apply: StringProperty(
name="Apply operation",
description="Apply operation",
maxlen=1,
default="Q",
)
bpy.types.Scene.Key_Scale: StringProperty(
name="Scale object",
description="Scale object",
maxlen=1,
default="S",
)
bpy.types.Scene.Key_Gapy: StringProperty(
name="Gap rows",
description="Scale gap between columns",
maxlen=1,
default="J",
)
bpy.types.Scene.Key_Gapx: StringProperty(
name="Gap columns",
description="Scale gap between columns",
maxlen=1,
default="U",
)
bpy.types.Scene.Key_Depth: StringProperty(
name="Depth",
description="Cursor depth or solidify pattern",
maxlen=1,
default="D",
)
bpy.types.Scene.Key_BrushDepth: StringProperty(
name="Brush Depth",
description="Brush depth",
maxlen=1,
default="C",
)
bpy.types.Scene.Key_Subadd: StringProperty(
name="Add subdivision",
description="Add subdivision",
maxlen=1,
default="X",
)
bpy.types.Scene.Key_Subrem: StringProperty(
name="Remove subdivision",
description="Remove subdivision",
maxlen=1,
default="W",
)
bpy.types.Scene.Key_Randrot: StringProperty(
name="Random rotation",
description="Random rotation",
maxlen=1,
default="R",
)
bpy.types.Scene.ProfilePrefix: StringProperty(
name="Profile prefix",
description="Prefix to look for profiles with",
default="Carver_Profile-"
)
def draw(self, context):
scene = context.scene
layout = self.layout
print("DRAW !")
icon_1 = "TRIA_RIGHT" if not self.Enable_Tab_01 else "TRIA_DOWN"
box = layout.box()
box.prop(self, "Enable_Tab_01", text="Info and Settings", emboss=False, icon=icon_1)
if self.Enable_Tab_01:
box.label(text="Carver Operator:", icon="LAYER_ACTIVE")
box.label(text="Select a Mesh Object and press [CTRL]+[SHIFT]+[X] to carve",
icon="LAYER_USED")
box.label(text="To finish carving press [ESC] or [RIGHT CLICK]",
icon="LAYER_USED")
box.prop(scene, "ProfilePrefix", text="Profile prefix")
icon_2 = "TRIA_RIGHT" if not self.Enable_Tab_02 else "TRIA_DOWN"
box = layout.box()
box.prop(self, "Enable_Tab_02", text="Keys", emboss=False, icon=icon_2)
if self.Enable_Tab_02:
split = box.split(align=True)
box = split.box()
col = box.column(align=True)
col.label(text="Object Creation:")
col.prop(scene, "Key_Create", text="")
col.label(text="Auto bevel update:")
col.prop(scene, "Key_Update", text="")
col.label(text="Boolean operation type:")
col.prop(scene, "Key_Bool", text="")
col.label(text="Brush Depth:")
col.prop(scene, "Key_BrushDepth", text="")
box = split.box()
col = box.column(align=True)
col.label(text="Brush Mode:")
col.prop(scene, "Key_Brush", text="")
col.label(text="Help display:")
col.prop(scene, "Key_Help", text="")
col.label(text="Instantiate object:")
col.prop(scene, "Key_Instant", text="")
col.label(text="Random rotation:")
col.prop(scene, "Key_Randrot", text="")
box = split.box()
col = box.column(align=True)
col.label(text="Close polygonal shape:")
col.prop(scene, "Key_Close", text="")
col.label(text="Apply operation:")
col.prop(scene, "Key_Apply", text="")
col.label(text="Scale object:")
col.prop(scene, "Key_Scale", text="")
col.label(text="Subdiv add:")
col.prop(scene, "Key_Subadd", text="")
box = split.box()
col = box.column(align=True)
col.label(text="Gap rows:")
col.prop(scene, "Key_Gapy", text="")
col.label(text="Gap columns:")
col.prop(scene, "Key_Gapx", text="")
col.label(text="Depth / Solidify:")
col.prop(scene, "Key_Depth", text="")
col.label(text="Subdiv Remove:")
col.prop(scene, "Key_Subrem", text="")
def register():
bpy.utils.register_class(CarverPrefs)
def unregister():
bpy.utils.unregister_class(CarverPrefs)

View File

@ -0,0 +1,409 @@
from mathutils import (
Vector,
)
Profils = [
("TEST",
Vector((0,0,1)),
[(-1, 1, 0.032334), (1, 1, 0.032334),(-1, -1, 0.032334), (1, -1, 0.01032334)],
[(0, 1, 2), (2,1,3)]),
("CTP_4882",
Vector((2.61824, -5.56469, 0)),
[(-1.156501, 0.799282, 0.032334),
(-0.967583, 0.838861, 0.032334),
(-1.10386, 0.846403, 0.032334),
(-1.034712, 0.86089, 0.032334),
(-1.88472, -0.564419, 0.032334),
(-1.924299, -0.375502, 0.032334),
(-1.93184, -0.511778, 0.032334),
(-1.946327, -0.44263, 0.032334),
(-0.219065, -0.869195, 0.032334),
(-0.149916, -0.854708, 0.032334),
(-0.286193, -0.847167, 0.032334),
(-0.097275, -0.807588, 0.032334),
(0.692551, 0.434324, 0.032334),
(0.678064, 0.503472, 0.032334),
(0.670523, 0.367196, 0.032334),
(0.630943, 0.556113, 0.032334),
(-0.780424, -0.44263, 0.032334),
(-0.765937, -0.511778, 0.032334),
(-0.758396, -0.375502, 0.032334),
(-0.718817, -0.564419, 0.032334),
(-0.53496, 0.556113, 0.032334),
(-0.49538, 0.367196, 0.032334),
(-0.487839, 0.503472, 0.032334),
(-0.473352, 0.434324, 0.032334),
(-1.263178, -0.807588, 0.032334),
(-1.452096, -0.847167, 0.032334),
(-1.315819, -0.854708, 0.032334),
(-1.384968, -0.869195, 0.032334),
(0.131191, 0.86089, 0.032334),
(0.062043, 0.846403, 0.032334),
(0.19832, 0.838861, 0.032334),
(0.009402, 0.799282, 0.032334),
(0.946838, -0.869195, 0.032334),
(1.015987, -0.854708, 0.032334),
(0.87971, -0.847167, 0.032334),
(1.068628, -0.807588, 0.032334),
(1.858454, 0.434324, 0.032334),
(1.843967, 0.503472, 0.032334),
(1.836426, 0.367196, 0.032334),
(1.796846, 0.556113, 0.032334),
(0.385479, -0.44263, 0.032334),
(0.399966, -0.511778, 0.032334),
(0.407507, -0.375502, 0.032334),
(0.447086, -0.564419, 0.032334),
(1.297095, 0.86089, 0.032334),
(1.227946, 0.846403, 0.032334),
(1.364223, 0.838861, 0.032334),
(1.175305, 0.799282, 0.032334),
],
[[16, 17, 19], [5, 4, 24], [14, 12, 15], [14, 15, 31], [10, 8, 11], [15, 30, 31], [19, 10, 11],
[11, 14, 31], [31, 18, 11], [8, 9, 11], [18, 16, 19], [12, 13, 15], [18, 19, 11], [28, 29, 31],
[30, 28, 31], [24, 21, 0], [23, 22, 20], [20, 1, 0], [3, 2, 0], [0, 5, 24], [7, 6, 4], [4, 25, 24],
[27, 26, 24], [21, 23, 20], [1, 3, 0], [5, 7, 4], [25, 27, 24], [21, 20, 0], [40, 41, 43], [38, 36, 39],
[38, 39, 47], [34, 32, 35], [39, 46, 47], [43, 34, 35], [35, 38, 47], [47, 42, 35], [32, 33, 35],
[42, 40, 43], [36, 37, 39], [42, 43, 35], [44, 45, 47], [46, 44, 47]]),
("CTP_8354",
Vector((-0.06267, -2.43829, -0.0)),
[(-0.534254, -1.0, 0.032334),
(-1.0, -0.534254, 0.032334),
(-0.654798, -0.98413, 0.032334),
(-0.767127, -0.937602, 0.032334),
(-0.863586, -0.863586, 0.032334),
(-0.937602, -0.767127, 0.032334),
(-0.98413, -0.654798, 0.032334),
(1.0, -0.534254, 0.032334),
(0.534254, -1.0, 0.032334),
(0.98413, -0.654798, 0.032334),
(0.937602, -0.767127, 0.032334),
(0.863586, -0.863586, 0.032334),
(0.767127, -0.937602, 0.032334),
(0.654798, -0.98413, 0.032334),
(-1.0, 0.534254, 0.032334),
(-0.534254, 1.0, 0.032334),
(-0.98413, 0.654798, 0.032334),
(-0.937602, 0.767127, 0.032334),
(-0.863586, 0.863586, 0.032334),
(-0.767127, 0.937602, 0.032334),
(-0.654798, 0.98413, 0.032334),
(0.534254, 1.0, 0.032334),
(1.0, 0.534254, 0.032334),
(0.654798, 0.98413, 0.032334),
(0.767127, 0.937602, 0.032334),
(0.863586, 0.863586, 0.032334),
(0.937602, 0.767127, 0.032334),
(0.98413, 0.654798, 0.032334),
(-0.763998, 0.518786, 0.032334),
(-0.763998, -0.518786, 0.032334),
(-0.754202, -0.593189, 0.032334),
(-0.731454, -0.648108, 0.032334),
(-0.695267, -0.695267, 0.032334),
(-0.648108, -0.731454, 0.032334),
(-0.593189, -0.754202, 0.032334),
(-0.518786, -0.763998, 0.032334),
(0.518786, -0.763998, 0.032334),
(0.593189, -0.754202, 0.032334),
(0.648108, -0.731454, 0.032334),
(0.695267, -0.695267, 0.032334),
(0.731454, -0.648108, 0.032334),
(0.754202, -0.593189, 0.032334),
(0.763998, -0.518786, 0.032334),
(0.763998, 0.518786, 0.032334),
(0.754202, 0.593189, 0.032334),
(0.731454, 0.648108, 0.032334),
(0.695267, 0.695267, 0.032334),
(0.648108, 0.731454, 0.032334),
(0.593189, 0.754202, 0.032334),
(0.518786, 0.763998, 0.032334),
(-0.518786, 0.763998, 0.032334),
(-0.593189, 0.754202, 0.032334),
(-0.648108, 0.731454, 0.032334),
(-0.695267, 0.695267, 0.032334),
(-0.731454, 0.648108, 0.032334),
(-0.754202, 0.593189, 0.032334),
(0.518786, 0.518786, 0.032334),
(-0.518786, 0.518786, 0.032334),
(0.518786, -0.518786, 0.032334),
(-0.518786, -0.518786, 0.032334),
(-0.593189, 0.518786, 0.032334),
(-0.593189, -0.518786, 0.032334),
(0.518786, -0.593189, 0.032334),
(-0.518786, -0.593189, 0.032334),
(-0.593189, -0.593189, 0.032334),
(0.593189, 0.518786, 0.032334),
(0.593189, -0.518786, 0.032334),
(0.593189, -0.593189, 0.032334),
(-0.593189, 0.593189, 0.032334),
(-0.518786, 0.593189, 0.032334),
(0.518786, 0.593189, 0.032334),
(0.593189, 0.593189, 0.032334),
(-0.648108, 0.593189, 0.032334),
(-0.648108, 0.518786, 0.032334),
(-0.648108, -0.518786, 0.032334),
(-0.648108, -0.593189, 0.032334),
(-0.695267, 0.593189, 0.032334),
(-0.695267, 0.518786, 0.032334),
(-0.695267, -0.518786, 0.032334),
(-0.695267, -0.593189, 0.032334),
(0.648108, 0.593189, 0.032334),
(0.648108, 0.518786, 0.032334),
(0.648108, -0.518786, 0.032334),
(0.648108, -0.593189, 0.032334),
(0.695267, 0.593189, 0.032334),
(0.695267, 0.518786, 0.032334),
(0.695267, -0.518786, 0.032334),
(0.695267, -0.593189, 0.032334),
],
[[87, 39, 40, 41], [29, 28, 14, 1], [30, 29, 1, 6], [31, 30, 6, 5], [32, 31, 5, 4], [33, 32, 4, 3],
[34, 33, 3, 2], [35, 34, 2, 0], [36, 35, 0, 8], [37, 36, 8, 13], [38, 37, 13, 12], [39, 38, 12, 11],
[40, 39, 11, 10], [41, 40, 10, 9], [42, 41, 9, 7], [43, 42, 7, 22], [44, 43, 22, 27], [45, 44, 27, 26],
[46, 45, 26, 25], [47, 46, 25, 24], [48, 47, 24, 23], [49, 48, 23, 21], [50, 49, 21, 15], [51, 50, 15, 20],
[52, 51, 20, 19], [53, 52, 19, 18], [54, 53, 18, 17], [55, 54, 17, 16], [28, 55, 16, 14], [68, 69, 50, 51],
[63, 35, 36, 62], [69, 57, 56, 70], [84, 85, 43, 44], [64, 34, 35, 63], [57, 59, 58, 56], [85, 86, 42, 43],
[60, 61, 59, 57], [73, 74, 61, 60], [72, 68, 51, 52], [75, 33, 34, 64], [61, 64, 63, 59], [59, 63, 62, 58],
[86, 87, 41, 42], [74, 75, 64, 61], [58, 62, 67, 66], [56, 58, 66, 65], [70, 56, 65, 71], [62, 36, 37, 67],
[49, 70, 71, 48], [50, 69, 70, 49], [60, 57, 69, 68], [73, 60, 68, 72], [46, 84, 44, 45], [78, 79, 75, 74],
[77, 78, 74, 73], [77, 73, 72, 76], [76, 72, 52, 53], [79, 32, 33, 75], [29, 30, 79, 78], [28, 29, 78, 77],
[28, 77, 76, 55], [55, 76, 53, 54], [30, 31, 32, 79], [66, 67, 83, 82], [65, 66, 82, 81], [71, 65, 81, 80],
[48, 71, 80, 47], [67, 37, 38, 83], [82, 83, 87, 86], [81, 82, 86, 85], [80, 81, 85, 84], [47, 80, 84, 46],
[83, 38, 39, 87]]),
("CTP_5585",
Vector((5.0114, -2.4281, 0.0)),
[(-0.490711, -1.0, 0.032334),
(-1.0, -0.490711, 0.032334),
(1.0, -0.490711, 0.032334),
(0.490711, -1.0, 0.032334),
(-1.0, 0.490711, 0.032334),
(-0.490711, 1.0, 0.032334),
(0.490711, 1.0, 0.032334),
(1.0, 0.490711, 0.032334),
(-0.51852, 0.291276, 0.032334),
(-0.51852, -0.291276, 0.032334),
(-0.291276, -0.51852, 0.032334),
(0.291276, -0.51852, 0.032334),
(0.51852, -0.291276, 0.032334),
(0.51852, 0.291276, 0.032334),
(0.291276, 0.51852, 0.032334),
(-0.291276, 0.51852, 0.032334),
],
[[11, 12, 13, 14], [9, 8, 4, 1], [10, 9, 1, 0], [11, 10, 0, 3], [12, 11, 3, 2], [13, 12, 2, 7],
[14, 13, 7, 6], [15, 14, 6, 5], [8, 15, 5, 4], [9, 10, 15, 8], [10, 11, 14, 15]]),
("CTP_6960",
Vector((-0.11417, 2.48371, -0.0)),
[(0.0, 1.0, 0.016827),
(-0.382683, 0.92388, 0.016827),
(-0.707107, 0.707107, 0.016827),
(-0.92388, 0.382683, 0.016827),
(-1.0, -0.0, 0.016827),
(-0.92388, -0.382684, 0.016827),
(-0.707107, -0.707107, 0.016827),
(-0.382683, -0.92388, 0.016827),
(-0.0, -1.0, 0.016827),
(0.382683, -0.92388, 0.016827),
(0.707107, -0.707107, 0.016827),
(0.92388, -0.382684, 0.016827),
(1.0, 0.0, 0.016827),
(0.923879, 0.382684, 0.016827),
(0.707107, 0.707107, 0.016827),
(0.382683, 0.92388, 0.016827),
(-0.0, 0.546859, 0.016827),
(-0.209274, 0.505231, 0.016827),
(-0.386687, 0.386687, 0.016827),
(-0.505231, 0.209274, 0.016827),
(-0.546859, -0.0, 0.016827),
(-0.505231, -0.209274, 0.016827),
(-0.386687, -0.386687, 0.016827),
(-0.209274, -0.505231, 0.016827),
(-0.0, -0.546859, 0.016827),
(0.209274, -0.505231, 0.016827),
(0.386687, -0.386688, 0.016827),
(0.505231, -0.209274, 0.016827),
(0.546858, 0.0, 0.016827),
(0.505231, 0.209274, 0.016827),
(0.386687, 0.386688, 0.016827),
(0.209273, 0.505232, 0.016827),
],
[[3, 19, 18, 2], [11, 27, 26, 10], [4, 20, 19, 3], [12, 28, 27, 11], [5, 21, 20, 4], [13, 29, 28, 12],
[6, 22, 21, 5], [14, 30, 29, 13], [7, 23, 22, 6], [15, 31, 30, 14], [8, 24, 23, 7], [1, 17, 16, 0],
[0, 16, 31, 15], [9, 25, 24, 8], [2, 18, 17, 1], [10, 26, 25, 9]]),
("CTP_5359",
Vector((5.50446, 2.41669, -0.0)),
[(0.0, 0.714247, 0.023261),
(-0.382683, 0.659879, 0.023261),
(-0.707107, 0.505049, 0.023261),
(-0.92388, 0.273331, 0.023261),
(-1.0, -0.0, 0.023261),
(-0.92388, -0.273331, 0.023261),
(-0.707107, -0.505049, 0.023261),
(-0.382683, -0.659879, 0.023261),
(-0.0, -0.714247, 0.023261),
(0.382683, -0.659879, 0.023261),
(0.707107, -0.505049, 0.023261),
(0.92388, -0.273331, 0.023261),
(1.0, 0.0, 0.023261),
(0.923879, 0.273331, 0.023261),
(0.707107, 0.505049, 0.023261),
(0.382683, 0.659879, 0.023261),
(-0.0, 0.303676, 0.023261),
(-0.162705, 0.28056, 0.023261),
(-0.30064, 0.214731, 0.023261),
(-0.392805, 0.116212, 0.023261),
(-0.425169, -0.0, 0.023261),
(-0.392805, -0.116212, 0.023261),
(-0.30064, -0.214731, 0.023261),
(-0.162705, -0.28056, 0.023261),
(-0.0, -0.303676, 0.023261),
(0.162705, -0.28056, 0.023261),
(0.30064, -0.214731, 0.023261),
(0.392805, -0.116212, 0.023261),
(0.425169, 0.0, 0.023261),
(0.392805, 0.116212, 0.023261),
(0.30064, 0.214731, 0.023261),
(0.162705, 0.28056, 0.023261),
],
[[3, 19, 18, 2], [11, 27, 26, 10], [4, 20, 19, 3], [12, 28, 27, 11], [5, 21, 20, 4], [13, 29, 28, 12],
[6, 22, 21, 5], [14, 30, 29, 13], [7, 23, 22, 6], [15, 31, 30, 14], [8, 24, 23, 7], [1, 17, 16, 0],
[0, 16, 31, 15], [9, 25, 24, 8], [2, 18, 17, 1], [10, 26, 25, 9]]),
("CTP_5424",
Vector((2.61824, 2.34147, 0.0)),
[(1.0, -1.0, 0.032334),
(-1.0, 1.0, 0.032334),
(1.0, 1.0, 0.032334),
(0.783867, -0.259989, 0.032334),
(-0.393641, 0.857073, 0.032334),
(0.73142, -0.116299, 0.032334),
(0.657754, 0.02916, 0.032334),
(0.564682, 0.172804, 0.032334),
(0.454497, 0.311098, 0.032334),
(0.329912, 0.440635, 0.032334),
(0.193995, 0.558227, 0.032334),
(0.050092, 0.660978, 0.032334),
(-0.098254, 0.746358, 0.032334),
(-0.247389, 0.812263, 0.032334),
],
[[3, 0, 2], [10, 9, 2], [2, 1, 4], [2, 4, 13], [5, 3, 2], [6, 5, 2], [2, 13, 12], [2, 12, 11], [7, 6, 2],
[8, 7, 2], [2, 11, 10], [9, 8, 2]]),
("CTP_3774",
Vector((2.61824, -2.52425, 0.0)),
[(1.0, 0.0, 0.020045),
(-1.0, 0.0, 0.020045),
(0.31903, -0.664947, 0.020045),
(-0.31903, -0.664947, 0.020045),
(-0.31903, 1.0, 0.020045),
(0.31903, 1.0, 0.020045),
(0.31903, 0.0, 0.020045),
(-0.31903, 0.0, 0.020045),
(-1.0, 0.614333, 0.020045),
(-0.614333, 1.0, 0.020045),
(-0.970643, 0.761921, 0.020045),
(-0.887041, 0.887041, 0.020045),
(-0.761921, 0.970643, 0.020045),
(0.614333, 1.0, 0.020045),
(1.0, 0.614333, 0.020045),
(0.761921, 0.970643, 0.020045),
(0.887041, 0.887041, 0.020045),
(0.970643, 0.761921, 0.020045),
(-0.31903, 0.614333, 0.020045),
(0.31903, 0.614333, 0.020045),
(0.31903, 0.761921, 0.020045),
(-0.31903, 0.761921, 0.020045),
(0.31903, 0.887041, 0.020045),
(-0.31903, 0.887041, 0.020045),
(0.614333, 0.614333, 0.020045),
(0.614333, 0.0, 0.020045),
(0.614333, 0.761921, 0.020045),
(0.614333, 0.887041, 0.020045),
(-0.614333, 0.761921, 0.020045),
(-0.614333, 0.0, 0.020045),
(-0.614333, 0.887041, 0.020045),
(-0.614333, 0.614333, 0.020045),
],
[[6, 25, 24, 19], [6, 19, 18, 7], [2, 6, 7, 3], [1, 29, 31, 8], [8, 31, 28, 10], [19, 24, 26, 20],
[18, 19, 20, 21], [21, 20, 22, 23], [10, 28, 30, 11], [20, 26, 27, 22], [22, 27, 13, 5], [23, 22, 5, 4],
[11, 30, 9, 12], [17, 16, 27, 26], [14, 17, 26, 24], [24, 25, 0, 14], [15, 13, 27, 16], [9, 30, 23, 4],
[31, 29, 7, 18], [28, 31, 18, 21], [30, 28, 21, 23]]),
("CTP_4473",
Vector((7.31539, 0.0, 0.0)),
[(0.24549, -1.0, 0.022454),
(-0.24549, -1.0, 0.022454),
(-0.24549, 1.0, 0.022454),
(0.24549, 1.0, 0.022454),
(1.0, 0.267452, 0.022454),
(1.0, -0.267452, 0.022454),
(-1.0, -0.267452, 0.022454),
(-1.0, 0.267452, 0.022454),
(0.24549, 0.267452, 0.022454),
(0.24549, -0.267452, 0.022454),
(-0.24549, 0.267452, 0.022454),
(-0.24549, -0.267452, 0.022454),
],
[[8, 3, 2, 10], [0, 9, 11, 1], [4, 8, 9, 5], [8, 10, 11, 9], [10, 7, 6, 11]]),
("CTP_4003",
Vector((4.91276, 0.0, 0.0)),
[(-1.0, -1.0, 0.026945),
(1.0, -1.0, 0.026945),
(-1.0, 1.0, 0.026945),
(-0.026763, -1.0, 0.026945),
(-0.026763, 1.0, 0.026945),
(1.0, -0.026763, 0.026945),
(0.238983, 0.965014, 0.026945),
(0.486619, 0.86244, 0.026945),
(0.699268, 0.699268, 0.026945),
(0.86244, 0.486619, 0.026945),
(0.965014, 0.238983, 0.026945),
(0.238983, -1.0, 0.026945),
(0.486619, -1.0, 0.026945),
(0.699268, -1.0, 0.026945),
(0.86244, -1.0, 0.026945),
(-0.026763, 0.479676, 0.026945),
(0.486619, 0.479676, 0.026945),
(0.699268, 0.479676, 0.026945),
(0.238983, 0.479676, 0.026945),
(0.865316, 0.479676, 0.026945),
(-1.0, 0.479676, 0.026945),
(0.86244, 0.479676, 0.026945),
(-0.026763, 0.238983, 0.026945),
(0.486619, 0.238983, 0.026945),
(0.699268, 0.238983, 0.026945),
(0.238983, 0.238983, 0.026945),
(-1.0, 0.238983, 0.026945),
(0.86244, 0.238983, 0.026945),
(-0.026763, -0.026763, 0.026945),
(0.486619, -0.026763, 0.026945),
(0.699268, -0.026763, 0.026945),
(0.238983, -0.026763, 0.026945),
(-1.0, -0.026763, 0.026945),
(0.86244, -0.026763, 0.026945),
],
[[0, 3, 28, 32], [4, 15, 18, 6], [6, 18, 16, 7], [7, 16, 17, 8], [8, 17, 21, 9], [9, 21, 19], [18, 15, 22, 25],
[19, 21, 27, 10], [16, 18, 25, 23], [17, 16, 23, 24], [20, 15, 4, 2], [21, 17, 24, 27], [27, 24, 30, 33],
[23, 25, 31, 29], [24, 23, 29, 30], [25, 22, 28, 31], [26, 22, 15, 20], [10, 27, 33, 5], [31, 28, 3, 11],
[33, 30, 13, 14], [29, 31, 11, 12], [5, 33, 14, 1], [30, 29, 12, 13], [32, 28, 22, 26]]),
("CTP_3430",
Vector((2.61824, 0.0, 0.0)),
[(-1.0, -1.0, 0.032334),
(1.0, -1.0, 0.032334),
(-1.0, 1.0, 0.032334),
(1.0, 1.0, 0.032334),
],
[[0, 1, 3, 2]]),
("CTP_7175",
Vector((0.0, 0.0, 0.0)),
[(-1.0, -1.0, 0.032334),
(1.0, -1.0, 0.032334),
(-1.0, 1.0, 0.032334),
(1.0, 1.0, 0.032334),
(0.0, 0.0, 0.032334),
(0.0, 0.0, 0.032334),
(0.0, 0.0, 0.032334),
(0.0, 0.0, 0.032334),
(0.0, 0.0, 0.032334),
(-0.636126, 0.636126, 0.032334),
(-0.636126, -0.636126, 0.032334),
(0.636126, -0.636126, 0.032334),
(0.636126, 0.636126, 0.032334),
],
[[10, 9, 2, 0], [11, 10, 0, 1], [12, 11, 1, 3], [9, 12, 3, 2]]),
]

View File

@ -0,0 +1,941 @@
import bpy
import bgl
import gpu
from gpu_extras.batch import batch_for_shader
import math
import sys
import random
import bmesh
from mathutils import (
Euler,
Matrix,
Vector,
Quaternion,
)
from mathutils.geometry import (
intersect_line_plane,
)
from math import (
sin,
cos,
pi,
)
import bpy_extras
from bpy_extras import view3d_utils
from bpy_extras.view3d_utils import (
region_2d_to_vector_3d,
region_2d_to_location_3d,
location_3d_to_region_2d,
)
# Cut Square
def CreateCutSquare(self, context):
""" Create a rectangle mesh """
far_limit = 10000.0
faces=[]
# Get the mouse coordinates
coord = self.mouse_path[0][0], self.mouse_path[0][1]
# New mesh
me = bpy.data.meshes.new('CMT_Square')
bm = bmesh.new()
bm.from_mesh(me)
# New object and link it to the scene
ob = bpy.data.objects.new('CMT_Square', me)
self.CurrentObj = ob
context.collection.objects.link(ob)
# Scene information
region = context.region
rv3d = context.region_data
depth_location = region_2d_to_vector_3d(region, rv3d, coord)
self.ViewVector = depth_location
# Get a point on a infinite plane and its direction
plane_normal = depth_location
plane_direction = plane_normal.normalized()
if self.snapCursor:
plane_point = context.scene.cursor.location
else:
plane_point = self.OpsObj.location if self.OpsObj is not None else Vector((0.0, 0.0, 0.0))
# Find the intersection of a line going thru each vertex and the infinite plane
for v_co in self.rectangle_coord:
vec = region_2d_to_vector_3d(region, rv3d, v_co)
p0 = region_2d_to_location_3d(region, rv3d,v_co, vec)
p1 = region_2d_to_location_3d(region, rv3d,v_co, vec) + plane_direction * far_limit
faces.append(bm.verts.new(intersect_line_plane(p0, p1, plane_point, plane_direction)))
# Update vertices index
bm.verts.index_update()
# New faces
t_face = bm.faces.new(faces)
# Set mesh
bm.to_mesh(me)
# Cut Line
def CreateCutLine(self, context):
""" Create a polygon mesh """
far_limit = 10000.0
vertices = []
faces = []
loc = []
# Get the mouse coordinates
coord = self.mouse_path[0][0], self.mouse_path[0][1]
# New mesh
me = bpy.data.meshes.new('CMT_Line')
bm = bmesh.new()
bm.from_mesh(me)
# New object and link it to the scene
ob = bpy.data.objects.new('CMT_Line', me)
self.CurrentObj = ob
context.collection.objects.link(ob)
# Scene information
region = context.region
rv3d = context.region_data
depth_location = region_2d_to_vector_3d(region, rv3d, coord)
self.ViewVector = depth_location
# Get a point on a infinite plane and its direction
plane_normal = depth_location
plane_direction = plane_normal.normalized()
if self.snapCursor:
plane_point = context.scene.cursor.location
else:
plane_point = self.OpsObj.location if self.OpsObj is not None else Vector((0.0, 0.0, 0.0))
# Use dict to remove doubles
# Find the intersection of a line going thru each vertex and the infinite plane
for idx, v_co in enumerate(list(dict.fromkeys(self.mouse_path))):
vec = region_2d_to_vector_3d(region, rv3d, v_co)
p0 = region_2d_to_location_3d(region, rv3d,v_co, vec)
p1 = region_2d_to_location_3d(region, rv3d,v_co, vec) + plane_direction * far_limit
loc.append(intersect_line_plane(p0, p1, plane_point, plane_direction))
vertices.append(bm.verts.new(loc[idx]))
if idx > 0:
bm.edges.new([vertices[idx-1],vertices[idx]])
faces.append(vertices[idx])
# Update vertices index
bm.verts.index_update()
# Nothing is selected, create close geometry
if self.CreateMode:
if self.Closed and len(vertices) > 1:
bm.edges.new([vertices[-1], vertices[0]])
bm.faces.new(faces)
else:
# Create faces if more than 2 vertices
if len(vertices) > 1 :
bm.edges.new([vertices[-1], vertices[0]])
bm.faces.new(faces)
bm.to_mesh(me)
# Cut Circle
def CreateCutCircle(self, context):
""" Create a circle mesh """
far_limit = 10000.0
FacesList = []
# Get the mouse coordinates
mouse_pos_x = self.mouse_path[0][0]
mouse_pos_y = self.mouse_path[0][1]
coord = self.mouse_path[0][0], self.mouse_path[0][1]
# Scene information
region = context.region
rv3d = context.region_data
depth_location = region_2d_to_vector_3d(region, rv3d, coord)
self.ViewVector = depth_location
# Get a point on a infinite plane and its direction
plane_point = context.scene.cursor.location if self.snapCursor else Vector((0.0, 0.0, 0.0))
plane_normal = depth_location
plane_direction = plane_normal.normalized()
# New mesh
me = bpy.data.meshes.new('CMT_Circle')
bm = bmesh.new()
bm.from_mesh(me)
# New object and link it to the scene
ob = bpy.data.objects.new('CMT_Circle', me)
self.CurrentObj = ob
context.collection.objects.link(ob)
# Create a circle using a tri fan
tris_fan, indices = draw_circle(self, mouse_pos_x, mouse_pos_y)
# Remove the vertex in the center to get the outer line of the circle
verts = tris_fan[1:]
# Find the intersection of a line going thru each vertex and the infinite plane
for vert in verts:
vec = region_2d_to_vector_3d(region, rv3d, vert)
p0 = region_2d_to_location_3d(region, rv3d, vert, vec)
p1 = p0 + plane_direction * far_limit
loc0 = intersect_line_plane(p0, p1, plane_point, plane_direction)
t_v0 = bm.verts.new(loc0)
FacesList.append(t_v0)
bm.verts.index_update()
bm.faces.new(FacesList)
bm.to_mesh(me)
def create_2d_circle(self, step, radius, rotation = 0):
""" Create the vertices of a 2d circle at (0,0) """
verts = []
for angle in range(0, 360, step):
verts.append(math.cos(math.radians(angle + rotation)) * radius)
verts.append(math.sin(math.radians(angle + rotation)) * radius)
verts.append(0.0)
verts.append(math.cos(math.radians(0.0 + rotation)) * radius)
verts.append(math.sin(math.radians(0.0 + rotation)) * radius)
verts.append(0.0)
return(verts)
def draw_circle(self, mouse_pos_x, mouse_pos_y):
""" Return the coordinates + indices of a circle using a triangle fan """
tris_verts = []
indices = []
segments = int(360 / self.stepAngle[self.step])
radius = self.mouse_path[1][0] - self.mouse_path[0][0]
rotation = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 2
# Get the vertices of a 2d circle
verts = create_2d_circle(self, self.stepAngle[self.step], radius, rotation)
# Create the first vertex at mouse position for the center of the circle
tris_verts.append(Vector((mouse_pos_x + self.xpos , mouse_pos_y + self.ypos)))
# For each vertex of the circle, add the mouse position and the translation
for idx in range(int(len(verts) / 3) - 1):
tris_verts.append(Vector((verts[idx * 3] + mouse_pos_x + self.xpos, \
verts[idx * 3 + 1] + mouse_pos_y + self.ypos)))
i1 = idx+1
i2 = idx+2 if idx+2 <= segments else 1
indices.append((0,i1,i2))
return(tris_verts, indices)
# Object dimensions (SCULPT Tools tips)
def objDiagonal(obj):
return ((obj.dimensions[0]**2) + (obj.dimensions[1]**2) + (obj.dimensions[2]**2))**0.5
# Bevel Update
def update_bevel(context):
selection = context.selected_objects.copy()
active = context.active_object
if len(selection) > 0:
for obj in selection:
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(True)
context.view_layer.objects.active = obj
# Test object name
# Subdive mode : Only bevel weight
if obj.data.name.startswith("S_") or obj.data.name.startswith("S "):
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.region_to_loop()
bpy.ops.transform.edge_bevelweight(value=1)
bpy.ops.object.mode_set(mode='OBJECT')
else:
# No subdiv mode : bevel weight + Crease + Sharp
CreateBevel(context, obj)
bpy.ops.object.select_all(action='DESELECT')
for obj in selection:
obj.select_set(True)
context.view_layer.objects.active = active
# Create bevel
def CreateBevel(context, CurrentObject):
# Save active object
SavActive = context.active_object
# Test if initial object has bevel
bevel_modifier = False
for modifier in SavActive.modifiers:
if modifier.name == 'Bevel':
bevel_modifier = True
if bevel_modifier:
# Active "CurrentObject"
context.view_layer.objects.active = CurrentObject
bpy.ops.object.mode_set(mode='EDIT')
# Edge mode
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
# Clear all
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.mark_sharp(clear=True)
bpy.ops.transform.edge_crease(value=-1)
bpy.ops.transform.edge_bevelweight(value=-1)
bpy.ops.mesh.select_all(action='DESELECT')
# Select (in radians) all 30° sharp edges
bpy.ops.mesh.edges_select_sharp(sharpness=0.523599)
# Apply bevel weight + Crease + Sharp to the selected edges
bpy.ops.mesh.mark_sharp()
bpy.ops.transform.edge_crease(value=1)
bpy.ops.transform.edge_bevelweight(value=1)
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')
CurrentObject.data.use_customdata_edge_bevel = True
for i in range(len(CurrentObject.data.edges)):
if CurrentObject.data.edges[i].select is True:
CurrentObject.data.edges[i].bevel_weight = 1.0
CurrentObject.data.edges[i].use_edge_sharp = True
bevel_modifier = False
for m in CurrentObject.modifiers:
if m.name == 'Bevel':
bevel_modifier = True
if bevel_modifier is False:
bpy.ops.object.modifier_add(type='BEVEL')
mod = context.object.modifiers[-1]
mod.limit_method = 'WEIGHT'
mod.width = 0.01
mod.profile = 0.699099
mod.use_clight_overlap = False
mod.segments = 3
mod.loop_slide = False
bpy.ops.object.shade_smooth()
context.object.data.use_auto_smooth = True
context.object.data.auto_smooth_angle = 1.0471975
# Restore the active object
context.view_layer.objects.active = SavActive
def MoveCursor(qRot, location, self):
""" In brush mode : Draw a circle around the brush """
if qRot is not None:
verts = create_2d_circle(self, 10, 1)
self.CLR_C.clear()
vc = Vector()
for idx in range(int(len(verts) / 3)):
vc.x = verts[idx * 3]
vc.y = verts[idx * 3 + 1]
vc.z = verts[idx * 3 + 2]
vc = qRot @ vc
self.CLR_C.append(vc.x)
self.CLR_C.append(vc.y)
self.CLR_C.append(vc.z)
def rot_axis_quat(vector1, vector2):
""" Find the rotation (quaternion) from vector 1 to vector 2"""
vector1 = vector1.normalized()
vector2 = vector2.normalized()
cosTheta = vector1.dot(vector2)
rotationAxis = Vector((0.0, 0.0, 0.0))
if (cosTheta < -1 + 0.001):
v = Vector((0.0, 1.0, 0.0))
#Get the vector at the right angles to both
rotationAxis = vector1.cross(v)
rotationAxis = rotationAxis.normalized()
q = Quaternion()
q.w = 0.0
q.x = rotationAxis.x
q.y = rotationAxis.y
q.z = rotationAxis.z
else:
rotationAxis = vector1.cross(vector2)
s = math.sqrt((1.0 + cosTheta) * 2.0)
invs = 1 / s
q = Quaternion()
q.w = s * 0.5
q.x = rotationAxis.x * invs
q.y = rotationAxis.y * invs
q.z = rotationAxis.z * invs
return q
# Picking (template)
def Picking(context, event):
""" Put the 3d cursor on the closest object"""
# get the context arguments
scene = context.scene
region = context.region
rv3d = context.region_data
coord = event.mouse_region_x, event.mouse_region_y
# get the ray from the viewport and mouse
view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
ray_target = ray_origin + view_vector
def visible_objects_and_duplis():
depsgraph = context.depsgraph
for dup in depsgraph.object_instances:
if dup.is_instance: # Real dupli instance
obj = dup.instance_object.original
yield (obj, dup.matrix.copy())
else: # Usual object
obj = dup.object.original
yield (obj, obj.matrix_world.copy())
def obj_ray_cast(obj, matrix):
# get the ray relative to the object
matrix_inv = matrix.inverted()
ray_origin_obj = matrix_inv @ ray_origin
ray_target_obj = matrix_inv @ ray_target
ray_direction_obj = ray_target_obj - ray_origin_obj
# cast the ray
success, location, normal, face_index = obj.ray_cast(ray_origin_obj, ray_direction_obj)
if success:
return location, normal, face_index
return None, None, None
# cast rays and find the closest object
best_length_squared = -1.0
best_obj = None
# cast rays and find the closest object
for obj, matrix in visible_objects_and_duplis():
if obj.type == 'MESH':
hit, normal, face_index = obj_ray_cast(obj, matrix)
if hit is not None:
hit_world = matrix @ hit
length_squared = (hit_world - ray_origin).length_squared
if best_obj is None or length_squared < best_length_squared:
scene.cursor.location = hit_world
best_length_squared = length_squared
best_obj = obj
else:
if best_obj is None:
depth_location = region_2d_to_vector_3d(region, rv3d, coord)
loc = region_2d_to_location_3d(region, rv3d, coord, depth_location)
scene.cursor.location = loc
def Pick(context, event, self, ray_max=10000.0):
region = context.region
rv3d = context.region_data
coord = event.mouse_region_x, event.mouse_region_y
view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
ray_target = ray_origin + (view_vector * ray_max)
def obj_ray_cast(obj, matrix):
matrix_inv = matrix.inverted()
ray_origin_obj = matrix_inv @ ray_origin
ray_target_obj = matrix_inv @ ray_target
success, hit, normal, face_index = obj.ray_cast(ray_origin_obj, ray_target_obj)
if success:
return hit, normal, face_index
return None, None, None
best_length_squared = ray_max * ray_max
best_obj = None
for obj in self.CList:
matrix = obj.matrix_world
hit, normal, face_index = obj_ray_cast(obj, matrix)
rotation = obj.rotation_euler.to_quaternion()
if hit is not None:
hit_world = matrix @ hit
length_squared = (hit_world - ray_origin).length_squared
if length_squared < best_length_squared:
best_length_squared = length_squared
best_obj = obj
hits = hit_world
ns = normal
fs = face_index
if best_obj is not None:
return hits, ns, rotation
return None, None, None
def SelectObject(self, copyobj):
copyobj.select_set(True)
for child in copyobj.children:
SelectObject(self, child)
if copyobj.parent is None:
bpy.context.view_layer.objects.active = copyobj
# Undo
def printUndo(self):
for l in self.UList:
print(l)
def UndoAdd(self, type, obj):
""" Create a backup mesh before apply the action to the object """
if obj is None:
return
if type != "DUPLICATE":
bm = bmesh.new()
bm.from_mesh(obj.data)
self.UndoOps.append((obj, type, bm))
else:
self.UndoOps.append((obj, type, None))
def UndoListUpdate(self):
self.UList.append((self.UndoOps.copy()))
self.UList_Index += 1
self.UndoOps.clear()
def Undo(self):
if self.UList_Index < 0:
return
# get previous mesh
for o in self.UList[self.UList_Index]:
if o[1] == "MESH":
bm = o[2]
bm.to_mesh(o[0].data)
SelectObjList = bpy.context.selected_objects.copy()
Active_Obj = bpy.context.active_object
bpy.ops.object.select_all(action='TOGGLE')
for o in self.UList[self.UList_Index]:
if o[1] == "REBOOL":
o[0].select_set(True)
o[0].hide_viewport = False
if o[1] == "DUPLICATE":
o[0].select_set(True)
o[0].hide_viewport = False
bpy.ops.object.delete(use_global=False)
for so in SelectObjList:
bpy.data.objects[so.name].select_set(True)
bpy.context.view_layer.objects.active = Active_Obj
self.UList_Index -= 1
self.UList[self.UList_Index + 1:] = []
def duplicateObject(self):
if self.Instantiate:
bpy.ops.object.duplicate_move_linked(
OBJECT_OT_duplicate={
"linked": True,
"mode": 'TRANSLATION',
},
TRANSFORM_OT_translate={
"value": (0, 0, 0),
},
)
else:
bpy.ops.object.duplicate_move(
OBJECT_OT_duplicate={
"linked": False,
"mode": 'TRANSLATION',
},
TRANSFORM_OT_translate={
"value": (0, 0, 0),
},
)
ob_new = bpy.context.active_object
ob_new.location = self.CurLoc
v = Vector()
v.x = v.y = 0.0
v.z = self.BrushDepthOffset
ob_new.location += self.qRot * v
if self.ObjectMode:
ob_new.scale = self.ObjectBrush.scale
if self.ProfileMode:
ob_new.scale = self.ProfileBrush.scale
e = Euler()
e.x = e.y = 0.0
e.z = self.aRotZ / 25.0
# If duplicate with a grid, no random rotation (each mesh in the grid is already rotated randomly)
if (self.alt is True) and ((self.nbcol + self.nbrow) < 3):
if self.RandomRotation:
e.z += random.random()
qe = e.to_quaternion()
qRot = self.qRot * qe
ob_new.rotation_mode = 'QUATERNION'
ob_new.rotation_quaternion = qRot
ob_new.rotation_mode = 'XYZ'
if (ob_new.display_type == "WIRE") and (self.BrushSolidify is False):
ob_new.hide_viewport = True
if self.BrushSolidify:
ob_new.display_type = "SOLID"
ob_new.show_in_front = False
for o in bpy.context.selected_objects:
UndoAdd(self, "DUPLICATE", o)
if len(bpy.context.selected_objects) > 0:
bpy.ops.object.select_all(action='TOGGLE')
for o in self.all_sel_obj_list:
o.select_set(True)
bpy.context.view_layer.objects.active = self.OpsObj
def update_grid(self, context):
"""
Thanks to batFINGER for his help :
source : http://blender.stackexchange.com/questions/55864/multiple-meshes-not-welded-with-pydata
"""
verts = []
edges = []
faces = []
numface = 0
if self.nbcol < 1:
self.nbcol = 1
if self.nbrow < 1:
self.nbrow = 1
if self.gapx < 0:
self.gapx = 0
if self.gapy < 0:
self.gapy = 0
# Get the data from the profils or the object
if self.ProfileMode:
brush = bpy.data.objects.new(
self.Profils[self.nProfil][0],
bpy.data.meshes[self.Profils[self.nProfil][0]]
)
obj = bpy.data.objects["CT_Profil"]
obfaces = brush.data.polygons
obverts = brush.data.vertices
lenverts = len(obverts)
else:
brush = bpy.data.objects["CarverBrushCopy"]
obj = context.selected_objects[0]
obverts = brush.data.vertices
obfaces = brush.data.polygons
lenverts = len(brush.data.vertices)
# Gap between each row / column
gapx = self.gapx
gapy = self.gapy
# Width of each row / column
widthx = brush.dimensions.x * self.scale_x
widthy = brush.dimensions.y * self.scale_y
# Compute the corners so the new object will be always at the center
left = -((self.nbcol - 1) * (widthx + gapx)) / 2
start = -((self.nbrow - 1) * (widthy + gapy)) / 2
for i in range(self.nbrow * self.nbcol):
row = i % self.nbrow
col = i // self.nbrow
startx = left + ((widthx + gapx) * col)
starty = start + ((widthy + gapy) * row)
# Add random rotation
if (self.RandomRotation) and not (self.GridScaleX or self.GridScaleY):
rotmat = Matrix.Rotation(math.radians(360 * random.random()), 4, 'Z')
for v in obverts:
v.co = v.co @ rotmat
verts.extend([((v.co.x - startx, v.co.y - starty, v.co.z)) for v in obverts])
faces.extend([[v + numface * lenverts for v in p.vertices] for p in obfaces])
numface += 1
# Update the mesh
# Create mesh data
mymesh = bpy.data.meshes.new("CT_Profil")
# Generate mesh data
mymesh.from_pydata(verts, edges, faces)
# Calculate the edges
mymesh.update(calc_edges=True)
# Update data
obj.data = mymesh
# Make the object active to remove doubles
context.view_layer.objects.active = obj
def boolean_operation(bool_type="DIFFERENCE"):
ActiveObj = bpy.context.active_object
sel_index = 0 if bpy.context.selected_objects[0] != bpy.context.active_object else 1
# bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_SOLIDIFY")
bool_name = "CT_" + bpy.context.selected_objects[sel_index].name
BoolMod = ActiveObj.modifiers.new(bool_name, "BOOLEAN")
BoolMod.object = bpy.context.selected_objects[sel_index]
BoolMod.operation = bool_type
bpy.context.selected_objects[sel_index].display_type = 'WIRE'
while ActiveObj.modifiers.find(bool_name) > 0:
bpy.ops.object.modifier_move_up(modifier=bool_name)
def Rebool(context, self):
target_obj = context.active_object
Brush = context.selected_objects[1]
Brush.display_type = "WIRE"
#Deselect all
bpy.ops.object.select_all(action='TOGGLE')
target_obj.display_type = "SOLID"
target_obj.select_set(True)
bpy.ops.object.duplicate()
rebool_obj = context.active_object
m = rebool_obj.modifiers.new("CT_INTERSECT", "BOOLEAN")
m.operation = "INTERSECT"
m.object = Brush
m = target_obj.modifiers.new("CT_DIFFERENCE", "BOOLEAN")
m.operation = "DIFFERENCE"
m.object = Brush
for mb in target_obj.modifiers:
if mb.type == 'BEVEL':
mb.show_viewport = False
if self.ObjectBrush or self.ProfileBrush:
rebool_obj.show_in_front = False
try:
bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_SOLIDIFY")
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
self.report({'ERROR'}, str(exc_value))
if self.dont_apply_boolean is False:
try:
bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_INTERSECT")
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
self.report({'ERROR'}, str(exc_value))
bpy.ops.object.select_all(action='TOGGLE')
for mb in target_obj.modifiers:
if mb.type == 'BEVEL':
mb.show_viewport = True
context.view_layer.objects.active = target_obj
target_obj.select_set(True)
if self.dont_apply_boolean is False:
try:
bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_DIFFERENCE")
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
self.report({'ERROR'}, str(exc_value))
bpy.ops.object.select_all(action='TOGGLE')
rebool_obj.select_set(True)
def createMeshFromData(self):
if self.Profils[self.nProfil][0] not in bpy.data.meshes:
# Create mesh and object
me = bpy.data.meshes.new(self.Profils[self.nProfil][0])
# Create mesh from given verts, faces.
me.from_pydata(self.Profils[self.nProfil][2], [], self.Profils[self.nProfil][3])
me.validate(verbose=True, clean_customdata=True)
# Update mesh with new data
me.update()
if "CT_Profil" not in bpy.data.objects:
ob = bpy.data.objects.new("CT_Profil", bpy.data.meshes[self.Profils[self.nProfil][0]])
ob.location = Vector((0.0, 0.0, 0.0))
# Link object to scene and make active
bpy.context.collection.objects.link(ob)
bpy.context.scene.update()
bpy.context.view_layer.objects.active = ob
ob.select_set(True)
ob.location = Vector((10000.0, 0.0, 0.0))
ob.display_type = "WIRE"
self.SolidifyPossible = True
else:
bpy.data.objects["CT_Profil"].data = bpy.data.meshes[self.Profils[self.nProfil][0]]
def Selection_Save_Restore(self):
if "CT_Profil" in bpy.data.objects:
Selection_Save(self)
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects["CT_Profil"].select_set(True)
bpy.context.view_layer.objects.active = bpy.data.objects["CT_Profil"]
if bpy.data.objects["CT_Profil"] in self.all_sel_obj_list:
self.all_sel_obj_list.remove(bpy.data.objects["CT_Profil"])
bpy.ops.object.delete(use_global=False)
Selection_Restore(self)
def Selection_Save(self):
obj_name = getattr(bpy.context.active_object, "name", None)
self.all_sel_obj_list = bpy.context.selected_objects.copy()
self.save_active_obj = obj_name
def Selection_Restore(self):
for o in self.all_sel_obj_list:
o.select_set(True)
if self.save_active_obj:
bpy.context.view_layer.objects.active = bpy.data.objects.get(self.save_active_obj, None)
def Snap_Cursor(self, context, event, mouse_pos):
""" Find the closest position on the overlay grid and snap the mouse on it """
# Get the context arguments
region = context.region
rv3d = context.region_data
# Get the VIEW3D area
for i, a in enumerate(context.screen.areas):
if a.type == 'VIEW_3D':
space = context.screen.areas[i].spaces.active
# Get the grid overlay for the VIEW_3D
grid_scale = space.overlay.grid_scale
grid_subdivisions = space.overlay.grid_subdivisions
# Use the grid scale and subdivision to get the increment
increment = (grid_scale / grid_subdivisions)
half_increment = increment / 2
# Convert the 2d location of the mouse in 3d
for index, loc in enumerate(reversed(mouse_pos)):
mouse_loc_3d = region_2d_to_location_3d(region, rv3d, loc, (0, 0, 0))
# Get the remainder from the mouse location and the ratio
# Test if the remainder > to the half of the increment
for i in range(3):
modulo = mouse_loc_3d[i] % increment
if modulo < half_increment:
modulo = - modulo
else:
modulo = increment - modulo
# Add the remainder to get the closest location on the grid
mouse_loc_3d[i] = mouse_loc_3d[i] + modulo
# Get the snapped 2d location
snap_loc_2d = location_3d_to_region_2d(region, rv3d, mouse_loc_3d)
# Replace the last mouse location by the snapped location
if len(self.mouse_path) > 0:
self.mouse_path[len(self.mouse_path) - (index + 1) ] = tuple(snap_loc_2d)
def mini_grid(self, context, color):
""" Draw a snap mini grid around the cursor based on the overlay grid"""
# Get the context arguments
region = context.region
rv3d = context.region_data
# Get the VIEW3D area
for i, a in enumerate(context.screen.areas):
if a.type == 'VIEW_3D':
space = context.screen.areas[i].spaces.active
screen_height = context.screen.areas[i].height
screen_width = context.screen.areas[i].width
#Draw the snap grid, only in ortho view
if not space.region_3d.is_perspective :
grid_scale = space.overlay.grid_scale
grid_subdivisions = space.overlay.grid_subdivisions
increment = (grid_scale / grid_subdivisions)
# Get the 3d location of the mouse forced to a snap value in the operator
mouse_coord = self.mouse_path[len(self.mouse_path) - 1]
snap_loc = region_2d_to_location_3d(region, rv3d, mouse_coord, (0, 0, 0))
# Add the increment to get the closest location on the grid
snap_loc[0] += increment
snap_loc[1] += increment
# Get the 2d location of the snap location
snap_loc = location_3d_to_region_2d(region, rv3d, snap_loc)
origin = location_3d_to_region_2d(region, rv3d, (0,0,0))
# Get the increment value
snap_value = snap_loc[0] - mouse_coord[0]
grid_coords = []
# Draw lines on X and Z axis from the cursor through the screen
grid_coords = [
(0, mouse_coord[1]), (screen_width, mouse_coord[1]),
(mouse_coord[0], 0), (mouse_coord[0], screen_height)
]
# Draw a mlini grid around the cursor to show the snap options
grid_coords += [
(mouse_coord[0] + snap_value, mouse_coord[1] + 25 + snap_value),
(mouse_coord[0] + snap_value, mouse_coord[1] - 25 - snap_value),
(mouse_coord[0] + 25 + snap_value, mouse_coord[1] + snap_value),
(mouse_coord[0] - 25 - snap_value, mouse_coord[1] + snap_value),
(mouse_coord[0] - snap_value, mouse_coord[1] + 25 + snap_value),
(mouse_coord[0] - snap_value, mouse_coord[1] - 25 - snap_value),
(mouse_coord[0] + 25 + snap_value, mouse_coord[1] - snap_value),
(mouse_coord[0] - 25 - snap_value, mouse_coord[1] - snap_value),
]
draw_shader(self, color, 0.3, 'LINES', grid_coords, size=2)
def draw_shader(self, color, alpha, type, coords, size=1, indices=None):
""" Create a batch for a draw type """
bgl.glEnable(bgl.GL_BLEND)
bgl.glEnable(bgl.GL_LINE_SMOOTH)
if type =='POINTS':
bgl.glPointSize(size)
else:
bgl.glLineWidth(size)
try:
if len(coords[0])>2:
shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR')
else:
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
batch = batch_for_shader(shader, type, {"pos": coords}, indices=indices)
shader.bind()
shader.uniform_float("color", (color[0], color[1], color[2], alpha))
batch.draw(shader)
bgl.glLineWidth(1)
bgl.glPointSize(1)
bgl.glDisable(bgl.GL_LINE_SMOOTH)
bgl.glDisable(bgl.GL_BLEND)
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
self.report({'ERROR'}, str(exc_value))