From: Svjatoslav Agejenko Date: Sat, 4 Apr 2026 20:49:52 +0000 (+0300) Subject: feat(terrain): add animated fractal tree with hierarchical transforms X-Git-Url: http://www2.svjatoslav.eu/gitweb/?a=commitdiff_plain;h=c7ab92c750ec7dfd36684803a47a9a6bea9733e2;p=sixth-3d-demos.git feat(terrain): add animated fractal tree with hierarchical transforms Implement recursive branching structure using AbstractCompositeShape with Transform inheritance. Each branch has its own transform (position + rotation) and recursively creates 3 sub-branches at 120-degree spacing, up to 5 levels deep. Branches animate with sine-wave wobble using beforeTransformHook, where parent motion cascades to children via TransformStack. Add FrameListener to TerrainDemo for continuous animation updates. Make galaxy generation deterministic with fixed rotation angles for reproducible demo screenshots. Reorganize demo launcher with improved grouping. Expand documentation with detailed descriptions for point cloud galaxy, raining numbers, procedural terrain, and benchmark demos. Add responsive image attributes and new screenshots. --- diff --git a/doc/Screenshots/Mathematical formulas.png b/doc/Screenshots/Mathematical formulas.png index fab8469..86e0be0 100644 Binary files a/doc/Screenshots/Mathematical formulas.png and b/doc/Screenshots/Mathematical formulas.png differ diff --git a/doc/Screenshots/Point cloud galaxy.png b/doc/Screenshots/Point cloud galaxy.png new file mode 100644 index 0000000..b2c1d67 Binary files /dev/null and b/doc/Screenshots/Point cloud galaxy.png differ diff --git a/doc/Screenshots/Procedural terrain.png b/doc/Screenshots/Procedural terrain.png new file mode 100644 index 0000000..82a484e Binary files /dev/null and b/doc/Screenshots/Procedural terrain.png differ diff --git a/doc/Screenshots/Raining numbers.png b/doc/Screenshots/Raining numbers.png new file mode 100644 index 0000000..ab29d4a Binary files /dev/null and b/doc/Screenshots/Raining numbers.png differ diff --git a/doc/Screenshots/Raytracing fractal in voxel polygon hybrid scene.png b/doc/Screenshots/Raytracing fractal in voxel polygon hybrid scene.png index 7094240..a329f14 100644 Binary files a/doc/Screenshots/Raytracing fractal in voxel polygon hybrid scene.png and b/doc/Screenshots/Raytracing fractal in voxel polygon hybrid scene.png differ diff --git a/doc/index.org b/doc/index.org index 4e30782..4b6b8cc 100644 --- a/doc/index.org +++ b/doc/index.org @@ -107,7 +107,6 @@ Add *Sixth 3D* to your pom.xml: #+END_SRC - *** Create Your First 3D Scene :PROPERTIES: :CUSTOM_ID: create-your-first-3d-scene @@ -351,6 +350,8 @@ devised by the British mathematician John Horton Conway in 1970. otherwise it dies. + Dead cell becomes alive if neighbors count is exactly 3. +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px [[file:Screenshots/Life.png]] This demo projects the 2D game grid onto three-dimensional space. The extra @@ -373,6 +374,8 @@ Usage: : eu.svjatoslav.sixth.e3d.examples.TextEditorDemo +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px [[file:Screenshots/Text editors.png]] Initial test for creating user interfaces in 3D, demonstrating: @@ -414,6 +417,8 @@ unfocused first by pressing the ESC key. *Quite a lot of text editors can be rendered:* +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px [[file:Screenshots/Text editors 2.png]] See also this [[https://hackers-1995.vercel.app/][similar-looking web-based demo]]! @@ -426,6 +431,8 @@ See also this [[https://hackers-1995.vercel.app/][similar-looking web-based demo : eu.svjatoslav.sixth.e3d.examples.graph_demo.MathGraphsDemo +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px [[file:Screenshots/Mathematical formulas.png]] + TODO: instead of projecting 2D visualizations onto 3D space, @@ -439,6 +446,8 @@ See also this [[https://hackers-1995.vercel.app/][similar-looking web-based demo : eu.svjatoslav.sixth.e3d.examples.SineHeightmap +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px [[file:Screenshots/Sine heightmap and sphere.png]] Simple test scene. Easy to implement and looks nice. @@ -451,6 +460,8 @@ Simple test scene. Easy to implement and looks nice. : eu.svjatoslav.sixth.e3d.examples.OctreeDemo +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px [[file:Screenshots/Raytracing fractal in voxel polygon hybrid scene.png]] Test scene that is generated simultaneously using: @@ -464,6 +475,149 @@ partitioned [[https://en.wikipedia.org/wiki/Octree][octree]] compresses the data the scene to raytrace the current view through the compressed voxel data structure. + +** Point cloud galaxy +:PROPERTIES: +:CUSTOM_ID: point-cloud-galaxy +:ID: f1a2b3c4-d5e6-7890-abcd-ef1234567890 +:END: + +: eu.svjatoslav.sixth.e3d.examples.galaxy_demo.PointCloudDemo + +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px +[[file:Screenshots/Point cloud galaxy.png]] + +Spiral galaxy simulation composed of 10,000 glowing points rendered as +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/GlowingPoint.html][GlowingPoint]] primitives. The galaxy features: + +| Parameter | Value | Description | +|---------------+-------+---------------------------------------| +| Stars | 10000 | Total number of glowing points | +| Spiral arms | 3 | Number of rotationally symmetric arms | +| Galaxy size | 500 | Scale factor determining radius | +| Unique colors | 30 | Palette size for texture reuse | + +*Optimization technique:* + +Colors are drawn from a pre-computed palette of 30 semi-bright colors +(RGB values 0.5-1.5 with alpha 255). This enables texture caching - all +stars of the same color share the same glow texture, reducing memory +usage and improving rendering performance. + +This demo showcases the engine's ability to efficiently render large +numbers of billboard-style particles with per-point coloring. + + +** Raining numbers +:PROPERTIES: +:CUSTOM_ID: raining-numbers +:ID: f2b3c4d5-e6f7-8901-bcde-f12345678901 +:END: + +: eu.svjatoslav.sixth.e3d.examples.RainingNumbersDemo + +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px +[[file:Screenshots/Raining numbers.png]] + +Animated "digital rain" effect. Creates 1000 randomly positioned +[[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/textcanvas/TextCanvas.html][TextCanvas]] objects displaying digits 0-9 that continuously fall +through 3D space. + +*Animation mechanics:* + +The demo implements the [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/gui/FrameListener.html][FrameListener]] interface to update positions each frame: + +1. All text canvases translate downward by =millisecondsSinceLastFrame / 50= units +2. Numbers falling below Y = +300 wrap around to Y = -300 +3. Creates an infinite, seamless animation loop +4. Frame-rate independent movement via delta-time calculation + +*Visual characteristics:* + +Each digit is rendered with: +- Random position within a 600×600×600 cube +- Random RGBA color (fully opaque to semi-transparent) +- Random digit value (0-9) + +This demo demonstrates: +- Real-time animation using frame listeners +- Efficient handling of many dynamic text objects +- World wrapping for infinite effects +- Per-instance color randomization + + +** Procedural terrain +:PROPERTIES: +:CUSTOM_ID: procedural-terrain +:ID: a8c9e1f3-4b2d-5e6f-7890-123456789abc +:END: + +: eu.svjatoslav.sixth.e3d.examples.terrain_demo.TerrainDemo + +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px +[[file:Procedural terrain.png]] + +Demonstrates procedurally generated mountain terrain with an animated +fractal tree. The scene features: + +| Feature | Description | +|---------------------+--------------------------------------------------------| +| Terrain generation | Diamond-square algorithm with 129×129 heightmap | +| Height range | 0-100 units with natural mountain variation | +| Fractal tree | Recursive branching with cylinders | +| Dynamic lighting | Three colored light sources (warm, cool, neutral) | +| Real-time animation | Tree branches sway gently with cascading wobble motion | + +*Terrain generation:* + +The terrain uses the [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/math/DiamondSquare.html][DiamondSquare]] algorithm to generate a heightmap +with realistic mountain-like features. + +*Fractal tree:* + +The tree at the terrain center demonstrates hierarchical transforms in Sixth 3D: + +- *Trunk:* Single vertical cylinder +- *Level 0:* 3 branches extending from trunk top (120° apart) +- *Level 1:* 9 sub-branches (3 per level-0 branch) +- *Level 2:* 27 fine branches (3 per level-1 branch) +- ... + + +Each branch is an [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/base/AbstractCompositeShape.html][AbstractCompositeShape]] containing: +- SolidPolygonCylinder for the branch geometry +- Child branches positioned at the tip (recursive structure) + +*Animation system:* + +Branches sway using the [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/eu/svjatoslav/sixth/e3d/gui/FrameListener.html][FrameListener]] interface: + +1. Each branch has unique random phase offsets for X and Z axes +2. Wobble calculated as: sin(frame × frequency + phase) × amplitude +3. Parent rotation automatically propagates to children via TransformStack +4. Creates cascading motion where sub-branches inherit parent wobble + add their own + +*Key classes:* + +- [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/eu/svjatoslav/sixth/e3d/examples/terrain_demo/TerrainDemo.html][TerrainDemo]] - Main entry point and scene setup +- [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/eu/svjatoslav/sixth/e3d/examples/terrain_demo/DiamondSquareTerrain.html][DiamondSquareTerrain]] - Procedural heightmap mesh generation +- [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/eu/svjatoslav/sixth/e3d/examples/terrain_demo/FractalTree.html][FractalTree]] - Tree trunk with recursive branch hierarchy +- [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/eu/svjatoslav/sixth/e3d/examples/terrain_demo/Branch.html][Branch]] - Individual branch with animation and child branches + +*Usage examples:* + +This demo showcases techniques useful for: +- Open world terrain generation +- Procedural vegetation (trees, plants) +- Hierarchical animation (character rigging, mechanical systems) + +*See also:* + +- [[https://en.wikipedia.org/wiki/Diamond-square_algorithm][Diamond-square algorithm]] on Wikipedia + * Graphics Benchmark :PROPERTIES: :CUSTOM_ID: graphics-benchmark @@ -472,22 +626,81 @@ data structure. : eu.svjatoslav.sixth.e3d.examples.benchmark.GraphicsBenchmark -An automated graphics benchmark that measures the engine's rendering -performance across different rendering modes. - [[file:Screenshots/Benchmark.png]] -The benchmark cycles through scenes that utilize different rendering -primitives (textured polygons, billboards, solid polygons, etc.) to measure -their relative performance. - -The camera follows a deterministic orbital path around the scene, -ensuring reproducible results across runs. +Automated graphics benchmark that measures the engine's rendering +performance across five different rendering modes. Runs tests +sequentially and outputs a detailed report with copy-to-clipboard +functionality. + +*Benchmark configuration:* + +| Parameter | Value | Description | +|---------------+------------+---------------------------------| +| Resolution | 1920×1080 | Fixed window size | +| Grid size | 16×16×16 | Number of objects (4,096 total) | +| Test duration | 30 seconds | Per-test measurement time | + +*Benchmark tests:* + +| # | Test | What it measures | +|---+-------------------+-----------------------------------------------------------------| +| 1 | Solid Cubes | Semi-transparent solid polygon rasterization (alpha = 40) | +| 2 | Lit Solid Cubes | Opaque polygons with dynamic lighting and per-pixel shading | +| 3 | Textured Cubes | Texture mapping and sampling with 20 unique glow textures | +| 4 | Wireframe Cubes | Line rendering with 5.5 unit stroke width | +| 5 | Star Grid | Billboard (GlowingPoint) rendering with texture blending | + +*Test details:* + +1. **Solid Cubes** ([[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/eu/svjatoslav/sixth/e3d/examples/benchmark/SolidCubesTest.html][SolidCubesTest]]) + - Uses [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/solid/SolidPolygonCube.html][SolidPolygonCube]] with random semi-transparent colors + - Alpha channel set to 40 for blending overhead measurement + +2. **Lit Solid Cubes** ([[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/eu/svjatoslav/sixth/e3d/examples/benchmark/LitSolidCubesTest.html][LitSolidCubesTest]]) + - Fully opaque cubes with shading enabled + - Three orbiting light sources (red, green, blue) + - Ambient light: Color(15, 15, 20) + - Lights follow elliptical paths at 600 unit radius + - Tests dynamic lighting calculations and shadow computation + +3. **Textured Cubes** ([[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/eu/svjatoslav/sixth/e3d/examples/benchmark/TexturedCubesTest.html][TexturedCubesTest]]) + - Uses custom [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/eu/svjatoslav/sixth/e3d/examples/benchmark/TexturedCube.html][TexturedCube]] class + - 20 procedurally generated glow textures (64×64 pixels) + - Tests texture memory usage and sampling performance + - Each cube assigned random texture from pool + +4. **Wireframe Cubes** ([[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/eu/svjatoslav/sixth/e3d/examples/benchmark/WireframeCubesTest.html][WireframeCubesTest]]) + - Uses [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/composite/wireframe/WireframeBox.html][WireframeBox]] with custom LineAppearance + - Line width: 5.5 units + - Semi-transparent colors (alpha = 50) + - Tests line rasterization and anti-aliasing + +5. **Star Grid** ([[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/eu/svjatoslav/sixth/e3d/examples/benchmark/StarGridTest.html][StarGridTest]]) + - Uses [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/GlowingPoint.html][GlowingPoint]] billboards + - Star size: 20 units + - 30 unique colors in shared palette (texture reuse optimization) + - Tests billboard rendering and texture blending efficiency + +*Usage:* + +1. Launch from demo launcher or run main class directly +2. Intro dialog appears with instructions +3. Click "OK" to begin benchmark +4. Press *Space* to skip current test (reduces measurement precision) +5. Results dialog appears after all tests complete +6. Click "Copy to Clipboard" to save results for comparison + +*Camera motion:* + +The camera follows a deterministic orbital path ensuring reproducible +results: +- Circular orbit at 1,200 unit radius around scene center (0,0,0) +- Sinusoidal vertical wobble (±800 units) at 1.8934× orbit frequency +- Continuous rotation + +*Example benchmark report:* -Upon completion, the benchmark outputs a report suitable for preservation -and later comparisons. - -Example benchmark report: #+begin_example ================================================================================ GRAPHICS BENCHMARK RESULTS @@ -515,6 +728,10 @@ Star Grid 318.97 ================================================================================ #+end_example +The benchmark captures CPU name, architecture, and core count for +cross-system comparisons. Results are sorted by test execution order +and show average FPS with two decimal precision. + * Source code :PROPERTIES: :CUSTOM_ID: source-code diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/Galaxy.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/Galaxy.java index a632268..2cecb84 100755 --- a/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/Galaxy.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/Galaxy.java @@ -1,5 +1,5 @@ /* - * Sixth 3D engine. Author: Svjatoslav Agejenko. + * Sixth 3D engine. Author: Svjatoslav Agejenko. * This project is released under Creative Commons Zero (CC0) license. */ package eu.svjatoslav.sixth.e3d.examples.galaxy_demo; @@ -38,10 +38,11 @@ public class Galaxy extends AbstractCompositeShape { /** * Constructs a galaxy with the specified parameters. + * * @param galaxySize the overall size scale of the galaxy - * @param tailCount the number of spiral arms + * @param tailCount the number of spiral arms * @param starsCount the total number of stars to generate - * @param transform the position and orientation of the galaxy + * @param transform the position and orientation of the galaxy */ public Galaxy(final int galaxySize, final int tailCount, final int starsCount, Transform transform) { @@ -50,8 +51,8 @@ public class Galaxy extends AbstractCompositeShape { ensureColorsAreInitialized(); - final double angle1 = random() * 10; - final double angle2 = random() * 10; + final double angle1 = 1.534; + final double angle2 = 2.124; final double angleSin1 = sin(angle1); final double angleCos1 = cos(angle1); @@ -90,8 +91,9 @@ public class Galaxy extends AbstractCompositeShape { /** * Adds a single star at the specified location. + * * @param starLocation the position of the star - * @param size the visual size of the star + * @param size the visual size of the star */ private void addStar(final Point3D starLocation, double size) { addShape(new GlowingPoint(starLocation, size, colors.get((int) (random() * colors.size())))); diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/PointCloudDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/PointCloudDemo.java index b382ad1..c1838ca 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/PointCloudDemo.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/PointCloudDemo.java @@ -1,12 +1,11 @@ /* - * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. * This project is released under Creative Commons Zero (CC0) license. * -*/ + */ package eu.svjatoslav.sixth.e3d.examples.galaxy_demo; -import eu.svjatoslav.sixth.e3d.geometry.Point3D; import eu.svjatoslav.sixth.e3d.gui.ViewFrame; import eu.svjatoslav.sixth.e3d.math.Transform; import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; @@ -25,13 +24,14 @@ public class PointCloudDemo { /** * Entry point for the point cloud demo. + * * @param args command line arguments (ignored) */ public static void main(final String[] args) { final ViewFrame viewFrame = new ViewFrame("Point Cloud Galaxy"); - viewFrame.getViewPanel().getCamera().getTransform().set(-1099.85, -2862.44, 144.32, -1.09, -0.60, 0); + viewFrame.getViewPanel().getCamera().getTransform().set(-1140.96, -667.54, -1448.15, -0.57, -0.04, -0.00); final ShapeCollection geometryCollection = viewFrame.getViewPanel() .getRootShapeCollection(); diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/ApplicationListPanel.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/ApplicationListPanel.java index 550e60d..86ed1da 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/ApplicationListPanel.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/ApplicationListPanel.java @@ -6,17 +6,9 @@ package eu.svjatoslav.sixth.e3d.examples.launcher; -import eu.svjatoslav.sixth.e3d.examples.OctreeDemo; -import eu.svjatoslav.sixth.e3d.examples.RainingNumbersDemo; -import eu.svjatoslav.sixth.e3d.examples.SineHeightmap; -import eu.svjatoslav.sixth.e3d.examples.TextEditorDemo; -import eu.svjatoslav.sixth.e3d.examples.TextEditorDemo2; -import eu.svjatoslav.sixth.e3d.examples.essentials.CoordinateSystemDemo; -import eu.svjatoslav.sixth.e3d.examples.essentials.CSGDemo; -import eu.svjatoslav.sixth.e3d.examples.essentials.MinimalExample; -import eu.svjatoslav.sixth.e3d.examples.essentials.ShapeGalleryDemo; -import eu.svjatoslav.sixth.e3d.examples.essentials.WindingOrderDemo; +import eu.svjatoslav.sixth.e3d.examples.*; import eu.svjatoslav.sixth.e3d.examples.benchmark.GraphicsBenchmark; +import eu.svjatoslav.sixth.e3d.examples.essentials.*; import eu.svjatoslav.sixth.e3d.examples.galaxy_demo.PointCloudDemo; import eu.svjatoslav.sixth.e3d.examples.graph_demo.MathGraphsDemo; import eu.svjatoslav.sixth.e3d.examples.terrain_demo.TerrainDemo; @@ -41,45 +33,59 @@ class ApplicationListPanel extends JPanel { new DemoEntry("Minimal example", "Minimal example showing a single red box", new ShowMinimalExample()), - new DemoEntry("Winding order", - "Triangle demo for winding order & backface culling", - new ShowWindingOrder()), + new DemoEntry("Coordinate system", "Coordinate system axes: X (red), Y (green), Z (blue)", new ShowCoordinateSystem()), + + new DemoEntry("Winding order", + "Triangle demo for winding order & backface culling", + new ShowWindingOrder()), + new DemoEntry("Shape gallery", "All 3D shapes with orbiting colored light sources", new ShowShadedShapes()), + new DemoEntry("CSG demo", "Boolean operations: union, subtract, intersect on 3D shapes", new ShowCSG()), }; private static final DemoEntry[] OTHER_DEMOS = { - new DemoEntry("Volumetric Octree", - "Octree-based rendering with on-demand raytracing", - new ShowOctree()), - new DemoEntry("Sine heightmap", - "Two wobbly sine wave surfaces with central sphere", - new ShowSineHeightmap()), + + new DemoEntry("Game of Life", + "Conway's Game of Life with 3D visualization", + new ShowGameOfLife()), + + new DemoEntry("Text editors", + "5x5 grid of 3D text editor components", + new ShowTextEditors()), + + new DemoEntry("Text editors city", + "3D city of text editor panels as buildings", + new ShowTextEditors2()), + new DemoEntry("Math graphs demo", "Function graphs (sin, cos, tan) rendered in 3D", new ShowMathGraphs()), + + new DemoEntry("Sine heightmap", + "Two wobbly sine wave surfaces with central sphere", + new ShowSineHeightmap()), + + new DemoEntry("Volumetric Octree", + "Octree-based rendering with on-demand raytracing", + new ShowOctree()), + new DemoEntry("Point cloud galaxy", "Spiral galaxy with 10,000 glowing points", new ShowPointCloud()), + new DemoEntry("Raining numbers", "Numbers falling through 3D space like rain", new ShowRain()), - new DemoEntry("Text editors", - "5x5 grid of 3D text editor components", - new ShowTextEditors()), - new DemoEntry("Text editors city", - "3D city of text editor panels as buildings", - new ShowTextEditors2()), - new DemoEntry("Game of Life", - "Conway's Game of Life with 3D visualization", - new ShowGameOfLife()), + + new DemoEntry("Procedural Terrain", "Procedural mountains with 3 colored light sources", new ShowTerrainDemo()), @@ -115,8 +121,8 @@ class ApplicationListPanel extends JPanel { /** * Adds a section header label. * - * @param title the section title - * @param verticalGroup the vertical layout group + * @param title the section title + * @param verticalGroup the vertical layout group * @param horizontalButtonGroup the horizontal group for buttons * @param horizontalDescGroup the horizontal group for descriptions * @param layout the GroupLayout @@ -142,8 +148,8 @@ class ApplicationListPanel extends JPanel { /** * Adds demo entry buttons to the layout. * - * @param entries the demo entries to add - * @param verticalGroup the vertical layout group + * @param entries the demo entries to add + * @param verticalGroup the vertical layout group * @param horizontalButtonGroup the horizontal group for buttons * @param horizontalDescGroup the horizontal group for descriptions * @param layout the GroupLayout diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/Branch.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/Branch.java new file mode 100644 index 0000000..bc5e81d --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/Branch.java @@ -0,0 +1,234 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.sixth.e3d.examples.terrain_demo; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.RenderingContext; +import eu.svjatoslav.sixth.e3d.math.Quaternion; +import eu.svjatoslav.sixth.e3d.math.Transform; +import eu.svjatoslav.sixth.e3d.math.TransformStack; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCylinder; + +import java.util.Random; + +/** + * A recursive branch that can contain sub-branches. + * + *

