Add block highlighting

This commit is contained in:
Sijmen 2021-05-30 00:12:33 +02:00
parent 3674f1020f
commit 8bf15dc924
Signed by: vijfhoek
GPG key ID: 82D05C89B28B0DAE
5 changed files with 87 additions and 60 deletions

View file

@ -1,5 +1,5 @@
use crate::instance::Instance;
use cgmath::{Deg, InnerSpace, Quaternion, Rotation3, Vector3};
use cgmath::{InnerSpace, Vector3};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -8,15 +8,6 @@ pub enum BlockType {
Cobblestone,
}
impl BlockType {
pub fn texture_index(self) -> u32 {
match self {
Self::Dirt => 0,
Self::Cobblestone => 1,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Block {
pub block_type: BlockType,
@ -26,6 +17,7 @@ const CHUNK_SIZE: usize = 16;
pub struct Chunk {
pub blocks: [[[Option<Block>; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE],
pub highlighted: Option<Vector3<usize>>,
}
impl Chunk {
@ -38,8 +30,10 @@ impl Chunk {
if let Some(block) = block {
let position = Vector3::new(x as f32, y as f32, z as f32);
let instances = map.entry(block.block_type).or_default();
instances.push(Instance {
position: position.into(),
highlighted: (self.highlighted == Some(Vector3::new(x, y, z))) as i32,
});
}
}
@ -49,6 +43,14 @@ impl Chunk {
map.drain().collect()
}
fn get_block(&self, x: usize, y: usize, z: usize) -> Option<Option<&Block>> {
self.blocks
.get(y)
.and_then(|blocks| blocks.get(z))
.and_then(|blocks| blocks.get(x))
.map(|block| block.as_ref())
}
fn calc_scale(a: Vector3<f32>, b: f32) -> f32 {
if b == 0.0 {
f32::INFINITY
@ -57,8 +59,8 @@ impl Chunk {
}
}
pub fn dda(&self, position: Vector3<f32>, direction: Vector3<f32>) -> Option<Vector3<usize>> {
assert!(f32::abs(direction.magnitude() - 1.0) < f32::EPSILON);
pub fn dda(&self, origin: Vector3<f32>, direction: Vector3<f32>) -> Option<Vector3<usize>> {
let direction = direction.normalize();
let scale = Vector3::new(
Self::calc_scale(direction, direction.x),
@ -66,28 +68,24 @@ impl Chunk {
Self::calc_scale(direction, direction.z),
);
let mut new_position = position;
let mut position = origin;
let mut lengths = Vector3::new(0.0, 0.0, 0.0);
loop {
let new_lengths = lengths + scale;
if new_lengths.x < f32::min(new_lengths.y, new_lengths.z) {
lengths.x = new_lengths.x;
new_position += direction * scale.x;
position += direction * scale.x;
} else if new_lengths.y < f32::min(new_lengths.x, new_lengths.z) {
lengths.y = new_lengths.y;
new_position += direction * scale.y;
position += direction * scale.y;
} else if new_lengths.z < f32::min(new_lengths.x, new_lengths.y) {
lengths.z = new_lengths.z;
new_position += direction * scale.z;
position += direction * scale.z;
}
let pos_usize = new_position.map(|field| field.round() as usize);
let block = self
.blocks
.get(pos_usize.y)
.and_then(|a| a.get(pos_usize.z))
.and_then(|a| a.get(pos_usize.x));
let position_rounded = position.map(|field| field.round() as usize);
let block = self.get_block(position_rounded.x, position_rounded.y, position_rounded.z);
match block {
None => {
@ -97,7 +95,7 @@ impl Chunk {
Some(None) => (),
Some(Some(_)) => {
// Intersected with a block, round position to coordinates and return it.
break Some(pos_usize);
break Some(position_rounded);
}
}
}

View file

@ -2,6 +2,7 @@
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Instance {
pub position: [f32; 3],
pub highlighted: i32,
}
impl Instance {
@ -10,11 +11,20 @@ impl Instance {
wgpu::VertexBufferLayout {
array_stride: mem::size_of::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Instance,
attributes: &[wgpu::VertexAttribute {
offset: 0,
shader_location: 5,
format: wgpu::VertexFormat::Float32x3,
}],
attributes: &[
// position: [f32; 3]
wgpu::VertexAttribute {
offset: 0,
shader_location: 5,
format: wgpu::VertexFormat::Float32x3,
},
// highlighted: i32
wgpu::VertexAttribute {
offset: 12,
shader_location: 6,
format: wgpu::VertexFormat::Sint32,
},
],
}
}
}

View file

@ -27,10 +27,12 @@ struct VertexOutput {
[[location(0)]] texture_coordinates: vec2<f32>;
[[location(1)]] world_normal: vec3<f32>;
[[location(2)]] world_position: vec3<f32>;
[[location(3)]] highlighted: i32;
};
struct InstanceInput {
[[location(5)]] position: vec3<f32>;
[[location(6)]] highlighted: i32;
};
@ -41,6 +43,7 @@ fn main(model: VertexInput, instance: InstanceInput) -> VertexOutput {
out.world_normal = model.normal;
out.world_position = model.position + instance.position;
out.clip_position = uniforms.view_projection * vec4<f32>(out.world_position, 1.0);
out.highlighted = instance.highlighted;
return out;
}
@ -65,6 +68,10 @@ fn main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
let specular_strength = pow(max(dot(in.world_normal, half_direction), 0.0), 32.0);
let specular_color = specular_strength * light.color;
let result = (ambient_color + diffuse_color + specular_color) * object_color.xyz;
var result: vec3<f32> = (ambient_color + diffuse_color + specular_color) * object_color.xyz;
if (in.highlighted != 0) {
result = result + 0.5;
}
return vec4<f32>(result, object_color.a);
}

View file

@ -1,6 +1,6 @@
use std::time::Duration;
use cgmath::{InnerSpace, Rad};
use cgmath::{EuclideanSpace, InnerSpace, Rad};
use wgpu::util::{BufferInitDescriptor, DeviceExt};
use winit::{
event::{DeviceEvent, ElementState, KeyboardInput, VirtualKeyCode},
@ -21,12 +21,12 @@ pub const CROSSHAIR_VERTICES: &[Vertex] = &[
},
Vertex {
position: [UI_SCALE_X * 8.0, UI_SCALE_Y * 8.0, 0.0],
texture_coordinates: [256.0 / 256.0, 0.0 / 256.0],
texture_coordinates: [1.0, 0.0 / 256.0],
normal: [0.0, 0.0, 0.0],
},
Vertex {
position: [UI_SCALE_X * 8.0, -UI_SCALE_Y * 8.0, 0.0],
texture_coordinates: [256.0 / 256.0, 16.0 / 256.0],
texture_coordinates: [1.0, 16.0 / 256.0],
normal: [0.0, 0.0, 0.0],
},
Vertex {
@ -77,7 +77,6 @@ pub struct State {
world_state: WorldState,
ui_texture_bind_group_layout: wgpu::BindGroupLayout,
ui_texture_bind_group: wgpu::BindGroup,
ui_render_pipeline: wgpu::RenderPipeline,
ui_crosshair_vertex_buffer: wgpu::Buffer,
@ -302,7 +301,6 @@ impl State {
ui_render_pipeline,
ui_texture_bind_group,
ui_texture_bind_group_layout,
ui_crosshair_vertex_buffer,
ui_crosshair_index_buffer,
@ -346,17 +344,27 @@ impl State {
}
fn update_camera(&mut self, dx: f64, dy: f64) {
self.world_state.camera.yaw += Rad(dx as f32 * 0.003);
self.world_state.camera.pitch -= Rad(dy as f32 * 0.003);
let camera = &mut self.world_state.camera;
camera.yaw += Rad(dx as f32 * 0.003);
camera.pitch -= Rad(dy as f32 * 0.003);
if self.world_state.camera.pitch < Rad::from(cgmath::Deg(-80.0)) {
self.world_state.camera.pitch = Rad::from(cgmath::Deg(-80.0));
} else if self.world_state.camera.pitch > Rad::from(cgmath::Deg(89.9)) {
self.world_state.camera.pitch = Rad::from(cgmath::Deg(89.9));
if camera.pitch < Rad::from(cgmath::Deg(-80.0)) {
camera.pitch = Rad::from(cgmath::Deg(-80.0));
} else if camera.pitch > Rad::from(cgmath::Deg(89.9)) {
camera.pitch = Rad::from(cgmath::Deg(89.9));
}
}
fn update_aim(&mut self) {}
fn update_aim(&mut self) {
let camera = &self.world_state.camera;
let chunk = &mut self.world_state.chunk;
let coords = chunk.dda(camera.position.to_vec(), camera.direction());
if coords != chunk.highlighted {
chunk.highlighted = coords;
self.world_state.update_chunk(&self.render_queue);
}
}
fn input_mouse(&mut self, dx: f64, dy: f64) {
if self.mouse_grabbed {
@ -443,10 +451,8 @@ impl State {
wgpu::IndexFormat::Uint16,
);
for (i, (block_type, instance_list)) in
self.world_state.instance_lists.iter().enumerate()
{
let (_, instance_buffer) = &self.world_state.instance_buffers[i];
for (block_type, instance_list) in &self.world_state.instance_lists {
let instance_buffer = &self.world_state.instance_buffers[block_type];
let texture_bind_group = &self.world_state.texture_bind_groups[block_type];
render_pass.set_bind_group(0, texture_bind_group, &[]);

View file

@ -1,6 +1,5 @@
use std::collections::HashMap;
use cgmath::EuclideanSpace;
use wgpu::util::{BufferInitDescriptor, DeviceExt};
use winit::dpi::PhysicalSize;
@ -26,9 +25,10 @@ pub struct WorldState {
pub instance_lists: Vec<(BlockType, Vec<Instance>)>,
pub vertex_buffer: wgpu::Buffer,
pub index_buffer: wgpu::Buffer,
pub instance_buffers: Vec<(BlockType, wgpu::Buffer)>,
pub instance_buffers: HashMap<BlockType, wgpu::Buffer>,
pub depth_texture: Texture,
pub light_bind_group: wgpu::BindGroup,
pub chunk: Chunk,
}
impl WorldState {
@ -294,7 +294,7 @@ impl WorldState {
chunk: &Chunk,
) -> (
Vec<(BlockType, Vec<Instance>)>,
Vec<(BlockType, wgpu::Buffer)>,
HashMap<BlockType, wgpu::Buffer>,
) {
let instance_lists = chunk.to_instances();
@ -306,7 +306,7 @@ impl WorldState {
render_device.create_buffer_init(&BufferInitDescriptor {
label: Some("instance_buffer"),
contents: bytemuck::cast_slice(&instance_list),
usage: wgpu::BufferUsage::VERTEX,
usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,
}),
)
})
@ -315,12 +315,24 @@ impl WorldState {
(instance_lists, instance_buffers)
}
pub fn update_chunk(&mut self, render_queue: &wgpu::Queue) {
let instance_lists = self.chunk.to_instances();
for (block_type, instance_list) in instance_lists {
if let Some(instance_buffer) = self.instance_buffers.get_mut(&block_type) {
render_queue.write_buffer(instance_buffer, 0, bytemuck::cast_slice(&instance_list));
} else {
todo!();
}
}
}
pub fn new(
render_device: &wgpu::Device,
queue: &wgpu::Queue,
swap_chain_descriptor: &wgpu::SwapChainDescriptor,
) -> WorldState {
let mut chunk = Chunk {
let chunk = Chunk {
blocks: [
[[Some(Block {
block_type: BlockType::Cobblestone,
@ -343,6 +355,7 @@ impl WorldState {
[[None; 16]; 16],
[[None; 16]; 16],
],
highlighted: None,
};
let (world_texture_layout, texture_bind_groups) =
@ -350,14 +363,6 @@ impl WorldState {
let (camera, projection) = Self::create_camera(&swap_chain_descriptor);
let pointy_at = chunk
.dda(camera.position.to_vec(), camera.direction())
.unwrap();
chunk.blocks[pointy_at.y][pointy_at.z][pointy_at.x] = Some(Block {
block_type: BlockType::Cobblestone,
});
let (uniforms, uniform_buffer, world_uniform_layout, uniform_bind_group) =
Self::create_uniforms(&camera, &projection, &render_device);
@ -398,12 +403,13 @@ impl WorldState {
texture_bind_groups,
camera,
projection,
light_bind_group,
instance_lists,
vertex_buffer,
index_buffer,
instance_lists,
instance_buffers,
depth_texture,
light_bind_group,
chunk,
}
}