diff options
author | Peter Kim <pk15950@gmail.com> | 2021-07-28 07:09:58 +0300 |
---|---|---|
committer | Peter Kim <pk15950@gmail.com> | 2021-07-28 07:09:58 +0300 |
commit | 62da286fc85e2bf775a2c4e98056e2b07877f9aa (patch) | |
tree | eaaafe95092f22f9fbec46d882855b8d0c504cd2 | |
parent | 45c234f953f336b07e4dcd34aca30ba783c74591 (diff) | |
parent | 69ad4c43d004c9788ba98e75ade02097284a25f9 (diff) |
Merge branch 'master' into xr-controller-support
-rw-r--r-- | blenderkit/__init__.py | 22 | ||||
-rw-r--r-- | blenderkit/asset_bar_op.py | 2 | ||||
-rw-r--r-- | blenderkit/download.py | 8 | ||||
-rw-r--r-- | blenderkit/search.py | 246 | ||||
-rw-r--r-- | blenderkit/thumbnails/thumbnail_notready.jpg | bin | 3663 -> 2999 bytes | |||
-rw-r--r-- | blenderkit/ui.py | 89 | ||||
-rw-r--r-- | blenderkit/ui_bgl.py | 12 | ||||
-rw-r--r-- | blenderkit/ui_panels.py | 68 | ||||
-rw-r--r-- | blenderkit/utils.py | 41 |
9 files changed, 258 insertions, 230 deletions
diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py index 37e91a0a..ec2268dc 100644 --- a/blenderkit/__init__.py +++ b/blenderkit/__init__.py @@ -1704,7 +1704,7 @@ class BlenderKitAddonPreferences(AddonPreferences): max_assetbar_rows: IntProperty(name="Max Assetbar Rows", description="max rows of assetbar in the 3D view", default=1, - min=0, + min=1, max=20) thumb_size: IntProperty(name="Assetbar thumbnail Size", default=96, min=-1, max=256) @@ -1841,7 +1841,7 @@ def register(): type=BlenderKitUIProps) # MODELS - bpy.types.Scene.blenderkit_models = PointerProperty( + bpy.types.WindowManager.blenderkit_models = PointerProperty( type=BlenderKitModelSearchProps) bpy.types.Object.blenderkit = PointerProperty( # for uploads, not now... type=BlenderKitModelUploadProps) @@ -1849,7 +1849,7 @@ def register(): type=BlenderKitRatingProps) # SCENES - bpy.types.Scene.blenderkit_scene = PointerProperty( + bpy.types.WindowManager.blenderkit_scene = PointerProperty( type=BlenderKitSceneSearchProps) bpy.types.Scene.blenderkit = PointerProperty( # for uploads, not now... type=BlenderKitSceneUploadProps) @@ -1857,7 +1857,7 @@ def register(): type=BlenderKitRatingProps) # HDRs - bpy.types.Scene.blenderkit_HDR = PointerProperty( + bpy.types.WindowManager.blenderkit_HDR = PointerProperty( type=BlenderKitHDRSearchProps) bpy.types.Image.blenderkit = PointerProperty( # for uploads, not now... type=BlenderKitHDRUploadProps) @@ -1865,7 +1865,7 @@ def register(): type=BlenderKitRatingProps) # MATERIALS - bpy.types.Scene.blenderkit_mat = PointerProperty( + bpy.types.WindowManager.blenderkit_mat = PointerProperty( type=BlenderKitMaterialSearchProps) bpy.types.Material.blenderkit = PointerProperty( # for uploads, not now... type=BlenderKitMaterialUploadProps) @@ -1873,7 +1873,7 @@ def register(): type=BlenderKitRatingProps) # BRUSHES - bpy.types.Scene.blenderkit_brush = PointerProperty( + bpy.types.WindowManager.blenderkit_brush = PointerProperty( type=BlenderKitBrushSearchProps) bpy.types.Brush.blenderkit = PointerProperty( # for uploads, not now... type=BlenderKitBrushUploadProps) @@ -1930,11 +1930,11 @@ def unregister(): tasks_queue.unregister() asset_bar_op.unregister() - del bpy.types.Scene.blenderkit_models - del bpy.types.Scene.blenderkit_scene - del bpy.types.Scene.blenderkit_HDR - del bpy.types.Scene.blenderkit_brush - del bpy.types.Scene.blenderkit_mat + del bpy.types.WindowManager.blenderkit_models + del bpy.types.WindowManager.blenderkit_scene + del bpy.types.WindowManager.blenderkit_HDR + del bpy.types.WindowManager.blenderkit_brush + del bpy.types.WindowManager.blenderkit_mat del bpy.types.Scene.blenderkit del bpy.types.Object.blenderkit diff --git a/blenderkit/asset_bar_op.py b/blenderkit/asset_bar_op.py index 19f51682..8727698d 100644 --- a/blenderkit/asset_bar_op.py +++ b/blenderkit/asset_bar_op.py @@ -518,7 +518,7 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): self.active_index = widget.search_index self.draw_tooltip = True - self.tooltip = asset_data['tooltip'] + # self.tooltip = asset_data['tooltip'] ui_props = scene.blenderkitUI ui_props.active_index = widget.search_index +self.scroll_offset diff --git a/blenderkit/download.py b/blenderkit/download.py index 4d7cfa52..d0b6cc98 100644 --- a/blenderkit/download.py +++ b/blenderkit/download.py @@ -305,14 +305,14 @@ def append_asset(asset_data, **kwargs): # downloaders=[], location=None, # how to do particle drop: # link the group we are interested in( there are more groups in File!!!! , have to get the correct one!) s = bpy.context.scene - + wm = bpy.context.window_manager user_preferences = bpy.context.preferences.addons['blenderkit'].preferences if user_preferences.api_key == '': user_preferences.asset_counter += 1 if asset_data['assetType'] == 'scene': - sprops = s.blenderkit_scene + sprops = wm.blenderkit_scene scene = append_link.append_scene(file_names[0], link=sprops.append_link == 'LINK', fake_user=False) # print('scene appended') @@ -330,7 +330,7 @@ def append_asset(asset_data, **kwargs): # downloaders=[], location=None, if asset_data['assetType'] == 'model': downloaders = kwargs.get('downloaders') - sprops = s.blenderkit_models + sprops = wm.blenderkit_models # TODO this is here because combinations of linking objects or appending groups are rather not-usefull if sprops.append_method == 'LINK_COLLECTION': sprops.append_link = 'LINK' @@ -468,7 +468,7 @@ def append_asset(asset_data, **kwargs): # downloaders=[], location=None, elif asset_data['assetType'] == 'material': inscene = False - sprops = s.blenderkit_mat + sprops = wm.blenderkit_mat for m in bpy.data.materials: if m.blenderkit.id == asset_data['id']: diff --git a/blenderkit/search.py b/blenderkit/search.py index 940375fa..6bbbac7f 100644 --- a/blenderkit/search.py +++ b/blenderkit/search.py @@ -52,7 +52,6 @@ import urllib import queue import logging - bk_logger = logging.getLogger('blenderkit') search_start_time = 0 @@ -77,9 +76,12 @@ def check_errors(rdata): search_threads = [] -thumb_sml_download_threads = {} -thumb_full_download_threads = {} +thumb_workers_sml = [] +thumb_workers_full = [] +thumb_sml_download_threads = queue.Queue() +thumb_full_download_threads = queue.Queue() reports_queue = queue.Queue() +all_thumbs_loaded = True 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.", @@ -282,7 +284,7 @@ def parse_result(r): if r['available_resolutions']: # should check only for non-empty sequences r['max_resolution'] = max(r['available_resolutions']) - tooltip = generate_tooltip(r) + # tooltip = generate_tooltip(r) # for some reason, the id was still int on some occurances. investigate this. r['author']['id'] = str(r['author']['id']) @@ -290,13 +292,13 @@ def parse_result(r): # so blender's data is same as on server. asset_data = {'thumbnail': tname, 'thumbnail_small': small_tname, - 'tooltip': tooltip, + # 'tooltip': tooltip, } asset_data['downloaded'] = 0 # parse extra params needed for blender here - params = utils.params_to_dict(r['parameters']) + params = r['dictParameters']#utils.params_to_dict(r['parameters']) if asset_type == 'model': if params.get('boundBoxMinX') != None: @@ -343,7 +345,6 @@ def parse_result(r): # @bpy.app.handlers.persistent def search_timer(): - # this makes a first search after opening blender. showing latest assets. # utils.p('timer search') # utils.p('start search timer') @@ -371,6 +372,19 @@ def search_timer(): # check_clipboard() + # finish loading thumbs from queues + global all_thumbs_loaded + if not all_thumbs_loaded: + ui_props = bpy.context.scene.blenderkitUI + search_name = f'bkit {ui_props.asset_type.lower()} search' + wm = bpy.context.window_manager + if wm.get(search_name) is not None: + all_loaded = True + for ri, r in enumerate(wm[search_name]): + if not r.get('thumb_small_loaded'): + all_loaded = all_loaded and load_preview(r, ri) + all_thumbs_loaded = all_loaded + global search_threads if len(search_threads) == 0: # utils.p('end search timer') @@ -384,6 +398,8 @@ def search_timer(): return 0.5 + + for thread in search_threads: # TODO this doesn't check all processes when one gets removed, # but most of the time only one is running anyway @@ -417,18 +433,16 @@ def search_timer(): rdata = thread[0].result - - ok, error = check_errors(rdata) if ok: ui_props = bpy.context.scene.blenderkitUI - orig_len = len(result_field) + for ri, r in enumerate(rdata['results']): asset_data = parse_result(r) if asset_data != None: result_field.append(asset_data) - load_preview(asset_data,ri + orig_len) + all_thumbs_loaded = all_thumbs_loaded and load_preview(asset_data, ri + orig_len) # Get ratings from BlenderKit server user_preferences = bpy.context.preferences.addons['blenderkit'].preferences @@ -437,17 +451,17 @@ def search_timer(): if utils.profile_is_validator(): for r in rdata['results']: if ratings_utils.get_rating_local(asset_data['id']) is None: - rating_thread = threading.Thread(target=ratings_utils.get_rating, args=([r['id'], headers]), daemon=True) + rating_thread = threading.Thread(target=ratings_utils.get_rating, args=([r['id'], headers]), + daemon=True) rating_thread.start() wm[search_name] = result_field wm['search results'] = result_field - #rdata=['results']=[] + # rdata=['results']=[] wm[search_name + ' orig'] = rdata wm['search results orig'] = rdata - # load_previews() if len(result_field) < ui_props.scrolloffset or not (thread[0].params.get('get_next')): # jump back ui_props.scrolloffset = 0 @@ -457,7 +471,7 @@ def search_timer(): tasks_queue.add_task((ui.add_report, ('No matching results found.',))) # undo push # bpy.ops.wm.undo_push_context(message='Get BlenderKit search') - #show asset bar automatically, but only on first page - others are loaded also when asset bar is hidden. + # show asset bar automatically, but only on first page - others are loaded also when asset bar is hidden. if not ui_props.assetbar_on and not thread[0].params.get('get_next'): bpy.ops.object.run_assetbar_fix_context() @@ -470,9 +484,11 @@ def search_timer(): # print('finished search thread') mt('preview loading finished') # utils.p('end search timer') - + if not all_thumbs_loaded: + return .1 return .3 + def load_preview(asset, index): scene = bpy.context.scene # FIRST START SEARCH @@ -480,11 +496,13 @@ def load_preview(asset, index): directory = paths.get_temp_dir('%s_search' % props.asset_type.lower()) s = bpy.context.scene results = bpy.context.window_manager.get('search results') - + loaded = True tpath = os.path.join(directory, asset['thumbnail_small']) - if not asset['thumbnail_small']: - tpath = paths.get_addon_thumbnail_path('thumbnail_not_available.jpg') + if not asset['thumbnail_small'] or not os.path.exists(tpath): + # tpath = paths.get_addon_thumbnail_path('thumbnail_notready.jpg') + asset['thumb_small_loaded'] = False + loaded = False iname = utils.previmg_name(index) @@ -493,14 +511,16 @@ def load_preview(asset, index): if img is None: if not os.path.exists(tpath): - return + return False img = bpy.data.images.load(tpath) img.name = iname elif img.filepath != tpath: if not os.path.exists(tpath): - return + #unload loaded previews from previous results + bpy.data.images.remove(img) + return False # had to add this check for autopacking files... - if img.packed_file is not None: + if bpy.data.use_autopack and img.packed_file is not None: img.unpack(method='USE_ORIGINAL') img.filepath = tpath img.reload() @@ -509,6 +529,8 @@ def load_preview(asset, index): image_utils.set_colorspace(img, 'Non-Color') else: image_utils.set_colorspace(img, 'sRGB') + asset['thumb_small_loaded'] = True + return loaded def load_previews(): @@ -522,7 +544,7 @@ def load_previews(): if results is not None: i = 0 for r in results: - load_preview(r,i) + load_preview(r, i) i += 1 @@ -636,14 +658,47 @@ def generate_author_textblock(adata): t = writeblockm(t, adata, key='aboutMe', pretext='', width=col_w) return t +def download_image(session, url, filepath): + r = None + try: + r = session.get(url, stream=False) + except Exception as e: + bk_logger.error('Thumbnail download failed') + bk_logger.error(str(e)) + if r and r.status_code == 200: + with open(filepath, 'wb') as f: + f.write(r.content) + +def thumb_download_worker(queue_sml, queue_full): + # print('thumb downloader', self.url) + # utils.p('start thumbdownloader thread') + while 1: + session = None + #start a session only for single search usually. + if not queue_sml.empty() or not queue_full.empty(): + session = requests.Session() + + while not queue_sml.empty(): + url, filepath = queue_sml.get() + download_image(session,url, filepath) + exit_full = False + # download full resolution image, but only if no small thumbs are waiting. + while not queue_full.empty() and queue_sml.empty(): + url, filepath = queue_full.get() + download_image(session,url, filepath) + + if queue_sml.empty() and queue_full.empty(): + if session is not None: + session.close() + time.sleep(.5) class ThumbDownloader(threading.Thread): - query = None - def __init__(self, url, path): + def __init__(self, url, path, session): super(ThumbDownloader, self).__init__() self.url = url self.path = path + self.session = session self._stop_event = threading.Event() def stop(self): @@ -657,7 +712,7 @@ class ThumbDownloader(threading.Thread): # utils.p('start thumbdownloader thread') r = None try: - r = requests.get(self.url, stream=False) + r = self.session.get(self.url, stream=False) except Exception as e: bk_logger.error('Thumbnail download failed') bk_logger.error(str(e)) @@ -671,7 +726,6 @@ class ThumbDownloader(threading.Thread): # utils.p('end thumbdownloader thread') - def write_gravatar(a_id, gravatar_path): ''' Write down gravatar path, as a result of thread-based gravatar image download. @@ -694,14 +748,14 @@ def fetch_gravatar(adata): ''' # utils.p('fetch gravatar') - #fetch new avatars if available already + # fetch new avatars if available already if adata.get('avatar128') is not None: - avatar_path = paths.get_temp_dir(subdir='bkit_g/') + adata['id']+ '.jpg' + avatar_path = paths.get_temp_dir(subdir='bkit_g/') + adata['id'] + '.jpg' if os.path.exists(avatar_path): tasks_queue.add_task((write_gravatar, (adata['id'], avatar_path))) return; - url= paths.get_bkit_url() + adata['avatar128'] + url = paths.get_bkit_url() + adata['avatar128'] r = rerequests.get(url, stream=False) # print(r.body) if r.status_code == 200: @@ -715,7 +769,7 @@ def fetch_gravatar(adata): utils.p('avatar for author not available.') return - #older gravatar code + # older gravatar code if adata.get('gravatarHash') is not None: gravatar_path = paths.get_temp_dir(subdir='bkit_g/') + adata['gravatarHash'] + '.jpg' @@ -825,6 +879,7 @@ def query_to_url(query={}, params={}): requeststring += '+' requeststring += q + ':' + str(query[q]).lower() + # add dict_parameters to make results smaller # result ordering: _score - relevance, score - BlenderKit score order = [] if params['free_first']: @@ -845,8 +900,9 @@ def query_to_url(query={}, params={}): order.append('-score,_score') else: order.append('_score') - if requeststring.find('+order:')==-1: + if requeststring.find('+order:') == -1: requeststring += '+order:' + ','.join(order) + requeststring += '&dict_parameters=1' requeststring += '&page_size=' + str(params['page_size']) requeststring += '&addon_version=%s' % params['addon_version'] @@ -883,7 +939,7 @@ class Searcher(threading.Thread): return self._stop_event.is_set() def run(self): - global reports_queue + global reports_queue, thumb_sml_download_threads, thumb_full_download_threads maxthreads = 50 query = self.query @@ -913,7 +969,7 @@ class Searcher(threading.Thread): try: rdata = r.json() except Exception as e: - if hasattr(r,'text'): + if hasattr(r, 'text'): 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))) @@ -988,62 +1044,21 @@ class Searcher(threading.Thread): # we can also prepend previous results. These have downloaded thumbnails already... self.result = rdata - # with open(json_filepath, 'w', encoding = 'utf-8') as outfile: - # json.dump(rdata, outfile, ensure_ascii=False, indent=4) - - killthreads_sml = [] - for k in thumb_sml_download_threads.keys(): - if k not in thumb_small_filepaths: - killthreads_sml.append(k) # do actual killing here? - - killthreads_full = [] - for k in thumb_full_download_threads.keys(): - if k not in thumb_full_filepaths: - killthreads_full.append(k) # do actual killing here? - # TODO do the killing/ stopping here. remember threads might have finished inbetween. if self.stopped(): utils.p('stopping search : ' + str(query)) # utils.p('end search thread') - return # this loop handles downloading of small thumbnails for imgpath, url in sml_thbs: - if imgpath not in thumb_sml_download_threads and not os.path.exists(imgpath): - thread = ThumbDownloader(url, imgpath) - # thread = threading.Thread(target=download_thumbnail, args=([url, imgpath]), - # daemon=True) - thread.start() - thumb_sml_download_threads[imgpath] = thread - # threads.append(thread) - - if len(thumb_sml_download_threads) > maxthreads: - while len(thumb_sml_download_threads) > maxthreads: - threads_copy = thumb_sml_download_threads.copy() # because for loop can erase some of the items. - for tk, thread in threads_copy.items(): - if not thread.is_alive(): - thread.join() - # utils.p(x) - del (thumb_sml_download_threads[tk]) - # utils.p('fetched thumbnail ', i) - i += 1 + if not os.path.exists(imgpath): + thumb_sml_download_threads.put((url, imgpath)) + if self.stopped(): utils.p('stopping search : ' + str(query)) # utils.p('end search thread') - return - idx = 0 - while len(thumb_sml_download_threads) > 0: - threads_copy = thumb_sml_download_threads.copy() # because for loop can erase some of the items. - for tk, thread in threads_copy.items(): - if not thread.is_alive(): - thread.join() - try: - del (thumb_sml_download_threads[tk]) - except Exception as e: - print(e) - i += 1 if self.stopped(): # utils.p('end search thread') @@ -1052,13 +1067,11 @@ class Searcher(threading.Thread): return # start downloading full thumbs in the end + tsession = requests.Session() + for imgpath, url in full_thbs: - if imgpath not in thumb_full_download_threads and not os.path.exists(imgpath): - thread = ThumbDownloader(url, imgpath) - # thread = threading.Thread(target=download_thumbnail, args=([url, imgpath]), - # daemon=True) - thread.start() - thumb_full_download_threads[imgpath] = thread + if not os.path.exists(imgpath): + thumb_full_download_threads.put((url, imgpath)) # utils.p('end search thread') mt('thumbnails finished') @@ -1068,7 +1081,7 @@ def build_query_common(query, props): query_common = {} if props.search_keywords != '': # keywords = urllib.parse.urlencode(props.search_keywords) - keywords = props.search_keywords.replace('&','%26') + keywords = props.search_keywords.replace('&', '%26') query_common["query"] = keywords if props.search_verification_status != 'ALL' and utils.profile_is_validator(): @@ -1090,7 +1103,7 @@ def build_query_common(query, props): def build_query_model(): '''use all search input to request results from server''' - props = bpy.context.scene.blenderkit_models + props = bpy.context.window_manager.blenderkit_models query = { "asset_type": 'model', # "engine": props.search_engine, @@ -1127,7 +1140,7 @@ def build_query_model(): def build_query_scene(): '''use all search input to request results from server''' - props = bpy.context.scene.blenderkit_scene + props = bpy.context.window_manager.blenderkit_scene query = { "asset_type": 'scene', # "engine": props.search_engine, @@ -1140,7 +1153,7 @@ def build_query_scene(): def build_query_HDR(): '''use all search input to request results from server''' - props = bpy.context.scene.blenderkit_HDR + props = bpy.context.window_manager.blenderkit_HDR query = { "asset_type": 'hdr', # "engine": props.search_engine, @@ -1151,7 +1164,7 @@ def build_query_HDR(): def build_query_material(): - props = bpy.context.scene.blenderkit_mat + props = bpy.context.window_manager.blenderkit_mat query = { "asset_type": 'material', @@ -1206,7 +1219,7 @@ def build_query_texture(): def build_query_brush(): - props = bpy.context.scene.blenderkit_brush + props = bpy.context.window_manager.blenderkit_brush brush_type = '' if bpy.context.sculpt_object is not None: @@ -1235,7 +1248,7 @@ def mt(text): def add_search_process(query, params): - global search_threads + global search_threads, thumb_workers_sml, thumb_workers_full, all_thumbs_loaded while (len(search_threads) > 0): old_thread = search_threads.pop(0) @@ -1250,6 +1263,16 @@ def add_search_process(query, params): else: urlquery = query_to_url(query, params) + if thumb_workers_sml == []: + for a in range(0, 8): + # worker = ThumbDownloadWorker(thumb_sml_download_threads, thumb_full_download_threads) + thread = threading.Thread(target=thumb_download_worker, args=(thumb_sml_download_threads, thumb_full_download_threads), + daemon=True) + thread.start() + thumb_workers_sml.append(thread) + + all_thumbs_loaded = False + thread = Searcher(query, params, tempdir=tempdir, headers=headers, urlquery=urlquery) thread.start() @@ -1317,38 +1340,39 @@ def search(category='', get_next=False, author_id=''): search_start_time = time.time() # mt('start') scene = bpy.context.scene + wm = bpy.context.window_manager ui_props = scene.blenderkitUI props = utils.get_search_props() if ui_props.asset_type == 'MODEL': - if not hasattr(scene, 'blenderkit'): + if not hasattr(wm, 'blenderkit_models'): return; query = build_query_model() if ui_props.asset_type == 'SCENE': - if not hasattr(scene, 'blenderkit_scene'): + if not hasattr(wm, 'blenderkit_scene'): return; query = build_query_scene() if ui_props.asset_type == 'HDR': - if not hasattr(scene, 'blenderkit_HDR'): + if not hasattr(wm, 'blenderkit_HDR'): return; query = build_query_HDR() if ui_props.asset_type == 'MATERIAL': - if not hasattr(scene, 'blenderkit_mat'): + if not hasattr(wm, 'blenderkit_mat'): return; query = build_query_material() if ui_props.asset_type == 'TEXTURE': - if not hasattr(scene, 'blenderkit_tex'): + if not hasattr(wm, 'blenderkit_tex'): return; # props = scene.blenderkit_tex # query = build_query_texture() if ui_props.asset_type == 'BRUSH': - if not hasattr(scene, 'blenderkit_brush'): + if not hasattr(wm, 'blenderkit_brush'): return; query = build_query_brush() @@ -1405,22 +1429,23 @@ def search(category='', get_next=False, author_id=''): props.report = 'BlenderKit searching....' + def update_filters(): sprops = utils.get_search_props() ui_props = bpy.context.scene.blenderkitUI fcommon = sprops.own_only or \ - sprops.search_texture_resolution or\ - sprops.search_file_size or \ - sprops.search_procedural != 'BOTH' or \ - sprops.free_only or \ - sprops.quality_limit>0 + sprops.search_texture_resolution or \ + sprops.search_file_size or \ + sprops.search_procedural != 'BOTH' or \ + sprops.free_only or \ + sprops.quality_limit > 0 - if ui_props.asset_type =='MODEL': + if ui_props.asset_type == 'MODEL': sprops.use_filters = fcommon or \ sprops.search_style != 'ANY' or \ - sprops.search_condition != 'UNSPECIFIED' or \ - sprops.search_design_year or \ - sprops.search_polycount + sprops.search_condition != 'UNSPECIFIED' or \ + sprops.search_design_year or \ + sprops.search_polycount elif ui_props.asset_type == 'MATERIAL': sprops.use_filters = fcommon @@ -1464,7 +1489,7 @@ def search_update(self, context): # return here since writing into search keywords triggers this update function once more. return - print('search update search') + # print('search update search') search() @@ -1522,7 +1547,6 @@ class SearchOperator(Operator): # description='Try to close the window below mouse before download', # default=False) - tooltip: bpy.props.StringProperty(default='Runs search and displays the asset bar at the same time') @classmethod @@ -1556,6 +1580,7 @@ class SearchOperator(Operator): # context.window.cursor_warp(event.mouse_x, event.mouse_y); # return self. execute(context) + class UrlOperator(Operator): """""" bl_idname = "wm.blenderkit_url" @@ -1574,6 +1599,7 @@ class UrlOperator(Operator): bpy.ops.wm.url_open(url=self.url) return {'FINISHED'} + class TooltipLabelOperator(Operator): """""" bl_idname = "wm.blenderkit_tooltip" @@ -1590,6 +1616,7 @@ class TooltipLabelOperator(Operator): def execute(self, context): return {'FINISHED'} + classes = [ SearchOperator, UrlOperator, @@ -1605,7 +1632,6 @@ def register_search(): user_preferences = bpy.context.preferences.addons['blenderkit'].preferences if user_preferences.use_timers: - bpy.app.timers.register(search_timer) categories.load_categories() @@ -1619,5 +1645,3 @@ def unregister_search(): if bpy.app.timers.is_registered(search_timer): bpy.app.timers.unregister(search_timer) - - diff --git a/blenderkit/thumbnails/thumbnail_notready.jpg b/blenderkit/thumbnails/thumbnail_notready.jpg Binary files differindex a32cb096..5be976b9 100644 --- a/blenderkit/thumbnails/thumbnail_notready.jpg +++ b/blenderkit/thumbnails/thumbnail_notready.jpg diff --git a/blenderkit/ui.py b/blenderkit/ui.py index f402de51..138f4a89 100644 --- a/blenderkit/ui.py +++ b/blenderkit/ui.py @@ -40,7 +40,6 @@ import os import logging - draw_time = 0 eval_time = 0 @@ -361,7 +360,7 @@ def draw_tooltip(x, y, name='', author='', quality='-', img=None, gravatar=None) # draw author's name author_text_size = int(name_height * .7) - ui_bgl.draw_text(author, author_x_text, gravatar_y, author_text_size, textcol, ralign=True) + ui_bgl.draw_text(author, author_x_text, gravatar_y, author_text_size, textcol, halign='RIGHT') # draw quality quality_text_size = int(name_height * 1) @@ -385,7 +384,7 @@ def draw_tooltip_with_author(asset_data, x, y): if a.get('gravatarImg') is not None: gimg = utils.get_hidden_image(a['gravatarImg'], a['gravatarHash']).name - if len(a['firstName'])>0 or len(a['lastName'])>0: + if len(a['firstName']) > 0 or len(a['lastName']) > 0: author_text = f"by {a['firstName']} {a['lastName']}" aname = asset_data['displayName'] @@ -398,13 +397,13 @@ def draw_tooltip_with_author(asset_data, x, y): rcount = 0 quality = '-' if rc: - rcount = min(rc.get('quality',0), rc.get('workingHours',0)) + rcount = min(rc.get('quality', 0), rc.get('workingHours', 0)) if rcount > show_rating_threshold: quality = round(asset_data['ratingsAverage'].get('quality')) - tooltip_data={ + tooltip_data = { 'aname': aname, 'author_text': author_text, - 'quality':quality, + 'quality': quality, 'gimg': gimg } asset_data['tooltip_data'] = tooltip_data @@ -419,7 +418,6 @@ def draw_tooltip_with_author(asset_data, x, y): gravatar=gimg) - def draw_callback_2d(self, context): if not utils.guard_from_crash(): return @@ -699,7 +697,9 @@ def draw_asset_bar(self, context): # w + 2*highlight_margin, h + 2*highlight_margin , highlight) else: - ui_bgl.draw_rect(x, y, ui_props.thumb_size, ui_props.thumb_size, white) + ui_bgl.draw_rect(x, y, ui_props.thumb_size, ui_props.thumb_size, grey2) + ui_bgl.draw_text('loading', x + ui_props.thumb_size // 2, y + ui_props.thumb_size // 2, + ui_props.thumb_size // 6, white, halign='CENTER', valign='CENTER') result = search_results[index] # code to inform validators that the validation is waiting too long and should be done asap @@ -744,7 +744,7 @@ def draw_asset_bar(self, context): # report = 'BlenderKit - No matching results found.' # ui_bgl.draw_text(report, ui_props.bar_x + ui_props.margin, # ui_props.bar_y - 25 - ui_props.margin, 15) - if ui_props.draw_tooltip and len(search_results)>ui_props.active_index: + if ui_props.draw_tooltip and len(search_results) > ui_props.active_index: r = search_results[ui_props.active_index] draw_tooltip_with_author(r, ui_props.mouse_x, ui_props.mouse_y) s = bpy.context.scene @@ -818,7 +818,7 @@ def mouse_raycast(context, mx, my): # rote = mathutils.Euler((0, 0, math.pi)) randoffset = math.pi if has_hit: - props = bpy.context.scene.blenderkit_models + props = bpy.context.window_manager.blenderkit_models up = Vector((0, 0, 1)) if props.perpendicular_snap: @@ -874,7 +874,7 @@ def floor_raycast(context, mx, my): object = None matrix = None snapped_rotation = snapped_normal.to_track_quat('Z', 'Y').to_euler() - props = bpy.context.scene.blenderkit_models + props = bpy.context.window_manager.blenderkit_models if props.randomize_rotation: randoffset = props.offset_rotation_amount + math.pi + ( random.random() - 0.5) * props.randomize_rotation_amount @@ -1193,7 +1193,6 @@ class AssetBarOperator(bpy.types.Operator): ui_props.has_hit = False ui_props.assetbar_on = False - def modal(self, context, event): # This is for case of closing the area or changing type: @@ -1256,7 +1255,6 @@ class AssetBarOperator(bpy.types.Operator): self.area.tag_redraw() s = context.scene - if ui_props.turn_off: ui_props.turn_off = False self.exit_modal() @@ -1432,7 +1430,6 @@ class AssetBarOperator(bpy.types.Operator): ui_props.scrolloffset = max(0, ui_props.scrolloffset - ui_props.wcount * ui_props.hcount) return {'RUNNING_MODAL'} - if ui_props.active_index == -3: return {'RUNNING_MODAL'} else: @@ -1529,7 +1526,6 @@ class AssetBarOperator(bpy.types.Operator): update_ui_size(self.area, self.region) - self._handle_2d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_2d, args, 'WINDOW', 'POST_PIXEL') ui_props.assetbar_on = True @@ -1592,10 +1588,10 @@ def draw_callback_dragging(self, context): try: img = bpy.data.images.get(self.iname) except: - # self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_dragging, args, 'WINDOW', 'POST_PIXEL') - # self._handle_3d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_3d_dragging, args, 'WINDOW', - # bpy.types.SpaceView3D.draw_handler_remove(self._handle, - # bpy.types.SpaceView3D.draw_handler_remove(self._handle_3d, 'WINDOW') + # self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_dragging, args, 'WINDOW', 'POST_PIXEL') + # self._handle_3d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_3d_dragging, args, 'WINDOW', + # bpy.types.SpaceView3D.draw_handler_remove(self._handle, + # bpy.types.SpaceView3D.draw_handler_remove(self._handle_3d, 'WINDOW') return linelength = 35 @@ -1646,7 +1642,7 @@ class AssetDragOperator(bpy.types.Operator): if ui_props.asset_type == 'MODEL': if not self.drag: self.snapped_location = scene.cursor.location - self.snapped_rotation = (0,0,0) + self.snapped_rotation = (0, 0, 0) target_object = '' if self.object_name is not None: @@ -1671,7 +1667,7 @@ class AssetDragOperator(bpy.types.Operator): target_object = '' target_slot = '' if not self.drag: - #click interaction + # click interaction object = bpy.context.active_object if object is None: ui_panels.ui_message(title='Nothing selected', @@ -1680,7 +1676,8 @@ class AssetDragOperator(bpy.types.Operator): target_object = object.name target_slot = object.active_material_index self.snapped_location = object.location - elif self.object_name is not None: + elif self.object_name is not None and self.has_hit: + # first, test if object can have material applied. object = bpy.data.objects[self.object_name] # this enables to run Bring to scene automatically when dropping on a linked objects. @@ -1696,9 +1693,7 @@ class AssetDragOperator(bpy.types.Operator): # create final mesh to extract correct material slot depsgraph = bpy.context.evaluated_depsgraph_get() object_eval = object.evaluated_get(depsgraph) - temp_mesh = object_eval.to_mesh() - target_slot = temp_mesh.polygons[self.face_index].material_index - object_eval.to_mesh_clear() + if object.type == 'MESH': temp_mesh = object_eval.to_mesh() target_slot = temp_mesh.polygons[self.face_index].material_index @@ -1726,7 +1721,6 @@ class AssetDragOperator(bpy.types.Operator): message=f"Can't assign materials to {object.type.lower()} object.") return - if target_object != '': # position is for downloader: loc = self.snapped_location @@ -1742,16 +1736,13 @@ class AssetDragOperator(bpy.types.Operator): target_object=target_object, material_target_slot=target_slot) - - if ui_props.asset_type == 'HDR': bpy.ops.scene.blenderkit_download('INVOKE_DEFAULT', - asset_index=self.asset_search_index, - # replace_resolution=True, - invoke_resolution=True, - max_resolution=self.asset_data.get('max_resolution', 0) - ) - + asset_index=self.asset_search_index, + # replace_resolution=True, + invoke_resolution=True, + max_resolution=self.asset_data.get('max_resolution', 0) + ) if ui_props.asset_type == 'SCENE': bpy.ops.scene.blenderkit_download('INVOKE_DEFAULT', @@ -1779,18 +1770,18 @@ class AssetDragOperator(bpy.types.Operator): self.mouse_x = event.mouse_region_x self.mouse_y = event.mouse_region_y - #are we dragging already? - self.drag = False + # are we dragging already? drag_threshold = 10 - if abs(self.start_mouse_x - self.mouse_x) > drag_threshold and \ - abs(self.start_mouse_y - self.mouse_y) > drag_threshold: + if not self.drag and \ + (abs(self.start_mouse_x - self.mouse_x) > drag_threshold or \ + abs(self.start_mouse_y - self.mouse_y) > drag_threshold): self.drag = True - #turn off asset bar here, shout start again after finishing drag drop. + # turn off asset bar here, shout start again after finishing drag drop. ui_props.turn_off = True if (event.type == 'ESC' or \ - not mouse_in_region(context.region, self.mouse_x, self.mouse_y))and \ - (not self.drag or self.steps<5): + not mouse_in_region(context.region, self.mouse_x, self.mouse_y)) and \ + (not self.drag or self.steps < 5): # this case is for canceling from inside popup card when there's an escape attempt to close the window return {'PASS_THROUGH'} @@ -1801,7 +1792,7 @@ class AssetDragOperator(bpy.types.Operator): ui_props.dragging = False return {'CANCELLED'} - sprops = bpy.context.scene.blenderkit_models + sprops = bpy.context.window_manager.blenderkit_models if event.type == 'WHEELUPMOUSE': sprops.offset_rotation_amount += sprops.offset_rotation_step return {'RUNNING_MODAL'} @@ -1809,8 +1800,7 @@ class AssetDragOperator(bpy.types.Operator): sprops.offset_rotation_amount -= sprops.offset_rotation_step return {'RUNNING_MODAL'} - - if event.type =='MOUSEMOVE': + if event.type == 'MOUSEMOVE': #### TODO - this snapping code below is 3x in this file.... refactor it. self.has_hit, self.snapped_location, self.snapped_normal, self.snapped_rotation, self.face_index, object, self.matrix = mouse_raycast( @@ -1832,16 +1822,16 @@ class AssetDragOperator(bpy.types.Operator): return {'RUNNING_MODAL'} if event.type == 'LEFTMOUSE' and event.value == 'RELEASE': - self.mouse_release()# does the main job with assets + self.mouse_release() # does the main job with assets self.handlers_remove() bpy.context.window.cursor_set("DEFAULT") - bpy.ops.object.run_assetbar_fix_context(keep_running = True, do_search = False) + bpy.ops.object.run_assetbar_fix_context(keep_running=True, do_search=False) ui_props.dragging = False return {'FINISHED'} - self.steps +=1 + self.steps += 1 - return {'PASS_THROUGH'} + return {'RUNNING_MODAL'} def invoke(self, context, event): if context.area.type == 'VIEW_3D': @@ -1851,8 +1841,6 @@ class AssetDragOperator(bpy.types.Operator): # draw in view space with 'POST_VIEW' and 'PRE_VIEW' self.iname = utils.previmg_name(self.asset_search_index) - - self.mouse_x = 0 self.mouse_y = 0 self.steps = 0 @@ -1884,6 +1872,7 @@ class AssetDragOperator(bpy.types.Operator): bpy.context.window.cursor_set("NONE") ui_props = bpy.context.scene.blenderkitUI ui_props.dragging = True + self.drag = False context.window_manager.modal_handler_add(self) return {'RUNNING_MODAL'} else: diff --git a/blenderkit/ui_bgl.py b/blenderkit/ui_bgl.py index 95c454fe..339f5983 100644 --- a/blenderkit/ui_bgl.py +++ b/blenderkit/ui_bgl.py @@ -134,14 +134,20 @@ def draw_image(x, y, width, height, image, transparency, crop=(0, 0, 1, 1), batc return batch -def draw_text(text, x, y, size, color=(1, 1, 1, 0.5), ralign = False): +def draw_text(text, x, y, size, color=(1, 1, 1, 0.5), halign = 'LEFT', valign = 'TOP'): font_id = 1 # bgl.glColor4f(*color) blf.color(font_id, color[0], color[1], color[2], color[3]) blf.size(font_id, size, 72) - if ralign: + if halign != 'LEFT': width,height = blf.dimensions(font_id, text) - x-=width + if halign == 'RIGHT': + x-=width + elif halign == 'CENTER': + x-=width//2 + if valign=='CENTER': + y-=height//2 + #bottom could be here but there's no reason for it blf.position(font_id, x, y, 0) blf.draw(font_id, text) diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py index c763ba9e..67f42148 100644 --- a/blenderkit/ui_panels.py +++ b/blenderkit/ui_panels.py @@ -198,7 +198,8 @@ def draw_panel_hdr_upload(self, context): def draw_panel_hdr_search(self, context): s = context.scene - props = s.blenderkit_HDR + wm = context.window_manager + props = wm.blenderkit_HDR layout = self.layout row = layout.row() @@ -211,7 +212,7 @@ def draw_panel_hdr_search(self, context): def draw_thumbnail_upload_panel(layout, props): update = False - tex = autothumb.get_texture_ui(props.thumbnail, '.upload_preview') + tex = autothumb.get_texture_ui(props.thumbnail, 'upload_preview') if not tex or not tex.image: return box = layout.box() @@ -346,9 +347,9 @@ def draw_assetbar_show_hide(layout, props): def draw_panel_model_search(self, context): - s = context.scene + wm = bpy.context.window_manager + props = wm.blenderkit_models - props = s.blenderkit_models layout = self.layout row = layout.row() @@ -377,8 +378,8 @@ def draw_panel_model_search(self, context): def draw_panel_scene_search(self, context): - s = context.scene - props = s.blenderkit_scene + wm = bpy.context.window_manager + props = wm.blenderkit_scene layout = self.layout # layout.label(text = "common search properties:") row = layout.row() @@ -677,7 +678,7 @@ def draw_panel_material_upload(self, context): def draw_panel_material_search(self, context): - wm = context.scene + wm = context.window_manager props = wm.blenderkit_mat layout = self.layout @@ -713,8 +714,8 @@ def draw_panel_brush_upload(self, context): def draw_panel_brush_search(self, context): - s = context.scene - props = s.blenderkit_brush + wm = context.window_manager + props = wm.blenderkit_brush layout = self.layout row = layout.row() @@ -773,9 +774,9 @@ class VIEW3D_PT_blenderkit_advanced_model_search(Panel): return ui_props.down_up == 'SEARCH' and ui_props.asset_type == 'MODEL' def draw(self, context): - s = context.scene + wm = bpy.context.window_manager - props = s.blenderkit_models + props = wm.blenderkit_models layout = self.layout layout.separator() @@ -842,9 +843,8 @@ class VIEW3D_PT_blenderkit_advanced_material_search(Panel): return ui_props.down_up == 'SEARCH' and ui_props.asset_type == 'MATERIAL' def draw(self, context): - s = context.scene - - props = s.blenderkit_mat + wm = context.window_manager + props = wm.blenderkit_mat layout = self.layout layout.separator() @@ -892,8 +892,8 @@ class VIEW3D_PT_blenderkit_categories(Panel): draw_panel_categories(self, context) def draw_scene_import_settings(self, context): - s = context.scene - props = s.blenderkit_scene + wm = bpy.context.window_manager + props = wm.blenderkit_scene layout = self.layout layout.prop(props, 'switch_after_append') # layout.label(text='Import method:') @@ -920,11 +920,12 @@ class VIEW3D_PT_blenderkit_import_settings(Panel): layout = self.layout s = context.scene + wm = bpy.context.window_manager ui_props = s.blenderkitUI if ui_props.asset_type == 'MODEL': # noinspection PyCallByClass - props = s.blenderkit_models + props = wm.blenderkit_models layout.prop(props, 'randomize_rotation') if props.randomize_rotation: layout.prop(props, 'randomize_rotation_amount') @@ -937,7 +938,7 @@ class VIEW3D_PT_blenderkit_import_settings(Panel): row.prop(props, 'append_method', expand=True, icon_only=False) if ui_props.asset_type == 'MATERIAL': - props = s.blenderkit_mat + props = wm.blenderkit_mat layout.prop(props, 'automap') layout.label(text='Import method:') row = layout.row() @@ -947,7 +948,7 @@ class VIEW3D_PT_blenderkit_import_settings(Panel): draw_scene_import_settings(self,context) if ui_props.asset_type == 'HDR': - props = s.blenderkit_HDR + props = wm.blenderkit_HDR if ui_props.asset_type in ['MATERIAL', 'MODEL', 'HDR']: layout.prop(props, 'resolution') @@ -1162,8 +1163,8 @@ class BlenderKitWelcomeOperator(bpy.types.Operator): random_search = random.choice(random_searches) ui_props.asset_type = random_search[0] - bpy.context.scene.blenderkit_mat.search_keywords = ''#random_search[1] - bpy.context.scene.blenderkit_mat.search_keywords = '+is_free:true+score_gte:1000+order:-created'#random_search[1] + bpy.context.window_manager.blenderkit_mat.search_keywords = ''#random_search[1] + bpy.context.window_manager.blenderkit_mat.search_keywords = '+is_free:true+score_gte:1000+order:-created'#random_search[1] # search.search() return {'FINISHED'} @@ -1395,7 +1396,10 @@ class OBJECT_MT_blenderkit_asset_menu(bpy.types.Menu): def numeric_to_str(s): if s: - s = str(round(s)) + if s<1: + s = str(round(s,1)) + else: + s = str(round(s)) else: s = '-' return s @@ -1502,10 +1506,11 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties): def draw_properties(self, layout, width=250): - if type(self.asset_data['parameters']) == list: - mparams = utils.params_to_dict(self.asset_data['parameters']) - else: - mparams = self.asset_data['parameters'] + # if type(self.asset_data['parameters']) == list: + # mparams = utils.params_to_dict(self.asset_data['parameters']) + # else: + # mparams = self.asset_data['parameters'] + mparams = self.asset_data['dictParameters'] pcoll = icons.icon_collections["main"] @@ -2197,17 +2202,18 @@ def header_search_draw(self, context): if preferences.search_in_header: layout = self.layout s = bpy.context.scene + wm = bpy.context.window_manager ui_props = s.blenderkitUI if ui_props.asset_type == 'MODEL': - props = s.blenderkit_models + props = wm.blenderkit_models if ui_props.asset_type == 'MATERIAL': - props = s.blenderkit_mat + props = wm.blenderkit_mat if ui_props.asset_type == 'BRUSH': - props = s.blenderkit_brush + props = wm.blenderkit_brush if ui_props.asset_type == 'HDR': - props = s.blenderkit_HDR + props = wm.blenderkit_HDR if ui_props.asset_type == 'SCENE': - props = s.blenderkit_scene + props = wm.blenderkit_scene # the center snap menu is in edit and object mode if tool settings are off. if context.space_data.show_region_tool_header == True or context.mode[:4] not in ('EDIT', 'OBJE'): diff --git a/blenderkit/utils.py b/blenderkit/utils.py index c16bc017..113dc130 100644 --- a/blenderkit/utils.py +++ b/blenderkit/utils.py @@ -154,36 +154,37 @@ def get_selected_replace_adepts(): def get_search_props(): scene = bpy.context.scene + wm = bpy.context.window_manager if scene is None: return; uiprops = scene.blenderkitUI props = None if uiprops.asset_type == 'MODEL': - if not hasattr(scene, 'blenderkit_models'): + if not hasattr(wm, 'blenderkit_models'): return; - props = scene.blenderkit_models + props = wm.blenderkit_models if uiprops.asset_type == 'SCENE': - if not hasattr(scene, 'blenderkit_scene'): + if not hasattr(wm, 'blenderkit_scene'): return; - props = scene.blenderkit_scene + props = wm.blenderkit_scene if uiprops.asset_type == 'HDR': - if not hasattr(scene, 'blenderkit_HDR'): + if not hasattr(wm, 'blenderkit_HDR'): return; - props = scene.blenderkit_HDR + props = wm.blenderkit_HDR if uiprops.asset_type == 'MATERIAL': - if not hasattr(scene, 'blenderkit_mat'): + if not hasattr(wm, 'blenderkit_mat'): return; - props = scene.blenderkit_mat + props = wm.blenderkit_mat if uiprops.asset_type == 'TEXTURE': - if not hasattr(scene, 'blenderkit_tex'): + if not hasattr(wm, 'blenderkit_tex'): return; # props = scene.blenderkit_tex if uiprops.asset_type == 'BRUSH': - if not hasattr(scene, 'blenderkit_brush'): + if not hasattr(wm, 'blenderkit_brush'): return; - props = scene.blenderkit_brush + props = wm.blenderkit_brush return props @@ -630,8 +631,8 @@ def scale_uvs(ob, scale=1.0, pivot=Vector((.5, .5))): # map uv cubic and switch of auto tex space and set it to 1,1,1 def automap(target_object=None, target_slot=None, tex_size=1, bg_exception=False, just_scale=False): - s = bpy.context.scene - mat_props = s.blenderkit_mat + wm = bpy.context.window_manager + mat_props = wm.blenderkit_mat if mat_props.automap: tob = bpy.data.objects[target_object] # only automap mesh models @@ -720,14 +721,16 @@ def fmt_length(prop): def get_param(asset_data, parameter_name, default=None): - if not asset_data.get('parameters'): + if not asset_data.get('dictParameters'): # this can appear in older version files. return default - for p in asset_data['parameters']: - if p.get('parameterType') == parameter_name: - return p['value'] - return default + return asset_data['dictParameters'].get(parameter_name, default) + + # for p in asset_data['parameters']: + # if p.get('parameterType') == parameter_name: + # return p['value'] + # return default def params_to_dict(params): @@ -813,7 +816,7 @@ def user_is_owner(asset_data=None): def asset_from_newer_blender_version(asset_data): bver = bpy.app.version aver = asset_data['sourceAppVersion'].split('.') - print(aver,bver) + # print(aver,bver) bver_f = bver[0] + bver[1] * .01 + bver[2] * .0001 aver_f = int(aver[0]) + int(aver[1]) * .01 + int(aver[2]) * .0001 return aver_f>bver_f |