From c72e66249180b103165679834129574d5868fa68 Mon Sep 17 00:00:00 2001 From: Vilem Duha Date: Sun, 4 Jul 2021 09:10:58 +0200 Subject: BlenderKit: fix category display was actually showing slugs, now shows names with links fix long descriptions issue - now has a 'more' button to read the rest online (label_multiline now has a max_lines parameter and returns True if max lenght was reached) fix avatars to match server --- blenderkit/categories.py | 13 ++++++++----- blenderkit/paths.py | 2 ++ blenderkit/search.py | 30 ++++++++++++++++++++++++++++++ blenderkit/ui_panels.py | 47 +++++++++++++++++++++++++++++++++++++++-------- blenderkit/utils.py | 28 ++++++++++++++++++++++------ 5 files changed, 101 insertions(+), 19 deletions(-) diff --git a/blenderkit/categories.py b/blenderkit/categories.py index 3a0d2624..fc7b929b 100644 --- a/blenderkit/categories.py +++ b/blenderkit/categories.py @@ -91,7 +91,7 @@ def get_category_name_path(categories, category): category_path = [] check_categories = categories[:] parents = {} - utils.pprint(categories) + # utils.pprint(categories) while len(check_categories) > 0: ccheck = check_categories.pop() # print(ccheck['name']) @@ -100,16 +100,19 @@ def get_category_name_path(categories, category): for ch in ccheck['children']: # print(ch['name']) - parents[ch['slug']] = ccheck['slug'] + parents[ch['slug']] = ccheck if ch['slug'] == category: - category_path = [ch['slug']] + category_path = [ch['name']] slug = ch['slug'] while parents.get(slug): - slug = parents.get(slug) - category_path.insert(0, slug) + parent = parents.get(slug) + slug = parent['slug'] + + category_path.insert(0, parent['name']) return category_path check_categories.append(ch) + return category_path def get_category(categories, cat_path=()): for category in cat_path: diff --git a/blenderkit/paths.py b/blenderkit/paths.py index 57511108..12b815e5 100644 --- a/blenderkit/paths.py +++ b/blenderkit/paths.py @@ -83,6 +83,8 @@ def get_oauth_landing_url(): def get_author_gallery_url(author_id): return f'{get_bkit_url()}/asset-gallery?query=author_id:{author_id}' +def get_asset_gallery_url(asset_id): + return f'{get_bkit_url()}/asset-gallery-detail/{asset_id}/' def default_global_dict(): from os.path import expanduser diff --git a/blenderkit/search.py b/blenderkit/search.py index dbe25d61..1b80251e 100644 --- a/blenderkit/search.py +++ b/blenderkit/search.py @@ -685,7 +685,37 @@ def write_gravatar(a_id, gravatar_path): def fetch_gravatar(adata): + ''' + Gets avatars from blenderkit server + Parameters + ---------- + adata - author data from elastic search result + + ''' # utils.p('fetch gravatar') + + #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' + if os.path.exists(avatar_path): + tasks_queue.add_task((write_gravatar, (adata['id'], avatar_path))) + return; + + url= paths.get_bkit_url() + adata['avatar128'] + r = rerequests.get(url, stream=False) + # print(r.body) + if r.status_code == 200: + # print(url) + # print(r.headers['content-disposition']) + with open(avatar_path, 'wb') as f: + f.write(r.content) + tasks_queue.add_task((write_gravatar, (adata['id'], avatar_path))) + elif r.status_code == '404': + adata['avatar128'] = None + utils.p('avatar for author not available.') + return + + #older gravatar code if adata.get('gravatarHash') is not None: gravatar_path = paths.get_temp_dir(subdir='bkit_g/') + adata['gravatarHash'] + '.jpg' diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py index fe91658c..ef963d91 100644 --- a/blenderkit/ui_panels.py +++ b/blenderkit/ui_panels.py @@ -1397,8 +1397,8 @@ def numeric_to_str(s): return s -def push_op_left(layout): - for a in range(0, 5): +def push_op_left(layout, strength =5): + for a in range(0, strength): layout.label(text='') @@ -1486,7 +1486,13 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties): box.scale_y = 0.4 box.label(text='Description') box.separator() - utils.label_multiline(box, self.asset_data['description'], width=width) + link_more = utils.label_multiline(box, self.asset_data['description'], width=width, max_lines = 10) + if link_more: + row = box.row() + row.scale_y = 2 + op = row.operator('wm.blenderkit_url', text='See full description', icon='URL') + op.url = paths.get_asset_gallery_url(self.asset_data['assetBaseId']) + op.tooltip = 'Read full description on website' box.separator() def draw_properties(self, layout, width=250): @@ -1818,15 +1824,40 @@ class AssetPopupCard(bpy.types.Operator, ratings_utils.RatingsProperties): cat_path = categories.get_category_path(bcats, self.asset_data['category'])[1:] - for i,c in enumerate(cat_path): - cat_path[i] = c.capitalize() - cat_path = ' > '.join(cat_path) - # box.label(text=cat_path) + + + cat_path_names = categories.get_category_name_path(bcats, + self.asset_data['category'])[1:] aname = asset_data['displayName'] aname = aname[0].upper() + aname[1:] - top_drag_bar.label(text=f'{cat_path} > {aname}') + if 1: + name_row = top_drag_bar.row() + # name_row = name_row.split(factor=0.5) + # name_row = name_row.column() + # name_row = name_row.row() + for i, c in enumerate(cat_path): + cat_name = cat_path_names[i] + op = name_row.operator('view3d.blenderkit_asset_bar', text=cat_name + ' >', emboss=True) + op.do_search = True + op.keep_running = True + op.tooltip = f"Browse {cat_name} category" + op.category = c + # name_row.label(text='>') + + name_row.label(text=aname) + push_op_left(name_row, strength = 3) + # for i,c in enumerate(cat_path_names): + # cat_path_names[i] = c.capitalize() + # cat_path_names_string = ' > '.join(cat_path_names) + # # box.label(text=cat_path) + # + # + # + # + # # name_row.label(text=' ') + # top_drag_bar.label(text=f'{cat_path_names_string} > {aname}') # left side row = layout.row(align=True) diff --git a/blenderkit/utils.py b/blenderkit/utils.py index 3d1f3eae..930fbd79 100644 --- a/blenderkit/utils.py +++ b/blenderkit/utils.py @@ -860,8 +860,22 @@ def get_fake_context(context, area_type='VIEW_3D'): # def is_url(text): -def label_multiline(layout, text='', icon='NONE', width=-1): - ''' draw a ui label, but try to split it in multiple lines.''' +def label_multiline(layout, text='', icon='NONE', width=-1, max_lines = 10): + ''' + draw a ui label, but try to split it in multiple lines. + + Parameters + ---------- + layout + text + icon + width width to split by in character count + max_lines maximum lines to draw + + Returns + ------- + True if max_lines was overstepped + ''' if text.strip() == '': return text = text.replace('\r\n','\n') @@ -870,11 +884,10 @@ def label_multiline(layout, text='', icon='NONE', width=-1): threshold = int(width / 5.5) else: threshold = 35 - maxlines = 8 li = 0 for l in lines: # if is_url(l): - + li+=1 while len(l) > threshold: i = l.rfind(' ', 0, threshold) if i < 1: @@ -884,12 +897,15 @@ def label_multiline(layout, text='', icon='NONE', width=-1): icon = 'NONE' l = l[i:].lstrip() li += 1 - if li > maxlines: + if li > max_lines: break; - if li > maxlines: + if li > max_lines: break; layout.label(text=l, icon=icon) icon = 'NONE' + if li>max_lines: + return True + def trace(): -- cgit v1.2.3 From 260ca332f88f643a302bcabf140e3c471c8c621b Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 4 Jul 2021 17:38:11 +0200 Subject: glTF importer: import custome properties from default scene --- io_scene_gltf2/__init__.py | 2 +- .../blender/exp/gltf2_blender_gather_animation_samplers.py | 1 + io_scene_gltf2/blender/imp/gltf2_blender_scene.py | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 187aac4e..a07fa7b7 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -15,7 +15,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (1, 7, 7), + "version": (1, 7, 8), 'blender': (2, 91, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py index 362e5b69..57df6306 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py @@ -331,6 +331,7 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve], object_path = get_target_object_path(target_datapath) else: object_path = None + is_armature_animation = bake_bone is not None or (blender_object_if_armature is not None and object_path != "") if is_armature_animation: diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_scene.py b/io_scene_gltf2/blender/imp/gltf2_blender_scene.py index d9dc9092..05520228 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_scene.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_scene.py @@ -17,6 +17,7 @@ import bpy from .gltf2_blender_node import BlenderNode from .gltf2_blender_animation import BlenderAnimation from .gltf2_blender_vnode import VNode, compute_vnodes +from ..com.gltf2_blender_extras import set_extras class BlenderScene(): @@ -34,6 +35,10 @@ class BlenderScene(): if scene.render.engine not in ['CYCLES', 'BLENDER_EEVEE']: scene.render.engine = "BLENDER_EEVEE" + if gltf.data.scene is not None: + pyscene = gltf.data.scenes[gltf.data.scene] + set_extras(scene, pyscene.extras) + compute_vnodes(gltf) gltf.display_current_node = 0 # for debugging -- cgit v1.2.3 From 57ef445314fef1290b155cb182ee7e513bc22008 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 4 Jul 2021 17:39:52 +0200 Subject: glTF exporter: do not export glTF internal settings as extras --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/com/gltf2_blender_extras.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index a07fa7b7..442836ed 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -15,7 +15,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (1, 7, 8), + "version": (1, 7, 9), 'blender': (2, 91, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/com/gltf2_blender_extras.py b/io_scene_gltf2/blender/com/gltf2_blender_extras.py index 3ef88222..26528aa4 100644 --- a/io_scene_gltf2/blender/com/gltf2_blender_extras.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_extras.py @@ -18,7 +18,7 @@ from .gltf2_blender_json import is_json_convertible # Custom properties, which are in most cases present and should not be imported/exported. -BLACK_LIST = ['cycles', 'cycles_visibility', 'cycles_curves', '_RNA_UI'] +BLACK_LIST = ['cycles', 'cycles_visibility', 'cycles_curves', '_RNA_UI', 'glTF2ExportSettings'] def generate_extras(blender_element): -- cgit v1.2.3 From eedbe303799fb8af4e7dafea81a0dd46973fb85b Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 4 Jul 2021 17:42:01 +0200 Subject: glTF Draco: more explicit error message --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 442836ed..5cc4e433 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -15,7 +15,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (1, 7, 9), + "version": (1, 7, 10), 'blender': (2, 91, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py b/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py index 22c44f97..e7518ac4 100644 --- a/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py +++ b/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py @@ -60,5 +60,9 @@ def dll_exists(quiet=False) -> bool: exists = dll_path().exists() if quiet is False: print("'{}' ".format(dll_path().absolute()) + ("exists, draco mesh compression is available" if exists else - "does not exist, draco mesh compression not available")) + "{} {} {}".format( + "does not exist, draco mesh compression not available,", + "please add it or create environment variable BLENDER_EXTERN_DRACO_LIBRARY_PATH", + "pointing to the folder" + ))) return exists -- cgit v1.2.3 From b986a52051d48c2cda5a38159d05fb198cc15db7 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 4 Jul 2021 17:44:28 +0200 Subject: glTF importer: fix issue involving flipped bone Z dir This is a candidate for 2.93.x LTS --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/imp/gltf2_blender_node.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 5cc4e433..efe00b85 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -15,7 +15,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (1, 7, 10), + "version": (1, 7, 11), 'blender': (2, 91, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_node.py b/io_scene_gltf2/blender/imp/gltf2_blender_node.py index ef410ad5..041bf1eb 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_node.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_node.py @@ -145,8 +145,8 @@ class BlenderNode(): arma_mat = vnode.editbone_arma_mat editbone.head = arma_mat @ Vector((0, 0, 0)) editbone.tail = arma_mat @ Vector((0, 1, 0)) - editbone.align_roll(arma_mat @ Vector((0, 0, 1)) - editbone.head) editbone.length = vnode.bone_length + editbone.align_roll(arma_mat @ Vector((0, 0, 1)) - editbone.head) if isinstance(id, int): pynode = gltf.data.nodes[id] -- cgit v1.2.3 From ac6fe2ff7c9b68b8a08e74ac3b8e350a6eb9d24d Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 4 Jul 2021 17:46:56 +0200 Subject: glTF exporter: fix bug when invalid shapekey driver --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index efe00b85..405ff3c9 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -15,7 +15,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (1, 7, 11), + "version": (1, 7, 12), 'blender': (2, 91, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py index ddbceab4..4dcad66f 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py @@ -51,7 +51,11 @@ def get_sk_drivers(blender_armature): idx_channel_mapping = [] all_sorted_channels = [] for sk_c in child.data.shape_keys.animation_data.drivers: - sk_name = child.data.shape_keys.path_resolve(get_target_object_path(sk_c.data_path)).name + # Check if driver is valid. If not, ignore this driver channel + try: + sk_name = child.data.shape_keys.path_resolve(get_target_object_path(sk_c.data_path)).name + except: + continue idx = shapekeys_idx[sk_name] idx_channel_mapping.append((shapekeys_idx[sk_name], sk_c)) existing_idx = dict(idx_channel_mapping) -- cgit v1.2.3 From 54d8bdf1f15597b9f6e1aaf308b5a0d5df785552 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 4 Jul 2021 17:48:34 +0200 Subject: glTF exporter: fix driver export when shapekey as a dot in name --- io_scene_gltf2/__init__.py | 2 +- .../blender/exp/gltf2_blender_gather_animation_samplers.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 405ff3c9..c09dc7d3 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -15,7 +15,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (1, 7, 12), + "version": (1, 7, 13), 'blender': (2, 91, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py index 57df6306..d555bcb9 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py @@ -332,7 +332,11 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve], else: object_path = None - is_armature_animation = bake_bone is not None or (blender_object_if_armature is not None and object_path != "") + # If driver_obj is set, this is a shapekey animation + if driver_obj is not None: + is_armature_animation = False + else: + is_armature_animation = bake_bone is not None or (blender_object_if_armature is not None and object_path != "") if is_armature_animation: if bake_bone is None: -- cgit v1.2.3 From 97cf91034d0d5255269a0b8c506f2cdc8e81b789 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 4 Jul 2021 17:50:07 +0200 Subject: glTF exporter: fix default date type at numpy array creation --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/exp/gltf2_blender_image.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index c09dc7d3..0f9ce6bb 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -15,7 +15,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (1, 7, 13), + "version": (1, 7, 14), 'blender': (2, 91, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_image.py b/io_scene_gltf2/blender/exp/gltf2_blender_image.py index ae16a26b..a0952bb5 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_image.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_image.py @@ -133,7 +133,7 @@ class ExportImage: if not images: # No ImageFills; use a 1x1 white pixel - pixels = np.array([1.0, 1.0, 1.0, 1.0]) + pixels = np.array([1.0, 1.0, 1.0, 1.0], np.float32) return self.__encode_from_numpy_array(pixels, (1, 1)) width = max(image.size[0] for image in images) -- cgit v1.2.3 From 0aa618c849ffef7b11cef81f1712b89e0b0e337b Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 4 Jul 2021 17:51:43 +0200 Subject: glTF exporter: Make sure that addon that changes root gltf are taken into account --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/exp/gltf2_blender_export.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 0f9ce6bb..4c758229 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -15,7 +15,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (1, 7, 14), + "version": (1, 7, 15), 'blender': (2, 91, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_export.py b/io_scene_gltf2/blender/exp/gltf2_blender_export.py index ce2b9721..5cb98479 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_export.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_export.py @@ -63,11 +63,13 @@ def __export(export_settings): __gather_gltf(exporter, export_settings) buffer = __create_buffer(exporter, export_settings) exporter.finalize_images() - json = __fix_json(exporter.glTF.to_dict()) export_user_extensions('gather_gltf_hook', export_settings, exporter.glTF) exporter.traverse_extensions() + # now that addons possibly add some fields in json, we can fix in needed + json = __fix_json(exporter.glTF.to_dict()) + return json, buffer -- cgit v1.2.3 From 0cdaac6f9a3e318b1d5db04ade2838d004cd500d Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 4 Jul 2021 17:54:15 +0200 Subject: glTF exporter: Add option to keep original texture files WARNING: if you use more than one texture, where pbr standard requires only one, only one texture will be used. This can lead to unexpected results --- io_scene_gltf2/__init__.py | 18 ++- .../blender/exp/gltf2_blender_gather_image.py | 173 +++++++++++++-------- io_scene_gltf2/blender/exp/gltf2_blender_image.py | 14 +- 3 files changed, 136 insertions(+), 69 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 4c758229..c2c028b9 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -15,7 +15,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (1, 7, 15), + "version": (1, 7, 16), 'blender': (2, 91, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', @@ -173,6 +173,16 @@ class ExportGLTF2_Base: default='', ) + export_keep_originals: BoolProperty( + name='Keep original', + description=('Keep original textures files if possible. ' + 'WARNING: if you use more than one texture, ' + 'where pbr standard requires only one, only one texture will be used.' + 'This can lead to unexpected results' + ), + default=False, + ) + export_texcoords: BoolProperty( name='UVs', description='Export UVs (texture coordinates) with meshes', @@ -517,6 +527,7 @@ class ExportGLTF2_Base: export_settings['gltf_filedirectory'], self.export_texture_dir, ) + export_settings['gltf_keep_original_textures'] = self.export_keep_originals export_settings['gltf_format'] = self.export_format export_settings['gltf_image_format'] = self.export_image_format @@ -653,7 +664,10 @@ class GLTF_PT_export_main(bpy.types.Panel): layout.prop(operator, 'export_format') if operator.export_format == 'GLTF_SEPARATE': - layout.prop(operator, 'export_texture_dir', icon='FILE_FOLDER') + layout.prop(operator, 'export_keep_originals') + if operator.export_keep_originals is False: + layout.prop(operator, 'export_texture_dir', icon='FILE_FOLDER') + layout.prop(operator, 'export_copyright') layout.prop(operator, 'will_save_settings') diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py index b0fb2c25..8e441f9c 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py @@ -42,7 +42,12 @@ def gather_image( mime_type = __gather_mime_type(blender_shader_sockets, image_data, export_settings) name = __gather_name(image_data, export_settings) - uri = __gather_uri(image_data, mime_type, name, export_settings) + if image_data.original is None: + uri = __gather_uri(image_data, mime_type, name, export_settings) + else: + # Retrieve URI relative to exported glTF files + uri = __gather_original_uri(image_data.original.filepath, export_settings) + buffer_view = __gather_buffer_view(image_data, mime_type, name, export_settings) image = __make_image( @@ -59,6 +64,27 @@ def gather_image( return image +def __gather_original_uri(original_uri, export_settings): + + def _path_to_uri(path): + import urllib + path = os.path.normpath(path) + path = path.replace(os.sep, '/') + return urllib.parse.quote(path) + + path_to_image = bpy.path.abspath(original_uri) + if not os.path.exists(path_to_image): return None + try: + rel_path = os.path.relpath( + path_to_image, + start=export_settings[gltf2_blender_export_keys.FILE_DIRECTORY], + ) + except ValueError: + # eg. because no relative path between C:\ and D:\ on Windows + return None + return _path_to_uri(rel_path) + + @cached def __make_image(buffer_view, extensions, extras, mime_type, name, uri, export_settings): return gltf2_io.Image( @@ -99,7 +125,12 @@ def __gather_mime_type(sockets, export_image, export_settings): return "image/png" if export_settings["gltf_image_format"] == "AUTO": - image = export_image.blender_image() + if export_image.original is None: # We are going to create a new image + image = export_image.blender_image() + else: + # Using original image + image = export_image.original + if image is not None and __is_blender_image_a_jpeg(image): return "image/jpeg" return "image/png" @@ -109,30 +140,33 @@ def __gather_mime_type(sockets, export_image, export_settings): def __gather_name(export_image, export_settings): - # Find all Blender images used in the ExportImage - imgs = [] - for fill in export_image.fills.values(): - if isinstance(fill, FillImage): - img = fill.image - if img not in imgs: - imgs.append(img) - - # If all the images have the same path, use the common filename - filepaths = set(img.filepath for img in imgs) - if len(filepaths) == 1: - filename = os.path.basename(list(filepaths)[0]) - name, extension = os.path.splitext(filename) - if extension.lower() in ['.png', '.jpg', '.jpeg']: - if name: - return name - - # Combine the image names: img1-img2-img3 - names = [] - for img in imgs: - name, extension = os.path.splitext(img.name) - names.append(name) - name = '-'.join(names) - return name or 'Image' + if export_image.original is None: + # Find all Blender images used in the ExportImage + imgs = [] + for fill in export_image.fills.values(): + if isinstance(fill, FillImage): + img = fill.image + if img not in imgs: + imgs.append(img) + + # If all the images have the same path, use the common filename + filepaths = set(img.filepath for img in imgs) + if len(filepaths) == 1: + filename = os.path.basename(list(filepaths)[0]) + name, extension = os.path.splitext(filename) + if extension.lower() in ['.png', '.jpg', '.jpeg']: + if name: + return name + + # Combine the image names: img1-img2-img3 + names = [] + for img in imgs: + name, extension = os.path.splitext(img.name) + names.append(name) + name = '-'.join(names) + return name or 'Image' + else: + return export_image.original.name @cached @@ -161,46 +195,55 @@ def __get_image_data(sockets, export_settings) -> ExportImage: result.shader_node.image)) continue - # rudimentarily try follow the node tree to find the correct image data. - src_chan = Channel.R - for elem in result.path: - if isinstance(elem.from_node, bpy.types.ShaderNodeSeparateRGB): - src_chan = { - 'R': Channel.R, - 'G': Channel.G, - 'B': Channel.B, - }[elem.from_socket.name] - if elem.from_socket.name == 'Alpha': - src_chan = Channel.A - - dst_chan = None - - # some sockets need channel rewriting (gltf pbr defines fixed channels for some attributes) - if socket.name == 'Metallic': - dst_chan = Channel.B - elif socket.name == 'Roughness': - dst_chan = Channel.G - elif socket.name == 'Occlusion': - dst_chan = Channel.R - elif socket.name == 'Alpha': - dst_chan = Channel.A - elif socket.name == 'Clearcoat': - dst_chan = Channel.R - elif socket.name == 'Clearcoat Roughness': - dst_chan = Channel.G - - if dst_chan is not None: - composed_image.fill_image(result.shader_node.image, dst_chan, src_chan) - - # Since metal/roughness are always used together, make sure - # the other channel is filled. - if socket.name == 'Metallic' and not composed_image.is_filled(Channel.G): - composed_image.fill_white(Channel.G) - elif socket.name == 'Roughness' and not composed_image.is_filled(Channel.B): - composed_image.fill_white(Channel.B) + # Assume that user know what he does, and that channels/images are already combined correctly for pbr + # If not, we are going to keep only the first texture found + # Example : If user set up 2 or 3 different textures for Metallic / Roughness / Occlusion + # Only 1 will be used at export + # This Warning is displayed in UI of this option + if export_settings['gltf_keep_original_textures']: + composed_image = ExportImage.from_original(result.shader_node.image) + else: - # copy full image...eventually following sockets might overwrite things - composed_image = ExportImage.from_blender_image(result.shader_node.image) + # rudimentarily try follow the node tree to find the correct image data. + src_chan = Channel.R + for elem in result.path: + if isinstance(elem.from_node, bpy.types.ShaderNodeSeparateRGB): + src_chan = { + 'R': Channel.R, + 'G': Channel.G, + 'B': Channel.B, + }[elem.from_socket.name] + if elem.from_socket.name == 'Alpha': + src_chan = Channel.A + + dst_chan = None + + # some sockets need channel rewriting (gltf pbr defines fixed channels for some attributes) + if socket.name == 'Metallic': + dst_chan = Channel.B + elif socket.name == 'Roughness': + dst_chan = Channel.G + elif socket.name == 'Occlusion': + dst_chan = Channel.R + elif socket.name == 'Alpha': + dst_chan = Channel.A + elif socket.name == 'Clearcoat': + dst_chan = Channel.R + elif socket.name == 'Clearcoat Roughness': + dst_chan = Channel.G + + if dst_chan is not None: + composed_image.fill_image(result.shader_node.image, dst_chan, src_chan) + + # Since metal/roughness are always used together, make sure + # the other channel is filled. + if socket.name == 'Metallic' and not composed_image.is_filled(Channel.G): + composed_image.fill_white(Channel.G) + elif socket.name == 'Roughness' and not composed_image.is_filled(Channel.B): + composed_image.fill_white(Channel.B) + else: + # copy full image...eventually following sockets might overwrite things + composed_image = ExportImage.from_blender_image(result.shader_node.image) return composed_image diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_image.py b/io_scene_gltf2/blender/exp/gltf2_blender_image.py index a0952bb5..8ac272d8 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_image.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_image.py @@ -64,9 +64,12 @@ class ExportImage: intelligent decisions about how to encode the image. """ - def __init__(self): + def __init__(self, original=None): self.fills = {} + # In case of keeping original texture images + self.original = original + @staticmethod def from_blender_image(image: bpy.types.Image): export_image = ExportImage() @@ -74,6 +77,10 @@ class ExportImage: export_image.fill_image(image, dst_chan=chan, src_chan=chan) return export_image + @staticmethod + def from_original(image: bpy.types.Image): + return ExportImage(image) + def fill_image(self, image: bpy.types.Image, dst_chan: Channel, src_chan: Channel): self.fills[dst_chan] = FillImage(image, src_chan) @@ -84,7 +91,10 @@ class ExportImage: return chan in self.fills def empty(self) -> bool: - return not self.fills + if self.original is None: + return not self.fills + else: + return False def blender_image(self) -> Optional[bpy.types.Image]: """If there's an existing Blender image we can use, -- cgit v1.2.3 From 6c1a227b3fa329d56bdaf0e4ebd6d72aa92d2bfe Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 4 Jul 2021 19:41:07 +0200 Subject: glTF exporter: dont erase empty dict extras --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/exp/gltf2_blender_export.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index c2c028b9..92126ac9 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -15,7 +15,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (1, 7, 16), + "version": (1, 7, 17), 'blender': (2, 91, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_export.py b/io_scene_gltf2/blender/exp/gltf2_blender_export.py index 5cb98479..522f13de 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_export.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_export.py @@ -106,6 +106,9 @@ def __fix_json(obj): if isinstance(obj, dict): fixed = {} for key, value in obj.items(): + if key == 'extras' and value is not None: + fixed[key] = value + continue if not __should_include_json_value(key, value): continue fixed[key] = __fix_json(value) -- cgit v1.2.3 From 6a2c44135019377ae124a0b358c8539f64057c11 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 5 Jul 2021 10:54:29 +0200 Subject: Node Wrangler: change preview shortcut for geometry nodes The ctr+shift+LMB was in conflict with the new viewer node introduced in rB9009ac2c3d62e0d30d96b8d35ff5ff620cfe053b. Since the builtin viewer node does not support viewport visualization yet, the operator is still kept in node wrangler. Just the shortcut is changed to shift+alt+LMB. Differential Revision: https://developer.blender.org/D11786 --- node_wrangler.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/node_wrangler.py b/node_wrangler.py index 118408c4..cf70308b 100644 --- a/node_wrangler.py +++ b/node_wrangler.py @@ -1710,6 +1710,11 @@ class NWPreviewNode(Operator, NWBase): bl_description = "Connect active node to Emission Shader for shadeless previews, or to the geometry node tree's output" bl_options = {'REGISTER', 'UNDO'} + # If false, the operator is not executed if the current node group happens to be a geometry nodes group. + # This is needed because geometry nodes has its own viewer node that uses the same shortcut as in the compositor. + # Geometry Nodes support can be removed here once the viewer node is supported in the viewport. + run_in_geometry_nodes: BoolProperty(default=True) + def __init__(self): self.shader_output_type = "" self.shader_output_ident = "" @@ -1864,6 +1869,10 @@ class NWPreviewNode(Operator, NWBase): def invoke(self, context, event): space = context.space_data + # Ignore operator when running in wrong context. + if self.run_in_geometry_nodes != (space.tree_type == "GeometryNodeTree"): + return {'PASS_THROUGH'} + shader_type = space.shader_type self.init_shader_variables(space, shader_type) shader_types = [x[1] for x in shaders_shader_nodes_props] @@ -5206,7 +5215,8 @@ kmi_defs = ( # Swap Outputs (NWSwapLinks.bl_idname, 'S', 'PRESS', False, False, True, None, "Swap Outputs"), # Preview Node - (NWPreviewNode.bl_idname, 'LEFTMOUSE', 'PRESS', True, True, False, None, "Preview node output"), + (NWPreviewNode.bl_idname, 'LEFTMOUSE', 'PRESS', True, True, False, (('run_in_geometry_nodes', False),), "Preview node output"), + (NWPreviewNode.bl_idname, 'LEFTMOUSE', 'PRESS', False, True, True, (('run_in_geometry_nodes', True),), "Preview node output"), # Reload Images (NWReloadImages.bl_idname, 'R', 'PRESS', False, False, True, None, "Reload images"), # Lazy Mix -- cgit v1.2.3