From: Svjatoslav Agejenko Date: Tue, 7 Apr 2026 15:56:31 +0000 (+0300) Subject: docs: expand engine documentation and modularize SVG diagrams X-Git-Url: http://www2.svjatoslav.eu/gitweb/?a=commitdiff_plain;h=c31b9ef6e27470d353d6d47c52618ca6b30aede5;p=sixth-3d.git docs: expand engine documentation and modularize SVG diagrams Move all inline SVG diagrams into separate files for better organization, reusability, and easier maintenance. Expand documentation across core geometry classes (Point3D, Vertex), rendering pipeline, lighting system, CSG operations, frustum culling, and texture mapping. - Extract 21 SVG diagrams from org files into standalone .svg files - Add comprehensive Color class docs explaining mutable field design - Document Point3D/Vertex distinction with visual comparison diagram - Expand geometry docs: edges, faces, meshes, normals, winding order - Add documentation index table to AGENTS.md - Update all diagram colors for dark theme compatibility - Rename lerp to interpolate for clearer API naming --- diff --git a/AGENTS.md b/AGENTS.md index 8985173..537e168 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,6 +10,20 @@ sixth-3d-engine is a Java-based 3D rendering engine. It provides: - A text editor component rendered in 3D space - Human input device (HID) tracking for mouse and keyboard +# Documentation + +Extensive documentation is available in Org mode format under `doc/`: + +| Path | Topic | +|----------------------------------------------|-------------------------------------------------------------------------------------------------| +| `doc/index.org` | Main documentation: engine intro, coordinate system, shapes, CSG overview, developer tools | +| `doc/rendering-loop/index.org` | Rendering pipeline: 5 phases (transform→sort→paint→blit), multi-threaded paint, frame listeners | +| `doc/shading/index.org` | Lighting: Lambert cosine law, light sources, ambient light, distance attenuation | +| `doc/csg/index.org` | Boolean operations: subtract/union/intersect via BSP trees, polygon clipping | +| `doc/perspective-correct-textures/index.org` | Tessellation for perspective-correct texture mapping (adaptive subdivision) | +| `doc/frustum-culling/index.org` | View frustum culling: 6 planes, AABB intersection tests, scene design tips | +| `TODO.org` | Project roadmap: planned features, performance improvements, demo ideas | + # Repository Structure src/main/java/eu/svjatoslav/sixth/e3d/ diff --git a/doc/coordinate-system.svg b/doc/coordinate-system.svg new file mode 100644 index 0000000..2a7eccb --- /dev/null +++ b/doc/coordinate-system.svg @@ -0,0 +1,18 @@ + + + + + + X + right (+) / left (-) + + + Y + down (+) / up (-) + + + Z + away (+) / towards (-) + Origin + (0, 0, 0) + \ No newline at end of file diff --git a/doc/csg/bsp-tree.svg b/doc/csg/bsp-tree.svg new file mode 100644 index 0000000..eb89c2c --- /dev/null +++ b/doc/csg/bsp-tree.svg @@ -0,0 +1,45 @@ + + + + + + + + + Plane P₁ + + + + + + + + + front + back + + + + Front (P₂) + + + + Back (P₃) + + + + + + + + + + + leaf + + leaf + + + Each plane divides space into front (normal side) and back (opposite) + Polygons are classified and split at each partitioning plane + diff --git a/doc/csg/csg-intersect.svg b/doc/csg/csg-intersect.svg new file mode 100644 index 0000000..a912f81 --- /dev/null +++ b/doc/csg/csg-intersect.svg @@ -0,0 +1,41 @@ + + + + + + + + + + Input + + A + + B + + + ∩ + intersect + + + + + + + Result: A ∩ B + + + + + + + + + + + + Find overlap + between both + Only shared volume + remains + diff --git a/doc/csg/csg-operations.svg b/doc/csg/csg-operations.svg new file mode 100644 index 0000000..3f73cbe --- /dev/null +++ b/doc/csg/csg-operations.svg @@ -0,0 +1,37 @@ + + + + + + + + + Input + + A + + B + + + − + subtract + + + + + + + Result: A − B + + + + + + cavity + + + B is the "cutter" + carves out of A + Cube with cavity + interior faces visible + diff --git a/doc/csg/csg-union.svg b/doc/csg/csg-union.svg new file mode 100644 index 0000000..f1eedec --- /dev/null +++ b/doc/csg/csg-union.svg @@ -0,0 +1,38 @@ + + + + + + + + Input + + A + + B + + + + + union + + + + + + + Result: A + B + + + + + + removed + + + Keeps all geometry + from both shapes + Single combined volume + interior faces removed + diff --git a/doc/csg/index.org b/doc/csg/index.org index f526950..b128990 100644 --- a/doc/csg/index.org +++ b/doc/csg/index.org @@ -94,45 +94,7 @@ producing the results shown from left to right in the image. :CUSTOM_ID: subtract-operation :END: -#+BEGIN_EXPORT html - - - - - - - - - Input - - A - - B - - - − - subtract - - - - - - - Result: A − B - - - - - - cavity - - - B is the "cutter" - carves out of A - Cube with cavity - interior faces visible - -#+END_EXPORT +#+INCLUDE: "csg-operations.svg" export html *Subtract* removes the orange sphere (B) from the green cube (A), carving out a cavity. The diagram shows B acting as a "cutter" — where it overlaps @@ -161,46 +123,7 @@ cube.subtract(sphere); // cube now has a spherical cavity :CUSTOM_ID: union-operation :END: -#+BEGIN_EXPORT html - - - - - - - - Input - - A - - B - - - + - union - - - - - - - Result: A + B - - - - - - removed - - - Keeps all geometry - from both shapes - Single combined volume - interior faces removed - -#+END_EXPORT +#+INCLUDE: "csg-union.svg" export html *Union* merges the green cube (A) and orange sphere (B) into one continuous volume. The diagram shows both shapes combining — the interior seam (where @@ -222,49 +145,7 @@ cube.union(sphere); // cube now contains the merged result :CUSTOM_ID: intersect-operation :END: -#+BEGIN_EXPORT html - - - - - - - - - - Input - - A - - B - - - ∩ - intersect - - - - - - - Result: A ∩ B - - - - - - - - - - - - Find overlap - between both - Only shared volume - remains - -#+END_EXPORT +#+INCLUDE: "csg-intersect.svg" export html *Intersect* keeps only the volume where the green cube (A) and orange sphere (B) overlap — the region that is inside *both* shapes @@ -303,53 +184,7 @@ and spatial queries. :CUSTOM_ID: bsp-tree-structure :END: -#+BEGIN_EXPORT html - - - - - - - - - Plane P₁ - - - - - - - - - front - back - - - - Front (P₂) - - - - Back (P₃) - - - - - - - - - - - leaf - - leaf - - - Each plane divides space into front (normal side) and back (opposite) - Polygons are classified and split at each partitioning plane - -#+END_EXPORT +#+INCLUDE: "bsp-tree.svg" export html Each BSP node contains: - A *partitioning plane* that divides space into two half-spaces @@ -384,47 +219,7 @@ transform subtraction and intersection into variations of clipping: When a polygon crosses a partitioning plane, it's *split* into two fragments: -#+BEGIN_EXPORT html - - - - - - - - Polygon crosses plane - - - plane - - - - - → - split - - - Split into fragments - - - - - front - - - - back - - - - - - new edge - - - Spanning polygons are split; each fragment goes to its respective subtree - -#+END_EXPORT +#+INCLUDE: "polygon-clipping.svg" export html This recursive splitting ensures that all polygons are cleanly classified as entirely in front, entirely behind, or exactly on a plane — never diff --git a/doc/csg/polygon-clipping.svg b/doc/csg/polygon-clipping.svg new file mode 100644 index 0000000..41de628 --- /dev/null +++ b/doc/csg/polygon-clipping.svg @@ -0,0 +1,39 @@ + + + + + + + + Polygon crosses plane + + + plane + + + + + → + split + + + Split into fragments + + + + + front + + + + back + + + + + + new edge + + + Spanning polygons are split; each fragment goes to its respective subtree + diff --git a/doc/edge.svg b/doc/edge.svg new file mode 100644 index 0000000..3ef023e --- /dev/null +++ b/doc/edge.svg @@ -0,0 +1,12 @@ + + + + + + + + V₁ + V₂ + V₃ + edge + diff --git a/doc/face-triangle.svg b/doc/face-triangle.svg new file mode 100644 index 0000000..8e8eb13 --- /dev/null +++ b/doc/face-triangle.svg @@ -0,0 +1,14 @@ + + + + + + + + + + V₁ + V₂ + V₃ + FACE + diff --git a/doc/frustum-culling/frustum-diagram.svg b/doc/frustum-culling/frustum-diagram.svg new file mode 100644 index 0000000..b59d4a8 --- /dev/null +++ b/doc/frustum-culling/frustum-diagram.svg @@ -0,0 +1,58 @@ + + + + + + + + + +Z + (view direction) + + + + Camera + + + + + + + + + + + + + + Near + + + + Far + + + + + + visible region + + + Top plane + Bottom plane + + + + ✓ + rendered + + + + ✗ + culled + + + + ✗ + culled + diff --git a/doc/frustum-culling/index.org b/doc/frustum-culling/index.org index 5d6fa3c..9bd15aa 100644 --- a/doc/frustum-culling/index.org +++ b/doc/frustum-culling/index.org @@ -49,66 +49,7 @@ :CUSTOM_ID: frustum-view-frustum-culling :END: -#+BEGIN_EXPORT html - - - - - - - - - +Z - (view direction) - - - - Camera - - - - - - - - - - - - - - Near - - - - Far - - - - - - visible region - - - Top plane - Bottom plane - - - - ✓ - rendered - - - - ✗ - culled - - - - ✗ - culled - -#+END_EXPORT +#+INCLUDE: "frustum-diagram.svg" export html The *view frustum* is a truncated pyramid-shaped volume that represents everything the camera can see. Objects completely outside this volume are @@ -178,46 +119,7 @@ during Phase 2 of the [[../rendering-loop/][rendering loop]]: The intersection test uses an optimized "P-vertex" approach: -#+BEGIN_EXPORT html - - - - - - - - P-vertex: corner most aligned with plane normal - If P is behind the plane → entire AABB is outside - - - - Plane - - - inside frustum - - outside frustum - - - - - N - - - - inside - - - P - - - - outside - - - P - -#+END_EXPORT +#+INCLUDE: "p-vertex-aabb.svg" export html For each plane, instead of testing all 8 corners of the bounding box, we test only the *P-vertex* — the corner most aligned with the plane diff --git a/doc/frustum-culling/p-vertex-aabb.svg b/doc/frustum-culling/p-vertex-aabb.svg new file mode 100644 index 0000000..a3acfb8 --- /dev/null +++ b/doc/frustum-culling/p-vertex-aabb.svg @@ -0,0 +1,38 @@ + + + + + + + + P-vertex: corner most aligned with plane normal + If P is behind the plane → entire AABB is outside + + + + Plane + + + inside frustum + + outside frustum + + + + + N + + + + inside + + + P + + + + outside + + + P + diff --git a/doc/index.org b/doc/index.org index 7d8863f..181fc4f 100644 --- a/doc/index.org +++ b/doc/index.org @@ -152,78 +152,25 @@ Also add the repository (the library is not on Maven Central): ** Main render loop -#+BEGIN_EXPORT html - - - - - - - - - - - Shapes - - - Transform - - - Sort - - - Paint - - - Blit - - - Screen - - - - - - - - - - 3D vertices - world→screen - back-to-front - 8 threads - copy buffer - -#+END_EXPORT - -To understand main render loop, see dedicated page: [[file:rendering-loop/][Rendering loop]] +The rendering loop is the heart of the engine, continuously generating +frames at a target rate (typically 60 FPS). Each frame transforms 3D +shapes through a multi-stage pipeline before displaying them on screen. + +#+INCLUDE: "rendering-loop/render-pipeline.svg" export html + +The render loop runs on a dedicated background daemon thread managed by +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/gui/ViewPanel.html][ViewPanel]], which can optionally sleep between frames to maintain a +target FPS or run unlimited for benchmarking. + +For a detailed walkthrough of each phase with diagrams and code +examples, see the dedicated page: [[file:rendering-loop/][Rendering loop]]. ** Coordinate System (X, Y, Z) :PROPERTIES: :CUSTOM_ID: coordinate-system :END: -#+BEGIN_EXPORT html - - - - - - X - right (+) / left (-) - - - Y - down (+) / up (-) - - - Z - away (+) / towards (-) - Origin - (0, 0, 0) - -#+END_EXPORT +#+INCLUDE: "coordinate-system.svg" export html *Sixth 3D* uses a **left-handed coordinate system with X pointing right and Y pointing down**, matching standard 2D screen coordinates. This @@ -244,129 +191,226 @@ graphics background. - To place object A "above" object B, give A a **smaller Y value** than B. +Coordinates in this system are stored using the +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/geometry/Point3D.html][Point3D]] class — a mutable container with public =x=, =y=, =z= fields +supporting vector operations like distance, rotation, and translation. +Vertices (see [[#vertex][below]]) are positioned within this coordinate system. + The [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/#coordinate-system][sixth-3d-demos]] project includes an interactive coordinate system reference showing X, Y, Z axes as colored arrows with a grid plane for spatial context. -** Vertex +** Point3D and Vertex :PROPERTIES: :CUSTOM_ID: vertex :END: -#+BEGIN_EXPORT html - - - - - - - - - - V - (x, y, z) - x - y - -#+END_EXPORT - -A *vertex* is a single point in 3D space, defined by three -coordinates: *x*, *y*, and *z*. Every 3D object is ultimately built -from vertices. A vertex can also carry additional data beyond -position. - -- Position: =(x, y, z)= -- Can also store: color, texture UV, normal vector -- A triangle = 3 vertices, a cube = 8 vertices -- Vertex maps to [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/geometry/Point3D.html][Point3D]] class in *Sixth 3D* engine. +#+INCLUDE: "point3d-vertex.svg" export html + +Every 3D object is built from *vertices* — corner points that define +the shape's geometry. A triangle has 3 vertices, a cube has 8, and +complex meshes have thousands. The engine uses two related classes to +represent points in 3D space, each serving a different purpose. + + + +*** Point3D — Raw Coordinates + +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/geometry/Point3D.html][Point3D]] is the fundamental coordinate type throughout the engine. It +stores a position or vector with three public fields: =x=, =y=, =z=. +The class provides vector math operations: distance calculation, +rotation, translation, scaling, dot/cross products, and interpolation. Methods follow a fluent API convention where mutating +operations (like =add=, =multiply=) return =this= for chaining, while +non-mutating variants (like =withAdded=, =withMultiplied=) return new +instances. + +Use =Point3D= for: +- Storing positions, vectors, or any raw 3D coordinate +- Distance and angle calculations between points +- Vector math (dot product, cross product, normalization) +- Rotating or translating positions before shape construction + +#+BEGIN_SRC java +Point3D p1 = new Point3D(100, 50, 200); +Point3D p2 = new Point3D(0, 0, 100); +double distance = p1.getDistanceTo(p2); // Euclidean distance +Point3D direction = p1.subtract(p2).unit(); // Unit vector from p2 to p1 +Point3D rotated = p1.rotate(new Point3D(0,0,0), Math.PI/4, 0); // Rotate 45° in XZ plane +#+END_SRC + +*** Vertex — Rendering-Ready Coordinates + +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/math/Vertex.html][Vertex]] wraps a =Point3D= and adds the coordinate spaces needed during +rendering. As a shape transforms through the render pipeline, each +vertex tracks its position in multiple spaces: + +| Field | Purpose | +|------------------------+--------------------------------------------------------| +| =coordinate= | Original position in local/model space | +| =transformedCoordinate=| Position relative to camera (after transform stack) | +| =onScreenCoordinate= | 2D screen pixels (after perspective projection) | +| =textureCoordinate= | Optional UV coords in pixel units (not normalized) | +| =normal= | Optional normal vector for CSG polygon splitting | + +During rendering, =calculateLocationRelativeToViewer()= transforms the +vertex through all spaces: first applying the +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/math/TransformStack.html][TransformStack]] to get =transformedCoordinate=, then projecting to 2D +for =onScreenCoordinate=. Results are cached per-frame to avoid +recomputing for vertices shared across multiple shapes. + +Use =Vertex= when: +- Constructing triangles, polygons, or textured shapes +- Your geometry needs texture UV coordinates +- You're performing CSG boolean operations (requires =normal=) + +#+BEGIN_SRC java +// Create a textured triangle (texture coordinates use pixel units) +// For a 256x256 texture: (0,0)=top-left, (256,256)=bottom-right +Vertex v1 = new Vertex(new Point3D(0, 0, 100), new Point2D(0, 0)); +Vertex v2 = new Vertex(new Point3D(100, 0, 100), new Point2D(256, 0)); +Vertex v3 = new Vertex(new Point3D(50, 100, 100), new Point2D(128, 256)); +TexturedTriangle triangle = new TexturedTriangle(v1, v2, v3, texture); +#+END_SRC + +*** When to Use Each + +| Use Point3D | Use Vertex | +|--------------------------------------+-----------------------------------------------| +| Positioning shapes, cameras, lights | Building triangles and polygons | +| Vector math (distances, directions) | Texture-mapped geometry | +| Rotating or translating positions | CSG operations | +| Temporary calculations | Shapes that render through transform pipeline | + +For simple shapes without textures, you can pass raw =Point3D= +coordinates directly to constructors — the shape will internally wrap +them in =Vertex= objects. The [[#coordinate-system][coordinate system]] above defines the +meaning of all =x=, =y=, =z= values in both classes. ** Edge :PROPERTIES: :CUSTOM_ID: edge :END: -#+BEGIN_EXPORT html - - - - - - - - V₁ - V₂ - V₃ - edge - -#+END_EXPORT - -An *edge* is a straight line segment connecting two vertices. Edges -define the wireframe skeleton of a 3D model. In rendering, edges -themselves are rarely drawn — they exist implicitly as boundaries of -faces. - -- Edge = line from V₁ to V₂ -- A triangle has 3 edges -- A cube has 12 edges -- Wireframe mode renders edges visibly -- Edge is related to and can be represented by the [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/Line.html][Line]] class in Sixth - 3D engine. +#+INCLUDE: "edge.svg" export html + +An *edge* is a straight line segment connecting two [[#vertex][vertices]]. Edges +form the wireframe skeleton of a 3D model — the structural framework +visible when surfaces are not rendered. A triangle has 3 edges, a cube +has 12 edges, and complex meshes have thousands. + +In *Sixth 3D*, the [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/Line.html][Line]] class implements edges as renderable shapes. Each +Line connects two [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/math/Vertex.html][Vertex]] endpoints and stores two properties: a +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/Line.html#width][width]] in world units (adjusted for perspective during rendering) and a +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/Line.html#color][color]] with alpha transparency. The rendering algorithm switches +between two modes based on the projected screen width: thin lines below +the threshold are drawn as single pixels with alpha-adjusted coloring, +while thicker lines are rendered as filled rectangles with perspective-correct +edge fading using four [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineInterpolator.html][LineInterpolator]] scanline boundaries. + +Wireframe shapes are composite objects built from multiple Line instances. +For example, [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeBox.html][WireframeBox]] creates 12 Line objects — four edges parallel to +each axis — using a [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/LineAppearance.html][LineAppearance]] factory to ensure consistent styling across +all edges. The [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeCube.html][WireframeCube]] convenience subclass provides a center-point +constructor. See the [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/#shape-gallery][Shape Gallery demo]] for a visual comparison of +wireframe (edges only) versus solid polygon (surfaces with lighting) +rendering modes. ** Face (Triangle) :PROPERTIES: :CUSTOM_ID: face-triangle :END: -#+BEGIN_EXPORT html - - - - - - - - - - V₁ - V₂ - V₃ - FACE - -#+END_EXPORT - -A *face* is a flat surface enclosed by edges. In most 3D engines, the fundamental face is a *triangle* — defined by exactly 3 vertices. Triangles are preferred because they are always planar (flat) and trivially simple to rasterize. - -- Triangle = 3 vertices + 3 edges -- Always guaranteed to be coplanar -- Quads (4 vertices) = 2 triangles -- Complex shapes = many triangles (a "mesh") -- Face maps to [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidTriangle.html][SolidTriangle]], [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.html][SolidPolygon]], or [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedTriangle.html][TexturedTriangle]] in *Sixth 3D*. +#+INCLUDE: "face-triangle.svg" export html + +A *face* is a flat surface enclosed by edges — the visible skin of a 3D +object. While faces can theoretically have any number of sides, 3D +engines standardize on *triangles* because three points always define a +flat plane. A quad (4 vertices) or pentagon (5 vertices) might be +non-planar depending on vertex positions, causing rendering artifacts. +Triangles avoid this problem entirely. + +*** SolidPolygon — Solid-Color Faces + +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.html][SolidPolygon]] is the primary face type, supporting any number of vertices +(3 or more). For triangles, it renders directly via scanline rasterization. +For N-vertex polygons (quads, pentagons, etc.), the engine automatically +triangulates using fan decomposition before rendering — a quad becomes 2 +triangles, a pentagon becomes 3. + +Each SolidPolygon stores a single fill color with optional alpha +transparency. When shading is enabled, the lighting manager computes +the polygon's illumination once during the transform phase, then +applies the shaded color during painting. Backface culling (see +[[#winding-order-backface-culling][Winding Order & Backface Culling]]) can be enabled per-polygon, or +applied recursively to an entire composite shape via +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.html#setBackfaceCulling(boolean)][AbstractCompositeShape.setBackfaceCulling(true)]] — this propagates the +setting to all SolidPolygon and TexturedTriangle sub-shapes, including +nested composites. + +#+BEGIN_SRC java +// Create a red triangle +SolidPolygon triangle = SolidPolygon.triangle( + new Point3D(0, 0, 100), + new Point3D(50, 0, 100), + new Point3D(25, 50, 100), + Color.RED +); + +// Create a blue quad (internally triangulated) +SolidPolygon quad = SolidPolygon.quad( + new Point3D(-50, -50, 100), + new Point3D(50, -50, 100), + new Point3D(50, 50, 100), + new Point3D(-50, 50, 100), + Color.BLUE +); + +// Enable lighting and culling for a closed mesh +quad.setShadingEnabled(true); +quad.setBackfaceCulling(true); +#+END_SRC + +*** TexturedTriangle — UV-Mapped Faces + +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedTriangle.html][TexturedTriangle]] renders faces with image textures mapped via UV +coordinates. Each of the three [[#vertex][vertices]] stores a =textureCoordinate= +(a [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/geometry/Point2D.html][Point2D]] with U and V values in *pixel units* matching the texture +dimensions). For a 256×256 texture, coordinates range from (0,0) at the +top-left corner to (256,256) at the bottom-right. During rasterization, the +engine interpolates these UV coordinates across the triangle's surface, +sampling the texture at each pixel. When mipmaps are used, the [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.html#multiplicationFactor][multiplicationFactor]] +scales coordinates to match the selected mipmap resolution. + +The texture system supports mipmaps — pre-scaled versions of the texture +selected based on the triangle's screen size to reduce aliasing artifacts +on distant surfaces. For large triangles that would show perspective +distortion, the engine can tessellate the triangle into smaller pieces +for more accurate rendering. + +#+BEGIN_SRC java +// Create a 256x256 texture +Texture texture = new Texture(256, 256, 2); // width, height, maxUpscale + +// Create a textured triangle with UV coordinates in pixel units +Vertex v1 = new Vertex(new Point3D(0, 0, 100), new Point2D(0, 0)); // top-left +Vertex v2 = new Vertex(new Point3D(100, 0, 100), new Point2D(256, 0)); // top-right +Vertex v3 = new Vertex(new Point3D(50, 100, 100), new Point2D(128, 256)); // bottom-center + +TexturedTriangle triangle = new TexturedTriangle(v1, v2, v3, texture); +triangle.setBackfaceCulling(true); +#+END_SRC + +Both SolidPolygon and TexturedTriangle extend +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractCoordinateShape.html][AbstractCoordinateShape]], which handles vertex transformation and depth +sorting. See the [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/#shape-gallery][Shape Gallery demo]] for a visual comparison of solid +versus textured polygon rendering. ** Normal Vector :PROPERTIES: :CUSTOM_ID: normal-vector :END: -#+BEGIN_EXPORT html - - - - - - - - - N̂ - unit normal - (perpendicular - to surface) - - - Light - - L · N = brightness - -#+END_EXPORT +#+INCLUDE: "normal-vector.svg" export html A *normal* is a vector perpendicular to a surface. It tells the renderer which direction a face is pointing. Normals are critical for @@ -404,45 +448,50 @@ To understand lighting and shading, read more about [[file:shading/][shading & l :CUSTOM_ID: mesh :END: -#+BEGIN_EXPORT html - - - - - - - - - - - - - - - - - - - triangulated - section - - -#+END_EXPORT - -A *mesh* is a collection of vertices, edges, and faces that together define the shape of a 3D object. Even curved surfaces like spheres are approximated by many small triangles — more triangles means a smoother appearance. - -- Mesh data = vertex array + index array -- Index array avoids duplicating shared vertices -- Cube: 8 vertices, 12 triangles -- Smooth sphere: hundreds–thousands of triangles -- =vertices[] + indices[]= → efficient storage -- In *Sixth 3D* engine: - - [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractCoordinateShape.html][AbstractCoordinateShape]]: base class for single shapes with vertices (triangles, lines). Use when creating one primitive. - - [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.html][AbstractCompositeShape]]: groups multiple shapes into one object. Use for complex models that move/rotate together. - -See the [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/#shape-gallery][Shape Gallery demo]] for a visual showcase of -all primitive shapes available in *Sixth 3D*, rendered in both -wireframe and solid polygon styles with dynamic lighting. +#+INCLUDE: "mesh.svg" export html + +A *mesh* is a collection of vertices, edges, and faces that together +define the shape of a 3D object. Even curved surfaces like spheres are +approximated by many small triangles — more triangles means a smoother +appearance. A cube has 8 vertices forming 12 triangular faces, while a +smooth sphere requires hundreds or thousands of triangles depending on +the desired quality. + +In *Sixth 3D*, meshes are built through composition rather than +monolithic vertex/index buffers. The [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractCoordinateShape.html][AbstractCoordinateShape]] class is +the foundation for primitive shapes — each instance stores its own +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/AbstractCoordinateShape.html#vertices][List<Vertex>]] directly. This includes [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.html][SolidPolygon]] (N-vertex +convex polygons, not limited to triangles), [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedTriangle.html][TexturedTriangle]] +(UV-mapped triangles), and [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/Line.html][Line]] (wireframe edges). The +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.html][AbstractCompositeShape]] class groups multiple shapes into a single +object with its own position, rotation, and transform — useful for +complex models that move or rotate together. + +Complex meshes are constructed procedurally by adding primitive shapes +during initialization. For example, [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonSphere.html][SolidPolygonSphere]] generates +triangles using a latitude-longitude grid: with 16 segments, it +creates approximately 900 SolidPolygon triangles by looping through +rings and sectors, calling [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.html#addShape(eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape)][addShape()]] for each. The generic +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonMesh.html][SolidPolygonMesh]] accepts any list of triangles, allowing custom +geometry from procedural generation or external sources. + +During rendering, several automatic optimizations occur. N-vertex +polygons (quads, pentagons, etc.) are triangulated using fan +triangulation in [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.html#retessellate(eu.svjatoslav.sixth.e3d.gui.RenderingContext)][AbstractCompositeShape.retessellate()]], converting an +N-vertex polygon into N-2 triangles. Textured polygons undergo +level-of-detail tessellation — distant triangles are subdivided into +smaller pieces for perspective-correct texture mapping. Composites +perform view frustum culling to skip rendering when entirely +off-screen. Sub-shapes can be organized into named groups via [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/SubShape.html][SubShape]] +wrappers, allowing [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.html#hideGroup(java.lang.String)][showGroup()]] and [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.html#hideGroup(java.lang.String)][hideGroup()]] to toggle visibility of +entire sections. Composite shapes also support CSG boolean operations +— see the [[file:csg/][Constructive Solid Geometry]] documentation for union, +subtract, and intersect operations. + +The [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/#shape-gallery][Shape Gallery demo]] showcases all primitive shapes available in +*Sixth 3D*, rendered in both wireframe mode (edges only via +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeBox.html][WireframeBox]] and similar) and solid polygon mode (filled surfaces with +dynamic lighting). ** Perspective correct textures @@ -457,66 +506,7 @@ about [[file:perspective-correct-textures/][perspective-correct texture implemen *Sixth 3D* implements view frustum culling. -#+BEGIN_EXPORT html - - - - - - - - - +Z - (view direction) - - - - Camera - - - - - - - - - - - - - - Near - - - - Far - - - - - - visible region - - - Top plane - Bottom plane - - - - ✓ - rendered - - - - ✗ - culled - - - - ✗ - culled - -#+END_EXPORT +#+INCLUDE: "frustum-culling/frustum-diagram.svg" export html To understand frustum culling and object-level visibility optimization, read more about [[file:frustum-culling/][frustum & view frustum culling.]] @@ -526,45 +516,7 @@ optimization, read more about [[file:frustum-culling/][frustum & view frustum cu *Sixth 3D* allows performing boolean operations against geometry shapes. So one can subtract, unionize or intersect shapes. -#+BEGIN_EXPORT html - - - - - - - - - Input - - A - - B - - - − - subtract - - - - - - - Result: A − B - - - - - - cavity - - - B is the "cutter" - carves out of A - Cube with cavity - interior faces visible - -#+END_EXPORT +#+INCLUDE: "csg/csg-operations.svg" export html To understand CSG boolean operations, read more about [[file:csg/][Constructive Solid Geometry]]. @@ -574,42 +526,7 @@ Solid Geometry]]. :CUSTOM_ID: winding-order-backface-culling :END: -#+BEGIN_EXPORT html - - - - - - - - - - - - - - - CCW - - - - V₁ - V₂ - V₃ - FRONT FACE ✓ - - - - - CW - - - BACK FACE ✗ - (culled — not drawn) - -#+END_EXPORT +#+INCLUDE: "winding-order.svg" export html The order in which a triangle's vertices are listed determines its *winding order*. In *Sixth 3D*, screen coordinates have Y-axis pointing @@ -641,23 +558,98 @@ See the [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/#winding-order][Win :ID: f2c9642a-a093-444f-8992-76c97ff28c16 :END: -*Sixth 3D* uses its own Color class (not java.awt.Color): +Sixth 3D uses its own [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html][Color class]] instead of [[https://docs.oracle.com/en/java/javase/21/docs/api/java.desktop/java/awt/Color.html][java.awt.Color]]. This +custom implementation is designed specifically for the engine's +software rasterizer, where avoiding object allocation during rendering +is critical for performance. When rendering thousands of polygons per +frame, creating new Color instances for each one would generate +excessive garbage and trigger frequent garbage collection +pauses. Instead, the engine's Color class uses mutable fields that can +be reused across frames. + +The class stores RGBA components as public integer fields in the range +0–255. This format matches the engine's pixel buffer layout and avoids +costly float-to-int conversions during rasterization. The [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#r][r]], [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#g][g]], [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#b][b]], and +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#a][a]] fields are accessible directly, allowing lighting calculations and +alpha blending to modify colors in-place without allocating new +objects. For example, the [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.html][SolidPolygon]] class maintains a reusable +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.html#shadedColor][shadedColor]] field that gets updated during each frame's lighting +calculation instead of creating a new Color instance per polygon. + +Color provides several constructors for different input formats. The +most common approach is using hex strings via [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#hex(java.lang.String)][Color.hex(String)]] or the +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#%3Cinit%3E(java.lang.String)][String constructor]], which support formats like ="F80"= (3-digit RGB), +="FF8800"= (6-digit RGB), ="F808"= (4-digit RGBA), and ="FF8800CC"= +(8-digit RGBA). You can also create colors from integer RGBA +components (0–255) using [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#%3Cinit%3E(int,int,int,int)][new Color(r, g, b, a)]], from floating-point +components (0.0–1.0) via [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#%3Cinit%3E(double,double,double,double)][new Color(double r, double g, double b, +double a)]], or from a packed RGB integer like =0xFF8800= using [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#%3Cinit%3E(int)][new +Color(int rgb)]]. The class also provides predefined constants for +common colors: [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#RED][Color.RED]], [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#GREEN][Color.GREEN]], [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#BLUE][Color.BLUE]], [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#YELLOW][Color.YELLOW]], +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#CYAN][Color.CYAN]], [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#MAGENTA][Color.MAGENTA]], [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#WHITE][Color.WHITE]], [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#BLACK][Color.BLACK]], and +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#TRANSPARENT][Color.TRANSPARENT]]. + +The [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#set(int,int,int,int)][set(int r, int g, int b, int a)]] method modifies a Color in-place +and returns =this= for method chaining, which is essential for +performance during rendering. For example, the [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/lighting/LightingManager.html][LightingManager]] +calculates lighting contributions from all light sources and stores +the final shaded color directly into a reusable Color instance via +=set()=, avoiding any allocation. The [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#toAwtColor()][toAwtColor()]] method converts a +Sixth 3D Color to a java.awt.Color when needed for Java2D graphics +operations, caching the result to avoid repeated conversion. The +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#toInt()][toInt()]] method packs the color into an ARGB integer suitable for the +engine's pixel buffer, used during rasterization to write pixels +directly. #+BEGIN_SRC java import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex; -// Using predefined colors +// Using predefined color constants Color red = Color.RED; -Color green = Color.GREEN; -Color blue = Color.BLUE; +Color transparent = Color.TRANSPARENT; -// Create custom color (R, G, B, A) -Color custom = new Color(255, 128, 64, 200); // semi-transparent orange +// Create from hex string (recommended for clarity) +Color orange = hex("FF8800"); // RGB, fully opaque +Color semiTransparent = hex("FF880080"); // RGBA, 50% transparent -// Or use hex string -Color hex = new Color("FF8040CC"); // same orange with alpha +// Create from integer components (0-255) +Color custom = new Color(255, 128, 64, 200); + +// Create from packed RGB integer +Color packed = new Color(0xFF8800); + +// Modify existing color in-place (no allocation) +Color reusable = new Color(); +reusable.set(100, 200, 50, 255); + +// Convert to AWT color for Java2D operations +java.awt.Color awtColor = custom.toAwtColor(); + +// Use in lighting calculations (LightingManager modifies in-place) +// See the Shading & Lighting documentation for details #+END_SRC +The alpha component controls transparency during rendering. A value of +0 makes the color fully transparent, while 255 makes it fully +opaque. The rasterizer implements alpha blending during the paint +phase: when drawing a semi-transparent pixel, the engine blends the +source color with the existing background pixel proportionally based +on the alpha value. The [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/texture/TextureBitmap.html#drawPixel(int,int\[\],int)][TextureBitmap.drawPixel()]] method handles this +blending, multiplying source colors by alpha and background colors by +=(255 - alpha)=, then combining them. You can test whether a color is +fully transparent using [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/Color.html#isTransparent()][isTransparent()]], which returns true when alpha +equals zero. + +For lighting calculations, the [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/lighting/LightSource.html][LightSource]] class uses Color to +represent the color and intensity of emitted light. Multiple light +sources contribute to the final shaded color of each polygon, as +described in the [[file:shading/index.org::#shading-lighting][Shading & Lighting]] documentation. The [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/lighting/LightingManager.html#setAmbientLight(eu.svjatoslav.sixth.e3d.renderer.raster.Color)][ambient light]] +provides base illumination that affects all surfaces equally, +regardless of orientation. Colors are also used for wireframe +rendering via the [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/line/Line.html][Line]] class, where the color field determines the +line's appearance. + * Developer tools :PROPERTIES: :CUSTOM_ID: developer-tools diff --git a/doc/mesh.svg b/doc/mesh.svg new file mode 100644 index 0000000..fcca520 --- /dev/null +++ b/doc/mesh.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + triangulated + section + + diff --git a/doc/normal-vector.svg b/doc/normal-vector.svg new file mode 100644 index 0000000..ce8aaa1 --- /dev/null +++ b/doc/normal-vector.svg @@ -0,0 +1,18 @@ + + + + + + + + + N̂ + unit normal + (perpendicular + to surface) + + + Light + + L · N = brightness + diff --git a/doc/perspective-correct-textures/index.org b/doc/perspective-correct-textures/index.org index 3be7399..a2787ef 100644 --- a/doc/perspective-correct-textures/index.org +++ b/doc/perspective-correct-textures/index.org @@ -95,95 +95,7 @@ void tessellate(TexturedTriangle polygon) { } #+END_SRC -#+BEGIN_EXPORT html - - - - - - - - - - 1. Original - - - - - A - B - C - - - - longest edge - - - - - - 2. Split - - - - - - - - - - - - - M - midpoint - - - - - - - - - 3. Recurse - - - - - - - - - - - - - - - - - - - - - - - Each split halves the longest edge at its midpoint. - Recursion stops when all edges < maxDistance. - - - - midpoint (3D + UV averaged) - -#+END_EXPORT +#+INCLUDE: "triangle-tessellation.svg" export html The midpoint is computed by averaging both 3D coordinates *and* texture coordinates. diff --git a/doc/perspective-correct-textures/triangle-tessellation.svg b/doc/perspective-correct-textures/triangle-tessellation.svg new file mode 100644 index 0000000..078638c --- /dev/null +++ b/doc/perspective-correct-textures/triangle-tessellation.svg @@ -0,0 +1,87 @@ + + + + + + + + + + 1. Original + + + + + A + B + C + + + + longest edge + + + + + + 2. Split + + + + + + + + + + + + + M + midpoint + + + + + + + + + 3. Recurse + + + + + + + + + + + + + + + + + + + + + + + Each split halves the longest edge at its midpoint. + Recursion stops when all edges < maxDistance. + + + + midpoint (3D + UV averaged) + diff --git a/doc/point3d-vertex.svg b/doc/point3d-vertex.svg new file mode 100644 index 0000000..a3e0d16 --- /dev/null +++ b/doc/point3d-vertex.svg @@ -0,0 +1,105 @@ + + + + + + + +Point3D +raw coordinates + + + + + + +(x, y, z) + + + + + +.getDistanceTo() + + + + +.rotate() + + + + +.add() +.subtract() + + + + +.crossProduct() + + + + +.unit() +.dot() + + + + +.multiply() + + +Mutable, fluent API +Positions, vectors, math + + +Vertex +rendering-ready wrapper + + + + + + + +coordinate +Point3D + + + +wraps + + + +transformedCoordinate + + + +onScreenCoordinate + + + +textureCoordinate +UV + + + +normal +for CSG + + +local +camera +space +2D +pixels + + + +local +screen + + + +Tracks position across coordinate spaces + diff --git a/doc/rendering-loop/double-buffering.svg b/doc/rendering-loop/double-buffering.svg new file mode 100644 index 0000000..141dad6 --- /dev/null +++ b/doc/rendering-loop/double-buffering.svg @@ -0,0 +1,47 @@ + + + + + Without double-buffering + + + display shows partial update + + + + old frame + + + + ← tear + + + + new frame + + + With double-buffering + + + + Back buffer + (draw here) + + + + + + + + + swap + + + + Front buffer + (displayed) + + + complete + frame + diff --git a/doc/rendering-loop/index.org b/doc/rendering-loop/index.org index 2a06c19..1da5809 100644 --- a/doc/rendering-loop/index.org +++ b/doc/rendering-loop/index.org @@ -49,50 +49,7 @@ can be displayed on screen. The process transforms shapes through multiple coordinate systems: -#+BEGIN_EXPORT html - - - - - - - - - - - Shapes - - - Transform - - - Sort - - - Paint - - - Blit - - - Screen - - - - - - - - - - 3D vertices - world→screen - back-to-front - 8 threads - copy buffer - -#+END_EXPORT +#+INCLUDE: "render-pipeline.svg" export html Each step has a specific purpose: @@ -203,21 +160,7 @@ This implements the *painter's algorithm* — like painting a landscape: first paint the sky (farthest), then mountains, then trees, then the foreground. Each layer covers what's behind it. -#+BEGIN_EXPORT html - - - - - - Far (Z=500) — painted first - - - Medium (Z=300) — painted second - - - Near (Z=100) — painted last - -#+END_EXPORT +#+INCLUDE: "painters-algorithm.svg" export html Without sorting, nearby objects might be painted first and then covered by distant ones, causing visual errors. This is especially important for @@ -239,27 +182,7 @@ The screen is divided into 8 horizontal segments, each processed by a separate t Both operations happen within the same thread task, ensuring clearing completes before painting begins. This provides greater RAM bandwidth utilization compared to single-threaded clearing. -#+BEGIN_EXPORT html - - - - - - - - - - - Segment 0 (Thread 0) - Segment 1 (Thread 1) - Segment 2 (Thread 2) - Segment 3 (Thread 3) - Segment 4 (Thread 4) - Segment 5 (Thread 5) - Segment 6 (Thread 6) - Segment 7 (Thread 7) - -#+END_EXPORT +#+INCLUDE: "paint-segments.svg" export html Each thread: - Gets a =SegmentRenderingContext= with Y-bounds (minY, maxY) @@ -321,55 +244,7 @@ Without double-buffering, the screen updates while pixels are being written. This causes *screen tearing* — visible horizontal splits where the top of the frame shows old content while the bottom shows new. -#+BEGIN_EXPORT html - - - - - Without double-buffering - - - display shows partial update - - - - old frame - - - - ← tear - - - - new frame - - - With double-buffering - - - - Back buffer - (draw here) - - - - - - - - - swap - - - - Front buffer - (displayed) - - - complete - frame - -#+END_EXPORT +#+INCLUDE: "double-buffering.svg" export html Double-buffering uses two pixel buffers: - *Back buffer*: Where rendering happens (offscreen, invisible) diff --git a/doc/rendering-loop/paint-segments.svg b/doc/rendering-loop/paint-segments.svg new file mode 100644 index 0000000..67d5f1d --- /dev/null +++ b/doc/rendering-loop/paint-segments.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + Segment 0 (Thread 0) + Segment 1 (Thread 1) + Segment 2 (Thread 2) + Segment 3 (Thread 3) + Segment 4 (Thread 4) + Segment 5 (Thread 5) + Segment 6 (Thread 6) + Segment 7 (Thread 7) + diff --git a/doc/rendering-loop/painters-algorithm.svg b/doc/rendering-loop/painters-algorithm.svg new file mode 100644 index 0000000..7727fc2 --- /dev/null +++ b/doc/rendering-loop/painters-algorithm.svg @@ -0,0 +1,13 @@ + + + + + + Far (Z=500) — painted first + + + Medium (Z=300) — painted second + + + Near (Z=100) — painted last + diff --git a/doc/rendering-loop/render-pipeline.svg b/doc/rendering-loop/render-pipeline.svg new file mode 100644 index 0000000..e0bacc0 --- /dev/null +++ b/doc/rendering-loop/render-pipeline.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + Shapes + + + Transform + + + Sort + + + Paint + + + Blit + + + Screen + + + + + + + + + + 3D vertices + world→screen + back-to-front + 8 threads + copy buffer + diff --git a/doc/shading/ambient-light-comparison.svg b/doc/shading/ambient-light-comparison.svg new file mode 100644 index 0000000..9e2f058 --- /dev/null +++ b/doc/shading/ambient-light-comparison.svg @@ -0,0 +1,21 @@ + + + + + + no ambient + (pure black) + + + → + + + + ambient + + + + ambient provides base illumination + diff --git a/doc/shading/distance-attenuation.svg b/doc/shading/distance-attenuation.svg new file mode 100644 index 0000000..a50c8df --- /dev/null +++ b/doc/shading/distance-attenuation.svg @@ -0,0 +1,35 @@ + + + + + + Light + + + + d = 100 + d = 250 + + + + bright + + + + medium + + + + dim + + + + attenuation = + 1 / (1 + 0.0001·d²) + + + Simplified inverse square law avoids harsh cutoffs + diff --git a/doc/shading/index.org b/doc/shading/index.org index 62f6d62..3f9bab6 100644 --- a/doc/shading/index.org +++ b/doc/shading/index.org @@ -63,51 +63,7 @@ light sources. This is a simple yet effective lighting model that gives :CUSTOM_ID: lambert-cosine-law :END: -#+BEGIN_EXPORT html - - - - - - - - - - - - - - - N̂ - normal - - - - Light - - - - L̂ - - - - θ - - - - brightness = - dot(N̂, L̂) - = cos(θ) - - - - - brightness - ← angle determines intensity - -#+END_EXPORT +#+INCLUDE: "lambert-cosine-law.svg" export html The *Lambert cosine law* states that the brightness of a surface depends on the angle between its normal vector and the light direction: @@ -162,29 +118,7 @@ by two lights from the right. :CUSTOM_ID: ambient-light :END: -#+BEGIN_EXPORT html - - - - - - no ambient - (pure black) - - - → - - - - ambient - - - - ambient provides base illumination - -#+END_EXPORT +#+INCLUDE: "ambient-light-comparison.svg" export html *Ambient light* provides base illumination that affects all surfaces equally, regardless of orientation. Without ambient light, surfaces not @@ -208,43 +142,7 @@ viewPanel.getLightingManager().setAmbientLight(new Color(20, 20, 20)); :CUSTOM_ID: distance-attenuation :END: -#+BEGIN_EXPORT html - - - - - - Light - - - - d = 100 - d = 250 - - - - bright - - - - medium - - - - dim - - - - attenuation = - 1 / (1 + 0.0001·d²) - - - Simplified inverse square law avoids harsh cutoffs - -#+END_EXPORT +#+INCLUDE: "distance-attenuation.svg" export html Light intensity decreases with distance using a *simplified inverse square law*: @@ -267,43 +165,7 @@ scene scales in Sixth 3D. :CUSTOM_ID: render-pipeline-integration :END: -#+BEGIN_EXPORT html - - - - - - - - - - - Transform - compute lighting - - - - Shapes - - - Sort - - - Paint - use cached color - - - Blit - - - - - - - - -#+END_EXPORT +#+INCLUDE: "shading-pipeline.svg" export html Lighting is computed during *Phase 2* (transform phase) of the [[file:../rendering-loop/][rendering loop]]: diff --git a/doc/shading/lambert-cosine-law.svg b/doc/shading/lambert-cosine-law.svg new file mode 100644 index 0000000..39d2a2e --- /dev/null +++ b/doc/shading/lambert-cosine-law.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + N̂ + normal + + + + Light + + + + L̂ + + + + θ + + + + brightness = + dot(N̂, L̂) + = cos(θ) + + + + + brightness + ← angle determines intensity + diff --git a/doc/shading/shading-pipeline.svg b/doc/shading/shading-pipeline.svg new file mode 100644 index 0000000..a58c431 --- /dev/null +++ b/doc/shading/shading-pipeline.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + Transform + compute lighting + + + + Shapes + + + Sort + + + Paint + use cached color + + + Blit + + + + + + + + diff --git a/doc/winding-order.svg b/doc/winding-order.svg new file mode 100644 index 0000000..9340194 --- /dev/null +++ b/doc/winding-order.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + CCW + + + + V₁ + V₂ + V₃ + FRONT FACE ✓ + + + + + CW + + + BACK FACE ✗ + (culled — not drawn) + diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point3D.java b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point3D.java index 1f51c1c..84e2838 100755 --- a/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point3D.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point3D.java @@ -551,7 +551,7 @@ public class Point3D implements Cloneable { * @param t the interpolation parameter (0 to 1) * @return a new Point3D representing the interpolated position */ - public Point3D lerp(final Point3D other, final double t) { + public Point3D interpolate(final Point3D other, final double t) { return new Point3D( x + (other.x - x) * t, y + (other.y - y) * t, diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/math/Vertex.java b/src/main/java/eu/svjatoslav/sixth/e3d/math/Vertex.java index f1eed76..8f83a50 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/math/Vertex.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/math/Vertex.java @@ -169,7 +169,7 @@ public class Vertex { */ public Vertex interpolate(final Vertex other, final double t) { final Vertex result = new Vertex( - coordinate.lerp(other.coordinate, t), + coordinate.interpolate(other.coordinate, t), (textureCoordinate != null && other.textureCoordinate != null) ? new Point2D( textureCoordinate.x + (other.textureCoordinate.x - textureCoordinate.x) * t, @@ -177,7 +177,7 @@ public class Vertex { : null ); if (normal != null && other.normal != null) { - result.normal = normal.lerp(other.normal, t); + result.normal = normal.interpolate(other.normal, t); } return result; }