disable (currently unused) addons, update
This commit is contained in:
@@ -0,0 +1,748 @@
|
||||
import bpy
|
||||
from bpy.types import Operator, Macro
|
||||
from bpy.props import StringProperty, BoolProperty
|
||||
from bpy.utils import register_class, unregister_class
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import base64
|
||||
import mathutils
|
||||
import tempfile
|
||||
import os
|
||||
import numpy as np
|
||||
|
||||
|
||||
suppress_for_preset_load = False
|
||||
_disable_impossible_running = False
|
||||
|
||||
def blender_refresh():
|
||||
|
||||
return True
|
||||
# print_message(context, "Blender refresh")
|
||||
# bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
|
||||
# for area in context.screen.areas:
|
||||
# area.tag_redraw()
|
||||
# context.view_layer.update()
|
||||
# context.view_layer.depsgraph.update()
|
||||
|
||||
|
||||
#Takes a path object
|
||||
def can_write_to_location(dir_path):
|
||||
#First check if the folder already exists or not
|
||||
if not os.path.exists(str(dir_path)):
|
||||
#Find the first parent that does exist, see if we can write there
|
||||
last = ""
|
||||
while not os.path.exists(str(dir_path)):
|
||||
dir_path = dir_path.parent
|
||||
|
||||
if str(dir_path) == last:
|
||||
break
|
||||
last = str(dir_path)
|
||||
|
||||
test_file_path = dir_path / "test_file.tmp"
|
||||
|
||||
try:
|
||||
# Attempt to create a temporary file in the directory
|
||||
with open(str(test_file_path), 'w') as temp_file:
|
||||
temp_file.write("Test")
|
||||
# If successful, remove the test file
|
||||
os.remove(str(test_file_path))
|
||||
return True
|
||||
except PermissionError:
|
||||
# Permission denied
|
||||
return False
|
||||
except Exception as e:
|
||||
# Other exceptions could be handled differently if needed
|
||||
print(f"An error occurred: {e}")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def disable_impossible(context):
|
||||
global suppress_for_preset_load, _disable_impossible_running
|
||||
if _disable_impossible_running:
|
||||
return
|
||||
_disable_impossible_running = True
|
||||
try:
|
||||
sbp = context.scene.SimpleBake_Props
|
||||
suppress_for_preset_load = True
|
||||
|
||||
#Start with a refresh of the objects list
|
||||
bpy.ops.simplebake.refresh_bake_object_list()
|
||||
#And AOVs
|
||||
bpy.ops.simplebake.refresh_aov_list()
|
||||
#And UVs
|
||||
bpy.ops.simplebake.sync_uv_list()
|
||||
|
||||
if bpy.app.version >= (4,0,0):
|
||||
sbp.selected_transrough = False
|
||||
sbp.selected_ssscol = False
|
||||
|
||||
if not sbp.save_bakes_external:
|
||||
sbp.rundenoise = False
|
||||
sbp.rough_glossy_switch = SBConstants.PBR_ROUGHNESS
|
||||
sbp.ccrough_glossy_switch = SBConstants.PBR_CLEARCOAT_ROUGH
|
||||
sbp.normal_format_switch = SBConstants.NORMAL_OPENGL
|
||||
sbp.multiply_diffuse_ao = "purediffuse"
|
||||
sbp.do_aa = False
|
||||
sbp.save_obj_external = False
|
||||
|
||||
for cpt in sbp.cp_list:
|
||||
if "png_compression" not in cpt:
|
||||
cpt.png_compression = 15
|
||||
|
||||
#if sbp.fbx_preset_name == "":
|
||||
#sbp.fbx_preset_name = "None"
|
||||
|
||||
if sbp.multiply_diffuse_ao == "diffusexao" and sbp.global_mode == SBConstants.PBR:
|
||||
sbp.selected_ao = True
|
||||
|
||||
if sbp.s2a_opmode == "decals":
|
||||
sbp.multiply_diffuse_ao = "purediffuse"
|
||||
|
||||
if not sbp.keep_internal_after_export:
|
||||
sbp.copy_and_apply = False
|
||||
|
||||
if sbp.tex_per_mat and (sbp.selected_s2a or sbp.cycles_s2a):
|
||||
sbp.tex_per_mat = False
|
||||
|
||||
if sbp.tex_per_mat and sbp.expand_mat_uvs:
|
||||
sbp.new_uv_option = True
|
||||
if sbp.tex_per_mat and not sbp.expand_mat_uvs:
|
||||
sbp.new_uv_option = False
|
||||
|
||||
if not sbp.merged_bake or not (sbp.export_mesh_individual_or_combined == "combined"):
|
||||
sbp.merge_export_obj = False
|
||||
|
||||
if not sbp.merged_bake:
|
||||
sbp.join_objs_to_proxy = False
|
||||
|
||||
finally:
|
||||
suppress_for_preset_load = False
|
||||
_disable_impossible_running = False
|
||||
|
||||
|
||||
def get_bake_objects(context):
|
||||
sbp = context.scene.SimpleBake_Props
|
||||
#Work out our object selection
|
||||
objects = []
|
||||
|
||||
if (sbp.selected_s2a or sbp.cycles_s2a) and sbp.s2a_opmode=="automatch":
|
||||
from .bake_operators.auto_match_operators import SimpleBake_OT_AutoMatch_Operation as amo
|
||||
#objects = [context.scene.objects[i] for i in list(amo.hl_matches.keys())]
|
||||
objects = list(amo.hl_matches.keys())
|
||||
|
||||
elif sbp.selected_s2a: #Standard S2A bake or Decals
|
||||
objects = [sbp.targetobj.name] if sbp.targetobj != None else []
|
||||
|
||||
elif sbp.cycles_s2a: #Standard S2A bake
|
||||
objects = [sbp.targetobj_cycles.name] if sbp.targetobj_cycles != None else []
|
||||
|
||||
else:
|
||||
objects = [i.obj_point.name for i in sbp.objects_list]
|
||||
|
||||
return objects
|
||||
|
||||
|
||||
|
||||
#active_tiles is sorted list of UDIM numbers (1001, 1002 etc.)
|
||||
#total_tiles is highest tile index (e.g. 11 if the highest numbered active tile is 1011)
|
||||
def get_udim_tiles(context, object_name, threshold=0.001):
|
||||
import numpy as np
|
||||
|
||||
obj = context.scene.objects.get(object_name)
|
||||
if not obj or not hasattr(obj.data, 'uv_layers') or not obj.data.uv_layers:
|
||||
return {"active_tiles": [], "total_tiles": 0, "bbox_tiles": 0}
|
||||
|
||||
uv_layer = obj.data.uv_layers.active
|
||||
if uv_layer is None:
|
||||
return {"active_tiles": [], "total_tiles": 0, "bbox_tiles": 0}
|
||||
|
||||
n = len(uv_layer.data)
|
||||
if n == 0:
|
||||
return {"active_tiles": [], "total_tiles": 0, "bbox_tiles": 0}
|
||||
|
||||
buf = np.empty(n * 2, dtype=np.float64)
|
||||
uv_layer.data.foreach_get("uv", buf)
|
||||
uvs = buf.reshape(-1, 2)
|
||||
|
||||
u = uvs[:, 0].copy()
|
||||
v = uvs[:, 1].copy()
|
||||
|
||||
# Snap UV coordinates that sit very close to the upper edge of a tile back
|
||||
# into that tile, to avoid a vertex at e.g. v=0.9999 being counted as tile 1.
|
||||
frac_u = u % 1.0
|
||||
frac_v = v % 1.0
|
||||
u[frac_u > 1.0 - threshold] -= threshold
|
||||
v[frac_v > 1.0 - threshold] -= threshold
|
||||
|
||||
# Subtract a tiny epsilon before flooring so that vertices sitting exactly
|
||||
# on an integer boundary (e.g. v=1.0, where 1.0 % 1.0 == 0.0) snap to the
|
||||
# tile below rather than the tile above.
|
||||
tu = np.floor(u - 1e-9).astype(np.int32)
|
||||
tv = np.floor(v - 1e-9).astype(np.int32)
|
||||
|
||||
mask = (tu >= 0) & (tu <= 9) & (tv >= 0) & (tv <= 9)
|
||||
tile_nums = 1001 + tu[mask] + 10 * tv[mask]
|
||||
|
||||
import math
|
||||
u_extent = math.ceil(float(np.max(u))) - math.floor(float(np.min(u)))
|
||||
v_extent = math.ceil(float(np.max(v))) - math.floor(float(np.min(v)))
|
||||
bbox_tiles = max(1, u_extent) * max(1, v_extent)
|
||||
|
||||
if tile_nums.size == 0:
|
||||
return {"active_tiles": [], "total_tiles": 0, "bbox_tiles": bbox_tiles}
|
||||
|
||||
udim_tiles = set(np.unique(tile_nums).tolist())
|
||||
|
||||
return {
|
||||
"active_tiles": sorted(udim_tiles),
|
||||
"total_tiles": max(udim_tiles) - 1000,
|
||||
"bbox_tiles": bbox_tiles
|
||||
}
|
||||
|
||||
# def get_max_udim_tiles_from_bake_objects(context):
|
||||
# sbp = context.scene.SimpleBake_Props
|
||||
# objs = [i.name for i in sbp.objects_list]
|
||||
#
|
||||
# max = 0
|
||||
#
|
||||
# for obj_name in objs:
|
||||
# try:
|
||||
# d = get_udim_tiles(context, obj_name)
|
||||
# if len(d["active_tiles"]) > max:
|
||||
# max = len(d["active_tiles"])
|
||||
# except:
|
||||
# pass
|
||||
#
|
||||
# return max
|
||||
|
||||
# --- UDIM tile cache -----------------------------------------------------------
|
||||
# Keyed by object name. Dirty set contains names needing recompute.
|
||||
_udim_tile_cache: dict = {}
|
||||
_udim_dirty_objects: set = set()
|
||||
|
||||
|
||||
def invalidate_udim_cache(object_names=None):
|
||||
"""Mark objects as needing recompute. Pass None to invalidate everything."""
|
||||
global _udim_dirty_objects, _udim_tile_cache
|
||||
if object_names is None:
|
||||
_udim_tile_cache.clear()
|
||||
_udim_dirty_objects.clear()
|
||||
else:
|
||||
for name in object_names:
|
||||
_udim_dirty_objects.add(name)
|
||||
|
||||
|
||||
@bpy.app.handlers.persistent
|
||||
def _sb_udim_depsgraph_handler(scene, depsgraph):
|
||||
for update in depsgraph.updates:
|
||||
if not update.is_updated_geometry:
|
||||
continue
|
||||
id_data = update.id
|
||||
if isinstance(id_data, bpy.types.Object) and id_data.type == 'MESH':
|
||||
# Topology change on the object itself
|
||||
_udim_dirty_objects.add(id_data.name)
|
||||
elif isinstance(id_data, bpy.types.Mesh):
|
||||
# UV edits come through as mesh data-block updates —
|
||||
# find all objects that use this mesh and dirty them
|
||||
for obj in scene.objects:
|
||||
if obj.type == 'MESH' and obj.data == id_data:
|
||||
_udim_dirty_objects.add(obj.name)
|
||||
|
||||
|
||||
def get_cached_udim_tiles(context, object_name, threshold=0.001):
|
||||
global _udim_tile_cache, _udim_dirty_objects
|
||||
if object_name in _udim_dirty_objects or object_name not in _udim_tile_cache:
|
||||
#Cache result from the "real" method
|
||||
_udim_tile_cache[object_name] = get_udim_tiles(context, object_name, threshold)
|
||||
#Now clean
|
||||
_udim_dirty_objects.discard(object_name)
|
||||
return _udim_tile_cache[object_name]
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
def find_closest_obj(context, object_name, max_distance=0.5):
|
||||
|
||||
ref_obj = context.scene.objects.get(object_name)
|
||||
if not ref_obj:
|
||||
raise ValueError(f"No object found with the name '{object_name}'")
|
||||
|
||||
closest_mesh = None
|
||||
closest_distance = float('inf')
|
||||
|
||||
for obj in context.scene.objects:
|
||||
if obj.type == 'MESH' and obj != ref_obj and "SB_replaced_orig_name" not in obj:
|
||||
distance = (ref_obj.location - obj.location).length
|
||||
if distance < closest_distance:
|
||||
closest_distance = distance
|
||||
closest_mesh = obj
|
||||
|
||||
return closest_mesh if closest_distance <= max_distance else None
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class SBConstants:
|
||||
|
||||
#Constants
|
||||
PBR = "PBR"
|
||||
PBRS2A = "PBR StoA"
|
||||
PBRS2A_AUTOMATCH = "PBR StoA AutoMatch"
|
||||
PBRS2A_DECALS = "PBR StoA Decals"
|
||||
CYCLESBAKE = "CyclesBake"
|
||||
CYCLESBAKE_S2A = "CyclesBake S2A"
|
||||
CYCLESBAKES2A_AUTOMATCH = "CyclesBake S2A Auto"
|
||||
SPECIALS = "Specials"
|
||||
SPECIALS_PBR_TARGET_ONLY = "specials_pbr_targetonly"
|
||||
SPECIALS_CYCLES_TARGET_ONLY = "specials_cycles_targetonly"
|
||||
PBRAOVS = "AOV"
|
||||
|
||||
|
||||
#PBR Names
|
||||
PBR_DIFFUSE = "Diffuse"
|
||||
PBR_METAL = "Metalness"
|
||||
PBR_SSS = "SSS"
|
||||
PBR_SSS_SCALE = "SSS Scale"
|
||||
PBR_SSSCOL = "SSS Colour"
|
||||
PBR_ROUGHNESS = "Roughness"
|
||||
PBR_GLOSSY = "Glossiness"
|
||||
PBR_NORMAL = "Normal"
|
||||
PBR_TRANSMISSION = "Transmission"
|
||||
PBR_TRANSMISSION_ROUGH = "Transmission Roughness"
|
||||
PBR_CLEARCOAT = "Clearcoat"
|
||||
PBR_CLEARCOAT_ROUGH = "Clearcoat Roughness"
|
||||
PBR_CLEARCOAT_GLOSS = "Clearcoat Glossiness"
|
||||
PBR_EMISSION = "Emission"
|
||||
PBR_EMISSION_STRENGTH = "Emission Strength"
|
||||
PBR_SPECULAR = "Specular"
|
||||
PBR_ALPHA = "Alpha"
|
||||
PBR_BUMP = "Bump"
|
||||
PBR_DISPLACEMENT = "Displacement"
|
||||
|
||||
ALL_PBR_MODES = ([PBR_DIFFUSE, PBR_METAL, PBR_SSS,PBR_SSS_SCALE, PBR_SSSCOL, PBR_ROUGHNESS, PBR_GLOSSY,
|
||||
PBR_NORMAL, PBR_TRANSMISSION, PBR_TRANSMISSION_ROUGH, PBR_CLEARCOAT,
|
||||
PBR_CLEARCOAT_ROUGH, PBR_CLEARCOAT_GLOSS, PBR_EMISSION, PBR_EMISSION_STRENGTH, PBR_SPECULAR, PBR_ALPHA, PBR_BUMP, PBR_DISPLACEMENT])
|
||||
|
||||
#Specials names
|
||||
THICKNESS = "Thickness"
|
||||
AO = "Ambient Occlusion"
|
||||
CURVATURE = "Curvature"
|
||||
COLOURID = "Colour ID"
|
||||
VERTEXCOL = "Vertex Colour"
|
||||
LIGHTMAP = "Lightmap"
|
||||
|
||||
ALL_SPECIALS = ([THICKNESS, AO, CURVATURE,COLOURID, VERTEXCOL, LIGHTMAP])
|
||||
COMP_DENOISE_SPECIALS = ([THICKNESS, AO, LIGHTMAP])
|
||||
COMP_NODENOISE_SPECIALS = ([CURVATURE, VERTEXCOL, COLOURID])
|
||||
|
||||
#UVs
|
||||
NEWUV_SMART_INDIVIDUAL = "SmartUVProject_Individual"
|
||||
NEWUV_SMART_ATLAS = "SmartUVProject_Atlas"
|
||||
NEWUV_COMBINE_EXISTING = "CombineExisting"
|
||||
|
||||
#Normals
|
||||
NORMAL_OPENGL = "OpenGL"
|
||||
NORMAL_DIRECTX = "DirectX"
|
||||
|
||||
@classmethod
|
||||
def get_socket_names(cls):
|
||||
|
||||
v = bpy.app.version
|
||||
|
||||
if v >= (4,0,0):
|
||||
return {
|
||||
cls.PBR_DIFFUSE: "Base Color",
|
||||
cls.PBR_METAL: "Metallic",
|
||||
cls.PBR_SSS: "Subsurface Weight",
|
||||
cls.PBR_SSS_SCALE: "Subsurface Scale",
|
||||
cls.PBR_SSSCOL: None,
|
||||
cls.PBR_ROUGHNESS: "Roughness",
|
||||
cls.PBR_GLOSSY: "Roughness",
|
||||
cls.PBR_NORMAL: "Normal",
|
||||
cls.PBR_TRANSMISSION: "Transmission Weight",
|
||||
cls.PBR_TRANSMISSION_ROUGH: None,
|
||||
cls.PBR_CLEARCOAT: "Coat Weight",
|
||||
cls.PBR_CLEARCOAT_ROUGH: "Coat Roughness",
|
||||
cls.PBR_CLEARCOAT_GLOSS: "Coat Roughness",
|
||||
cls.PBR_EMISSION: "Emission Color",
|
||||
cls.PBR_EMISSION_STRENGTH: "Emission Strength",
|
||||
cls.PBR_SPECULAR: "Specular IOR Level",
|
||||
cls.PBR_ALPHA: "Alpha"
|
||||
}
|
||||
|
||||
else:
|
||||
|
||||
#Principled socket names
|
||||
return {
|
||||
cls.PBR_DIFFUSE: "Base Color",
|
||||
cls.PBR_METAL: "Metallic",
|
||||
cls.PBR_SSS: "Subsurface",
|
||||
cls.PBR_SSS_SCALE: "Subsurface Scale",
|
||||
cls.PBR_SSSCOL: "Subsurface Color",
|
||||
cls.PBR_ROUGHNESS: "Roughness",
|
||||
cls.PBR_GLOSSY: "Roughness",
|
||||
cls.PBR_NORMAL: "Normal",
|
||||
cls.PBR_TRANSMISSION: "Transmission",
|
||||
cls.PBR_TRANSMISSION_ROUGH: "Transmission Roughness",
|
||||
cls.PBR_CLEARCOAT: "Clearcoat",
|
||||
cls.PBR_CLEARCOAT_ROUGH: "Clearcoat Roughness",
|
||||
cls.PBR_CLEARCOAT_GLOSS: "Clearcoat Glossiness",
|
||||
cls.PBR_EMISSION: "Emission",
|
||||
cls.PBR_EMISSION_STRENGTH: "Emission Strength",
|
||||
cls.PBR_SPECULAR: "Specular",
|
||||
cls.PBR_ALPHA: "Alpha"
|
||||
}
|
||||
|
||||
def get_texture_channels(context, tex):
|
||||
sbp = context.scene.SimpleBake_Props
|
||||
|
||||
if sbp.global_mode in [SBConstants.CYCLESBAKE, SBConstants.CYCLESBAKE_S2A]:
|
||||
return "RGB"
|
||||
elif tex in [SBConstants.PBR_DIFFUSE, SBConstants.PBR_SSSCOL, SBConstants.PBR_EMISSION, SBConstants.PBR_NORMAL, SBConstants.COLOURID, SBConstants.VERTEXCOL, SBConstants.LIGHTMAP]:
|
||||
return "RGB"
|
||||
elif SBConstants.PBRAOVS in tex:
|
||||
num = int(tex.replace(f"{SBConstants.PBRAOVS}_", ""))
|
||||
aov = [i for i in sbp.aov_items if i.aov_number == num]
|
||||
assert len(aov)>0, "Unable to find matching AOV"
|
||||
aov = aov[0]
|
||||
if aov.aov_type == "C":
|
||||
return "RGB"
|
||||
else:
|
||||
return "RGB" if sbp.bw_to_rgb else "BW"
|
||||
|
||||
else:
|
||||
return "RGB" if sbp.bw_to_rgb else "BW"
|
||||
|
||||
def object_list_to_names_list(context):
|
||||
sbp = context.scene.SimpleBake_Props
|
||||
names = sbp.objects_list.items()
|
||||
names = [n[0] for n in names]
|
||||
|
||||
return names
|
||||
|
||||
def get_base_folder_patho(context):
|
||||
sbp = context.scene.SimpleBake_Props
|
||||
if "--background" in sys.argv or sbp.fake_background:
|
||||
base_folder = Path(sbp.base_folder_override)
|
||||
else:
|
||||
base_folder = Path(bpy.data.filepath).parent
|
||||
|
||||
return base_folder
|
||||
|
||||
|
||||
def force_to_object_mode(context):
|
||||
sbp = context.scene.SimpleBake_Props
|
||||
|
||||
def object_in_view_layer(obj):
|
||||
return obj is not None and any(view_obj == obj for view_obj in context.view_layer.objects)
|
||||
|
||||
if not object_in_view_layer(context.active_object):
|
||||
for item in sbp.objects_list:
|
||||
obj = item.obj_point
|
||||
if object_in_view_layer(obj):
|
||||
context.view_layer.objects.active = obj
|
||||
break
|
||||
|
||||
if context.active_object is None:
|
||||
for obj in context.view_layer.objects:
|
||||
if obj.type == "MESH" and obj.visible_get():
|
||||
context.view_layer.objects.active = obj
|
||||
break
|
||||
|
||||
if context.active_object is None:
|
||||
show_message_box(context, ["SimpleBake could not switch to Object Mode because Blender has no usable active mesh object in the current view layer."], "Errors occured", icon='ERROR')
|
||||
return False
|
||||
|
||||
if context.mode != "OBJECT":
|
||||
bpy.ops.object.mode_set(mode="OBJECT", toggle=False)
|
||||
|
||||
return context.mode == "OBJECT"
|
||||
|
||||
|
||||
# def check_for_safe_context_class(context):
|
||||
# sbp = context.scene.SimpleBake_Props
|
||||
# t = []
|
||||
# #t.append(base64.b64decode("bGV3aXM=").decode("utf-8"))
|
||||
# if sbp.safe_context_check in t:
|
||||
# print_message(context, "Warning - Safe context class check failed. This is not fatal. Continuing")
|
||||
# return False
|
||||
# else:
|
||||
# return True
|
||||
#
|
||||
# return True
|
||||
|
||||
# def select_selected_to_active(context, selected_objs, active_obj):
|
||||
# bpy.ops.object.select_all(action="DESELECT")
|
||||
#
|
||||
# for obj in selected_objs:
|
||||
# obj.select_set(state=True)
|
||||
# active_obj.select_set(state=True)
|
||||
# context.view_layer.objects.active = active_obj
|
||||
|
||||
def select_only_these(context, objs):
|
||||
#When running an image sequence, this sometimes gets called with an empty list and I don't know why
|
||||
if len(objs) < 1:
|
||||
return False
|
||||
#bpy.ops.object.select_all(action="DESELECT")
|
||||
[o.select_set(state=False) for o in context.scene.objects]
|
||||
for obj in objs:
|
||||
obj.select_set(state=True)
|
||||
context.view_layer.objects.active = objs[0] #arbitary
|
||||
|
||||
def select_only_this(context, obj):
|
||||
#bpy.ops.object.select_all(action="DESELECT")
|
||||
[o.select_set(state=False) for o in context.scene.objects]
|
||||
obj.select_set(state=True)
|
||||
context.view_layer.objects.active = obj
|
||||
|
||||
def pbr_selections_to_list(context):
|
||||
sbp = context.scene.SimpleBake_Props
|
||||
|
||||
selectedbakes = []
|
||||
selectedbakes.append(SBConstants.PBR_DIFFUSE) if sbp.selected_col else False
|
||||
selectedbakes.append(SBConstants.PBR_METAL) if sbp.selected_metal else False
|
||||
selectedbakes.append(SBConstants.PBR_ROUGHNESS) if sbp.selected_rough and sbp.rough_glossy_switch==SBConstants.PBR_ROUGHNESS else False
|
||||
selectedbakes.append(SBConstants.PBR_GLOSSY) if sbp.selected_rough and sbp.rough_glossy_switch==SBConstants.PBR_GLOSSY else False
|
||||
selectedbakes.append(SBConstants.PBR_NORMAL) if sbp.selected_normal else False
|
||||
selectedbakes.append(SBConstants.PBR_TRANSMISSION) if sbp.selected_trans else False
|
||||
selectedbakes.append(SBConstants.PBR_TRANSMISSION_ROUGH) if sbp.selected_transrough else False
|
||||
selectedbakes.append(SBConstants.PBR_CLEARCOAT) if sbp.selected_clearcoat else False
|
||||
selectedbakes.append(SBConstants.PBR_CLEARCOAT_ROUGH) if sbp.selected_clearcoat_rough and sbp.ccrough_glossy_switch==SBConstants.PBR_CLEARCOAT_ROUGH else False
|
||||
selectedbakes.append(SBConstants.PBR_CLEARCOAT_GLOSS) if sbp.selected_clearcoat_rough and sbp.ccrough_glossy_switch==SBConstants.PBR_CLEARCOAT_GLOSS else False
|
||||
selectedbakes.append(SBConstants.PBR_EMISSION) if sbp.selected_emission else False
|
||||
selectedbakes.append(SBConstants.PBR_EMISSION_STRENGTH) if sbp.selected_emission_strength else False
|
||||
selectedbakes.append(SBConstants.PBR_SPECULAR) if sbp.selected_specular else False
|
||||
selectedbakes.append(SBConstants.PBR_ALPHA) if sbp.selected_alpha else False
|
||||
selectedbakes.append(SBConstants.PBR_SSS) if sbp.selected_sss else False
|
||||
selectedbakes.append(SBConstants.PBR_SSS_SCALE) if sbp.selected_sss_scale else False
|
||||
selectedbakes.append(SBConstants.PBR_SSSCOL) if sbp.selected_ssscol else False
|
||||
selectedbakes.append(SBConstants.PBR_BUMP) if sbp.selected_bump else False
|
||||
selectedbakes.append(SBConstants.PBR_DISPLACEMENT) if sbp.selected_displacement else False
|
||||
|
||||
for aov in sbp.aov_items:
|
||||
if aov.enabled:
|
||||
selectedbakes.append(SBConstants.PBRAOVS + f"_{aov.aov_number}")
|
||||
|
||||
return selectedbakes
|
||||
|
||||
def specials_selection_to_list(context):
|
||||
sbp = context.scene.SimpleBake_Props
|
||||
selected_specials = []
|
||||
selected_specials.append(SBConstants.THICKNESS) if sbp.selected_thickness else False
|
||||
selected_specials.append(SBConstants.COLOURID) if sbp.selected_col_mats else False
|
||||
selected_specials.append(SBConstants.VERTEXCOL) if sbp.selected_col_vertex else False
|
||||
selected_specials.append(SBConstants.CURVATURE) if sbp.selected_curvature else False
|
||||
selected_specials.append(SBConstants.LIGHTMAP) if sbp.selected_lightmap else False
|
||||
selected_specials.append(SBConstants.AO) if sbp.selected_ao else False
|
||||
|
||||
return selected_specials
|
||||
|
||||
def _find_colorspace_fallback(cs_names, fallback_names):
|
||||
if not cs_names:
|
||||
return (0, "")
|
||||
|
||||
normalised_names = {name.casefold(): i for i, name in enumerate(cs_names)}
|
||||
for fallback_name in fallback_names:
|
||||
i = normalised_names.get(fallback_name.casefold())
|
||||
if i is not None:
|
||||
return (i, cs_names[i])
|
||||
|
||||
for fallback_name in fallback_names:
|
||||
fallback_name = fallback_name.casefold()
|
||||
for i, cs_name in enumerate(cs_names):
|
||||
if fallback_name in cs_name.casefold():
|
||||
return (i, cs_name)
|
||||
|
||||
return (0, cs_names[0])
|
||||
|
||||
|
||||
def blender_default_colorspace(float_buffer: bool, col: bool):
|
||||
|
||||
col_index = 0
|
||||
non_col_index = 0
|
||||
cs_names = [cs.identifier for cs in bpy.types.Image.bl_rna.properties['colorspace_settings'].fixed_type.bl_rna.properties['name'].enum_items]
|
||||
|
||||
if bpy.app.version >= (5,0,0):
|
||||
col_index = 23
|
||||
non_col_index = 18
|
||||
elif bpy.app.version >= (4,2,0):
|
||||
col_index = 20
|
||||
non_col_index = 17
|
||||
elif bpy.app.version >= (4,0,0):
|
||||
col_index = 19
|
||||
non_col_index = 16
|
||||
else:
|
||||
assert False, "Blender version too old"
|
||||
|
||||
if float_buffer or not col:
|
||||
if non_col_index < len(cs_names):
|
||||
return (non_col_index, cs_names[non_col_index])
|
||||
return _find_colorspace_fallback(cs_names, ["Non-Color", "Non-Colour", "Raw", "Data"])
|
||||
else:
|
||||
if col_index < len(cs_names):
|
||||
return (col_index, cs_names[col_index])
|
||||
return _find_colorspace_fallback(cs_names, ["sRGB", "AgX Base sRGB", "Linear Rec.709"])
|
||||
|
||||
def get_cs_list(self, context):
|
||||
|
||||
cs_items = [(cs.identifier,cs.identifier,"") for cs in bpy.types.Image.bl_rna.properties['colorspace_settings'].fixed_type.bl_rna.properties['name'].enum_items]
|
||||
return cs_items
|
||||
|
||||
def clean_file_name(filename):
|
||||
keepcharacters = (' ','.','_','~',"-", "[", "]")
|
||||
return "".join(c for c in filename if c.isalnum() or c in keepcharacters)#.rstrip()
|
||||
|
||||
def transfer_tags(old_img, new_img):
|
||||
|
||||
tags= ([
|
||||
"SB_bake_object",
|
||||
"SB_global_mode",
|
||||
"SB_this_bake",
|
||||
"SB_merged_bake",
|
||||
"SB_merged_bake_name",
|
||||
"SB_bake_operation_id",
|
||||
"SB_float_buffer",
|
||||
"SB_scaled",
|
||||
"SB_exported_list"
|
||||
])
|
||||
|
||||
for tag in tags:
|
||||
if tag in old_img: new_img[tag] = old_img[tag]
|
||||
|
||||
return True
|
||||
|
||||
class SimpleBake_OT_Halt(Operator):
|
||||
bl_idname = "simplebake.halt"
|
||||
bl_label = ""
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
raise Exception
|
||||
return {'FINISHED'}
|
||||
|
||||
def show_message_box(context, messageitems_list, title, icon = 'INFO'):
|
||||
|
||||
|
||||
message=""
|
||||
for li in messageitems_list:
|
||||
message+=f"{li}|"
|
||||
|
||||
bpy.ops.simplebake.show_message_box('INVOKE_DEFAULT', message=message, centre=True, icon=icon)
|
||||
|
||||
# def draw(self, context):
|
||||
# for m in messageitems_list:
|
||||
# self.layout.label(text=m)
|
||||
# context.window_manager.popup_menu(draw, title = title, icon = icon)
|
||||
|
||||
return True
|
||||
|
||||
class SimpleBake_OT_Show_Message_Box(Operator):
|
||||
bl_idname = "simplebake.show_message_box"
|
||||
bl_label = ""
|
||||
|
||||
message: StringProperty()
|
||||
icon: StringProperty(default="INFO")
|
||||
centre: BoolProperty(default=False)
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.restored = False
|
||||
if self.centre:
|
||||
self.orig_x = event.mouse_x
|
||||
self.orig_y = event.mouse_y
|
||||
|
||||
w = int(context.window.width/2)
|
||||
h = int(context.window.height/2)
|
||||
h = h + (20*len(self.message.split("|")))
|
||||
context.window.cursor_warp(w, h)
|
||||
|
||||
longest = 0
|
||||
for li in self.message.split("|"):
|
||||
longest = len(li) if (len(li)>longest) else longest
|
||||
width = longest*6 if (longest*6)>=400 else 400
|
||||
|
||||
return context.window_manager.invoke_props_dialog(self, width = width)
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.label(text="SIMPLEBAKE", icon=self.icon)
|
||||
message_list = self.message.split("|")
|
||||
for li in message_list:
|
||||
row=self.layout.row()
|
||||
row.scale_y = 0.8
|
||||
row.alignment = 'LEFT'
|
||||
row.label(text=li)
|
||||
if not self.restored and self.centre:
|
||||
context.window.cursor_warp(self.orig_x, self.orig_y)
|
||||
self.restored = True
|
||||
|
||||
def find_3d_viewport(context=None, screen=None):
|
||||
if context == None:
|
||||
context = bpy.context
|
||||
if screen == None:
|
||||
screen = bpy.context.window.screen
|
||||
|
||||
found = []
|
||||
# Iterate through the areas in the current screen
|
||||
for area in screen.areas:
|
||||
# Check if the area is a 3D viewport
|
||||
if area.type == 'VIEW_3D':
|
||||
# Optionally, you can access the space data of the area
|
||||
for space in area.spaces:
|
||||
if space.type == 'VIEW_3D':
|
||||
found.append(space)
|
||||
return found
|
||||
|
||||
|
||||
|
||||
def is_blend_saved():
|
||||
path = bpy.data.filepath
|
||||
if(path=="/" or path==""):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def is_all_black_or_transparent(img: bpy.types.Image, tol=1e-6):
|
||||
if not img.has_data:
|
||||
img.update()
|
||||
|
||||
# Blender exposes pixels as RGBA floats in [0..1]; length = w*h*4
|
||||
px = np.asarray(img.pixels[:], dtype=np.float32).reshape(-1, 4)
|
||||
rgb = px[:, :3]
|
||||
a = px[:, 3]
|
||||
|
||||
all_black = np.all(np.abs(rgb) <= tol) # R=G=B≈0 everywhere
|
||||
all_transparent = np.all(a <= tol) # A≈0 everywhere
|
||||
return all_black, all_transparent
|
||||
|
||||
|
||||
conflicting_addons = []#"ZenUV"]
|
||||
|
||||
classes = ([
|
||||
SimpleBake_OT_Show_Message_Box,
|
||||
#SimpleBake_OT_Print_Message,
|
||||
SimpleBake_OT_Halt
|
||||
])
|
||||
|
||||
def register():
|
||||
|
||||
global classes
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
|
||||
if _sb_udim_depsgraph_handler not in bpy.app.handlers.depsgraph_update_post:
|
||||
bpy.app.handlers.depsgraph_update_post.append(_sb_udim_depsgraph_handler)
|
||||
|
||||
def unregister():
|
||||
global classes
|
||||
for cls in classes:
|
||||
unregister_class(cls)
|
||||
|
||||
if _sb_udim_depsgraph_handler in bpy.app.handlers.depsgraph_update_post:
|
||||
bpy.app.handlers.depsgraph_update_post.remove(_sb_udim_depsgraph_handler)
|
||||
Reference in New Issue
Block a user