diff --git a/README.md b/README.md index ddb4eb9..8594084 100644 --- a/README.md +++ b/README.md @@ -56,3 +56,15 @@ It is not support editing the same blender file across add-on versions. In order But even then, there is still no guarantee that the new version of the add-on will work on the old blender file. Therefore, it is highly recommended to open a new blender file to start the creating, not based on the old one. Alternatively, objects from the old file that have nothing to do with Bioxel Nodes could be append to the new blender file. + +## About EEVEE Render + +Bioxel Nodes is designed for Cycles Render. However, it does support eevee render partially. "Solid Shader" node and "Volume Shader" node have a toggle called "EEVEE Render". If you want to render Bioxel Component in real-time, turn it on. + +Also, there are some limitations: + +1. Only one cutter supported. +2. You cannot use "Color Ramp" over 2 colors. +3. EEVEE Render result is not that great as Cycles does. + +> "Volume Shader" node is not work properly in EEVEE Next since 4.2. It is because EEVEE Next is not support attributes from instances of volume shader by now. But the Blender 4.2 docs still say attributes reading is ok, so I suppose this feature will eventually be implemented. diff --git a/bioxelnodes/assets/Nodes/BioxelNodes_4.0.blend b/bioxelnodes/assets/Nodes/BioxelNodes_4.0.blend index 3bbca24..eb31331 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:673262476653e04ca793e97f2d998cba31f338e1219199585a3844fd3c8d6b33 -size 4887206 +oid sha256:6721aaf62fda8d104312d290032ab045b9f7d06f7f71ef9dd17b851c0e7293b5 +size 5410056 diff --git a/bioxelnodes/assets/Nodes/BioxelNodes_4.1.blend b/bioxelnodes/assets/Nodes/BioxelNodes_4.1.blend index 72513ee..992f1f8 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:cecabd497316a2c0a0e64cb30274ea4c62000288fb13481102b4f2f2eaca2e7d -size 4196897 +oid sha256:9dc863260e46ad32b0101f9e082a4c7162ccf1e1df7a8ea678d02961d9866d99 +size 4702346 diff --git a/bioxelnodes/io.py b/bioxelnodes/io.py index e389a69..98c4cde 100644 --- a/bioxelnodes/io.py +++ b/bioxelnodes/io.py @@ -6,10 +6,11 @@ import numpy as np from pathlib import Path from uuid import uuid4 import mathutils +import random from .nodes import custom_nodes from .props import BIOXELNODES_Series -from .utils import (calc_bbox_verts, get_container, get_layer, get_text_index_str, +from .utils import (calc_bbox_verts, get_all_layers, get_container, get_layer, get_text_index_str, get_node_by_type, hide_in_ray, lock_transform, show_message) try: @@ -218,6 +219,7 @@ class ImportVolumeDataDialog(bpy.types.Operator): ) # type: ignore def execute(self, context): + is_first_import = len(get_all_layers()) == 0 image, name = read_image(self.filepath, self.series_id) container_name = self.container_name or name bioxel_size = self.bioxel_size @@ -258,7 +260,7 @@ class ImportVolumeDataDialog(bpy.types.Operator): interpolator = sitk.sitkNearestNeighbor elif self.resample_method == "gaussian": interpolator = sitk.sitkGaussian - + if self.read_as == "labels": interpolator = sitk.sitkNearestNeighbor @@ -451,11 +453,14 @@ class ImportVolumeDataDialog(bpy.types.Operator): input_node = get_node_by_type(nodes, 'NodeGroupInput')[0] output_node = get_node_by_type(nodes, 'NodeGroupOutput')[0] - to_layer_node = custom_nodes.add_node(nodes, "BioxelNodes__ConvertToLayer") + to_layer_node = custom_nodes.add_node(nodes, + "BioxelNodes__ConvertToLayer") links.new(input_node.outputs[0], to_layer_node.inputs[0]) links.new(to_layer_node.outputs[0], output_node.inputs[0]) + to_layer_node.inputs['Layer ID'].default_value = random.randint(-200000000, + 200000000) 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 @@ -531,7 +536,7 @@ class ImportVolumeDataDialog(bpy.types.Operator): bpy.context.view_layer.objects.active = container # Change render setting for better result - if preferences.do_change_render_setting: + if preferences.do_change_render_setting and is_first_import: bpy.context.scene.render.engine = 'CYCLES' bpy.context.scene.cycles.volume_bounces = 12 bpy.context.scene.cycles.transparent_max_bounces = 16 diff --git a/bioxelnodes/operators.py b/bioxelnodes/operators.py index 49d5cb3..4e95966 100644 --- a/bioxelnodes/operators.py +++ b/bioxelnodes/operators.py @@ -1,4 +1,5 @@ from pathlib import Path +import random import bpy import pyopenvdb as vdb from uuid import uuid4 @@ -107,45 +108,6 @@ 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 - - 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" @@ -162,6 +124,52 @@ class JoinLayers(bpy.types.Operator): 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): @@ -206,8 +214,8 @@ class ConvertToMesh(bpy.types.Operator): 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") + separate_node = custom_nodes.add_node(nodes, + "BioxelNodes__SeparateComponent") object_node.inputs[0].default_value = container separate_node.inputs[1].default_value = 1 diff --git a/docs/index.md b/docs/index.md index 8b15d62..d3d339d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -54,3 +54,15 @@ It is not support editing the same blender file across add-on versions. In order But even then, there is still no guarantee that the new version of the add-on will work on the old blender file. Therefore, it is highly recommended to open a new blender file to start the creating, not based on the old one. Alternatively, objects from the old file that have nothing to do with Bioxel Nodes could be append to the new blender file. + +## About EEVEE Render + +Bioxel Nodes is designed for Cycles Render. However, it does support eevee render partially. "Solid Shader" node and "Volume Shader" node have a toggle called "EEVEE Render". If you want to render Bioxel Component in real-time, turn it on. + +Also, there are some limitations: + +1. Only one cutter supported. +2. You cannot use "Color Ramp" over 2 colors. +3. EEVEE Render result is not that great as Cycles does. + +> "Volume Shader" node is not work properly in EEVEE Next since 4.2. It is because EEVEE Next is not support attributes from instances of volume shader by now. But the Blender 4.2 docs still say attributes reading is ok, so I suppose this feature will eventually be implemented. diff --git a/extension/blender_manifest.toml b/extension/blender_manifest.toml index 9108043..da2d453 100644 --- a/extension/blender_manifest.toml +++ b/extension/blender_manifest.toml @@ -3,7 +3,7 @@ schema_version = "1.0.0" # Example of manifest file for a Blender extension # Change the values according to your extension id = "bioxelnodes" -version = "0.2.0" +version = "0.2.2" name = "Bioxel Nodes" tagline = "For scientific volumetric data visualization in Blender" maintainer = "Ma Nan " diff --git a/pyproject.toml b/pyproject.toml index 2e8fcf8..232a7d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "bioxelnodes" -version = "0.2.0" +version = "0.2.2" description = "" authors = ["Ma Nan "] license = "MIT"