Files
pdfcraft/crates/prc-render
KenM76andClaude Opus 5.5 ce393b276b M12: prc-render review fixes (missing tessellations, vertex ceiling, seams, perspective shade)
- assemble: a placement naming a tessellation its file structure does not
  have is now PrcError::Malformed instead of a silently missing part;
  wires, markups and compressed tessellations are still skipped.
- assemble: positions, normals and texture coordinates are charged against
  a new MAX_ASSEMBLED_VERTICES (2^24) with RenderError::TooManyVertices, so
  a one-triangle mesh with many vertices placed many times cannot outgrow
  the triangle ceiling's memory bound. A refused mesh spends nothing.
- raster: a field of view too small for a finite focal length is a Camera
  error, not a background-only image.
- raster: a pixel centre exactly on an edge belongs to one triangle only
  (the side the triangle lies on, +x first, then +y), so translucent
  quads no longer blend their diagonal twice.
- raster: under a perspective camera the shade is interpolated with the
  1/z depth key, so it follows the surface rather than the screen.
- vec3: normalize divides by the largest component first, so a cross
  product above ~1e154 (or a subnormal one) still normalizes.
- render docs and README state the near plane: 1/10,000 of the
  eye-to-target distance.

Each new test fails with its fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-11-03 21:41:05 -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 1/10,000 of the eye-to-target distance from the eye; 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 and MAX_ASSEMBLED_VERTICES (2^24 each) bound one assemble call. None 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.