From 258ee1d9222d1c2427403e72b38cc13b75ec70b7 Mon Sep 17 00:00:00 2001 From: Svjatoslav Agejenko Date: Sun, 20 Sep 2026 05:05:12 +0300 Subject: [PATCH] Delete verified dead code (~340 LOC) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Classes unreferenced across engine, tests and all consumer repos (demos, aukio, aukio-environment-fo4): geometry.Circle, gui.ViewUpdateTimerTask, gui.humaninput.Connexion3D, renderer.octree.raytracer.RayHit, shapes.composite.solid.SolidPolygonMesh, shapes.composite.wireframe.WireframeDrawing. Also: ViewPanel.renderExecutor — a thread pool that was created, resized and shut down but never submitted to; and the write-only static renderFrameCount counter. --- .../aukio-3d-architecture-review.md | 343 ++++++++++++++++++ .../aukio-3d-perf-review.md | 273 ++++++++++++++ .../svjatoslav/aukio/e3d/geometry/Circle.java | 30 -- .../svjatoslav/aukio/e3d/gui/ViewPanel.java | 24 -- .../aukio/e3d/gui/ViewUpdateTimerTask.java | 31 -- .../aukio/e3d/gui/humaninput/Connexion3D.java | 48 --- .../e3d/renderer/octree/raytracer/RayHit.java | 56 --- .../composite/solid/SolidPolygonMesh.java | 61 ---- .../composite/wireframe/WireframeDrawing.java | 75 ---- 9 files changed, 616 insertions(+), 325 deletions(-) create mode 100644 Documentation/reviews/2026-09-20-codebase-review/aukio-3d-architecture-review.md create mode 100644 Documentation/reviews/2026-09-20-codebase-review/aukio-3d-perf-review.md delete mode 100644 src/main/java/eu/svjatoslav/aukio/e3d/geometry/Circle.java delete mode 100755 src/main/java/eu/svjatoslav/aukio/e3d/gui/ViewUpdateTimerTask.java delete mode 100644 src/main/java/eu/svjatoslav/aukio/e3d/gui/humaninput/Connexion3D.java delete mode 100755 src/main/java/eu/svjatoslav/aukio/e3d/renderer/octree/raytracer/RayHit.java delete mode 100644 src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/composite/solid/SolidPolygonMesh.java delete mode 100755 src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/composite/wireframe/WireframeDrawing.java diff --git a/Documentation/reviews/2026-09-20-codebase-review/aukio-3d-architecture-review.md b/Documentation/reviews/2026-09-20-codebase-review/aukio-3d-architecture-review.md new file mode 100644 index 0000000..883839d --- /dev/null +++ b/Documentation/reviews/2026-09-20-codebase-review/aukio-3d-architecture-review.md @@ -0,0 +1,343 @@ +# Architecture & maintainability review — aukio-3d + +Scope: 152 main-source files, ~31.7k LOC at `/home/n0/workspace/Aukio/aukio-3d`. +Read fully: ViewPanel, TexturedTriangle, AbstractCompositeShape, OctreeVolume, RenderAggregator, +RenderingContext, AbstractCoordinateShape, ShapeCollection, SolidPolygon, LineInterpolator, +PolygonBorderInterpolator, SegmentRenderingContext, AbstractShape; skimmed the rest. +Dead-code claims verified by grep across aukio-3d (main+test), aukio-3d-demos, aukio, +aukio-environment-fo4 (no reflection usage found in consumers; demos touch 64 engine classes, +aukio 24, fo4 17 — the API surface is genuinely exercised). + +--- + +## [HIGH] 1. ViewPanel is a god class — 10 distinct responsibilities + +`gui/ViewPanel.java` (1611 lines). Responsibilities, with evidence: + +1. AWT/Swing integration — `getPreferredSize/getMinimumSize/getMaximumSize` (420-432), + `paint/update` overrides (537-543), `initializeCanvas` (545-556), `addNotify` (558-562), + `ensureBufferStrategy` (564-594). +2. Frame-loop & FPS pacing — `renderLoop` (1444-1456), `maintainTargetFps` (1464-1485), + `ensureThatViewIsUpToDate` (1494-1521), `noteFrameBlitted` (987-1001). +3. Pipeline scheduling (transform/sort/bin/paint triple-buffered passes) — `renderFrame` + (598-652), `transformPass` (1094-1132), `submitPaintPass` (1146-1273), + `flushPendingPaint` (800-877), `flushCompletedPasses` (889-901), `PendingPaint`/`PresentJob` + (240-266). +4. Presentation thread + mailbox — `presentFrame` (671-684), `presentLoop` (703-742), + `blitFrame` (744-791), mailbox fields (198-208), `PRESENT_RATE_LIMIT_FPS` (223-224). +5. Stereo configuration & eye-offset math — fields (1029-1039), `transformPass` IPD offset + (1100-1104), stereo accessors (1046-1080). +6. Thread-pool lifecycle — `getOrCreateTransformExecutor` (1295-1321), + `defaultRenderThreadCount` (1284-1287), plus the dead `renderExecutor` machinery + (see finding 7). +7. Input device hot-plug — `initializeHeadTracking` (1346-1352), `initializeSpaceMouse` + (1360-1366). +8. Developer tools integration — `showDeveloperToolsPanel` (516-535), segment-boundary + overlay drawn inline into the framebuffer (849-869). +9. Lighting & GI lifecycle — `lightingManager` (145), `enableGlobalIllumination` (490-510). +10. Raster helpers that belong to the renderer — `clearSegmentPixels` (911-926), + `combineMouseResults` (928-941). + +Refactor sketch: extract (a) `PipelineScheduler` owning renderFrame/transformPass/ +submitPaintPass/flush* + pendingPaints/passCounter/frameContexts (~550 lines), (b) +`FramePresenter` owning mailbox/presentLoop/blitFrame/PresentJob (~150 lines), (c) +`DeviceHotplug` for headtrack+spacemouse init/stop (~60 lines). ViewPanel keeps AWT glue, +public API delegating to the three. The four participants communicate through +RenderingContext fields that already exist. + +Risk: HIGH but contained to the engine — no rasterizer math touched, so golden pixels are +untouched by construction. The risk is concurrency regression in the pipeline itself; mitigated +by the `aukio3d.pipeline=false` kill switch (232-233) and the existing +ParallelTransformTest/SegmentBinningTest. Move code verbatim first (no logic edits), +verify with the demos' HouseGoldens + Fo4Shot runs. + +## [HIGH] 2. Package structure is fictional — total cycle, renderer↔gui inverted + +The import graph over top-level packages contains every possible cycle: gui↔renderer, +geometry↔renderer, geometry↔gui, math↔gui, math↔geometry (computed over all 152 files). + +Root cause: the renderer's core types live in `gui`: +- `gui/RenderingContext.java`, `gui/SegmentRenderingContext.java`, `gui/HiZPyramid.java`, + `gui/CullingStatistics.java`, `gui/ThreadActivityRecorder.java`. +- 15 renderer files import `gui.*`, e.g. `renderer/raster/RenderAggregator.java:7`, + `renderer/raster/ShapeCollection.java:9-12` (imports gui.CullingStatistics, gui.Camera, + **gui.ViewPanel** — the renderer depends on the AWT Canvas), + `renderer/raster/shapes/basic/texturedpolygon/TriangleMeshBlock.java:6` (gui.HiZPyramid). +- 5 gui files import renderer (ViewPanel:21-23, RenderingContext:11+18, GuiComponent:14-16, + LookAndFeel:7, TextEditComponent:13) → direct gui↔renderer cycle. +- geometry depends upward: `geometry/BspTree.java:7` and `geometry/Plane.java:8` import + renderer...SolidPolygon; `geometry/Frustum.java:7` imports gui.Camera; + `geometry/Point3D.java:7` imports renderer.octree.IntegerPoint (for one convenience + constructor, Point3D.java:121). +- `math/Vertex.java:9` imports gui.RenderingContext. + +Refactor sketch (mechanical, move-only): relocate RenderingContext, SegmentRenderingContext, +HiZPyramid, CullingStatistics, ThreadActivityRecorder, StereoEye to `renderer.raster` (or a new +`renderer.core`); move `gui.Camera`+`gui.ViewSpaceTracker` to geometry/math (Camera is pure +transform state). Break geometry→renderer by moving BspTree to +renderer...shapes.basic.solidpolygon (it stores SolidPolygon) or abstracting its element type. +Fix Point3D→IntegerPoint by moving that constructor to IntegerPoint or a small adapter. +ShapeCollection's ViewPanel dependency is only `viewPanel.getCamera()` +(ShapeCollection.java:302-305) — the Camera overload already exists (314), so the ViewPanel +overload is a convenience that could delegate from gui side or take a Camera. +After moves, the intended order math ← geometry ← renderer ← gui/headless becomes real. + +Risk: LOW for correctness (no code changes, only moves + import updates), MEDIUM for consumers: +three downstream repos must update imports. Public API breakage is the cost; do it in one sweep +with a consumer-side sed. Golden tests unaffected (no FP code moves between classes in a way +that changes execution order — class relocation has no runtime effect). + +## [HIGH] 3. Verified dead code — 6 classes + 2 fields (~340 lines) + +Grepped simple-name references across engine main+test and all three consumer repos: + +- `geometry/Circle.java` (30 LOC) — no references anywhere. +- `gui/ViewUpdateTimerTask.java` (31 LOC) — no references; legacy of the pre-render-thread + design (its `run()` calls the now-package-private `ensureThatViewIsUpToDate`). +- `gui/humaninput/Connexion3D.java` (48 LOC) — standalone /dev/hidraw experiment with its own + `main()`; superseded by `gui/spacemouse/SpaceNavigatorHid`. +- `renderer/octree/raytracer/RayHit.java` (56 LOC) — unreferenced even inside the raytracer. +- `renderer/raster/shapes/composite/solid/SolidPolygonMesh.java` (60 LOC) — unreferenced. +- `renderer/raster/shapes/composite/wireframe/WireframeDrawing.java` (75 LOC) — unreferenced. +- `ViewPanel.renderExecutor` + `ensureExecutorMatchesThreadCount()` (ViewPanel.java:121, + 1327-1339, 1406): created, resized, shut down — but **never used to submit a single task**; + `submitPaintPass` uses `getOrCreateTransformExecutor()` (1155). Legacy of the pre-ForkJoinPool + design. ~35 dead lines including the eager `Executors.newFixedThreadPool(...)` allocation at + construction time (121) — a real thread pool allocated and thrown away per ViewPanel. +- `ViewPanel.renderFrameCount` (596, incremented at 607): static, write-only, never read. + +Refactor sketch: delete all of the above. +Risk: ZERO for the classes/fields (no references exist, no reflection in consumers). Keep a +note that Connexion3D's main() is a hardware experiment if the maintainer wants it archived. + +## [HIGH] 4. AbstractCompositeShape carries an embeddable CSG engine + parallel-transform machinery + +`renderer/raster/shapes/composite/base/AbstractCompositeShape.java` (1295 lines). Responsibilities: + +1. Sub-shape registry + group visibility (88-133, 179-195, 335-377, 756-764). +2. Render-list caching & triangulation (775-821, 886-910). +3. CSG boolean engine (~240 lines, fully self-contained): `extractSolidPolygons` (304-315), + `union` (549-574), `subtract` (597-631), `intersect` (654-683), `clonePolygons` (694-700), + `replaceSolidPolygons` (709-725), `mergeNonPolygonChildrenFrom` (735-748). +4. Bounding-box aggregation (230-280). +5. Frustum culling inline in `transform` (912-997; AABB corner transform at 929-971). +6. Parallel-transform machinery (~280 lines): weight-cache fields (1026-1077), + `getTransformWeight` (1095-1120), `shouldForkTransform` (1132-1140), + `transformChildrenParallel` (1192-1275), constants (1003-1020, 1147). +7. Bulk property fan-out via instanceof chains — 8 sites: `setColor` (397-409), + `setShadingEnabled` (494-504), `setBackfaceCulling` (514-526), + `setMouseInteractionController` (422-432), plus 254, 308-311, 478, 791-807, 851-856. + +Also: `transformChildrenParallel(…, aggregator, …)` takes a **dead parameter** — its own +javadoc admits it (1187-1190: "unused in the parallel path"), and the body never uses it. + +Refactor sketch: (a) move the CSG block to a new `Csg` utility class in the same package — +all methods take/return `List` plus the registry mutations already isolated in +`replaceSolidPolygons`/`mergeNonPolygonChildrenFrom`; AbstractCompositeShape keeps three +one-line delegating methods for API compatibility. (b) Extract the weight/fork machinery into +`ParallelTransformPlanner` (fields + getTransformWeight + shouldForkTransform + chunking), +leaving `transform()` as orchestration. (c) Drop the dead `aggregator` parameter from +`transformChildrenParallel`. Result: ~1295 → ~700 lines. + +Risk: LOW-MEDIUM. CSG is call-graph-isolated (only BspTree + registry), no FP-order-sensitive +output (CSG is geometry, not rasterization — pixel risk none; geometry-identical because the +code moves verbatim). The parallel planner touches concurrency — move verbatim, keep field +semantics; verified by ParallelTransformTest. + +## [MEDIUM] 5. RenderAggregator: cohesive, but three separable machines + +`renderer/raster/RenderAggregator.java` (861 lines). Responsibilities: + +1. Shape queue + merge: `queueShapeForRendering` (703-706), `mergeFrom` (716-720), + `mergeAllParallel` (734-795), `reset` (831-838). +2. Sorting with three strategies (~210 lines): `tryRadixSort` (187-228), `parallelMergeSort` + (235-280), `runSortTasks` (283-308), `mergeRuns` (311-322), `awaitAll` (324-334), driver + `sort` (131-174). +3. Tile binning CSR (~280 lines): fields (400-418), `matchingBinIndex` (427-439), `matchAxis` + (452-463), `binForTiles` (500-523), `buildBins` (543-593), `binPhase` (596-634), + `binRangeCsr` (642-683). +4. Two-pass paint orchestration: `paintSorted` (353-366), `paintRange` (370-397). + +Refactor sketch: extract `ShapeSortMachine` (sort strategies + scratch arrays) and +`TileBinMachine` (CSR fields + build/match), aggregator keeps queue + paint and holds one of +each. Also drop `implements Serializable` on the comparator (844) — nothing serializes it. +Note the sort order contract (Z desc, shapeId asc) is the bit-exactness backbone for +binning/paint; extraction must move code verbatim. + +Risk: LOW (pure control flow, no FP math; order preserved by construction). Verified by +SegmentBinningTest + HouseGoldens. + +## [MEDIUM] 6. Rasterizer duplication: 7 span writers, 3 interpolator classes, 2 blend formulas + +Interpolators — three classes with byte-identical cores: +`LineInterpolator` (solidpolygon), `PolygonBorderInterpolator` (texturedpolygon), +`PerspectiveBorderInterpolator` (texturedpolygon). Identical: `containsY` (LineInterpolator: +84-88 = PolygonBorderInterpolator: 85-89 = PerspectiveBorderInterpolator: 101-105), the +`getX` formula (`round(p1.x + (width*(y-p1.y))/height)`, LineInterpolator:100-105 vs +PolygonBorderInterpolator:129-134), `setPointsZW` (LineInterpolator:130-134 = +PolygonBorderInterpolator:182-186 = PerspectiveBorderInterpolator:137+). + +Span/line writers — 7 sites: TexturedTriangle's `drawHorizontalLinePerspectiveZ` (223-405), +`drawHorizontalLineSdf` (968-1086), `drawHorizontalLinePerspectiveSdf` (1093-1263), +`drawHorizontalLineZ` (1312-1429); SolidPolygon's `drawHorizontalLine` (305-386); Line's two +single-pixel painters (180-233, 242-298). Within TexturedTriangle alone: +- the SDF bilinear-fetch + coverage block is byte-identical twice (1023-1066 vs 1197-1240, ~45 lines); +- the adaptive-interval ladder is byte-identical twice (322-335 vs 1163-1177); +- the edge-pair Y-scan loop appears 4× (698-708, 927-937, 950-960, 1293-1303), a 5th in + SolidPolygon (456-468); +- the yTop/yBottom clamp block appears 2× in-file (467-486 vs 596-615), a 3rd in SolidPolygon + (425-440); +- the swap-left/right + clamp preamble appears 4× (239-260, 986-994, 1117-1127, 1327-1346). + +Two *different* alpha-blend approximations exist: SolidPolygon/Line use +`((dest*bgAlpha) + src*alpha) >> 8` (SolidPolygon:376-378, Line:223-225, 289-291); +TexturedTriangle uses `dest + ((alpha*(src-dest) - dest) >> 8)` (386-388 and 3 more sites). +**They are not pixel-equivalent** — unifying span writers would change golden output. + +Refactor sketch (ordered by safety): +(a) SAFE: unify the three interpolators into one `EdgeInterpolator` — keep the exact FP +expression shapes (`(width * (y - p1.y)) / height`, midpoint for `|height| < EPSILON`), only +collapsing storage. No arithmetic changes → bit-exact. +(b) SAFE: extract the SDF bilinear fetch + coverage evaluation into one private static helper +called from both SDF writers — it is already byte-identical, so extraction cannot change +output if the expression text moves verbatim (watch implicit constant folding: keep `int` +casts and shifts identical). +(c) SAFE: single-source the edge-pair Y-scan loop as a small static helper taking the three +interpolators + a span callback — control-flow only. +(d) UNSAFE without re-golden: merging the two blend formulas. Don't — instead add a comment at +each site naming the formula and why they differ, or standardize deliberately and re-bless +goldens as a separate, announced change. + +Risk: (a)-(c) LOW if done as verbatim text motion (FP op order preserved); (d) is a visible +output change — treat as a feature, not a refactor. HouseGoldens/Fo4Shot are the gate. + +## [MEDIUM] 7. RenderingContext: ~45 fields in the wrong package + +`gui/RenderingContext.java` (707 lines). 43 instance fields + 2 statics; 22 public non-final +(measured). Natural clusters: + +- Framebuffer: bufferedImage (158), pixels (91), depth (99), graphics (78), segmentGraphics + (85), width/height (126/131). +- Projection: centerCoordinate (137), projectionScale (144), nearPlaneDistance (184), + frustum (289), viewerPosition (298). +- Tile/viewport geometry: tilesX/tilesY/viewportCount/numRenderSegments (63-72), + renderMinY/MaxY (150/156, **final**), renderMinX/MaxX (274/281, **mutable** — asymmetric, + mutated post-construction at ViewPanel:1224-1225), stereo* (255-267). +- Pipeline bookkeeping: vertexSlot (176), frameNumber (190), transformCycleId (166), + presentGate (344), depthPass (109), transformExecutor (324), transformCoordinator (333), + lastTransformTaskCount (350). +- Culling: subpixelCullingThreshold/Epoch (198/208), cullingStatistics (306), + occlusionPyramid (316). +- Mouse picking (~120 lines incl. methods 627-705): mouseEvent (222), + objectPreviouslyUnderMouseCursor (213), currentObjectUnderMouseCursor (226), + currentMouseTextureU/V (231-232). +- Services smuggled through: developerTools (237), debugLogBuffer (243), lightingManager (250). + +Belongs elsewhere: mouse-picking state+methods → `MousePickState` (owned by context, one +field); `presentGate` → pipeline glue owned by the scheduler/ViewPanel (it is only written at +ViewPanel:617-619 and awaited in the paint continuation); `depthPass` → RenderAggregator +passes it to shapes via context purely as a side-channel; `lastTransformTaskCount` → +diagnostics bundle; `lightingManager`/`developerTools`/`debugLogBuffer` → a small +`FrameServices` bag would cut constructor/copy-constructor duplication. + +Also note the two copy constructors (442-470, 482-521) enumerate fields by hand and already +diverge (the pass copy omits frustum "by design" — comment 520 — and silently drops +presentGate/lastTransformTaskCount). Every new field must be added in up to 3 places; this is +where the next pipeline bug comes from. + +Refactor sketch: (1) move class to renderer.raster (see finding 2); (2) extract MousePickState +(whole methods move); (3) move presentGate to the scheduler; (4) cluster remaining fields into +final sub-objects (Framebuffer, ViewportGeometry) so the copy constructors shrink to field +copies of immutable parts + shallow shares. + +Risk: LOW-MEDIUM — all mechanical, but the class is read by ~every shape; keep accessors as +delegates so shape code (`renderBuffer.pixels`, `.depth`, `.renderMinX`…) is untouched until a +second pass updates call sites. No FP code → goldens safe. The public-field style means +consumers may read these fields directly; keep the same field names visible during transition. + +## [MEDIUM] 8. OctreeVolume: 560-line copy-paste tracer + fully public internals + +`renderer/octree/OctreeVolume.java` (1102 lines), used only by demos' OctreeDemo. + +- Raw storage exposed: `public int[] cell1..cell8` (42-56), `public int cellAllocationPointer` + (61), `usedCellsCount` (64), `masterCellSize` (67), plus `initWorld` (298) re-allocating the + arrays under any concurrent reader. Invariants (cell state encoding -1/-2, 0 = null child) + are unenforceable. +- `doesIntersect` (133-248): 7 near-identical ~16-line face slabs. +- `traceCell` (512-1100): 8 octant branches × up to 7 recursive-probe stanzas each (~12 lines + per stanza) — ~560 lines of mechanical copy-paste with hand-maintained visit orders + ("// 6 8 3 5 2 4 1", 533). +- `getNewCellPointer` (335-350): linear-scan allocator with wraparound; infinite-loops when the + buffer is full (no exhaustion check) — a latent hang, not just style. + +Refactor sketch: (a) represent the 8 children as `int[][] children` or one `int[8]` per cell +indexed by octant — the putCell sub-cube selection (411-461) and traceCell collapse to loops +over octant index with a precomputed per-ray-octant visit-order table (8 orders × 8 octants, +64 entries, generated once). That alone removes ~500 lines. (b) Encapsulate arrays; add +exhaustion failure in getNewCellPointer (return -1 / throw) instead of an infinite loop. +(c) Optionally extract the ray-trace half (doesIntersect/traceCell) into `OctreeRayWalker`. + +Risk: LOW correctness-wise for (b) and the hang fix; MEDIUM for (a) — visit order affects +*which* cell is returned first for ties, and OctreeDemo visuals depend on it. Not covered by +golden tests (octree is not in the golden harnesses), so verify by side-by-side OctreeDemo +screenshots. This subsystem is demo-only; alternatively quarantine it as-is and spend effort +elsewhere. + +## [LOW] 9. AbstractCoordinateShape: slot-explosion + internal duplication + +- 12 scalar fields + 3 lists encode "screen state × 3 slots" (83-110, 165-175); accessors + repeat the same 3-way ternary 7 times (218-271). +- `setSlotScreenState` (126-148) and the write block inside `transform` (503-521) are the same + 3-way branch twice — `transform` could call `setSlotScreenState(slot, …)` (one-line fix). +- Stale docs: six accessors say "slot (0 or 1)" (226, 236, 246, 256, 266, 276) though slots are + 0/1/2. + +Refactor sketch: introduce `private final ScreenState[] slots = {new ScreenState(), …}` (z, +minY, maxY, minX, maxX, clippedVertices) — collapses 15 fields to 1 array + deletes 7 branchy +accessors. Keep public method signatures. Risk: ZERO pixel risk (pure storage, no FP +expressions), LOW merge risk; internal-only. + +## [LOW] 10. API surface & documentation drift + +- `ViewPanel.setFrameRate` (957) vs `getTargetFPS` (966) — setter/getter name mismatch + (setTargetFPS expected). +- TexturedTriangle javadoc links to methods that no longer exist: `{@link + #drawHorizontalLinePerspective}` (214), `{@link #drawHorizontalLine}` (318, 1090, 1308, + 1354, 1381) — the class has only the …Z/…Sdf variants (verified: no `drawHorizontalLine(` + definition). Javadoc build emits warnings for these. +- Stale pipeline comments on the most subtle code: "Double-buffered frame contexts" (ViewPanel + 160) over a 3-element array (165); "Parity (0/1)" (167) while `frameParity = (frameParity + + 1) % 3` (645); duplicated/orphaned javadoc stub above `getInputManager` (394-402). +- ShapeCollection slot docs say "(0 or 1)" (428, 469); a commented-out code block + TODO + (336-337); slot-0-only helpers (`sortShapes()` 421, `getQueuedShapeCount` 499, + `getBinSizes` 520) beside slot-parameterized siblings — test-only callers, fine, but mark + them as such. +- Direct field poke across classes: `AbstractCompositeShape.setColor` writes + `((Line) shape).color = color` (404) into Line's public field (Line.java:61) instead of a + setter — bypasses any future invalidation logic. +- `RenderingContext.bufferedImageType` (48) — constant not in CONSTANT_CASE. +- `RenderAggregator.ShapesZIndexComparator implements Serializable` (844) — nothing + serializes it; also `import java.io.Serializable` (10). + +Risk: ZERO-LOW. All are comment/name/mechanical fixes; renaming setFrameRate would break +consumers — prefer adding a correctly-named alias and deprecating. + +--- + +## Verdict + +**Yes — the architecture is sound for a software rasterizer of this size.** The load-bearing +structure (transform/sort/bin/paint phases, triple-buffered pipeline with per-slot vertex +state, painter + z-buffer two-pass visibility, Hi-Z, SoA TriangleMeshBlock) is deliberately +designed, and unusually well documented: the comments record *measurements and dates* (e.g. +ViewPanel:113-116, 219-221, 1299-1309; AbstractCompositeShape:1009-1012), which is exactly the +evidence-based culture that keeps a performance codebase honest. The problems are not the +design but **entropy concentrated in identifiable places**: ViewPanel's ten responsibilities, +RenderingContext's 45-field blob sitting in the wrong package (which drags the whole import +graph into cycles), and hand-maintained copy-paste in the span writers and the octree tracer. +None of these threaten correctness today; all of them raise the cost of the *next* change. +The recommended program is: (1) delete the verified dead code (free), (2) package relocation + +RenderingContext/ViewPanel extraction (mechanical, no FP risk), (3) safe de-duplication only — +interpolators, identical SDF block, scan-loop helper — leaving the two blend formulas alone, +(4) extract CSG and the transform planner from AbstractCompositeShape. Every step except +octree rework is gateable by the existing golden harnesses with bit-exact expectations. diff --git a/Documentation/reviews/2026-09-20-codebase-review/aukio-3d-perf-review.md b/Documentation/reviews/2026-09-20-codebase-review/aukio-3d-perf-review.md new file mode 100644 index 0000000..4b8a8f1 --- /dev/null +++ b/Documentation/reviews/2026-09-20-codebase-review/aukio-3d-perf-review.md @@ -0,0 +1,273 @@ +# aukio-3d performance review — what's left on the table + +Read-only static analysis, 2026-09-20. Scope: span writers, AoS-vs-SoA split, sort/bin/transform, +memory layout, threading, SDF text path, GI/octree. All line numbers against current HEAD. + +Known-good state (verified, not re-litigated): parallel chunk transform with pooled scratch, +radix sort over packed long keys, CSR tile bins, tiled MT paint with two-pass z-buffer, +Hi-Z block culling, subpixel epoch cache, TriangleMeshBlock SoA transform loop. + +--- + +## Tier 1 — multi-ms/frame class at 500k queued triangles + +### 1. Radix sort runs single-threaded; the executor handed to `sort()` is ignored in the radix path +`RenderAggregator.sort(ExecutorService)` (`RenderAggregator.java:162-165`): +```java +if (executor != null && sortedCount >= PARALLEL_SORT_THRESHOLD) { + if (!tryRadixSort(sortedArray, sortedCount)) // <- executor NOT passed + parallelMergeSort(sortedArray, sortedCount, comparator, executor); +``` +`tryRadixSort` (`RenderAggregator.java:187-228`) is one serial loop: key build +(`:193-196`, one virtual `getZ(slot)` per shape), then `RadixLongSort.sortPairs` +(`RadixLongSort.java:66-98`) = 8 LSD passes, each streaming 500k×(8B key + 4B idx) read + +12B scatter-write ≈ 96 MB of traffic on ONE core, then a serial random-gather permute +(`:224-226`) plus a full `System.arraycopy` back. This is the measured ~17 ms sort. +Bonus: `tryRadixSort` ends `return true` unconditionally — `parallelMergeSort` is dead code. + +- Why slow: serial memory-bound passes on one core while 17 workers idle (sort sits between + drain and bin in the paint continuation — `ViewPanel.java:1200-1205` — so it directly + delays paint-task submission every pass). +- Change sketch: parallel LSD radix on the same pool — per-chunk 256-bin histograms (parallel), + serial 256×chunks prefix, parallel stable scatter with per-(chunk,digit) offsets. Fixed chunk + boundaries + stability = bit-identical output to today (keys exact, equal-key order preserved, + tie-run shapeId fix unchanged). Also: permute into the *other* grow-only buffer and swap + references instead of `arraycopy` back (saves one 4 MB serial copy). Even simpler first step: + parallelize only the key build by folding it into `mergeAllParallel`'s already-parallel copy + (`RenderAggregator.java:755-790`) — compute `zSortKey` while copying each part. +- Impact class: sort 17 ms → ~4-6 ms wall. The largest single remaining pipeline lever. + +### 2. Per-triangle raster setup is recomputed per overlapped tile, per frame +`TexturedTriangle.paintFlat` (`TexturedTriangle.java:621-628`): +```java +final double edge12 = projectedPoint1.getDistanceTo(projectedPoint2); // sqrt +final double edge13 = projectedPoint1.getDistanceTo(projectedPoint3); // sqrt +final double edge23 = projectedPoint2.getDistanceTo(projectedPoint3); // sqrt +final double scaleFactor = (totalVisibleDistance / totalTextureDistance) * 1.2d; +final TextureBitmap mipmap = texture.getMipmapForScale(scaleFactor); +``` +plus the perspective setup (`:673-682`, three `1d/z` divides + muls) and the per-span curvature +ladder (`:322-335`, ~7 divides per span). All of this depends only on transform-phase outputs, +yet `paint()` runs once per overlapped tile — a triangle in 4 tiles pays it 4×. +Worse on the object path: `paintTriangle` (`:492-497`) computes the same three `getDistanceTo` +values, then `paintFlat` recomputes them — 6 sqrt per tile-paint for non-SDF triangles. +And `MeshTriangle.paint` (`MeshTriangle.java:109`) calls `block.origTtd(index)` +(`TriangleMeshBlock.java:444-456`) = 3 sqrt over the *final, build-time* `uv[]` array, +recomputed every paint of every tile of every frame. + +- Why slow: sqrt ~15-20c each; at ~150k painted triangles × ~1.5 tiles × (3-6 sqrt + divides) + ≈ 30-60M cycles/frame aggregate paint-side setup that is definitionally redundant. +- Change sketch (bit-exact — same expressions, evaluated once instead of N times): + compute `scaleFactor`/mip level/affine-vs-perspective flag once per triangle per slot in + `TriangleMeshBlock.transform` (mesh path) / `AbstractCoordinateShape.transform` (object path), + stash in per-slot handle state, pass into `paintFlat`. Precompute `origTtd` into a + `double[]` at block build (uv is final). In `paintTriangle`, pass the already-computed + `scaleFactor` down instead of recomputing. +- Impact class: ~1-3 ms/frame aggregate at FO4 queue sizes; larger for big-screen triangles + (text quads, terrain near the camera) that span tens of tiles. + +### 3. Scanline edge evaluation: per-getter divisions + per-scanline edge re-selection +`PerspectiveBorderInterpolator.getSU/getSV/getSW/getZW` (`PerspectiveBorderInterpolator.java:112-148`) +each call `interpolationT()` = `(currentY - p1.y) / height` — a **double division per getter**. +`drawHorizontalLinePerspectiveZ` (`TexturedTriangle.java:233-259`) calls getX + 4 channel getters +per edge per scanline: up to 10 divisions/scanline if C2 doesn't CSE them across the inlined +getters, 4 if it does (getX's `(width * (currentY - p1.y)) / height` is a different expression +than `width*t`, so it never shares). Same shape in `PolygonBorderInterpolator` +(`:98-119,129-134,189-194`) and `LineInterpolator.getX/getZW` (`:100-105,144-149`). +Additionally `containsY` (`PerspectiveBorderInterpolator.java:101-105`) recomputes +`Math.min/max(p1.y,p2.y)` per call, and the triangle y-loop (`TexturedTriangle.java:698-708`) +re-tests 2-3 `containsY` per scanline to re-derive which edge pair is active — when the pair +only changes once, at the middle vertex. + +- Why slow: double div ~13-20c; even at the CSE-friendly 4/scanline that's 50-80c/scanline of + division alone. At 500k queued tris with mean height ~5-15 scanlines, several M scanlines/frame + → multiple ms of pure edge math, concentrated on exactly the small distant triangles that + dominate the FO4 scene. +- Change sketch: + a) bit-exact: cache `minY/maxY` at `setPoints`; make one explicit `t = (currentY-p1.y)/height` + per edge per scanline and pass it to the four channel reads (identical expression → + identical bits, and no longer JIT-CSE-dependent). + b) needs-tolerance: express `x = p1.x + width*t` (removes the second div; differs by ≤1ulp + from `(width*dy)/height` — golden tolerance should absorb, verify). + c) bit-exact with care: split the y-loop into [yTop..yMid] and (yMid..yBottom] halves with the + edge pair fixed per half (the current inclusive `containsY` semantics pin which pair owns + the seam scanline — replicate). Removes ~3 containsY + min/max per scanline. +- Impact class: ~2-5 ms/frame aggregate at small-triangle-heavy views. + +--- + +## Tier 2 — ~0.5-2% frame time each, cheap and safe + +### 4. Depth margin costs 2 muls + 1 sub per pixel even though it defaults to 0 +All four z-buffered span writers test +```java +if (zw > depth[offset] - RenderingContext.DEPTH_MARGIN_DZ * zw * zw) +``` +`TexturedTriangle.java:356` (perspective-Z), `:1386` (affine-Z), `SolidPolygon.java:354,370`. +`DEPTH_MARGIN_DZ` (`RenderingContext.java:120-121`) parses `-Daukio.zbuffer.margin`, default `"0"`. +It's `static final` so C2 folds the constant to 0.0, but `0.0 * zw * zw` is NOT eliminable +(NaN semantics), so every z-tested pixel pays two dependent muls + a sub for a disabled feature. + +- Change sketch (bit-exact: `0.0*zw*zw == +0.0` and `d - 0.0 == d` exactly for finite zw, which + near-plane clipping guarantees): hoist once per span — + `final boolean strict = DEPTH_MARGIN_DZ == 0;` and branch to a margin-free loop copy, or + duplicate the pixel loops under one perfectly-predicted branch. If margin>0 is ever used, + the quadratic term can also be strength-reduced (`m += (2*c*dzw)*zw + c*dzw*dzw`, both + coefficients span-invariant) — that variant reassociates FP, so tolerance-check it. +- Impact class: ~1-1.5c/pixel on every z-tested pixel; at 2-4M tested pixels/frame ≈ 0.5-1% + frame time, for near-zero code risk. + +### 5. Hi-Z pyramid build is a serial full-depth-buffer sweep on the render thread; `occluded()` is `synchronized` +`ViewPanel.flushPendingPaint` (`:825-829`) calls `HiZPyramid.buildFrom` on the render thread +after each pass. `buildFrom` (`HiZPyramid.java:61-108`) min-pools the entire float depth buffer +(8×8 tiles) single-threaded: 8.3 MB @1080p, ~14.7 MB @1440p, ~58 MB at the 4920×2960 stereo +buffer — ~1-2 ms, up to ~5 ms, serial, before the next transform fork can start. +And `occluded()` (`:121`) is `synchronized` — every `TriangleMeshBlock.transform` on every +parallel chunk thread (`TriangleMeshBlock.java:184-216`) takes the same monitor, serializing +block tests against each other and against the multi-ms build. + +- Change sketch: build level 0 per tile in the paint workers' epilogue (depth just written is + still in L2), reduce upper levels serially (tiny); publish the pyramid as an immutable + per-build snapshot behind a volatile reference and drop `synchronized` from `occluded()` + (reads are all from the snapshot). Telemetry `AtomicLong`s → LongAdder. +- Impact class: ~1-2% @1440p mono, ~5% at 4K-class buffers; removes a contention edge that + scales with block count × worker count. + +### 6. SDF text path: 257×Math.pow + LUT allocation + 2×hypot + 2×log per triangle **per tile** per frame +`TexturedTriangle.paintSdf` (`:789-961`) runs per tile-paint (a text canvas quad overlapping +N tiles paints N times). Per call: +- `Math.hypot` ×2 for footprints (`:820-821`) — hypot pays overflow-safe scaling; + `sqrt(x*x+y*y)` is 3-5× cheaper (≤1ulp difference: tolerance-check), +- auto-gamma does `Math.log` ×2 (`:863-864`), +- when minified (any text past arm's length): `covLut = new int[257]` + 257×`Math.pow` + (`:865-869`). For a terminal-sized canvas (~2 triangles × 50-100 tiles) that's + ~200 × (257 pow + 1 KB alloc) ≈ 2M cycles per frame per canvas. + +- Change sketch: cache the LUT — gamma is a smooth function of `maxFootprint`; quantize gamma + to 1/256 steps and keep a per-thread last-LUT (or a tiny `ConcurrentHashMap`). + Hoist footprint/gamma/aaK to once per triangle per frame (paint-margin-free, so it's + tile-invariant). This is computation caching, not allocation pooling. +- Inner loop: the fixed-point bilinear (`:1037-1038` and `:1211-1212`) uses 8 int muls; the + two-lerp form `top = m00*(256-fx)+m10*fx; bot = m01*(256-fx)+m11*fx; d=(top*(256-fy)+bot*fy)>>16` + is **bit-identical** (integer associativity; worst case 33.4M < 2^31, no overflow) at 4 muls. + Halves the mask-eval multiply count on every text pixel. +- Impact class: workspace/terminal views (aukio TerminalPanel renders through this path): + ~1-3 ms/frame with several text canvases; FO4: negligible. + +--- + +## Tier 3 — smaller or situational + +### 7. Two-pass paint visits every bin entry twice +`RenderAggregator.paintSorted` (`:353-366`) iterates each tile's bin twice; +`paintRange` (`:370-397`) virtual-calls `paint()` on every entry in both passes and the shape +early-outs (`TexturedTriangle.paintFlat:563-565`, `SolidPolygon.paint:703-705`) only after a +volatile texture load + field reads. One of the two visits is always waste: ~queue×tiles +no-op dispatches per frame (~750k at 500k×1.5 tiles ≈ 0.5-1% frame). +- Sketch: split each bin into [opaque|alpha] segments at CSR build time (two counts per tile — + binning order within each class preserved, so pixels are bit-identical); pass 1 walks the + opaque segment reversed, pass 2 the alpha segment forward. + +### 8. Mip-chain lazy build races across paint threads; level selection loops per paint +`Texture.getDownscaledBitmap` (`Texture.java:278-291`) checks `downSampled[i] == null` and +builds unsynchronized while 18 paint threads may first-touch the same level on the same frame +→ duplicated full box-filter chain builds per texture (startup stutter on texture-heavy loads), +and the array-slot write has no happens-before edge. `getDownscaleMipmapLevel` (`:180-189`) +loops ≤8 iterations per triangle per tile-paint. +- Sketch: synchronize per-texture on build (or prebuild chains at texture-load time in the + FO4 `TextureSource`); publish via the array only after construction (final fields make the + bitmap itself safe). Replace the halving loop with an exponent-based level pick + (`Math.getExponent`) — verify threshold equivalence for exact powers of two. + +### 9. Octree ray tracer (OctreeDemo path): allocation-per-face-test and non-parametric traversal +`OctreeVolume.doesIntersect` (`OctreeVolume.java:133-248`) allocates a `new Point3D` (or +`clone()`) on *every* face candidate — up to 6 allocations per cell visit — and +`traceCell` (`:512-1100`) orders children by the **origin's octant**, not by parametric +distance along the ray. Because the first solid hit returns immediately, this is both slower +(no near-first pruning; every level re-runs 6-division slab tests per child from scratch) and +suspect (can return a farther voxel over a nearer one when the origin is outside the parent +cell). `RayTracer.run` (`RayTracer.java:157-175`) allocates a `Ray` + 2 `Point3D` + `Color` +per pixel and does 3 divides/pixel for view-plane interpolation (`(x3p * x) / width` — should +be incremental adds); `traceRay` then fires 6 shadow rays × 2 Point3D + Ray allocations per +light. `getNewCellPointer` (`:335-350`) is a linear scan for a free cell — O(cells) per +allocation, so filling big voxel volumes is O(n²). +- Sketch (only if OctreeDemo matters; nothing in FO4 touches this): slab test with precomputed + per-ray inverse direction, hit point computed once from the winning t (no per-face allocs), + Revelles-style parametric child ordering, free-list for cells. +- Impact: background/progressive demo renderer — not frame-critical. + +### 10. GI package: BVH build and query constant factors +`TriangleBvh.build` (`TriangleBvh.java:92-96`) sorts at every tree level with a comparator → +O(n log² n) build per snapshot rebuild (median selection via quickselect is O(n log n) and +lambda-free). `rayBox` (`:172-198`) does 6 divisionss per node visit + NaN fixup branches — +precompute `1/dx,1/dy,1/dz` once per ray and multiply (not bit-identical to division; GI +output feeds lightmaps, not golden pixels — still verify). `nearestNode` (`:123-140`) visits +left before right unconditionally; ordering children by nearer-box-first makes the `hit.t` +bound prune the far child far more often (typically ~2× fewer node visits on nearest-hit). +`GlobalIllumination.updateComposites` (`:610-637`) fills invalid lightmap texels with a +`while(progressed)` whole-map rescan — O(texels × map diameter); a BFS/multi-source queue is +O(texels). Per-sample `new double[3]` returns (`:537`, `:890`) churn on the GI threads — +background threads, so low stakes, but free to remove. +- Impact: GI convergence latency and background CPU burn only; render path is unaffected + (render-side GI cost is already ~zero by design). + +--- + +## Question 2 answer: how much AoS is left, and is extending SoA worth it? + +FO4 (the 500k-tri target): **~100% mesh blocks.** `NifSceneBuilder.java:169-222` (nif parts) and +`WorldStreamer.java:1309-1348` (terrain cells) both emit `TriangleMeshBlock` by default +(`aukio.fo4.meshBlocks`, default true); the `new TexturedTriangle` fallbacks +(`NifSceneBuilder.java:244`, `WorldStreamer.java:1420`) are behind the same flag. +`SolidPolygon` is imported in WorldStreamer but never instantiated for geometry. + +AoS `Vertex`-graph shapes (SolidPolygon, Line, Billboard, SDF text quads, lightmapped +triangles) carry 8-9 heap objects per vertex with per-slot pointer chasing — they remain in: +- demos/benchmarks: SolidCubesTest 16³ cubes ≈ 49k triangles, LitSolidCubesTest, sphere scenes; +- the Aukio workspace UI (TerminalPanel → TextCanvas → 2 SDF triangles per panel); +- CSG/lightmapped content where per-shape objects are semantically needed. + +Verdict: extending SoA to a `SolidPolygonMeshBlock` would only move the cube/sphere demos — +not the FO4 number. **Not worth it** for the stated target; the remaining AoS cost is in +small-N UI/demo scenes where per-shape setup, not pointer chasing, dominates anyway. + +## Question 4 answer: memory layout + +Pixel buffer and depth buffer are both row-major with identical addressing +(`y*width + x` everywhere) — span writes are unit-stride in both, tile clears use +`Arrays.fill` per row (intrinsic-vectorized). No column-major striding anywhere in the hot +paths. Texture reads (`ity*texW+itx`) stride by texture row — inherent to UV mapping and +already mitigated by the mip chain. The only structural note: pixels and depth are two +separate arrays, so each z-tested pixel write touches two cache lines; interleaving them +would break `BufferedImage` blit interop — not recommended. + +## Question 5 answer: thread utilization + +Tiles = 10×workers, near-square (`ViewPanel.java:1548-1552`, TILES_PER_THREAD=10) with one +task per tile on a work-stealing ForkJoinPool (75% of cores, measured choice) — sound. +Per-pass `CountDownLatch(segments)` is one await per frame, negligible. Two notes: the paint +continuation (`ViewPanel.java:1177-1268`) blocks a worker on `previousGate.await` +(`CountDownLatch` — no FJP compensation) — at most a few parked workers, fine; and the +serial sort inside that continuation is the real utilization gap (finding 1). + +--- + +## Verdict + +The architecture is **not** at the pure-Java ceiling. The big structural pieces are right +(SoA transform, radix keys, CSR bins, tiled two-pass z, Hi-Z), but there is one large and +several medium wins left, all compatible with bit-exactness or within stated tolerance: + +**Top 3:** +1. **Parallelize the radix sort** (it's single-threaded inside a pool it was explicitly + handed): ~17 ms → ~4-6 ms at 500k shapes. `RenderAggregator.java:162-228`. +2. **Hoist per-triangle raster setup out of per-tile paint** (mip metric sqrt×3, `origTtd`, + perspective divides — computed once per slot, not once per tile-overlap): + `TexturedTriangle.java:492-497,621-682`, `MeshTriangle.java:109`. Plus the guaranteed-safe + half of the scanline-divide fix (cached min/max, one explicit shared `t` per edge). +3. **Kill the default-disabled depth-margin math in the four z-span writers** + (strict-loop specialization, bit-exact) — ~0.5-1% of frame for a few lines; + and **de-serialize the Hi-Z build + lock-free `occluded()`** — ~1-2% at 1440p, more at 4K. + +(3 is two cheap items bundled; if forced to pick three single items: parallel radix, setup +hoisting, scanline edge-evaluation restructure.) diff --git a/src/main/java/eu/svjatoslav/aukio/e3d/geometry/Circle.java b/src/main/java/eu/svjatoslav/aukio/e3d/geometry/Circle.java deleted file mode 100644 index 70eca94..0000000 --- a/src/main/java/eu/svjatoslav/aukio/e3d/geometry/Circle.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Aukio 3D engine. Author: Svjatoslav Agejenko. - * This project is released under Creative Commons Zero (CC0) license. - */ -package eu.svjatoslav.aukio.e3d.geometry; - -/** - * A circle in 2D space defined by a center point and radius. - * - * @see Point2D - */ -public class Circle { - - /** - * The center point of the circle. - */ - Point2D location; - - /** - * The radius of the circle. - */ - double radius; - - /** - * Creates a circle with default values. - */ - public Circle() { - } - -} diff --git a/src/main/java/eu/svjatoslav/aukio/e3d/gui/ViewPanel.java b/src/main/java/eu/svjatoslav/aukio/e3d/gui/ViewPanel.java index 065f3b5..1b87b20 100755 --- a/src/main/java/eu/svjatoslav/aukio/e3d/gui/ViewPanel.java +++ b/src/main/java/eu/svjatoslav/aukio/e3d/gui/ViewPanel.java @@ -117,8 +117,6 @@ public class ViewPanel extends Canvas { */ private static final int TILES_PER_THREAD = 10; - /** The executor service for parallel rendering. */ - private ExecutorService renderExecutor = Executors.newFixedThreadPool(defaultRenderThreadCount()); /** Number of render threads. Can be changed at runtime via {@link #setNumRenderThreads(int)}. */ private volatile int numRenderThreads = defaultRenderThreadCount(); /** @@ -593,18 +591,15 @@ public class ViewPanel extends Canvas { } } - private static int renderFrameCount = 0; private void renderFrame() { ensureBufferStrategy(); - ensureExecutorMatchesThreadCount(); if (bufferStrategy == null || renderingContext == null) { debugLogBuffer.log("[VIEWPANEL] renderFrame ABORT: bufferStrategy=" + bufferStrategy + ", renderingContext=" + renderingContext); return; } - renderFrameCount++; ThreadActivityRecorder.setFrameParity(frameParity); // Install this frame-use's present gate and capture the previous @@ -1320,24 +1315,6 @@ public class ViewPanel extends Canvas { return transformExecutor; } - /** - * Recreates the executor service if the thread count has changed since last creation. - * Called from the render thread only (no synchronization needed). - */ - private void ensureExecutorMatchesThreadCount() { - if (renderExecutor == null || renderExecutor.isShutdown()) { - renderExecutor = Executors.newFixedThreadPool(numRenderThreads); - return; - } - if (renderExecutor instanceof java.util.concurrent.ThreadPoolExecutor) { - final java.util.concurrent.ThreadPoolExecutor tpe = (java.util.concurrent.ThreadPoolExecutor) renderExecutor; - if (tpe.getCorePoolSize() != numRenderThreads) { - tpe.shutdown(); - renderExecutor = Executors.newFixedThreadPool(numRenderThreads); - } - } - } - /** * Starts the head tracking hot-plug manager: RayNeo glasses are * detected when plugged in (even after startup) and head look-around @@ -1403,7 +1380,6 @@ public class ViewPanel extends Canvas { dropped.gate.countDown(); } pendingPaints.clear(); - renderExecutor.shutdownNow(); if (transformExecutor != null) { transformExecutor.shutdownNow(); } diff --git a/src/main/java/eu/svjatoslav/aukio/e3d/gui/ViewUpdateTimerTask.java b/src/main/java/eu/svjatoslav/aukio/e3d/gui/ViewUpdateTimerTask.java deleted file mode 100755 index 061880c..0000000 --- a/src/main/java/eu/svjatoslav/aukio/e3d/gui/ViewUpdateTimerTask.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Aukio 3D engine. Author: Svjatoslav Agejenko. - * This project is released under Creative Commons Zero (CC0) license. - */ -package eu.svjatoslav.aukio.e3d.gui; - -/** - * Timer task that updates view. - * - * Tries to keep constant FPS. - */ -public class ViewUpdateTimerTask extends java.util.TimerTask { - - /** The view panel to update. */ - public ViewPanel viewPanel; - - /** - * Creates a new timer task for the given view panel. - * - * @param viewPanel the view panel to update - */ - public ViewUpdateTimerTask(final ViewPanel viewPanel) { - this.viewPanel = viewPanel; - } - - @Override - public void run() { - viewPanel.ensureThatViewIsUpToDate(); - } - -} diff --git a/src/main/java/eu/svjatoslav/aukio/e3d/gui/humaninput/Connexion3D.java b/src/main/java/eu/svjatoslav/aukio/e3d/gui/humaninput/Connexion3D.java deleted file mode 100644 index bcd13c0..0000000 --- a/src/main/java/eu/svjatoslav/aukio/e3d/gui/humaninput/Connexion3D.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Aukio 3D engine. Author: Svjatoslav Agejenko. - * This project is released under Creative Commons Zero (CC0) license. - */ -package eu.svjatoslav.aukio.e3d.gui.humaninput; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; - -/** - * I have Space Mouse Compact 3D Connexion mouse: https://3dconnexion.com/us/product/spacemouse-compact/ - * - * I discovered that it is possible to read raw data from it by reading /dev/hidraw4 file. - * - * TODO: reverse engineer the data format and implement a driver for it. - */ - -public class Connexion3D { - - /** - * Creates a new Connexion3D instance. - */ - public Connexion3D() { - } - - /** - * Reads raw data from the 3Dconnexion device for testing purposes. - * - * @param args command line arguments (ignored) - * @throws IOException if the device cannot be read - */ - public static void main(final String[] args) throws IOException { - - final BufferedReader in = new BufferedReader(new FileReader( - "/dev/hidraw4")); - - - // for testing purposes - while (true) { - System.out.print(in.read() + " "); - System.out.println("\n"); - } - - // in.close(); - - } -} diff --git a/src/main/java/eu/svjatoslav/aukio/e3d/renderer/octree/raytracer/RayHit.java b/src/main/java/eu/svjatoslav/aukio/e3d/renderer/octree/raytracer/RayHit.java deleted file mode 100755 index b3710b7..0000000 --- a/src/main/java/eu/svjatoslav/aukio/e3d/renderer/octree/raytracer/RayHit.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Aukio 3D engine. Author: Svjatoslav Agejenko. - * This project is released under Creative Commons Zero (CC0) license. - */ -package eu.svjatoslav.aukio.e3d.renderer.octree.raytracer; - -/** - * Records the result of a ray-octree intersection test. - * - *

