FBX IO: Only import the first animation curve per channel #104866
@ -5,7 +5,7 @@
|
||||
bl_info = {
|
||||
"name": "FBX format",
|
||||
"author": "Campbell Barton, Bastien Montagne, Jens Restemeier, @Mysteryem",
|
||||
"version": (5, 7, 0),
|
||||
"version": (5, 7, 1),
|
||||
"blender": (3, 6, 0),
|
||||
"location": "File > Import-Export",
|
||||
"description": "FBX IO meshes, UVs, vertex colors, materials, textures, cameras, lamps and actions",
|
||||
|
@ -635,7 +635,8 @@ def _combine_curve_keyframe_times(times_and_values_tuples, initial_values):
|
||||
interpolating the keyframe values is a TODO."""
|
||||
if len(times_and_values_tuples) == 1:
|
||||
# Nothing to do when there is only a single curve.
|
||||
return times_and_values_tuples[0]
|
||||
times, values = times_and_values_tuples[0]
|
||||
return times, [values]
|
||||
|
||||
all_times = [t[0] for t in times_and_values_tuples]
|
||||
|
||||
|
@ -5,7 +5,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": (4, 0, 7),
|
||||
"version": (4, 0, 15),
|
||||
'blender': (4, 0, 0),
|
||||
'location': 'File > Import-Export',
|
||||
'description': 'Import-Export as glTF 2.0',
|
||||
|
@ -119,7 +119,8 @@ def get_numpy_type(attribute_component_type):
|
||||
def get_attribute_type(component_type, data_type):
|
||||
if gltf2_io_constants.DataType.num_elements(data_type) == 1:
|
||||
return {
|
||||
gltf2_io_constants.ComponentType.Float: "FLOAT"
|
||||
gltf2_io_constants.ComponentType.Float: "FLOAT",
|
||||
gltf2_io_constants.ComponentType.UnsignedByte: "INT" # What is the best for compatibility?
|
||||
}[component_type]
|
||||
elif gltf2_io_constants.DataType.num_elements(data_type) == 2:
|
||||
return {
|
||||
@ -132,7 +133,8 @@ def get_attribute_type(component_type, data_type):
|
||||
elif gltf2_io_constants.DataType.num_elements(data_type) == 4:
|
||||
return {
|
||||
gltf2_io_constants.ComponentType.Float: "FLOAT_COLOR",
|
||||
gltf2_io_constants.ComponentType.UnsignedShort: "BYTE_COLOR"
|
||||
gltf2_io_constants.ComponentType.UnsignedShort: "BYTE_COLOR",
|
||||
gltf2_io_constants.ComponentType.UnsignedByte: "BYTE_COLOR" # What is the best for compatibility?
|
||||
}[component_type]
|
||||
else:
|
||||
pass
|
||||
|
@ -45,10 +45,19 @@ def is_bone_anim_channel(data_path: str) -> bool:
|
||||
|
||||
def get_sk_exported(key_blocks):
|
||||
return [
|
||||
key_block
|
||||
for key_block in key_blocks
|
||||
if not skip_sk(key_block)
|
||||
k
|
||||
for k in key_blocks
|
||||
if not skip_sk(key_blocks, k)
|
||||
]
|
||||
|
||||
def skip_sk(k):
|
||||
return k == k.relative_key or k.mute
|
||||
def skip_sk(key_blocks, k):
|
||||
# Do not export:
|
||||
# - if muted
|
||||
# - if relative key is SK itself (this avoid exporting Basis too if user didn't change order)
|
||||
# - the Basis (the first SK of the list)
|
||||
return k == k.relative_key \
|
||||
or k.mute \
|
||||
or is_first_index(key_blocks, k) is True
|
||||
|
||||
def is_first_index(key_blocks, k):
|
||||
return key_blocks[0].name == k.name
|
||||
|
@ -30,7 +30,7 @@ def gather_animation_fcurves_channels(
|
||||
custom_range = (blender_action.frame_start, blender_action.frame_end)
|
||||
|
||||
channels = []
|
||||
for chan in [chan for chan in channels_to_perform.values() if len(chan['properties']) != 0]:
|
||||
for chan in [chan for chan in channels_to_perform.values() if len(chan['properties']) != 0 and chan['type'] != "EXTRA"]:
|
||||
for channel_group in chan['properties'].values():
|
||||
channel = __gather_animation_fcurve_channel(chan['obj_uuid'], channel_group, chan['bone'], custom_range, export_settings)
|
||||
if channel is not None:
|
||||
@ -73,10 +73,13 @@ def get_channel_groups(obj_uuid: str, blender_action: bpy.types.Action, export_s
|
||||
else:
|
||||
try:
|
||||
target = get_object_from_datapath(blender_object, object_path)
|
||||
type_ = "BONE"
|
||||
if blender_object.type == "ARMATURE" and fcurve.data_path.startswith("pose.bones["):
|
||||
type_ = "BONE"
|
||||
else:
|
||||
type_ = "EXTRA"
|
||||
if blender_object.type == "MESH" and object_path.startswith("key_blocks"):
|
||||
shape_key = blender_object.data.shape_keys.path_resolve(object_path)
|
||||
if skip_sk(shape_key):
|
||||
if skip_sk(blender_object.data.shape_keys.key_blocks, shape_key):
|
||||
continue
|
||||
target = blender_object.data.shape_keys
|
||||
type_ = "SK"
|
||||
@ -86,7 +89,7 @@ def get_channel_groups(obj_uuid: str, blender_action: bpy.types.Action, export_s
|
||||
if blender_object.type == "MESH":
|
||||
try:
|
||||
shape_key = blender_object.data.shape_keys.path_resolve(object_path)
|
||||
if skip_sk(shape_key):
|
||||
if skip_sk(blender_object.data.shape_keys.key_blocks, shape_key):
|
||||
continue
|
||||
target = blender_object.data.shape_keys
|
||||
type_ = "SK"
|
||||
@ -181,7 +184,7 @@ def __get_channel_group_sorted(channels: typing.Tuple[bpy.types.FCurve], blender
|
||||
shapekeys_idx = {}
|
||||
cpt_sk = 0
|
||||
for sk in blender_object.data.shape_keys.key_blocks:
|
||||
if skip_sk(sk):
|
||||
if skip_sk(blender_object.data.shape_keys.key_blocks, sk):
|
||||
continue
|
||||
shapekeys_idx[sk.name] = cpt_sk
|
||||
cpt_sk += 1
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
import bpy
|
||||
import typing
|
||||
from .....blender.com.gltf2_blender_data_path import skip_sk
|
||||
from .....blender.com.gltf2_blender_data_path import get_sk_exported
|
||||
from ....com.gltf2_blender_data_path import get_target_object_path
|
||||
from ...gltf2_blender_gather_cache import cached
|
||||
from ..gltf2_blender_gather_keyframes import Keyframe
|
||||
@ -165,9 +165,7 @@ def __gather_non_keyed_values(
|
||||
if object_path:
|
||||
shapekeys_idx = {}
|
||||
cpt_sk = 0
|
||||
for sk in blender_object.data.shape_keys.key_blocks:
|
||||
if skip_sk(sk):
|
||||
continue
|
||||
for sk in get_sk_exported(blender_object.data.shape_keys.key_blocks):
|
||||
shapekeys_idx[cpt_sk] = sk.name
|
||||
cpt_sk += 1
|
||||
|
||||
|
@ -294,6 +294,8 @@ def gather_action_animations( obj_uuid: int,
|
||||
channel = gather_sampled_object_channel(obj_uuid, prop, blender_action.name, True, get_gltf_interpolation("LINEAR"), export_settings)
|
||||
elif type_ == "SK":
|
||||
channel = gather_sampled_sk_channel(obj_uuid, blender_action.name, export_settings)
|
||||
elif type_ == "EXTRA":
|
||||
channel = None
|
||||
else:
|
||||
print("Type unknown. Should not happen")
|
||||
|
||||
|
@ -61,7 +61,7 @@ def get_sk_drivers(blender_armature_uuid, export_settings):
|
||||
sk_name = child.data.shape_keys.path_resolve(get_target_object_path(sk_c.data_path)).name
|
||||
except:
|
||||
continue
|
||||
if skip_sk(child.data.shape_keys.key_blocks[sk_name]):
|
||||
if skip_sk(child.data.shape_keys.key_blocks, child.data.shape_keys.key_blocks[sk_name]):
|
||||
continue
|
||||
idx_channel_mapping.append((shapekeys_idx[sk_name], sk_c))
|
||||
existing_idx = dict(idx_channel_mapping)
|
||||
|
@ -6,6 +6,7 @@ import math
|
||||
import bpy
|
||||
from mathutils import Matrix, Quaternion, Vector
|
||||
|
||||
from ...io.com.gltf2_io_debug import print_console
|
||||
from ...io.com import gltf2_io
|
||||
from ...io.com import gltf2_io_extensions
|
||||
from ...io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||
@ -181,7 +182,9 @@ def __gather_mesh(vnode, blender_object, export_settings):
|
||||
return None
|
||||
|
||||
# Be sure that object is valid (no NaN for example)
|
||||
blender_object.data.validate()
|
||||
res = blender_object.data.validate()
|
||||
if res is True:
|
||||
print_console("WARNING", "Mesh " + blender_object.data.name + " is not valid, and may be exported wrongly")
|
||||
|
||||
modifiers = blender_object.modifiers
|
||||
if len(modifiers) == 0:
|
||||
|
@ -35,7 +35,7 @@ def gather_primitive_attributes(blender_primitive, export_settings):
|
||||
return attributes
|
||||
|
||||
|
||||
def array_to_accessor(array, component_type, data_type, include_max_and_min=False):
|
||||
def array_to_accessor(array, component_type, data_type, include_max_and_min=False, normalized=None):
|
||||
|
||||
amax = None
|
||||
amin = None
|
||||
@ -53,7 +53,7 @@ def array_to_accessor(array, component_type, data_type, include_max_and_min=Fals
|
||||
max=amax,
|
||||
min=amin,
|
||||
name=None,
|
||||
normalized=None,
|
||||
normalized=normalized,
|
||||
sparse=None,
|
||||
type=data_type,
|
||||
)
|
||||
@ -183,6 +183,7 @@ def __gather_attribute(blender_primitive, attribute, export_settings):
|
||||
data['data'],
|
||||
component_type=data['component_type'],
|
||||
data_type=data['data_type'],
|
||||
include_max_and_min=include_max_and_mins.get(attribute, False)
|
||||
include_max_and_min=include_max_and_mins.get(attribute, False),
|
||||
normalized=data.get('normalized')
|
||||
)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import numpy as np
|
||||
from mathutils import Vector
|
||||
from ...blender.com.gltf2_blender_data_path import get_sk_exported
|
||||
from ...io.com.gltf2_io_debug import print_console
|
||||
from ...io.com.gltf2_io_constants import NORMALS_ROUNDING_DIGIT
|
||||
from ...io.com.gltf2_io_constants import ROUNDING_DIGIT
|
||||
from ...io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||
from ...io.com import gltf2_io_constants
|
||||
from ..com import gltf2_blender_conversion
|
||||
@ -357,9 +357,16 @@ class PrimitiveCreator:
|
||||
def primitive_split(self):
|
||||
# Calculate triangles and sort them into primitives.
|
||||
|
||||
self.blender_mesh.calc_loop_triangles()
|
||||
loop_indices = np.empty(len(self.blender_mesh.loop_triangles) * 3, dtype=np.uint32)
|
||||
self.blender_mesh.loop_triangles.foreach_get('loops', loop_indices)
|
||||
try:
|
||||
self.blender_mesh.calc_loop_triangles()
|
||||
loop_indices = np.empty(len(self.blender_mesh.loop_triangles) * 3, dtype=np.uint32)
|
||||
self.blender_mesh.loop_triangles.foreach_get('loops', loop_indices)
|
||||
except:
|
||||
# For some not valid meshes, we can't get loops without errors
|
||||
# We already displayed a Warning message after validate() check, so here
|
||||
# we can return without a new one
|
||||
self.prim_indices = {}
|
||||
return
|
||||
|
||||
self.prim_indices = {} # maps material index to TRIANGLES-style indices into dots
|
||||
|
||||
@ -715,7 +722,7 @@ class PrimitiveCreator:
|
||||
|
||||
self.normals = self.normals.reshape(len(self.blender_mesh.loops), 3)
|
||||
|
||||
self.normals = np.round(self.normals, NORMALS_ROUNDING_DIGIT)
|
||||
self.normals = np.round(self.normals, ROUNDING_DIGIT)
|
||||
# Force normalization of normals in case some normals are not (why ?)
|
||||
PrimitiveCreator.normalize_vecs(self.normals)
|
||||
|
||||
@ -723,7 +730,7 @@ class PrimitiveCreator:
|
||||
for key_block in key_blocks:
|
||||
ns = np.array(key_block.normals_split_get(), dtype=np.float32)
|
||||
ns = ns.reshape(len(self.blender_mesh.loops), 3)
|
||||
ns = np.round(ns, NORMALS_ROUNDING_DIGIT)
|
||||
ns = np.round(ns, ROUNDING_DIGIT)
|
||||
self.morph_normals.append(ns)
|
||||
|
||||
# Transform for skinning
|
||||
@ -782,6 +789,7 @@ class PrimitiveCreator:
|
||||
self.tangents = np.empty(len(self.blender_mesh.loops) * 3, dtype=np.float32)
|
||||
self.blender_mesh.loops.foreach_get('tangent', self.tangents)
|
||||
self.tangents = self.tangents.reshape(len(self.blender_mesh.loops), 3)
|
||||
self.tangents = np.round(self.tangents, ROUNDING_DIGIT)
|
||||
|
||||
# Transform for skinning
|
||||
if self.armature and self.blender_object:
|
||||
@ -789,6 +797,7 @@ class PrimitiveCreator:
|
||||
tangent_transform = apply_matrix.to_quaternion().to_matrix()
|
||||
self.tangents = PrimitiveCreator.apply_mat_to_all(tangent_transform, self.tangents)
|
||||
PrimitiveCreator.normalize_vecs(self.tangents)
|
||||
self.tangents = np.round(self.tangents, ROUNDING_DIGIT)
|
||||
|
||||
if self.export_settings['gltf_yup']:
|
||||
PrimitiveCreator.zup2yup(self.tangents)
|
||||
|
@ -308,11 +308,19 @@ def previous_node(socket):
|
||||
return prev_socket.node
|
||||
return None
|
||||
|
||||
#TODOExt is this the same as __get_tex_from_socket from gather_image ?
|
||||
def has_image_node_from_socket(socket):
|
||||
def get_tex_from_socket(socket):
|
||||
result = gltf2_blender_search_node_tree.from_socket(
|
||||
socket,
|
||||
gltf2_blender_search_node_tree.FilterByType(bpy.types.ShaderNodeTexImage))
|
||||
if not result:
|
||||
return False
|
||||
return True
|
||||
return None
|
||||
if result[0].shader_node.image is None:
|
||||
return None
|
||||
return result[0]
|
||||
|
||||
def has_image_node_from_socket(socket):
|
||||
return get_tex_from_socket(socket) is not None
|
||||
|
||||
def image_tex_is_valid_from_socket(socket):
|
||||
res = get_tex_from_socket(socket)
|
||||
return res is not None and res.shader_node.image is not None and res.shader_node.image.channels != 0
|
||||
|
@ -55,6 +55,7 @@ def export_clearcoat(blender_material, export_settings):
|
||||
clearcoat_texture, clearcoat_texture_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
|
||||
clearcoat_socket,
|
||||
clearcoat_roughness_slots,
|
||||
(),
|
||||
export_settings,
|
||||
)
|
||||
clearcoat_extension['clearcoatTexture'] = clearcoat_texture
|
||||
@ -64,6 +65,7 @@ def export_clearcoat(blender_material, export_settings):
|
||||
clearcoat_roughness_texture, clearcoat_roughness_texture_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
|
||||
clearcoat_roughness_socket,
|
||||
clearcoat_roughness_slots,
|
||||
(),
|
||||
export_settings,
|
||||
)
|
||||
clearcoat_extension['clearcoatRoughnessTexture'] = clearcoat_roughness_texture
|
||||
|
@ -52,7 +52,7 @@ def export_emission_texture(blender_material, export_settings):
|
||||
emissive = gltf2_blender_get.get_socket(blender_material, "Emissive")
|
||||
if emissive is None:
|
||||
emissive = gltf2_blender_get.get_socket_old(blender_material, "Emissive")
|
||||
emissive_texture, use_actives_uvmap_emissive, _ = gltf2_blender_gather_texture_info.gather_texture_info(emissive, (emissive,), export_settings)
|
||||
emissive_texture, use_actives_uvmap_emissive, _ = gltf2_blender_gather_texture_info.gather_texture_info(emissive, (emissive,), (), export_settings)
|
||||
return emissive_texture, ["emissiveTexture"] if use_actives_uvmap_emissive else None
|
||||
|
||||
def export_emission_strength_extension(emissive_factor, export_settings):
|
||||
|
@ -40,6 +40,7 @@ def export_sheen(blender_material, export_settings):
|
||||
original_sheenColor_texture, original_sheenColor_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
|
||||
sheenColor_socket,
|
||||
(sheenColor_socket,),
|
||||
(),
|
||||
export_settings,
|
||||
)
|
||||
sheen_extension['sheenColorTexture'] = original_sheenColor_texture
|
||||
@ -64,6 +65,7 @@ def export_sheen(blender_material, export_settings):
|
||||
original_sheenRoughness_texture, original_sheenRoughness_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
|
||||
sheenRoughness_socket,
|
||||
(sheenRoughness_socket,),
|
||||
(),
|
||||
export_settings,
|
||||
)
|
||||
sheen_extension['sheenRoughnessTexture'] = original_sheenRoughness_texture
|
||||
|
@ -7,7 +7,8 @@ from .....io.com.gltf2_io_extensions import Extension
|
||||
from .....io.com.gltf2_io_constants import GLTF_IOR
|
||||
from ....exp import gltf2_blender_get
|
||||
from ....com.gltf2_blender_default import BLENDER_SPECULAR, BLENDER_SPECULAR_TINT
|
||||
from ...material import gltf2_blender_gather_texture_info
|
||||
from ...material.gltf2_blender_gather_texture_info import gather_texture_info
|
||||
from ...gltf2_blender_get import image_tex_is_valid_from_socket
|
||||
|
||||
def export_original_specular(blender_material, export_settings):
|
||||
specular_extension = {}
|
||||
@ -36,9 +37,10 @@ def export_original_specular(blender_material, export_settings):
|
||||
|
||||
# Texture
|
||||
if gltf2_blender_get.has_image_node_from_socket(original_specular_socket):
|
||||
original_specular_texture, original_specular_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
|
||||
original_specular_texture, original_specular_use_active_uvmap, _ = gather_texture_info(
|
||||
original_specular_socket,
|
||||
(original_specular_socket,),
|
||||
(),
|
||||
export_settings,
|
||||
)
|
||||
specular_extension['specularTexture'] = original_specular_texture
|
||||
@ -58,9 +60,10 @@ def export_original_specular(blender_material, export_settings):
|
||||
|
||||
# Texture
|
||||
if gltf2_blender_get.has_image_node_from_socket(original_specularcolor_socket):
|
||||
original_specularcolor_texture, original_specularcolor_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
|
||||
original_specularcolor_texture, original_specularcolor_use_active_uvmap, _ = gather_texture_info(
|
||||
original_specularcolor_socket,
|
||||
(original_specularcolor_socket,),
|
||||
(),
|
||||
export_settings,
|
||||
)
|
||||
specular_extension['specularColorTexture'] = original_specularcolor_texture
|
||||
@ -86,12 +89,11 @@ def export_specular(blender_material, export_settings):
|
||||
if base_color_socket is None:
|
||||
return None, None
|
||||
|
||||
# TODOExt replace by __has_image_node_from_socket calls
|
||||
specular_not_linked = isinstance(specular_socket, bpy.types.NodeSocket) and not specular_socket.is_linked
|
||||
specular_tint_not_linked = isinstance(specular_tint_socket, bpy.types.NodeSocket) and not specular_tint_socket.is_linked
|
||||
base_color_not_linked = isinstance(base_color_socket, bpy.types.NodeSocket) and not base_color_socket.is_linked
|
||||
transmission_not_linked = isinstance(transmission_socket, bpy.types.NodeSocket) and not transmission_socket.is_linked
|
||||
ior_not_linked = isinstance(ior_socket, bpy.types.NodeSocket) and not ior_socket.is_linked
|
||||
specular_not_linked = not image_tex_is_valid_from_socket(specular_socket)
|
||||
specular_tint_not_linked = not image_tex_is_valid_from_socket(specular_tint_socket)
|
||||
base_color_not_linked = not image_tex_is_valid_from_socket(base_color_socket)
|
||||
transmission_not_linked = not image_tex_is_valid_from_socket(transmission_socket)
|
||||
ior_not_linked = not image_tex_is_valid_from_socket(ior_socket)
|
||||
|
||||
specular = specular_socket.default_value if specular_not_linked else None
|
||||
specular_tint = specular_tint_socket.default_value if specular_tint_not_linked else None
|
||||
@ -149,9 +151,10 @@ def export_specular(blender_material, export_settings):
|
||||
if base_color_not_linked:
|
||||
primary_socket = transmission_socket
|
||||
|
||||
specularColorTexture, use_active_uvmap, specularColorFactor = gltf2_blender_gather_texture_info.gather_texture_info(
|
||||
specularColorTexture, use_active_uvmap, specularColorFactor = gather_texture_info(
|
||||
primary_socket,
|
||||
sockets,
|
||||
(),
|
||||
export_settings,
|
||||
filter_type='ANY')
|
||||
if specularColorTexture is None:
|
||||
|
@ -38,6 +38,7 @@ def export_transmission(blender_material, export_settings):
|
||||
combined_texture, use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
|
||||
transmission_socket,
|
||||
transmission_slots,
|
||||
(),
|
||||
export_settings,
|
||||
)
|
||||
if has_transmission_texture:
|
||||
|
@ -66,6 +66,7 @@ def export_volume(blender_material, export_settings):
|
||||
combined_texture, use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
|
||||
thicknesss_socket,
|
||||
thickness_slots,
|
||||
(),
|
||||
export_settings,
|
||||
)
|
||||
if has_thickness_texture:
|
||||
|
@ -28,6 +28,11 @@ class FillWhite:
|
||||
"""Fills a channel with all ones (1.0)."""
|
||||
pass
|
||||
|
||||
class FillWith:
|
||||
"""Fills a channel with all same values"""
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
class StoreData:
|
||||
def __init__(self, data):
|
||||
"""Store numeric data (not an image channel"""
|
||||
@ -99,6 +104,9 @@ class ExportImage:
|
||||
def fill_white(self, dst_chan: Channel):
|
||||
self.fills[dst_chan] = FillWhite()
|
||||
|
||||
def fill_with(self, dst_chan, value):
|
||||
self.fills[dst_chan] = FillWith(value)
|
||||
|
||||
def is_filled(self, chan: Channel) -> bool:
|
||||
return chan in self.fills
|
||||
|
||||
@ -183,6 +191,8 @@ class ExportImage:
|
||||
for dst_chan, fill in self.fills.items():
|
||||
if isinstance(fill, FillImage) and fill.image == image:
|
||||
out_buf[int(dst_chan)::4] = tmp_buf[int(fill.src_chan)::4]
|
||||
elif isinstance(fill, FillWith):
|
||||
out_buf[int(dst_chan)::4] = fill.value
|
||||
|
||||
tmp_buf = None # GC this
|
||||
|
||||
|
@ -12,17 +12,18 @@ from ....io.exp import gltf2_io_binary_data, gltf2_io_image_data
|
||||
from ....io.com import gltf2_io_debug
|
||||
from ....io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||
from ..gltf2_blender_gather_cache import cached
|
||||
from . import gltf2_blender_search_node_tree
|
||||
from .extensions.gltf2_blender_image import Channel, ExportImage, FillImage
|
||||
from ..gltf2_blender_get import get_tex_from_socket
|
||||
|
||||
@cached
|
||||
def gather_image(
|
||||
blender_shader_sockets: typing.Tuple[bpy.types.NodeSocket],
|
||||
default_sockets: typing.Tuple[bpy.types.NodeSocket],
|
||||
export_settings):
|
||||
if not __filter_image(blender_shader_sockets, export_settings):
|
||||
return None, None
|
||||
|
||||
image_data = __get_image_data(blender_shader_sockets, export_settings)
|
||||
image_data = __get_image_data(blender_shader_sockets, default_sockets, export_settings)
|
||||
if image_data.empty():
|
||||
# The export image has no data
|
||||
return None, None
|
||||
@ -174,24 +175,32 @@ def __gather_uri(image_data, mime_type, name, export_settings):
|
||||
return None, None
|
||||
|
||||
|
||||
def __get_image_data(sockets, export_settings) -> ExportImage:
|
||||
def __get_image_data(sockets, default_sockets, export_settings) -> ExportImage:
|
||||
# For shared resources, such as images, we just store the portion of data that is needed in the glTF property
|
||||
# in a helper class. During generation of the glTF in the exporter these will then be combined to actual binary
|
||||
# resources.
|
||||
results = [__get_tex_from_socket(socket, export_settings) for socket in sockets]
|
||||
results = [get_tex_from_socket(socket) for socket in sockets]
|
||||
|
||||
# Check if we need a simple mapping or more complex calculation
|
||||
if any([socket.name == "Specular" and socket.node.type == "BSDF_PRINCIPLED" for socket in sockets]):
|
||||
return __get_image_data_specular(sockets, results, export_settings)
|
||||
else:
|
||||
return __get_image_data_mapping(sockets, results, export_settings)
|
||||
return __get_image_data_mapping(sockets, default_sockets, results, export_settings)
|
||||
|
||||
def __get_image_data_mapping(sockets, results, export_settings) -> ExportImage:
|
||||
def __get_image_data_mapping(sockets, default_sockets, results, export_settings) -> ExportImage:
|
||||
"""
|
||||
Simple mapping
|
||||
Will fit for most of exported textures : RoughnessMetallic, Basecolor, normal, ...
|
||||
"""
|
||||
composed_image = ExportImage()
|
||||
|
||||
default_metallic = None
|
||||
default_roughness = None
|
||||
if "Metallic" in [s.name for s in default_sockets]:
|
||||
default_metallic = [s for s in default_sockets if s.name == "Metallic"][0].default_value
|
||||
if "Roughness" in [s.name for s in default_sockets]:
|
||||
default_roughness = [s for s in default_sockets if s.name == "Roughness"][0].default_value
|
||||
|
||||
for result, socket in zip(results, sockets):
|
||||
# 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
|
||||
@ -242,9 +251,15 @@ def __get_image_data_mapping(sockets, results, export_settings) -> ExportImage:
|
||||
# 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)
|
||||
if default_roughness is not None:
|
||||
composed_image.fill_with(Channel.G, default_roughness)
|
||||
else:
|
||||
composed_image.fill_white(Channel.G)
|
||||
elif socket.name == 'Roughness' and not composed_image.is_filled(Channel.B):
|
||||
composed_image.fill_white(Channel.B)
|
||||
if default_metallic is not None:
|
||||
composed_image.fill_with(Channel.B, default_metallic)
|
||||
else:
|
||||
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)
|
||||
@ -271,7 +286,7 @@ def __get_image_data_specular(sockets, results, export_settings) -> ExportImage:
|
||||
|
||||
composed_image.store_data("ior", sockets[4].default_value, type="Data")
|
||||
|
||||
results = [__get_tex_from_socket(socket, export_settings) for socket in sockets[:-1]] #Do not retrieve IOR --> No texture allowed
|
||||
results = [get_tex_from_socket(socket) for socket in sockets[:-1]] #Do not retrieve IOR --> No texture allowed
|
||||
|
||||
mapping = {
|
||||
0: "specular",
|
||||
@ -281,7 +296,7 @@ def __get_image_data_specular(sockets, results, export_settings) -> ExportImage:
|
||||
}
|
||||
|
||||
for idx, result in enumerate(results):
|
||||
if __get_tex_from_socket(sockets[idx], export_settings):
|
||||
if get_tex_from_socket(sockets[idx]):
|
||||
|
||||
composed_image.store_data(mapping[idx], result.shader_node.image, type="Image")
|
||||
|
||||
@ -308,16 +323,6 @@ def __get_image_data_specular(sockets, results, export_settings) -> ExportImage:
|
||||
|
||||
return composed_image
|
||||
|
||||
# TODOExt deduplicate
|
||||
@cached
|
||||
def __get_tex_from_socket(blender_shader_socket: bpy.types.NodeSocket, export_settings):
|
||||
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 __is_blender_image_a_jpeg(image: bpy.types.Image) -> bool:
|
||||
if image.source != 'FILE':
|
||||
|
@ -52,13 +52,13 @@ def gather_material(blender_material, active_uvmap_index, export_settings):
|
||||
export_user_extensions('gather_material_hook', export_settings, mat_unlit, blender_material)
|
||||
return mat_unlit
|
||||
|
||||
orm_texture = __gather_orm_texture(blender_material, export_settings)
|
||||
orm_texture, default_sockets = __gather_orm_texture(blender_material, export_settings)
|
||||
|
||||
emissive_factor = __gather_emissive_factor(blender_material, export_settings)
|
||||
emissive_texture, uvmap_actives_emissive_texture = __gather_emissive_texture(blender_material, export_settings)
|
||||
extensions, uvmap_actives_extensions = __gather_extensions(blender_material, emissive_factor, export_settings)
|
||||
normal_texture, uvmap_actives_normal_texture = __gather_normal_texture(blender_material, export_settings)
|
||||
occlusion_texture, uvmap_actives_occlusion_texture = __gather_occlusion_texture(blender_material, orm_texture, export_settings)
|
||||
occlusion_texture, uvmap_actives_occlusion_texture = __gather_occlusion_texture(blender_material, orm_texture, default_sockets, export_settings)
|
||||
pbr_metallic_roughness, uvmap_actives_pbr_metallic_roughness = __gather_pbr_metallic_roughness(blender_material, orm_texture, export_settings)
|
||||
|
||||
if any([i>1.0 for i in emissive_factor or []]) is True:
|
||||
@ -303,7 +303,7 @@ def __gather_orm_texture(blender_material, export_settings):
|
||||
if occlusion is None or not gltf2_blender_get.has_image_node_from_socket(occlusion):
|
||||
occlusion = gltf2_blender_get.get_socket_old(blender_material, "Occlusion")
|
||||
if occlusion is None or not gltf2_blender_get.has_image_node_from_socket(occlusion):
|
||||
return None
|
||||
return None, None
|
||||
|
||||
metallic_socket = gltf2_blender_get.get_socket(blender_material, "Metallic")
|
||||
roughness_socket = gltf2_blender_get.get_socket(blender_material, "Roughness")
|
||||
@ -311,38 +311,43 @@ def __gather_orm_texture(blender_material, export_settings):
|
||||
hasMetal = metallic_socket is not None and gltf2_blender_get.has_image_node_from_socket(metallic_socket)
|
||||
hasRough = roughness_socket is not None and gltf2_blender_get.has_image_node_from_socket(roughness_socket)
|
||||
|
||||
default_sockets = ()
|
||||
if not hasMetal and not hasRough:
|
||||
metallic_roughness = gltf2_blender_get.get_socket_old(blender_material, "MetallicRoughness")
|
||||
if metallic_roughness is None or not gltf2_blender_get.has_image_node_from_socket(metallic_roughness):
|
||||
return None
|
||||
return None, default_sockets
|
||||
result = (occlusion, metallic_roughness)
|
||||
elif not hasMetal:
|
||||
result = (occlusion, roughness_socket)
|
||||
default_sockets = (metallic_socket,)
|
||||
elif not hasRough:
|
||||
result = (occlusion, metallic_socket)
|
||||
default_sockets = (roughness_socket,)
|
||||
else:
|
||||
result = (occlusion, roughness_socket, metallic_socket)
|
||||
default_sockets = ()
|
||||
|
||||
if not gltf2_blender_gather_texture_info.check_same_size_images(result):
|
||||
print_console("INFO",
|
||||
"Occlusion and metal-roughness texture will be exported separately "
|
||||
"(use same-sized images if you want them combined)")
|
||||
return None
|
||||
return None, ()
|
||||
|
||||
# Double-check this will past the filter in texture_info
|
||||
info, info_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(result[0], result, export_settings)
|
||||
info, info_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(result[0], result, default_sockets, export_settings)
|
||||
if info is None:
|
||||
return None
|
||||
return None, ()
|
||||
|
||||
return result
|
||||
return result, default_sockets
|
||||
|
||||
def __gather_occlusion_texture(blender_material, orm_texture, export_settings):
|
||||
def __gather_occlusion_texture(blender_material, orm_texture, default_sockets, export_settings):
|
||||
occlusion = gltf2_blender_get.get_socket(blender_material, "Occlusion")
|
||||
if occlusion is None:
|
||||
occlusion = gltf2_blender_get.get_socket_old(blender_material, "Occlusion")
|
||||
occlusion_texture, use_active_uvmap_occlusion, _ = gltf2_blender_gather_texture_info.gather_material_occlusion_texture_info_class(
|
||||
occlusion,
|
||||
orm_texture or (occlusion,),
|
||||
default_sockets,
|
||||
export_settings)
|
||||
return occlusion_texture, ["occlusionTexture"] if use_active_uvmap_occlusion else None
|
||||
|
||||
|
@ -8,8 +8,8 @@ from ....io.com import gltf2_io
|
||||
from ....io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||
from ...exp import gltf2_blender_get
|
||||
from ..gltf2_blender_gather_cache import cached
|
||||
from . import gltf2_blender_search_node_tree
|
||||
from . import gltf2_blender_gather_texture_info
|
||||
from ..gltf2_blender_get import image_tex_is_valid_from_socket
|
||||
from .gltf2_blender_gather_texture_info import gather_texture_info
|
||||
|
||||
@cached
|
||||
def gather_material_pbr_metallic_roughness(blender_material, orm_texture, export_settings):
|
||||
@ -93,12 +93,12 @@ def __gather_base_color_texture(blender_material, export_settings):
|
||||
# keep sockets that have some texture : color and/or alpha
|
||||
inputs = tuple(
|
||||
socket for socket in [base_color_socket, alpha_socket]
|
||||
if socket is not None and __has_image_node_from_socket(socket)
|
||||
if socket is not None and image_tex_is_valid_from_socket(socket)
|
||||
)
|
||||
if not inputs:
|
||||
return None, None, None
|
||||
|
||||
return gltf2_blender_gather_texture_info.gather_texture_info(inputs[0], inputs, export_settings)
|
||||
return gather_texture_info(inputs[0], inputs, (), export_settings)
|
||||
|
||||
|
||||
def __gather_extensions(blender_material, export_settings):
|
||||
@ -126,24 +126,29 @@ def __gather_metallic_roughness_texture(blender_material, orm_texture, export_se
|
||||
metallic_socket = gltf2_blender_get.get_socket(blender_material, "Metallic")
|
||||
roughness_socket = gltf2_blender_get.get_socket(blender_material, "Roughness")
|
||||
|
||||
hasMetal = metallic_socket is not None and __has_image_node_from_socket(metallic_socket)
|
||||
hasRough = roughness_socket is not None and __has_image_node_from_socket(roughness_socket)
|
||||
hasMetal = metallic_socket is not None and image_tex_is_valid_from_socket(metallic_socket)
|
||||
hasRough = roughness_socket is not None and image_tex_is_valid_from_socket(roughness_socket)
|
||||
|
||||
default_sockets = ()
|
||||
if not hasMetal and not hasRough:
|
||||
metallic_roughness = gltf2_blender_get.get_socket_old(blender_material, "MetallicRoughness")
|
||||
if metallic_roughness is None or not __has_image_node_from_socket(metallic_roughness):
|
||||
if metallic_roughness is None or not image_tex_is_valid_from_socket(metallic_roughness):
|
||||
return None, None, None
|
||||
texture_input = (metallic_roughness,)
|
||||
elif not hasMetal:
|
||||
texture_input = (roughness_socket,)
|
||||
default_sockets = (metallic_socket,)
|
||||
elif not hasRough:
|
||||
texture_input = (metallic_socket,)
|
||||
default_sockets = (roughness_socket,)
|
||||
else:
|
||||
texture_input = (metallic_socket, roughness_socket)
|
||||
default_sockets = ()
|
||||
|
||||
return gltf2_blender_gather_texture_info.gather_texture_info(
|
||||
return gather_texture_info(
|
||||
texture_input[0],
|
||||
orm_texture or texture_input,
|
||||
default_sockets,
|
||||
export_settings,
|
||||
)
|
||||
|
||||
@ -160,14 +165,6 @@ def __gather_roughness_factor(blender_material, export_settings):
|
||||
return fac if fac != 1 else None
|
||||
return None
|
||||
|
||||
def __has_image_node_from_socket(socket):
|
||||
result = gltf2_blender_search_node_tree.from_socket(
|
||||
socket,
|
||||
gltf2_blender_search_node_tree.FilterByType(bpy.types.ShaderNodeTexImage))
|
||||
if not result:
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_default_pbr_for_emissive_node():
|
||||
return gltf2_io.MaterialPBRMetallicRoughness(
|
||||
base_color_factor=[0.0,0.0,0.0,1.0],
|
||||
|
@ -131,6 +131,7 @@ def gather_base_color_texture(info, export_settings):
|
||||
unlit_texture, unlit_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(
|
||||
sockets[0],
|
||||
sockets,
|
||||
(),
|
||||
export_settings,
|
||||
)
|
||||
return unlit_texture, ["unlitTexture"] if unlit_use_active_uvmap else None
|
||||
|
@ -9,12 +9,13 @@ from ....io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||
from ....io.com import gltf2_io
|
||||
from ..gltf2_blender_gather_cache import cached
|
||||
from ..gltf2_blender_gather_sampler import gather_sampler
|
||||
from . import gltf2_blender_search_node_tree
|
||||
from ..gltf2_blender_get import get_tex_from_socket
|
||||
from . import gltf2_blender_gather_image
|
||||
|
||||
@cached
|
||||
def gather_texture(
|
||||
blender_shader_sockets: typing.Tuple[bpy.types.NodeSocket],
|
||||
default_sockets: typing.Tuple[bpy.types.NodeSocket],
|
||||
export_settings):
|
||||
"""
|
||||
Gather texture sampling information and image channels from a blender shader texture attached to a shader socket.
|
||||
@ -27,7 +28,7 @@ def gather_texture(
|
||||
if not __filter_texture(blender_shader_sockets, export_settings):
|
||||
return None, None
|
||||
|
||||
source, factor = __gather_source(blender_shader_sockets, export_settings)
|
||||
source, factor = __gather_source(blender_shader_sockets, default_sockets, export_settings)
|
||||
|
||||
texture = gltf2_io.Texture(
|
||||
extensions=__gather_extensions(blender_shader_sockets, export_settings),
|
||||
@ -67,7 +68,7 @@ def __gather_name(blender_shader_sockets, export_settings):
|
||||
|
||||
|
||||
def __gather_sampler(blender_shader_sockets, export_settings):
|
||||
shader_nodes = [__get_tex_from_socket(socket) for socket in blender_shader_sockets]
|
||||
shader_nodes = [get_tex_from_socket(socket) for socket in blender_shader_sockets]
|
||||
if len(shader_nodes) > 1:
|
||||
gltf2_io_debug.print_console("WARNING",
|
||||
"More than one shader node tex image used for a texture. "
|
||||
@ -78,16 +79,5 @@ def __gather_sampler(blender_shader_sockets, export_settings):
|
||||
export_settings)
|
||||
|
||||
|
||||
def __gather_source(blender_shader_sockets, export_settings):
|
||||
return gltf2_blender_gather_image.gather_image(blender_shader_sockets, export_settings)
|
||||
|
||||
# Helpers
|
||||
|
||||
# TODOExt deduplicate
|
||||
def __get_tex_from_socket(socket):
|
||||
result = gltf2_blender_search_node_tree.from_socket(
|
||||
socket,
|
||||
gltf2_blender_search_node_tree.FilterByType(bpy.types.ShaderNodeTexImage))
|
||||
if not result:
|
||||
return None
|
||||
return result[0]
|
||||
def __gather_source(blender_shader_sockets, default_sockets, export_settings):
|
||||
return gltf2_blender_gather_image.gather_image(blender_shader_sockets, default_sockets, export_settings)
|
||||
|
@ -8,7 +8,7 @@ from ....io.com import gltf2_io
|
||||
from ....io.com.gltf2_io_extensions import Extension
|
||||
from ....io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||
from ...exp import gltf2_blender_get
|
||||
from ..gltf2_blender_get import previous_node
|
||||
from ..gltf2_blender_get import previous_node, get_tex_from_socket
|
||||
from ..gltf2_blender_gather_sampler import detect_manual_uv_wrapping
|
||||
from ..gltf2_blender_gather_cache import cached
|
||||
from . import gltf2_blender_gather_texture
|
||||
@ -19,20 +19,25 @@ from . import gltf2_blender_search_node_tree
|
||||
# occlusion the primary_socket would be the occlusion socket, and
|
||||
# blender_shader_sockets would be the (O,R,M) sockets.
|
||||
|
||||
def gather_texture_info(primary_socket, blender_shader_sockets, export_settings, filter_type='ALL'):
|
||||
return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'DEFAULT', filter_type, export_settings)
|
||||
# Default socket parameter is used when there is a mapping between channels, and one of the channel is not a texture
|
||||
# In that case, we will create a texture with one channel from texture, other from default socket value
|
||||
# Example: MetallicRoughness
|
||||
|
||||
def gather_texture_info(primary_socket, blender_shader_sockets, default_sockets, export_settings, filter_type='ALL'):
|
||||
return __gather_texture_info_helper(primary_socket, blender_shader_sockets, default_sockets, 'DEFAULT', filter_type, export_settings)
|
||||
|
||||
def gather_material_normal_texture_info_class(primary_socket, blender_shader_sockets, export_settings, filter_type='ALL'):
|
||||
return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'NORMAL', filter_type, export_settings)
|
||||
return __gather_texture_info_helper(primary_socket, blender_shader_sockets, (), 'NORMAL', filter_type, export_settings)
|
||||
|
||||
def gather_material_occlusion_texture_info_class(primary_socket, blender_shader_sockets, export_settings, filter_type='ALL'):
|
||||
return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'OCCLUSION', filter_type, export_settings)
|
||||
def gather_material_occlusion_texture_info_class(primary_socket, blender_shader_sockets, default_sockets, export_settings, filter_type='ALL'):
|
||||
return __gather_texture_info_helper(primary_socket, blender_shader_sockets, default_sockets, 'OCCLUSION', filter_type, export_settings)
|
||||
|
||||
|
||||
@cached
|
||||
def __gather_texture_info_helper(
|
||||
primary_socket: bpy.types.NodeSocket,
|
||||
blender_shader_sockets: typing.Tuple[bpy.types.NodeSocket],
|
||||
default_sockets: typing.Tuple[bpy.types.NodeSocket],
|
||||
kind: str,
|
||||
filter_type: str,
|
||||
export_settings):
|
||||
@ -41,7 +46,7 @@ def __gather_texture_info_helper(
|
||||
|
||||
tex_transform, tex_coord, use_active_uvmap = __gather_texture_transform_and_tex_coord(primary_socket, export_settings)
|
||||
|
||||
index, factor = __gather_index(blender_shader_sockets, export_settings)
|
||||
index, factor = __gather_index(blender_shader_sockets, default_sockets, export_settings)
|
||||
|
||||
fields = {
|
||||
'extensions': __gather_extensions(tex_transform, export_settings),
|
||||
@ -72,7 +77,7 @@ def __gather_texture_info_helper(
|
||||
def __filter_texture_info(primary_socket, blender_shader_sockets, filter_type, export_settings):
|
||||
if primary_socket is None:
|
||||
return False
|
||||
if __get_tex_from_socket(primary_socket) is None:
|
||||
if get_tex_from_socket(primary_socket) is None:
|
||||
return False
|
||||
if not blender_shader_sockets:
|
||||
return False
|
||||
@ -80,12 +85,12 @@ def __filter_texture_info(primary_socket, blender_shader_sockets, filter_type, e
|
||||
return False
|
||||
if filter_type == "ALL":
|
||||
# Check that all sockets link to texture
|
||||
if any([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets]):
|
||||
if any([get_tex_from_socket(socket) is None for socket in blender_shader_sockets]):
|
||||
# sockets do not lead to a texture --> discard
|
||||
return False
|
||||
elif filter_type == "ANY":
|
||||
# Check that at least one socket link to texture
|
||||
if all([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets]):
|
||||
if all([get_tex_from_socket(socket) is None for socket in blender_shader_sockets]):
|
||||
return False
|
||||
elif filter_type == "NONE":
|
||||
# No check
|
||||
@ -136,9 +141,9 @@ def __gather_occlusion_strength(primary_socket, export_settings):
|
||||
return None
|
||||
|
||||
|
||||
def __gather_index(blender_shader_sockets, export_settings):
|
||||
def __gather_index(blender_shader_sockets, default_sockets, export_settings):
|
||||
# We just put the actual shader into the 'index' member
|
||||
return gltf2_blender_gather_texture.gather_texture(blender_shader_sockets, export_settings)
|
||||
return gltf2_blender_gather_texture.gather_texture(blender_shader_sockets, default_sockets, export_settings)
|
||||
|
||||
|
||||
def __gather_texture_transform_and_tex_coord(primary_socket, export_settings):
|
||||
@ -148,7 +153,7 @@ def __gather_texture_transform_and_tex_coord(primary_socket, export_settings):
|
||||
#
|
||||
# The [UV Wrapping] is for wrap modes like MIRROR that use nodes,
|
||||
# [Mapping] is for KHR_texture_transform, and [UV Map] is for texCoord.
|
||||
blender_shader_node = __get_tex_from_socket(primary_socket).shader_node
|
||||
blender_shader_node = get_tex_from_socket(primary_socket).shader_node
|
||||
|
||||
# Skip over UV wrapping stuff (it goes in the sampler)
|
||||
result = detect_manual_uv_wrapping(blender_shader_node)
|
||||
@ -178,17 +183,6 @@ def __gather_texture_transform_and_tex_coord(primary_socket, export_settings):
|
||||
|
||||
return texture_transform, texcoord_idx or None, use_active_uvmap
|
||||
|
||||
# TODOExt deduplicate
|
||||
def __get_tex_from_socket(socket):
|
||||
result = gltf2_blender_search_node_tree.from_socket(
|
||||
socket,
|
||||
gltf2_blender_search_node_tree.FilterByType(bpy.types.ShaderNodeTexImage))
|
||||
if not result:
|
||||
return None
|
||||
if result[0].shader_node.image is None:
|
||||
return None
|
||||
return result[0]
|
||||
|
||||
|
||||
def check_same_size_images(
|
||||
blender_shader_sockets: typing.Tuple[bpy.types.NodeSocket],
|
||||
@ -199,7 +193,7 @@ def check_same_size_images(
|
||||
|
||||
sizes = set()
|
||||
for socket in blender_shader_sockets:
|
||||
tex = __get_tex_from_socket(socket)
|
||||
tex = get_tex_from_socket(socket)
|
||||
if tex is None:
|
||||
return False
|
||||
size = tex.shader_node.image.size
|
||||
|
@ -152,5 +152,5 @@ GLTF_DATA_TYPE_MAT4 = "MAT4"
|
||||
|
||||
GLTF_IOR = 1.5
|
||||
|
||||
# Rounding digit used for normal rounding
|
||||
NORMALS_ROUNDING_DIGIT = 4
|
||||
# Rounding digit used for normal/tangent rounding
|
||||
ROUNDING_DIGIT = 4
|
||||
|
Loading…
Reference in New Issue
Block a user