feat: support blender 4.2 extension

This commit is contained in:
2024-06-05 09:11:22 +08:00
parent 470cdab4f6
commit f3be5d8660
9 changed files with 141 additions and 204 deletions

View File

@@ -22,7 +22,7 @@ jobs:
# id: draft_release
# uses: cardinalby/git-get-release-action@1.2.4
# with:
# releaseName: Draft
# releaseName: Upload
# env:
# GITHUB_TOKEN: ${{ github.token }}
@@ -70,5 +70,38 @@ jobs:
with:
upload_url: ${{ needs.draft_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./package.zip
asset_name: BioxelNodes_${{ needs.draft_release.outputs.version }}.zip
asset_name: BioxelNodes_Addon_${{ needs.draft_release.outputs.version }}.zip
asset_content_type: application/zip
upload_blender_extension:
name: Upload Blender Extension
needs: draft_release
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
lfs: 'true'
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.11
- name: Zip Extension
run: |
pip download SimpleITK --dest bioxelnodes/wheels --only-binary=:all: --python-version=3.11 --platform=macosx_11_0_arm64
pip download SimpleITK --dest bioxelnodes/wheels --only-binary=:all: --python-version=3.11 --platform=win_amd64
rm -r bioxelnodes/externalpackage
cp extension/__init__.py bioxelnodes/__init__.py
cp extension/preferences.py bioxelnodes/preferences.py
cp extension/blender_manifest.toml bioxelnodes/blender_manifest.toml
zip -r package.zip bioxelnodes
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.draft_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./package.zip
asset_name: BioxelNodes_Extension_${{ needs.draft_release.outputs.version }}.zip
asset_content_type: application/zip

View File

@@ -57,7 +57,7 @@ def unregister():
#################################################
def get_all_submodules(directory):
return list(iter_submodules(directory, directory.name))
return list(iter_submodules(directory, __package__))
def iter_submodules(path, package_name):

View File

@@ -22,200 +22,4 @@ def create_points_obj(points, name="points"):
# make the bmesh the object's mesh
bm.to_mesh(mesh)
bm.free() # always do this when finished
return obj
# class ImportDICOMPointsDialog(bpy.types.Operator):
# bl_idname = "bioxelnodes.import_dicom_points_dialog"
# bl_label = "Volume Data as Bioxels"
# bl_description = "Import Volume Data as Bioxels (VDB)"
# bl_options = {'UNDO'}
# filepath: bpy.props.StringProperty(
# subtype="FILE_PATH",
# options={'HIDDEN'}
# ) # type: ignore
# bioxels_shape: bpy.props.IntVectorProperty(
# name="Bioxels Shape (ReadOnly)",
# min=0,
# default=(100, 100, 100)
# ) # type: ignore
# bioxel_size: bpy.props.FloatProperty(
# name="Bioxel Size",
# soft_min=0.1, soft_max=10.0,
# min=1e-2, max=1e2,
# default=1,
# update=on_bioxel_size_changed
# ) # type: ignore
# orig_spacing: bpy.props.FloatVectorProperty(
# name="Original Spacing",
# default=(1, 1, 1),
# update=on_orig_spacing_changed
# ) # type: ignore
# orig_shape: bpy.props.IntVectorProperty(
# name="Original Shape",
# default=(100, 100, 100),
# options={'HIDDEN'}
# ) # type: ignore
# auto: bpy.props.BoolProperty(
# name="Auto Setting",
# default=False,
# options={'HIDDEN'}
# ) # type: ignore
# scene_scale: bpy.props.FloatProperty(
# name="Scene Scale",
# soft_min=0.001, soft_max=100.0,
# min=1e-6, max=1e6,
# default=0.01,
# ) # type: ignore
# do_add_segmentnode: bpy.props.BoolProperty(
# name="Add Segment Node",
# default=True,
# ) # type: ignore
# do_change_render_setting: bpy.props.BoolProperty(
# name="Change Render Setting",
# default=True,
# ) # type: ignore
# def execute(self, context):
# files = get_data_files(self.filepath)
# name = Path(self.filepath).parent.name
# image = sitk.ReadImage(files)
# bioxel_size = float(self.bioxel_size)
# orig_spacing = tuple(self.orig_spacing)
# image_spacing = image.GetSpacing()
# image_shape = image.GetSize()
# bioxels_spacing = (
# image_spacing[0] / orig_spacing[0] * bioxel_size,
# image_spacing[1] / orig_spacing[1] * bioxel_size,
# image_spacing[2] / orig_spacing[2] * bioxel_size
# )
# bioxels_shape = (
# int(image_shape[0] / bioxel_size * orig_spacing[0]),
# int(image_shape[1] / bioxel_size * orig_spacing[1]),
# int(image_shape[2] / bioxel_size * orig_spacing[2]),
# )
# print("Resampling...")
# image = sitk.Resample(
# image1=image,
# size=bioxels_shape,
# transform=sitk.Transform(),
# interpolator=sitk.sitkLinear,
# outputOrigin=image.GetOrigin(),
# outputSpacing=bioxels_spacing,
# outputDirection=image.GetDirection(),
# defaultPixelValue=0,
# outputPixelType=image.GetPixelID(),
# )
# print("Orienting to RAS...")
# image = sitk.DICOMOrient(image, 'RAS')
# array = sitk.GetArrayFromImage(image)
# orig_dtype = str(array.dtype)
# print(f"Coverting Dtype from {orig_dtype} to float...")
# array = array.astype(float)
# # ITK indices, by convention, are [i,j,k] while NumPy indices are [k,j,i]
# # https://www.slicer.org/wiki/Coordinate_systems
# # ITK Numpy 3D
# # R (ight) i -> k -> x
# # A (nterior) j -> j -> y
# # S (uperior) k -> i -> z
# array = np.transpose(array)
# bioxels_max = float(np.max(array))
# bioxels_min = float(np.min(array))
# bioxels_shape = array.shape
# print("Bioxel Size:", bioxel_size)
# print("Bioxels Shape:", bioxels_shape)
# bioxels_offset = 0.0
# if bioxels_min < 0 and orig_dtype[0] != "u":
# bioxels_offset = -bioxels_min
# array = array + np.full_like(array, bioxels_offset)
# bioxels_max = float(np.max(array))
# bioxels_min = float(np.min(array))
# print("Offseted Max:", bioxels_max)
# print("Offseted Min:", bioxels_min)
# points = []
# for x in range(array.shape[0]):
# for y in range(array.shape[1]):
# for z in range(array.shape[2]):
# points.append({
# "pos": (x, y, z),
# "value": array[x, y, z],
# })
# # # Build VDB
# # grid = vdb.FloatGrid()
# # grid.copyFromArray(array.copy())
# # # After sitk.DICOMOrient(), origin and direction will also orient base on LPS
# # # so we need to convert them into RAS
# # mat_lps2ras = axis_conversion(
# # from_forward='-Z',
# # from_up='-Y',
# # to_forward='-Z',
# # to_up='Y'
# # ).to_4x4()
# # mat_location = mathutils.Matrix.Translation(
# # mathutils.Vector(image.GetOrigin())
# # )
# # mat_rotation = mathutils.Matrix(
# # np.array(image.GetDirection()).reshape((3, 3))
# # ).to_4x4()
# # mat_scale = mathutils.Matrix.Scale(
# # bioxel_size, 4
# # )
# # transfrom = mat_lps2ras @ mat_location @ mat_rotation @ mat_scale
# obj = create_points_obj(points)
# # Make transformation
# scene_scale = float(self.scene_scale)
# # (S)uperior -Z -> Y
# # (A)osterior Y -> Z
# mat_ras2blender = axis_conversion(
# from_forward='-Z',
# from_up='Y',
# to_forward='Y',
# to_up='Z'
# ).to_4x4()
# mat_scene_scale = mathutils.Matrix.Scale(
# scene_scale, 4
# )
# obj.matrix_world = mat_ras2blender @ mat_scene_scale
# self.report({"INFO"}, "Successfully Imported")
# return {'FINISHED'}
# def invoke(self, context, event):
# # print(tuple(self.orig_shape))
# self.auto = True
# self.bioxel_size = 1
# context.window_manager.invoke_props_dialog(self)
# return {'RUNNING_MODAL'}
return obj

View File

@@ -1,7 +1,7 @@
import bpy
from pathlib import Path
import shutil
from bioxelnodes.utils import get_all_layers, get_container, get_container_layers
from .utils import get_all_layers, get_container, get_container_layers
def save_layer(layer, output_dir):

View File

@@ -7,7 +7,7 @@ import bpy
def add_driver_to_node_factory(source_prop, target_prop):
callback_str = f"""
import bpy
from bioxelnodes.utils import add_direct_driver, get_bioxels_obj
from .utils import add_direct_driver, get_bioxels_obj
bioxels_obj = get_bioxels_obj(bpy.context.active_object)
if bioxels_obj:
container_obj = bioxels_obj.parent
@@ -26,7 +26,7 @@ else:
def set_prop_to_node_factory(source_prop, target_prop):
callback_str = f"""
import bpy
from bioxelnodes.utils import get_bioxels_obj
from .utils import get_bioxels_obj
bioxels_obj = get_bioxels_obj(bpy.context.active_object)
if bioxels_obj:
container_obj = bioxels_obj.parent

View File

@@ -26,5 +26,4 @@ class BioxelNodesPreferences(bpy.types.AddonPreferences, ExternalPackagePreferen
layout.label(text="Configuration")
layout.prop(self, 'cache_dir')
layout.prop(self, "do_change_render_setting")

22
extension/__init__.py Normal file
View File

@@ -0,0 +1,22 @@
import bpy
from . import auto_load
from . import menus
auto_load.init()
def register():
auto_load.register()
menus.add()
bpy.types.Scene.bioxel_layer_dir = bpy.props.StringProperty(
name="Bioxel Layers Directory",
subtype='DIR_PATH',
default="//"
)
def unregister():
menus.remove()
auto_load.unregister()

View File

@@ -0,0 +1,56 @@
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"
name = "Bioxel Nodes"
tagline = "For scientific volumetric data visualization in Blender"
maintainer = "Ma Nan <icrdr2010@outlook.com>"
# Supported types: "add-on", "theme"
type = "add-on"
# Optional: add-ons can list which resources they will require:
# * "files" (for access of any filesystem operations)
# * "network" (for internet access)
# * "clipboard" (to read and/or write the system clipboard)
# * "camera" (to capture photos and videos)
# * "microphone" (to capture audio)
permissions = ["files"]
# Optional link to documentation, support, source files, etc
website = "https://omoolab.github.io/BioxelNodes/latest"
# Optional list defined by Blender and server, see:
# https://docs.blender.org/manual/en/dev/extensions/tags.html
tags = ["Geometry Nodes", "Render", "Material"]
blender_version_min = "4.2.0"
# Optional: maximum supported Blender version
# blender_version_max = "5.1.0"
# License conforming to https://spdx.org/licenses/ (use "SPDX: prefix)
# https://docs.blender.org/manual/en/dev/extensions/licenses.html
license = ["SPDX:MIT"]
# Optional: required by some licenses.
copyright = ["2024 OmooLab"]
# Optional list of supported platforms. If omitted, the extension will be available in all operating systems.
platforms = ["windows-amd64", "macos-x86_64"]
# Other supported platforms: "windows-arm64", "macos-x86_64"
# TODO: externalpackage to wheels
# Optional: bundle 3rd party Python modules.
# https://docs.blender.org/manual/en/dev/extensions/python_wheels.html
wheels = [
"./wheels/SimpleITK-2.3.1-cp311-cp311-win_amd64.whl",
"./wheels/SimpleITK-2.3.1-cp311-cp311-macosx_11_0_arm64.whl",
]
# Optional: build setting.
# https://docs.blender.org/manual/en/dev/extensions/command_line_arguments.html#command-line-args-extension-build
# [build]
# paths_exclude_pattern = [
# "/.git/"
# "__pycache__/"
# ]

23
extension/preferences.py Normal file
View File

@@ -0,0 +1,23 @@
import bpy
from pathlib import Path
class BioxelNodesPreferences(bpy.types.AddonPreferences):
bl_idname = __package__
cache_dir: bpy.props.StringProperty(
name="Set Cache Directory",
subtype='DIR_PATH',
default=str(Path(Path.home(), '.bioxelnodes'))
) # type: ignore
do_change_render_setting: bpy.props.BoolProperty(
name="Change Render Setting",
default=True,
) # type: ignore
def draw(self, context):
layout = self.layout
layout.label(text="Configuration")
layout.prop(self, 'cache_dir')
layout.prop(self, "do_change_render_setting")