work
save startup blend for animation tab & whatnot
This commit is contained in:
@@ -32,6 +32,7 @@ from bpy.props import (
|
||||
FloatProperty,
|
||||
IntProperty,
|
||||
FloatVectorProperty,
|
||||
StringProperty,
|
||||
)
|
||||
|
||||
from . import bg_blender, global_vars, paths, tasks_queue, utils, upload, search
|
||||
@@ -81,16 +82,31 @@ def get_texture_ui(tpath, iname):
|
||||
return tex
|
||||
|
||||
|
||||
def check_thumbnail(props, imgpath):
|
||||
def check_thumbnail(
|
||||
props,
|
||||
imgpath,
|
||||
*,
|
||||
texture_name="upload_preview",
|
||||
flag_attr="has_thumbnail",
|
||||
state_attr="thumbnail_generating_state",
|
||||
):
|
||||
"""Reload a thumbnail preview and update status attributes."""
|
||||
|
||||
def _set_prop(attr_name, value):
|
||||
if attr_name and hasattr(props, attr_name):
|
||||
setattr(props, attr_name, value)
|
||||
|
||||
# TODO implement check if the file exists, if size is correct etc. needs some care
|
||||
if imgpath == "":
|
||||
props.has_thumbnail = False
|
||||
_set_prop(flag_attr, False)
|
||||
return None
|
||||
img = utils.get_hidden_image(imgpath, "upload_preview", force_reload=True)
|
||||
img = utils.get_hidden_image(imgpath, texture_name, force_reload=True)
|
||||
if img is not None: # and img.size[0] == img.size[1] and img.size[0] >= 512 and (
|
||||
# img.file_format == 'JPEG' or img.file_format == 'PNG'):
|
||||
props.has_thumbnail = True
|
||||
props.thumbnail_generating_state = ""
|
||||
_set_prop(flag_attr, True)
|
||||
if hasattr(props, "THUMBNAIL_GENERATING_STATE"):
|
||||
props.THUMBNAIL_GENERATING_STATE = ""
|
||||
_set_prop(state_attr, "")
|
||||
|
||||
utils.get_hidden_texture(img.name)
|
||||
# pcoll = icons.icon_collections["previews"]
|
||||
@@ -98,7 +114,7 @@ def check_thumbnail(props, imgpath):
|
||||
|
||||
return img
|
||||
else:
|
||||
props.has_thumbnail = False
|
||||
_set_prop(flag_attr, False)
|
||||
output = ""
|
||||
if (
|
||||
img is None
|
||||
@@ -115,7 +131,7 @@ def check_thumbnail(props, imgpath):
|
||||
# output += 'image too small, should be at least 512x512\n'
|
||||
# if img.file_format != 'JPEG' or img.file_format != 'PNG':
|
||||
# output += 'image has to be a jpeg or png'
|
||||
props.thumbnail_generating_state = output
|
||||
_set_prop(state_attr, output)
|
||||
|
||||
|
||||
def update_upload_model_preview(self, context):
|
||||
@@ -126,6 +142,20 @@ def update_upload_model_preview(self, context):
|
||||
check_thumbnail(props, imgpath)
|
||||
|
||||
|
||||
def update_wire_thumbnail_preview(self, context):
|
||||
ob = utils.get_active_model()
|
||||
if ob is not None:
|
||||
props = ob.blenderkit
|
||||
imgpath = props.wire_thumbnail
|
||||
check_thumbnail(
|
||||
props,
|
||||
imgpath,
|
||||
texture_name=".upload_preview_wire",
|
||||
flag_attr=None,
|
||||
state_attr="wire_thumbnail_generating_state",
|
||||
)
|
||||
|
||||
|
||||
def update_upload_scene_preview(self, context):
|
||||
s = bpy.context.scene
|
||||
props = s.blenderkit
|
||||
@@ -149,7 +179,7 @@ def update_upload_brush_preview(self, context):
|
||||
brush = utils.get_active_brush()
|
||||
if brush is not None:
|
||||
props = brush.blenderkit
|
||||
imgpath = bpy.path.abspath(brush.icon_filepath)
|
||||
imgpath = props.thumbnail
|
||||
check_thumbnail(props, imgpath)
|
||||
|
||||
|
||||
@@ -182,9 +212,26 @@ def start_model_thumbnailer(
|
||||
):
|
||||
"""Start Blender in background and render the thumbnail."""
|
||||
SCRIPT_NAME = "autothumb_model_bg.py"
|
||||
thumbnail_upload_type = (
|
||||
json_args.get("thumbnail_upload_type") if json_args else None
|
||||
)
|
||||
is_wire_upload = thumbnail_upload_type == "wire_thumbnail"
|
||||
computing_attr = (
|
||||
"is_generating_wire_thumbnail" if is_wire_upload else "is_generating_thumbnail"
|
||||
)
|
||||
state_attr = (
|
||||
"wire_thumbnail_generating_state"
|
||||
if is_wire_upload
|
||||
else "thumbnail_generating_state"
|
||||
)
|
||||
|
||||
def _set_prop(attr_name, value):
|
||||
if props and hasattr(props, attr_name):
|
||||
setattr(props, attr_name, value)
|
||||
|
||||
if props:
|
||||
props.is_generating_thumbnail = True
|
||||
props.thumbnail_generating_state = "Saving .blend file"
|
||||
_set_prop(computing_attr, True)
|
||||
_set_prop(state_attr, "Saving .blend file")
|
||||
|
||||
datafile = os.path.join(json_args["tempdir"], BLENDERKIT_EXPORT_DATA_FILE)
|
||||
user_preferences = bpy.context.preferences.addons[__package__].preferences
|
||||
@@ -194,6 +241,10 @@ def start_model_thumbnailer(
|
||||
"cycles"
|
||||
].preferences.compute_device_type
|
||||
|
||||
json_args["thumbnail_disable_subdivision"] = (
|
||||
user_preferences.thumbnail_disable_subdivision
|
||||
)
|
||||
|
||||
try:
|
||||
with open(datafile, "w", encoding="utf-8") as s:
|
||||
json.dump(json_args, s, ensure_ascii=False, indent=4)
|
||||
@@ -228,9 +279,10 @@ def start_model_thumbnailer(
|
||||
env=env,
|
||||
)
|
||||
bk_logger.info("Started Blender executing %s on file %s", SCRIPT_NAME, datafile)
|
||||
eval_path_computing = f"bpy.data.objects['{json_args['asset_name']}'].blenderkit.is_generating_thumbnail"
|
||||
eval_path_state = f"bpy.data.objects['{json_args['asset_name']}'].blenderkit.thumbnail_generating_state"
|
||||
eval_path = f"bpy.data.objects['{json_args['asset_name']}']"
|
||||
eval_path_base = f"bpy.data.objects['{json_args['asset_name']}']"
|
||||
eval_path = eval_path_base
|
||||
eval_path_computing = f"{eval_path_base}.blenderkit.{computing_attr}"
|
||||
eval_path_state = f"{eval_path_base}.blenderkit.{state_attr}"
|
||||
name = f"{json_args['asset_name']} thumbnailer"
|
||||
bg_blender.add_bg_process(
|
||||
name=name,
|
||||
@@ -241,7 +293,7 @@ def start_model_thumbnailer(
|
||||
process=proc,
|
||||
)
|
||||
if props:
|
||||
props.thumbnail_generating_state = "Started Blender instance"
|
||||
_set_prop(state_attr, "Started Blender instance")
|
||||
|
||||
if wait:
|
||||
while proc.poll() is None:
|
||||
@@ -449,6 +501,132 @@ class GenerateThumbnailOperator(bpy.types.Operator):
|
||||
return wm.invoke_props_dialog(self, width=400)
|
||||
|
||||
|
||||
class GenerateWireframeThumbnailOperator(bpy.types.Operator):
|
||||
"""Generate Cycles wireframe thumbnail for model assets"""
|
||||
|
||||
bl_idname = "object.blenderkit_generate_wireframe_thumbnail"
|
||||
bl_label = "BlenderKit Wireframe Thumbnail Generator"
|
||||
bl_options = {"REGISTER", "INTERNAL"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return bpy.context.view_layer.objects.active is not None
|
||||
|
||||
def draw(self, context):
|
||||
ui_props = bpy.context.window_manager.blenderkitUI
|
||||
asset_type = ui_props.asset_type
|
||||
|
||||
ob = utils.get_active_model()
|
||||
props = ob.blenderkit
|
||||
layout = self.layout
|
||||
layout.label(text="thumbnailer settings")
|
||||
layout.prop(props, "thumbnail_background_lightness")
|
||||
# for printable models
|
||||
if asset_type == "PRINTABLE":
|
||||
layout.prop(props, "thumbnail_material_color")
|
||||
layout.prop(props, "thumbnail_angle")
|
||||
layout.prop(props, "thumbnail_snap_to")
|
||||
layout.prop(props, "thumbnail_samples")
|
||||
layout.prop(props, "thumbnail_resolution")
|
||||
layout.prop(props, "thumbnail_denoising")
|
||||
preferences = bpy.context.preferences.addons[__package__].preferences
|
||||
layout.prop(preferences, "thumbnail_use_gpu")
|
||||
# TODO: wireframe
|
||||
# layout.prop(preferences, "thumbnail_disable_subdivision")
|
||||
|
||||
def execute(self, context):
|
||||
if not upload.wire_thumbnail_upload_enabled():
|
||||
self.report({"ERROR"}, "Wireframe thumbnail uploads are disabled.")
|
||||
return {"CANCELLED"}
|
||||
asset = utils.get_active_model()
|
||||
asset.blenderkit.is_generating_wire_thumbnail = True
|
||||
asset.blenderkit.wire_thumbnail_generating_state = "starting blender instance"
|
||||
tempdir = tempfile.mkdtemp()
|
||||
ext = ".blend"
|
||||
filepath = os.path.join(tempdir, "thumbnailer_wf_blenderkit" + ext)
|
||||
|
||||
path_can_be_relative = True
|
||||
thumb_dir = os.path.dirname(bpy.data.filepath)
|
||||
if thumb_dir == "":
|
||||
thumb_dir = tempdir
|
||||
path_can_be_relative = False
|
||||
|
||||
an_slug = paths.slugify(asset.name)
|
||||
|
||||
# add suffix to distinguish from regular thumbnail
|
||||
an_slug += "_wf"
|
||||
|
||||
thumb_path = os.path.join(thumb_dir, an_slug)
|
||||
|
||||
if path_can_be_relative:
|
||||
rel_thumb_path = f"//{an_slug}"
|
||||
else:
|
||||
rel_thumb_path = thumb_path
|
||||
|
||||
i = 0
|
||||
while os.path.isfile(thumb_path + ".jpg"):
|
||||
thumb_name = f"{an_slug}_{str(i).zfill(4)}"
|
||||
thumb_path = os.path.join(thumb_dir, thumb_name)
|
||||
if path_can_be_relative:
|
||||
rel_thumb_path = f"//{thumb_name}"
|
||||
|
||||
i += 1
|
||||
bkit = asset.blenderkit
|
||||
|
||||
bkit.wire_thumbnail = rel_thumb_path + ".jpg"
|
||||
bkit.wire_thumbnail_generating_state = "Saving .blend file"
|
||||
|
||||
# if this isn't here, blender crashes.
|
||||
if bpy.app.version >= (3, 0, 0):
|
||||
bpy.context.preferences.filepaths.file_preview_type = "NONE"
|
||||
# save a copy of actual scene but don't interfere with the users models
|
||||
|
||||
bpy.ops.wm.save_as_mainfile(filepath=filepath, compress=False, copy=True)
|
||||
# get all included objects
|
||||
obs = utils.get_hierarchy(asset)
|
||||
obnames = []
|
||||
for ob in obs:
|
||||
obnames.append(ob.name)
|
||||
# asset type can be model or printable
|
||||
ui_props = bpy.context.window_manager.blenderkitUI
|
||||
asset_type = ui_props.asset_type
|
||||
args_dict = {
|
||||
"type": asset_type,
|
||||
"asset_name": asset.name,
|
||||
"filepath": filepath,
|
||||
"thumbnail_path": thumb_path,
|
||||
"tempdir": tempdir,
|
||||
"thumbnail_render_type": "WIREFRAME",
|
||||
"thumbnail_upload_type": "wire_thumbnail",
|
||||
}
|
||||
thumbnail_args = {
|
||||
"type": asset_type,
|
||||
"models": str(obnames),
|
||||
"thumbnail_angle": bkit.thumbnail_angle,
|
||||
"thumbnail_snap_to": bkit.thumbnail_snap_to,
|
||||
"thumbnail_background_lightness": 0.2,
|
||||
"thumbnail_material_color": (
|
||||
bkit.thumbnail_material_color[0],
|
||||
bkit.thumbnail_material_color[1],
|
||||
bkit.thumbnail_material_color[2],
|
||||
),
|
||||
"thumbnail_resolution": bkit.thumbnail_resolution,
|
||||
"thumbnail_samples": bkit.thumbnail_samples,
|
||||
"thumbnail_denoising": bkit.thumbnail_denoising,
|
||||
}
|
||||
args_dict.update(thumbnail_args)
|
||||
|
||||
start_model_thumbnailer(
|
||||
self, json_args=args_dict, props=asset.blenderkit, wait=False
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
|
||||
return wm.invoke_props_dialog(self, width=400)
|
||||
|
||||
|
||||
class ReGenerateThumbnailOperator(bpy.types.Operator):
|
||||
"""
|
||||
Generate default thumbnail with Cycles renderer and upload it.
|
||||
@@ -464,6 +642,12 @@ class ReGenerateThumbnailOperator(bpy.types.Operator):
|
||||
name="Asset Index", description="asset index in search results", default=-1
|
||||
)
|
||||
|
||||
asset_type: StringProperty( # type: ignore[valid-type]
|
||||
name="Asset Type",
|
||||
description="Asset type used for thumbnail generation",
|
||||
default="",
|
||||
)
|
||||
|
||||
render_locally: BoolProperty( # type: ignore[valid-type]
|
||||
name="Render Locally",
|
||||
description="Render thumbnail locally instead of using server-side rendering",
|
||||
@@ -529,7 +713,12 @@ class ReGenerateThumbnailOperator(bpy.types.Operator):
|
||||
layout.label(text="thumbnailer settings")
|
||||
layout.prop(props, "thumbnail_background_lightness")
|
||||
# for printable models
|
||||
if self.asset_type == "PRINTABLE":
|
||||
asset_type = (
|
||||
getattr(self, "asset_type", "")
|
||||
or getattr(self, "asset_data", {}).get("assetType", "")
|
||||
or bpy.context.window_manager.blenderkitUI.asset_type
|
||||
).upper()
|
||||
if asset_type == "PRINTABLE":
|
||||
layout.prop(props, "thumbnail_material_color")
|
||||
layout.prop(props, "thumbnail_angle")
|
||||
layout.prop(props, "thumbnail_snap_to")
|
||||
@@ -545,6 +734,11 @@ class ReGenerateThumbnailOperator(bpy.types.Operator):
|
||||
|
||||
preferences = bpy.context.preferences.addons[__package__].preferences
|
||||
|
||||
# Ensure asset_type is set when execution is triggered directly.
|
||||
ui_props = bpy.context.window_manager.blenderkitUI
|
||||
if not getattr(self, "asset_type", ""):
|
||||
self.asset_type = ui_props.asset_type
|
||||
|
||||
if not self.render_locally:
|
||||
# Use server-side thumbnail regeneration
|
||||
success = upload.mark_for_thumbnail(
|
||||
@@ -575,8 +769,7 @@ class ReGenerateThumbnailOperator(bpy.types.Operator):
|
||||
thumb_path = os.path.join(tempdir, an_slug)
|
||||
|
||||
# asset type can be model or printable
|
||||
ui_props = bpy.context.window_manager.blenderkitUI
|
||||
self.asset_type = ui_props.asset_type
|
||||
self.asset_type = self.asset_type or ui_props.asset_type
|
||||
args_dict = {
|
||||
"type": self.asset_type,
|
||||
"asset_name": self.asset_data["name"],
|
||||
@@ -607,6 +800,12 @@ class ReGenerateThumbnailOperator(bpy.types.Operator):
|
||||
history_step = search.get_active_history_step()
|
||||
sr = history_step.get("search_results", [])
|
||||
self.asset_data = sr[self.asset_index]
|
||||
# Prepopulate asset_type so draw() can safely access it.
|
||||
self.asset_type = (
|
||||
self.asset_data.get("assetType", "")
|
||||
if isinstance(self.asset_data, dict)
|
||||
else ""
|
||||
).upper() or bpy.context.window_manager.blenderkitUI.asset_type
|
||||
|
||||
return wm.invoke_props_dialog(self, width=400)
|
||||
|
||||
@@ -891,6 +1090,7 @@ class ReGenerateMaterialThumbnailOperator(bpy.types.Operator):
|
||||
def register_thumbnailer():
|
||||
bpy.utils.register_class(GenerateThumbnailOperator)
|
||||
bpy.utils.register_class(ReGenerateThumbnailOperator)
|
||||
bpy.utils.register_class(GenerateWireframeThumbnailOperator)
|
||||
bpy.utils.register_class(GenerateMaterialThumbnailOperator)
|
||||
bpy.utils.register_class(ReGenerateMaterialThumbnailOperator)
|
||||
|
||||
@@ -898,5 +1098,6 @@ def register_thumbnailer():
|
||||
def unregister_thumbnailer():
|
||||
bpy.utils.unregister_class(GenerateThumbnailOperator)
|
||||
bpy.utils.unregister_class(ReGenerateThumbnailOperator)
|
||||
bpy.utils.unregister_class(GenerateWireframeThumbnailOperator)
|
||||
bpy.utils.unregister_class(GenerateMaterialThumbnailOperator)
|
||||
bpy.utils.unregister_class(ReGenerateMaterialThumbnailOperator)
|
||||
|
||||
Reference in New Issue
Block a user