1 package eu.svjatoslav.aukio.e3d.renderer.raster.shapes.basic.texturedpolygon;
3 import eu.svjatoslav.aukio.e3d.geometry.Point2D;
4 import eu.svjatoslav.aukio.e3d.gui.RenderingContext;
5 import eu.svjatoslav.aukio.e3d.renderer.raster.RenderAggregator;
6 import eu.svjatoslav.aukio.e3d.math.TransformStack;
9 * Paint/sort handle for one triangle of a {@link TriangleMeshBlock}. The
10 * triangle's geometry lives in the block's flat arrays (SoA layout); this
11 * object exists only so the existing sort/bin/paint pipeline — typed on
12 * individual shapes — can address one triangle. Handles are allocated once
13 * at block build time and reused every frame; {@link #transform} is a
14 * no-op because the block transforms all its triangles in one tight loop.
16 * <p>Per-slot screen data is read from the block's arrays through the
17 * overridden accessors, so the Z-comparator and tile binning see exactly
18 * the values an object-backed {@link TexturedTriangle} would expose.</p>
20 final class MeshTriangle extends TexturedTriangle {
23 * Scratch Point2D carriers for the flat paint call. Interpolators
24 * hold references to the screen/UV points they are given, so the
25 * objects must stay stable for the duration of one paint — but a
26 * paint never nests, so three screen + three UV points per thread
27 * suffice and nothing is allocated per triangle.
29 private static final ThreadLocal<Point2D[]> SCREEN_SCRATCH =
30 ThreadLocal.withInitial(() -> new Point2D[]{
31 new Point2D(), new Point2D(), new Point2D()});
32 private static final ThreadLocal<Point2D[]> UV_SCRATCH =
33 ThreadLocal.withInitial(() -> new Point2D[]{
34 new Point2D(), new Point2D(), new Point2D()});
36 private final TriangleMeshBlock block;
37 private final int index;
39 MeshTriangle(final TriangleMeshBlock block, final int index,
40 final eu.svjatoslav.aukio.e3d.renderer.raster.texture.Texture texture) {
47 * No-op: the owning block transforms all its triangles (including this
48 * one) in a single flat-array loop. Queued handles are never
49 * transformed through the shape path. Sort Z and screen bounds live in
50 * the inherited per-slot fields, written by the block via
51 * {@code setSlotScreenState}.
54 public void transform(final TransformStack transforms,
55 final RenderAggregator aggregator,
56 final RenderingContext renderingContext) {
57 // intentionally empty — see TriangleMeshBlock.transform
61 * Publishes this triangle's per-slot screen state (called by the
62 * owning block during its flat transform loop). Trampoline: the
63 * inherited setter is protected, so the call must go through the
66 void publishSlotState(final int slot, final double z,
67 final double minY, final double maxY,
68 final double minX, final double maxX) {
69 setSlotScreenState(slot, z, minY, maxY, minX, maxX);
73 public void paint(final RenderingContext renderBuffer) {
74 final int slot = renderBuffer.vertexSlot;
75 final Point2D[] screen = SCREEN_SCRATCH.get();
76 final Point2D[] uvs = UV_SCRATCH.get();
78 final int clip = block.clipOffset(slot, index);
80 // Near-plane straddler: the block clipped to a loop of 3-4
81 // vertices, stored as (x, y, z, u, v, sx, sy) tuples. Paint
82 // as a fan, mirroring TexturedTriangle.paint's clipped path.
83 final int count = block.clipCount(slot, index);
84 final double[] store = block.clipStore(slot);
85 loadClipVertex(screen[0], uvs[0], store, clip);
86 for (int i = 1; i + 1 < count; i++) {
87 loadClipVertex(screen[1], uvs[1], store, clip + i * 7);
88 loadClipVertex(screen[2], uvs[2], store, clip + (i + 1) * 7);
89 paintFlat(renderBuffer, block.texture(index), block.backfaceCull(),
90 screen[0], screen[1], screen[2],
91 uvs[0], uvs[1], uvs[2],
93 store[clip + i * 7 + 2],
94 store[clip + (i + 1) * 7 + 2],
95 block.clipTtd(slot, clip));
100 block.loadScreenVertex(screen[0], uvs[0], slot, index, 0, renderBuffer);
101 block.loadScreenVertex(screen[1], uvs[1], slot, index, 1, renderBuffer);
102 block.loadScreenVertex(screen[2], uvs[2], slot, index, 2, renderBuffer);
103 paintFlat(renderBuffer, block.texture(index), block.backfaceCull(),
104 screen[0], screen[1], screen[2],
105 uvs[0], uvs[1], uvs[2],
106 block.camZ(slot, index, 0),
107 block.camZ(slot, index, 1),
108 block.camZ(slot, index, 2),
109 block.origTtd(index));
113 * Fills the scratch screen/UV points from one clip-loop entry; screen
114 * coordinates were stored at clip time with the exact
115 * {@code Vertex.setCameraSpaceCoordinate} expression.
117 private void loadClipVertex(final Point2D screen, final Point2D uv,
118 final double[] store, final int entry) {
119 screen.x = store[entry + 5];
120 screen.y = store[entry + 6];
121 uv.x = store[entry + 3];
122 uv.y = store[entry + 4];