make wasm work + cleaning + fix mouse
This commit is contained in:
parent
a07f0fee0f
commit
f49be0b90d
@ -1,6 +1,2 @@
|
||||
[target.wasm32-unknown-unknown]
|
||||
runner = "wasm-server-runner"
|
||||
|
||||
# [target.aarch64-unknown-linux-gnu]
|
||||
# linker = "clang"
|
||||
# rustflags = ["-C", "link-arg=-fuse-ld=/usr/bin/mold"]
|
||||
runner = "wasm-server-runner"
|
68
Cargo.lock
generated
68
Cargo.lock
generated
@ -196,24 +196,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arboard"
|
||||
version = "3.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4"
|
||||
dependencies = [
|
||||
"clipboard-win",
|
||||
"core-graphics",
|
||||
"image",
|
||||
"log",
|
||||
"objc2",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation",
|
||||
"parking_lot",
|
||||
"windows-sys 0.48.0",
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.8"
|
||||
@ -655,7 +637,6 @@ version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "954fbe8551af4b40767ea9390ec7d32fe1070a6ab55d524cf0868c17f8469a55"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"bevy_app",
|
||||
"bevy_asset",
|
||||
"bevy_derive",
|
||||
@ -676,7 +657,6 @@ dependencies = [
|
||||
"encase",
|
||||
"js-sys",
|
||||
"log",
|
||||
"thread_local",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
@ -1605,15 +1585,6 @@ dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
@ -2161,12 +2132,6 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "3.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.22.11"
|
||||
@ -2832,7 +2797,6 @@ dependencies = [
|
||||
"byteorder-lite",
|
||||
"num-traits",
|
||||
"png",
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2936,12 +2900,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.76"
|
||||
@ -4438,17 +4396,6 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"jpeg-decoder",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
@ -5007,12 +4954,6 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
||||
|
||||
[[package]]
|
||||
name = "wgpu"
|
||||
version = "22.1.0"
|
||||
@ -5427,15 +5368,6 @@ dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
|
24
Cargo.toml
24
Cargo.toml
@ -17,10 +17,6 @@ path = "src/lib.rs"
|
||||
name = "forestiles"
|
||||
|
||||
[dependencies]
|
||||
# bevy = { version = "0.15", default-features = false, features = [
|
||||
# "bevy_color","bevy_core_pipeline","bevy_render","bevy_winit","bevy_window","multi_threaded","wayland","bevy_sprite",
|
||||
# "bevy_picking","bevy_mesh_picking_backend","bevy_ui_picking_backend","bevy_sprite_picking_backend"
|
||||
# ]}
|
||||
bevy = { version = "0.15", default-features = false, features = [
|
||||
"android-native-activity",
|
||||
"android_shared_stdcxx",
|
||||
@ -57,20 +53,16 @@ bevy = { version = "0.15", default-features = false, features = [
|
||||
"webgl2",
|
||||
"wayland",
|
||||
]}
|
||||
bevy-inspector-egui = { version = "0.28" }
|
||||
# winit = { version = "0.30", features = ["rwh_05", "android-native-activity"] }
|
||||
# env_logger = "0.11"
|
||||
bevy-inspector-egui = { version = "0.28", default-features = false, features = [
|
||||
"bevy_pbr",
|
||||
"bevy_image",
|
||||
"bevy_render",
|
||||
"egui_open_url"
|
||||
]}
|
||||
log = "0.4"
|
||||
# wgpu = "22.1"
|
||||
# cfg-if = "1"
|
||||
# pollster = "0.3"
|
||||
# bytemuck = { version = "1.18", features = [ "derive" ] }
|
||||
rand = { version = "0.8", features = ["small_rng"] }
|
||||
# nalgebra = "0.33"
|
||||
voronoice = "0.2"
|
||||
noise = "0.9"
|
||||
# lazy_static = "1.5"
|
||||
# image = { version = "0.25", default-features = false, features = ["rayon", "png"]}
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
console_error_panic_hook = "0.1.6"
|
||||
@ -99,9 +91,9 @@ opt-level = 3
|
||||
[package.metadata.android]
|
||||
package = "org.forestiles.example"
|
||||
apk_name = "forestiles"
|
||||
# strip = "strip"
|
||||
strip = "strip"
|
||||
# see https://github.com/rust-mobile/cargo-apk
|
||||
# assets = "assets"
|
||||
assets = "assets"
|
||||
build_targets = ["aarch64-linux-android", "armv7-linux-androideabi"]
|
||||
|
||||
[package.metadata.android.sdk]
|
||||
|
@ -49,6 +49,7 @@ fn move_cam(
|
||||
// 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, _, _)| {
|
||||
acc + (old_pos/pressed_on_map.len() as f32)
|
||||
});
|
||||
@ -70,4 +71,8 @@ fn move_cam(
|
||||
cam.scale.x /= zoom;
|
||||
cam.scale.y /= zoom;
|
||||
}
|
||||
|
||||
for (_, new_pos, _, id, _) in ps {
|
||||
pointers.0.insert(*id, new_pos);
|
||||
}
|
||||
}
|
331
src/graphics.rs
331
src/graphics.rs
@ -1,331 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use wgpu::{include_wgsl, util::DeviceExt, BindGroup, Buffer, Device, Queue, RenderPipeline, Surface, SurfaceConfiguration, VertexBufferLayout};
|
||||
use winit::{event::WindowEvent, window::Window};
|
||||
|
||||
use crate::state::State;
|
||||
|
||||
mod texture;
|
||||
use texture::{Texture, TEXTURE_DIMENSIONS};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Zeroable, Pod, Debug)]
|
||||
pub struct Vertex {
|
||||
pub pos: [f32; 2],
|
||||
/// Rgba color by default but if texture flag is not set.
|
||||
/// Else the first 2 f32 are texture coordinates and the 2 last are not used
|
||||
pub color: [f32; 4],
|
||||
/// Each bit is used as a flag :
|
||||
///
|
||||
/// 1: Scaled and moved according to camera
|
||||
///
|
||||
/// 2: Grayscale
|
||||
///
|
||||
/// 4: Texture instead of color
|
||||
///
|
||||
/// For example 0b001 corresponds to Scaled and 0b011 to Scaled and Grayscale
|
||||
pub effect: u32,
|
||||
}
|
||||
impl Vertex {
|
||||
const DESC: VertexBufferLayout<'static> = VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x4, 2 => Uint32],
|
||||
};
|
||||
pub const fn new_col(pos: [f32; 2], color: [f32; 4], effect: u32) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
color,
|
||||
effect
|
||||
}
|
||||
}
|
||||
pub const fn new_tex(pos: [f32; 2], tex_pos: [u16; 2], effect: u32) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
color: [tex_pos[0] as f32/TEXTURE_DIMENSIONS[0] as f32, tex_pos[1] as f32/TEXTURE_DIMENSIONS[1] as f32, 0., 0.],
|
||||
effect: effect | 0b100
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Zeroable, Pod, Debug)]
|
||||
pub struct Uniforms {
|
||||
pub camera: [f32; 2],
|
||||
pub zooms: [f32; 2]
|
||||
}
|
||||
impl Default for Uniforms {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
camera: [0., 0.],
|
||||
zooms: [1., 1.]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Graphics<'a> {
|
||||
// window: &'a Window,
|
||||
pub surface_config: SurfaceConfiguration,
|
||||
surface: Surface<'a>,
|
||||
device: Device,
|
||||
render_pipeline: RenderPipeline,
|
||||
queue: Queue,
|
||||
vertex_buf: Buffer,
|
||||
index_buf: Buffer,
|
||||
uniforms_buf: Buffer,
|
||||
uniforms_bind_group: BindGroup,
|
||||
diffuse_bind_group: BindGroup,
|
||||
}
|
||||
impl<'a> Graphics<'a> {
|
||||
pub async fn init(state: &State, window: Arc<Window>) -> Self {
|
||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||
backends: wgpu::Backends::PRIMARY,
|
||||
dx12_shader_compiler: Default::default(),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let surface = instance.create_surface(window).unwrap();
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::default(),
|
||||
force_fallback_adapter: false,
|
||||
// Request an adapter which can render to our surface
|
||||
compatible_surface: Some(&surface),
|
||||
})
|
||||
.await
|
||||
.expect("Failed to find an appropriate adapter");
|
||||
|
||||
// Create the logical device and command queue
|
||||
let (device, queue) = adapter
|
||||
.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: None,
|
||||
required_features: wgpu::Features::empty(),
|
||||
// Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain.
|
||||
required_limits: wgpu::Limits::default()
|
||||
.using_resolution(adapter.limits()),
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.expect("Failed to create device");
|
||||
|
||||
let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Vertex Buffer"),
|
||||
contents: &[bytemuck::cast_slice::<Vertex, _>(&state.vertices), &[0; 100000]].concat(),
|
||||
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
|
||||
let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Index Buffer"),
|
||||
contents: &[bytemuck::cast_slice::<u32, _>(&state.indices), &[0; 100000]].concat(),
|
||||
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
|
||||
let uniforms_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Uniforms Buffer"),
|
||||
contents: bytemuck::cast_slice(&[state.uniforms]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
let uniforms_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}
|
||||
],
|
||||
label: Some("Uniforms Bind Group Layout"),
|
||||
});
|
||||
let uniforms_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &uniforms_bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: uniforms_buf.as_entire_binding(),
|
||||
}
|
||||
],
|
||||
label: Some("Uniforms Bind Group"),
|
||||
});
|
||||
let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
multisampled: false,
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
// This should match the filterable field of the
|
||||
// corresponding Texture entry above.
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
label: Some("texture_bind_group_layout"),
|
||||
});
|
||||
|
||||
// Load the shaders from disk
|
||||
let shader = device.create_shader_module(include_wgsl!("shader.wgsl"));
|
||||
|
||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: None,
|
||||
bind_group_layouts: &[
|
||||
&uniforms_bind_group_layout,
|
||||
&texture_bind_group_layout
|
||||
],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let swapchain_capabilities = surface.get_capabilities(&adapter);
|
||||
let swapchain_format = swapchain_capabilities.formats[0];
|
||||
|
||||
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: None,
|
||||
layout: Some(&pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[
|
||||
Vertex::DESC
|
||||
],
|
||||
compilation_options: Default::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
compilation_options: Default::default(),
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: swapchain_format,
|
||||
blend: Some(wgpu::BlendState {
|
||||
color: wgpu::BlendComponent {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add
|
||||
},
|
||||
alpha: wgpu::BlendComponent {
|
||||
src_factor: wgpu::BlendFactor::One,
|
||||
dst_factor: wgpu::BlendFactor::One,
|
||||
operation: wgpu::BlendOperation::Add
|
||||
}
|
||||
}),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState::default(),
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
let surface_config = surface
|
||||
.get_default_config(&adapter, 1, 1)
|
||||
.unwrap();
|
||||
surface.configure(&device, &surface_config);
|
||||
|
||||
let diffuse_bytes = include_bytes!("../assets/texture.png");
|
||||
let diffuse_texture = Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png");
|
||||
let diffuse_bind_group = device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
layout: &texture_bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(&diffuse_texture.view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
|
||||
}
|
||||
],
|
||||
label: Some("diffuse_bind_group"),
|
||||
}
|
||||
);
|
||||
|
||||
Self {
|
||||
// window,
|
||||
surface_config,
|
||||
surface,
|
||||
device,
|
||||
render_pipeline,
|
||||
queue,
|
||||
vertex_buf,
|
||||
index_buf,
|
||||
uniforms_buf,
|
||||
uniforms_bind_group,
|
||||
diffuse_bind_group
|
||||
}
|
||||
}
|
||||
pub fn window_event(&mut self, event: &WindowEvent, window: &Window) {
|
||||
match event {
|
||||
WindowEvent::Resized(new_size) => {
|
||||
// Reconfigure the surface with the new size
|
||||
self.surface_config.width = new_size.width;
|
||||
self.surface_config.height = new_size.height;
|
||||
self.surface.configure(&self.device, &self.surface_config);
|
||||
// On macos the window needs to be redrawn manually after resizing
|
||||
window.request_redraw();
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
pub fn render(&self, state: &State) {
|
||||
let frame = self.surface
|
||||
.get_current_texture()
|
||||
.expect("Failed to acquire next swap chain texture");
|
||||
let view = frame
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder =
|
||||
self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: None,
|
||||
});
|
||||
{
|
||||
let mut rpass =
|
||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: None,
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
rpass.set_pipeline(&self.render_pipeline);
|
||||
rpass.set_bind_group(0, &self.uniforms_bind_group, &[]);
|
||||
rpass.set_bind_group(1, &self.diffuse_bind_group, &[]);
|
||||
rpass.set_vertex_buffer(0, self.vertex_buf.slice(..));
|
||||
rpass.set_index_buffer(self.index_buf.slice(..), wgpu::IndexFormat::Uint32);
|
||||
rpass.draw_indexed(0..state.indices.len() as u32, 0, 0..1);
|
||||
// rpass.draw(0..self.state.vertices.len() as u32, 0..1);
|
||||
}
|
||||
|
||||
self.queue.submit(Some(encoder.finish()));
|
||||
frame.present();
|
||||
}
|
||||
pub fn update(&mut self, state: &State) {
|
||||
self.queue.write_buffer(&self.vertex_buf, 0, bytemuck::cast_slice(&state.vertices));
|
||||
self.queue.write_buffer(&self.index_buf, 0, bytemuck::cast_slice(&state.indices));
|
||||
self.queue.write_buffer(&self.uniforms_buf, 0, bytemuck::cast_slice(&[state.uniforms]));
|
||||
}
|
||||
}
|
||||
|
@ -1,80 +0,0 @@
|
||||
use image::GenericImageView;
|
||||
|
||||
pub const TEXTURE_DIMENSIONS: [usize; 2] = [64, 64];
|
||||
|
||||
pub struct Texture {
|
||||
pub texture: wgpu::Texture,
|
||||
pub view: wgpu::TextureView,
|
||||
pub sampler: wgpu::Sampler,
|
||||
}
|
||||
|
||||
impl Texture {
|
||||
pub fn from_bytes(
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
bytes: &[u8],
|
||||
label: &str
|
||||
) -> Self {
|
||||
let img = image::load_from_memory(bytes).unwrap();
|
||||
Self::from_image(device, queue, &img, Some(label))
|
||||
}
|
||||
|
||||
pub fn from_image(
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
img: &image::DynamicImage,
|
||||
label: Option<&str>
|
||||
) -> Self {
|
||||
let rgba = img.to_rgba8();
|
||||
let dimensions = img.dimensions();
|
||||
|
||||
let size = wgpu::Extent3d {
|
||||
width: dimensions.0,
|
||||
height: dimensions.1,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
let texture = device.create_texture(
|
||||
&wgpu::TextureDescriptor {
|
||||
label,
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
view_formats: &[],
|
||||
}
|
||||
);
|
||||
|
||||
queue.write_texture(
|
||||
wgpu::ImageCopyTexture {
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
texture: &texture,
|
||||
mip_level: 0,
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
},
|
||||
&rgba,
|
||||
wgpu::ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(4 * dimensions.0),
|
||||
rows_per_image: Some(dimensions.1),
|
||||
},
|
||||
size,
|
||||
);
|
||||
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let sampler = device.create_sampler(
|
||||
&wgpu::SamplerDescriptor {
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Nearest,
|
||||
min_filter: wgpu::FilterMode::Nearest,
|
||||
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
Self { texture, view, sampler }
|
||||
}
|
||||
}
|
127
src/lib.rs
127
src/lib.rs
@ -1,109 +1,12 @@
|
||||
// mod graphics;
|
||||
// mod state;
|
||||
use bevy::{prelude::*, diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}};
|
||||
use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
||||
|
||||
pub mod map;
|
||||
pub mod camera;
|
||||
pub mod ui;
|
||||
|
||||
use std::{fmt::Debug, sync::Arc};
|
||||
|
||||
use log::debug;
|
||||
// use state::State;
|
||||
// use graphics::Graphics;
|
||||
// use winit::{application::ApplicationHandler, dpi::PhysicalSize, event::{Event, WindowEvent}, event_loop::EventLoop, window::{Window, WindowAttributes}};
|
||||
use bevy::{prelude::*, diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}};
|
||||
use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
||||
|
||||
pub fn dbg<V: Debug>(v: V) -> V {
|
||||
debug!(target: "app", "{:?}", v);
|
||||
v
|
||||
}
|
||||
|
||||
// struct App<'a> {
|
||||
// // event_loop: EventLoop<()>,
|
||||
// window: Option<Arc<Window>>,
|
||||
// graphics: Option<Graphics<'a>>,
|
||||
// state: State
|
||||
// }
|
||||
// impl App<'_> {
|
||||
// fn new() -> Self {
|
||||
// Self {
|
||||
// window: None,
|
||||
// graphics: None,
|
||||
// state: State::new()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl ApplicationHandler for App<'_> {
|
||||
// fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
// #[cfg(not(any(target_family = "wasm", target_os = "android")))]
|
||||
// let window = event_loop.create_window(
|
||||
// WindowAttributes::default()
|
||||
// .with_inner_size(PhysicalSize::new(1080*2/5, 2000*2/5))
|
||||
// ).unwrap();
|
||||
// #[cfg(target_os = "android")]
|
||||
// let window = event_loop.create_window(
|
||||
// WindowAttributes::default()
|
||||
// // .with_inner_size(PhysicalSize::new(1080*2/5, 2000*2/5))
|
||||
// ).unwrap();
|
||||
// self.window = Some(Arc::new(window));
|
||||
// self.graphics = Some(pollster::block_on(Graphics::init(&self.state, self.window.clone().unwrap())));
|
||||
// }
|
||||
// fn window_event(
|
||||
// &mut self,
|
||||
// event_loop: &winit::event_loop::ActiveEventLoop,
|
||||
// window_id: winit::window::WindowId,
|
||||
// event: WindowEvent,
|
||||
// ) {
|
||||
// match &event {
|
||||
// WindowEvent::CloseRequested => event_loop.exit(),
|
||||
// WindowEvent::RedrawRequested => {
|
||||
// if let Some(g) = &mut self.graphics {
|
||||
// self.state.update_if_needed();
|
||||
// self.state.render(self.window.as_ref().unwrap().inner_size());
|
||||
// g.update(&self.state);
|
||||
// g.render(&self.state);
|
||||
// }
|
||||
// },
|
||||
// WindowEvent::MouseWheel { delta, .. } => {
|
||||
// dbg!(delta);
|
||||
// },
|
||||
// _ => {}
|
||||
// }
|
||||
// self.graphics.as_mut().unwrap().window_event(&event, &self.window.as_ref().unwrap());
|
||||
// self.state.window_event(&event, &self.window.as_ref().unwrap());
|
||||
// }
|
||||
// fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
// if let Some(window) = self.window.as_ref() {
|
||||
// window.request_redraw();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// #[cfg(not(target_os = "android"))]
|
||||
#[bevy_main]
|
||||
pub fn main() {
|
||||
|
||||
// #[cfg(target_family = "wasm")]
|
||||
// let (event_loop, window) = {
|
||||
// use winit::platform::web::WindowExtWebSys;
|
||||
// std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
// console_log::init().expect("could not initialize logger");
|
||||
|
||||
// let event_loop = winit::event_loop::EventLoop::new().unwrap();
|
||||
// let window = winit::window::WindowBuilder::new().build(&event_loop).unwrap();
|
||||
|
||||
// web_sys::window()
|
||||
// .unwrap()
|
||||
// .document()
|
||||
// .unwrap()
|
||||
// .body()
|
||||
// .unwrap()
|
||||
// .append_child(&window.canvas().unwrap())
|
||||
// .unwrap();
|
||||
// (event_loop, window)
|
||||
// };
|
||||
|
||||
App::new()
|
||||
.add_plugins((
|
||||
DefaultPlugins,
|
||||
@ -118,26 +21,4 @@ pub fn main() {
|
||||
}
|
||||
))
|
||||
.run();
|
||||
}
|
||||
|
||||
// #[cfg(target_os = "android")]
|
||||
// #[no_mangle]
|
||||
// fn android_main(app: winit::platform::android::activity::AndroidApp) {
|
||||
// println!("test");
|
||||
// // use winit::platform::android::{EventLoopBuilderExtAndroid, activity::WindowManagerFlags};
|
||||
|
||||
// // android_logger::init_once(
|
||||
// // android_logger::Config::default()
|
||||
// // .with_max_level(log::LevelFilter::Debug)
|
||||
// // .with_filter(android_logger::FilterBuilder::new().parse("app").build())
|
||||
// // );
|
||||
|
||||
// // app.set_window_flags(WindowManagerFlags::KEEP_SCREEN_ON | WindowManagerFlags::FULLSCREEN, WindowManagerFlags::empty());
|
||||
|
||||
// // let event_loop = winit::event_loop::EventLoopBuilder::new()
|
||||
// // .with_android_app(app)
|
||||
// // .build()
|
||||
// // .unwrap();
|
||||
|
||||
// // event_loop.run_app(&mut App::new()).unwrap();
|
||||
// }
|
||||
}
|
@ -85,8 +85,8 @@ fn setup(
|
||||
let mut colors = Vec::new();
|
||||
let mut indices = Vec::new();
|
||||
|
||||
for (c, mut cd) in voronoi.iter_cells().zip(cells_data.iter_mut()).filter(|(_,cd)| cd.kind != CellKind::Forest) {
|
||||
let mut color = cd.color();
|
||||
for (c, cd) in voronoi.iter_cells().zip(cells_data.iter_mut()).filter(|(_,cd)| cd.kind != CellKind::Forest) {
|
||||
let color = cd.color();
|
||||
// if c.site() == selected_tile {
|
||||
// color[0] = (color[0]+0.4).clamp(0., 1.);
|
||||
// color[1] = (color[1]+0.4).clamp(0., 1.);
|
||||
|
@ -1,52 +0,0 @@
|
||||
struct Uniforms {
|
||||
camera: vec2f,
|
||||
zooms: vec2f
|
||||
}
|
||||
@group(0) @binding(0) var<uniform> uniforms : Uniforms;
|
||||
|
||||
struct VertexOutput {
|
||||
@location(0) color: vec4f,
|
||||
@location(1) effect: u32,
|
||||
@builtin(position) pos: vec4f
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
@location(0) pos: vec2f,
|
||||
@location(1) color: vec4f,
|
||||
@location(2) effect: u32
|
||||
) -> VertexOutput {
|
||||
var screen_pos: vec4f;
|
||||
if (effect & 1) == 0 {
|
||||
screen_pos = vec4f(pos, 0, 1);
|
||||
} else {
|
||||
screen_pos = vec4f((pos - uniforms.camera) * uniforms.zooms, 0, 1);
|
||||
}
|
||||
var out = VertexOutput(
|
||||
color,
|
||||
effect,
|
||||
screen_pos
|
||||
);
|
||||
return out;
|
||||
}
|
||||
|
||||
@group(1) @binding(0)
|
||||
var t_diffuse: texture_2d<f32>;
|
||||
@group(1) @binding(1)
|
||||
var s_diffuse: sampler;
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
||||
var color: vec4f;
|
||||
if (in.effect & 4) == 0 {
|
||||
color = in.color;
|
||||
} else {
|
||||
color = textureSample(t_diffuse, s_diffuse, in.color.xy);
|
||||
}
|
||||
// Grayscale
|
||||
if (in.effect & 2) != 0 {
|
||||
var v = (color.r*0.299) + (color.g*0.587) + (color.b*0.114);
|
||||
color = vec4f(v, v, v, color.a);
|
||||
}
|
||||
return color;
|
||||
}
|
368
src/state.rs
368
src/state.rs
@ -1,368 +0,0 @@
|
||||
use std::{collections::{BTreeMap, HashMap}, time::{Duration, Instant}};
|
||||
use log::{debug, trace};
|
||||
use map::{CellKind, Map};
|
||||
use rand::prelude::*;
|
||||
use voronoice::Point;
|
||||
use winit::{dpi::{PhysicalPosition, PhysicalSize}, event::{DeviceEvent, Event, KeyEvent, MouseButton, MouseScrollDelta, Touch as WTouch, TouchPhase, WindowEvent}, keyboard::{KeyCode, PhysicalKey}, window::Window};
|
||||
|
||||
use crate::{dbg, graphics::{Uniforms, Vertex}};
|
||||
|
||||
mod entity;
|
||||
mod map;
|
||||
mod ui;
|
||||
use entity::{Entity, EntityKind, ExternOp};
|
||||
use ui::{Kind, UI};
|
||||
|
||||
fn rgba_to_grayscale(c: [f32; 4]) -> [f32; 4] {
|
||||
let v = (c[0]*0.299) + (c[1]*0.587) + (c[2]*0.114);
|
||||
[v, v, v, c[3]]
|
||||
}
|
||||
|
||||
struct Touch {
|
||||
pub pos: PhysicalPosition<f64>,
|
||||
/// id=1000 is for mouse
|
||||
pub id: u64,
|
||||
pub start: Instant
|
||||
}
|
||||
impl PartialEq for Touch {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
impl From<WTouch> for Touch {
|
||||
fn from(value: WTouch) -> Self {
|
||||
Self::new(value.location, value.id)
|
||||
}
|
||||
}
|
||||
impl Touch {
|
||||
pub fn new(pos: PhysicalPosition<f64>, id: u64) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
id,
|
||||
start: Instant::now()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
pub vertices: Vec<Vertex>,
|
||||
pub indices: Vec<u32>,
|
||||
pub uniforms: Uniforms,
|
||||
zoom: f32,
|
||||
map: Map,
|
||||
ui: UI,
|
||||
start: Instant,
|
||||
last_frame: Instant,
|
||||
t: usize, // Time in frames
|
||||
selected_tile: usize,
|
||||
framerate: f32, // Update per second
|
||||
pub entities: BTreeMap<usize, Entity>, // entity id --> Entities
|
||||
next_eid: usize,
|
||||
pub cells_entities: HashMap<usize, Vec<usize>>, // cell id --> entities id
|
||||
/// Also acount for mouse
|
||||
touches: Vec<Touch>,
|
||||
mouse_pos: Option<PhysicalPosition<f64>>
|
||||
}
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
let mut s = Self {
|
||||
vertices: vec![],
|
||||
indices: vec![],
|
||||
uniforms: Uniforms::default(),
|
||||
zoom: 1.,
|
||||
start: Instant::now(),
|
||||
last_frame: Instant::now(),
|
||||
t: 0,
|
||||
map: Map::new(0, 0),
|
||||
ui: UI::new(),
|
||||
selected_tile: 0,
|
||||
framerate: 1.,
|
||||
entities: BTreeMap::new(),
|
||||
next_eid: 0,
|
||||
cells_entities: HashMap::new(),
|
||||
touches: Vec::new(),
|
||||
mouse_pos: None
|
||||
};
|
||||
// Create vertices / indices to estimate vertex / index buffer size
|
||||
s.render(PhysicalSize::new(1, 1));
|
||||
s
|
||||
}
|
||||
fn set_zoom(&mut self, v: f32) {
|
||||
self.zoom = v.clamp(1., 10.);
|
||||
}
|
||||
fn update_zooms(&mut self, screen_size: PhysicalSize<u32>) {
|
||||
self.uniforms.zooms = [
|
||||
(screen_size.height as f32 / screen_size.width as f32).max(1.) * self.zoom,
|
||||
(screen_size.width as f32 / screen_size.height as f32).max(1.) * self.zoom
|
||||
];
|
||||
}
|
||||
fn handle_click(&mut self, window: &Window, pos: &PhysicalPosition<f64>) {
|
||||
|
||||
}
|
||||
pub fn window_event(&mut self, event: &WindowEvent, window: &Window) {
|
||||
match event {
|
||||
WindowEvent::Touch(touch) => {
|
||||
match touch.phase {
|
||||
TouchPhase::Started => {
|
||||
self.touches.push((*touch).into());
|
||||
},
|
||||
TouchPhase::Moved => {
|
||||
let w_size = window.inner_size();
|
||||
let old_touch_n = self.touches.iter().position(|t| t.id == touch.id).unwrap();
|
||||
let old_touch = &self.touches[old_touch_n];
|
||||
let t = [
|
||||
touch.location.x as f32/w_size.width as f32 * Map::WIDTH / self.uniforms.zooms[0],
|
||||
touch.location.y as f32/w_size.height as f32 * Map::HEIGHT / self.uniforms.zooms[1]
|
||||
];
|
||||
let old_t = [
|
||||
old_touch.pos.x as f32/w_size.width as f32 * Map::WIDTH / self.uniforms.zooms[0],
|
||||
old_touch.pos.y as f32/w_size.height as f32 * Map::HEIGHT / self.uniforms.zooms[1]
|
||||
];
|
||||
// Handle pinch zoom
|
||||
if self.touches.len() == 2 {
|
||||
let old_touch2 = &self.touches[if old_touch_n == 0 {1} else {0}];
|
||||
let old_t2 = [
|
||||
old_touch2.pos.x as f32/w_size.width as f32 * Map::WIDTH / self.uniforms.zooms[0],
|
||||
old_touch2.pos.y as f32/w_size.height as f32 * Map::HEIGHT / self.uniforms.zooms[1]
|
||||
];
|
||||
self.set_zoom(self.zoom + (((((t[0]-old_t2[0])*self.zoom).powi(2)+((t[0]-old_t2[0])*self.zoom).powi(2)).sqrt() - (((old_t[0]-old_t2[0])*self.zoom).powi(2)+((old_t[0]-old_t2[0])*self.zoom).powi(2)).sqrt())*4.));
|
||||
}
|
||||
self.uniforms.camera[0] -= (t[0] - old_t[0]) / self.touches.len() as f32;
|
||||
self.uniforms.camera[1] += (t[1] - old_t[1]) / self.touches.len() as f32;
|
||||
dbg(&self.uniforms.camera);
|
||||
self.touches[old_touch_n].pos = touch.location;
|
||||
},
|
||||
TouchPhase::Ended => {
|
||||
let old_touch_n = self.touches.iter().position(|t| t.id == touch.id).unwrap();
|
||||
let old_touch = &self.touches[old_touch_n];
|
||||
if old_touch.start.elapsed() < Duration::from_millis(500) {
|
||||
self.handle_click(window, &touch.location);
|
||||
}
|
||||
self.touches.remove(self.touches.iter().position(|t| t.id == touch.id).unwrap());
|
||||
},
|
||||
TouchPhase::Cancelled => {
|
||||
self.touches.remove(self.touches.iter().position(|t| t.id == touch.id).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowEvent::MouseInput { state, button, ..} => {
|
||||
if state.is_pressed() {
|
||||
match button {
|
||||
MouseButton::Left => {
|
||||
if !self.touches.iter().any(|t| t.id == 1000) {
|
||||
if let Some(pos) = self.mouse_pos {
|
||||
self.touches.push(Touch::new(pos, 1000));
|
||||
}
|
||||
}
|
||||
},
|
||||
MouseButton::Right => {
|
||||
self.spawn_entity(self.selected_tile, Entity::new(EntityKind::Horse, self.selected_tile));
|
||||
},
|
||||
_ => {}
|
||||
};
|
||||
} else {
|
||||
match button {
|
||||
MouseButton::Left => {
|
||||
if let Some(i) = self.touches.iter().position(|t| t.id == 1000) {
|
||||
self.touches.remove(i);
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
},
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
self.mouse_pos = None;
|
||||
if let Some(i) = self.touches.iter().position(|t| t.id == 1000) {
|
||||
self.touches.remove(i);
|
||||
}
|
||||
},
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
self.mouse_pos = Some(*position);
|
||||
let w_size = window.inner_size();
|
||||
self.update_zooms(w_size);
|
||||
let pos = Point {
|
||||
x: (((position.x / w_size.width as f64)*2.)-1.)/(self.uniforms.zooms[0] as f64) + self.uniforms.camera[0] as f64,
|
||||
y: -(((position.y / w_size.height as f64)*2.)-1.)/(self.uniforms.zooms[1] as f64) + self.uniforms.camera[1] as f64
|
||||
};
|
||||
let c = self.map.voronoi.cell(self.selected_tile);
|
||||
if let Some(i) = self.touches.iter().position(|t| t.id == 1000) {
|
||||
self.touches[i].pos = *position;
|
||||
for i in c.iter_path(pos.clone()) {
|
||||
self.selected_tile = i;
|
||||
let cd = &mut self.map.cells_data[self.selected_tile];
|
||||
if let CellKind::Dirt = cd.kind {
|
||||
cd.kind = CellKind::Forest;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.selected_tile = c.iter_path(pos).last().unwrap();
|
||||
}
|
||||
},
|
||||
WindowEvent::KeyboardInput { event: KeyEvent { physical_key: PhysicalKey::Code(kc), state, .. }, .. } => {
|
||||
if state.is_pressed() {
|
||||
match kc {
|
||||
KeyCode::KeyW => {
|
||||
self.uniforms.camera[1] += 0.1 * self.zoom;
|
||||
},
|
||||
KeyCode::KeyS => {
|
||||
self.uniforms.camera[1] -= 0.1 / self.zoom;
|
||||
},
|
||||
KeyCode::KeyA => {
|
||||
self.uniforms.camera[0] -= 0.1 / self.zoom;
|
||||
},
|
||||
KeyCode::KeyD => {
|
||||
self.uniforms.camera[0] += 0.1 / self.zoom;
|
||||
},
|
||||
KeyCode::KeyR => {
|
||||
self.set_zoom(self.zoom + 0.1);
|
||||
},
|
||||
KeyCode::KeyF => {
|
||||
self.set_zoom(self.zoom - 0.1);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
self.update_zooms(window.inner_size());
|
||||
}
|
||||
},
|
||||
WindowEvent::MouseWheel { delta, .. } => {
|
||||
self.framerate -= match delta {
|
||||
MouseScrollDelta::PixelDelta(pos) => pos.y as f32,
|
||||
MouseScrollDelta::LineDelta(_, y) => *y
|
||||
} * 0.1;
|
||||
self.framerate = self.framerate.max(0.);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
pub fn render(&mut self, screen_size: PhysicalSize<u32>) {
|
||||
trace!("render");
|
||||
self.update_zooms(screen_size);
|
||||
self.vertices = Vec::new();
|
||||
self.indices = Vec::new();
|
||||
|
||||
for (c, cd) in self.map.voronoi.iter_cells().zip(self.map.cells_data.iter()).filter(|(_,cd)| cd.kind != CellKind::Forest) {
|
||||
let mut color = cd.color();
|
||||
if c.site() == self.selected_tile {
|
||||
color[0] = (color[0]+0.4).clamp(0., 1.);
|
||||
color[1] = (color[1]+0.4).clamp(0., 1.);
|
||||
color[2] = (color[2]+0.4).clamp(0., 1.);
|
||||
}
|
||||
let vs = c.iter_vertices().collect::<Vec<_>>();
|
||||
let i = self.vertices.len() as u32;
|
||||
for v in vs.iter() {
|
||||
self.vertices.push(Vertex::new_col([v.x as f32, v.y as f32], color, 1));
|
||||
}
|
||||
for v in 1..(vs.len()-1) as u32 {
|
||||
self.indices.push(i);
|
||||
self.indices.push(i+v);
|
||||
self.indices.push(i+v+1);
|
||||
}
|
||||
}
|
||||
|
||||
for e in self.entities.values() {
|
||||
e.render(&mut self.vertices, &mut self.indices, &self.map, );
|
||||
}
|
||||
|
||||
for (c, cd) in self.map.voronoi.iter_cells().zip(self.map.cells_data.iter()).filter(|(_,cd)| cd.kind == CellKind::Forest) {
|
||||
let mut color = cd.color();
|
||||
if c.site() == self.selected_tile {
|
||||
color[0] = (color[0]+0.4).clamp(0., 1.);
|
||||
color[1] = (color[1]+0.4).clamp(0., 1.);
|
||||
color[2] = (color[2]+0.4).clamp(0., 1.);
|
||||
}
|
||||
let vs = c.iter_vertices().collect::<Vec<_>>();
|
||||
let i = self.vertices.len() as u32;
|
||||
for v in vs.iter() {
|
||||
self.vertices.push(Vertex::new_col([v.x as f32, v.y as f32], color, 1));
|
||||
}
|
||||
for v in 1..(vs.len()-1) as u32 {
|
||||
self.indices.push(i);
|
||||
self.indices.push(i+v);
|
||||
self.indices.push(i+v+1);
|
||||
}
|
||||
}
|
||||
|
||||
self.ui.render(&mut self.vertices, &mut self.indices, screen_size);
|
||||
}
|
||||
pub fn update_if_needed(&mut self) {
|
||||
while self.last_frame.elapsed().as_secs_f32() > 1. / self.framerate {
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
pub fn update(&mut self) {
|
||||
trace!("update");
|
||||
|
||||
self.last_frame = Instant::now();
|
||||
let mut rng = thread_rng();
|
||||
let mut new_kind = Vec::new();
|
||||
for cd in self.map.cells_data.iter_mut() {
|
||||
cd.update();
|
||||
}
|
||||
for cd in self.map.cells_data.iter() {
|
||||
if cd.kind == CellKind::Forest || cd.kind == CellKind::Grass {
|
||||
let r = rng.gen::<f32>();
|
||||
if r < (0.035*cd.moisture) {
|
||||
let c = self.map.voronoi.cell(cd.cid);
|
||||
let n = c.iter_neighbors().choose(&mut rng).unwrap();
|
||||
let k = if r < (0.005*cd.moisture) && (self.map.cells_data[n].kind == CellKind::Dirt || self.map.cells_data[n].kind == CellKind::Grass) && cd.kind == CellKind::Forest {
|
||||
Some(CellKind::Forest)
|
||||
} else if self.map.cells_data[n].kind == CellKind::Dirt {
|
||||
Some(CellKind::Grass)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(k) = k {
|
||||
new_kind.push((n, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (n, k) in new_kind {
|
||||
let cd = &mut self.map.cells_data[n];
|
||||
cd.kind = k;
|
||||
cd.resource = 1.;
|
||||
}
|
||||
|
||||
// if Option is None remove the entity
|
||||
let mut entities_to_move: Vec<(usize, Option<usize>)> = Vec::new();
|
||||
for (eid, e) in self.entities.iter_mut() {
|
||||
match e.update(&mut self.map, self.t, &mut rng) {
|
||||
Some(ExternOp::Move(cid)) => {
|
||||
entities_to_move.push((*eid, Some(cid)));
|
||||
},
|
||||
Some(ExternOp::Remove) => {
|
||||
entities_to_move.push((*eid, None));
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
for (eid, new_cid) in entities_to_move {
|
||||
let entity = self.entities.get_mut(&eid).unwrap();
|
||||
let cell_entities = self.cells_entities.get_mut(&entity.cid).unwrap();
|
||||
cell_entities.remove(cell_entities.iter().position(|e| *e == eid).unwrap());
|
||||
match new_cid {
|
||||
Some(new_cid) => {
|
||||
entity.cid = new_cid;
|
||||
match self.cells_entities.get_mut(&new_cid) {
|
||||
Some(v) => v.push(eid),
|
||||
None => {self.cells_entities.insert(new_cid, vec![eid]);}
|
||||
}
|
||||
},
|
||||
None => {self.entities.remove(&eid);}
|
||||
}
|
||||
}
|
||||
|
||||
self.t += 1;
|
||||
}
|
||||
pub fn spawn_entity(&mut self, cid: usize, e: Entity) {
|
||||
let eid = self.next_eid;
|
||||
self.next_eid += 1;
|
||||
self.entities.insert(eid, e);
|
||||
if let Some(v) = self.cells_entities.get_mut(&cid) {
|
||||
v.push(eid);
|
||||
} else {
|
||||
self.cells_entities.insert(cid, vec![eid]);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,221 +0,0 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use rand::{rngs::ThreadRng, seq::IteratorRandom, Rng};
|
||||
|
||||
use crate::graphics::Vertex;
|
||||
|
||||
use super::{map::CellKind, Map};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EntityKind {
|
||||
Horse
|
||||
}
|
||||
impl EntityKind {
|
||||
const fn is_herbivore(&self) -> bool {
|
||||
match self {
|
||||
Self::Horse => true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EntityState {
|
||||
Walking(Instant), // Start of walk
|
||||
Resting
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ExternOp {
|
||||
Remove,
|
||||
Move(usize)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Entity {
|
||||
pub cid: usize,
|
||||
kind: EntityKind,
|
||||
start: Instant,
|
||||
state: EntityState,
|
||||
health: f32 // between 0 and 1
|
||||
}
|
||||
impl Entity {
|
||||
pub fn new(kind: EntityKind, cid: usize, ) -> Self {
|
||||
Self {
|
||||
cid,
|
||||
kind,
|
||||
start: Instant::now(),
|
||||
state: EntityState::Walking(Instant::now()),
|
||||
health: 1.
|
||||
}
|
||||
}
|
||||
pub fn set_health(&mut self, val: f32) {
|
||||
self.health = val.clamp(0., 1.);
|
||||
}
|
||||
pub fn render(&self, vertices: &mut Vec<Vertex>, indices: &mut Vec<u32>, map: &Map) {
|
||||
let pos = &map.voronoi.sites()[self.cid];
|
||||
match self.kind {
|
||||
EntityKind::Horse => {
|
||||
let color = [171./255. * self.health, 122./255. * self.health, 50./255. * self.health, 1.];
|
||||
let dark = [color[0]-0.3, color[1]-0.3, color[2]-0.3, 1.];
|
||||
let (vs, is) = match self.state {
|
||||
EntityState::Walking(now) => {
|
||||
let now = now.elapsed().as_secs_f32()*5.;
|
||||
(
|
||||
[
|
||||
// back left leg
|
||||
Vertex::new_col([-0.5, 0.3], dark, 1),
|
||||
Vertex::new_col([-0.4 + (now.sin()*0.1), -0.7 + (now.cos().max(-0.5)*0.1)], dark, 1),
|
||||
Vertex::new_col([-0.25, 0.1], dark, 1),
|
||||
|
||||
// back right leg
|
||||
Vertex::new_col([-0.5, 0.3], color, 1),
|
||||
Vertex::new_col([-0.4 + ((now + 1.).sin()*0.1), -0.7 + ((now + 1.).cos().max(-0.5)*0.1)], color, 1),
|
||||
Vertex::new_col([-0.25, 0.1], color, 1),
|
||||
|
||||
// front left leg
|
||||
Vertex::new_col([0.3, 0.2], dark, 1),
|
||||
Vertex::new_col([0.4 + ((now-1.).sin()*0.1), -0.7 + ((now-1.).cos().max(-0.5)*0.1)], dark, 1),
|
||||
Vertex::new_col([0.5, 0.3], dark, 1),
|
||||
|
||||
// front right leg
|
||||
Vertex::new_col([0.3, 0.2], color, 1),
|
||||
Vertex::new_col([0.4 + ((now-2.).sin()*0.1), -0.7 + ((now-2.).cos().max(-0.5)*0.1)], color, 1),
|
||||
Vertex::new_col([0.5, 0.3], color, 1),
|
||||
|
||||
// body
|
||||
// 3
|
||||
Vertex::new_col([-0.3, 0.], color, 1),
|
||||
Vertex::new_col([0.4, -0.1], color, 1),
|
||||
// 11
|
||||
Vertex::new_col([0.3, 0.4], color, 1),
|
||||
],
|
||||
[
|
||||
0,1,2,
|
||||
3,4,5,
|
||||
6,7,8,
|
||||
9,10,11,
|
||||
3,12,13,
|
||||
3,13,11,
|
||||
3,11,14
|
||||
]
|
||||
)
|
||||
},
|
||||
EntityState::Resting => {
|
||||
(
|
||||
[
|
||||
// back left leg
|
||||
Vertex::new_col([-0.5, 0.3], dark, 1),
|
||||
Vertex::new_col([-0.4, -0.75], dark, 1),
|
||||
Vertex::new_col([-0.25, 0.1], dark, 1),
|
||||
|
||||
// back right leg
|
||||
Vertex::new_col([-0.5, 0.3], color, 1),
|
||||
Vertex::new_col([-0.4, -0.75], color, 1),
|
||||
Vertex::new_col([-0.25, 0.1], color, 1),
|
||||
|
||||
// front left leg
|
||||
Vertex::new_col([0.3, 0.2], dark, 1),
|
||||
Vertex::new_col([0.4, -0.75], dark, 1),
|
||||
Vertex::new_col([0.5, 0.3], dark, 1),
|
||||
|
||||
// front right leg
|
||||
Vertex::new_col([0.3, 0.2], color, 1),
|
||||
Vertex::new_col([0.4, -0.75], color, 1),
|
||||
Vertex::new_col([0.5, 0.3], color, 1),
|
||||
|
||||
// body
|
||||
// 3
|
||||
Vertex::new_col([-0.3, 0.], color, 1),
|
||||
Vertex::new_col([0.4, -0.1], color, 1),
|
||||
// 11
|
||||
Vertex::new_col([0.3, 0.4], color, 1),
|
||||
],
|
||||
[
|
||||
0,1,2,
|
||||
3,4,5,
|
||||
6,7,8,
|
||||
9,10,11,
|
||||
3,12,13,
|
||||
3,13,11,
|
||||
3,11,14
|
||||
]
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
vertices.reserve(vs.len());
|
||||
let base = vertices.len() as u32;
|
||||
for mut v in vs {
|
||||
v.pos[0] = v.pos[0]/50. + pos.x as f32;
|
||||
v.pos[1] = (v.pos[1] + 0.75)/50. + pos.y as f32;
|
||||
vertices.push(v)
|
||||
}
|
||||
|
||||
indices.reserve(is.len());
|
||||
for i in is {
|
||||
indices.push(base + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Returns new cell if entity moves
|
||||
pub fn update(&mut self, map: &mut Map, t: usize, rng: &mut ThreadRng) -> Option<ExternOp> {
|
||||
// Let’s take 0.57 kg of grass / m2
|
||||
// Let’s take 7.5 kg of food / day for a horse
|
||||
// Let’s say that a horse can survive up to 20 days without food
|
||||
let cd = &mut map.cells_data[self.cid];
|
||||
if self.kind.is_herbivore() {
|
||||
let food_needed: f32 = match self.kind {
|
||||
EntityKind::Horse => 7.5 / (0.57 * Map::CELL_AREA)
|
||||
}; // in cell resource fraction
|
||||
match cd.kind {
|
||||
CellKind::Forest => {}, // Infinite food in forests
|
||||
CellKind::Grass => {
|
||||
let food_eaten = food_needed.min(cd.resource);
|
||||
self.set_health(self.health - ((((food_needed - food_eaten)/food_needed)-0.5)/20.));
|
||||
cd.set_resource(cd.resource-food_eaten, t);
|
||||
},
|
||||
_ => {
|
||||
self.set_health(self.health - 1./20.);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.health == 0. {
|
||||
return Some(ExternOp::Remove);
|
||||
}
|
||||
|
||||
let r = cd.resource;
|
||||
if cd.kind != CellKind::Grass {
|
||||
map.voronoi.cell(cd.cid).iter_neighbors().filter(|n| {
|
||||
let cd = &map.cells_data[*n];
|
||||
cd.kind == CellKind::Grass
|
||||
}).choose(rng).map(|c| ExternOp::Move(c))
|
||||
} else if r < 0.5 {
|
||||
let cd = &map.cells_data[self.cid];
|
||||
Some(ExternOp::Move(
|
||||
map.voronoi.cell(cd.cid)
|
||||
.iter_neighbors()
|
||||
.filter(|n| map.cells_data[*n].kind == CellKind::Grass)
|
||||
.fold(cd, |acc, c| {
|
||||
let cd = &map.cells_data[c];
|
||||
if acc.kind != CellKind::Grass {
|
||||
cd
|
||||
} else if cd.kind != CellKind::Grass {
|
||||
acc
|
||||
} else if acc.resource > cd.resource {
|
||||
acc
|
||||
} else if acc.resource < cd.resource {
|
||||
cd
|
||||
} else if rng.gen_bool(0.5) {
|
||||
cd
|
||||
} else {
|
||||
acc
|
||||
}
|
||||
}).cid
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
use winit::{dpi::PhysicalSize, event::{Touch, TouchPhase, WindowEvent}};
|
||||
use crate::graphics::Vertex;
|
||||
|
||||
const SECONDARY_COLOR: [f32; 4] = [84./255., 33./255., 32./255., 1.];
|
||||
const KIND_BUTTON_SIZE: f32 = 0.2;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Kind {
|
||||
None,
|
||||
Terrain,
|
||||
Entities
|
||||
}
|
||||
|
||||
pub struct UI {
|
||||
pub kind_selected: Kind
|
||||
}
|
||||
impl UI {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
kind_selected: Kind::Entities
|
||||
}
|
||||
}
|
||||
pub fn render(&self, vertices: &mut Vec<Vertex>, indices: &mut Vec<u32>, screen_size: PhysicalSize<u32>) {
|
||||
let ratio = screen_size.height as f32/screen_size.width as f32;
|
||||
let vs = [
|
||||
// Terrain
|
||||
Vertex::new_tex([-1. + (0.02*ratio), -1. + (0.02+KIND_BUTTON_SIZE)], [0, 1], 0),
|
||||
Vertex::new_tex([-1. + (0.02*ratio), -1. + 0.02], [0, 13], 0),
|
||||
Vertex::new_tex([-1. + ((0.02+KIND_BUTTON_SIZE)*ratio), -1. + 0.02], [12, 13], 0),
|
||||
Vertex::new_tex([-1. + ((0.02+KIND_BUTTON_SIZE)*ratio), -1. + (0.02+KIND_BUTTON_SIZE)], [12, 1], 0),
|
||||
|
||||
// Entities
|
||||
Vertex::new_tex([-1. + ((0.02+KIND_BUTTON_SIZE)*ratio)+(0.02*ratio), -1. + (0.02+KIND_BUTTON_SIZE)], [12, 1], 0),
|
||||
Vertex::new_tex([-1. + ((0.02+KIND_BUTTON_SIZE)*ratio)+(0.02*ratio), -1. + 0.02], [12, 13], 0),
|
||||
Vertex::new_tex([-1. + ((0.02+KIND_BUTTON_SIZE)*ratio)+((0.02+KIND_BUTTON_SIZE)*ratio), -1. + 0.02], [24, 13], 0),
|
||||
Vertex::new_tex([-1. + ((0.02+KIND_BUTTON_SIZE)*ratio)+((0.02+KIND_BUTTON_SIZE)*ratio), -1. + (0.02+KIND_BUTTON_SIZE)], [24, 1], 0),
|
||||
];
|
||||
let ids = [
|
||||
0,1,2,
|
||||
2,3,0,
|
||||
4,5,6,
|
||||
6,7,4
|
||||
];
|
||||
let i = vertices.len() as u32;
|
||||
vertices.extend_from_slice(&vs);
|
||||
indices.extend_from_slice(&ids.map(|id| id+i));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user