Compare commits
13 Commits
master
...
feature/cu
Author | SHA1 | Date | |
---|---|---|---|
|
e6fb210c3b | ||
acd7edb9ed | |||
4e9848226a | |||
4cfb249dd9 | |||
7f959e05cd | |||
8736e63fb6 | |||
35ad1762d6 | |||
62bbf97c6a | |||
4d95a873b0 | |||
2d22de28b9 | |||
9bb2f4c20d | |||
72ebb412f0 | |||
f3167ac12d |
@ -2,6 +2,8 @@
|
||||
name = "rust-game-engine"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["phito", "endmove"]
|
||||
repository = "https://git.endmove.eu/phito/rust-game-engine.git"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -11,3 +13,9 @@ env_logger = "0.10.0"
|
||||
pollster = "0.3.0"
|
||||
wgpu = "0.17.0"
|
||||
winit = "0.28.6"
|
||||
criterion = { version = "0.5.1", features = ["html_reports"] }
|
||||
|
||||
|
||||
[[bench]]
|
||||
name = "voxel_map_benchmark"
|
||||
harness = false
|
BIN
assets/icon.ico
Normal file
BIN
assets/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
37
benches/voxel_map_benchmark.rs
Normal file
37
benches/voxel_map_benchmark.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
const MAP_WIDTH: usize = 100;
|
||||
const MAP_HEIGHT: usize = 100;
|
||||
const MAP_DEPTH: usize = 100;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Voxel {
|
||||
r: u8, g: u8, b: u8
|
||||
}
|
||||
|
||||
fn voxel_vec_1d(data: Vec<Voxel>) -> Voxel {
|
||||
const X: usize = 0;
|
||||
const Y: usize = 0;
|
||||
const Z: usize = 0;
|
||||
|
||||
data[X + Y * MAP_WIDTH + Z * MAP_WIDTH * MAP_HEIGHT]
|
||||
}
|
||||
|
||||
fn voxel_vec_3d(data: Vec<Vec<Vec<Voxel>>>) -> Voxel {
|
||||
const X: usize = 0;
|
||||
const Y: usize = 0;
|
||||
const Z: usize = 0;
|
||||
|
||||
data[X][Y][Z]
|
||||
}
|
||||
|
||||
fn voxel_map_benchmark(c: &mut Criterion) {
|
||||
let vec_1d = vec![Voxel { r: 0, g: 0, b: 0 }; MAP_WIDTH * MAP_HEIGHT * MAP_DEPTH];
|
||||
let vec_3d = vec![vec![vec![Voxel { r: 0, g: 0, b: 0 }; MAP_DEPTH]; MAP_HEIGHT]; MAP_WIDTH];
|
||||
|
||||
c.bench_function("voxels in 1d vector", |b| b.iter(|| voxel_vec_1d(black_box(vec_1d.clone()))));
|
||||
c.bench_function("voxels in 3d vector", |b| b.iter(|| voxel_vec_3d(black_box(vec_3d.clone()))));
|
||||
}
|
||||
|
||||
criterion_group!(benches, voxel_map_benchmark);
|
||||
criterion_main!(benches);
|
27
shaders/cube.wgsl
Normal file
27
shaders/cube.wgsl
Normal file
@ -0,0 +1,27 @@
|
||||
struct VertexOutput {
|
||||
@location(0) color: vec3<f32>,
|
||||
@builtin(position) position: vec4<f32>
|
||||
};
|
||||
|
||||
struct Locals {
|
||||
transform: mat4x4<f32>
|
||||
};
|
||||
|
||||
@group(0) @binding(0)
|
||||
var r_locals: Locals;
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
@location(0) position: vec4<f32>,
|
||||
@location(1) color: vec3<f32>,
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.color = color;
|
||||
out.position = r_locals.transform * position;
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(in.color.r, in.color.g, in.color.b, 1.0);
|
||||
}
|
58
src/cube.rs
Normal file
58
src/cube.rs
Normal file
@ -0,0 +1,58 @@
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Vertex {
|
||||
position: [f32; 4],
|
||||
color: [f32; 3]
|
||||
}
|
||||
|
||||
fn vertex(pos: [i8; 3], color: [i8; 3]) -> Vertex {
|
||||
Vertex {
|
||||
position: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0],
|
||||
color: [color[0] as f32, color[1] as f32, color[2] as f32]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_vertices() -> (Vec<Vertex>, Vec<u16>) {
|
||||
let vertex_data = [
|
||||
// top (0, 0, 1)
|
||||
vertex([-1, -1, 1], [1, 0, 0]),
|
||||
vertex([1, -1, 1], [1, 0, 0]),
|
||||
vertex([1, 1, 1], [1, 0, 0]),
|
||||
vertex([-1, 1, 1], [1, 0, 0]),
|
||||
// bottom (0, 0, -1)
|
||||
vertex([-1, 1, -1], [0, 1, 0]),
|
||||
vertex([1, 1, -1], [0, 1, 0]),
|
||||
vertex([1, -1, -1], [0, 1, 0]),
|
||||
vertex([-1, -1, -1],[0, 1, 0]),
|
||||
// right (1, 0, 0)
|
||||
vertex([1, -1, -1], [0, 0, 1]),
|
||||
vertex([1, 1, -1], [0, 0, 1]),
|
||||
vertex([1, 1, 1], [0, 0, 1]),
|
||||
vertex([1, -1, 1], [0, 0, 1]),
|
||||
// left (-1, 0, 0)
|
||||
vertex([-1, -1, 1], [1, 1, 1]),
|
||||
vertex([-1, 1, 1], [1, 1, 1]),
|
||||
vertex([-1, 1, -1], [1, 1, 1]),
|
||||
vertex([-1, -1, -1], [1, 1, 1]),
|
||||
// front (0, 1, 0)
|
||||
vertex([1, 1, -1], [1, 1, 0]),
|
||||
vertex([-1, 1, -1], [1, 1, 0]),
|
||||
vertex([-1, 1, 1], [1, 1, 0]),
|
||||
vertex([1, 1, 1], [1, 1, 0]),
|
||||
// back (0, -1, 0)
|
||||
vertex([1, -1, 1], [0, 0, 0]),
|
||||
vertex([-1, -1, 1], [0, 0, 0]),
|
||||
vertex([-1, -1, -1], [0, 0, 0]),
|
||||
vertex([1, -1, -1], [0, 0, 0]),
|
||||
];
|
||||
|
||||
let index_data: &[u16] = &[
|
||||
0, 1, 2, 2, 3, 0, // top
|
||||
4, 5, 6, 6, 7, 4, // bottom
|
||||
8, 9, 10, 10, 11, 8, // right
|
||||
12, 13, 14, 14, 15, 12, // left
|
||||
16, 17, 18, 18, 19, 16, // front
|
||||
20, 21, 22, 22, 23, 20, // back
|
||||
];
|
||||
|
||||
(vertex_data.to_vec(), index_data.to_vec())
|
||||
}
|
124
src/main.rs
124
src/main.rs
@ -1,23 +1,30 @@
|
||||
mod voxel;
|
||||
mod cube;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use wgpu::util::DeviceExt;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::Window,
|
||||
window::{WindowBuilder, Window},
|
||||
};
|
||||
use std::mem;
|
||||
|
||||
async fn run(event_loop: EventLoop<()>, window: Window) {
|
||||
let size = window.inner_size();
|
||||
|
||||
let instance = wgpu::Instance::default();
|
||||
let surface = unsafe { instance.create_surface(&window) }
|
||||
.expect("Failed to create surface");
|
||||
|
||||
let adapter_options = wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::default(),
|
||||
force_fallback_adapter: false,
|
||||
compatible_surface: None,
|
||||
};
|
||||
|
||||
let surface = unsafe { 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),
|
||||
})
|
||||
.request_adapter(&adapter_options)
|
||||
.await
|
||||
.expect("Failed to find an appropriate adapter");
|
||||
|
||||
@ -36,35 +43,98 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
|
||||
.await
|
||||
.expect("Failed to create device");
|
||||
|
||||
// Load the shaders from disk
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
let (vertex_data, index_data) = cube::create_vertices();
|
||||
|
||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Vertex Buffer"),
|
||||
contents: bytemuck::cast_slice(&vertex_data),
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
});
|
||||
|
||||
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Index Buffer"),
|
||||
contents: bytemuck::cast_slice(&index_data),
|
||||
usage: wgpu::BufferUsages::INDEX,
|
||||
});
|
||||
|
||||
let vertex_size = mem::size_of::<cube::Vertex>();
|
||||
let vertex_buffer = wgpu::VertexBufferLayout {
|
||||
array_stride: vertex_size as wgpu::BufferAddress,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &[
|
||||
wgpu::VertexAttribute {
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
offset: mem::size_of::<f32>() as u64 * 4,
|
||||
shader_location: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: None,
|
||||
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: wgpu::BufferSize::new(64),
|
||||
},
|
||||
count: None,
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: None,
|
||||
bind_group_layouts: &[],
|
||||
label: Some("Pipeline Layout"),
|
||||
bind_group_layouts: &[&bind_group_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
// Create bind group
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: uniform_buffer.as_entire_binding(),
|
||||
}
|
||||
],
|
||||
label: None,
|
||||
});
|
||||
|
||||
// Load the shaders from disk
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: None,
|
||||
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("../shaders/cube.wgsl"))),
|
||||
});
|
||||
|
||||
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,
|
||||
label: Some("Render Pipeline"),
|
||||
layout: Some(&pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[],
|
||||
buffers: &[vertex_buffer],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
targets: &[Some(swapchain_format.into())],
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState::default(),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
cull_mode: Some(wgpu::Face::Back),
|
||||
..Default::default()
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
@ -124,7 +194,10 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
rpass.set_pipeline(&render_pipeline);
|
||||
rpass.draw(0..3, 0..1);
|
||||
rpass.set_bind_group(0, &bind_group, &[]);
|
||||
rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint16);
|
||||
rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
|
||||
rpass.draw_indexed(0..index_data.len() as u32, 0, 0..1);
|
||||
}
|
||||
|
||||
queue.submit(Some(encoder.finish()));
|
||||
@ -140,8 +213,23 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Instantiate event loop and win builder
|
||||
let event_loop = EventLoop::new();
|
||||
let window = winit::window::Window::new(&event_loop).unwrap();
|
||||
let win_builder = WindowBuilder::new();
|
||||
|
||||
// Create and configure window
|
||||
let window_def_size = winit::dpi::LogicalSize::new(640, 360);
|
||||
// let window_icon = Icon::from_path("../assets/icon.ico", None).expect("Failed to load icon");
|
||||
let window = win_builder.with_title("Rust Game Engine")
|
||||
.with_inner_size(window_def_size)
|
||||
.with_min_inner_size(window_def_size)
|
||||
.with_resizable(true)
|
||||
.with_enabled_buttons(winit::window::WindowButtons::from_bits(3).unwrap())
|
||||
.with_transparent(true)
|
||||
// .with_window_icon(Some(window_icon))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
env_logger::init();
|
||||
|
@ -1,11 +0,0 @@
|
||||
@vertex
|
||||
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
|
||||
let x = f32(i32(in_vertex_index) - 1);
|
||||
let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
|
||||
return vec4<f32>(x, y, 0.0, 1.0);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main() -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
59
src/voxel.rs
Normal file
59
src/voxel.rs
Normal file
@ -0,0 +1,59 @@
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
struct Voxel {
|
||||
r: u8, g: u8, b: u8
|
||||
}
|
||||
|
||||
const AIR_VOXEL: Voxel = Voxel { r: 0, g: 0, b: 0};
|
||||
|
||||
struct VoxelMap {
|
||||
data: Vec<Voxel>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
depth: usize
|
||||
}
|
||||
|
||||
impl VoxelMap {
|
||||
fn new(width: usize, height: usize, depth: usize) -> Self {
|
||||
Self {
|
||||
data: vec![AIR_VOXEL; width * height * depth],
|
||||
width,
|
||||
height,
|
||||
depth
|
||||
}
|
||||
}
|
||||
|
||||
fn set(&mut self, x: usize, y: usize, z: usize, voxel: Voxel) {
|
||||
if x < self.width && y < self.height && z < self.depth {
|
||||
self.data[x + y * self.width + z * self.width * self.height] = voxel;
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, x: usize, y: usize, z: usize) -> Voxel {
|
||||
if x < self.width && y < self.height && z < self.depth {
|
||||
self.data[x + y * self.width + z * self.width * self.height]
|
||||
} else {
|
||||
AIR_VOXEL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_voxel_map() {
|
||||
let mut map = VoxelMap::new(10, 10, 10);
|
||||
map.set(0, 0, 0, Voxel { r: 255, g: 0, b: 0 });
|
||||
assert_eq!(map.get(0, 0, 0), Voxel { r: 255, g: 0, b: 0 });
|
||||
assert_eq!(map.get(1, 0, 0), AIR_VOXEL);
|
||||
assert_eq!(map.get(0, 1, 0), AIR_VOXEL);
|
||||
assert_eq!(map.get(0, 0, 1), AIR_VOXEL);
|
||||
assert_eq!(map.get(10, 0, 0), AIR_VOXEL);
|
||||
assert_eq!(map.get(0, 10, 0), AIR_VOXEL);
|
||||
assert_eq!(map.get(0, 0, 10), AIR_VOXEL);
|
||||
|
||||
// out of bounds
|
||||
assert_eq!(map.get(0, 0, 11), AIR_VOXEL);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user