diff --git a/Cargo.lock b/Cargo.lock index 2a3d033..ce454a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "ahash" version = "0.8.12" @@ -246,6 +252,12 @@ dependencies = [ "syn", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.11.1" @@ -313,6 +325,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.5" @@ -398,6 +416,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -501,12 +528,31 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "foldhash" version = "0.1.5" @@ -575,10 +621,14 @@ name = "game" version = "0.1.0" dependencies = [ "anyhow", + "bytemuck", "console_error_panic_hook", "console_log", "env_logger", + "glam", + "image", "log", + "mars", "pollster", "wasm-bindgen", "wasm-bindgen-futures", @@ -620,6 +670,15 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "glam" +version = "0.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360bd2cd76e0cd9032d42cf2922155cecea2685b0cfa4630c3246df030bcfd6" +dependencies = [ + "bytemuck", +] + [[package]] name = "glow" version = "0.17.0" @@ -725,6 +784,20 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder", + "num-traits", + "png", +] + [[package]] name = "indexmap" version = "2.14.0" @@ -833,6 +906,12 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" + [[package]] name = "js-sys" version = "0.3.98" @@ -925,9 +1004,13 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "mars" +version = "0.1.0" [[package]] name = "memchr" @@ -944,6 +1027,16 @@ dependencies = [ "libc", ] +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "naga" version = "29.0.3" @@ -1398,6 +1491,19 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "polling" version = "3.11.0" @@ -1696,6 +1802,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + [[package]] name = "simd_cesu8" version = "1.1.1" diff --git a/Cargo.toml b/Cargo.toml index 81491de..b6eda9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,11 @@ env_logger = "0.11.10" log = "0.4" wgpu = "29.0.3" pollster = "0.4.0" +glam = { version = "0.33.3", features = [ "bytemuck" ] } console_error_panic_hook = "0.1.7" +bytemuck = { version = "1.25.0", features = [ "derive" ]} +image = { version = "0.24", default-features = false, features = ["png", "jpeg"]} +mars = { path = "../Mars" } [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook = "0.1.6" diff --git a/src/assets/script.lua b/src/assets/script.lua deleted file mode 100644 index 73a6987..0000000 --- a/src/assets/script.lua +++ /dev/null @@ -1,12 +0,0 @@ -local two -do - local one - if something then - local three - print(something) - end - local four - if something2 then - - end -end \ No newline at end of file diff --git a/src/assets/shader.wgsl b/src/assets/shader.wgsl index f5a0eae..0aa3136 100644 --- a/src/assets/shader.wgsl +++ b/src/assets/shader.wgsl @@ -1,19 +1,35 @@ +// Vertex shader +@group(1) @binding(0) +var view: mat4x4; + +struct VertexInput { + @location(0) position: vec3, + @location(1) tex_coords: vec2, +} + struct VertexOutput { @builtin(position) clip_position: vec4, -}; + @location(0) tex_coords: vec2, +} @vertex fn vs_main( - @builtin(vertex_index) in_vertex_index: u32, + model: VertexInput, ) -> VertexOutput { var out: VertexOutput; - let x = f32(1 - i32(in_vertex_index)) * 0.5; - let y = f32(i32(in_vertex_index & 1u) * 2 - 1) * 0.5; - out.clip_position = vec4(x, y, 0.0, 1.0); + out.tex_coords = model.tex_coords; + out.clip_position = view * vec4(model.position, 1.0); return out; } +// Fragment shader + +@group(0) @binding(0) +var t_diffuse: texture_2d; +@group(0) @binding(1) +var s_diffuse: sampler; + @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { - return vec4(0.3, 0.2, 0.1, 1.0); + return textureSample(t_diffuse, s_diffuse, in.tex_coords); } \ No newline at end of file diff --git a/src/engine.rs b/src/engine.rs index 4806748..a4a625a 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,5 +1,73 @@ +use std::collections::HashMap; // Credit of most code to https://sotrh.github.io/learn-wgpu/ since I'm not familiar with wgpu use std::sync::Arc; +use glam::camera::lh::proj::directx::perspective; +use wgpu::util::DeviceExt; +use glam::prelude::*; + +struct Controller { + buttons: HashMap, + keys: HashMap, + mouse: Vec2, +} + +impl Controller { + fn new() -> Controller { + Controller { + buttons: Default::default(), + keys: HashMap::new(), + mouse: Vec2::new(0.0,0.0), + } + } +} + +struct Camera { + frame: Affine3A, + aspect_ratio: f32, + z_near: f32, + z_far: f32, + fov_y: f32, +} + +impl Camera { + fn view(&self) -> Mat4 { + let projection = perspective(self.fov_y, self.aspect_ratio, self.z_near, self.z_far); + projection * self.frame.inverse() + } + fn new(window: &Arc) -> Camera { + Camera { + aspect_ratio: window.inner_size().width as f32 / window.inner_size().height as f32, + frame: Affine3A::IDENTITY, + fov_y: 90.0, + z_near: 0.1, + z_far: 1000.0 + } + } + fn resize(&mut self, window: &Arc) { + self.aspect_ratio = window.inner_size().width as f32 / window.inner_size().height as f32 + } + fn control(&mut self, delta: Vec3) { + self.frame = self.frame * Affine3A::from_translation(delta); + } + fn rotate(&mut self, yaw: f32, pitch: f32) { + let (mut y,mut x,z) = self.frame.matrix3.to_euler(EulerRot::YXZ); + x = (x + pitch * 0.005).clamp(-1.4,1.4); + y = y + yaw * 0.005; + self.frame.matrix3 = Mat3A::from_euler(EulerRot::YXZ,y,x,z); + } +} + + +const VERTICES: &[Vertex] = &[ + // Changed + Vertex { position: [-0.5,-0.5,-0.5], tex_coords: [0,0]} +]; + +const INDICES: &[u16] = &[ + 0, 1, 4, + 1, 2, 4, + 2, 3, 4, +]; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; @@ -14,17 +82,94 @@ use winit::{ window::Window, }; -// This will store the state of our game -pub struct State { - surface: wgpu::Surface<'static>, +type Kt = HashMap>; + +struct Renderer { + surface: (wgpu::Surface<'static>,bool), + config: wgpu::SurfaceConfiguration, device: wgpu::Device, queue: wgpu::Queue, - config: wgpu::SurfaceConfiguration, - is_surface_configured: bool, + camera: Camera, + clear_color: wgpu::Color, +} + +struct UniformPlan { + bind_group: wgpu::BindGroup, + buffer: wgpu::Buffer, +} + +struct TexturePlan { + bind_group: wgpu::BindGroup, + texture: wgpu::Texture, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +struct SimpleVertex { + pos: [f32; 3], + uv: [f32; 2], + norm: [f32; 3], +} + +impl SimpleVertex { + fn desc() -> wgpu::VertexBufferLayout<'static> { + wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[ + wgpu::VertexAttribute { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float32x2, + }, + wgpu::VertexAttribute { + offset: std::mem::size_of::<[f32; 5]>() as wgpu::BufferAddress, + shader_location: 2, + format: wgpu::VertexFormat::Float32x3, + } + ] + } + } +} + +struct SimpleMesh { + vertex_buffer: wgpu::Buffer, + index_buffer: wgpu::Buffer, + indices_count: u32, +} + +struct SimpleMaterial { + albedo: TexturePlan, + normal: Option, + specular: Option, +} + +struct SimpleModel { + affine: Affine3A, + material: mars::gc::Gc, + +} + +struct SimpleRenderPlan { + pipeline: wgpu::RenderPipeline, + // todo: clients: Kt>> + clients: Vec +} + +struct FluidRigidBody { + +} + +pub struct State { + renderer: Renderer, + controller: Controller, window: Arc, - render_pipeline1: wgpu::RenderPipeline, - render_pipeline2: wgpu::RenderPipeline, - other_pipeline: bool, + diffuse_bind_group: wgpu::BindGroup, sky: wgpu::Color, } @@ -47,7 +192,7 @@ impl State { display: None, }); - let surface = instance.create_surface(window.clone()).unwrap(); + let surface = instance.create_surface(window.clone())?; let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { @@ -95,22 +240,205 @@ impl State { desired_maximum_frame_latency: 2, }; + let diffuse_bytes = include_bytes!("assets/test.png"); + let diffuse_image = image::load_from_memory(diffuse_bytes)?; + let diffuse_rgba = diffuse_image.to_rgba8(); + + use image::GenericImageView; + let dimensions = diffuse_image.dimensions(); + + println!("{:?}",dimensions); + + let texture_size = wgpu::Extent3d { + width: dimensions.0, + height: dimensions.1, + // All textures are stored as 3D, we represent our 2D texture + // by setting depth to 1. + depth_or_array_layers: 1, + }; + let diffuse_texture = device.create_texture( + &wgpu::TextureDescriptor { + size: texture_size, + mip_level_count: 1, // We'll talk about this a little later + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + // Most images are stored using sRGB, so we need to reflect that here. + format: wgpu::TextureFormat::Rgba8UnormSrgb, + // TEXTURE_BINDING tells wgpu that we want to use this texture in shaders + // COPY_DST means that we want to copy data to this texture + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + label: Some("diffuse_texture"), + // This is the same as with the SurfaceConfig. It + // specifies what texture formats can be used to + // create TextureViews for this texture. The base + // texture format (Rgba8UnormSrgb in this case) is + // always supported. Note that using a different + // texture format is not supported on the WebGL2 + // backend. + view_formats: &[], + } + ); + + queue.write_texture( + // Tells wgpu where to copy the pixel data + wgpu::TexelCopyTextureInfo { + texture: &diffuse_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + // The actual pixel data + &diffuse_rgba, + // The layout of the texture + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(4 * dimensions.0), + rows_per_image: Some(dimensions.1), + }, + texture_size, + ); + + let diffuse_texture_view = diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default()); + let diffuse_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::Repeat, + address_mode_v: wgpu::AddressMode::Repeat, + address_mode_w: wgpu::AddressMode::Repeat, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::MipmapFilterMode::Nearest, + ..Default::default() + }); + + 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"), + }); + + 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_sampler), + } + ], + label: Some("diffuse_bind_group"), + } + ); + + let camera = Camera::new(&window); + + let camera_buffer = device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("Camera Buffer"), + contents: bytemuck::cast_slice(&[camera.view()]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + } + ); + + let camera_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("camera_bind_group_layout"), + }); + + let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &camera_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: camera_buffer.as_entire_binding(), + } + ], + label: Some("camera_bind_group"), + }); + + let vertex_buffer = device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(VERTICES), + usage: wgpu::BufferUsages::VERTEX, + } + ); + + let index_buffer = device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(INDICES), + usage: wgpu::BufferUsages::INDEX, + } + ); + + let num_indices = INDICES.len() as u32; + let shader1 = device.create_shader_module(wgpu::include_wgsl!("assets/shader.wgsl")); let render_pipeline_layout1 = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Render Pipeline Layout"), - bind_group_layouts: &[], + bind_group_layouts: &[ + Some(&texture_bind_group_layout), + Some(&camera_bind_group_layout), + ], immediate_size: 0, }); + /*let water_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Water Pipeline"), + layout: Some(&water_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader1, + entry_point: Some("vs_main"), + buffers: &[ + + ], + compilation_options: wgpu::PipelineCompilationOptions::default(); + } + });*/ + let render_pipeline1 = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Render Pipeline"), layout: Some(&render_pipeline_layout1), vertex: wgpu::VertexState { module: &shader1, - entry_point: Some("vs_main"), // 1. - buffers: &[], // 2. + entry_point: Some("vs_main"), + buffers: &[ + Vertex::desc(), + ], compilation_options: wgpu::PipelineCompilationOptions::default(), }, fragment: Some(wgpu::FragmentState { @@ -147,74 +475,28 @@ impl State { cache: None, // 6. }); - let shader2 = device.create_shader_module(wgpu::include_wgsl!("assets/shader2.wgsl")); - - let render_pipeline_layout2 = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), - bind_group_layouts: &[], - immediate_size: 0, - }); - - let render_pipeline2 = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&render_pipeline_layout2), - vertex: wgpu::VertexState { - module: &shader2, - entry_point: Some("vs_main"), // 1. - buffers: &[], // 2. - compilation_options: wgpu::PipelineCompilationOptions::default(), - }, - fragment: Some(wgpu::FragmentState { - // 3. - module: &shader2, - entry_point: Some("fs_main"), - targets: &[Some(wgpu::ColorTargetState { - // 4. - format: config.format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, // 1. - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, // 2. - cull_mode: Some(wgpu::Face::Back), - // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE - polygon_mode: wgpu::PolygonMode::Fill, - // Requires Features::DEPTH_CLIP_CONTROL - unclipped_depth: false, - // Requires Features::CONSERVATIVE_RASTERIZATION - conservative: false, - }, - depth_stencil: None, // 1. - multisample: wgpu::MultisampleState { - count: 1, // 2. - mask: !0, // 3. - alpha_to_coverage_enabled: false, // 4. - }, - multiview_mask: None, // 5. - cache: None, // 6. - }); - Ok(Self { surface, + camera_buffer, + camera_bind_group, + diffuse_bind_group, + index_buffer, + vertex_buffer, + controller: Controller::new(), device, queue, config, is_surface_configured: false, window, - render_pipeline1, - render_pipeline2, - other_pipeline: false, + num_indices, + render_pipeline: render_pipeline1, sky: wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0, }, + camera, }) } @@ -224,6 +506,7 @@ impl State { self.config.width = width.min(max); self.config.height = height.min(max); self.surface.configure(&self.device, &self.config); + self.camera.resize(&self.window); self.is_surface_configured = true; } } @@ -288,12 +571,47 @@ impl State { multiview_mask: None, }); - if self.other_pipeline { - render_pass.set_pipeline(&self.render_pipeline1); - } else { - render_pass.set_pipeline(&self.render_pipeline2); + let mut movement = Vec3::new(0.0,0.0,0.0); + + let pressed = |keycode: KeyCode| { + if let Some(true) = self.controller.keys.get(&keycode) { + true + } else { + false + } + }; + + if pressed(KeyCode::KeyA) { + movement.x -= 1.0; } - render_pass.draw(0..3, 0..1); // 3. + if pressed(KeyCode::KeyD) { + movement.x += 1.0; + } + if pressed(KeyCode::KeyW) { + movement.z += 1.0; + } + if pressed(KeyCode::KeyS) { + movement.z -= 1.0; + } + if pressed(KeyCode::KeyE) { + movement.y += 1.0; + } + if pressed(KeyCode::KeyQ) { + movement.y -= 1.0; + } + + println!("{:?} {:?} {:?}",movement,self.camera.frame.translation,self.camera.frame.matrix3.to_euler(EulerRot::YXZ)); + + self.camera.control(movement * 0.1); + + self.queue.write_buffer(&self.camera_buffer,0,bytemuck::cast_slice(&[self.camera.view()])); + + render_pass.set_pipeline(&self.render_pipeline); + render_pass.set_bind_group(1, &self.camera_bind_group, &[]); + render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]); + render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); + render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); + render_pass.draw_indexed(0..self.num_indices, 0, 0..1); } // submit will accept anything that implements IntoIter @@ -306,11 +624,13 @@ impl State { fn handle_key(&mut self, event_loop: &ActiveEventLoop, code: KeyCode, is_pressed: bool) { match (code, is_pressed) { (KeyCode::Escape, true) => event_loop.exit(), - (KeyCode::Space, true) => { - self.other_pipeline = !self.other_pipeline; + (KeyCode::Space, true) => {}, + (KeyCode::KeyR, true) => { + self.camera.frame = Affine3A::IDENTITY } _ => {} } + self.controller.keys.insert(code, is_pressed); } fn handle_mouse_moved(&mut self, position: PhysicalPosition) { @@ -319,7 +639,15 @@ impl State { g: position.x / 1000.0, b: position.y / 1000.0, a: 1.0, + }; + if let Some(true) = self.controller.buttons.get(&MouseButton::Right) { + self.camera.rotate(position.x as f32 - self.controller.mouse.x, position.y as f32 - self.controller.mouse.y); } + self.controller.mouse = Vec2::new(position.x as f32, position.y as f32); + } + + fn handle_mouse_button(&mut self, button: MouseButton, state: ElementState ) { + self.controller.buttons.insert(button,state.is_pressed()); } } @@ -428,6 +756,7 @@ impl ApplicationHandler for App { } } } + WindowEvent::MouseInput { button, state: element, .. } => state.handle_mouse_button(button,element), WindowEvent::CursorMoved { position: pos, .. } => state.handle_mouse_moved(pos), WindowEvent::KeyboardInput { event: diff --git a/src/lang.rs b/src/lang.rs deleted file mode 100644 index e882a55..0000000 --- a/src/lang.rs +++ /dev/null @@ -1,1243 +0,0 @@ -use std::any::Any; -use std::collections::HashMap; -use std::fmt::Formatter; -use std::ops::Add; -use std::rc::Rc; -// TODO: Vec> is good but sometimes we either need at least one or none, current fix is just to check if there is one and if not skip it, this takes extra performance - -#[derive(Clone, Copy)] -struct Reader<'a> { - slice: &'a str, - full: &'a str, - white: bool, // Is the previous 'thing' whitespace? -} - -struct Error(String,Piece<()>); - -impl Error { - fn new(message: &str, reader: &Reader) -> Result { - Err(Error(message.to_string(), reader.marker())) - } - fn msg(&self, reader: &Reader) -> String { - self.0.clone().add(reader.slice) - } -} - -impl std::fmt::Debug for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("Error: {} {}", self.0, self.1.index)) - } -} - - -type Comment = String; - -struct Piece { - comment: Comment, - index: usize, - width: usize, - it: T, -} - -impl std::fmt::Debug for Piece where T: std::fmt::Debug { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.it.fmt(f) - } -} - - -impl Piece { - fn of(it: T, reader: &Reader, width: usize) -> Piece { - Piece { - it, - index: reader.slice.as_ptr() as usize - reader.full.as_ptr() as usize, - width, - comment: String::new() - } - } - fn from(it: T, begin: &Piece, end: &Piece) -> Piece { - Piece { - it, - index: begin.index, - width: end.index + end.width - begin.index, - comment: begin.comment.clone() - } - } - fn unit(&self) -> Piece<()> { - Piece { - it: (), - index: self.index, - width: self.width, - comment: self.comment.clone() - } - } - fn map(self, f: F) -> Piece where F: FnOnce(T) -> U { - let it = f(self.it); - Piece { - it, - index: self.index, - width: self.width, - comment: self.comment // this is stupid - } - } - fn new(it: T) -> Piece { - Piece { - it, - index: 0, - width: 0, - comment: String::new() - } - } -} - -// Strips comments and whitespace -// todo: implement comments returning for debugging. - -impl<'a> From<&'a str> for Reader<'a> { - fn from(str: &'a str) -> Self { - Reader { - full: str, - slice: str, - white: true, - } - } -} - -impl<'a> Reader<'a> { - fn trim(&mut self) -> Result { - let start = self.slice.as_ptr() as usize; - self.slice = self.slice.trim_start(); - if self.slice.starts_with("--") { - let comment = if self.slice[2..].starts_with("[[") { - match self.slice.find("]]") { - Some(i) => { - let comment = &self.slice[4..i + 2]; - self.slice = &self.slice[i + 2..].trim(); - self.white = true; - Ok(comment) - } - _ => self.error("Unterminated multi-line comment"), - } - } else { - let comment; - if let Some(i) = self.slice.find("\n") { - self.slice = &self.slice[i + 1..].trim(); - comment = &self.slice[2..i+1]; - self.white = true; - } else { - self.slice = &self.slice[..self.slice.len() - 1]; - comment = &self.slice[2..]; - self.white = false; - } - Ok(comment) - }?; - self.trim().map(|it| { // Try another comment and append them to each other, just in case - it + "\n" + comment - }) - } else { - if start < self.slice.as_ptr() as usize { - self.white = true; - } - Ok(String::new()) - } - } - fn white(&mut self) -> Result<&mut Self, Error> { - if self.white { - Ok(self) - } else { - self.error("Expected whitespace") - } - } - fn consume(&mut self, start: &str) -> Result<&mut Self, Error> { - self.trim()?; - if self.slice.starts_with(start) { - self.slice = &self.slice[start.len()..]; - Ok(self) - } else { - self.error(format!("Expected '{}'", start).as_str()) - } - } - // Consume but require whitespace before - fn white_consume(&mut self, start: &str) -> Result<&mut Self, Error> { - if self.white { - self.consume(start) - } else { - self.error(format!("Expected whitespace before parsing {}",start).as_str()) - } - } - // Consume but set whitespace after - fn consume_white(&mut self, start: &str) -> Result<&mut Self, Error> { - let result = self.consume(start)?; - result.white = true; - Ok(result) - } - fn take(&mut self) -> Result, Error> - where - T: Parse, - { - T::consume(self) - } - fn finished(&self) -> bool { - self.slice.is_empty() - } - fn error(&self, message: &str) -> Result { - Error::new(message, self) - } - fn marker(&self) -> Piece<()> { - Piece::of((), self, 0) - } -} - -#[derive(Debug)] -struct Number(String); -#[derive(Debug, Clone)] -struct Name(String); -// Requirements for Parse: -// - The reader.slice must be pushed to the end of the consumed text (or restored in an error) -// - ??? -// - The reader may have not been trimmed -trait Parse { - fn consume(reader: &mut Reader) -> Result, Error> - where - Self: Sized; -} - -static KEYWORDS: &[&str] = &["and","break","do","else","elseif","end","false","for","function","func","global","goto","if","in","local","nil","not","or","repeat","return","then","true","until","while"]; -impl Parse for Name { - fn consume(reader: &mut Reader) -> Result, Error> - where - Self: Sized, - { - reader.trim()?; - reader.white()?; - let mut chars = reader.slice.chars(); - let mut end = 1; - match chars.next() { - None => { - return reader.error("Expected identifier"); - } - Some(c) => { - if !(c.is_alphabetic() || c == '_') { - return reader.error("Identifier must begin with alphabetic character"); - } - } - }; - end += chars - .take_while(|c| c.is_alphanumeric() || *c == '_') - .count(); - let name = reader.slice[..end].to_string(); - // TODO: do we need to check if this is a keyword here? - if KEYWORDS.contains(&name.as_str()) { - return reader.error(format!("Expected name, got keyword {}",name).as_str()) - } - let result = Ok(Piece::of( - Name(name), - reader, - end, - )); - reader.slice = &reader.slice[end..]; - result - } -} -impl Parse for Number { - fn consume(reader: &mut Reader) -> Result, Error> - where - Self: Sized, - { - reader.trim()?; - reader.white()?; - if !reader.white { - return reader.error("Expected whitespace before parsing number"); - } - let mut end; - if reader.slice.starts_with("0x") { - end = reader.slice[2..] - .chars() - .take_while(|c| c.is_digit(16)) - .count() - + 2; - if end == 2 { - end = 0 - } - } else { - end = reader - .slice - .chars() - .take_while(|c| c.is_digit(10) || *c == '.') - .count(); - } - if end == 0 { - reader.error("Could not parse number".into()) - } else { - reader.slice = &reader.slice[end..]; - Ok(Piece::of( - Number(reader.slice[..end].to_string()), - reader, - end, - )) - } - } -} - -impl Parse for String { - fn consume(reader: &mut Reader) -> Result, Error> - where - Self: Sized, - { - reader.trim()?; - let chars: &mut std::str::Chars = &mut reader.slice.chars(); - let mut string = String::new(); - let term1: char; - let term2: Option; - let extra; - match chars.next() { - Some('"') => { - term1 = '"'; - term2 = None; - extra = 2; - } - Some('\'') => { - term1 = '\''; - term2 = None; - extra = 2; - } - Some('[') if chars.next().is_some_and(|it| it == '[') => { - term1 = ']'; - term2 = Some(']'); - extra = 4; - } - _ => { - return reader.error("Could not parse string"); - } - } - loop { - match chars.next() { - Some(c) => { - match c { - '\\' => match chars.next() { - Some(c) => match c { - 'n' => string.push('\n'), - 'r' => string.push('\r'), - 't' => string.push('\t'), - '"' => string.push('"'), - '\'' => string.push('\''), - '\\' => string.push('\\'), - it => { - string.push(it); - } - }, - None => {} - }, - '\r' => {} // todo: ? - '\n' if term2.is_none() => { - return reader.error("Found newline while parsing string"); - } - term if term == term1 => { - // If the second terminator exists and is the next char, or there is no second terminator - if !term2.is_some() - || chars.next().is_some_and(|it| it == term2.unwrap()) - { - let length = string.len(); - reader.slice = &reader.slice[length + extra..]; - return Ok(Piece::of(string, reader, length)); - // There is a second terminator and it wasn't the next char - } else { - string.push(term) - } - } - _ => { - string.push(c); - } - } - } - None => { - return reader.error("Found EOF while parsing string"); - } - } - } - } -} - -// AST -#[derive(Debug)] -struct Block(Vec>, Vec>); // -#[derive(Debug)] -struct Chunk(Piece); -#[derive(Debug)] -enum Statement { - Semicolon, - Assignment(Vec>, Vec>), - Call(Call), - Label(Piece), - Break, - Continue, - Goto(Piece), - Do(Block), - While(Piece, Block), - Repeat(Block, Piece), - If(Vec>, Vec, Option), - Range( - Piece, - Piece, - Piece, - Option>, - Block, - ), - Iterator(Vec>, Vec>, Block), - Function(Vec>, Option>, Function), - LocalFunction(Piece, Function), - Declaration(Vec>, Vec>), -} -#[derive(Debug)] -enum Variable { - Name(Name), - Index(Box>, Piece), -} -#[derive(Debug)] -enum Expression { - Nil, - True, - False, - Number(Number), - String(String), - VarArg(VarArg), - Function(Function), - Prefix(Box), - Table(Constructor), - Binary(Box>, Piece, Box>), - Unary(Piece, Box>), -} -#[derive(Debug)] -struct Constructor(Vec>); -#[derive(Debug)] -enum Prefix { - Ident(Variable), - Call(Call), - Expression(Piece), -} -#[derive(Debug)] -struct VarArg(Option); -#[derive(Debug)] -struct Call(Box>, Option>, Piece); -#[derive(Debug)] -struct Args(Vec>); -#[derive(Debug)] -struct Function(Vec>, Option>, Piece); -#[derive(Debug)] -struct Field(Option>, Piece); -#[derive(Debug)] -enum FieldSep { - Comma, - Semicolon, -} -#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)] -enum BinaryOp { - Or, - And, - Less, - Greater, - LessEqual, - GreatEqual, - Equivalent, - NotEqual, - Pipe, - Tilde, - Ampersand, - ShiftLeft, - ShiftRight, - Concat, - Plus, - Minus, - Mul, - Divide, - DivFloor, - Modulo, - Caret, -} -#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)] -enum UnaryOp { - Negate, - Not, - Ampersand, - Tilde -} - -impl Parse for Vec> { - fn consume(reader: &mut Reader) -> Result>>, Error> - where - Self: Sized, - { - let mut result: Vec> = Vec::new(); - reader.trim()?; - result.push(reader.take::()?); - while reader.consume_white(",").is_ok() { - result.push(reader.take::()?); - } - let first = result.first().unwrap().unit(); - let last = result.last().unwrap().unit(); - Ok(Piece::from(result, &first, &last)) - } -} - -impl Parse for BinaryOp { - fn consume(reader: &mut Reader) -> Result, Error> { - reader.trim()?; - let mut chars = reader.slice.chars(); - if let Some(char) = chars.next() { - let mut count = 1; - let op = match char { - '+' => BinaryOp::Plus, - '-' => BinaryOp::Minus, - '*' => BinaryOp::Mul, - '/' => { - if let Some('/') = chars.next() { - count += 1; - BinaryOp::DivFloor - } else { - BinaryOp::Divide - } - }, - '^' => BinaryOp::Caret, - '%' => BinaryOp::Modulo, - '&' => BinaryOp::Ampersand, - '~' => { - match chars.next() { - Some('=') => { - count += 1; - BinaryOp::NotEqual - }, - _ => { - BinaryOp::Tilde - } - } - }, - '|' => BinaryOp::Pipe, - '>' => { - match chars.next() { - Some('>') => { - count += 1; - BinaryOp::ShiftRight - }, - Some('=') => { - count += 1; - BinaryOp::GreatEqual - } - _ => { - BinaryOp::Greater - } - } - }, - '<' => { - match chars.next() { - Some('<') => { - count += 1; - BinaryOp::ShiftLeft - }, - Some('=') => { - count += 1; - BinaryOp::LessEqual - } - _ => { - BinaryOp::Less - } - } - }, - '.' => { - if let Some('.') = chars.next() { - count += 1; - BinaryOp::Concat - } else { - return reader.error("Expected '.' for concatenation") - } - }, - '=' => { - if let Some('=') = chars.next() { - count += 1; - BinaryOp::Equivalent - } else { - return reader.error("Expected '=' for equality") - } - }, - _ => { - if reader.white_consume("and").is_ok() { - count+=2; - BinaryOp::And - } else if reader.white_consume("or").is_ok() { - count+=1; - BinaryOp::Or - } else { - return reader.error("Expected binary operation") - } - } - }; - if !matches!(op,BinaryOp::And | BinaryOp::Or) { - reader.white = true; - } - let result = Ok(Piece::of(op,reader,count)); - reader.slice = &reader.slice[count..]; - result - } else { - reader.error("Expected binary operand, got EOF") - } - } -} - -impl Parse for UnaryOp { - fn consume(reader: &mut Reader) -> Result, Error> { - let start = &reader.marker(); - reader.trim()?; - let op = if reader.consume("-").is_ok() { - UnaryOp::Negate - } else if reader.white_consume("not").is_ok() { - UnaryOp::Not - } else if reader.consume("#").is_ok() { - UnaryOp::Ampersand - } else if reader.consume("~").is_ok() { - UnaryOp::Tilde - } else { - return reader.error("Expected unary operand") - }; - if !matches!(op,UnaryOp::Not) { - reader.white = true; - } - Ok(Piece::from(op, &start, &reader.marker())) - } -} - -impl Parse for Function { - fn consume(reader: &mut Reader) -> Result, Error> { - reader.trim()?; - let start = reader.marker(); - reader.consume_white("(")?; - let mut parameters: Vec> = Vec::new(); - let mut vararg = None; - while let Ok(name) = reader.take::() { - parameters.push(name); - if vararg.is_some() { - return reader.error("Unexpected argument after vararg '...'"); - } - if !reader.consume_white(",").is_ok() { - break; - } - vararg = reader.take::().ok() - } - reader.consume_white(")")?; - let end = reader.marker(); - let block = reader.take::()?; - reader.white_consume("end")?; - Ok(Piece::from( - Function(parameters, vararg, block), - &start, - &end, - )) - } -} - -impl Parse for VarArg { - fn consume(reader: &mut Reader) -> Result, Error> { - reader.trim()?; - let start = reader.marker(); - reader.white()?.consume_white("...")?; - let name = reader.take::().ok(); - let end; - if let Some(name) = name { - let end = name.unit(); - Ok(Piece::from(VarArg(Some(name.it)), &start, &end)) - } else { - end = reader.marker(); - Ok(Piece::from( - VarArg(name.map(|piece| piece.it)), - &start, - &end, - )) - } - } -} - -impl BinaryOp { - fn compare(&self, other: &BinaryOp) -> bool { - self < other - } - fn compare_unary(&self) -> bool { - match self { - BinaryOp::Caret => true, - _ => false, - } - } -} - -fn inject(current: Piece, op: Piece, next: Piece) -> Piece { - let start = current.unit(); - if let Expression::Binary(left,op2,right) = current.it { - if op.it.compare(&op2.it) { - let start = left.unit(); - let new_right = Box::new(inject(*right, op, next)); - let end = new_right.unit(); - Piece::from(Expression::Binary(left,op2,new_right),&start,&end) - } else { - let start2 = start.unit(); - let end = next.unit(); - Piece::from(Expression::Binary(Box::new(start.map(|_| Expression::Binary(left,op2,right))),op,Box::new(next)),&start2,&end) - } - } else if let Expression::Unary(op2,inner) = current.it { - if op.it.compare_unary() { - let start = op2.unit(); - let inner = Box::new(inject(*inner, op, next)); - let end = inner.unit(); - Piece::from(Expression::Unary(op2,inner),&start,&end) - } else { - let start2 = start.unit(); - let end = next.unit(); - Piece::from(Expression::Binary(Box::new(start.map(|_| Expression::Unary(op2,inner))),op,Box::new(next)),&start2,&end) - } - } else { - let start = current.unit(); - let end = next.unit(); - Piece::from(Expression::Binary(Box::new(current),op,Box::new(next)),&start,&end) - } -} - -impl Parse for Expression { - fn consume(reader: &mut Reader) -> Result, Error> { - let taker = |reader: &mut Reader| { - let start = reader.marker(); - reader.trim()?; - let result = if reader.white_consume("nil").is_ok() { - Expression::Nil - } else if reader.white_consume("true").is_ok() { - Expression::True - } else if reader.white_consume("false").is_ok() { - Expression::False - } else if let Ok(vararg) = reader.take::() { - Expression::VarArg(vararg.it) - } else if let Ok(number) = reader.take::() { - Expression::Number(number.it) - } else if let Ok(string) = reader.take::() { - Expression::String(string.it) - } else if reader.white_consume("function").is_ok() || reader.white_consume("func").is_ok() { - Expression::Function(reader.take::()?.it) - } else if let Ok(table) = reader.take::() { - Expression::Table(table.it) - } else if let Ok(unary) = reader.take::() { - Expression::Unary(unary,Box::new(reader.take::()?)) - } else { - Expression::Prefix(Box::new(reader.take::()?.it)) - }; - Ok(Piece::from(result,&start,&reader.marker())) - }; - let mut current = taker(reader)?; - while let Ok(binary) = reader.take::() { - let next = taker(reader)?; - current = inject(current,binary,next); - } - Ok(current) - } -} - -impl Parse for Call { - fn consume(reader: &mut Reader) -> Result, Error> { - reader.trim()?; - let prefix = reader.take::()?; - let piece = prefix.unit(); - if let Prefix::Call(call) = prefix.it { - Ok(piece.map(|_| call)) - } else { - reader.error("Expected variable, found expression.") - } - } -} - -impl Parse for Field { - fn consume(reader: &mut Reader) -> Result, Error> { - let start = reader.marker(); - reader.trim()?; - let result = if reader.consume_white("[").is_ok() { - let index = reader.take::()?; - reader.consume_white("=")?; - let value = reader.take::()?; - reader.consume_white("]")?; - Field(Some(index),value) - } else if let Ok(name) = reader.take::() { - reader.consume_white("=")?; - let index = name.map(|it| Expression::String(it.0)); - let value = reader.take::()?; - Field(Some(index),value) - } else { - Field(None,reader.take::()?) - }; - Ok(Piece::from(result, &start, &reader.marker())) - } -} - -impl Parse for Constructor { - fn consume(reader: &mut Reader) -> Result, Error> { - reader.trim()?; - let start = reader.marker(); - reader.consume_white("{")?; - let fields: Piece>>; - if reader.clone().take::().is_ok() { - fields = reader.take::>>()?; - } else { - fields = Piece::from(Vec::new(),&reader.marker(),&reader.marker()); - } - reader.consume_white("}")?; - Ok(fields.map(|it| Constructor(it))) - } -} - -impl Parse for Args { - fn consume(reader: &mut Reader) -> Result, Error> { - reader.trim()?; - let start = reader.marker(); - let result = if reader.consume_white("(").is_ok() { - let expressions = reader.take::>>()?; - reader.consume_white(")")?; - Args(expressions.it) - } else if let Ok(table) = reader.take::() { - Args(vec!(table.map(|it| Expression::Table(it)))) - } else if let Ok(string) = reader.take::() { - Args(vec!(string.map(|it| Expression::String(it)))) - } else { - return reader.error("Expected arguments") - }; - Ok(Piece::from(result, &start, &reader.marker())) - } -} - -impl Parse for Prefix { - fn consume(reader: &mut Reader) -> Result, Error> { - reader.trim()?; - let start = reader.marker(); - let mut current: Box> = if reader.consume_white("(").is_ok() { - let exp = reader.take::()?; - reader.consume_white(")")?; - Box::new(Piece::from(Prefix::Expression(exp),&start,&reader.marker())) - } else if let Ok(name) = reader.take::() { - Box::new(Piece::from(Prefix::Ident(Variable::Name(name.it)),&start,&reader.marker())) - } else { - return reader.error("Expected expression") - }; - loop { - let start = reader.marker(); - let result: Prefix = if reader.consume_white("[").is_ok() { - let exp = reader.take::()?; - reader.consume_white("]")?; - Prefix::Ident(Variable::Index(current, exp)) - } else if reader.consume_white(".").is_ok() { - let field = reader.take::()?; - Prefix::Ident(Variable::Index(current,field.map(|it| Expression::String(it.0)))) - } else if reader.consume_white(":").is_ok() { - let field = reader.take::()?; - let args = reader.take::()?; - Prefix::Call(Call(current,Some(field),args)) - } else if let Ok(args) = reader.take::() { - Prefix::Call(Call(current,None,args)) - } else { - break - }; - current = Box::new(Piece::from(*Box::new(result), &start, &reader.marker())); - } - Ok(*current) - } -} - -impl Parse for Variable { - fn consume(reader: &mut Reader) -> Result, Error> { - reader.trim()?; - let prefix = reader.take::()?; - let piece = prefix.unit(); - if let Prefix::Ident(var) = prefix.it { - Ok(piece.map(|_| var)) - } else { - reader.error("Expected variable, found expression.") - } - } -} - -impl Parse for Statement { - fn consume(reader: &mut Reader) -> Result, Error> { - reader.trim()?; - let start = reader.marker(); - // todo: it is safer to pass back the new reader in the Ok, and then map it out - if reader.white_consume("break").is_ok() { - Ok(Piece::from(Statement::Break, &start, &reader.marker())) - } else if reader.white_consume("continue").is_ok() { - Ok(Piece::from(Statement::Continue, &start, &reader.marker())) - } else if reader.consume_white(";").is_ok() { - Ok(Piece::from(Statement::Semicolon, &start, &reader.marker())) - } else if reader.white_consume("local").is_ok() { - if reader.white_consume("function").is_ok() || - reader.white_consume("func").is_ok() { - let name = reader.take::()?; - let function = reader.take::()?; - *reader = *reader; - Ok(Piece::from( - Statement::LocalFunction(name, function.it), - &start, - &reader.marker(), - )) - } else { - let names = reader.take::>>()?.it; - reader.consume_white("=")?; - let expressions = reader.take::>>()?.it; - *reader = *reader; - Ok(Piece::from( - Statement::Declaration(names, expressions), - &start, - &reader.marker(), - )) - } - } else if reader.white_consume("function").is_ok() || - reader.white_consume("func").is_ok() { - let mut names = Vec::new(); - loop { - if let Ok(name) = reader.take::() { - names.push(name); - if !reader.consume_white(".").is_ok() { - break; - } - } - } - let mut member = None; - if reader.consume_white(":").is_ok() { - member = Some(reader.take::()?); - } - let function = reader.take::()?; - *reader = *reader; - Ok(Piece::from( - Statement::Function(names, member, function.it), - &start, - &reader.marker(), - )) - } else if reader.white_consume("for").is_ok() { - let names = reader.take::>>()?.it; - let result = if names.len() < 1 { - let name = names.first().unwrap(); - reader.consume_white("=")?; - let begin = reader.take::()?; - reader.consume_white(",")?; - let finish = reader.take::()?; - let delta = if reader.consume(",").is_ok() { - Some(reader.take::()?) - } else { - None - }; - reader.white_consume("do")?; - let block = reader.take::()?; - reader.white_consume("end")?; - Ok(Piece::from( - Statement::Range(Piece { - it: name.it.clone(), - width: name.width, - index: name.index, - comment: name.comment.clone(), - }, begin, finish, delta, block.it), - &start, - &reader.marker(), - )) - } else { - reader.white_consume("in")?; - let expressions = reader.take::>>()?.it; - reader.white_consume("do")?; - let block = reader.take::()?; - reader.white_consume("end")?; - Ok(Piece::from( - Statement::Iterator(names, expressions, block.it), - &start, - &reader.marker(), - )) - }; - *reader = *reader; - result - } else if reader.white_consume("while").is_ok() { - let condition = reader.take::()?; - reader.white_consume("do")?; - let block = reader.take::()?; - reader.white_consume("end")?; - *reader = *reader; - Ok(Piece::from( - Statement::While(condition, block.it), - &start, - &reader.marker(), - )) - } else if reader.white_consume("if").is_ok() { - let mut conditions = Vec::new(); - let mut blocks = Vec::new(); - let mut branch = |reader: &mut Reader| -> Result<(), Error> { - conditions.push(reader.take::()?); - reader.white_consume("then")?; - blocks.push(reader.take::()?.it); - Ok(()) - }; - branch(reader)?; - let mut block = None; - while reader.white_consume("else").is_ok() { - if reader.consume("if").is_ok() { - branch(reader)?; - } else { - block = Some(reader.take::()?.it); - break - } - } - reader.white_consume("end")?; - *reader = *reader; - Ok(Piece::from( - Statement::If(conditions, blocks, block), - &start, - &reader.marker(), - )) - } else if reader.white_consume("goto").is_ok() { - let name = reader.take::()?; - *reader = *reader; - Ok(Piece::from(Statement::Goto(name), &start, &reader.marker())) - } else if reader.white_consume("do").is_ok() { - let block = reader.take::()?; - *reader = *reader; - Ok(Piece::from( - Statement::Do(block.it), - &start, - &reader.marker(), - )) - } else if reader.white_consume("repeat").is_ok() { - reader.white_consume("repeat")?; - let block = reader.take::()?; - reader.white_consume("until")?; - let condition = reader.take::()?; - *reader = *reader; - Ok(Piece::from( - Statement::Repeat(block.it, condition), - &start, - &reader.marker(), - )) - } else if reader.consume_white("::").is_ok() { - let name = reader.take::()?; - reader.consume_white("::")?; - *reader = *reader; - Ok(Piece::from( - Statement::Label(name), - &start, - &reader.marker(), - )) - } else { - let mut clone = reader.clone(); - if let Ok(call) = clone.take::() { - *reader = clone; - Ok(call.map(|it| Statement::Call(it))) - } else { - let mut clone = reader.clone(); - let vars = clone.take::>>()?; - clone.consume("=")?; - let expressions = clone.take::>>()?; - *reader = clone; - Ok(Piece::from(Statement::Assignment(vars.it,expressions.it),&start,&reader.marker())) - } - } - } -} - -impl Parse for Block { - fn consume(reader: &mut Reader) -> Result, Error> - where - Self: Sized, - { - let mut statements = Vec::new(); - let start = reader.marker(); - while let Ok(statement) = reader.take::() { - statements.push(statement); - } - let expressions = if reader.consume("return").is_ok() { - if reader.clone().take::().is_ok() { // TODO: make this dirty check less silly - let result = reader.take::>>()?; - reader.consume_white(";").ok(); - result.it - } else { - Vec::new() - } - } else { - Vec::new() - }; - Ok(Piece::from( - Block(statements, expressions), - &start, - &reader.marker(), - )) - } -} - -impl Parse for Chunk { - fn consume(reader: &mut Reader) -> Result, Error> - where - Self: Sized, - { - let mut statements = Vec::new(); - let start = reader.marker(); - while !reader.finished() { - let statement = reader.take::()?; - statements.push(statement); - reader.trim()?; - } - let expressions = if reader.white_consume("return").is_ok() { - let result = reader.take::>>()?; - reader.consume_white(";").ok(); - result.it - } else { - Vec::new() - }; - Ok(Piece::from( - Chunk(Piece::from( - Block(statements, expressions), - &start, - &reader.marker(), - )), - &start, - &reader.marker(), - )) - } -} - -fn parse(str: &str) -> Result { - let mut reader = Reader::from(str); - reader.take::().map(|it| it.it) -} - -// COMPILER - -#[derive(Debug)] -struct Table(HashMap, Option>); - -#[derive(Debug, Clone)] -enum Object { - Nil, - Bool(bool), - Number(f64), - Table(Rc), - String(String), - Object(Rc,Option>), -} - -struct Frame<'a> { - begin: Index, // Where this frame begins in the stack (after this) - base: Index, // Where arguments begin (before this) - parent: &'a mut Frame<'a>, -} - -#[derive(Clone)] -enum Register { - Upvalue(Index), - Stack(Index), - Label(Index), - VarArg, -} - -struct Context<'a> { - code: Vec, - symbols: HashMap, - parent: Option<&'a mut Context<'a>>, -} - -impl<'a> Context<'a> { - fn new() -> Context<'a> { - Context { - code: Vec::new(), - symbols: HashMap::new(), - parent: None, - } - } - fn from(&'a mut self) -> Context<'a> { - Context { - code: Vec::new(), - symbols: HashMap::new(), - parent: Some(self) - } - } - fn commit(&mut self) { - - } - fn compile(&mut self, it: &T) -> Result<(), Error> where T: Compile { - it.compile(self) - } - fn function(self) -> Callable { - Callable::Function(self.code) - } -} - -struct Machine { - stack: [Object; 1024], -} - -#[derive(Clone)] -struct Native(Rc Result<(),Error>>); - -impl std::fmt::Debug for Native { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("") - } -} - -#[derive(Debug, Clone)] -enum Callable { - Rust(Native), - Function(Vec), - Closure(Vec,Vec>) -} - -type Index = i16; -type Count = i16; - -#[derive(Debug, Clone)] -enum Literal { - Nil, - Table, - Bool(bool), - Number(f64), - String(String), - Function(Callable), -} - -#[derive(Debug, Clone)] -enum Code { - Call(Callable,Count), - Literal(Literal), - Push(Index), - Binary(BinaryOp), - Unary(UnaryOp), - Up(Index), - Pop(Count), - Jump(Index), - Return(Count), -} - -trait Compile { - fn compile<'a>(&self, context: &mut Context<'a>) -> Result<(),Error>; -} - -impl Compile for Chunk { - fn compile<'a>(&self, context: &mut Context<'a>) -> Result<(), Error> { - Ok(()) - } -} - -impl Machine { - fn load(&self, source: &str) -> Result { - let chunk = parse(source)?; - let mut context = Context::new(); - context.compile(&chunk)?; - Ok(context.function()) - } - fn new() -> Machine { - Machine { - stack: [const {Object::Nil}; 1024] - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn simple() { - let mut machine = Machine::new(); - let result = machine.load(include_str!("assets/script.lua")); - println!("{:#?}", result); - } -} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 911bacc..db89b9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,2 @@ #![recursion_limit = "256"] -pub mod engine; -pub mod lang; \ No newline at end of file +pub mod engine; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 758c277..899b868 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,5 @@ use game::*; fn main() { - - //engine::run().unwrap(); + engine::run().unwrap(); }