Merge pull request #1573 from erenoku/navigation_cube

Add navigation cube
This commit is contained in:
Hanno Braun 2023-02-13 14:38:24 +01:00 committed by GitHub
commit 5879a0c650
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 825 additions and 2 deletions

40
Cargo.lock generated
View File

@ -51,6 +51,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [
"cfg-if",
"getrandom",
"once_cell",
"version_check",
]
@ -581,6 +582,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 = "concurrent-queue"
version = "2.1.0"
@ -1267,9 +1274,12 @@ dependencies = [
"fj-interop",
"fj-math",
"getrandom",
"image",
"nalgebra",
"raw-window-handle 0.5.0",
"rfd",
"thiserror",
"tobj",
"tracing",
"wgpu",
"wgpu_glyph",
@ -1807,6 +1817,21 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "image"
version = "0.24.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945"
dependencies = [
"bytemuck",
"byteorder 1.4.3",
"color_quant",
"jpeg-decoder",
"num-rational",
"num-traits",
"png",
]
[[package]]
name = "indexmap"
version = "1.9.2"
@ -1910,6 +1935,12 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jpeg-decoder"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
[[package]]
name = "js-sys"
version = "0.3.61"
@ -3737,6 +3768,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tobj"
version = "3.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d0bde887a49e2e09f30ba3b454cdba3fbc971703e436c527f9f69a035b45b9b"
dependencies = [
"ahash 0.8.3",
]
[[package]]
name = "tokio"
version = "1.25.0"

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

View File

@ -0,0 +1,62 @@
# Blender 3.4.1 MTL File: 'cube.blend'
# www.blender.org
newmtl bottom
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd bottom.png
newmtl front
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd front.png
newmtl left
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd left.png
newmtl rear
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd rear.png
newmtl right
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd right.png
newmtl top
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Kd top.png

View File

@ -0,0 +1,51 @@
# Blender 3.4.1
# www.blender.org
mtllib cube.mtl
o Cube
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
vn -0.0000 1.0000 -0.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn 1.0000 -0.0000 -0.0000
vt 0.999900 0.000100
vt 0.999900 0.999900
vt 0.000100 0.999900
vt 0.999900 0.999900
vt 0.999900 0.000100
vt 0.000100 0.000100
vt 0.000100 0.000100
vt 0.999900 0.999900
vt 0.000100 0.999900
vt 0.999900 0.000100
vt 0.000100 0.999900
vt 0.000100 0.000100
vt 0.000100 0.999900
vt 0.999900 0.999900
vt 0.000100 0.000100
vt 0.999900 0.000100
vt 0.000100 0.999900
vt 0.999900 0.999900
vt 0.999900 0.000100
vt 0.000100 0.000100
s 0
usemtl top
f 1/1/1 5/14/1 7/17/1 3/7/1
usemtl left
f 4/10/2 3/8/2 7/17/2 8/20/2
usemtl front
f 2/5/5 1/2/5 3/9/5 4/12/5
usemtl right
f 6/16/6 5/14/6 1/3/6 2/6/6
usemtl rear
f 8/19/3 7/18/3 5/13/3 6/15/3
usemtl bottom
f 6/16/4 2/4/4 4/11/4 8/20/4

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -40,6 +40,13 @@ impl Transform {
))
}
/// Construct a scaling
pub fn scale(scaling_factor: f64) -> Self {
Self(nalgebra::Transform::from_matrix_unchecked(
nalgebra::OMatrix::new_scaling(scaling_factor),
))
}
/// Transform the given point
pub fn transform_point(&self, point: &Point<3>) -> Point<3> {
Point::from(self.0.transform_point(&point.to_na()))
@ -119,6 +126,11 @@ impl Transform {
array.map(Scalar::from)
}
/// Return a copy of the inner nalgebra transform
pub fn get_inner(&self) -> nalgebra::Transform<f64, nalgebra::TAffine, 3> {
self.0
}
/// Transform the given axis-aligned bounding box
pub fn transform_aabb(&self, aabb: &Aabb<3>) -> Aabb<3> {
Aabb {

View File

@ -18,11 +18,18 @@ egui = "0.20.1"
egui-wgpu = "0.20.0"
fj-interop.workspace = true
fj-math.workspace = true
nalgebra = "0.32.1"
tobj = "3.2.4"
raw-window-handle = "0.5.0"
thiserror = "1.0.35"
tracing = "0.1.37"
wgpu_glyph = "0.18.0"
[dependencies.image]
version = "0.24"
default-features = false
features = ["png", "jpeg"]
[dependencies.rfd]
version = "0.11.1"
default_features = false

View File

@ -0,0 +1,59 @@
pub struct Assets<'a> {
pub cube_obj: &'a [u8],
pub cube_mtl: &'a [u8],
pub front_texture: &'a [u8],
pub right_texture: &'a [u8],
pub rear_texture: &'a [u8],
pub left_texture: &'a [u8],
pub top_texture: &'a [u8],
pub bottom_texture: &'a [u8],
}
impl<'a> Assets<'a> {
pub fn get_instance() -> Self {
let cube_obj: &[u8] =
include_bytes!("../../../assets/navigation_cube/cube.obj");
let cube_mtl: &[u8] =
include_bytes!("../../../assets/navigation_cube/cube.mtl");
let front_texture: &[u8] =
include_bytes!("../../../assets/navigation_cube/front.png");
let right_texture: &[u8] =
include_bytes!("../../../assets/navigation_cube/right.png");
let rear_texture: &[u8] =
include_bytes!("../../../assets/navigation_cube/rear.png");
let left_texture: &[u8] =
include_bytes!("../../../assets/navigation_cube/left.png");
let top_texture: &[u8] =
include_bytes!("../../../assets/navigation_cube/top.png");
let bottom_texture: &[u8] =
include_bytes!("../../../assets/navigation_cube/bottom.png");
Self {
cube_obj,
cube_mtl,
front_texture,
right_texture,
rear_texture,
left_texture,
top_texture,
bottom_texture,
}
}
pub fn get_asset(&self, file_name: &str) -> &[u8] {
match file_name {
"cube.obj" => self.cube_obj,
"cube.mtl" => self.cube_mtl,
"front.png" => self.front_texture,
"right.png" => self.right_texture,
"rear.png" => self.rear_texture,
"left.png" => self.left_texture,
"top.png" => self.top_texture,
"bottom.png" => self.bottom_texture,
_ => unreachable!(
"An unknown asset: {} is trying to be loaded",
file_name
),
}
}
}

View File

@ -3,9 +3,12 @@
mod draw_config;
mod drawables;
mod geometries;
mod model;
mod navigation_cube;
mod pipelines;
mod renderer;
mod shaders;
mod texture;
mod transform;
mod uniforms;
mod vertices;

View File

@ -0,0 +1,226 @@
use std::ops::Range;
use tobj::LoadError;
use wgpu::util::DeviceExt;
use super::texture::{self, LoadTextureError};
use crate::assets::Assets;
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct ModelVertex {
pub position: [f32; 3],
pub tex_coords: [f32; 2],
}
impl ModelVertex {
pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
use std::mem;
wgpu::VertexBufferLayout {
array_stride: mem::size_of::<ModelVertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
offset: 0,
shader_location: 0,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
shader_location: 1,
format: wgpu::VertexFormat::Float32x2,
},
],
}
}
}
#[derive(Debug)]
pub struct Model {
pub meshes: Vec<Mesh>,
pub materials: Vec<Material>,
}
#[derive(Debug)]
pub struct Material {
pub name: String,
pub diffuse_texture: super::texture::Texture,
pub bind_group: wgpu::BindGroup,
}
#[derive(Debug)]
pub struct Mesh {
pub name: String,
pub vertex_buffer: wgpu::Buffer,
pub index_buffer: wgpu::Buffer,
pub num_elements: u32,
pub material: usize,
}
#[derive(Debug, thiserror::Error)]
pub enum LoadModelError {
#[error("Object loading error")]
ObjLoad(#[from] LoadError),
#[error("Load texture error")]
Texture(#[from] LoadTextureError),
}
pub fn load_model(
file_name: &str,
device: &wgpu::Device,
queue: &wgpu::Queue,
layout: &wgpu::BindGroupLayout,
) -> Result<Model, LoadModelError> {
let assets = Assets::get_instance();
let (models, obj_materials) = tobj::load_obj_buf(
&mut assets.get_asset(file_name),
&tobj::LoadOptions {
triangulate: true,
single_index: true,
..Default::default()
},
|p| {
tobj::load_mtl_buf(
&mut assets.get_asset(
p.file_name()
.unwrap()
.to_str()
.expect("OsStr could not be converted to a str"),
),
)
},
)?;
let mut materials = Vec::new();
for m in obj_materials? {
let texture_data: &[u8] = assets.get_asset(m.diffuse_texture.as_str());
let diffuse_texture = texture::Texture::from_bytes(
device,
queue,
texture_data,
file_name,
)?;
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(
&diffuse_texture.view,
),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(
&diffuse_texture.sampler,
),
},
],
label: None,
});
materials.push(Material {
name: m.name,
diffuse_texture,
bind_group,
})
}
let meshes = models
.into_iter()
.map(|m| {
let vertices = (0..m.mesh.positions.len() / 3)
.map(|i| ModelVertex {
position: [
m.mesh.positions[i * 3],
m.mesh.positions[i * 3 + 1],
m.mesh.positions[i * 3 + 2],
],
tex_coords: [
m.mesh.texcoords[i * 2],
1.0 - m.mesh.texcoords[i * 2 + 1],
],
})
.collect::<Vec<_>>();
let vertex_buffer =
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{file_name:?} Vertex Buffer")),
contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let index_buffer =
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{file_name:?} Index Buffer")),
contents: bytemuck::cast_slice(&m.mesh.indices),
usage: wgpu::BufferUsages::INDEX,
});
Mesh {
name: file_name.to_string(),
vertex_buffer,
index_buffer,
num_elements: m.mesh.indices.len() as u32,
material: m.mesh.material_id.unwrap_or(0),
}
})
.collect::<Vec<_>>();
Ok(Model { meshes, materials })
}
pub trait DrawModel<'a> {
fn draw_mesh(&mut self, mesh: &'a Mesh, material: &'a Material);
fn draw_mesh_instanced(
&mut self,
mesh: &'a Mesh,
material: &'a Material,
instances: Range<u32>,
);
fn draw_model(&mut self, model: &'a Model);
fn draw_model_instanced(&mut self, model: &'a Model, instances: Range<u32>);
}
impl<'a, 'b> DrawModel<'b> for wgpu::RenderPass<'a>
where
'b: 'a,
{
fn draw_mesh(&mut self, mesh: &'b Mesh, material: &'b Material) {
self.draw_mesh_instanced(mesh, material, 0..1);
}
fn draw_mesh_instanced(
&mut self,
mesh: &'b Mesh,
material: &'b Material,
instances: Range<u32>,
) {
self.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
self.set_index_buffer(
mesh.index_buffer.slice(..),
wgpu::IndexFormat::Uint32,
);
self.set_bind_group(0, &material.bind_group, &[]);
self.draw_indexed(0..mesh.num_elements, 0, instances);
}
fn draw_model(&mut self, model: &'b Model) {
self.draw_model_instanced(model, 0..1);
}
fn draw_model_instanced(
&mut self,
model: &'b Model,
instances: Range<u32>,
) {
for mesh in &model.meshes {
let material = &model.materials[mesh.material];
self.draw_mesh_instanced(mesh, material, instances.clone());
}
}
}

View File

@ -0,0 +1,220 @@
use fj_math::Transform;
use wgpu::util::DeviceExt;
use super::model::{self, load_model, DrawModel, Model};
#[derive(Debug)]
pub struct NavigationCubeRenderer {
cube_model: Model,
render_pipeline: wgpu::RenderPipeline,
mvp_matrix_bind_group: wgpu::BindGroup,
mvp_matrix_buffer: wgpu::Buffer,
}
const SCALE_FACTOR: f64 = 0.13;
const CUBE_TRANSLATION: [f64; 3] = [0.8, 0.7, 0.0];
impl NavigationCubeRenderer {
pub fn new(
device: &wgpu::Device,
queue: &wgpu::Queue,
config: &wgpu::SurfaceConfiguration,
aspect_ratio: f64,
) -> Self {
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,
ty: wgpu::BindingType::Sampler(
wgpu::SamplerBindingType::Filtering,
),
count: None,
},
],
label: Some("texture_bind_group_layout"),
});
let mvp_matrix =
Self::get_mvp_matrix(Transform::identity(), aspect_ratio);
let mvp_matrix_buffer =
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Model Matrix Buffer"),
contents: bytemuck::cast_slice(&[mvp_matrix]),
usage: wgpu::BufferUsages::UNIFORM
| wgpu::BufferUsages::COPY_DST,
});
let mvp_matrix_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("mvp_matrix_group_layout"),
});
let mvp_matrix_bind_group =
device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &mvp_matrix_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: mvp_matrix_buffer.as_entire_binding(),
}],
label: Some("mvp_matrix_bind_group"),
});
let shader =
device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Shadow Display Shader"),
source: wgpu::ShaderSource::Wgsl(
include_str!("navigation_cube.wgsl").into(),
),
});
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: &[
&texture_bind_group_layout,
&mvp_matrix_bind_group_layout,
],
push_constant_ranges: &[],
});
let render_pipeline =
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Navigation Cube Renderer"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vertex",
buffers: &[model::ModelVertex::desc()],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fragment",
targets: &[Some(wgpu::ColorTargetState {
format: config.format,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent::REPLACE,
alpha: wgpu::BlendComponent::REPLACE,
}),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
// Setting this to anything other than Fill requires Features::POLYGON_MODE_LINE
// or Features::POLYGON_MODE_POINT
polygon_mode: wgpu::PolygonMode::Fill,
// Requires Features::DEPTH_CLIP_CONTROL
unclipped_depth: false,
// Requires Features::CONSERVATIVE_RASTERIZATION
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
});
let cube_model =
load_model("cube.obj", device, queue, &texture_bind_group_layout)
.unwrap();
Self {
cube_model,
render_pipeline,
mvp_matrix_bind_group,
mvp_matrix_buffer,
}
}
pub fn draw(
&mut self,
view: &wgpu::TextureView,
encoder: &mut wgpu::CommandEncoder,
queue: &wgpu::Queue,
aspect_ratio: f64,
rotation: Transform,
) {
let mvp_matrix = Self::get_mvp_matrix(rotation, aspect_ratio);
queue.write_buffer(
&self.mvp_matrix_buffer,
0,
bytemuck::cast_slice(&[mvp_matrix]),
);
let mut render_pass =
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Depth Visual Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: true,
},
})],
depth_stencil_attachment: None,
});
render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_bind_group(1, &self.mvp_matrix_bind_group, &[]);
render_pass.draw_model(&self.cube_model);
}
fn get_mvp_matrix(rotation: Transform, aspect_ratio: f64) -> [f32; 16] {
let scale = Transform::scale(SCALE_FACTOR);
let world_translation = Transform::translation([0.0, 0.0, -1.0]);
let mut model_matrix = Transform::identity();
model_matrix = model_matrix * world_translation;
model_matrix = model_matrix * rotation;
model_matrix = model_matrix * scale;
let perspective =
nalgebra::Perspective3::new(aspect_ratio, 30.0, 0.1, 2.0);
let view_matrix = nalgebra::Matrix4::look_at_lh(
&nalgebra::Point3::new(0.0, 0.0, 0.0),
&nalgebra::Point3::new(0.0, 0.0, 1.0),
&nalgebra::Vector3::new(0.0, -1.0, 0.0),
);
let screen_translation = Transform::translation(CUBE_TRANSLATION);
let matrix = screen_translation.get_inner().matrix()
* *perspective.to_projective().matrix()
* view_matrix
* model_matrix.get_inner().matrix();
let mut mat = [0.; 16];
mat.copy_from_slice(matrix.as_slice());
mat.map(|x| x as f32)
}
}

