Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVilém Duha <vilda.novak@gmail.com>2021-04-26 18:48:18 +0300
committerVilém Duha <vilda.novak@gmail.com>2021-04-26 18:48:18 +0300
commite7acb93c89fc00a5dfffcff90ab3956a0eaf34f0 (patch)
tree28853a2e858e7f6ae059fcbc52887e297dedc2db
parent4cb833e84acfd2be5fa08ce75118ce9cb60643b8 (diff)
BlenderKit:fixes
- improve material and model thumbnailers -Now it's possible to re-render assets directly from right-click menu. -fix appending of assets with wrong name -several fixes for experimental asset bar - small fixes in bg_blender for background operations - material thumbnailer background fixed - draw upload thumbnail in upload UI
-rw-r--r--blenderkit/__init__.py50
-rw-r--r--blenderkit/append_link.py19
-rw-r--r--blenderkit/asset_bar_op.py68
-rw-r--r--blenderkit/autothumb.py567
-rw-r--r--blenderkit/autothumb_material_bg.py47
-rw-r--r--blenderkit/autothumb_model_bg.py69
-rw-r--r--blenderkit/bg_blender.py24
-rw-r--r--blenderkit/blendfiles/material_thumbnailer_cycles.blendbin2975884 -> 2976697 bytes
-rw-r--r--blenderkit/categories.py6
-rw-r--r--blenderkit/download.py15
-rw-r--r--blenderkit/icons.py1
-rw-r--r--blenderkit/resolutions.py4
-rw-r--r--blenderkit/search.py65
-rw-r--r--blenderkit/ui.py21
-rw-r--r--blenderkit/ui_bgl.py2
-rw-r--r--blenderkit/ui_panels.py56
-rw-r--r--blenderkit/upload.py6
-rw-r--r--blenderkit/upload_bg.py5
-rw-r--r--blenderkit/utils.py20
19 files changed, 789 insertions, 256 deletions
diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py
index 15f8c23a..9633ae50 100644
--- a/blenderkit/__init__.py
+++ b/blenderkit/__init__.py
@@ -227,26 +227,10 @@ mesh_poly_types = (
('OTHER', 'other', ''),
)
-thumbnail_angles = (
- ('DEFAULT', 'default', ''),
- ('FRONT', 'front', ''),
- ('SIDE', 'side', ''),
- ('TOP', 'top', ''),
-)
-thumbnail_snap = (
- ('GROUND', 'ground', ''),
- ('WALL', 'wall', ''),
- ('CEILING', 'ceiling', ''),
- ('FLOAT', 'floating', ''),
-)
-thumbnail_resolutions = (
- ('256', '256', ''),
- ('512', '512', ''),
- ('1024', '1024 - minimum for public', ''),
- ('2048', '2048', ''),
-)
+
+
def udate_down_up(self, context):
@@ -588,8 +572,30 @@ def update_free(self, context):
" based on our fair share system. " \
"Part of subscription is sent to artists based on usage by paying users.\n")
+# common_upload_props = [
+# {
+# 'identifier':'id',
+# 'name':"Asset Version Id",
+# 'type':'StringProperty',
+# 'description':'Unique name of the asset version(hidden)',
+# 'default':''
+# }
+# {
+# 'identifier':'id',
+# 'name':"Asset Version Id",
+# 'type':'StringProperty',
+# 'description':'Unique name of the asset version(hidden)',
+# 'default':''
+# }
+# ]
+
+
+
class BlenderKitCommonUploadProps(object):
+ # for p in common_upload_props:
+ # exec(f"{p['identifier']}: {p['type']}(name='{p['name']}',description='{p['description']}',default='{p['default']}')")
+
id: StringProperty(
name="Asset Version Id",
description="Unique name of the asset version(hidden)",
@@ -888,7 +894,7 @@ class BlenderKitMaterialUploadProps(PropertyGroup, BlenderKitCommonUploadProps):
thumbnail_resolution: EnumProperty(
name="Resolution",
- items=thumbnail_resolutions,
+ items=autothumb.thumbnail_resolutions,
description="Thumbnail resolution",
default="1024",
)
@@ -1064,21 +1070,21 @@ class BlenderKitModelUploadProps(PropertyGroup, BlenderKitCommonUploadProps):
thumbnail_angle: EnumProperty(
name='Thumbnail Angle',
- items=thumbnail_angles,
+ items=autothumb.thumbnail_angles,
default='DEFAULT',
description='thumbnailer angle',
)
thumbnail_snap_to: EnumProperty(
name='Model Snaps To:',
- items=thumbnail_snap,
+ items=autothumb.thumbnail_snap,
default='GROUND',
description='typical placing of the interior. Leave on ground for most objects that respect gravity :)',
)
thumbnail_resolution: EnumProperty(
name="Resolution",
- items=thumbnail_resolutions,
+ items=autothumb.thumbnail_resolutions,
description="Thumbnail resolution",
default="1024",
)
diff --git a/blenderkit/append_link.py b/blenderkit/append_link.py
index a8b5c718..28b0d24e 100644
--- a/blenderkit/append_link.py
+++ b/blenderkit/append_link.py
@@ -55,23 +55,28 @@ def append_material(file_name, matname=None, link=False, fake_user=True):
break;
#not found yet? probably some name inconsistency then.
- # if not found and len(data_from.materials)>0:
- # data_to.materials = data_from.materials[0]
- # matname = data_from.materials[0]
- # print('had to assign')
+ if not found and len(data_from.materials)>0:
+ data_to.materials = [data_from.materials[0]]
+ matname = data_from.materials[0]
+ print(f"the material wasn't found under the exact name, appended another one: {matname}")
# print('in the appended file the name is ', matname)
except Exception as e:
print(e)
print('failed to open the asset file')
- # we have to find the new material :(
+ # we have to find the new material , due to possible name changes
+ mat = None
for m in bpy.data.materials:
+ print(m.name)
if m not in mats_before:
mat = m
- break
+ break;
+ #still not found?
+ if mat is None:
+ mat = bpy.data.materials.get(matname)
+
if fake_user:
mat.use_fake_user = True
-
return mat
diff --git a/blenderkit/asset_bar_op.py b/blenderkit/asset_bar_op.py
index 41ead99c..acb9cf94 100644
--- a/blenderkit/asset_bar_op.py
+++ b/blenderkit/asset_bar_op.py
@@ -52,6 +52,11 @@ BL_UI_Widget.get_area_height = get_area_height
def asset_bar_modal(self, context, event):
+ ui_props = bpy.context.scene.blenderkitUI
+ if ui_props.turn_off:
+ ui_props.turn_off = False
+ self.finish()
+
if self._finished:
return {'FINISHED'}
@@ -76,7 +81,9 @@ def asset_bar_modal(self, context, event):
self.scroll_update()
return {'RUNNING_MODAL'}
-
+ if self.check_ui_resized(context):
+ self.update_ui_size(context)
+ self.update_layout(context)
return {"PASS_THROUGH"}
def asset_bar_invoke(self, context, event):
@@ -235,6 +242,27 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
for w in self.tooltip_widgets:
w.visible = True
+ def check_ui_resized(self,context):
+ region = context.region
+ area = context.area
+ ui_props = bpy.context.scene.blenderkitUI
+ ui_scale = bpy.context.preferences.view.ui_scale
+
+ reg_multiplier = 1
+ if not bpy.context.preferences.system.use_region_overlap:
+ reg_multiplier = 0
+
+ for r in area.regions:
+ if r.type == 'TOOLS':
+ self.bar_x = r.width * reg_multiplier + self.margin + ui_props.bar_x_offset * ui_scale
+ elif r.type == 'UI':
+ self.bar_end = r.width * reg_multiplier + 100 * ui_scale
+
+ bar_width = region.width - self.bar_x - self.bar_end
+ if bar_width != self.bar_width:
+ return True
+ return False
+
def update_ui_size(self, context):
if bpy.app.background or not context.area:
@@ -264,16 +292,18 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
elif r.type == 'UI':
self.bar_end = r.width * reg_multiplier + 100 * ui_scale
- self.bar_width = region.width - ui_props.bar_x - ui_props.bar_end
+ self.bar_width = region.width - self.bar_x - self.bar_end
self.wcount = math.floor(
(self.bar_width) / (self.button_size))
search_results = bpy.context.window_manager.get('search results')
- if search_results is not None and self.wcount > 0:
- self.hcount = min(user_preferences.max_assetbar_rows, math.ceil(len(search_results) / self.wcount))
- else:
- self.hcount = 1
+ # we need to init all possible thumb previews in advance/
+ self.hcount = user_preferences.max_assetbar_rows
+ # if search_results is not None and self.wcount > 0:
+ # self.hcount = min(user_preferences.max_assetbar_rows, math.ceil(len(search_results) / self.wcount))
+ # else:
+ # self.hcount = 1
self.bar_height = (self.button_size) * self.hcount + 2 * self.assetbar_margin
# self.bar_y = region.height - ui_props.bar_y_offset * ui_scale
@@ -285,6 +315,9 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
self.reports_y = self.bar_y - self.bar_height - 100
self.reports_x = self.bar_x
+ def update_layout(self, context):
+ pass;
+
def __init__(self):
super().__init__()
@@ -490,7 +523,14 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
if img:
self.tooltip_image.set_image(img.filepath)
self.asset_name.text = asset_data['name']
- self.tooltip_panel.update(widget.x_screen + widget.width, widget.y_screen + widget.height)
+
+ properties_width = 0
+ for r in bpy.context.area.regions:
+ if r.type == 'UI':
+ properties_width = r.width
+ tooltip_x = min(widget.x_screen + widget.width, bpy.context.region.width - self.tooltip_panel.width -properties_width)
+
+ self.tooltip_panel.update(tooltip_x, widget.y_screen + widget.height)
self.tooltip_panel.layout_widgets()
def exit_button(self, widget):
@@ -518,8 +558,9 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
blenderkit.search.search(get_next=True)
def update_images(self):
- sr = bpy.context.window_manager['search results']
-
+ sr = bpy.context.window_manager.get('search results')
+ if not sr:
+ return
for asset_button in self.asset_buttons:
asset_button.asset_index = asset_button.button_index + self.scroll_offset
if asset_button.asset_index < len(sr):
@@ -580,6 +621,15 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator):
self.scroll_offset -= self.wcount * self.hcount
self.scroll_update()
+ def update_sizes(self):
+ properties_width = 0
+ for r in bpy.context.area.regions:
+ if r.type == 'UI':
+ properties_width = r.width
+ tooltip_x = min(widget.x_screen + widget.width,
+ bpy.context.region.width - self.tooltip_panel.width - properties_width)
+ print(widget.x_screen + widget.width, bpy.context.region.width - self.tooltip_panel.width)
+
def register():
bpy.utils.register_class(BlenderKitAssetBarOperator)
diff --git a/blenderkit/autothumb.py b/blenderkit/autothumb.py
index 9f47d3c3..26697d74 100644
--- a/blenderkit/autothumb.py
+++ b/blenderkit/autothumb.py
@@ -16,23 +16,69 @@
#
# ##### END GPL LICENSE BLOCK #####
-
-from blenderkit import paths, utils, bg_blender, ui_panels
+from blenderkit import paths, utils, bg_blender, ui_panels, icons, tasks_queue, download
import tempfile, os, subprocess, json, sys
import bpy
+from bpy.props import (
+ FloatProperty,
+ IntProperty,
+ EnumProperty,
+ BoolProperty,
+ StringProperty,
+)
BLENDERKIT_EXPORT_DATA_FILE = "data.json"
+thumbnail_resolutions = (
+ ('256', '256', ''),
+ ('512', '512', ''),
+ ('1024', '1024 - minimum for public', ''),
+ ('2048', '2048', ''),
+)
+
+thumbnail_angles = (
+ ('DEFAULT', 'default', ''),
+ ('FRONT', 'front', ''),
+ ('SIDE', 'side', ''),
+ ('TOP', 'top', ''),
+)
+
+thumbnail_snap = (
+ ('GROUND', 'ground', ''),
+ ('WALL', 'wall', ''),
+ ('CEILING', 'ceiling', ''),
+ ('FLOAT', 'floating', ''),
+)
+
+
+def get_texture_ui(tpath, iname):
+ tex = bpy.data.textures.get(iname)
+
+ if tpath.startswith('//'):
+ tpath = bpy.path.abspath(tpath)
+
+ if not tex or not tex.image or not tex.image.filepath == tpath:
+ tasks_queue.add_task((utils.get_hidden_image, (tpath, iname)), only_last=True)
+ tasks_queue.add_task((utils.get_hidden_texture, (iname,)), only_last=True)
+ return None
+ return tex
+
def check_thumbnail(props, imgpath):
img = utils.get_hidden_image(imgpath, 'upload_preview', force_reload=True)
+ print(' check thumbnail ', img)
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 = ''
- return
+
+ tex = utils.get_hidden_texture(img.name)
+ # pcoll = icons.icon_collections["previews"]
+ # pcoll.load(img.name, img.filepath, 'IMAGE')
+
+ return img
else:
props.has_thumbnail = False
output = ''
@@ -55,7 +101,7 @@ def update_upload_model_preview(self, context):
if ob is not None:
props = ob.blenderkit
imgpath = props.thumbnail
- check_thumbnail(props, imgpath)
+ img = check_thumbnail(props, imgpath)
def update_upload_scene_preview(self, context):
@@ -83,55 +129,19 @@ def update_upload_brush_preview(self, context):
check_thumbnail(props, imgpath)
-def start_thumbnailer(self, context):
+def start_thumbnailer(self=None, json_args=None, props=None, wait=False, add_bg_process=True):
# Prepare to save the file
- mainmodel = utils.get_active_model()
- mainmodel.blenderkit.is_generating_thumbnail = True
- mainmodel.blenderkit.thumbnail_generating_state = 'starting blender instance'
binary_path = bpy.app.binary_path
script_path = os.path.dirname(os.path.realpath(__file__))
- basename, ext = os.path.splitext(bpy.data.filepath)
- if not basename:
- basename = os.path.join(basename, "temp")
- if not ext:
- ext = ".blend"
- asset_name = mainmodel.name
- tempdir = tempfile.mkdtemp()
-
- file_dir = os.path.dirname(bpy.data.filepath)
- thumb_path = os.path.join(file_dir, asset_name)
- rel_thumb_path = os.path.join('//', asset_name)
-
- i = 0
- while os.path.isfile(thumb_path + '.jpg'):
- thumb_path = os.path.join(file_dir, asset_name + '_' + str(i).zfill(4))
- rel_thumb_path = os.path.join('//', asset_name + '_' + str(i).zfill(4))
- i += 1
-
- filepath = os.path.join(tempdir, "thumbnailer_blenderkit" + ext)
+
+ ext = '.blend'
+
tfpath = paths.get_thumbnailer_filepath()
- datafile = os.path.join(tempdir, BLENDERKIT_EXPORT_DATA_FILE)
+ datafile = os.path.join(json_args['tempdir'], BLENDERKIT_EXPORT_DATA_FILE)
try:
- # 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)
-
- obs = utils.get_hierarchy(mainmodel)
- obnames = []
- for ob in obs:
- obnames.append(ob.name)
- with open(datafile, 'w', encoding = 'utf-8') as s:
- bkit = mainmodel.blenderkit
- json.dump({
- "type": "model",
- "models": str(obnames),
- "thumbnail_angle": bkit.thumbnail_angle,
- "thumbnail_snap_to": bkit.thumbnail_snap_to,
- "thumbnail_background_lightness": bkit.thumbnail_background_lightness,
- "thumbnail_resolution": bkit.thumbnail_resolution,
- "thumbnail_samples": bkit.thumbnail_samples,
- "thumbnail_denoising": bkit.thumbnail_denoising,
- }, s, ensure_ascii=False, indent=4)
+ with open(datafile, 'w', encoding='utf-8') as s:
+ json.dump(json_args, s, ensure_ascii=False, indent=4)
proc = subprocess.Popen([
binary_path,
@@ -139,72 +149,49 @@ def start_thumbnailer(self, context):
"-noaudio",
tfpath,
"--python", os.path.join(script_path, "autothumb_model_bg.py"),
- "--", datafile, filepath, thumb_path, tempdir
+ "--", datafile,
], bufsize=1, stdout=subprocess.PIPE, stdin=subprocess.PIPE, creationflags=utils.get_process_flags())
- eval_path_computing = "bpy.data.objects['%s'].blenderkit.is_generating_thumbnail" % mainmodel.name
- eval_path_state = "bpy.data.objects['%s'].blenderkit.thumbnail_generating_state" % mainmodel.name
- eval_path = "bpy.data.objects['%s']" % mainmodel.name
+ eval_path_computing = "bpy.data.objects['%s'].blenderkit.is_generating_thumbnail" % json_args['asset_name']
+ eval_path_state = "bpy.data.objects['%s'].blenderkit.thumbnail_generating_state" % json_args['asset_name']
+ eval_path = "bpy.data.objects['%s']" % json_args['asset_name']
bg_blender.add_bg_process(eval_path_computing=eval_path_computing, eval_path_state=eval_path_state,
eval_path=eval_path, process_type='THUMBNAILER', process=proc)
- mainmodel.blenderkit.thumbnail = rel_thumb_path + '.jpg'
- mainmodel.blenderkit.thumbnail_generating_state = 'Saving .blend file'
except Exception as e:
self.report({'WARNING'}, "Error while exporting file: %s" % str(e))
return {'FINISHED'}
-def start_material_thumbnailer(self, context, wait=False):
- # Prepare to save the file
- mat = bpy.context.active_object.active_material
- mat.blenderkit.is_generating_thumbnail = True
- mat.blenderkit.thumbnail_generating_state = 'starting blender instance'
+def start_material_thumbnailer(self=None, json_args=None, props=None, wait=False, add_bg_process=True):
+ '''
+
+ Parameters
+ ----------
+ self
+ json_args - all arguments:
+ props - blenderkit upload props with thumbnail settings, to communicate back, if not present, not used.
+ wait - wait for the rendering to finish
+
+ Returns
+ -------
+
+ '''
+ if props:
+ props.is_generating_thumbnail = True
+ props.thumbnail_generating_state = 'starting blender instance'
binary_path = bpy.app.binary_path
script_path = os.path.dirname(os.path.realpath(__file__))
- basename, ext = os.path.splitext(bpy.data.filepath)
- if not basename:
- basename = os.path.join(basename, "temp")
- if not ext:
- ext = ".blend"
- asset_name = mat.name
- tempdir = tempfile.mkdtemp()
-
- file_dir = os.path.dirname(bpy.data.filepath)
-
- thumb_path = os.path.join(file_dir, asset_name)
- rel_thumb_path = os.path.join('//', mat.name)
- i = 0
- while os.path.isfile(thumb_path + '.png'):
- thumb_path = os.path.join(file_dir, mat.name + '_' + str(i).zfill(4))
- rel_thumb_path = os.path.join('//', mat.name + '_' + str(i).zfill(4))
- i += 1
-
- filepath = os.path.join(tempdir, "material_thumbnailer_cycles" + ext)
+
tfpath = paths.get_material_thumbnailer_filepath()
- datafile = os.path.join(tempdir, BLENDERKIT_EXPORT_DATA_FILE)
- try:
- # 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)
+ datafile = os.path.join(json_args['tempdir'], BLENDERKIT_EXPORT_DATA_FILE)
- with open(datafile, 'w', encoding = 'utf-8') as s:
- bkit = mat.blenderkit
- json.dump({
- "type": "material",
- "material": mat.name,
- "thumbnail_type": bkit.thumbnail_generator_type,
- "thumbnail_scale": bkit.thumbnail_scale,
- "thumbnail_background": bkit.thumbnail_background,
- "thumbnail_background_lightness": bkit.thumbnail_background_lightness,
- "thumbnail_resolution": bkit.thumbnail_resolution,
- "thumbnail_samples": bkit.thumbnail_samples,
- "thumbnail_denoising": bkit.thumbnail_denoising,
- "adaptive_subdivision": bkit.adaptive_subdivision,
- "texture_size_meters": bkit.texture_size_meters,
- }, s, ensure_ascii=False, indent=4)
+ try:
+ with open(datafile, 'w', encoding='utf-8') as s:
+ json.dump(json_args, s, ensure_ascii=False, indent=4)
proc = subprocess.Popen([
binary_path,
@@ -212,20 +199,28 @@ def start_material_thumbnailer(self, context, wait=False):
"-noaudio",
tfpath,
"--python", os.path.join(script_path, "autothumb_material_bg.py"),
- "--", datafile, filepath, thumb_path, tempdir
+ "--", datafile,
], bufsize=1, stdout=subprocess.PIPE, stdin=subprocess.PIPE, creationflags=utils.get_process_flags())
- eval_path_computing = "bpy.data.materials['%s'].blenderkit.is_generating_thumbnail" % mat.name
- eval_path_state = "bpy.data.materials['%s'].blenderkit.thumbnail_generating_state" % mat.name
- eval_path = "bpy.data.materials['%s']" % mat.name
+ eval_path_computing = "bpy.data.materials['%s'].blenderkit.is_generating_thumbnail" % json_args['asset_name']
+ eval_path_state = "bpy.data.materials['%s'].blenderkit.thumbnail_generating_state" % json_args['asset_name']
+ eval_path = "bpy.data.materials['%s']" % json_args['asset_name']
- bg_blender.add_bg_process(eval_path_computing=eval_path_computing, eval_path_state=eval_path_state,
+ bg_blender.add_bg_process(name=json_args['asset_name'], eval_path_computing=eval_path_computing,
+ eval_path_state=eval_path_state,
eval_path=eval_path, process_type='THUMBNAILER', process=proc)
+ if props:
+ props.thumbnail_generating_state = 'Saving .blend file'
- mat.blenderkit.thumbnail = rel_thumb_path + '.png'
- mat.blenderkit.thumbnail_generating_state = 'Saving .blend file'
+ if wait:
+ while proc.poll() is None:
+ stdout_data, stderr_data = proc.communicate()
+ print(stdout_data)
except Exception as e:
- self.report({'WARNING'}, "Error while packing file: %s" % str(e))
+ if self:
+ self.report({'WARNING'}, "Error while packing file: %s" % str(e))
+ else:
+ print(e)
return {'FINISHED'}
@@ -256,24 +251,198 @@ class GenerateThumbnailOperator(bpy.types.Operator):
layout.prop(preferences, "thumbnail_use_gpu")
def execute(self, context):
- start_thumbnailer(self, context)
+ asset = utils.get_active_model()
+ asset.blenderkit.is_generating_thumbnail = True
+ asset.blenderkit.thumbnail_generating_state = 'starting blender instance'
+
+ tempdir = tempfile.mkdtemp()
+ ext = '.blend'
+ filepath = os.path.join(tempdir, "thumbnailer_blenderkit" + ext)
+
+ path_can_be_relative = True
+ file_dir = os.path.dirname(bpy.data.filepath)
+ if file_dir == '':
+ file_dir = tempdir
+ path_can_be_relative = False
+
+ an_slug = paths.slugify(asset.name)
+ thumb_path = os.path.join(file_dir, an_slug)
+ if path_can_be_relative:
+ rel_thumb_path = os.path.join('//', an_slug)
+ else:
+ rel_thumb_path = thumb_path
+
+
+ i = 0
+ while os.path.isfile(thumb_path + '.jpg'):
+ thumb_path = os.path.join(file_dir, an_slug + '_' + str(i).zfill(4))
+ rel_thumb_path = os.path.join('//', an_slug + '_' + str(i).zfill(4))
+ i += 1
+ bkit = asset.blenderkit
+
+ bkit.thumbnail = rel_thumb_path + '.jpg'
+ bkit.thumbnail_generating_state = 'Saving .blend file'
+
+ # 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)
+
+ args_dict = {
+ "type": "material",
+ "asset_name": asset.name,
+ "filepath": filepath,
+ "thumbnail_path": thumb_path,
+ "tempdir": tempdir,
+ }
+ thumbnail_args = {
+ "type": "model",
+ "models": str(obnames),
+ "thumbnail_angle": bkit.thumbnail_angle,
+ "thumbnail_snap_to": bkit.thumbnail_snap_to,
+ "thumbnail_background_lightness": bkit.thumbnail_background_lightness,
+ "thumbnail_resolution": bkit.thumbnail_resolution,
+ "thumbnail_samples": bkit.thumbnail_samples,
+ "thumbnail_denoising": bkit.thumbnail_denoising,
+ }
+ args_dict.update(thumbnail_args)
+
+ start_thumbnailer(self,
+ json_args=args_dict,
+ props=asset.blenderkit, wait=False)
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
- if bpy.data.filepath == '':
- ui_panels.ui_message(
- title = "Can't render thumbnail",
- message = "please save your file first")
+ # if bpy.data.filepath == '':
+ # ui_panels.ui_message(
+ # title="Can't render thumbnail",
+ # message="please save your file first")
+ #
+ # return {'FINISHED'}
+
+ return wm.invoke_props_dialog(self)
+
+
+class ReGenerateThumbnailOperator(bpy.types.Operator):
+ """Generate Cycles thumbnail for model assets"""
+ bl_idname = "object.blenderkit_regenerate_thumbnail"
+ bl_label = "BlenderKit Thumbnail Re-generate"
+ bl_options = {'REGISTER', 'INTERNAL'}
+
+ asset_index: IntProperty(name="Asset Index", description='asset index in search results', default=-1)
+
+ thumbnail_background_lightness: FloatProperty(name="Thumbnail Background Lightness",
+ description="set to make your material stand out", default=1.0,
+ min=0.01, max=10)
+
+ thumbnail_angle: EnumProperty(
+ name='Thumbnail Angle',
+ items=thumbnail_angles,
+ default='DEFAULT',
+ description='thumbnailer angle',
+ )
+
+ thumbnail_snap_to: EnumProperty(
+ name='Model Snaps To:',
+ items=thumbnail_snap,
+ default='GROUND',
+ description='typical placing of the interior. Leave on ground for most objects that respect gravity :)',
+ )
+
+ thumbnail_resolution: EnumProperty(
+ name="Resolution",
+ items=thumbnail_resolutions,
+ description="Thumbnail resolution",
+ default="1024",
+ )
+
+ thumbnail_samples: IntProperty(name="Cycles Samples",
+ description="cycles samples setting", default=100,
+ min=5, max=5000)
+ thumbnail_denoising: BoolProperty(name="Use Denoising",
+ description="Use denoising", default=True)
+
+ @classmethod
+ def poll(cls, context):
+ return True # bpy.context.view_layer.objects.active is not None
+
+ def draw(self, context):
+ ob = bpy.context.active_object
+ while ob.parent is not None:
+ ob = ob.parent
+ props = self
+ layout = self.layout
+ layout.label(text='thumbnailer settings')
+ layout.prop(props, 'thumbnail_background_lightness')
+ 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['blenderkit'].preferences
+ layout.prop(preferences, "thumbnail_use_gpu")
- return {'FINISHED'}
+ def execute(self, context):
+ if not self.asset_index > -1:
+ return {'CANCELLED'}
+
+ # either get the data from search results
+ sr = bpy.context.window_manager['search results']
+ asset_data = sr[self.asset_index].to_dict()
+
+ tempdir = tempfile.mkdtemp()
+
+ an_slug = paths.slugify(asset_data['name'])
+ thumb_path = os.path.join(tempdir, an_slug)
+
+
+ args_dict = {
+ "type": "material",
+ "asset_name": asset_data['name'],
+ "asset_data": asset_data,
+ # "filepath": filepath,
+ "thumbnail_path": thumb_path,
+ "tempdir": tempdir,
+ "do_download": True,
+ "upload_after_render": True,
+ }
+ thumbnail_args = {
+ "type": "model",
+ "thumbnail_angle": self.thumbnail_angle,
+ "thumbnail_snap_to": self.thumbnail_snap_to,
+ "thumbnail_background_lightness": self.thumbnail_background_lightness,
+ "thumbnail_resolution": self.thumbnail_resolution,
+ "thumbnail_samples": self.thumbnail_samples,
+ "thumbnail_denoising": self.thumbnail_denoising,
+ }
+ args_dict.update(thumbnail_args)
+
+ start_thumbnailer(self,
+ json_args=args_dict,
+ wait=False)
+ return {'FINISHED'}
+ start_thumbnailer(self, context)
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ wm = context.window_manager
+ # if bpy.data.filepath == '':
+ # ui_panels.ui_message(
+ # title="Can't render thumbnail",
+ # message="please save your file first")
+ #
+ # return {'FINISHED'}
return wm.invoke_props_dialog(self)
class GenerateMaterialThumbnailOperator(bpy.types.Operator):
"""Generate default thumbnail with Cycles renderer."""
- bl_idname = "object.blenderkit_material_thumbnail"
+ bl_idname = "object.blenderkit_generate_material_thumbnail"
bl_label = "BlenderKit Material Thumbnail Generator"
bl_options = {'REGISTER', 'INTERNAL'}
@@ -300,7 +469,48 @@ class GenerateMaterialThumbnailOperator(bpy.types.Operator):
layout.prop(preferences, "thumbnail_use_gpu")
def execute(self, context):
- start_material_thumbnailer(self, context)
+ asset = bpy.context.active_object.active_material
+ tempdir = tempfile.mkdtemp()
+ filepath = os.path.join(tempdir, "material_thumbnailer_cycles.blend")
+ # 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)
+
+ thumb_dir = os.path.dirname(bpy.data.filepath)
+ thumb_path = os.path.join(thumb_dir, asset.name)
+ rel_thumb_path = os.path.join('//', asset.name)
+ # auto increase number of the generated thumbnail.
+ i = 0
+ while os.path.isfile(thumb_path + '.png'):
+ thumb_path = os.path.join(thumb_dir, asset.name + '_' + str(i).zfill(4))
+ rel_thumb_path = os.path.join('//', asset.name + '_' + str(i).zfill(4))
+ i += 1
+
+ asset.blenderkit.thumbnail = rel_thumb_path + '.png'
+ bkit = asset.blenderkit
+
+ args_dict = {
+ "type": "material",
+ "asset_name": asset.name,
+ "filepath": filepath,
+ "thumbnail_path": thumb_path,
+ "tempdir": tempdir,
+ }
+
+ thumbnail_args = {
+ "thumbnail_type": bkit.thumbnail_generator_type,
+ "thumbnail_scale": bkit.thumbnail_scale,
+ "thumbnail_background": bkit.thumbnail_background,
+ "thumbnail_background_lightness": bkit.thumbnail_background_lightness,
+ "thumbnail_resolution": bkit.thumbnail_resolution,
+ "thumbnail_samples": bkit.thumbnail_samples,
+ "thumbnail_denoising": bkit.thumbnail_denoising,
+ "adaptive_subdivision": bkit.adaptive_subdivision,
+ "texture_size_meters": bkit.texture_size_meters,
+ }
+ args_dict.update(thumbnail_args)
+ start_material_thumbnailer(self,
+ json_args=args_dict,
+ props=asset.blenderkit, wait=False)
return {'FINISHED'}
@@ -309,11 +519,144 @@ class GenerateMaterialThumbnailOperator(bpy.types.Operator):
return wm.invoke_props_dialog(self)
+class ReGenerateMaterialThumbnailOperator(bpy.types.Operator):
+ """
+ Generate default thumbnail with Cycles renderer.
+ Works also for assets from search results, without being downloaded before.
+ """
+ bl_idname = "object.blenderkit_regenerate_material_thumbnail"
+ bl_label = "BlenderKit Material Thumbnail Re-Generator"
+ bl_options = {'REGISTER', 'INTERNAL'}
+
+ asset_index: IntProperty(name="Asset Index", description='asset index in search results', default=-1)
+
+ thumbnail_scale: FloatProperty(name="Thumbnail Object Size",
+ description="Size of material preview object in meters."
+ "Change for materials that look better at sizes different than 1m",
+ default=1, min=0.00001, max=10)
+ thumbnail_background: BoolProperty(name="Thumbnail Background (for Glass only)",
+ description="For refractive materials, you might need a background.\n"
+ "Don't use for other types of materials.\n"
+ "Transparent background is preferred",
+ default=False)
+ thumbnail_background_lightness: FloatProperty(name="Thumbnail Background Lightness",
+ description="Set to make your material stand out with enough contrast",
+ default=.9,
+ min=0.00001, max=1)
+ thumbnail_samples: IntProperty(name="Cycles Samples",
+ description="Cycles samples", default=100,
+ min=5, max=5000)
+ thumbnail_denoising: BoolProperty(name="Use Denoising",
+ description="Use denoising", default=True)
+ adaptive_subdivision: BoolProperty(name="Adaptive Subdivide",
+ description="Use adaptive displacement subdivision", default=False)
+
+ thumbnail_resolution: EnumProperty(
+ name="Resolution",
+ items=thumbnail_resolutions,
+ description="Thumbnail resolution",
+ default="1024",
+ )
+
+ thumbnail_generator_type: EnumProperty(
+ name="Thumbnail Style",
+ items=(
+ ('BALL', 'Ball', ""),
+ ('BALL_COMPLEX', 'Ball complex', 'Complex ball to highlight edgewear or material thickness'),
+ ('FLUID', 'Fluid', 'Fluid'),
+ ('CLOTH', 'Cloth', 'Cloth'),
+ ('HAIR', 'Hair', 'Hair ')
+ ),
+ description="Style of asset",
+ default="BALL",
+ )
+
+ @classmethod
+ def poll(cls, context):
+ return True # bpy.context.view_layer.objects.active is not None
+
+ def check(self, context):
+ return True
+
+ def draw(self, context):
+ layout = self.layout
+ props = self
+ layout.prop(props, 'thumbnail_generator_type')
+ layout.prop(props, 'thumbnail_scale')
+ layout.prop(props, 'thumbnail_background')
+ if props.thumbnail_background:
+ layout.prop(props, 'thumbnail_background_lightness')
+ layout.prop(props, 'thumbnail_resolution')
+ layout.prop(props, 'thumbnail_samples')
+ layout.prop(props, 'thumbnail_denoising')
+ layout.prop(props, 'adaptive_subdivision')
+ preferences = bpy.context.preferences.addons['blenderkit'].preferences
+ layout.prop(preferences, "thumbnail_use_gpu")
+
+ def execute(self, context):
+
+ if not self.asset_index > -1:
+ return {'CANCELLED'}
+
+ # either get the data from search results
+ sr = bpy.context.window_manager['search results']
+ asset_data = sr[self.asset_index].to_dict()
+
+ tempdir = tempfile.mkdtemp()
+
+ thumb_path = os.path.join(tempdir, asset_data['name'])
+
+ args_dict = {
+ "type": "material",
+ "asset_name": asset_data['name'],
+ "asset_data": asset_data,
+ "thumbnail_path": thumb_path,
+ "tempdir": tempdir,
+ "do_download": True,
+ "upload_after_render": True,
+ }
+ thumbnail_args = {
+ "thumbnail_type": self.thumbnail_generator_type,
+ "thumbnail_scale": self.thumbnail_scale,
+ "thumbnail_background": self.thumbnail_background,
+ "thumbnail_background_lightness": self.thumbnail_background_lightness,
+ "thumbnail_resolution": self.thumbnail_resolution,
+ "thumbnail_samples": self.thumbnail_samples,
+ "thumbnail_denoising": self.thumbnail_denoising,
+ "adaptive_subdivision": self.adaptive_subdivision,
+ "texture_size_meters": utils.get_param(asset_data, 'textureSizeMeters', 1.0),
+ }
+ args_dict.update(thumbnail_args)
+ start_material_thumbnailer(self,
+ json_args=args_dict,
+ wait=False)
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ # scene = bpy.context.scene
+ # ui_props = scene.blenderkitUI
+ # if ui_props.active_index > -1:
+ # sr = bpy.context.window_manager['search results']
+ # self.asset_data = dict(sr[ui_props.active_index])
+ # else:
+ #
+ # active_asset = utils.get_active_asset_by_type(asset_type = self.asset_type)
+ # self.asset_data = active_asset.get('asset_data')
+
+ wm = context.window_manager
+ return wm.invoke_props_dialog(self)
+
+
def register_thumbnailer():
bpy.utils.register_class(GenerateThumbnailOperator)
+ bpy.utils.register_class(ReGenerateThumbnailOperator)
bpy.utils.register_class(GenerateMaterialThumbnailOperator)
+ bpy.utils.register_class(ReGenerateMaterialThumbnailOperator)
def unregister_thumbnailer():
bpy.utils.unregister_class(GenerateThumbnailOperator)
+ bpy.utils.unregister_class(ReGenerateThumbnailOperator)
bpy.utils.unregister_class(GenerateMaterialThumbnailOperator)
+ bpy.utils.unregister_class(ReGenerateMaterialThumbnailOperator)
diff --git a/blenderkit/autothumb_material_bg.py b/blenderkit/autothumb_material_bg.py
index d27d7b90..0a0ce5db 100644
--- a/blenderkit/autothumb_material_bg.py
+++ b/blenderkit/autothumb_material_bg.py
@@ -18,16 +18,14 @@
-from blenderkit import utils, append_link, bg_blender
+from blenderkit import utils, append_link, bg_blender, upload_bg, download
import sys, json, math
import bpy
from pathlib import Path
-BLENDERKIT_EXPORT_TEMP_DIR = sys.argv[-1]
-BLENDERKIT_THUMBNAIL_PATH = sys.argv[-2]
-BLENDERKIT_EXPORT_FILE_INPUT = sys.argv[-3]
-BLENDERKIT_EXPORT_DATA = sys.argv[-4]
+
+BLENDERKIT_EXPORT_DATA = sys.argv[-1]
def render_thumbnails():
@@ -44,13 +42,26 @@ def unhide_collection(cname):
if __name__ == "__main__":
try:
bg_blender.progress('preparing thumbnail scene')
+ user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
+
with open(BLENDERKIT_EXPORT_DATA, 'r',encoding='utf-8') as s:
data = json.load(s)
# append_material(file_name, matname = None, link = False, fake_user = True)
- mat = append_link.append_material(file_name=BLENDERKIT_EXPORT_FILE_INPUT, matname=data["material"], link=True,
+ if data.get('do_download'):
+ asset_data = data['asset_data']
+ has_url = download.get_download_url(asset_data, download.get_scene_id(), user_preferences.api_key, tcom=None,
+ resolution='blend')
+ if not has_url:
+ bg_blender.progress("couldn't download asset for thumnbail re-rendering")
+ exit()
+ # download first, or rather make sure if it's already downloaded
+ bg_blender.progress('downloading asset')
+ fpath = download.download_asset_file(asset_data)
+ data['filepath'] = fpath
+
+ mat = append_link.append_material(file_name=data['filepath'], matname=data["asset_name"], link=True,
fake_user=False)
- user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
s = bpy.context.scene
@@ -61,7 +72,6 @@ if __name__ == "__main__":
'CLOTH': 'Cloth',
'HAIR': 'Hair'
}
-
unhide_collection(colmapdict[data["thumbnail_type"]])
if data['thumbnail_background']:
unhide_collection('Background')
@@ -70,6 +80,9 @@ if __name__ == "__main__":
tscale = data["thumbnail_scale"]
bpy.context.view_layer.objects['scaler'].scale = (tscale, tscale, tscale)
bpy.context.view_layer.update()
+ print('we have this materialB')
+ print(mat)
+
for ob in bpy.context.visible_objects:
if ob.name[:15] == 'MaterialPreview':
ob.material_slots[0].material = mat
@@ -86,6 +99,7 @@ if __name__ == "__main__":
if data["thumbnail_type"] in ['BALL', 'BALL_COMPLEX', 'CLOTH']:
utils.automap(ob.name, tex_size = ts / tscale, just_scale = True, bg_exception=True)
bpy.context.view_layer.update()
+ print('got to C')
s.cycles.volume_step_size = tscale * .1
@@ -113,9 +127,24 @@ if __name__ == "__main__":
bpy.context.scene.render.resolution_x = int(data['thumbnail_resolution'])
bpy.context.scene.render.resolution_y = int(data['thumbnail_resolution'])
- bpy.context.scene.render.filepath = BLENDERKIT_THUMBNAIL_PATH
+ bpy.context.scene.render.filepath = data['thumbnail_path']
bg_blender.progress('rendering thumbnail')
render_thumbnails()
+ if data.get('upload_after_render') and data.get('asset_data'):
+ bg_blender.progress('uploading thumbnail')
+ preferences = bpy.context.preferences.addons['blenderkit'].preferences
+
+ file = {
+ "type": "thumbnail",
+ "index": 0,
+ "file_path": data['thumbnail_path'] + '.png'
+ }
+ upload_data = {
+ "name": data['asset_data']['name'],
+ "token": preferences.api_key,
+ "id": data['asset_data']['id']
+ }
+ upload_bg.upload_file(upload_data, file)
bg_blender.progress('background autothumbnailer finished successfully')
diff --git a/blenderkit/autothumb_model_bg.py b/blenderkit/autothumb_model_bg.py
index 87acfa19..9be56d9f 100644
--- a/blenderkit/autothumb_model_bg.py
+++ b/blenderkit/autothumb_model_bg.py
@@ -18,17 +18,14 @@
-from blenderkit import utils, append_link, bg_blender
+from blenderkit import utils, append_link, bg_blender, download, upload_bg
import sys, json, math
from pathlib import Path
import bpy
import mathutils
-BLENDERKIT_EXPORT_TEMP_DIR = sys.argv[-1]
-BLENDERKIT_THUMBNAIL_PATH = sys.argv[-2]
-BLENDERKIT_EXPORT_FILE_INPUT = sys.argv[-3]
-BLENDERKIT_EXPORT_DATA = sys.argv[-4]
+BLENDERKIT_EXPORT_DATA = sys.argv[-1]
def get_obnames():
@@ -42,6 +39,8 @@ def center_obs_for_thumbnail(obs):
s = bpy.context.scene
# obs = bpy.context.selected_objects
parent = obs[0]
+ if parent.type == 'EMPTY' and parent.instance_collection is not None:
+ obs = parent.instance_collection.objects[:]
while parent.parent != None:
parent = parent.parent
@@ -79,18 +78,42 @@ def render_thumbnails():
if __name__ == "__main__":
try:
+ print( 'got to A')
with open(BLENDERKIT_EXPORT_DATA, 'r',encoding='utf-8') as s:
data = json.load(s)
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
- bg_blender.progress('preparing thumbnail scene')
- obnames = get_obnames()
- main_object, allobs = append_link.append_objects(file_name=BLENDERKIT_EXPORT_FILE_INPUT,
+
+ if data.get('do_download'):
+ bg_blender.progress('Downloading asset')
+
+ asset_data = data['asset_data']
+ has_url = download.get_download_url(asset_data, download.get_scene_id(), user_preferences.api_key, tcom=None,
+ resolution='blend')
+ if not has_url == True:
+ bg_blender.progress("couldn't download asset for thumnbail re-rendering")
+ # download first, or rather make sure if it's already downloaded
+ bg_blender.progress('downloading asset')
+ fpath = download.download_asset_file(asset_data)
+ data['filepath'] = fpath
+ main_object, allobs = append_link.link_collection(fpath,
+ location=(0,0,0),
+ rotation=(0,0,0),
+ link=True,
+ name=asset_data['name'],
+ parent=None)
+ allobs = [main_object]
+ else:
+ bg_blender.progress('preparing thumbnail scene')
+
+ obnames = get_obnames()
+ main_object, allobs = append_link.append_objects(file_name=data['filepath'],
obnames=obnames,
- link=True)
+ link=True)
bpy.context.view_layer.update()
+
camdict = {
'GROUND': 'camera ground',
'WALL': 'camera wall',
@@ -100,7 +123,7 @@ if __name__ == "__main__":
bpy.context.scene.camera = bpy.data.objects[camdict[data['thumbnail_snap_to']]]
center_obs_for_thumbnail(allobs)
- bpy.context.scene.render.filepath = BLENDERKIT_THUMBNAIL_PATH
+ bpy.context.scene.render.filepath = data['thumbnail_path']
if user_preferences.thumbnail_use_gpu:
bpy.context.scene.cycles.device = 'GPU'
@@ -112,6 +135,7 @@ if __name__ == "__main__":
}
s = bpy.context.scene
s.frame_set(fdict[data['thumbnail_angle']])
+ print( 'got to C')
snapdict = {
'GROUND': 'Ground',
@@ -131,6 +155,7 @@ if __name__ == "__main__":
s.cycles.samples = data['thumbnail_samples']
bpy.context.view_layer.cycles.use_denoising = data['thumbnail_denoising']
bpy.context.view_layer.update()
+ print( 'got to D')
# import blender's HDR here
# hdr_path = Path('datafiles/studiolights/world/interior.exr')
@@ -152,10 +177,32 @@ if __name__ == "__main__":
bg_blender.progress('rendering thumbnail')
render_thumbnails()
- fpath = BLENDERKIT_THUMBNAIL_PATH + '0001.jpg'
+ fpath = data['thumbnail_path'] + '.jpg'
+ if data.get('upload_after_render') and data.get('asset_data'):
+ bg_blender.progress('uploading thumbnail')
+ preferences = bpy.context.preferences.addons['blenderkit'].preferences
+ print('uploading A')
+ file = {
+ "type": "thumbnail",
+ "index": 0,
+ "file_path": fpath
+ }
+ upload_data = {
+ "name": data['asset_data']['name'],
+ "token": preferences.api_key,
+ "id": data['asset_data']['id']
+ }
+ print('uploading B')
+
+ upload_bg.upload_file(upload_data, file)
+ print('uploading C')
+
bg_blender.progress('background autothumbnailer finished successfully')
+ print( 'got to E')
+
+
except:
import traceback
diff --git a/blenderkit/bg_blender.py b/blenderkit/bg_blender.py
index bdce32af..01c62538 100644
--- a/blenderkit/bg_blender.py
+++ b/blenderkit/bg_blender.py
@@ -56,6 +56,9 @@ def threadread(tcom):
fills the data, dies.'''
found = False
while not found:
+ if tcom.proc.poll() is not None:
+ #process terminated
+ return
inline = tcom.proc.stdout.readline()
# print('readthread', time.time())
inline = str(inline)
@@ -111,7 +114,15 @@ def bg_update():
global bg_processes
if len(bg_processes) == 0:
return 2
+ #cleanup dead processes first
+ remove_processes = []
+ for p in bg_processes:
+ if p[1].proc.poll() is not None:
+ remove_processes.append(p)
+ for p in remove_processes:
+ bg_processes.remove(p)
+ #Parse process output
for p in bg_processes:
# proc=p[1].proc
readthread = p[0]
@@ -119,26 +130,29 @@ def bg_update():
if not readthread.is_alive():
readthread.join()
# readthread.
+ estring = None
if tcom.error:
estring = tcom.eval_path_computing + ' = False'
- exec(estring)
-
tcom.lasttext = tcom.outtext
if tcom.outtext != '':
tcom.outtext = ''
text =tcom.lasttext.replace("'","")
estring = tcom.eval_path_state + ' = text'
-
- exec(estring)
# print(tcom.lasttext)
if 'finished successfully' in tcom.lasttext:
bg_processes.remove(p)
estring = tcom.eval_path_computing + ' = False'
- exec(estring)
else:
readthread = threading.Thread(target=threadread, args=([tcom]), daemon=True)
readthread.start()
p[0] = readthread
+ if estring:
+ try:
+ exec(estring)
+ except Exception as e:
+ print('Exception while reading from background process')
+ print(e)
+
# if len(bg_processes) == 0:
# bpy.app.timers.unregister(bg_update)
if len(bg_processes) > 0:
diff --git a/blenderkit/blendfiles/material_thumbnailer_cycles.blend b/blenderkit/blendfiles/material_thumbnailer_cycles.blend
index 23df6817..1faa5807 100644
--- a/blenderkit/blendfiles/material_thumbnailer_cycles.blend
+++ b/blenderkit/blendfiles/material_thumbnailer_cycles.blend
Binary files differ
diff --git a/blenderkit/categories.py b/blenderkit/categories.py
index 2da830cb..f91fe512 100644
--- a/blenderkit/categories.py
+++ b/blenderkit/categories.py
@@ -17,7 +17,7 @@
# ##### END GPL LICENSE BLOCK #####
-from blenderkit import paths, utils, tasks_queue, rerequests
+from blenderkit import paths, utils, tasks_queue, rerequests, ui, colors
import requests
import json
@@ -233,7 +233,9 @@ def fetch_categories(API_key, force=False):
json.dump(categories, s, ensure_ascii=False, indent=4)
tasks_queue.add_task((load_categories, ()))
except Exception as e:
- bk_logger.debug('category fetching failed')
+ t = 'BlenderKit failed to download fresh categories from the server'
+ tasks_queue.add_task((ui.add_report,(t, 15, colors.RED)))
+ bk_logger.debug(t)
bk_logger.exception(e)
if not os.path.exists(categories_filepath):
source_path = paths.get_addon_file(subpath='data' + os.sep + 'categories.json')
diff --git a/blenderkit/download.py b/blenderkit/download.py
index 5d14ee41..eca35c41 100644
--- a/blenderkit/download.py
+++ b/blenderkit/download.py
@@ -492,6 +492,8 @@ def append_asset(asset_data, **kwargs): # downloaders=[], location=None,
udpate_asset_data_in_dicts(asset_data)
asset_main['asset_data'] = asset_data # TODO remove this??? should write to blenderkit Props?
+ asset_main.blenderkit.asset_base_id = asset_data['assetBaseId']
+ asset_main.blenderkit.id = asset_data['id']
bpy.ops.wm.undo_push_context(message='add %s to scene' % asset_data['name'])
# moving reporting to on save.
# report_use_success(asset_data['id'])
@@ -683,7 +685,7 @@ def delete_unfinished_file(file_name):
return
-def download_file(asset_data, resolution='blend'):
+def download_asset_file(asset_data, resolution='blend', api_key = ''):
# this is a simple non-threaded way to download files for background resolution genenration tool
file_name = paths.get_download_filepaths(asset_data, resolution)[0] # prefer global dir if possible.
@@ -691,14 +693,11 @@ def download_file(asset_data, resolution='blend'):
# this sends the thread for processing, where another check should occur, since the file might be corrupted.
bk_logger.debug('not downloading, already in db')
return file_name
- preferences = bpy.context.preferences.addons['blenderkit'].preferences
- api_key = preferences.api_key
download_canceled = False
with open(file_name, "wb") as f:
print("Downloading %s" % file_name)
- headers = utils.get_headers(api_key)
res_file_info, resolution = paths.get_res_file(asset_data, resolution)
response = requests.get(res_file_info['url'], stream=True)
total_length = response.headers.get('Content-Length')
@@ -1110,18 +1109,18 @@ def get_download_url(asset_data, scene_id, api_key, tcom=None, resolution='blend
tasks_queue.add_task((ui.add_report, (str(r), 10, colors.RED)))
if r.status_code == 403:
- r = 'You need Full plan to get this item.'
+ report = 'You need Full plan to get this item.'
# r1 = 'All materials and brushes are available for free. Only users registered to Standard plan can use all models.'
# tasks_queue.add_task((ui.add_report, (r1, 5, colors.RED)))
if tcom is not None:
- tcom.report = r
+ tcom.report = report
tcom.error = True
if r.status_code == 404:
- r = 'Url not found - 404.'
+ report = 'Url not found - 404.'
# r1 = 'All materials and brushes are available for free. Only users registered to Standard plan can use all models.'
if tcom is not None:
- tcom.report = r
+ tcom.report = report
tcom.error = True
elif r.status_code >= 500:
diff --git a/blenderkit/icons.py b/blenderkit/icons.py
index 5d877b25..45c729df 100644
--- a/blenderkit/icons.py
+++ b/blenderkit/icons.py
@@ -51,6 +51,7 @@ def register_icons():
# iprev.image_pixels_float = img.pixels[:]
icon_collections["main"] = pcoll
+ icon_collections["previews"] = bpy.utils.previews.new()
def unregister_icons():
diff --git a/blenderkit/resolutions.py b/blenderkit/resolutions.py
index b2a5e806..1a4af5fc 100644
--- a/blenderkit/resolutions.py
+++ b/blenderkit/resolutions.py
@@ -410,7 +410,7 @@ def regenerate_thumbnail_material(data):
# preferences = bpy.context.preferences.addons['blenderkit'].preferences
# layout.prop(preferences, "thumbnail_use_gpu")
# TODO: here it should call start_material_thumbnailer , but with the wait property on, so it can upload afterwards.
- bpy.ops.object.blenderkit_material_thumbnail()
+ bpy.ops.object.blenderkit_generate_material_thumbnail()
time.sleep(130)
# save
# this does the actual job
@@ -525,7 +525,7 @@ def download_asset(asset_data, resolution='blend', unpack=False, api_key=''):
has_url = download.get_download_url(asset_data, download.get_scene_id(), api_key, tcom=None,
resolution='blend')
if has_url:
- fpath = download.download_file(asset_data)
+ fpath = download.download_asset_file(asset_data, api_key = api_key)
if fpath and unpack and asset_data['assetType'] != 'hdr':
send_to_bg(asset_data, fpath, command='unpack', wait=True)
return fpath
diff --git a/blenderkit/search.py b/blenderkit/search.py
index d802973b..1465d103 100644
--- a/blenderkit/search.py
+++ b/blenderkit/search.py
@@ -48,7 +48,7 @@ import copy
import json
import math
import unicodedata
-
+import queue
import logging
bk_logger = logging.getLogger('blenderkit')
@@ -75,7 +75,7 @@ def check_errors(rdata):
search_threads = []
thumb_sml_download_threads = {}
thumb_full_download_threads = {}
-reports = ''
+reports_queue = queue.Queue()
rtips = ['Click or drag model or material in scene to link/append ',
"Please rate responsively and plentifully. This helps us distribute rewards to the authors.",
@@ -412,9 +412,10 @@ def timer_update():
wm[search_name] = []
- global reports
- if reports != '':
- props.report = str(reports)
+ global reports_queue
+
+ while not reports_queue.empty():
+ props.report = str(reports_queue.get())
return .2
rdata = thread[0].result
@@ -628,7 +629,7 @@ def generate_tooltip(mdata):
t += 'Size: %s x %s x %sm\n' % (fmt_length(mparams['dimensionX']),
fmt_length(mparams['dimensionY']),
fmt_length(mparams['dimensionZ']))
- if has(mparams, 'faceCount'):
+ if has(mparams, 'faceCount') and mdata['assetType'] == 'model':
t += 'Face count: %s\n' % (mparams['faceCount'])
# t += 'face count: %s, render: %s\n' % (mparams['faceCount'], mparams['faceCountRender'])
@@ -709,7 +710,7 @@ def generate_tooltip(mdata):
show_rating_threshold = 5
- if rcount < show_rating_threshold:
+ if rcount < show_rating_threshold and mdata['assetType'] != 'hdr':
t += f"Only assets with enough ratings \nshow the rating value. Please rate.\n"
if rc['quality'] >= show_rating_threshold:
# t += f"{int(mdata['ratingsAverage']['quality']) * '*'}\n"
@@ -765,26 +766,6 @@ def generate_author_textblock(adata):
return t
-def get_items_models(self, context):
- global search_items_models
- return search_items_models
-
-
-def get_items_brushes(self, context):
- global search_items_brushes
- return search_items_brushes
-
-
-def get_items_materials(self, context):
- global search_items_materials
- return search_items_materials
-
-
-def get_items_textures(self, context):
- global search_items_textures
- return search_items_textures
-
-
class ThumbDownloader(threading.Thread):
query = None
@@ -801,7 +782,12 @@ class ThumbDownloader(threading.Thread):
return self._stop_event.is_set()
def run(self):
- r = rerequests.get(self.url, stream=False)
+ print('thumb downloader', self.url)
+ try:
+ r = requests.get(self.url, stream=False)
+ except Exception as e:
+ bk_logger.error('Thumbnail download failed')
+ bk_logger.error(str(e))
if r.status_code == 200:
with open(self.path, 'wb') as f:
f.write(r.content)
@@ -962,6 +948,11 @@ def query_to_url(query = {}, params = {}):
urlquery = url + requeststring
return urlquery
+def parse_html_formated_error(text):
+ report = text[text.find('<title>') + 7: text.find('</title>')]
+
+ return report
+
class Searcher(threading.Thread):
query = None
@@ -982,10 +973,12 @@ class Searcher(threading.Thread):
return self._stop_event.is_set()
def run(self):
+ global reports_queue
+
maxthreads = 50
query = self.query
params = self.params
- global reports
+
t = time.time()
mt('search thread started')
@@ -999,21 +992,21 @@ class Searcher(threading.Thread):
try:
utils.p(self.urlquery)
r = rerequests.get(self.urlquery, headers=self.headers) # , params = rparameters)
- # print(r.url)
- reports = ''
- # utils.p(r.text)
except requests.exceptions.RequestException as e:
bk_logger.error(e)
- reports = e
- # props.report = e
+ reports_queue.put(str(e))
return
+
mt('search response is back ')
try:
rdata = r.json()
except Exception as e:
- reports = r.text
- bk_logger.error(e)
+ error_description = parse_html_formated_error(r.text)
+ reports_queue.put(error_description)
+ tasks_queue.add_task((ui.add_report, (error_description, 10, colors.RED)))
+ bk_logger.error(e)
+ return
mt('data parsed ')
if not rdata.get('results'):
utils.pprint(rdata)
diff --git a/blenderkit/ui.py b/blenderkit/ui.py
index 7969bea2..7117055f 100644
--- a/blenderkit/ui.py
+++ b/blenderkit/ui.py
@@ -655,7 +655,10 @@ def draw_callback_2d_progress(self, context):
for process in bg_blender.bg_processes:
tcom = process[1]
- draw_progress(x, y - index * 30, '%s' % tcom.lasttext,
+ n=''
+ if tcom.name is not None:
+ n = tcom.name +': '
+ draw_progress(x, y - index * 30, '%s' % n+tcom.lasttext,
tcom.progress)
index += 1
global reports
@@ -1873,6 +1876,11 @@ class AssetBarOperator(bpy.types.Operator):
context.window_manager.modal_handler_add(self)
ui_props.assetbar_on = True
+
+ #in an exceptional case these were accessed before drag start.
+ self.drag_start_x = 0
+ self.drag_start_y = 0
+
return {'RUNNING_MODAL'}
def execute(self, context):
@@ -2151,6 +2159,9 @@ class RunAssetBarWithContext(bpy.types.Operator):
bl_description = "Run assetbar with fixed context"
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
+ keep_running: BoolProperty(name="Keep Running", description='', default=True, options={'SKIP_SAVE'})
+ do_search: BoolProperty(name="Run Search", description='', default=False, options={'SKIP_SAVE'})
+
# def modal(self, context, event):
# return {'RUNNING_MODAL'}
@@ -2159,11 +2170,11 @@ class RunAssetBarWithContext(bpy.types.Operator):
if C_dict.get('window'): # no 3d view, no asset bar.
preferences = bpy.context.preferences.addons['blenderkit'].preferences
if preferences.experimental_features:
- bpy.ops.view3d.blenderkit_asset_bar_widget(C_dict, 'INVOKE_REGION_WIN', keep_running=True,
- do_search=False)
+ bpy.ops.view3d.blenderkit_asset_bar_widget(C_dict, 'INVOKE_REGION_WIN', keep_running=self.keep_running,
+ do_search=self.do_search)
else:
- bpy.ops.view3d.blenderkit_asset_bar(C_dict, 'INVOKE_REGION_WIN', keep_running=True, do_search=False)
+ bpy.ops.view3d.blenderkit_asset_bar(C_dict, 'INVOKE_REGION_WIN', keep_running=self.keep_running, do_search=self.do_search)
return {'FINISHED'}
@@ -2208,7 +2219,7 @@ def register_ui():
return
km = wm.keyconfigs.addon.keymaps.new(name="Window", space_type='EMPTY')
# asset bar shortcut
- kmi = km.keymap_items.new(AssetBarOperator.bl_idname, 'SEMI_COLON', 'PRESS', ctrl=False, shift=False)
+ kmi = km.keymap_items.new("object.run_assetbar_fix_context", 'SEMI_COLON', 'PRESS', ctrl=False, shift=False)
kmi.properties.keep_running = False
kmi.properties.do_search = False
addon_keymapitems.append(kmi)
diff --git a/blenderkit/ui_bgl.py b/blenderkit/ui_bgl.py
index 2165cdc5..fabda329 100644
--- a/blenderkit/ui_bgl.py
+++ b/blenderkit/ui_bgl.py
@@ -77,6 +77,8 @@ def draw_rect_3d(coords, color):
def draw_image(x, y, width, height, image, transparency, crop=(0, 0, 1, 1)):
# draw_rect(x,y, width, height, (.5,0,0,.5))
+ if not image:
+ return;
coords = [
(x, y), (x + width, y),
diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py
index 0dbeaf89..6bacc895 100644
--- a/blenderkit/ui_panels.py
+++ b/blenderkit/ui_panels.py
@@ -17,7 +17,8 @@
# ##### END GPL LICENSE BLOCK #####
-from blenderkit import paths, ratings, utils, download, categories, icons, search, resolutions, ui
+from blenderkit import paths, ratings, utils, download, categories, icons, search, resolutions, ui, tasks_queue, \
+ autothumb
from bpy.types import (
Panel
@@ -35,8 +36,11 @@ from bpy.props import (
import bpy
import os
import random
+import logging
import blenderkit
+bk_logger = logging.getLogger('blenderkit')
+
# this was moved to separate interface:
@@ -153,6 +157,7 @@ def draw_upload_common(layout, props, asset_type, context):
layout.prop(props, 'description')
layout.prop(props, 'tags')
+
def poll_local_panels():
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
return user_preferences.panel_behaviour == 'BOTH' or user_preferences.panel_behaviour == 'LOCAL'
@@ -189,7 +194,6 @@ def draw_panel_hdr_upload(self, context):
draw_upload_common(layout, props, 'HDR', context)
-
def draw_panel_hdr_search(self, context):
s = context.scene
props = s.blenderkit_HDR
@@ -203,6 +207,15 @@ def draw_panel_hdr_search(self, context):
utils.label_multiline(layout, text=props.report)
+
+def draw_thumbnail_upload_panel(layout, props):
+ update = False
+ tex = autothumb.get_texture_ui(props.thumbnail, '.upload_preview')
+ if not tex or not tex.image:
+ return
+ box = layout.box()
+ box.template_icon(icon_value=tex.image.preview.icon_id, scale=6.0)
+
def draw_panel_model_upload(self, context):
ob = bpy.context.active_object
while ob.parent is not None:
@@ -216,6 +229,9 @@ def draw_panel_model_upload(self, context):
col = layout.column()
if props.is_generating_thumbnail:
col.enabled = False
+
+ draw_thumbnail_upload_panel(col, props)
+
prop_needed(col, props, 'thumbnail', props.thumbnail)
if bpy.context.scene.render.engine in ('CYCLES', 'BLENDER_EEVEE'):
col.operator("object.blenderkit_generate_thumbnail", text='Generate thumbnail', icon='IMAGE')
@@ -275,6 +291,8 @@ def draw_panel_scene_upload(self, context):
col = layout.column()
# if props.is_generating_thumbnail:
# col.enabled = False
+ draw_thumbnail_upload_panel(col, props)
+
prop_needed(col, props, 'thumbnail', props.has_thumbnail, False)
# if bpy.context.scene.render.engine == 'CYCLES':
# col.operator("object.blenderkit_generate_thumbnail", text='Generate thumbnail', icon='IMAGE_COL')
@@ -289,8 +307,6 @@ def draw_panel_scene_upload(self, context):
# elif props.thumbnail_generating_state != '':
# utils.label_multiline(layout, text = props.thumbnail_generating_state)
-
-
layout.prop(props, 'style')
layout.prop(props, 'production_level')
layout.prop(props, 'use_design_year')
@@ -621,15 +637,16 @@ def draw_panel_material_upload(self, context):
draw_upload_common(layout, props, 'MATERIAL', context)
# THUMBNAIL
- row = layout.row()
+ row = layout.column()
if props.is_generating_thumbnail:
row.enabled = False
- prop_needed(row, props, 'thumbnail', props.has_thumbnail, False)
+ draw_thumbnail_upload_panel(row, props)
+ prop_needed(row, props, 'thumbnail', props.has_thumbnail, False)
if bpy.context.scene.render.engine in ('CYCLES', 'BLENDER_EEVEE'):
- layout.operator("object.blenderkit_material_thumbnail", text='Render thumbnail with Cycles', icon='EXPORT')
+ layout.operator("object.blenderkit_generate_material_thumbnail", text='Render thumbnail with Cycles', icon='EXPORT')
if props.is_generating_thumbnail:
row = layout.row(align=True)
row.label(text=props.thumbnail_generating_state, icon='RENDER_STILL')
@@ -653,8 +670,6 @@ def draw_panel_material_upload(self, context):
layout.prop(props, 'animated')
layout.prop(props, 'texture_size_meters')
-
-
# tname = "." + bpy.context.active_object.active_material.name + "_thumbnail"
# if props.has_thumbnail and bpy.data.textures.get(tname) is not None:
# row = layout.row()
@@ -971,15 +986,15 @@ class VIEW3D_PT_blenderkit_unified(Panel):
if ui_props.asset_type_fold:
expand_icon = 'TRIA_RIGHT'
row = layout.row()
- split = row.split(factor = 0.15)
- split.prop(ui_props, 'asset_type_fold', icon = expand_icon, icon_only = True, emboss = False)
+ split = row.split(factor=0.15)
+ split.prop(ui_props, 'asset_type_fold', icon=expand_icon, icon_only=True, emboss=False)
if ui_props.asset_type_fold:
pass
- #expanded interface with names in column
+ # expanded interface with names in column
split = split.row()
split.scale_x = 8
- split.scale_y =1.6
+ split.scale_y = 1.6
# split = row
# split = layout.row()
else:
@@ -1160,10 +1175,9 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False):
# build search string from description and tags:
op.keywords = asset_data['name']
if asset_data.get('description'):
- op.keywords += ' ' + asset_data.get('description')+' '
+ op.keywords += ' ' + asset_data.get('description') + ' '
op.keywords += ' '.join(asset_data.get('tags'))
-
if asset_data.get('canDownload') != 0:
if len(bpy.context.selected_objects) > 0 and ui_props.asset_type == 'MODEL':
aob = bpy.context.active_object
@@ -1228,7 +1242,7 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False):
0) # str(utils.get_param(asset_data, 'textureResolutionMax'))
elif asset_data['assetBaseId'] in s['assets used'].keys() and asset_data['assetType'] != 'hdr':
- #HDRs are excluded from replacement, since they are always replaced.
+ # HDRs are excluded from replacement, since they are always replaced.
# called from asset bar:
print('context menu')
op = col.operator('scene.blenderkit_download', text='Replace asset resolution')
@@ -1287,6 +1301,16 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False):
op.asset_id = asset_data['id']
op.asset_type = asset_data['assetType']
+ if asset_data['assetType'] == 'model':
+ op = layout.operator('object.blenderkit_regenerate_thumbnail', text='Regenerate thumbnail')
+ op.asset_index = ui_props.active_index
+
+ if asset_data['assetType'] == 'material':
+ op = layout.operator('object.blenderkit_regenerate_material_thumbnail', text='Regenerate thumbnail')
+ op.asset_index = ui_props.active_index
+ # op.asset_id = asset_data['id']
+ # op.asset_type = asset_data['assetType']
+
if author_id == str(profile['user']['id']):
row = layout.row()
row.operator_context = 'INVOKE_DEFAULT'
diff --git a/blenderkit/upload.py b/blenderkit/upload.py
index b2395f68..cc72b92a 100644
--- a/blenderkit/upload.py
+++ b/blenderkit/upload.py
@@ -659,6 +659,11 @@ class FastMetadata(bpy.types.Operator):
update=update_free_full
)
+
+ ####################
+
+
+
@classmethod
def poll(cls, context):
scene = bpy.context.scene
@@ -739,6 +744,7 @@ class FastMetadata(bpy.types.Operator):
self.subcategory = cat_path[2]
except Exception as e:
print(e)
+
self.message = f"Fast edit metadata of {asset_data['name']}"
self.name = asset_data['displayName']
self.description = asset_data['description']
diff --git a/blenderkit/upload_bg.py b/blenderkit/upload_bg.py
index 3307a72a..1b91d51b 100644
--- a/blenderkit/upload_bg.py
+++ b/blenderkit/upload_bg.py
@@ -93,8 +93,9 @@ def upload_file(upload_data, f):
uploaded = True
upload_done_url = paths.get_api_url() + 'uploads_s3/' + upload['id'] + '/upload-file/'
upload_response = rerequests.post(upload_done_url, headers=headers, verify=True)
- print(upload_response)
- tasks_queue.add_task((ui.add_report, (f"Finished file upload{os.path.basename(f['file_path'])}",)))
+ # print(upload_response)
+ # print(upload_response.text)
+ tasks_queue.add_task((ui.add_report, (f"Finished file upload: {os.path.basename(f['file_path'])}",)))
return True
else:
print(upload_response.text)
diff --git a/blenderkit/utils.py b/blenderkit/utils.py
index 7abc07d5..953ded5e 100644
--- a/blenderkit/utils.py
+++ b/blenderkit/utils.py
@@ -326,14 +326,14 @@ def uploadable_asset_poll():
return True
-def get_hidden_texture(img, force_reload=False):
- # i = get_hidden_image(tpath, bdata_name, force_reload=force_reload)
- # bdata_name = f".{bdata_name}"
- t = bpy.data.textures.get(img.name)
+def get_hidden_texture(name, force_reload=False):
+ t = bpy.data.textures.get(name)
if t is None:
- t = bpy.data.textures.new(img.name, 'IMAGE')
- if t.image != img:
- t.image = img
+ t = bpy.data.textures.new(name, 'IMAGE')
+ if not t.image or t.image.name != name:
+ img = bpy.data.images.get(name)
+ if img:
+ t.image = img
return t
@@ -691,15 +691,15 @@ def name_update(props):
asset.name = fname
-def get_param(asset_data, parameter_name):
+def get_param(asset_data, parameter_name, default = None):
if not asset_data.get('parameters'):
# this can appear in older version files.
- return None
+ return default
for p in asset_data['parameters']:
if p.get('parameterType') == parameter_name:
return p['value']
- return None
+ return default
def params_to_dict(params):