mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-18 00:23:14 +00:00
Refactoring, reorganization and cleanup (#7)
### New `math` module * Traits for error-tolerant floating-point comparisons * Traits for interpolation and easing * Bevy plugin to manage 3D interpolations with ECS components ### `skybox` module refactor * ECS resource to enable external skybox selection/updates * System run conditions for more idiomatic/ECS-friendly control flow ### Camera controller refactor * Renamed from `orbit_cam` to `camera_controller` * Now delegates to the new interpolation plugin for WASD motion smoothing * Simple API for attaching the `CameraController` component to the camera from another module -- this resolves an awkward separation-of-concerns situation where we had to configure a bunch of render-quality settings in the camera controller module ### Scene module refactor * Renamed from `test_scene` to `scene` * This module is now responsible for configuring the skybox * This module is now responsible for spawning the camera and configuring the render settings
This commit is contained in:
+68
-56
@@ -11,79 +11,91 @@ pub struct SkyboxPlugin;
|
||||
|
||||
impl Plugin for SkyboxPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup)
|
||||
.add_systems(Update, asset_loaded);
|
||||
app.add_systems(
|
||||
Update,
|
||||
(
|
||||
load_skybox.run_if(resource_exists_and_changed::<SkyboxId>()),
|
||||
spawn_skybox
|
||||
.run_if(resource_exists::<SkyboxAssets>())
|
||||
.run_if(skybox_assets_loaded),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Deref, DerefMut)]
|
||||
pub struct SkyboxId(pub String);
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct Cubemap {
|
||||
is_loaded: bool,
|
||||
image_handle: Handle<Image>,
|
||||
struct SkyboxAssets {
|
||||
cubemap: Handle<Image>,
|
||||
env_diffuse: Handle<Image>,
|
||||
env_specular: Handle<Image>,
|
||||
}
|
||||
|
||||
impl Cubemap {
|
||||
fn new(name: impl fmt::Display, assets: &Res<AssetServer>) -> Self {
|
||||
let paths = SkyboxPaths::new(name);
|
||||
|
||||
Self {
|
||||
is_loaded: false,
|
||||
image_handle: assets.load(paths.main),
|
||||
env_diffuse: assets.load(paths.env_diffuse),
|
||||
env_specular: assets.load(paths.env_specular),
|
||||
}
|
||||
}
|
||||
fn load_skybox(mut cmd: Commands, assets: Res<AssetServer>, id: Res<SkyboxId>) {
|
||||
let paths = SkyboxPaths::new(&**id);
|
||||
cmd.insert_resource(SkyboxAssets {
|
||||
cubemap: assets.load(paths.cubemap),
|
||||
env_diffuse: assets.load(paths.env_diffuse),
|
||||
env_specular: assets.load(paths.env_specular),
|
||||
});
|
||||
}
|
||||
|
||||
pub struct SkyboxPaths {
|
||||
pub main: String,
|
||||
pub env_diffuse: String,
|
||||
pub env_specular: String,
|
||||
fn skybox_assets_loaded(
|
||||
assets: Res<AssetServer>,
|
||||
skybox_assets: Option<Res<SkyboxAssets>>,
|
||||
) -> bool {
|
||||
let Some(skybox_assets) = skybox_assets else {
|
||||
return false;
|
||||
};
|
||||
|
||||
assets.get_load_state(skybox_assets.cubemap.clone_weak()) == LoadState::Loaded
|
||||
&& assets.get_load_state(skybox_assets.env_diffuse.clone_weak()) == LoadState::Loaded
|
||||
&& assets.get_load_state(skybox_assets.env_specular.clone_weak()) == LoadState::Loaded
|
||||
}
|
||||
|
||||
fn spawn_skybox(
|
||||
mut cmd: Commands,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
skybox_assets: Res<SkyboxAssets>,
|
||||
q_camera: Query<Entity, With<Camera3d>>,
|
||||
) {
|
||||
// Configure the skybox texture as a cubemap
|
||||
let image = images.get_mut(&skybox_assets.cubemap).unwrap();
|
||||
if image.texture_descriptor.array_layer_count() == 1 {
|
||||
image.reinterpret_stacked_2d_as_array(6);
|
||||
image.texture_view_descriptor = Some(TextureViewDescriptor {
|
||||
dimension: Some(TextureViewDimension::Cube),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
// Add components to the scene camera
|
||||
cmd.entity(q_camera.single()).insert((
|
||||
Skybox(skybox_assets.cubemap.clone()),
|
||||
EnvironmentMapLight {
|
||||
diffuse_map: skybox_assets.env_diffuse.clone(),
|
||||
specular_map: skybox_assets.env_specular.clone(),
|
||||
},
|
||||
));
|
||||
|
||||
// Remove the intermediate resource
|
||||
cmd.remove_resource::<SkyboxAssets>();
|
||||
}
|
||||
|
||||
struct SkyboxPaths {
|
||||
cubemap: String,
|
||||
env_diffuse: String,
|
||||
env_specular: String,
|
||||
}
|
||||
|
||||
impl SkyboxPaths {
|
||||
fn new(name: impl fmt::Display) -> Self {
|
||||
Self {
|
||||
main: format!("skyboxes/{name}.hdr"),
|
||||
cubemap: format!("skyboxes/{name}.hdr"),
|
||||
env_diffuse: format!("skyboxes/{name}_diffuse.zstd.ktx2"),
|
||||
env_specular: format!("skyboxes/{name}_specular.zstd.ktx2"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(mut cmd: Commands, assets: Res<AssetServer>) {
|
||||
cmd.insert_resource(Cubemap::new("test_scene", &assets));
|
||||
}
|
||||
|
||||
fn asset_loaded(
|
||||
assets: Res<AssetServer>,
|
||||
camera: Query<Entity, With<Camera3d>>,
|
||||
mut cmd: Commands,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
mut cubemap: ResMut<Cubemap>,
|
||||
) {
|
||||
if !cubemap.is_loaded
|
||||
&& assets.get_load_state(cubemap.image_handle.clone_weak()) == LoadState::Loaded
|
||||
{
|
||||
let image = images.get_mut(&cubemap.image_handle).unwrap();
|
||||
if image.texture_descriptor.array_layer_count() == 1 {
|
||||
image.reinterpret_stacked_2d_as_array(6);
|
||||
image.texture_view_descriptor = Some(TextureViewDescriptor {
|
||||
dimension: Some(TextureViewDimension::Cube),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
cmd.entity(camera.single()).insert((
|
||||
Skybox(cubemap.image_handle.clone()),
|
||||
EnvironmentMapLight {
|
||||
diffuse_map: cubemap.env_diffuse.clone(),
|
||||
specular_map: cubemap.env_specular.clone(),
|
||||
},
|
||||
));
|
||||
|
||||
cubemap.is_loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user