diff --git a/bioxelnodes/assets/Nodes/BioxelNodes_4.0.blend b/bioxelnodes/assets/Nodes/BioxelNodes_4.0.blend index 32ee958..3bbca24 100644 --- a/bioxelnodes/assets/Nodes/BioxelNodes_4.0.blend +++ b/bioxelnodes/assets/Nodes/BioxelNodes_4.0.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1675dac841e714283283fd5975deb33f0e776b1b4f55896b93ec0d9fb14c1f1 -size 4850215 +oid sha256:673262476653e04ca793e97f2d998cba31f338e1219199585a3844fd3c8d6b33 +size 4887206 diff --git a/bioxelnodes/assets/Nodes/BioxelNodes_4.1.blend b/bioxelnodes/assets/Nodes/BioxelNodes_4.1.blend index cbe9a13..72513ee 100644 --- a/bioxelnodes/assets/Nodes/BioxelNodes_4.1.blend +++ b/bioxelnodes/assets/Nodes/BioxelNodes_4.1.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:44905112564c51547c1565ef77ad2b2f08686c8500dce0aa010762f8ad5d53d2 -size 4178018 +oid sha256:cecabd497316a2c0a0e64cb30274ea4c62000288fb13481102b4f2f2eaca2e7d +size 4196897 diff --git a/bioxelnodes/customnodes/menus.py b/bioxelnodes/customnodes/menus.py index dfd93f8..7b129d1 100644 --- a/bioxelnodes/customnodes/menus.py +++ b/bioxelnodes/customnodes/menus.py @@ -163,16 +163,18 @@ def find_item(self, node_type: str): def add_node(self, node_tree, node_type: str): item = self.find_item(node_type) + op = AddCustomNode() + op.nodes_file = self.nodes_file + op.node_type = node_type if item: - op = AddCustomNode() - op.nodes_file = self.nodes_file - op.node_type = item['node_type'] op.node_label = item.get('label') or "" op.node_link = item.get('link') or True op.node_callback = item.get('node_callback') or "" - return op.add_node(node_tree) else: - raise RuntimeError("Not found in menu.") + op.node_label = "" + op.node_link = True + op.node_callback = "" + return op.add_node(node_tree) def register(self): for cls in self.menu_classes: diff --git a/bioxelnodes/io.py b/bioxelnodes/io.py index ff8bfdf..e389a69 100644 --- a/bioxelnodes/io.py +++ b/bioxelnodes/io.py @@ -236,14 +236,14 @@ def execute(self, context): if self.read_as == "labels": if "vector" in image.GetPixelIDTypeAsString(): - print("Convet to Grayscale...") + print("Conveting to Grayscale...") image = x2gray(image) else: image = sitk.Cast(image, sitk.sitkUInt16) default_value = 0 elif self.read_as == "scalar": if "vector" in image.GetPixelIDTypeAsString(): - print("Convet to Grayscale...") + print("Conveting to Grayscale...") image = rgb2gray(image) else: image = sitk.Cast(image, sitk.sitkFloat32) @@ -258,6 +258,9 @@ def execute(self, context): interpolator = sitk.sitkNearestNeighbor elif self.resample_method == "gaussian": interpolator = sitk.sitkGaussian + + if self.read_as == "labels": + interpolator = sitk.sitkNearestNeighbor print(f"Resampling...") image = sitk.Resample( @@ -448,16 +451,15 @@ def create_layer(array, layer_name, layer_type="scalar"): input_node = get_node_by_type(nodes, 'NodeGroupInput')[0] output_node = get_node_by_type(nodes, 'NodeGroupOutput')[0] - node_type = 'BioxelNodes_AsLabel' if layer_type == "label" else 'BioxelNodes_AsScalar' - source_node = custom_nodes.add_node(nodes, node_type) + to_layer_node = custom_nodes.add_node(nodes, "BioxelNodes__ConvertToLayer") - links.new(input_node.outputs[0], source_node.inputs[0]) - links.new(source_node.outputs[0], output_node.inputs[0]) + links.new(input_node.outputs[0], to_layer_node.inputs[0]) + links.new(to_layer_node.outputs[0], output_node.inputs[0]) - source_node.inputs['Bioxel Size'].default_value = bioxel_size - source_node.inputs['Shape'].default_value = layer_shape - source_node.inputs['Origin'].default_value = layer_origin - source_node.inputs['Rotation'].default_value = layer_rotation + to_layer_node.inputs['Bioxel Size'].default_value = bioxel_size + to_layer_node.inputs['Shape'].default_value = layer_shape + to_layer_node.inputs['Origin'].default_value = layer_origin + to_layer_node.inputs['Rotation'].default_value = layer_rotation return layer @@ -511,10 +513,10 @@ def create_layer(array, layer_name, layer_type="scalar"): layer_type="scalar") layer_node_tree = layer.modifiers[0].node_group - source_node = layer_node_tree.nodes['BioxelNodes_AsScalar'] - source_node.inputs['Offset'].default_value = scalar_offset - source_node.inputs['Max'].default_value = orig_max - source_node.inputs['Min'].default_value = orig_min + to_layer_node = layer_node_tree.nodes['BioxelNodes__ConvertToLayer'] + to_layer_node.inputs['Scalar Offset'].default_value = scalar_offset + to_layer_node.inputs['Scalar Max'].default_value = orig_max + to_layer_node.inputs['Scalar Min'].default_value = orig_min output_node = get_node_by_type(container_node_tree.nodes, 'NodeGroupOutput')[0] diff --git a/bioxelnodes/nodes.py b/bioxelnodes/nodes.py index 7589fdc..0a95ec0 100644 --- a/bioxelnodes/nodes.py +++ b/bioxelnodes/nodes.py @@ -166,29 +166,6 @@ def set_prop_to_node_factory(source_prop, target_prop): 'icon': 'CONSTRAINT_BONE', 'node_type': 'BioxelNodes_JoinComponent', 'node_description': '' - }, - { - 'label': 'Separate Component', - 'node_type': 'BioxelNodes_SeparateComponent', - 'node_description': '' - } - ] - }, - { - 'label': 'Others', - 'icon': 'PACKAGE', - 'items': [ - { - 'label': 'Source As Label', - 'icon': 'GP_SELECT_POINTS', - 'node_type': 'BioxelNodes_AsLabel', - 'node_description': '' - }, - { - 'label': 'Source As Scalar', - 'icon': 'GP_SELECT_STROKES', - 'node_type': 'BioxelNodes_AsScalar', - 'node_description': '' } ] } @@ -286,9 +263,9 @@ def set_prop_to_node_factory(source_prop, target_prop): 'icon': 'MOD_BEVEL', 'items': [ { - 'label': 'Apply Cutters', + 'label': 'Cut', 'icon': 'MOD_BEVEL', - 'node_type': 'BioxelNodes_ApplyCutters', + 'node_type': 'BioxelNodes_Cut', 'node_description': '' }, "separator", @@ -315,29 +292,6 @@ def set_prop_to_node_factory(source_prop, target_prop): 'icon': 'CONSTRAINT_BONE', 'node_type': 'BioxelNodes_JoinComponent', 'node_description': '' - }, - { - 'label': 'Separate Component', - 'node_type': 'BioxelNodes_SeparateComponent', - 'node_description': '' - } - ] - }, - { - 'label': 'Others', - 'icon': 'PACKAGE', - 'items': [ - { - 'label': 'Source As Label', - 'icon': 'GP_SELECT_POINTS', - 'node_type': 'BioxelNodes_AsLabel', - 'node_description': '' - }, - { - 'label': 'Source As Scalar', - 'icon': 'GP_SELECT_STROKES', - 'node_type': 'BioxelNodes_AsScalar', - 'node_description': '' } ] } diff --git a/bioxelnodes/operators.py b/bioxelnodes/operators.py index c842f8d..40f17fc 100644 --- a/bioxelnodes/operators.py +++ b/bioxelnodes/operators.py @@ -79,13 +79,14 @@ def execute(self, context): layers = [] if self.scalar_layer != "None": - layers.append(bpy.data.objects[self.scalar_layer]) + scalar_layer = bpy.data.objects[self.scalar_layer] + layers.append(scalar_layer) if self.label_layer != "None": - layers.append(bpy.data.objects[self.label_layer]) + label_layer = bpy.data.objects[self.label_layer] + layers.append(label_layer) - # if self.color_layer: - # layers.append(bpy.data.objects[self.color_layer]) + # TODO: add color and vector if len(layers) == 0: self.report({"WARNING"}, "No additinal layers setted.") @@ -106,8 +107,45 @@ def execute(self, context): filepath=str(vdb_path), align='WORLD', location=(0, 0, 0), scale=(1, 1, 1)) joined_layer = bpy.context.active_object - bpy.ops.object.modifier_add(type='NODES') - joined_layer.modifiers[0].node_group = base_layer.modifiers[0].node_group + + 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['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 # Set props to VDB object joined_layer.name = f"{base_layer.name}_Joined" @@ -169,7 +207,7 @@ def execute(self, context): object_node = nodes.new("GeometryNodeObjectInfo") realize_nodes = nodes.new("GeometryNodeRealizeInstances") separate_node = custom_nodes.add_node( - nodes, "BioxelNodes_SeparateComponent") + nodes, "BioxelNodes__SeparateComponent") object_node.inputs[0].default_value = container separate_node.inputs[1].default_value = 1 diff --git a/docs/features.md b/docs/features.md index 21c106c..d2f06c5 100644 --- a/docs/features.md +++ b/docs/features.md @@ -2,7 +2,7 @@ ## Bioxel Design Concept -According to Bioxel design concept, Bioxel Nodes imports volume data and put it into a **Container** as a **Layer**. One container may has more than one layer, and each layer stores the information of different fields under the same location, which is similar to the view layer of map app, except that here it is in 3D space. In order to render the volume the way we want it to, we need to build a renderable object from layers. We call this object **Component**. The following diagram shows the relationship of **Container**, **Layer**, and **Component**: +According to Bioxel design concept, Bioxel Nodes imports volume data and put it into a **Container** as a **Layer**. One container may has more than one layer, and each layer stores the information of different fields under the same location, which is similar to the view layers in map app, except that here it is in 3D space. In order to render the volume the way we want it to, we need to build renderable objects from layers. We call those objects **Component**. The following diagram shows the relationship of **Container**, **Layer**, and **Component**: ![alt text](assets/features_concept.png) diff --git a/docs/nodes.md b/docs/nodes.md index 8497dad..835ab78 100644 --- a/docs/nodes.md +++ b/docs/nodes.md @@ -251,41 +251,6 @@ This type of node is responsible for cutting components to present cross-section -## Utils - -### 📦 Join Component - -
- -- ![alt text](assets/nodes_join-component.png) - -- Components' combination should not be done by "Join Geometry" node because Blender Cycles can't render volumes in the same position, so the node will slightly offset all components randomly to avoid this problem. - - *** - - Node Parameter: - - - **Component 0~4**, _the components_ - -
- -### 🗂️ Separate Component - -
- -- ![alt text](assets/nodes_separate-component.png) - -- Volumes, previews, and guides are included within a component. This node can split them by type. - - *** - - Node Parameter: - - - **Component**, _the upstream component_ - - **Type**, _the sub-object type, 0 is volume, 1 is preview, 2 is guide_ - -
- ## Colors This type of node is responsible for setting the color properties of the shader @@ -406,3 +371,21 @@ This type of node is responsible for setting the color properties of the shader - **Contrast**, _the larger the contrast, the harder color ramp will be_ + +## Utils + +### 📦 Join Component + +
+ +- ![alt text](assets/nodes_join-component.png) + +- Components' combination should not be done by "Join Geometry" node because Blender Cycles can't render volumes in the same position, so the node will slightly offset all components randomly to avoid this problem. + + *** + + Node Parameter: + + - **Component 0~4**, _the components_ + +