Compare commits
4 Commits
53e68ad58f
...
68eac8d320
Author | SHA1 | Date |
---|---|---|
kaosat.dev | 68eac8d320 | |
kaosat.dev | 079aed7627 | |
kaosat.dev | 65c009c210 | |
kaosat.dev | 1891903f03 |
|
@ -66,14 +66,12 @@ pub(crate) fn materials_inject(
|
||||||
} else {
|
} else {
|
||||||
let material_file_handle: Handle<Gltf> = asset_server.load(materials_path.clone());
|
let material_file_handle: Handle<Gltf> = asset_server.load(materials_path.clone());
|
||||||
let material_file_id = material_file_handle.id();
|
let material_file_id = material_file_handle.id();
|
||||||
let asset_infos: Vec<AssetLoadTracker<Gltf>> = vec![
|
let asset_infos: Vec<AssetLoadTracker<Gltf>> = vec![AssetLoadTracker {
|
||||||
AssetLoadTracker {
|
|
||||||
name: material_full_path,
|
name: material_full_path,
|
||||||
id: material_file_id,
|
id: material_file_id,
|
||||||
loaded: false,
|
loaded: false,
|
||||||
handle: material_file_handle.clone(),
|
handle: material_file_handle.clone(),
|
||||||
}
|
}];
|
||||||
];
|
|
||||||
|
|
||||||
commands
|
commands
|
||||||
.entity(entity)
|
.entity(entity)
|
||||||
|
|
|
@ -46,10 +46,12 @@ pub struct AddToGameWorld;
|
||||||
/// helper component, just to transfer child data
|
/// helper component, just to transfer child data
|
||||||
pub(crate) struct OriginalChildren(pub Vec<Entity>);
|
pub(crate) struct OriginalChildren(pub Vec<Entity>);
|
||||||
|
|
||||||
|
/// helper component, is used to store the list of sub blueprints to enable automatic loading of dependend blueprints
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct BlueprintsList(pub HashMap<String, Vec<String>>);
|
pub struct BlueprintsList(pub HashMap<String, Vec<String>>);
|
||||||
|
|
||||||
|
/// helper component, for tracking loaded assets's loading state, id , handle etc
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub(crate) struct AssetLoadTracker<T: bevy::prelude::Asset> {
|
pub(crate) struct AssetLoadTracker<T: bevy::prelude::Asset> {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -59,19 +61,25 @@ pub(crate) struct AssetLoadTracker<T: bevy::prelude::Asset> {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub handle: Handle<T>,
|
pub handle: Handle<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// helper component, for tracking loaded assets
|
||||||
#[derive(Component, Debug)]
|
#[derive(Component, Debug)]
|
||||||
pub(crate) struct AssetsToLoad<T: bevy::prelude::Asset> {
|
pub(crate) struct AssetsToLoad<T: bevy::prelude::Asset> {
|
||||||
pub all_loaded: bool,
|
pub all_loaded: bool,
|
||||||
pub asset_infos: Vec<AssetLoadTracker<T>>,
|
pub asset_infos: Vec<AssetLoadTracker<T>>,
|
||||||
pub progress: f32,
|
pub progress: f32,
|
||||||
}
|
}
|
||||||
impl <T: bevy::prelude::Asset>Default for AssetsToLoad<T> {
|
impl<T: bevy::prelude::Asset> Default for AssetsToLoad<T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { all_loaded: Default::default(), asset_infos: Default::default(), progress: Default::default() }
|
Self {
|
||||||
|
all_loaded: Default::default(),
|
||||||
|
asset_infos: Default::default(),
|
||||||
|
progress: Default::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// flag component
|
/// flag component, usually added when a blueprint is loaded
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub(crate) struct BlueprintAssetsLoaded;
|
pub(crate) struct BlueprintAssetsLoaded;
|
||||||
/// flag component
|
/// flag component
|
||||||
|
@ -221,7 +229,7 @@ pub(crate) fn spawn_from_blueprints(
|
||||||
name,
|
name,
|
||||||
) in spawn_placeholders.iter()
|
) in spawn_placeholders.iter()
|
||||||
{
|
{
|
||||||
info!(
|
debug!(
|
||||||
"attempting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}",
|
"attempting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}",
|
||||||
blupeprint_name.0, name, entity, original_parent
|
blupeprint_name.0, name, entity, original_parent
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_gltf_blueprints::{
|
use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag};
|
||||||
BluePrintBundle, BlueprintName, GameWorldTag,
|
|
||||||
};
|
|
||||||
use bevy_gltf_worlflow_examples_common_rapier::{GameState, InAppRunning};
|
use bevy_gltf_worlflow_examples_common_rapier::{GameState, InAppRunning};
|
||||||
|
|
||||||
use bevy_rapier3d::prelude::Velocity;
|
use bevy_rapier3d::prelude::Velocity;
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy_gltf_blueprints::{AnimationPlayerLink, BlueprintName};
|
use bevy_gltf_blueprints::{AnimationPlayerLink, BlueprintName, BlueprintsList};
|
||||||
pub use in_game::*;
|
pub use in_game::*;
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
|
@ -22,7 +22,8 @@ fn start_game(mut next_app_state: ResMut<NextState<AppState>>) {
|
||||||
// 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 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 "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
|
// 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
|
||||||
|
// if the export from Blender worked correctly, we should have a blueprints_list
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn validate_export(
|
fn validate_export(
|
||||||
parents: Query<&Parent>,
|
parents: Query<&Parent>,
|
||||||
children: Query<&Children>,
|
children: Query<&Children>,
|
||||||
|
@ -31,6 +32,8 @@ fn validate_export(
|
||||||
animation_player_links: Query<(Entity, &AnimationPlayerLink)>,
|
animation_player_links: Query<(Entity, &AnimationPlayerLink)>,
|
||||||
exported_cylinder: Query<(Entity, &Name, &UnitTest, &TupleTestF32)>,
|
exported_cylinder: Query<(Entity, &Name, &UnitTest, &TupleTestF32)>,
|
||||||
empties_candidates: Query<(Entity, &Name, &GlobalTransform)>,
|
empties_candidates: Query<(Entity, &Name, &GlobalTransform)>,
|
||||||
|
|
||||||
|
blueprints_list: Query<(Entity, &BlueprintsList)>,
|
||||||
) {
|
) {
|
||||||
let animations_found = !animation_player_links.is_empty();
|
let animations_found = !animation_player_links.is_empty();
|
||||||
|
|
||||||
|
@ -69,11 +72,13 @@ fn validate_export(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let blueprints_list_found = !blueprints_list.is_empty();
|
||||||
|
|
||||||
fs::write(
|
fs::write(
|
||||||
"bevy_diagnostics.json",
|
"bevy_diagnostics.json",
|
||||||
format!(
|
format!(
|
||||||
"{{ \"animations\": {}, \"cylinder_found\": {} , \"nested_blueprint_found\": {}, \"empty_found\": {} }}",
|
"{{ \"animations\": {}, \"cylinder_found\": {} , \"nested_blueprint_found\": {}, \"empty_found\": {}, \"blueprints_list_found\": {} }}",
|
||||||
animations_found, cylinder_found, nested_blueprint_found, empty_found
|
animations_found, cylinder_found, nested_blueprint_found, empty_found, blueprints_list_found
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.expect("Unable to write file");
|
.expect("Unable to write file");
|
||||||
|
@ -99,7 +104,6 @@ impl Plugin for GamePlugin {
|
||||||
.add_systems(Update, validate_export)
|
.add_systems(Update, validate_export)
|
||||||
.add_systems(OnEnter(AppState::MenuRunning), start_game)
|
.add_systems(OnEnter(AppState::MenuRunning), start_game)
|
||||||
.add_systems(OnEnter(AppState::AppRunning), setup_game)
|
.add_systems(OnEnter(AppState::AppRunning), setup_game)
|
||||||
|
|
||||||
.add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once
|
.add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import bpy
|
||||||
from ..helpers.generate_and_export import generate_and_export
|
from ..helpers.generate_and_export import generate_and_export
|
||||||
from .export_gltf import (generate_gltf_export_preferences, export_gltf)
|
from .export_gltf import (generate_gltf_export_preferences, export_gltf)
|
||||||
from ..modules.bevy_dynamic import is_object_dynamic, is_object_static
|
from ..modules.bevy_dynamic import is_object_dynamic, is_object_static
|
||||||
from ..helpers.helpers_scenes import clear_hollow_scene, copy_hollowed_collection_into, inject_blueprints_list_into_main_scene
|
from ..helpers.helpers_scenes import clear_hollow_scene, copy_hollowed_collection_into, inject_blueprints_list_into_main_scene, remove_blueprints_list_from_main_scene
|
||||||
|
|
||||||
|
|
||||||
# export all main scenes
|
# export all main scenes
|
||||||
|
@ -16,6 +16,7 @@ def export_main_scene(scene, folder_path, addon_prefs, library_collections):
|
||||||
gltf_export_preferences = generate_gltf_export_preferences(addon_prefs)
|
gltf_export_preferences = generate_gltf_export_preferences(addon_prefs)
|
||||||
export_output_folder = getattr(addon_prefs,"export_output_folder")
|
export_output_folder = getattr(addon_prefs,"export_output_folder")
|
||||||
export_blueprints = getattr(addon_prefs,"export_blueprints")
|
export_blueprints = getattr(addon_prefs,"export_blueprints")
|
||||||
|
legacy_mode = getattr(addon_prefs, "export_legacy_mode")
|
||||||
export_separate_dynamic_and_static_objects = getattr(addon_prefs, "export_separate_dynamic_and_static_objects")
|
export_separate_dynamic_and_static_objects = getattr(addon_prefs, "export_separate_dynamic_and_static_objects")
|
||||||
|
|
||||||
gltf_output_path = os.path.join(folder_path, export_output_folder, scene.name)
|
gltf_output_path = os.path.join(folder_path, export_output_folder, scene.name)
|
||||||
|
@ -29,6 +30,7 @@ def export_main_scene(scene, folder_path, addon_prefs, library_collections):
|
||||||
}
|
}
|
||||||
|
|
||||||
if export_blueprints :
|
if export_blueprints :
|
||||||
|
if not legacy_mode:
|
||||||
inject_blueprints_list_into_main_scene(scene)
|
inject_blueprints_list_into_main_scene(scene)
|
||||||
|
|
||||||
if export_separate_dynamic_and_static_objects:
|
if export_separate_dynamic_and_static_objects:
|
||||||
|
@ -69,5 +71,8 @@ def export_main_scene(scene, folder_path, addon_prefs, library_collections):
|
||||||
print(" exporting gltf to", gltf_output_path, ".gltf/glb")
|
print(" exporting gltf to", gltf_output_path, ".gltf/glb")
|
||||||
export_gltf(gltf_output_path, export_settings)
|
export_gltf(gltf_output_path, export_settings)
|
||||||
|
|
||||||
|
if not legacy_mode:
|
||||||
|
remove_blueprints_list_from_main_scene(scene)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -73,14 +73,18 @@ def copy_hollowed_collection_into(source_collection, destination_collection, par
|
||||||
"""we inject the collection/blueprint name, as a component called 'BlueprintName', but we only do this in the empty, not the original object"""
|
"""we inject the collection/blueprint name, as a component called 'BlueprintName', but we only do this in the empty, not the original object"""
|
||||||
empty_obj['BlueprintName'] = '"'+collection_name+'"' if legacy_mode else '("'+collection_name+'")'
|
empty_obj['BlueprintName'] = '"'+collection_name+'"' if legacy_mode else '("'+collection_name+'")'
|
||||||
empty_obj['SpawnHere'] = '()'
|
empty_obj['SpawnHere'] = '()'
|
||||||
|
|
||||||
# we also inject a list of all sub blueprints, so that the bevy side can preload them
|
# we also inject a list of all sub blueprints, so that the bevy side can preload them
|
||||||
|
if not legacy_mode:
|
||||||
root_node = CollectionNode()
|
root_node = CollectionNode()
|
||||||
root_node.name = "root"
|
root_node.name = "root"
|
||||||
children_per_collection = {}
|
children_per_collection = {}
|
||||||
|
print("collection stuff", original_name)
|
||||||
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":[]}
|
||||||
|
|
||||||
|
|
||||||
# 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():
|
||||||
if component_name not in custom_properties_to_filter_out and is_component_valid(object, component_name): #copy only valid properties
|
if component_name not in custom_properties_to_filter_out and is_component_valid(object, component_name): #copy only valid properties
|
||||||
|
@ -163,13 +167,14 @@ def inject_blueprints_list_into_main_scene(scene):
|
||||||
print("injecting assets/blueprints data into scene")
|
print("injecting assets/blueprints data into scene")
|
||||||
root_collection = scene.collection
|
root_collection = scene.collection
|
||||||
assets_list = None
|
assets_list = None
|
||||||
|
assets_list_name = f"assets_list_{scene.name}_components"
|
||||||
for object in scene.objects:
|
for object in scene.objects:
|
||||||
if object.name == "assets_list"+scene.name:
|
if object.name == assets_list_name:
|
||||||
assets_list = object
|
assets_list = object
|
||||||
break
|
break
|
||||||
|
|
||||||
if assets_list is None:
|
if assets_list is None:
|
||||||
assets_list = make_empty('assets_list_'+scene.name+"_components", [0,0,0], [0,0,0], [0,0,0], root_collection)
|
assets_list = make_empty(assets_list_name, [0,0,0], [0,0,0], [0,0,0], root_collection)
|
||||||
|
|
||||||
# find all blueprints used in a scene
|
# find all blueprints used in a scene
|
||||||
# TODO: export a tree rather than a flat list ? because you could have potential clashing items in flat lists (amongst other issues)
|
# TODO: export a tree rather than a flat list ? because you could have potential clashing items in flat lists (amongst other issues)
|
||||||
|
@ -179,7 +184,7 @@ def inject_blueprints_list_into_main_scene(scene):
|
||||||
children_per_collection = {}
|
children_per_collection = {}
|
||||||
|
|
||||||
#print("collection_names", collection_names, "collections", collections)
|
#print("collection_names", collection_names, "collections", collections)
|
||||||
(bla, bli ) = get_sub_collections(collections, root_node, children_per_collection)
|
get_sub_collections(collections, root_node, children_per_collection)
|
||||||
# what about marked assets ?
|
# what about marked assets ?
|
||||||
# what about audio assets ?
|
# what about audio assets ?
|
||||||
# what about materials ?
|
# what about materials ?
|
||||||
|
@ -188,4 +193,13 @@ def inject_blueprints_list_into_main_scene(scene):
|
||||||
#assets_list["blueprints_direct"] = list(collection_names)
|
#assets_list["blueprints_direct"] = list(collection_names)
|
||||||
assets_list["BlueprintsList"] = f"({json.dumps(dict(children_per_collection))})"
|
assets_list["BlueprintsList"] = f"({json.dumps(dict(children_per_collection))})"
|
||||||
#assets_list["Materials"]= '()'
|
#assets_list["Materials"]= '()'
|
||||||
# print("assets list", assets_list["BlueprintsList"], children_per_collection)
|
|
||||||
|
def remove_blueprints_list_from_main_scene(scene):
|
||||||
|
assets_list = None
|
||||||
|
assets_list_name = f"assets_list_{scene.name}_components"
|
||||||
|
|
||||||
|
for object in scene.objects:
|
||||||
|
if object.name == assets_list_name:
|
||||||
|
assets_list = object
|
||||||
|
if assets_list is not None:
|
||||||
|
bpy.data.objects.remove(assets_list, do_unlink=True)
|
||||||
|
|
|
@ -83,7 +83,6 @@ def test_export_complex(setup_data):
|
||||||
# blueprint5 => has NO instance, not marked as asset, should NOT export
|
# blueprint5 => has NO instance, not marked as asset, should NOT export
|
||||||
|
|
||||||
assert os.path.exists(os.path.join(models_path, "World.glb")) == True
|
assert os.path.exists(os.path.join(models_path, "World.glb")) == True
|
||||||
|
|
||||||
assert os.path.exists(os.path.join(models_path, "library", "Blueprint1.glb")) == True
|
assert os.path.exists(os.path.join(models_path, "library", "Blueprint1.glb")) == True
|
||||||
assert os.path.exists(os.path.join(models_path, "library", "Blueprint2.glb")) == True
|
assert os.path.exists(os.path.join(models_path, "library", "Blueprint2.glb")) == True
|
||||||
assert os.path.exists(os.path.join(models_path, "library", "Blueprint3.glb")) == True
|
assert os.path.exists(os.path.join(models_path, "library", "Blueprint3.glb")) == True
|
||||||
|
@ -92,6 +91,11 @@ def test_export_complex(setup_data):
|
||||||
assert os.path.exists(os.path.join(models_path, "library", "Blueprint6_animated.glb")) == True
|
assert os.path.exists(os.path.join(models_path, "library", "Blueprint6_animated.glb")) == True
|
||||||
assert os.path.exists(os.path.join(models_path, "library", "Blueprint7_hierarchy.glb")) == True
|
assert os.path.exists(os.path.join(models_path, "library", "Blueprint7_hierarchy.glb")) == True
|
||||||
|
|
||||||
|
# 'assets_list_'+scene.name+"_components" should have been removed after the export
|
||||||
|
assets_list_object_name = "assets_list_"+"World"+"_components"
|
||||||
|
assets_list_object_present = assets_list_object_name in bpy.data.objects
|
||||||
|
assert assets_list_object_present == False
|
||||||
|
|
||||||
# now run bevy
|
# now run bevy
|
||||||
command = "cargo run --features bevy/dynamic_linking"
|
command = "cargo run --features bevy/dynamic_linking"
|
||||||
FNULL = open(os.devnull, 'w') #use this if you want to suppress output to stdout from the subprocess
|
FNULL = open(os.devnull, 'w') #use this if you want to suppress output to stdout from the subprocess
|
||||||
|
@ -105,6 +109,7 @@ def test_export_complex(setup_data):
|
||||||
assert diagnostics["animations"] == True
|
assert diagnostics["animations"] == True
|
||||||
assert diagnostics["cylinder_found"] == True
|
assert diagnostics["cylinder_found"] == True
|
||||||
assert diagnostics["empty_found"] == True
|
assert diagnostics["empty_found"] == True
|
||||||
|
assert diagnostics["blueprints_list_found"] == True
|
||||||
|
|
||||||
# last but not least, do a visual compare
|
# last but not least, do a visual compare
|
||||||
screenshot_expected_path = os.path.join(root_path, "expected_screenshot.png")
|
screenshot_expected_path = os.path.join(root_path, "expected_screenshot.png")
|
||||||
|
|
Loading…
Reference in New Issue