feat: add save bioxels function
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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'}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'}
|
||||
|
||||
@@ -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
65
bioxelnodes/misc.py
Normal 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'}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user