diff --git a/addons/io_scene_gltf2/__init__.py b/addons/io_scene_gltf2/__init__.py index d1c258f55..48bedc5bf 100644 --- a/addons/io_scene_gltf2/__init__.py +++ b/addons/io_scene_gltf2/__init__.py @@ -659,6 +659,13 @@ class ExportGLTF2_Base(ConvertGLTF2_Base): default=False ) + export_compare_meshes: BoolProperty( + name='Compare meshes', + description='Compare mesh objects after applying modifiers ' + '(to avoid duplication of identical meshes in the exported file)', + default=False + ) + export_shared_accessors: BoolProperty( name='Shared Accessors', description='Export Primitives using shared accessors for attributes', @@ -1200,6 +1207,7 @@ def execute(self, context): export_settings['gltf_extras'] = self.export_extras export_settings['gltf_yup'] = self.export_yup export_settings['gltf_apply'] = self.export_apply + export_settings['gltf_compare_meshes'] = self.export_compare_meshes export_settings['gltf_shared_accessors'] = self.export_shared_accessors export_settings['gltf_current_frame'] = self.export_current_frame export_settings['gltf_animations'] = self.export_animations @@ -1497,6 +1505,7 @@ def export_panel_data_mesh(layout, operator): header.label(text="Mesh") if body: body.prop(operator, 'export_apply') + body.prop(operator, 'export_compare_meshes') body.prop(operator, 'export_texcoords') body.prop(operator, 'export_normals') col = body.column() diff --git a/addons/io_scene_gltf2/blender/exp/gather.py b/addons/io_scene_gltf2/blender/exp/gather.py index f49036682..f29950dfc 100644 --- a/addons/io_scene_gltf2/blender/exp/gather.py +++ b/addons/io_scene_gltf2/blender/exp/gather.py @@ -111,6 +111,7 @@ def __gather_scene(blender_scene, export_settings): export_user_extensions('vtree_after_filter_hook', export_settings, vtree) export_settings['vtree'] = vtree + export_settings['mesh_instances'] = {} # 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 @@ -149,6 +150,8 @@ def __gather_scene(blender_scene, export_settings): export_user_extensions('gather_scene_hook', export_settings, scene, blender_scene) + export_settings['mesh_instances'].clear() + return scene diff --git a/addons/io_scene_gltf2/blender/exp/nodes.py b/addons/io_scene_gltf2/blender/exp/nodes.py index 829168940..2168aa333 100644 --- a/addons/io_scene_gltf2/blender/exp/nodes.py +++ b/addons/io_scene_gltf2/blender/exp/nodes.py @@ -262,6 +262,8 @@ def __gather_matrix(blender_object, export_settings): def __gather_mesh(vnode, blender_object, export_settings): + need_to_mesh_clear = False + if vnode.blender_type == VExportNode.COLLECTION: return None if blender_object and blender_object.type in ['CURVE', 'SURFACE', 'FONT']: @@ -319,6 +321,8 @@ def __gather_mesh(vnode, blender_object, export_settings): depsgraph = bpy.context.evaluated_depsgraph_get() blender_mesh_owner = blender_object.evaluated_get(depsgraph) blender_mesh = blender_mesh_owner.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph) + need_to_mesh_clear = True + # Seems now (from 4.2) the custom properties are Statically Typed # so no need to copy them in that case, because overwriting them will crash if len(blender_mesh.keys()) == 0: @@ -332,6 +336,23 @@ def __gather_mesh(vnode, blender_object, export_settings): # Store that this evaluated mesh has been created by the exporter, and is not a GN instance mesh blender_mesh['gltf2_mesh_applied'] = True + # check if other object used the same origin mesh produced the same mesh after modifier applies, if yes use this mesh + if export_settings["gltf_compare_meshes"]: + if not blender_object.data in export_settings["mesh_instances"]: + # make own copy of mesh because result of `to_mesh` is temporary + # (and can be overwrite for example by call `to_mesh` on same object in other instance of collection) + blender_mesh = blender_mesh.copy() + export_settings["mesh_instances"][blender_object.data] = [blender_mesh] + else: + for other_mesh in export_settings["mesh_instances"][blender_object.data]: + compare = blender_mesh.unit_test_compare(mesh=other_mesh) + if compare == "Same": + blender_mesh = other_mesh + break + else: + blender_mesh = blender_mesh.copy() + export_settings["mesh_instances"][blender_object.data].append(blender_mesh) + if export_settings['gltf_skins']: # restore Armature modifiers for idx, show_viewport in armature_modifiers.items(): @@ -348,12 +369,6 @@ def __gather_mesh(vnode, blender_object, export_settings): else: blender_mesh = blender_object.data - if not export_settings['gltf_skins']: - modifiers = None - else: - # Check if there is an armature modidier - if len([mod for mod in blender_object.modifiers if mod.type == "ARMATURE"]) == 0: - modifiers = None # Keep materials from object, as no modifiers are applied, so no risk that # modifiers changed them @@ -369,6 +384,12 @@ def __gather_mesh(vnode, blender_object, export_settings): if modifier.type == 'ARMATURE': uuid_for_skined_data = vnode.uuid + # no need keep modifiers if skins are not exported or object don't have an armature modifier + # (applying different set of modifiers results in different blender_mesh object) + # so set it to None to avoid unnecessary mesh duplication + if uuid_for_skined_data is None: + modifiers = None + result = gltf2_blender_gather_mesh.gather_mesh(blender_mesh, uuid_for_skined_data, blender_object.vertex_groups if blender_object else None, @@ -377,7 +398,7 @@ def __gather_mesh(vnode, blender_object, export_settings): None, export_settings) - if export_settings['gltf_apply'] and modifiers is not None: + if need_to_mesh_clear: blender_mesh_owner.to_mesh_clear() return result