diff --git a/Cargo.lock b/Cargo.lock index 54175c9..375be30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1929,8 +1929,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -1940,7 +1950,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -1952,6 +1972,15 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + [[package]] name = "rav1e" version = "0.7.1" @@ -1978,8 +2007,8 @@ dependencies = [ "once_cell", "paste", "profiling", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "simd_helpers", "system-deps", "thiserror 1.0.69", @@ -2112,6 +2141,7 @@ dependencies = [ "glam", "image", "log", + "rand 0.9.1", "thiserror 2.0.12", "vulkano", "vulkano-shaders", diff --git a/Cargo.toml b/Cargo.toml index 50be745..feb1bca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,6 @@ glam = { version = "0.30" } # Log and tracing log = "0.4" env_logger = "0.11" + +# Random +rand = "0.9" diff --git a/res/shaders/vertex.vert b/res/shaders/vertex.vert index 0445e54..8c1df7c 100644 --- a/res/shaders/vertex.vert +++ b/res/shaders/vertex.vert @@ -1,7 +1,8 @@ #version 450 -layout (location = 0) in vec2 position; +layout (location = 0) in vec3 position; layout (location = 1) in vec2 uv; +layout (location = 2) in mat4 model; layout (location = 0) out vec2 fragUv; @@ -13,6 +14,7 @@ layout (set = 0, binding = 0) uniform MVP { void main() { mat4 worldview = uniforms.view * uniforms.world; - gl_Position = uniforms.projection * worldview * vec4(position, 0.0, 1.0); + vec4 modelPosition = model * vec4(position, 1.0); + gl_Position = uniforms.projection * worldview * modelPosition; fragUv = uv; } diff --git a/src/core/render/primitives/transform.rs b/src/core/render/primitives/transform.rs index 88e6422..131e6f1 100644 --- a/src/core/render/primitives/transform.rs +++ b/src/core/render/primitives/transform.rs @@ -1,9 +1,26 @@ +use std::sync::Arc; + use glam::{Mat4, Quat, Vec3}; +use vulkano::{ + Validated, + buffer::{ + AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer, + }, + memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, + pipeline::graphics::vertex_input::Vertex, +}; pub struct Transform { - position: Vec3, - rotation: Quat, - scale: Vec3, + pub position: Vec3, + pub rotation: Quat, + pub scale: Vec3, +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +pub struct TransformRaw { + #[format(R32G32B32A32_SFLOAT)] + pub model: [[f32; 4]; 4], } impl Default for Transform { @@ -29,9 +46,35 @@ impl Transform { self.scale *= scale; } - pub fn get_mat4(&self) -> Mat4 { - Mat4::from_translation(self.position) - * Mat4::from_quat(self.rotation) - * Mat4::from_scale(self.scale) + pub fn into_raw(&self) -> TransformRaw { + TransformRaw { + model: (Mat4::from_translation(self.position) + * Mat4::from_quat(self.rotation) + * Mat4::from_scale(self.scale)) + .to_cols_array_2d(), + } + } + + pub fn create_buffer( + memory_allocator: &Arc, + transforms: &[Transform], + ) -> Result, Validated> { + let transform_raws: Vec = transforms.iter().map(|t| t.into_raw()).collect(); + + let buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::VERTEX_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + transform_raws, + )?; + + Ok(buffer) } } diff --git a/src/core/render/primitives/vertex.rs b/src/core/render/primitives/vertex.rs index 566df22..166ac44 100644 --- a/src/core/render/primitives/vertex.rs +++ b/src/core/render/primitives/vertex.rs @@ -10,3 +10,13 @@ pub struct Vertex2D { #[format(R32G32_SFLOAT)] pub uv: [f32; 2], } + +#[derive(BufferContents, Vertex)] +#[repr(C)] +pub struct Vertex3D { + #[format(R32G32B32_SFLOAT)] + pub position: [f32; 3], + + #[format(R32G32_SFLOAT)] + pub uv: [f32; 2], +} diff --git a/src/game/assets/square.rs b/src/game/assets/square.rs index acca0b6..ca16b93 100644 --- a/src/game/assets/square.rs +++ b/src/game/assets/square.rs @@ -30,26 +30,26 @@ use vulkano::{ }; use crate::core::render::{ - primitives::{mvp::MVP, vertex::Vertex2D}, + primitives::{mvp::MVP, transform::TransformRaw, vertex::Vertex3D}, texture::Texture, }; -const VERTICES: [Vertex2D; 4] = [ - Vertex2D { - position: [0.0, 0.0], +const VERTICES: [Vertex3D; 4] = [ + Vertex3D { + position: [-0.5, -0.5, 0.0], uv: [0.0, 0.0], }, - Vertex2D { - position: [0.0, 5.0], - uv: [0.0, 0.5], + Vertex3D { + position: [-0.5, 0.5, 0.0], + uv: [0.0, 1.0], }, - Vertex2D { - position: [10.0, 0.0], + Vertex3D { + position: [0.5, -0.5, 0.0], uv: [1.0, 0.0], }, - Vertex2D { - position: [10.0, 5.0], - uv: [1.0, 0.5], + Vertex3D { + position: [0.5, 0.5, 0.0], + uv: [1.0, 1.0], }, ]; @@ -74,7 +74,7 @@ pub mod shaders { } pub struct Square { - vertex_buffer: Subbuffer<[Vertex2D]>, + vertex_buffer: Subbuffer<[Vertex3D]>, index_buffer: Subbuffer<[u32]>, pipeline: Arc, } @@ -120,7 +120,8 @@ impl Square { .entry_point("main") .ok_or("Failed find main entry point of fragment shader".to_string())?; - let vertex_input_state = Vertex2D::per_vertex().definition(&vs)?; + let vertex_input_state = + [Vertex3D::per_vertex(), TransformRaw::per_instance()].definition(&vs)?; let stages = [ PipelineShaderStageCreateInfo::new(vs), @@ -207,6 +208,7 @@ impl Square { command_buffer: &mut AutoCommandBufferBuilder, descriptor_set_allocator: &Arc, mvp_uniform: &Subbuffer<[MVP]>, + transform_uniform: &Subbuffer<[TransformRaw]>, texture: &Texture, ) -> Result<(), Box> { let layouts = self.pipeline.layout().set_layouts(); @@ -235,11 +237,18 @@ impl Square { 0, vec![uniform_descriptor_set, texture_descriptor_set], )?; - command_buffer.bind_vertex_buffers(0, self.vertex_buffer.clone())?; + command_buffer + .bind_vertex_buffers(0, (self.vertex_buffer.clone(), transform_uniform.clone()))?; command_buffer.bind_index_buffer(self.index_buffer.clone())?; unsafe { - command_buffer.draw_indexed(INDICES.len() as u32, 1, 0, 0, 0)?; + command_buffer.draw_indexed( + INDICES.len() as u32, + transform_uniform.len() as u32, + 0, + 0, + 0, + )?; } Ok(()) diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index 7063d58..5fd757a 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -1,11 +1,14 @@ use std::{error::Error, sync::Arc}; use crate::core::render::primitives::camera::Camera3D; +use crate::core::render::primitives::transform::Transform; use crate::core::render::texture::Texture; use crate::core::scene::Scene; use crate::core::scene::SceneContext; use egui_winit_vulkano::{Gui, egui}; -use glam::Mat4; +use glam::EulerRot; +use glam::Quat; +use glam::Vec3; use vulkano::{ command_buffer::{ AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract, @@ -21,6 +24,7 @@ use super::assets::square::Square; pub struct MainSceneState { square: Square, + instances: Vec, camera: Camera3D, texture: Texture, speed: f32, @@ -43,6 +47,27 @@ impl Scene for MainScene { scene_context.swapchain_format, )?; + let num_instances = 100; + let instance_size = 10.0; + let instance_spacing = 10.0; + let num_instances_per_row = (num_instances as f32 / instance_spacing as f32).ceil() as u32; + let instances: Vec = (0..num_instances) + .map(|i| Transform { + position: Vec3::new( + (i % num_instances_per_row) as f32 * (instance_spacing + instance_size), + 0.0, + (i / num_instances_per_row) as f32 * (instance_spacing + instance_size), + ), + rotation: Quat::from_euler( + EulerRot::XYZ, + 0.0, + rand::random_range(0.0..=360.0), + 0.0, + ), + scale: Vec3::new(instance_size, instance_size, instance_size), + }) + .collect(); + let camera = Camera3D::new( scene_context.aspect_ratio, std::f32::consts::FRAC_PI_2, @@ -69,6 +94,7 @@ impl Scene for MainScene { self.state = Some(MainSceneState { square, + instances, camera, texture, speed: 50.0, @@ -129,12 +155,16 @@ impl Scene for MainScene { .camera .create_buffer(&scene_context.memory_allocator)?; + let transform_uniform = + Transform::create_buffer(&scene_context.memory_allocator, &state.instances)?; + state .square .render( &mut builder, &scene_context.descriptor_set_allocator, &camera_uniform, + &transform_uniform, &state.texture, ) .unwrap();