feat: add save bioxels function

This commit is contained in:
2024-04-24 14:54:22 +08:00
parent 15b24b61b5
commit a579b24f9a
11 changed files with 132 additions and 33 deletions

View File

@@ -18,6 +18,12 @@ bl_info = {
auto_load.init()
bpy.types.Scene.bioxels_dir = bpy.props.StringProperty(
name="Bioxels Directory",
subtype='DIR_PATH',
default="//"
)
def register():
auto_load.register()

View File

@@ -1,5 +1,5 @@
import bpy
from .utils import get_bioxels_obj
from .utils import get_bioxels_obj, get_node_by_type
class ConvertToMesh(bpy.types.Operator):
@@ -8,6 +8,15 @@ class ConvertToMesh(bpy.types.Operator):
bl_description = "Convert Bioxels To Mesh."
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
bioxels_obj = None
for obj in bpy.context.selected_objects:
bioxels_obj = get_bioxels_obj(obj)
break
return True if bioxels_obj else False
def execute(self, context):
bioxels_objs = []
@@ -16,7 +25,7 @@ class ConvertToMesh(bpy.types.Operator):
if bioxels_obj:
bioxels_objs.append(bioxels_obj)
if len(bioxels_objs)==0:
if len(bioxels_objs) == 0:
self.report({"WARNING"}, "Cannot find any bioxels.")
return {'FINISHED'}
@@ -31,7 +40,7 @@ class ConvertToMesh(bpy.types.Operator):
nodes = modifier.node_group.nodes
links = modifier.node_group.links
output_node = nodes.get("Group Output")
output_node = get_node_by_type(nodes, 'NodeGroupOutput')[0]
object_node = nodes.new("GeometryNodeObjectInfo")
realize_nodes = nodes.new("GeometryNodeRealizeInstances")
object_node.inputs[0].default_value = bioxels_obj
@@ -43,4 +52,6 @@ class ConvertToMesh(bpy.types.Operator):
bpy.context.object.active_material_index = 1
bpy.ops.object.material_slot_remove()
self.report({"INFO"}, f"Successfully convert to mesh")
return {'FINISHED'}

View File

@@ -12,10 +12,10 @@ class CustomNodes():
root_icon='NONE'
) -> None:
if not Path(nodes_file).is_file():
raise FileNotFoundError(Path(nodes_file).resolve().as_posix())
raise FileNotFoundError(str(Path(nodes_file).resolve()))
self.menu_items = menu_items
self.nodes_file = Path(nodes_file).resolve().as_posix()
self.nodes_file = str(Path(nodes_file).resolve())
self.root_label = root_label
self.root_icon = root_icon
@@ -28,7 +28,7 @@ class CustomNodes():
)
self.menu_classes = menu_classes
idname = f"CUSTOMNODES_MT_NODES__{root_label.replace(' ', '').upper()}"
idname = f"CUSTOMNODES_MT_NODES_{root_label.replace(' ', '').upper()}"
def add_node_menu(self, context):
if ('GeometryNodeTree' == bpy.context.area.spaces[0].tree_type):
@@ -40,7 +40,7 @@ class CustomNodes():
def _create_menu_class(self, menu_classes, items, label='CustomNodes', icon=0, idname_namespace=None):
nodes_file = self.nodes_file
idname_namespace = idname_namespace or "CUSTOMNODES_MT_NODES_"
idname_namespace = idname_namespace or "CUSTOMNODES_MT_NODES"
idname = f"{idname_namespace}_{label.replace(' ', '').upper()}"
# create submenu class if item is menu.

View File

