Compare commits

..

No commits in common. "a7b4d8c7abbe7cd99bfda55fec865b54944e7bc7" and "5f3c0e1b36760a96f9ca5d243bc0926d1723f61f" have entirely different histories.

6 changed files with 66 additions and 229 deletions

8
Cargo.lock generated
View File

@ -338,9 +338,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy-inspector-egui" name = "bevy-inspector-egui"
version = "0.28.1" version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36172627eb6fd8586600972bcbba2880ed6f59e4e243dcf2ed7ff68d987577ce" checksum = "dd64580f4496ed987c6231c6a7d833068914331a9084bf5a3dd9dcbc66fd8a73"
dependencies = [ dependencies = [
"bevy-inspector-egui-derive", "bevy-inspector-egui-derive",
"bevy_app", "bevy_app",
@ -2278,7 +2278,7 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
[[package]] [[package]]
name = "forestiles" name = "forestiles"
version = "0.2.0" version = "0.1.0"
dependencies = [ dependencies = [
"android_logger", "android_logger",
"bevy", "bevy",
@ -2402,7 +2402,7 @@ dependencies = [
"vec_map", "vec_map",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
"windows 0.58.0", "windows 0.52.0",
] ]
[[package]] [[package]]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "forestiles" name = "forestiles"
version = "0.2.0" version = "0.1.0"
edition = "2021" edition = "2021"
[lib] [lib]
@ -30,7 +30,7 @@ bevy = { version = "0.15", default-features = false, features = [
"bevy_gltf", "bevy_gltf",
"bevy_mesh_picking_backend", "bevy_mesh_picking_backend",
"bevy_pbr", "bevy_pbr",
# "bevy_picking", "bevy_picking",
"bevy_render", "bevy_render",
"bevy_scene", "bevy_scene",
"bevy_sprite", "bevy_sprite",
@ -84,9 +84,6 @@ android_logger = "0.14"
[profile.dev] [profile.dev]
opt-level = 1 opt-level = 1
[profile.release]
debug = true
# Enable a large amount of optimization in the dev profile for dependencies. # Enable a large amount of optimization in the dev profile for dependencies.
[profile.dev.package."*"] [profile.dev.package."*"]
opt-level = 3 opt-level = 3

View File

@ -1,15 +1,13 @@
use std::time::Duration; use bevy::{math::NormedVectorSpace, picking::{focus::HoverMap, pointer::PointerId}, prelude::*, utils::HashMap, window::PrimaryWindow};
use bevy::{input::mouse::MouseWheel, math::NormedVectorSpace, picking::{focus::HoverMap, pointer::PointerId}, prelude::*, utils::HashMap, window::PrimaryWindow};
use crate::ui; use crate::ui;
pub struct Plugin; pub struct Plugin;
impl bevy::prelude::Plugin for Plugin { impl bevy::prelude::Plugin for Plugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Startup, setup); app.add_systems(Startup, setup)
// .add_systems(Update, move_cam) .add_systems(Update, move_cam)
// .init_resource::<Pointers>(); .init_resource::<Pointers>();
} }
} }
@ -17,39 +15,40 @@ fn setup(mut cmds: Commands, window: Query<&Window>) {
let zoom = 2./window.single().width().min(window.single().height()); let zoom = 2./window.single().width().min(window.single().height());
cmds.spawn(( cmds.spawn((
Camera2d, Camera2d,
Transform::from_scale(Vec3::new(zoom, zoom, zoom)) Transform::from_scale(Vec3::new(zoom, zoom, 1.))
)); ));
} }
#[derive(Resource, Default)] #[derive(Resource, Default)]
struct Pointers(HashMap<PointerId, (Vec2, Option<Duration>)>); struct Pointers(HashMap<PointerId, Vec2>);
fn move_cam( fn move_cam(
mut cam: Query<&mut Transform, With<Camera2d>>, mut cam: Query<&mut Transform, With<Camera2d>>,
map_ui_entity: Query<Entity, With<ui::MapUIComponent>>, map_ui_entity: Query<Entity, With<ui::MapUIComponent>>,
mouse_buttons: Res<ButtonInput<MouseButton>>, mouse_buttons: Res<ButtonInput<MouseButton>>,
mut ev_scroll: EventReader<MouseWheel>,
touches: Res<Touches>, touches: Res<Touches>,
window: Query<&Window, With<PrimaryWindow>>, window: Query<&Window, With<PrimaryWindow>>,
mut pointers: ResMut<Pointers>, mut pointers: ResMut<Pointers>,
hover_map: Res<HoverMap>, hover_map: Res<HoverMap>
time: Res<Time>
) { ) {
let window = window.single(); let window = window.single();
let mut cam = cam.single_mut(); let mut cam = cam.single_mut();
let map_ui_entity = map_ui_entity.single(); let map_ui_entity = map_ui_entity.single();
let ps = hover_map.iter().filter_map(|(id, hit_map)| match id { let ps = hover_map.iter().filter_map(|(id, hit_map)| match id {
PointerId::Mouse => window.cursor_position().map(|p| PointerId::Mouse => window.cursor_position().map(|p| (mouse_buttons.pressed(MouseButton::Left), p, pointers.0.get(id).map(|p| *p).unwrap_or(p))),
match pointers.0.get(id) { PointerId::Touch(i) => touches.get_pressed(*i).map(|t| (true, t.position(), t.previous_position())),
Some(p_cache) => (p_cache.1.filter(|_| mouse_buttons.pressed(MouseButton::Left)), p, p_cache.0),
None => (None, p, p)
}
),
PointerId::Touch(i) => touches.get_pressed(*i).map(|t| (pointers.0.get(id).map(|(pos, start)| (*start).unwrap_or(time.elapsed())), t.position(), t.previous_position())),
_ => None _ => None
}.map(|(pressed_start,new_pos, old_pos)| (pressed_start,new_pos,old_pos,id,hit_map)) }.map(|(pressed,new_pos, old_pos)| (pressed,new_pos,old_pos,id,hit_map))
).collect::<Vec<_>>(); ).collect::<Vec<_>>();
let pressed_on_map = ps.iter().filter(|p| p.0.is_some() && p.4.contains_key(&map_ui_entity)).collect::<Vec<_>>(); let pressed_on_map = ps.iter().filter(|p| p.0 && p.4.contains_key(&map_ui_entity)).collect::<Vec<_>>();
// let mut movement = Vec2::ZERO;
// for (pressed, new_pos, old_pos, id, hit_map) in pressed_on_map.iter() {
// let delta = new_pos - old_pos;
// cam.scale += delta.dot(movement)/delta.norm();
// movement.x -= delta.x*cam.scale.x/pressed_num as f32;
// movement.y += delta.y*cam.scale.y/pressed_num as f32;
// pointers.0.insert(**id, *new_pos);
// }
let old_midpoint = pressed_on_map.iter().fold(Vec2::ZERO, |acc, (_, _, old_pos, _, _)| { let old_midpoint = pressed_on_map.iter().fold(Vec2::ZERO, |acc, (_, _, old_pos, _, _)| {
acc + (old_pos/pressed_on_map.len() as f32) acc + (old_pos/pressed_on_map.len() as f32)
@ -57,12 +56,9 @@ fn move_cam(
let new_midpoint = pressed_on_map.iter().fold(Vec2::ZERO, |acc, (_, new_pos, _, _, _)| { let new_midpoint = pressed_on_map.iter().fold(Vec2::ZERO, |acc, (_, new_pos, _, _, _)| {
acc + (new_pos/pressed_on_map.len() as f32) acc + (new_pos/pressed_on_map.len() as f32)
}); });
// move camera
cam.translation.x -= (new_midpoint.x - old_midpoint.x)*cam.scale.x; cam.translation.x -= (new_midpoint.x - old_midpoint.x)*cam.scale.x;
cam.translation.y += (new_midpoint.y - old_midpoint.y)*cam.scale.y; cam.translation.y += (new_midpoint.y - old_midpoint.y)*cam.scale.y;
// multiple fingers zoom
if pressed_on_map.len() > 1 { if pressed_on_map.len() > 1 {
let old_d_to_midpoint = pressed_on_map.iter().fold(0., |acc, (_, _, old_pos, _, _)| { let old_d_to_midpoint = pressed_on_map.iter().fold(0., |acc, (_, _, old_pos, _, _)| {
acc + (old_midpoint-old_pos).norm() acc + (old_midpoint-old_pos).norm()
@ -71,22 +67,12 @@ fn move_cam(
acc + (new_midpoint-new_pos).norm() acc + (new_midpoint-new_pos).norm()
}); });
let zoom = new_d_to_midpoint/old_d_to_midpoint; let zoom = new_d_to_midpoint/old_d_to_midpoint;
cam.scale /= zoom; dbg!(zoom);
cam.scale.x /= zoom;
cam.scale.y /= zoom;
} }
// mouse scroll zoom for (_, new_pos, _, id, _) in ps {
for ev in ev_scroll.read() { pointers.0.insert(*id, new_pos);
let scale = (cam.scale.x-(ev.y*0.1/window.width().min(window.height()))).clamp(0.0001, 2./window.width().min(window.height()));
cam.scale = Vec3::new(scale, scale, scale);
}
// update cached pointer positions
pointers.0.clear();
for (pressed_start, new_pos, _, id, _) in ps {
match id {
PointerId::Mouse => {pointers.0.insert(*id, (new_pos, pressed_start));},
PointerId::Touch(_) => {pointers.0.insert(*id, (new_pos, pressed_start));},
_ => {}
}
} }
} }

View File

@ -1,13 +1,9 @@
use std::time::Duration; use bevy::{asset::RenderAssetUsages, prelude::*, render::mesh::{Indices, PrimitiveTopology}};
use bevy::{asset::RenderAssetUsages, picking::PickSet, prelude::*, render::mesh::{Indices, PrimitiveTopology}, utils::HashMap};
use noise::{Fbm, MultiFractal, NoiseFn, Perlin}; use noise::{Fbm, MultiFractal, NoiseFn, Perlin};
use rand::{thread_rng, Rng, SeedableRng}; use rand::{Rng, SeedableRng};
use voronoice::{BoundingBox, Point, VoronoiBuilder}; use voronoice::{BoundingBox, Point, VoronoiBuilder};
mod cells; mod cells;
mod picking;
use picking::*;
use cells::*; use cells::*;
pub struct Plugin; pub struct Plugin;
@ -16,10 +12,8 @@ impl bevy::prelude::Plugin for Plugin {
app.add_systems(Startup, setup) app.add_systems(Startup, setup)
.insert_resource(Time::<Fixed>::from_seconds(0.25)) // Time for a day .insert_resource(Time::<Fixed>::from_seconds(0.25)) // Time for a day
.add_systems(FixedUpdate, update_cells) .add_systems(FixedUpdate, update_cells)
.add_systems(PreUpdate, picking_backend.in_set(PickSet::Backend))
.add_systems(Update, update_map_mesh)
.insert_resource(ClearColor(Color::srgb(0., 0., 1.))) .insert_resource(ClearColor(Color::srgb(0., 0., 1.)))
.insert_resource(Seed(thread_rng().gen())); .insert_resource(Seed(0));
} }
} }
@ -34,7 +28,7 @@ pub const SIZE: usize = 10000;
struct Seed(u32); struct Seed(u32);
#[derive(Component)] #[derive(Component)]
pub struct Voronoi (voronoice::Voronoi); struct Voronoi (voronoice::Voronoi);
#[derive(Component)] #[derive(Component)]
pub struct MapMarker; pub struct MapMarker;
@ -42,12 +36,6 @@ pub struct MapMarker;
#[derive(Component)] #[derive(Component)]
struct MapColors (Vec<[f32; 4]>); struct MapColors (Vec<[f32; 4]>);
#[derive(Component)]
pub struct CellsEntities (Vec<Entity>);
#[derive(Component)]
pub struct MeshNeedsUpdate(bool);
fn setup( fn setup(
mut cmds: Commands, mut cmds: Commands,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
@ -85,7 +73,7 @@ fn setup(
} else if z <= 0.52 { } else if z <= 0.52 {
CellKind::Beach CellKind::Beach
} else if z < 0.8 { } else if z < 0.8 {
CellKind::Dirt CellKind::Grass
} else { } else {
CellKind::Stone CellKind::Stone
}; };
@ -130,36 +118,21 @@ fn setup(
) )
.with_inserted_indices(Indices::U32(indices)); .with_inserted_indices(Indices::U32(indices));
let mut cells_entities = Vec::with_capacity(cells_data.len());
cmds.spawn(( cmds.spawn((
Mesh2d(meshes.add(mesh)), Mesh2d(meshes.add(mesh)),
MeshMaterial2d(materials.add(ColorMaterial::default())), MeshMaterial2d(materials.add(ColorMaterial::default())),
Transform::default(), Transform::default(),
Voronoi(voronoi), Voronoi(voronoi),
MapColors(colors), MapColors(colors),
MeshNeedsUpdate(true),
MapMarker MapMarker
)).with_children(|parent| { )).with_children(|parent| {
for cd in cells_data { for cd in cells_data {
let mut cmd = parent.spawn((cd, LastUpdate(0)));
cmd.observe(|trigger: Trigger<Pointer<Click>>, mut cells: Query<&mut CellData>, mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>>| {
if trigger.duration > Duration::from_millis(100) {
return
}
let mut cd = cells.get_mut(trigger.target).unwrap();
match cd.kind { match cd.kind {
CellKind::Dirt | CellKind::Grass => { CellKind::Forest | CellKind::Grass => parent.spawn((cd, LastUpdate(0))),
cd.kind = CellKind::Forest; _ => parent.spawn(cd)
map_needs_update.single_mut().0 = true; };
},
_ => {}
} }
dbg!(trigger.duration);
}); });
cells_entities.push(cmd.id());
}
}).insert(CellsEntities(cells_entities));
} }
#[derive(Component)] #[derive(Component)]
@ -167,9 +140,13 @@ pub struct LastUpdate(usize);
fn update_cells( fn update_cells(
mut cells: Query<(&mut CellData, &mut LastUpdate)>, mut cells: Query<(&mut CellData, &mut LastUpdate)>,
mut map_needs_update: Query<&mut MeshNeedsUpdate, With<MapMarker>> mut map: Query<(&Mesh2d, &mut MapColors), With<MapMarker>>,
mut meshes: ResMut<Assets<Mesh>>
) { ) {
let mut map_needs_update = map_needs_update.single_mut(); let (mesh, mut cols) = map.single_mut();
if let Some(mesh) = meshes.get_mut(mesh) {
// let cols = mesh.attribute_mut(Mesh::ATTRIBUTE_COLOR).unwrap();
let mut modified = false;
for (mut cd, mut lu) in cells.iter_mut() { for (mut cd, mut lu) in cells.iter_mut() {
lu.0 += 1; lu.0 += 1;
if lu.0 > match cd.kind { if lu.0 > match cd.kind {
@ -179,24 +156,11 @@ fn update_cells(
} { } {
lu.0 = 0; lu.0 = 0;
cd.resource = (cd.resource + 1).clamp(0, 4); cd.resource = (cd.resource + 1).clamp(0, 4);
map_needs_update.0 = true; modified = true;
} }
} // cd.update();
}
fn update_map_mesh(
cells: Query<&CellData>,
mut map: Query<(&Mesh2d, &mut MapColors, &mut MeshNeedsUpdate), With<MapMarker>>,
mut meshes: ResMut<Assets<Mesh>>
) {
let (mesh, mut cols, mut needs_update) = map.single_mut();
if needs_update.0 {
if let Some(mesh) = meshes.get_mut(mesh) {
let mut modified = false;
for cd in cells.iter() {
let col = cd.color(); let col = cd.color();
for id in cd.vertices.iter() { for id in cd.vertices.iter() {
modified = modified || cols.0[*id] != col;
cols.0[*id] = col.clone(); cols.0[*id] = col.clone();
} }
} }
@ -204,6 +168,4 @@ fn update_map_mesh(
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, cols.0.clone()); mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, cols.0.clone());
} }
} }
needs_update.0 = false;
}
} }

View File

@ -1,39 +0,0 @@
use bevy::{picking::{backend::{HitData, PointerHits}, pointer::{PointerId, PointerLocation}}, prelude::*, window::PrimaryWindow};
use voronoice::Point;
use super::{CellsEntities, MapMarker, Voronoi};
pub fn picking_backend(
cam: Query<(&Transform, Entity), With<Camera2d>>,
window: Query<&Window, With<PrimaryWindow>>,
pointers: Query<(&PointerId, &PointerLocation)>,
map: Query<(&Voronoi, &CellsEntities, &Transform), With<MapMarker>>,
mut output: EventWriter<PointerHits>
) {
let (cam, cam_id) = cam.single();
let window = window.single();
let (voronoi, cells_entities, map_pos) = map.single();
let mut last_cell = 0;
for (id, l) in pointers.iter() {
if let Some(mut pos) = l.location().map(|l| l.position) {
pos -= window.size()/2.;
pos *= cam.scale.xy();
pos.x += cam.translation.x;
pos.y -= cam.translation.y;
if let Some(c) = voronoi.0.cell(last_cell).iter_path(Point { x: pos.x as f64, y: -pos.y as f64 }).last() {
last_cell = c;
output.send(PointerHits {
pointer: *id,
picks: vec![(cells_entities.0[c], HitData {
camera: cam_id,
depth: map_pos.translation.z,
position: Some(Vec3 { x: pos.x, y: pos.y, z: map_pos.translation.z }),
normal: None
})],
order: map_pos.translation.z
});
}
}
}
}

View File

@ -1,20 +1,12 @@
use std::collections::BTreeMap; use bevy::prelude::*;
use bevy::{input::mouse::MouseWheel, math::{NormedVectorSpace, VectorSpace}, picking::{focus::HoverMap, pointer::PointerId}, prelude::*, utils::HashMap, window::PrimaryWindow};
use crate::map::{self, MapMarker};
pub struct Plugin; pub struct Plugin;
impl bevy::prelude::Plugin for Plugin { impl bevy::prelude::Plugin for Plugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Startup, setup) app.add_systems(Startup, setup);
.add_systems(Update, zoom_with_scroll);
} }
} }
#[derive(Component)]
struct PointersDragging(HashMap<PointerId, Vec2>);
#[derive(Component)] #[derive(Component)]
pub struct MapUIComponent; pub struct MapUIComponent;
@ -29,68 +21,7 @@ fn setup(
justify_content: JustifyContent::Center, justify_content: JustifyContent::Center,
..default() ..default()
}, },
PickingBehavior { MapUIComponent
should_block_lower: false, ));
is_hoverable: true
},
MapUIComponent,
PointersDragging(HashMap::new())
)).observe(|trigger: Trigger<Pointer<DragStart>>, mut ptrs: Query<&mut PointersDragging>| {
let event = trigger.event();
// dbg!(event);
if event.button == PointerButton::Primary {
ptrs.get_mut(event.target).unwrap().0.insert(event.pointer_id, event.pointer_location.position);
}
}).observe(|trigger: Trigger<Pointer<DragEnd>>, mut ptrs: Query<&mut PointersDragging>| {
let event = trigger.event();
// dbg!(event);
if event.button == PointerButton::Primary {
ptrs.get_mut(event.target).unwrap().0.remove(&event.pointer_id);
}
}).observe(|
trigger: Trigger<Pointer<Drag>>,
mut ptrs: Query<&mut PointersDragging>,
mut cam: Query<&mut Transform, With<Camera2d>>,
| {
let event = trigger.event();
// dbg!(event);
if event.button == PointerButton::Primary {
let mut cam = cam.single_mut();
let mut ptrs = ptrs.get_mut(event.target).unwrap();
let old_midpoint = ptrs.0.values().fold(Vec2::ZERO, |acc, pos| acc + (pos/ptrs.0.len() as f32));
let old_d_to_midpoint = ptrs.0.values().fold(0., |acc, pos| acc + (old_midpoint-pos).norm());
ptrs.0.insert(event.pointer_id, event.pointer_location.position);
let new_midpoint = ptrs.0.values().fold(Vec2::ZERO, |acc, pos| acc + (pos/ptrs.0.len() as f32));
let new_d_to_midpoint = ptrs.0.values().fold(0., |acc, pos| acc + (new_midpoint-pos).norm());
// move camera
cam.translation.x -= (new_midpoint.x - old_midpoint.x)*cam.scale.x;
cam.translation.y += (new_midpoint.y - old_midpoint.y)*cam.scale.y;
if ptrs.0.len() > 1 {
cam.scale *= old_d_to_midpoint/new_d_to_midpoint;
}
}
}
);
// Spawn all ui elements as children of this one // Spawn all ui elements as children of this one
} }
fn zoom_with_scroll(
mut cam: Query<&mut Transform, With<Camera2d>>,
mut ev_scroll: EventReader<MouseWheel>,
hover_map: Res<HoverMap>,
window: Query<&Window, With<PrimaryWindow>>,
map_ui_id: Query<Entity, With<MapUIComponent>>
) {
let map_ui_id = map_ui_id.single();
if hover_map.get(&PointerId::Mouse).and_then(|hovered_ids| hovered_ids.get(&map_ui_id)).is_some() {
let window = window.single();
let mut cam = cam.single_mut();
for ev in ev_scroll.read() {
let scale = (cam.scale.x-(ev.y*0.1/window.width().min(window.height()))).clamp(0.0001, 2./window.width().min(window.height()));
cam.scale = Vec3::new(scale, scale, scale);
}
}
}