Newer
Older
bpy.ops.object.select_all(action='DESELECT')
# check visibility
obs = utils.get_hierarchy(source)
if not check_all_visible(obs):
return None
# check selectability and select in one run
if not check_selectible(obs):
return None
# duplicate the asset objects
bpy.ops.object.duplicate(linked=True)
nobs = bpy.context.selected_objects[:]
for ob in nobs:
if ob.parent not in nobs:
# in case of replacement,there might be a paarent relationship that can be restored
if kwargs.get('parent'):
parent = bpy.data.objects[kwargs['parent']]
asset_main.parent = parent # even if parent is None this is ok without if condition
else:
asset_main.parent = None
# restore original selection
utils.selection_set(sel)
'''checks if the asset is already in scene. If yes, modifies asset data so the asset can be reached again.'''
scene = bpy.context.scene
au = scene.get('assets used', {})
id = asset_data['assetBaseId']
if id in au.keys():
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
if ad.get('files'):
for fi in ad['files']:
if fi.get('file_name') != None:
for fi1 in asset_data['files']:
if fi['fileType'] == fi1['fileType']:
fi1['file_name'] = fi['file_name']
fi1['url'] = fi['url']
# browse all collections since linked collections can have same name.
if asset_data['assetType'] == 'MODEL':
for c in bpy.data.collections:
if c.name == ad['name']:
# there can also be more linked collections with same name, we need to check id.
if c.library and c.library.get('asset_data') and c.library['asset_data'][
'assetBaseId'] == id:
print('asset linked')
return 'LINKED', ad.get('resolution')
elif asset_data['assetType'] == 'MATERIAL':
for m in bpy.data.materials:
if not m.get('asset_data'):
continue
if m['asset_data']['assetBaseId'] == asset_data[
'assetBaseId'] and bpy.context.active_object.active_material.library:
return 'LINKED', ad.get('resolution')
print('asset appended')
return 'APPENDED', ad.get('resolution')
return False, None
def fprint(text):
print('###################################################################################')
print('\n\n\n')
print(text)
print('\n\n\n')
print('###################################################################################')
def get_download_url(asset_data, scene_id, api_key, tcom=None, resolution='blend'):
''''retrieves the download url. The server checks if user can download the item.'''
mt = time.time()
headers = utils.get_headers(api_key)
res_file_info, resolution = paths.get_res_file(asset_data, resolution)
r = rerequests.get(res_file_info['downloadUrl'], params=data, headers=headers)
except Exception as e:
print(e)
if tcom is not None:
tcom.error = True
if r == None:
if tcom is not None:
tcom.report = 'Connection Error'
tcom.error = True
if r.status_code < 400:
data = r.json()
url = data['filePath']
res_file_info['url'] = url
res_file_info['file_name'] = paths.extract_filename_from_url(url)
# print(res_file_info, url)
print(url)
# let's print it into UI
tasks_queue.add_task((ui.add_report, (str(r), 10, colors.RED)))
# 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)))
# r1 = 'All materials and brushes are available for free. Only users registered to Standard plan can use all models.'
if tcom is not None:
# bk_logger.debug(r.text)
if tcom is not None:
tcom.report = 'Server error'
tcom.error = True
def start_download(asset_data, **kwargs):
'''
check if file isn't downloading or doesn't exist, then start new download
'''
# first check if the asset is already in scene. We can use that asset without checking with server
ain, resolution = asset_in_scene(asset_data)
# quota_ok = ain is not False
# if resolution:
# kwargs['resolution'] = resolution
# otherwise, check on server
s = bpy.context.scene
done = False
# is the asseet being currently downloaded?
downloading = check_downloading(asset_data, **kwargs)
if not downloading:
# check if there are files already. This check happens 2x once here(for free assets),
fexists = check_existing(asset_data, resolution=kwargs['resolution'])
bk_logger.debug('does file exist?' + str(fexists))
bk_logger.debug('asset is in scene' + str(ain))
if ain and not kwargs.get('replace_resolution'):
# this goes to appending asset - where it should duplicate the original asset already in scene.
done = try_finished_append(asset_data, **kwargs)
# else:
# props = utils.get_search_props()
# props.report = str('asset ')
if not done:
at = asset_data['assetType']
if at in ('model', 'material'):
downloader = {'location': kwargs['model_location'],
'rotation': kwargs['model_rotation']}
download(asset_data, downloaders=[downloader], **kwargs)
asset_types = (
('MODEL', 'Model', 'set of objects'),
('SCENE', 'Scene', 'scene'),
('MATERIAL', 'Material', 'any .blend Material'),
('TEXTURE', 'Texture', 'a texture, or texture set'),
('BRUSH', 'Brush', 'brush, can be any type of blender brush'),
('ADDON', 'Addon', 'addnon'),
)
class BlenderkitKillDownloadOperator(bpy.types.Operator):
bl_idname = "scene.blenderkit_download_kill"
bl_label = "BlenderKit Kill Asset Download"
thread_index: IntProperty(name="Thread index", description='index of the thread to kill', default=-1)
def execute(self, context):
global download_threads
td = download_threads[self.thread_index]
download_threads.remove(td)
td[0].stop()
return {'FINISHED'}
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
def available_resolutions_callback(self, context):
'''
Returns
checks active asset for available resolutions and offers only those available
TODO: this currently returns always the same list of resolutions, make it actually work
'''
# print('callback called', self.asset_data)
pat_items = (
('512', '512', '', 1),
('1024', '1024', '', 2),
('2048', '2048', '', 3),
('4096', '4096', '', 4),
('8192', '8192', '', 5),
)
items = []
for item in pat_items:
if int(self.max_resolution) >= int(item[0]):
items.append(item)
items.append(('ORIGINAL', 'Original', '', 6))
return items
def show_enum_values(obj, prop_name):
print([item.identifier for item in obj.bl_rna.properties[prop_name].enum_items])
class BlenderkitDownloadOperator(bpy.types.Operator):
"""Download and link asset to scene. Only link if asset already available locally"""
# asset_type: EnumProperty(
# name="Type",
# items=asset_types,
# description="Type of download",
# default="MODEL",
# )
asset_index: IntProperty(name="Asset Index", description='asset index in search results', default=-1)
asset_base_id: StringProperty(
name="Asset base Id",
description="Asset base id, used instead of search result index",
name="Target Object",
description="Material or object target for replacement",
material_target_slot: IntProperty(name="Asset Index", description='asset index in search results', default=0)
model_location: FloatVectorProperty(name='Asset Location', default=(0, 0, 0))
model_rotation: FloatVectorProperty(name='Asset Rotation', default=(0, 0, 0))
replace: BoolProperty(name='Replace', description='replace selection with the asset', default=False)
replace_resolution: BoolProperty(name='Replace resolution', description='replace resolution of the active asset',
default=False)
invoke_resolution: BoolProperty(name='Replace resolution popup',
description='pop up to ask which resolution to download', default=False)
invoke_scene_settings: BoolProperty(name='Scene import settings popup',
description='pop up scene import settings', default=False)
resolution: EnumProperty(
items=available_resolutions_callback,
default=0,
description='Replace resolution'
)
#needs to be passed to the operator to not show all resolution possibilities
max_resolution: IntProperty(
name="Max resolution",
description="",
default=0)
# has_res_0_5k: BoolProperty(name='512',
# description='', default=False)
cast_parent: StringProperty(
name="Particles Target Object",
description="",
default="")
# close_window: BoolProperty(name='Close window',
# description='Try to close the window below mouse before download',
# default=False)
# @classmethod
# def poll(cls, context):
# return bpy.context.window_manager.BlenderKitModelThumbnails is not ''
def get_asset_data(self, context):
# get asset data - it can come from scene, or from search results.
if self.asset_index > -1:
# either get the data from search results
sr = bpy.context.window_manager['search results']
asset_data = sr[
self.asset_index].to_dict() # TODO CHECK ALL OCCURRENCES OF PASSING BLENDER ID PROPS TO THREADS!
asset_base_id = asset_data['assetBaseId']
else:
# or from the scene.
asset_base_id = self.asset_base_id
au = s.get('assets used')
if au == None:
s['assets used'] = {}
if asset_base_id in s.get('assets used'):
# already used assets have already download link and especially file link.
asset_data = s['assets used'][asset_base_id].to_dict()
return asset_data
def execute(self, context):
sprops = utils.get_search_props()
self.asset_data = self.get_asset_data(context)
# print('after getting asset data')
# print(self.asset_base_id)
atype = self.asset_data['assetType']
atype == 'model' or atype == 'material') and bpy.context.view_layer.objects.active is not None:
if self.resolution == 0 or self.resolution == '':
resolution = sprops.resolution
else:
resolution = self.resolution
resolution = resolutions.resolution_props_to_server[resolution]
if self.replace: # cleanup first, assign later.
obs = utils.get_selected_replace_adepts()
for ob in obs:
# this is for a case when replace is called from a panel,
# this uses active object as replacement source instead of target.
if ob.get('asset_data') is not None and \
(ob['asset_data']['assetBaseId'] == self.asset_base_id and ob['asset_data'][
'resolution'] == resolution):
# print('skipping this one')
parent = ob.parent
if parent:
parent = ob.parent.name # after this, parent is either name or None.
kwargs = {
'cast_parent': self.cast_parent,
'target_object': ob.name,
'material_target_slot': ob.active_material_index,
'model_location': tuple(ob.matrix_world.translation),
'model_rotation': tuple(ob.matrix_world.to_euler()),
'replace': True,
'replace_resolution': False,
'parent': parent,
'resolution': resolution
# TODO - move this After download, not before, so that the replacement
utils.delete_hierarchy(ob)
# replace resolution needs to replace all instances of the resolution in the scene
# and deleting originals has to be thus done after the downlaod
kwargs = {
'cast_parent': self.cast_parent,
'target_object': self.target_object,
'material_target_slot': self.material_target_slot,
'model_location': tuple(self.model_location),
'model_rotation': tuple(self.model_rotation),
'replace': False,
'replace_resolution': self.replace_resolution,
'resolution': resolution
def draw(self, context):
layout = self.layout
if self.invoke_resolution:
layout.prop(self, 'resolution', expand=True, icon_only=False)
if self.invoke_scene_settings:
ui_panels.draw_scene_import_settings(self, context)
# if self.close_window:
# context.window.cursor_warp(event.mouse_x-1000, event.mouse_y - 1000);
print(self.asset_base_id)
wm = context.window_manager
# only make a pop up in case of switching resolutions
if self.invoke_resolution:
# show_enum_values(self, 'resolution')
self.asset_data = self.get_asset_data(context)
sprops = utils.get_search_props()
#set initial resolutions enum activation
if sprops.resolution != 'ORIGINAL' and int(sprops.resolution) <= int(self.max_resolution):
self.resolution = sprops.resolution
elif int(self.max_resolution) > 0:
else:
self.resolution = 'ORIGINAL'
return wm.invoke_props_dialog(self)
if self.invoke_scene_settings:
return wm.invoke_props_dialog(self)
# if self.close_window:
# time.sleep(0.1)
# context.area.tag_redraw()
# time.sleep(0.1)
#
# context.window.cursor_warp(event.mouse_x, event.mouse_y);
return self.execute(context)
def register_download():
bpy.utils.register_class(BlenderkitDownloadOperator)
bpy.utils.register_class(BlenderkitKillDownloadOperator)
bpy.app.handlers.load_post.append(scene_load)
bpy.app.handlers.save_pre.append(scene_save)
user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
if user_preferences.use_timers:
bpy.app.timers.register(download_timer)
def unregister_download():
bpy.utils.unregister_class(BlenderkitDownloadOperator)
bpy.utils.unregister_class(BlenderkitKillDownloadOperator)
bpy.app.handlers.load_post.remove(scene_load)
bpy.app.handlers.save_pre.remove(scene_save)
if bpy.app.timers.is_registered(download_timer):
bpy.app.timers.unregister(download_timer)