Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSybren A. Stüvel <sybren@stuvel.eu>2021-07-05 18:35:53 +0300
committerSybren A. Stüvel <sybren@stuvel.eu>2021-07-05 18:35:53 +0300
commit022cd94be488327a1635a56e1bf84d6f9e6405cb (patch)
tree87d659d76eeeab5509bead4f27c5935ad1e33793
parent55b0e323ba2e8b09a27db0817b30a3978e2b45f6 (diff)
parent6a2c44135019377ae124a0b358c8539f64057c11 (diff)
Merge remote-tracking branch 'origin/master' into asset-browser-poselib
-rw-r--r--blenderkit/categories.py13
-rw-r--r--blenderkit/paths.py2
-rw-r--r--blenderkit/search.py30
-rw-r--r--blenderkit/ui_panels.py47
-rw-r--r--blenderkit/utils.py28
-rwxr-xr-xio_scene_gltf2/__init__.py18
-rw-r--r--io_scene_gltf2/blender/com/gltf2_blender_extras.py2
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_export.py7
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py7
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py6
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_image.py173
-rw-r--r--io_scene_gltf2/blender/exp/gltf2_blender_image.py16
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_node.py2
-rwxr-xr-xio_scene_gltf2/blender/imp/gltf2_blender_scene.py5
-rw-r--r--io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py6
-rw-r--r--node_wrangler.py12
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