Files
pdfcraft/crates/prc-render
KenM76andClaude Opus 5.5 ea6d0c6bfb M12: pdfcraft-prc-render, a headless renderer for PRC models
New L3 crate over pdfcraft-prc: assemble places every product-tree
tessellation into world space with its colours (pure `place` does the
transform), Camera fits a view to the model, and render fills
depth-buffered triangles with headlight or unlit shading into an RGBA
image. Opaque triangles draw first, translucent ones blend over them;
perspective views clip at a near plane.

Never panics: MAX_RENDER_PIXELS and MAX_ASSEMBLED_TRIANGLES cap the
work, every pixel and vertex index is checked, and damage returns
RenderError. Single-threaded.

Tests: 24 unit tests (two drive the product-tree placement path) plus an end-to-end one that builds a synthetic
PRC stream in-test, renders it, and checks the background, the nearer
triangle and the farther one by pixel; every truncation of that stream
parses and assembles without panicking. Reverting the depth test or the
placement transform fails the matching tests.

Adapted from pdfcer (MIT, same author) at 912437c3, trimmed to
the camera, raster and headlight parts and rewritten to the
pdfcraft-prc types.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-11-03 20:50:36 -05:00
..

pdfcraft-prc-render

A headless CPU renderer for PRC 3D models read by pdfcraft-prc: placed meshes in world space, a fitted camera, a depth-buffered triangle fill and headlight shading, into a straight-RGBA image.

  • Layer: L3. Depends on pdfcraft-prc only.
  • Licence: MIT OR Apache-2.0.
  • #![forbid(unsafe_code)]. Never panics on input: image size, triangle count and every index are checked, and damage returns RenderError.

API

assemble(&PrcFile) -> Result<Vec<WorldMesh>, RenderError>   // every placement's mesh moved into the world and coloured
place(&TriangleMesh, &Matrix, colour, triangle_colours) -> WorldMesh
Camera::fit(&Bounds, direction, up, perspective, aspect) / Camera::fit_meshes(&[WorldMesh], ...)
render(&[WorldMesh], &Camera, &RenderOptions) -> Result<Image, RenderError>
  • With no product tree, each tessellated mesh is drawn where it lies, in the default colour.
  • A triangle's own colour wins over its mesh's, which wins over RenderOptions::colour.
  • Opaque triangles are drawn first, then translucent ones blended over them; alpha 0 is not drawn.
  • Lighting::Headlight (default) shades by the angle to the view direction; Lighting::Unlit draws flat colour.
  • Perspective triangles are clipped at a near plane; orthographic views are not clipped.
  • MAX_RENDER_PIXELS (2^24, about 16 MP) bounds the image: at most 64 MB of RGBA plus 128 MB of depth. MAX_ASSEMBLED_TRIANGLES (2^24) bounds one assemble call. Neither bounds time: a model of many large triangles at full size is a long CPU render, so callers on a UI thread should render small or off-thread.
  • Not yet: shadows, ambient occlusion, specular light, scene lights, textures, wires and markups.