Demonstrates hierarchical transforms in Sixth 3D: each branch has its own + * transform (position + rotation), and sub-branches are added as child composites. + * The engine automatically propagates parent transforms to children via TransformStack.

+ * + *

Each branch wobbles around its local axes with random phase offsets. + * Parent wobble is automatically inherited by children, creating cascading motion.

+ * + * @see FractalTree + * @see AbstractCompositeShape + */ +public class Branch extends AbstractCompositeShape { + + /** + * Maximum recursion depth for sub-branches. + */ + private static final int MAX_LEVEL = 5; + + /** + * Animation frequency (very slow: ~2 seconds per cycle). + */ + private static final double FREQUENCY = 0.005; + + /** + * Wobble amplitude for X-axis (tilt) in radians. + */ + private static final double WOBBLE_AMPLITUDE_X = Math.toRadians(20); + + /** + * Wobble amplitude for Z-axis (rotation) in radians. + */ + private static final double WOBBLE_AMPLITUDE_Z = Math.toRadians(10); + + /** + * Base outward tilt angle (45 degrees from vertical). + */ + private static final double BASE_TILT = Math.toRadians(45); + + /** + * Branch level (0 = first level from trunk, 1 = sub-branches). + */ + private final int level; + + /** + * Random phase offset for X-axis wobble animation. + */ + private final double phaseOffsetX; + + /** + * Random phase offset for Z-axis wobble animation. + */ + private final double phaseOffsetZ; + + /** + * Base angle around parent axis (determines direction outward). + */ + private final double baseAngle; + + /** + * Height of this branch cylinder. + */ + private final double branchHeight; + + /** + * Radius of this branch cylinder. + */ + private final double branchRadius; + + /** + * Creates a branch with optional recursive sub-branches. + * + *

The branch is positioned at the given start point relative to its parent. + * In local coordinates, the branch cylinder extends from origin to a calculated + * endpoint. The Transform positions and rotates this local geometry.

+ * + * @param startPosition position relative to parent where this branch attaches + * @param level recursion level (0 = first branch level, 1 = sub-branches) + * @param branchIndex index among siblings (0, 1, or 2) determines angle around parent + * @param parentHeight height of parent (for scaling this branch) + * @param parentRadius radius of parent (for scaling this branch) + */ + public Branch(final Point3D startPosition, final int level, final int branchIndex, + final double parentHeight, final double parentRadius) { + super(new Transform(startPosition)); + + this.level = level; + + // Branch dimensions scale down with each level + // Level 0: 120% of trunk height, 60% of trunk radius + // Level 1: 60% of parent (level 0) dimensions + branchHeight = level == 0 ? parentHeight * 1.2 : parentHeight * 0.6; + branchRadius = parentRadius * 0.6; + + // Random phase offsets for unique animation timing + final Random random = new Random(); + phaseOffsetX = random.nextDouble() * Math.PI * 2; + phaseOffsetZ = random.nextDouble() * Math.PI * 2; + + // Calculate base angle around parent axis + // Both levels use even 120-degree spacing for uniform distribution + // Level 0: around vertical trunk axis + // Level 1: around tilted parent branch axis + baseAngle = Math.toRadians(120 * branchIndex); + + // Create branch cylinder in LOCAL coordinates + // The cylinder extends from local origin to endpoint + final Point3D localStart = Point3D.origin(); + final Point3D localEnd = calculateLocalEndpoint(branchHeight, BASE_TILT); + + final SolidPolygonCylinder cylinder = new SolidPolygonCylinder( + localStart, localEnd, branchRadius, 6, FractalTree.BROWN); + cylinder.setShadingEnabled(true); + addShape(cylinder); + + // Create sub-branches recursively if not at max level + if (level < MAX_LEVEL) { + for (int i = 0; i < 3; i++) { + final Branch childBranch = new Branch( + localEnd, level + 1, i, branchHeight, branchRadius); + addShape(childBranch); + } + } + + // Set initial rotation for base orientation + setInitialRotation(); + + setBackfaceCulling(true); + } + + /** + * Calculates branch endpoint in local coordinates. + * + *

The branch is created as a simple vertical cylinder in local space, + * extending from origin upward (negative Y in Sixth 3D). The Transform's + * rotation will tilt it outward to create the desired orientation.

+ * + * @param height branch length + * @param tilt unused (tilt is applied via Transform rotation) + * @return endpoint position in local coordinates (straight up) + */ + private Point3D calculateLocalEndpoint(final double height, final double tilt) { + // Simple vertical cylinder: extends upward in local Y + // The Transform's rotation will tilt it outward + return new Point3D(0, -height, 0); + } + + /** + * Sets the initial rotation for base orientation. + * + *

Both level-0 and level-1 use the same rotation order: tilt first around + * local X-axis, then yaw around local Y-axis.

+ * + *

How it works:

+ * + * + *

The transform hierarchy automatically handles the different parent + * orientations - level-1's Y-axis is tilted by its parent's rotation.

+ */ + private void setInitialRotation() { + // Step 1: Tilt around local X-axis (makes vertical cylinder tilt toward +Z) + final Quaternion tiltQ = Quaternion.fromAxisAngle(new Point3D(1, 0, 0), BASE_TILT); + + // Step 2: Rotate around local Y-axis by baseAngle (0°, 120°, or 240°) + final Quaternion yawQ = Quaternion.fromAxisAngle(new Point3D(0, 1, 0), baseAngle); + + // Combine: apply tilt first, then yaw (same for both levels) + final Quaternion combined = yawQ.multiply(tiltQ); + + getTransform().getRotation().set(combined); + getTransform().invalidateCache(); + } + + /** + * Animates this branch with wobble around its local axes. + * + *

Parent's wobble is automatically inherited via TransformStack - no special + * code needed. Each branch adds its own wobble contribution, creating cascading + * motion that flows down the tree structure.

+ * + *

The wobble is added to the base orientation, so the branch oscillates + * around its default outward-facing position.

+ * + * @param transformPipe the current transform stack (contains parent transforms) + * @param context the rendering context with frame number for animation + */ + @Override + public void beforeTransformHook(final TransformStack transformPipe, + final RenderingContext context) { + final int frame = context.frameNumber; + + // Calculate this branch's own wobble contribution + // Different frequencies for X and Z axes create organic, non-synchronized movement + final double wobbleX = Math.sin(frame * FREQUENCY + phaseOffsetX) * WOBBLE_AMPLITUDE_X; + final double wobbleZ = Math.sin(frame * FREQUENCY * 1.2 + phaseOffsetZ) * WOBBLE_AMPLITUDE_Z; + + // Combine base angles with wobble + final double effectiveTilt = BASE_TILT + wobbleX; + final double effectiveAngle = baseAngle + wobbleZ; + + // Apply rotation: tilt first (around X), then yaw (around Y) + // Same order for both levels - transform hierarchy handles parent orientation + final Quaternion tiltQ = Quaternion.fromAxisAngle(new Point3D(1, 0, 0), effectiveTilt); + final Quaternion yawQ = Quaternion.fromAxisAngle(new Point3D(0, 1, 0), effectiveAngle); + final Quaternion combined = yawQ.multiply(tiltQ); + + getTransform().getRotation().set(combined); + getTransform().invalidateCache(); + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/FractalTree.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/FractalTree.java index 8b78e72..78084d4 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/FractalTree.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/FractalTree.java @@ -11,44 +11,79 @@ import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCom import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCylinder; /** - * A simple tree trunk made of a single cylinder. - * Positioned to extend through the full terrain height range. + * A fractal tree demonstrating hierarchical transforms in Sixth 3D. * + *

The tree consists of a trunk and 3 level-0 branches. Each branch + * recursively contains 3 sub-branches (level-1), demonstrating how + * {@link AbstractCompositeShape} objects can be attached to each other.

+ * + *

Transform inheritance: Each branch has its own transform (position + rotation). + * When the tree is rendered, the engine propagates parent transforms to children + * via {@link eu.svjatoslav.sixth.e3d.math.TransformStack}. This means:

+ * + * + *

See {@link Branch} for the implementation of animated, recursive branching.

+ * + * @see Branch * @see TerrainDemo - * @see SolidPolygonCylinder */ public class FractalTree extends AbstractCompositeShape { - private static final Color BROWN = new Color(139, 90, 43); + /** + * Brown color for tree trunk and branches (50% transparent). + */ + public static final Color BROWN = new Color(139, 90, 43, 128); /** - * Creates a tree trunk at the specified base position. + * Creates a fractal tree with trunk and recursive branches. * - * @param basePosition the position of the trunk base (on the terrain surface) - * @param trunkHeight the height of the trunk - * @param trunkRadius the radius of the trunk base + *

The tree has 3 level-0 branches, each with 3 level-1 sub-branches + * (total of 9 sub-branches). Each branch animates independently with + * random wobble, and children inherit parent motion via transform hierarchy.

+ * + * @param basePosition the position of the trunk base (on terrain surface) + * @param trunkHeight the height of the trunk cylinder + * @param trunkRadius the radius of the trunk cylinder */ public FractalTree(final Point3D basePosition, final double trunkHeight, final double trunkRadius) { super(); // In Sixth 3D, Y increases downward (screen-space coordinates). - // The trunk extends upward from basePosition, so the top has a smaller Y value. - final Point3D topPosition = new Point3D( + // Trunk extends upward from basePosition (smaller Y = higher visually). + final Point3D trunkTop = new Point3D( basePosition.x, basePosition.y - trunkHeight, basePosition.z ); + // Create main trunk cylinder final SolidPolygonCylinder trunk = new SolidPolygonCylinder( - basePosition, // start (bottom of trunk) - topPosition, // end (top of trunk) + basePosition, // bottom of trunk + trunkTop, // top of trunk trunkRadius, - 8, + 8, // segments for smoothness BROWN); trunk.setShadingEnabled(true); - addShape(trunk); + + // Create 3 level-0 branches at trunk top + // Each branch will recursively create its own 3 sub-branches + for (int i = 0; i < 3; i++) { + final Branch branch = new Branch( + trunkTop, // position relative to tree root + 0, // level 0 + i, // branch index (0, 1, or 2) + trunkHeight, // parent height for scaling + trunkRadius); // parent radius for scaling + addShape(branch); + } + setBackfaceCulling(true); } } \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/TerrainDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/TerrainDemo.java index 1bf03ec..b433a5f 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/TerrainDemo.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/TerrainDemo.java @@ -6,6 +6,7 @@ package eu.svjatoslav.sixth.e3d.examples.terrain_demo; import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.FrameListener; import eu.svjatoslav.sixth.e3d.gui.ViewFrame; import eu.svjatoslav.sixth.e3d.gui.ViewPanel; import eu.svjatoslav.sixth.e3d.renderer.raster.Color; @@ -47,6 +48,15 @@ public class TerrainDemo { final double terrainHeight = terrain.getHeightAt(0, 0); shapes.addShape(new FractalTree(point(0, terrainHeight, 0), 30, 6)); + // Add frame listener to enable continuous animation updates + viewPanel.addFrameListener(new FrameListener() { + @Override + public boolean onFrame(final ViewPanel panel, final int millisecondsSinceLastFrame) { + // Always request repaint to keep animation running + return true; + } + }); + viewPanel.repaintDuringNextViewUpdate(); }