diff --git a/bioxelnodes/assets/Nodes/BioxelNodes_4.2.blend b/bioxelnodes/assets/Nodes/BioxelNodes_4.2.blend index 4442571..15e4bec 100644 --- a/bioxelnodes/assets/Nodes/BioxelNodes_4.2.blend +++ b/bioxelnodes/assets/Nodes/BioxelNodes_4.2.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2de3498292c416d2ddc48f088c380330276bd75085f4261e8857c77160a3fa9b -size 7489655 +oid sha256:1de545ce6fa3f392f57a26f241d2d95877371b86aecfc8d7a5408b35f4ffeeaa +size 7626389 diff --git a/bioxelnodes/bioxelutils/layer.py b/bioxelnodes/bioxelutils/layer.py index 45f9207..587a9f3 100644 --- a/bioxelnodes/bioxelutils/layer.py +++ b/bioxelnodes/bioxelutils/layer.py @@ -9,7 +9,8 @@ from uuid import uuid4 from ..nodes import custom_nodes from ..bioxel.layer import Layer -from .utils import get_layer_prop_value, move_node_between_nodes +from .utils import (get_layer_prop_value, + move_node_between_nodes, add_direct_driver) def obj_to_layer(layer_obj: bpy.types.Object): @@ -135,9 +136,14 @@ def layer_to_obj(layer: Layer, cache_filepaths = [cache_filepath] layer_data = bpy.data.volumes.new(layer_display_name) + layer_data.render.space = 'WORLD' + layer_data.render.step_size = container_obj.scale[0] * layer.bioxel_size[0] layer_data.sequence_mode = 'REPEAT' layer_data.filepath = str(cache_filepaths[0]) + # add_direct_driver(layer_data, "render.step_size", + # container_obj, "scale[0]") + if layer.frame_count > 1: layer_data.is_sequence = True layer_data.frame_duration = layer.frame_count @@ -145,7 +151,6 @@ def layer_to_obj(layer: Layer, layer_data.is_sequence = False layer_obj = bpy.data.objects.new(layer_display_name, layer_data) - layer_obj['bioxel_layer'] = True print(f"Creating Node for {layer.name}...") diff --git a/bioxelnodes/bioxelutils/utils.py b/bioxelnodes/bioxelutils/utils.py index 55823b7..153f149 100644 --- a/bioxelnodes/bioxelutils/utils.py +++ b/bioxelnodes/bioxelutils/utils.py @@ -94,3 +94,35 @@ def get_all_layer_objs(): layer_objs.append(obj) return layer_objs + +def add_driver(target, target_prop, var_sources, expression): + driver = target.driver_add(target_prop) + is_vector = isinstance(driver, list) + drivers = driver if is_vector else [driver] + + for i, driver in enumerate(drivers): + for j, var_source in enumerate(var_sources): + + source = var_source['source'] + prop = var_source['prop'] + + var = driver.driver.variables.new() + var.name = f"var{j}" + + var.targets[0].id_type = source.id_type + var.targets[0].id = source + var.targets[0].data_path = f'["{prop}"][{i}]'\ + if is_vector else f'["{prop}"]' + + driver.driver.expression = expression + + +def add_direct_driver(target, target_prop, source, source_prop): + drivers = [ + { + "source": source, + "prop": source_prop + } + ] + expression = "var0" + add_driver(target, target_prop, drivers, expression) \ No newline at end of file diff --git a/bioxelnodes/menus.py b/bioxelnodes/menus.py index f6e65e8..70715ae 100644 --- a/bioxelnodes/menus.py +++ b/bioxelnodes/menus.py @@ -13,10 +13,10 @@ from .operators.layer import (FetchLayerMenu, FetchLayer, from .operators.container import (SaveAllLayerCaches, SaveContainer, LoadContainer, AddPieCutter, AddPlaneCutter, AddCylinderCutter, AddCubeCutter, AddSphereCutter, - PickBboxWire, PickMesh, PickVolume) + PickBboxWire, PickMesh, PickVolume, ScaleContainer) from .operators.io import (ImportAsLabel, ImportAsScalar, ImportAsColor) from .operators.misc import (CleanAllCaches, - ReLinkNodes, RenderSettingPreset, SaveStagedData) + ReLinkNodes, RenderSettingPreset, SaveStagedData, SliceViewer) class PickFromContainerMenu(bpy.types.Menu): @@ -156,19 +156,19 @@ class BioxelNodesNodeMenu(bpy.types.Menu): active_obj_name = "" layout = self.layout - + layout.separator() layout.menu(AddLayerMenu.bl_idname, icon=AddLayerMenu.bl_icon) - - layout.operator(RemoveMissingLayers.bl_idname, - icon=RemoveMissingLayers.bl_icon) - layout.separator() layout.operator(SaveContainer.bl_idname) + layout.separator() + layout.operator(ScaleContainer.bl_idname) layout.menu(AddCutterMenu.bl_idname) layout.menu(PickFromContainerMenu.bl_idname) layout.operator(SaveAllLayerCaches.bl_idname, icon=SaveAllLayerCaches.bl_icon) + layout.operator(RemoveMissingLayers.bl_idname, + icon=RemoveMissingLayers.bl_icon) layout.separator() layout.menu(FetchLayerMenu.bl_idname) @@ -247,6 +247,8 @@ def NODE_PT(self, context): sidebar.menu(AddLayerMenu.bl_idname, icon=AddLayerMenu.bl_icon, text="") + sidebar.operator(SaveAllLayerCaches.bl_idname, + icon=SaveAllLayerCaches.bl_icon, text="") sidebar.operator(RemoveMissingLayers.bl_idname, icon=RemoveMissingLayers.bl_icon, text="") @@ -285,13 +287,21 @@ def NODE_PT(self, context): layout.separator() +def VIEW3D_TOPBAR(self, context): + layout = self.layout + layout.operator(SliceViewer.bl_idname, + icon=SliceViewer.bl_icon, text="") + + def add(): + bpy.types.VIEW3D_HT_header.append(VIEW3D_TOPBAR) bpy.types.NODE_PT_node_tree_properties.prepend(NODE_PT) bpy.types.TOPBAR_MT_editor_menus.append(TOPBAR) bpy.types.NODE_MT_editor_menus.append(NODE) def remove(): + bpy.types.VIEW3D_HT_header.remove(VIEW3D_TOPBAR) bpy.types.NODE_PT_node_tree_properties.remove(NODE_PT) bpy.types.TOPBAR_MT_editor_menus.remove(TOPBAR) bpy.types.NODE_MT_editor_menus.remove(NODE) diff --git a/bioxelnodes/nodes.py b/bioxelnodes/nodes.py index b219a85..bd94e93 100644 --- a/bioxelnodes/nodes.py +++ b/bioxelnodes/nodes.py @@ -60,9 +60,9 @@ MENU_ITEMS = [ 'node_description': '' }, { - 'label': 'Set Color by Layer', + 'label': 'Set Color by Color', 'icon': 'IPO_QUINT', - 'node_type': 'BioxelNodes_SetColorByLayer', + 'node_type': 'BioxelNodes_SetColorByColor', 'node_description': '' }, { diff --git a/bioxelnodes/operators/container.py b/bioxelnodes/operators/container.py index 452c636..0683327 100644 --- a/bioxelnodes/operators/container.py +++ b/bioxelnodes/operators/container.py @@ -6,7 +6,8 @@ from ..nodes import custom_nodes from ..customnodes.nodes import AddCustomNode from ..bioxel.io import load_container, save_container from ..bioxelutils.container import container_to_obj, obj_to_container -from ..bioxelutils.utils import (get_container_layer_objs, get_container_obj, get_nodes_by_type, +from ..bioxelutils.utils import (get_container_layer_objs, get_container_obj, + get_layer_prop_value, get_nodes_by_type, move_node_between_nodes, move_node_to_node, get_all_layer_objs) @@ -66,7 +67,8 @@ class LoadContainer(bpy.types.Operator): select_object(container_obj) if is_first_import: - change_render_setting(context) + bpy.ops.bioxelnodes.render_setting_preset('EXEC_DEFAULT', + preset="preview_c") self.report({"INFO"}, f"Successfully load {load_path}") return {'FINISHED'} @@ -158,14 +160,24 @@ class AddCutter(): bpy.ops.mesh.primitive_ico_sphere_add( radius=1, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1)) elif self.cutter_type == "pie": + bpy.ops.mesh.primitive_plane_add( + size=2, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1)) + pie = bpy.context.active_object + pie.name = "Pie" + orig_data = pie.data + # Create mesh pie_mesh = bpy.data.meshes.new('Pie') - # Create object - pie = bpy.data.objects.new('Pie', pie_mesh) + pie.data = pie_mesh + bpy.data.meshes.remove(orig_data) + + # # Create object + # pie = bpy.data.objects.new('Pie', pie_mesh) + + # # Link object to scene + # bpy.context.scene.collection.objects.link(pie) - # Link object to scene - bpy.context.scene.collection.objects.link(pie) # Get a BMesh representation bm = bmesh.new() # create an empty BMesh bm.from_mesh(pie_mesh) # fill it in from a Mesh @@ -189,7 +201,7 @@ class AddCutter(): # Finish up, write the bmesh back to the mesh bm.to_mesh(pie_mesh) - bpy.context.view_layer.objects.active = pie + # bpy.context.view_layer.objects.active = pie cutter_obj = bpy.context.active_object cutter_obj.location = container_obj.location @@ -325,6 +337,46 @@ class AddPieCutter(bpy.types.Operator, AddCutter): cutter_type = "pie" +class ScaleContainer(bpy.types.Operator): + bl_idname = "bioxelnodes.scale_container" + bl_label = "Scale Container" + bl_description = "Scale Container." + bl_icon = "FILE_TICK" + + scene_scale: bpy.props.FloatProperty(name="Scene Scale", + soft_min=0.0001, soft_max=10.0, + min=1e-6, max=1e6, + default=0.01) # type: ignore + + 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'} + + container_obj.scale[0] = self.scene_scale + container_obj.scale[1] = self.scene_scale + container_obj.scale[2] = self.scene_scale + + for layer_obj in get_container_layer_objs(container_obj): + bioxel_size = get_layer_prop_value(layer_obj, "bioxel_size") + layer_obj.data.render.step_size = self.scene_scale * bioxel_size + + return {'FINISHED'} + + def invoke(self, context, event): + container_obj = get_container_obj(context.object) + + if container_obj is None: + return self.execute(context) + else: + self.scene_scale = container_obj.scale[0] + context.window_manager.invoke_props_dialog(self, + width=500) + return {'RUNNING_MODAL'} + + class SaveAllLayerCaches(bpy.types.Operator): bl_idname = "bioxelnodes.save_all_layer_caches" bl_label = "Save All Layer Caches" diff --git a/bioxelnodes/operators/io.py b/bioxelnodes/operators/io.py index e0cb49d..6bfcd1c 100644 --- a/bioxelnodes/operators/io.py +++ b/bioxelnodes/operators/io.py @@ -782,9 +782,8 @@ class ImportVolumetricDataDialog(bpy.types.Operator): # Change render setting for better result if is_first_import: bpy.ops.bioxelnodes.render_setting_preset('EXEC_DEFAULT', - preset="slice_viewer") - bpy.ops.bioxelnodes.render_setting_preset('EXEC_DEFAULT', - preset="cycles_preview") + preset="preview_c") + # bpy.ops.bioxelnodes.slice_viewer('EXEC_DEFAULT') self.report({"INFO"}, "Successfully Imported") return {'FINISHED'} diff --git a/bioxelnodes/operators/misc.py b/bioxelnodes/operators/misc.py index 20435f8..5d378c9 100644 --- a/bioxelnodes/operators/misc.py +++ b/bioxelnodes/operators/misc.py @@ -151,20 +151,19 @@ class RenderSettingPreset(bpy.types.Operator): bl_description = "Render Setting Preset" PRESETS = { - "slice_viewer": "Slice Viewer", - "eevee_preview": "EEVEE Preview", - "eevee_production": "EEVEE Production", - "cycles_preview": "Cycles Preview", - "cycles_production": "Cycles Production" + "preview_e": "Preview (EEVEE)", + "preview_c": "Preview (Cycles)", + "production_e": "Production (EEVEE)", + "production_c": "Production (Cycles)" } preset: bpy.props.EnumProperty(name="Preset", - default="eevee_preview", + default="preview_c", items=[(k, v, "") for k, v in PRESETS.items()]) # type: ignore def execute(self, context): - if self.preset == "eevee_preview": + if self.preset == "preview_e": bpy.context.scene.render.engine = 'BLENDER_EEVEE_NEXT' bpy.context.scene.eevee.use_taa_reprojection = False bpy.context.scene.eevee.taa_samples = 16 @@ -174,7 +173,7 @@ class RenderSettingPreset(bpy.types.Operator): bpy.context.scene.eevee.volumetric_ray_depth = 16 bpy.context.scene.eevee.use_volumetric_shadows = True - elif self.preset == "eevee_production": + elif self.preset == "production_e": bpy.context.scene.render.engine = 'BLENDER_EEVEE_NEXT' bpy.context.scene.eevee.use_taa_reprojection = False bpy.context.scene.eevee.taa_samples = 16 @@ -184,30 +183,46 @@ class RenderSettingPreset(bpy.types.Operator): bpy.context.scene.eevee.volumetric_ray_depth = 16 bpy.context.scene.eevee.use_volumetric_shadows = True - elif self.preset == "cycles_preview": + elif self.preset == "preview_c": bpy.context.scene.render.engine = 'CYCLES' bpy.context.scene.cycles.shading_system = True bpy.context.scene.cycles.volume_bounces = 12 bpy.context.scene.cycles.transparent_max_bounces = 16 - bpy.context.scene.cycles.volume_preview_step_rate = 10 - bpy.context.scene.cycles.volume_step_rate = 10 + bpy.context.scene.cycles.volume_preview_step_rate = 1 + bpy.context.scene.cycles.volume_step_rate = 1 - elif self.preset == "cycles_production": + elif self.preset == "production_c": bpy.context.scene.render.engine = 'CYCLES' bpy.context.scene.cycles.shading_system = True bpy.context.scene.cycles.volume_bounces = 16 bpy.context.scene.cycles.transparent_max_bounces = 32 - bpy.context.scene.cycles.volume_preview_step_rate = 1 - bpy.context.scene.cycles.volume_step_rate = 1 - - elif self.preset == "slice_viewer": - # bpy.context.scene.render.engine = 'BLENDER_EEVEE_NEXT' - bpy.context.scene.eevee.use_taa_reprojection = False - bpy.context.scene.eevee.taa_samples = 4 - bpy.context.scene.eevee.volumetric_tile_size = '2' - bpy.context.scene.eevee.volumetric_shadow_samples = 128 - bpy.context.scene.eevee.volumetric_samples = 128 - bpy.context.scene.eevee.volumetric_ray_depth = 1 - bpy.context.scene.eevee.use_volumetric_shadows = False + bpy.context.scene.cycles.volume_preview_step_rate = 0.1 + bpy.context.scene.cycles.volume_step_rate = 0.1 return {'FINISHED'} + +class SliceViewer(bpy.types.Operator): + bl_idname = "bioxelnodes.slice_viewer" + bl_label = "Slice Viewer" + bl_description = "Slice Viewer" + bl_icon = "FILE_VOLUME" + + def execute(self, context): + bpy.context.scene.eevee.use_taa_reprojection = False + bpy.context.scene.eevee.taa_samples = 4 + bpy.context.scene.eevee.volumetric_tile_size = '2' + bpy.context.scene.eevee.volumetric_shadow_samples = 128 + bpy.context.scene.eevee.volumetric_samples = 128 + bpy.context.scene.eevee.volumetric_ray_depth = 1 + bpy.context.scene.eevee.use_volumetric_shadows = False + + for area in bpy.context.screen.areas: + if area.type == 'VIEW_3D': + area.spaces[0].shading.type = 'MATERIAL' + area.spaces[0].shading.studio_light = 'studio.exr' + area.spaces[0].shading.studiolight_intensity = 2.5 + area.spaces[0].shading.use_scene_lights = False + area.spaces[0].shading.use_scene_world = False + + + return {'FINISHED'} \ No newline at end of file