diff options
author | Sybren A. Stüvel <sybren@stuvel.eu> | 2021-07-05 18:35:53 +0300 |
---|---|---|
committer | Sybren A. Stüvel <sybren@stuvel.eu> | 2021-07-05 18:35:53 +0300 |
commit | 022cd94be488327a1635a56e1bf84d6f9e6405cb (patch) | |
tree | 87d659d76eeeab5509bead4f27c5935ad1e33793 | |
parent | 55b0e323ba2e8b09a27db0817b30a3978e2b45f6 (diff) | |
parent | 6a2c44135019377ae124a0b358c8539f64057c11 (diff) |
Merge remote-tracking branch 'origin/master' into asset-browser-poselib
-rw-r--r-- | blenderkit/categories.py | 13 | ||||
-rw-r--r-- | blenderkit/paths.py | 2 | ||||
-rw-r--r-- | blenderkit/search.py | 30 | ||||
-rw-r--r-- | blenderkit/ui_panels.py | 47 | ||||
-rw-r--r-- | blenderkit/utils.py | 28 | ||||
-rwxr-xr-x | io_scene_gltf2/__init__.py | 18 | ||||
-rw-r--r-- | io_scene_gltf2/blender/com/gltf2_blender_extras.py | 2 | ||||
-rwxr-xr-x | io_scene_gltf2/blender/exp/gltf2_blender_export.py | 7 | ||||
-rwxr-xr-x | io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py | 7 | ||||
-rw-r--r-- | io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py | 6 | ||||
-rwxr-xr-x | io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py | 173 | ||||
-rw-r--r-- | io_scene_gltf2/blender/exp/gltf2_blender_image.py | 16 | ||||
-rwxr-xr-x | io_scene_gltf2/blender/imp/gltf2_blender_node.py | 2 | ||||
-rwxr-xr-x | io_scene_gltf2/blender/imp/gltf2_blender_scene.py | 5 | ||||
-rw-r--r-- | io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py | 6 | ||||
-rw-r--r-- | node_wrangler.py | 12 |
16 files changed, 278 insertions, 96 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(): diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 187aac4e..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, 7), + "version": (1, 7, 17), '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/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): diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_export.py b/io_scene_gltf2/blender/exp/gltf2_blender_export.py index ce2b9721..522f13de 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 @@ -104,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) 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..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 @@ -331,7 +331,12 @@ 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 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: 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) 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 ae16a26b..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, @@ -133,7 +143,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) 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] 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 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 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 |