@@ -231,7 +231,7 @@ class PackageInstaller():
"""
# path to python.exe
python_exe = Path(sys.executable).resolve().as_posix()
python_exe = str(Path(sys.executable).resolve())
# build the command list
cmd_list = [python_exe] + cmd_list

View File

@@ -14,13 +14,13 @@ class ExternalPackagePreferences():
requirements_dir: bpy.props.StringProperty(
name="requirements.txt Directory",
subtype='DIR_PATH',
default=Path(__file__).parent.as_posix()
default=str(Path(__file__).parent)
) # type: ignore
log_dir: bpy.props.StringProperty(
name="Python Log Directory",
subtype='DIR_PATH',
default=Path(Path.home(), '.externalpackage', 'logs').as_posix()
default=str(Path(Path.home(), '.externalpackage', 'logs'))
) # type: ignore
pypi_mirror_provider: bpy.props.StringProperty(
@@ -40,7 +40,6 @@ class ExternalPackagePreferences():
row_import = col_main.row()
row_import.prop(self, 'pypi_mirror_provider', text='Set PyPI Mirror')
installer = PackageInstaller(
pypi_mirror_provider=self.pypi_mirror_provider,
log_dir=self.log_dir,

View File

@@ -446,18 +446,18 @@ class ExportVDB(bpy.types.Operator):
filename_ext = ".vdb"
def invoke(self, context, event):
@classmethod
def poll(cls, context):
bioxels_obj = None
for obj in bpy.context.selected_objects:
bioxels_obj = get_bioxels_obj(obj)
break
if bioxels_obj:
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
else:
self.report({"WARNING"}, "Cannot find any bioxels.")
return {'CANCELLED'}
return True if bioxels_obj else False
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
def execute(self, context):
bioxels_obj = None
@@ -466,12 +466,15 @@ class ExportVDB(bpy.types.Operator):
break
filepath = f"{self.filepath.split('.')[0]}.vdb"
# "//"
source_dir = bpy.path.abspath(bioxels_obj.data.filepath)
output_path: Path = Path(filepath).resolve()
source_path: Path = Path(bioxels_obj.data.filepath).resolve()
source_path: Path = Path(source_dir).resolve()
# print('output_path', output_path)
# print('source_path', source_path)
shutil.copy(source_path, output_path)
self.report({"INFO"}, "Successfully Exported")
self.report({"INFO"}, f"Successfully exported to {output_path}")
return {'FINISHED'}

View File

@@ -1,8 +1,20 @@
from bioxelnodes.misc import SaveBioxels
import bpy
from .convert import ConvertToMesh
from .io import ExportVDB, ImportDICOM
class ObjectMenu(bpy.types.Menu):
bl_idname = "BIOXELNODES_MT_OBJECT"
bl_label = "Bioxels"
def draw(self, context):
layout = self.layout
layout.operator(SaveBioxels.bl_idname)
layout.separator()
layout.operator(ConvertToMesh.bl_idname)
def TOPBAR_FILE_IMPORT(self, context):
layout = self.layout
layout.separator()
@@ -17,7 +29,8 @@ def TOPBAR_FILE_EXPORT(self, context):
def VIEW3D_OBJECT(self, context):
layout = self.layout
layout.operator(ConvertToMesh.bl_idname)
layout.menu(ObjectMenu.bl_idname)
layout.separator()

65
bioxelnodes/misc.py Normal file
View File

@@ -0,0 +1,65 @@
from pathlib import Path
import shutil
from bioxelnodes.utils import get_bioxels_obj
import bpy
class BIOXELNODES_PT_Bioxels(bpy.types.Panel):
bl_idname = "BIOXELNODES_PT_Bioxels"
bl_label = "Bioxels"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
layout = self.layout
scene = context.scene
layout.prop(scene, 'bioxels_dir')
class SaveBioxels(bpy.types.Operator):
bl_idname = "bioxelnodes.save_bioxels"
bl_label = "Save Bioxels"
bl_description = "Save Bioxels to Directory."
bl_options = {'UNDO'}
@classmethod
def poll(cls, context):
bioxels_obj = None
for obj in bpy.context.selected_objects:
bioxels_obj = get_bioxels_obj(obj)
break
return True if bioxels_obj else False
def execute(self, context):
bioxels_objs = []
for obj in bpy.context.selected_objects:
bioxels_obj = get_bioxels_obj(obj)
if bioxels_obj:
bioxels_objs.append(bioxels_obj)
if len(bioxels_objs) == 0:
self.report({"WARNING"}, "Cannot find any bioxels.")
return {'FINISHED'}
for bioxels_obj in bioxels_objs:
name = bioxels_obj.parent.name
# "//"
bioxels_dir = bpy.path.abspath(context.scene.bioxels_dir)
source_dir = bpy.path.abspath(bioxels_obj.data.filepath)
output_path: Path = Path(bioxels_dir, f"{name}.vdb").resolve()
source_path: Path = Path(source_dir).resolve()
if output_path != source_path:
shutil.copy(source_path, output_path)
blend_path = Path(bpy.path.abspath("//")).resolve()
bioxels_obj.data.filepath = bpy.path.relpath(
str(output_path), start=str(blend_path))
self.report({"INFO"}, f"Successfully saved to {output_path}")
return {'FINISHED'}

View File

@@ -7,9 +7,9 @@ class BioxelNodesPreferences(bpy.types.AddonPreferences, ExternalPackagePreferen
bl_idname = __package__
cache_dir: bpy.props.StringProperty(
name="Python Log Directory",
name="VDB Cache Directory",
subtype='DIR_PATH',
default=Path(Path.home(), '.bioxelnodes').as_posix()
default=str(Path(Path.home(), '.bioxelnodes'))
) # type: ignore
def draw(self, context):
@@ -18,5 +18,5 @@ class BioxelNodesPreferences(bpy.types.AddonPreferences, ExternalPackagePreferen
layout.prop(self, 'cache_dir', text='Set Cache Directory')
# ExternalPackagePreferences Config
self.requirements_dir = Path(__file__).parent.as_posix()
self.requirements_dir = str(Path(__file__).parent)
super().draw(context)