View File

@ -0,0 +1,36 @@
// Vertex shader
@group(1) @binding(0)
var<uniform> mvp_matrix: mat4x4<f32>;
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) tex_coords: vec2<f32>,
}
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) tex_coords: vec2<f32>,
}
@vertex
fn vertex(
model: VertexInput,
) -> VertexOutput {
var out: VertexOutput;
out.tex_coords = model.tex_coords;
out.clip_position = mvp_matrix * vec4<f32>(model.position, 1.0);
return out;
}
// Fragment shader
@group(0) @binding(0)
var t_diffuse: texture_2d<f32>;
@group(0)@binding(1)
var s_diffuse: sampler;
@fragment
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
return textureSample(t_diffuse, s_diffuse, in.tex_coords);
}

View File

@ -13,8 +13,9 @@ use crate::{
use super::{
draw_config::DrawConfig, drawables::Drawables, geometries::Geometries,
pipelines::Pipelines, transform::Transform, uniforms::Uniforms,
vertices::Vertices, DEPTH_FORMAT, SAMPLE_COUNT,
navigation_cube::NavigationCubeRenderer, pipelines::Pipelines,
transform::Transform, uniforms::Uniforms, vertices::Vertices, DEPTH_FORMAT,
SAMPLE_COUNT,
};
/// Graphics rendering state and target abstraction
@ -34,6 +35,8 @@ pub struct Renderer {
geometries: Geometries,
pipelines: Pipelines,
navigation_cube_renderer: NavigationCubeRenderer,
}
impl Renderer {
@ -183,6 +186,13 @@ impl Renderer {
let pipelines =
Pipelines::new(&device, &bind_group_layout, color_format);
let navigation_cube_renderer = NavigationCubeRenderer::new(
&device,
&queue,
&surface_config,
800.0 / 600.0,
);
Ok(Self {
surface,
features,
@ -198,6 +208,8 @@ impl Renderer {
geometries,
pipelines,
navigation_cube_renderer,
})
}
@ -331,6 +343,14 @@ impl Renderer {
gui.draw(&mut render_pass, &clipped_primitives, &screen_descriptor);
}
self.navigation_cube_renderer.draw(
&color_view,
&mut encoder,
&self.queue,
aspect_ratio,
camera.rotation,
);
let command_buffer = encoder.finish();
self.queue.submit(Some(command_buffer));

View File

@ -0,0 +1,86 @@
use image::{GenericImageView, ImageError};
use std::num::NonZeroU32;
#[derive(Debug)]
pub struct Texture {
pub texture: wgpu::Texture,
pub view: wgpu::TextureView,
pub sampler: wgpu::Sampler,
}
#[derive(Debug, thiserror::Error)]
pub enum LoadTextureError {
#[error("Image processing error")]
ImageError(#[from] ImageError),
}
impl Texture {
pub fn from_bytes(
device: &wgpu::Device,
queue: &wgpu::Queue,
bytes: &[u8],
label: &str,
) -> Result<Self, LoadTextureError> {
let img = image::load_from_memory(bytes)?;
Ok(Self::from_image(device, queue, &img, Some(label)))
}
pub fn from_image(
device: &wgpu::Device,
queue: &wgpu::Queue,
img: &image::DynamicImage,
label: Option<&str>,
) -> Self {
let dimensions = img.dimensions();
let rgba = img.to_rgba8();
let size = wgpu::Extent3d {
width: dimensions.0,
height: dimensions.1,
depth_or_array_layers: 1,
};
let texture = device.create_texture(&wgpu::TextureDescriptor {
label,
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::COPY_DST,
});
queue.write_texture(
wgpu::ImageCopyTexture {
aspect: wgpu::TextureAspect::All,
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
},
&rgba,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: NonZeroU32::new(4 * dimensions.0),
rows_per_image: NonZeroU32::new(dimensions.1),
},
size,
);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
Self {
texture,
view,
sampler,
}
}
}

View File

@ -14,6 +14,7 @@
#![warn(missing_docs)]
mod assets;
mod camera;
mod graphics;
mod gui;