Headless pictures draw on the GPU, with the viewport's pipelines (CPU rasterizer as last tier) (#1555)
Thanks @jyviko.
This commit is contained in:
1 parent
48322f6464
commit
1a0b33fcf3
54 files changed
+3525
-1998
No files matched your search
@@ -0,0 +1,54 @@
|
||||
//! Surface analysis as a view draws it: the shading mode and the colours it uses.
|
||||
|
||||
use solidcraft_geom::Vec3;
|
||||
|
||||
/// Surface analysis colours, straight sRGB 0..1 as the shader takes them.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct AnalysisColors {
|
||||
/// Draft Analysis: at least the draft angle, between, at most minus the angle.
|
||||
pub draft: [[f32; 3]; 3],
|
||||
/// Accessibility Analysis: reachable, blocked.
|
||||
pub access: [[f32; 3]; 2],
|
||||
/// Curvature Map: flat, the middle of the scale, at or below the analysis radius.
|
||||
pub curvature: [[f32; 3]; 3],
|
||||
/// Stripe the negative draft and blocked faces too, so colour is not the only cue.
|
||||
pub hatch: bool,
|
||||
}
|
||||
|
||||
impl AnalysisColors {
|
||||
/// The light theme's colours (no colour vision mode): what headless pictures use.
|
||||
pub const DEFAULT: AnalysisColors = AnalysisColors {
|
||||
draft: [[0.20, 0.75, 0.30], [0.95, 0.80, 0.15], [0.85, 0.22, 0.20]],
|
||||
access: [[0.20, 0.75, 0.30], [0.85, 0.22, 0.20]],
|
||||
curvature: [[0.15, 0.35, 0.95], [0.10, 0.80, 0.25], [0.90, 0.15, 0.10]],
|
||||
hatch: false,
|
||||
};
|
||||
}
|
||||
|
||||
impl Default for AnalysisColors {
|
||||
fn default() -> Self {
|
||||
AnalysisColors::DEFAULT
|
||||
}
|
||||
}
|
||||
|
||||
/// A way of shading the model's faces to judge their shape.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum AnalysisMode {
|
||||
/// Reflected stripes: `stripes` across the view.
|
||||
Zebra { stripes: f64 },
|
||||
/// Faces by draft angle (degrees) to `pull`.
|
||||
Draft { pull: Vec3, angle: f64 },
|
||||
/// Colour by curvature, reaching the tight end at 1 / `radius`.
|
||||
Curvature { radius: f64 },
|
||||
/// Mirror finish reflecting a studio.
|
||||
Environment,
|
||||
/// Reachable along `dir` (facing it, nothing above) or blocked.
|
||||
Access { dir: Vec3 },
|
||||
}
|
||||
|
||||
/// A surface analysis shading a picture: the mode and its colours.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct SceneAnalysis {
|
||||
pub mode: AnalysisMode,
|
||||
pub colors: AnalysisColors,
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
mod analysis;
|
||||
mod backend;
|
||||
mod camera;
|
||||
mod curve_display;
|
||||
@@ -11,6 +12,7 @@ pub mod job;
|
||||
pub mod lighting;
|
||||
mod product;
|
||||
mod raster;
|
||||
mod scene;
|
||||
pub mod studio;
|
||||
pub mod surface;
|
||||
pub mod texture_input;
|
||||
@@ -18,6 +20,7 @@ pub mod texture_interpolation;
|
||||
pub mod texture_lod;
|
||||
pub mod texture_sampling;
|
||||
|
||||
pub use analysis::{AnalysisColors, AnalysisMode, SceneAnalysis};
|
||||
pub use backend::{
|
||||
Availability, BackendDescriptor, BackendId, ExecutorDescriptor, ExecutorId, MAX_RENDER_ID_BYTES, RenderFeature, RenderFormat, RenderLimits,
|
||||
RenderMode, RenderOperation, RenderPolicy, RenderProfile, RenderRequest, ResolveError, ResolvedRequest, ScratchLayout, builtin_backend,
|
||||
@@ -26,4 +29,5 @@ pub use backend::{
|
||||
pub use camera::{Camera, CameraAnim, Mat4, Quat, StandardView, UpAxis, ease_in_out};
|
||||
pub use curve_display::{PixelCurveView, SampledPath, SamplingBudget, SamplingQuality, sample_segment, sample_segment_unclipped};
|
||||
pub use product::{FaceAppearance, ProductMesh, ProductScene};
|
||||
pub use raster::{Canvas, Rgb, SECTION_CAP, Scene, SceneLine, SceneMesh, png, render, render_png, render_transparent};
|
||||
pub use raster::{Canvas, render, render_png, render_transparent};
|
||||
pub use scene::{Rgb, SECTION_CAP, Scene, SceneLine, SceneMesh, png};
|
||||
@@ -1,54 +1,10 @@
|
||||
//! CPU rasterizer for headless snapshots: z-buffered, smooth-shaded triangles and depth-tested
|
||||
//! lines, rendered at 2× and downsampled for anti-aliasing.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use solidcraft_geom::{Mesh, Vec3};
|
||||
use solidcraft_geom::Vec3;
|
||||
|
||||
use crate::Camera;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Rgb(pub u8, pub u8, pub u8);
|
||||
|
||||
impl Rgb {
|
||||
fn f(self) -> [f64; 3] {
|
||||
[self.0 as f64 / 255.0, self.1 as f64 / 255.0, self.2 as f64 / 255.0]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SceneMesh {
|
||||
pub mesh: Arc<Mesh>,
|
||||
pub color: Rgb,
|
||||
/// Faces in colours of their own: (B-rep face index, colour).
|
||||
pub face_colors: Vec<(u32, Rgb)>,
|
||||
}
|
||||
|
||||
pub struct SceneLine {
|
||||
pub points: Vec<Vec3>,
|
||||
pub color: Rgb,
|
||||
/// Width in output pixels.
|
||||
pub width: f64,
|
||||
/// Draw over everything (sketch geometry being edited).
|
||||
pub on_top: bool,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Scene {
|
||||
pub meshes: Vec<SceneMesh>,
|
||||
pub lines: Vec<SceneLine>,
|
||||
/// Background gradient top → bottom.
|
||||
pub background: Option<(Rgb, Rgb)>,
|
||||
/// Scene radius for clipping planes.
|
||||
pub radius: f64,
|
||||
/// A section cut: (normal, d); what lies where normal·p > d is cut away, and the inside
|
||||
/// shows as a hatched cap.
|
||||
pub clip: Option<(Vec3, f64)>,
|
||||
/// The colour the section cap is tinted toward (default: [`SECTION_CAP`]).
|
||||
pub cap: Option<Rgb>,
|
||||
}
|
||||
|
||||
/// The section cap's default tint, as in the viewport.
|
||||
pub const SECTION_CAP: Rgb = Rgb(214, 128, 72);
|
||||
use crate::scene::{Rgb, SECTION_CAP, Scene, png};
|
||||
|
||||
/// RGBA pixels plus depth.
|
||||
pub struct Canvas {
|
||||
@@ -220,7 +176,7 @@ pub fn render(scene: &Scene, cam: &Camera, w: usize, h: usize) -> Canvas {
|
||||
*s = s.max(cap_slope);
|
||||
}
|
||||
}
|
||||
for l in &scene.lines {
|
||||
for l in scene.grid.iter().chain(&scene.lines) {
|
||||
let col = l.color.f();
|
||||
let half = (l.width * SS as f64 * 0.5).max(0.5);
|
||||
for seg in l.points.windows(2) {
|
||||
@@ -326,14 +282,6 @@ pub fn render_png(scene: &Scene, cam: &Camera, w: usize, h: usize) -> Option<Vec
|
||||
png(c.w, c.h, c.rgba)
|
||||
}
|
||||
|
||||
/// RGBA pixels (`w × h`) to PNG bytes.
|
||||
pub fn png(w: usize, h: usize, rgba: Vec<u8>) -> Option<Vec<u8>> {
|
||||
let img = image::RgbaImage::from_raw(u32::try_from(w).ok()?, u32::try_from(h).ok()?, rgba)?;
|
||||
let mut buf = std::io::Cursor::new(Vec::new());
|
||||
img.write_to(&mut buf, image::ImageFormat::Png).ok()?;
|
||||
Some(buf.into_inner())
|
||||
}
|
||||
|
||||
/// Render on a transparent background: the scene drawn over black and over white, and the
|
||||
/// alpha recovered from the difference (exact for the anti-aliased edges).
|
||||
pub fn render_transparent(scene: &mut Scene, cam: &Camera, w: usize, h: usize) -> Canvas {
|
||||
@@ -359,6 +307,9 @@ pub fn render_transparent(scene: &mut Scene, cam: &Camera, w: usize, h: usize) -
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::StandardView;
|
||||
use crate::scene::{SceneLine, SceneMesh};
|
||||
use solidcraft_geom::Mesh;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Lines are cut by the section in 3D: the kept piece ends on the plane.
|
||||
#[test]
|
||||
@@ -391,6 +342,7 @@ mod tests {
|
||||
radius: 20.0,
|
||||
clip: None,
|
||||
cap: None,
|
||||
..Default::default()
|
||||
};
|
||||
let mut cam = Camera { distance: 50.0, ..Default::default() };
|
||||
cam.set_view(StandardView::Front);
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
//! What a picture of the model shows, independent of how it is drawn: meshes in colours, lines,
|
||||
//! the background, a section cut and a surface analysis. The GPU draws it with the viewport's
|
||||
//! pipelines (`solidcraft_gpu::capture`).
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use solidcraft_geom::{Mesh, Vec3};
|
||||
|
||||
use crate::SceneAnalysis;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Rgb(pub u8, pub u8, pub u8);
|
||||
|
||||
impl Rgb {
|
||||
/// The colour as 0..1 per channel.
|
||||
pub fn f(self) -> [f64; 3] {
|
||||
[self.0 as f64 / 255.0, self.1 as f64 / 255.0, self.2 as f64 / 255.0]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SceneMesh {
|
||||
pub mesh: Arc<Mesh>,
|
||||
pub color: Rgb,
|
||||
/// Faces in colours of their own: (B-rep face index, colour).
|
||||
pub face_colors: Vec<(u32, Rgb)>,
|
||||
}
|
||||
|
||||
pub struct SceneLine {
|
||||
pub points: Vec<Vec3>,
|
||||
pub color: Rgb,
|
||||
/// Width in output pixels.
|
||||
pub width: f64,
|
||||
/// Draw over everything (sketch geometry being edited).
|
||||
pub on_top: bool,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Scene {
|
||||
pub meshes: Vec<SceneMesh>,
|
||||
pub lines: Vec<SceneLine>,
|
||||
/// The ground grid: drawn under everything (the model hides it; it never hides the model).
|
||||
pub grid: Vec<SceneLine>,
|
||||
/// Background gradient top → bottom.
|
||||
pub background: Option<(Rgb, Rgb)>,
|
||||
/// Scene radius for clipping planes.
|
||||
pub radius: f64,
|
||||
/// A section cut: (normal, d); what lies where normal·p > d is cut away, and the inside
|
||||
/// shows as a hatched cap.
|
||||
pub clip: Option<(Vec3, f64)>,
|
||||
/// The colour the section cap is tinted toward (default: [`SECTION_CAP`]).
|
||||
pub cap: Option<Rgb>,
|
||||
/// A surface analysis shading the meshes (zebra, draft, curvature map, ...).
|
||||
pub analysis: Option<SceneAnalysis>,
|
||||
}
|
||||
|
||||
/// The section cap's default tint, as in the viewport.
|
||||
pub const SECTION_CAP: Rgb = Rgb(214, 128, 72);
|
||||
|
||||
/// RGBA pixels (`w × h`) to PNG bytes.
|
||||
pub fn png(w: usize, h: usize, rgba: Vec<u8>) -> Option<Vec<u8>> {
|
||||
let img = image::RgbaImage::from_raw(u32::try_from(w).ok()?, u32::try_from(h).ok()?, rgba)?;
|
||||
let mut buf = std::io::Cursor::new(Vec::new());
|
||||
img.write_to(&mut buf, image::ImageFormat::Png).ok()?;
|
||||
Some(buf.into_inner())
|
||||
}
|
||||
Reference in new issue
Block a user