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
This commit is contained in:
Vilém Duha 2021-04-26 17:48:18 +02:00
parent 4cb833e84a
commit e7acb93c89
19 changed files with 787 additions and 254 deletions

View File

@ -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",
)

View File

@ -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

View File

@ -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)

View File

@ -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)
ext = '.blend'
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)
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):
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")
#
# 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")
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'}
# 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)

View File

@ -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')

View File

@ -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

View File

@ -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:

View File

@ -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')

View File

@ -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:

View File

@ -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():

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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),

View File

@ -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
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'

View File

@ -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']

View File

@ -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)

View File

@ -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):