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:
authorJulien Duroure <julien.duroure@gmail.com>2019-01-09 21:46:45 +0300
committerJulien Duroure <julien.duroure@gmail.com>2019-01-09 21:46:45 +0300
commitb410a70fa9ba09bf9c1091d87f38f0ed8dae4895 (patch)
tree1d29e31952f861dbf02f8433f6e6861f5607f587 /io_scene_gltf2/blender/exp
parent52111c31d7d0d39893b11a5242cbfe2bfbda0d12 (diff)
glTF export: enhancement & fixes:
* implement KHR_materials_unlit export * Fix primitive restart values * Fix jpg uri/mime type * Fix bug when image has no color channels * Check animation has actions * Ignore meshes without primitives * Fix materials when not selected in export settings * Improve error message for invalid animation target type * Animation with errors are ignored, but export continues * Export of BaseColorFactor
Diffstat (limited to 'io_scene_gltf2/blender/exp')
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_extract.py7
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py2
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py23
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_image.py23
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py11
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py51
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py4
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py12
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_get.py3
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py2
-rwxr-xr-xio_scene_gltf2/blender/exp/gltf2_blender_search_node_tree.py10
11 files changed, 115 insertions, 33 deletions
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_extract.py b/io_scene_gltf2/blender/exp/gltf2_blender_extract.py
index 0add794a..42be29b7 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_extract.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_extract.py
@@ -975,7 +975,12 @@ def extract_primitives(glTF, blender_mesh, blender_vertex_groups, modifiers, exp
#
- range_indices = 65536
+ # NOTE: Values used by some graphics APIs as "primitive restart" values are disallowed.
+ # Specifically, the value 65535 (in UINT16) cannot be used as a vertex index.
+ # https://github.com/KhronosGroup/glTF/issues/1142
+ # https://github.com/KhronosGroup/glTF/pull/1476/files
+
+ range_indices = 65535
#
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py
index 6ef7fb0b..351a036a 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py
@@ -46,7 +46,7 @@ class Keyframe:
}.get(self.__target)
if length is None:
- raise RuntimeError("Unknown target type {}".format(self.__target))
+ raise RuntimeError("Animations with target type '{}' are not supported.".format(self.__target))
return length
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py
index bfbc03ed..7a7cda02 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py
@@ -17,6 +17,7 @@ import typing
from io_scene_gltf2.io.com import gltf2_io
from io_scene_gltf2.blender.exp import gltf2_blender_gather_animation_channels
+from io_scene_gltf2.io.com.gltf2_io_debug import print_console
def gather_animations(blender_object: bpy.types.Object, export_settings) -> typing.List[gltf2_io.Animation]:
@@ -48,13 +49,18 @@ def __gather_animation(blender_action: bpy.types.Action,
if not __filter_animation(blender_action, blender_object, export_settings):
return None
- animation = gltf2_io.Animation(
- channels=__gather_channels(blender_action, blender_object, export_settings),
- extensions=__gather_extensions(blender_action, blender_object, export_settings),
- extras=__gather_extras(blender_action, blender_object, export_settings),
- name=__gather_name(blender_action, blender_object, export_settings),
- samplers=__gather_samplers(blender_action, blender_object, export_settings)
- )
+ name = __gather_name(blender_action, blender_object, export_settings)
+ try:
+ animation = gltf2_io.Animation(
+ channels=__gather_channels(blender_action, blender_object, export_settings),
+ extensions=__gather_extensions(blender_action, blender_object, export_settings),
+ extras=__gather_extras(blender_action, blender_object, export_settings),
+ name=name,
+ samplers=__gather_samplers(blender_action, blender_object, export_settings)
+ )
+ except RuntimeError as error:
+ print_console("WARNING", "Animation '{}' could not be exported. Cause: {}".format(name, error))
+ return None
# To allow reuse of samplers in one animation,
__link_samplers(animation, export_settings)
@@ -159,7 +165,8 @@ def __get_blender_actions(blender_object: bpy.types.Object
if blender_object.type == "MESH" \
and blender_object.data is not None \
and blender_object.data.shape_keys is not None \
- and blender_object.data.shape_keys.animation_data is not None:
+ and blender_object.data.shape_keys.animation_data is not None \
+ and blender_object.data.shape_keys.animation_data.action is not None:
blender_actions.append(blender_object.data.shape_keys.animation_data.action)
# Remove duplicate actions.
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 a694cac3..6b969668 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py
@@ -11,6 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+import re
import bpy
import typing
@@ -30,13 +31,17 @@ def gather_image(
export_settings):
if not __filter_image(blender_shader_sockets_or_texture_slots, export_settings):
return None
+
+ uri = __gather_uri(blender_shader_sockets_or_texture_slots, export_settings)
+ mime_type = __gather_mime_type(uri.filepath if uri is not None else "")
+
image = gltf2_io.Image(
buffer_view=__gather_buffer_view(blender_shader_sockets_or_texture_slots, export_settings),
extensions=__gather_extensions(blender_shader_sockets_or_texture_slots, export_settings),
extras=__gather_extras(blender_shader_sockets_or_texture_slots, export_settings),
- mime_type=__gather_mime_type(blender_shader_sockets_or_texture_slots, export_settings),
+ mime_type=mime_type,
name=__gather_name(blender_shader_sockets_or_texture_slots, export_settings),
- uri=__gather_uri(blender_shader_sockets_or_texture_slots, export_settings)
+ uri=uri
)
return image
@@ -51,7 +56,7 @@ def __gather_buffer_view(sockets_or_slots, export_settings):
if export_settings[gltf2_blender_export_keys.FORMAT] != 'GLTF_SEPARATE':
image = __get_image_data(sockets_or_slots, export_settings)
return gltf2_io_binary_data.BinaryData(
- data=image.to_image_data(__gather_mime_type(sockets_or_slots, export_settings)))
+ data=image.to_image_data(__gather_mime_type()))
return None
@@ -63,9 +68,13 @@ def __gather_extras(sockets_or_slots, export_settings):
return None
-def __gather_mime_type(sockets_or_slots, export_settings):
- return 'image/png'
- # return 'image/jpeg'
+def __gather_mime_type(filepath=""):
+ extension_types = {'.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg'}
+ default_extension = extension_types['.png']
+
+ matches = re.findall(r'\.\w+$', filepath)
+ extension = matches[0] if len(matches) > 0 else default_extension
+ return extension_types[extension] if extension.lower() in extension_types.keys() else default_extension
def __gather_name(sockets_or_slots, export_settings):
@@ -98,6 +107,8 @@ def __get_image_data(sockets_or_slots, export_settings):
# in a helper class. During generation of the glTF in the exporter these will then be combined to actual binary
# ressources.
def split_pixels_by_channels(image: bpy.types.Image, export_settings) -> typing.List[typing.List[float]]:
+ assert image.channels > 0, "Image '{}' has no color channels and cannot be exported.".format(image.name)
+
channelcache = export_settings['gltf_channelcache']
if image.name in channelcache:
return channelcache[image.name]
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py
index 427f07ce..e1c17480 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py
@@ -16,7 +16,8 @@ import bpy
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
from io_scene_gltf2.io.com import gltf2_io
-from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info
+from io_scene_gltf2.io.com.gltf2_io_extensions import Extension
+from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info, gltf2_blender_export_keys
from io_scene_gltf2.blender.exp import gltf2_blender_gather_material_normal_texture_info_class
from io_scene_gltf2.blender.exp import gltf2_blender_gather_material_occlusion_texture_info_class
@@ -69,11 +70,7 @@ def gather_material(blender_material, export_settings):
def __filter_material(blender_material, export_settings):
- # if not blender_material.use_nodes:
- # return False
- # if not blender_material.node_tree:
- # return False
- return True
+ return export_settings[gltf2_blender_export_keys.MATERIALS]
def __gather_alpha_cutoff(blender_material, export_settings):
@@ -113,6 +110,8 @@ def __gather_emissive_texture(blender_material, export_settings):
def __gather_extensions(blender_material, export_settings):
extensions = {}
+ if gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Background") is not None:
+ extensions["KHR_materials_unlit"] = Extension("KHR_materials_unlit", {}, False)
# TODO specular glossiness extension
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py
index 2a6315bf..67e7fa12 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py
@@ -13,11 +13,13 @@
# limitations under the License.
import bpy
+from mathutils import Color
from io_scene_gltf2.io.com import gltf2_io
-from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info
+from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info, gltf2_blender_search_node_tree
from io_scene_gltf2.blender.exp import gltf2_blender_get
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
+from io_scene_gltf2.io.com.gltf2_io_debug import print_console
@cached
@@ -48,9 +50,41 @@ def __gather_base_color_factor(blender_material, export_settings):
base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "BaseColor")
if base_color_socket is None:
base_color_socket = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "BaseColorFactor")
- if isinstance(base_color_socket, bpy.types.NodeSocket) and not base_color_socket.is_linked:
+ if base_color_socket is None:
+ base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Background")
+ if not isinstance(base_color_socket, bpy.types.NodeSocket):
+ return None
+ if not base_color_socket.is_linked:
return list(base_color_socket.default_value)
- return None
+
+ texture_node = __get_tex_from_socket(base_color_socket)
+ if texture_node is None:
+ return None
+
+ def is_valid_multiply_node(node):
+ return isinstance(node, bpy.types.ShaderNodeMixRGB) and \
+ node.blend_type == "MULTIPLY" and \
+ len(node.inputs) == 3
+
+ multiply_node = next((link.from_node for link in texture_node.path if is_valid_multiply_node(link.from_node)), None)
+ if multiply_node is None:
+ return None
+
+ def is_factor_socket(socket):
+ return isinstance(socket, bpy.types.NodeSocketColor) and \
+ (not socket.is_linked or socket.links[0] not in texture_node.path)
+
+ factor_socket = next((socket for socket in multiply_node.inputs if is_factor_socket(socket)), None)
+ if factor_socket is None:
+ return None
+
+ if factor_socket.is_linked:
+ print_console("WARNING", "BaseColorFactor only supports sockets without links (in Node '{}')."
+ .format(multiply_node.name))
+ return None
+
+ return list(factor_socket.default_value)
+
def __gather_base_color_texture(blender_material, export_settings):
base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Base Color")
@@ -58,9 +92,20 @@ def __gather_base_color_texture(blender_material, export_settings):
base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "BaseColor")
if base_color_socket is None:
base_color_socket = gltf2_blender_get.get_socket_or_texture_slot_old(blender_material, "BaseColor")
+ if base_color_socket is None:
+ base_color_socket = gltf2_blender_get.get_socket_or_texture_slot(blender_material, "Background")
return gltf2_blender_gather_texture_info.gather_texture_info((base_color_socket,), export_settings)
+def __get_tex_from_socket(blender_shader_socket: bpy.types.NodeSocket):
+ result = gltf2_blender_search_node_tree.from_socket(
+ blender_shader_socket,
+ gltf2_blender_search_node_tree.FilterByType(bpy.types.ShaderNodeTexImage))
+ if not result:
+ return None
+ return result[0]
+
+
def __gather_extensions(blender_material, export_settings):
return None
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py
index f32eb733..d6058c60 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_mesh.py
@@ -18,6 +18,7 @@ from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
from io_scene_gltf2.io.com import gltf2_io
from io_scene_gltf2.blender.exp import gltf2_blender_gather_primitives
from io_scene_gltf2.blender.exp import gltf2_blender_generate_extras
+from io_scene_gltf2.io.com.gltf2_io_debug import print_console
@cached
@@ -38,6 +39,9 @@ def gather_mesh(blender_mesh: bpy.types.Mesh,
weights=__gather_weights(blender_mesh, vertex_groups, modifiers, export_settings)
)
+ if len(mesh.primitives) == 0:
+ print_console("WARNING", "Mesh '{}' has no primitives and will be omitted.".format(mesh.name))
+ return None
return mesh
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py
index 23d51fa6..bf81e808 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py
@@ -72,13 +72,19 @@ def __gather_materials(blender_primitive, blender_mesh, modifiers, export_settin
def __gather_indices(blender_primitive, blender_mesh, modifiers, export_settings):
indices = blender_primitive['indices']
+ # NOTE: Values used by some graphics APIs as "primitive restart" values are disallowed.
+ # Specifically, the values 65535 (in UINT16) and 4294967295 (in UINT32) cannot be used as indices.
+ # https://github.com/KhronosGroup/glTF/issues/1142
+ # https://github.com/KhronosGroup/glTF/pull/1476/files
+ # Also, UINT8 mode is not supported:
+ # https://github.com/KhronosGroup/glTF/issues/1471
max_index = max(indices)
- if max_index < (1 << 16):
+ if max_index < 65535:
component_type = gltf2_io_constants.ComponentType.UnsignedShort
- elif max_index < (1 << 32):
+ elif max_index < 4294967295:
component_type = gltf2_io_constants.ComponentType.UnsignedInt
else:
- print_console('ERROR', 'Invalid max_index: ' + str(max_index))
+ print_console('ERROR', 'A mesh contains too many vertices (' + str(max_index) + ') and needs to be split before export.')
return None
element_type = gltf2_io_constants.DataType.Scalar
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_get.py b/io_scene_gltf2/blender/exp/gltf2_blender_get.py
index 7801190b..08e3a307 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_get.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_get.py
@@ -48,6 +48,9 @@ def get_socket_or_texture_slot(blender_material: bpy.types.Material, name: str):
if name == "Emissive":
type = bpy.types.ShaderNodeEmission
name = "Color"
+ elif name == "Background":
+ type = bpy.types.ShaderNodeBackground
+ name = "Color"
else:
type = bpy.types.ShaderNodeBsdfPrincipled
nodes = [n for n in blender_material.node_tree.nodes if isinstance(n, type)]
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py b/io_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py
index 65efafef..ea26eb1c 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py
@@ -215,7 +215,7 @@ class GlTF2Exporter:
# TODO: we need to know the image url at this point already --> maybe add all options to the constructor of the
# exporter
# TODO: allow embedding of images (base64)
- return image.name + ".png"
+ return image.name + image.get_extension()
@classmethod
def __get_key_path(cls, d: dict, keypath: List[str], default=[]):
diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_search_node_tree.py b/io_scene_gltf2/blender/exp/gltf2_blender_search_node_tree.py
index 92b63c7d..72eb5252 100755
--- a/io_scene_gltf2/blender/exp/gltf2_blender_search_node_tree.py
+++ b/io_scene_gltf2/blender/exp/gltf2_blender_search_node_tree.py
@@ -82,14 +82,16 @@ def from_socket(start_socket: bpy.types.NodeSocket,
for link in start_socket.links:
# follow the link to a shader node
linked_node = link.from_node
- # add the link to the current path
- search_path.append(link)
# check if the node matches the filter
if shader_node_filter(linked_node):
- results.append(NodeTreeSearchResult(linked_node, search_path))
+ results.append(NodeTreeSearchResult(linked_node, search_path + [link]))
# traverse into inputs of the node
for input_socket in linked_node.inputs:
- results += __search_from_socket(input_socket, shader_node_filter, search_path)
+ linked_results = __search_from_socket(input_socket, shader_node_filter, search_path + [link])
+ if linked_results:
+ # add the link to the current path
+ search_path.append(link)
+ results += linked_results
return results