save startup blend for animation tab & whatnot
This commit is contained in:
2026-04-08 12:10:18 -06:00
parent 57a652524a
commit 692e200ffe
180 changed files with 12336 additions and 3431 deletions
+136 -7
View File
@@ -19,12 +19,12 @@
import datetime
import json
import logging
import numpy as np
import os
import platform
import re
import shutil
import sys
import tempfile
import uuid
from typing import Optional
@@ -43,7 +43,6 @@ from . import (
search,
)
bk_logger = logging.getLogger(__name__)
ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000
@@ -123,8 +122,26 @@ def selection_set(sel):
bpy.context.view_layer.objects.active = sel[0]
for ob in sel[1]:
ob.select_set(True)
except Exception as e:
bk_logger.exception(f"failed to select objects: {str(e)}")
except Exception:
bk_logger.exception("Failed to select objects:")
def get_asset_data_from_ob(ob) -> Optional[dict]:
"""Return the BlenderKit asset_data dict for an object.
Checks the object's own IDProperty first. When the object is a
collection-instance EMPTY (imported from a local asset library) it falls
back to the asset_data stored on the instanced collection, which
unpack_asset_bg.py writes there during local-library processing.
"""
if ob is None:
return None
ad = ob.get("asset_data")
if ad is not None:
return ad
if ob.instance_collection is not None:
return ob.instance_collection.get("asset_data")
return None
def get_active_model() -> Optional[bpy.types.Object]:
@@ -309,6 +326,11 @@ def get_search_props():
if not hasattr(wm, "blenderkit_addon"):
return
props = wm.blenderkit_addon
if uiprops.asset_type == "AUTHOR":
if not hasattr(wm, "blenderkit_author"):
return
props = wm.blenderkit_author
return props
@@ -383,6 +405,8 @@ def get_active_asset():
return get_active_nodegroup()
elif ui_props.asset_type == "ADDON":
return None # Addons don't have an active asset concept
elif ui_props.asset_type == "AUTHOR":
return None # Authors don't have an active asset concept
return None
@@ -422,6 +446,8 @@ def get_upload_props():
return b.blenderkit
elif ui_props.asset_type == "ADDON":
return None # Addons don't have upload props
elif ui_props.asset_type == "AUTHOR":
return None # Authors don't have upload props
return None
@@ -444,6 +470,41 @@ def get_active_brush():
return brush
def get_brush_icon_path(brush) -> str:
"""Get the path to the brush icon image.
In Blender <= 4.5, brushes have icon_filepath. In Blender 5.0+, icon_filepath
was removed and brush preview is stored as in-memory pixels (brush.preview).
For 5.0+ we save the preview pixels to a temp PNG file.
"""
if bpy.app.version <= (4, 5, 0):
return bpy.path.abspath(brush.icon_filepath)
if brush.preview is None:
return ""
width, height = brush.preview.image_size
if width == 0 or height == 0:
return ""
filepath = os.path.join(
tempfile.gettempdir(), f"blenderkit_brush_{brush.name}_icon.png"
)
img = bpy.data.images.new(
name=f".bk_brush_preview_{brush.name}",
width=width,
height=height,
alpha=True,
)
try:
img.pixels.foreach_set(brush.preview.image_pixels_float)
img.filepath_raw = filepath
img.file_format = "PNG"
img.save()
finally:
bpy.data.images.remove(img)
return filepath
def get_scene_id():
"""gets scene id and possibly also generates a new one"""
bpy.context.scene["uuid"] = bpy.context.scene.get("uuid", str(uuid.uuid4()))
@@ -474,6 +535,7 @@ def get_preferences_as_dict():
"global_dir": user_preferences.global_dir,
"project_subdir": user_preferences.project_subdir,
"unpack_files": user_preferences.unpack_files,
"write_asset_metadata": user_preferences.write_asset_metadata,
# GUI
"show_on_start": user_preferences.show_on_start,
"thumb_size": user_preferences.thumb_size,
@@ -482,6 +544,7 @@ def get_preferences_as_dict():
"search_in_header": user_preferences.search_in_header,
"tips_on_start": user_preferences.tips_on_start,
"announcements_on_start": user_preferences.announcements_on_start,
"assetbar_follows_cursor": user_preferences.assetbar_follows_cursor,
# NETWORK
"client_port": user_preferences.client_port,
"ip_version": user_preferences.ip_version,
@@ -525,6 +588,7 @@ def get_preferences() -> datas.Prefs:
global_dir=user_preferences.global_dir, # type: ignore[union-attr]
project_subdir=user_preferences.project_subdir, # type: ignore[union-attr]
unpack_files=user_preferences.unpack_files, # type: ignore[union-attr]
write_asset_metadata=user_preferences.write_asset_metadata, # type: ignore[union-attr]
# GUI
show_on_start=user_preferences.show_on_start, # type: ignore[union-attr]
thumb_size=user_preferences.thumb_size, # type: ignore[union-attr]
@@ -533,6 +597,7 @@ def get_preferences() -> datas.Prefs:
search_in_header=user_preferences.search_in_header, # type: ignore[union-attr]
tips_on_start=user_preferences.tips_on_start, # type: ignore[union-attr]
announcements_on_start=user_preferences.announcements_on_start, # type: ignore[union-attr]
assetbar_follows_cursor=user_preferences.assetbar_follows_cursor, # type: ignore[union-attr]
# NETWORK
client_port=user_preferences.client_port, # type: ignore[union-attr]
ip_version=user_preferences.ip_version, # type: ignore[union-attr]
@@ -562,7 +627,15 @@ def save_prefs(user_preferences, context, **kwargs):
if bpy.app.background is True or bpy.app.factory_startup is True:
return
previous_global_dir = (
global_vars.PREFS.get("global_dir")
if isinstance(global_vars.PREFS, dict)
else None
)
global_vars.PREFS = get_preferences_as_dict()
paths.ensure_asset_library_path(
global_vars.PREFS.get("global_dir"), previous_global_dir
)
if user_preferences.preferences_lock is True:
return
@@ -637,6 +710,8 @@ def img_to_preview(img, copy_original=False):
if not copy_original:
return
import numpy as np
# Only process if image has alpha channel and needs filling
if img.channels == 4 and (
img.alpha_mode == "STRAIGHT" or img.alpha_mode == "PREMUL"
@@ -800,7 +875,7 @@ def copy_asset(fp1, fp2):
"""Synchronizes the asset between directories, including it's texture subdirectories."""
if 1:
bk_logger.debug("copy asset")
bk_logger.debug(fp1 + " " + fp2)
bk_logger.debug("%s %s", fp1, fp2)
if not os.path.exists(fp2):
shutil.copyfile(fp1, fp2)
bk_logger.debug("copied")
@@ -989,6 +1064,58 @@ def get_dimensions(obs):
return dim, bbmin, bbmax
def get_scene_dimensions(scene: bpy.types.Scene) -> tuple[Vector, Vector, Vector]:
"""Calculate world-space bounds for the entire scene."""
minx = miny = minz = float("inf")
maxx = maxy = maxz = float("-inf")
has_geometry = False
depsgraph = bpy.context.evaluated_depsgraph_get()
def accumulate(coord: Vector) -> None:
nonlocal minx, miny, minz, maxx, maxy, maxz, has_geometry
has_geometry = True
minx = min(minx, coord.x)
miny = min(miny, coord.y)
minz = min(minz, coord.z)
maxx = max(maxx, coord.x)
maxy = max(maxy, coord.y)
maxz = max(maxz, coord.z)
mesh_like_types = {"MESH", "CURVE", "SURFACE", "META", "FONT", "GPENCIL"}
for ob in scene.objects:
object_eval = ob.evaluated_get(depsgraph)
mw = object_eval.matrix_world
if ob.type in mesh_like_types:
to_mesh = getattr(object_eval, "to_mesh", None)
mesh = to_mesh() if callable(to_mesh) else None
if mesh:
for vert in mesh.vertices:
accumulate(mw @ vert.co)
to_mesh_clear = getattr(object_eval, "to_mesh_clear", None)
if callable(to_mesh_clear):
to_mesh_clear()
elif ob.type == "VOLUME":
for corner in object_eval.bound_box:
accumulate(mw @ Vector(corner))
elif hasattr(object_eval, "bound_box") and object_eval.bound_box:
for corner in object_eval.bound_box:
accumulate(mw @ Vector(corner))
else:
accumulate(mw @ Vector((0.0, 0.0, 0.0)))
if not has_geometry:
zero = Vector((0.0, 0.0, 0.0))
return zero.copy(), zero.copy(), zero.copy()
bbmin = Vector((minx, miny, minz))
bbmax = Vector((maxx, maxy, maxz))
dim = Vector((maxx - minx, maxy - miny, maxz - minz))
return dim, bbmin, bbmax
def get_simple_headers() -> dict[str, str]:
headers = {
"accept": "application/json",
@@ -1147,7 +1274,7 @@ def name_update(props, context=None):
fname = fname.replace("'", "")
fname = fname.replace('"', "")
if ui_props.asset_type == "HDR" or fname == "":
bk_logger.info(f"Skiping the rename")
bk_logger.info("Skipping the rename")
return # don't rename HDR's or with empty name
else:
asset = get_active_asset()
@@ -1273,6 +1400,8 @@ def asset_from_newer_blender_version(asset_data, blender_version=None):
# addons don't have a blender version, so we return False
if asset_data["assetType"] == "addon":
return False, ""
if asset_data["assetType"] == "author":
return False, ""
asset_ver = asset_data["sourceAppVersion"].split(".")
if blender_version is None:
blender_version = bpy.app.version
@@ -1543,7 +1672,7 @@ def check_globaldir_permissions():
)
return False
if not os.path.isdir(global_dir):
bk_logger.info(f"Global dir does not exist. Creating it at {global_dir}")
bk_logger.info("Global dir does not exist. Creating it at %s", global_dir)
try:
os.mkdir(global_dir)
except Exception as e: