tutorial12-camera running in wasm (needs input fix)

web2
Ben Hansen 2 years ago
parent 0a28699d00
commit e8ce0e710d

53
Cargo.lock generated

@ -1379,7 +1379,6 @@ checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3"
dependencies = [
"libc",
"libloading",
"pkg-config",
]
[[package]]
@ -1517,19 +1516,6 @@ dependencies = [
"objc",
]
[[package]]
name = "metal"
version = "0.23.1"
source = "git+https://github.com/gfx-rs/metal-rs?rev=a357159#a35715916fec38bbc08a510ecf7d115edc500c72"
dependencies = [
"bitflags",
"block",
"core-graphics-types",
"foreign-types",
"log",
"objc",
]
[[package]]
name = "mime"
version = "0.3.16"
@ -1645,7 +1631,7 @@ dependencies = [
[[package]]
name = "naga"
version = "0.8.0"
source = "git+https://github.com/gfx-rs/naga?rev=a45b9a6#a45b9a6cc691a671aa24a32114b51c5acae02420"
source = "git+https://github.com/gfx-rs/naga?rev=09d35f3#09d35f363134920a9a477cefa32b29bb4416092b"
dependencies = [
"bit-set",
"bitflags",
@ -2945,7 +2931,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"wgpu 0.12.0 (git+https://github.com/gfx-rs/wgpu)",
"wgpu 0.12.0 (git+https://github.com/gfx-rs/wgpu?branch=gecko)",
"winit",
]
@ -2970,7 +2956,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"wgpu 0.12.0 (git+https://github.com/gfx-rs/wgpu)",
"wgpu 0.12.0 (git+https://github.com/gfx-rs/wgpu?branch=gecko)",
"winit",
]
@ -2988,6 +2974,7 @@ dependencies = [
"fs_extra",
"glob",
"image 0.24.0",
"instant",
"log",
"pollster",
"reqwest",
@ -2995,7 +2982,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"wgpu 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"wgpu 0.12.0 (git+https://github.com/gfx-rs/wgpu?branch=gecko)",
"winit",
]
@ -3594,21 +3581,21 @@ dependencies = [
[[package]]
name = "wgpu"
version = "0.12.0"
source = "git+https://github.com/gfx-rs/wgpu#d3c210160f90e5fb960025bc1ca155a8eea19052"
source = "git+https://github.com/gfx-rs/wgpu?branch=gecko#f99d8604039549272abf523abc0a2cc2d4ac4fb9"
dependencies = [
"arrayvec",
"js-sys",
"log",
"naga 0.8.0 (git+https://github.com/gfx-rs/naga?rev=a45b9a6)",
"naga 0.8.0 (git+https://github.com/gfx-rs/naga?rev=09d35f3)",
"parking_lot",
"raw-window-handle 0.4.2",
"smallvec",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"wgpu-core 0.12.0 (git+https://github.com/gfx-rs/wgpu)",
"wgpu-hal 0.12.0 (git+https://github.com/gfx-rs/wgpu)",
"wgpu-types 0.12.0 (git+https://github.com/gfx-rs/wgpu)",
"wgpu-core 0.12.0 (git+https://github.com/gfx-rs/wgpu?branch=gecko)",
"wgpu-hal 0.12.0 (git+https://github.com/gfx-rs/wgpu?branch=gecko)",
"wgpu-types 0.12.0 (git+https://github.com/gfx-rs/wgpu?branch=gecko)",
]
[[package]]
@ -3637,7 +3624,7 @@ dependencies = [
[[package]]
name = "wgpu-core"
version = "0.12.0"
source = "git+https://github.com/gfx-rs/wgpu#d3c210160f90e5fb960025bc1ca155a8eea19052"
source = "git+https://github.com/gfx-rs/wgpu?branch=gecko#f99d8604039549272abf523abc0a2cc2d4ac4fb9"
dependencies = [
"arrayvec",
"bitflags",
@ -3646,14 +3633,14 @@ dependencies = [
"copyless",
"fxhash",
"log",
"naga 0.8.0 (git+https://github.com/gfx-rs/naga?rev=a45b9a6)",
"naga 0.8.0 (git+https://github.com/gfx-rs/naga?rev=09d35f3)",
"parking_lot",
"profiling",
"raw-window-handle 0.4.2",
"smallvec",
"thiserror",
"wgpu-hal 0.12.0 (git+https://github.com/gfx-rs/wgpu)",
"wgpu-types 0.12.0 (git+https://github.com/gfx-rs/wgpu)",
"wgpu-hal 0.12.0 (git+https://github.com/gfx-rs/wgpu?branch=gecko)",
"wgpu-types 0.12.0 (git+https://github.com/gfx-rs/wgpu?branch=gecko)",
]
[[package]]
@ -3679,7 +3666,7 @@ dependencies = [
"khronos-egl",
"libloading",
"log",
"metal 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)",
"metal",
"naga 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"objc",
"parking_lot",
@ -3697,7 +3684,7 @@ dependencies = [
[[package]]
name = "wgpu-hal"
version = "0.12.0"
source = "git+https://github.com/gfx-rs/wgpu#d3c210160f90e5fb960025bc1ca155a8eea19052"
source = "git+https://github.com/gfx-rs/wgpu?branch=gecko#f99d8604039549272abf523abc0a2cc2d4ac4fb9"
dependencies = [
"arrayvec",
"ash 0.35.2+1.2.203",
@ -3716,8 +3703,8 @@ dependencies = [
"khronos-egl",
"libloading",
"log",
"metal 0.23.1 (git+https://github.com/gfx-rs/metal-rs?rev=a357159)",
"naga 0.8.0 (git+https://github.com/gfx-rs/naga?rev=a45b9a6)",
"metal",
"naga 0.8.0 (git+https://github.com/gfx-rs/naga?rev=09d35f3)",
"objc",
"parking_lot",
"profiling",
@ -3727,7 +3714,7 @@ dependencies = [
"thiserror",
"wasm-bindgen",
"web-sys",
"wgpu-types 0.12.0 (git+https://github.com/gfx-rs/wgpu)",
"wgpu-types 0.12.0 (git+https://github.com/gfx-rs/wgpu?branch=gecko)",
"winapi",
]
@ -3756,7 +3743,7 @@ dependencies = [
[[package]]
name = "wgpu-types"
version = "0.12.0"
source = "git+https://github.com/gfx-rs/wgpu#d3c210160f90e5fb960025bc1ca155a8eea19052"
source = "git+https://github.com/gfx-rs/wgpu?branch=gecko#f99d8604039549272abf523abc0a2cc2d4ac4fb9"
dependencies = [
"bitflags",
]

@ -16,7 +16,7 @@ env_logger = "0.9"
pollster = "0.2"
log = "0.4"
tobj = { version = "3.2", features = ["async"]}
wgpu = { version = "0.12", git="https://github.com/gfx-rs/wgpu"}
wgpu = { version = "0.12", git="https://github.com/gfx-rs/wgpu", branch="gecko"}
winit = "0.26"
[dependencies.image]
@ -28,7 +28,7 @@ features = ["png", "jpeg"]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "0.2"
wgpu = { version = "0.12", git="https://github.com/gfx-rs/wgpu", features = ["webgl"]}
wgpu = { version = "0.12", git="https://github.com/gfx-rs/wgpu", branch="gecko", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -4,26 +4,26 @@ struct Camera {
view_pos: vec4<f32>;
view_proj: mat4x4<f32>;
};
@group(0) @binding(0)
[[group(0), binding(0)]]
var<uniform> camera: Camera;
struct Light {
position: vec3<f32>;
color: vec3<f32>;
};
@group(1) @binding(0)
[[group(1), binding(0)]]
var<uniform> light: Light;
struct VertexInput {
@location(0) position: vec3<f32>;
[[location(0)]] position: vec3<f32>;
};
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>;
@location(0) color: vec3<f32>;
[[builtin(position)]] clip_position: vec4<f32>;
[[location(0)]] color: vec3<f32>;
};
@stage(vertex)
[[stage(vertex)]]
fn vs_main(
model: VertexInput,
) -> VertexOutput {
@ -36,7 +36,7 @@ fn vs_main(
// Fragment shader
@stage(fragment)
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
[[stage(fragment)]]
fn fs_main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
return vec4<f32>(in.color, 1.0);
}

@ -4,39 +4,39 @@ struct Camera {
view_pos: vec4<f32>;
view_proj: mat4x4<f32>;
};
@group(1) @binding(0)
[[group(1), binding(0)]]
var<uniform> camera: Camera;
struct Light {
position: vec3<f32>;
color: vec3<f32>;
};
@group(2) @binding(0)
[[group(2), binding(0)]]
var<uniform> light: Light;
struct VertexInput {
@location(0) position: vec3<f32>;
@location(1) tex_coords: vec2<f32>;
@location(2) normal: vec3<f32>;
[[location(0)]] position: vec3<f32>;
[[location(1)]] tex_coords: vec2<f32>;
[[location(2)]] normal: vec3<f32>;
};
struct InstanceInput {
@location(5) model_matrix_0: vec4<f32>;
@location(6) model_matrix_1: vec4<f32>;
@location(7) model_matrix_2: vec4<f32>;
@location(8) model_matrix_3: vec4<f32>;
@location(9) normal_matrix_0: vec3<f32>;
@location(10) normal_matrix_1: vec3<f32>;
@location(11) normal_matrix_2: vec3<f32>;
[[location(5)]] model_matrix_0: vec4<f32>;
[[location(6)]] model_matrix_1: vec4<f32>;
[[location(7)]] model_matrix_2: vec4<f32>;
[[location(8)]] model_matrix_3: vec4<f32>;
[[location(9)]] normal_matrix_0: vec3<f32>;
[[location(10)]] normal_matrix_1: vec3<f32>;
[[location(11)]] normal_matrix_2: vec3<f32>;
};
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>;
@location(0) tex_coords: vec2<f32>;
@location(1) world_normal: vec3<f32>;
@location(2) world_position: vec3<f32>;
[[builtin(position)]] clip_position: vec4<f32>;
[[location(0)]] tex_coords: vec2<f32>;
[[location(1)]] world_normal: vec3<f32>;
[[location(2)]] world_position: vec3<f32>;
};
@stage(vertex)
[[stage(vertex)]]
fn vs_main(
model: VertexInput,
instance: InstanceInput,
@ -63,13 +63,13 @@ fn vs_main(
// Fragment shader
@group(0) @binding(0)
[[group(0), binding(0)]]
var t_diffuse: texture_2d<f32>;
@group(0) @binding(1)
[[group(0), binding(1)]]
var s_diffuse: sampler;
@stage(fragment)
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
[[stage(fragment)]]
fn fs_main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
// We don't need (or want) much ambient light, so 0.1 is fine

@ -16,7 +16,7 @@ env_logger = "0.9"
pollster = "0.2"
log = "0.4"
tobj = { version = "3.2", features = ["async"]}
wgpu = { version = "0.12", git="https://github.com/gfx-rs/wgpu"}
wgpu = { version = "0.12", git="https://github.com/gfx-rs/wgpu", branch="gecko"}
winit = "0.26"
[dependencies.image]
@ -28,7 +28,7 @@ features = ["png", "jpeg"]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "0.2"
wgpu = { version = "0.12", git="https://github.com/gfx-rs/wgpu", features = ["webgl"]}
wgpu = { version = "0.12", git="https://github.com/gfx-rs/wgpu", branch="gecko", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -4,26 +4,26 @@ struct Camera {
view_pos: vec4<f32>;
view_proj: mat4x4<f32>;
};
@group(0) @binding(0)
[[group(0), binding(0)]]
var<uniform> camera: Camera;
struct Light {
position: vec3<f32>;
color: vec3<f32>;
};
@group(1) @binding(0)
[[group(1), binding(0)]]
var<uniform> light: Light;
struct VertexInput {
@location(0) position: vec3<f32>;
[[location(0)]] position: vec3<f32>;
};
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>;
@location(0) color: vec3<f32>;
[[builtin(position)]] clip_position: vec4<f32>;
[[location(0)]] color: vec3<f32>;
};
@stage(vertex)
[[stage(vertex)]]
fn vs_main(
model: VertexInput,
) -> VertexOutput {
@ -36,7 +36,7 @@ fn vs_main(
// Fragment shader
@stage(fragment)
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
[[stage(fragment)]]
fn fs_main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
return vec4<f32>(in.color, 1.0);
}

@ -4,42 +4,42 @@ struct Camera {
view_pos: vec4<f32>;
view_proj: mat4x4<f32>;
};
@group(1) @binding(0)
[[group(1), binding(0)]]
var<uniform> camera: Camera;
struct Light {
position: vec3<f32>;
color: vec3<f32>;
};
@group(2) @binding(0)
[[group(2), binding(0)]]
var<uniform> light: Light;
struct VertexInput {
@location(0) position: vec3<f32>;
@location(1) tex_coords: vec2<f32>;
@location(2) normal: vec3<f32>;
@location(3) tangent: vec3<f32>;
@location(4) bitangent: vec3<f32>;
[[location(0)]] position: vec3<f32>;
[[location(1)]] tex_coords: vec2<f32>;
[[location(2)]] normal: vec3<f32>;
[[location(3)]] tangent: vec3<f32>;
[[location(4)]] bitangent: vec3<f32>;
};
struct InstanceInput {
@location(5) model_matrix_0: vec4<f32>;
@location(6) model_matrix_1: vec4<f32>;
@location(7) model_matrix_2: vec4<f32>;
@location(8) model_matrix_3: vec4<f32>;
@location(9) normal_matrix_0: vec3<f32>;
@location(10) normal_matrix_1: vec3<f32>;
@location(11) normal_matrix_2: vec3<f32>;
[[location(5)]] model_matrix_0: vec4<f32>;
[[location(6)]] model_matrix_1: vec4<f32>;
[[location(7)]] model_matrix_2: vec4<f32>;
[[location(8)]] model_matrix_3: vec4<f32>;
[[location(9)]] normal_matrix_0: vec3<f32>;
[[location(10)]] normal_matrix_1: vec3<f32>;
[[location(11)]] normal_matrix_2: vec3<f32>;
};
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>;
@location(0) tex_coords: vec2<f32>;
@location(1) tangent_position: vec3<f32>;
@location(2) tangent_light_position: vec3<f32>;
@location(3) tangent_view_position: vec3<f32>;
[[builtin(position)]] clip_position: vec4<f32>;
[[location(0)]] tex_coords: vec2<f32>;
[[location(1)]] tangent_position: vec3<f32>;
[[location(2)]] tangent_light_position: vec3<f32>;
[[location(3)]] tangent_view_position: vec3<f32>;
};
@stage(vertex)
[[stage(vertex)]]
fn vs_main(
model: VertexInput,
instance: InstanceInput,
@ -79,17 +79,17 @@ fn vs_main(
// Fragment shader
@group(0) @binding(0)
[[group(0), binding(0)]]
var t_diffuse: texture_2d<f32>;
@group(0) @binding(1)
[[group(0), binding(1)]]
var s_diffuse: sampler;
@group(0) @binding(2)
[[group(0), binding(2)]]
var t_normal: texture_2d<f32>;
@group(0) @binding(3)
[[group(0), binding(3)]]
var s_normal: sampler;
@stage(fragment)
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
[[stage(fragment)]]
fn fs_main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
let object_normal: vec4<f32> = textureSample(t_normal, s_normal, in.tex_coords);

@ -16,8 +16,9 @@ env_logger = "0.9"
pollster = "0.2"
log = "0.4"
tobj = { version = "3.2", features = ["async"]}
wgpu = "0.12"
wgpu = { version = "0.12", git="https://github.com/gfx-rs/wgpu", branch="gecko"}
winit = "0.26"
instant = "0.1"
[dependencies.image]
version = "0.24"
@ -28,13 +29,14 @@ features = ["png", "jpeg"]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "0.2"
wgpu = { version = "0.12", features = ["webgl"]}
wgpu = { version = "0.12", git="https://github.com/gfx-rs/wgpu", branch="gecko", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [
"Document",
"Window",
"Element",
"Location",
]}
[build-dependencies]

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pong with WASM</title>
</head>
<body>
<div id="wasm-example"></div>
<script type="module">
import init from "./pkg/tutorial12_camera.js";
init().then(() => {
console.log("WASM Loaded");
});
</script>
<style>
body {
background-color: #444;
}
canvas {
background-color: black;
}
</style>
</body>
</html>

@ -13,7 +13,8 @@ use wasm_bindgen::prelude::*;
mod camera;
mod model;
mod texture; // NEW!
mod resources;
mod texture;
use model::{DrawLight, DrawModel, Vertex};
@ -370,16 +371,12 @@ impl State {
label: Some("camera_bind_group"),
});
let res_dir = std::path::Path::new(env!("OUT_DIR")).join("res");
let now = std::time::Instant::now();
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();
println!("Elapsed (Original): {:?}", std::time::Instant::now() - now);
).await.unwrap();
let light_uniform = LightUniform {
position: [2.0, 2.0, 2.0],
@ -652,16 +649,46 @@ 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::Info).expect("Could't initialize logger");
} else {
env_logger::init();
}
}
log::info!("asd;lfkaj;lj");
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.");
}
let mut state = State::new(&window).await; // NEW!
let mut last_render_time = std::time::Instant::now();
let mut last_render_time = instant::Instant::now();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
@ -670,6 +697,7 @@ pub async fn run() {
ref event,
.. // We're not using device_id currently
} => {
log::info!("event: {:?}", event);
state.input(event);
}
// UPDATED!
@ -699,7 +727,7 @@ pub async fn run() {
}
// UPDATED!
Event::RedrawRequested(window_id) if window_id == window.id() => {
let now = std::time::Instant::now();
let now = instant::Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);

@ -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,217 @@
use std::io::{BufReader, Cursor};
use cfg_if::cfg_if;
use wgpu::util::DeviceExt;
use crate::{model, texture};
#[cfg(target_arch = "wasm32")]
fn format_url(file_name: &str) -> reqwest::Url {
let window = web_sys::window().unwrap();
let location = window.location();
let base = reqwest::Url::parse(&format!(
"{}/{}/",
location.origin().unwrap(),
option_env!("RES_PATH").unwrap_or("res"),
)).unwrap();
base.join(file_name).unwrap()
}
pub async fn load_string(file_name: &str) -> anyhow::Result<String> {
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
let url = format_url(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_url(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 })
}

@ -8,6 +8,7 @@
<script>
const GITHUB_REPO = "https://github.com/sotrh/learn-wgpu/tree/master/code"
export default {
name: "AutoGithubLink",
computed: {
link() {
return GITHUB_REPO + this.$page.path;

@ -12,15 +12,15 @@
<script>
export default {
name: "RecentArticles",
data() {
return {};
},
computed:{
recentFiles() {
let files = this.$site.pages.filter(p => {
// return p.path.indexOf('/posts/') >= 0;
return p.path.includes('beginner')
|| p.path.includes('intermediate');
return p.regularPath.includes('beginner')
|| p.regularPath.includes('intermediate');
}).sort((a,b) => {
let aDate = new Date(a.frontmatter.published).getTime();
let bDate = new Date(b.frontmatter.published).getTime();

Loading…
Cancel
Save