This commit is contained in:
Mark Moissette 2024-03-19 21:14:24 +00:00 committed by GitHub
commit 260663e38e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 71 additions and 16 deletions

View File

@ -14,3 +14,7 @@ pub struct Animations {
/// this is for convenience, because currently , Bevy's gltf parsing inserts `AnimationPlayers` "one level down" /// this is for convenience, because currently , Bevy's gltf parsing inserts `AnimationPlayers` "one level down"
/// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid /// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid
pub struct AnimationPlayerLink(pub Entity); pub struct AnimationPlayerLink(pub Entity);
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct Animated;

View File

@ -120,6 +120,7 @@ impl Plugin for BlueprintsPlugin {
.register_type::<MaterialInfo>() .register_type::<MaterialInfo>()
.register_type::<SpawnHere>() .register_type::<SpawnHere>()
.register_type::<Animations>() .register_type::<Animations>()
.register_type::<Animated>()
.register_type::<BlueprintsList>() .register_type::<BlueprintsList>()
.register_type::<Vec<String>>() .register_type::<Vec<String>>()
.register_type::<HashMap<String, Vec<String>>>() .register_type::<HashMap<String, Vec<String>>>()

View File

@ -27,13 +27,42 @@ def remove_unwanted_custom_properties(object):
def duplicate_object(object): def duplicate_object(object):
obj_copy = object.copy() obj_copy = object.copy()
if object.data: # FIXME: orphan data comes from this one
"""if object.data:
data = object.data.copy() data = object.data.copy()
obj_copy.data = data obj_copy.data = data"""
if object.animation_data and object.animation_data.action: copy_animation_data(object, obj_copy)
obj_copy.animation_data.action = object.animation_data.action.copy() """if object.animation_data and object.animation_data.action:
if obj_copy.animation_data == None:
obj_copy.animation_data_create()
obj_copy.animation_data.action = object.animation_data.action.copy()"""
return obj_copy return obj_copy
# TODO: rename actions
def copy_animation_data(source, target):
"""if source.data:
data = source.data.copy()
target.data = data"""
if source.animation_data and source.animation_data.action:
print("current action name", source.animation_data.action.name)
print("copying animation data from", source.name, "to", target.name)
"""if target.animation_data == None:
target.animation_data_create()
target.animation_data.action = source.animation_data.action.copy()"""
# alternative method, using the build in link animation operator
with bpy.context.temp_override(active_object=source, selected_editable_objects=[target]):
bpy.ops.object.make_links_data(type='ANIMATION')
"""print("copying animation data for", source.name, target.animation_data)
properties = [p.identifier for p in source.animation_data.bl_rna.properties if not p.is_readonly]
for prop in properties:
print("copying stuff", prop)
setattr(target.animation_data, prop, getattr(source.animation_data, prop))"""
#also removes unwanted custom_properties for all objects in hiearchy #also removes unwanted custom_properties for all objects in hiearchy
def duplicate_object_recursive(object, parent, collection): def duplicate_object_recursive(object, parent, collection):
original_name = object.name original_name = object.name
@ -83,7 +112,16 @@ def copy_hollowed_collection_into(source_collection, destination_collection, par
get_sub_collections([object.instance_collection], root_node, children_per_collection) get_sub_collections([object.instance_collection], root_node, children_per_collection)
empty_obj["BlueprintsList"] = f"({json.dumps(dict(children_per_collection))})" empty_obj["BlueprintsList"] = f"({json.dumps(dict(children_per_collection))})"
#empty_obj["Assets"] = {"Animations": [], "Materials": [], "Models":[], "Textures":[], "Audio":[], "Other":[]} #empty_obj["Assets"] = {"Animations": [], "Materials": [], "Models":[], "Textures":[], "Audio":[], "Other":[]}
if object.animation_data:
print("I have animation data")
ad = object.animation_data
if ad.action:
print(object.name,'uses',ad.action.name)
for t in ad.nla_tracks:
for s in t.strips:
print(object.name,'uses',s.action.name)
empty_obj['Animated'] = '()'
copy_animation_data(object, empty_obj)
# we copy custom properties over from our original object to our empty # we copy custom properties over from our original object to our empty
for component_name, component_value in object.items(): for component_name, component_value in object.items():
@ -92,13 +130,13 @@ def copy_hollowed_collection_into(source_collection, destination_collection, par
if parent_empty is not None: if parent_empty is not None:
empty_obj.parent = parent_empty empty_obj.parent = parent_empty
else: else:
# we create a copy of our object and its children, to leave the original one as it is # we create a copy of our object and its children, to leave the original one as it is
if object.parent == None: if object.parent == None:
copy = duplicate_object_recursive(object, None, destination_collection) copy = duplicate_object_recursive(object, None, destination_collection)
if parent_empty is not None: if parent_empty is not None:
copy.parent = parent_empty copy.parent = parent_empty
if object.animation_data:
copy['Animated'] = '()'
# for every sub-collection of the source, copy its content into a new sub-collection of the destination # for every sub-collection of the source, copy its content into a new sub-collection of the destination
for collection in source_collection.children: for collection in source_collection.children:
@ -138,14 +176,14 @@ def clear_hollow_scene(temp_scene, original_root_collection):
# reset original names # reset original names
restore_original_names(original_root_collection) restore_original_names(original_root_collection)
# remove empties (only needed when we go via ops ????) # remove any data we created
temp_root_collection = temp_scene.collection temp_root_collection = temp_scene.collection
temp_scene_objects = [o for o in temp_root_collection.objects] temp_scene_objects = [o for o in temp_root_collection.all_objects]
for object in temp_scene_objects: for object in temp_scene_objects:
print("removing", object.name)
bpy.data.objects.remove(object, do_unlink=True) bpy.data.objects.remove(object, do_unlink=True)
# remove the temporary scene # remove the temporary scene
bpy.data.scenes.remove(temp_scene) bpy.data.scenes.remove(temp_scene, do_unlink=True)
# convenience utility to get lists of scenes # convenience utility to get lists of scenes
def get_scenes(addon_prefs): def get_scenes(addon_prefs):

View File

@ -19,7 +19,6 @@ def setup_data(request):
def finalizer(): def finalizer():
print("\nPerforming teardown...") print("\nPerforming teardown...")
get_orphan_data()
if os.path.exists(models_path): if os.path.exists(models_path):
shutil.rmtree(models_path) shutil.rmtree(models_path)
@ -38,7 +37,10 @@ def setup_data(request):
def get_orphan_data(): def get_orphan_data():
orphan_meshes = [m.name for m in bpy.data.meshes if m.users == 0] orphan_meshes = [m.name for m in bpy.data.meshes if m.users == 0]
orphan_objects = [m.name for m in bpy.data.objects if m.users == 0]
#print("orphan meshes before", orphan_meshes) #print("orphan meshes before", orphan_meshes)
return orphan_meshes + orphan_objects
def test_export_do_not_export_blueprints(setup_data): def test_export_do_not_export_blueprints(setup_data):
auto_export_operator = bpy.ops.export_scenes.auto_gltf auto_export_operator = bpy.ops.export_scenes.auto_gltf
@ -61,6 +63,9 @@ def test_export_do_not_export_blueprints(setup_data):
) )
assert os.path.exists(os.path.join(setup_data["models_path"], "World.glb")) == True assert os.path.exists(os.path.join(setup_data["models_path"], "World.glb")) == True
assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint1.glb")) == False assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint1.glb")) == False
orphan_data = get_orphan_data()
assert len(orphan_data) == 0
def test_export_custom_blueprints_path(setup_data): def test_export_custom_blueprints_path(setup_data):
auto_export_operator = bpy.ops.export_scenes.auto_gltf auto_export_operator = bpy.ops.export_scenes.auto_gltf
@ -83,6 +88,7 @@ def test_export_custom_blueprints_path(setup_data):
) )
assert os.path.exists(os.path.join(setup_data["models_path"], "World.glb")) == True assert os.path.exists(os.path.join(setup_data["models_path"], "World.glb")) == True
assert os.path.exists(os.path.join(setup_data["models_path"], "another_library_path", "Blueprint1.glb")) == True assert os.path.exists(os.path.join(setup_data["models_path"], "another_library_path", "Blueprint1.glb")) == True
assert len(get_orphan_data()) == 0
def test_export_materials_library(setup_data): def test_export_materials_library(setup_data):
auto_export_operator = bpy.ops.export_scenes.auto_gltf auto_export_operator = bpy.ops.export_scenes.auto_gltf
@ -107,7 +113,7 @@ def test_export_materials_library(setup_data):
assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint1.glb")) == True assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint1.glb")) == True
assert os.path.exists(os.path.join(setup_data["materials_path"], "testing_materials_library.glb")) == True assert os.path.exists(os.path.join(setup_data["materials_path"], "testing_materials_library.glb")) == True
assert len(get_orphan_data()) == 0
def test_export_materials_library_custom_path(setup_data): def test_export_materials_library_custom_path(setup_data):
auto_export_operator = bpy.ops.export_scenes.auto_gltf auto_export_operator = bpy.ops.export_scenes.auto_gltf
@ -134,6 +140,7 @@ def test_export_materials_library_custom_path(setup_data):
assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint1.glb")) == True assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint1.glb")) == True
assert os.path.exists(os.path.join(setup_data["materials_path"], "testing_materials_library.glb")) == False assert os.path.exists(os.path.join(setup_data["materials_path"], "testing_materials_library.glb")) == False
assert os.path.exists(os.path.join(setup_data["other_materials_path"], "testing_materials_library.glb")) == True assert os.path.exists(os.path.join(setup_data["other_materials_path"], "testing_materials_library.glb")) == True
assert len(get_orphan_data()) == 0
def test_export_collection_instances_combine_mode(setup_data): # TODO: change & check this def test_export_collection_instances_combine_mode(setup_data): # TODO: change & check this
auto_export_operator = bpy.ops.export_scenes.auto_gltf auto_export_operator = bpy.ops.export_scenes.auto_gltf
@ -160,6 +167,7 @@ def test_export_collection_instances_combine_mode(setup_data): # TODO: change &
assert os.path.exists(os.path.join(setup_data["models_path"], "World.glb")) == True assert os.path.exists(os.path.join(setup_data["models_path"], "World.glb")) == True
assert os.path.exists(os.path.join(setup_data["models_path"], "World_dynamic.glb")) == False assert os.path.exists(os.path.join(setup_data["models_path"], "World_dynamic.glb")) == False
assert len(get_orphan_data()) == 0
def test_export_do_not_export_marked_assets(setup_data): def test_export_do_not_export_marked_assets(setup_data):
@ -188,6 +196,7 @@ def test_export_do_not_export_marked_assets(setup_data):
assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint3.glb")) == True assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint3.glb")) == True
assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint4_nested.glb")) == True assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint4_nested.glb")) == True
assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint5.glb")) == False assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint5.glb")) == False
assert len(get_orphan_data()) == 0
def test_export_separate_dynamic_and_static_objects(setup_data): def test_export_separate_dynamic_and_static_objects(setup_data):
@ -216,6 +225,7 @@ def test_export_separate_dynamic_and_static_objects(setup_data):
assert os.path.exists(os.path.join(setup_data["models_path"], "World.glb")) == True assert os.path.exists(os.path.join(setup_data["models_path"], "World.glb")) == True
assert os.path.exists(os.path.join(setup_data["models_path"], "World_dynamic.glb")) == True assert os.path.exists(os.path.join(setup_data["models_path"], "World_dynamic.glb")) == True
assert len(get_orphan_data()) == 0
def test_export_should_not_generate_orphan_data(setup_data): def test_export_should_not_generate_orphan_data(setup_data):
@ -239,4 +249,5 @@ def test_export_should_not_generate_orphan_data(setup_data):
) )
assert os.path.exists(os.path.join(setup_data["models_path"], "World.glb")) == True assert os.path.exists(os.path.join(setup_data["models_path"], "World.glb")) == True
assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint1.glb")) == False assert os.path.exists(os.path.join(setup_data["models_path"], "library", "Blueprint1.glb")) == False
assert len(get_orphan_data()) == 0

View File

@ -56,7 +56,8 @@ def test_export_complex(setup_data):
# we use the global settings for that # we use the global settings for that
export_props = { export_props = {
"main_scene_names" : ['World'], "main_scene_names" : ['World'],
"library_scene_names": ['Library'] "library_scene_names": ['Library'],
# "export_format":'GLTF_SEPARATE'
} }
stored_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings") stored_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings")
stored_settings.clear() stored_settings.clear()