feat: layer list system

This commit is contained in:
2024-08-14 14:16:40 +08:00
parent 21e0b85662
commit 9e885865f7
21 changed files with 1451 additions and 825 deletions

View File

@@ -5,9 +5,12 @@ from bpy_extras.io_utils import axis_conversion
from mathutils import Matrix, Vector
from .layer import Layer, get_container_layer_objs, layer_to_obj, obj_to_layer
from .layer import Layer, layer_to_obj, obj_to_layer
from ..nodes import custom_nodes
from .node import get_nodes_by_type, move_node_to_node
from .utils import (get_container_layer_objs,
get_layer_prop_value,
get_nodes_by_type,
move_node_to_node)
NODE_TYPE = {
@@ -66,51 +69,6 @@ def calc_bbox_verts(origin: tuple, size: tuple):
return bbox_verts
def get_container_objs_from_selection():
container_objs = []
for obj in bpy.context.selected_objects:
if get_container_obj(obj):
container_objs.append(obj)
return list(set(container_objs))
def get_container_obj(current_obj):
if current_obj:
if current_obj.get('bioxel_container'):
return current_obj
elif current_obj.get('bioxel_layer'):
parent = current_obj.parent
return parent if parent.get('bioxel_container') else None
return None
def add_layers(layers: list[Layer],
container_obj: bpy.types.Object,
cache_dir: str):
container_node_group = container_obj.modifiers[0].node_group
for i, layer in enumerate(layers):
layer_obj = layer_to_obj(layer, container_obj, cache_dir)
mask_node = custom_nodes.add_node(container_node_group,
NODE_TYPE[layer.kind])
mask_node.label = layer_obj.name
mask_node.inputs[0].default_value = layer_obj
# Connect to output if no output linked
output_node = get_nodes_by_type(container_node_group,
'NodeGroupOutput')[0]
if len(output_node.inputs[0].links) == 0:
container_node_group.links.new(mask_node.outputs[0],
output_node.inputs[0])
move_node_to_node(mask_node, output_node, (-300, 0))
else:
move_node_to_node(mask_node, output_node, (0, -100 * (i+1)))
return container_obj
def obj_to_container(container_obj: bpy.types.Object):
layer_objs = get_container_layer_objs(container_obj)
layers = [obj_to_layer(obj) for obj in layer_objs]
@@ -119,6 +77,31 @@ def obj_to_container(container_obj: bpy.types.Object):
return container
def add_layers(layers: list[Layer],
container_obj: bpy.types.Object,
cache_dir: str):
node_group = container_obj.modifiers[0].node_group
output_node = get_nodes_by_type(node_group,
'NodeGroupOutput')[0]
for i, layer in enumerate(layers):
layer_obj = layer_to_obj(layer, container_obj, cache_dir)
fetch_node = custom_nodes.add_node(node_group,
"BioxelNodes_FetchLayer")
fetch_node.label = get_layer_prop_value(layer_obj, "name")
fetch_node.inputs[0].default_value = layer_obj
if len(output_node.inputs[0].links) == 0:
node_group.links.new(fetch_node.outputs[0],
output_node.inputs[0])
move_node_to_node(fetch_node, output_node, (-600, 0))
else:
move_node_to_node(fetch_node, output_node, (0, -100 * (i+1)))
return container_obj
def container_to_obj(container: Container,
scene_scale: float,
cache_dir: str):
@@ -153,28 +136,16 @@ def container_to_obj(container: Container,
container_obj.show_in_front = True
container_obj['bioxel_container'] = True
bpy.ops.node.new_geometry_nodes_modifier()
container_node_group = container_obj.modifiers[0].node_group
input_node = get_nodes_by_type(container_node_group,
'NodeGroupInput')[0]
container_node_group.links.remove(
input_node.outputs[0].links[0])
modifier = container_obj.modifiers.new("GeometryNodes", 'NODES')
node_group = bpy.data.node_groups.new('GeometryNodes', 'GeometryNodeTree')
node_group.interface.new_socket(name="Component",
in_out="OUTPUT",
socket_type="NodeSocketGeometry")
modifier.node_group = node_group
node_group.nodes.new("NodeGroupOutput")
for i, layer in enumerate(container.layers):
layer_obj = layer_to_obj(layer, container_obj, cache_dir)
mask_node = custom_nodes.add_node(container_node_group,
NODE_TYPE[layer.kind])
mask_node.label = layer_obj.name
mask_node.inputs[0].default_value = layer_obj
# Connect to output if no output linked
output_node = get_nodes_by_type(container_node_group,
'NodeGroupOutput')[0]
if len(output_node.inputs[0].links) == 0:
container_node_group.links.new(mask_node.outputs[0],
output_node.inputs[0])
move_node_to_node(mask_node, output_node, (-300, 0))
else:
move_node_to_node(mask_node, output_node, (0, -100 * (i+1)))
container_obj = add_layers(container.layers,
container_obj=container_obj,
cache_dir=cache_dir)
return container_obj

View File

@@ -9,33 +9,7 @@ from uuid import uuid4
from ..nodes import custom_nodes
from ..bioxel.layer import Layer
from .node import get_nodes_by_type, move_node_between_nodes
def get_layer_obj(current_obj: bpy.types.Object):
if current_obj:
if current_obj.get('bioxel_layer') and current_obj.parent:
if current_obj.parent.get('bioxel_container'):
return current_obj
return None
def get_container_layer_objs(container_obj: bpy.types.Object):
layer_objs = []
for obj in bpy.context.scene.objects:
if obj.parent == container_obj and get_layer_obj(obj):
layer_objs.append(obj)
return layer_objs
def get_all_layer_objs():
layer_objs = []
for obj in bpy.context.scene.objects:
if get_layer_obj(obj):
layer_objs.append(obj)
return layer_objs
from .utils import get_layer_prop_value, move_node_between_nodes
def obj_to_layer(layer_obj: bpy.types.Object):
@@ -63,11 +37,16 @@ def obj_to_layer(layer_obj: bpy.types.Object):
grid.copyToArray(data)
data = np.expand_dims(data, axis=0) # expend frame
name = metadata["layer_name"]
kind = metadata["layer_kind"]
name = get_layer_prop_value(layer_obj, "name") \
or metadata["layer_name"]
kind = get_layer_prop_value(layer_obj, "kind") \
or metadata["layer_kind"]
affine = metadata["layer_affine"]
dtype = metadata.get("data_dtype") or "float32"
offset = metadata.get("data_offset") or 0
dtype = get_layer_prop_value(layer_obj, "dtype") \
or metadata.get("data_dtype") or "float32"
offset = get_layer_prop_value(layer_obj, "offset") \
or metadata.get("data_offset") or 0
data = data - np.full_like(data, offset)
data = data.astype(dtype)
@@ -110,6 +89,7 @@ def layer_to_obj(layer: Layer,
"data_offset": offset
}
layer_display_name = f"{container_obj.name}_{layer.name}"
if layer.frame_count > 1:
print(f"Saving the Cache of {layer.name}...")
vdb_name = str(uuid4())
@@ -118,8 +98,14 @@ def layer_to_obj(layer: Layer,
cache_filepaths = []
for f in range(layer.frame_count):
grid = vdb.FloatGrid()
grid.copyFromArray(data[f, :, :, :].copy().astype(np.float32))
if layer.kind in ['label', 'scalar']:
grid = vdb.FloatGrid()
grid.copyFromArray(data[f, :, :, :].copy().astype(np.float32))
else:
# color
grid = vdb.Vec3SGrid()
grid.copyFromArray(
data[f, :, :, :, :].copy().astype(np.float32))
grid.transform = vdb.createLinearTransform(
layer.affine.transpose())
grid.metadata = metadata
@@ -130,17 +116,14 @@ def layer_to_obj(layer: Layer,
vdb.write(str(cache_filepath), grids=[grid])
cache_filepaths.append(cache_filepath)
print(f"Loading the Cache of {layer.name}...")
files = [{"name": str(cache_filepath.name), "name": str(cache_filepath.name)}
for cache_filepath in cache_filepaths]
bpy.ops.object.volume_import(filepath=str(cache_filepaths[0]),
directory=str(cache_filepaths[0].parent),
files=files, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
else:
grid = vdb.FloatGrid()
grid.copyFromArray(data[0, :, :, :].copy().astype(np.float32))
if layer.kind in ['label', 'scalar']:
grid = vdb.FloatGrid()
grid.copyFromArray(data[0, :, :, :].copy().astype(np.float32))
else:
# color
grid = vdb.Vec3SGrid()
grid.copyFromArray(data[0, :, :, :, :].copy().astype(np.float32))
grid.transform = vdb.createLinearTransform(
layer.affine.transpose())
grid.metadata = metadata
@@ -151,52 +134,33 @@ def layer_to_obj(layer: Layer,
vdb.write(str(cache_filepath), grids=[grid])
cache_filepaths = [cache_filepath]
print(f"Loading the Cache of {layer.name}...")
bpy.ops.object.volume_import(filepath=str(cache_filepaths[0]),
align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
layer_data = bpy.data.volumes.new(layer_display_name)
layer_data.sequence_mode = 'REPEAT'
layer_data.filepath = str(cache_filepaths[0])
layer_obj = bpy.context.active_object
layer_obj.data.sequence_mode = 'REPEAT'
if layer.frame_count > 1:
layer_data.is_sequence = True
layer_data.frame_duration = layer.frame_count
else:
layer_data.is_sequence = False
# Set props to VDB object
layer_obj.name = f"{container_obj.name}_{layer.name}"
layer_obj.data.name = f"{container_obj.name}_{layer.name}"
layer_obj.lock_location[0] = True
layer_obj.lock_location[1] = True
layer_obj.lock_location[2] = True
layer_obj.lock_rotation[0] = True
layer_obj.lock_rotation[1] = True
layer_obj.lock_rotation[2] = True
layer_obj.lock_scale[0] = True
layer_obj.lock_scale[1] = True
layer_obj.lock_scale[2] = True
layer_obj.visible_camera = False
layer_obj.visible_diffuse = False
layer_obj.visible_glossy = False
layer_obj.visible_transmission = False
layer_obj.visible_volume_scatter = False
layer_obj.visible_shadow = False
layer_obj.hide_select = True
layer_obj.hide_render = True
layer_obj.hide_viewport = True
layer_obj = bpy.data.objects.new(layer_display_name, layer_data)
layer_obj['bioxel_layer'] = True
layer_obj.parent = container_obj
for collection in layer_obj.users_collection:
collection.objects.unlink(layer_obj)
for collection in container_obj.users_collection:
collection.objects.link(layer_obj)
print(f"Creating Node for {layer.name}...")
bpy.ops.node.new_geometry_nodes_modifier()
node_group = layer_obj.modifiers[0].node_group
modifier = layer_obj.modifiers.new("GeometryNodes", 'NODES')
node_group = bpy.data.node_groups.new('GeometryNodes', 'GeometryNodeTree')
node_group.interface.new_socket(name="Cache",
in_out="INPUT",
socket_type="NodeSocketGeometry")
node_group.interface.new_socket(name="Layer",
in_out="OUTPUT",
socket_type="NodeSocketGeometry")
modifier.node_group = node_group
layer_node = custom_nodes.add_node(node_group,
"BioxelNodes__Layer")
"BioxelNodes__Layer")
layer_node.inputs['name'].default_value = layer.name
layer_node.inputs['shape'].default_value = layer.shape
@@ -208,7 +172,7 @@ def layer_to_obj(layer: Layer,
layer_node.inputs[affine_key].default_value = layer.affine[j, i]
layer_node.inputs['id'].default_value = random.randint(-200000000,
200000000)
200000000)
layer_node.inputs['bioxel_size'].default_value = layer.bioxel_size[0]
layer_node.inputs['dtype'].default_value = layer.dtype.str
layer_node.inputs['dtype_num'].default_value = layer.dtype.num
@@ -216,10 +180,8 @@ def layer_to_obj(layer: Layer,
layer_node.inputs['min'].default_value = layer.min
layer_node.inputs['max'].default_value = layer.max
input_node = get_nodes_by_type(node_group,
'NodeGroupInput')[0]
output_node = get_nodes_by_type(node_group,
'NodeGroupOutput')[0]
input_node = node_group.nodes.new("NodeGroupInput")
output_node = node_group.nodes.new("NodeGroupOutput")
node_group.links.new(input_node.outputs[0],
layer_node.inputs[0])
@@ -229,4 +191,6 @@ def layer_to_obj(layer: Layer,
move_node_between_nodes(
layer_node, [input_node, output_node])
layer_obj.parent = container_obj
return layer_obj

View File

@@ -1,26 +0,0 @@
def move_node_to_node(node, target_node, offset=(0, 0)):
node.location.x = target_node.location.x + offset[0]
node.location.y = target_node.location.y + offset[1]
def move_node_between_nodes(node, target_nodes, offset=(0, 0)):
xs = []
ys = []
for target_node in target_nodes:
xs.append(target_node.location.x)
ys.append(target_node.location.y)
node.location.x = sum(xs) / len(xs) + offset[0]
node.location.y = sum(ys) / len(ys) + offset[1]
def get_node_type(node):
node_type = type(node).__name__
if node_type == "GeometryNodeGroup":
node_type = node.node_tree.name
return node_type
def get_nodes_by_type(node_group, type_name: str):
return [node for node in node_group.nodes if get_node_type(node) == type_name]

View File

@@ -0,0 +1,96 @@
import bpy
def move_node_to_node(node, target_node, offset=(0, 0)):
node.location.x = target_node.location.x + offset[0]
node.location.y = target_node.location.y + offset[1]
def move_node_between_nodes(node, target_nodes, offset=(0, 0)):
xs = []
ys = []
for target_node in target_nodes:
xs.append(target_node.location.x)
ys.append(target_node.location.y)
node.location.x = sum(xs) / len(xs) + offset[0]
node.location.y = sum(ys) / len(ys) + offset[1]
def get_node_type(node):
node_type = type(node).__name__
if node_type == "GeometryNodeGroup":
node_type = node.node_tree.name
return node_type
def get_nodes_by_type(node_group, type_name: str):
return [node for node in node_group.nodes if get_node_type(node) == type_name]
def get_container_objs_from_selection():
container_objs = []
for obj in bpy.context.selected_objects:
if get_container_obj(obj):
container_objs.append(obj)
return list(set(container_objs))
def get_container_obj(current_obj):
if current_obj:
if current_obj.get('bioxel_container'):
return current_obj
elif current_obj.get('bioxel_layer'):
parent = current_obj.parent
return parent if parent.get('bioxel_container') else None
return None
def get_layer_prop_value(layer_obj: bpy.types.Object, prop: str):
node_group = layer_obj.modifiers[0].node_group
layer_node = get_nodes_by_type(node_group, "BioxelNodes__Layer")[0]
value = layer_node.inputs[prop].default_value
if type(value).__name__ == "bpy_prop_array":
value = tuple(value)
return tuple([int(v) for v in value]) \
if prop in ["shape"] else value
elif type(value).__name__ == "str":
return str(value)
if type(value).__name__ == "float":
value = float(value)
return round(value, 2) \
if prop in ["bioxel_size"] else value
def set_layer_prop_value(layer_obj: bpy.types.Object, prop: str, value):
node_group = layer_obj.modifiers[0].node_group
layer_node = get_nodes_by_type(node_group, "BioxelNodes__Layer")[0]
layer_node.inputs[prop].default_value = value
def get_layer_obj(current_obj: bpy.types.Object):
if current_obj:
if current_obj.get('bioxel_layer') and current_obj.parent:
if current_obj.parent.get('bioxel_container'):
return current_obj
return None
def get_container_layer_objs(container_obj: bpy.types.Object):
layer_objs = []
for obj in bpy.data.objects:
if obj.parent == container_obj and get_layer_obj(obj):
layer_objs.append(obj)
return layer_objs
def get_all_layer_objs():
layer_objs = []
for obj in bpy.data.objects:
if get_layer_obj(obj):
layer_objs.append(obj)
return layer_objs