</repositories>
#+END_SRC
-
*** Create Your First 3D Scene
:PROPERTIES:
:CUSTOM_ID: create-your-first-3d-scene
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
: 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:
*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]]!
: 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,
: 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.
: 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:
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
: 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
================================================================================
#+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
/*
- * 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;
/**
* 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) {
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);
/**
* 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()))));
/*
- * 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;
/**
* 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();
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;
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()),
/**
* 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
/**
* 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
--- /dev/null
+/*
+ * 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.
+ *
+ * <p>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.</p>
+ *
+ * <p>Each branch wobbles around its local axes with random phase offsets.
+ * Parent wobble is automatically inherited by children, creating cascading motion.</p>
+ *
+ * @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.
+ *
+ * <p>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.</p>
+ *
+ * @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.
+ *
+ * <p>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.</p>
+ *
+ * @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.
+ *
+ * <p>Both level-0 and level-1 use the same rotation order: tilt first around
+ * local X-axis, then yaw around local Y-axis.</p>
+ *
+ * <p><b>How it works:</b></p>
+ * <ul>
+ * <li><b>Level-0</b> (parent is vertical trunk): Yaw around vertical Y-axis
+ * positions branches at 0°, 120°, 240° around the trunk.</li>
+ * <li><b>Level-1</b> (parent is tilted branch): Yaw around tilted Y-axis
+ * (parent's direction) positions sub-branches at 0°, 120°, 240° around
+ * the parent branch. The tilt angle makes them splay outward.</li>
+ * </ul>
+ *
+ * <p>The transform hierarchy automatically handles the different parent
+ * orientations - level-1's Y-axis is tilted by its parent's rotation.</p>
+ */
+ 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.
+ *
+ * <p>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.</p>
+ *
+ * <p>The wobble is added to the base orientation, so the branch oscillates
+ * around its default outward-facing position.</p>
+ *
+ * @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
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.
*
+ * <p>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.</p>
+ *
+ * <p><b>Transform inheritance:</b> 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:</p>
+ * <ul>
+ * <li>Level-0 branches are positioned relative to the trunk's tip</li>
+ * <li>Level-1 sub-branches are positioned relative to their parent branch's tip</li>
+ * <li>Parent wobble rotation automatically affects all descendants</li>
+ * <li>Each level adds its own wobble, creating cascading animation</li>
+ * </ul>
+ *
+ * <p>See {@link Branch} for the implementation of animated, recursive branching.</p>
+ *
+ * @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
+ * <p>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.</p>
+ *
+ * @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
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;
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();
}