| Class | File | Purpose | Key Methods |
|--------------------------+------------------------------------+-----------------------+-------------------------------------------------------------------------------------------------|
| ~AbstractCompositeShape~ | ~base/AbstractCompositeShape.java~ | Group shapes, CSG ops | ~.addShape()~, ~.subtract()~, ~.union()~, ~.intersect()~, ~.setShadingEnabled()~, ~.setColor()~ |
+| ~LightmappedCompositeShape~ | ~composite/LightmappedCompositeShape.java~ | Composite that fan-triangulates and optionally carries GI lightmaps | ~.setLightmappingEnabled(true)~ |
+| ~TriangleMeshBlock~ | ~basic/texturedpolygon/TriangleMeshBlock.java~ | SoA fast path for large textured triangle meshes (flat double[] arrays, one tight transform loop) | build from mesh data |
** Text (~shapes/composite/textcanvas/~)
#+begin_example
ViewPanel.renderFrame()
- 1. ShapeCollection.transformShapes() — apply camera transform
- 2. ShapeCollection.sortShapes() — sort by Z (back-to-front)
- 3. ShapeCollection.paintShapes() — painter's algorithm
+ 1. ShapeCollection.transformShapes() — apply camera transform, cull, queue
+ 2. ShapeCollection.sortShapes() — radix sort by Z (back-to-front)
+ 3. ShapeCollection.paintShapes() — tiled parallel paint, two-pass z-buffer
+ (opaque front-to-back with depth writes, then alpha back-to-front)
4. BufferStrategy.show() — page flip to display
#+end_example
| ~Documentation/Near plane clip/index.org~ | Near-plane polygon clipping (straddling geometry) |
| ~Documentation/Global illumination/index.org~ | Progressive GI: lightmaps, bounces, convergence |
| ~Documentation/Perspective correct textures/index.org~ | Texture mapping math |
-| ~Documentation/Agentic development/index.org~ | Stub: headless-toolkit docs moved to ~Documentation/index.org~ :: Agentic development |
| ~Documentation/Stereoscopic rendering/index.org~ | Side-by-side stereo: two passes, per-eye viewports, IPD |
-| ~Documentation/BSP-tree painter's algorithm/index.org~ | BSP compile + rank traversal fixing average-Z sort |
+| ~Documentation/Depth buffer/index.org~ | Two-pass z-buffer, zw, depth margin, Hi-Z pyramid, determinism |
| ~Documentation/SDF textures/index.org~ | SDF text: glyph fields, coverage window, TextCanvas |
Regenerate all HTML: ~Documentation/export-docs.sh~ (add ~--check~ for rendered
-* Add 3D mouse support
-:PROPERTIES:
-:CUSTOM_ID: add-3d-mouse-support
-:END:
-
* Demos
:PROPERTIES:
:CUSTOM_ID: demos
final DataBufferInt dbi = (DataBufferInt) raster.getDataBuffer();
pixels = dbi.getData();
- // Z-buffer mode (-Daukio.zbuffer=true): one w-depth (biased 1/z)
- // value per pixel, cleared per tile in the paint workers. Depth
- // turns the queue order into a performance heuristic only;
- // correctness comes from the per-pixel test. (The queue itself
- // stays painter back-to-front — Z descending, see
- // RenderAggregator.) Null in classic painter mode.
+ // Z-buffer: one w-depth (biased 1/z) value per pixel, cleared per
+ // tile in the paint workers. Depth turns the queue order into a
+ // performance heuristic only; correctness comes from the per-pixel
+ // test. (The queue itself stays painter back-to-front — Z
+ // descending, see RenderAggregator.) Always allocated: the
+ // z-buffer path is the only renderer.
depth = new float[width * height];
graphics = (Graphics2D) bufferedImage.getGraphics();
private RenderingContext renderingContext = null;
/**
- * Double-buffered frame contexts, indexed by frame parity. While frame N
- * is still being painted from one buffer, frame N+1 already transforms
- * into the other, so paint threads never idle waiting for the transform
- * phase and transform threads never wait for the last tile.
+ * Triple-buffered frame contexts, indexed by pass counter modulo 3.
+ * While frame N is still being painted from one buffer, frame N+1
+ * already transforms into another, so paint threads never idle waiting
+ * for the transform phase and transform threads never wait for the
+ * last tile.
*/
private final RenderingContext[] frameContexts = new RenderingContext[3];
- /** Parity (0/1) of the frame currently being prepared. */
+ /** Slot index (0..2) of the frame context currently being prepared. */
private int frameParity = 0;
/**
/**
* Collects transformed shapes during a render frame and paints them in depth-sorted order.
*
- * <p>The {@code RenderAggregator} implements the painter's algorithm: shapes are sorted
- * from back to front (highest Z-depth first) and then painted sequentially. This ensures
- * that closer shapes correctly occlude those behind them.</p>
+ * <p>Shapes are sorted from back to front (highest Z-depth first). Under the
+ * (unconditional) z-buffer, occlusion correctness comes from the per-pixel
+ * depth test; the queue order is a performance and coherence heuristic:
+ * pass 1 paints opaque shapes front-to-back (queue reversed) so the depth
+ * test rejects hidden fragments before the texture fetch, pass 2 paints
+ * alpha shapes back-to-front without depth writes so translucent overlap
+ * stays painter-coherent.</p>
*
* <p>When two shapes have the same Z-depth, their unique {@link AbstractCoordinateShape#shapeId}
* is used as a tiebreaker to guarantee deterministic rendering order.</p>
/**
* Creates an aggregator bound to a projection buffer slot. Sorting and
* tile binning read the shapes' screen state for this slot, so the
- * double-buffered pipeline can fill one slot's aggregator while the
- * other slot's aggregator is still being painted.
+ * triple-buffered pipeline can fill one slot's aggregator while the
+ * other slots' aggregators are still being painted.
*
- * @param slot the buffer slot (0 or 1) this aggregator serves
+ * @param slot the buffer slot (0..2) this aggregator serves
*/
public RenderAggregator(final int slot) {
this.slot = slot;
/**
* Comparator that sorts shapes by Z-depth in descending order (farthest first)
- * for the painter's algorithm. Uses shape ID as a tiebreaker.
+ * for the render queue. Uses shape ID as a tiebreaker.
*/
static class ShapesZIndexComparator implements Comparator<AbstractCoordinateShape>, Serializable {
/**
* Sorts the given buffer slot's queued shapes by Z-depth.
*
- * @param slot buffer slot to sort (0 or 1)
+ * @param slot buffer slot to sort (0..2)
*/
public void sortShapes(final int slot) {
aggregators[slot].sort();
/**
* Slot-selecting variant of
* {@link #binShapesForTiles(int, int, int, int, int, ExecutorService)}
- * for the double-buffered pipeline.
+ * for the triple-buffered pipeline.
*
- * @param slot buffer slot whose queue gets binned (0 or 1)
+ * @param slot buffer slot whose queue gets binned (0..2)
* @param tilesX tile columns across the viewport
* @param tilesY tile rows down the viewport
* @param originX X origin of the tiled viewport (eye offset in stereo)
* <li><b>Wireframe rendering</b> - lines and wireframe shapes</li>
* <li><b>Solid polygon rendering</b> - filled polygons with flat shading</li>
* <li><b>Textured polygon rendering</b> - polygons with texture mapping and mipmap support</li>
- * <li><b>Depth sorting</b> - back-to-front painter's algorithm using Z-index ordering</li>
+ * <li><b>Depth sorting</b> - back-to-front Z-index ordering feeding the two-pass z-buffer paint</li>
* </ul>
*
* <p>Key classes in this package:</p>
/**
* Average Z-depth of this shape in screen space after transformation,
* per buffer slot. Used by the {@link RenderAggregator} to sort shapes
- * back-to-front for correct painter's algorithm rendering.
+ * back-to-front (the queue order the two-pass z-buffer paint consumes).
* Access via {@link #getZ(RenderingContext)} / {@link #getZ(int)}.
*/
private double onScreenZ0;
}
/**
- * Z-buffer variant of {@link #drawHorizontalLinePerspective}: the
- * biased 1/z endpoint values ride the interpolators' zw channel, and
- * every pixel is depth-tested BEFORE the texture fetch — rejected
- * pixels cost one float compare instead of a texel read. Opaque
- * texels (alpha 255) write depth; blended texels write color only,
- * so translucency never occludes. Requires
- * {@code renderBuffer.depth != null} and {@code setPointsZW} called
- * on both interpolators.
+ * Z-buffer span writer: the biased 1/z endpoint values ride the
+ * interpolators' zw channel, and every pixel is depth-tested BEFORE
+ * the texture fetch — rejected pixels cost one float compare instead
+ * of a texel read. Opaque texels (alpha 255) write depth; blended
+ * texels write color only, so translucency never occludes.
+ * {@code renderBuffer.depth} is always allocated (the z-buffer path
+ * is the only renderer); requires {@code setPointsZW} called on both
+ * interpolators.
*/
private void drawHorizontalLinePerspectiveZ(
final PerspectiveBorderInterpolator line1,
// Alpha pass (depthPass 2): depth-test but never depth-write,
// so cutout foliage cannot occlude later fragments
final boolean writeDepth = renderBuffer.depthPass != 2;
- // see drawHorizontalLine: null texture (unit tests) = clamp
+ // null texture (unit tests) = clamp
final boolean wrap = texture != null && texture.wrap;
- // Same adaptive-subdivision ladder as the painter variant
+ // Adaptive-subdivision perspective ladder (Quake-style stepping)
final double ue1 = su1 / sw1;
final double ue2 = su2 / sw2;
final double ve1 = sv1 / sw1;
/**
* SDF scanline with Quake-style subdivided perspective correction —
- * same stepping structure as {@link #drawHorizontalLinePerspective},
+ * same stepping structure as {@link #drawHorizontalLinePerspectiveZ},
* with the coverage fetch replaced by the distance-field evaluation.
*/
private void drawHorizontalLinePerspectiveSdf(
}
/**
- * Z-buffer variant of {@link #drawHorizontalLine}: per-pixel depth
- * test (biased 1/z, linear along the span) BEFORE the texture fetch.
- * Opaque texels write depth; blended texels write color only.
+ * Z-buffer span writer: per-pixel depth test (biased 1/z, linear
+ * along the span) BEFORE the texture fetch. Opaque texels write
+ * depth; blended texels write color only.
*/
private void drawHorizontalLineZ(final PolygonBorderInterpolator line1,
final PolygonBorderInterpolator line2,