testing some things

web2
Ben Hansen 2 years ago
parent b8b8444030
commit c269f46a0e

@ -1,32 +0,0 @@
matrix:
include:
- language: rust
addons:
apt:
packages:
- libasound2-dev
rust:
- stable
# - beta
# - nightly
jobs:
# allow_failures:
# - rust: nightly
fast_finish: true
scripts:
- cargo build --verbose
- language: node_js
node_js:
- lts/*
install:
- npm ci
script:
- npm run build
deploy:
provider: pages
skip_cleanup: true
local_dir: docs/.vuepress/dist
github_token: $GITHUB_TOKEN # A token generated on GitHub allowing Travis to push code on you repository. Set in the Travis settings page of your repository, as a secure variable
keep_history: true
on:
branch: master

28
Cargo.lock generated

@ -2870,6 +2870,7 @@ dependencies = [
"image 0.24.0",
"log",
"pollster",
"reqwest",
"tobj 3.2.0",
"wasm-bindgen",
"wasm-bindgen-futures",
@ -2884,14 +2885,21 @@ version = "0.1.0"
dependencies = [
"anyhow",
"bytemuck",
"cfg-if 1.0.0",
"cgmath",
"console_error_panic_hook",
"console_log",
"env_logger",
"fs_extra",
"glob",
"image 0.23.14",
"image 0.24.0",
"log",
"pollster",
"reqwest",
"tobj 3.2.0",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"wgpu",
"winit",
]
@ -2902,14 +2910,21 @@ version = "0.1.0"
dependencies = [
"anyhow",
"bytemuck",
"cfg-if 1.0.0",
"cgmath",
"console_error_panic_hook",
"console_log",
"env_logger",
"fs_extra",
"glob",
"image 0.23.14",
"image 0.24.0",
"log",
"pollster",
"reqwest",
"tobj 3.2.0",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"wgpu",
"winit",
]
@ -2920,15 +2935,22 @@ version = "0.1.0"
dependencies = [
"anyhow",
"bytemuck",
"cfg-if 1.0.0",
"cgmath",
"console_error_panic_hook",
"console_log",
"env_logger",
"fs_extra",
"glob",
"image 0.23.14",
"image 0.24.0",
"log",
"pollster",
"rayon",
"reqwest",
"tobj 3.2.0",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"wgpu",
"winit",
]

@ -1,4 +1,4 @@
(trap 'kill 0' SIGINT;
(trap 'killall background' INT;
wasm-pack build --out-dir ../../../docs/.vuepress/components/wasm/pong code/showcase/pong &
wasm-pack build --out-dir ../../../docs/.vuepress/components/wasm/tutorial1_window code/beginner/tutorial1-window &
wasm-pack build --out-dir ../../../docs/.vuepress/components/wasm/tutorial2_surface code/beginner/tutorial2-surface &
@ -8,5 +8,7 @@ wasm-pack build --out-dir ../../../docs/.vuepress/components/wasm/tutorial5_text
wasm-pack build --out-dir ../../../docs/.vuepress/components/wasm/tutorial6_uniforms code/beginner/tutorial6-uniforms &
wasm-pack build --out-dir ../../../docs/.vuepress/components/wasm/tutorial7_instancing code/beginner/tutorial7-instancing &
wasm-pack build --out-dir ../../../docs/.vuepress/components/wasm/tutorial8_depth code/beginner/tutorial8-depth &
wasm-pack build --out-dir ../../../docs/.vuepress/components/wasm/tutorial9_models code/beginner/tutorial9-models
wasm-pack build --out-dir ../../../docs/.vuepress/components/wasm/tutorial9_models code/beginner/tutorial9-models &
wasm-pack build --out-dir ../../../docs/.vuepress/components/wasm/tutorial10_lighting code/intermediate/tutorial10-lighting &
wasm-pack build --out-dir ../../../docs/.vuepress/components/wasm/tutorial11_normals code/intermediate/tutorial11-normals
)

@ -8,7 +8,7 @@ use winit::{
use wasm_bindgen::prelude::*;
#[cfg_attr(target_arch="wasm32", wasm_bindgen(start))]
pub async fn run() {
pub fn run() {
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));

@ -23,7 +23,7 @@ impl State {
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::Backends::all());
let instance = wgpu::Instance::new(wgpu::Backends::GL);
let surface = unsafe { instance.create_surface(window) };
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {

@ -596,7 +596,6 @@ pub async fn run() {
}
// State::new uses async code, so we're going to wait for it to finish
log::warn!("Creating State");
let mut state = State::new(&window).await;
event_loop.run(move |event, _, control_flow| {

@ -4,6 +4,9 @@ version = "0.1.0"
authors = ["Ben Hansen <bhbenjaminhansen@gmail.com>"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
cfg-if = "1"
anyhow = "1.0"
@ -22,6 +25,7 @@ default-features = false
features = ["png", "jpeg"]
[target.'cfg(target_arch = "wasm32")'.dependencies]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "0.2"
wgpu = { version = "0.12", features = ["webgl"]}

@ -14,7 +14,7 @@
</div>
<script type="module">
import init from "./pkg/tutorial9_models.js";
import init from "./pkg/tutorial10_lighting.js";
init().then(() => {
console.log("WASM Loaded");
});

@ -9,4 +9,4 @@ class CORSRequestHandler(SimpleHTTPRequestHandler):
SimpleHTTPRequestHandler.end_headers(self)
if __name__ == '__main__':
test(CORSRequestHandler, HTTPServer, port=int(sys.argv[1]) if len(sys.argv) > 1 else 8000)
test(CORSRequestHandler, HTTPServer, port=int(sys.argv[1]) if len(sys.argv) > 1 else 8080)

@ -695,15 +695,47 @@ impl State {
}
}
#[cfg_attr(target_arch="wasm32", wasm_bindgen(start))]
pub async fn run() {
env_logger::init();
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(log::Level::Warn).expect("Could't initialize logger");
} else {
env_logger::init();
}
}
let event_loop = EventLoop::new();
let title = env!("CARGO_PKG_NAME");
let window = winit::window::WindowBuilder::new()
.with_title(title)
.build(&event_loop)
.unwrap();
#[cfg(target_arch = "wasm32")]
{
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
dst.append_child(&canvas).ok()?;
Some(())
})
.expect("Couldn't append canvas to document body.");
}
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(&window).await;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {

@ -1,8 +0,0 @@
#version 450
layout(location=0) in vec3 v_color;
layout(location=0) out vec4 f_color;
void main() {
f_color = vec4(v_color, 1.0);
}

@ -1,27 +0,0 @@
#version 450
layout(location=0) in vec3 a_position;
layout(location=0) out vec3 v_color;
layout(set=0, binding=0)
uniform Camera {
vec3 u_view_position; // unused
mat4 u_view_proj;
};
layout(set=1, binding=0)
uniform Light {
vec3 u_position;
vec3 u_color;
};
// Let's keep our light smaller than our other objects
float scale = 0.25;
void main() {
vec3 v_position = a_position * scale + u_position;
gl_Position = u_view_proj * vec4(v_position, 1);
v_color = u_color;
}

@ -59,7 +59,6 @@ pub async fn load_model(
queue: &wgpu::Queue,
layout: &wgpu::BindGroupLayout,
) -> anyhow::Result<model::Model> {
log::warn!("Loading obj text");
let obj_text = load_string(file_name).await?;
let obj_cursor = Cursor::new(obj_text);
let mut obj_reader = BufReader::new(obj_cursor);

@ -1,47 +0,0 @@
#version 450
layout(location=0) in vec2 v_tex_coords;
layout(location=1) in vec3 v_normal;
layout(location=2) in vec3 v_position;
layout(location=0) out vec4 f_color;
layout(set = 0, binding = 0) uniform texture2D t_diffuse;
layout(set = 0, binding = 1) uniform sampler s_diffuse;
layout(set=1, binding=0)
uniform Camera {
vec3 u_view_position;
mat4 u_view_proj; // unused
};
layout(set=2, binding=0) uniform Light {
vec3 light_position;
vec3 light_color;
};
void main() {
vec4 object_color = texture(sampler2D(t_diffuse, s_diffuse), v_tex_coords);
// We don't need (or want) much ambient light, so 0.1 is fine
float ambient_strength = 0.1;
vec3 ambient_color = light_color * ambient_strength;
vec3 normal = normalize(v_normal);
vec3 light_dir = normalize(light_position - v_position);
float diffuse_strength = max(dot(normal, light_dir), 0.0);
vec3 diffuse_color = light_color * diffuse_strength;
vec3 view_dir = normalize(u_view_position - v_position);
vec3 half_dir = normalize(view_dir + light_dir);
float specular_strength = pow(max(dot(normal, half_dir), 0.0), 32);
vec3 specular_color = specular_strength * light_color;
vec3 result = (ambient_color + diffuse_color + specular_color) * object_color.xyz;
// Since lights don't typically (afaik) cast transparency, so we use
// the alpha here at the end.
f_color = vec4(result, object_color.a);
}

@ -1,37 +0,0 @@
#version 450
layout(location=0) in vec3 a_position;
layout(location=1) in vec2 a_tex_coords;
layout(location=2) in vec3 a_normal;
layout(location=0) out vec2 v_tex_coords;
layout(location=1) out vec3 v_normal;
layout(location=2) out vec3 v_position;
layout(set=1, binding=0)
uniform Camera {
vec3 u_view_position; // unused
mat4 u_view_proj;
};
layout(location=5) in vec4 model_matrix_0;
layout(location=6) in vec4 model_matrix_1;
layout(location=7) in vec4 model_matrix_2;
layout(location=8) in vec4 model_matrix_3;
void main() {
mat4 model_matrix = mat4(
model_matrix_0,
model_matrix_1,
model_matrix_2,
model_matrix_3
);
v_tex_coords = a_tex_coords;
mat3 normal_matrix = mat3(transpose(inverse(model_matrix)));
v_normal = normal_matrix * a_normal;
vec4 model_space = model_matrix * vec4(a_position, 1.0);
v_position = model_space.xyz;
gl_Position = u_view_proj * model_space;
}

@ -4,18 +4,39 @@ version = "0.1.0"
authors = ["Ben Hansen <bhbenjaminhansen@gmail.com>"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
cfg-if = "1"
anyhow = "1.0"
bytemuck = { version = "1.4", features = [ "derive" ] }
cgmath = "0.18"
env_logger = "0.9"
pollster = "0.2"
image = "0.23"
log = "0.4"
tobj = "3.0"
tobj = { version = "3.2", features = ["async"]}
wgpu = "0.12"
winit = "0.26"
[dependencies.image]
version = "0.24"
default-features = false
features = ["png", "jpeg"]
[target.'cfg(target_arch = "wasm32")'.dependencies]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "0.2"
wgpu = { version = "0.12", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [
"Document",
"Window",
"Element",
]}
[build-dependencies]
anyhow = "1.0"
fs_extra = "1.2"

@ -8,7 +8,11 @@ use winit::{
window::Window,
};
#[cfg(target_arch="wasm32")]
use wasm_bindgen::prelude::*;
mod model;
mod resources;
mod texture;
use model::{DrawLight, DrawModel, Vertex};
@ -493,14 +497,12 @@ impl State {
label: Some("camera_bind_group"),
});
let res_dir = std::path::Path::new(env!("OUT_DIR")).join("res");
let obj_model = model::Model::load(
let obj_model = resources::load_model(
"cube.obj",
&device,
&queue,
&texture_bind_group_layout,
res_dir.join("cube.obj"),
)
.unwrap();
).await.unwrap();
let light_uniform = LightUniform {
position: [2.0, 2.0, 2.0],
@ -742,15 +744,47 @@ impl State {
}
}
#[cfg_attr(target_arch="wasm32", wasm_bindgen(start))]
pub async fn run() {
env_logger::init();
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(log::Level::Warn).expect("Could't initialize logger");
} else {
env_logger::init();
}
}
let event_loop = EventLoop::new();
let title = env!("CARGO_PKG_NAME");
let window = winit::window::WindowBuilder::new()
.with_title(title)
.build(&event_loop)
.unwrap();
#[cfg(target_arch = "wasm32")]
{
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
dst.append_child(&canvas).ok()?;
Some(())
})
.expect("Couldn't append canvas to document body.");
}
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(&window).await;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {

@ -1,9 +1,4 @@
use anyhow::*;
use cgmath::InnerSpace;
use std::ops::Range;
use std::path::Path;
use tobj::LoadOptions;
use wgpu::util::DeviceExt;
use crate::texture;
@ -14,11 +9,11 @@ pub trait Vertex {
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct ModelVertex {
position: [f32; 3],
tex_coords: [f32; 2],
normal: [f32; 3],
tangent: [f32; 3],
bitangent: [f32; 3],
pub position: [f32; 3],
pub tex_coords: [f32; 2],
pub normal: [f32; 3],
pub tangent: [f32; 3],
pub bitangent: [f32; 3],
}
impl Vertex for ModelVertex {
@ -119,164 +114,6 @@ pub struct Model {
pub materials: Vec<Material>,
}
impl Model {
pub fn load<P: AsRef<Path>>(
device: &wgpu::Device,
queue: &wgpu::Queue,
layout: &wgpu::BindGroupLayout,
path: P,
) -> Result<Self> {
let (obj_models, obj_materials) = tobj::load_obj(
path.as_ref(),
&LoadOptions {
triangulate: true,
single_index: true,
..Default::default()
},
)?;
let obj_materials = obj_materials?;
// We're assuming that the texture files are stored with the obj file
let containing_folder = path.as_ref().parent().context("Directory has no parent")?;
let mut materials = Vec::new();
for mat in obj_materials {
let diffuse_path = mat.diffuse_texture;
let diffuse_texture =
texture::Texture::load(device, queue, containing_folder.join(diffuse_path), false)?;
let normal_path = mat.normal_texture;
let normal_texture =
texture::Texture::load(device, queue, containing_folder.join(normal_path), true)?;
materials.push(Material::new(
device,
&mat.name,
diffuse_texture,
normal_texture,
layout,
));
}
let mut meshes = Vec::new();
for m in obj_models {
let mut vertices = Vec::new();
for i in 0..m.mesh.positions.len() / 3 {
vertices.push(ModelVertex {
position: [
m.mesh.positions[i * 3],
m.mesh.positions[i * 3 + 1],
m.mesh.positions[i * 3 + 2],
]
.into(),
tex_coords: [m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1]].into(),
normal: [
m.mesh.normals[i * 3],
m.mesh.normals[i * 3 + 1],
m.mesh.normals[i * 3 + 2],
]
.into(),
// We'll calculate these later
tangent: [0.0; 3].into(),
bitangent: [0.0; 3].into(),
});
}
let indices = &m.mesh.indices;
let mut triangles_included = (0..vertices.len()).collect::<Vec<_>>();
// Calculate tangents and bitangets. We're going to
// use the triangles, so we need to loop through the
// indices in chunks of 3
for c in indices.chunks(3) {
let v0 = vertices[c[0] as usize];
let v1 = vertices[c[1] as usize];
let v2 = vertices[c[2] as usize];
let pos0: cgmath::Vector3<_> = v0.position.into();
let pos1: cgmath::Vector3<_> = v1.position.into();
let pos2: cgmath::Vector3<_> = v2.position.into();
let uv0: cgmath::Vector2<_> = v0.tex_coords.into();
let uv1: cgmath::Vector2<_> = v1.tex_coords.into();
let uv2: cgmath::Vector2<_> = v2.tex_coords.into();
// Calculate the edges of the triangle
let delta_pos1 = pos1 - pos0;
let delta_pos2 = pos2 - pos0;
// This will give us a direction to calculate the
// tangent and bitangent
let delta_uv1 = uv1 - uv0;
let delta_uv2 = uv2 - uv0;
// Solving the following system of equations will
// give us the tangent and bitangent.
// delta_pos1 = delta_uv1.x * T + delta_u.y * B
// delta_pos2 = delta_uv2.x * T + delta_uv2.y * B
// Luckily, the place I found this equation provided
// the solution!
let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r;
let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r;
// We'll use the same tangent/bitangent for each vertex in the triangle
vertices[c[0] as usize].tangent =
(tangent + cgmath::Vector3::from(vertices[c[0] as usize].tangent)).into();
vertices[c[1] as usize].tangent =
(tangent + cgmath::Vector3::from(vertices[c[1] as usize].tangent)).into();
vertices[c[2] as usize].tangent =
(tangent + cgmath::Vector3::from(vertices[c[2] as usize].tangent)).into();
vertices[c[0] as usize].bitangent =
(bitangent + cgmath::Vector3::from(vertices[c[0] as usize].bitangent)).into();
vertices[c[1] as usize].bitangent =
(bitangent + cgmath::Vector3::from(vertices[c[1] as usize].bitangent)).into();
vertices[c[2] as usize].bitangent =
(bitangent + cgmath::Vector3::from(vertices[c[2] as usize].bitangent)).into();
// Used to average the tangents/bitangents
triangles_included[c[0] as usize] += 1;
triangles_included[c[1] as usize] += 1;
triangles_included[c[2] as usize] += 1;
}
// Average the tangents/bitangents
for (i, n) in triangles_included.into_iter().enumerate() {
let denom = 1.0 / n as f32;
let mut v = &mut vertices[i];
v.tangent = (cgmath::Vector3::from(v.tangent) * denom)
.normalize()
.into();
v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom)
.normalize()
.into();
}
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", path.as_ref())),
contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Index Buffer", path.as_ref())),
contents: bytemuck::cast_slice(&m.mesh.indices),
usage: wgpu::BufferUsages::INDEX,
});
meshes.push(Mesh {
name: m.name,
vertex_buffer,
index_buffer,
num_elements: m.mesh.indices.len() as u32,
material: m.mesh.material_id.unwrap_or(0),
});
}
Ok(Self { meshes, materials })
}
}
pub trait DrawModel<'a> {
fn draw_mesh(
&mut self,

@ -0,0 +1,205 @@
use std::io::{BufReader, Cursor};
use cfg_if::cfg_if;
use wgpu::util::DeviceExt;
use crate::{model, texture};
pub async fn load_string(file_name: &str) -> anyhow::Result<String> {
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
let url = format!("http://127.0.0.1:8080/learn-wgpu/{}", file_name);
let txt = reqwest::get(&url)
.await?
.text()
.await?;
} else {
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>> {
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
let url = format!("http://127.0.0.1:8080/learn-wgpu/{}", file_name);
let data = reqwest::get(url)
.await?
.bytes()
.await?
.to_vec();
} else {
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,
is_normal_map: bool,
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, is_normal_map)
}
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, false, device, queue).await?;
let normal_texture = load_texture(&m.normal_texture, true, device, queue).await?;
materials.push(model::Material::new(
device,
&m.name,
diffuse_texture,
normal_texture,
layout,
));
}
let meshes = models
.into_iter()
.map(|m| {
let mut vertices = (0..m.mesh.positions.len() / 3)
.map(|i| 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], m.mesh.texcoords[i * 2 + 1]],
normal: [
m.mesh.normals[i * 3],
m.mesh.normals[i * 3 + 1],
m.mesh.normals[i * 3 + 2],
],
// We'll calculate these later
tangent: [0.0; 3],
bitangent: [0.0; 3],
})
.collect::<Vec<_>>();
let indices = &m.mesh.indices;
let mut triangles_included = vec![0; vertices.len()];
// Calculate tangents and bitangets. We're going to
// use the triangles, so we need to loop through the
// indices in chunks of 3
for c in indices.chunks(3) {
let v0 = vertices[c[0] as usize];
let v1 = vertices[c[1] as usize];
let v2 = vertices[c[2] as usize];
let pos0: cgmath::Vector3<_> = v0.position.into();
let pos1: cgmath::Vector3<_> = v1.position.into();
let pos2: cgmath::Vector3<_> = v2.position.into();
let uv0: cgmath::Vector2<_> = v0.tex_coords.into();
let uv1: cgmath::Vector2<_> = v1.tex_coords.into();
let uv2: cgmath::Vector2<_> = v2.tex_coords.into();
// Calculate the edges of the triangle
let delta_pos1 = pos1 - pos0;
let delta_pos2 = pos2 - pos0;
// This will give us a direction to calculate the
// tangent and bitangent
let delta_uv1 = uv1 - uv0;
let delta_uv2 = uv2 - uv0;
// Solving the following system of equations will
// give us the tangent and bitangent.
// delta_pos1 = delta_uv1.x * T + delta_u.y * B
// delta_pos2 = delta_uv2.x * T + delta_uv2.y * B
// Luckily, the place I found this equation provided
// the solution!
let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r;
let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r;
// We'll use the same tangent/bitangent for each vertex in the triangle
vertices[c[0] as usize].tangent =
(tangent + cgmath::Vector3::from(vertices[c[0] as usize].tangent)).into();
vertices[c[1] as usize].tangent =
(tangent + cgmath::Vector3::from(vertices[c[1] as usize].tangent)).into();
vertices[c[2] as usize].tangent =
(tangent + cgmath::Vector3::from(vertices[c[2] as usize].tangent)).into();
vertices[c[0] as usize].bitangent =
(bitangent + cgmath::Vector3::from(vertices[c[0] as usize].bitangent)).into();
vertices[c[1] as usize].bitangent =
(bitangent + cgmath::Vector3::from(vertices[c[1] as usize].bitangent)).into();
vertices[c[2] as usize].bitangent =
(bitangent + cgmath::Vector3::from(vertices[c[2] as usize].bitangent)).into();
// Used to average the tangents/bitangents
triangles_included[c[0] as usize] += 1;
triangles_included[c[1] as usize] += 1;
triangles_included[c[2] as usize] += 1;
}
// Average the tangents/bitangents
for (i, n) in triangles_included.into_iter().enumerate() {
let denom = 1.0 / n as f32;
let mut v = &mut vertices[i];
v.tangent = (cgmath::Vector3::from(v.tangent) * denom).into();
v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom).into();
}
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 })
}

@ -1,6 +1,6 @@
use anyhow::*;
use image::GenericImageView;
use std::{num::NonZeroU32, path::Path};
use std::num::NonZeroU32;
pub struct Texture {
pub texture: wgpu::Texture,
@ -11,20 +11,6 @@ pub struct Texture {
impl Texture {
pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
pub fn load<P: AsRef<Path>>(
device: &wgpu::Device,
queue: &wgpu::Queue,
path: P,
is_normal_map: bool,
) -> Result<Self> {
// Needed to appease the borrow checker
let path_copy = path.as_ref().to_path_buf();
let label = path_copy.to_str();
let img = image::open(path)?;
Self::from_image(device, queue, &img, label, is_normal_map)
}
pub fn create_depth_texture(
device: &wgpu::Device,
config: &wgpu::SurfaceConfiguration,

@ -4,18 +4,39 @@ version = "0.1.0"
authors = ["Ben Hansen <bhbenjaminhansen@gmail.com>"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
cfg-if = "1"
anyhow = "1.0"
bytemuck = { version = "1.4", features = [ "derive" ] }
cgmath = "0.18"
env_logger = "0.9"
pollster = "0.2"
image = "0.23"
log = "0.4"
tobj = "3.0"
tobj = { version = "3.2", features = ["async"]}
wgpu = "0.12"
winit = "0.26"
[dependencies.image]
version = "0.24"
default-features = false
features = ["png", "jpeg"]
[target.'cfg(target_arch = "wasm32")'.dependencies]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "0.2"
wgpu = { version = "0.12", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [
"Document",
"Window",
"Element",
]}
[build-dependencies]
anyhow = "1.0"
fs_extra = "1.2"

@ -8,6 +8,9 @@ use winit::{
window::Window,
};
#[cfg(target_arch="wasm32")]
use wasm_bindgen::prelude::*;
mod camera;
mod model;
mod texture; // NEW!

@ -4,19 +4,40 @@ version = "0.1.0"
authors = ["Ben Hansen <bhbenjaminhansen@gmail.com>"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
cfg-if = "1"
anyhow = "1.0"
bytemuck = { version = "1.4", features = [ "derive" ] }
cgmath = "0.18"
env_logger = "0.9"
pollster = "0.2"
image = "0.23"
log = "0.4"
rayon = "1.4" # NEW!
tobj = "3.0"
tobj = { version = "3.2", features = ["async"]}
wgpu = "0.12"
winit = "0.26"
[dependencies.image]
version = "0.24"
default-features = false
features = ["png", "jpeg"]
[target.'cfg(target_arch = "wasm32")'.dependencies]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "0.2"
wgpu = { version = "0.12", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [
"Document",
"Window",
"Element",
]}
[build-dependencies]
anyhow = "1.0"
fs_extra = "1.2"

@ -8,6 +8,9 @@ use winit::{
window::Window,
};
#[cfg(target_arch="wasm32")]
use wasm_bindgen::prelude::*;
mod camera;
mod model;
mod texture; // NEW!

@ -1,28 +0,0 @@
#!/usr/bin/env sh
# abort on errors
set -e
# build
npm run build
# navigate into the build output directory
cd docs/.vuepress/dist
# if you are deploying to a custom domain
# echo 'www.example.com' > CNAME
git init
git add -A
git commit -m 'deploy'
# if you are deploying to https://<USERNAME>.github.io
# git push -f git@github.com:<USERNAME>/<USERNAME>.github.io.git master
# if you are deploying to https://<USERNAME>.github.io/<REPO>
git push -f git@github.com:sotrh/learn-wgpu.git master:gh-pages
# cleanup
rm -rf ./.git
cd -

@ -195,6 +195,11 @@ I'm using [reqwest](https://docs.rs/reqwest) to handle loading the requests when
reqwest = { version = "0.11" }
```
Make sure to add `resources` as a module in `lib.rs`:
```rust
mod resources;
```
## Loading models with TOBJ

@ -805,7 +805,7 @@ Don't forget to update the `Camera` struct in `light.wgsl` as well, as if it doe
We're going to need to update the `CameraUniform` struct as well.
```rust
// main.rs
// lib.rs
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct CameraUniform {
@ -832,7 +832,7 @@ impl CameraUniform {
Since we want to use our uniforms in the fragment shader now, we need to change it's visibility.
```rust
// main.rs
// lib.rs
let camera_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutBinding {
@ -891,4 +891,6 @@ It's hard to tell the difference, but here's the results.
![./half_dir.png](./half_dir.png)
<WasmExample example="tutorial10_lighting"></WasmExample>
<AutoGithubLink/>

@ -627,4 +627,6 @@ That gives us something like this.
You can find the textures I use in the Github Repository.
<WasmExample example="tutorial11_normals"></WasmExample>
<AutoGithubLink/>

Loading…
Cancel
Save