1280 lines
47 KiB
Rust
1280 lines
47 KiB
Rust
pub mod eye;
|
|
|
|
use crate::render::eye::Eye;
|
|
use crate::world::{Shape, SimpleColliderData, SimpleLight, SimpleObject, SimpleObjectData};
|
|
use bytemuck::{Pod, Zeroable};
|
|
use glam::prelude::*;
|
|
use gltf::Semantic;
|
|
use image::EncodableLayout;
|
|
use std::cell::RefCell;
|
|
use std::collections::HashMap;
|
|
use std::error::Error;
|
|
use std::fmt::{Debug, Display, Formatter};
|
|
use std::hash::{BuildHasherDefault, Hash, Hasher};
|
|
use std::ops::Range;
|
|
use std::rc::Rc;
|
|
use std::sync::Arc;
|
|
use wgpu::naga::{FastHashMap, FastHashSet};
|
|
use wgpu::util::DeviceExt;
|
|
use wgpu::{Device, Queue};
|
|
use winit::window::Window;
|
|
|
|
const DEFAULT_VERTICES: [SimpleVertex; 0] = [];
|
|
|
|
#[repr(C)]
|
|
#[derive(Pod, Zeroable, Copy, Clone)]
|
|
pub struct SimpleModelInstance {
|
|
pub transform: Mat4,
|
|
pub color: Vec4,
|
|
pub lights: [u16; 16],
|
|
pub point_lights: u32,
|
|
pub spot_lights: u32,
|
|
pub metal: f32,
|
|
pub rough: f32,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Pod, Copy, Clone, Zeroable)]
|
|
pub struct SimpleLightInstance {
|
|
pub location: Vec4,
|
|
pub rotation: Vec4,
|
|
pub color: Vec4,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct SimpleLightData {
|
|
pub index: usize,
|
|
pub instance: SimpleLightInstance,
|
|
pub transform: Affine3,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct SimpleModelData {
|
|
pub instance: SimpleModelInstance,
|
|
pub material: Id<SimpleMaterial>,
|
|
pub mesh: Id<SimpleMesh>,
|
|
}
|
|
|
|
pub struct SimpleInstances {
|
|
light_count: usize,
|
|
light_buffer: wgpu::Buffer,
|
|
instance_count: usize,
|
|
instance_buffer: wgpu::Buffer,
|
|
//light_ref_last: usize,
|
|
//light_ref_buffer: wgpu::Buffer,
|
|
objects:
|
|
FastHashMap<Id<SimpleMesh>, FastHashMap<Id<SimpleMaterial>, FastHashSet<SimpleObject>>>,
|
|
lights: FastHashMap<SimpleLight, usize>,
|
|
}
|
|
|
|
enum SimpleRenderCode {
|
|
Material(wgpu::BindGroup),
|
|
Mesh((wgpu::Buffer, u32), Option<(wgpu::Buffer, u32)>),
|
|
Draw(Range<u32>),
|
|
}
|
|
|
|
struct SimpleRenderProgram(Vec<SimpleRenderCode>);
|
|
|
|
impl SimpleRenderProgram {
|
|
fn push(&mut self, item: SimpleRenderCode) {
|
|
self.0.push(item)
|
|
}
|
|
fn new() -> SimpleRenderProgram {
|
|
SimpleRenderProgram(Vec::new())
|
|
}
|
|
fn render(self, pass: &mut wgpu::RenderPass) {
|
|
let mut count = 0;
|
|
let mut indexed = false;
|
|
for code in self.0 {
|
|
match code {
|
|
SimpleRenderCode::Material(material) => pass.set_bind_group(
|
|
Renderer::SIMPLE_RENDER_TEXTURE_GROUP_POSITION,
|
|
&material,
|
|
&[],
|
|
),
|
|
SimpleRenderCode::Mesh(vertices, indices) => {
|
|
pass.set_vertex_buffer(0, vertices.0.slice(..));
|
|
if let Some(indices) = indices {
|
|
pass.set_index_buffer(indices.0.slice(..), wgpu::IndexFormat::Uint32);
|
|
count = indices.1;
|
|
indexed = true;
|
|
} else {
|
|
count = vertices.1;
|
|
indexed = false;
|
|
}
|
|
}
|
|
SimpleRenderCode::Draw(instances) => {
|
|
if indexed {
|
|
pass.draw_indexed(0..count, 0, instances)
|
|
} else {
|
|
pass.draw(0..count, instances);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SimpleInstances {
|
|
const MIN_SIZE: u64 = 64;
|
|
pub fn add_object(&mut self, object: SimpleObject) {
|
|
if let Some(ref model) = object.0.borrow().model {
|
|
self.instance_count += 1;
|
|
self.objects
|
|
.entry(model.mesh.clone())
|
|
.or_insert(FastHashMap::with_hasher(BuildHasherDefault::new()))
|
|
.entry(model.material.clone())
|
|
.or_insert(FastHashSet::with_hasher(BuildHasherDefault::new()))
|
|
.insert(object.clone());
|
|
}
|
|
}
|
|
|
|
pub fn remove_object(&mut self, object: SimpleObject) {
|
|
if let Some(ref model) = object.0.borrow().model {
|
|
self.instance_count -= 1;
|
|
self.objects
|
|
.entry(model.mesh.clone())
|
|
.or_insert(FastHashMap::with_hasher(BuildHasherDefault::new()))
|
|
.entry(model.material.clone())
|
|
.or_insert(FastHashSet::with_hasher(BuildHasherDefault::new()))
|
|
.remove(&object);
|
|
}
|
|
}
|
|
|
|
pub fn add_light(&mut self, light: SimpleLight) {
|
|
self.light_count += 1;
|
|
self.lights.insert(light, 0);
|
|
}
|
|
|
|
pub fn remove_light(&mut self, light: &SimpleLight) {
|
|
self.light_count -= 1;
|
|
self.lights.remove(light);
|
|
}
|
|
|
|
pub fn reallocate_buffer(
|
|
device: &wgpu::Device,
|
|
buffer: &mut wgpu::Buffer,
|
|
count: usize,
|
|
item_size: usize,
|
|
) {
|
|
let size = buffer.size() / item_size as wgpu::BufferAddress;
|
|
if count > SimpleInstances::MIN_SIZE as usize {
|
|
let mut reallocate: Option<usize> = None;
|
|
if count < (size / 2) as usize {
|
|
reallocate = Some(count / 2);
|
|
} else if count > size as usize {
|
|
reallocate = Some(count * 2);
|
|
}
|
|
if let Some(new_size) = reallocate {
|
|
*buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
|
label: Some("Instance Buffer"),
|
|
size: (item_size * new_size) as wgpu::BufferAddress,
|
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
|
|
mapped_at_creation: false,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
pub fn write_lights(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) {
|
|
//SimpleInstances::reallocate_buffer(device, queue, &mut self.light_ref_buffer, self.light_ref_last, size_of::<u32>());
|
|
/*
|
|
let mut light_ref_buffer = queue.write_buffer_with(
|
|
&self.light_ref_buffer,
|
|
0 as wgpu::BufferAddress,
|
|
wgpu::BufferSize::new(self.instance_buffer.size()).unwrap()
|
|
).unwrap();
|
|
*/
|
|
SimpleInstances::reallocate_buffer(
|
|
device,
|
|
&mut self.light_buffer,
|
|
self.light_count,
|
|
size_of::<SimpleLight>(),
|
|
);
|
|
let mut buffer = queue
|
|
.write_buffer_with(
|
|
&self.light_buffer,
|
|
0 as wgpu::BufferAddress,
|
|
wgpu::BufferSize::new(self.light_buffer.size()).unwrap(),
|
|
)
|
|
.unwrap();
|
|
let stride = size_of::<SimpleLight>();
|
|
for (new_index, (light, index)) in self.lights.iter_mut().enumerate() {
|
|
*index = new_index + 1;
|
|
let begin = *index * stride;
|
|
buffer
|
|
.slice(begin..begin + stride)
|
|
.copy_from_slice(bytemuck::cast_slice(&[light.0.borrow().instance]));
|
|
}
|
|
}
|
|
fn write_instances(
|
|
&mut self,
|
|
device: &wgpu::Device,
|
|
queue: &wgpu::Queue,
|
|
) -> SimpleRenderProgram {
|
|
let mut program = SimpleRenderProgram::new();
|
|
SimpleInstances::reallocate_buffer(
|
|
device,
|
|
&mut self.instance_buffer,
|
|
self.instance_count,
|
|
size_of::<SimpleModelInstance>(),
|
|
);
|
|
let mut buffer = queue
|
|
.write_buffer_with(
|
|
&self.instance_buffer,
|
|
0 as wgpu::BufferAddress,
|
|
wgpu::BufferSize::new(self.instance_buffer.size()).unwrap(),
|
|
)
|
|
.unwrap();
|
|
let mut index: u32 = 0;
|
|
let stride = size_of::<SimpleModelInstance>();
|
|
for (mesh, materials) in self.objects.iter() {
|
|
program.push(SimpleRenderCode::Mesh(
|
|
mesh.it.vertices.clone(),
|
|
mesh.it.indices.clone(),
|
|
));
|
|
for (material, objects) in materials.iter() {
|
|
program.push(SimpleRenderCode::Material(material.it.group.clone()));
|
|
let before = index;
|
|
for object in objects.iter() {
|
|
let begin = index as usize * stride;
|
|
buffer
|
|
.slice(begin..begin + stride)
|
|
.copy_from_slice(bytemuck::cast_slice(&[object
|
|
.0
|
|
.borrow()
|
|
.model
|
|
.as_ref()
|
|
.unwrap()
|
|
.instance]));
|
|
index += 1;
|
|
}
|
|
program.push(SimpleRenderCode::Draw(Range::from(before..index)));
|
|
}
|
|
}
|
|
program
|
|
}
|
|
|
|
fn desc() -> wgpu::VertexBufferLayout<'static> {
|
|
wgpu::VertexBufferLayout {
|
|
array_stride: size_of::<SimpleModelInstance>() as wgpu::BufferAddress,
|
|
step_mode: wgpu::VertexStepMode::Instance,
|
|
attributes: &[
|
|
// todo: is this too big?
|
|
wgpu::VertexAttribute {
|
|
offset: 0,
|
|
shader_location: 4,
|
|
format: wgpu::VertexFormat::Float32x4,
|
|
},
|
|
wgpu::VertexAttribute {
|
|
offset: size_of::<[f32; 4]>() as wgpu::BufferAddress,
|
|
shader_location: 5,
|
|
format: wgpu::VertexFormat::Float32x4,
|
|
},
|
|
wgpu::VertexAttribute {
|
|
offset: size_of::<[f32; 8]>() as wgpu::BufferAddress,
|
|
shader_location: 6,
|
|
format: wgpu::VertexFormat::Float32x4,
|
|
},
|
|
wgpu::VertexAttribute {
|
|
offset: size_of::<[f32; 12]>() as wgpu::BufferAddress,
|
|
shader_location: 7,
|
|
format: wgpu::VertexFormat::Float32x4,
|
|
},
|
|
wgpu::VertexAttribute {
|
|
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
|
shader_location: 8,
|
|
format: wgpu::VertexFormat::Float32x4,
|
|
},
|
|
wgpu::VertexAttribute {
|
|
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
|
shader_location: 9,
|
|
format: wgpu::VertexFormat::Float32x4,
|
|
},
|
|
wgpu::VertexAttribute {
|
|
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
|
shader_location: 10,
|
|
format: wgpu::VertexFormat::Float32x4,
|
|
},
|
|
wgpu::VertexAttribute {
|
|
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
|
shader_location: 11,
|
|
format: wgpu::VertexFormat::Float32x4,
|
|
},
|
|
],
|
|
}
|
|
}
|
|
|
|
pub fn new(device: &wgpu::Device) -> SimpleInstances {
|
|
let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
|
label: Some("Instance Buffer"),
|
|
size: (size_of::<SimpleModelInstance>() * SimpleInstances::MIN_SIZE as usize)
|
|
as wgpu::BufferAddress,
|
|
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
|
mapped_at_creation: false,
|
|
});
|
|
let light_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
|
label: Some("Light Buffer"),
|
|
size: (size_of::<SimpleLightInstance>() * SimpleInstances::MIN_SIZE as usize)
|
|
as wgpu::BufferAddress,
|
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
|
mapped_at_creation: false,
|
|
});
|
|
/*let light_ref_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
|
label: Some("Light Ref Buffer"),
|
|
size: (size_of::<u32>() * SimpleInstances::MIN_SIZE as usize) as wgpu::BufferAddress,
|
|
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
|
mapped_at_creation: false,
|
|
});*/
|
|
SimpleInstances {
|
|
light_count: 0,
|
|
//light_ref_last: 0,
|
|
instance_count: 0,
|
|
light_buffer,
|
|
instance_buffer,
|
|
//light_ref_buffer,
|
|
objects: FastHashMap::with_hasher(BuildHasherDefault::new()),
|
|
lights: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Renderer {
|
|
id_count: u64,
|
|
surface: wgpu::Surface<'static>,
|
|
config: wgpu::SurfaceConfiguration,
|
|
device: wgpu::Device,
|
|
queue: wgpu::Queue,
|
|
pub(crate) eye: eye::Eye,
|
|
material_layout: wgpu::BindGroupLayout,
|
|
default_texture: SimpleTexture,
|
|
default_material: SimpleMaterial,
|
|
sky_pipeline: wgpu::RenderPipeline,
|
|
instance_pipeline: wgpu::RenderPipeline,
|
|
depth_texture: SimpleTexture,
|
|
pub(crate) instances: SimpleInstances,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
|
struct SimpleVertex {
|
|
position: [f32; 3],
|
|
normal: [f32; 3],
|
|
tex_coord: [f32; 2],
|
|
tangent: [f32; 3],
|
|
bitangent: [f32; 3],
|
|
}
|
|
|
|
impl SimpleVertex {
|
|
fn desc() -> wgpu::VertexBufferLayout<'static> {
|
|
wgpu::VertexBufferLayout {
|
|
array_stride: size_of::<SimpleVertex>() as wgpu::BufferAddress,
|
|
step_mode: wgpu::VertexStepMode::Vertex,
|
|
attributes: &[
|
|
wgpu::VertexAttribute {
|
|
offset: 0,
|
|
shader_location: 0,
|
|
format: wgpu::VertexFormat::Float32x3,
|
|
},
|
|
wgpu::VertexAttribute {
|
|
offset: size_of::<[f32; 3]>() as wgpu::BufferAddress,
|
|
shader_location: 1,
|
|
format: wgpu::VertexFormat::Float32x3,
|
|
},
|
|
wgpu::VertexAttribute {
|
|
offset: size_of::<[f32; 6]>() as wgpu::BufferAddress,
|
|
shader_location: 2,
|
|
format: wgpu::VertexFormat::Float32x2,
|
|
},
|
|
],
|
|
}
|
|
}
|
|
}
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct SimpleMesh {
|
|
indices: Option<(wgpu::Buffer, u32)>,
|
|
vertices: (wgpu::Buffer, u32),
|
|
}
|
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
|
pub struct SimpleTexture {
|
|
texture: wgpu::Texture,
|
|
view: wgpu::TextureView,
|
|
}
|
|
pub struct TextureProperties {
|
|
width: u32,
|
|
height: u32,
|
|
}
|
|
// todo: use texture compression!
|
|
impl SimpleTexture {
|
|
pub fn load(
|
|
device: &Device,
|
|
queue: &Queue,
|
|
slice: impl AsRef<[u8]>,
|
|
properties: TextureProperties,
|
|
) -> SimpleTexture {
|
|
let size = wgpu::Extent3d {
|
|
width: properties.width,
|
|
height: properties.height,
|
|
depth_or_array_layers: 1,
|
|
};
|
|
let diffuse_texture = device.create_texture(&wgpu::TextureDescriptor {
|
|
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,
|
|
label: Some("texture"),
|
|
view_formats: &[],
|
|
});
|
|
let diffuse_texture_view =
|
|
diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
queue.write_texture(
|
|
wgpu::TexelCopyTextureInfo {
|
|
texture: &diffuse_texture,
|
|
mip_level: 0,
|
|
origin: wgpu::Origin3d::ZERO,
|
|
aspect: wgpu::TextureAspect::All,
|
|
},
|
|
slice.as_ref(),
|
|
wgpu::TexelCopyBufferLayout {
|
|
offset: 0,
|
|
bytes_per_row: Some(4 * size.width),
|
|
rows_per_image: Some(size.height),
|
|
},
|
|
size,
|
|
);
|
|
SimpleTexture {
|
|
texture: diffuse_texture,
|
|
view: diffuse_texture_view,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct SimpleMaterial {
|
|
group: wgpu::BindGroup,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct Id<T> {
|
|
id: u64,
|
|
it: T,
|
|
}
|
|
|
|
impl<T> Hash for Id<T> {
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
self.id.hash(state)
|
|
}
|
|
}
|
|
|
|
impl<T> PartialEq for Id<T> {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
other.id == self.id
|
|
}
|
|
}
|
|
|
|
impl<T> Eq for Id<T> {}
|
|
|
|
struct MaterialProperties {
|
|
edge: wgpu::AddressMode,
|
|
filter: wgpu::FilterMode,
|
|
}
|
|
|
|
impl Default for MaterialProperties {
|
|
fn default() -> MaterialProperties {
|
|
MaterialProperties {
|
|
edge: wgpu::AddressMode::Repeat,
|
|
filter: wgpu::FilterMode::Linear,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl MaterialProperties {
|
|
fn sampler(&self, device: &wgpu::Device) -> wgpu::Sampler {
|
|
device.create_sampler(&wgpu::SamplerDescriptor {
|
|
address_mode_u: self.edge,
|
|
address_mode_v: self.edge,
|
|
address_mode_w: self.edge,
|
|
mag_filter: self.filter,
|
|
min_filter: self.filter,
|
|
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
|
..Default::default()
|
|
})
|
|
}
|
|
}
|
|
|
|
impl SimpleMaterial {
|
|
fn new(
|
|
device: &wgpu::Device,
|
|
layout: &wgpu::BindGroupLayout,
|
|
base: &SimpleTexture,
|
|
normal: &SimpleTexture,
|
|
reflect: &SimpleTexture,
|
|
config: MaterialProperties,
|
|
) -> SimpleMaterial {
|
|
let sampler = config.sampler(&device);
|
|
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
layout: layout,
|
|
entries: &[
|
|
wgpu::BindGroupEntry {
|
|
binding: 0,
|
|
resource: wgpu::BindingResource::Sampler(&sampler),
|
|
},
|
|
wgpu::BindGroupEntry {
|
|
binding: 1,
|
|
resource: wgpu::BindingResource::TextureView(&base.view),
|
|
},
|
|
wgpu::BindGroupEntry {
|
|
binding: 2,
|
|
resource: wgpu::BindingResource::TextureView(&normal.view),
|
|
},
|
|
wgpu::BindGroupEntry {
|
|
binding: 3,
|
|
resource: wgpu::BindingResource::TextureView(&reflect.view),
|
|
},
|
|
],
|
|
label: Some("diffuse_bind_group"),
|
|
});
|
|
SimpleMaterial { group }
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct PoolError(String);
|
|
|
|
impl Display for PoolError {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl Error for PoolError {}
|
|
|
|
pub struct SimpleTreeNode {
|
|
object: Option<SimpleObject>,
|
|
children: Vec<SimpleTreeNode>,
|
|
name: String,
|
|
}
|
|
|
|
impl SimpleTreeNode {
|
|
pub fn first_object(&self) -> Option<SimpleObject> {
|
|
if let Some(object) = self.object.clone() {
|
|
Some(object)
|
|
} else {
|
|
for child in self.children.iter() {
|
|
if let Some(object) = child.first_object() {
|
|
return Some(object);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
type GltfVertexBufferKey = (Option<usize>, Option<usize>, Option<usize>, Option<usize>);
|
|
|
|
impl Renderer {
|
|
const SIMPLE_RENDER_EYE_GROUP_POSITION: u32 = 0;
|
|
const SIMPLE_RENDER_TEXTURE_GROUP_POSITION: u32 = 1;
|
|
const SIMPLE_RENDER_MODEL_GROUP_POSITION: u32 = 2;
|
|
pub fn new_id<T>(&mut self, value: T) -> Id<T> {
|
|
self.id_count += 1;
|
|
Id {
|
|
it: value,
|
|
id: self.id_count,
|
|
}
|
|
}
|
|
pub fn load_texture_from_bytes(
|
|
&mut self,
|
|
slice: impl AsRef<[u8]>,
|
|
format: Option<image::ImageFormat>,
|
|
) -> SimpleTexture {
|
|
let image = if let Some(format) = format {
|
|
image::load_from_memory_with_format(slice.as_ref(), format)
|
|
} else {
|
|
image::load_from_memory(slice.as_ref())
|
|
};
|
|
if let Ok(data) = image {
|
|
let data = data.into_rgba8();
|
|
SimpleTexture::load(
|
|
&self.device,
|
|
&self.queue,
|
|
data.as_bytes(),
|
|
TextureProperties {
|
|
width: data.width(),
|
|
height: data.height(),
|
|
},
|
|
)
|
|
} else {
|
|
println!("failed to load texture! error: {:?}", image.unwrap_err());
|
|
self.default_texture.clone()
|
|
}
|
|
}
|
|
pub fn new_material(
|
|
&mut self,
|
|
base: &SimpleTexture,
|
|
normal: &SimpleTexture,
|
|
reflect: &SimpleTexture,
|
|
) -> Id<SimpleMaterial> {
|
|
self.new_id(SimpleMaterial::new(
|
|
&self.device,
|
|
&self.material_layout,
|
|
base,
|
|
normal,
|
|
reflect,
|
|
MaterialProperties {
|
|
edge: wgpu::AddressMode::Repeat,
|
|
filter: wgpu::FilterMode::Linear,
|
|
},
|
|
))
|
|
}
|
|
pub fn new_texture_from_gltf(
|
|
&mut self,
|
|
info: &gltf::texture::Texture,
|
|
images: &Vec<gltf::image::Data>,
|
|
) -> SimpleTexture {
|
|
if let Some(image) = images.get(info.source().index()) {
|
|
let mut new_pixels = Vec::new();
|
|
let pixels: &Vec<u8>;
|
|
match image.format {
|
|
gltf::image::Format::R8G8B8 => {
|
|
for pixel in image.pixels.chunks(3) {
|
|
new_pixels.push(pixel[0]);
|
|
new_pixels.push(pixel[0]);
|
|
new_pixels.push(pixel[0]);
|
|
new_pixels.push(255);
|
|
}
|
|
pixels = &new_pixels;
|
|
}
|
|
gltf::image::Format::R8G8B8A8 => pixels = &image.pixels,
|
|
_ => return self.default_texture.clone(),
|
|
}
|
|
SimpleTexture::load(
|
|
&self.device,
|
|
&self.queue,
|
|
pixels.as_slice(),
|
|
TextureProperties {
|
|
width: image.width,
|
|
height: image.height,
|
|
},
|
|
)
|
|
} else {
|
|
self.default_texture.clone()
|
|
}
|
|
}
|
|
pub fn new_mesh_from_gltf(
|
|
&mut self,
|
|
primitive: gltf::Primitive,
|
|
meshes: &mut HashMap<GltfVertexBufferKey, SimpleMesh>,
|
|
buffers: &[gltf::buffer::Data],
|
|
) -> Id<SimpleMesh> {
|
|
let position_index = primitive
|
|
.get(&Semantic::Positions)
|
|
.and_then(|it| it.view())
|
|
.and_then(|it| Some(it.buffer().index()));
|
|
let normals_index = primitive
|
|
.get(&Semantic::Normals)
|
|
.and_then(|it| it.view())
|
|
.and_then(|it| Some(it.buffer().index()));
|
|
let tex_coords_index = primitive
|
|
.get(&Semantic::TexCoords(0))
|
|
.and_then(|it| it.view())
|
|
.and_then(|it| Some(it.buffer().index()));
|
|
let indices_index = primitive
|
|
.indices()
|
|
.and_then(|it| it.view())
|
|
.and_then(|it| Some(it.buffer().index()));
|
|
let tangent_index = primitive
|
|
.get(&Semantic::Tangents)
|
|
.and_then(|it| it.view())
|
|
.and_then(|it| Some(it.buffer().index()));
|
|
let key = (
|
|
position_index,
|
|
normals_index,
|
|
tex_coords_index,
|
|
indices_index,
|
|
);
|
|
self.new_id(
|
|
meshes
|
|
.entry(key)
|
|
.or_insert_with(|| {
|
|
let mut tangents = false;
|
|
let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()]));
|
|
let mut vertex_data: Vec<SimpleVertex>;
|
|
if let Some(positions) = reader.read_positions() {
|
|
vertex_data = Vec::with_capacity(positions.len());
|
|
let mut normal = reader.read_normals().map(|it| it.into_iter());
|
|
let mut tex_coord = reader
|
|
.read_tex_coords(0)
|
|
.map(|it| it.into_f32().into_iter());
|
|
let mut tangent = reader.read_tangents().map(|it| it.into_iter());
|
|
tangents = tangent.is_some();
|
|
for position in positions {
|
|
vertex_data.push(SimpleVertex {
|
|
position,
|
|
normal: if let Some(ref mut normals) = normal {
|
|
if let Some(normal) = normals.next() {
|
|
normal
|
|
} else {
|
|
[0.0; 3]
|
|
}
|
|
} else {
|
|
[0.0; 3]
|
|
},
|
|
tex_coord: if let Some(ref mut tex_coords) = tex_coord {
|
|
tex_coords.next()
|
|
} else {
|
|
None
|
|
}
|
|
.unwrap_or([0.0; 2]),
|
|
tangent: [0.0; 3], // todo: tangent from model
|
|
bitangent: [0.0; 3],
|
|
})
|
|
}
|
|
} else {
|
|
vertex_data = Vec::new();
|
|
}
|
|
let vertex_buffer =
|
|
self.device
|
|
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
label: Some("Vertex Buffer"),
|
|
contents: bytemuck::cast_slice(vertex_data.as_slice()),
|
|
usage: wgpu::BufferUsages::VERTEX,
|
|
});
|
|
let vertices_result = (vertex_buffer, vertex_data.len() as u32);
|
|
let indices_result = if let Some(indices) = reader.read_indices() {
|
|
let index_data: Vec<u32> = indices.into_u32().collect();
|
|
Some((
|
|
self.device
|
|
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
label: Some("Index Buffer"),
|
|
contents: bytemuck::cast_slice(index_data.as_slice()),
|
|
usage: wgpu::BufferUsages::INDEX,
|
|
}),
|
|
index_data.len() as u32,
|
|
))
|
|
} else {
|
|
None
|
|
};
|
|
SimpleMesh {
|
|
indices: indices_result,
|
|
vertices: vertices_result,
|
|
}
|
|
})
|
|
.clone(),
|
|
)
|
|
}
|
|
pub fn load_node_from_gltf(
|
|
&mut self,
|
|
node: gltf::Node,
|
|
meshes: &mut HashMap<GltfVertexBufferKey, SimpleMesh>,
|
|
textures: &mut HashMap<usize, SimpleTexture>,
|
|
images: &Vec<gltf::image::Data>,
|
|
buffers: &Vec<gltf::buffer::Data>,
|
|
) -> SimpleTreeNode {
|
|
let mut tree_node = SimpleTreeNode {
|
|
object: None,
|
|
children: Vec::new(),
|
|
name: node.name().unwrap_or("Node").to_string(),
|
|
};
|
|
if let Some(mesh) = node.mesh() {
|
|
let len = mesh.primitives().len();
|
|
for primitive in mesh.primitives() {
|
|
let pbr = primitive.material().pbr_metallic_roughness();
|
|
let material = primitive.material();
|
|
let color = pbr.base_color_factor();
|
|
let metal = pbr.metallic_factor();
|
|
let rough = pbr.roughness_factor();
|
|
let light = primitive.material().emissive_factor();
|
|
let base = match pbr.base_color_texture() {
|
|
Some(info) => textures
|
|
.entry(info.texture().source().index())
|
|
.or_insert_with(|| self.new_texture_from_gltf(&info.texture(), &images))
|
|
.clone(),
|
|
None => self.default_texture.clone(),
|
|
};
|
|
let reflect = match pbr.metallic_roughness_texture() {
|
|
Some(info) => textures
|
|
.entry(info.texture().source().index())
|
|
.or_insert_with(|| self.new_texture_from_gltf(&info.texture(), &images))
|
|
.clone(),
|
|
None => self.default_texture.clone(),
|
|
};
|
|
let normal = match material.normal_texture() {
|
|
Some(info) => textures
|
|
.entry(info.texture().source().index())
|
|
.or_insert_with(|| self.new_texture_from_gltf(&info.texture(), &images))
|
|
.clone(),
|
|
None => self.default_texture.clone(),
|
|
};
|
|
let material = self.new_material(&base, &normal, &reflect);
|
|
let mesh = self.new_mesh_from_gltf(primitive, meshes, buffers);
|
|
let object = SimpleObject(Rc::new(RefCell::new(SimpleObjectData {
|
|
model: Some(SimpleModelData {
|
|
instance: SimpleModelInstance {
|
|
transform: Mat4::default(),
|
|
color: Vec4::from_array(color),
|
|
lights: [0; 16],
|
|
point_lights: 0,
|
|
spot_lights: 0,
|
|
metal,
|
|
rough,
|
|
},
|
|
material,
|
|
mesh,
|
|
}),
|
|
collider: SimpleColliderData {
|
|
transform: Mat4::default(),
|
|
shape: Shape::None,
|
|
radius: 0.0,
|
|
},
|
|
})));
|
|
if len == 1 {
|
|
tree_node.object = Some(object);
|
|
} else {
|
|
tree_node.children.push(SimpleTreeNode {
|
|
object: Some(object),
|
|
children: Vec::new(),
|
|
name: "Primitive".to_string(),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
for node in node.children() {
|
|
tree_node
|
|
.children
|
|
.push(self.load_node_from_gltf(node, meshes, textures, images, buffers));
|
|
}
|
|
tree_node
|
|
}
|
|
pub fn load_from_gltf(&mut self, slice: impl AsRef<[u8]>) -> SimpleTreeNode {
|
|
let mut root = SimpleTreeNode {
|
|
object: None,
|
|
children: Vec::new(),
|
|
name: "Root".to_string(),
|
|
};
|
|
if let Ok((document, buffers, images)) = gltf::import_slice(slice) {
|
|
let mut meshes: HashMap<GltfVertexBufferKey, SimpleMesh> = HashMap::new();
|
|
let mut textures: HashMap<usize, SimpleTexture> = HashMap::new();
|
|
for scene in document.scenes() {
|
|
let mut scene_node = SimpleTreeNode {
|
|
object: None,
|
|
children: Vec::new(),
|
|
name: scene.name().unwrap_or("Scene").to_string(),
|
|
};
|
|
for node in scene.nodes() {
|
|
scene_node.children.push(self.load_node_from_gltf(
|
|
node,
|
|
&mut meshes,
|
|
&mut textures,
|
|
&images,
|
|
&buffers,
|
|
));
|
|
}
|
|
root.children.push(scene_node)
|
|
}
|
|
}
|
|
root
|
|
}
|
|
pub fn resize(&mut self, width: u32, height: u32) {
|
|
self.surface.configure(&self.device, &self.config);
|
|
self.eye.resize(width, height);
|
|
}
|
|
|
|
|
|
pub fn set_skybox(&mut self, skybox: SimpleTexture) {
|
|
self.eye.skybox(&self.device, skybox);
|
|
}
|
|
pub async fn new(window: &Arc<Window>) -> anyhow::Result<Self> {
|
|
static SKIP_EXTERNAL_OVERLAYS : bool = true; // use a config file or something to allow this to be toggled
|
|
|
|
let bounds = window.inner_size();
|
|
let width = bounds.width;
|
|
let height = bounds.height;
|
|
|
|
if(SKIP_EXTERNAL_OVERLAYS) {
|
|
unsafe { // currently will crash on most Windows systems because wGPU in its infinite
|
|
// wisdom tries to attach overlays from closed or non-existent programs
|
|
std::env::set_var("VK_LOADER_LAYERS_DISABLE", "~implicit~");
|
|
}
|
|
}
|
|
// The instance is a handle to our GPU
|
|
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
|
|
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
backends: wgpu::Backends::PRIMARY,
|
|
#[cfg(target_arch = "wasm32")]
|
|
backends: wgpu::Backends::GL,
|
|
flags: Default::default(),
|
|
memory_budget_thresholds: Default::default(),
|
|
backend_options: Default::default(),
|
|
display: None,
|
|
});
|
|
|
|
let surface = instance.create_surface(window.clone())?;
|
|
|
|
let adapter = instance
|
|
.request_adapter(&wgpu::RequestAdapterOptions {
|
|
power_preference: wgpu::PowerPreference::default(),
|
|
compatible_surface: Some(&surface),
|
|
force_fallback_adapter: false,
|
|
})
|
|
.await?;
|
|
|
|
let (device, queue) = adapter
|
|
.request_device(&wgpu::DeviceDescriptor {
|
|
label: None,
|
|
required_features: wgpu::Features::empty(),
|
|
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
|
// WebGL doesn't support all of wgpu's features, so if
|
|
// we're building for the web we'll have to disable some.
|
|
required_limits: if cfg!(target_arch = "wasm32") {
|
|
wgpu::Limits::downlevel_webgl2_defaults()
|
|
} else {
|
|
wgpu::Limits::default()
|
|
},
|
|
memory_hints: Default::default(),
|
|
trace: wgpu::Trace::Off,
|
|
})
|
|
.await?;
|
|
|
|
let surface_caps = surface.get_capabilities(&adapter);
|
|
// Shader code in this tutorial assumes an sRGB surface texture. Using a different
|
|
// one will result in all the colors coming out darker. If you want to support non
|
|
// sRGB surfaces, you'll need to account for that when drawing to the frame.
|
|
let surface_format = surface_caps
|
|
.formats
|
|
.iter()
|
|
.find(|f| f.is_srgb())
|
|
.copied()
|
|
.unwrap_or(surface_caps.formats[0]);
|
|
let config = wgpu::SurfaceConfiguration {
|
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
format: surface_format,
|
|
width,
|
|
height,
|
|
present_mode: surface_caps.present_modes[0],
|
|
alpha_mode: surface_caps.alpha_modes[0],
|
|
view_formats: vec![],
|
|
desired_maximum_frame_latency: 2,
|
|
};
|
|
|
|
let shader =
|
|
device.create_shader_module(wgpu::include_wgsl!("../assets/SimpleShader.wgsl"));
|
|
|
|
let default_texture = SimpleTexture::load(
|
|
&device,
|
|
&queue,
|
|
[0, 0, 255, 255],
|
|
TextureProperties {
|
|
width: 1,
|
|
height: 1,
|
|
},
|
|
);
|
|
|
|
let eye = Eye::new(&device, width, height, default_texture.clone());
|
|
let texture_bind_group_layout =
|
|
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
entries: &[
|
|
wgpu::BindGroupLayoutEntry {
|
|
binding: 0,
|
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
count: None,
|
|
},
|
|
wgpu::BindGroupLayoutEntry {
|
|
binding: 1,
|
|
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: 2,
|
|
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: 3,
|
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
ty: wgpu::BindingType::Texture {
|
|
multisampled: false,
|
|
view_dimension: wgpu::TextureViewDimension::D2,
|
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
|
},
|
|
count: None,
|
|
},
|
|
],
|
|
label: Some("texture_bind_group_layout"),
|
|
});
|
|
|
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
label: Some("Render Pipeline Layout"),
|
|
bind_group_layouts: &[Some(&eye.layout), Some(&texture_bind_group_layout)],
|
|
immediate_size: 0,
|
|
});
|
|
|
|
let instance_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
label: Some("Render Pipeline"),
|
|
layout: Some(&pipeline_layout),
|
|
vertex: wgpu::VertexState {
|
|
module: &shader,
|
|
entry_point: Some("vs_main"),
|
|
buffers: &[SimpleVertex::desc(), SimpleInstances::desc()],
|
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
|
},
|
|
fragment: Some(wgpu::FragmentState {
|
|
// 3.
|
|
module: &shader,
|
|
entry_point: Some("fs_main"),
|
|
targets: &[Some(wgpu::ColorTargetState {
|
|
// 4.
|
|
format: config.format,
|
|
blend: Some(wgpu::BlendState::REPLACE),
|
|
write_mask: wgpu::ColorWrites::ALL,
|
|
})],
|
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
|
}),
|
|
primitive: Default::default(), /*wgpu::PrimitiveState {
|
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
|
strip_index_format: None,
|
|
front_face: wgpu::FrontFace::Ccw,
|
|
cull_mode: None,
|
|
polygon_mode: wgpu::PolygonMode::Fill,
|
|
unclipped_depth: false,
|
|
conservative: false,
|
|
}*/
|
|
depth_stencil: Some(wgpu::DepthStencilState {
|
|
stencil: wgpu::StencilState::default(),
|
|
format: wgpu::TextureFormat::Depth32Float,
|
|
depth_write_enabled: Some(true),
|
|
depth_compare: Some(wgpu::CompareFunction::Less),
|
|
bias: wgpu::DepthBiasState::default(),
|
|
}),
|
|
multisample: wgpu::MultisampleState {
|
|
count: 1,
|
|
mask: !0,
|
|
alpha_to_coverage_enabled: false,
|
|
},
|
|
multiview_mask: None,
|
|
cache: None,
|
|
});
|
|
|
|
//todo: use a pipeline cache!
|
|
let sky_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
label: Some("Sky Pipeline"),
|
|
layout: Some(&pipeline_layout),
|
|
vertex: wgpu::VertexState {
|
|
module: &shader,
|
|
entry_point: Some("vs_sky"),
|
|
compilation_options: Default::default(),
|
|
buffers: &[],
|
|
},
|
|
fragment: Some(wgpu::FragmentState {
|
|
module: &shader,
|
|
entry_point: Some("fs_sky"),
|
|
compilation_options: Default::default(),
|
|
targets: &[Some(wgpu::ColorTargetState {
|
|
// 4.
|
|
format: config.format,
|
|
blend: Some(wgpu::BlendState::REPLACE),
|
|
write_mask: wgpu::ColorWrites::ALL,
|
|
})],
|
|
}),
|
|
primitive: wgpu::PrimitiveState {
|
|
front_face: wgpu::FrontFace::Cw,
|
|
..Default::default()
|
|
},
|
|
depth_stencil: Some(wgpu::DepthStencilState {
|
|
format: wgpu::TextureFormat::Depth32Float,
|
|
depth_write_enabled: Some(false),
|
|
depth_compare: Some(wgpu::CompareFunction::LessEqual),
|
|
stencil: wgpu::StencilState::default(),
|
|
bias: wgpu::DepthBiasState::default(),
|
|
}),
|
|
multisample: wgpu::MultisampleState::default(),
|
|
multiview_mask: None,
|
|
cache: None,
|
|
});
|
|
|
|
let size = wgpu::Extent3d {
|
|
// 2.
|
|
width: config.width.max(1),
|
|
height: config.height.max(1),
|
|
depth_or_array_layers: 1,
|
|
};
|
|
let desc = wgpu::TextureDescriptor {
|
|
label: Some("depth texture"),
|
|
size,
|
|
mip_level_count: 1,
|
|
sample_count: 1,
|
|
dimension: wgpu::TextureDimension::D2,
|
|
format: wgpu::TextureFormat::Depth32Float,
|
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT // 3.
|
|
| wgpu::TextureUsages::TEXTURE_BINDING,
|
|
view_formats: &[],
|
|
};
|
|
let depth_texture = device.create_texture(&desc);
|
|
let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
let depth_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
|
// 4.
|
|
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::Linear,
|
|
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
|
compare: Some(wgpu::CompareFunction::LessEqual), // 5.
|
|
lod_min_clamp: 0.0,
|
|
lod_max_clamp: 100.0,
|
|
..Default::default()
|
|
});
|
|
|
|
let instances = SimpleInstances::new(&device);
|
|
|
|
let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
label: Some("Vertex Buffer"),
|
|
contents: bytemuck::cast_slice(&DEFAULT_VERTICES),
|
|
usage: wgpu::BufferUsages::VERTEX,
|
|
});
|
|
|
|
let default_material = SimpleMaterial::new(
|
|
&device,
|
|
&texture_bind_group_layout,
|
|
&default_texture,
|
|
&default_texture,
|
|
&default_texture,
|
|
MaterialProperties::default(),
|
|
);
|
|
|
|
Ok(Renderer {
|
|
id_count: 0,
|
|
material_layout: texture_bind_group_layout,
|
|
default_texture,
|
|
default_material,
|
|
depth_texture: SimpleTexture {
|
|
texture: depth_texture,
|
|
view: depth_view,
|
|
},
|
|
instances,
|
|
sky_pipeline,
|
|
instance_pipeline,
|
|
surface,
|
|
config,
|
|
device,
|
|
queue,
|
|
eye,
|
|
})
|
|
}
|
|
pub(crate) fn render(&mut self, window: &Arc<Window>) -> anyhow::Result<()> {
|
|
window.request_redraw();
|
|
|
|
let mut resize_renderer = false;
|
|
|
|
let output = match self.surface.get_current_texture() {
|
|
wgpu::CurrentSurfaceTexture::Success(surface_texture) => surface_texture,
|
|
wgpu::CurrentSurfaceTexture::Suboptimal(surface_texture) => {
|
|
resize_renderer = true; //moved reconfigure to avoid a crash when resizing window
|
|
surface_texture
|
|
}
|
|
wgpu::CurrentSurfaceTexture::Timeout
|
|
| wgpu::CurrentSurfaceTexture::Occluded
|
|
| wgpu::CurrentSurfaceTexture::Validation => {
|
|
// Skip this frame
|
|
return Ok(());
|
|
}
|
|
wgpu::CurrentSurfaceTexture::Outdated => {
|
|
self.surface.configure(&self.device, &self.config);
|
|
return Ok(());
|
|
}
|
|
wgpu::CurrentSurfaceTexture::Lost => {
|
|
// You could recreate the devices and all resources
|
|
// created with it here, but we'll just bail
|
|
anyhow::bail!("Lost device");
|
|
}
|
|
};
|
|
let view = output
|
|
.texture
|
|
.create_view(&wgpu::TextureViewDescriptor::default());
|
|
|
|
let program = self.instances.write_instances(&self.device, &self.queue);
|
|
|
|
let mut encoder = self
|
|
.device
|
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
|
label: Some("Render Encoder"),
|
|
});
|
|
|
|
{
|
|
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
label: Some("Render Pass"),
|
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
view: &view,
|
|
resolve_target: None,
|
|
depth_slice: None,
|
|
ops: wgpu::Operations {
|
|
load: wgpu::LoadOp::Clear(wgpu::Color {
|
|
r: 0.5,
|
|
g: 0.6,
|
|
b: 0.8,
|
|
a: 1.0,
|
|
}),
|
|
store: wgpu::StoreOp::Store,
|
|
},
|
|
})],
|
|
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
|
view: &self.depth_texture.view,
|
|
depth_ops: Some(wgpu::Operations {
|
|
load: wgpu::LoadOp::Clear(1.0),
|
|
store: wgpu::StoreOp::Store,
|
|
}),
|
|
stencil_ops: None,
|
|
}),
|
|
occlusion_query_set: None,
|
|
timestamp_writes: None,
|
|
multiview_mask: None,
|
|
});
|
|
|
|
pass.set_pipeline(&self.instance_pipeline);
|
|
pass.set_bind_group(0, &self.eye.group, &[]);
|
|
pass.set_vertex_buffer(1, self.instances.instance_buffer.slice(..));
|
|
program.render(&mut pass);
|
|
|
|
pass.set_bind_group(1, &self.default_material.group, &[]);
|
|
|
|
pass.set_pipeline(&self.sky_pipeline);
|
|
pass.draw(0..6, 0..1);
|
|
}
|
|
|
|
self.queue.submit(Some(encoder.finish()));
|
|
|
|
self.eye.write(&self.queue);
|
|
|
|
output.present();
|
|
|
|
|
|
if(resize_renderer){
|
|
std::mem::drop(view); //idk how this would affect a multi-pass system like deferred rendering, but this makes sure it's not gonna collide with resizing the swap chain or whatever wGPU does
|
|
self.surface.configure(&self.device, &self.config);
|
|
/*
|
|
also in this snippet call the functions to
|
|
a: resize window resolution
|
|
b: resize projection
|
|
currently when you resize the window the projection matrix is not updated
|
|
and therefore it gets very skewed and weird looking if you resize the current
|
|
small window to a big screen like mine(halbear) which is 3440x1440
|
|
i would add it myself but i don't know wGPU or rust all that well sooooo
|
|
*/
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|