3D Print Toolbox: Add hollow out #105194
@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
|||||||
|
min = 0.01,
|
||||||
|
subtype = 'DISTANCE',
|
||||||
MikhailRachinskiy marked this conversation as resolved
Mikhail Rachinskiy
commented
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
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.
|
|||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
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.