--- /dev/null
+/.idea/
+/target/
+/.classpath
+/.project
+/.settings/
+/doc/graphs/
+/doc/apidocs/
+/*.iml
+*.html
--- /dev/null
+# Project Overview
+
+Sixth 3D Demos is a collection of demo applications showcasing the Sixth 3D rendering engine. It features interactive 3D
+visualizations including Conway's Game of Life, galaxy simulations, and various geometric demos.
+
+## Build and Test Commands
+
+### Build the project
+
+```bash
+mvn clean package
+```
+
+### Compile only (without packaging)
+
+```bash
+mvn clean compile
+```
+
+### Run the application
+
+```bash
+java -jar target/sixth-3d-demos.jar
+```
+
+### Generate API documentation
+
+```bash
+mvn javadoc:javadoc
+```
+
+### Run a specific demo directly
+
+```bash
+# Run the launcher
+java -cp target/sixth-3d-demos.jar eu.svjatoslav.sixth.e3d.examples.launcher.Main
+```
+
+### Testing
+
+This project currently has no automated tests. Testing is done manually by running the demo applications.
+
+## Code Organization
+
+```
+src/main/java/eu/svjatoslav/sixth/e3d/examples/
+├── launcher/ - Application launcher and GUI
+│ ├── Main.java - Main entry point
+│ └── ApplicationListPanel.java
+├── life_demo/ - Conway's Game of Life 3D demo
+│ ├── Main.java
+│ ├── Cell.java
+│ ├── Matrix.java
+│ └── Star.java
+├── terrain_demo/ - Procedural terrain generation demo
+│ ├── DiamondSquareTerrain.java
+│ ├── FractalTree.java
+│ └── TerrainDemo.java
+├── galaxy_demo/ - Galaxy simulation demo
+│ ├── Galaxy.java
+│ └── PointCloudDemo.java
+├── RandomPolygonsDemo.java
+├── OctreeDemo.java
+├── graph_demo/ - 3D graph visualization demos
+│ ├── MathGraphsDemo.java
+│ └── SurfaceGraph3D.java
+├── TextEditorDemo.java
+├── TextEditorDemo2.java
+├── RainingNumbersDemo.java
+├── WindingOrderDemo.java - Tests winding order & backface culling
+└── package-info.java
+```
+
+## Code Style Guidelines
+
+### Java Version
+
+- Java 21 (source and target compatibility)
+
+### Indentation and Formatting
+
+- Use 4 spaces for indentation (no tabs)
+
+## External Dependencies
+
+| Dependency | Version | Description |
+|-------------------|--------------|---------------------|
+| sixth-3d | 1.3-SNAPSHOT | 3D rendering engine |
+| svjatoslavcommons | 1.8 | Utility library |
+
+Both are available from the svjatoslav.eu Maven repository.
+
+## Common Patterns in This Codebase
+
+### Creating a Demo Application
+
+1. Create a class with a `main` method
+2. Create a `ViewFrame` to display the 3D scene
+3. Get the `ShapeCollection` from the view panel
+4. Add shapes to the collection
+5. Optionally set up user input handling
+
+### Extending AbstractCompositeShape
+
+Use this pattern for creating composite 3D objects:
+
+```java
+public class MyShape extends AbstractCompositeShape {
+ public MyShape(Transform transform) {
+ super(transform);
+ // Add child shapes using addShape()
+ }
+}
+```
+
+### Handling User Input
+
+Implement `MouseInteractionController` for mouse events, or extend input tracker classes for keyboard input.
+
+### Polygon Winding Order
+
+When creating triangles with backface culling enabled, use CCW winding in screen space:
+
+- Vertex order: top → lower-left → lower-right (as seen from camera)
+- `signedArea < 0` = front-facing = visible
+- See `WindingOrderDemo.java` for a minimal example
+
+## Documentation
+
+Always make sure that documentation in`doc/index.org` stays up to date.
--- /dev/null
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
--- /dev/null
+#!/bin/bash
+
+# This script launches IntelliJ IDEA with the current project
+# directory. The script is designed to be run by double-clicking it in
+# the GNOME Nautilus file manager.
+
+# First, we change the current working directory to the directory of
+# the script.
+
+# "${0%/*}" gives us the path of the script itself, without the
+# script's filename.
+
+# This command basically tells the system "change the current
+# directory to the directory containing this script".
+
+cd "${0%/*}"
+
+# Then, we move up one directory level.
+# The ".." tells the system to go to the parent directory of the current directory.
+# This is done because we assume that the project directory is one level up from the script.
+cd ..
+
+# Now, we use the 'setsid' command to start a new session and run
+# IntelliJ IDEA in the background. 'setsid' is a UNIX command that
+# runs a program in a new session.
+
+# The command 'idea .' opens IntelliJ IDEA with the current directory
+# as the project directory. The '&' at the end is a UNIX command that
+# runs the process in the background. The '> /dev/null' part tells
+# the system to redirect all output (both stdout and stderr, denoted
+# by '&') that would normally go to the terminal to go to /dev/null
+# instead, which is a special file that discards all data written to
+# it.
+
+setsid idea . &>/dev/null &
+
+# The 'disown' command is a shell built-in that removes a shell job
+# from the shell's active list. Therefore, the shell will not send a
+# SIGHUP to this particular job when the shell session is terminated.
+
+# '-h' option specifies that if the shell receives a SIGHUP, it also
+# doesn't send a SIGHUP to the job.
+
+# '$!' is a shell special parameter that expands to the process ID of
+# the most recent background job.
+disown -h $!
+
+
+sleep 2
+
+# Finally, we use the 'exit' command to terminate the shell script.
+# This command tells the system to close the terminal window after
+# IntelliJ IDEA has been opened.
+exit
--- /dev/null
+#!/bin/bash
+
+cd ..
+
+# Function to export org to html using emacs in batch mode
+export_org_to_html() {
+ local org_file=$1
+ local dir=$(dirname "$org_file")
+ local base=$(basename "$org_file" .org)
+ (
+ cd "$dir" || return 1
+ local html_file="${base}.html"
+ if [ -f "$html_file" ]; then
+ rm -f "$html_file"
+ fi
+ echo "Exporting: $org_file → $dir/$html_file"
+ emacs --batch -l ~/.emacs --visit="${base}.org" --funcall=org-html-export-to-html --kill
+ if [ $? -eq 0 ]; then
+ echo "✓ Successfully exported $org_file"
+ else
+ echo "✗ Failed to export $org_file"
+ return 1
+ fi
+ )
+}
+
+export_org_files_to_html() {
+ echo "🔍 Searching for .org files in doc/ ..."
+ echo "======================================="
+
+ mapfile -t ORG_FILES < <(find doc -type f -name "*.org" | sort)
+
+ if [ ${#ORG_FILES[@]} -eq 0 ]; then
+ echo "❌ No .org files found!"
+ return 1
+ fi
+
+ echo "Found ${#ORG_FILES[@]} .org file(s):"
+ printf '%s\n' "${ORG_FILES[@]}"
+ echo "======================================="
+
+ SUCCESS_COUNT=0
+ FAILED_COUNT=0
+
+ for org_file in "${ORG_FILES[@]}"; do
+ export_org_to_html "$org_file"
+ if [ $? -eq 0 ]; then
+ ((SUCCESS_COUNT++))
+ else
+ ((FAILED_COUNT++))
+ fi
+ done
+
+ echo "======================================="
+ echo "📊 SUMMARY:"
+ echo " ✓ Successful: $SUCCESS_COUNT"
+ echo " ✗ Failed: $FAILED_COUNT"
+ echo " Total: $((SUCCESS_COUNT + FAILED_COUNT))"
+ echo ""
+}
+
+build_visualization_graphs() {
+ rm -rf doc/graphs/
+ mkdir -p doc/graphs/
+
+ # javainspect -j target/sixth-3d-demos.jar -d doc/graphs/ -n "All classes" -t png -w "eu.svjatoslav.sixth.e3d.examples.*"
+ javainspect -j target/sixth-3d-demos.jar -d doc/graphs/ -n "Game of Life" -t png -w "eu.svjatoslav.sixth.e3d.examples.life_demo.*"
+
+ meviz index -w doc/graphs/ -t "Sixth 3D Demos classes"
+}
+
+# Build project jar file and JavaDocs
+mvn clean package
+
+# Put generated JavaDoc HTML files to documentation directory
+rm -rf doc/apidocs/
+cp -r target/apidocs/ doc/
+
+# Copy runnable jar file for publishing on website.
+cp target/sixth-3d-demos.jar doc/
+
+
+# Publish Emacs org-mode files into HTML format
+export_org_files_to_html
+
+# Generate nice looking code visualization diagrams
+build_visualization_graphs
+
+
+## Upload assembled documentation to server
+echo "📤 Uploading to server..."
+rsync -avz --delete -e 'ssh -p 10006' doc/ \
+ n0@www3.svjatoslav.eu:/mnt/big/projects/sixth-3d-demos/
+
+if [ $? -eq 0 ]; then
+ echo "✓ Upload completed successfully!"
+else
+ echo "✗ Upload failed!"
+fi
+
+echo ""
+echo "Press ENTER to close this window."
+read
--- /dev/null
+apidocs/
+sixth-3d-demos.jar
--- /dev/null
+#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
+#+TITLE: Sixth 3D engine demos
+#+LANGUAGE: en
+#+LATEX_HEADER: \usepackage[margin=1.0in]{geometry}
+#+LATEX_HEADER: \usepackage{parskip}
+#+LATEX_HEADER: \usepackage[none]{hyphenat}
+
+#+OPTIONS: H:20 num:20
+#+OPTIONS: author:nil
+
+#+begin_export html
+<style>
+ .flex-center {
+ display: flex; /* activate flexbox */
+ justify-content: center; /* horizontally center anything inside */
+ }
+
+ .flex-center video {
+ width: min(90%, 1000px); /* whichever is smaller wins */
+ height: auto; /* preserve aspect ratio */
+ }
+
+ .responsive-img {
+ width: min(100%, 1000px);
+ height: auto;
+ }
+</style>
+#+end_export
+
+
+* Overview
+:PROPERTIES:
+:CUSTOM_ID: overview
+:ID: 4430626f-21f4-42d7-89b1-7097a0b99916
+:END:
+
+
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:overview.png]]
+
+The goal of this project is to show off capabilities and API usage of
+[[https://www3.svjatoslav.eu/projects/sixth-3d/][Sixth 3D]] engine.
+
+All [[id:5f88b493-6ab3-4659-8280-803f75dbd5e0][example scenes in this repository]] render at interactive
+framerates.
+
+You can download pre-compiled runnable JAR file with embedded demos
+from here: [[file:sixth-3d-demos.jar]]
+
+It requires Java 21 or newer to run.
+
+To start the demo application, use command:
+: java -jar sixth-3d-demos.jar
+
+* Essentials
+
+*Resources to help you understand the Sixth 3D library:*
+- Read online [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/][JavaDoc]].
+- See [[https://www3.svjatoslav.eu/projects/sixth-3d/graphs/][Sixth 3D class diagrams]]. (Diagrams were generated by using
+ [[https://www3.svjatoslav.eu/projects/javainspect/][JavaInspect]] utility)
+
+** Minimal example
+:PROPERTIES:
+:CUSTOM_ID: minimal-example
+:ID: 1a2b3c4d-5e6f-7890-abcd-ef1234567890
+:END:
+
+This is the "Hello World" of *Sixth 3D* - the minimal boilerplate
+needed to render any 3D geometry.
+
+: eu.svjatoslav.sixth.e3d.examples.essentials.MinimalExample
+
+*Brief tutorial:*
+
+Here we guide you through creating your first 3D scene with Sixth 3D
+engine.
+
+Prerequisites:
+- Java 21 or later installed
+- Maven 3.x
+- Basic Java knowledge
+
+*** Add Dependency to Your Project
+:PROPERTIES:
+:CUSTOM_ID: add-dependency-to-your-project
+:ID: 3fffc32e-ae66-40b7-ad7d-fab6093c778b
+:END:
+
+Add *Sixth 3D* to your pom.xml:
+
+#+BEGIN_SRC xml
+<dependencies>
+ <dependency>
+ <groupId>eu.svjatoslav</groupId>
+ <artifactId>sixth-3d</artifactId>
+ <version>1.4-SNAPSHOT</version>
+ </dependency>
+</dependencies>
+
+<repositories>
+ <repository>
+ <id>svjatoslav.eu</id>
+ <name>Svjatoslav repository</name>
+ <url>https://www3.svjatoslav.eu/maven/</url>
+ </repository>
+</repositories>
+#+END_SRC
+
+
+*** Create Your First 3D Scene
+:PROPERTIES:
+:CUSTOM_ID: create-your-first-3d-scene
+:ID: 564fa596-9b2b-418a-9df9-baa46f0d0a66
+:END:
+
+
+#+BEGIN_SRC java
+ import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+ import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+ import eu.svjatoslav.sixth.e3d.math.Transform;
+ import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+ import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+ import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonRectangularBox;
+
+ public class MyFirstScene {
+ public static void main(String[] args) {
+ // Create the application window
+ ViewFrame viewFrame = new ViewFrame("My First Scene");
+
+ // Get the collection where you add 3D shapes
+ ShapeCollection shapes = viewFrame.getViewPanel().getRootShapeCollection();
+
+ // Create a red box centered at origin
+ final Transform boxTransform = new Transform();
+ SolidPolygonRectangularBox box = new SolidPolygonRectangularBox(
+ point(-50, -50, -50),
+ point(50, 50, 50),
+ Color.RED
+ );
+ box.setTransform(boxTransform);
+ shapes.addShape(box);
+
+ // Position your camera
+ viewFrame.getViewPanel().getCamera().getTransform()
+ .setTranslation(point(0, -100, -300));
+
+ // Trigger initial render
+ viewFrame.getViewPanel().repaintDuringNextViewUpdate();
+ }
+ }
+#+END_SRC
+
+Compile and run *MyFirstScene* class. A new window should open that will
+display 3D scene with red box.
+
+
+You should see this:
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:Screenshots/Essentials/Minimal example.png]]
+
+
+*Navigating the scene:*
+
+| Input | Action |
+|--------------------+-----------------------------|
+| Arrow Up | Move forward |
+| Arrow Down | Move backward |
+| Arrow Left | Move left (strafe) |
+| Arrow Right | Move right (strafe) |
+| Mouse drag | Look around (rotate camera) |
+| Mouse scroll wheel | Move up / down |
+
+Movement uses physics-based acceleration for smooth, natural
+motion. The faster you're moving, the more acceleration builds up,
+creating an intuitive flying experience.
+
+Press *F12* in any demo to open the [[https://www3.svjatoslav.eu/projects/sixth-3d/#outline-container-developer-tools][Developer Tools panel]]. This
+debugging interface provides real-time insight into the rendering
+pipeline with diagnostic toggles.
+
+** Coordinate system
+:PROPERTIES:
+:CUSTOM_ID: coordinate-system
+:ID: 2b3c4d5e-6f7a-8901-bcde-f12345678901
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.essentials.CoordinateSystemDemo
+
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:Screenshots/Essentials/Coordinate system.png]]
+
+Visual reference for the Sixth 3D coordinate system using three colored
+arrows originating from (0,0,0):
+
+| Axis | Color | Direction |
+|------+-------+------------------------------------|
+| X | Red | Points RIGHT (positive X) |
+| Y | Green | Points DOWN (positive Y) |
+| Z | Blue | Points AWAY from viewer (positive Z) |
+
+A wireframe grid on the Y=0 plane provides spatial context and scale. Text
+labels (X, Y, Z) appear at each arrow tip.
+
+This demo is essential for understanding Sixth 3D's coordinate system where:
+- Positive Y goes down (screen coordinates convention)
+- Positive Z goes into the screen (away from camera)
+- Positive X goes to the right
+
+See the [[https://www3.svjatoslav.eu/projects/sixth-3d/#coordinate-system][Coordinate System]] section in the Sixth 3D documentation
+for a detailed explanation of the axis conventions and coordinate math.
+
+** Winding order
+:PROPERTIES:
+:CUSTOM_ID: winding-order
+:ID: 3c4d5e6f-7a8b-9012-cdef-123456789012
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.essentials.WindingOrderDemo
+
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:Screenshots/Essentials/Winding order.png]]
+
+This demo demonstrates polygon winding order and backface culling - fundamental
+concepts for efficient 3D rendering.
+
+The green triangle uses counter-clockwise (CCW) vertex ordering:
+1. Upper-center vertex
+2. Lower-left vertex
+3. Lower-right vertex
+
+With backface culling enabled:
+- *CCW winding* (negative signed area) = front-facing = visible
+- *CW winding* (positive signed area) = back-facing = culled (not rendered)
+
+This optimization prevents rendering polygons facing away from the camera,
+improving performance by roughly 50% for closed meshes. Navigate around the
+scene and observe that the triangle becomes invisible when viewed from behind,
+demonstrating the culling effect.
+
+See the [[https://www3.svjatoslav.eu/projects/sixth-3d/#winding-order-backface-culling][Winding Order & Backface Culling]] section
+in the Sixth 3D documentation for a detailed explanation with diagrams.
+
+** Shape gallery
+:PROPERTIES:
+:CUSTOM_ID: shape-gallery
+:ID: 4d5e6f7a-8b9c-0123-def0-234567890123
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.essentials.ShapeGalleryDemo
+
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:Screenshots/Essentials/Shape gallery.png]]
+
+Comprehensive showcase of all primitive 3D shapes available in Sixth 3D,
+organized in a 7×2 grid:
+
+| Column | Shape Type | Description |
+|--------+------------+--------------------------------|
+| 1 | Arrow | 3D arrow with conical tip |
+| 2 | Cone | Circular base cone |
+| 3 | Cube | Regular hexahedron |
+| 4 | Cylinder | Tube with circular caps |
+| 5 | Pyramid | Square base pyramid |
+| 6 | Box | Rectangular parallelepiped |
+| 7 | Sphere | Geodesic sphere approximation |
+
+*Top row:* Wireframe versions - edges only, no surfaces
+*Bottom row:* Solid polygon versions - filled surfaces with lighting
+
+Additional features demonstrated:
+- Dynamic lighting from 10 orbiting colored light sources (visible as small
+ glowing dots)
+- Per-shape color assignment for visual distinction
+- Grid floor showing spatial layout and alignment
+- Text labels identifying each shape type
+- Shading enabled on solid shapes to show lighting effects
+
+See the [[https://www3.svjatoslav.eu/projects/sixth-3d/#mesh][Mesh]] section in the Sixth 3D documentation
+for an explanation of 3D mesh fundamentals and how shapes are constructed.
+
+** CSG demo
+:PROPERTIES:
+:CUSTOM_ID: csg-demo
+:ID: 5e6f7a8b-9c0d-1234-ef01-345678901234
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.essentials.CSGDemo
+
+#+attr_html: :class responsive-img
+#+attr_latex: :width 1000px
+[[file:Screenshots/Essentials/CSG demo.png]]
+
+Constructive Solid Geometry (CSG) boolean operations on 3D meshes. Three
+operations displayed left to right:
+
+1. *Subtract (Cube - Sphere)* - A red cube with a spherical cavity carved out
+ where a blue sphere intersects it. Shows the cube's interior surfaces.
+
+2. *Union (Cube + Sphere)* - A merged shape combining both volumes. Red
+ surfaces from the cube, blue surfaces from the sphere, seamlessly joined.
+
+3. *Intersect (Cube ∩ Sphere)* - Only the volume shared by both shapes
+ remains. Shows which parts of space were occupied by both the cube and
+ sphere simultaneously.
+
+Color scheme:
+- *Red* = geometry from the first operand (cube)
+- *Blue* = geometry from the second operand (sphere)
+
+Each operation includes a text description panel below the shape. The demo
+uses:
+- SolidPolygonCube (red)
+- SolidPolygonSphere (blue)
+- CSGSolid.subtract(), union(), intersect() methods
+- toMesh() conversion for rendering
+- Backface culling and shading enabled
+
+CSG is powerful for procedural modeling, allowing complex shapes to be built
+from simple primitives through boolean combinations.
+
+* Advanced examples
+:PROPERTIES:
+:CUSTOM_ID: example-scenes
+:ID: 5f88b493-6ab3-4659-8280-803f75dbd5e0
+:END:
+
+
+** Conway's Game of Life
+:PROPERTIES:
+:CUSTOM_ID: conways-game-of-life
+:ID: 08914390-742b-4c78-88bf-602ab9640082
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.life_demo.Main
+
+The Game of Life, also known simply as Life, is a cellular automaton
+devised by the British mathematician John Horton Conway in 1970.
+
++ https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
+ + Game rules:
+ + 2 cell states: alive / dead
+ + Each cell sees 8 neighboring cells.
+ + If alive cell neighbors count is 2 or 3, then cell survives,
+ otherwise it dies.
+ + Dead cell becomes alive if neighbors count is exactly 3.
+
+[[file:Screenshots/Life.png]]
+
+This demo projects the 2D game grid onto three-dimensional space. The extra
+dimension (height) visualizes history (previous iterations) using glowing dots
+suspended in space.
+
+Usage:
+| key | result |
+|--------------------------------+--------------------------------------|
+| mouse click on the cell (cell) | toggles cell state |
+| <space> | next iteration |
+| ENTER | next iteration with the history |
+| "c" | clear the matrix |
+
+** Text editors
+:PROPERTIES:
+:CUSTOM_ID: text-editors
+:ID: 39250157-db8e-4861-a21b-8568912bd160
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.TextEditorDemo
+
+[[file:Screenshots/Text editors.png]]
+
+Initial test for creating user interfaces in 3D, demonstrating:
++ window focus handling
++ picking objects using the mouse
++ redirecting keyboard input to the focused window
+
+Window focus acts like a stack.
+
+When a window is clicked with the mouse, the previously focused window (if
+any) is pushed to the focus stack and the new window receives focus. A red
+frame appears around the window to indicate this.
+
+When the ESC key is pressed, focus is returned to the previous window (if any).
+
+When any window is focused, all keyboard input is redirected to that window,
+including cursor keys. To navigate around the world again, the window must be
+unfocused first by pressing the ESC key.
+
+
++ TODO:
+ + Improve focus handling:
+ + Perhaps add shortcut to navigate world without exiting entire
+ stack of focus.
+ + Possibility to retain and reuse recently focused elements.
+ + Store user location in the world and view direction with the
+ focused window. So that when returning focus to far away object,
+ user is redirected also to proper location in the world.
+ + Possibility to store recently visited locations in the world and
+ return to them.
+
+** Text editors demo gallery
+:PROPERTIES:
+:CUSTOM_ID: text-editors-demo-gallery
+:ID: a7b8c9d0-e1f2-3456-0123-567890123456
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.TextEditorDemo2
+
+*Quite a lot of text editors can be rendered:*
+
+[[file:Screenshots/Text editors 2.png]]
+
+See also this [[https://hackers-1995.vercel.app/][similar-looking web-based demo]]!
+
+** Math graphs demo
+:PROPERTIES:
+:CUSTOM_ID: mathematical-formulas
+:ID: b1c2d3e4-f5a6-7890-bcde-f12345678901
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.graph_demo.MathGraphsDemo
+
+[[file:Screenshots/Mathematical formulas.png]]
+
++ TODO: instead of projecting 2D visualizations onto 3D space,
+ visualize some formula using all 3 dimensions available.
+
+** Sine heightmap and sphere
+:PROPERTIES:
+:CUSTOM_ID: sine-heightmaps-and-sphere
+:ID: b2c3d4e5-f6a7-8901-bcde-f12345678901
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.SineHeightmap
+
+[[file:Screenshots/Sine heightmap and sphere.png]]
+
+Simple test scene. Easy to implement and looks nice.
+
+** Raytracing through voxels
+:PROPERTIES:
+:CUSTOM_ID: raytracing-through-voxels
+:ID: c3d4e5f6-a7b8-9012-cdef-123456789012
+:END:
+
+: eu.svjatoslav.sixth.e3d.examples.OctreeDemo
+
+[[file:Screenshots/Raytracing fractal in voxel polygon hybrid scene.png]]
+
+Test scene that is generated simultaneously using:
++ conventional polygons
+ + for realtime navigation, and
++ voxels
+ + for on-demand raytracing
+
+Instead of storing voxels in a naive [X × Y × Z] array, a dynamically
+partitioned [[https://en.wikipedia.org/wiki/Octree][octree]] compresses the data. Press the "r" key anywhere in
+the scene to raytrace the current view through the compressed voxel
+data structure.
+
+* Graphics Benchmark
+:PROPERTIES:
+:CUSTOM_ID: graphics-benchmark
+:ID: e5f6a7b8-c9d0-1234-ef01-345678901234
+:END:
+
+: 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.
+
+Upon completion, the benchmark outputs a report suitable for preservation
+and later comparisons.
+
+Example benchmark report:
+#+begin_example
+================================================================================
+ GRAPHICS BENCHMARK RESULTS
+================================================================================
+Date: 2026-03-16 19:17:51
+Resolution: 1920x1080
+Cubes: 4096 (16x16x16 grid)
+Duration: 30 seconds per test
+
+--------------------------------------------------------------------------------
+SYSTEM INFORMATION
+--------------------------------------------------------------------------------
+CPU Name: AMD Ryzen AI 9 HX 370 w/ Radeon 890M
+Arch: amd64
+CPU cores: 24
+
+--------------------------------------------------------------------------------
+Test Avg FPS
+--------------------------------------------------------------------------------
+Solid Cubes 49.65
+Lit Solid Cubes 41.40
+Textured Cubes 32.80
+Wireframe Cubes 42.84
+Star Grid 318.97
+================================================================================
+#+end_example
+
+* Source code
+:PROPERTIES:
+:CUSTOM_ID: source-code
+:ID: d4e5f6a7-b8c9-0123-def0-234567890123
+:END:
+
+*This program is free software, released under the Creative Commons Zero
+(CC0) license.*
+
+*Program author:*
+- Svjatoslav Agejenko
+- Homepage: https://svjatoslav.eu
+- Email: mailto://svjatoslav@svjatoslav.eu
+- See also: [[https://www.svjatoslav.eu/projects/][Other software projects hosted at svjatoslav.eu]]
+
+*Getting the source code:*
+- [[https://www2.svjatoslav.eu/gitweb/?p=sixth-3d-demos.git;a=snapshot;h=HEAD;sf=tgz][Download latest snapshot in TAR GZ format]]
+- [[https://www2.svjatoslav.eu/gitweb/?p=sixth-3d-demos.git;a=summary][Browse Git repository online]]
+- Clone Git repository using command:
+ : git clone https://www2.svjatoslav.eu/git/sixth-3d-demos.git
+
+*Understanding the Sixth 3D demos source code:*
+- Read the online [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/][JavaDoc]].
+- Study the underlying [[https://www3.svjatoslav.eu/projects/sixth-3d/][Sixth 3D]] engine.
--- /dev/null
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>eu.svjatoslav</groupId>
+ <artifactId>sixth-3d-demos</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <name>Sixth 3D demos</name>
+ <description>3D engine demos</description>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ <maven.compiler.source>21</maven.compiler.source>
+ <maven.compiler.target>21</maven.compiler.target>
+ </properties>
+
+ <organization>
+ <name>svjatoslav.eu</name>
+ <url>https://svjatoslav.eu</url>
+ </organization>
+
+ <dependencies>
+ <dependency>
+ <groupId>eu.svjatoslav</groupId>
+ <artifactId>sixth-3d</artifactId>
+ <version>1.4-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>eu.svjatoslav</groupId>
+ <artifactId>svjatoslavcommons</artifactId>
+ <version>1.8</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.11.0</version>
+ <configuration>
+ <source>21</source>
+ <target>21</target>
+ <optimize>true</optimize>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.2.1</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>3.6.0</version>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <!-- workaround for https://bugs.openjdk.java.net/browse/JDK-8212233 -->
+ <javaApiLinks>
+ <property>
+ <name>foo</name>
+ <value>bar</value>
+ </property>
+ </javaApiLinks>
+ <!-- Workaround for https://stackoverflow.com/questions/49472783/maven-is-unable-to-find-javadoc-command -->
+ <javadocExecutable>${java.home}/bin/javadoc</javadocExecutable>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.4.3</version>
+ <configuration>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>eu.svjatoslav.sixth.e3d.examples.launcher.Main</mainClass>
+ </manifest>
+ </archive>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ <finalName>sixth-3d-demos</finalName>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+
+ <executions>
+ <execution>
+ <id>package-jar-with-dependencies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ <archive>
+ <manifest>
+ <mainClass>eu.svjatoslav.sixth.e3d.examples.launcher.Main</mainClass>
+ </manifest>
+ </archive>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ <extensions>
+ <extension>
+ <groupId>org.apache.maven.wagon</groupId>
+ <artifactId>wagon-ssh-external</artifactId>
+ <version>2.6</version>
+ </extension>
+ </extensions>
+ </build>
+
+
+ <distributionManagement>
+ <snapshotRepository>
+ <id>svjatoslav.eu</id>
+ <name>svjatoslav.eu</name>
+ <url>scpexe://svjatoslav.eu:10006/srv/maven</url>
+ </snapshotRepository>
+ <repository>
+ <id>svjatoslav.eu</id>
+ <name>svjatoslav.eu</name>
+ <url>scpexe://svjatoslav.eu:10006/srv/maven</url>
+ </repository>
+ </distributionManagement>
+
+ <repositories>
+ <repository>
+ <id>svjatoslav.eu</id>
+ <name>Svjatoslav repository</name>
+ <url>https://www3.svjatoslav.eu/maven/</url>
+ </repository>
+ </repositories>
+
+ <scm>
+ <connection>scm:git:ssh://n0@svjatoslav.eu/home/git/repositories/sixth-3d-demos.git</connection>
+ <developerConnection>scm:git:ssh://n0@svjatoslav.eu/home/git/repositories/sixth-3d-demos.git
+ </developerConnection>
+ </scm>
+
+
+</project>
--- /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;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonArrow;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCone;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid3D;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+
+/**
+ * Demo showcasing the SolidPolygonArrow shape with various colors, sizes,
+ * orientations, and transparency levels.
+ *
+ * <p>This demo displays arrows pointing in different directions to demonstrate
+ * the flexibility of the arrow shape. A 3D grid provides spatial reference.</p>
+ */
+public class ArrowDemo {
+
+ /**
+ * Entry point for the arrow demo.
+ *
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+ final ViewFrame viewFrame = new ViewFrame("Arrow Demo");
+ final ViewPanel viewPanel = viewFrame.getViewPanel();
+ final ShapeCollection shapes = viewPanel.getRootShapeCollection();
+
+ // Position camera to view the scene
+ viewPanel.getCamera().getTransform().setTranslation(point(0, -200, -600));
+
+ // Add a 3D grid for spatial reference
+ final LineAppearance gridAppearance = new LineAppearance(1, hex("64646450"));
+ final Grid3D grid = new Grid3D(
+ point(-300, -200, -300),
+ point(300, 200, 300),
+ 100,
+ gridAppearance
+ );
+ shapes.addShape(grid);
+
+ // Create arrows pointing in different directions with different properties
+
+ // Red arrow pointing up (negative Y direction)
+ final SolidPolygonArrow redArrow = new SolidPolygonArrow(
+ point(0, 150, 0),
+ point(0, -150, 0),
+ 8, Color.RED
+ );
+ shapes.addShape(redArrow);
+
+ // Green arrow pointing along positive X
+ final SolidPolygonArrow greenArrow = new SolidPolygonArrow(
+ point(-200, 0, 0),
+ point(0, 0, 0),
+ 6, Color.GREEN
+ );
+ shapes.addShape(greenArrow);
+
+ // Blue arrow pointing along positive Z
+ final SolidPolygonArrow blueArrow = new SolidPolygonArrow(
+ point(0, 0, -200),
+ point(0, 0, 0),
+ 6, Color.BLUE
+ );
+ shapes.addShape(blueArrow);
+
+ // Yellow arrow pointing diagonally
+ final SolidPolygonArrow yellowArrow = new SolidPolygonArrow(
+ point(100, 100, 100),
+ point(300, -100, 300),
+ 10, Color.YELLOW
+ );
+ shapes.addShape(yellowArrow);
+
+ // Semi-transparent cyan arrow (50% opacity)
+ final SolidPolygonArrow transparentCyanArrow = new SolidPolygonArrow(
+ point(-150, 50, -100),
+ point(-50, -100, 100),
+ 8, hex("00FFFF80")
+ );
+ shapes.addShape(transparentCyanArrow);
+
+ // Semi-transparent magenta arrow (25% opacity)
+ final SolidPolygonArrow transparentMagentaArrow = new SolidPolygonArrow(
+ point(50, 200, 50),
+ point(50, 0, 50),
+ 12, hex("FF00FF40")
+ );
+ shapes.addShape(transparentMagentaArrow);
+
+ // Small white arrows forming a circle pattern
+ for (int i = 0; i < 8; i++) {
+ final double angle = i * Math.PI / 4;
+ final double radius = 250;
+ final double startX = radius * Math.cos(angle);
+ final double startZ = radius * Math.sin(angle);
+ final double endX = (radius + 80) * Math.cos(angle);
+ final double endZ = (radius + 80) * Math.sin(angle);
+
+ final SolidPolygonArrow circleArrow = new SolidPolygonArrow(
+ point(startX, -80, startZ),
+ point(endX, -80, endZ),
+ 4, Color.WHITE
+ );
+ shapes.addShape(circleArrow);
+ }
+
+ // A standalone cone to demonstrate SolidPolygonCone
+ final SolidPolygonCone standaloneCone = new SolidPolygonCone(
+ point(-300, 0, 0),
+ 40,
+ 80,
+ 16,
+ hex("FF8000FF")
+ );
+ shapes.addShape(standaloneCone);
+
+ viewPanel.repaintDuringNextViewUpdate();
+ }
+}
\ No newline at end of file
--- /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;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.WorldNavigationUserInputTracker;
+import eu.svjatoslav.sixth.e3d.math.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.octree.IntegerPoint;
+import eu.svjatoslav.sixth.e3d.renderer.octree.OctreeVolume;
+import eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.RayTracer;
+import eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.RaytracingCamera;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.GlowingPoint;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.LightSourceMarker;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonRectangularBox;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid3D;
+
+import java.awt.event.KeyEvent;
+import java.util.Vector;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+
+/**
+ * Demo showing volumetric octree rendering with raytracing capability.
+ * Creates a 3D scene with various geometric shapes stored in an octree data structure.
+ * Press 'r' to render the current view using software raytracing.
+ */
+public class OctreeDemo extends WorldNavigationUserInputTracker {
+
+ /**
+ * Scale factor for rendering octree voxels in the scene.
+ */
+ private static final double magnification = 5;
+ private final LineAppearance gridAppearance = new LineAppearance(40, hex("FF00003C"));
+ private final Vector<eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.LightSource> lights = new Vector<>();
+ private OctreeVolume octreeVolume;
+ private ShapeCollection shapeCollection;
+ private ViewPanel viewPanel;
+
+ /**
+ * Creates a new OctreeDemo instance.
+ */
+ public OctreeDemo() {
+ }
+
+ /**
+ * Entry point for the octree demo.
+ *
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+ new OctreeDemo().init();
+ }
+
+ /**
+ * Adds a light source to both the raytracer and rasterizer lighting systems.
+ *
+ * @param location position in octree space (will be scaled by magnification for display)
+ * @param color color of the light
+ * @param brightness intensity of the light
+ */
+ private void addLightToBothSystems(final Point3D location, final Color color,
+ final float brightness) {
+ shapeCollection.addShape(new LightSourceMarker(new Point3D(location)
+ .multiply(magnification), color));
+
+ lights.add(new eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.LightSource(
+ location, color, brightness));
+
+ viewPanel.getLightingManager().addLight(new LightSource(
+ new Point3D(location).multiply(magnification), color, brightness * 50));
+ }
+
+ /**
+ * Creates a colorful spiral pattern of voxels in the octree.
+ */
+ private void dotSpiral() {
+ for (double i = 0; i < 20; i = i + .1) {
+
+ double w = 1;
+ double h = 1;
+
+ final double x = Math.sin(i) * 20f * h;
+ final double y = Math.cos(i) * 20f * w;
+ final double c1 = (Math.cos(i * 3f) * 100) + 127;
+ final double c2 = (Math.cos(i * 5.3332f) * 100f) + 127;
+ final double c3 = (Math.cos(i * 1.342f) * 100f) + 127;
+
+ putPixel((int) x, (int) y, (int) (i * 4f), new Color((int) c1,
+ (int) c2, (int) c3, 255));
+ }
+ }
+
+ /**
+ * Recursively creates a fractal pattern of rectangles.
+ *
+ * @param x center X coordinate
+ * @param y center Y coordinate
+ * @param z center Z coordinate
+ * @param size size of the current rectangle
+ * @param step recursion depth counter
+ */
+ private void fractal(final int x, final int y, final int z, final int size,
+ final int step) {
+ final double c1 = (Math.cos(y / 7f) * 100f) + 127;
+ final double c2 = (Math.cos(x / 10f) * 100f) + 127;
+ final double c3 = (Math.cos(z / 12f) * 100f) + 127;
+
+ putRect(
+ new IntegerPoint(x - size, y - size, z - size),
+ new IntegerPoint(x + size, y + size, z + size),
+ new Color((int) c1, (int) c2, (int) c3, 200));
+
+ if (size > 1) {
+ fractal(x, y - (size * 3), z, size / 2, step + 1);
+ fractal(x + (size * 3), y, z, size / 2, step + 1);
+ fractal(x, y, z + (size * 3), size / 2, step + 1);
+ }
+ }
+
+ /**
+ * Initializes the demo scene with grid, lights, shapes, and fractal pattern.
+ */
+ private void init() {
+
+ final ViewFrame viewFrame = new ViewFrame("Volumetric octree");
+ viewPanel = viewFrame.getViewPanel();
+
+ viewPanel.getCamera().getTransform().set(104.13, -65.04, -370.53, 0.12, 0.14, 0);
+
+ viewPanel.getLightingManager().setAmbientLight(new Color(50, 50, 50));
+
+ octreeVolume = new OctreeVolume();
+
+ shapeCollection = viewPanel.getRootShapeCollection();
+
+ shapeCollection.addShape(new Grid3D(
+ point(-10000, -10000, -10000), point(10000, 10000,
+ 10000), 4000, gridAppearance));
+
+ addLightToBothSystems(point(20, -450, 240), new Color(255, 255, 255), 100);
+ addLightToBothSystems(point(-150, -116, 141), new Color(255, 0, 0), 10);
+
+ dotSpiral();
+
+ // arbitrary rectangles
+ putRect(new IntegerPoint(-10, -10, -10),
+ new IntegerPoint(10, 10, -20),
+ hex("C8FFC864"));
+
+ putRect(new IntegerPoint(-3, 0, -30),
+ new IntegerPoint(12, 3, 300),
+ hex("FFC8C864"));
+
+ putRect(new IntegerPoint(-20, 20, -20),
+ new IntegerPoint(20, 80, 20),
+ hex("FFC8FF64"));
+
+ tiledFloor();
+
+ fractal(-50, 20, 100, 32, 1);
+
+ final TextCanvas message = new TextCanvas(new Transform(point(
+ -10, 20, -180)), "Press \"r\" to raytrace current view",
+ Color.WHITE, Color.PURPLE);
+ shapeCollection.addShape(message);
+
+ viewPanel.getKeyboardFocusStack().pushFocusOwner(this);
+ viewPanel.repaintDuringNextViewUpdate();
+ }
+
+ /**
+ * Handles keyboard input for triggering raytracing.
+ *
+ * @param event the key event
+ * @param viewPanel the view panel
+ * @return true if the event was consumed
+ */
+ @Override
+ public boolean keyPressed(final KeyEvent event, final ViewPanel viewPanel) {
+
+ if ('r' == event.getKeyChar()) {
+ raytrace();
+ return true;
+ }
+ return super.keyPressed(event, viewPanel);
+ }
+
+ /**
+ * Places a single voxel at the specified position with the given color.
+ *
+ * @param x X coordinate in octree space
+ * @param y Y coordinate in octree space
+ * @param z Z coordinate in octree space
+ * @param color the color of the voxel
+ */
+ private void putPixel(final int x, final int y, final int z,
+ final Color color) {
+ shapeCollection.addShape(new GlowingPoint(point(x, y, z)
+ .multiply(magnification), 3 * magnification, color));
+ octreeVolume.putCell(x, y, z, color);
+
+ }
+
+ /**
+ * Fills a rectangular region in the octree with the specified color.
+ *
+ * @param p1 first corner of the rectangle
+ * @param p2 opposite corner of the rectangle
+ * @param color the color to fill
+ */
+ private void putRect(IntegerPoint p1, IntegerPoint p2, final Color color) {
+ final SolidPolygonRectangularBox box = new SolidPolygonRectangularBox(
+ new Point3D(p1).multiply(magnification),
+ new Point3D(p2).multiply(magnification), color);
+ box.setShadingEnabled(true);
+ shapeCollection.addShape(box);
+ octreeVolume.fillRectangle(p1, p2, color);
+ }
+
+ /**
+ * Starts a raytracing render of the current view in a background thread.
+ */
+ private void raytrace() {
+ // create and add camera object to scene
+ final RaytracingCamera raytracingCamera = new RaytracingCamera(viewPanel.getCamera(), magnification);
+ shapeCollection.addShape(raytracingCamera);
+
+ // initialize and start Raytracer in a separate thread
+ final RayTracer rayTracer = new RayTracer(raytracingCamera.getTexture(),
+ octreeVolume, lights, raytracingCamera, viewPanel);
+ final Thread thread = new Thread(rayTracer);
+ thread.start();
+ }
+
+ /**
+ * Creates a tiled floor pattern using small rectangular tiles.
+ */
+ private void tiledFloor() {
+ final int step = 40;
+ final int size = step - 15;
+ Color color = hex("FFFFFF64");
+ for (int x = -200; x < 200; x += step)
+ for (int z = -200; z < 200; z += step)
+ putRect(
+ new IntegerPoint(x, 100, z),
+ new IntegerPoint(x + size, 110, z + size),
+ color);
+ }
+
+}
--- /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;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.gui.FrameListener;
+import eu.svjatoslav.sixth.e3d.math.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas;
+
+import java.util.Collection;
+import java.util.Random;
+
+/**
+ * Demo showing numbers falling through 3D space like rain.
+ * Creates 1000 randomly positioned text canvases displaying digits 0-9,
+ * which continuously fall downward and wrap around to create a "Matrix" style effect.
+ */
+public class RainingNumbersDemo implements FrameListener {
+
+ /**
+ * Creates a new RainingNumbersDemo instance.
+ */
+ public RainingNumbersDemo() {
+ }
+
+ /** Number of falling numbers in the scene. */
+ private static final int NUMBERS_COUNT = 1000;
+ /** Size of the cubic area containing the numbers. */
+ private final static int AREA = 600;
+ /** Half of the area for positioning calculations. */
+ private final static int AREA_HALF = AREA / 2;
+
+ /**
+ * Entry point for the raining numbers demo.
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+ new RainingNumbersDemo().run();
+ }
+
+ /**
+ * Animates all text canvases to fall downward each frame.
+ * Numbers that fall below the area wrap around to the top.
+ * @param viewPanel the view panel
+ * @param millisecondsSinceLastFrame time elapsed since last frame
+ * @return true to continue animation
+ */
+ @Override
+ public boolean onFrame(final ViewPanel viewPanel,
+ final int millisecondsSinceLastFrame) {
+
+ final Collection<AbstractShape> shapes = viewPanel
+ .getRootShapeCollection().getShapes();
+
+ final double translateAmount = millisecondsSinceLastFrame / 50d;
+
+ shapes.stream().filter(shape -> shape instanceof TextCanvas).forEach(shape -> {
+ final TextCanvas block = (TextCanvas) shape;
+ final Point3D location = block.getLocation();
+ location.translateY(translateAmount);
+
+ if (location.y > AREA_HALF)
+ location.y -= AREA;
+ });
+
+ return true;
+ }
+
+ /** Initializes the demo by creating the view frame and adding falling numbers. */
+ private void run() {
+ final ViewFrame viewFrame = new ViewFrame("Raining Numbers");
+
+ viewFrame.getViewPanel().getCamera().getTransform().set(-129.75, 228.27, -220.69, -0.55, 0.54, 0);
+
+ final ShapeCollection geometryCollection = viewFrame.getViewPanel()
+ .getRootShapeCollection();
+
+ Random random = new Random();
+
+ for (int i = 0; i < NUMBERS_COUNT; i++) {
+ final Point3D location = point((Math.random() * AREA)
+ - AREA_HALF, (Math.random() * AREA) - AREA_HALF,
+ (Math.random() * AREA) - AREA_HALF);
+
+ final Color color = new Color(Math.random(), Math.random(),
+ Math.random(), Math.random());
+
+ final TextCanvas textCanvas = new TextCanvas(
+ new Transform(location), String.valueOf(random.nextInt(10)), color,
+ Color.TRANSPARENT);
+
+ geometryCollection.addShape(textCanvas);
+ }
+
+ viewFrame.getViewPanel().addFrameListener(this);
+
+ viewFrame.getViewPanel().repaintDuringNextViewUpdate();
+ }
+}
--- /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;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.WireframeSphere;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin;
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+
+/**
+ * Demo showing a sine heightmap surface with a central wireframe sphere.
+ * Two wobbly surfaces are positioned above and below the sphere.
+ */
+public class SineHeightmap {
+
+ /**
+ * Frequency of the wave pattern in the wobbly surfaces.
+ */
+ private static final double WAVE_FREQUENCY = 50d;
+ /**
+ * Amplitude of the wave pattern in the wobbly surfaces.
+ */
+ private static final double WAVE_AMPLITUDE = 50d;
+ /**
+ * Color for the square plates in the wobbly surfaces.
+ */
+ private static final Color SQUARE_PLATE_COLOR = hex("88F7");
+ /**
+ * Creates a new GraphDemo instance.
+ */
+ public SineHeightmap() {
+ }
+
+ /**
+ * Creates a single square plate at the specified position.
+ *
+ * @param shapeCollection the collection to add the plate to
+ * @param y the Y coordinate (elevation)
+ * @param x the X coordinate
+ * @param z the Z coordinate
+ */
+ private static void makeSquarePlate(final ShapeCollection shapeCollection,
+ final double y, final double x, final double z) {
+ final Point3D p1 = point(x, y, z);
+ final Point3D p2 = point(x + 20, y, z);
+ final Point3D p3 = point(x, y, z + 20);
+ final Point3D p4 = point(x + 20, y, z + 20);
+ final SolidPolygon quad = SolidPolygon.quad(p1, p2, p4, p3, SQUARE_PLATE_COLOR);
+ shapeCollection.addShape(quad);
+ }
+
+ /**
+ * Creates a wobbly surface composed of square plates arranged in a wave pattern.
+ *
+ * @param shapeCollection the collection to add plates to
+ * @param surfaceElevation the base Y elevation of the surface
+ */
+ private static void addWobblySurface(final ShapeCollection shapeCollection,
+ final double surfaceElevation) {
+ for (double x = -500; x < 500; x += 20)
+ for (double z = -500; z < 500; z += 20) {
+
+ final double distanceFromCenter = Math.sqrt((x * x) + (z * z));
+
+ double plateElevation = Math.sin(distanceFromCenter / WAVE_FREQUENCY) * WAVE_AMPLITUDE;
+
+ makeSquarePlate(shapeCollection, plateElevation + surfaceElevation, x,
+ z);
+ }
+ }
+
+ /**
+ * Entry point for the graph demo.
+ *
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+
+ final ViewFrame viewFrame = new ViewFrame("Sine Heightmap");
+ final ShapeCollection geometryCollection = viewFrame.getViewPanel()
+ .getRootShapeCollection();
+
+ viewFrame.getViewPanel().getCamera().getTransform().setTranslation(point(0, 0, -500));
+
+ addSphere(geometryCollection);
+ addWobblySurface(geometryCollection, 200);
+ addWobblySurface(geometryCollection, -200);
+
+ viewFrame.getViewPanel().repaintDuringNextViewUpdate();
+ }
+
+ /**
+ * Adds a wireframe sphere at the center of the scene.
+ *
+ * @param geometryCollection the collection to add the sphere to
+ */
+ private static void addSphere(ShapeCollection geometryCollection) {
+ geometryCollection.addShape(new WireframeSphere(origin(),
+ 100,
+ new LineAppearance(
+ 4,
+ hex("FF00001E"))
+ ));
+ }
+
+}
--- /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;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Rectangle;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.LookAndFeel;
+import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.TextEditComponent;
+import eu.svjatoslav.sixth.e3d.math.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid2D;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+
+/**
+ * Demo showing a grid of 3D text editor components.
+ * Creates a 5x5 grid of text editors floating in 3D space with a decorative grid
+ * below them. This demonstrates the basic text editing capabilities of the Sixth 3D engine.
+ */
+public class TextEditorDemo {
+
+ /**
+ * Creates a new TextEditorDemo instance.
+ */
+ public TextEditorDemo() {
+ }
+
+ /**
+ * Entry point for the text editor demo.
+ *
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+
+ final ViewFrame viewFrame = new ViewFrame("Text Editors");
+ final ViewPanel viewPanel = viewFrame.getViewPanel();
+
+ viewPanel.getCamera().getTransform().set(500, -300, -800, 0.6, -0.5, 0);
+
+ final ShapeCollection shapeCollection = viewFrame.getViewPanel()
+ .getRootShapeCollection();
+
+ addGrid(shapeCollection);
+
+ addTextEditors(viewPanel, shapeCollection);
+
+ viewFrame.getViewPanel().repaintDuringNextViewUpdate();
+ }
+
+ /**
+ * Adds a decorative grid below the text editors.
+ *
+ * @param shapeCollection the collection to add the grid to
+ */
+ private static void addGrid(ShapeCollection shapeCollection) {
+ final Transform transform = Transform.fromAngles(0, 100, 0, 0, Math.PI / 2, 0);
+
+ final Rectangle rectangle = new Rectangle(2000);
+ final LineAppearance appearance = new LineAppearance(10, hex("00b3ad"));
+
+ shapeCollection.addShape(new Grid2D(transform, rectangle, 10, 10, appearance));
+ }
+
+ /**
+ * Creates a grid of text editor components arranged in 3D space.
+ *
+ * @param viewPanel the view panel for the text editors
+ * @param shapeCollection the collection to add editors to
+ */
+ private static void addTextEditors(ViewPanel viewPanel, ShapeCollection shapeCollection) {
+ final double m = 1.5;
+ for (double z = -500 * m; z <= (500 * m); z += 250 * m)
+ for (double x = -500 * m; x <= (500 * m); x += 250 * m) {
+
+ final TextEditComponent textEditor = new TextEditComponent(
+ new Transform(point(x, 0, z)), viewPanel,
+ new Point2D(200, 120), new LookAndFeel());
+
+ shapeCollection.addShape(textEditor);
+ }
+ }
+
+}
--- /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;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Rectangle;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.LookAndFeel;
+import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.TextEditComponent;
+import eu.svjatoslav.sixth.e3d.math.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid2D;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URISyntaxException;
+import java.util.stream.Collectors;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.math.Transform.fromAngles;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+import static java.lang.Math.PI;
+
+/**
+ * "Text Editor City" demo showing a 3D city of text editor components.
+ * Creates a grid of buildings where each building has four text editor panels
+ * facing different directions. This demonstrates the 3D text editing capabilities
+ * of the Sixth 3D engine in a large-scale scene.
+ */
+public class TextEditorDemo2 {
+
+ /**
+ * Creates a new TextEditorDemo2 instance.
+ */
+ public TextEditorDemo2() {
+ }
+
+ /**
+ * Entry point for the text editor city demo.
+ *
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+ try {
+ new TextEditorDemo2().build();
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Adds a grid to the scene for visual reference.
+ *
+ * @param shapeCollection the collection to add the grid to
+ */
+ private static void addGrid(ShapeCollection shapeCollection) {
+ final Transform transform = fromAngles(0, 100, 0, 0, PI / 2, 0);
+
+ final Rectangle rectangle = new Rectangle(10000);
+ final LineAppearance appearance = new LineAppearance(10, hex("00b3ad"));
+
+ shapeCollection.addShape(new Grid2D(transform, rectangle, 50, 50, appearance));
+ }
+
+ /**
+ * Reads a resource file as a string.
+ *
+ * @param fileName the name of the resource file
+ * @return the file contents as a string, or null if not found
+ * @throws IOException if an I/O error occurs
+ */
+ static String getResourceFileAsString(String fileName) throws IOException {
+ ClassLoader classLoader = ClassLoader.getSystemClassLoader();
+ try (InputStream is = classLoader.getResourceAsStream(fileName)) {
+ if (is == null) return null;
+ try (InputStreamReader isr = new InputStreamReader(is);
+ BufferedReader reader = new BufferedReader(isr)) {
+ return reader.lines().collect(Collectors.joining(System.lineSeparator()));
+ }
+ }
+ }
+
+ /**
+ * Builds and displays the 3D city scene with text editors.
+ *
+ * @throws URISyntaxException if resource URI is malformed
+ * @throws IOException if demo text file cannot be read
+ */
+ public void build() throws URISyntaxException, IOException {
+ final ViewFrame viewFrame = new ViewFrame("Text Editors City");
+ final ViewPanel viewPanel = viewFrame.getViewPanel();
+
+ viewPanel.getCamera().getTransform().set(500, -300, -800, 0.6, -0.5, 0);
+
+ final ShapeCollection shapeCollection = viewFrame.getViewPanel()
+ .getRootShapeCollection();
+
+ addGrid(shapeCollection);
+
+ addCity(viewPanel, shapeCollection);
+
+ viewFrame.getViewPanel().repaintDuringNextViewUpdate();
+ }
+
+ /**
+ * Creates a grid of buildings in the city.
+ *
+ * @param viewPanel the view panel for the text editors
+ * @param shapeCollection the collection to add buildings to
+ * @throws URISyntaxException if resource URI is malformed
+ * @throws IOException if demo text file cannot be read
+ */
+ private void addCity(ViewPanel viewPanel, ShapeCollection shapeCollection) throws URISyntaxException, IOException {
+ int citySize = 4000;
+ for (int z = -citySize; z < citySize; z += 1000) {
+ for (int x = -citySize; x < citySize; x += 1000) {
+ addBuilding(viewPanel, shapeCollection, x, z);
+ }
+ }
+ }
+
+ /**
+ * Adds a single building with four text editor panels facing different directions.
+ *
+ * @param viewPanel the view panel for the text editors
+ * @param shapeCollection the collection to add the building to
+ * @param x the X coordinate of the building center
+ * @param z the Z coordinate of the building center
+ * @throws URISyntaxException if resource URI is malformed
+ * @throws IOException if demo text file cannot be read
+ */
+ private void addBuilding(ViewPanel viewPanel, ShapeCollection shapeCollection, double x, double z) throws URISyntaxException, IOException {
+ addTextEditor(viewPanel, shapeCollection, new Transform(point(x, -390, z - 200)));
+
+ addTextEditor(viewPanel, shapeCollection, fromAngles(x, -390, z + 200, PI, 0, 0));
+
+ addTextEditor(viewPanel, shapeCollection, fromAngles(x - 200, -390, z, PI / 2, 0, 0));
+
+ addTextEditor(viewPanel, shapeCollection, fromAngles(x + 200, -390, z, PI / 2 * 3f, 0, 0));
+ }
+
+ /**
+ * Adds a single text editor component at the specified transform.
+ *
+ * @param viewPanel the view panel for the text editor
+ * @param shapeCollection the collection to add the editor to
+ * @param transform the position and orientation of the editor
+ * @throws IOException if demo text file cannot be read
+ */
+ private void addTextEditor(ViewPanel viewPanel, ShapeCollection shapeCollection, Transform transform) throws IOException {
+ LookAndFeel lookAndFeel = getLookAndFeel();
+
+ final TextEditComponent textEditor = new TextEditComponent(
+ transform,
+ viewPanel,
+ new Point2D(400, 1000),
+ lookAndFeel
+ );
+
+ String text = getResourceFileAsString("demo.txt");
+
+ textEditor.setText(text);
+ textEditor.goToLine((int) (Math.random() * 200f));
+ shapeCollection.addShape(textEditor);
+ }
+
+ /**
+ * Creates the look and feel for text editors with a dark theme.
+ *
+ * @return a LookAndFeel configured with dark blue background and light text
+ */
+ private LookAndFeel getLookAndFeel() {
+ LookAndFeel lookAndFeel = new LookAndFeel();
+ lookAndFeel.background = hex("141E3296");
+ lookAndFeel.tabStopBackground = lookAndFeel.background;
+ lookAndFeel.foreground = hex("9696FFFA");
+ return lookAndFeel;
+ }
+
+}
--- /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.benchmark;
+
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+
+/**
+ * Interface for a single benchmark test.
+ * Implementations define how to set up and tear down the test scene.
+ */
+public interface BenchmarkTest {
+
+ /**
+ * Returns the display name for this benchmark test.
+ * @return the test name
+ */
+ String getName();
+
+ /**
+ * Sets up the test scene by adding shapes to the collection.
+ * @param shapes the shape collection to populate
+ */
+ void setup(ShapeCollection shapes);
+
+ /**
+ * Tears down the test scene by removing all shapes added during setup.
+ * @param shapes the shape collection to clean up
+ */
+ void teardown(ShapeCollection shapes);
+
+ /**
+ * Called after setup to provide the view panel for tests that need animation.
+ * Default implementation does nothing.
+ * @param viewPanel the view panel
+ */
+ default void setViewPanel(ViewPanel viewPanel) {
+ }
+}
\ No newline at end of file
--- /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.benchmark;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.Camera;
+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.gui.humaninput.KeyboardInputHandler;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.datatransfer.StringSelection;
+import java.awt.event.KeyEvent;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Automated graphics benchmark that tests the engine's rendering performance.
+ * Runs multiple tests sequentially, each for a fixed duration, and displays
+ * results in a dialog with copy-to-clipboard functionality.
+ *
+ * <p>The benchmark creates a 16x16x16 grid of cubes (4096 total) with the camera
+ * following a deterministic orbital path. Each test runs for 30 seconds by default.</p>
+ *
+ * <p>Press <b>Space</b> to skip to the next test immediately.</p>
+ *
+ * <p>Available tests:</p>
+ * <ul>
+ * <li>{@link SolidCubesTest} - Semi-transparent solid polygon rendering</li>
+ * <li>{@link LitSolidCubesTest} - Opaque solid polygons with dynamic lighting</li>
+ * <li>{@link TexturedCubesTest} - Textured polygon rendering</li>
+ * <li>{@link WireframeCubesTest} - Line rendering</li>
+ * <li>{@link StarGridTest} - Billboard (glowing point) rendering</li>
+ * </ul>
+ */
+public class GraphicsBenchmark implements FrameListener, KeyboardInputHandler {
+
+ private static final int WINDOW_WIDTH = 1920;
+ private static final int WINDOW_HEIGHT = 1080;
+ private static final int GRID_SIZE = 16;
+ private static final double ORBIT_DISTANCE = 1200;
+ private static final double ORBIT_SPEED = 0.0003;
+ private static final double WOBBLE_AMPLITUDE = 800;
+ private static final int TEST_DURATION_MS = 30000;
+
+ private ViewFrame viewFrame;
+ private ViewPanel viewPanel;
+ private ShapeCollection shapes;
+ private Camera camera;
+
+ private double orbitAngle = 0;
+ private long testStartTime;
+ private long frameCount;
+ private BenchmarkTest currentTest;
+ private boolean testFinished = false;
+ private boolean benchmarkFinished = false;
+ private boolean pendingTestTransition = false;
+ private final List<TestResult> results = new ArrayList<>();
+ private final List<BenchmarkTest> tests = new ArrayList<>();
+
+ /**
+ * Entry point for the graphics benchmark.
+ * @param args command line arguments (ignored)
+ */
+ public static void main(String[] args) {
+ SwingUtilities.invokeLater(() -> {
+ if (showIntroDialog()) {
+ new GraphicsBenchmark();
+ }
+ });
+ }
+
+ private static boolean showIntroDialog() {
+ String message =
+ "<html><div style='width:400px; font-family: sans-serif;'>" +
+ "<h2>Graphics Benchmark</h2>" +
+ "<p>This will run a series of performance tests.</p>" +
+ "<ul>" +
+ "<li>Each test runs for 30 seconds</li>" +
+ "<li>Press <b>SPACE</b> to skip any test<br>" +
+ "<span style='color: gray;'>(skipping reduces measurement precision)</span></li>" +
+ "<li>A summary will be shown at the end</li>" +
+ "</ul>" +
+ "</div></html>";
+
+ int choice = JOptionPane.showConfirmDialog(
+ null,
+ message,
+ "Graphics Benchmark",
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.INFORMATION_MESSAGE
+ );
+
+ return choice == JOptionPane.OK_OPTION;
+ }
+
+ /**
+ * Constructs and runs the graphics benchmark.
+ */
+ public GraphicsBenchmark() {
+ registerTests();
+ initializeWindow();
+ }
+
+ private void initializeWindow() {
+ viewFrame = new ViewFrame("Graphics Benchmark", WINDOW_WIDTH, WINDOW_HEIGHT);
+ viewPanel = viewFrame.getViewPanel();
+ viewPanel.setFrameRate(0);
+ shapes = viewPanel.getRootShapeCollection();
+
+ camera = viewPanel.getCamera();
+ camera.getTransform().set(0, -500, -800, 0, -0.5, 0);
+
+ viewPanel.addFrameListener(this);
+ viewPanel.getKeyboardFocusStack().pushFocusOwner(this);
+ viewPanel.repaintDuringNextViewUpdate();
+ }
+
+ private void registerTests() {
+ tests.add(new SolidCubesTest());
+ tests.add(new LitSolidCubesTest());
+ tests.add(new TexturedCubesTest());
+ tests.add(new WireframeCubesTest());
+ tests.add(new StarGridTest());
+ }
+
+ private void startNextTest() {
+ int nextIndex = results.size();
+ if (nextIndex >= tests.size()) {
+ scheduleBenchmarkFinish();
+ return;
+ }
+
+ currentTest = tests.get(nextIndex);
+ testFinished = false;
+ orbitAngle = 0;
+ frameCount = 0;
+ testStartTime = System.currentTimeMillis();
+
+ currentTest.setup(shapes);
+ currentTest.setViewPanel(viewPanel);
+ }
+
+ private void finishCurrentTest() {
+ if (currentTest == null || testFinished) {
+ return;
+ }
+
+ testFinished = true;
+ long elapsed = System.currentTimeMillis() - testStartTime;
+ double durationSeconds = elapsed / 1000.0;
+ results.add(new TestResult(currentTest.getName(), frameCount, durationSeconds));
+
+ pendingTestTransition = true;
+ }
+
+ private void performTestTransition() {
+ if (currentTest != null) {
+ currentTest.teardown(shapes);
+ }
+ startNextTest();
+ }
+
+ private void scheduleBenchmarkFinish() {
+ benchmarkFinished = true;
+ SwingUtilities.invokeLater(this::finishBenchmark);
+ }
+
+ private void finishBenchmark() {
+ currentTest = null;
+ viewPanel.getKeyboardFocusStack().popFocusOwner();
+ viewPanel.removeFrameListener(this);
+ viewPanel.stop();
+ viewFrame.dispose();
+
+ showResultsDialog();
+ }
+
+ private String formatResults() {
+ StringBuilder sb = new StringBuilder();
+ String separator = "================================================================================";
+ String thinSeparator = "--------------------------------------------------------------------------------";
+
+ Runtime runtime = Runtime.getRuntime();
+
+ sb.append(separator).append("\n");
+ sb.append(" GRAPHICS BENCHMARK RESULTS\n");
+ sb.append(separator).append("\n");
+ sb.append("Date: ").append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("\n");
+ sb.append("Resolution: ").append(WINDOW_WIDTH).append("x").append(WINDOW_HEIGHT).append("\n");
+ sb.append("Cubes: ").append(GRID_SIZE * GRID_SIZE * GRID_SIZE)
+ .append(" (").append(GRID_SIZE).append("x").append(GRID_SIZE).append("x").append(GRID_SIZE).append(" grid)\n");
+ sb.append("Duration: ").append(TEST_DURATION_MS / 1000).append(" seconds per test\n");
+ sb.append("\n");
+ sb.append(thinSeparator).append("\n");
+ sb.append("SYSTEM INFORMATION\n");
+ sb.append(thinSeparator).append("\n");
+ sb.append("CPU Name: ").append(getCpuName()).append("\n");
+ sb.append("Arch: ").append(System.getProperty("os.arch")).append("\n");
+ sb.append("CPU cores: ").append(runtime.availableProcessors()).append("\n");
+ sb.append("\n");
+ sb.append(thinSeparator).append("\n");
+ sb.append(String.format("%-28s %s%n", "Test", "Avg FPS"));
+ sb.append(thinSeparator).append("\n");
+
+ for (TestResult result : results) {
+ sb.append(String.format("%-28s %.2f%n", result.testName, result.averageFps));
+ }
+
+ sb.append(separator).append("\n");
+
+ return sb.toString();
+ }
+
+ private void showResultsDialog() {
+ String resultsText = formatResults();
+
+ JTextArea textArea = new JTextArea(resultsText);
+ textArea.setEditable(false);
+ textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
+ textArea.setCaretPosition(0);
+
+ JScrollPane scrollPane = new JScrollPane(textArea);
+ scrollPane.setPreferredSize(new Dimension(600, 400));
+
+ JButton copyButton = new JButton("Copy to Clipboard");
+ JButton closeButton = new JButton("Close");
+
+ copyButton.addActionListener(e -> {
+ StringSelection selection = new StringSelection(resultsText);
+ Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null);
+ copyButton.setText("Copied!");
+ copyButton.setEnabled(false);
+ });
+
+ closeButton.addActionListener(e -> {
+ Window window = SwingUtilities.getWindowAncestor(closeButton);
+ if (window != null) {
+ window.dispose();
+ }
+ });
+
+ JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+ buttonPanel.add(copyButton);
+ buttonPanel.add(closeButton);
+
+ JPanel panel = new JPanel(new BorderLayout(0, 10));
+ panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ panel.add(scrollPane, BorderLayout.CENTER);
+ panel.add(buttonPanel, BorderLayout.SOUTH);
+
+ JOptionPane.showMessageDialog(
+ null,
+ panel,
+ "Benchmark Results",
+ JOptionPane.PLAIN_MESSAGE
+ );
+ }
+
+ private String getCpuName() {
+ try {
+ java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.FileReader("/proc/cpuinfo"));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (line.startsWith("model name")) {
+ reader.close();
+ return line.substring(line.indexOf(':') + 1).trim();
+ }
+ }
+ reader.close();
+ } catch (Exception ignored) {
+ }
+ return System.getProperty("java.vm.name", "Unknown");
+ }
+
+ @Override
+ public boolean onFrame(ViewPanel viewPanel, int millisecondsSinceLastFrame) {
+ if (pendingTestTransition) {
+ pendingTestTransition = false;
+ performTestTransition();
+ }
+
+ if (currentTest == null && !benchmarkFinished && results.isEmpty()) {
+ startNextTest();
+ }
+
+ if (benchmarkFinished) {
+ return false;
+ }
+
+ if (currentTest == null) {
+ return false;
+ }
+
+ orbitAngle += ORBIT_SPEED * millisecondsSinceLastFrame;
+
+ double x = Math.sin(orbitAngle) * ORBIT_DISTANCE;
+ double z = Math.cos(orbitAngle) * ORBIT_DISTANCE;
+ double y = Math.sin(orbitAngle * 1.8934) * WOBBLE_AMPLITUDE;
+
+ camera.getTransform().setTranslation(new Point3D(x, y, z));
+ camera.lookAt(new Point3D(0, 0, 0));
+
+ frameCount++;
+
+ long elapsed = System.currentTimeMillis() - testStartTime;
+ if (elapsed >= TEST_DURATION_MS && !testFinished) {
+ finishCurrentTest();
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean keyPressed(KeyEvent event, ViewPanel viewPanel) {
+ if (event.getKeyChar() == ' ') {
+ finishCurrentTest();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean keyReleased(KeyEvent event, ViewPanel viewPanel) {
+ return false;
+ }
+
+ @Override
+ public boolean focusLost(ViewPanel viewPanel) {
+ return false;
+ }
+
+ @Override
+ public boolean focusReceived(ViewPanel viewPanel) {
+ return false;
+ }
+}
\ No newline at end of file
--- /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.benchmark;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.FrameListener;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource;
+import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightingManager;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.LightSourceMarker;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCube;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import static eu.svjatoslav.sixth.e3d.math.Transform.*;
+
+/**
+ * Benchmark test for solid opaque cubes with dynamic lighting.
+ * Renders a grid of fully opaque cubes lit by three orbiting light sources
+ * to test shaded polygon rasterization performance.
+ */
+public class LitSolidCubesTest implements BenchmarkTest {
+
+ private static final int GRID_SIZE = 16;
+ private static final double SPACING = 80;
+ private static final double CUBE_SIZE = 25;
+ private static final long RANDOM_SEED = 42;
+
+ private final Random random = new Random(RANDOM_SEED);
+ private final List<SolidPolygonCube> cubes = new ArrayList<>();
+ private final List<OrbitingLight> orbitingLights = new ArrayList<>();
+ private FrameListener animator;
+ private ViewPanel viewPanel;
+
+ @Override
+ public String getName() {
+ return "Lit Solid Cubes";
+ }
+
+ @Override
+ public void setup(final ShapeCollection shapes) {
+ random.setSeed(RANDOM_SEED);
+
+ // Note: lights will be added when setViewPanel is called
+ final Color[] lightColors = {
+ new Color(255, 100, 100),
+ new Color(100, 255, 100),
+ new Color(100, 100, 255)
+ };
+
+ for (int i = 0; i < 3; i++) {
+ final double angleOffset = i * (Math.PI * 2 / 3);
+ final LightSource light = new LightSource(
+ new Point3D(0, 0, 0),
+ lightColors[i],
+ 2.5
+ );
+
+ final LightSourceMarker marker = new LightSourceMarker(light.getPosition(), lightColors[i]);
+ shapes.addShape(marker);
+
+ orbitingLights.add(new OrbitingLight(light, marker, 600, 0.002, angleOffset, i));
+ }
+
+ final double offset = -(GRID_SIZE - 1) * SPACING / 2;
+
+ for (int x = 0; x < GRID_SIZE; x++) {
+ for (int y = 0; y < GRID_SIZE; y++) {
+ for (int z = 0; z < GRID_SIZE; z++) {
+ final double px = offset + x * SPACING;
+ final double py = offset + y * SPACING;
+ final double pz = offset + z * SPACING;
+
+ final Color color = new Color(
+ 150 + random.nextInt(105),
+ 150 + random.nextInt(105),
+ 150 + random.nextInt(105)
+ );
+
+ final SolidPolygonCube cube = new SolidPolygonCube(
+ new Point3D(px, py, pz),
+ CUBE_SIZE,
+ color
+ );
+ cube.setShadingEnabled(true);
+ shapes.addShape(cube);
+ cubes.add(cube);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void teardown(final ShapeCollection shapes) {
+ shapes.clear();
+ cubes.clear();
+
+ for (final OrbitingLight ol : orbitingLights) {
+ if (viewPanel != null) {
+ viewPanel.getLightingManager().removeLight(ol.light);
+ }
+ }
+ orbitingLights.clear();
+
+ if (viewPanel != null && animator != null) {
+ viewPanel.removeFrameListener(animator);
+ }
+ viewPanel = null;
+ animator = null;
+ }
+
+ /**
+ * Sets the view panel for animation callbacks.
+ * @param viewPanel the view panel
+ */
+ public void setViewPanel(final ViewPanel viewPanel) {
+ this.viewPanel = viewPanel;
+ if (viewPanel != null) {
+ // Configure global lighting
+ LightingManager lightingManager = viewPanel.getLightingManager();
+ lightingManager.setAmbientLight(new Color(15, 15, 20));
+ for (final OrbitingLight ol : orbitingLights) {
+ lightingManager.addLight(ol.light);
+ }
+
+ animator = new LightAnimator();
+ viewPanel.addFrameListener(animator);
+ }
+ }
+
+ private static class OrbitingLight {
+ final LightSource light;
+ final LightSourceMarker marker;
+ final double orbitRadius;
+ final double speed;
+ final int axisIndex;
+ double angle;
+
+ OrbitingLight(final LightSource light, final LightSourceMarker marker,
+ final double orbitRadius, final double speed, final double angleOffset, final int axisIndex) {
+ this.light = light;
+ this.marker = marker;
+ this.orbitRadius = orbitRadius;
+ this.speed = speed;
+ this.angle = angleOffset;
+ this.axisIndex = axisIndex;
+ }
+ }
+
+ private class LightAnimator implements FrameListener {
+ @Override
+ public boolean onFrame(final ViewPanel vp, final int millisecondsSinceLastFrame) {
+ for (final OrbitingLight ol : orbitingLights) {
+ ol.angle += ol.speed * millisecondsSinceLastFrame;
+
+ double x, y, z;
+ switch (ol.axisIndex) {
+ case 0:
+ x = ol.orbitRadius * Math.cos(ol.angle);
+ y = ol.orbitRadius * 0.3 * Math.sin(ol.angle * 2);
+ z = ol.orbitRadius * Math.sin(ol.angle);
+ break;
+ case 1:
+ x = ol.orbitRadius * Math.sin(ol.angle);
+ y = ol.orbitRadius * Math.cos(ol.angle);
+ z = ol.orbitRadius * 0.3 * Math.sin(ol.angle * 2);
+ break;
+ default:
+ x = ol.orbitRadius * 0.3 * Math.sin(ol.angle * 2);
+ y = ol.orbitRadius * Math.sin(ol.angle);
+ z = ol.orbitRadius * Math.cos(ol.angle);
+ break;
+ }
+
+ final Point3D newPos = new Point3D(x, y, z);
+ ol.light.setPosition(newPos);
+ ol.marker.setTransform(fromAngles(newPos, 0, 0));
+ }
+ return true;
+ }
+ }
+}
\ No newline at end of file
--- /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.benchmark;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCube;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Benchmark test for solid-color cubes.
+ * Renders a grid of cubes with random semi-transparent colors to test
+ * solid polygon rasterization performance.
+ */
+public class SolidCubesTest implements BenchmarkTest {
+
+ /**
+ * Creates a new SolidCubesTest instance.
+ */
+ public SolidCubesTest() {
+ }
+
+ private static final int GRID_SIZE = 16;
+ private static final double SPACING = 80;
+ private static final double CUBE_SIZE = 25;
+ private static final long RANDOM_SEED = 42;
+
+ private final Random random = new Random(RANDOM_SEED);
+ private final List<Object> cubes = new ArrayList<>();
+
+ @Override
+ public String getName() {
+ return "Solid Cubes";
+ }
+
+ @Override
+ public void setup(ShapeCollection shapes) {
+ random.setSeed(RANDOM_SEED);
+ double offset = -(GRID_SIZE - 1) * SPACING / 2;
+
+ for (int x = 0; x < GRID_SIZE; x++) {
+ for (int y = 0; y < GRID_SIZE; y++) {
+ for (int z = 0; z < GRID_SIZE; z++) {
+ double px = offset + x * SPACING;
+ double py = offset + y * SPACING;
+ double pz = offset + z * SPACING;
+
+ Color color = new Color(
+ 50 + random.nextInt(150),
+ 100 + random.nextInt(155),
+ 50 + random.nextInt(150),
+ 40
+ );
+ SolidPolygonCube cube = new SolidPolygonCube(
+ new Point3D(px, py, pz),
+ CUBE_SIZE,
+ color
+ );
+ shapes.addShape(cube);
+ cubes.add(cube);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void teardown(ShapeCollection shapes) {
+ shapes.clear();
+ cubes.clear();
+ }
+}
\ No newline at end of file
--- /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.benchmark;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.GlowingPoint;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Benchmark test for glowing point (star) billboards.
+ * Renders a grid of glowing points arranged in a cube formation to test
+ * billboard rendering and texture blending performance.
+ */
+public class StarGridTest implements BenchmarkTest {
+
+ /**
+ * Creates a new StarGridTest instance.
+ */
+ public StarGridTest() {
+ }
+
+ private static final int GRID_SIZE = 16;
+ private static final double SPACING = 80;
+ private static final double STAR_SIZE = 20;
+ private static final int UNIQUE_COLORS_COUNT = 30;
+ private static final long RANDOM_SEED = 42;
+
+ private final Random random = new Random(RANDOM_SEED);
+ private final List<Object> stars = new ArrayList<>();
+ private List<Color> colors;
+
+ @Override
+ public String getName() {
+ return "Star Grid";
+ }
+
+ @Override
+ public void setup(ShapeCollection shapes) {
+ initializeColors();
+ random.setSeed(RANDOM_SEED);
+ double offset = -(GRID_SIZE - 1) * SPACING / 2;
+
+ for (int x = 0; x < GRID_SIZE; x++) {
+ for (int y = 0; y < GRID_SIZE; y++) {
+ for (int z = 0; z < GRID_SIZE; z++) {
+ double px = offset + x * SPACING;
+ double py = offset + y * SPACING;
+ double pz = offset + z * SPACING;
+
+ Color color = colors.get(random.nextInt(colors.size()));
+ GlowingPoint star = new GlowingPoint(
+ new Point3D(px, py, pz),
+ STAR_SIZE,
+ color
+ );
+ shapes.addShape(star);
+ stars.add(star);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void teardown(ShapeCollection shapes) {
+ shapes.clear();
+ stars.clear();
+ }
+
+ private void initializeColors() {
+ colors = new ArrayList<>();
+ random.setSeed(RANDOM_SEED);
+ for (int i = 0; i < UNIQUE_COLORS_COUNT; i++) {
+ colors.add(new Color(
+ random.nextDouble() + 0.5,
+ random.nextDouble() + 0.5,
+ random.nextDouble() + 0.5,
+ 255
+ ));
+ }
+ }
+}
\ No newline at end of file
--- /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.benchmark;
+
+/**
+ * Holds the results of a single benchmark test.
+ */
+public class TestResult {
+
+ /** The name of the benchmark test. */
+ public final String testName;
+
+ /** Total number of frames rendered during the test. */
+ public final long frameCount;
+
+ /** Duration of the test in seconds. */
+ public final double durationSeconds;
+
+ /** Average frames per second achieved during the test. */
+ public final double averageFps;
+
+ /**
+ * Creates a test result record.
+ * @param testName the name of the test
+ * @param frameCount total frames rendered
+ * @param durationSeconds test duration in seconds
+ */
+ public TestResult(String testName, long frameCount, double durationSeconds) {
+ this.testName = testName;
+ this.frameCount = frameCount;
+ this.durationSeconds = durationSeconds;
+ this.averageFps = frameCount / durationSeconds;
+ }
+}
\ No newline at end of file
--- /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.benchmark;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.math.Vertex;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedTriangle;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+/**
+ * A cube composed of textured polygons.
+ * Used by the {@link TexturedCubesTest} benchmark to measure texture rendering performance.
+ */
+public class TexturedCube extends AbstractCompositeShape {
+
+ /**
+ * Creates a textured cube centered at the given position.
+ * @param center the center position of the cube
+ * @param size half the edge length of the cube
+ * @param texture the texture to apply to all faces
+ */
+ public TexturedCube(Point3D center, double size, Texture texture) {
+ double s = size;
+ Point3D p1 = new Point3D(center.x - s, center.y - s, center.z - s);
+ Point3D p7 = new Point3D(center.x + s, center.y + s, center.z + s);
+
+ Point3D p2 = new Point3D(p7.x, p1.y, p1.z);
+ Point3D p3 = new Point3D(p7.x, p1.y, p7.z);
+ Point3D p4 = new Point3D(p1.x, p1.y, p7.z);
+ Point3D p5 = new Point3D(p1.x, p7.y, p1.z);
+ Point3D p6 = new Point3D(p7.x, p7.y, p1.z);
+ Point3D p8 = new Point3D(p1.x, p7.y, p7.z);
+
+ Point2D t00 = new Point2D(0, 0);
+ Point2D t10 = new Point2D(64, 0);
+ Point2D t01 = new Point2D(0, 64);
+ Point2D t11 = new Point2D(64, 64);
+
+ addTexturedFace(p1, p2, p3, p4, t00, t10, t11, t01, texture);
+ addTexturedFace(p5, p8, p7, p6, t00, t01, t11, t10, texture);
+ addTexturedFace(p1, p5, p6, p2, t00, t01, t11, t10, texture);
+ addTexturedFace(p3, p7, p8, p4, t00, t10, t11, t01, texture);
+ addTexturedFace(p1, p4, p8, p5, t00, t10, t11, t01, texture);
+ addTexturedFace(p2, p6, p7, p3, t00, t01, t11, t10, texture);
+
+ setBackfaceCulling(true);
+ }
+
+ private void addTexturedFace(Point3D p1, Point3D p2, Point3D p3, Point3D p4,
+ Point2D t1, Point2D t2, Point2D t3, Point2D t4,
+ Texture texture) {
+ TexturedTriangle tri1 = new TexturedTriangle(
+ new Vertex(p1, t1),
+ new Vertex(p2, t2),
+ new Vertex(p3, t3),
+ texture
+ );
+ tri1.setBackfaceCulling(true);
+
+ TexturedTriangle tri2 = new TexturedTriangle(
+ new Vertex(p1, t1),
+ new Vertex(p3, t3),
+ new Vertex(p4, t4),
+ texture
+ );
+ tri2.setBackfaceCulling(true);
+
+ addShape(tri1);
+ addShape(tri2);
+ }
+}
\ No newline at end of file
--- /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.benchmark;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Benchmark test for textured cubes.
+ * Renders a grid of cubes with textures to test textured polygon rendering
+ * and texture sampling performance.
+ */
+public class TexturedCubesTest implements BenchmarkTest {
+
+ /**
+ * Creates a new TexturedCubesTest instance.
+ */
+ public TexturedCubesTest() {
+ }
+
+ private static final int GRID_SIZE = 16;
+ private static final double SPACING = 80;
+ private static final double CUBE_SIZE = 25;
+ private static final int TEXTURE_COUNT = 20;
+ private static final long RANDOM_SEED = 42;
+
+ private final Random random = new Random(RANDOM_SEED);
+ private final List<Object> cubes = new ArrayList<>();
+ private Texture[] textures;
+ private int[] cubeTextureIndices;
+
+ @Override
+ public String getName() {
+ return "Textured Cubes";
+ }
+
+ @Override
+ public void setup(ShapeCollection shapes) {
+ initializeTextures();
+ initializeCubeTextureIndices();
+
+ double offset = -(GRID_SIZE - 1) * SPACING / 2;
+ int idx = 0;
+
+ for (int x = 0; x < GRID_SIZE; x++) {
+ for (int y = 0; y < GRID_SIZE; y++) {
+ for (int z = 0; z < GRID_SIZE; z++) {
+ double px = offset + x * SPACING;
+ double py = offset + y * SPACING;
+ double pz = offset + z * SPACING;
+
+ Texture tex = textures[cubeTextureIndices[idx]];
+ TexturedCube cube = new TexturedCube(
+ new Point3D(px, py, pz),
+ CUBE_SIZE,
+ tex
+ );
+ shapes.addShape(cube);
+ cubes.add(cube);
+ idx++;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void teardown(ShapeCollection shapes) {
+ shapes.clear();
+ cubes.clear();
+ }
+
+ private void initializeTextures() {
+ textures = new Texture[TEXTURE_COUNT];
+ for (int i = 0; i < TEXTURE_COUNT; i++) {
+ textures[i] = createGlowTexture(
+ 50 + random.nextInt(200),
+ 50 + random.nextInt(200),
+ 50 + random.nextInt(200)
+ );
+ }
+ }
+
+ private void initializeCubeTextureIndices() {
+ random.setSeed(RANDOM_SEED);
+ cubeTextureIndices = new int[GRID_SIZE * GRID_SIZE * GRID_SIZE];
+ for (int i = 0; i < cubeTextureIndices.length; i++) {
+ cubeTextureIndices[i] = random.nextInt(TEXTURE_COUNT);
+ }
+ }
+
+ private Texture createGlowTexture(int r, int g, int b) {
+ int texSize = 64;
+ Texture texture = new Texture(texSize, texSize, 2);
+
+ java.awt.Graphics2D gr = texture.graphics;
+ gr.setBackground(new java.awt.Color(r, g, b, 30));
+ gr.clearRect(0, 0, texSize, texSize);
+
+ int glowWidth = 6;
+ for (int i = 0; i < glowWidth; i++) {
+ int intensity = (int) (120.0 * (glowWidth - i) / glowWidth);
+ java.awt.Color glowColor = new java.awt.Color(
+ Math.min(255, r + intensity),
+ Math.min(255, g + intensity),
+ Math.min(255, b + intensity),
+ 50 - i * 8
+ );
+ gr.setColor(glowColor);
+ gr.drawRect(i, i, texSize - 1 - 2 * i, texSize - 1 - 2 * i);
+ }
+
+ gr.dispose();
+ texture.resetResampledBitmapCache();
+ return texture;
+ }
+}
\ No newline at end of file
--- /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.benchmark;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.WireframeBox;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Benchmark test for wireframe cubes.
+ * Renders a grid of wireframe cube outlines to test line rendering performance.
+ */
+public class WireframeCubesTest implements BenchmarkTest {
+
+ /**
+ * Creates a new WireframeCubesTest instance.
+ */
+ public WireframeCubesTest() {
+ }
+
+ private static final int GRID_SIZE = 16;
+ private static final double SPACING = 80;
+ private static final double CUBE_SIZE = 25;
+ private static final long RANDOM_SEED = 42;
+
+ private final Random random = new Random(RANDOM_SEED);
+ private final List<Object> cubes = new ArrayList<>();
+
+ @Override
+ public String getName() {
+ return "Wireframe Cubes";
+ }
+
+ @Override
+ public void setup(ShapeCollection shapes) {
+ random.setSeed(RANDOM_SEED);
+ double offset = -(GRID_SIZE - 1) * SPACING / 2;
+
+ for (int x = 0; x < GRID_SIZE; x++) {
+ for (int y = 0; y < GRID_SIZE; y++) {
+ for (int z = 0; z < GRID_SIZE; z++) {
+ double px = offset + x * SPACING;
+ double py = offset + y * SPACING;
+ double pz = offset + z * SPACING;
+
+ Color color = new Color(
+ 100 + random.nextInt(155),
+ 100 + random.nextInt(155),
+ 100 + random.nextInt(155),
+ 50
+ );
+ LineAppearance appearance = new LineAppearance(5.5, color);
+
+ Point3D center = new Point3D(px, py, pz);
+ Point3D p1 = new Point3D(center.x - CUBE_SIZE, center.y - CUBE_SIZE, center.z - CUBE_SIZE);
+ Point3D p2 = new Point3D(center.x + CUBE_SIZE, center.y + CUBE_SIZE, center.z + CUBE_SIZE);
+
+ WireframeBox cube = new WireframeBox(p1, p2, appearance);
+ shapes.addShape(cube);
+ cubes.add(cube);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void teardown(ShapeCollection shapes) {
+ shapes.clear();
+ cubes.clear();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+/**
+ * Graphics benchmark components for measuring the Sixth 3D engine's rendering performance.
+ *
+ * <p>The benchmark suite includes:</p>
+ * <ul>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.benchmark.GraphicsBenchmark} - Main benchmark runner</li>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.benchmark.BenchmarkTest} - Interface for test implementations</li>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.benchmark.SolidCubesTest} - Solid-color cube rendering test</li>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.benchmark.TexturedCubesTest} - Textured cube rendering test</li>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.benchmark.WireframeCubesTest} - Wireframe cube rendering test</li>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.benchmark.StarGridTest} - Billboard (glowing point) rendering test</li>
+ * </ul>
+ */
+
+package eu.svjatoslav.sixth.e3d.examples.benchmark;
\ No newline at end of file
--- /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.essentials;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.TextPointer;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.math.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCube;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonSphere;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin;
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.*;
+
+/**
+ * Demo showcasing Constructive Solid Geometry (CSG) boolean operations.
+ *
+ * <p>This demo displays three CSG operations side by side:</p>
+ * <ul>
+ * <li><b>Subtract:</b> A cube with a spherical cavity carved out</li>
+ * <li><b>Union:</b> A cube merged with a sphere</li>
+ * <li><b>Intersect:</b> The volume shared by a cube and sphere</li>
+ * </ul>
+ *
+ * <p>All operations use a consistent color scheme: red for the first argument
+ * (cube) and blue for the second argument (sphere). This makes it easy to see
+ * which parts of the result came from which input shape.</p>
+ *
+ * <p><b>Run this demo:</b></p>
+ * <pre>{@code
+ * java -cp target/sixth-3d-demos.jar eu.svjatoslav.sixth.e3d.examples.essentials.CSGDemo
+ * }</pre>
+ *
+ * @see eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape#union
+ * @see eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape#subtract
+ * @see eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape#intersect
+ */
+public class CSGDemo {
+
+ /**
+ * Distance between shapes on the X axis.
+ */
+ private static final double SPACING = 400;
+
+ // ==================== SHAPE FACTORY METHODS ====================
+
+ /**
+ * Creates a cube for CSG operations with current demo parameters.
+ *
+ * <p>The cube is created with shading enabled for realistic lighting.</p>
+ *
+ * @return a new SolidPolygonCube centered at origin with shading enabled
+ */
+ private static SolidPolygonCube createCube() {
+ final SolidPolygonCube cube = new SolidPolygonCube(origin(), 80, hex("39FF14D9"));
+ cube.setShadingEnabled(true);
+ return cube;
+ }
+
+ /**
+ * Creates a sphere for CSG operations with current demo parameters.
+ *
+ * <p>The sphere is created with shading disabled, causing it to glow
+ * at full intensity regardless of lighting conditions.</p>
+ *
+ * @return a new SolidPolygonSphere centered at origin with shading disabled
+ */
+ private static SolidPolygonSphere createSphere() {
+ final SolidPolygonSphere sphere = new SolidPolygonSphere(origin(), 96, 12, hex("FF6600C0"));
+ sphere.setShadingEnabled(false);
+ return sphere;
+ }
+
+ // ==================== MAIN ENTRY POINT ====================
+
+ /**
+ * Entry point for the CSG demo.
+ *
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+ final ViewFrame viewFrame = new ViewFrame("CSG demo - Boolean operations");
+ final ViewPanel viewPanel = viewFrame.getViewPanel();
+ final ShapeCollection shapes = viewPanel.getRootShapeCollection();
+
+ // Position camera to view all three shapes
+ viewPanel.getCamera().getTransform().set(-244.24, 254.40, -458.83, -0.26, 0.24, -0.00);
+
+ // Set up lighting
+ viewPanel.getLightingManager().setAmbientLight(hex("0D0D0D"));
+
+ // Create lights
+ createLights(viewPanel, shapes);
+
+ // Create the three CSG demonstrations
+ createSubtractDemo(shapes, point(-SPACING, 0, 0));
+ createUnionDemo(shapes, origin());
+ createIntersectDemo(shapes, point(SPACING, 0, 0));
+
+ viewPanel.repaintDuringNextViewUpdate();
+ }
+
+ /**
+ * Creates the subtract operation demo: cube - sphere.
+ * Results in a cube with a spherical cavity.
+ *
+ * @param shapes the shape collection
+ * @param location the position for the result
+ */
+ private static void createSubtractDemo(final ShapeCollection shapes, final Point3D location) {
+
+ final SolidPolygonCube cube = createCube();
+ cube.subtract(createSphere());
+
+ shapes.addShape(cube
+ .setTransform(new Transform(location))
+ .setBackfaceCulling(true));
+
+ final String description =
+ "Subtract: Cube - Sphere\n" +
+ "\n" +
+ "Green = Cube (kept)\n" +
+ "Orange = Sphere (carved out)";
+
+ shapes.addShape(createDescriptionPanel(location, description));
+ }
+
+ /**
+ * Creates the union operation demo: cube + sphere.
+ * Results in a combined shape.
+ *
+ * @param shapes the shape collection
+ * @param location the position for the result
+ */
+ private static void createUnionDemo(final ShapeCollection shapes, final Point3D location) {
+
+ final SolidPolygonCube cube = createCube();
+ cube.union(createSphere());
+
+ shapes.addShape(cube
+ .setTransform(new Transform(location))
+ .setBackfaceCulling(true));
+
+ final String description =
+ "Union: Cube + Sphere\n" +
+ "\n" +
+ "Green = Cube\n" +
+ "Orange = Sphere";
+
+ shapes.addShape(createDescriptionPanel(location, description));
+ }
+
+ /**
+ * Creates the intersect operation demo: cube ∩ sphere.
+ * Results in the volume shared by both shapes.
+ *
+ * @param shapes the shape collection
+ * @param location the position for the result
+ */
+ private static void createIntersectDemo(final ShapeCollection shapes, final Point3D location) {
+
+ final SolidPolygonCube cube = createCube();
+ cube.intersect(createSphere());
+
+ shapes.addShape(cube
+ .setTransform(new Transform(location))
+ .setBackfaceCulling(true));
+
+ final String description =
+ "Intersect: Cube ∩ Sphere\n" +
+ "\n" +
+ "Green = from Cube\n" +
+ "Orange = from Sphere";
+
+ shapes.addShape(createDescriptionPanel(location, description));
+ }
+
+ /**
+ * Creates a description panel positioned below a shape.
+ *
+ * @param location the position of the associated shape
+ * @param text the description text to display
+ * @return a TextCanvas positioned below the shape
+ */
+ private static TextCanvas createDescriptionPanel(final Point3D location, final String text) {
+ final Transform transform = Transform.fromAngles(
+ point(location.x, location.y + 220, location.z),
+ 0, 0);
+
+ final TextCanvas panel = new TextCanvas(
+ transform,
+ new TextPointer(5, 35),
+ WHITE,
+ TRANSPARENT);
+
+ panel.setText(text);
+ return panel;
+ }
+
+ /**
+ * Creates lighting for the scene.
+ *
+ * @param viewPanel the view panel
+ * @param shapes the shape collection
+ */
+ private static void createLights(final ViewPanel viewPanel, final ShapeCollection shapes) {
+ // Main light from above-front - bright white
+ final LightSource mainLight = new LightSource(
+ point(0, -300, -400),
+ hex("FFFFFF"),
+ 5.5
+ );
+ viewPanel.getLightingManager().addLight(mainLight);
+
+ // Fill light from the side - warm orange tint
+ final LightSource fillLight = new LightSource(
+ point(500, 100, -200),
+ hex("FF6600"),
+ 5.5
+ );
+ viewPanel.getLightingManager().addLight(fillLight);
+
+ viewPanel.getLightingManager().setAmbientLight(hex("3D3D5D"));
+ }
+}
\ No newline at end of file
--- /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.essentials;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.ForwardOrientedTextBlock;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonArrow;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid3D;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin;
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+
+/**
+ * Demo displaying coordinate system axes using 3D arrows.
+ *
+ * <p>This demo illustrates the Sixth 3D coordinate system with three colored
+ * arrows originating from the origin (0,0,0), each pointing along a positive
+ * axis direction:</p>
+ *
+ * <ul>
+ * <li><b>Red arrow</b> — +X axis (points RIGHT)</li>
+ * <li><b>Green arrow</b> — +Y axis (points DOWN visually)</li>
+ * <li><b>Blue arrow</b> — +Z axis (points AWAY from viewer)</li>
+ * </ul>
+ *
+ * <p>A reference grid at Y=0 provides spatial context. Text labels identify
+ * each axis.</p>
+ *
+ * @see SolidPolygonArrow
+ * @see ForwardOrientedTextBlock
+ */
+public class CoordinateSystemDemo {
+
+ /**
+ * Length of each axis arrow.
+ */
+ private static final double ARROW_LENGTH = 200;
+
+ /**
+ * Radius of the arrow body (cylindrical shaft).
+ */
+ private static final double BODY_RADIUS = 6;
+
+ /**
+ * Distance from arrow tip to text label position.
+ */
+ private static final double LABEL_OFFSET = 20;
+
+ /**
+ * Scale factor for text labels.
+ */
+ private static final double LABEL_SCALE = 20.0;
+
+ /**
+ * Entry point for the coordinate system demo.
+ *
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+ final ViewFrame viewFrame = new ViewFrame("Coordinate System Demo");
+ final ViewPanel viewPanel = viewFrame.getViewPanel();
+ final ShapeCollection shapes = viewPanel.getRootShapeCollection();
+
+ // Position camera to view all three axes
+ viewPanel.getCamera().getTransform().set(130.66, -65.49, -248.18, -0.06, -0.36, -0.00);
+
+ // Create reference grid at Y=0 plane
+ createReferenceGrid(shapes);
+
+ // Create axis arrows
+ createAxisArrows(shapes);
+
+ viewPanel.repaintDuringNextViewUpdate();
+ }
+
+ /**
+ * Creates a reference grid on the Y=0 plane.
+ *
+ * @param shapes the shape collection to add the grid to
+ */
+ private static void createReferenceGrid(final ShapeCollection shapes) {
+ final LineAppearance gridAppearance = new LineAppearance(
+ 1, hex("505064FF"));
+ final Grid3D grid = new Grid3D(
+ point(-300, 0, -300),
+ point(300, 0, 300),
+ 50,
+ gridAppearance
+ );
+ shapes.addShape(grid);
+ }
+
+ /**
+ * Creates the three axis arrows and their labels.
+ *
+ * @param shapes the shape collection to add the arrows to
+ */
+ private static void createAxisArrows(final ShapeCollection shapes) {
+ // X-axis arrow (RED) - points in positive X direction (RIGHT)
+ final SolidPolygonArrow xArrow = new SolidPolygonArrow(
+ origin(),
+ point(ARROW_LENGTH, 0, 0),
+ BODY_RADIUS, hex("FF0000B4")
+ );
+ shapes.addShape(xArrow);
+ createLabel(shapes, "X", point(ARROW_LENGTH + LABEL_OFFSET, 0, 0), Color.RED);
+
+ // Y-axis arrow (GREEN) - points in positive Y direction (DOWN)
+ final SolidPolygonArrow yArrow = new SolidPolygonArrow(
+ origin(),
+ point(0, ARROW_LENGTH, 0),
+ BODY_RADIUS, hex("00FF00B4")
+ );
+ shapes.addShape(yArrow);
+ createLabel(shapes, "Y", point(0, ARROW_LENGTH + LABEL_OFFSET, 0), Color.GREEN);
+
+ // Z-axis arrow (BLUE) - points in positive Z direction (AWAY)
+ final SolidPolygonArrow zArrow = new SolidPolygonArrow(
+ origin(),
+ point(0, 0, ARROW_LENGTH),
+ BODY_RADIUS, hex("0000FFB4")
+ );
+ shapes.addShape(zArrow);
+ createLabel(shapes, "Z", point(0, 0, ARROW_LENGTH + LABEL_OFFSET), Color.BLUE);
+ }
+
+ /**
+ * Creates a text label at the specified position.
+ *
+ * @param shapes the shape collection to add the label to
+ * @param text the label text
+ * @param position the 3D position of the label
+ * @param color the text color
+ */
+ private static void createLabel(final ShapeCollection shapes,
+ final String text,
+ final Point3D position,
+ final Color color) {
+ final ForwardOrientedTextBlock label = new ForwardOrientedTextBlock(
+ position, LABEL_SCALE, 2, text, color
+ );
+ shapes.addShape(label);
+ }
+}
\ No newline at end of file
--- /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.essentials;
+
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.math.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonRectangularBox;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+
+/**
+ * Minimal example demonstrating how to create a basic 3D scene.
+ * <p>
+ * Creates a window with a single red box. This is the "Create Your First 3D Scene"
+ * example from the Sixth 3D documentation.
+ */
+public class MinimalExample {
+
+ /**
+ * Entry point for the minimal scene demo.
+ *
+ * @param args command line arguments (ignored)
+ */
+ public static void main(String[] args) {
+ ViewFrame viewFrame = new ViewFrame("Minimal example");
+ ShapeCollection shapes = viewFrame.getViewPanel().getRootShapeCollection();
+
+ viewFrame.getViewPanel().getCamera().getTransform().setTranslation(point(0, -100, -300));
+
+ final Transform boxTransform = new Transform();
+ SolidPolygonRectangularBox box = new SolidPolygonRectangularBox(
+ point(-50, -50, -50),
+ point(50, 50, 50),
+ Color.RED
+ );
+ box.setTransform(boxTransform);
+ shapes.addShape(box);
+
+ viewFrame.getViewPanel().repaintDuringNextViewUpdate();
+ }
+}
\ No newline at end of file
--- /dev/null
+package eu.svjatoslav.sixth.e3d.examples.essentials;
+
+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;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.ForwardOrientedTextBlock;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.LightSourceMarker;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.*;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.math.Transform.fromAngles;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+
+/**
+ * Gallery demo showcasing all available 3D shapes in the Sixth 3D engine.
+ *
+ * <p>This demo displays a 7x2 grid of shapes organized by type (columns) and
+ * rendering style (rows). The top row shows solid polygon shapes, and the bottom
+ * row shows wireframe shapes. Each column represents a different shape type.</p>
+ *
+ * <p>Features:</p>
+ * <ul>
+ * <li>Orbiting colored lights for dynamic lighting demonstration</li>
+ * <li>Floor grid showing the layout structure</li>
+ * <li>Text labels identifying each shape type</li>
+ * </ul>
+ */
+public class ShapeGalleryDemo {
+
+ /**
+ * Number of columns (shape types).
+ */
+ private static final int COLUMN_COUNT = 7;
+
+ /**
+ * Size of each cell in world units.
+ */
+ private static final double CELL_SIZE = 250;
+
+ /**
+ * Radius/size for most shapes.
+ */
+ private static final double SHAPE_RADIUS = 50;
+
+ /**
+ * Number of segments for smooth curved shapes.
+ */
+ private static final int SEGMENTS = 16;
+
+ /**
+ * Y offset for labels above shapes.
+ */
+ private static final double LABEL_Y_OFFSET = -100;
+
+ /**
+ * Number of orbiting light sources in the scene.
+ */
+ private static final int LIGHT_COUNT = 10;
+
+ /**
+ * Color palette - one color per column (shape type).
+ */
+ private static final Color[] SHAPE_COLORS = {
+ hex("FF6464FF"), // Arrow - Red
+ hex("FFB464FF"), // Cone - Orange
+ hex("FFFF64FF"), // Cube - Yellow
+ hex("64FF64FF"), // Cylinder - Green
+ hex("64FFFFFF"), // Pyramid - Cyan
+ hex("6496FFFF"), // RectangularBox - Blue
+ hex("C896FFFF"), // Sphere - Purple
+ };
+
+ /**
+ * Shape type names for labels.
+ */
+ private static final String[] SHAPE_NAMES = {
+ "Arrow",
+ "Cone",
+ "Cube",
+ "Cylinder",
+ "Pyramid",
+ "Box",
+ "Sphere",
+ };
+
+ /**
+ * Entry point for the shaded shapes demo.
+ *
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+ final ViewFrame viewFrame = new ViewFrame("3D Shape Gallery");
+ final ViewPanel viewPanel = viewFrame.getViewPanel();
+ final ShapeCollection shapes = viewPanel.getRootShapeCollection();
+
+ // Position camera to view the entire gallery
+ viewPanel.getCamera().getTransform().set(-615.31, -299.50, -378.64, -0.62, -0.66, 0.00);
+
+ // Set up lighting
+ viewPanel.getLightingManager().setAmbientLight(hex("282832FF"));
+
+ // Create floor grid
+ createFloorGrid(shapes);
+
+ // Create all shapes
+ createAllShapes(shapes);
+
+ // Create orbiting lights
+ createOrbitingLights(viewPanel, shapes);
+
+ viewPanel.repaintDuringNextViewUpdate();
+ }
+
+ /**
+ * Creates the floor grid that outlines the gallery cells.
+ *
+ * @param shapes the shape collection to add the grid to
+ */
+ private static void createFloorGrid(final ShapeCollection shapes) {
+ final LineAppearance gridAppearance = new LineAppearance(1, hex("505064FF"));
+
+ // Calculate grid bounds: 7 columns, 2 rows, centered around origin
+ final double halfWidth = (COLUMN_COUNT * CELL_SIZE) / 2.0;
+ final double gridDepth = 2 * CELL_SIZE;
+
+ final Point3D cornerA = point(-halfWidth, 0, -CELL_SIZE / 2);
+ final Point3D cornerB = point(halfWidth, 0, gridDepth - CELL_SIZE / 2);
+
+ final Grid3D grid = new Grid3D(cornerA, cornerB, CELL_SIZE, gridAppearance);
+ shapes.addShape(grid);
+ }
+
+ /**
+ * Creates all solid and wireframe shapes in the gallery grid.
+ *
+ * @param shapes the shape collection to add shapes to
+ */
+ private static void createAllShapes(final ShapeCollection shapes) {
+ for (int col = 0; col < COLUMN_COUNT; col++) {
+ final double x = getColumnX(col);
+ final Color color = SHAPE_COLORS[col];
+ final String name = SHAPE_NAMES[col];
+
+ // Row 0: Solid polygon shapes
+ final Point3D solidPos = point(x, 0, 0);
+ createSolidShape(shapes, col, solidPos, color);
+ createLabel(shapes, solidPos, name, color);
+
+ // Row 1: Wireframe shapes
+ final Point3D wireframePos = point(x, 0, CELL_SIZE);
+ createWireframeShape(shapes, col, wireframePos, color);
+ createLabel(shapes, wireframePos, "Wireframe " + name, color);
+ }
+ }
+
+ /**
+ * Calculates the X coordinate for a given column index.
+ *
+ * @param col the column index (0-6)
+ * @return the X coordinate in world units
+ */
+ private static double getColumnX(final int col) {
+ return (col - (COLUMN_COUNT - 1) / 2.0) * CELL_SIZE;
+ }
+
+ /**
+ * Creates a solid polygon shape for the given column.
+ *
+ * @param shapes the shape collection
+ * @param col the column index (determines shape type)
+ * @param pos the center position
+ * @param color the shape color
+ */
+ private static void createSolidShape(final ShapeCollection shapes, final int col,
+ final Point3D pos, final Color color) {
+ switch (col) {
+ case 0: // Arrow
+ final SolidPolygonArrow arrow = new SolidPolygonArrow(
+ point(pos.x, SHAPE_RADIUS, pos.z),
+ point(pos.x, -SHAPE_RADIUS, pos.z),
+ 8, color);
+ arrow.setShadingEnabled(true);
+ shapes.addShape(arrow);
+ break;
+
+ case 1: // Cone (pointing upward)
+ final SolidPolygonCone cone = new SolidPolygonCone(
+ point(pos.x, -SHAPE_RADIUS, pos.z), // apex above
+ point(pos.x, SHAPE_RADIUS / 2, pos.z), // base below
+ SHAPE_RADIUS * 0.8, SEGMENTS, color);
+ cone.setShadingEnabled(true);
+ shapes.addShape(cone);
+ break;
+
+ case 2: // Cube
+ final SolidPolygonCube cube = new SolidPolygonCube(pos, SHAPE_RADIUS * 0.8, color);
+ cube.setShadingEnabled(true);
+ shapes.addShape(cube);
+ break;
+
+ case 3: // Cylinder
+ final SolidPolygonCylinder cylinder = new SolidPolygonCylinder(
+ point(pos.x, SHAPE_RADIUS, pos.z),
+ point(pos.x, -SHAPE_RADIUS, pos.z),
+ SHAPE_RADIUS * 0.7, SEGMENTS, color);
+ cylinder.setShadingEnabled(true);
+ shapes.addShape(cylinder);
+ break;
+
+ case 4: // Pyramid
+ final SolidPolygonPyramid pyramid = new SolidPolygonPyramid(
+ point(pos.x, -SHAPE_RADIUS, pos.z), // apex above
+ point(pos.x, SHAPE_RADIUS / 2, pos.z), // base below
+ SHAPE_RADIUS * 0.8, color);
+ pyramid.setShadingEnabled(true);
+ shapes.addShape(pyramid);
+ break;
+
+ case 5: // RectangularBox
+ final SolidPolygonRectangularBox box = new SolidPolygonRectangularBox(
+ point(pos.x - SHAPE_RADIUS * 0.35, pos.y - SHAPE_RADIUS, pos.z - SHAPE_RADIUS * 0.35),
+ point(pos.x + SHAPE_RADIUS * 0.35, pos.y + SHAPE_RADIUS, pos.z + SHAPE_RADIUS * 0.35),
+ color);
+ box.setShadingEnabled(true);
+ shapes.addShape(box);
+ break;
+
+ case 6: // Sphere
+ final SolidPolygonSphere sphere = new SolidPolygonSphere(pos, SHAPE_RADIUS, SEGMENTS, color);
+ sphere.setShadingEnabled(true);
+ shapes.addShape(sphere);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Creates a wireframe shape for the given column.
+ *
+ * @param shapes the shape collection
+ * @param col the column index (determines shape type)
+ * @param pos the center position
+ * @param color the line color
+ */
+ private static void createWireframeShape(final ShapeCollection shapes, final int col,
+ final Point3D pos, final Color color) {
+ final LineAppearance appearance = new LineAppearance(2, color);
+
+ switch (col) {
+ case 0: // Arrow
+ final WireframeArrow arrow = new WireframeArrow(
+ point(pos.x, SHAPE_RADIUS, pos.z),
+ point(pos.x, -SHAPE_RADIUS, pos.z),
+ 8, appearance);
+ shapes.addShape(arrow);
+ break;
+
+ case 1: // Cone
+ final WireframeCone cone = new WireframeCone(
+ point(pos.x, -SHAPE_RADIUS, pos.z), // apex above
+ point(pos.x, SHAPE_RADIUS / 2, pos.z), // base below
+ SHAPE_RADIUS * 0.8, SEGMENTS, appearance);
+ shapes.addShape(cone);
+ break;
+
+ case 2: // Cube
+ final WireframeCube cube = new WireframeCube(pos, SHAPE_RADIUS * 0.8, appearance);
+ shapes.addShape(cube);
+ break;
+
+ case 3: // Cylinder
+ final WireframeCylinder cylinder = new WireframeCylinder(
+ point(pos.x, SHAPE_RADIUS, pos.z),
+ point(pos.x, -SHAPE_RADIUS, pos.z),
+ SHAPE_RADIUS * 0.7, SEGMENTS, appearance);
+ shapes.addShape(cylinder);
+ break;
+
+ case 4: // Pyramid
+ final WireframePyramid pyramid = new WireframePyramid(
+ point(pos.x, -SHAPE_RADIUS, pos.z), // apex above
+ point(pos.x, SHAPE_RADIUS / 2, pos.z), // base below
+ SHAPE_RADIUS * 0.8, appearance);
+ shapes.addShape(pyramid);
+ break;
+
+ case 5: // Box (WireframeBox)
+ final WireframeBox box = new WireframeBox(
+ point(pos.x - SHAPE_RADIUS * 0.35, pos.y - SHAPE_RADIUS, pos.z - SHAPE_RADIUS * 0.35),
+ point(pos.x + SHAPE_RADIUS * 0.35, pos.y + SHAPE_RADIUS, pos.z + SHAPE_RADIUS * 0.35),
+ appearance);
+ shapes.addShape(box);
+ break;
+
+ case 6: // Sphere
+ final WireframeSphere sphere = new WireframeSphere(pos, (float) SHAPE_RADIUS, appearance);
+ shapes.addShape(sphere);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Creates a text label above a shape.
+ *
+ * @param shapes the shape collection
+ * @param pos the shape position
+ * @param text the label text
+ * @param color the text color
+ */
+ private static void createLabel(final ShapeCollection shapes, final Point3D pos,
+ final String text, final Color color) {
+ final Point3D labelPos = point(pos.x, pos.y + LABEL_Y_OFFSET, pos.z);
+ final ForwardOrientedTextBlock label = new ForwardOrientedTextBlock(
+ labelPos, 8.0, 2, text, color);
+ shapes.addShape(label);
+ }
+
+ /**
+ * Creates orbiting light sources around the scene.
+ *
+ * @param viewPanel the view panel
+ * @param shapes the shape collection
+ */
+ private static void createOrbitingLights(final ViewPanel viewPanel, final ShapeCollection shapes) {
+ final Random random = new Random(42);
+ final List<OrbitingLight> orbitingLights = new ArrayList<>();
+
+ for (int i = 0; i < LIGHT_COUNT; i++) {
+ final Color color = new Color(
+ random.nextInt(256),
+ random.nextInt(256),
+ random.nextInt(256)
+ );
+
+ final double orbitRadius = 600 + random.nextInt(200);
+ final double speed = 0.01 + random.nextDouble() * 0.02;
+ final double angleOffset = random.nextDouble() * Math.PI * 2;
+ final double intensity = 2.0 + random.nextDouble() * 2.0;
+
+ final int axis = random.nextInt(3);
+ final double ellipseFactor = 0.7 + random.nextDouble() * 0.3;
+
+ final LightSource light = new LightSource(point(0, 0, CELL_SIZE / 2), color, intensity);
+ final LightSourceMarker marker = new LightSourceMarker(light.getPosition(), color);
+
+ viewPanel.getLightingManager().addLight(light);
+ shapes.addShape(marker);
+
+ orbitingLights.add(new OrbitingLight(
+ light, marker,
+ orbitRadius, speed, angleOffset, axis, ellipseFactor
+ ));
+ }
+
+ final MultiLightAnimator animator = new MultiLightAnimator(orbitingLights);
+ viewPanel.addFrameListener(animator);
+ }
+
+ /**
+ * Represents a light source that orbits around the scene center.
+ */
+ private static class OrbitingLight {
+ final LightSource light;
+ final LightSourceMarker marker;
+ final double orbitRadius;
+ final double speed;
+ final int axis;
+ final double ellipseFactor;
+ double angle;
+
+ OrbitingLight(final LightSource light, final LightSourceMarker marker,
+ final double orbitRadius, final double speed, final double angleOffset,
+ final int axis, final double ellipseFactor) {
+ this.light = light;
+ this.marker = marker;
+ this.orbitRadius = orbitRadius;
+ this.speed = speed;
+ this.angle = angleOffset;
+ this.axis = axis;
+ this.ellipseFactor = ellipseFactor;
+ }
+ }
+
+ /**
+ * Frame listener that animates all orbiting lights.
+ */
+ private record MultiLightAnimator(List<OrbitingLight> lights) implements FrameListener {
+
+ @Override
+ public boolean onFrame(final ViewPanel viewPanel, final int millisecondsSinceLastFrame) {
+ final double centerY = CELL_SIZE / 2;
+
+ for (final OrbitingLight orbitingLight : lights) {
+ orbitingLight.angle += orbitingLight.speed * millisecondsSinceLastFrame / 100;
+
+ final double r = orbitingLight.orbitRadius;
+ final double e = orbitingLight.ellipseFactor;
+
+ double x, y, z;
+ switch (orbitingLight.axis) {
+ case 0:
+ x = r * Math.cos(orbitingLight.angle);
+ y = r * e * Math.sin(orbitingLight.angle);
+ z = r * 0.5 * Math.sin(orbitingLight.angle * 2);
+ break;
+ case 1:
+ x = r * e * Math.sin(orbitingLight.angle * 2);
+ y = r * Math.cos(orbitingLight.angle);
+ z = r * 0.5 * Math.sin(orbitingLight.angle);
+ break;
+ default:
+ x = r * 0.5 * Math.sin(orbitingLight.angle);
+ y = r * Math.sin(orbitingLight.angle * 2);
+ z = r * e * Math.cos(orbitingLight.angle);
+ break;
+ }
+
+ final Point3D newPosition = point(x, y + centerY, z);
+ orbitingLight.light.setPosition(newPosition);
+ orbitingLight.marker.setTransform(fromAngles(
+ newPosition.x, newPosition.y, newPosition.z, 0, 0, 0));
+ }
+ return true;
+ }
+ }
+}
\ No newline at end of file
--- /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.essentials;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+
+/**
+ * Demo to test winding order and backface culling documentation.
+ * <p>
+ * Creates one triangle with CCW winding (front face) following the docs:
+ * <ul>
+ * <li>upper-center → lower-left → lower-right</li>
+ * <li>Backface culling enabled</li>
+ * </ul>
+ * <p>
+ * Expected: green triangle visible (CCW = front face).
+ */
+public class WindingOrderDemo {
+
+ /**
+ * Creates a new WindingOrderDemo instance.
+ */
+ public WindingOrderDemo() {
+ }
+
+ /**
+ * Entry point for the winding order demo.
+ * @param args command line arguments (ignored)
+ */
+ public static void main(String[] args) {
+ ViewFrame viewFrame = new ViewFrame("Winding order demo");
+ ViewPanel viewPanel = viewFrame.getViewPanel();
+ ShapeCollection shapes = viewPanel.getRootShapeCollection();
+
+ viewPanel.getCamera().getTransform().setTranslation(point(0, 0, -500));
+
+ double size = 150;
+
+ Point3D upperCenter = point(0, -size, 0);
+ Point3D lowerLeft = point(-size, +size, 0);
+ Point3D lowerRight = point(+size, +size, 0);
+
+ SolidPolygon triangle = new SolidPolygon(upperCenter, lowerLeft, lowerRight, Color.GREEN);
+ triangle.setBackfaceCulling(true);
+
+ shapes.addShape(triangle);
+
+ viewPanel.repaintDuringNextViewUpdate();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine. 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.math.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.GlowingPoint;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import static java.lang.Math.*;
+
+/**
+ * Represents a spiral galaxy composed of glowing points.
+ * Uses a mathematical spiral distribution to create a realistic galaxy shape
+ * with multiple spiral arms. Points are colored from a limited palette to
+ * enable texture reuse optimization.
+ */
+public class Galaxy extends AbstractCompositeShape {
+
+ /**
+ * The number of unique colors used in the galaxy.
+ * Used to reuse textures of glowing points of the same color.
+ */
+ public static final int UNIQUE_COLORS_COUNT = 30;
+
+ /**
+ * A list of all colors used in the galaxy.
+ * Used to reuse textures of glowing points of the same color.
+ */
+ private static List<Color> colors;
+
+ /**
+ * Constructs a galaxy with the specified parameters.
+ * @param galaxySize the overall size scale of the galaxy
+ * @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
+ */
+ public Galaxy(final int galaxySize, final int tailCount, final int starsCount,
+ Transform transform) {
+
+ super(transform);
+
+ ensureColorsAreInitialized();
+
+ final double angle1 = random() * 10;
+ final double angle2 = random() * 10;
+
+ final double angleSin1 = sin(angle1);
+ final double angleCos1 = cos(angle1);
+ final double angleSin2 = sin(angle2);
+ final double angleCos2 = cos(angle2);
+
+ Random random = new Random();
+
+ double starSize = galaxySize / 70d;
+
+ for (int i = 1; i < starsCount; i++) {
+ final double b = random() * 10;
+
+ final double s = (b * b) / 30;
+
+ final double v1 = (random() * (11.5 - b)) / 3;
+ final double v1p = v1 / 2;
+
+ final double ane = ((random() * (s / 2)) / tailCount) * 2;
+ final double sba = ((2 * PI) / tailCount)
+ * random.nextInt(tailCount);
+
+ final double x = (((sin((b - sba) + ane) * s) + (random() * v1)) - v1p) * galaxySize;
+ final double z = (((cos((b - sba) + ane) * s) + (random() * v1)) - v1p) * galaxySize;
+ final double y = ((random() * v1) - v1p) * galaxySize;
+
+ final double x1 = (x * angleCos1) + (z * angleSin1);
+ final double z1 = (z * angleCos1) - (x * angleSin1);
+
+ final double y1 = (y * angleCos2) + (z1 * angleSin2);
+ final double z2 = (z1 * angleCos2) - (y * angleSin2);
+
+ addStar(new Point3D(x1, y1, z2), starSize);
+ }
+ }
+
+ /**
+ * Adds a single star at the specified location.
+ * @param starLocation the position 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()))));
+ }
+
+ /**
+ * Initializes the shared color palette for all galaxies.
+ * Creates a limited set of random colors to enable texture reuse.
+ */
+ private synchronized void ensureColorsAreInitialized() {
+ if (colors != null) return;
+
+ colors = new ArrayList<>();
+
+ for (int i = 0; i < UNIQUE_COLORS_COUNT; i++)
+ colors.add(
+ new Color(
+ random() + 0.5,
+ random() + 0.5,
+ random() + 0.5,
+ 255));
+ }
+
+}
--- /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.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;
+
+/**
+ * Demo showing a point cloud galaxy simulation.
+ * Creates a spiral galaxy with 10,000 glowing points using the Galaxy class.
+ */
+public class PointCloudDemo {
+
+ /**
+ * Creates a new PointCloudDemo instance.
+ */
+ public 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);
+
+ final ShapeCollection geometryCollection = viewFrame.getViewPanel()
+ .getRootShapeCollection();
+
+ Transform transform = Transform.fromAngles(0, -1000, 1000, 0, 0, 0);
+
+ // add galaxy
+ geometryCollection.addShape(new Galaxy(500, 3, 10000, transform));
+
+ viewFrame.getViewPanel().repaintDuringNextViewUpdate();
+
+ }
+}
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+/**
+ * Galaxy simulation demo using glowing points.
+ *
+ * <p>Creates a spiral galaxy visualization using
+ * {@link eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.GlowingPoint}
+ * shapes arranged in a mathematical spiral pattern.</p>
+ *
+ * <p>Key classes:</p>
+ * <ul>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.galaxy_demo.Galaxy} - The galaxy shape</li>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.galaxy_demo.PointCloudDemo} - Demo entry point</li>
+ * </ul>
+ *
+ * @see eu.svjatoslav.sixth.e3d.examples.galaxy_demo.Galaxy
+ */
+
+package eu.svjatoslav.sixth.e3d.examples.galaxy_demo;
\ No newline at end of file
--- /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.graph_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin;
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.Graph;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Demo showing mathematical function graphs rendered in 3D.
+ * Displays sine, cosine, tangent, and composite function graphs.
+ * Also includes a 3D surface graph showing z = (x² - y²) / 20 with
+ * intersecting planes and highlighted intersection curves.
+ */
+public class MathGraphsDemo {
+
+ private static final double GRAPH_SCALE = 50d;
+
+ /**
+ * Creates a new MathGraphsDemo instance.
+ */
+ public MathGraphsDemo() {
+ }
+
+ /**
+ * Creates a graph of the cosine function.
+ * @param location the position of the graph in 3D space
+ * @return a Graph component showing y = cos(x)
+ */
+ private static Graph getCosineGraph(final Point3D location) {
+ final List<Point2D> data = new ArrayList<>();
+ for (double x = 0; x < 20; x += 0.25) {
+ final double y = Math.cos(x);
+
+ final Point2D p = new Point2D(x, y);
+ data.add(p);
+ }
+
+ return new Graph(GRAPH_SCALE, data, "Cosine", location);
+ }
+
+ /**
+ * Creates a graph of y = sin(tan(x)).
+ * @param location the position of the graph in 3D space
+ * @return a Graph component showing the composite function
+ */
+ private static Graph getFormula1Graph(final Point3D location) {
+ final List<Point2D> data = new ArrayList<>();
+ for (double x = 0; x < 20; x += 0.25) {
+ final double y = Math.sin(Math.tan(x));
+
+ final Point2D p = new Point2D(x, y);
+ data.add(p);
+ }
+
+ return new Graph(GRAPH_SCALE, data, "y = sin(tan(x))", location);
+ }
+
+ /**
+ * Creates a graph of y = (10-x)^2 / 30.
+ * @param location the position of the graph in 3D space
+ * @return a Graph component showing the parabola
+ */
+ private static Graph getFormula2Graph(final Point3D location) {
+ final List<Point2D> data = new ArrayList<>();
+ for (double x = 0; x < 20; x += 0.25) {
+ final double y = (Math.pow((10 - x), 2) / 30) - 2;
+
+ final Point2D p = new Point2D(x, y);
+ data.add(p);
+ }
+
+ return new Graph(GRAPH_SCALE, data, "y = ( (10-x)^2 ) / 30", location);
+ }
+
+ /**
+ * Creates a graph of y = sin(x/2) + sin(x/1.26).
+ * @param location the position of the graph in 3D space
+ * @return a Graph component showing the composite sine wave
+ */
+ private static Graph getFormula3Graph(final Point3D location) {
+ final List<Point2D> data = new ArrayList<>();
+ for (double x = 0; x < 20; x += 0.25) {
+ final double y = Math.sin(x / 2) + Math.sin(x / 1.26);
+
+ final Point2D p = new Point2D(x, y);
+ data.add(p);
+ }
+
+ return new Graph(GRAPH_SCALE, data, "y = sin(x/2) + sin(x/1.26)", location);
+ }
+
+ /**
+ * Creates a graph of the sine function.
+ * @param location the position of the graph in 3D space
+ * @return a Graph component showing y = sin(x)
+ */
+ private static Graph getSineGraph(final Point3D location) {
+ final List<Point2D> data = new ArrayList<>();
+ for (double x = 0; x < 20; x += 0.25) {
+ final double y = Math.sin(x);
+
+ final Point2D p = new Point2D(x, y);
+ data.add(p);
+ }
+
+ return new Graph(GRAPH_SCALE, data, "Sine", location);
+ }
+
+ /**
+ * Creates a graph of the tangent function with clamped values.
+ * @param location the position of the graph in 3D space
+ * @return a Graph component showing y = tan(x) with clamped range
+ */
+ private static Graph getTangentGraph(final Point3D location) {
+ final List<Point2D> data = new ArrayList<>();
+ for (double x = 0; x < 20; x += 0.25) {
+ double y = Math.tan(x);
+
+ if (y > 2)
+ y = 2;
+ if (y < -2)
+ y = -2;
+
+ final Point2D p = new Point2D(x, y);
+ data.add(p);
+ }
+
+ return new Graph(GRAPH_SCALE, data, "Tangent", location);
+ }
+
+ /**
+ * Entry point for the math graphs demo.
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+
+ final ViewFrame viewFrame = new ViewFrame("Math Graphs");
+ final ShapeCollection geometryCollection = viewFrame.getViewPanel()
+ .getRootShapeCollection();
+
+ viewFrame.getViewPanel().getCamera().getTransform().set(-613.58, -408.70, -351.41, -0.60, -0.54, -0.00);
+
+ addMathFormulas(geometryCollection);
+ addSurfaceGraph(geometryCollection);
+
+ viewFrame.getViewPanel().repaintDuringNextViewUpdate();
+ }
+
+ /**
+ * Adds all mathematical formula graphs to the scene.
+ * @param geometryCollection the collection to add graphs to
+ */
+ private static void addMathFormulas(ShapeCollection geometryCollection) {
+ int z = 1000;
+ Point3D location = point(-600, -300, z);
+ geometryCollection.addShape(getSineGraph(location));
+
+ location = point(600, -300, z);
+ geometryCollection.addShape(getFormula1Graph(location));
+
+ location = point(-600, 0, z);
+ geometryCollection.addShape(getCosineGraph(location));
+
+ location = point(600, 0, z);
+ geometryCollection.addShape(getFormula2Graph(location));
+
+ location = point(-600, 300, z);
+ geometryCollection.addShape(getTangentGraph(location));
+
+ location = point(600, 300, z);
+ geometryCollection.addShape(getFormula3Graph(location));
+ }
+
+ private static void addSurfaceGraph(ShapeCollection geometryCollection) {
+ final double range = 10;
+ final double step = 0.5;
+ final double scale = 30;
+
+ final SurfaceGraph3D.MathFunction3D function = (x, y) -> (x * x - y * y) / 20;
+
+ SurfaceGraph3D surface = new SurfaceGraph3D(
+ -range, range, step,
+ -range, range, step,
+ function,
+ new Color(150, 150, 150, 180),
+ Color.WHITE,
+ scale,
+ origin()
+ );
+
+ surface.addYIntersectionCurve(4, function, new Color(255, 255, 0, 220), 1.2);
+ surface.addXIntersectionCurve(-4, function, new Color(255, 120, 0, 220), 1.2);
+
+ geometryCollection.addShape(surface);
+ }
+
+}
\ No newline at end of file
--- /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.graph_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.Line;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+
+/**
+ * A 3D surface graph that visualizes mathematical functions of the form z = f(x, y).
+ *
+ * <p>The surface is rendered as a grid of quadrilaterals (split into triangles) with
+ * optional wireframe overlay. The surface color can be customized, and the wireframe
+ * lines are drawn in a contrasting color.</p>
+ *
+ * <p><b>Usage example:</b></p>
+ * <pre>{@code
+ * // Create a saddle surface: z = x^2 - y^2
+ * SurfaceGraph3D saddle = new SurfaceGraph3D(
+ * -5, 5, 0.5, // x range and step
+ * -5, 5, 0.5, // y range and step
+ * (x, y) -> x * x - y * y, // function z = f(x, y)
+ * new Color(180, 180, 180, 200), // gray semi-transparent surface
+ * new Color(255, 255, 255), // white grid lines
+ * new Point3D(0, 0, 0) // position in 3D space
+ * );
+ * shapeCollection.addShape(saddle);
+ * }</pre>
+ *
+ * @see AbstractCompositeShape
+ * @see SolidPolygon
+ * @see Line
+ */
+public class SurfaceGraph3D extends AbstractCompositeShape {
+
+ private final double xMin;
+ private final double xMax;
+ private final double xStep;
+ private final double yMin;
+ private final double yMax;
+ private final double yStep;
+ private final double scale;
+ private final Color surfaceColor;
+ private final Color gridColor;
+ private final double lineWidth;
+
+ /**
+ * Creates a 3D surface graph at the specified location.
+ *
+ * @param xMin minimum X value of the domain
+ * @param xMax maximum X value of the domain
+ * @param xStep step size between grid points along X axis
+ * @param yMin minimum Y value of the domain
+ * @param yMax maximum Y value of the domain
+ * @param yStep step size between grid points along Y axis
+ * @param function the mathematical function z = f(x, y) to visualize
+ * @param surfaceColor color of the surface polygons (use semi-transparent for see-through effect)
+ * @param gridColor color of the wireframe grid lines
+ * @param lineWidth width of grid lines in world units
+ * @param scale scale factor applied to all generated vertices
+ * @param location the 3D position of the graph's origin
+ */
+ public SurfaceGraph3D(final double xMin, final double xMax, final double xStep,
+ final double yMin, final double yMax, final double yStep,
+ final MathFunction3D function,
+ final Color surfaceColor, final Color gridColor,
+ final double lineWidth, final double scale, final Point3D location) {
+ super(location);
+
+ this.xMin = xMin;
+ this.xMax = xMax;
+ this.yMin = yMin;
+ this.yMax = yMax;
+ this.xStep = xStep;
+ this.yStep = yStep;
+ this.scale = scale;
+ this.surfaceColor = surfaceColor;
+ this.gridColor = gridColor;
+ this.lineWidth = lineWidth;
+
+ generateSurface(function);
+ }
+
+ public SurfaceGraph3D(final double xMin, final double xMax, final double xStep,
+ final double yMin, final double yMax, final double yStep,
+ final MathFunction3D function,
+ final Color surfaceColor, final Color gridColor,
+ final double scale, final Point3D location) {
+ this(xMin, xMax, xStep, yMin, yMax, yStep, function, surfaceColor, gridColor, 0.1, scale, location);
+ }
+
+ private void generateSurface(final MathFunction3D function) {
+ final int xCount = (int) ((xMax - xMin) / xStep) + 1;
+ final int yCount = (int) ((yMax - yMin) / yStep) + 1;
+
+ final Point3D[][] vertices = new Point3D[xCount][yCount];
+
+ for (int i = 0; i < xCount; i++) {
+ for (int j = 0; j < yCount; j++) {
+ final double x = xMin + i * xStep;
+ final double y = yMin + j * yStep;
+ final double z = function.apply(x, y);
+ vertices[i][j] = new Point3D(x * scale, -z * scale, y * scale);
+ }
+ }
+
+ for (int i = 0; i < xCount - 1; i++) {
+ for (int j = 0; j < yCount - 1; j++) {
+ final Point3D p1 = vertices[i][j];
+ final Point3D p2 = vertices[i + 1][j];
+ final Point3D p3 = vertices[i][j + 1];
+ final Point3D p4 = vertices[i + 1][j + 1];
+
+ addQuad(p1, p2, p3, p4);
+ addGridLines(p1, p2, p3, p4);
+ }
+ }
+ }
+
+ private void addQuad(final Point3D p1, final Point3D p2, final Point3D p3, final Point3D p4) {
+ final SolidPolygon quad = SolidPolygon.quad(p1, p2, p4, p3, surfaceColor);
+ quad.setBackfaceCulling(false);
+ addShape(quad);
+ }
+
+ private void addGridLines(final Point3D p1, final Point3D p2, final Point3D p3, final Point3D p4) {
+ addShape(new Line(p1, p2, gridColor, lineWidth));
+ addShape(new Line(p2, p4, gridColor, lineWidth));
+ addShape(new Line(p3, p4, gridColor, lineWidth));
+ addShape(new Line(p1, p3, gridColor, lineWidth));
+ }
+
+ /**
+ * Adds a curve highlighting the intersection of the surface with a vertical plane at constant X.
+ *
+ * @param xValue the X coordinate of the intersecting plane
+ * @param function the mathematical function z = f(x, y)
+ * @param color the color of the intersection curve
+ * @param width the line width
+ */
+ public void addXIntersectionCurve(final double xValue, final MathFunction3D function,
+ final Color color, final double width) {
+ Point3D prevPoint = null;
+ for (double y = yMin; y <= yMax; y += 0.15) {
+ final double z = function.apply(xValue, y);
+ final Point3D currentPoint = new Point3D(xValue * scale, -z * scale, y * scale);
+ if (prevPoint != null) {
+ addShape(new Line(prevPoint, currentPoint, color, width));
+ }
+ prevPoint = currentPoint;
+ }
+ }
+
+ /**
+ * Adds a curve highlighting the intersection of the surface with a vertical plane at constant Y.
+ *
+ * @param yValue the Y coordinate of the intersecting plane
+ * @param function the mathematical function z = f(x, y)
+ * @param color the color of the intersection curve
+ * @param width the line width
+ */
+ public void addYIntersectionCurve(final double yValue, final MathFunction3D function,
+ final Color color, final double width) {
+ Point3D prevPoint = null;
+ for (double x = xMin; x <= xMax; x += 0.15) {
+ final double z = function.apply(x, yValue);
+ final Point3D currentPoint = new Point3D(x * scale, -z * scale, yValue * scale);
+ if (prevPoint != null) {
+ addShape(new Line(prevPoint, currentPoint, color, width));
+ }
+ prevPoint = currentPoint;
+ }
+ }
+
+ /**
+ * Functional interface for 3D mathematical functions of the form z = f(x, y).
+ */
+ @FunctionalInterface
+ public interface MathFunction3D {
+ /**
+ * Computes the Z value for given X and Y coordinates.
+ *
+ * @param x the X coordinate
+ * @param y the Y coordinate
+ * @return the Z value (height) at the given (x, y) position
+ */
+ double apply(double x, double y);
+ }
+}
--- /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.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.benchmark.GraphicsBenchmark;
+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;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+
+/**
+ * Panel containing buttons to launch each demo application.
+ * Displays demos organized into sections with header labels.
+ */
+class ApplicationListPanel extends JPanel {
+ private static final long serialVersionUID = 2012721856427052560L;
+
+ private static final int BUTTON_WIDTH = 160;
+ private static final int GAP = 12;
+ private static final int SECTION_TOP_MARGIN = 16;
+
+ private static final DemoEntry[] ESSENTIALS = {
+ 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("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("Math graphs demo",
+ "Function graphs (sin, cos, tan) rendered in 3D",
+ new ShowMathGraphs()),
+ 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()),
+ new DemoEntry("Graphics benchmark",
+ "Automated performance measuring FPS across modes",
+ new ShowGraphicsBenchmark()),
+ };
+
+ ApplicationListPanel() {
+ final GroupLayout layout = new GroupLayout(this);
+ setLayout(layout);
+
+ layout.setAutoCreateGaps(true);
+ layout.setAutoCreateContainerGaps(true);
+
+ final GroupLayout.SequentialGroup verticalGroup = layout.createSequentialGroup();
+ final GroupLayout.ParallelGroup horizontalButtonGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING);
+ final GroupLayout.ParallelGroup horizontalDescGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING);
+
+ addSectionHeader("Essentials", verticalGroup, horizontalButtonGroup, horizontalDescGroup, layout, false);
+ addDemoEntries(ESSENTIALS, verticalGroup, horizontalButtonGroup, horizontalDescGroup, layout);
+
+ addSectionHeader("Demos", verticalGroup, horizontalButtonGroup, horizontalDescGroup, layout, true);
+ addDemoEntries(OTHER_DEMOS, verticalGroup, horizontalButtonGroup, horizontalDescGroup, layout);
+
+ layout.setVerticalGroup(verticalGroup);
+ layout.setHorizontalGroup(layout.createSequentialGroup()
+ .addGroup(horizontalButtonGroup)
+ .addGap(GAP)
+ .addGroup(horizontalDescGroup));
+ }
+
+ /**
+ * Adds a section header label.
+ *
+ * @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
+ * @param addTopMargin whether to add extra top margin for visual separation
+ */
+ private void addSectionHeader(final String title,
+ final GroupLayout.SequentialGroup verticalGroup,
+ final GroupLayout.ParallelGroup horizontalButtonGroup,
+ final GroupLayout.ParallelGroup horizontalDescGroup,
+ final GroupLayout layout,
+ final boolean addTopMargin) {
+ final JLabel headerLabel = new JLabel(title);
+ headerLabel.setFont(headerLabel.getFont().deriveFont(Font.BOLD, 14f));
+ if (addTopMargin) {
+ headerLabel.setBorder(new EmptyBorder(SECTION_TOP_MARGIN, 0, 0, 0));
+ }
+
+ verticalGroup.addComponent(headerLabel);
+ horizontalButtonGroup.addComponent(headerLabel);
+ horizontalDescGroup.addComponent(headerLabel);
+ }
+
+ /**
+ * Adds demo entry buttons to the layout.
+ *
+ * @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
+ */
+ private void addDemoEntries(final DemoEntry[] entries,
+ final GroupLayout.SequentialGroup verticalGroup,
+ final GroupLayout.ParallelGroup horizontalButtonGroup,
+ final GroupLayout.ParallelGroup horizontalDescGroup,
+ final GroupLayout layout) {
+ for (final DemoEntry entry : entries) {
+ final JButton button = new JButton(entry.action());
+ button.setPreferredSize(new Dimension(BUTTON_WIDTH, button.getPreferredSize().height));
+
+ final JLabel descLabel = new JLabel(entry.description());
+ descLabel.setFont(descLabel.getFont().deriveFont(Font.PLAIN));
+
+ verticalGroup.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+ .addComponent(button)
+ .addComponent(descLabel));
+
+ horizontalButtonGroup.addComponent(button);
+ horizontalDescGroup.addComponent(descLabel);
+ }
+ }
+
+ private record DemoEntry(String name, String description, AbstractAction action) {
+ }
+
+ private static class ShowMinimalExample extends AbstractAction {
+ ShowMinimalExample() {
+ putValue(NAME, "Minimal example");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ MinimalExample.main(null);
+ }
+ }
+
+ private static class ShowWindingOrder extends AbstractAction {
+ ShowWindingOrder() {
+ putValue(NAME, "Winding order");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ WindingOrderDemo.main(null);
+ }
+ }
+
+ private static class ShowTextEditors extends AbstractAction {
+ ShowTextEditors() {
+ putValue(NAME, "Text editors");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ TextEditorDemo.main(null);
+ }
+ }
+
+ private static class ShowTextEditors2 extends AbstractAction {
+ ShowTextEditors2() {
+ putValue(NAME, "Text editors city");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ TextEditorDemo2.main(null);
+ }
+ }
+
+ private static class ShowRain extends AbstractAction {
+ ShowRain() {
+ putValue(NAME, "Raining numbers");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ RainingNumbersDemo.main(null);
+ }
+ }
+
+ private static class ShowPointCloud extends AbstractAction {
+ ShowPointCloud() {
+ putValue(NAME, "Point cloud galaxy");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ PointCloudDemo.main(null);
+ }
+ }
+
+ private static class ShowMathGraphs extends AbstractAction {
+ ShowMathGraphs() {
+ putValue(NAME, "Mathematical graphs");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ MathGraphsDemo.main(null);
+ }
+ }
+
+ private static class ShowSineHeightmap extends AbstractAction {
+ ShowSineHeightmap() {
+ putValue(NAME, "Sine heightmap");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ SineHeightmap.main(null);
+ }
+ }
+
+ private static class ShowOctree extends AbstractAction {
+ ShowOctree() {
+ putValue(NAME, "Volumetric octree");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ OctreeDemo.main(null);
+ }
+ }
+
+ private static class ShowGameOfLife extends AbstractAction {
+ ShowGameOfLife() {
+ putValue(NAME, "Game of Life");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ eu.svjatoslav.sixth.e3d.examples.life_demo.Main.main(null);
+ }
+ }
+
+ private static class ShowShadedShapes extends AbstractAction {
+ ShowShadedShapes() {
+ putValue(NAME, "Shape gallery");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ ShapeGalleryDemo.main(null);
+ }
+ }
+
+ private static class ShowCoordinateSystem extends AbstractAction {
+ ShowCoordinateSystem() {
+ putValue(NAME, "Coordinate system");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ CoordinateSystemDemo.main(null);
+ }
+ }
+
+ private static class ShowTerrainDemo extends AbstractAction {
+ ShowTerrainDemo() {
+ putValue(NAME, "Procedural terrain");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ TerrainDemo.main(null);
+ }
+ }
+
+ private static class ShowGraphicsBenchmark extends AbstractAction {
+ ShowGraphicsBenchmark() {
+ putValue(NAME, "Graphics benchmark");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ GraphicsBenchmark.main(null);
+ }
+ }
+
+ private static class ShowCSG extends AbstractAction {
+ ShowCSG() {
+ putValue(NAME, "CSG demo");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ CSGDemo.main(null);
+ }
+ }
+}
\ No newline at end of file
--- /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.launcher;
+
+import javax.swing.*;
+import java.awt.*;
+
+import static java.awt.BorderLayout.CENTER;
+import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE;
+
+
+/**
+ * Launcher application for all Sixth 3D demos.
+ * Displays a window with buttons to launch each demo application.
+ */
+class Main {
+
+ /**
+ * Entry point for the demo launcher.
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+ buildAndShowGuiWindow();
+ }
+
+ /**
+ * Builds and shows the main window of the application.
+ */
+ private static void buildAndShowGuiWindow() {
+ JFrame frame = new JFrame("Sixth 3D engine demos");
+
+ // Keep application running until last frame is closed.
+ frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+
+ frame.getContentPane().setLayout(new BorderLayout());
+ frame.getContentPane().add(new ApplicationListPanel(), CENTER);
+
+ frame.pack();
+ frame.setMinimumSize(frame.getSize());
+
+ frame.setLocationRelativeTo(null); // center frame on screen
+ frame.setVisible(true);
+ }
+
+}
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+/**
+ * Demo launcher application.
+ *
+ * <p>Provides a GUI window with buttons to launch each demo application.
+ * The main entry point is {@link eu.svjatoslav.sixth.e3d.examples.launcher.Main}.</p>
+ *
+ * @see eu.svjatoslav.sixth.e3d.examples.launcher.Main
+ */
+
+package eu.svjatoslav.sixth.e3d.examples.launcher;
\ No newline at end of file
--- /dev/null
+package eu.svjatoslav.sixth.e3d.examples.life_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+
+/**
+ * Represents a single cell in Conway's Game of Life matrix.
+ * Each cell can be active (alive) or inactive (dead), and responds to mouse
+ * interactions for toggling its state. The cell changes color based on its
+ * state and whether the mouse is hovering over it.
+ */
+class Cell extends AbstractCompositeShape implements
+ MouseInteractionController {
+
+ /**
+ * Visual size of each cell in world units.
+ */
+ static final int SIZE = 20;
+ /**
+ * Color of the active cell (R, G, B, A)
+ */
+ private static final Color ACTIVE_COLOR = new Color("A8FF");
+ /**
+ * Color of the active cell (R, G, B, A) while the mouse is over it.
+ */
+ private static final Color ACTIVE_COLOR_MOUSE_OVER = new Color("F9FF");
+ /**
+ * Color of the inactive cell (R, G, B, A)
+ */
+ private static final Color INACTIVE_COLOR = new Color("55F8");
+ /**
+ * Color of the inactive cell (R, G, B, A) while mouse is over it.
+ */
+ private static final Color INACTIVE_COLOR_MOUSE_OVER = new Color("77F8");
+ /**
+ * A placeholder variable to help in next generation computation. Indicates
+ * whether cell is going to survive within next generation.
+ */
+ public boolean survives;
+ /**
+ * Indicates whether cell is currently active
+ */
+ private boolean active;
+ /**
+ * Indicates whether mouse pointer is currently over this cell.
+ */
+ private boolean isMouseOver = false;
+
+ /**
+ * Constructs a cell at the specified center position.
+ *
+ * @param center the 3D position of the cell center
+ */
+ public Cell(final Point3D center) {
+ super(center);
+
+ createCellShape();
+
+ // enable receiving of mouse events
+ setMouseInteractionController(this);
+ }
+
+ /**
+ * Creates the visual representation of the cell as two triangles.
+ */
+ private void createCellShape() {
+ final double halfSize = SIZE / 2f;
+
+ // define 4 points corresponding to cell borders
+ final Point3D p1 = new Point3D(-halfSize, 0, -halfSize);
+
+ final Point3D p2 = new Point3D(+halfSize, 0, -halfSize);
+
+ final Point3D p3 = new Point3D(+halfSize, 0, +halfSize);
+
+ final Point3D p4 = new Point3D(-halfSize, 0, +halfSize);
+
+ // connect 4 points with 2 polygons
+ addShape(new SolidPolygon(p1, p2, p3, computeCellColor()));
+ addShape(new SolidPolygon(p1, p4, p3, computeCellColor()));
+ }
+
+ /**
+ * Computes the cell color based on active state and mouse hover.
+ *
+ * @return the appropriate color for the current state
+ */
+ private Color computeCellColor() {
+ if (active)
+ if (isMouseOver)
+ return ACTIVE_COLOR_MOUSE_OVER;
+ else
+ return ACTIVE_COLOR;
+ else if (isMouseOver)
+ return INACTIVE_COLOR_MOUSE_OVER;
+ else
+ return INACTIVE_COLOR;
+ }
+
+ /**
+ * Returns whether this cell is currently active (alive).
+ *
+ * @return true if the cell is active
+ */
+ public boolean isActive() {
+ return active;
+ }
+
+ /**
+ * Sets the active state of this cell and updates its color.
+ *
+ * @param active true to make the cell active, false for inactive
+ */
+ public void setActive(final boolean active) {
+ this.active = active;
+ updateColor();
+ }
+
+ /**
+ * Handles mouse click by toggling the cell state.
+ *
+ * @param button the mouse button that was clicked
+ * @return true to indicate the event was consumed
+ */
+ @Override
+ public boolean mouseClicked(int button) {
+ setActive(!isActive());
+ return true;
+ }
+
+ /**
+ * Handles mouse entering the cell area by highlighting it.
+ *
+ * @return true to indicate the event was consumed
+ */
+ @Override
+ public boolean mouseEntered() {
+ setMouseOver(true);
+ return true;
+ }
+
+ /**
+ * Handles mouse exiting the cell area by removing highlight.
+ *
+ * @return true to indicate the event was consumed
+ */
+ @Override
+ public boolean mouseExited() {
+ setMouseOver(false);
+ return true;
+ }
+
+ /**
+ * Sets the mouse-over state and updates the cell color.
+ *
+ * @param isMouseOver true if mouse is over the cell
+ */
+ private void setMouseOver(final boolean isMouseOver) {
+ this.isMouseOver = isMouseOver;
+ updateColor();
+ }
+
+ /**
+ * Updates the cell's visual color to match its current state.
+ */
+ private void updateColor() {
+ setColor(computeCellColor());
+ }
+
+}
--- /dev/null
+package eu.svjatoslav.sixth.e3d.examples.life_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Rectangle;
+import eu.svjatoslav.sixth.e3d.gui.TextPointer;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.WorldNavigationUserInputTracker;
+import eu.svjatoslav.sixth.e3d.math.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin;
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid2D;
+
+import java.awt.event.KeyEvent;
+
+
+/**
+ * Main entry point for Conway's Game of Life 3D demo.
+ * Creates a 30x30 cell matrix where cells evolve based on Conway's rules.
+ * The user can interact with cells by clicking to toggle their state.
+ *
+ * <p>Key controls:
+ * <ul>
+ * <li>Space - Evolve one generation</li>
+ * <li>Enter - Evolve with history (leaves stars marking previous positions)</li>
+ * <li>C - Clear the matrix</li>
+ * </ul>
+ */
+public class Main extends WorldNavigationUserInputTracker {
+
+ /**
+ * The game of life matrix, centered at the origin.
+ */
+ private static final Matrix MATRIX = new Matrix(
+ origin() // position matrix in the center of the scene
+ );
+
+ /**
+ * Creates a new Main instance for the Game of Life demo.
+ */
+ public Main() {
+ }
+
+ /**
+ * Entry point for the Game of Life demo.
+ *
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+ new Main().run();
+ }
+
+ /**
+ * Handle keyboard input.
+ */
+ @Override
+ public boolean keyPressed(final KeyEvent event, final ViewPanel viewPanel) {
+ switch (event.getKeyChar()) {
+ case ' ': // space key
+ MATRIX.evolve(false);
+ break;
+ case 10: // ENTER
+ MATRIX.evolve(true);
+ break;
+ case 'c': // reset matrix
+ MATRIX.clear();
+ break;
+ default:
+ return super.keyPressed(event, viewPanel);
+ }
+ return true;
+ }
+
+ /**
+ * Initializes and displays the Game of Life demo.
+ */
+ private void run() {
+
+ // create application frame visible to the user
+ final ViewFrame viewFrame = new ViewFrame("Game of Life");
+ final ViewPanel viewPanel = viewFrame.getViewPanel();
+
+ viewPanel.getCamera().getTransform().set(855.83, -174.40, -31.46, 1.58, -0.02, -0.00);
+
+ final ShapeCollection shapeCollection = viewPanel.getRootShapeCollection();
+
+ // add matrix
+ shapeCollection.addShape(MATRIX);
+
+ // add help panel explaining rules and controls
+ shapeCollection.addShape(createHelpPanel());
+
+ // add wire-frame grid (optional)
+ shapeCollection.addShape(createGrid());
+
+ // enable receiving of keyboard events
+ viewPanel.getKeyboardFocusStack().pushFocusOwner(this);
+
+ // Done! World is built. So ensure screen is updated too.
+ viewPanel.repaintDuringNextViewUpdate();
+ }
+
+ /**
+ * Creates a pink wire-frame grid below the matrix for decorative purposes.
+ *
+ * @return a Grid2D positioned below the game matrix
+ */
+ private Grid2D createGrid() {
+ return new Grid2D(
+ Transform.fromAngles(0, 100, 0, 0, Math.PI / 2, 0),
+
+ new Rectangle(800),
+
+ 5, 5,
+
+ new LineAppearance(3,
+ hex("FF000050")
+ )
+ );
+ }
+
+ /**
+ * Creates a help panel displaying game rules and controls.
+ * The panel is positioned to the right of the matrix and faces toward the viewer.
+ *
+ * @return a TextCanvas with help information
+ */
+ private TextCanvas createHelpPanel() {
+ final String helpText =
+ "=== CONWAY'S GAME OF LIFE ===\n" +
+ "\n" +
+ "RULES:\n" +
+ "- Live cell with 2-3 neighbors survives\n" +
+ "- Dead cell with 3 neighbors becomes alive\n" +
+ "- All other cells die or stay dead\n" +
+ "\n" +
+ "KEYBOARD:\n" +
+ "Space - Evolve one generation\n" +
+ "Enter - Evolve with history trail\n" +
+ "C - Clear the matrix\n" +
+ "Arrows - Move camera\n" +
+ "\n" +
+ "MOUSE:\n" +
+ "Click - Toggle cell state\n" +
+ "Hover - Highlight cell";
+
+ final Transform location = Transform.fromAngles(
+ point(500, -80, 0),
+ -Math.PI / 2,
+ 0
+ );
+
+ final TextPointer dimensions = new TextPointer(16, 45);
+
+ final TextCanvas panel = new TextCanvas(
+ location,
+ dimensions,
+ Color.WHITE,
+ hex("000050C8")
+ );
+
+ panel.setText(helpText);
+
+ return panel;
+ }
+
+}
--- /dev/null
+package eu.svjatoslav.sixth.e3d.examples.life_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.SubShape;
+
+/**
+ * Represents the 2D game board for Conway's Game of Life.
+ * Contains a 30x30 grid of cells that can evolve according to Conway's rules.
+ * Supports optional history tracking where stars mark previously active cells.
+ *
+ * @see Cell
+ * @see Star
+ */
+class Matrix extends AbstractCompositeShape {
+
+ /**
+ * Empty space between adjacent cells.
+ */
+ private static final int BORDER = 5;
+
+ /**
+ * Number of cells along each dimension (30x30 grid).
+ */
+ private static final int SIZE = 30;
+
+ /**
+ * Group ID for history-tracking stars.
+ */
+ private static final String GROUP_STARS = "stars";
+
+ /**
+ * Group ID for the cell surface.
+ */
+ private static final String GROUP_SURFACE = "surface";
+
+ /**
+ * 2D array of cells forming the game board.
+ */
+ private final Cell[][] cells = new Cell[SIZE][];
+
+ /**
+ * Constructs a game matrix at the specified location.
+ *
+ * @param location the center position of the matrix in 3D space
+ */
+ public Matrix(final Point3D location) {
+ super(location);
+
+
+ for (int x = 0; x < SIZE; x++) {
+ cells[x] = new Cell[SIZE];
+
+ // init Y row
+ for (int z = 0; z < SIZE; z++) {
+ // create cell and register it
+ final Cell cell = new Cell(getCellLocation(x, z));
+ cells[x][z] = cell;
+ addShape(cell);
+ }
+ }
+
+ setGroupForUngrouped(GROUP_SURFACE);
+ }
+
+ /**
+ * Clears all cells to inactive state and removes history stars.
+ */
+ public void clear() {
+
+ // mark every cell as inactive
+ for (int x = 0; x < SIZE; x++)
+ for (int y = 0; y < SIZE; y++)
+ cells[x][y].setActive(false);
+
+ // remove history stars
+ removeGroup(GROUP_STARS);
+ }
+
+ /**
+ * Computes the next generation using Conway's Game of Life rules.
+ * Each cell's survival is determined by its number of alive neighbors.
+ */
+ private void computeSurvivedCells() {
+
+ for (int y = 0; y < SIZE; y++)
+ for (int x = 0; x < SIZE; x++)
+ processCell(x, y);
+
+ for (int y = 0; y < SIZE; y++)
+ for (int x = 0; x < SIZE; x++)
+ cells[x][y].setActive(cells[x][y].survives);
+
+ }
+
+ /**
+ * Processes a single cell to determine if it survives to the next generation.
+ *
+ * @param x the X coordinate of the cell
+ * @param y the Y coordinate of the cell
+ */
+ private void processCell(int x, int y) {
+ int aliveNeighbours = countNeighbours(x, y);
+
+ if (cells[x][y].isActive()) {
+ cells[x][y].survives = ((aliveNeighbours == 2) || (aliveNeighbours == 3));
+ } else {
+ cells[x][y].survives = aliveNeighbours == 3;
+ }
+ }
+
+ /**
+ * Counts the number of alive neighbors around the specified cell.
+ *
+ * @param x the X coordinate of the cell
+ * @param y the Y coordinate of the cell
+ * @return the count of alive neighboring cells
+ */
+ private int countNeighbours(int x, int y) {
+ int result = 0;
+ for (int ny = y - 1; ny <= y + 1; ny++)
+ for (int nx = x - 1; nx <= x + 1; nx++)
+ if (isCellAlive(nx, ny)) result++;
+
+ if (isCellAlive(x, y)) result--;
+
+ return result;
+ }
+
+ /**
+ * Checks if the cell at the specified coordinates is alive.
+ *
+ * @param x the X coordinate of the cell
+ * @param y the Y coordinate of the cell
+ * @return true if the cell is within bounds and active
+ */
+ private boolean isCellAlive(int x, int y) {
+ if (x < 0) return false;
+ if (x >= SIZE) return false;
+ if (y < 0) return false;
+ if (y >= SIZE) return false;
+ return cells[x][y].isActive();
+ }
+
+ /**
+ * Evolves the matrix by one generation.
+ *
+ * @param preserveHistory if true, places stars at positions of previously active cells
+ */
+ public void evolve(final boolean preserveHistory) {
+ if (preserveHistory)
+ markActiveCells();
+
+ shiftStarsUp();
+
+ computeSurvivedCells();
+ }
+
+ /**
+ * Computes the world position of a cell given its grid coordinates.
+ *
+ * @param x the X grid coordinate
+ * @param z the Z grid coordinate
+ * @return the 3D world position of the cell center
+ */
+ private Point3D getCellLocation(final int x, final int z) {
+ final int shift = -((SIZE / 2) * (Cell.SIZE + BORDER));
+
+ return new Point3D(
+ (x * (Cell.SIZE + BORDER)) + shift,
+ 0,
+ (z * (Cell.SIZE + BORDER)) + shift);
+ }
+
+ /**
+ * Marks all currently active cells with stars for history tracking.
+ * Stars are added to the GROUP_STARS group.
+ */
+ private void markActiveCells() {
+ // mark survived cells
+ for (int x = 0; x < SIZE; x++)
+ for (int y = 0; y < SIZE; y++)
+ if (cells[x][y].isActive())
+ addShape(new Star(getCellLocation(x, y)));
+
+ setGroupForUngrouped(GROUP_STARS);
+ }
+
+ /**
+ * Shifts all history tracking stars upward to create a trailing effect.
+ * Also invalidates the Matrix's cached bounding box so frustum culling
+ * uses the updated star positions.
+ */
+ private void shiftStarsUp() {
+ for (final SubShape subShape : getGroup(GROUP_STARS)) {
+ final Star star = (Star) subShape.getShape();
+ star.translate(0, -10, 0);
+ }
+ invalidateBounds();
+ }
+}
--- /dev/null
+package eu.svjatoslav.sixth.e3d.examples.life_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.GlowingPoint;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a glowing star marker in the Game of Life history trail.
+ * When cells evolve with history enabled, stars are placed at previous
+ * active cell positions to create a visual trail of the game's evolution.
+ * Uses a limited color palette to enable texture reuse optimization.
+ */
+class Star extends GlowingPoint {
+ /** Visual size of each star. */
+ public static final int STAR_SIZE = 10;
+
+ /** Number of unique star colors for texture reuse optimization. */
+ public static final int UNIQUE_STARS_COUNT = 30;
+
+ /** Shared pool of star colors for texture reuse. */
+ private static final List<Color> uniqueStarColors = new ArrayList<>();
+
+ /*
+ * Initializes a limited set of precomputed star colors.
+ * This optimization allows the Sixth 3D engine to reuse textures
+ * for stars with identical colors, saving memory when many stars are present.
+ */
+ static {
+ for (int i = 0; i < UNIQUE_STARS_COUNT; i++)
+ uniqueStarColors.add(
+ new Color(
+ Math.random() + 0.5,
+ Math.random() + 0.5,
+ Math.random() + 0.5,
+ 255));
+ }
+
+ /**
+ * Constructs a star at the specified location with a random color from the palette.
+ * @param location the 3D position of the star
+ */
+ public Star(Point3D location) {
+ super(location,
+ STAR_SIZE,
+ uniqueStarColors.get((int) (Math.random() * uniqueStarColors.size())) // pick random pre-generated color
+ );
+ }
+
+}
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+/**
+ * Conway's Game of Life 3D demo.
+ *
+ * <p>Implements a 30x30 cell matrix where cells evolve based on Conway's rules.
+ * Users can interact by clicking to toggle cell states. The simulation runs
+ * in a 3D view with camera navigation.</p>
+ *
+ * <p>Key classes:</p>
+ * <ul>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.life_demo.Main} - Demo entry point</li>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.life_demo.Cell} - Individual cell representation</li>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.life_demo.Matrix} - The cell grid</li>
+ * </ul>
+ *
+ * @see eu.svjatoslav.sixth.e3d.examples.life_demo.Main
+ */
+
+package eu.svjatoslav.sixth.e3d.examples.life_demo;
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ *
+*/
+
+/**
+ * Example applications that make use of the API.
+ */
+
+package eu.svjatoslav.sixth.e3d.examples;
+
--- /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.math.DiamondSquare;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+
+/**
+ * A procedurally generated terrain mesh using the diamond-square algorithm.
+ * Creates a grid of triangular polygons with height variation for mountains.
+ */
+public class DiamondSquareTerrain extends AbstractCompositeShape {
+
+ private static final double TERRAIN_SIZE = 800.0;
+
+ private final double[][] heightmap;
+ private final int gridSize;
+
+ /**
+ * Creates a diamond-square terrain with the specified parameters.
+ *
+ * @param gridSize the size of the heightmap grid (must be 2^n + 1)
+ * @param minHeight the minimum terrain height
+ * @param maxHeight the maximum terrain height
+ * @param seed random seed for reproducible terrain
+ */
+ public DiamondSquareTerrain(final int gridSize, final double minHeight, final double maxHeight, final long seed) {
+ super();
+
+ this.gridSize = gridSize;
+ this.heightmap = DiamondSquare.generateMap(gridSize, minHeight, maxHeight, seed);
+ createPolygons(gridSize, heightmap);
+ setBackfaceCulling(true);
+ }
+
+ /**
+ * Returns the terrain height at the specified world XZ coordinates.
+ *
+ * @param worldX the world X coordinate
+ * @param worldZ the world Z coordinate
+ * @return the terrain height at that position, or 0 if outside terrain bounds
+ */
+ public double getHeightAt(final double worldX, final double worldZ) {
+ final double cellSize = TERRAIN_SIZE / (gridSize - 1);
+ final double offsetX = -TERRAIN_SIZE / 2;
+ final double offsetZ = -TERRAIN_SIZE / 2;
+
+ final int gridX = (int) Math.round((worldX - offsetX) / cellSize);
+ final int gridZ = (int) Math.round((worldZ - offsetZ) / cellSize);
+
+ if (gridX < 0 || gridX >= gridSize || gridZ < 0 || gridZ >= gridSize) {
+ return 0;
+ }
+
+ return heightmap[gridZ][gridX];
+ }
+
+ /**
+ * Creates triangular polygons from the heightmap.
+ */
+ private void createPolygons(final int gridSize, final double[][] heightmap) {
+ final double cellSize = TERRAIN_SIZE / (gridSize - 1);
+ final double offsetX = -TERRAIN_SIZE / 2;
+ final double offsetZ = -TERRAIN_SIZE / 2;
+
+ for (int z = 0; z < gridSize - 1; z++) {
+ for (int x = 0; x < gridSize - 1; x++) {
+ final double x0 = offsetX + x * cellSize;
+ final double x1 = offsetX + (x + 1) * cellSize;
+ final double z0 = offsetZ + z * cellSize;
+ final double z1 = offsetZ + (z + 1) * cellSize;
+
+ final double y00 = heightmap[z][x];
+ final double y10 = heightmap[z][x + 1];
+ final double y01 = heightmap[z + 1][x];
+ final double y11 = heightmap[z + 1][x + 1];
+
+ final Color color = new Color(10, 10, 10);
+
+ final Point3D p00 = new Point3D(x0, y00, z0);
+ final Point3D p10 = new Point3D(x1, y10, z0);
+ final Point3D p01 = new Point3D(x0, y01, z1);
+ final Point3D p11 = new Point3D(x1, y11, z1);
+
+ final SolidPolygon tri1 = new SolidPolygon(p00, p10, p01, color);
+ final SolidPolygon tri2 = new SolidPolygon(p10, p11, p01, color);
+
+ tri1.setShadingEnabled(true);
+ tri2.setShadingEnabled(true);
+
+ addShape(tri1);
+ addShape(tri2);
+ }
+ }
+ }
+}
\ No newline at end of file
--- /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.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+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.
+ *
+ * @see TerrainDemo
+ * @see SolidPolygonCylinder
+ */
+public class FractalTree extends AbstractCompositeShape {
+
+ private static final Color BROWN = new Color(139, 90, 43);
+
+ /**
+ * Creates a tree trunk at the specified base position.
+ *
+ * @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
+ */
+ 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(
+ basePosition.x,
+ basePosition.y - trunkHeight,
+ basePosition.z
+ );
+
+ final SolidPolygonCylinder trunk = new SolidPolygonCylinder(
+ basePosition, // start (bottom of trunk)
+ topPosition, // end (top of trunk)
+ trunkRadius,
+ 8,
+ BROWN);
+ trunk.setShadingEnabled(true);
+
+ addShape(trunk);
+ setBackfaceCulling(true);
+ }
+}
\ No newline at end of file
--- /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.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin;
+import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point;
+import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource;
+import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightingManager;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.LightSourceMarker;
+
+/**
+ * Demo showing a procedurally generated mountain landscape using the diamond-square algorithm.
+ * Three colored light sources (warm orange, cool cyan, neutral white) illuminate the terrain
+ * from above, demonstrating dynamic flat shading. A fractal tree stands at the center of the terrain.
+ */
+public class TerrainDemo {
+
+ /**
+ * Entry point for the terrain demo.
+ *
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+ final ViewFrame viewFrame = new ViewFrame("Procedural terrain");
+ ViewPanel viewPanel = viewFrame.getViewPanel();
+
+ viewPanel.getCamera().getTransform().set(-76.43, -72.05, -159.08, -0.64, -0.46, -0.00);
+
+ final ShapeCollection shapes = viewPanel.getRootShapeCollection();
+
+ setLights(viewFrame, shapes);
+
+ final DiamondSquareTerrain terrain = new DiamondSquareTerrain(129, 0.0, 100.0, 42);
+ shapes.addShape(terrain);
+
+ // Get terrain height at center and place tree trunk on it
+ final double terrainHeight = terrain.getHeightAt(0, 0);
+ shapes.addShape(new FractalTree(point(0, terrainHeight, 0), 30, 6));
+
+ viewPanel.repaintDuringNextViewUpdate();
+ }
+
+ /**
+ * Sets up the lighting for the terrain scene.
+ *
+ * @param viewFrame the view frame containing the lighting manager
+ * @param shapes the shape collection to add light source markers to
+ */
+ private static void setLights(ViewFrame viewFrame, ShapeCollection shapes) {
+ LightingManager lightingManager = viewFrame.getViewPanel().getLightingManager();
+ lightingManager.setAmbientLight(hex("FA9664FF"));
+
+ final LightSource warmLight = new LightSource(
+ point(-400, -500, 0),
+ hex("FFB464FF"),
+ 190.0
+ );
+ final LightSource coolLight = new LightSource(
+ point(400, -500, 0),
+ hex("64C8FFFF"),
+ 220.0
+ );
+ final LightSource neutralLight = new LightSource(
+ point(0, -600, 300),
+ hex("FFFFFFFF"),
+ 250.0
+ );
+
+ lightingManager.addLight(warmLight);
+ lightingManager.addLight(coolLight);
+ lightingManager.addLight(neutralLight);
+
+ shapes.addShape(new LightSourceMarker(warmLight.getPosition(), hex("FFB464FF")));
+ shapes.addShape(new LightSourceMarker(coolLight.getPosition(), hex("64C8FFFF")));
+ shapes.addShape(new LightSourceMarker(neutralLight.getPosition(), hex("FFFFFFFF")));
+ }
+}
\ No newline at end of file
--- /dev/null
+microcode: microcode updated early to revision 0xe2, date = 2020-07-14
+Linux version 5.10.0-6-amd64 (debian-kernel@lists.debian.org) (gcc-10 (Debian 10.2.1-6) 10.2.1 20210110, GNU ld (GNU Binutils for Debian) 2.35.2) #1 SMP Debian 5.10.28-1 (2021-04-09)
+Command line: BOOT_IMAGE=/vmlinuz-5.10.0-6-amd64 root=/dev/mapper/main-root ro quiet
+x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
+x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
+x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'
+x86/fpu: Supporting XSAVE feature 0x008: 'MPX bounds registers'
+x86/fpu: Supporting XSAVE feature 0x010: 'MPX CSR'
+x86/fpu: xstate_offset[2]: 576, xstate_sizes[2]: 256
+x86/fpu: xstate_offset[3]: 832, xstate_sizes[3]: 64
+x86/fpu: xstate_offset[4]: 896, xstate_sizes[4]: 64
+x86/fpu: Enabled xstate features 0x1f, context size is 960 bytes, using 'compacted' format.
+BIOS-provided physical RAM map:
+BIOS-e820: [mem 0x0000000000000000-0x000000000009e7ff] usable
+BIOS-e820: [mem 0x000000000009e800-0x000000000009ffff] reserved
+BIOS-e820: [mem 0x00000000000e0000-0x00000000000fffff] reserved
+BIOS-e820: [mem 0x0000000000100000-0x000000005f692fff] usable
+BIOS-e820: [mem 0x000000005f693000-0x000000005f693fff] ACPI NVS
+BIOS-e820: [mem 0x000000005f694000-0x000000005f6ddfff] reserved
+BIOS-e820: [mem 0x000000005f6de000-0x000000005f789fff] usable
+BIOS-e820: [mem 0x000000005f78a000-0x0000000060089fff] reserved
+BIOS-e820: [mem 0x000000006008a000-0x000000007558efff] usable
+BIOS-e820: [mem 0x000000007558f000-0x0000000075f7efff] reserved
+BIOS-e820: [mem 0x0000000075f7f000-0x0000000077f7efff] ACPI NVS
+BIOS-e820: [mem 0x0000000077f7f000-0x0000000077ffefff] ACPI data
+BIOS-e820: [mem 0x0000000077fff000-0x0000000077ffffff] usable
+BIOS-e820: [mem 0x0000000078000000-0x00000000780fffff] reserved
+BIOS-e820: [mem 0x0000000079000000-0x000000007e7fffff] reserved
+BIOS-e820: [mem 0x00000000e0000000-0x00000000efffffff] reserved
+BIOS-e820: [mem 0x00000000fd000000-0x00000000fe7fffff] reserved
+BIOS-e820: [mem 0x00000000feb00000-0x00000000feb03fff] reserved
+BIOS-e820: [mem 0x00000000fec00000-0x00000000fec00fff] reserved
+BIOS-e820: [mem 0x00000000fed00000-0x00000000fed00fff] reserved
+BIOS-e820: [mem 0x00000000fed10000-0x00000000fed19fff] reserved
+BIOS-e820: [mem 0x00000000fed84000-0x00000000fed84fff] reserved
+BIOS-e820: [mem 0x00000000fee00000-0x00000000fee00fff] reserved
+BIOS-e820: [mem 0x00000000ffa00000-0x00000000ffffffff] reserved
+BIOS-e820: [mem 0x0000000100000000-0x00000004807fffff] usable
+NX (Execute Disable) protection: active
+SMBIOS 2.8 present.
+DMI: LENOVO 80NV/Allsparks 5A, BIOS CDCN53WW 09/19/2016
+tsc: Detected 2600.000 MHz processor
+tsc: Detected 2599.992 MHz TSC
+e820: update [mem 0x00000000-0x00000fff] usable ==> reserved
+e820: remove [mem 0x000a0000-0x000fffff] usable
+last_pfn = 0x480800 max_arch_pfn = 0x400000000
+MTRR default type: uncachable
+MTRR fixed ranges enabled:
+ 00000-9FFFF write-back
+ A0000-BFFFF uncachable
+ C0000-FFFFF write-protect
+MTRR variable ranges enabled:
+ 0 base 0000000000 mask 7800000000 write-back
+ 1 base 0078000000 mask 7FF8000000 uncachable
+ 2 base 0080000000 mask 7F80000000 uncachable
+ 3 disabled
+ 4 disabled
+ 5 disabled
+ 6 disabled
+ 7 disabled
+ 8 disabled
+ 9 disabled
+x86/PAT: Configuration [0-7]: WB WC UC- UC WB WP UC- WT
+e820: update [mem 0x78000000-0xffffffff] usable ==> reserved
+last_pfn = 0x78000 max_arch_pfn = 0x400000000
+found SMP MP-table at [mem 0x000fe1b0-0x000fe1bf]
+ACPI: Early table checksum verification disabled
+ACPI: RSDP 0x00000000000FE020 000024 (v02 LENOVO)
+ACPI: XSDT 0x0000000077FC0188 0000D4 (v01 LENOVO CB-01 00000001 01000013)
+ACPI: FACP 0x0000000077FEE000 0000F4 (v05 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: DSDT 0x0000000077FCB000 01E97D (v02 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: FACS 0x0000000077F60000 000040
+ACPI: UEFI 0x0000000077FFD000 000236 (v01 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: UEFI 0x0000000077FFC000 000042 (v01 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: MSDM 0x0000000077FFB000 000055 (v03 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: SSDT 0x0000000077FF5000 005104 (v02 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: DBGP 0x0000000077FF4000 000034 (v01 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: DBG2 0x0000000077FF3000 000061 (v00 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: SSDT 0x0000000077FF2000 00077C (v02 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: ASF! 0x0000000077FF1000 0000A5 (v32 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: ASPT 0x0000000077FF0000 000034 (v07 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: BOOT 0x0000000077FEF000 000028 (v01 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: HPET 0x0000000077FED000 000038 (v01 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: LPIT 0x0000000077FEC000 000094 (v01 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: APIC 0x0000000077FEB000 0000BC (v03 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: MCFG 0x0000000077FEA000 00003C (v01 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: SSDT 0x0000000077FCA000 0002D4 (v01 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: SSDT 0x0000000077FC9000 00019A (v02 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: SSDT 0x0000000077FC8000 0003F4 (v02 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: SSDT 0x0000000077FC7000 000E58 (v02 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: SSDT 0x0000000077FC2000 004354 (v01 Insyde NVOP 00001000 INTL 20130927)
+ACPI: DMAR 0x0000000077FC1000 0000A8 (v01 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: FPDT 0x0000000077FBF000 000044 (v01 LENOVO CB-01 00000001 ACPI 00040000)
+ACPI: Reserving FACP table memory at [mem 0x77fee000-0x77fee0f3]
+ACPI: Reserving DSDT table memory at [mem 0x77fcb000-0x77fe997c]
+ACPI: Reserving FACS table memory at [mem 0x77f60000-0x77f6003f]
+ACPI: Reserving UEFI table memory at [mem 0x77ffd000-0x77ffd235]
+ACPI: Reserving UEFI table memory at [mem 0x77ffc000-0x77ffc041]
+ACPI: Reserving MSDM table memory at [mem 0x77ffb000-0x77ffb054]
+ACPI: Reserving SSDT table memory at [mem 0x77ff5000-0x77ffa103]
+ACPI: Reserving DBGP table memory at [mem 0x77ff4000-0x77ff4033]
+ACPI: Reserving DBG2 table memory at [mem 0x77ff3000-0x77ff3060]
+ACPI: Reserving SSDT table memory at [mem 0x77ff2000-0x77ff277b]
+ACPI: Reserving ASF! table memory at [mem 0x77ff1000-0x77ff10a4]
+ACPI: Reserving ASPT table memory at [mem 0x77ff0000-0x77ff0033]
+ACPI: Reserving BOOT table memory at [mem 0x77fef000-0x77fef027]
+ACPI: Reserving HPET table memory at [mem 0x77fed000-0x77fed037]
+ACPI: Reserving LPIT table memory at [mem 0x77fec000-0x77fec093]
+ACPI: Reserving APIC table memory at [mem 0x77feb000-0x77feb0bb]
+ACPI: Reserving MCFG table memory at [mem 0x77fea000-0x77fea03b]
+ACPI: Reserving SSDT table memory at [mem 0x77fca000-0x77fca2d3]
+ACPI: Reserving SSDT table memory at [mem 0x77fc9000-0x77fc9199]
+ACPI: Reserving SSDT table memory at [mem 0x77fc8000-0x77fc83f3]
+ACPI: Reserving SSDT table memory at [mem 0x77fc7000-0x77fc7e57]
+ACPI: Reserving SSDT table memory at [mem 0x77fc2000-0x77fc6353]
+ACPI: Reserving DMAR table memory at [mem 0x77fc1000-0x77fc10a7]
+ACPI: Reserving FPDT table memory at [mem 0x77fbf000-0x77fbf043]
+Using GB pages for direct mapping
+RAMDISK: [mem 0x2efdd000-0x337e5fff]
+ACPI: Local APIC address 0xfee00000
+No NUMA configuration found
+Faking a node at [mem 0x0000000000000000-0x00000004807fffff]
+NODE_DATA(0) allocated [mem 0x4807d6000-0x4807fffff]
+Zone ranges:
+ DMA [mem 0x0000000000001000-0x0000000000ffffff]
+ DMA32 [mem 0x0000000001000000-0x00000000ffffffff]
+ Normal [mem 0x0000000100000000-0x00000004807fffff]
+ Device empty
+Movable zone start for each node
+Early memory node ranges
+ node 0: [mem 0x0000000000001000-0x000000000009dfff]
+ node 0: [mem 0x0000000000100000-0x000000005f692fff]
+ node 0: [mem 0x000000005f6de000-0x000000005f789fff]
+ node 0: [mem 0x000000006008a000-0x000000007558efff]
+ node 0: [mem 0x0000000077fff000-0x0000000077ffffff]
+ node 0: [mem 0x0000000100000000-0x00000004807fffff]
+Initmem setup node 0 [mem 0x0000000000001000-0x00000004807fffff]
+On node 0 totalpages: 4150242
+ DMA zone: 64 pages used for memmap
+ DMA zone: 21 pages reserved
+ DMA zone: 3997 pages, LIFO batch:0
+ DMA zone: 28771 pages in unavailable ranges
+ DMA32 zone: 7410 pages used for memmap
+ DMA32 zone: 474181 pages, LIFO batch:63
+ DMA32 zone: 13243 pages in unavailable ranges
+ Normal zone: 57376 pages used for memmap
+ Normal zone: 3672064 pages, LIFO batch:63
+ Normal zone: 30720 pages in unavailable ranges
+Reserving Intel graphics memory at [mem 0x7a800000-0x7e7fffff]
+ACPI: PM-Timer IO Port: 0x1808
+ACPI: Local APIC address 0xfee00000
+ACPI: LAPIC_NMI (acpi_id[0x01] high edge lint[0x1])
+ACPI: LAPIC_NMI (acpi_id[0x02] high edge lint[0x1])
+ACPI: LAPIC_NMI (acpi_id[0x03] high edge lint[0x1])
+ACPI: LAPIC_NMI (acpi_id[0x04] high edge lint[0x1])
+ACPI: LAPIC_NMI (acpi_id[0x05] high edge lint[0x1])
+ACPI: LAPIC_NMI (acpi_id[0x06] high edge lint[0x1])
+ACPI: LAPIC_NMI (acpi_id[0x07] high edge lint[0x1])
+ACPI: LAPIC_NMI (acpi_id[0x08] high edge lint[0x1])
+IOAPIC[0]: apic_id 2, version 32, address 0xfec00000, GSI 0-119
+ACPI: INT_SRC_OVR (bus 0 bus_irq 0 global_irq 2 dfl dfl)
+ACPI: INT_SRC_OVR (bus 0 bus_irq 9 global_irq 9 high level)
+ACPI: IRQ0 used by override.
+ACPI: IRQ9 used by override.
+Using ACPI (MADT) for SMP configuration information
+ACPI: HPET id: 0x8086a201 base: 0xfed00000
+TSC deadline timer available
+smpboot: Allowing 8 CPUs, 0 hotplug CPUs
+PM: hibernation: Registered nosave memory: [mem 0x00000000-0x00000fff]
+PM: hibernation: Registered nosave memory: [mem 0x0009e000-0x0009efff]
+PM: hibernation: Registered nosave memory: [mem 0x0009f000-0x0009ffff]
+PM: hibernation: Registered nosave memory: [mem 0x000a0000-0x000dffff]
+PM: hibernation: Registered nosave memory: [mem 0x000e0000-0x000fffff]
+PM: hibernation: Registered nosave memory: [mem 0x5f693000-0x5f693fff]
+PM: hibernation: Registered nosave memory: [mem 0x5f694000-0x5f6ddfff]
+PM: hibernation: Registered nosave memory: [mem 0x5f78a000-0x60089fff]
+PM: hibernation: Registered nosave memory: [mem 0x7558f000-0x75f7efff]
+PM: hibernation: Registered nosave memory: [mem 0x75f7f000-0x77f7efff]
+PM: hibernation: Registered nosave memory: [mem 0x77f7f000-0x77ffefff]
+PM: hibernation: Registered nosave memory: [mem 0x78000000-0x780fffff]
+PM: hibernation: Registered nosave memory: [mem 0x78100000-0x78ffffff]
+PM: hibernation: Registered nosave memory: [mem 0x79000000-0x7e7fffff]
+PM: hibernation: Registered nosave memory: [mem 0x7e800000-0xdfffffff]
+PM: hibernation: Registered nosave memory: [mem 0xe0000000-0xefffffff]
+PM: hibernation: Registered nosave memory: [mem 0xf0000000-0xfcffffff]
+PM: hibernation: Registered nosave memory: [mem 0xfd000000-0xfe7fffff]
+PM: hibernation: Registered nosave memory: [mem 0xfe800000-0xfeafffff]
+PM: hibernation: Registered nosave memory: [mem 0xfeb00000-0xfeb03fff]
+PM: hibernation: Registered nosave memory: [mem 0xfeb04000-0xfebfffff]
+PM: hibernation: Registered nosave memory: [mem 0xfec00000-0xfec00fff]
+PM: hibernation: Registered nosave memory: [mem 0xfec01000-0xfecfffff]
+PM: hibernation: Registered nosave memory: [mem 0xfed00000-0xfed00fff]
+PM: hibernation: Registered nosave memory: [mem 0xfed01000-0xfed0ffff]
+PM: hibernation: Registered nosave memory: [mem 0xfed10000-0xfed19fff]
+PM: hibernation: Registered nosave memory: [mem 0xfed1a000-0xfed83fff]
+PM: hibernation: Registered nosave memory: [mem 0xfed84000-0xfed84fff]
+PM: hibernation: Registered nosave memory: [mem 0xfed85000-0xfedfffff]
+PM: hibernation: Registered nosave memory: [mem 0xfee00000-0xfee00fff]
+PM: hibernation: Registered nosave memory: [mem 0xfee01000-0xff9fffff]
+PM: hibernation: Registered nosave memory: [mem 0xffa00000-0xffffffff]
+[mem 0x7e800000-0xdfffffff] available for PCI devices
+Booting paravirtualized kernel on bare hardware
+clocksource: refined-jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645519600211568 ns
+setup_percpu: NR_CPUS:8192 nr_cpumask_bits:8 nr_cpu_ids:8 nr_node_ids:1
+percpu: Embedded 54 pages/cpu s183960 r8192 d29032 u262144
+pcpu-alloc: s183960 r8192 d29032 u262144 alloc=1*2097152
+pcpu-alloc: [0] 0 1 2 3 4 5 6 7
+Built 1 zonelists, mobility grouping on. Total pages: 4085371
+Policy zone: Normal
+Kernel command line: BOOT_IMAGE=/vmlinuz-5.10.0-6-amd64 root=/dev/mapper/main-root ro quiet
+Dentry cache hash table entries: 2097152 (order: 12, 16777216 bytes, linear)
+Inode-cache hash table entries: 1048576 (order: 11, 8388608 bytes, linear)
+mem auto-init: stack:off, heap alloc:on, heap free:off
+Memory: 1904140K/16600968K available (12295K kernel code, 2544K rwdata, 7548K rodata, 2388K init, 3712K bss, 463332K reserved, 0K cma-reserved)
+random: get_random_u64 called from __kmem_cache_create+0x2e/0x550 with crng_init=0
+SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=8, Nodes=1
+Kernel/User page tables isolation: enabled
+ftrace: allocating 36387 entries in 143 pages
+ftrace: allocated 143 pages with 5 groups
+rcu: Hierarchical RCU implementation.
+rcu: RCU restricting CPUs from NR_CPUS=8192 to nr_cpu_ids=8.
+ Rude variant of Tasks RCU enabled.
+ Tracing variant of Tasks RCU enabled.
+rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
+rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=8
+NR_IRQS: 524544, nr_irqs: 2048, preallocated irqs: 16
+random: crng done (trusting CPU's manufacturer)
+Console: colour VGA+ 80x25
+printk: console [tty0] enabled
+ACPI: Core revision 20200925
+clocksource: hpet: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 79635855245 ns
+APIC: Switch to symmetric I/O mode setup
+DMAR: Host address width 39
+DMAR: DRHD base: 0x000000fed90000 flags: 0x0
+DMAR: dmar0: reg_base_addr fed90000 ver 1:0 cap 1c0000c40660462 ecap 7e3ff0501e
+DMAR: DRHD base: 0x000000fed91000 flags: 0x1
+DMAR: dmar1: reg_base_addr fed91000 ver 1:0 cap d2008c40660462 ecap f050da
+DMAR: RMRR base: 0x00000075e5a000 end: 0x00000075e79fff
+DMAR: RMRR base: 0x0000007a000000 end: 0x0000007e7fffff
+DMAR-IR: IOAPIC id 2 under DRHD base 0xfed91000 IOMMU 1
+DMAR-IR: HPET id 0 under DRHD base 0xfed91000
+DMAR-IR: x2apic is disabled because BIOS sets x2apic opt out bit.
+DMAR-IR: Use 'intremap=no_x2apic_optout' to override the BIOS setting.
+DMAR-IR: Enabled IRQ remapping in xapic mode
+x2apic: IRQ remapping doesn't support X2APIC mode
+..TIMER: vector=0x30 apic1=0 pin1=2 apic2=-1 pin2=-1
+clocksource: tsc-early: mask: 0xffffffffffffffff max_cycles: 0x257a34a6eea, max_idle_ns: 440795264358 ns
+Calibrating delay loop (skipped), value calculated using timer frequency.. 5199.98 BogoMIPS (lpj=10399968)
+pid_max: default: 32768 minimum: 301
+LSM: Security Framework initializing
+Yama: disabled by default; enable with sysctl kernel.yama.*
+AppArmor: AppArmor initialized
+TOMOYO Linux initialized
+Mount-cache hash table entries: 32768 (order: 6, 262144 bytes, linear)
+Mountpoint-cache hash table entries: 32768 (order: 6, 262144 bytes, linear)
+mce: CPU0: Thermal monitoring enabled (TM1)
+process: using mwait in idle threads
+Last level iTLB entries: 4KB 64, 2MB 8, 4MB 8
+Last level dTLB entries: 4KB 64, 2MB 0, 4MB 0, 1GB 4
+Spectre V1 : Mitigation: usercopy/swapgs barriers and __user pointer sanitization
+Spectre V2 : Mitigation: Full generic retpoline
+Spectre V2 : Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch
+Spectre V2 : Enabling Restricted Speculation for firmware calls
+Spectre V2 : mitigation: Enabling conditional Indirect Branch Prediction Barrier
+Spectre V2 : User space: Mitigation: STIBP via seccomp and prctl
+Speculative Store Bypass: Mitigation: Speculative Store Bypass disabled via prctl and seccomp
+TAA: Mitigation: Clear CPU buffers
+SRBDS: Mitigation: Microcode
+MDS: Mitigation: Clear CPU buffers
+Freeing SMP alternatives memory: 32K
+smpboot: CPU0: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz (family: 0x6, model: 0x5e, stepping: 0x3)
+Performance Events: PEBS fmt3+, Skylake events, 32-deep LBR, full-width counters, Intel PMU driver.
+... version: 4
+... bit width: 48
+... generic registers: 4
+... value mask: 0000ffffffffffff
+... max period: 00007fffffffffff
+... fixed-purpose events: 3
+... event mask: 000000070000000f
+rcu: Hierarchical SRCU implementation.
+NMI watchdog: Enabled. Permanently consumes one hw-PMU counter.
+smp: Bringing up secondary CPUs ...
+x86: Booting SMP configuration:
+.... node #0, CPUs: #1 #2 #3 #4
+MDS CPU bug present and SMT on, data leak possible. See https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/mds.html for more details.
+TAA CPU bug present and SMT on, data leak possible. See https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/tsx_async_abort.html for more details.
+ #5 #6 #7
+smp: Brought up 1 node, 8 CPUs
+smpboot: Max logical packages: 1
+smpboot: Total of 8 processors activated (41599.87 BogoMIPS)
+node 0 deferred pages initialised in 16ms
+devtmpfs: initialized
+x86/mm: Memory block size: 128MB
+PM: Registering ACPI NVS region [mem 0x5f693000-0x5f693fff] (4096 bytes)
+PM: Registering ACPI NVS region [mem 0x75f7f000-0x77f7efff] (33554432 bytes)
+clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
+futex hash table entries: 2048 (order: 5, 131072 bytes, linear)
+pinctrl core: initialized pinctrl subsystem
+NET: Registered protocol family 16
+audit: initializing netlink subsys (disabled)
+audit: type=2000 audit(1622213389.056:1): state=initialized audit_enabled=0 res=1
+thermal_sys: Registered thermal governor 'fair_share'
+thermal_sys: Registered thermal governor 'bang_bang'
+thermal_sys: Registered thermal governor 'step_wise'
+thermal_sys: Registered thermal governor 'user_space'
+thermal_sys: Registered thermal governor 'power_allocator'
+cpuidle: using governor ladder
+cpuidle: using governor menu
+Simple Boot Flag at 0x44 set to 0x1
+ACPI: bus type PCI registered
+acpiphp: ACPI Hot Plug PCI Controller Driver version: 0.5
+PCI: MMCONFIG for domain 0000 [bus 00-ff] at [mem 0xe0000000-0xefffffff] (base 0xe0000000)
+PCI: MMCONFIG at [mem 0xe0000000-0xefffffff] reserved in E820
+pmd_set_huge: Cannot satisfy [mem 0xe0000000-0xe0200000] with a huge-page mapping due to MTRR override.
+PCI: Using configuration type 1 for base access
+ENERGY_PERF_BIAS: Set to 'normal', was 'performance'
+Kprobes globally optimized
+HugeTLB registered 1.00 GiB page size, pre-allocated 0 pages
+HugeTLB registered 2.00 MiB page size, pre-allocated 0 pages
+ACPI: Added _OSI(Module Device)
+ACPI: Added _OSI(Processor Device)
+ACPI: Added _OSI(3.0 _SCP Extensions)
+ACPI: Added _OSI(Processor Aggregator Device)
+ACPI: Added _OSI(Linux-Dell-Video)
+ACPI: Added _OSI(Linux-Lenovo-NV-HDMI-Audio)
+ACPI: Added _OSI(Linux-HPI-Hybrid-Graphics)
+ACPI: 8 ACPI AML tables successfully acquired and loaded
+ACPI: [Firmware Bug]: BIOS _OSI(Linux) query ignored
+ACPI: Dynamic OEM Table Load:
+ACPI: SSDT 0xFFFF9FC7C0C14800 0005DC (v02 PmRef Cpu0Ist 00003000 INTL 20130927)
+ACPI: \_PR_.CPU0: _OSC native thermal LVT Acked
+ACPI: Dynamic OEM Table Load:
+ACPI: SSDT 0xFFFF9FC7C10D0800 00037F (v02 PmRef Cpu0Cst 00003001 INTL 20130927)
+ACPI: Dynamic OEM Table Load:
+ACPI: SSDT 0xFFFF9FC7C0C12000 0005AA (v02 PmRef ApIst 00003000 INTL 20130927)
+ACPI: Dynamic OEM Table Load:
+ACPI: SSDT 0xFFFF9FC7C1023200 000119 (v02 PmRef ApCst 00003000 INTL 20130927)
+ACPI: EC: EC started
+ACPI: EC: interrupt blocked
+ACPI: EC: EC_CMD/EC_SC=0x66, EC_DATA=0x62
+ACPI: \_SB_.PCI0.LPCB.EC0_: Boot DSDT EC used to handle transactions
+ACPI: Interpreter enabled
+ACPI: (supports S0 S3 S4 S5)
+ACPI: Using IOAPIC for interrupt routing
+PCI: Using host bridge windows from ACPI; if necessary, use "pci=nocrs" and report a bug
+ACPI: Enabled 9 GPEs in block 00 to 7F
+ACPI: Power Resource [PG00] (on)
+ACPI: PCI Root Bridge [PCI0] (domain 0000 [bus 00-fe])
+acpi PNP0A08:00: _OSC: OS supports [ExtendedConfig ASPM ClockPM Segments MSI HPX-Type3]
+acpi PNP0A08:00: _OSC: OS now controls [PCIeHotplug SHPCHotplug PME AER PCIeCapability LTR]
+PCI host bridge to bus 0000:00
+pci_bus 0000:00: root bus resource [io 0x0000-0x0cf7 window]
+pci_bus 0000:00: root bus resource [io 0x0d00-0xffff window]
+pci_bus 0000:00: root bus resource [mem 0x000a0000-0x000bffff window]
+pci_bus 0000:00: root bus resource [mem 0x7e800000-0xdfffffff window]
+pci_bus 0000:00: root bus resource [mem 0xfd000000-0xfe7fffff window]
+pci_bus 0000:00: root bus resource [bus 00-fe]
+pci 0000:00:00.0: [8086:1910] type 00 class 0x060000
+pci 0000:00:01.0: [8086:1901] type 01 class 0x060400
+pci 0000:00:01.0: PME# supported from D0 D3hot D3cold
+pci 0000:00:02.0: [8086:191b] type 00 class 0x030000
+pci 0000:00:02.0: reg 0x10: [mem 0x92000000-0x92ffffff 64bit]
+pci 0000:00:02.0: reg 0x18: [mem 0xa0000000-0xafffffff 64bit pref]
+pci 0000:00:02.0: reg 0x20: [io 0x5000-0x503f]
+pci 0000:00:14.0: [8086:a12f] type 00 class 0x0c0330
+pci 0000:00:14.0: reg 0x10: [mem 0x94300000-0x9430ffff 64bit]
+pci 0000:00:14.0: PME# supported from D3hot D3cold
+pci 0000:00:16.0: [8086:a13a] type 00 class 0x078000
+pci 0000:00:16.0: reg 0x10: [mem 0x9432a000-0x9432afff 64bit]
+pci 0000:00:16.0: PME# supported from D3hot
+pci 0000:00:17.0: [8086:a103] type 00 class 0x010601
+pci 0000:00:17.0: reg 0x10: [mem 0x94328000-0x94329fff]
+pci 0000:00:17.0: reg 0x14: [mem 0x9432d000-0x9432d0ff]
+pci 0000:00:17.0: reg 0x18: [io 0x5080-0x5087]
+pci 0000:00:17.0: reg 0x1c: [io 0x5088-0x508b]
+pci 0000:00:17.0: reg 0x20: [io 0x5060-0x507f]
+pci 0000:00:17.0: reg 0x24: [mem 0x9432b000-0x9432b7ff]
+pci 0000:00:17.0: PME# supported from D3hot
+pci 0000:00:1c.0: [8086:a111] type 01 class 0x060400
+pci 0000:00:1c.0: PME# supported from D0 D3hot D3cold
+pci 0000:00:1c.0: Intel SPT PCH root port ACS workaround enabled
+pci 0000:00:1c.2: [8086:a112] type 01 class 0x060400
+pci 0000:00:1c.2: PME# supported from D0 D3hot D3cold
+pci 0000:00:1c.2: Intel SPT PCH root port ACS workaround enabled
+pci 0000:00:1c.3: [8086:a113] type 01 class 0x060400
+pci 0000:00:1c.3: PME# supported from D0 D3hot D3cold
+pci 0000:00:1c.3: Intel SPT PCH root port ACS workaround enabled
+pci 0000:00:1f.0: [8086:a14e] type 00 class 0x060100
+pci 0000:00:1f.2: [8086:a121] type 00 class 0x058000
+pci 0000:00:1f.2: reg 0x10: [mem 0x94324000-0x94327fff]
+pci 0000:00:1f.3: [8086:a170] type 00 class 0x040300
+pci 0000:00:1f.3: reg 0x10: [mem 0x94320000-0x94323fff 64bit]
+pci 0000:00:1f.3: reg 0x20: [mem 0x94310000-0x9431ffff 64bit]
+pci 0000:00:1f.3: PME# supported from D3hot D3cold
+pci 0000:00:1f.4: [8086:a123] type 00 class 0x0c0500
+pci 0000:00:1f.4: reg 0x10: [mem 0x9432c000-0x9432c0ff 64bit]
+pci 0000:00:1f.4: reg 0x20: [io 0x5040-0x505f]
+pci 0000:01:00.0: [10de:139b] type 00 class 0x030200
+pci 0000:01:00.0: reg 0x10: [mem 0x93000000-0x93ffffff]
+pci 0000:01:00.0: reg 0x14: [mem 0x80000000-0x8fffffff 64bit pref]
+pci 0000:01:00.0: reg 0x1c: [mem 0x90000000-0x91ffffff 64bit pref]
+pci 0000:01:00.0: reg 0x24: [io 0x4000-0x407f]
+pci 0000:01:00.0: reg 0x30: [mem 0xfff80000-0xffffffff pref]
+pci 0000:01:00.0: Enabling HDA controller
+pci 0000:00:01.0: PCI bridge to [bus 01-06]
+pci 0000:00:01.0: bridge window [io 0x4000-0x4fff]
+pci 0000:00:01.0: bridge window [mem 0x93000000-0x93ffffff]
+pci 0000:00:01.0: bridge window [mem 0x80000000-0x91ffffff 64bit pref]
+pci 0000:07:00.0: [1217:8520] type 00 class 0x080501
+pci 0000:07:00.0: reg 0x10: [mem 0x94201000-0x94201fff]
+pci 0000:07:00.0: reg 0x14: [mem 0x94200000-0x942007ff]
+pci 0000:07:00.0: PME# supported from D3hot D3cold
+pci 0000:00:1c.0: PCI bridge to [bus 07]
+pci 0000:00:1c.0: bridge window [mem 0x94200000-0x942fffff]
+pci 0000:08:00.0: [8086:3166] type 00 class 0x028000
+pci 0000:08:00.0: reg 0x10: [mem 0x94100000-0x94101fff 64bit]
+pci 0000:08:00.0: PME# supported from D0 D3hot D3cold
+pci 0000:00:1c.2: PCI bridge to [bus 08]
+pci 0000:00:1c.2: bridge window [mem 0x94100000-0x941fffff]
+pci 0000:09:00.0: [10ec:8168] type 00 class 0x020000
+pci 0000:09:00.0: reg 0x10: [io 0x3000-0x30ff]
+pci 0000:09:00.0: reg 0x18: [mem 0x94004000-0x94004fff 64bit]
+pci 0000:09:00.0: reg 0x20: [mem 0x94000000-0x94003fff 64bit]
+pci 0000:09:00.0: supports D1 D2
+pci 0000:09:00.0: PME# supported from D0 D1 D2 D3hot D3cold
+pci 0000:00:1c.3: PCI bridge to [bus 09]
+pci 0000:00:1c.3: bridge window [io 0x3000-0x3fff]
+pci 0000:00:1c.3: bridge window [mem 0x94000000-0x940fffff]
+ACPI: PCI Interrupt Link [LNKA] (IRQs 3 4 5 6 10 *11 12 14 15)
+ACPI: PCI Interrupt Link [LNKB] (IRQs 3 4 5 6 *10 11 12 14 15)
+ACPI: PCI Interrupt Link [LNKC] (IRQs 3 4 5 6 10 *11 12 14 15)
+ACPI: PCI Interrupt Link [LNKD] (IRQs 3 4 5 6 10 *11 12 14 15)
+ACPI: PCI Interrupt Link [LNKE] (IRQs 3 4 5 6 10 *11 12 14 15)
+ACPI: PCI Interrupt Link [LNKF] (IRQs 3 4 5 6 10 *11 12 14 15)
+ACPI: PCI Interrupt Link [LNKG] (IRQs 3 4 5 6 10 *11 12 14 15)
+ACPI: PCI Interrupt Link [LNKH] (IRQs 3 4 5 6 10 *11 12 14 15)
+ACPI: EC: interrupt unblocked
+ACPI: EC: event unblocked
+ACPI: EC: EC_CMD/EC_SC=0x66, EC_DATA=0x62
+ACPI: EC: GPE=0x2
+ACPI: \_SB_.PCI0.LPCB.EC0_: Boot DSDT EC initialization complete
+ACPI: \_SB_.PCI0.LPCB.EC0_: EC: Used to handle transactions and events
+iommu: Default domain type: Translated
+pci 0000:00:02.0: vgaarb: setting as boot VGA device
+pci 0000:00:02.0: vgaarb: VGA device added: decodes=io+mem,owns=io+mem,locks=none
+pci 0000:00:02.0: vgaarb: bridge control possible
+vgaarb: loaded
+EDAC MC: Ver: 3.0.0
+NetLabel: Initializing
+NetLabel: domain hash size = 128
+NetLabel: protocols = UNLABELED CIPSOv4 CALIPSO
+NetLabel: unlabeled traffic allowed by default
+PCI: Using ACPI for IRQ routing
+PCI: pci_cache_line_size set to 64 bytes
+e820: reserve RAM buffer [mem 0x0009e800-0x0009ffff]
+e820: reserve RAM buffer [mem 0x5f693000-0x5fffffff]
+e820: reserve RAM buffer [mem 0x5f78a000-0x5fffffff]
+e820: reserve RAM buffer [mem 0x7558f000-0x77ffffff]
+e820: reserve RAM buffer [mem 0x480800000-0x483ffffff]
+hpet0: at MMIO 0xfed00000, IRQs 2, 8, 0, 0, 0, 0, 0, 0
+hpet0: 8 comparators, 64-bit 24.000000 MHz counter
+clocksource: Switched to clocksource tsc-early
+VFS: Disk quotas dquot_6.6.0
+VFS: Dquot-cache hash table entries: 512 (order 0, 4096 bytes)
+AppArmor: AppArmor Filesystem Enabled
+pnp: PnP ACPI init
+system 00:00: [mem 0xfd000000-0xfdabffff] has been reserved
+system 00:00: [mem 0xfdad0000-0xfdadffff] has been reserved
+system 00:00: [mem 0xfdb00000-0xfdffffff] has been reserved
+system 00:00: [mem 0xfe000000-0xfe01ffff] has been reserved
+system 00:00: [mem 0xfe036000-0xfe03bfff] has been reserved
+system 00:00: [mem 0xfe03d000-0xfe3fffff] has been reserved
+system 00:00: [mem 0xfe410000-0xfe7fffff] has been reserved
+system 00:00: Plug and Play ACPI device, IDs PNP0c02 (active)
+system 00:01: [io 0x2000-0x20fe] has been reserved
+system 00:01: Plug and Play ACPI device, IDs PNP0c02 (active)
+system 00:02: [io 0x0680-0x069f] has been reserved
+system 00:02: [io 0xffff] has been reserved
+system 00:02: [io 0xffff] has been reserved
+system 00:02: [io 0xffff] has been reserved
+system 00:02: [io 0x1800-0x18fe] has been reserved
+system 00:02: [io 0x164e-0x164f] has been reserved
+system 00:02: Plug and Play ACPI device, IDs PNP0c02 (active)
+system 00:03: [io 0x0800-0x087f] has been reserved
+system 00:03: Plug and Play ACPI device, IDs PNP0c02 (active)
+pnp 00:04: Plug and Play ACPI device, IDs PNP0b00 (active)
+system 00:05: [io 0x1854-0x1857] has been reserved
+system 00:05: Plug and Play ACPI device, IDs INT3f0d PNP0c02 (active)
+pnp 00:06: Plug and Play ACPI device, IDs PNP0303 (active)
+pnp 00:07: Plug and Play ACPI device, IDs SYN2b5b PNP0f03 (active)
+system 00:08: [mem 0xfe035000-0xfe035fff] has been reserved
+system 00:08: [mem 0xfe034008-0xfe034fff] has been reserved
+system 00:08: [mem 0xfdaf0000-0xfdafffff] has been reserved
+system 00:08: [mem 0xfdae0000-0xfdaeffff] has been reserved
+system 00:08: [mem 0xfdac0000-0xfdacffff] has been reserved
+system 00:08: Plug and Play ACPI device, IDs PNP0c02 (active)
+system 00:09: [mem 0xfe034000-0xfe034007] has been reserved
+system 00:09: Plug and Play ACPI device, IDs PNP0c02 (active)
+system 00:0a: [mem 0xfed10000-0xfed17fff] has been reserved
+system 00:0a: [mem 0xfed18000-0xfed18fff] has been reserved
+system 00:0a: [mem 0xfed19000-0xfed19fff] has been reserved
+system 00:0a: [mem 0xe0000000-0xefffffff] has been reserved
+system 00:0a: [mem 0xfed20000-0xfed3ffff] has been reserved
+system 00:0a: [mem 0xfed90000-0xfed93fff] could not be reserved
+system 00:0a: [mem 0xfed45000-0xfed8ffff] could not be reserved