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 @@ class CustomNodes(): 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 @@ class ImportVolumeDataDialog(bpy.types.Operator): 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 @@ class ImportVolumeDataDialog(bpy.types.Operator): 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 @@ class ImportVolumeDataDialog(bpy.types.Operator): 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 @@ class ImportVolumeDataDialog(bpy.types.Operator): 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 @@ if bpy.app.version >= (4, 1, 0): '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 @@ else: '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 @@ else: '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 @@ class JoinLayers(bpy.types.Operator): 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 @@ class JoinLayers(bpy.types.Operator): 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 @@ class ConvertToMesh(bpy.types.Operator): 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_ + +