From 6f4195998ec69777fb0b1d95446f08a436fb5b4b Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Thu, 10 Aug 2023 10:29:07 +0200 Subject: [PATCH 01/10] WIP - remove armature --- .../blender/exp/gltf2_blender_gather.py | 22 +++++-- .../exp/gltf2_blender_gather_joints.py | 8 ++- .../blender/exp/gltf2_blender_gather_nodes.py | 53 +++++++++------ .../blender/exp/gltf2_blender_gather_tree.py | 66 +++++++++++++------ 4 files changed, 101 insertions(+), 48 deletions(-) diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py index 4a84a1ba9..e6a7760cc 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py @@ -19,6 +19,7 @@ from ..com.gltf2_blender_extras import generate_extras from .gltf2_blender_gather_cache import cached from . import gltf2_blender_gather_nodes +from . import gltf2_blender_gather_joints from . import gltf2_blender_gather_tree from .animation.sampled.object.gltf2_blender_gather_object_keyframes import get_cache_data from .animation.gltf2_blender_gather_animations import gather_animations @@ -62,6 +63,7 @@ def __gather_scene(blender_scene, export_settings): vtree = gltf2_blender_gather_tree.VExportTree(export_settings) vtree.construct(blender_scene) vtree.search_missing_armature() # In case armature are no parented correctly + vtree.bake_armature_bone_list() # Used in case we remove the armature export_user_extensions('vtree_before_filter_hook', export_settings, vtree) @@ -76,12 +78,22 @@ def __gather_scene(blender_scene, export_settings): export_settings['vtree'] = vtree + #TODO add option for r in [vtree.nodes[r] for r in vtree.roots]: - node = gltf2_blender_gather_nodes.gather_node( - r, export_settings) - if node is not None: - scene.nodes.append(node) - + if r.blender_type != gltf2_blender_gather_tree.VExportNode.BONE: + node = gltf2_blender_gather_nodes.gather_node( + r, export_settings) + if node is not None: + print(node.name) + scene.nodes.append(node) + else: + # We can have bone are root of scene because we remove the armature object + # and the armature as at root of scene + node = gltf2_blender_gather_joints.gather_joint_vnode( + r.uuid, export_settings) + if node is not None: + print(node.name) + scene.nodes.append(node) vtree.add_neutral_bones() export_user_extensions('gather_scene_hook', export_settings, scene, blender_scene) diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py index d21699472..11668c613 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py @@ -58,11 +58,13 @@ def gather_joint_vnode(vnode, export_settings): :return: a glTF2 node (acting as a joint) """ vtree = export_settings['vtree'] - blender_object = vtree.nodes[vnode].blender_object blender_bone = vtree.nodes[vnode].blender_bone - - mat = vtree.nodes[vtree.nodes[vnode].parent_uuid].matrix_world.inverted_safe() @ vtree.nodes[vnode].matrix_world + #TODO add option + if vtree.nodes[vnode].parent_uuid is not None: + mat = vtree.nodes[vtree.nodes[vnode].parent_uuid].matrix_world.inverted_safe() @ vtree.nodes[vnode].matrix_world + else: + mat = vtree.nodes[vtree.nodes[vnode].armature].matrix_world.inverted_safe() @ vtree.nodes[vnode].matrix_world trans, rot, sca = mat.decompose() diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py index 9503d1d3f..70f555c4c 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py @@ -30,6 +30,7 @@ from .gltf2_blender_gather_tree import VExportNode def gather_node(vnode, export_settings): + blender_object = vnode.blender_object skin = gather_skin(vnode.uuid, export_settings) @@ -38,7 +39,7 @@ def gather_node(vnode, export_settings): node = gltf2_io.Node( camera=__gather_camera(blender_object, export_settings), - children=__gather_children(vnode, blender_object, export_settings), + children=__gather_children(vnode, export_settings), extensions=__gather_extensions(blender_object, export_settings), extras=__gather_extras(blender_object, export_settings), matrix=__gather_matrix(blender_object, export_settings), @@ -70,24 +71,36 @@ def __gather_camera(blender_object, export_settings): return gltf2_blender_gather_cameras.gather_camera(blender_object.data, export_settings) -def __gather_children(vnode, blender_object, export_settings): +def __gather_children(vnode, export_settings): children = [] vtree = export_settings['vtree'] + if vnode.blender_object.name == "Plane": + print([vtree.nodes[c].blender_object.name for c in vnode.children]) + # Standard Children / Collection - for c in [vtree.nodes[c] for c in vnode.children if vtree.nodes[c].blender_type != gltf2_blender_gather_tree.VExportNode.BONE]: - node = gather_node(c, export_settings) - if node is not None: - children.append(node) + # TODO add option + # for c in [vtree.nodes[c] for c in vnode.children if vtree.nodes[c].blender_type != gltf2_blender_gather_tree.VExportNode.BONE]: + # node = gather_node(c, export_settings) + # if node is not None: + # children.append(node) + for c in [vtree.nodes[c] for c in vnode.children]: + if c.blender_type != gltf2_blender_gather_tree.VExportNode.BONE: + node = gather_node(c, export_settings) + if node is not None: + children.append(node) + else: + # We come here because armature was remove, and bone can be a child of any object + joint = gltf2_blender_gather_joints.gather_joint_vnode(c.uuid, export_settings) + children.append(joint) + #TODO : object parented to bone # Armature --> Retrieve Blender bones if vnode.blender_type == gltf2_blender_gather_tree.VExportNode.ARMATURE: root_joints = [] - - all_armature_children = vnode.children - root_bones_uuid = [c for c in all_armature_children if export_settings['vtree'].nodes[c].blender_type == VExportNode.BONE] + root_bones_uuid = export_settings['vtree'].get_root_bones_uuid(vnode.uuid) for bone_uuid in root_bones_uuid: joint = gltf2_blender_gather_joints.gather_joint_vnode(bone_uuid, export_settings) children.append(joint) @@ -98,25 +111,14 @@ def __gather_children(vnode, blender_object, export_settings): for n in [vtree.nodes[i] for i in vtree.get_all_bones(vnode.uuid)]: direct_bone_children.extend([c for c in n.children if vtree.nodes[c].blender_type != gltf2_blender_gather_tree.VExportNode.BONE]) - - def find_parent_joint(joints, name): - for joint in joints: - if joint.name == name: - return joint - parent_joint = find_parent_joint(joint.children, name) - if parent_joint: - return parent_joint - return None - for child in direct_bone_children: # List of object that are parented to bones # find parent joint - parent_joint = find_parent_joint(root_joints, vtree.nodes[child].blender_object.parent_bone) + parent_joint = __find_parent_joint(root_joints, vtree.nodes[child].blender_object.parent_bone) if not parent_joint: continue child_node = gather_node(vtree.nodes[child], export_settings) if child_node is None: continue - blender_bone = blender_object.pose.bones[parent_joint.name] mat = vtree.nodes[vtree.nodes[child].parent_bone_uuid].matrix_world.inverted_safe() @ vtree.nodes[child].matrix_world loc, rot_quat, scale = mat.decompose() @@ -142,6 +144,15 @@ def find_parent_joint(joints, name): return children +def __find_parent_joint(joints, name): + for joint in joints: + if joint.name == name: + return joint + parent_joint = __find_parent_joint(joint.children, name) + if parent_joint: + return parent_joint + return None + def __gather_extensions(blender_object, export_settings): extensions = {} diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py index 98f6245d7..5ac0b86b7 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py @@ -22,6 +22,7 @@ from ...io.com import gltf2_io_constants from ...io.exp import gltf2_io_binary_data from . import gltf2_blender_gather_accessors +from .gltf2_blender_gather_joints import gather_joint_vnode class VExportNode: @@ -278,23 +279,39 @@ def recursive_node_traverse(self, blender_object, blender_bone, parent_uuid, par def get_all_objects(self): return [n.uuid for n in self.nodes.values() if n.blender_type != VExportNode.BONE] - def get_all_bones(self, uuid): #For armatue Only - if self.nodes[uuid].blender_type == VExportNode.ARMATURE: - def recursive_get_all_bones(uuid): - total = [] - if self.nodes[uuid].blender_type == VExportNode.BONE: - total.append(uuid) - for child_uuid in self.nodes[uuid].children: - total.extend(recursive_get_all_bones(child_uuid)) - - return total - - tot = [] - for c_uuid in self.nodes[uuid].children: - tot.extend(recursive_get_all_bones(c_uuid)) - return tot + def get_all_bones(self, uuid): #For armature only + if not hasattr(self.nodes[uuid], "all_bones"): + if self.nodes[uuid].blender_type == VExportNode.ARMATURE: + def recursive_get_all_bones(uuid): + total = [] + if self.nodes[uuid].blender_type == VExportNode.BONE: + total.append(uuid) + for child_uuid in self.nodes[uuid].children: + total.extend(recursive_get_all_bones(child_uuid)) + + return total + + tot = [] + for c_uuid in self.nodes[uuid].children: + tot.extend(recursive_get_all_bones(c_uuid)) + self.nodes[uuid].all_bones = tot + return tot + else: + self.nodes[uuid].all_bones = [] + return [] + else: + return self.nodes[uuid].all_bones + + def get_root_bones_uuid(self, uuid): #For armature only + if not hasattr(self.nodes[uuid], "root_bones_uuid"): + if self.nodes[uuid].blender_type == VExportNode.ARMATURE: + all_armature_children = self.nodes[uuid].children + self.nodes[uuid].root_bones_uuid = [c for c in all_armature_children if self.nodes[c].blender_type == VExportNode.BONE] + else: + self.nodes[uuid].root_bones_uuid = [] + return [] else: - return [] + return self.nodes[uuid].root_bones_uuid def get_all_node_of_type(self, node_type): return [n.uuid for n in self.nodes.values() if n.blender_type == node_type] @@ -305,7 +322,6 @@ def display(self, mode): print("Root", self.nodes[n].blender_object.name, "/", self.nodes[n].blender_bone.name if self.nodes[n].blender_bone else "" ) self.nodes[n].recursive_display(self, mode) - def filter_tag(self): roots = self.roots.copy() for r in roots: @@ -322,7 +338,6 @@ def filter(self): self.filter_perform() self.remove_filtered_nodes() - def recursive_filter_tag(self, uuid, parent_keep_tag): # parent_keep_tag is for collection instance # some properties (selection, visibility, renderability) @@ -434,10 +449,15 @@ def node_filter_inheritable_is_kept(self, uuid): if not found: return False + #TODO add option + if self.nodes[uuid].blender_type == VExportNode.ARMATURE: + return False + return True def remove_filtered_nodes(self): - self.nodes = {k:n for (k, n) in self.nodes.items() if n.keep_tag is True} + # TODO add option + self.nodes = {k:n for (k, n) in self.nodes.items() if n.keep_tag is True or (n.keep_tag is False and n.blender_type == VExportNode.ARMATURE)} def search_missing_armature(self): for n in [n for n in self.nodes.values() if hasattr(n, "armature_needed") is True]: @@ -446,6 +466,14 @@ def search_missing_armature(self): n.armature = candidates[0].uuid del n.armature_needed + def bake_armature_bone_list(self): + # Used to store data in armature vnode + # If armature is removed from export + # Data are still available, even if armature is not exported (so bones are re-parented) + for n in [n for n in self.nodes.values() if n.blender_type == VExportNode.ARMATURE]: + self.get_all_bones(n.uuid) + self.get_root_bones_uuid(n.uuid) + def add_neutral_bones(self): added_armatures = [] for n in [n for n in self.nodes.values() if \ From 1174dc88170aec77ccaceffdd2e40564d91176ac Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Mon, 11 Sep 2023 16:36:20 +0200 Subject: [PATCH 02/10] Manage objects parented to bones --- .../blender/exp/gltf2_blender_gather.py | 13 +++- .../blender/exp/gltf2_blender_gather_nodes.py | 76 +++++++++++-------- .../blender/exp/gltf2_blender_gather_tree.py | 2 + 3 files changed, 57 insertions(+), 34 deletions(-) diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py index e6a7760cc..939c01253 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py @@ -79,21 +79,28 @@ def __gather_scene(blender_scene, export_settings): export_settings['vtree'] = vtree #TODO add option + armature_root_joints = {} for r in [vtree.nodes[r] for r in vtree.roots]: if r.blender_type != gltf2_blender_gather_tree.VExportNode.BONE: node = gltf2_blender_gather_nodes.gather_node( r, export_settings) if node is not None: - print(node.name) scene.nodes.append(node) else: # We can have bone are root of scene because we remove the armature object - # and the armature as at root of scene + # and the armature was at root of scene node = gltf2_blender_gather_joints.gather_joint_vnode( r.uuid, export_settings) if node is not None: - print(node.name) scene.nodes.append(node) + if r.armature not in armature_root_joints.keys(): + armature_root_joints[r.armature] = [] + armature_root_joints[r.armature].append(node) + + # Manage objects parented to bones, now we go through all root objects + for k, v in armature_root_joints.items(): + gltf2_blender_gather_nodes.get_objects_parented_to_bones(k, v, export_settings) + vtree.add_neutral_bones() export_user_extensions('gather_scene_hook', export_settings, scene, blender_scene) diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py index a09acc7f9..55fc82c72 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py @@ -77,8 +77,8 @@ def __gather_children(vnode, export_settings): vtree = export_settings['vtree'] - if vnode.blender_object.name == "Plane": - print([vtree.nodes[c].blender_object.name for c in vnode.children]) + + armature_object_uuid = None # Standard Children / Collection # TODO add option @@ -86,6 +86,7 @@ def __gather_children(vnode, export_settings): # node = gather_node(c, export_settings) # if node is not None: # children.append(node) + root_joints = [] for c in [vtree.nodes[c] for c in vnode.children]: if c.blender_type != gltf2_blender_gather_tree.VExportNode.BONE: node = gather_node(c, export_settings) @@ -95,11 +96,15 @@ def __gather_children(vnode, export_settings): # We come here because armature was remove, and bone can be a child of any object joint = gltf2_blender_gather_joints.gather_joint_vnode(c.uuid, export_settings) children.append(joint) + armature_object_uuid = c.armature + root_joints.append(joint) - #TODO : object parented to bone + # Now got all bone children (that are root joints), we can get object parented to bones # Armature --> Retrieve Blender bones + # This can't happen if we remove the Armature Object if vnode.blender_type == gltf2_blender_gather_tree.VExportNode.ARMATURE: + armature_object_uuid = vnode.uuid root_joints = [] root_bones_uuid = export_settings['vtree'].get_root_bones_uuid(vnode.uuid) for bone_uuid in root_bones_uuid: @@ -107,43 +112,52 @@ def __gather_children(vnode, export_settings): children.append(joint) root_joints.append(joint) + + if vnode.blender_type == gltf2_blender_gather_tree.VExportNode.ARMATURE \ + or armature_object_uuid is not None: + # Object parented to bones - direct_bone_children = [] - for n in [vtree.nodes[i] for i in vtree.get_all_bones(vnode.uuid)]: - direct_bone_children.extend([c for c in n.children if vtree.nodes[c].blender_type != gltf2_blender_gather_tree.VExportNode.BONE]) + get_objects_parented_to_bones(armature_object_uuid, root_joints, export_settings) - for child in direct_bone_children: # List of object that are parented to bones - # find parent joint - parent_joint = __find_parent_joint(root_joints, vtree.nodes[child].blender_object.parent_bone) - if not parent_joint: - continue - child_node = gather_node(vtree.nodes[child], export_settings) - if child_node is None: - continue + return children - mat = vtree.nodes[vtree.nodes[child].parent_bone_uuid].matrix_world.inverted_safe() @ vtree.nodes[child].matrix_world - loc, rot_quat, scale = mat.decompose() +def get_objects_parented_to_bones(armature_object_uuid, root_joints, export_settings): + vtree = export_settings['vtree'] + direct_bone_children = [] + for n in [vtree.nodes[i] for i in vtree.get_all_bones(armature_object_uuid)]: + direct_bone_children.extend([c for c in n.children if vtree.nodes[c].blender_type != gltf2_blender_gather_tree.VExportNode.BONE]) - trans = __convert_swizzle_location(loc, export_settings) - rot = __convert_swizzle_rotation(rot_quat, export_settings) - sca = __convert_swizzle_scale(scale, export_settings) + for child in direct_bone_children: # List of object that are parented to bones + # find parent joint + parent_joint = __find_parent_joint(root_joints, vtree.nodes[child].blender_object.parent_bone) + if not parent_joint: + continue + child_node = gather_node(vtree.nodes[child], export_settings) + if child_node is None: + continue + mat = vtree.nodes[vtree.nodes[child].parent_bone_uuid].matrix_world.inverted_safe() @ vtree.nodes[child].matrix_world + loc, rot_quat, scale = mat.decompose() - translation, rotation, scale = (None, None, None) - if trans[0] != 0.0 or trans[1] != 0.0 or trans[2] != 0.0: - translation = [trans[0], trans[1], trans[2]] - if rot[0] != 1.0 or rot[1] != 0.0 or rot[2] != 0.0 or rot[3] != 0.0: - rotation = [rot[1], rot[2], rot[3], rot[0]] - if sca[0] != 1.0 or sca[1] != 1.0 or sca[2] != 1.0: - scale = [sca[0], sca[1], sca[2]] + trans = __convert_swizzle_location(loc, export_settings) + rot = __convert_swizzle_rotation(rot_quat, export_settings) + sca = __convert_swizzle_scale(scale, export_settings) - child_node.translation = translation - child_node.rotation = rotation - child_node.scale = scale - parent_joint.children.append(child_node) + translation, rotation, scale = (None, None, None) + if trans[0] != 0.0 or trans[1] != 0.0 or trans[2] != 0.0: + translation = [trans[0], trans[1], trans[2]] + if rot[0] != 1.0 or rot[1] != 0.0 or rot[2] != 0.0 or rot[3] != 0.0: + rotation = [rot[1], rot[2], rot[3], rot[0]] + if sca[0] != 1.0 or sca[1] != 1.0 or sca[2] != 1.0: + scale = [sca[0], sca[1], sca[2]] + + child_node.translation = translation + child_node.rotation = rotation + child_node.scale = scale + + parent_joint.children.append(child_node) - return children def __find_parent_joint(joints, name): for joint in joints: diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py index 5ac0b86b7..ef8d5b204 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py @@ -450,6 +450,7 @@ def node_filter_inheritable_is_kept(self, uuid): return False #TODO add option + # If we remove the Armature object if self.nodes[uuid].blender_type == VExportNode.ARMATURE: return False @@ -457,6 +458,7 @@ def node_filter_inheritable_is_kept(self, uuid): def remove_filtered_nodes(self): # TODO add option + # If we remove the Armature object self.nodes = {k:n for (k, n) in self.nodes.items() if n.keep_tag is True or (n.keep_tag is False and n.blender_type == VExportNode.ARMATURE)} def search_missing_armature(self): From cf729196686dd59acda85d884f08b63ae58d7e2b Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Mon, 11 Sep 2023 16:45:44 +0200 Subject: [PATCH 03/10] comments --- addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py | 2 +- .../io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py | 2 +- .../io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py index 939c01253..1dbc0628e 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py @@ -78,7 +78,7 @@ def __gather_scene(blender_scene, export_settings): export_settings['vtree'] = vtree - #TODO add option + #TODOARMA add option armature_root_joints = {} for r in [vtree.nodes[r] for r in vtree.roots]: if r.blender_type != gltf2_blender_gather_tree.VExportNode.BONE: diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py index 11668c613..59ad1fa6b 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py @@ -60,7 +60,7 @@ def gather_joint_vnode(vnode, export_settings): vtree = export_settings['vtree'] blender_bone = vtree.nodes[vnode].blender_bone - #TODO add option + #TODOARMA add option if vtree.nodes[vnode].parent_uuid is not None: mat = vtree.nodes[vtree.nodes[vnode].parent_uuid].matrix_world.inverted_safe() @ vtree.nodes[vnode].matrix_world else: diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py index ef8d5b204..5338e43ad 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py @@ -449,7 +449,7 @@ def node_filter_inheritable_is_kept(self, uuid): if not found: return False - #TODO add option + #TODOARMA add option # If we remove the Armature object if self.nodes[uuid].blender_type == VExportNode.ARMATURE: return False @@ -457,7 +457,7 @@ def node_filter_inheritable_is_kept(self, uuid): return True def remove_filtered_nodes(self): - # TODO add option + #TODOARMA add option # If we remove the Armature object self.nodes = {k:n for (k, n) in self.nodes.items() if n.keep_tag is True or (n.keep_tag is False and n.blender_type == VExportNode.ARMATURE)} From ceb4b8f0a4840acf79e1e3e17c6fc591a0aa9137 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Mon, 11 Sep 2023 18:58:05 +0200 Subject: [PATCH 04/10] WIP - animations are back - WIP --- .../exp/animation/gltf2_blender_gather_action.py | 12 +++++++++--- .../gltf2_blender_gather_scene_animation.py | 4 +++- .../exp/animation/gltf2_blender_gather_tracks.py | 4 +++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_action.py b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_action.py index ad8c7d872..ea5bce421 100644 --- a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_action.py +++ b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_action.py @@ -44,7 +44,9 @@ def gather_actions_animations(export_settings): # Do not manage not exported objects if vtree.nodes[obj_uuid].node is None: - continue + #TODOARMA + if not vtree.nodes[obj_uuid].blender_object: + continue animations_, merged_tracks = gather_action_animations(obj_uuid, merged_tracks, len(animations), export_settings) animations += animations_ @@ -73,7 +75,9 @@ def prepare_actions_range(export_settings): # Do not manage not exported objects if vtree.nodes[obj_uuid].node is None: - continue + #TODOARMA + if not vtree.nodes[obj_uuid].blender_object: + continue if obj_uuid not in export_settings['ranges']: export_settings['ranges'][obj_uuid] = {} @@ -178,7 +182,9 @@ def prepare_actions_range(export_settings): # Do not manage not exported objects if vtree.nodes[obj_uuid].node is None: - continue + #TODOARMA + if not vtree.nodes[obj_uuid].blender_object: + continue blender_actions = __get_blender_actions(obj_uuid, export_settings) for blender_action, track, type_ in blender_actions: diff --git a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_scene_animation.py b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_scene_animation.py index 51c628f41..be8d4a58f 100644 --- a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_scene_animation.py +++ b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_scene_animation.py @@ -45,7 +45,9 @@ def gather_scene_animations(export_settings): # Do not manage not exported objects if vtree.nodes[obj_uuid].node is None: - continue + #TODOARMA + if not vtree.nodes[obj_uuid].blender_object: + continue blender_object = export_settings['vtree'].nodes[obj_uuid].blender_object diff --git a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_tracks.py b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_tracks.py index 29176ec63..09ac43cd2 100644 --- a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_tracks.py +++ b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_tracks.py @@ -32,7 +32,9 @@ def gather_tracks_animations(export_settings): # Do not manage not exported objects if vtree.nodes[obj_uuid].node is None: - continue + #TODOARMA + if not vtree.nodes[obj_uuid].blender_object: + continue animations_, merged_tracks = gather_track_animations(obj_uuid, merged_tracks, len(animations), export_settings) animations += animations_ From 4de5303e41215fe757206630ccfb584abff5aa16 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Mon, 11 Sep 2023 21:25:25 +0200 Subject: [PATCH 05/10] Fix unused skin export when no armature --- .../blender/exp/gltf2_blender_gather_tree.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py index 5338e43ad..5f704f067 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py @@ -79,7 +79,7 @@ def set_blender_data(self, blender_object, blender_bone): def recursive_display(self, tree, mode): if mode == "simple": for c in self.children: - print(self.blender_object.name, "/", self.blender_bone.name if self.blender_bone else "", "-->", tree.nodes[c].blender_object.name, "/", tree.nodes[c].blender_bone.name if tree.nodes[c].blender_bone else "" ) + print(tree.nodes[c].uuid, self.blender_object.name, "/", self.blender_bone.name if self.blender_bone else "", "-->", tree.nodes[c].blender_object.name, "/", tree.nodes[c].blender_bone.name if tree.nodes[c].blender_bone else "" ) tree.nodes[c].recursive_display(tree, mode) class VExportTree: @@ -319,7 +319,7 @@ def get_all_node_of_type(self, node_type): def display(self, mode): if mode == "simple": for n in self.roots: - print("Root", self.nodes[n].blender_object.name, "/", self.nodes[n].blender_bone.name if self.nodes[n].blender_bone else "" ) + print(self.nodes[n].uuid, "Root", self.nodes[n].blender_object.name, "/", self.nodes[n].blender_bone.name if self.nodes[n].blender_bone else "" ) self.nodes[n].recursive_display(self, mode) def filter_tag(self): @@ -452,6 +452,7 @@ def node_filter_inheritable_is_kept(self, uuid): #TODOARMA add option # If we remove the Armature object if self.nodes[uuid].blender_type == VExportNode.ARMATURE: + self.nodes[uuid].arma_exported = True return False return True @@ -543,6 +544,9 @@ def get_unused_skins(self): from .gltf2_blender_gather_skins import gather_skin skins = [] for n in [n for n in self.nodes.values() if n.blender_type == VExportNode.ARMATURE]: + #TODO add option + if hasattr(n, "arma_exported") is False: + continue if len([m for m in self.nodes.values() if m.keep_tag is True and m.blender_type == VExportNode.OBJECT and m.armature == n.uuid]) == 0: skin = gather_skin(n.uuid, self.export_settings) skins.append(skin) From 8023259f34a09b008e57446fc25b1f8328da4720 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Tue, 12 Sep 2023 03:16:57 +0200 Subject: [PATCH 06/10] Fix TRS of root bone --- .../io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py index 59ad1fa6b..1a7d6acd9 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py @@ -64,7 +64,7 @@ def gather_joint_vnode(vnode, export_settings): if vtree.nodes[vnode].parent_uuid is not None: mat = vtree.nodes[vtree.nodes[vnode].parent_uuid].matrix_world.inverted_safe() @ vtree.nodes[vnode].matrix_world else: - mat = vtree.nodes[vtree.nodes[vnode].armature].matrix_world.inverted_safe() @ vtree.nodes[vnode].matrix_world + mat = vtree.nodes[vnode].matrix_world trans, rot, sca = mat.decompose() From f1ffaa52eb3b424f573752a45f2e805f2ca8df2c Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Tue, 12 Sep 2023 03:17:08 +0200 Subject: [PATCH 07/10] Fix anim TRS of root bone --- .../sampled/gltf2_blender_gather_animation_sampling_cache.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/io_scene_gltf2/blender/exp/animation/sampled/gltf2_blender_gather_animation_sampling_cache.py b/addons/io_scene_gltf2/blender/exp/animation/sampled/gltf2_blender_gather_animation_sampling_cache.py index e803df63c..8605d751c 100644 --- a/addons/io_scene_gltf2/blender/exp/animation/sampled/gltf2_blender_gather_animation_sampling_cache.py +++ b/addons/io_scene_gltf2/blender/exp/animation/sampled/gltf2_blender_gather_animation_sampling_cache.py @@ -125,6 +125,10 @@ def get_cache_data(path: str, # Bone has a parent, but in export, after filter, is at root of armature matrix = blender_bone.matrix.copy() + #TODO add option + # Because there is no armature object, we need to apply the TRS of armature to the root bone + matrix = matrix @ blender_obj.matrix_world + if blender_obj.animation_data and blender_obj.animation_data.action \ and export_settings['gltf_animation_mode'] in ["ACTIVE_ACTIONS", "ACTIONS"]: if blender_bone.name not in data[obj_uuid][blender_obj.animation_data.action.name]['bone'].keys(): From fc39a946ce57bbcf33625caf8792ef963957d570 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Mon, 18 Sep 2023 15:04:13 +0200 Subject: [PATCH 08/10] Add option to choose remove armature object or not --- addons/io_scene_gltf2/__init__.py | 12 +++++ .../animation/gltf2_blender_gather_action.py | 21 +++++--- .../gltf2_blender_gather_scene_animation.py | 7 ++- .../animation/gltf2_blender_gather_tracks.py | 7 ++- ...blender_gather_animation_sampling_cache.py | 4 +- .../blender/exp/gltf2_blender_gather.py | 50 ++++++++++++------- .../exp/gltf2_blender_gather_joints.py | 10 ++-- .../blender/exp/gltf2_blender_gather_nodes.py | 29 +++++------ .../blender/exp/gltf2_blender_gather_tree.py | 37 +++++++++----- 9 files changed, 117 insertions(+), 60 deletions(-) diff --git a/addons/io_scene_gltf2/__init__.py b/addons/io_scene_gltf2/__init__.py index 1ad3ab557..8f2704306 100644 --- a/addons/io_scene_gltf2/__init__.py +++ b/addons/io_scene_gltf2/__init__.py @@ -465,6 +465,15 @@ def __init__(self): default=False ) + export_armature_object_remove: BoolProperty( + name='Remove Armature Object', + description=( + 'Remove Armature object if possible.' + 'If Armature has multiple root bones, object will not be removed' + ), + default=False + ) + export_optimize_animation_size: BoolProperty( name='Optimize Animation Size', description=( @@ -773,6 +782,7 @@ def execute(self, context): export_settings['gltf_animations'] = self.export_animations export_settings['gltf_def_bones'] = self.export_def_bones export_settings['gltf_flatten_bones_hierarchy'] = self.export_hierarchy_flatten_bones + export_settings['gltf_armature_object_remove'] = self.export_armature_object_remove if self.export_animations: export_settings['gltf_frame_range'] = self.export_frame_range export_settings['gltf_force_sampling'] = self.export_force_sampling @@ -1185,6 +1195,8 @@ def draw(self, context): if operator.export_force_sampling is False and operator.export_def_bones is True: layout.label(text="Export only deformation bones is not possible when not sampling animation") row = layout.row() + row.prop(operator, 'export_armature_object_remove') + row = layout.row() row.prop(operator, 'export_hierarchy_flatten_bones') class GLTF_PT_export_data_compression(bpy.types.Panel): diff --git a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_action.py b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_action.py index ea5bce421..e08e625c4 100644 --- a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_action.py +++ b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_action.py @@ -44,8 +44,11 @@ def gather_actions_animations(export_settings): # Do not manage not exported objects if vtree.nodes[obj_uuid].node is None: - #TODOARMA - if not vtree.nodes[obj_uuid].blender_object: + if export_settings["gltf_armature_object_remove"] is True: + # Manage armature object, as this is the object that has the animation + if not vtree.nodes[obj_uuid].blender_object: + continue + else: continue animations_, merged_tracks = gather_action_animations(obj_uuid, merged_tracks, len(animations), export_settings) @@ -75,8 +78,11 @@ def prepare_actions_range(export_settings): # Do not manage not exported objects if vtree.nodes[obj_uuid].node is None: - #TODOARMA - if not vtree.nodes[obj_uuid].blender_object: + if export_settings["gltf_armature_object_remove"] is True: + # Manage armature object, as this is the object that has the animation + if not vtree.nodes[obj_uuid].blender_object: + continue + else: continue if obj_uuid not in export_settings['ranges']: @@ -182,8 +188,11 @@ def prepare_actions_range(export_settings): # Do not manage not exported objects if vtree.nodes[obj_uuid].node is None: - #TODOARMA - if not vtree.nodes[obj_uuid].blender_object: + if export_settings['gltf_armature_object_remove'] is True: + # Manage armature object, as this is the object that has the animation + if not vtree.nodes[obj_uuid].blender_object: + continue + else: continue blender_actions = __get_blender_actions(obj_uuid, export_settings) diff --git a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_scene_animation.py b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_scene_animation.py index be8d4a58f..e8abcab8c 100644 --- a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_scene_animation.py +++ b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_scene_animation.py @@ -45,8 +45,11 @@ def gather_scene_animations(export_settings): # Do not manage not exported objects if vtree.nodes[obj_uuid].node is None: - #TODOARMA - if not vtree.nodes[obj_uuid].blender_object: + if export_settings['gltf_armature_object_remove'] is True: + # Manage armature object, as this is the object that has the animation + if not vtree.nodes[obj_uuid].blender_object: + continue + else: continue blender_object = export_settings['vtree'].nodes[obj_uuid].blender_object diff --git a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_tracks.py b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_tracks.py index 09ac43cd2..ecee97902 100644 --- a/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_tracks.py +++ b/addons/io_scene_gltf2/blender/exp/animation/gltf2_blender_gather_tracks.py @@ -32,8 +32,11 @@ def gather_tracks_animations(export_settings): # Do not manage not exported objects if vtree.nodes[obj_uuid].node is None: - #TODOARMA - if not vtree.nodes[obj_uuid].blender_object: + if export_settings['gltf_armature_object_remove'] is True: + # Manage armature object, as this is the object that has the animation + if not vtree.nodes[obj_uuid].blender_object: + continue + else: continue animations_, merged_tracks = gather_track_animations(obj_uuid, merged_tracks, len(animations), export_settings) diff --git a/addons/io_scene_gltf2/blender/exp/animation/sampled/gltf2_blender_gather_animation_sampling_cache.py b/addons/io_scene_gltf2/blender/exp/animation/sampled/gltf2_blender_gather_animation_sampling_cache.py index 8605d751c..351f6e673 100644 --- a/addons/io_scene_gltf2/blender/exp/animation/sampled/gltf2_blender_gather_animation_sampling_cache.py +++ b/addons/io_scene_gltf2/blender/exp/animation/sampled/gltf2_blender_gather_animation_sampling_cache.py @@ -125,9 +125,9 @@ def get_cache_data(path: str, # Bone has a parent, but in export, after filter, is at root of armature matrix = blender_bone.matrix.copy() - #TODO add option # Because there is no armature object, we need to apply the TRS of armature to the root bone - matrix = matrix @ blender_obj.matrix_world + if export_settings['gltf_armature_object_remove'] is True: + matrix = matrix @ blender_obj.matrix_world if blender_obj.animation_data and blender_obj.animation_data.action \ and export_settings['gltf_animation_mode'] in ["ACTIVE_ACTIONS", "ACTIONS"]: diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py index 1dbc0628e..dd1bc02a3 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather.py @@ -64,6 +64,7 @@ def __gather_scene(blender_scene, export_settings): vtree.construct(blender_scene) vtree.search_missing_armature() # In case armature are no parented correctly vtree.bake_armature_bone_list() # Used in case we remove the armature + vtree.check_if_we_can_remove_armature() # Check if we can remove the armatures objects export_user_extensions('vtree_before_filter_hook', export_settings, vtree) @@ -78,28 +79,41 @@ def __gather_scene(blender_scene, export_settings): export_settings['vtree'] = vtree - #TODOARMA add option - armature_root_joints = {} - for r in [vtree.nodes[r] for r in vtree.roots]: - if r.blender_type != gltf2_blender_gather_tree.VExportNode.BONE: + + + + # If we don't remove armature object, we can't have bones directly at root of scene + # So looping only on root nodes, as they are all nodes, not bones + if export_settings['gltf_armature_object_remove'] is False: + for r in [vtree.nodes[r] for r in vtree.roots]: node = gltf2_blender_gather_nodes.gather_node( r, export_settings) if node is not None: scene.nodes.append(node) - else: - # We can have bone are root of scene because we remove the armature object - # and the armature was at root of scene - node = gltf2_blender_gather_joints.gather_joint_vnode( - r.uuid, export_settings) - if node is not None: - scene.nodes.append(node) - if r.armature not in armature_root_joints.keys(): - armature_root_joints[r.armature] = [] - armature_root_joints[r.armature].append(node) - - # Manage objects parented to bones, now we go through all root objects - for k, v in armature_root_joints.items(): - gltf2_blender_gather_nodes.get_objects_parented_to_bones(k, v, export_settings) + else: + # If we remove armature objects, we can have bone at root of scene + armature_root_joints = {} + for r in [vtree.nodes[r] for r in vtree.roots]: + # Classic Object/node case + if r.blender_type != gltf2_blender_gather_tree.VExportNode.BONE: + node = gltf2_blender_gather_nodes.gather_node( + r, export_settings) + if node is not None: + scene.nodes.append(node) + else: + # We can have bone are root of scene because we remove the armature object + # and the armature was at root of scene + node = gltf2_blender_gather_joints.gather_joint_vnode( + r.uuid, export_settings) + if node is not None: + scene.nodes.append(node) + if r.armature not in armature_root_joints.keys(): + armature_root_joints[r.armature] = [] + armature_root_joints[r.armature].append(node) + + # Manage objects parented to bones, now we go through all root objects + for k, v in armature_root_joints.items(): + gltf2_blender_gather_nodes.get_objects_parented_to_bones(k, v, export_settings) vtree.add_neutral_bones() diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py index 1a7d6acd9..f3c54fbc7 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_joints.py @@ -60,11 +60,13 @@ def gather_joint_vnode(vnode, export_settings): vtree = export_settings['vtree'] blender_bone = vtree.nodes[vnode].blender_bone - #TODOARMA add option - if vtree.nodes[vnode].parent_uuid is not None: - mat = vtree.nodes[vtree.nodes[vnode].parent_uuid].matrix_world.inverted_safe() @ vtree.nodes[vnode].matrix_world + if export_settings['gltf_armature_object_remove'] is True: + if vtree.nodes[vnode].parent_uuid is not None: + mat = vtree.nodes[vtree.nodes[vnode].parent_uuid].matrix_world.inverted_safe() @ vtree.nodes[vnode].matrix_world + else: + mat = vtree.nodes[vnode].matrix_world else: - mat = vtree.nodes[vnode].matrix_world + mat = vtree.nodes[vtree.nodes[vnode].parent_uuid].matrix_world.inverted_safe() @ vtree.nodes[vnode].matrix_world trans, rot, sca = mat.decompose() diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py index 55fc82c72..92c7f4bc5 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_nodes.py @@ -81,23 +81,24 @@ def __gather_children(vnode, export_settings): armature_object_uuid = None # Standard Children / Collection - # TODO add option - # for c in [vtree.nodes[c] for c in vnode.children if vtree.nodes[c].blender_type != gltf2_blender_gather_tree.VExportNode.BONE]: - # node = gather_node(c, export_settings) - # if node is not None: - # children.append(node) - root_joints = [] - for c in [vtree.nodes[c] for c in vnode.children]: - if c.blender_type != gltf2_blender_gather_tree.VExportNode.BONE: + if export_settings['gltf_armature_object_remove'] is False: + for c in [vtree.nodes[c] for c in vnode.children if vtree.nodes[c].blender_type != gltf2_blender_gather_tree.VExportNode.BONE]: node = gather_node(c, export_settings) if node is not None: children.append(node) - else: - # We come here because armature was remove, and bone can be a child of any object - joint = gltf2_blender_gather_joints.gather_joint_vnode(c.uuid, export_settings) - children.append(joint) - armature_object_uuid = c.armature - root_joints.append(joint) + else: + root_joints = [] + for c in [vtree.nodes[c] for c in vnode.children]: + if c.blender_type != gltf2_blender_gather_tree.VExportNode.BONE: + node = gather_node(c, export_settings) + if node is not None: + children.append(node) + else: + # We come here because armature was remove, and bone can be a child of any object + joint = gltf2_blender_gather_joints.gather_joint_vnode(c.uuid, export_settings) + children.append(joint) + armature_object_uuid = c.armature + root_joints.append(joint) # Now got all bone children (that are root joints), we can get object parented to bones diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py index 5f704f067..c89126ab1 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py @@ -295,7 +295,7 @@ def recursive_get_all_bones(uuid): for c_uuid in self.nodes[uuid].children: tot.extend(recursive_get_all_bones(c_uuid)) self.nodes[uuid].all_bones = tot - return tot + return tot # Not really needed to return, we are just baking it before export really starts else: self.nodes[uuid].all_bones = [] return [] @@ -307,6 +307,7 @@ def get_root_bones_uuid(self, uuid): #For armature only if self.nodes[uuid].blender_type == VExportNode.ARMATURE: all_armature_children = self.nodes[uuid].children self.nodes[uuid].root_bones_uuid = [c for c in all_armature_children if self.nodes[c].blender_type == VExportNode.BONE] + return self.nodes[uuid].root_bones_uuid # Not really needed to return, we are just baking it before export really starts else: self.nodes[uuid].root_bones_uuid = [] return [] @@ -449,18 +450,20 @@ def node_filter_inheritable_is_kept(self, uuid): if not found: return False - #TODOARMA add option - # If we remove the Armature object - if self.nodes[uuid].blender_type == VExportNode.ARMATURE: - self.nodes[uuid].arma_exported = True - return False + if self.export_settings['gltf_armature_object_remove'] is True: + # If we remove the Armature object + if self.nodes[uuid].blender_type == VExportNode.ARMATURE: + self.nodes[uuid].arma_exported = True + return False return True def remove_filtered_nodes(self): - #TODOARMA add option - # If we remove the Armature object - self.nodes = {k:n for (k, n) in self.nodes.items() if n.keep_tag is True or (n.keep_tag is False and n.blender_type == VExportNode.ARMATURE)} + if self.export_settings['gltf_armature_object_remove'] is True: + # If we remove the Armature object + self.nodes = {k:n for (k, n) in self.nodes.items() if n.keep_tag is True or (n.keep_tag is False and n.blender_type == VExportNode.ARMATURE)} + else: + self.nodes = {k:n for (k, n) in self.nodes.items() if n.keep_tag is True} def search_missing_armature(self): for n in [n for n in self.nodes.values() if hasattr(n, "armature_needed") is True]: @@ -544,9 +547,9 @@ def get_unused_skins(self): from .gltf2_blender_gather_skins import gather_skin skins = [] for n in [n for n in self.nodes.values() if n.blender_type == VExportNode.ARMATURE]: - #TODO add option - if hasattr(n, "arma_exported") is False: - continue + if self.export_settings['gltf_armature_object_remove'] is True: + if hasattr(n, "arma_exported") is False: + continue if len([m for m in self.nodes.values() if m.keep_tag is True and m.blender_type == VExportNode.OBJECT and m.armature == n.uuid]) == 0: skin = gather_skin(n.uuid, self.export_settings) skins.append(skin) @@ -578,3 +581,13 @@ def break_bone_hierarchy(self): self.nodes[self.nodes[bone].parent_uuid].children.remove(bone) self.nodes[bone].parent_uuid = arma self.nodes[arma].children.append(bone) + + def check_if_we_can_remove_armature(self): + # If user requested to remove armature, we need to check if it is possible + # If is impossible to remove it if armature has multiple root bones. (glTF validator error) + # Currently, we manage it at export level, not at each armature level + for arma_uuid in [n for n in self.nodes.keys() if self.nodes[n].blender_type == VExportNode.ARMATURE]: + if len(self.get_root_bones_uuid(arma_uuid)) > 1: + # We can't remove armature + self.export_settings['gltf_armature_object_remove'] = False + break From e7db8735a4518014557902c607f20606e762948c Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Mon, 18 Sep 2023 15:07:31 +0200 Subject: [PATCH 09/10] Add warning if we can't remove the armature object --- addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py index c89126ab1..28f35c562 100644 --- a/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py +++ b/addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py @@ -590,4 +590,5 @@ def check_if_we_can_remove_armature(self): if len(self.get_root_bones_uuid(arma_uuid)) > 1: # We can't remove armature self.export_settings['gltf_armature_object_remove'] = False + print("WARNING: We can't remove armature object because some armatures have multiple root bones.") break From f3e9e1b3f0bbc099c92f5b4367a4e77bcf751c89 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Mon, 18 Sep 2023 15:09:31 +0200 Subject: [PATCH 10/10] Update doc --- docs/blender_docs/scene_gltf2.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/blender_docs/scene_gltf2.rst b/docs/blender_docs/scene_gltf2.rst index 4966fd5ea..a1c6ef46f 100644 --- a/docs/blender_docs/scene_gltf2.rst +++ b/docs/blender_docs/scene_gltf2.rst @@ -906,6 +906,8 @@ Use Rest Position Armature Export Deformation Bones only Export Deformation bones only, not other bones. Animation for deformation bones are baked. +Remove Armature Object + Remove Armature Objects if possible. If some armature(s) have multiple root bones, we can't remove them. Flatten Bone Hierarchy Useful in case of non-decomposable TRS matrix.