io_scene_3ds: Update for Blender 3.x #104511

Merged
Sebastian Sille merged 3 commits from nrgsille-patch-1 into main 2023-03-31 23:33:27 +02:00
7 changed files with 75 additions and 50 deletions
Showing only changes of commit 15e5e9c1f1 - Show all commits

View File

@ -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',

View File

@ -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]]:

View File

@ -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

View File

@ -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]:

View File

@ -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:

View File

@ -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]

View File

@ -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