3D Print Toolbox: Add hollow out #105194
@ -123,21 +123,28 @@ class SceneProperties(PropertyGroup):
|
|||||||
)
|
)
|
||||||
hollow_offset: FloatProperty(
|
hollow_offset: FloatProperty(
|
||||||
name="Offset",
|
name="Offset",
|
||||||
description = "Surface offset in relation to original mesh. Negative -> inwards",
|
description="Surface offset in relation to original mesh",
|
||||||
default = -5.0,
|
default=1.0,
|
||||||
|
subtype='DISTANCE',
|
||||||
|
min=0.0,
|
||||||
|
step=1,
|
||||||
|
)
|
||||||
|
hollow_voxel_size: FloatProperty(
|
||||||
|
name="Voxel size",
|
||||||
|
description="Size of the voxel used for volume evaluation. Lower values preserve finer details",
|
||||||
|
default=1.0,
|
||||||
|
min=0.0001,
|
||||||
|
step=1,
|
||||||
subtype='DISTANCE',
|
subtype='DISTANCE',
|
||||||
)
|
)
|
||||||
hollow_resolution: FloatProperty(
|
make_hollow_duplicate: BoolProperty(
|
||||||
name = "Resolution",
|
name="Hollow Duplicate",
|
||||||
description = "Resolution of the voxel grid used for hollowing",
|
description="Create hollowed out copy of the object",
|
||||||
default = 5.0,
|
|
||||||
min = 0.01,
|
|
||||||
subtype = 'DISTANCE',
|
|
||||||
)
|
)
|
||||||
hollow_create: BoolProperty(
|
make_hollow_inside: BoolProperty(
|
||||||
name = "Create Hollow Copy",
|
name="Inside",
|
||||||
description = "Create a hollow copy of the target object, with applied modifiers and scale",
|
description="Offset surface inside of the object",
|
||||||
default = False
|
default=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -146,7 +153,7 @@ classes = (
|
|||||||
|
|
||||||
ui.VIEW3D_PT_print3d_analyze,
|
ui.VIEW3D_PT_print3d_analyze,
|
||||||
ui.VIEW3D_PT_print3d_cleanup,
|
ui.VIEW3D_PT_print3d_cleanup,
|
||||||
ui.VIEW3D_PT_print3d_transform,
|
ui.VIEW3D_PT_print3d_edit,
|
||||||
ui.VIEW3D_PT_print3d_export,
|
ui.VIEW3D_PT_print3d_export,
|
||||||
|
|
||||||
operators.MESH_OT_print3d_info_volume,
|
operators.MESH_OT_print3d_info_volume,
|
||||||
|
@ -826,27 +826,34 @@ class MESH_OT_print3d_export(Operator):
|
|||||||
|
|
||||||
class MESH_OT_print3d_hollow(Operator):
|
class MESH_OT_print3d_hollow(Operator):
|
||||||
bl_idname = "mesh.print3d_hollow"
|
bl_idname = "mesh.print3d_hollow"
|
||||||
bl_label = "Create offset surface"
|
bl_label = "Hollow"
|
||||||
bl_description = "Create offset surface for hollowing objects"
|
bl_description = "Create offset surface"
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
offset: FloatProperty(
|
offset: FloatProperty(
|
||||||
name="Offset",
|
name="Offset",
|
||||||
description = "Surface offset in relation to original mesh. Negative -> inwards",
|
description="Surface offset in relation to original mesh",
|
||||||
default = -5.0,
|
default=1.0,
|
||||||
subtype='DISTANCE',
|
subtype='DISTANCE',
|
||||||
|
min=0.0,
|
||||||
|
step=1,
|
||||||
)
|
)
|
||||||
resolution: FloatProperty(
|
voxel_size: FloatProperty(
|
||||||
name = "Resolution",
|
name="Voxel size",
|
||||||
MikhailRachinskiy marked this conversation as resolved
|
|||||||
description = "Resolution of the voxel grid used for hollowing",
|
description="Size of the voxel used for volume evaluation. Lower values preserve finer details",
|
||||||
default=5.0,
|
default=5.0,
|
||||||
MikhailRachinskiy marked this conversation as resolved
Mikhail Rachinskiy
commented
default=1 default=1
|
|||||||
min = 0.01,
|
min=0.0001,
|
||||||
|
step=1,
|
||||||
subtype='DISTANCE',
|
subtype='DISTANCE',
|
||||||
)
|
)
|
||||||
create_hollow: BoolProperty(
|
make_hollow_duplicate: BoolProperty(
|
||||||
name = "Create Hollow Copy",
|
name="Hollow Duplicate",
|
||||||
description = "Create a hollow copy of the target object, with applied modifiers and scale",
|
description="Create hollowed out copy of the object",
|
||||||
default = False
|
)
|
||||||
|
make_hollow_inside: BoolProperty(
|
||||||
|
name="Inside",
|
||||||
|
description="Offset surface inside of the object",
|
||||||
|
default=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
MikhailRachinskiy marked this conversation as resolved
Mikhail Rachinskiy
commented
It's better to not create unnecessary local variables, especially when they are rarely used. It's better to not create unnecessary local variables, especially when they are rarely used.
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@ -877,62 +884,64 @@ class MESH_OT_print3d_hollow(Operator):
|
|||||||
tris.shape = (-1, 3)
|
tris.shape = (-1, 3)
|
||||||
|
|
||||||
# Generate VDB levelset
|
# Generate VDB levelset
|
||||||
half_width = max(3.0, math.ceil(abs(self.offset) / self.resolution) + 2.0) # half_width has to envelop offset
|
half_width = max(3.0, math.ceil(abs(self.offset) / self.voxel_size) + 2.0) # half_width has to envelop offset
|
||||||
trans = vdb.Transform()
|
trans = vdb.Transform()
|
||||||
trans.scale(self.resolution)
|
trans.scale(self.voxel_size)
|
||||||
levelset = vdb.FloatGrid.createLevelSetFromPolygons(verts, triangles=tris, transform=trans, halfWidth=half_width)
|
levelset = vdb.FloatGrid.createLevelSetFromPolygons(verts, triangles=tris, transform=trans, halfWidth=half_width)
|
||||||
|
|
||||||
# Generate offset surface
|
# Generate offset surface
|
||||||
|
if self.make_hollow_inside:
|
||||||
|
newverts, newquads = levelset.convertToQuads(-self.offset)
|
||||||
|
else:
|
||||||
newverts, newquads = levelset.convertToQuads(self.offset)
|
newverts, newquads = levelset.convertToQuads(self.offset)
|
||||||
|
|
||||||
polys = list(newquads)
|
polys = list(newquads)
|
||||||
|
|
||||||
# Instantiate new object in Blender
|
# Instantiate new object in Blender
|
||||||
mesh_offset = bpy.data.meshes.new(mesh_target.name + ' offset')
|
|
||||||
mesh_offset.from_pydata(newverts, [], polys)
|
|
||||||
obj_offset = bpy.data.objects.new(obj.name + ' offset', mesh_offset)
|
|
||||||
bpy.context.collection.objects.link(obj_offset)
|
|
||||||
|
|
||||||
if not self.create_hollow:
|
|
||||||
# For some reason OpenVDB has inverted normals
|
|
||||||
mesh_offset.flip_normals()
|
|
||||||
# Translate surface object to target object's position
|
|
||||||
obj_offset.matrix_world.translation = obj.matrix_world.translation
|
|
||||||
# This mesh will not be used anymore
|
|
||||||
bpy.data.meshes.remove(mesh_target)
|
|
||||||
else:
|
|
||||||
# Create a copy of the target object with applied modifiers and scale
|
|
||||||
obj_hollow = bpy.data.objects.new(obj.name + " hollow", mesh_target)
|
|
||||||
bpy.context.collection.objects.link(obj_hollow)
|
|
||||||
if self.offset < 0.0:
|
|
||||||
# Offset surface already has normals as they should, see above
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
# Offset surface is outside, correct normals, see above
|
|
||||||
mesh_offset.flip_normals()
|
|
||||||
# Original surface is inside, flip its normals
|
|
||||||
mesh_target.flip_normals()
|
|
||||||
|
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
mesh_offset = bpy.data.meshes.new(mesh_target.name + ' offset')
|
||||||
|
mesh_offset.from_pydata(newverts, [], polys)
|
||||||
|
|
||||||
|
# For some reason OpenVDB has inverted normals
|
||||||
|
mesh_offset.flip_normals()
|
||||||
|
obj_offset = bpy.data.objects.new(obj.name + ' offset', mesh_offset)
|
||||||
|
obj_offset.matrix_world.translation = obj.matrix_world.translation
|
||||||
|
bpy.context.collection.objects.link(obj_offset)
|
||||||
obj_offset.select_set(True)
|
obj_offset.select_set(True)
|
||||||
|
context.view_layer.objects.active = obj_offset
|
||||||
|
|
||||||
|
if self.make_hollow_duplicate:
|
||||||
|
obj_hollow = bpy.data.objects.new(obj.name + " hollow", mesh_target)
|
||||||
|
bpy.context.collection.objects.link(obj_hollow)
|
||||||
|
obj_hollow.matrix_world.translation = obj.matrix_world.translation
|
||||||
obj_hollow.select_set(True)
|
obj_hollow.select_set(True)
|
||||||
|
if self.make_hollow_inside:
|
||||||
|
mesh_offset.flip_normals()
|
||||||
|
else:
|
||||||
|
mesh_target.flip_normals()
|
||||||
context.view_layer.objects.active = obj_hollow
|
context.view_layer.objects.active = obj_hollow
|
||||||
bpy.ops.object.join()
|
bpy.ops.object.join()
|
||||||
|
else:
|
||||||
|
bpy.data.meshes.remove(mesh_target)
|
||||||
|
|
||||||
# Translate hollow object to target object's position
|
print_3d = context.scene.print_3d
|
||||||
obj_hollow.matrix_world.translation = obj.matrix_world.translation
|
print_3d.hollow_offset = self.offset
|
||||||
|
print_3d.hollow_voxel_size = self.voxel_size
|
||||||
|
print_3d.make_hollow_duplicate = self.make_hollow_duplicate
|
||||||
|
print_3d.make_hollow_inside = self.make_hollow_inside
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
print_3d = context.scene.print_3d
|
print_3d = context.scene.print_3d
|
||||||
self.offset = print_3d.hollow_offset
|
self.offset = print_3d.hollow_offset
|
||||||
self.resolution = print_3d.hollow_resolution
|
self.voxel_size = print_3d.hollow_voxel_size
|
||||||
self.create_hollow = print_3d.hollow_create
|
self.make_hollow_duplicate = print_3d.make_hollow_duplicate
|
||||||
|
self.make_hollow_inside = print_3d.make_hollow_inside
|
||||||
if context.mode == 'EDIT_MESH':
|
if context.mode == 'EDIT_MESH':
|
||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
|
wm = context.window_manager
|
||||||
return self.execute(context)
|
return wm.invoke_props_dialog(self)
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,8 +102,8 @@ class VIEW3D_PT_print3d_cleanup(View3DPrintPanel, Panel):
|
|||||||
# layout.operator("mesh.print3d_clean_thin", text="Wall Thickness")
|
# layout.operator("mesh.print3d_clean_thin", text="Wall Thickness")
|
||||||
|
|
||||||
|
|
||||||
class VIEW3D_PT_print3d_transform(View3DPrintPanel, Panel):
|
class VIEW3D_PT_print3d_edit(View3DPrintPanel, Panel):
|
||||||
bl_label = "Transform"
|
bl_label = "Edit"
|
||||||
bl_options = {"DEFAULT_CLOSED"}
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
@ -111,6 +111,7 @@ class VIEW3D_PT_print3d_transform(View3DPrintPanel, Panel):
|
|||||||
|
|
||||||
print_3d = context.scene.print_3d
|
print_3d = context.scene.print_3d
|
||||||
|
|
||||||
|
layout.operator("mesh.print3d_hollow")
|
||||||
layout.label(text="Scale To")
|
layout.label(text="Scale To")
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.operator("mesh.print3d_scale_to_volume", text="Volume")
|
row.operator("mesh.print3d_scale_to_volume", text="Volume")
|
||||||
@ -118,12 +119,6 @@ class VIEW3D_PT_print3d_transform(View3DPrintPanel, Panel):
|
|||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.operator("mesh.print3d_align_to_xy", text="Align XY")
|
row.operator("mesh.print3d_align_to_xy", text="Align XY")
|
||||||
row.prop(print_3d, "use_alignxy_face_area")
|
row.prop(print_3d, "use_alignxy_face_area")
|
||||||
layout.label(text="Hollow")
|
|
||||||
col = layout.column(align=True)
|
|
||||||
col.prop(print_3d, "hollow_create")
|
|
||||||
col.prop(print_3d, "hollow_offset")
|
|
||||||
col.prop(print_3d, "hollow_resolution")
|
|
||||||
col.operator("mesh.print3d_hollow", text="Create Offset Surface")
|
|
||||||
|
|
||||||
|
|
||||||
class VIEW3D_PT_print3d_export(View3DPrintPanel, Panel):
|
class VIEW3D_PT_print3d_export(View3DPrintPanel, Panel):
|
||||||
|
Loading…
Reference in New Issue
Block a user
Enum values are supposed to be in
UPPERCASE
I noticed inconsistent use of double vs single quotation marks, Blender code guidelines recommends: single quotes when using enum values, double quotes everything else.
Sorry, my bad. It should be fixed now.