Files
Bioxel/bioxelnodes/operators.py
2024-06-12 15:23:09 +08:00

235 lines
8.6 KiB
Python

from pathlib import Path
import random
import bpy
import pyopenvdb as vdb
from uuid import uuid4
from .nodes import custom_nodes
from .utils import (get_container, get_container_layers,
get_layer, get_node_by_type, hide_in_ray, lock_transform)
def get_scalar_layer_selection(self, context):
items = [("None", "None", "")]
base_layer = bpy.data.objects[self.base_layer]
container = base_layer.parent
for layer in get_container_layers(container):
if layer.get("bioxel_layer_type") == "scalar":
items.append((
layer.name,
layer.name,
""
))
return items
def get_label_layer_selection(self, context):
items = [("None", "None", "")]
base_layer = bpy.data.objects[self.base_layer]
container = base_layer.parent
for layer in get_container_layers(container):
if layer.get("bioxel_layer_type") == "label":
items.append((
layer.name,
layer.name,
""
))
return items
class JoinLayers(bpy.types.Operator):
bl_idname = "bioxelnodes.join_layers"
bl_label = "Join Additinal Layers"
bl_description = "Join Layers"
bl_options = {'UNDO'}
base_layer: bpy.props.StringProperty(
options={"HIDDEN"}
) # type: ignore
scalar_layer: bpy.props.EnumProperty(
name="Scaler Layer",
items=get_scalar_layer_selection
) # type: ignore
label_layer: bpy.props.EnumProperty(
name="Label Layer",
items=get_label_layer_selection
) # type: ignore
# color_layer: bpy.props.StringProperty() # type: ignore
@classmethod
def poll(cls, context):
layer = get_layer(bpy.context.active_object)
return True if layer else False
def execute(self, context):
base_layer = bpy.data.objects[self.base_layer]
if not base_layer:
self.report({"WARNING"}, "Cannot find any bioxel layer as base.")
return {'FINISHED'}
base_layer_dir = bpy.path.abspath(base_layer.data.filepath)
base_grids, base_metadata = vdb.readAll(base_layer_dir)
preferences = context.preferences.addons[__package__].preferences
cache_dir = Path(preferences.cache_dir, 'VDBs')
cache_dir.mkdir(parents=True, exist_ok=True)
layers = []
if self.scalar_layer != "None":
scalar_layer = bpy.data.objects[self.scalar_layer]
layers.append(scalar_layer)
if self.label_layer != "None":
label_layer = bpy.data.objects[self.label_layer]
layers.append(label_layer)
# TODO: add color and vector
if len(layers) == 0:
self.report({"WARNING"}, "No additinal layers setted.")
return {'FINISHED'}
for layer in layers:
layer_dir = bpy.path.abspath(layer.data.filepath)
grids, metadata = vdb.readAll(layer_dir)
base_grids.extend(grids)
vdb_path = Path(cache_dir, f"{uuid4()}.vdb")
print(f"Storing the VDB file ({str(vdb_path)})...")
vdb.write(str(vdb_path), grids=base_grids)
# Read VDB
print(f"Loading the cache to Blender scene...")
bpy.ops.object.volume_import(
filepath=str(vdb_path), align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
joined_layer = bpy.context.active_object
# Set props to VDB object
joined_layer.name = f"{base_layer.name}_Joined"
joined_layer.data.name = f"{base_layer.name}_Joined"
lock_transform(joined_layer)
hide_in_ray(joined_layer)
joined_layer.hide_select = True
joined_layer.hide_render = True
joined_layer.hide_viewport = True
joined_layer.data.display.use_slice = True
joined_layer.data.display.density = 1e-05
joined_layer['bioxel_layer'] = True
joined_layer.parent = base_layer.parent
for collection in layer.users_collection:
collection.objects.unlink(layer)
for collection in base_layer.users_collection:
collection.objects.link(layer)
# add convert to layer node
base_layer_node = base_layer.modifiers[0].node_group.nodes['BioxelNodes__ConvertToLayer']
bioxel_size = base_layer_node.inputs['Bioxel Size'].default_value
layer_shape = base_layer_node.inputs['Shape'].default_value
layer_origin = base_layer_node.inputs['Origin'].default_value
layer_rotation = base_layer_node.inputs['Rotation'].default_value
scalar_offset = base_layer_node.inputs['Scalar Offset'].default_value
scalar_min = base_layer_node.inputs['Scalar Min'].default_value
scalar_max = base_layer_node.inputs['Scalar Max'].default_value
if self.scalar_layer != "None":
scalar_layer_node = scalar_layer.modifiers[0].node_group.nodes['BioxelNodes__ConvertToLayer']
scalar_offset = scalar_layer_node.inputs['Scalar Offset'].default_value
scalar_min = scalar_layer_node.inputs['Scalar Min'].default_value
scalar_max = scalar_layer_node.inputs['Scalar Max'].default_value
bpy.ops.node.new_geometry_nodes_modifier()
node_tree = joined_layer.modifiers[0].node_group
nodes = node_tree.nodes
links = node_tree.links
input_node = get_node_by_type(nodes, 'NodeGroupInput')[0]
output_node = get_node_by_type(nodes, 'NodeGroupOutput')[0]
joined_layer_node = custom_nodes.add_node(
nodes, "BioxelNodes__ConvertToLayer")
links.new(input_node.outputs[0], joined_layer_node.inputs[0])
links.new(joined_layer_node.outputs[0], output_node.inputs[0])
joined_layer_node.inputs['Layer ID'].default_value = random.randint(-200000000,
200000000)
joined_layer_node.inputs['Bioxel Size'].default_value = bioxel_size
joined_layer_node.inputs['Shape'].default_value = layer_shape
joined_layer_node.inputs['Origin'].default_value = layer_origin
joined_layer_node.inputs['Rotation'].default_value = layer_rotation
joined_layer_node.inputs['Scalar Offset'].default_value = scalar_offset
joined_layer_node.inputs['Scalar Min'].default_value = scalar_min
joined_layer_node.inputs['Scalar Max'].default_value = scalar_max
return {'FINISHED'}
def invoke(self, context, event):
base_layer = get_layer(bpy.context.active_object)
self.base_layer = base_layer.name
context.window_manager.invoke_props_dialog(self, width=400)
return {'RUNNING_MODAL'}
class ConvertToMesh(bpy.types.Operator):
bl_idname = "bioxelnodes.convert_to_mesh"
bl_label = "Bioxel Components To Mesh"
bl_description = "Convert Bioxel Components To Mesh"
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
container = get_container(bpy.context.active_object)
return True if container else False
def execute(self, context):
container = get_container(bpy.context.active_object)
if not container:
self.report({"WARNING"}, "Cannot find any bioxel container.")
return {'FINISHED'}
bpy.ops.mesh.primitive_cube_add(
size=2, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
mesh = bpy.context.active_object
mesh.name = f"Mesh_{container.name}"
bpy.ops.object.constraint_add(type='COPY_TRANSFORMS')
mesh.constraints[0].target = container
bpy.ops.node.new_geometry_nodes_modifier()
modifier = mesh.modifiers[0]
nodes = modifier.node_group.nodes
links = modifier.node_group.links
output_node = get_node_by_type(nodes, 'NodeGroupOutput')[0]
object_node = nodes.new("GeometryNodeObjectInfo")
realize_nodes = nodes.new("GeometryNodeRealizeInstances")
separate_node = custom_nodes.add_node(nodes,
"BioxelNodes__SeparateComponent")
object_node.inputs[0].default_value = container
separate_node.inputs[1].default_value = 1
links.new(object_node.outputs['Geometry'], separate_node.inputs[0])
links.new(separate_node.outputs[0], realize_nodes.inputs[0])
links.new(realize_nodes.outputs[0], output_node.inputs[0])
bpy.ops.object.convert(target='MESH')
bpy.ops.constraint.apply(constraint="Copy Transforms", owner='OBJECT')
bpy.context.object.active_material_index = 1
bpy.ops.object.material_slot_remove()
self.report({"INFO"}, f"Successfully convert to mesh")
return {'FINISHED'}