diff --git a/bioxelnodes/assets/Nodes/BioxelNodes_latest.blend b/bioxelnodes/assets/Nodes/BioxelNodes_latest.blend new file mode 100644 index 0000000..ae27a75 --- /dev/null +++ b/bioxelnodes/assets/Nodes/BioxelNodes_latest.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a3d686d86c7519ea46bd7d8a4699076395de719a1c3165aca56b41cae070456 +size 8986342 diff --git a/bioxelnodes/assets/Nodes/BioxelNodes_v1.0.0.blend b/bioxelnodes/assets/Nodes/BioxelNodes_v1.0.0.blend deleted file mode 100644 index 6852c30..0000000 --- a/bioxelnodes/assets/Nodes/BioxelNodes_v1.0.0.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b541b9cbf734c398a47dd1d0ae4000f406e3728550a09aa720c5c0ad2626a7c1 -size 8979242 diff --git a/bioxelnodes/bioxelutils/common.py b/bioxelnodes/bioxelutils/common.py index b7e0011..d5876f5 100644 --- a/bioxelnodes/bioxelutils/common.py +++ b/bioxelnodes/bioxelutils/common.py @@ -2,7 +2,7 @@ from ast import literal_eval from pathlib import Path import bpy -from ..constants import NODE_LIB_DIRPATH, VERSIONS +from ..constants import LATEST_NODE_LIB_PATH, NODE_LIB_DIRPATH, VERSIONS from ..utils import get_cache_dir @@ -244,7 +244,7 @@ def get_node_lib_path(node_version): def local_lib_not_updated(): addon_version = VERSIONS[0]["node_version"] - addon_lib_path = get_node_lib_path(addon_version) + addon_lib_path = LATEST_NODE_LIB_PATH use_local = False for node_group in bpy.data.node_groups: @@ -258,3 +258,12 @@ def local_lib_not_updated(): not_update = get_node_version() != addon_version return use_local and not_update + +def get_output_node(node_group): + try: + output_node = get_nodes_by_type(node_group, + 'NodeGroupOutput')[0] + except: + output_node = node_group.nodes.new("NodeGroupOutput") + + return output_node \ No newline at end of file diff --git a/bioxelnodes/bioxelutils/container.py b/bioxelnodes/bioxelutils/container.py index f8cb3d3..e947dca 100644 --- a/bioxelnodes/bioxelutils/container.py +++ b/bioxelnodes/bioxelutils/container.py @@ -8,7 +8,7 @@ from mathutils import Matrix, Vector from .layer import Layer, layer_to_obj, obj_to_layer from .common import (get_container_layer_objs, get_layer_prop_value, - get_nodes_by_type, + get_nodes_by_type, get_output_node, move_node_to_node) from .node import add_node_to_graph from ..utils import get_use_link @@ -82,11 +82,7 @@ def add_layers(layers: list[Layer], cache_dir: str): node_group = container_obj.modifiers[0].node_group - try: - output_node = get_nodes_by_type(node_group, - 'NodeGroupOutput')[0] - except: - output_node = node_group.nodes.new("NodeGroupOutput") + output_node = get_output_node(node_group) for i, layer in enumerate(layers): layer_obj = layer_to_obj(layer, container_obj, cache_dir) diff --git a/bioxelnodes/bioxelutils/node.py b/bioxelnodes/bioxelutils/node.py index f642ea6..973b7cc 100644 --- a/bioxelnodes/bioxelutils/node.py +++ b/bioxelnodes/bioxelutils/node.py @@ -4,7 +4,7 @@ import bpy from .common import get_file_prop, get_node_lib_path, set_file_prop from ..exceptions import Incompatible, NoFound -from ..constants import VERSIONS +from ..constants import LATEST_NODE_LIB_PATH, VERSIONS def get_node_group(node_type: str, use_link=True): @@ -14,7 +14,7 @@ def get_node_group(node_type: str, use_link=True): # added node is always from latest node version addon_version = VERSIONS[0]["node_version"] - addon_lib_path = get_node_lib_path(addon_version) + addon_lib_path = LATEST_NODE_LIB_PATH if get_file_prop("node_version") is None: set_file_prop("node_version", addon_version) diff --git a/bioxelnodes/blender_manifest.toml b/bioxelnodes/blender_manifest.toml index 20c5057..224984a 100644 --- a/bioxelnodes/blender_manifest.toml +++ b/bioxelnodes/blender_manifest.toml @@ -1,7 +1,7 @@ schema_version = "1.0.0" id = "bioxelnodes" -version = "1.0.0" +version = "1.0.1" name = "Bioxel Nodes" tagline = "For scientific volumetric data visualization in Blender" maintainer = "Ma Nan " @@ -10,7 +10,7 @@ website = "https://omoolab.github.io/BioxelNodes/latest" tags = ["Geometry Nodes", "Render", "Import-Export"] blender_version_min = "4.2.0" -license = ["SPDX:MIT"] +license = ['SPDX:GPL-3.0-or-later'] copyright = ["2024 OmooLab"] platforms = ["windows-x64", "linux-x64", "macos-arm64", "macos-x64"] diff --git a/bioxelnodes/constants.py b/bioxelnodes/constants.py index 53692f7..173f508 100644 --- a/bioxelnodes/constants.py +++ b/bioxelnodes/constants.py @@ -1,12 +1,31 @@ from pathlib import Path -VERSIONS = [{"label": "Current", "node_version": (1, 0, 0)}, +VERSIONS = [{"label": "Latest", "node_version": (1, 0, 1)}, {"label": "v0.3.x", "node_version": (0, 3, 3)}, {"label": "v0.2.x", "node_version": (0, 2, 9)}] NODE_LIB_DIRPATH = Path(Path(__file__).parent, "assets/Nodes").resolve() +LATEST_NODE_LIB_PATH = lib_path = Path(NODE_LIB_DIRPATH, + "BioxelNodes_latest.blend").resolve() + +COMPONENT_OUTPUT_NODES = [ + "CutoutByThreshold", + "CutoutByRange", + "CutoutByHue", + "JoinComponent", + "SetProperties", + "SetColor", + "SetColorByColor", + "SetColorByColor", + "SetColorByRamp2", + "SetColorByRamp3", + "SetColorByRamp4", + "SetColorByRamp5", + "Cut", +] + MENU_ITEMS = [ { 'label': 'Component', @@ -179,9 +198,9 @@ MENU_ITEMS = [ 'icon': 'MODIFIER', 'items': [ { - 'label': 'Fetch Surface', + 'label': 'Fetch Mesh', 'icon': 'OUTLINER_OB_MESH', - 'name': 'FetchSurface', + 'name': 'FetchMesh', 'description': '' }, { diff --git a/bioxelnodes/menus.py b/bioxelnodes/menus.py index 2323026..396e9de 100644 --- a/bioxelnodes/menus.py +++ b/bioxelnodes/menus.py @@ -10,16 +10,19 @@ from .operators.layer import (FetchLayer, RelocateLayer, RetimeLayer, RenameLaye RemoveSelectedLayers, SaveSelectedLayersCache, ResampleLayer, SignScalar, CombineLabels, FillByLabel, FillByThreshold, FillByRange) -from .operators.container import (AddLocator, AddSlicer, ExtractShapeWire, +from .operators.container import (AddLocator, AddSlicer, ContainerProps, SaveContainerLayersCache, RemoveContainerMissingLayers, SaveContainer, LoadContainer, AddPieCutter, AddPlaneCutter, AddCylinderCutter, AddCubeCutter, AddSphereCutter, - ExtractBboxWire, ExtractSurface, ExtractVolume, ContainerProps) + ExtractBboxWire, ExtractMesh, ExtractShapeWire, + ExtractNodeMesh, ExtractNodeBboxWire, ExtractNodeShapeWire) from .operators.io import (ImportAsLabel, ImportAsScalar, ImportAsColor) from .operators.misc import (CleanTemp, - ReLinkNodeLib, RemoveAllMissingLayers, RenderSettingPreset, SaveAllLayersCache, SaveNodeLib, SliceViewer) + ReLinkNodeLib, RemoveAllMissingLayers, + RenderSettingPreset, SaveAllLayersCache, + SaveNodeLib, SliceViewer) class IncompatibleMenu(bpy.types.Menu): @@ -76,8 +79,7 @@ class ExtractFromContainerMenu(bpy.types.Menu): def draw(self, context): layout = self.layout - layout.operator(ExtractSurface.bl_idname) - layout.operator(ExtractVolume.bl_idname) + layout.operator(ExtractMesh.bl_idname) layout.operator(ExtractShapeWire.bl_idname) layout.operator(ExtractBboxWire.bl_idname) @@ -199,8 +201,8 @@ class BioxelNodesTopbarMenu(bpy.types.Menu): layout.operator(RemoveAllMissingLayers.bl_idname) layout.separator() - layout.operator(SaveNodeLib.bl_idname) layout.menu(ReLinkNodeLibMenu.bl_idname) + layout.operator(SaveNodeLib.bl_idname) layout.separator() layout.menu(DangerZoneMenu.bl_idname) @@ -257,6 +259,11 @@ class NodeContextMenu(bpy.types.Menu): def draw(self, context): layout = self.layout + layout.operator(ExtractNodeMesh.bl_idname) + layout.operator(ExtractNodeShapeWire.bl_idname) + layout.operator(ExtractNodeBboxWire.bl_idname) + + layout.separator() layout.operator(SaveSelectedLayersCache.bl_idname, icon=SaveSelectedLayersCache.bl_icon) layout.operator(RemoveSelectedLayers.bl_idname) diff --git a/bioxelnodes/operators/container.py b/bioxelnodes/operators/container.py index df4c347..5c523ac 100644 --- a/bioxelnodes/operators/container.py +++ b/bioxelnodes/operators/container.py @@ -1,13 +1,15 @@ import bpy + import bmesh +from ..constants import COMPONENT_OUTPUT_NODES from ..utils import get_cache_dir, get_use_link, select_object from ..bioxel.io import load_container, save_container from ..bioxelutils.container import container_to_obj, obj_to_container from ..bioxelutils.node import add_node_to_graph -from ..bioxelutils.common import (get_container_layer_objs, get_container_obj, - get_nodes_by_type, is_missing_layer, +from ..bioxelutils.common import (get_container_layer_objs, get_container_obj, get_node_type, + get_nodes_by_type, get_output_node, is_missing_layer, move_node_between_nodes, move_node_to_node, get_all_layer_objs) @@ -77,7 +79,7 @@ class LoadContainer(bpy.types.Operator): return {'RUNNING_MODAL'} -class ExtractObject(): +class ExtractNodeObject(): bl_options = {'UNDO'} def execute(self, context): @@ -87,6 +89,26 @@ class ExtractObject(): self.report({"WARNING"}, "Cannot find any bioxel container.") return {'FINISHED'} + # nodes = [node for node in context.selected_nodes + # if get_node_type(node).removeprefix("BioxelNodes_") in COMPONENT_OUTPUT_NODES] + + if len(context.selected_nodes) == 0: + self.report({"WARNING"}, "No node selected.") + return {'FINISHED'} + + selected_node = context.selected_nodes[0] + container_node_group = container_obj.modifiers[0].node_group + + container_output_node = get_output_node(container_node_group) + + if len(container_output_node.inputs[0].links) == 0: + pre_socket = None + else: + pre_socket = container_output_node.inputs[0].links[0].from_socket + + container_node_group.links.new(selected_node.outputs[0], + container_output_node.inputs[0]) + bpy.ops.mesh.primitive_cube_add( size=2, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1)) obj = bpy.context.active_object @@ -106,6 +128,15 @@ class ExtractObject(): fetch_mesh_node.inputs[0].default_value = container_obj node_group.links.new(fetch_mesh_node.outputs[0], output_node.inputs[0]) + for modifier in obj.modifiers: + bpy.ops.object.modifier_apply(modifier=modifier.name) + + obj.data.materials.clear() + + if pre_socket: + container_node_group.links.new(pre_socket, + container_output_node.inputs[0]) + select_object(obj) self.report({"INFO"}, f"Successfully picked") @@ -113,24 +144,92 @@ class ExtractObject(): return {'FINISHED'} -class ExtractSurface(bpy.types.Operator, ExtractObject): - bl_idname = "bioxelnodes.fetch_mesh" - bl_label = "Extract Surface" - bl_description = "Extract Surface" +class ExtractNodeMesh(bpy.types.Operator, ExtractNodeObject): + bl_idname = "bioxelnodes.extract_node_mesh" + bl_label = "Extract Mesh" + bl_description = "Extract Mesh" bl_icon = "OUTLINER_OB_MESH" - object_type = "Surface" + object_type = "Mesh" -class ExtractVolume(bpy.types.Operator, ExtractObject): - bl_idname = "bioxelnodes.pick_volume" - bl_label = "Extract Volume" - bl_description = "Extract Volume" - bl_icon = "OUTLINER_OB_VOLUME" - object_type = "Volume" +class ExtractNodeShapeWire(bpy.types.Operator, ExtractNodeObject): + bl_idname = "bioxelnodes.extract_node_shape_wire" + bl_label = "Extract Shape Wire" + bl_description = "Extract Shape Wire" + bl_icon = "FILE_VOLUME" + object_type = "ShapeWire" + + +class ExtractNodeBboxWire(bpy.types.Operator, ExtractNodeObject): + bl_idname = "bioxelnodes.extract_node_bbox_wire" + bl_label = "Extract Bbox Wire" + bl_description = "Extract Bbox Wire" + bl_icon = "MESH_CUBE" + object_type = "BboxWire" + + +class ExtractObject(): + bl_options = {'UNDO'} + + def execute(self, context): + container_obj = get_container_obj(context.object) + + if container_obj is None: + self.report({"WARNING"}, "Cannot find any bioxel container.") + return {'FINISHED'} + + bpy.ops.mesh.primitive_cube_add( + size=2, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1)) + obj = bpy.context.active_object + + obj.name = f"{container_obj.name}_{self.object_type}" + + bpy.ops.node.new_geometry_nodes_modifier() + modifier = obj.modifiers[0] + node_group = modifier.node_group + + output_node = get_output_node(node_group) + + fetch_mesh_node = add_node_to_graph(f"Fetch{self.object_type}", + node_group, + node_label=f"Fetch {self.object_type}", + use_link=get_use_link()) + + fetch_mesh_node.inputs[0].default_value = container_obj + + if self.object_type == "Mesh": + node_group.links.new( + fetch_mesh_node.outputs[0], output_node.inputs[0]) + else: + curve_to_mesh_node = node_group.nodes.new( + "GeometryNodeCurveToMesh") + node_group.links.new( + fetch_mesh_node.outputs[0], curve_to_mesh_node.inputs[0]) + node_group.links.new( + curve_to_mesh_node.outputs[0], output_node.inputs[0]) + + for modifier in obj.modifiers: + bpy.ops.object.modifier_apply(modifier=modifier.name) + + obj.data.materials.clear() + + select_object(obj) + + self.report({"INFO"}, f"Successfully picked") + + return {'FINISHED'} + + +class ExtractMesh(bpy.types.Operator, ExtractObject): + bl_idname = "bioxelnodes.extract_mesh" + bl_label = "Extract Mesh" + bl_description = "Extract Mesh" + bl_icon = "OUTLINER_OB_MESH" + object_type = "Mesh" class ExtractShapeWire(bpy.types.Operator, ExtractObject): - bl_idname = "bioxelnodes.pick_shape_wire" + bl_idname = "bioxelnodes.extract_shape_wire" bl_label = "Extract Shape Wire" bl_description = "Extract Shape Wire" bl_icon = "FILE_VOLUME" @@ -138,7 +237,7 @@ class ExtractShapeWire(bpy.types.Operator, ExtractObject): class ExtractBboxWire(bpy.types.Operator, ExtractObject): - bl_idname = "bioxelnodes.pick_bbox_wire" + bl_idname = "bioxelnodes.extract_bbox_wire" bl_label = "Extract Bbox Wire" bl_description = "Extract Bbox Wire" bl_icon = "MESH_CUBE" @@ -253,8 +352,8 @@ class AddCutter(): node_group, use_link=get_use_link()) - output_node = get_nodes_by_type(node_group, - 'NodeGroupOutput')[0] + output_node = get_output_node(node_group) + if len(output_node.inputs[0].links) == 0: node_group.links.new(cut_node.outputs[0], output_node.inputs[0]) @@ -277,7 +376,7 @@ class AddCutter(): node_name="ObjectCutter", node_label=name) node = bpy.context.active_node - node.inputs[0].default_value = name + node.inputs[0].default_value = self.cutter_type.capitalize() node.inputs[1].default_value = cutter_obj return {'FINISHED'} @@ -368,8 +467,8 @@ class AddSlicer(bpy.types.Operator): slice_node.inputs[1].default_value = slicer_obj - output_node = get_nodes_by_type(node_group, - 'NodeGroupOutput')[0] + output_node = get_output_node(node_group) + if len(output_node.inputs[0].links) == 0: node_group.links.new(slice_node.outputs[0], output_node.inputs[0]) @@ -419,8 +518,8 @@ class AddLocator(bpy.types.Operator): parent_node.inputs[1].default_value = locator_obj - output_node = get_nodes_by_type(node_group, - 'NodeGroupOutput')[0] + output_node = get_output_node(node_group) + if len(output_node.inputs[0].links) == 0: node_group.links.new(parent_node.outputs[0], output_node.inputs[0]) diff --git a/bioxelnodes/operators/misc.py b/bioxelnodes/operators/misc.py index 8b11151..b7bf190 100644 --- a/bioxelnodes/operators/misc.py +++ b/bioxelnodes/operators/misc.py @@ -5,7 +5,7 @@ import shutil from ..bioxelutils.common import get_all_layer_objs, get_node_lib_path, get_node_version, is_missing_layer, set_file_prop from .layer import RemoveLayers, SaveLayersCache -from ..constants import NODE_LIB_DIRPATH, VERSIONS +from ..constants import LATEST_NODE_LIB_PATH, VERSIONS from ..utils import get_cache_dir @@ -19,7 +19,8 @@ class ReLinkNodeLib(bpy.types.Operator): def execute(self, context): node_version = VERSIONS[self.index]['node_version'] - lib_path = get_node_lib_path(node_version) + lib_path = get_node_lib_path(node_version) \ + if self.index != 0 else LATEST_NODE_LIB_PATH node_libs = [] for node_group in bpy.data.node_groups: @@ -56,16 +57,23 @@ class SaveNodeLib(bpy.types.Operator): def execute(self, context): node_version = get_node_version() + if node_version is None: node_version = VERSIONS[0]["node_version"] else: if node_version not in [v["node_version"] for v in VERSIONS]: node_version = VERSIONS[0]["node_version"] - - lib_path = get_node_lib_path(node_version) - + + if node_version == VERSIONS[0]["node_version"]: + lib_path = LATEST_NODE_LIB_PATH + else: + lib_path = get_node_lib_path(node_version) + + version_str = "v"+".".join([str(i) for i in list(node_version)]) + lib_dir = bpy.path.abspath(self.lib_dir) - local_lib_path: Path = Path(lib_dir, lib_path.name).resolve() + local_lib_path: Path = Path(lib_dir, + f"BioxelNodes_{version_str}.blend").resolve() node_lib_path: Path = lib_path blend_path = Path(bpy.path.abspath("//")).resolve() diff --git a/pyproject.toml b/pyproject.toml index 70a02b5..697c612 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "bioxelnodes" -version = "1.0.0" +version = "1.0.1" description = "" authors = ["Ma Nan "] license = "MIT"