initial commit

This commit is contained in:
lisk77 2024-10-26 02:15:26 +02:00
commit 6154c72b0e
55 changed files with 9481 additions and 0 deletions

View file

@ -0,0 +1,19 @@
[package]
name = "comet_resources"
version = "0.1.0"
edition = "2021"
[dependencies]
comet_log = { path = "../comet_log" }
chrono = "0.4.38"
wgpu = { version = "22.0"}
anyhow = "1.0"
tobj = { version = "3.2", default-features = false, features = ["async"]}
bytemuck = { version = "1.16", features = [ "derive" ] }
log = "0.4.22"
[dependencies.image]
version = "0.24"
default-features = false
features = ["png", "jpeg", "hdr"]

View file

@ -0,0 +1,146 @@
pub use resources::*;
pub use texture::*;
pub use vertex::*;
pub mod resources;
pub mod texture;
pub mod vertex;
pub mod texture_atlas;
/*use std::io::{BufReader, Cursor};
use wgpu::util::DeviceExt;
use crate::{model, texture};
pub async fn load_string(file_name: &str) -> anyhow::Result<String> {
let path = std::path::Path::new(env!("OUT_DIR"))
.join("res")
.join(file_name);
let txt = std::fs::read_to_string(path)?;
Ok(txt)
}
pub async fn load_binary(file_name: &str) -> anyhow::Result<Vec<u8>> {
let path = std::path::Path::new(env!("OUT_DIR"))
.join("res")
.join(file_name);
let data = std::fs::read(path)?;
Ok(data)
}
pub async fn load_texture(
file_name: &str,
device: &wgpu::Device,
queue: &wgpu::Queue,
) -> anyhow::Result<texture::Texture> {
let data = load_binary(file_name).await?;
texture::Texture::from_bytes(device, queue, &data, file_name)
}
pub async fn load_model(
file_name: &str,
device: &wgpu::Device,
queue: &wgpu::Queue,
layout: &wgpu::BindGroupLayout,
) -> anyhow::Result<model::Model> {
let obj_text = load_string(file_name).await?;
let obj_cursor = Cursor::new(obj_text);
let mut obj_reader = BufReader::new(obj_cursor);
let (models, obj_materials) = tobj::load_obj_buf_async(
&mut obj_reader,
&tobj::LoadOptions {
triangulate: true,
single_index: true,
..Default::default()
},
|p| async move {
let mat_text = load_string(&p).await.unwrap();
tobj::load_mtl_buf(&mut BufReader::new(Cursor::new(mat_text)))
},
)
.await?;
let mut materials = Vec::new();
for m in obj_materials? {
let diffuse_texture = load_texture(&m.diffuse_texture, device, queue).await?;
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(model::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| {
if m.mesh.normals.is_empty(){
model::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]],
normal: [0.0, 0.0, 0.0],
}
}else{
model::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]],
normal: [
m.mesh.normals[i * 3],
m.mesh.normals[i * 3 + 1],
m.mesh.normals[i * 3 + 2],
],
}
}
})
.collect::<Vec<_>>();
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", file_name)),
contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Index Buffer", file_name)),
contents: bytemuck::cast_slice(&m.mesh.indices),
usage: wgpu::BufferUsages::INDEX,
});
log::info!("Mesh: {}", m.name);
model::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::Model { meshes, materials })
}*/

View file

