parent
023dd9dc00
commit
618d0c87b4
@ -1,59 +0,0 @@
|
||||
use bevy::{gltf::Gltf, prelude::*};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins((DefaultPlugins,))
|
||||
.add_systems(Update, (inspect, load_gltf))
|
||||
.run();
|
||||
}
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
struct InspectScene;
|
||||
|
||||
fn inspect(
|
||||
mut events: EventReader<AssetEvent<Scene>>,
|
||||
current: Query<Entity, With<InspectScene>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
events.read().for_each(|event| match event {
|
||||
AssetEvent::LoadedWithDependencies { id } => {
|
||||
// Cleanup existing scenes
|
||||
current.iter().for_each(|e| {
|
||||
commands.entity(e).despawn_recursive();
|
||||
});
|
||||
|
||||
// Spawn default GLTF scene
|
||||
commands.spawn((
|
||||
SceneBundle {
|
||||
scene: Handle::Weak(*id),
|
||||
..default()
|
||||
},
|
||||
InspectScene,
|
||||
));
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
}
|
||||
|
||||
fn load_gltf(
|
||||
mut events: EventReader<FileDragAndDrop>,
|
||||
server: Res<AssetServer>,
|
||||
mut handle: Local<Handle<Gltf>>,
|
||||
) {
|
||||
events.read().for_each(|event| match event {
|
||||
FileDragAndDrop::DroppedFile { path_buf, .. } => {
|
||||
let p = path_buf
|
||||
.clone()
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.unwrap()
|
||||
.replace(
|
||||
"D:\\Projects\\src\\gitea.elijah.run\\martian-chess\\assets\\",
|
||||
"",
|
||||
);
|
||||
info!("Loading {:?}", p);
|
||||
*handle = server.load(p);
|
||||
}
|
||||
_ => warn!("ignored"),
|
||||
})
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
use gltf::*;
|
||||
|
||||
fn main() -> gltf::Result<()> {
|
||||
let g = Gltf::open("assets/models/Martian Chess.glb")?;
|
||||
g.animations().into_iter().for_each(|x| {
|
||||
println!("{:?}", x.name());
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@ -1,92 +0,0 @@
|
||||
//! This example demonstrates how to use the FmodPlugin to play a sound.
|
||||
//! Make sure to follow the instructions in the README.md to set up the demo project.
|
||||
|
||||
use bevy::input::keyboard::KeyboardInput;
|
||||
use bevy::input::ButtonState;
|
||||
use bevy::prelude::*;
|
||||
use bevy_fmod::prelude::AudioSource;
|
||||
use bevy_fmod::prelude::*;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins((
|
||||
DefaultPlugins,
|
||||
FmodPlugin {
|
||||
audio_banks_paths: &[
|
||||
"./assets/audio/Martian Chess/Build/Desktop/Master.bank",
|
||||
"./assets/audio/Martian Chess/Build/Desktop/Master.strings.bank",
|
||||
"./assets/audio/Martian Chess/Build/Desktop/Music.bank",
|
||||
"./assets/audio/Martian Chess/Build/Desktop/SFX.bank",
|
||||
],
|
||||
},
|
||||
))
|
||||
.add_systems(Startup, startup)
|
||||
.add_systems(Update, play_music)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct MyMusicPlayer;
|
||||
|
||||
fn startup(mut commands: Commands, studio: Res<FmodStudio>) {
|
||||
commands
|
||||
.spawn(NodeBundle {
|
||||
style: Style {
|
||||
flex_direction: FlexDirection::Column,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
})
|
||||
.with_children(|parent| {
|
||||
studio
|
||||
.0
|
||||
.get_bank_list(10)
|
||||
.expect("List banks")
|
||||
.into_iter()
|
||||
.flat_map(|bank| {
|
||||
bank.get_event_list(100)
|
||||
.into_iter()
|
||||
.flat_map(|events| events.into_iter())
|
||||
.filter_map(|event| event.get_path().ok())
|
||||
})
|
||||
.for_each(|event_path| {
|
||||
parent
|
||||
.spawn((
|
||||
ButtonBundle {
|
||||
style: Style { ..default() },
|
||||
..default()
|
||||
},
|
||||
AudioSource::new(studio.0.get_event(event_path.as_str()).unwrap()),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn(TextBundle::from_section(
|
||||
event_path,
|
||||
TextStyle {
|
||||
color: Color::BLACK.into(),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
commands.spawn((
|
||||
Camera2dBundle { ..default() },
|
||||
UiCameraConfig { show_ui: true },
|
||||
));
|
||||
}
|
||||
|
||||
fn play_music(
|
||||
mut events: Query<
|
||||
(&Interaction, &mut AudioSource),
|
||||
(Changed<Interaction>, With<Button>, With<AudioSource>),
|
||||
>,
|
||||
) {
|
||||
events
|
||||
.iter_mut()
|
||||
.filter(|(&i, _)| i == Interaction::Pressed)
|
||||
.for_each(|(_, a)| {
|
||||
info!("Toggling Audio");
|
||||
a.play();
|
||||
});
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
//! This example demonstrates Bevy's immediate mode drawing API intended for visual debugging.
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, system)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands) {
|
||||
commands.spawn(Camera3dBundle {
|
||||
transform: Transform::from_xyz(0., 1.5, 6.).looking_at(Vec3::ZERO, Vec3::Y),
|
||||
..default()
|
||||
});
|
||||
// light
|
||||
commands.spawn(PointLightBundle {
|
||||
point_light: PointLight {
|
||||
intensity: 1500.0,
|
||||
shadows_enabled: true,
|
||||
..default()
|
||||
},
|
||||
transform: Transform::from_xyz(4.0, 8.0, 4.0),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
fn system(mut gizmos: Gizmos) {
|
||||
gizmos.cuboid(
|
||||
Transform::from_translation(Vec3::Y * 0.5).with_scale(Vec3::splat(1.)),
|
||||
Color::BLACK,
|
||||
);
|
||||
}
|
||||
@ -1,227 +0,0 @@
|
||||
#![feature(iter_array_chunks)]
|
||||
|
||||
/// Example to illustrate selecting objects in 3d space
|
||||
///
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
render::mesh::MeshVertexAttribute,
|
||||
render::render_resource::{Extent3d, TextureDimension, TextureFormat},
|
||||
render::{mesh::VertexAttributeValues, render_resource::VertexFormat},
|
||||
window::PrimaryWindow,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_systems(Startup, startup)
|
||||
.add_systems(Update, select)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn startup(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
) {
|
||||
let debug_material = materials.add(StandardMaterial {
|
||||
base_color_texture: Some(images.add(uv_debug_texture())),
|
||||
..default()
|
||||
});
|
||||
|
||||
commands.spawn(Camera3dBundle {
|
||||
transform: Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||
..default()
|
||||
});
|
||||
|
||||
commands.spawn(PointLightBundle {
|
||||
transform: Transform::from_xyz(0.0, 0.0, 5.0),
|
||||
..default()
|
||||
});
|
||||
|
||||
commands.spawn(PbrBundle {
|
||||
mesh: meshes.add(shape::Cube::default().into()),
|
||||
material: debug_material.clone(),
|
||||
transform: Transform::from_xyz(0.0, 0.0, 0.0),
|
||||
..default()
|
||||
});
|
||||
|
||||
commands.spawn(PbrBundle {
|
||||
mesh: meshes.add(shape::Torus::default().into()),
|
||||
material: debug_material.clone(),
|
||||
transform: Transform::from_xyz(3.0, 0.0, 0.0),
|
||||
..default()
|
||||
});
|
||||
|
||||
commands.spawn(PbrBundle {
|
||||
mesh: meshes.add(shape::Icosphere::default().try_into().unwrap()),
|
||||
material: debug_material.clone(),
|
||||
transform: Transform::from_xyz(0.0, 3.0, 0.0),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
/// Creates a colorful test pattern
|
||||
fn uv_debug_texture() -> Image {
|
||||
const TEXTURE_SIZE: usize = 8;
|
||||
|
||||
let mut palette: [u8; 32] = [
|
||||
255, 102, 159, 255, 255, 159, 102, 255, 236, 255, 102, 255, 121, 255, 102, 255, 102, 255,
|
||||
198, 255, 102, 198, 255, 255, 121, 102, 255, 255, 236, 102, 255, 255,
|
||||
];
|
||||
|
||||
let mut texture_data = [0; TEXTURE_SIZE * TEXTURE_SIZE * 4];
|
||||
for y in 0..TEXTURE_SIZE {
|
||||
let offset = TEXTURE_SIZE * y * 4;
|
||||
texture_data[offset..(offset + TEXTURE_SIZE * 4)].copy_from_slice(&palette);
|
||||
palette.rotate_right(4);
|
||||
}
|
||||
|
||||
Image::new_fill(
|
||||
Extent3d {
|
||||
width: TEXTURE_SIZE as u32,
|
||||
height: TEXTURE_SIZE as u32,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
TextureDimension::D2,
|
||||
&texture_data,
|
||||
TextureFormat::Rgba8UnormSrgb,
|
||||
)
|
||||
}
|
||||
|
||||
fn select(
|
||||
query: Query<(Entity, &Handle<Mesh>, &GlobalTransform)>,
|
||||
meshes: Res<Assets<Mesh>>,
|
||||
cameras: Query<(&Camera, &GlobalTransform)>,
|
||||
windows: Query<&Window, With<PrimaryWindow>>,
|
||||
) {
|
||||
// TODO:
|
||||
// When mouse moves
|
||||
// Ray trace to find object being selected
|
||||
windows.iter().for_each(|window| {
|
||||
if let Some(pos) = window.cursor_position() {
|
||||
cameras.iter().for_each(|(camera, gt)| {
|
||||
if let Some(ray) = camera.viewport_to_world(gt, pos) {
|
||||
query
|
||||
.iter()
|
||||
.filter_map(|(entity, handle, gt)| {
|
||||
meshes.get(handle).map(|mesh| (entity, mesh, gt))
|
||||
})
|
||||
.for_each(|(entity, mesh, gt)| {
|
||||
let hit = intersects(&ray, >, mesh);
|
||||
if hit.is_some() {
|
||||
info!("We got a hit on {:?} at {:?}", entity, hit);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Triangle {
|
||||
v0: Vec3,
|
||||
v1: Vec3,
|
||||
v2: Vec3,
|
||||
}
|
||||
|
||||
impl Triangle {
|
||||
fn normal(&self) -> Vec3 {
|
||||
(self.edge_a()).cross(self.edge_b())
|
||||
}
|
||||
|
||||
fn edge_a(&self) -> Vec3 {
|
||||
self.v1 - self.v0
|
||||
}
|
||||
|
||||
fn edge_b(&self) -> Vec3 {
|
||||
self.v2 - self.v0
|
||||
}
|
||||
|
||||
fn edge0(&self) -> Vec3 {
|
||||
self.v1 - self.v0
|
||||
}
|
||||
|
||||
fn edge1(&self) -> Vec3 {
|
||||
self.v2 - self.v1
|
||||
}
|
||||
|
||||
fn edge2(&self) -> Vec3 {
|
||||
self.v0 - self.v2
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Hit {
|
||||
distance: f32,
|
||||
point: Vec3,
|
||||
}
|
||||
|
||||
/// Heavily synthesized by these two things:
|
||||
/// * Textbook: https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/ray-triangle-intersection-geometric-solution.html
|
||||
/// * Example: https://github.com/aevyrie/bevy_mod_raycast/blob/435d8ef100738797161ac3a9b910ea346a4ed6e6/src/raycast.rs#L43
|
||||
fn intersects(ray: &Ray, gt: &GlobalTransform, mesh: &Mesh) -> Option<Hit> {
|
||||
let attr = MeshVertexAttribute::new("Vertex_Position", 0, VertexFormat::Float32x3);
|
||||
if let Some(verts) = mesh.attribute(attr) {
|
||||
if let Some(idxs) = mesh.indices() {
|
||||
match verts {
|
||||
VertexAttributeValues::Float32x3(vals) => {
|
||||
idxs.iter()
|
||||
.array_chunks::<3>()
|
||||
// Convert arrays to vec3
|
||||
.map(|[x, y, z]| {
|
||||
[
|
||||
Vec3::from_array(vals[x]),
|
||||
Vec3::from_array(vals[y]),
|
||||
Vec3::from_array(vals[z]),
|
||||
]
|
||||
})
|
||||
// Transform each point by the global transform
|
||||
.map(|[a, b, c]| {
|
||||
[
|
||||
gt.transform_point(a),
|
||||
gt.transform_point(b),
|
||||
gt.transform_point(c),
|
||||
]
|
||||
})
|
||||
// Collect everything into a triangle for easy operations
|
||||
.map(|[v0, v1, v2]| Triangle { v0, v1, v2 })
|
||||
.filter_map(|triangle| {
|
||||
// Calculate the distance this ray hits the plane normal to the tri
|
||||
if let Some(d) = ray.intersect_plane(triangle.v0, triangle.normal()) {
|
||||
// Calculate the point on that plane which intersects
|
||||
let p = ray.get_point(d);
|
||||
// Inside out test
|
||||
let hit = {
|
||||
// Determine if p is w/in edge0
|
||||
let c0 = triangle.edge0().cross(p - triangle.v0);
|
||||
// Determine if p is w/in edge1
|
||||
let c1 = triangle.edge1().cross(p - triangle.v1);
|
||||
// Determine if p is w/in edge2
|
||||
let c2 = triangle.edge2().cross(p - triangle.v2);
|
||||
|
||||
// Check all three at once
|
||||
triangle.normal().dot(c0) > 0.0
|
||||
&& triangle.normal().dot(c1) > 0.0
|
||||
&& triangle.normal().dot(c2) > 0.0
|
||||
};
|
||||
hit.then_some(Hit {
|
||||
distance: d,
|
||||
point: p,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.min_by(|a, b| a.distance.total_cmp(&b.distance))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
@ -1,186 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
type MyMat = ExtendedMaterial<StandardMaterial, MatExt>;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins((
|
||||
DefaultPlugins.set(ImagePlugin::default_nearest()),
|
||||
MaterialPlugin::<MyMat>::default(),
|
||||
))
|
||||
.insert_resource(Msaa::Off)
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, apply_skybox)
|
||||
.add_systems(Update, set_scene)
|
||||
.add_systems(Update, rotate)
|
||||
.add_systems(
|
||||
Update,
|
||||
toggle_material
|
||||
.run_if(|keys: Res<Input<KeyCode>>| -> bool { keys.just_pressed(KeyCode::Space) }),
|
||||
)
|
||||
.add_systems(
|
||||
Update,
|
||||
init_materials
|
||||
.run_if(|events: Query<Entity, Added<Transform>>| -> bool { !events.is_empty() }),
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct Root;
|
||||
|
||||
fn setup(mut commands: Commands, assets: Res<AssetServer>) {
|
||||
commands.spawn((SceneBundle { ..default() }, Root));
|
||||
|
||||
commands.spawn(PointLightBundle {
|
||||
point_light: PointLight {
|
||||
intensity: 10.0,
|
||||
..default()
|
||||
},
|
||||
transform: Transform::from_xyz(-1.0, 4.5, 4.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||
..default()
|
||||
});
|
||||
|
||||
commands.spawn((Camera3dBundle {
|
||||
transform: Transform::from_xyz(-1.0, 1.5, 4.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||
..default()
|
||||
},));
|
||||
}
|
||||
|
||||
fn apply_skybox(
|
||||
cameras: Query<Entity, With<Camera>>,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
assets: Res<AssetServer>,
|
||||
mut commands: Commands,
|
||||
mut done: Local<bool>,
|
||||
mut handle: Local<Handle<Image>>,
|
||||
) {
|
||||
if !*done {
|
||||
info!("Applying skybox...");
|
||||
*handle = assets.load("images/skybox.png");
|
||||
if let Some(image) = images.get_mut(handle.clone()) {
|
||||
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()
|
||||
});
|
||||
|
||||
cameras.iter().for_each(|e| {
|
||||
commands.entity(e).insert(Skybox { image: handle.clone(), brightness: 1.0 });
|
||||
});
|
||||
|
||||
*done = true;
|
||||
}
|
||||
} else {
|
||||
*done = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_scene(
|
||||
mut events: Query<&mut Handle<Scene>, (With<Root>, Added<Root>)>,
|
||||
assets: Res<AssetServer>,
|
||||
) {
|
||||
events.iter_mut().for_each(|mut scene| {
|
||||
*scene = assets.load("models/Martian Chess.glb#Scene0");
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Asset, AsBindGroup, Reflect, Debug, Clone)]
|
||||
struct MatExt {
|
||||
#[uniform(100)]
|
||||
cutoff: f32,
|
||||
}
|
||||
|
||||
impl MaterialExtension for MatExt {
|
||||
fn fragment_shader() -> ShaderRef {
|
||||
"examples/shaders/dissolve.wgsl".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
struct Backup<T: Component>(T);
|
||||
|
||||
fn init_materials(
|
||||
events: Query<(Entity, &Handle<StandardMaterial>), Added<Handle<StandardMaterial>>>,
|
||||
standard_materials: Res<Assets<StandardMaterial>>,
|
||||
mut materials: ResMut<Assets<MyMat>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
info!("initializing materials");
|
||||
|
||||
events.iter().for_each(|(entity, std_handle)| {
|
||||
// Extension we will add to existing gltf-sourced materials
|
||||
let extension = MatExt { cutoff: 1.0 };
|
||||
// Base material we will extend for the duration of the dissolve effect
|
||||
let mut base = standard_materials
|
||||
.get(std_handle)
|
||||
.expect("Resolve material data")
|
||||
.clone();
|
||||
|
||||
base.opaque_render_method = OpaqueRendererMethod::Auto;
|
||||
|
||||
let ext_handle = materials.add(ExtendedMaterial { base, extension });
|
||||
|
||||
commands.entity(entity).insert(Backup(ext_handle.clone()));
|
||||
});
|
||||
}
|
||||
|
||||
fn toggle_material(
|
||||
query: Query<Entity, Or<(With<Handle<StandardMaterial>>, With<Handle<MyMat>>)>>,
|
||||
std_mat: Query<(&Handle<StandardMaterial>, Option<&Backup<Handle<MyMat>>>)>,
|
||||
ext_mat: Query<(&Handle<MyMat>, Option<&Backup<Handle<StandardMaterial>>>)>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
query.iter().for_each(|entity| {
|
||||
// Entity has standard material, and had extended material
|
||||
// Swap these materials
|
||||
if let Ok((std_handle, Some(Backup(ext_handle)))) = std_mat.get(entity) {
|
||||
info!("Swapping standard material for extended material");
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert(ext_handle.clone())
|
||||
.insert(Backup(std_handle.clone()))
|
||||
.remove::<Backup<Handle<MyMat>>>()
|
||||
.remove::<Handle<StandardMaterial>>();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// In this branch we have the extended material assigned to the object
|
||||
if let Ok((ext_handle, Some(Backup(std_handle)))) = ext_mat.get(entity) {
|
||||
// Entity has standard material, and had extended material // Swap these materials
|
||||
info!("Swapping extended material for standard material");
|
||||
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert(std_handle.clone())
|
||||
.insert(Backup(ext_handle.clone()))
|
||||
.remove::<Backup<Handle<StandardMaterial>>>()
|
||||
.remove::<Handle<MyMat>>();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
panic!("What is happening?")
|
||||
});
|
||||
}
|
||||
|
||||
fn rotate(
|
||||
mut query: Query<&mut Transform, With<Handle<Mesh>>>,
|
||||
time: Res<Time>,
|
||||
mut materials: ResMut<Assets<MyMat>>,
|
||||
) {
|
||||
query.iter_mut().for_each(|mut t| {
|
||||
t.rotate_local_y(time.delta_seconds() / 2.0);
|
||||
});
|
||||
materials.iter_mut().for_each(|(_id, m)| {
|
||||
m.extension.cutoff = time.elapsed_seconds().sin().abs();
|
||||
})
|
||||
}
|
||||
@ -1,70 +0,0 @@
|
||||
use bevy::{
|
||||
core_pipeline::Skybox,
|
||||
input::mouse::MouseMotion,
|
||||
prelude::*,
|
||||
render::render_resource::{TextureViewDescriptor, TextureViewDimension},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_systems(Startup, init)
|
||||
.add_systems(Update, (add_skybox, move_camera))
|
||||
.run()
|
||||
}
|
||||
|
||||
fn init(mut commands: Commands) {
|
||||
commands.spawn(Camera3dBundle {
|
||||
camera: Camera {
|
||||
is_active: true,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
fn add_skybox(
|
||||
server: Res<AssetServer>,
|
||||
query: Query<Entity, With<Camera>>,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
mut commands: Commands,
|
||||
mut handle: Local<Option<Handle<Image>>>,
|
||||
mut loaded: Local<bool>,
|
||||
) {
|
||||
if !(*loaded) {
|
||||
if let Some(handle) = (*handle).clone() {
|
||||
if let Some(image) = images.get_mut(&handle) {
|
||||
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()
|
||||
});
|
||||
}
|
||||
query.iter().for_each(|entity| {
|
||||
commands.entity(entity).insert(Skybox(handle.clone()));
|
||||
});
|
||||
*loaded = true;
|
||||
}
|
||||
} else {
|
||||
*handle = Some(server.load("images/skybox.png"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn move_camera(
|
||||
mut events: EventReader<MouseMotion>,
|
||||
mut camera: Query<&mut Transform, With<Camera>>,
|
||||
) {
|
||||
events.iter().for_each(|MouseMotion { delta }| {
|
||||
camera.iter_mut().for_each(|mut t| {
|
||||
t.rotate_local_y(-delta.x / 256.0);
|
||||
t.rotate_local_x(-delta.y / 256.0);
|
||||
})
|
||||
});
|
||||
}
|
||||
@ -1,111 +0,0 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use bevy::{asset::ChangeWatcher, prelude::*, sprite::MaterialMesh2dBundle};
|
||||
|
||||
const SCALE: f32 = 80.0;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins((DefaultPlugins
|
||||
.set(ImagePlugin::default_nearest())
|
||||
.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
title: "2D Sprites".into(),
|
||||
resolution: (640., 480.).into(),
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
})
|
||||
.set(AssetPlugin {
|
||||
watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
|
||||
..default()
|
||||
}),))
|
||||
.add_systems(Startup, (initialize_camera, load_spritesheet))
|
||||
.add_systems(Update, initialize_board.run_if(check_initialize_board))
|
||||
.run();
|
||||
}
|
||||
|
||||
/// Sprite sheet Resource for later reference
|
||||
#[derive(Debug, Resource)]
|
||||
struct SpriteSheet {
|
||||
handle: Handle<TextureAtlas>,
|
||||
}
|
||||
|
||||
// Marker component for the 2d board entity
|
||||
#[derive(Debug, Component)]
|
||||
struct Board2d;
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
struct BoardIndex {
|
||||
x: usize,
|
||||
y: usize,
|
||||
}
|
||||
|
||||
/// STARTUP: Initialize 2d gameplay Camera
|
||||
fn initialize_camera(mut commands: Commands) {
|
||||
commands.spawn(Camera2dBundle::default());
|
||||
}
|
||||
|
||||
/// STARTUP: Load sprite sheet and insert texture atlas
|
||||
fn load_spritesheet(
|
||||
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
|
||||
server: Res<AssetServer>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
let atlas = TextureAtlas::from_grid(
|
||||
server.load("sprites.png"),
|
||||
Vec2::new(16.0, 16.0),
|
||||
5,
|
||||
1,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
commands.insert_resource(SpriteSheet {
|
||||
handle: texture_atlases.add(atlas),
|
||||
});
|
||||
}
|
||||
|
||||
fn check_initialize_board(query: Query<Entity, With<Board2d>>) -> bool {
|
||||
query.is_empty()
|
||||
}
|
||||
|
||||
/// STARTUP: Initialize the board for representation
|
||||
fn initialize_board(sprite_sheet: Option<Res<SpriteSheet>>, mut commands: Commands) {
|
||||
if let Some(sprite_sheet) = sprite_sheet {
|
||||
commands
|
||||
.spawn((
|
||||
SpatialBundle {
|
||||
transform: Transform::from_xyz(-SCALE * 3.5, -SCALE * 1.5, 0.0),
|
||||
..default()
|
||||
},
|
||||
Board2d,
|
||||
))
|
||||
.with_children(|parent| {
|
||||
for i in 0..32 {
|
||||
let x = i % 8;
|
||||
let y = i / 8;
|
||||
let s = (x % 2) ^ (y % 2);
|
||||
|
||||
let transform = Transform::from_scale(Vec3::splat(5.0))
|
||||
.with_translation(Vec3::new(SCALE * x as f32, SCALE * y as f32, 0.0));
|
||||
|
||||
let sprite = TextureAtlasSprite::new(s);
|
||||
|
||||
let texture_atlas = sprite_sheet.handle.clone();
|
||||
|
||||
let index = BoardIndex { x, y };
|
||||
|
||||
// Rectangle
|
||||
parent.spawn((
|
||||
SpriteSheetBundle {
|
||||
texture_atlas,
|
||||
sprite,
|
||||
transform,
|
||||
..default()
|
||||
},
|
||||
index,
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
use bevy::prelude::Color;
|
||||
use serde::Deserialize;
|
||||
use toml::Table;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
enum Val {
|
||||
A(String),
|
||||
B(usize),
|
||||
C(bool),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = r#"
|
||||
[color]
|
||||
asdf = true
|
||||
[color.foo]
|
||||
qwert = false
|
||||
[color.foo.bar]
|
||||
hjkl = [1,2,3]
|
||||
[color.foo.bar.baz]
|
||||
Rgba = { red = 1.0, blue = 1.0, green = 1.0, alpha = 1.0 }
|
||||
"#;
|
||||
let vt = toml::from_str::<Table>(s).unwrap();
|
||||
|
||||
iter_all(&vt, "")
|
||||
.iter()
|
||||
.for_each(|(k, v)| println!("k {:?} :: v {:?}", k, v));
|
||||
|
||||
let x = get::<Color>(&vt, "color_foo_bar_baz").unwrap();
|
||||
println!("{:?}", x);
|
||||
|
||||
let y = get::<Vec<usize>>(&vt, "color_foo_bar_hjkl").unwrap();
|
||||
println!("{:?}", y);
|
||||
|
||||
let z = get::<bool>(&vt, "color_foo_qwert").unwrap();
|
||||
println!("{:?}", z);
|
||||
}
|
||||
|
||||
fn iter_all(t: &toml::Table, key: &str) -> Vec<(String, toml::Value)> {
|
||||
t.iter()
|
||||
.flat_map(|(k, v)| {
|
||||
let nk = if key.is_empty() {
|
||||
k.to_string()
|
||||
} else {
|
||||
format!("{}_{}", key, k)
|
||||
};
|
||||
match v {
|
||||
toml::Value::Table(nt) => iter_all(nt, nk.as_str()),
|
||||
_ => vec![(nk, v.clone())],
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn locate(t: &toml::Table, key: &str) -> Option<toml::Value> {
|
||||
t.iter().find_map(|(k, v)| {
|
||||
if key == k {
|
||||
Some(v.clone())
|
||||
} else if key.starts_with(k) {
|
||||
let prefix = format!("{}_", k);
|
||||
match v {
|
||||
toml::Value::Table(nt) => locate(nt, key.strip_prefix(prefix.as_str()).unwrap()),
|
||||
_ => Some(v.clone()),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get<'de, T: Deserialize<'de>>(t: &toml::Table, key: &str) -> Option<T> {
|
||||
locate(t, key).and_then(|val| val.try_into().ok())
|
||||
}
|
||||
Loading…
Reference in New Issue