feat(render): double-single vertices for the mesh pipeline
3D solid / surface meshes were the last single-f32 path. truck's mesh tessellation now returns a paired verts_low residual (TruckTessResult::Mesh, polygon_to_result), MeshModel/MeshLodSet carry it, the model-space offset and block-instance transforms reconstruct the f64 position and re-split it after subtracting world_offset, MeshVertex gains position_low (location 3), and mesh.wgsl reconstructs the eye-relative position. Native SAT / ACIS / OBJ mesh paths default to a zero residual (no regression — they were never sub-f32 precise). With this every world-space pipeline (wire, hatch, image, face3d, mesh) is full double-single relative-to-eye; large-coordinate jitter is gone. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
c0f3d0de5e
commit
ddf6344dbe
10 changed files with 112 additions and 46 deletions
|
|
@ -1274,11 +1274,13 @@ impl OpenCADStudio {
|
||||||
match truck_tess::tessellate_solid(&solid, woff) {
|
match truck_tess::tessellate_solid(&solid, woff) {
|
||||||
truck_tess::TruckTessResult::Mesh {
|
truck_tess::TruckTessResult::Mesh {
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
} => Some(crate::scene::model::mesh_model::MeshModel {
|
} => Some(crate::scene::model::mesh_model::MeshModel {
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
color,
|
color,
|
||||||
|
|
@ -1352,11 +1354,13 @@ impl OpenCADStudio {
|
||||||
match truck_tess::tessellate_shell(&shell, woff) {
|
match truck_tess::tessellate_shell(&shell, woff) {
|
||||||
truck_tess::TruckTessResult::Mesh {
|
truck_tess::TruckTessResult::Mesh {
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
} => Some(crate::scene::model::mesh_model::MeshModel {
|
} => Some(crate::scene::model::mesh_model::MeshModel {
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
color,
|
color,
|
||||||
|
|
@ -1437,11 +1441,13 @@ impl OpenCADStudio {
|
||||||
match truck_tess::tessellate_solid(&solid, woff) {
|
match truck_tess::tessellate_solid(&solid, woff) {
|
||||||
truck_tess::TruckTessResult::Mesh {
|
truck_tess::TruckTessResult::Mesh {
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
} => Some(crate::scene::model::mesh_model::MeshModel {
|
} => Some(crate::scene::model::mesh_model::MeshModel {
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
color,
|
color,
|
||||||
|
|
@ -1454,11 +1460,13 @@ impl OpenCADStudio {
|
||||||
match truck_tess::tessellate_shell(&shell, woff) {
|
match truck_tess::tessellate_shell(&shell, woff) {
|
||||||
truck_tess::TruckTessResult::Mesh {
|
truck_tess::TruckTessResult::Mesh {
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
} => Some(crate::scene::model::mesh_model::MeshModel {
|
} => Some(crate::scene::model::mesh_model::MeshModel {
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
color,
|
color,
|
||||||
|
|
@ -1486,11 +1494,13 @@ impl OpenCADStudio {
|
||||||
match truck_tess::tessellate_solid(&solid, woff) {
|
match truck_tess::tessellate_solid(&solid, woff) {
|
||||||
truck_tess::TruckTessResult::Mesh {
|
truck_tess::TruckTessResult::Mesh {
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
} => Some(crate::scene::model::mesh_model::MeshModel {
|
} => Some(crate::scene::model::mesh_model::MeshModel {
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
color,
|
color,
|
||||||
|
|
@ -1503,11 +1513,13 @@ impl OpenCADStudio {
|
||||||
match truck_tess::tessellate_shell(&shell, woff) {
|
match truck_tess::tessellate_shell(&shell, woff) {
|
||||||
truck_tess::TruckTessResult::Mesh {
|
truck_tess::TruckTessResult::Mesh {
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
} => Some(crate::scene::model::mesh_model::MeshModel {
|
} => Some(crate::scene::model::mesh_model::MeshModel {
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
color,
|
color,
|
||||||
|
|
@ -1593,11 +1605,13 @@ impl OpenCADStudio {
|
||||||
match truck_tess::tessellate_shell(&shell, woff) {
|
match truck_tess::tessellate_shell(&shell, woff) {
|
||||||
truck_tess::TruckTessResult::Mesh {
|
truck_tess::TruckTessResult::Mesh {
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
} => Some(crate::scene::model::mesh_model::MeshModel {
|
} => Some(crate::scene::model::mesh_model::MeshModel {
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
color,
|
color,
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ pub fn parse_obj(src: &str, color: [f32; 4]) -> Option<MeshModel> {
|
||||||
Some(MeshModel {
|
Some(MeshModel {
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
verts,
|
verts,
|
||||||
|
verts_low: Vec::new(),
|
||||||
normals: norms,
|
normals: norms,
|
||||||
indices,
|
indices,
|
||||||
color,
|
color,
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,7 @@ pub fn tessellate_sat_truck(
|
||||||
let mut mesh = MeshModel {
|
let mut mesh = MeshModel {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
verts: Vec::new(),
|
verts: Vec::new(),
|
||||||
|
verts_low: Vec::new(),
|
||||||
normals: Vec::new(),
|
normals: Vec::new(),
|
||||||
indices: Vec::new(),
|
indices: Vec::new(),
|
||||||
color,
|
color,
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,7 @@ fn tessellate_sat(
|
||||||
Some(MeshModel {
|
Some(MeshModel {
|
||||||
name,
|
name,
|
||||||
verts,
|
verts,
|
||||||
|
verts_low: Vec::new(),
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
color,
|
color,
|
||||||
|
|
|
||||||
|
|
@ -78,9 +78,11 @@ pub enum TruckTessResult {
|
||||||
/// returned as parallel double-single (high, low) f32 buffers so the GPU
|
/// returned as parallel double-single (high, low) f32 buffers so the GPU
|
||||||
/// keeps sub-unit precision at UTM-scale coordinates.
|
/// keeps sub-unit precision at UTM-scale coordinates.
|
||||||
Lines(Vec<[f32; 3]>, Vec<[f32; 3]>),
|
Lines(Vec<[f32; 3]>, Vec<[f32; 3]>),
|
||||||
/// A triangle mesh (from Face, Shell, or Solid).
|
/// A triangle mesh (from Face, Shell, or Solid). `verts` is the high half
|
||||||
|
/// of the double-single position; `verts_low` the paired residual.
|
||||||
Mesh {
|
Mesh {
|
||||||
verts: Vec<[f32; 3]>,
|
verts: Vec<[f32; 3]>,
|
||||||
|
verts_low: Vec<[f32; 3]>,
|
||||||
normals: Vec<[f32; 3]>,
|
normals: Vec<[f32; 3]>,
|
||||||
indices: Vec<u32>,
|
indices: Vec<u32>,
|
||||||
},
|
},
|
||||||
|
|
@ -177,6 +179,7 @@ pub fn tessellate_shell(s: &Shell, offset: [f64; 3]) -> TruckTessResult {
|
||||||
pub fn tessellate_shell(_s: &Shell, _offset: [f64; 3]) -> TruckTessResult {
|
pub fn tessellate_shell(_s: &Shell, _offset: [f64; 3]) -> TruckTessResult {
|
||||||
TruckTessResult::Mesh {
|
TruckTessResult::Mesh {
|
||||||
verts: Vec::new(),
|
verts: Vec::new(),
|
||||||
|
verts_low: Vec::new(),
|
||||||
normals: Vec::new(),
|
normals: Vec::new(),
|
||||||
indices: Vec::new(),
|
indices: Vec::new(),
|
||||||
}
|
}
|
||||||
|
|
@ -197,6 +200,7 @@ pub fn tessellate_solid(s: &Solid, offset: [f64; 3]) -> TruckTessResult {
|
||||||
pub fn tessellate_solid(_s: &Solid, _offset: [f64; 3]) -> TruckTessResult {
|
pub fn tessellate_solid(_s: &Solid, _offset: [f64; 3]) -> TruckTessResult {
|
||||||
TruckTessResult::Mesh {
|
TruckTessResult::Mesh {
|
||||||
verts: Vec::new(),
|
verts: Vec::new(),
|
||||||
|
verts_low: Vec::new(),
|
||||||
normals: Vec::new(),
|
normals: Vec::new(),
|
||||||
indices: Vec::new(),
|
indices: Vec::new(),
|
||||||
}
|
}
|
||||||
|
|
@ -206,11 +210,14 @@ pub fn tessellate_solid(_s: &Solid, _offset: [f64; 3]) -> TruckTessResult {
|
||||||
|
|
||||||
#[cfg(feature = "solid3d")]
|
#[cfg(feature = "solid3d")]
|
||||||
fn polygon_to_result(mesh: PolygonMesh, offset: [f64; 3]) -> TruckTessResult {
|
fn polygon_to_result(mesh: PolygonMesh, offset: [f64; 3]) -> TruckTessResult {
|
||||||
let verts: Vec<[f32; 3]> = mesh
|
let positions = mesh.positions();
|
||||||
.positions()
|
let mut verts: Vec<[f32; 3]> = Vec::with_capacity(positions.len());
|
||||||
.iter()
|
let mut verts_low: Vec<[f32; 3]> = Vec::with_capacity(positions.len());
|
||||||
.map(|p| to_local(p.x, p.y, p.z, offset))
|
for p in positions.iter() {
|
||||||
.collect();
|
let hi = to_local(p.x, p.y, p.z, offset);
|
||||||
|
verts_low.push(to_local_low(p.x, p.y, p.z, offset, hi));
|
||||||
|
verts.push(hi);
|
||||||
|
}
|
||||||
|
|
||||||
// Per-vertex normals: if the mesh has normals, map each triangle vertex's
|
// Per-vertex normals: if the mesh has normals, map each triangle vertex's
|
||||||
// normal index back to the normals array. Fall back to empty if absent.
|
// normal index back to the normals array. Fall back to empty if absent.
|
||||||
|
|
@ -262,6 +269,7 @@ fn polygon_to_result(mesh: PolygonMesh, offset: [f64; 3]) -> TruckTessResult {
|
||||||
|
|
||||||
TruckTessResult::Mesh {
|
TruckTessResult::Mesh {
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -557,20 +557,31 @@ fn overlap_len(a: (f32, f32), b: (f32, f32)) -> f32 {
|
||||||
/// uses the same space.
|
/// uses the same space.
|
||||||
fn offset_mesh_lod_set(mut set: MeshLodSet, world_offset: [f64; 3]) -> MeshLodSet {
|
fn offset_mesh_lod_set(mut set: MeshLodSet, world_offset: [f64; 3]) -> MeshLodSet {
|
||||||
let [ox, oy, oz] = world_offset;
|
let [ox, oy, oz] = world_offset;
|
||||||
let (fx, fy, fz) = (ox as f32, oy as f32, oz as f32);
|
|
||||||
let mut min_x = f32::INFINITY;
|
let mut min_x = f32::INFINITY;
|
||||||
let mut min_y = f32::INFINITY;
|
let mut min_y = f32::INFINITY;
|
||||||
let mut max_x = f32::NEG_INFINITY;
|
let mut max_x = f32::NEG_INFINITY;
|
||||||
let mut max_y = f32::NEG_INFINITY;
|
let mut max_y = f32::NEG_INFINITY;
|
||||||
for lod in &mut set.lods {
|
for lod in &mut set.lods {
|
||||||
for v in &mut lod.verts {
|
// Reconstruct the f64 absolute position from the double-single pair,
|
||||||
v[0] -= fx;
|
// subtract world_offset in f64, then re-split into (high, low) so the
|
||||||
v[1] -= fy;
|
// relative-to-eye shader keeps sub-unit precision at UTM scale.
|
||||||
v[2] -= fz;
|
let has_low = lod.verts_low.len() == lod.verts.len();
|
||||||
if v[0] < min_x { min_x = v[0]; }
|
if !has_low {
|
||||||
if v[1] < min_y { min_y = v[1]; }
|
lod.verts_low = vec![[0.0; 3]; lod.verts.len()];
|
||||||
if v[0] > max_x { max_x = v[0]; }
|
}
|
||||||
if v[1] > max_y { max_y = v[1]; }
|
for (v, vl) in lod.verts.iter_mut().zip(lod.verts_low.iter_mut()) {
|
||||||
|
let ax = v[0] as f64 + vl[0] as f64 - ox;
|
||||||
|
let ay = v[1] as f64 + vl[1] as f64 - oy;
|
||||||
|
let az = v[2] as f64 + vl[2] as f64 - oz;
|
||||||
|
let hx = ax as f32;
|
||||||
|
let hy = ay as f32;
|
||||||
|
let hz = az as f32;
|
||||||
|
*v = [hx, hy, hz];
|
||||||
|
*vl = [(ax - hx as f64) as f32, (ay - hy as f64) as f32, (az - hz as f64) as f32];
|
||||||
|
if hx < min_x { min_x = hx; }
|
||||||
|
if hy < min_y { min_y = hy; }
|
||||||
|
if hx > max_x { max_x = hx; }
|
||||||
|
if hy > max_y { max_y = hy; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if min_x.is_finite() {
|
if min_x.is_finite() {
|
||||||
|
|
@ -596,15 +607,30 @@ fn transform_block_mesh_lod_set(
|
||||||
let mut max_x = f32::NEG_INFINITY;
|
let mut max_x = f32::NEG_INFINITY;
|
||||||
let mut max_y = f32::NEG_INFINITY;
|
let mut max_y = f32::NEG_INFINITY;
|
||||||
for lod in &mut out.lods {
|
for lod in &mut out.lods {
|
||||||
for v in &mut lod.verts {
|
let has_low = lod.verts_low.len() == lod.verts.len();
|
||||||
let w = xform.apply(Vector3::new(v[0] as f64, v[1] as f64, v[2] as f64));
|
if !has_low {
|
||||||
v[0] = (w.x - ox) as f32;
|
lod.verts_low = vec![[0.0; 3]; lod.verts.len()];
|
||||||
v[1] = (w.y - oy) as f32;
|
}
|
||||||
v[2] = (w.z - oz) as f32;
|
for (v, vl) in lod.verts.iter_mut().zip(lod.verts_low.iter_mut()) {
|
||||||
if v[0] < min_x { min_x = v[0]; }
|
// Reconstruct the block-local f64, apply the INSERT transform and
|
||||||
if v[1] < min_y { min_y = v[1]; }
|
// subtract world_offset in f64, then re-split into (high, low).
|
||||||
if v[0] > max_x { max_x = v[0]; }
|
let w = xform.apply(Vector3::new(
|
||||||
if v[1] > max_y { max_y = v[1]; }
|
v[0] as f64 + vl[0] as f64,
|
||||||
|
v[1] as f64 + vl[1] as f64,
|
||||||
|
v[2] as f64 + vl[2] as f64,
|
||||||
|
));
|
||||||
|
let ax = w.x - ox;
|
||||||
|
let ay = w.y - oy;
|
||||||
|
let az = w.z - oz;
|
||||||
|
let hx = ax as f32;
|
||||||
|
let hy = ay as f32;
|
||||||
|
let hz = az as f32;
|
||||||
|
*v = [hx, hy, hz];
|
||||||
|
*vl = [(ax - hx as f64) as f32, (ay - hy as f64) as f32, (az - hz as f64) as f32];
|
||||||
|
if hx < min_x { min_x = hx; }
|
||||||
|
if hy < min_y { min_y = hy; }
|
||||||
|
if hx > max_x { max_x = hx; }
|
||||||
|
if hy > max_y { max_y = hy; }
|
||||||
}
|
}
|
||||||
for n in &mut lod.normals {
|
for n in &mut lod.normals {
|
||||||
let d = xform.apply_rotation(Vector3::new(n[0] as f64, n[1] as f64, n[2] as f64));
|
let d = xform.apply_rotation(Vector3::new(n[0] as f64, n[1] as f64, n[2] as f64));
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,11 @@
|
||||||
pub struct MeshModel {
|
pub struct MeshModel {
|
||||||
/// Unique identifier (entity handle value as decimal string).
|
/// Unique identifier (entity handle value as decimal string).
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// World-space vertex positions.
|
/// World-space vertex positions (high half of the double-single pair).
|
||||||
pub verts: Vec<[f32; 3]>,
|
pub verts: Vec<[f32; 3]>,
|
||||||
|
/// Low residual paired with `verts` so meshes stay precise at UTM scale.
|
||||||
|
/// Empty = all-zero (legacy / interactive meshes near the origin).
|
||||||
|
pub verts_low: Vec<[f32; 3]>,
|
||||||
/// Per-vertex normals (may be empty if not available).
|
/// Per-vertex normals (may be empty if not available).
|
||||||
pub normals: Vec<[f32; 3]>,
|
pub normals: Vec<[f32; 3]>,
|
||||||
/// Triangle indices into `verts` (every 3 values = one triangle).
|
/// Triangle indices into `verts` (every 3 values = one triangle).
|
||||||
|
|
|
||||||
|
|
@ -172,12 +172,14 @@ pub fn mesh_from_solid(solid: &Solid, color: [f32; 4]) -> Option<MeshLodSet> {
|
||||||
match tessellate_solid(solid, [0.0; 3]) {
|
match tessellate_solid(solid, [0.0; 3]) {
|
||||||
TruckTessResult::Mesh {
|
TruckTessResult::Mesh {
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
} if !indices.is_empty() => {
|
} if !indices.is_empty() => {
|
||||||
let mesh = MeshModel {
|
let mesh = MeshModel {
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
verts,
|
verts,
|
||||||
|
verts_low,
|
||||||
normals,
|
normals,
|
||||||
indices,
|
indices,
|
||||||
color,
|
color,
|
||||||
|
|
|
||||||
|
|
@ -19,30 +19,37 @@ pub struct MeshVertex {
|
||||||
pub position: [f32; 3],
|
pub position: [f32; 3],
|
||||||
pub normal: [f32; 3],
|
pub normal: [f32; 3],
|
||||||
pub color: [f32; 4],
|
pub color: [f32; 4],
|
||||||
|
pub position_low: [f32; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MeshVertex {
|
impl MeshVertex {
|
||||||
pub fn layout<'a>() -> wgpu::VertexBufferLayout<'a> {
|
pub fn layout<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||||
|
const ATTRS: &[wgpu::VertexAttribute] = &[
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: std::mem::offset_of!(MeshVertex, position) as u64,
|
||||||
|
shader_location: 0,
|
||||||
|
format: wgpu::VertexFormat::Float32x3,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: std::mem::offset_of!(MeshVertex, normal) as u64,
|
||||||
|
shader_location: 1,
|
||||||
|
format: wgpu::VertexFormat::Float32x3,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: std::mem::offset_of!(MeshVertex, color) as u64,
|
||||||
|
shader_location: 2,
|
||||||
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: std::mem::offset_of!(MeshVertex, position_low) as u64,
|
||||||
|
shader_location: 3,
|
||||||
|
format: wgpu::VertexFormat::Float32x3,
|
||||||
|
},
|
||||||
|
];
|
||||||
wgpu::VertexBufferLayout {
|
wgpu::VertexBufferLayout {
|
||||||
array_stride: std::mem::size_of::<MeshVertex>() as u64,
|
array_stride: std::mem::size_of::<MeshVertex>() as u64,
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
attributes: &[
|
attributes: ATTRS,
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: 0,
|
|
||||||
shader_location: 0,
|
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: 12,
|
|
||||||
shader_location: 1,
|
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: 24,
|
|
||||||
shader_location: 2,
|
|
||||||
format: wgpu::VertexFormat::Float32x4,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -133,6 +140,7 @@ impl MeshGpu {
|
||||||
position: pos,
|
position: pos,
|
||||||
normal,
|
normal,
|
||||||
color,
|
color,
|
||||||
|
position_low: mesh.verts_low.get(i).copied().unwrap_or([0.0; 3]),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,10 @@ struct Uniforms {
|
||||||
var<uniform> u: Uniforms;
|
var<uniform> u: Uniforms;
|
||||||
|
|
||||||
struct VertexIn {
|
struct VertexIn {
|
||||||
@location(0) position: vec3<f32>,
|
@location(0) position: vec3<f32>,
|
||||||
@location(1) normal: vec3<f32>,
|
@location(1) normal: vec3<f32>,
|
||||||
@location(2) color: vec4<f32>,
|
@location(2) color: vec4<f32>,
|
||||||
|
@location(3) position_low: vec3<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexOut {
|
struct VertexOut {
|
||||||
|
|
@ -45,7 +46,8 @@ struct VertexOut {
|
||||||
@vertex
|
@vertex
|
||||||
fn vs_main(v: VertexIn) -> VertexOut {
|
fn vs_main(v: VertexIn) -> VertexOut {
|
||||||
var out: VertexOut;
|
var out: VertexOut;
|
||||||
out.clip_pos = u.view_rot * vec4<f32>((v.position - u.eye_high) - u.eye_low, 1.0);
|
let rel = (v.position - u.eye_high) + (v.position_low - u.eye_low);
|
||||||
|
out.clip_pos = u.view_rot * vec4<f32>(rel, 1.0);
|
||||||
out.color = v.color;
|
out.color = v.color;
|
||||||
out.normal = v.normal;
|
out.normal = v.normal;
|
||||||
out.world_pos = v.position;
|
out.world_pos = v.position;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue