diff --git a/testing/bevy_example/Cargo.toml b/testing/bevy_example/Cargo.toml index 3dbc4ca..04043fb 100644 --- a/testing/bevy_example/Cargo.toml +++ b/testing/bevy_example/Cargo.toml @@ -14,3 +14,4 @@ bevy_rapier3d = { version = "0.25.0", features = ["serde-serialize", "debug-rend bevy_asset_loader = { version = "0.20", features = ["standard_dynamic_assets"] } bevy_editor_pls = { version = "0.8" } rand = "0.8.5" +json-writer ="0.3" \ No newline at end of file diff --git a/testing/bevy_example/assets/registry.json b/testing/bevy_example/assets/registry.json index 33887c3..2607842 100644 --- a/testing/bevy_example/assets/registry.json +++ b/testing/bevy_example/assets/registry.json @@ -3516,6 +3516,17 @@ "type": "object", "typeInfo": "Struct" }, + "bevy_gltf_blueprints::animation::Animated": { + "additionalProperties": false, + "isComponent": true, + "isResource": false, + "properties": {}, + "required": [], + "short_name": "Animated", + "title": "bevy_gltf_blueprints::animation::Animated", + "type": "object", + "typeInfo": "Struct" + }, "bevy_gltf_blueprints::animation::Animations": { "additionalProperties": false, "isComponent": true, diff --git a/testing/bevy_example/assets/testing.blend b/testing/bevy_example/assets/testing.blend index 0442749..bf0893b 100644 Binary files a/testing/bevy_example/assets/testing.blend and b/testing/bevy_example/assets/testing.blend differ diff --git a/testing/bevy_example/src/game/mod.rs b/testing/bevy_example/src/game/mod.rs index 973140b..a38b573 100644 --- a/testing/bevy_example/src/game/mod.rs +++ b/testing/bevy_example/src/game/mod.rs @@ -1,24 +1,30 @@ pub mod in_game; use std::{ - fs::{self}, - time::Duration, + collections::HashMap, fs, time::Duration }; -use bevy_gltf_blueprints::{AnimationPlayerLink, BlueprintName, BlueprintsList}; +use bevy_gltf_blueprints::{AnimationPlayerLink, Animations, BlueprintName, BlueprintsList}; pub use in_game::*; use bevy::{ - prelude::*, render::view::screenshot::ScreenshotManager, time::common_conditions::on_timer, - window::PrimaryWindow, + ecs::query, prelude::*, render::view::screenshot::ScreenshotManager, time::common_conditions::on_timer, window::PrimaryWindow }; use bevy_gltf_worlflow_examples_common_rapier::{AppState, GameState}; use crate::{TupleTestF32, UnitTest}; +use json_writer::to_json_string; fn start_game(mut next_app_state: ResMut>) { next_app_state.set(AppState::AppLoading); } + +#[derive(Debug)] +struct Node { + value: String, + children: Vec, +} + // if the export from Blender worked correctly, we should have animations (simplified here by using AnimationPlayerLink) // if the export from Blender worked correctly, we should have an Entity called "Cylinder" that has two components: UnitTest, TupleTestF32 // if the export from Blender worked correctly, we should have an Entity called "Blueprint4_nested" that has a child called "Blueprint3" that has a "BlueprintName" component with value Blueprint3 @@ -34,19 +40,10 @@ fn validate_export( empties_candidates: Query<(Entity, &Name, &GlobalTransform)>, blueprints_list: Query<(Entity, &BlueprintsList)>, + root: Query<(Entity, &Name, &Children), (Without, With)> ) { let animations_found = !animation_player_links.is_empty(); - let mut cylinder_found = false; - if let Ok(nested_cylinder) = exported_cylinder.get_single() { - let parent_name = names - .get(parents.get(nested_cylinder.0).unwrap().get()) - .unwrap(); - cylinder_found = parent_name.to_string() == *"Cube.001" - && nested_cylinder.1.to_string() == *"Cylinder" - && nested_cylinder.3 .0 == 75.1; - } - let mut nested_blueprint_found = false; for (entity, name, blueprint_name) in blueprints.iter() { if name.to_string() == *"Blueprint4_nested" && blueprint_name.0 == *"Blueprint4_nested" { @@ -71,14 +68,46 @@ fn validate_export( break; } } - + // check if there are blueprints_list components let blueprints_list_found = !blueprints_list.is_empty(); + // there should be no entity named xxx____bak as it means an error in the Blender side export process + let mut exported_names_correct = true; + for name in names.iter() { + if name.to_string().ends_with("___bak") { + exported_names_correct = false; + break; + } + } + + // generate parent/child tree + if !root.is_empty() { + let root = root.single(); + let mut tree: HashMap> = HashMap::new(); + // println!("root {}", root.1); + + for child in children.iter_descendants(root.0) { + let child_name:String = names.get(child).map_or(String::from("no_name"), |e| e.to_string() ); //|e| e.to_string(), || "no_name".to_string()); + //println!(" child {}", child_name); + let parent = parents.get(child).unwrap(); + let parent_name:String = names.get(parent.get()).map_or(String::from("no_name"), |e| e.to_string() ); //|e| e.to_string(), || "no_name".to_string()); + tree.entry(parent_name).or_default().push(child_name.clone()); + } + + let hierarchy = to_json_string(&tree); + fs::write( + "bevy_hierarchy.json", + hierarchy + ) + .expect("unable to write hierarchy file") + } + + fs::write( "bevy_diagnostics.json", format!( - "{{ \"animations\": {}, \"cylinder_found\": {} , \"nested_blueprint_found\": {}, \"empty_found\": {}, \"blueprints_list_found\": {} }}", - animations_found, cylinder_found, nested_blueprint_found, empty_found, blueprints_list_found + "{{ \"animations\": {}, \"nested_blueprint_found\": {}, \"empty_found\": {}, \"blueprints_list_found\": {}, \"exported_names_correct\": {} }}", + animations_found, nested_blueprint_found, empty_found, blueprints_list_found, exported_names_correct ), ) .expect("Unable to write file"); @@ -97,6 +126,35 @@ fn exit_game(mut app_exit_events: ResMut>) { app_exit_events.send(bevy::app::AppExit); } +/* +#[derive(Resource)] +struct Animations(Vec>); +*/ + +fn animations( + foo:Query<(Entity, &Name, &AnimationPlayer),(Added)>, + asset_server: Res, + mut commands: Commands, +) { + for bla in foo.iter() { + let mut counter = 0; + counter +=1; + println!("found some animations {} {}", counter, bla.1); + + if bla.1.to_string() == "Collection".to_string(){ + /*commands.insert_resource(Animations(vec![ + asset_server.load("models/World.glb#Animation0"), + asset_server.load("models/World.glb#Animation1"), + ]));*/ + /*commands.entity(bla.0).insert(Animations { + named_animations: + })*/ + } + + + } +} + pub struct GamePlugin; impl Plugin for GamePlugin { fn build(&self, app: &mut App) { @@ -104,6 +162,7 @@ impl Plugin for GamePlugin { .add_systems(Update, validate_export) .add_systems(OnEnter(AppState::MenuRunning), start_game) .add_systems(OnEnter(AppState::AppRunning), setup_game) + .add_systems(Update, animations) .add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once .add_systems( Update, diff --git a/tools/gltf_auto_export/auto_export/get_standard_exporter_settings.py b/tools/gltf_auto_export/auto_export/get_standard_exporter_settings.py new file mode 100644 index 0000000..8756e7d --- /dev/null +++ b/tools/gltf_auto_export/auto_export/get_standard_exporter_settings.py @@ -0,0 +1,6 @@ +import bpy + +def get_standard_exporter_settings(): + for scene in bpy.data.scenes: + if 'glTF2ExportSettings' in scene: + print("standard exporter settings", scene['glTF2ExportSettings']) \ No newline at end of file diff --git a/tools/gltf_auto_export/tests/expected_bevy_hierarchy.json b/tools/gltf_auto_export/tests/expected_bevy_hierarchy.json new file mode 100644 index 0000000..8a1165b --- /dev/null +++ b/tools/gltf_auto_export/tests/expected_bevy_hierarchy.json @@ -0,0 +1 @@ +{"b_Tail02_013":["b_Tail03_014"],"Blueprint4_nested.001":["Blueprint3"],"Collection 2 1":["Empty_in_sub_collection"],"b_Root_00":["b_Hip_01"],"b_LeftForeArm_010":["b_LeftHand_011"],"b_Spine01_02":["b_Spine02_03"],"Blueprint7_hierarchy.001":["Blueprint4_nested.001","Cube.001"],"b_RightLeg01_019":["b_RightLeg02_020"],"b_LeftUpperArm_09":["b_LeftForeArm_010"],"no_name":["Parent_Object","lighting_components_World","assets_list_World_components","Collection","Collection 2"],"Blueprint3":["Blueprint3_mesh","Blueprint3_mesh"],"world":["no_name"],"Parent_Object":["Cube.003","Blueprint1","Cylinder.001"],"Light":["Light","DirectionalLight Gizmo"],"Blueprint1.001":["Blueprint1_mesh"],"Blueprint7_hierarchy":["Cube.001"],"Spot":["Spot"],"b_Hip_01":["b_Spine01_02","b_Tail01_012","b_LeftLeg01_015","b_RightLeg01_019"],"Cylinder":["Cylinder.001","Cylinder.001"],"Collection 2":["Collection 2 1","Empty_in_collection","Spot"],"b_RightForeArm_07":["b_RightHand_08"],"Blueprint3_mesh":["Cylinder","Cylinder"],"Blueprint4_nested":["Blueprint3"],"Fox_mesh":["fox1"],"b_LeftLeg01_015":["b_LeftLeg02_016"],"b_Neck_04":["b_Head_05"],"b_RightFoot01_021":["b_RightFoot02_022"],"Blueprint1_mesh":["Cube.001","Cube.001"],"b_Tail01_012":["b_Tail02_013"],"Fox":["Fox_mesh","_rootJoint"],"Collection":["Blueprint1.001","Blueprint4_nested","Blueprint6_animated","Blueprint7_hierarchy","Camera","Cube","Empty","Light","Plane"],"Cube":["Cube"],"_rootJoint":["b_Root_00"],"b_RightLeg02_020":["b_RightFoot01_021"],"b_RightUpperArm_06":["b_RightForeArm_07"],"Plane":["Plane"],"Camera":["Camera Gizmo"],"Blueprint6_animated":["Fox"],"b_Spine02_03":["b_Neck_04","b_RightUpperArm_06","b_LeftUpperArm_09"],"b_LeftLeg02_016":["b_LeftFoot01_017"],"b_LeftFoot01_017":["b_LeftFoot02_018"],"Cube.001":["Cube.002","Cylinder","Cube.002","Cylinder"],"Cylinder.001":["Cylinder.002","Blueprint7_hierarchy.001","Empty_as_child"],"Blueprint1":["Blueprint1_mesh"]} \ No newline at end of file diff --git a/tools/gltf_auto_export/tests/test_bevy_integration.py b/tools/gltf_auto_export/tests/test_bevy_integration.py index 7c54fd4..b64f237 100644 --- a/tools/gltf_auto_export/tests/test_bevy_integration.py +++ b/tools/gltf_auto_export/tests/test_bevy_integration.py @@ -5,6 +5,7 @@ import json import pytest import shutil +import filecmp from PIL import Image from pixelmatch.contrib.PIL import pixelmatch @@ -108,9 +109,15 @@ def test_export_complex(setup_data): diagnostics = json.load(diagnostics_file) print("diagnostics", diagnostics) assert diagnostics["animations"] == True - assert diagnostics["cylinder_found"] == True assert diagnostics["empty_found"] == True assert diagnostics["blueprints_list_found"] == True + assert diagnostics["exported_names_correct"] == True + + with open(os.path.join(root_path, "bevy_hierarchy.json")) as hierarchy_file: + with open(os.path.join(os.path.dirname(__file__), "expected_bevy_hierarchy.json")) as expexted_hierarchy_file: + hierarchy = json.load(hierarchy_file) + expected = json.load(expexted_hierarchy_file) + assert sorted(hierarchy.items()) == sorted(expected.items()) # last but not least, do a visual compare screenshot_expected_path = os.path.join(root_path, "expected_screenshot.png")