@ -0,0 +1,182 @@
use std::{
collections::HashMap, path::Path
};
use wgpu::{Device, FilterMode, Queue, TextureFormat, TextureUsages};
use crate::{texture, Texture};
use crate::texture_atlas::{TextureAtlas, TextureRegion};
pub struct ResourceManager {
texture_atlas: TextureAtlas,
data_files: HashMap<String, String>
}
impl ResourceManager {
pub fn new() -> Self {
Self {
texture_atlas: TextureAtlas::empty(),
data_files: HashMap::new()
}
}
pub fn texture_atlas(&self) -> &TextureAtlas {
&self.texture_atlas
}
pub fn texture_locations(&self) -> &HashMap<String, TextureRegion> {
&self.texture_atlas.textures()
}
pub fn data_files(&self) -> &HashMap<String, String> {
&self.data_files
}
pub fn set_texture_atlas(&mut self, texture_atlas: TextureAtlas) {
self.texture_atlas = texture_atlas;
// This is just for testing purposes
//self.texture_locations.insert("normal_comet.png".to_string(), ([0,0], [15,15]));
//self.texture_locations.insert("green_comet.png".to_string(), ([0,15], [15,31]));
}
pub fn create_texture_atlas(&mut self, paths: Vec<String>) {
self.texture_atlas = TextureAtlas::from_texture_paths(paths)
}
pub async fn load_string(&self, file_name: &str) -> anyhow::Result<String> {
let path = Path::new(std::env::var("OUT_DIR")?.as_str())
.join("res")
.join(file_name);
let txt = std::fs::read_to_string(path)?;
Ok(txt)
}
pub async fn load_binary(&self, file_name: &str) -> anyhow::Result<Vec<u8>> {
let path = Path::new(std::env::var("OUT_DIR").unwrap().as_str())
.join("res")
.join(file_name);
let data = std::fs::read(path)?;
Ok(data)
}
pub async fn load_texture(
&self,
file_name: &str,
is_normal_map: bool,
device: &wgpu::Device,
queue: &wgpu::Queue,
) -> anyhow::Result<texture::Texture> {
let data = self.load_binary(file_name).await?;
texture::Texture::from_bytes(device, queue, &data, file_name, is_normal_map)
}
/*pub async fn load_model(
&self,
file_name: &str,
device: &wgpu::Device,
queue: &wgpu::Queue,
layout: &wgpu::BindGroupLayout,
) -> anyhow::Result<model::Model> {
let obj_text = self.load_string(file_name).await?;
let obj_cursor = Cursor::new(obj_text);
let mut obj_reader = BufReader::new(obj_cursor);
let (models, obj_materials) = tobj::load_obj_buf_async(
&mut obj_reader,
&tobj::LoadOptions {
triangulate: true,
single_index: true,
..Default::default()
},
|p| async move {
let mat_text = self.load_string(&p).await.unwrap();
tobj::load_mtl_buf(&mut BufReader::new(Cursor::new(mat_text)))
},
)
.await?;
let mut materials = Vec::new();
for m in obj_materials? {
let diffuse_texture = self.load_texture(&m.diffuse_texture, false, device, queue).await?;
let normal_texture = self.load_texture(&m.normal_texture, true, device, queue).await?;
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(model::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| {
if m.mesh.normals.is_empty() {
model::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]],
normal: [0.0, 0.0, 0.0],
}
} else {
model::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]],
normal: [
m.mesh.normals[i * 3],
m.mesh.normals[i * 3 + 1],
m.mesh.normals[i * 3 + 2],
],
}
}
})
.collect::<Vec<_>>();
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", file_name)),
contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Index Buffer", file_name)),
contents: bytemuck::cast_slice(&m.mesh.indices),
usage: wgpu::BufferUsages::INDEX,
});
model::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::Model { meshes, materials })
}*/
}

View file

