|
|
|
|
@ -1,19 +1,32 @@
|
|
|
|
|
use crate::{game::BoardIndex, prelude::*};
|
|
|
|
|
use crate::{
|
|
|
|
|
game::{Board, BoardIndex, Piece, Side},
|
|
|
|
|
prelude::*,
|
|
|
|
|
};
|
|
|
|
|
use bevy::{
|
|
|
|
|
core_pipeline::Skybox,
|
|
|
|
|
render::render_resource::{TextureViewDescriptor, TextureViewDimension},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub(crate) struct Display3dPlugin;
|
|
|
|
|
|
|
|
|
|
impl Plugin for Display3dPlugin {
|
|
|
|
|
fn build(&self, app: &mut App) {
|
|
|
|
|
app.add_systems(Startup, initialize_camera)
|
|
|
|
|
.add_systems(OnEnter(GameState::Loading), load_models)
|
|
|
|
|
.add_systems(OnExit(GameState::Loading), initialize_board)
|
|
|
|
|
app.add_systems(OnEnter(GameState::Loading), load_assets)
|
|
|
|
|
.add_systems(
|
|
|
|
|
OnExit(GameState::Loading),
|
|
|
|
|
(initialize, fix_skybox.before(initialize)),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(
|
|
|
|
|
Update,
|
|
|
|
|
menu::exit_to_menu.run_if(in_state(GameState::Display3d)),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(
|
|
|
|
|
Update,
|
|
|
|
|
set_piece_position.run_if(in_state(GameState::Display3d)),
|
|
|
|
|
(
|
|
|
|
|
set_piece_position.run_if(any_component_changed::<BoardIndex>),
|
|
|
|
|
set_piece_model.run_if(any_component_changed::<Side>),
|
|
|
|
|
set_board_model.run_if(any_component_added::<Board3d>),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(
|
|
|
|
|
Update,
|
|
|
|
|
@ -32,7 +45,29 @@ struct Display3d;
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
pub(crate) struct Piece3d;
|
|
|
|
|
|
|
|
|
|
fn initialize_camera(mut commands: Commands) {
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
pub(crate) struct Board3d;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Resource)]
|
|
|
|
|
struct AssetsMap {
|
|
|
|
|
models: Handle<Gltf>,
|
|
|
|
|
skybox: Handle<Image>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Load 3d models
|
|
|
|
|
/// This is kind of pulling double duty.
|
|
|
|
|
/// Both loads the GLTF file _and_ populates the ModelMap once that is loaded.
|
|
|
|
|
fn load_assets(server: Res<AssetServer>, mut commands: Commands) {
|
|
|
|
|
commands.insert_resource(AssetsMap {
|
|
|
|
|
models: server.load("models/Martian Chess.glb"),
|
|
|
|
|
skybox: server.load("images/cubemap.png"),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Initialize the 3d board
|
|
|
|
|
fn initialize(mut commands: Commands, board: Option<Res<game::Board>>, assets: Res<AssetsMap>) {
|
|
|
|
|
info!("Initialize 3d camera");
|
|
|
|
|
// let handle = server.load("images/mars.hdr");
|
|
|
|
|
commands.spawn((
|
|
|
|
|
Display3d,
|
|
|
|
|
Camera3dBundle {
|
|
|
|
|
@ -48,6 +83,11 @@ fn initialize_camera(mut commands: Commands) {
|
|
|
|
|
transform: Transform::from_xyz(0.0, 20.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
Skybox(assets.skybox.clone()),
|
|
|
|
|
// EnvironmentMapLight {
|
|
|
|
|
// diffuse_map: server.load("images/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
|
|
|
|
// specular_map: server.load("images/pisa_specular_rgb9e5_zstd.ktx2"),
|
|
|
|
|
// },
|
|
|
|
|
UiCameraConfig { show_ui: true },
|
|
|
|
|
));
|
|
|
|
|
// light
|
|
|
|
|
@ -60,32 +100,6 @@ fn initialize_camera(mut commands: Commands) {
|
|
|
|
|
transform: Transform::from_xyz(4.0, 8.0, 4.0),
|
|
|
|
|
..default()
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Resource)]
|
|
|
|
|
struct ModelsFile {
|
|
|
|
|
handle: Handle<Gltf>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Load 3d models
|
|
|
|
|
/// This is kind of pulling double duty.
|
|
|
|
|
/// Both loads the GLTF file _and_ populates the ModelMap once that is loaded.
|
|
|
|
|
fn load_models(server: Res<AssetServer>, mut commands: Commands) {
|
|
|
|
|
commands.insert_resource(ModelsFile {
|
|
|
|
|
handle: server.load("models/Martian Chess.glb"),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Initialize the 3d board
|
|
|
|
|
fn initialize_board(
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
model_file: Option<Res<ModelsFile>>,
|
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
|
board: Option<Res<game::Board>>,
|
|
|
|
|
) {
|
|
|
|
|
info!("Initializing board");
|
|
|
|
|
if let Some(mf) = model_file {
|
|
|
|
|
let gltf = gltfs.get(&mf.handle).expect("Load GLTF content");
|
|
|
|
|
|
|
|
|
|
info!("Initializing root");
|
|
|
|
|
commands
|
|
|
|
|
@ -111,45 +125,73 @@ fn initialize_board(
|
|
|
|
|
},
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
info!("Intializeing 3D Board!");
|
|
|
|
|
info!("Intializing 3D Board!");
|
|
|
|
|
parent
|
|
|
|
|
.spawn((
|
|
|
|
|
Display3d,
|
|
|
|
|
SceneBundle {
|
|
|
|
|
scene: gltf
|
|
|
|
|
.named_scenes
|
|
|
|
|
.get("Gameboard")
|
|
|
|
|
.expect("Game board model")
|
|
|
|
|
.clone(),
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
))
|
|
|
|
|
.spawn((Display3d, Board3d, SceneBundle { ..default() }))
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
// TODO: Add this to a reactive system when board is added
|
|
|
|
|
board
|
|
|
|
|
.unwrap()
|
|
|
|
|
.pieces()
|
|
|
|
|
.into_iter()
|
|
|
|
|
.for_each(|(index, piece)| {
|
|
|
|
|
// TODO: Move this to a reactive system like index -> Transform
|
|
|
|
|
let scene = match piece {
|
|
|
|
|
game::Piece::Pawn => gltf.named_scenes.get("Pawn"),
|
|
|
|
|
game::Piece::Drone => gltf.named_scenes.get("Drone"),
|
|
|
|
|
game::Piece::Queen => gltf.named_scenes.get("Queen"),
|
|
|
|
|
}
|
|
|
|
|
.expect("Game board model")
|
|
|
|
|
.clone();
|
|
|
|
|
board.unwrap().pieces().iter().for_each(|(index, piece)| {
|
|
|
|
|
let side = Board::side(index).expect("Spawn valid side");
|
|
|
|
|
|
|
|
|
|
parent.spawn((
|
|
|
|
|
side,
|
|
|
|
|
Piece3d,
|
|
|
|
|
piece,
|
|
|
|
|
index,
|
|
|
|
|
SceneBundle { scene, ..default() },
|
|
|
|
|
piece.clone(),
|
|
|
|
|
index.clone(),
|
|
|
|
|
SceneBundle { ..default() },
|
|
|
|
|
));
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn fix_skybox(mut images: ResMut<Assets<Image>>, assets: Res<AssetsMap>) {
|
|
|
|
|
let image = images.get_mut(&assets.skybox).unwrap();
|
|
|
|
|
info!("Loaded skybox image");
|
|
|
|
|
// NOTE: PNGs do not have any metadata that could indicate they contain a cubemap texture,
|
|
|
|
|
// so they appear as one texture. The following code reconfigures the texture as necessary.
|
|
|
|
|
if image.texture_descriptor.array_layer_count() == 1 {
|
|
|
|
|
image.reinterpret_stacked_2d_as_array(
|
|
|
|
|
image.texture_descriptor.size.height / image.texture_descriptor.size.width,
|
|
|
|
|
);
|
|
|
|
|
image.texture_view_descriptor = Some(TextureViewDescriptor {
|
|
|
|
|
dimension: Some(TextureViewDimension::Cube),
|
|
|
|
|
..default()
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_piece_model(
|
|
|
|
|
mut events: Query<(&mut Handle<Scene>, &Piece), (With<Piece3d>, Changed<Side>)>,
|
|
|
|
|
assets_map: Option<Res<AssetsMap>>,
|
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
|
) {
|
|
|
|
|
let mf = assets_map.expect("Models file");
|
|
|
|
|
events.iter_mut().for_each(|(mut handle, piece)| {
|
|
|
|
|
let gltf = gltfs.get(&mf.models).expect("Load GLTF content");
|
|
|
|
|
*handle = match piece {
|
|
|
|
|
game::Piece::Pawn => gltf.named_scenes.get("Pawn"),
|
|
|
|
|
game::Piece::Drone => gltf.named_scenes.get("Drone"),
|
|
|
|
|
game::Piece::Queen => gltf.named_scenes.get("Queen"),
|
|
|
|
|
}
|
|
|
|
|
.expect("Game board model")
|
|
|
|
|
.clone();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_board_model(
|
|
|
|
|
mut events: Query<&mut Handle<Scene>, With<Board3d>>,
|
|
|
|
|
assets_map: Option<Res<AssetsMap>>,
|
|
|
|
|
gltfs: Res<Assets<Gltf>>,
|
|
|
|
|
) {
|
|
|
|
|
let mf = assets_map.expect("Models file");
|
|
|
|
|
events.iter_mut().for_each(|mut handle| {
|
|
|
|
|
let gltf = gltfs.get(&mf.models).expect("Load GLTF content");
|
|
|
|
|
*handle = gltf
|
|
|
|
|
.named_scenes
|
|
|
|
|
.get("Gameboard")
|
|
|
|
|
.expect("Game board model")
|
|
|
|
|
.clone();
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Sets a piece location given it's board index
|
|
|
|
|
|