Add instances support

This commit is contained in:
Florian RICHER 2025-05-29 17:13:01 +02:00
parent f8b81f3269
commit 77c717f90b
Signed by: florian.richer
GPG key ID: C73D37CBED7BFC77
7 changed files with 158 additions and 31 deletions

40
Cargo.lock generated
View file

@ -1929,8 +1929,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [ dependencies = [
"libc", "libc",
"rand_chacha", "rand_chacha 0.3.1",
"rand_core", "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]] [[package]]
@ -1940,7 +1950,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [ dependencies = [
"ppv-lite86", "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]] [[package]]
@ -1952,6 +1972,15 @@ dependencies = [
"getrandom 0.2.16", "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]] [[package]]
name = "rav1e" name = "rav1e"
version = "0.7.1" version = "0.7.1"
@ -1978,8 +2007,8 @@ dependencies = [
"once_cell", "once_cell",
"paste", "paste",
"profiling", "profiling",
"rand", "rand 0.8.5",
"rand_chacha", "rand_chacha 0.3.1",
"simd_helpers", "simd_helpers",
"system-deps", "system-deps",
"thiserror 1.0.69", "thiserror 1.0.69",
@ -2112,6 +2141,7 @@ dependencies = [
"glam", "glam",
"image", "image",
"log", "log",
"rand 0.9.1",
"thiserror 2.0.12", "thiserror 2.0.12",
"vulkano", "vulkano",
"vulkano-shaders", "vulkano-shaders",

View file

@ -23,3 +23,6 @@ glam = { version = "0.30" }
# Log and tracing # Log and tracing
log = "0.4" log = "0.4"
env_logger = "0.11" env_logger = "0.11"
# Random
rand = "0.9"

View file

@ -1,7 +1,8 @@
#version 450 #version 450
layout (location = 0) in vec2 position; layout (location = 0) in vec3 position;
layout (location = 1) in vec2 uv; layout (location = 1) in vec2 uv;
layout (location = 2) in mat4 model;
layout (location = 0) out vec2 fragUv; layout (location = 0) out vec2 fragUv;
@ -13,6 +14,7 @@ layout (set = 0, binding = 0) uniform MVP {
void main() { void main() {
mat4 worldview = uniforms.view * uniforms.world; 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; fragUv = uv;
} }

View file

@ -1,9 +1,26 @@
use std::sync::Arc;
use glam::{Mat4, Quat, Vec3}; 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 { pub struct Transform {
position: Vec3, pub position: Vec3,
rotation: Quat, pub rotation: Quat,
scale: Vec3, pub scale: Vec3,
}
#[derive(BufferContents, Vertex)]
#[repr(C)]
pub struct TransformRaw {
#[format(R32G32B32A32_SFLOAT)]
pub model: [[f32; 4]; 4],
} }
impl Default for Transform { impl Default for Transform {
@ -29,9 +46,35 @@ impl Transform {
self.scale *= scale; self.scale *= scale;
} }
pub fn get_mat4(&self) -> Mat4 { pub fn into_raw(&self) -> TransformRaw {
Mat4::from_translation(self.position) TransformRaw {
* Mat4::from_quat(self.rotation) model: (Mat4::from_translation(self.position)
* Mat4::from_scale(self.scale) * Mat4::from_quat(self.rotation)
* Mat4::from_scale(self.scale))
.to_cols_array_2d(),
}
}
pub fn create_buffer(
memory_allocator: &Arc<StandardMemoryAllocator>,
transforms: &[Transform],
) -> Result<Subbuffer<[TransformRaw]>, Validated<AllocateBufferError>> {
let transform_raws: Vec<TransformRaw> = 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)
} }
} }

View file

@ -10,3 +10,13 @@ pub struct Vertex2D {
#[format(R32G32_SFLOAT)] #[format(R32G32_SFLOAT)]
pub uv: [f32; 2], 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],
}

View file

@ -30,26 +30,26 @@ use vulkano::{
}; };
use crate::core::render::{ use crate::core::render::{
primitives::{mvp::MVP, vertex::Vertex2D}, primitives::{mvp::MVP, transform::TransformRaw, vertex::Vertex3D},
texture::Texture, texture::Texture,
}; };
const VERTICES: [Vertex2D; 4] = [ const VERTICES: [Vertex3D; 4] = [
Vertex2D { Vertex3D {
position: [0.0, 0.0], position: [-0.5, -0.5, 0.0],
uv: [0.0, 0.0], uv: [0.0, 0.0],
}, },
Vertex2D { Vertex3D {
position: [0.0, 5.0], position: [-0.5, 0.5, 0.0],
uv: [0.0, 0.5], uv: [0.0, 1.0],
}, },
Vertex2D { Vertex3D {
position: [10.0, 0.0], position: [0.5, -0.5, 0.0],
uv: [1.0, 0.0], uv: [1.0, 0.0],
}, },
Vertex2D { Vertex3D {
position: [10.0, 5.0], position: [0.5, 0.5, 0.0],
uv: [1.0, 0.5], uv: [1.0, 1.0],
}, },
]; ];
@ -74,7 +74,7 @@ pub mod shaders {
} }
pub struct Square { pub struct Square {
vertex_buffer: Subbuffer<[Vertex2D]>, vertex_buffer: Subbuffer<[Vertex3D]>,
index_buffer: Subbuffer<[u32]>, index_buffer: Subbuffer<[u32]>,
pipeline: Arc<GraphicsPipeline>, pipeline: Arc<GraphicsPipeline>,
} }
@ -120,7 +120,8 @@ impl Square {
.entry_point("main") .entry_point("main")
.ok_or("Failed find main entry point of fragment shader".to_string())?; .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 = [ let stages = [
PipelineShaderStageCreateInfo::new(vs), PipelineShaderStageCreateInfo::new(vs),
@ -207,6 +208,7 @@ impl Square {
command_buffer: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>, command_buffer: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>, descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
mvp_uniform: &Subbuffer<[MVP]>, mvp_uniform: &Subbuffer<[MVP]>,
transform_uniform: &Subbuffer<[TransformRaw]>,
texture: &Texture, texture: &Texture,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
let layouts = self.pipeline.layout().set_layouts(); let layouts = self.pipeline.layout().set_layouts();
@ -235,11 +237,18 @@ impl Square {
0, 0,
vec![uniform_descriptor_set, texture_descriptor_set], 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())?; command_buffer.bind_index_buffer(self.index_buffer.clone())?;
unsafe { 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(()) Ok(())

View file

@ -1,11 +1,14 @@
use std::{error::Error, sync::Arc}; use std::{error::Error, sync::Arc};
use crate::core::render::primitives::camera::Camera3D; use crate::core::render::primitives::camera::Camera3D;
use crate::core::render::primitives::transform::Transform;
use crate::core::render::texture::Texture; use crate::core::render::texture::Texture;
use crate::core::scene::Scene; use crate::core::scene::Scene;
use crate::core::scene::SceneContext; use crate::core::scene::SceneContext;
use egui_winit_vulkano::{Gui, egui}; use egui_winit_vulkano::{Gui, egui};
use glam::Mat4; use glam::EulerRot;
use glam::Quat;
use glam::Vec3;
use vulkano::{ use vulkano::{
command_buffer::{ command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract, AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract,
@ -21,6 +24,7 @@ use super::assets::square::Square;
pub struct MainSceneState { pub struct MainSceneState {
square: Square, square: Square,
instances: Vec<Transform>,
camera: Camera3D, camera: Camera3D,
texture: Texture, texture: Texture,
speed: f32, speed: f32,
@ -43,6 +47,27 @@ impl Scene for MainScene {
scene_context.swapchain_format, 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<Transform> = (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( let camera = Camera3D::new(
scene_context.aspect_ratio, scene_context.aspect_ratio,
std::f32::consts::FRAC_PI_2, std::f32::consts::FRAC_PI_2,
@ -69,6 +94,7 @@ impl Scene for MainScene {
self.state = Some(MainSceneState { self.state = Some(MainSceneState {
square, square,
instances,
camera, camera,
texture, texture,
speed: 50.0, speed: 50.0,
@ -129,12 +155,16 @@ impl Scene for MainScene {
.camera .camera
.create_buffer(&scene_context.memory_allocator)?; .create_buffer(&scene_context.memory_allocator)?;
let transform_uniform =
Transform::create_buffer(&scene_context.memory_allocator, &state.instances)?;
state state
.square .square
.render( .render(
&mut builder, &mut builder,
&scene_context.descriptor_set_allocator, &scene_context.descriptor_set_allocator,
&camera_uniform, &camera_uniform,
&transform_uniform,
&state.texture, &state.texture,
) )
.unwrap(); .unwrap();