A {@code RayHit} stores the 3D world-space coordinates where a {@link Ray} - * intersected an octree cell, along with a pointer (index) to the intersected cell - * within the {@link eu.svjatoslav.aukio.e3d.renderer.octree.OctreeVolume}'s internal - * cell arrays.

- * - * @see Ray - * @see RayTracer - * @see eu.svjatoslav.aukio.e3d.renderer.octree.OctreeVolume - */ -public class RayHit { - - /** - * The x coordinate of the intersection point in world space. - */ - float x; - - /** - * The y coordinate of the intersection point in world space. - */ - float y; - - /** - * The z coordinate of the intersection point in world space. - */ - float z; - - /** - * The index (pointer) into the octree's cell arrays identifying the cell that was hit. - */ - int cellPointer; - - /** - * Creates a new ray hit record. - * - * @param x the x coordinate of the intersection point - * @param y the y coordinate of the intersection point - * @param z the z coordinate of the intersection point - * @param cellPointer the index of the intersected cell in the octree's cell arrays - */ - public RayHit(final float x, final float y, final float z, - final int cellPointer) { - this.x = x; - this.y = y; - this.z = z; - this.cellPointer = cellPointer; - } -} diff --git a/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/composite/solid/SolidPolygonMesh.java b/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/composite/solid/SolidPolygonMesh.java deleted file mode 100644 index 885f285..0000000 --- a/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/composite/solid/SolidPolygonMesh.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Aukio 3D engine. Author: Svjatoslav Agejenko. - * This project is released under Creative Commons Zero (CC0) license. - */ -package eu.svjatoslav.aukio.e3d.renderer.raster.shapes.composite.solid; - -import eu.svjatoslav.aukio.e3d.geometry.Point3D; -import eu.svjatoslav.aukio.e3d.renderer.raster.Color; -import eu.svjatoslav.aukio.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon; -import eu.svjatoslav.aukio.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; - -import java.util.List; - -/** - * A renderable mesh composed of SolidPolygon triangles. - * - *

This is a generic composite shape that holds a collection of triangles. - * It can be constructed from any source of triangles, such as procedural - * geometry generation or loaded mesh data.

- * - *

Usage:

- *
{@code
- * // From list of triangles
- * List triangles = ...;
- * SolidPolygonMesh mesh = new SolidPolygonMesh(triangles, location);
- *
- * // With fluent configuration
- * shapes.addShape(mesh.setShadingEnabled(true).setBackfaceCulling(true));
- * }
- * - * @see SolidPolygon the triangle type for rendering - */ -public class SolidPolygonMesh extends AbstractCompositeShape { - - private int triangleCount; - - /** - * Creates a mesh from a list of SolidPolygon triangles. - * - * @param triangles the triangles to include in the mesh - * @param location the position in 3D space - */ - public SolidPolygonMesh(final List triangles, final Point3D location) { - super(location); - this.triangleCount = 0; - - for (final SolidPolygon triangle : triangles) { - addShape(triangle); - triangleCount++; - } - } - - /** - * Returns the number of triangles in this mesh. - * - * @return the triangle count - */ - public int getTriangleCount() { - return triangleCount; - } -} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/composite/wireframe/WireframeDrawing.java b/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/composite/wireframe/WireframeDrawing.java deleted file mode 100755 index 1510ea4..0000000 --- a/src/main/java/eu/svjatoslav/aukio/e3d/renderer/raster/shapes/composite/wireframe/WireframeDrawing.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Aukio 3D engine. Author: Svjatoslav Agejenko. - * This project is released under Creative Commons Zero (CC0) license. - */ -package eu.svjatoslav.aukio.e3d.renderer.raster.shapes.composite.wireframe; - -import eu.svjatoslav.aukio.e3d.geometry.Point3D; -import eu.svjatoslav.aukio.e3d.renderer.raster.shapes.basic.line.Line; -import eu.svjatoslav.aukio.e3d.renderer.raster.shapes.basic.line.LineAppearance; -import eu.svjatoslav.aukio.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; - -/** - * A freeform polyline drawing tool that connects sequential points with line - * segments. Points are added one at a time via {@link #addPoint(Point3D)}; - * each new point is connected to the previously added point by a line. - * - *

The first point added establishes the starting position without drawing - * a line. Each subsequent point creates a new line segment from the previous - * point to the new one.

- * - *

This shape is useful for drawing paths, trails, trajectories, or - * arbitrary wireframe shapes that are defined as a sequence of vertices.

- * - *

Usage example:

- *
{@code
- * LineAppearance appearance = new LineAppearance(2, Color.YELLOW);
- * WireframeDrawing drawing = new WireframeDrawing(appearance);
- * drawing.addPoint(new Point3D(0, 0, 0));
- * drawing.addPoint(new Point3D(100, 50, 0));
- * drawing.addPoint(new Point3D(200, 0, 0));
- * shapeCollection.addShape(drawing);
- * }
- * - * @see LineAppearance - * @see AbstractCompositeShape - */ -public class WireframeDrawing extends AbstractCompositeShape { - - /** The line appearance used for all segments in this drawing. */ - final private LineAppearance lineAppearance; - - /** The most recently added point, used as the start of the next line segment. */ - Point3D currentPoint; - - /** - * Constructs a new empty wireframe drawing with the given line appearance. - * - * @param lineAppearance the line appearance (color, width) used for all - * line segments added to this drawing - */ - public WireframeDrawing(final LineAppearance lineAppearance) { - super(); - this.lineAppearance = lineAppearance; - } - - /** - * Adds a new point to the drawing. If this is the first point, it sets the - * starting position. Otherwise, a line segment is created from the previous - * point to this new point. - * - *

The point is defensively copied, so subsequent modifications to the - * passed {@code point3d} object will not affect the drawing.

- * - * @param point3d the point to add to the polyline - */ - public void addPoint(final Point3D point3d) { - if (currentPoint != null) { - final Line line = lineAppearance.getLine(currentPoint, point3d); - addShape(line); - } - - currentPoint = new Point3D(point3d); - } - -} -- 2.20.1