feat: more time sequence choice
fix: contrast formulation cause leak of high value fix: empty series_id cause fail to read chore: restructure folder chore!: change layer vars BREAKING CHANGE: not support 4.1 any more
This commit is contained in:
232
bioxelnodes/bioxelutils/layer.py
Normal file
232
bioxelnodes/bioxelutils/layer.py
Normal file
@@ -0,0 +1,232 @@
|
||||
import random
|
||||
import re
|
||||
import bpy
|
||||
import numpy as np
|
||||
|
||||
import pyopenvdb as vdb
|
||||
from pathlib import Path
|
||||
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
|
||||
|
||||
|
||||
def obj_to_layer(layer_obj: bpy.types.Object):
|
||||
cache_filepath = Path(bpy.path.abspath(layer_obj.data.filepath)).resolve()
|
||||
is_sequence = re.search(r'\.\d{4}\.',
|
||||
cache_filepath.name) is not None
|
||||
if is_sequence:
|
||||
cache_path = cache_filepath.parent
|
||||
data_frames = ()
|
||||
for f in cache_path.iterdir():
|
||||
if not f.is_file() or f.suffix != ".vdb":
|
||||
continue
|
||||
grids, base_metadata = vdb.readAll(str(f))
|
||||
grid = grids[0]
|
||||
metadata = grid.metadata
|
||||
data_frame = np.ndarray(grid["data_shape"], np.float32)
|
||||
grid.copyToArray(data_frame)
|
||||
data_frames += (data_frame,)
|
||||
data = np.stack(data_frames)
|
||||
else:
|
||||
grids, base_metadata = vdb.readAll(str(cache_filepath))
|
||||
grid = grids[0]
|
||||
metadata = grid.metadata
|
||||
data = np.ndarray(grid["data_shape"], np.float32)
|
||||
grid.copyToArray(data)
|
||||
data = np.expand_dims(data, axis=0) # expend frame
|
||||
|
||||
name = metadata["layer_name"]
|
||||
kind = metadata["layer_kind"]
|
||||
affine = metadata["layer_affine"]
|
||||
dtype = metadata.get("data_dtype") or "float32"
|
||||
offset = metadata.get("data_offset") or 0
|
||||
data = data - np.full_like(data, offset)
|
||||
data = data.astype(dtype)
|
||||
|
||||
if kind in ["scalar", "label"]:
|
||||
data = np.expand_dims(data, axis=-1) # expend channel
|
||||
|
||||
layer = Layer(data=data,
|
||||
name=name,
|
||||
kind=kind,
|
||||
affine=affine)
|
||||
|
||||
return layer
|
||||
|
||||
|
||||
def layer_to_obj(layer: Layer,
|
||||
container_obj: bpy.types.Object,
|
||||
cache_dir: str):
|
||||
|
||||
data = layer.data
|
||||
|
||||
# TXYZC > TXYZ
|
||||
if layer.kind in ['label', 'scalar']:
|
||||
data = np.amax(data, -1)
|
||||
|
||||
offset = 0
|
||||
if layer.kind in ['scalar']:
|
||||
data = data.astype(np.float32)
|
||||
orig_min = float(np.min(data))
|
||||
if orig_min < 0:
|
||||
offset = -orig_min
|
||||
|
||||
data = data + np.full_like(data, offset)
|
||||
|
||||
metadata = {
|
||||
"layer_name": layer.name,
|
||||
"layer_kind": layer.kind,
|
||||
"layer_affine": layer.affine.tolist(),
|
||||
"data_shape": layer.shape,
|
||||
"data_dtype": layer.data.dtype.str,
|
||||
"data_offset": offset
|
||||
}
|
||||
|
||||
if layer.frame_count > 1:
|
||||
print(f"Saving the Cache of {layer.name}...")
|
||||
vdb_name = str(uuid4())
|
||||
sequence_path = Path(cache_dir, vdb_name)
|
||||
sequence_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
cache_filepaths = []
|
||||
for f in range(layer.frame_count):
|
||||
grid = vdb.FloatGrid()
|
||||
grid.copyFromArray(data[f, :, :, :].copy().astype(np.float32))
|
||||
grid.transform = vdb.createLinearTransform(
|
||||
layer.affine.transpose())
|
||||
grid.metadata = metadata
|
||||
grid.name = layer.kind
|
||||
|
||||
cache_filepath = Path(sequence_path,
|
||||
f"{vdb_name}.{str(f+1).zfill(4)}.vdb")
|
||||
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))
|
||||
grid.transform = vdb.createLinearTransform(
|
||||
layer.affine.transpose())
|
||||
grid.metadata = metadata
|
||||
grid.name = layer.kind
|
||||
|
||||
print(f"Saving the Cache of {layer.name}...")
|
||||
cache_filepath = Path(cache_dir, f"{uuid4()}.vdb")
|
||||
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_obj = bpy.context.active_object
|
||||
layer_obj.data.sequence_mode = 'REPEAT'
|
||||
|
||||
# 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['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
|
||||
layer_node = custom_nodes.add_node(node_group,
|
||||
"BioxelNodes__Layer")
|
||||
|
||||
layer_node.inputs['name'].default_value = layer.name
|
||||
layer_node.inputs['shape'].default_value = layer.shape
|
||||
layer_node.inputs['kind'].default_value = layer.kind
|
||||
|
||||
for i in range(layer.affine.shape[1]):
|
||||
for j in range(layer.affine.shape[0]):
|
||||
affine_key = f"affine{i}{j}"
|
||||
layer_node.inputs[affine_key].default_value = layer.affine[j, i]
|
||||
|
||||
layer_node.inputs['id'].default_value = random.randint(-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
|
||||
layer_node.inputs['offset'].default_value = max(0, -layer.min)
|
||||
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]
|
||||
|
||||
node_group.links.new(input_node.outputs[0],
|
||||
layer_node.inputs[0])
|
||||
node_group.links.new(layer_node.outputs[0],
|
||||
output_node.inputs[0])
|
||||
|
||||
move_node_between_nodes(
|
||||
layer_node, [input_node, output_node])
|
||||
|
||||
return layer_obj
|
||||
Reference in New Issue
Block a user