io_scene_3ds: Update for Blender 3.x #104511
@ -4,7 +4,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": (3, 6, 6),
|
||||
"version": (3, 6, 10),
|
||||
'blender': (3, 5, 0),
|
||||
'location': 'File > Import-Export',
|
||||
'description': 'Import-Export as glTF 2.0',
|
||||
|
@ -14,7 +14,6 @@ def get_mesh_cache_key(blender_mesh,
|
||||
blender_object,
|
||||
vertex_groups,
|
||||
modifiers,
|
||||
skip_filter,
|
||||
materials,
|
||||
original_mesh,
|
||||
export_settings):
|
||||
@ -34,21 +33,19 @@ def get_mesh_cache_key(blender_mesh,
|
||||
return (
|
||||
(id(mesh_to_id_cache),),
|
||||
(modifiers,),
|
||||
(skip_filter,), #TODO to check if still needed
|
||||
mats
|
||||
)
|
||||
|
||||
@cached_by_key(key=get_mesh_cache_key)
|
||||
def gather_mesh(blender_mesh: bpy.types.Mesh,
|
||||
uuid_for_skined_data,
|
||||
vertex_groups: Optional[bpy.types.VertexGroups],
|
||||
vertex_groups: bpy.types.VertexGroups,
|
||||
modifiers: Optional[bpy.types.ObjectModifiers],
|
||||
skip_filter: bool,
|
||||
materials: Tuple[bpy.types.Material],
|
||||
original_mesh: bpy.types.Mesh,
|
||||
export_settings
|
||||
) -> Optional[gltf2_io.Mesh]:
|
||||
if not skip_filter and not __filter_mesh(blender_mesh, vertex_groups, modifiers, export_settings):
|
||||
if not __filter_mesh(blender_mesh, vertex_groups, modifiers, export_settings):
|
||||
return None
|
||||
|
||||
mesh = gltf2_io.Mesh(
|
||||
@ -75,25 +72,21 @@ def gather_mesh(blender_mesh: bpy.types.Mesh,
|
||||
blender_object,
|
||||
vertex_groups,
|
||||
modifiers,
|
||||
skip_filter,
|
||||
materials)
|
||||
|
||||
return mesh
|
||||
|
||||
|
||||
def __filter_mesh(blender_mesh: bpy.types.Mesh,
|
||||
vertex_groups: Optional[bpy.types.VertexGroups],
|
||||
vertex_groups: bpy.types.VertexGroups,
|
||||
modifiers: Optional[bpy.types.ObjectModifiers],
|
||||
export_settings
|
||||
) -> bool:
|
||||
|
||||
if blender_mesh.users == 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def __gather_extensions(blender_mesh: bpy.types.Mesh,
|
||||
vertex_groups: Optional[bpy.types.VertexGroups],
|
||||
vertex_groups: bpy.types.VertexGroups,
|
||||
modifiers: Optional[bpy.types.ObjectModifiers],
|
||||
export_settings
|
||||
) -> Any:
|
||||
@ -101,7 +94,7 @@ def __gather_extensions(blender_mesh: bpy.types.Mesh,
|
||||
|
||||
|
||||
def __gather_extras(blender_mesh: bpy.types.Mesh,
|
||||
vertex_groups: Optional[bpy.types.VertexGroups],
|
||||
vertex_groups: bpy.types.VertexGroups,
|
||||
modifiers: Optional[bpy.types.ObjectModifiers],
|
||||
export_settings
|
||||
) -> Optional[Dict[Any, Any]]:
|
||||
@ -128,7 +121,7 @@ def __gather_extras(blender_mesh: bpy.types.Mesh,
|
||||
|
||||
|
||||
def __gather_name(blender_mesh: bpy.types.Mesh,
|
||||
vertex_groups: Optional[bpy.types.VertexGroups],
|
||||
vertex_groups: bpy.types.VertexGroups,
|
||||
modifiers: Optional[bpy.types.ObjectModifiers],
|
||||
export_settings
|
||||
) -> str:
|
||||
@ -137,7 +130,7 @@ def __gather_name(blender_mesh: bpy.types.Mesh,
|
||||
|
||||
def __gather_primitives(blender_mesh: bpy.types.Mesh,
|
||||
uuid_for_skined_data,
|
||||
vertex_groups: Optional[bpy.types.VertexGroups],
|
||||
vertex_groups: bpy.types.VertexGroups,
|
||||
modifiers: Optional[bpy.types.ObjectModifiers],
|
||||
materials: Tuple[bpy.types.Material],
|
||||
export_settings
|
||||
@ -151,7 +144,7 @@ def __gather_primitives(blender_mesh: bpy.types.Mesh,
|
||||
|
||||
|
||||
def __gather_weights(blender_mesh: bpy.types.Mesh,
|
||||
vertex_groups: Optional[bpy.types.VertexGroups],
|
||||
vertex_groups: bpy.types.VertexGroups,
|
||||
modifiers: Optional[bpy.types.ObjectModifiers],
|
||||
export_settings
|
||||
) -> Optional[List[float]]:
|
||||
|
@ -182,11 +182,7 @@ def __gather_mesh(vnode, blender_object, export_settings):
|
||||
# Be sure that object is valid (no NaN for example)
|
||||
blender_object.data.validate()
|
||||
|
||||
# If not using vertex group, they are irrelevant for caching --> ensure that they do not trigger a cache miss
|
||||
vertex_groups = blender_object.vertex_groups
|
||||
modifiers = blender_object.modifiers
|
||||
if len(vertex_groups) == 0:
|
||||
vertex_groups = None
|
||||
if len(modifiers) == 0:
|
||||
modifiers = None
|
||||
|
||||
@ -194,7 +190,6 @@ def __gather_mesh(vnode, blender_object, export_settings):
|
||||
if export_settings['gltf_apply']:
|
||||
if modifiers is None: # If no modifier, use original mesh, it will instance all shared mesh in a single glTF mesh
|
||||
blender_mesh = blender_object.data
|
||||
skip_filter = False
|
||||
else:
|
||||
armature_modifiers = {}
|
||||
if export_settings['gltf_skins']:
|
||||
@ -209,7 +204,6 @@ def __gather_mesh(vnode, blender_object, export_settings):
|
||||
blender_mesh = blender_mesh_owner.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph)
|
||||
for prop in blender_object.data.keys():
|
||||
blender_mesh[prop] = blender_object.data[prop]
|
||||
skip_filter = True
|
||||
|
||||
if export_settings['gltf_skins']:
|
||||
# restore Armature modifiers
|
||||
@ -217,15 +211,12 @@ def __gather_mesh(vnode, blender_object, export_settings):
|
||||
blender_object.modifiers[idx].show_viewport = show_viewport
|
||||
else:
|
||||
blender_mesh = blender_object.data
|
||||
skip_filter = False
|
||||
# If no skin are exported, no need to have vertex group, this will create a cache miss
|
||||
if not export_settings['gltf_skins']:
|
||||
vertex_groups = None
|
||||
modifiers = None
|
||||
else:
|
||||
# Check if there is an armature modidier
|
||||
if len([mod for mod in blender_object.modifiers if mod.type == "ARMATURE"]) == 0:
|
||||
vertex_groups = None # Not needed if no armature, avoid a cache miss
|
||||
modifiers = None
|
||||
|
||||
materials = tuple(ms.material for ms in blender_object.material_slots)
|
||||
@ -241,9 +232,8 @@ def __gather_mesh(vnode, blender_object, export_settings):
|
||||
|
||||
result = gltf2_blender_gather_mesh.gather_mesh(blender_mesh,
|
||||
uuid_for_skined_data,
|
||||
vertex_groups,
|
||||
blender_object.vertex_groups,
|
||||
modifiers,
|
||||
skip_filter,
|
||||
materials,
|
||||
None,
|
||||
export_settings)
|
||||
@ -279,17 +269,14 @@ def __gather_mesh_from_nonmesh(blender_object, export_settings):
|
||||
|
||||
needs_to_mesh_clear = True
|
||||
|
||||
skip_filter = True
|
||||
materials = tuple([ms.material for ms in blender_object.material_slots if ms.material is not None])
|
||||
vertex_groups = None
|
||||
modifiers = None
|
||||
blender_object_for_skined_data = None
|
||||
|
||||
result = gltf2_blender_gather_mesh.gather_mesh(blender_mesh,
|
||||
blender_object_for_skined_data,
|
||||
vertex_groups,
|
||||
blender_object.vertex_groups,
|
||||
modifiers,
|
||||
skip_filter,
|
||||
materials,
|
||||
blender_object.data,
|
||||
export_settings)
|
||||
@ -361,8 +348,7 @@ def gather_skin(vnode, export_settings):
|
||||
return None
|
||||
|
||||
# no skin needed when the modifier is linked without having a vertex group
|
||||
vertex_groups = blender_object.vertex_groups
|
||||
if len(vertex_groups) == 0:
|
||||
if len(blender_object.vertex_groups) == 0:
|
||||
return None
|
||||
|
||||
# check if any vertices in the mesh are part of a vertex group
|
||||
|
@ -15,9 +15,9 @@ from .material import gltf2_blender_gather_materials
|
||||
from .material.extensions import gltf2_blender_gather_materials_variants
|
||||
|
||||
@cached
|
||||
def get_primitive_cache_key(
|
||||
def gather_primitive_cache_key(
|
||||
blender_mesh,
|
||||
blender_object,
|
||||
uuid_for_skined_data,
|
||||
vertex_groups,
|
||||
modifiers,
|
||||
materials,
|
||||
@ -36,11 +36,11 @@ def get_primitive_cache_key(
|
||||
)
|
||||
|
||||
|
||||
@cached_by_key(key=get_primitive_cache_key)
|
||||
@cached_by_key(key=gather_primitive_cache_key)
|
||||
def gather_primitives(
|
||||
blender_mesh: bpy.types.Mesh,
|
||||
uuid_for_skined_data,
|
||||
vertex_groups: Optional[bpy.types.VertexGroups],
|
||||
vertex_groups: bpy.types.VertexGroups,
|
||||
modifiers: Optional[bpy.types.ObjectModifiers],
|
||||
materials: Tuple[bpy.types.Material],
|
||||
export_settings
|
||||
@ -92,11 +92,33 @@ def gather_primitives(
|
||||
|
||||
return primitives
|
||||
|
||||
|
||||
@cached
|
||||
def get_primitive_cache_key(
|
||||
blender_mesh,
|
||||
uuid_for_skined_data,
|
||||
vertex_groups,
|
||||
modifiers,
|
||||
export_settings):
|
||||
|
||||
# Use id of mesh
|
||||
# Do not use bpy.types that can be unhashable
|
||||
# Do not use mesh name, that can be not unique (when linked)
|
||||
# Do not use materials here
|
||||
|
||||
# TODO check what is really needed for modifiers
|
||||
|
||||
return (
|
||||
(id(blender_mesh),),
|
||||
(modifiers,)
|
||||
)
|
||||
|
||||
|
||||
@cached_by_key(key=get_primitive_cache_key)
|
||||
def __gather_cache_primitives(
|
||||
blender_mesh: bpy.types.Mesh,
|
||||
uuid_for_skined_data,
|
||||
vertex_groups: Optional[bpy.types.VertexGroups],
|
||||
vertex_groups: bpy.types.VertexGroups,
|
||||
modifiers: Optional[bpy.types.ObjectModifiers],
|
||||
export_settings
|
||||
) -> List[dict]:
|
||||
|
@ -85,7 +85,7 @@ class PrimitiveCreator:
|
||||
# Check if we have to export skin
|
||||
self.armature = None
|
||||
self.skin = None
|
||||
if self.blender_vertex_groups and self.export_settings['gltf_skins']:
|
||||
if self.export_settings['gltf_skins']:
|
||||
if self.modifiers is not None:
|
||||
modifiers_dict = {m.type: m for m in self.modifiers}
|
||||
if "ARMATURE" in modifiers_dict:
|
||||
@ -197,15 +197,6 @@ class PrimitiveCreator:
|
||||
attr['skip_getting_to_dots'] = True
|
||||
self.blender_attributes.append(attr)
|
||||
|
||||
# Manage uvs TEX_COORD_x
|
||||
for tex_coord_i in range(self.tex_coord_max):
|
||||
attr = {}
|
||||
attr['blender_data_type'] = 'FLOAT2'
|
||||
attr['blender_domain'] = 'CORNER'
|
||||
attr['gltf_attribute_name'] = 'TEXCOORD_' + str(tex_coord_i)
|
||||
attr['get'] = self.get_function()
|
||||
self.blender_attributes.append(attr)
|
||||
|
||||
# Manage NORMALS
|
||||
if self.use_normals:
|
||||
attr = {}
|
||||
@ -216,6 +207,15 @@ class PrimitiveCreator:
|
||||
attr['get'] = self.get_function()
|
||||
self.blender_attributes.append(attr)
|
||||
|
||||
# Manage uvs TEX_COORD_x
|
||||
for tex_coord_i in range(self.tex_coord_max):
|
||||
attr = {}
|
||||
attr['blender_data_type'] = 'FLOAT2'
|
||||
attr['blender_domain'] = 'CORNER'
|
||||
attr['gltf_attribute_name'] = 'TEXCOORD_' + str(tex_coord_i)
|
||||
attr['get'] = self.get_function()
|
||||
self.blender_attributes.append(attr)
|
||||
|
||||
# Manage TANGENT
|
||||
if self.use_tangents:
|
||||
attr = {}
|
||||
@ -269,6 +269,13 @@ class PrimitiveCreator:
|
||||
attr['len'] = gltf2_blender_conversion.get_data_length(attr['blender_data_type'])
|
||||
attr['type'] = gltf2_blender_conversion.get_numpy_type(attr['blender_data_type'])
|
||||
|
||||
|
||||
# Now we have all attribtues, we can change order if we want
|
||||
# Note that the glTF specification doesn't say anything about order
|
||||
# Attributes are defined only by name
|
||||
# But if user want it in a particular order, he can use this hook to perform it
|
||||
export_user_extensions('gather_attributes_change', self.export_settings, self.blender_attributes)
|
||||
|
||||
def create_dots_data_structure(self):
|
||||
# Now that we get all attributes that are going to be exported, create numpy array that will store them
|
||||
dot_fields = [('vertex_index', np.uint32)]
|
||||
@ -698,6 +705,8 @@ class PrimitiveCreator:
|
||||
self.normals = self.normals.reshape(len(self.blender_mesh.loops), 3)
|
||||
|
||||
self.normals = np.round(self.normals, NORMALS_ROUNDING_DIGIT)
|
||||
# Force normalization of normals in case some normals are not (why ?)
|
||||
PrimitiveCreator.normalize_vecs(self.normals)
|
||||
|
||||
self.morph_normals = []
|
||||
for key_block in key_blocks:
|
||||
|
@ -596,7 +596,22 @@ def skin_into_bind_pose(gltf, skin_idx, vert_joints, vert_weights, locs, vert_no
|
||||
for i in range(4):
|
||||
skinning_mats += ws[:, i].reshape(len(ws), 1, 1) * joint_mats[js[:, i]]
|
||||
weight_sums += ws[:, i]
|
||||
# Normalize weights to one; necessary for old files / quantized weights
|
||||
|
||||
# Some invalid files have 0 weight sum.
|
||||
# To avoid to have this vertices at 0.0 / 0.0 / 0.0
|
||||
# We set all weight ( aka 1.0 ) to the first bone
|
||||
zeros_indices = np.where(weight_sums == 0)[0]
|
||||
if zeros_indices.shape[0] > 0:
|
||||
print_console('ERROR', 'File is invalid: Some vertices are not assigned to bone(s) ')
|
||||
vert_weights[0][:, 0][zeros_indices] = 1.0 # Assign to first bone with all weight
|
||||
|
||||
# Reprocess IBM for these vertices
|
||||
skinning_mats[zeros_indices] = np.zeros((4, 4), dtype=np.float32)
|
||||
for js, ws in zip(vert_joints, vert_weights):
|
||||
for i in range(4):
|
||||
skinning_mats[zeros_indices] += ws[:, i][zeros_indices].reshape(len(ws[zeros_indices]), 1, 1) * joint_mats[js[:, i][zeros_indices]]
|
||||
weight_sums[zeros_indices] += ws[:, i][zeros_indices]
|
||||
|
||||
skinning_mats /= weight_sums.reshape(num_verts, 1, 1)
|
||||
|
||||
skinning_mats_3x3 = skinning_mats[:, :3, :3]
|
||||
|
@ -62,7 +62,7 @@ def pose_library_list_item_context_menu(self: UIList, context: Context) -> None:
|
||||
list = getattr(context, "ui_list", None)
|
||||
if not list or list.bl_idname != "UI_UL_asset_view" or list.list_id != "pose_assets":
|
||||
return False
|
||||
if not context.asset_handle:
|
||||
if not context.active_file:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user