forked from 1b-agent/fieldcraft-upstream
render: translucent faces in both renderers; datum planes get a light fill (#302)
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
89dfc62c2f
commit
bbf2dba092
4 files changed
+115
-33
No files matched your search
+14
-14
@@ -51,7 +51,7 @@ use std::collections::{BTreeMap, BTreeSet, HashSet};
|
|||||||
use fieldcraft_model::{BoundaryCondition, ContactSurfaces, ElementFace, Load, Model, Procedure, ResolvedSet, Target};
|
use fieldcraft_model::{BoundaryCondition, ContactSurfaces, ElementFace, Load, Model, Procedure, ResolvedSet, Target};
|
||||||
|
|
||||||
use crate::display::{centre, polygon_normal};
|
use crate::display::{centre, polygon_normal};
|
||||||
use crate::{Aabb3, Rgb, Scene, SceneLabel, SceneLine, ScenePoint, Vec3};
|
use crate::{Aabb3, Rgb, Scene, SceneFill, SceneLabel, SceneLine, ScenePoint, SceneTri, Vec3};
|
||||||
|
|
||||||
/// The symbol unit drawn when the caller gives none: a fraction of the model's bounding box
|
/// The symbol unit drawn when the caller gives none: a fraction of the model's bounding box
|
||||||
/// diagonal.
|
/// diagonal.
|
||||||
@@ -894,8 +894,8 @@ impl Glyphs {
|
|||||||
const DATUM_MARGIN: f64 = 0.05;
|
const DATUM_MARGIN: f64 = 0.05;
|
||||||
/// A datum point's cross: each arm's length, as a fraction of the bounding box diagonal.
|
/// A datum point's cross: each arm's length, as a fraction of the bounding box diagonal.
|
||||||
const DATUM_POINT: f64 = 0.02;
|
const DATUM_POINT: f64 = 0.02;
|
||||||
/// Hatch lines across a datum plane's rectangle (its light fill).
|
/// A datum plane's fill: how opaque its translucent rectangle is.
|
||||||
const DATUM_HATCH: usize = 4;
|
pub const DATUM_FILL_ALPHA: f64 = 0.12;
|
||||||
|
|
||||||
/// Corner `k` (0..8, one bit per axis) of box `b`.
|
/// Corner `k` (0..8, one bit per axis) of box `b`.
|
||||||
fn box_corner(b: &Aabb3, k: usize) -> Vec3 {
|
fn box_corner(b: &Aabb3, k: usize) -> Vec3 {
|
||||||
@@ -958,7 +958,7 @@ fn axis_ends(bounds: &Aabb3, p: Vec3, a: Vec3) -> (Vec3, Vec3) {
|
|||||||
(p + a * (t0 - margin), p + a * (t1 + margin))
|
(p + a * (t0 - margin), p + a * (t1 + margin))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the model's datum planes (an outlined, lightly hatched rectangle: the model's extent
|
/// Draw the model's datum planes (an outlined rectangle with a light translucent fill: the model's extent
|
||||||
/// within the plane, `bounds`, plus a margin), axes (a line along the model's extent) and points
|
/// within the plane, `bounds`, plus a margin), axes (a line along the model's extent) and points
|
||||||
/// (a small cross), each with its name; returns the box around them. Only the datums `shown`
|
/// (a small cross), each with its name; returns the box around them. Only the datums `shown`
|
||||||
/// takes are drawn, and `highlight` (the one selected in the browser) whether shown or not, in
|
/// takes are drawn, and `highlight` (the one selected in the browser) whether shown or not, in
|
||||||
@@ -976,7 +976,7 @@ pub fn datums(
|
|||||||
for (name, datum) in model.datums().iter().filter(|(n, _)| shown(n) || highlight == Some(n.as_str())) {
|
for (name, datum) in model.datums().iter().filter(|(n, _)| shown(n) || highlight == Some(n.as_str())) {
|
||||||
let picked = highlight == Some(name.as_str());
|
let picked = highlight == Some(name.as_str());
|
||||||
let color = if picked { colors.highlight } else { colors.datum };
|
let color = if picked { colors.highlight } else { colors.datum };
|
||||||
let (outline, thin) = if picked { (HIGHLIGHT_WIDTH, OUTLINE_WIDTH) } else { (OUTLINE_WIDTH, COUPLING_WIDTH) };
|
let outline = if picked { HIGHLIGHT_WIDTH } else { OUTLINE_WIDTH };
|
||||||
// The one selected in the browser shows through the part.
|
// The one selected in the browser shows through the part.
|
||||||
(d.color, d.on_top) = (color, picked);
|
(d.color, d.on_top) = (color, picked);
|
||||||
match *datum {
|
match *datum {
|
||||||
@@ -991,11 +991,9 @@ pub fn datums(
|
|||||||
let b = ring.get((k + 1) % 4).copied().unwrap_or(*a);
|
let b = ring.get((k + 1) % 4).copied().unwrap_or(*a);
|
||||||
d.line(*a, b);
|
d.line(*a, b);
|
||||||
}
|
}
|
||||||
d.width = thin;
|
let [a, b, c2, e] = ring;
|
||||||
for k in 1..DATUM_HATCH {
|
for p in [[a, b, c2], [a, c2, e]] {
|
||||||
let t = -1.0 + 2.0 * k as f64 / DATUM_HATCH as f64;
|
d.scene.fills.push(SceneFill { tri: SceneTri { p, normal: n, color }, alpha: DATUM_FILL_ALPHA });
|
||||||
d.line(corner(t, -1.0), corner(t, 1.0));
|
|
||||||
d.line(corner(-1.0, t), corner(1.0, t));
|
|
||||||
}
|
}
|
||||||
d.scene.labels.push(SceneLabel { p: corner(1.0, 1.0), text: name.clone(), color });
|
d.scene.labels.push(SceneLabel { p: corner(1.0, 1.0), text: name.clone(), color });
|
||||||
}
|
}
|
||||||
@@ -1712,9 +1710,11 @@ mod tests {
|
|||||||
let bounds = cube(0.0, 10.0);
|
let bounds = cube(0.0, 10.0);
|
||||||
let mut scene = Scene::default();
|
let mut scene = Scene::default();
|
||||||
let drawn = datums(&m, &bounds, &|_| true, None, &COLORS, &mut scene);
|
let drawn = datums(&m, &bounds, &|_| true, None, &COLORS, &mut scene);
|
||||||
// A rectangle with its hatch (4 + 2 × 3 lines), the axis line and the point's three arms,
|
// A rectangle (4 lines, filled by two translucent triangles), the axis line and the
|
||||||
// each crossing at the point.
|
// point's three arms, each crossing at the point.
|
||||||
assert_eq!(scene.lines.len(), 14);
|
assert_eq!(scene.lines.len(), 8);
|
||||||
|
assert_eq!(scene.fills.len(), 2);
|
||||||
|
assert!(scene.fills.iter().all(|f| f.alpha == DATUM_FILL_ALPHA && f.tri.normal == Vec3::X));
|
||||||
let p = Vec3::new(1.0, 2.0, 3.0);
|
let p = Vec3::new(1.0, 2.0, 3.0);
|
||||||
assert!(scene.lines.iter().rev().take(3).all(|l| ((l.a + l.b) * 0.5).dist(p) < 1e-12));
|
assert!(scene.lines.iter().rev().take(3).all(|l| ((l.a + l.b) * 0.5).dist(p) < 1e-12));
|
||||||
let mut names: Vec<&str> = scene.labels.iter().map(|l| l.text.as_str()).collect();
|
let mut names: Vec<&str> = scene.labels.iter().map(|l| l.text.as_str()).collect();
|
||||||
@@ -1734,7 +1734,7 @@ mod tests {
|
|||||||
assert_eq!((only.lines.len(), only.labels.len()), (1, 1));
|
assert_eq!((only.lines.len(), only.labels.len()), (1, 1));
|
||||||
let mut picked = Scene::default();
|
let mut picked = Scene::default();
|
||||||
datums(&m, &bounds, &|_| false, Some("Mid"), &COLORS, &mut picked);
|
datums(&m, &bounds, &|_| false, Some("Mid"), &COLORS, &mut picked);
|
||||||
assert_eq!((picked.lines.len(), picked.on_top.len()), (0, 10));
|
assert_eq!((picked.lines.len(), picked.on_top.len(), picked.fills.len()), (0, 4, 2));
|
||||||
assert!(picked.on_top.iter().all(|l| l.color == COLORS.highlight));
|
assert!(picked.on_top.iter().all(|l| l.color == COLORS.highlight));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ mod vec;
|
|||||||
pub use camera::{Camera, FIT_MARGIN, FULL_VIEW, Mat4, Orientation, StandardView};
|
pub use camera::{Camera, FIT_MARGIN, FULL_VIEW, Mat4, Orientation, StandardView};
|
||||||
pub use display::{DisplayEdge, DisplayLine, DisplayMesh, DisplayNode, DisplayTri, SurfaceCurve, SurfaceTri};
|
pub use display::{DisplayEdge, DisplayLine, DisplayMesh, DisplayNode, DisplayTri, SurfaceCurve, SurfaceTri};
|
||||||
pub use raster::{
|
pub use raster::{
|
||||||
Canvas, LABEL_OFFSET, MAX_DIM, MAX_PIXELS, MAX_PRIMITIVES, RenderError, Rgb, Scene, SceneLabel, SceneLine, ScenePoint, SceneTri, fit_size, png,
|
Canvas, LABEL_OFFSET, MAX_DIM, MAX_PIXELS, MAX_PRIMITIVES, RenderError, Rgb, Scene, SceneFill, SceneLabel, SceneLine, ScenePoint, SceneTri,
|
||||||
render, render_layers, render_layers_png, render_png, shade,
|
fit_size, png, render, render_layers, render_layers_png, render_png, shade,
|
||||||
};
|
};
|
||||||
pub use vec::{Aabb3, Vec3};
|
pub use vec::{Aabb3, Vec3};
|
||||||
@@ -26,6 +26,14 @@ pub struct SceneTri {
|
|||||||
pub color: Rgb,
|
pub color: Rgb,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A translucent triangle (a datum plane's fill): drawn after the opaque faces, depth-tested
|
||||||
|
/// against them without hiding what is behind, blended at `alpha` (0–1).
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct SceneFill {
|
||||||
|
pub tri: SceneTri,
|
||||||
|
pub alpha: f64,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct SceneLine {
|
pub struct SceneLine {
|
||||||
pub a: Vec3,
|
pub a: Vec3,
|
||||||
@@ -55,6 +63,8 @@ pub struct SceneLabel {
|
|||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
pub tris: Vec<SceneTri>,
|
pub tris: Vec<SceneTri>,
|
||||||
|
/// Translucent triangles ([`SceneFill`]).
|
||||||
|
pub fills: Vec<SceneFill>,
|
||||||
pub lines: Vec<SceneLine>,
|
pub lines: Vec<SceneLine>,
|
||||||
pub points: Vec<ScenePoint>,
|
pub points: Vec<ScenePoint>,
|
||||||
pub labels: Vec<SceneLabel>,
|
pub labels: Vec<SceneLabel>,
|
||||||
@@ -71,6 +81,7 @@ impl Scene {
|
|||||||
/// Add `other`'s primitives (not its background or radius) to this scene.
|
/// Add `other`'s primitives (not its background or radius) to this scene.
|
||||||
pub fn append(&mut self, other: Scene) {
|
pub fn append(&mut self, other: Scene) {
|
||||||
self.tris.extend(other.tris);
|
self.tris.extend(other.tris);
|
||||||
|
self.fills.extend(other.fills);
|
||||||
self.lines.extend(other.lines);
|
self.lines.extend(other.lines);
|
||||||
self.points.extend(other.points);
|
self.points.extend(other.points);
|
||||||
self.labels.extend(other.labels);
|
self.labels.extend(other.labels);
|
||||||
@@ -221,7 +232,7 @@ const DEPTH_BIAS: f64 = 2e-4;
|
|||||||
|
|
||||||
/// [`render`] with overlays drawn over the scene (the setup's symbols, a selection highlight):
|
/// [`render`] with overlays drawn over the scene (the setup's symbols, a selection highlight):
|
||||||
/// their faces over the scene's faces they coincide with (a highlight repeats the faces it
|
/// their faces over the scene's faces they coincide with (a highlight repeats the faces it
|
||||||
/// marks), then every layer's lines and points depth-tested, then every layer's
|
/// marks), then every layer's translucent faces blended over them, then every layer's lines and points depth-tested, then every layer's
|
||||||
/// [`Scene::on_top`] lines and labels over everything. The overlays' background and radius are
|
/// [`Scene::on_top`] lines and labels over everything. The overlays' background and radius are
|
||||||
/// not used.
|
/// not used.
|
||||||
pub fn render_layers(scene: &Scene, overlays: &[&Scene], cam: &Camera, w: usize, h: usize) -> Result<Canvas, RenderError> {
|
pub fn render_layers(scene: &Scene, overlays: &[&Scene], cam: &Camera, w: usize, h: usize) -> Result<Canvas, RenderError> {
|
||||||
@@ -229,7 +240,13 @@ pub fn render_layers(scene: &Scene, overlays: &[&Scene], cam: &Camera, w: usize,
|
|||||||
return Err(RenderError::Size { w, h });
|
return Err(RenderError::Size { w, h });
|
||||||
}
|
}
|
||||||
let count = |s: &Scene| {
|
let count = |s: &Scene| {
|
||||||
s.tris.len().saturating_add(s.lines.len()).saturating_add(s.points.len()).saturating_add(s.labels.len()).saturating_add(s.on_top.len())
|
s.tris
|
||||||
|
.len()
|
||||||
|
.saturating_add(s.fills.len())
|
||||||
|
.saturating_add(s.lines.len())
|
||||||
|
.saturating_add(s.points.len())
|
||||||
|
.saturating_add(s.labels.len())
|
||||||
|
.saturating_add(s.on_top.len())
|
||||||
};
|
};
|
||||||
let primitives = overlays.iter().fold(count(scene), |n, o| n.saturating_add(count(o)));
|
let primitives = overlays.iter().fold(count(scene), |n, o| n.saturating_add(count(o)));
|
||||||
if primitives > MAX_PRIMITIVES {
|
if primitives > MAX_PRIMITIVES {
|
||||||
@@ -277,6 +294,18 @@ pub fn render_layers(scene: &Scene, overlays: &[&Scene], cam: &Camera, w: usize,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
let layers = || std::iter::once(scene).chain(overlays.iter().copied());
|
let layers = || std::iter::once(scene).chain(overlays.iter().copied());
|
||||||
|
// Translucent faces over the opaque ones, without writing depth: what is behind shows.
|
||||||
|
for f in layers().flat_map(|s| &s.fills) {
|
||||||
|
let t = &f.tri;
|
||||||
|
let (Some(a), Some(b), Some(cc)) = (project(t.p[0]), project(t.p[1]), project(t.p[2])) else { continue };
|
||||||
|
let alpha = if f.alpha.is_finite() { f.alpha.clamp(0.0, 1.0) } else { 0.0 };
|
||||||
|
let col = shade(t.normal, view_dir, t.color.f());
|
||||||
|
raster_tri(a, b, cc, sw, sh, |x, y, z| {
|
||||||
|
if c.depth.get(y * sw + x).is_some_and(|d| z < *d) {
|
||||||
|
c.put(x, y, col, alpha);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
let line = |c: &mut Canvas, l: &SceneLine, depth_test: bool| {
|
let line = |c: &mut Canvas, l: &SceneLine, depth_test: bool| {
|
||||||
let (Some(a), Some(b)) = (biased(l.a, 1.0), biased(l.b, 1.0)) else { return };
|
let (Some(a), Some(b)) = (biased(l.a, 1.0), biased(l.b, 1.0)) else { return };
|
||||||
let len = ((b[0] - a[0]).powi(2) + (b[1] - a[1]).powi(2)).sqrt();
|
let len = ((b[0] - a[0]).powi(2) + (b[1] - a[1]).powi(2)).sqrt();
|
||||||
@@ -486,6 +515,28 @@ mod tests {
|
|||||||
assert!(p[2] > 200 && p[0] < 60, "{p:?}");
|
assert!(p[2] > 200 && p[0] < 60, "{p:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A translucent face tints what is behind it and hides nothing; one behind an opaque face
|
||||||
|
/// does not show (#302).
|
||||||
|
#[test]
|
||||||
|
fn translucent_faces_blend_over_what_is_in_front_only() {
|
||||||
|
let mut cam = Camera { distance: 50.0, ..Default::default() };
|
||||||
|
cam.set_view(StandardView::Front);
|
||||||
|
let px = |c: &Canvas| c.rgba[(32 * c.w + 32) * 4..(32 * c.w + 32) * 4 + 3].to_vec();
|
||||||
|
let red = red_triangle();
|
||||||
|
let plain = px(&render(&red, &cam, 64, 64).unwrap());
|
||||||
|
let fill = |y: f64| SceneFill {
|
||||||
|
tri: SceneTri { p: red.tris[0].p.map(|p| Vec3::new(p.x, y, p.z)), color: Rgb(0, 0, 255), ..red.tris[0] },
|
||||||
|
alpha: 0.25,
|
||||||
|
};
|
||||||
|
let mut front = red.clone();
|
||||||
|
front.fills.push(fill(-1.0));
|
||||||
|
let tinted = px(&render(&front, &cam, 64, 64).unwrap());
|
||||||
|
assert!(tinted[2] > plain[2] + 20 && tinted[0] > 120, "red still shows, tinted blue: {tinted:?} over {plain:?}");
|
||||||
|
let mut back = red.clone();
|
||||||
|
back.fills.push(fill(1.0));
|
||||||
|
assert_eq!(px(&render(&back, &cam, 64, 64).unwrap()), plain);
|
||||||
|
}
|
||||||
|
|
||||||
/// In perspective, close to the part (the near plane at its smallest), a line a little
|
/// In perspective, close to the part (the near plane at its smallest), a line a little
|
||||||
/// behind a face stays hidden and one on it shows (#282).
|
/// behind a face stays hidden and one on it shows (#282).
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+46
-15
@@ -5,7 +5,8 @@
|
|||||||
//! (which resizes them) never uploads the mesh again. The selection highlight is a third, small
|
//! (which resizes them) never uploads the mesh again. The selection highlight is a third, small
|
||||||
//! scene (the overlay) drawn over the first: its faces pass the depth test over the faces they
|
//! scene (the overlay) drawn over the first: its faces pass the depth test over the faces they
|
||||||
//! repeat, so a hover change uploads only the overlay. Every layer's `on_top` lines are drawn
|
//! repeat, so a hover change uploads only the overlay. Every layer's `on_top` lines are drawn
|
||||||
//! last, with no depth test.
|
//! last, with no depth test. Translucent faces (a datum plane's fill) are drawn after every
|
||||||
|
//! opaque face, blended, depth-tested without writing depth, as the CPU renderer draws them.
|
||||||
//!
|
//!
|
||||||
//! A large mesh does not fit one vertex buffer (wgpu's default `max_buffer_size`, which eframe
|
//! A large mesh does not fit one vertex buffer (wgpu's default `max_buffer_size`, which eframe
|
||||||
//! requests, is 256 MiB): geometry is split into batches of at most [`MAX_BATCH_BYTES`] and the
|
//! requests, is 256 MiB): geometry is split into batches of at most [`MAX_BATCH_BYTES`] and the
|
||||||
@@ -39,6 +40,8 @@ pub type GpuErrors = Arc<Mutex<Option<String>>>;
|
|||||||
#[derive(Default, Clone, Debug, PartialEq)]
|
#[derive(Default, Clone, Debug, PartialEq)]
|
||||||
pub struct GpuScene {
|
pub struct GpuScene {
|
||||||
pub tris: Vec<u8>,
|
pub tris: Vec<u8>,
|
||||||
|
/// Translucent triangles ([`Scene::fills`]), in the triangle format with their alpha.
|
||||||
|
pub fills: Vec<u8>,
|
||||||
pub lines: Vec<u8>,
|
pub lines: Vec<u8>,
|
||||||
/// Lines drawn over everything ([`Scene::on_top`]).
|
/// Lines drawn over everything ([`Scene::on_top`]).
|
||||||
pub top_lines: Vec<u8>,
|
pub top_lines: Vec<u8>,
|
||||||
@@ -48,10 +51,16 @@ impl GpuScene {
|
|||||||
/// The scene's triangles, lines and points; widths and sizes scaled by `pixels_per_point`
|
/// The scene's triangles, lines and points; widths and sizes scaled by `pixels_per_point`
|
||||||
/// (the scene gives them in points).
|
/// (the scene gives them in points).
|
||||||
pub fn from_scene(scene: &Scene, pixels_per_point: f32) -> GpuScene {
|
pub fn from_scene(scene: &Scene, pixels_per_point: f32) -> GpuScene {
|
||||||
let mut g = GpuScene { tris: Vec::with_capacity(scene.tris.len() * 3 * TRI_SIZE), lines: Vec::new(), top_lines: Vec::new() };
|
let mut g = GpuScene { tris: Vec::with_capacity(scene.tris.len() * 3 * TRI_SIZE), ..GpuScene::default() };
|
||||||
for t in &scene.tris {
|
for t in &scene.tris {
|
||||||
for p in t.p {
|
for p in t.p {
|
||||||
g.vertex(p.to_f32(), t.normal.to_f32(), t.color);
|
push_vertex(&mut g.tris, p.to_f32(), t.normal.to_f32(), t.color, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for f in &scene.fills {
|
||||||
|
let alpha = if f.alpha.is_finite() { (f.alpha.clamp(0.0, 1.0) * 255.0).round() as u8 } else { 0 };
|
||||||
|
for p in f.tri.p {
|
||||||
|
push_vertex(&mut g.fills, p.to_f32(), f.tri.normal.to_f32(), f.tri.color, alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for l in &scene.lines {
|
for l in &scene.lines {
|
||||||
@@ -65,17 +74,19 @@ impl GpuScene {
|
|||||||
}
|
}
|
||||||
g
|
g
|
||||||
}
|
}
|
||||||
fn vertex(&mut self, p: [f32; 3], n: [f32; 3], c: Rgb) {
|
|
||||||
for v in p.iter().chain(&n) {
|
|
||||||
self.tris.extend_from_slice(&v.to_le_bytes());
|
|
||||||
}
|
|
||||||
self.tris.extend_from_slice(&[c.0, c.1, c.2, 255]);
|
|
||||||
}
|
|
||||||
fn line(&mut self, a: [f32; 3], b: [f32; 3], c: Rgb, width: f32) {
|
fn line(&mut self, a: [f32; 3], b: [f32; 3], c: Rgb, width: f32) {
|
||||||
push_line(&mut self.lines, a, b, c, width);
|
push_line(&mut self.lines, a, b, c, width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A triangle vertex into `buf`.
|
||||||
|
fn push_vertex(buf: &mut Vec<u8>, p: [f32; 3], n: [f32; 3], c: Rgb, alpha: u8) {
|
||||||
|
for v in p.iter().chain(&n) {
|
||||||
|
buf.extend_from_slice(&v.to_le_bytes());
|
||||||
|
}
|
||||||
|
buf.extend_from_slice(&[c.0, c.1, c.2, alpha]);
|
||||||
|
}
|
||||||
|
|
||||||
/// A line instance into `buf`.
|
/// A line instance into `buf`.
|
||||||
fn push_line(buf: &mut Vec<u8>, a: [f32; 3], b: [f32; 3], c: Rgb, width: f32) {
|
fn push_line(buf: &mut Vec<u8>, a: [f32; 3], b: [f32; 3], c: Rgb, width: f32) {
|
||||||
for v in a.iter().chain(&b) {
|
for v in a.iter().chain(&b) {
|
||||||
@@ -119,6 +130,7 @@ struct Batch {
|
|||||||
struct Layer {
|
struct Layer {
|
||||||
key: Option<u64>,
|
key: Option<u64>,
|
||||||
tris: Vec<Batch>,
|
tris: Vec<Batch>,
|
||||||
|
fills: Vec<Batch>,
|
||||||
lines: Vec<Batch>,
|
lines: Vec<Batch>,
|
||||||
top_lines: Vec<Batch>,
|
top_lines: Vec<Batch>,
|
||||||
}
|
}
|
||||||
@@ -131,10 +143,12 @@ impl Layer {
|
|||||||
if let Some(sc) = slot.lock().ok().and_then(|mut s| s.take()) {
|
if let Some(sc) = slot.lock().ok().and_then(|mut s| s.take()) {
|
||||||
// The old buffers go first, so the new ones don't have to fit beside them.
|
// The old buffers go first, so the new ones don't have to fit beside them.
|
||||||
self.tris.clear();
|
self.tris.clear();
|
||||||
|
self.fills.clear();
|
||||||
self.lines.clear();
|
self.lines.clear();
|
||||||
self.top_lines.clear();
|
self.top_lines.clear();
|
||||||
let max = device.limits().max_buffer_size;
|
let max = device.limits().max_buffer_size;
|
||||||
self.tris = upload(device, queue, "fc_tris", &sc.tris, TRI_SIZE, 3, max);
|
self.tris = upload(device, queue, "fc_tris", &sc.tris, TRI_SIZE, 3, max);
|
||||||
|
self.fills = upload(device, queue, "fc_fills", &sc.fills, TRI_SIZE, 3, max);
|
||||||
self.lines = upload(device, queue, "fc_lines", &sc.lines, LINE_SIZE, 1, max);
|
self.lines = upload(device, queue, "fc_lines", &sc.lines, LINE_SIZE, 1, max);
|
||||||
self.top_lines = upload(device, queue, "fc_top_lines", &sc.top_lines, LINE_SIZE, 1, max);
|
self.top_lines = upload(device, queue, "fc_top_lines", &sc.top_lines, LINE_SIZE, 1, max);
|
||||||
self.key = Some(key);
|
self.key = Some(key);
|
||||||
@@ -142,12 +156,16 @@ impl Layer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn paint_tris(&self, pass: &mut wgpu::RenderPass<'static>, tri: &wgpu::RenderPipeline) {
|
fn paint_tris(&self, pass: &mut wgpu::RenderPass<'static>, tri: &wgpu::RenderPipeline) {
|
||||||
if !self.tris.is_empty() {
|
paint_tri_batches(&self.tris, pass, tri);
|
||||||
pass.set_pipeline(tri);
|
}
|
||||||
for b in &self.tris {
|
}
|
||||||
pass.set_vertex_buffer(0, b.buffer.slice(..));
|
|
||||||
pass.draw(0..b.count, 0..1);
|
fn paint_tri_batches(batches: &[Batch], pass: &mut wgpu::RenderPass<'static>, tri: &wgpu::RenderPipeline) {
|
||||||
}
|
if !batches.is_empty() {
|
||||||
|
pass.set_pipeline(tri);
|
||||||
|
for b in batches {
|
||||||
|
pass.set_vertex_buffer(0, b.buffer.slice(..));
|
||||||
|
pass.draw(0..b.count, 0..1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,6 +184,8 @@ struct Resources {
|
|||||||
tri: wgpu::RenderPipeline,
|
tri: wgpu::RenderPipeline,
|
||||||
/// The overlay's faces: pulled toward the eye and depth-tested without writing depth.
|
/// The overlay's faces: pulled toward the eye and depth-tested without writing depth.
|
||||||
overlay_tri: wgpu::RenderPipeline,
|
overlay_tri: wgpu::RenderPipeline,
|
||||||
|
/// Translucent faces: blended, depth-tested without writing depth.
|
||||||
|
fill: wgpu::RenderPipeline,
|
||||||
line: wgpu::RenderPipeline,
|
line: wgpu::RenderPipeline,
|
||||||
/// Lines over everything: no depth test.
|
/// Lines over everything: no depth test.
|
||||||
top_line: wgpu::RenderPipeline,
|
top_line: wgpu::RenderPipeline,
|
||||||
@@ -380,6 +400,14 @@ impl Resources {
|
|||||||
Resources {
|
Resources {
|
||||||
tri: pipeline("fc_tris", "vs_tri", "fs_tri", tri_layout(), depth(wgpu::CompareFunction::Less, true), None),
|
tri: pipeline("fc_tris", "vs_tri", "fs_tri", tri_layout(), depth(wgpu::CompareFunction::Less, true), None),
|
||||||
overlay_tri: pipeline("fc_overlay_tris", "vs_overlay_tri", "fs_tri", tri_layout(), depth(wgpu::CompareFunction::LessEqual, false), None),
|
overlay_tri: pipeline("fc_overlay_tris", "vs_overlay_tri", "fs_tri", tri_layout(), depth(wgpu::CompareFunction::LessEqual, false), None),
|
||||||
|
fill: pipeline(
|
||||||
|
"fc_fills",
|
||||||
|
"vs_tri",
|
||||||
|
"fs_tri",
|
||||||
|
tri_layout(),
|
||||||
|
depth(wgpu::CompareFunction::Less, false),
|
||||||
|
Some(wgpu::BlendState::ALPHA_BLENDING),
|
||||||
|
),
|
||||||
line: pipeline(
|
line: pipeline(
|
||||||
"fc_lines",
|
"fc_lines",
|
||||||
"vs_line",
|
"vs_line",
|
||||||
@@ -489,6 +517,9 @@ impl egui_wgpu::CallbackTrait for ViewportCallback {
|
|||||||
res.scene.paint_tris(pass, &res.tri);
|
res.scene.paint_tris(pass, &res.tri);
|
||||||
res.symbols.paint_tris(pass, &res.tri);
|
res.symbols.paint_tris(pass, &res.tri);
|
||||||
res.overlay.paint_tris(pass, &res.overlay_tri);
|
res.overlay.paint_tris(pass, &res.overlay_tri);
|
||||||
|
for layer in [&res.scene, &res.symbols, &res.overlay] {
|
||||||
|
paint_tri_batches(&layer.fills, pass, &res.fill);
|
||||||
|
}
|
||||||
for layer in [&res.scene, &res.symbols, &res.overlay] {
|
for layer in [&res.scene, &res.symbols, &res.overlay] {
|
||||||
paint_line_batches(&layer.lines, pass, &res.line);
|
paint_line_batches(&layer.lines, pass, &res.line);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user