3D Print Toolbox: Add hollow out #105194
@ -122,22 +122,29 @@ class SceneProperties(PropertyGroup):
|
||||
max=math.radians(90.0),
|
||||
)
|
||||
hollow_offset: FloatProperty(
|
||||
name = "Offset",
|
||||
description = "Surface offset in relation to original mesh. Negative -> inwards",
|
||||
default = -5.0,
|
||||
subtype = 'DISTANCE',
|
||||
name="Offset",
|
||||
description="Surface offset in relation to original mesh",
|
||||
default=1.0,
|
||||
subtype='DISTANCE',
|
||||
min=0.0,
|
||||
step=1,
|
||||
)
|
||||
hollow_resolution: FloatProperty(
|
||||
name = "Resolution",
|
||||
description = "Resolution of the voxel grid used for hollowing",
|
||||
default = 5.0,
|
||||
min = 0.01,
|
||||
subtype = 'DISTANCE',
|
||||
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',
|
||||
)
|
||||
hollow_create: BoolProperty(
|
||||
name = "Create Hollow Copy",
|
||||
description = "Create a hollow copy of the target object, with applied modifiers and scale",
|
||||
default = False
|
||||
make_hollow_duplicate: BoolProperty(
|
||||
name="Hollow Duplicate",
|
||||
description="Create hollowed out copy of the object",
|
||||
)
|
||||
make_hollow_inside: BoolProperty(
|
||||
name="Inside",
|
||||
description="Offset surface inside of the object",
|
||||
default=True,
|
||||
)
|
||||
|
||||
|
||||
@ -146,7 +153,7 @@ classes = (
|
||||
|
||||
ui.VIEW3D_PT_print3d_analyze,
|
||||
ui.VIEW3D_PT_print3d_cleanup,
|
||||
ui.VIEW3D_PT_print3d_transform,
|
||||
ui.VIEW3D_PT_print3d_edit,
|
||||
ui.VIEW3D_PT_print3d_export,
|
||||
|
||||
operators.MESH_OT_print3d_info_volume,
|
||||
|
@ -826,27 +826,34 @@ class MESH_OT_print3d_export(Operator):
|
||||
|
||||
class MESH_OT_print3d_hollow(Operator):
|
||||
bl_idname = "mesh.print3d_hollow"
|
||||
bl_label = "Create offset surface"
|
||||
bl_description = "Create offset surface for hollowing objects"
|
||||
bl_label = "Hollow"
|
||||
bl_description = "Create offset surface"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
offset: FloatProperty(
|
||||
name = "Offset",
|
||||
description = "Surface offset in relation to original mesh. Negative -> inwards",
|
||||
default = -5.0,
|
||||
subtype = 'DISTANCE',
|
||||
name="Offset",
|
||||
description="Surface offset in relation to original mesh",
|
||||
default=1.0,
|
||||
subtype='DISTANCE',
|
||||
min=0.0,
|
||||
step=1,
|
||||
)
|
||||
resolution: FloatProperty(
|
||||
name = "Resolution",
|
||||
description = "Resolution of the voxel grid used for hollowing",
|
||||
default = 5.0,
|
||||
min = 0.01,
|
||||
subtype = 'DISTANCE',
|
||||
voxel_size: FloatProperty(
|
||||
name="Voxel size",
|
||||
MikhailRachinskiy marked this conversation as resolved
|
||||
description="Size of the voxel used for volume evaluation. Lower values preserve finer details",
|
||||
default=5.0,
|
||||
MikhailRachinskiy marked this conversation as resolved
Mikhail Rachinskiy
commented
default=1 default=1
|
||||
min=0.0001,
|
||||
step=1,
|
||||
subtype='DISTANCE',
|
||||
)
|
||||
create_hollow: BoolProperty(
|
||||
name = "Create Hollow Copy",
|
||||
description = "Create a hollow copy of the target object, with applied modifiers and scale",
|
||||
default = False
|
||||
make_hollow_duplicate: BoolProperty(
|
||||
name="Hollow Duplicate",
|
||||
description="Create hollowed out copy of the object",
|
||||
)
|
||||
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):
|
||||
@ -877,62 +884,64 @@ class MESH_OT_print3d_hollow(Operator):
|
||||
tris.shape = (-1, 3)
|
||||
|
||||
# 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.scale(self.resolution)
|
||||
trans.scale(self.voxel_size)
|
||||
levelset = vdb.FloatGrid.createLevelSetFromPolygons(verts, triangles=tris, transform=trans, halfWidth=half_width)
|
||||
|
||||
# Generate offset surface
|
||||
newverts, newquads = levelset.convertToQuads(self.offset)
|
||||
if self.make_hollow_inside:
|
||||
newverts, newquads = levelset.convertToQuads(-self.offset)
|
||||
else:
|
||||
newverts, newquads = levelset.convertToQuads(self.offset)
|
||||
|
||||
polys = list(newquads)
|
||||
|
||||
# Instantiate new object in Blender
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
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
|
||||
# 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)
|
||||
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)
|
||||
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.select_all(action='DESELECT')
|
||||
obj_offset.select_set(True)
|
||||
obj_hollow.matrix_world.translation = obj.matrix_world.translation
|
||||
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
|
||||
bpy.ops.object.join()
|
||||
else:
|
||||
bpy.data.meshes.remove(mesh_target)
|
||||
|
||||
# Translate hollow object to target object's position
|
||||
obj_hollow.matrix_world.translation = obj.matrix_world.translation
|
||||
|
||||
print_3d = context.scene.print_3d
|
||||
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'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
print_3d = context.scene.print_3d
|
||||
self.offset = print_3d.hollow_offset
|
||||
self.resolution = print_3d.hollow_resolution
|
||||
self.create_hollow = print_3d.hollow_create
|
||||
self.voxel_size = print_3d.hollow_voxel_size
|
||||
self.make_hollow_duplicate = print_3d.make_hollow_duplicate
|
||||
self.make_hollow_inside = print_3d.make_hollow_inside
|
||||
if context.mode == 'EDIT_MESH':
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
return self.execute(context)
|
||||
wm = context.window_manager
|
||||
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")
|
||||
|
||||
|
||||
class VIEW3D_PT_print3d_transform(View3DPrintPanel, Panel):
|
||||
bl_label = "Transform"
|
||||
class VIEW3D_PT_print3d_edit(View3DPrintPanel, Panel):
|
||||
bl_label = "Edit"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
|
||||
def draw(self, context):
|
||||
@ -111,6 +111,7 @@ class VIEW3D_PT_print3d_transform(View3DPrintPanel, Panel):
|
||||
|
||||
print_3d = context.scene.print_3d
|
||||
|
||||
layout.operator("mesh.print3d_hollow")
|
||||
layout.label(text="Scale To")
|
||||
row = layout.row(align=True)
|
||||
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.operator("mesh.print3d_align_to_xy", text="Align XY")
|
||||
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):
|
||||
|
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.