3D Print Toolbox: Add hollow out #105194

Merged
Mikhail Rachinskiy merged 9 commits from usfreitas/blender-addons:hollow into main 2024-03-18 12:24:30 +01:00
3 changed files with 132 additions and 0 deletions
Showing only changes of commit 78d946756d - Show all commits

View File

@ -121,6 +121,24 @@ class SceneProperties(PropertyGroup):
min=0.0, min=0.0,
max=math.radians(90.0), 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',
)
hollow_resolution: FloatProperty(
name = "Resolution",
description = "Resolution of the voxel grid used for hollowing",
default = 5.0,
min = 0.01,
subtype = 'DISTANCE',
)
hollow_join: BoolProperty(
name = "Join",
description = "Join the generated offset mesh to the original object, effectively hollowing it",
default = False
)
classes = ( classes = (
@ -149,6 +167,7 @@ classes = (
operators.MESH_OT_print3d_scale_to_bounds, operators.MESH_OT_print3d_scale_to_bounds,
operators.MESH_OT_print3d_align_to_xy, operators.MESH_OT_print3d_align_to_xy,
operators.MESH_OT_print3d_export, operators.MESH_OT_print3d_export,
operators.MESH_OT_print3d_hollow,
) )

View File

@ -12,6 +12,7 @@ from bpy.types import Operator
from bpy.props import ( from bpy.props import (
IntProperty, IntProperty,
FloatProperty, FloatProperty,
BoolProperty,
) )
import bmesh import bmesh
@ -818,3 +819,109 @@ class MESH_OT_print3d_export(Operator):
return {'FINISHED'} return {'FINISHED'}
return {'CANCELLED'} return {'CANCELLED'}
# ------
# Hollow out
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_options = {'REGISTER', 'UNDO'}
offset: FloatProperty(
name = "Offset",
description = "Surface offset in relation to original mesh. Negative -> inwards",
default = -5.0,
subtype = 'DISTANCE',
)
resolution: FloatProperty(
name = "Resolution",
description = "Resolution of the voxel grid used for hollowing",
default = 5.0,
MikhailRachinskiy marked this conversation as resolved

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.

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.
Review

Sorry, my bad. It should be fixed now.

Sorry, my bad. It should be fixed now.
min = 0.01,
subtype = 'DISTANCE',
MikhailRachinskiy marked this conversation as resolved

default=1

default=1
)
join: BoolProperty(
name = "Join",
description = "Join the generated offset mesh to the original object, effectively hollowing it",
default = False
)
def execute(self, context):
import numpy as np
import pyopenvdb as vdb
offset = self.offset
resolution = self.resolution
join = self.join
MikhailRachinskiy marked this conversation as resolved

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.
mode_orig = context.mode
# Target object
obj = context.active_object
m = obj.data # mesh
# Read mesh to numpy arrays
nverts = len(m.vertices)
ntris = len(m.loop_triangles)
verts = np.zeros(3*nverts, dtype=np.float32)
tris = np.zeros(3*ntris, dtype=np.int32)
m.vertices.foreach_get('co', verts)
verts.shape = (-1, 3)
m.loop_triangles.foreach_get('vertices', tris)
tris.shape = (-1, 3)
# Generate VDB levelset
half_width = max(3.0, math.ceil(abs(offset)/resolution) + 2.0) # half_width has to envelop offset
trans = vdb.Transform()
trans.scale(resolution)
levelset = vdb.FloatGrid.createLevelSetFromPolygons(verts, triangles=tris,
transform=trans, halfWidth=half_width)
# Generate offset surface
newverts, newquads = levelset.convertToQuads(offset)
polys = [x for x in newquads]
# Instantiate new object in Blender
mesh = bpy.data.meshes.new(m.name + ' offset')
mesh.from_pydata(newverts, [], polys)
newobj = bpy.data.objects.new(obj.name + ' offset', mesh)
newobj.matrix_world = obj.matrix_world.copy()
bpy.context.collection.objects.link(newobj)
if not join:
# For some reason OpenVDB has inverted normals
mesh.flip_normals()
else:
if offset < 0.0:
# Offset surface already has normals as they should, see above
pass
else:
# Offset surface is outside, correct normals, see above
mesh.flip_normals()
# Original surface is inside, flip its normals
m.flip_normals()
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
newobj.select_set(True)
obj.select_set(True)
context.view_layer.objects.active = obj
bpy.ops.object.join()
if mode_orig == 'EDIT_MESH':
bpy.ops.object.mode_set(mode='EDIT')
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.join = print_3d.hollow_join
return self.execute(context)

View File

@ -118,6 +118,12 @@ 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_join")
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):