@ -0,0 +1,325 @@
use anyhow::*;
use image::{DynamicImage, GenericImageView, RgbaImage};
use wgpu::{Device, Queue};
#[derive(Debug)]
pub struct Texture {
#[allow(unused)]
pub texture: wgpu::Texture,
pub view: wgpu::TextureView,
pub sampler: wgpu::Sampler,
pub size: wgpu::Extent3d,
}
impl Texture {
pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
pub fn create_depth_texture(
device: &wgpu::Device,
config: &wgpu::SurfaceConfiguration,
label: &str,
) -> Self {
let size = wgpu::Extent3d {
width: config.width.max(1),
height: config.height.max(1),
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {
label: Some(label),
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: Self::DEPTH_FORMAT,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[Self::DEPTH_FORMAT],
};
let texture = device.create_texture(&desc);
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::Linear,
mipmap_filter: wgpu::FilterMode::Nearest,
compare: Some(wgpu::CompareFunction::LessEqual),
lod_min_clamp: 0.0,
lod_max_clamp: 100.0,
..Default::default()
});
Self {
texture,
view,
sampler,
size, // NEW!
}
}
#[allow(dead_code)]
pub fn from_bytes(
device: &wgpu::Device,
queue: &wgpu::Queue,
bytes: &[u8],
label: &str,
is_normal_map: bool,
) -> Result<Self> {
let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, Some(label), is_normal_map)
}
pub fn from_image(
device: &wgpu::Device,
queue: &wgpu::Queue,
img: &image::DynamicImage,
label: Option<&str>,
is_normal_map: bool,
) -> Result<Self> {
let dimensions = img.dimensions();
let rgba = img.to_rgba8();
let format = if is_normal_map {
wgpu::TextureFormat::Rgba8Unorm
} else {
wgpu::TextureFormat::Rgba8UnormSrgb
};
let usage = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST;
let size = wgpu::Extent3d {
width: img.width(),
height: img.height(),
depth_or_array_layers: 1,
};
let texture = Self::create_2d_texture(
device,
size.width,
size.height,
format,
usage,
wgpu::FilterMode::Nearest,
label,
);
queue.write_texture(
wgpu::ImageCopyTexture {
aspect: wgpu::TextureAspect::All,
texture: &texture.texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
},
&rgba,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(4 * dimensions.0),
rows_per_image: Some(dimensions.1),
},
size,
);
Ok(texture)
}
pub(crate) fn create_2d_texture(
device: &wgpu::Device,
width: u32,
height: u32,
format: wgpu::TextureFormat,
usage: wgpu::TextureUsages,
mag_filter: wgpu::FilterMode,
label: Option<&str>,
) -> Self {
let size = wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
};
Self::create_texture(
device,
label,
size,
format,
usage,
wgpu::TextureDimension::D2,
mag_filter,
)
}
pub fn create_texture(
device: &wgpu::Device,
label: Option<&str>,
size: wgpu::Extent3d,
format: wgpu::TextureFormat,
usage: wgpu::TextureUsages,
dimension: wgpu::TextureDimension,
mag_filter: wgpu::FilterMode,
) -> Self {
let texture = device.create_texture(&wgpu::TextureDescriptor {
label,
size,
mip_level_count: 1,
sample_count: 1,
dimension,
format,
usage,
view_formats: &[],
});
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,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
Self {
texture,
view,
sampler,
size, // NEW!
}
}
pub fn to_image(
&self,
device: &wgpu::Device,
queue: &wgpu::Queue,
) -> Result<DynamicImage> {
// Size of the texture
let width = self.size.width;
let height = self.size.height;
// Calculate the size of the texture in bytes
let texture_size_bytes = (4 * width * height) as wgpu::BufferAddress;
// Create a buffer for reading the texture data back from the GPU
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Texture Readback Buffer"),
size: texture_size_bytes,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});
// Create a command encoder to copy the texture data to the buffer
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Texture to Buffer Encoder"),
});
// Define the copy operation from the texture to the buffer
encoder.copy_texture_to_buffer(
wgpu::ImageCopyTexture {
texture: &self.texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::ImageCopyBuffer {
buffer: &buffer,
layout: wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(4 * width),
rows_per_image: Some(height),
},
},
self.size,
);
// Submit the command to the queue
queue.submit(Some(encoder.finish()));
// Wait for the GPU to finish the operation
let buffer_slice = buffer.slice(..);
buffer_slice.map_async(wgpu::MapMode::Read, |result| {
if let Err(e) = result {
eprintln!("Failed to map buffer: {:?}", e);
}
});
// Get the buffer data
let data = buffer_slice.get_mapped_range();
// Convert the raw data into an image::RgbaImage
let image = RgbaImage::from_raw(width, height, data.to_vec())
.ok_or_else(|| anyhow!("Failed to create image from raw texture data"))?;
// Unmap the buffer now that we're done with it
buffer.unmap();
// Convert the RgbaImage into a DynamicImage
Ok(DynamicImage::ImageRgba8(image))
}
}
pub struct CubeTexture {
texture: wgpu::Texture,
sampler: wgpu::Sampler,
view: wgpu::TextureView,
}
impl CubeTexture {
pub fn create_2d(
device: &wgpu::Device,
width: u32,
height: u32,
format: wgpu::TextureFormat,
mip_level_count: u32,
usage: wgpu::TextureUsages,
mag_filter: wgpu::FilterMode,
label: Option<&str>,
) -> Self {
let texture = device.create_texture(&wgpu::TextureDescriptor {
label,
size: wgpu::Extent3d {
width,
height,
// A cube has 6 sides, so we need 6 layers
depth_or_array_layers: 6,
},
mip_level_count,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor {
label,
dimension: Some(wgpu::TextureViewDimension::Cube),
array_layer_count: Some(6),
..Default::default()
});
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
label,
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
Self {
texture,
sampler,
view,
}
}
pub fn texture(&self) -> &wgpu::Texture {
&self.texture
}
pub fn view(&self) -> &wgpu::TextureView {
&self.view
}
pub fn sampler(&self) -> &wgpu::Sampler {
&self.sampler
}
}

View file

@ -0,0 +1,186 @@
use std::collections::HashMap;
use std::path::Path;
use chrono::Local;
use std::time::Instant;
use image::{DynamicImage, GenericImage, GenericImageView, ImageFormat};
use comet_log::info;
use wgpu::{Device, FilterMode, TextureFormat, TextureUsages};
use crate::Texture;
#[derive(Debug)]
pub struct TextureRegion {
x0: f32,
y0: f32,
x1: f32,
y1: f32,
dimensions: (u32, u32)
}
impl TextureRegion {
pub fn new(x0: f32, y0: f32, x1: f32, y1: f32, dimensions: (u32, u32)) -> Self {
Self {
x0,
y0,
x1,
y1,
dimensions
}
}
pub fn x0(&self) -> f32 {
self.x0
}
pub fn x1(&self) -> f32 {
self.x1
}
pub fn y0(&self) -> f32 {
self.y0
}
pub fn y1(&self) -> f32 {
self.y1
}
pub fn dimensions(&self) -> (u32, u32) {
self.dimensions
}
}
#[derive(Debug)]
pub struct TextureAtlas {
atlas: DynamicImage,
textures: HashMap<String, TextureRegion>,
}
impl TextureAtlas {
pub fn empty() -> Self {
Self {
atlas: DynamicImage::new(1,1, image::ColorType::Rgb8),
textures: HashMap::new()
}
}
pub fn texture_paths(&self) -> Vec<String> {
self.textures.keys().map(|k| k.to_string()).collect()
}
fn calculate_atlas_width(textures: &Vec<DynamicImage>) -> u32 {
let mut last_height: u32 = textures.get(0).unwrap().height();
let mut widths: Vec<u32> = Vec::new();
let mut current_width: u32 = 0;
for texture in textures {
if last_height != texture.height() {
widths.push(current_width);
current_width = 0;
last_height = texture.height();
}
current_width += texture.width();
}
widths.push(current_width);
*widths.iter().max().unwrap()
}
fn calculate_atlas_height(textures: &Vec<DynamicImage>) -> u32 {
let last_height: u32 = textures.get(0).unwrap().height();
let mut height: u32 = 0;
height += last_height;
for texture in textures {
if last_height == texture.height() {
continue;
}
height += texture.height();
}
height
}
fn insert_texture_at(base: &mut DynamicImage, texture: &DynamicImage, x_pos: u32, y_pos: u32) {
for y in 0..texture.height() {
for x in 0..texture.width() {
let pixel = texture.get_pixel(x,y);
base.put_pixel(x + x_pos, y + y_pos, pixel);
}
}
}
pub fn from_texture_paths(
paths: Vec<String>,
) -> Self {
//let t0 = Instant::now();
let mut textures: Vec<DynamicImage> = Vec::new();
let mut regions: HashMap<String, TextureRegion> = HashMap::new();
info!("Loading textures...");
for path in &paths {
textures.push(image::open(&Path::new(path.as_str())).expect("Failed to load texture"));
}
info!("Textures loaded!");
info!("Sorting textures by height...");
let mut texture_path_pairs: Vec<(&DynamicImage, &String)> = textures.iter().zip(paths.iter()).collect();
texture_path_pairs.sort_by(|a, b| b.0.height().cmp(&a.0.height()));
let (sorted_textures, sorted_paths): (Vec<&DynamicImage>, Vec<&String>) = texture_path_pairs.into_iter().unzip();
let sorted_textures: Vec<DynamicImage> = sorted_textures.into_iter().map(|t| t.clone()).collect();
let sorted_paths: Vec<String> = sorted_paths.into_iter().map(|s| s.to_string()).collect();
let (height, width) = (Self::calculate_atlas_height(&sorted_textures), Self::calculate_atlas_width(&sorted_textures));
let mut base = DynamicImage::new_rgba8(width,height);
let mut previous = sorted_textures.get(0).unwrap().height();
let mut x_offset: u32 = 0;
let mut y_offset: u32 = 0;
info!("Creating texture atlas...");
for (texture, path) in sorted_textures.iter().zip(sorted_paths.iter()) {
if texture.height() != previous {
y_offset += previous;
x_offset = 0;
previous = texture.height();
}
//base.copy_from(texture, x_offset, y_offset).expect("Nope, you propably failed the offets");
Self::insert_texture_at(&mut base, &texture, x_offset, y_offset);
regions.insert(path.to_string(), TextureRegion::new(
x_offset as f32 / width as f32,
y_offset as f32 / height as f32,
(x_offset + texture.width()) as f32 / width as f32,
(y_offset + texture.height()) as f32 / height as f32,
texture.dimensions()
));
x_offset += texture.width();
}
// Save the image to disk as a PNG
//let output_path = Path::new(r"C:\Users\lisk77\Code Sharing\comet-engine\resources\textures\atlas.png");
//base.save_with_format(output_path, ImageFormat::Png).expect("Failed to save texture atlas");
info!("Texture atlas created!");
/*let t1 = Instant::now();
let delta = t1.duration_since(t0);
println!("{:?}", delta);*/
TextureAtlas {
atlas: base,
textures: regions
}
}
pub fn atlas(&self) -> &DynamicImage {
&self.atlas
}
pub fn textures(&self) -> &HashMap<String, TextureRegion> {
&self.textures
}
}

View file

@ -0,0 +1,44 @@
use wgpu::Color;
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable, PartialEq)]
pub struct Vertex {
position: [f32; 3],
tex_coords: [f32; 2],
}
impl Vertex {
pub fn new(position: [f32; 3], tex_coords: [f32; 2]) -> Self {
Self {
position,
tex_coords
}
}
pub fn set_position(&mut self, new_position: [f32;3]) {
self.position = new_position
}
pub fn set_tex_coords(&mut self, new_tex_coords: [f32; 2]) {
self.tex_coords = new_tex_coords
}
pub fn desc() -> wgpu::VertexBufferLayout<'static> {
wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
offset: 0,
shader_location: 0,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
shader_location: 1,
format: wgpu::VertexFormat::Float32x2,
}
]
}
}
}