use super::scene_tree::SceneTreeRef;
use crate::plugins::assets::GodotResource;
use crate::plugins::transforms::IntoGodotTransform2D;
use crate::prelude::main_thread_system;
use crate::{interop::GodotNodeHandle, plugins::transforms::IntoGodotTransform};
use bevy::{
app::{App, Plugin, PostUpdate},
asset::{Assets, Handle},
ecs::{
component::Component,
entity::Entity,
query::Without,
system::{Commands, Query, ResMut},
},
log::tracing,
transform::components::Transform,
};
use godot::{
builtin::GString,
classes::{Node, Node2D, Node3D, PackedScene, ResourceLoader},
obj::Singleton,
};
use std::str::FromStr;
use tracing::error;
#[derive(Default)]
pub struct GodotPackedScenePlugin;
impl Plugin for GodotPackedScenePlugin {
fn build(&self, app: &mut App) {
app.add_systems(PostUpdate, spawn_scene);
}
}
#[allow(rustdoc::private_intra_doc_links)]
#[derive(Debug, Component)]
pub struct GodotScene {
resource: GodotSceneResource,
parent: Option<GodotNodeHandle>,
}
#[derive(Debug)]
enum GodotSceneResource {
Handle(Handle<GodotResource>),
Path(String),
}
impl GodotScene {
pub fn from_handle(handle: Handle<GodotResource>) -> Self {
Self {
resource: GodotSceneResource::Handle(handle),
parent: None,
}
}
pub fn from_path(path: &str) -> Self {
Self {
resource: GodotSceneResource::Path(path.to_string()),
parent: None,
}
}
pub fn with_parent(mut self, parent: GodotNodeHandle) -> Self {
self.parent = Some(parent);
self
}
}
#[main_thread_system]
fn spawn_scene(
mut commands: Commands,
mut new_scenes: Query<(&mut GodotScene, Entity, Option<&Transform>), Without<GodotNodeHandle>>,
mut scene_tree: SceneTreeRef,
mut assets: ResMut<Assets<GodotResource>>,
) {
for (mut scene, ent, transform) in new_scenes.iter_mut() {
let packed_scene = match &scene.resource {
GodotSceneResource::Handle(handle) => assets
.get_mut(handle)
.expect("packed scene to exist in assets")
.get()
.clone(),
GodotSceneResource::Path(path) => ResourceLoader::singleton()
.load(&GString::from_str(path).expect("path to be a valid GString"))
.expect("packed scene to load"),
};
let packed_scene_cast = packed_scene.clone().try_cast::<PackedScene>();
if packed_scene_cast.is_err() {
error!("Resource is not a PackedScene: {:?}", packed_scene);
continue;
}
let packed_scene = packed_scene_cast.unwrap();
let instance = match packed_scene.instantiate() {
Some(instance) => instance,
None => {
error!("Failed to instantiate PackedScene");
continue;
}
};
if let Some(transform) = transform {
if let Ok(mut node) = instance.clone().try_cast::<Node3D>() {
node.set_global_transform(transform.to_godot_transform());
} else if let Ok(mut node) = instance.clone().try_cast::<Node2D>() {
node.set_global_transform(transform.to_godot_transform_2d());
} else {
error!(
"attempted to spawn a scene with a transform on Node that did not inherit from Node, the transform was not set"
)
}
}
match &mut scene.parent {
Some(parent) => {
let mut parent = parent.get::<Node>();
parent.add_child(&instance);
}
None => {
scene_tree.get().get_root().unwrap().add_child(&instance);
}
}
commands.entity(ent).insert(GodotNodeHandle::new(instance));
}
}