239 lines
8.6 KiB
Python
239 lines
8.6 KiB
Python
import bpy
|
|
from pathlib import Path
|
|
import shutil
|
|
|
|
from ..bioxelutils.common import get_all_layer_objs, is_missing_layer, set_file_prop
|
|
from .layer import RemoveLayers, SaveLayersCache
|
|
|
|
from ..constants import NODE_LIB_FILEPATH, VERSION
|
|
from ..utils import get_cache_dir
|
|
|
|
|
|
class ReLinkNodeLib(bpy.types.Operator):
|
|
bl_idname = "bioxelnodes.relink_node_lib"
|
|
bl_label = "Relink Node Library"
|
|
bl_description = "Relink all nodes to addon library source"
|
|
bl_options = {'UNDO'}
|
|
|
|
node_lib_filename: bpy.props.StringProperty(
|
|
default=""
|
|
) # type: ignore
|
|
|
|
def execute(self, context):
|
|
lib_filepath = Path(NODE_LIB_FILEPATH.parent,
|
|
f"{self.node_lib_filename}.blend")
|
|
node_libs = []
|
|
for node_group in bpy.data.node_groups:
|
|
if node_group.name.startswith("BioxelNodes"):
|
|
node_lib = node_group.library
|
|
if node_lib:
|
|
node_libs.append(node_lib)
|
|
|
|
node_libs = list(set(node_libs))
|
|
|
|
for node_lib in node_libs:
|
|
node_lib.filepath = str(lib_filepath)
|
|
# FIXME: may cause crash
|
|
node_lib.reload()
|
|
|
|
self.report({"INFO"}, f"Successfully relinked.")
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class SaveNodeLib(bpy.types.Operator):
|
|
bl_idname = "bioxelnodes.save_node_lib"
|
|
bl_label = "Save Node Library"
|
|
bl_description = "Save node library file to local"
|
|
bl_options = {'UNDO'}
|
|
|
|
lib_dir: bpy.props.StringProperty(
|
|
name="Library Directory",
|
|
subtype='DIR_PATH',
|
|
default="//"
|
|
) # type: ignore
|
|
|
|
def execute(self, context):
|
|
lib_dir = bpy.path.abspath(self.lib_dir)
|
|
local_lib_path: Path = Path(lib_dir, NODE_LIB_FILEPATH.name).resolve()
|
|
addon_lib_path: Path = NODE_LIB_FILEPATH
|
|
blend_path = Path(bpy.path.abspath("//")).resolve()
|
|
|
|
if local_lib_path != addon_lib_path:
|
|
shutil.copy(addon_lib_path, local_lib_path)
|
|
|
|
libs = []
|
|
for node_group in bpy.data.node_groups:
|
|
if node_group.name.startswith("BioxelNodes"):
|
|
if node_group.library:
|
|
libs.append(node_group.library)
|
|
|
|
libs = list(set(libs))
|
|
for lib in libs:
|
|
lib.filepath = bpy.path.relpath(str(local_lib_path),
|
|
start=str(blend_path))
|
|
|
|
set_file_prop("addon_version", VERSION)
|
|
|
|
return {'FINISHED'}
|
|
|
|
def invoke(self, context, event):
|
|
context.window_manager.invoke_props_dialog(self)
|
|
return {'RUNNING_MODAL'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return bpy.data.is_saved
|
|
|
|
|
|
class CleanTemp(bpy.types.Operator):
|
|
bl_idname = "bioxelnodes.clear_temp"
|
|
bl_label = "Clean Temp"
|
|
bl_description = "Clean all cache in temp (include other project cache)"
|
|
|
|
def execute(self, context):
|
|
cache_dir = get_cache_dir()
|
|
try:
|
|
shutil.rmtree(cache_dir)
|
|
self.report({"INFO"}, f"Successfully cleaned temp.")
|
|
return {'FINISHED'}
|
|
except:
|
|
self.report({"WARNING"},
|
|
"Fail to clean temp, you may do it manually.")
|
|
return {'CANCELLED'}
|
|
|
|
def invoke(self, context, event):
|
|
context.window_manager.invoke_confirm(self,
|
|
event,
|
|
message="All temp files will be removed, include other project cache, do you still want to clean?")
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
|
class RenderSettingPreset(bpy.types.Operator):
|
|
bl_idname = "bioxelnodes.render_setting_preset"
|
|
bl_label = "Render Setting Presets"
|
|
bl_description = "Render setting presets for bioxel"
|
|
bl_options = {'UNDO'}
|
|
|
|
PRESETS = {
|
|
"preview_e": "Preview (EEVEE)",
|
|
"preview_c": "Preview (Cycles)",
|
|
"production_e": "Production (EEVEE)",
|
|
"production_c": "Production (Cycles)"
|
|
}
|
|
|
|
preset: bpy.props.EnumProperty(name="Preset",
|
|
default="preview_c",
|
|
items=[(k, v, "")
|
|
for k, v in PRESETS.items()]) # type: ignore
|
|
|
|
def execute(self, context):
|
|
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
|
|
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 = 16
|
|
bpy.context.scene.eevee.use_volumetric_shadows = True
|
|
|
|
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
|
|
bpy.context.scene.eevee.volumetric_tile_size = '1'
|
|
bpy.context.scene.eevee.volumetric_shadow_samples = 128
|
|
bpy.context.scene.eevee.volumetric_samples = 256
|
|
bpy.context.scene.eevee.volumetric_ray_depth = 16
|
|
bpy.context.scene.eevee.use_volumetric_shadows = True
|
|
|
|
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 = 1
|
|
bpy.context.scene.cycles.volume_step_rate = 1
|
|
# bpy.context.scene.cycles.use_fast_gi = True
|
|
|
|
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 = 0.5
|
|
bpy.context.scene.cycles.volume_step_rate = 0.5
|
|
# bpy.context.scene.cycles.use_fast_gi = False
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class SliceViewer(bpy.types.Operator):
|
|
bl_idname = "bioxelnodes.slice_viewer"
|
|
bl_label = "Slice Viewer"
|
|
bl_description = "A preview scene setting for viewing slicers"
|
|
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'}
|
|
|
|
|
|
def get_all_layers(layer_filter=None):
|
|
def _layer_filter(layer_obj):
|
|
return True
|
|
|
|
layer_filter = layer_filter or _layer_filter
|
|
layer_objs = get_all_layer_objs()
|
|
return [obj for obj in layer_objs if layer_filter(obj)]
|
|
|
|
|
|
class SaveAllLayersCache(bpy.types.Operator, SaveLayersCache):
|
|
bl_idname = "bioxelnodes.save_all_layers_cache"
|
|
bl_label = "Save All Layers Cache"
|
|
bl_description = "Save all cache of this file"
|
|
bl_icon = "FILE_TICK"
|
|
|
|
success_msg = "Successfully saved all layers."
|
|
|
|
def get_layers(self, context):
|
|
def is_not_missing(layer_obj):
|
|
return not is_missing_layer(layer_obj)
|
|
return get_all_layers(is_not_missing)
|
|
|
|
|
|
class RemoveAllMissingLayers(bpy.types.Operator, RemoveLayers):
|
|
bl_idname = "bioxelnodes.remove_all_missing_layers"
|
|
bl_label = "Remove All Missing Layers"
|
|
bl_description = "Remove all current container missing layers"
|
|
bl_icon = "BRUSH_DATA"
|
|
|
|
success_msg = "Successfully removed all missing layers."
|
|
|
|
def get_layers(self, context):
|
|
def is_missing(layer_obj):
|
|
return is_missing_layer(layer_obj)
|
|
return get_all_layers(is_missing)
|
|
|
|
def invoke(self, context, event):
|
|
context.window_manager.invoke_confirm(self,
|
|
event,
|
|
message=f"Are you sure to remove all **Missing** layers?")
|
|
return {'RUNNING_MODAL'}
|