Initial commit
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Sun, 15 Mar 2026 01:16:30 +0000 (03:16 +0200)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Sun, 15 Mar 2026 01:16:30 +0000 (03:16 +0200)
33 files changed:
.gitignore [new file with mode: 0644]
AGENTS.md [new file with mode: 0644]
COPYING [new file with mode: 0644]
Tools/Open with IntelliJ IDEA [new file with mode: 0755]
Tools/Update web site [new file with mode: 0755]
doc/.gitignore [new file with mode: 0644]
doc/index.org [new file with mode: 0644]
doc/overview.png [new file with mode: 0644]
doc/screenshots/life.png [new file with mode: 0644]
doc/screenshots/mathematical formulas.png [new file with mode: 0644]
doc/screenshots/raytracing fractal in voxel polygon hybrid scene.png [new file with mode: 0644]
doc/screenshots/sinus heightmaps and sphere.png [new file with mode: 0644]
doc/screenshots/text editors 2.png [new file with mode: 0644]
doc/screenshots/text editors.png [new file with mode: 0644]
pom.xml [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/FillRateTest.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/GraphDemo.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/examples/OctreeDemo.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/examples/RainingNumbersDemo.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/RandomPolygonsDemo.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/examples/ShadedShapesDemo.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/TextEditorDemo.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/TextEditorDemo2.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/Galaxy.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/PointCloudDemo.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/ApplicationListPanel.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/Main.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Cell.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Main.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Matrix.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Star.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/sixth/e3d/examples/package-info.java [new file with mode: 0755]
src/main/resources/demo.txt [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..31378ad
--- /dev/null
@@ -0,0 +1,9 @@
+/.idea/
+/target/
+/.classpath
+/.project
+/.settings/
+/doc/graphs/
+/doc/apidocs/
+/*.iml
+*.html
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644 (file)
index 0000000..8b6057f
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,121 @@
+# 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
+
+# Run Game of Life demo
+java -cp target/sixth-3d-demos.jar eu.svjatoslav.sixth.e3d.examples.life_demo.Main
+
+# Run Point Cloud Galaxy demo
+java -cp target/sixth-3d-demos.jar eu.svjatoslav.sixth.e3d.examples.galaxy_demo.PointCloudDemo
+```
+
+### 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
+├── galaxy_demo/        - Galaxy simulation demo
+│   ├── Galaxy.java
+│   └── PointCloudDemo.java
+├── RandomPolygonsDemo.java
+├── OctreeDemo.java
+├── GraphDemo.java
+├── TextEditorDemo.java
+├── TextEditorDemo2.java
+├── RainingNumbersDemo.java
+└── 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.
+
+## Documentation
+
+Always make sure that documentation in`doc/index.org` stays up to date.
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..0e259d4
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,121 @@
+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.
diff --git a/Tools/Open with IntelliJ IDEA b/Tools/Open with IntelliJ IDEA
new file mode 100755 (executable)
index 0000000..304bf94
--- /dev/null
@@ -0,0 +1,54 @@
+#!/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
diff --git a/Tools/Update web site b/Tools/Update web site
new file mode 100755 (executable)
index 0000000..26537ed
--- /dev/null
@@ -0,0 +1,103 @@
+#!/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
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644 (file)
index 0000000..0cc8fd2
--- /dev/null
@@ -0,0 +1,2 @@
+apidocs/
+sixth-3d-demos.jar
diff --git a/doc/index.org b/doc/index.org
new file mode 100644 (file)
index 0000000..6ca38c4
--- /dev/null
@@ -0,0 +1,242 @@
+#+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]]
+
+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 demo application, use command:
+: java -jar sixth-3d-demos.jar
+
+* Navigating in space
+:PROPERTIES:
+:CUSTOM_ID: navigating-in-space
+:ID:       0aca7419-48df-48ac-b18b-a8bf7b9db0ac
+:END:
+
+| key                 | result                               |
+|---------------------+--------------------------------------|
+| Keyboard arrow keys | Move: left, right, forward, backward |
+| Mouse scroll wheel  | Move: up <-> down                    |
+| Mouse drag          | Turn head around in 3D space         |
+
+* Example scenes
+: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:
+
+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]]
+
+Current application projects 2D game grid/matrix onto three
+dimensional space. Extra dimension (height) is used to visualize
+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 iteeration with the history     |
+| "c"                            | clear the matrix                     |
+
+** Text editors
+:PROPERTIES:
+:CUSTOM_ID: text-editors
+:ID:       39250157-db8e-4861-a21b-8568912bd160
+:END:
+
+[[file:screenshots/text editors.png]]
+
+Initial test for creating user interfaces in 3D and:
++ window focus handling
++ picking objecs using mouse
++ redirecting keyboard input to focused window
+
+
+Window focus acts like a stack.
+
+When window is clicked with the mouse, previously focused window (if
+any) is pushed to the focus stack and new window receives focus. Red
+frame appears around the window to indicate this.
+
+When ESC key is pressed, window focus is returned to previous window
+(if any).
+
+When any window is focused, all keyboard input is redirected to that
+window, including cursor keys. To be able to navigate around the world
+again, window must be unfocused first using 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:
+
+*Quite a lot of text editors can be rendered:*
+
+[[file:screenshots/text editors 2.png]]
+
+See also [[https://hackers-1995.vercel.app/][similar looking web based demo]] ! :)
+
+** Mathematical formulas
+:PROPERTIES:
+:CUSTOM_ID: mathematical-formulas
+:ID:       a1b2c3d4-e5f6-7890-abcd-ef1234567890
+:END:
+
+[[file:screenshots/mathematical formulas.png]]
+
++ TODO: instead of projecting 2D visualizations onto 3D space,
+  visualize some formula using all 3 dimensions avaliable.
+
+** Sine heightmaps and sphere
+:PROPERTIES:
+:CUSTOM_ID: sine-heightmaps-and-sphere
+:ID:       b2c3d4e5-f6a7-8901-bcde-f12345678901
+:END:
+
+[[file:screenshots/sinus heightmaps 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:
+
+[[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 dumb [X * Y * Z] array, dynamically
+partitioned [[https://en.wikipedia.org/wiki/Octree][octree]] is used to compress data. Press "r" key anywhere in
+the scene to raytrace current view through compressed voxel
+datastructure.
+
+** Fill-rate test
+:PROPERTIES:
+:CUSTOM_ID: fill-rate-test
+:ID:       e5f6a7b8-c9d0-1234-ef01-345678901234
+:END:
+
+A benchmark for polygon rasterization performance testing. This demo
+tests pixel fill throughput by rendering 40 large screen-filling quads
+(600x600) layered at different Z depths.
+
+The low vertex count (4 vertices per quad) combined with high overdraw
+isolates the pixel fill cost from vertex processing overhead, allowing
+you to measure pure rasterization performance.
+
+Controls:
+| key | result              |
+|-----+---------------------|
+| 1   | Solid colored quads |
+| 2   | Textured quads      |
+
+Compare FPS between solid and textured modes to see exactly what
+texture sampling costs on your hardware.
+
+* Source code
+:PROPERTIES:
+:CUSTOM_ID: source-code
+:ID:       d4e5f6a7-b8c9-0123-def0-234567890123
+:END:
+
+*This program is free software: released under 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 online [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/][JavaDoc]].
+- Study underlying [[https://www3.svjatoslav.eu/projects/sixth-3d/][Sixth 3D]] engine.
diff --git a/doc/overview.png b/doc/overview.png
new file mode 100644 (file)
index 0000000..ba977ab
Binary files /dev/null and b/doc/overview.png differ
diff --git a/doc/screenshots/life.png b/doc/screenshots/life.png
new file mode 100644 (file)
index 0000000..565f5ab
Binary files /dev/null and b/doc/screenshots/life.png differ
diff --git a/doc/screenshots/mathematical formulas.png b/doc/screenshots/mathematical formulas.png
new file mode 100644 (file)
index 0000000..fab8469
Binary files /dev/null and b/doc/screenshots/mathematical formulas.png differ
diff --git a/doc/screenshots/raytracing fractal in voxel polygon hybrid scene.png b/doc/screenshots/raytracing fractal in voxel polygon hybrid scene.png
new file mode 100644 (file)
index 0000000..7094240
Binary files /dev/null and b/doc/screenshots/raytracing fractal in voxel polygon hybrid scene.png differ
diff --git a/doc/screenshots/sinus heightmaps and sphere.png b/doc/screenshots/sinus heightmaps and sphere.png
new file mode 100644 (file)
index 0000000..0d3e92b
Binary files /dev/null and b/doc/screenshots/sinus heightmaps and sphere.png differ
diff --git a/doc/screenshots/text editors 2.png b/doc/screenshots/text editors 2.png
new file mode 100644 (file)
index 0000000..cb1733f
Binary files /dev/null and b/doc/screenshots/text editors 2.png differ
diff --git a/doc/screenshots/text editors.png b/doc/screenshots/text editors.png
new file mode 100644 (file)
index 0000000..8567b8b
Binary files /dev/null and b/doc/screenshots/text editors.png differ
diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..4baf761
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,173 @@
+<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>
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/FillRateTest.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/FillRateTest.java
new file mode 100644 (file)
index 0000000..74f415b
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * 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.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.WorldNavigationUserInputTracker;
+import eu.svjatoslav.sixth.e3d.math.Vertex;
+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.texturedpolygon.TexturedPolygon;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCube;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Fill-rate benchmark demo that tests the engine's rendering performance.
+ * Creates a 16x16x16 grid of cubes (4096 total) that orbit around the center point.
+ * The camera follows a wobbling orbital path while FPS statistics are printed to console.
+ * 
+ * <p>Key controls:
+ * <ul>
+ *   <li>Press 1 - Switch to solid color cubes (faster rendering)</li>
+ *   <li>Press 2 - Switch to textured cubes (slower rendering, tests texture fill-rate)</li>
+ * </ul>
+ * 
+ * @see SolidPolygonCube
+ * @see TexturedPolygon
+ */
+public class FillRateTest extends WorldNavigationUserInputTracker implements FrameListener {
+
+    /** Number of cubes along each axis of the grid. */
+    private static final int GRID_SIZE = 16;
+    /** Spacing between cube centers in world units. */
+    private static final double SPACING = 80;
+    /** Half-size of each cube. */
+    private static final double CUBE_SIZE = 25;
+    /** Distance of camera from center during orbit. */
+    private static final double ORBIT_DISTANCE = 1200;
+    /** Angular speed of camera orbit in radians per millisecond. */
+    private static final double ORBIT_SPEED = 0.0003;
+    /** Amplitude of vertical wobble during orbit. */
+    private static final double WOBBLE_AMPLITUDE = 800;
+    /** Number of unique textures to create for textured mode. */
+    private static final int TEXTURE_COUNT = 20;
+
+    private final ViewFrame viewFrame;
+    private final ViewPanel viewPanel;
+    private final ShapeCollection shapes;
+    private final Random random = new Random(42);
+    private final Camera camera;
+
+    private final List<Object> cubes = new ArrayList<>();
+    private final Texture[] textures = new Texture[TEXTURE_COUNT];
+    private final int[] cubeTextureIndices;
+
+    private boolean texturedMode = false;
+    private double orbitAngle = 0;
+
+    private long frameCount = 0;
+    private long fpsStartTime = System.currentTimeMillis();
+    private long totalFrameCount = 0;
+    private long appStartTime = System.currentTimeMillis();
+
+    /** Resets FPS statistics counters. */
+    private void resetFpsStats() {
+        frameCount = 0;
+        fpsStartTime = System.currentTimeMillis();
+        totalFrameCount = 0;
+        appStartTime = fpsStartTime;
+    }
+
+    /**
+     * Entry point for the fill-rate test demo.
+     * @param args command line arguments (ignored)
+     */
+    public static void main(String[] args) {
+        new FillRateTest();
+    }
+
+    /**
+     * Constructs the fill-rate test demo with a 1920x1080 window.
+     * Creates the cube grid and initializes camera orbit animation.
+     */
+    public FillRateTest() {
+        viewFrame = new ViewFrame(1920, 1080);
+        viewPanel = viewFrame.getViewPanel();
+        viewPanel.setFrameRate(0);
+        shapes = viewPanel.getRootShapeCollection();
+
+        viewPanel.addFrameListener(this);
+        viewPanel.getKeyboardFocusStack().pushFocusOwner(this);
+
+        camera = viewPanel.getCamera();
+
+        cubeTextureIndices = new int[GRID_SIZE * GRID_SIZE * GRID_SIZE];
+        for (int i = 0; i < cubeTextureIndices.length; i++) {
+            cubeTextureIndices[i] = random.nextInt(TEXTURE_COUNT);
+        }
+
+        createTextures();
+        createCubes(false);
+    }
+
+    /** Creates the pool of randomly colored glow textures. */
+    private void createTextures() {
+        for (int i = 0; i < TEXTURE_COUNT; i++) {
+            textures[i] = createGlowTexture(
+                    50 + random.nextInt(200),
+                    50 + random.nextInt(200),
+                    50 + random.nextInt(200)
+            );
+        }
+    }
+
+    /**
+     * Creates a glow texture with the specified RGB color.
+     * @param r red component (0-255)
+     * @param g green component (0-255)
+     * @param b blue component (0-255)
+     * @return a 64x64 texture with glowing border effect
+     */
+    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, 80));
+        gr.clearRect(0, 0, texSize, texSize);
+
+        int glowWidth = 6;
+        for (int i = 0; i < glowWidth; i++) {
+            int intensity = (int) (255.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),
+                    200 - i * 30
+            );
+            gr.setColor(glowColor);
+            gr.drawRect(i, i, texSize - 1 - 2 * i, texSize - 1 - 2 * i);
+        }
+
+        gr.dispose();
+        texture.resetResampledBitmapCache();
+        return texture;
+    }
+
+    /** Removes all cubes from the scene. */
+    private void clearCubes() {
+        for (Object cube : cubes) {
+            shapes.getShapes().remove(cube);
+        }
+        cubes.clear();
+    }
+
+    /**
+     * Creates the grid of cubes in the scene.
+     * @param textured if true, creates textured cubes; if false, creates solid-colored cubes
+     */
+    private void createCubes(boolean textured) {
+        clearCubes();
+        random.setSeed(42);
+
+        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;
+
+                    if (textured) {
+                        Texture tex = textures[cubeTextureIndices[idx]];
+                        TexturedCube cube = new TexturedCube(
+                                new Point3D(px, py, pz),
+                                CUBE_SIZE,
+                                tex
+                        );
+                        shapes.addShape(cube);
+                        cubes.add(cube);
+                    } else {
+                        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);
+                    }
+                    idx++;
+                }
+            }
+        }
+    }
+
+    /**
+     * Called each frame to animate the camera orbit and track FPS.
+     * @param viewPanel the view panel rendering the scene
+     * @param millisecondsSinceLastFrame time elapsed since last frame
+     * @return true to continue rendering
+     */
+    @Override
+    public boolean onFrame(ViewPanel viewPanel, int millisecondsSinceLastFrame) {
+        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++;
+        totalFrameCount++;
+        long now = System.currentTimeMillis();
+        long elapsed = now - fpsStartTime;
+        if (elapsed >= 1000) {
+            int fps = (int) (frameCount * 1000 / elapsed);
+            long totalElapsed = now - appStartTime;
+            double avgFps = totalFrameCount * 1000.0 / totalElapsed;
+            System.out.println("current: " + fps + " average: " + String.format("%.2f", avgFps));
+            frameCount = 0;
+            fpsStartTime = now;
+        }
+
+        return true;
+    }
+
+    /**
+     * Handles keyboard input for switching between rendering modes.
+     * @param event the key event
+     * @param viewPanel the view panel
+     * @return true if the event was consumed
+     */
+    @Override
+    public boolean keyPressed(KeyEvent event, ViewPanel viewPanel) {
+        switch (event.getKeyCode()) {
+            case KeyEvent.VK_1:
+                if (texturedMode) {
+                    texturedMode = false;
+                    createCubes(false);
+                    resetFpsStats();
+                }
+                return true;
+            case KeyEvent.VK_2:
+                if (!texturedMode) {
+                    texturedMode = true;
+                    createCubes(true);
+                    resetFpsStats();
+                }
+                return true;
+        }
+        return super.keyPressed(event, viewPanel);
+    }
+
+    /**
+     * A cube composed of textured polygons.
+     * Each face is rendered as two triangles with UV coordinates for texture mapping.
+     */
+    private static class TexturedCube extends AbstractCompositeShape {
+
+        /**
+         * Constructs a textured cube at the specified position.
+         * @param center the center position of the cube
+         * @param size half-size of the cube (total width is 2*size)
+         * @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);
+        }
+
+        /**
+         * Adds a quad face as two textured triangles.
+         * @param p1 first corner position
+         * @param p2 second corner position
+         * @param p3 third corner position
+         * @param p4 fourth corner position
+         * @param t1 texture coordinate for p1
+         * @param t2 texture coordinate for p2
+         * @param t3 texture coordinate for p3
+         * @param t4 texture coordinate for p4
+         * @param texture the texture to apply
+         */
+        private void addTexturedFace(Point3D p1, Point3D p2, Point3D p3, Point3D p4,
+                                     Point2D t1, Point2D t2, Point2D t3, Point2D t4,
+                                     Texture texture) {
+            TexturedPolygon tri1 = new TexturedPolygon(
+                    new Vertex(p1, t1),
+                    new Vertex(p2, t2),
+                    new Vertex(p3, t3),
+                    texture
+            );
+            tri1.setBackfaceCulling(true);
+
+            TexturedPolygon tri2 = new TexturedPolygon(
+                    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
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/GraphDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/GraphDemo.java
new file mode 100755 (executable)
index 0000000..ae7ff23
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * 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.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.Graph;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.WireframeSphere;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Demo showing various mathematical function graphs rendered in 3D.
+ * Displays sine, cosine, tangent, and composite function graphs arranged around
+ * a central sphere, with two wobbly surfaces above and below.
+ */
+public class GraphDemo {
+
+    /** 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 = new Color("88F7");
+    /** Scale factor for the graph rendering. */
+    private static final double GRAPH_SCALE = 50d;
+
+    /**
+     * 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 = new Point3D(x, y, z);
+        final Point3D p2 = new Point3D(x + 20, y, z);
+        final Point3D p3 = new Point3D(x, y, z + 20);
+        final Point3D p4 = new Point3D(x + 20, y, z + 20);
+        final SolidPolygon polygon1 = new SolidPolygon(p1, p2, p3, SQUARE_PLATE_COLOR);
+        final SolidPolygon polygon2 = new SolidPolygon(p4, p2, p3, SQUARE_PLATE_COLOR);
+        shapeCollection.addShape(polygon1);
+        shapeCollection.addShape(polygon2);
+    }
+
+    /**
+     * 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) {
+
+                // use Pythagorean theorem to compute distance from the center
+                final double distanceFromCenter = Math.sqrt((x * x) + (z * z));
+
+                double plateElevation = Math.sin(distanceFromCenter / WAVE_FREQUENCY) * WAVE_AMPLITUDE;
+
+                makeSquarePlate(shapeCollection, plateElevation + surfaceElevation, x,
+                        z);
+            }
+    }
+
+    /**
+     * 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 graph demo.
+     * @param args command line arguments (ignored)
+     */
+    public static void main(final String[] args) {
+
+        final ViewFrame viewFrame = new ViewFrame();
+        final ShapeCollection geometryCollection = viewFrame.getViewPanel()
+                .getRootShapeCollection();
+
+        addMathFormulas(geometryCollection);
+        addSphere(geometryCollection);
+        addWobblySurface(geometryCollection, 200);
+        addWobblySurface(geometryCollection, -200);
+
+        setCameraLocation(viewFrame);
+    }
+
+    /**
+     * 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) {
+        // add sphere
+        geometryCollection.addShape(new WireframeSphere(new Point3D(0, 0, 0),
+                100,
+                new LineAppearance(
+                        4,
+                        new Color(255,0, 0, 30))
+        ));
+    }
+
+    /**
+     * 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 = new Point3D(-600, -300, z);
+        geometryCollection.addShape(getSineGraph(location));
+
+        location = new Point3D(600, -300, z);
+        geometryCollection.addShape(getFormula1Graph(location));
+
+        location = new Point3D(-600, 0, z);
+        geometryCollection.addShape(getCosineGraph(location));
+
+        location = new Point3D(600, 0, z);
+        geometryCollection.addShape(getFormula2Graph(location));
+
+        location = new Point3D(-600, 300, z);
+        geometryCollection.addShape(getTangentGraph(location));
+
+        location = new Point3D(600, 300, z);
+        geometryCollection.addShape(getFormula3Graph(location));
+    }
+
+    /**
+     * Sets the camera to an initial viewing position.
+     * @param viewFrame the view frame whose camera to configure
+     */
+    private static void setCameraLocation(ViewFrame viewFrame) {
+        viewFrame.getViewPanel().getCamera().getTransform().setTranslation(new Point3D(0, 0, -500));
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/OctreeDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/OctreeDemo.java
new file mode 100755 (executable)
index 0000000..23a5282
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * 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.RaytracingCamera;
+import eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.LightSource;
+import eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.RayTracer;
+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 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;
+
+/**
+ * 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, new Color(255,
+            0, 0, 60));
+    private final Vector<LightSource> lights = new Vector<>();
+    private OctreeVolume octreeVolume;
+    private ShapeCollection shapeCollection;
+    private ViewPanel viewPanel;
+
+    /**
+     * 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 visual scene and the raytracer.
+     * @param location position of the light
+     * @param color color of the light
+     * @param brightness intensity of the light
+     */
+    private void addLight(final Point3D location, final Color color,
+                          final float brightness) {
+        shapeCollection.addShape(new LightSourceMarker(new Point3D(location)
+                .scaleUp(magnification), color));
+
+        final LightSource lightSource = new LightSource(location, color,
+                brightness);
+
+        lights.add(lightSource);
+    }
+
+    /** 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, 100));
+
+        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();
+        viewPanel = viewFrame.getViewPanel();
+
+        viewPanel.getCamera().getTransform().setTranslation(new Point3D(0, -30, -300));
+
+        octreeVolume = new OctreeVolume();
+
+        shapeCollection = viewPanel.getRootShapeCollection();
+
+        shapeCollection.addShape(new Grid3D(
+                new Point3D(-10000, -10000, -10000), new Point3D(10000, 10000,
+                10000), 4000, gridAppearance));
+
+        // yellow light
+        addLight(new Point3D(20, -450, 240), new Color(255, 255, 255, 255), 100);
+
+        // red light
+        addLight(new Point3D(-150, -116, 141), new Color(255, 0, 0, 255), 10);
+
+        dotSpiral();
+
+        // arbitrary rectangles
+        putRect(new IntegerPoint(-10, -10, -10),
+                new IntegerPoint(10, 10, -20),
+                new Color(200, 255, 200, 100));
+
+        putRect(new IntegerPoint(-3, 0, -30),
+                new IntegerPoint( 12, 3, 300),
+                new Color(255, 200, 200, 100));
+
+        putRect(new IntegerPoint(-20, 20, -20),
+                new IntegerPoint(20, 80, 20),
+                new Color(255, 200, 255, 100));
+
+        tiledFloor();
+
+        fractal(-50, 20, 100, 32, 1);
+
+        final TextCanvas message = new TextCanvas(new Transform(new Point3D(
+                -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(new Point3D(x, y, z)
+                .scaleUp(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) {
+
+        shapeCollection
+                .addShape(new SolidPolygonRectangularBox(
+                        new Point3D(p1).scaleUp(magnification),
+                        new Point3D(p2).scaleUp(magnification), color));
+
+        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 = new Color(255, 255, 255, 100);
+        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);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/RainingNumbersDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/RainingNumbersDemo.java
new file mode 100644 (file)
index 0000000..7f97540
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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 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 {
+
+    /** 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();
+
+        final ShapeCollection geometryCollection = viewFrame.getViewPanel()
+                .getRootShapeCollection();
+
+        Random random = new Random();
+
+        for (int i = 0; i < NUMBERS_COUNT; i++) {
+            final Point3D location = new Point3D((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);
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/RandomPolygonsDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/RandomPolygonsDemo.java
new file mode 100755 (executable)
index 0000000..8754044
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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.Grid3D;
+
+/**
+ * Demo showing 1000 randomly positioned and colored triangles in 3D space.
+ * Demonstrates the engine's ability to render many semi-transparent polygons
+ * with proper depth sorting.
+ */
+public class RandomPolygonsDemo {
+
+    /** Average size of each random polygon. */
+    private static final double POLYGON_AVERAGE_SIZE = 130;
+    /** Number of polygons to generate. */
+    private static final int POLYGON_COUNT = 1000;
+
+    /**
+     * Adds a single random triangle to the scene.
+     * @param geometryCollection the collection to add the polygon to
+     */
+    private static void addRandomPolygon(final ShapeCollection geometryCollection) {
+        final Point3D polygonLocation = getRandomPoint(1000);
+
+        final Point3D point1 = new Point3D(polygonLocation);
+        point1.add(getRandomPoint(POLYGON_AVERAGE_SIZE));
+
+        final Point3D point2 = new Point3D(polygonLocation);
+        point2.add(getRandomPoint(POLYGON_AVERAGE_SIZE));
+
+        final Point3D point3 = new Point3D(polygonLocation);
+        point3.add(getRandomPoint(POLYGON_AVERAGE_SIZE));
+
+        final Color color = new Color(
+                getColorChannelBrightness(),
+                getColorChannelBrightness(),
+                getColorChannelBrightness(),
+                1);
+
+        final SolidPolygon polygon = new SolidPolygon(point1, point2, point3,
+                color);
+        geometryCollection.addShape(polygon);
+    }
+
+    /**
+     * Generates a random color channel brightness.
+     * Ensures minimum brightness of 0.3 to avoid very dark polygons.
+     * @return a brightness value between 0.3 and 1.0
+     */
+    private static double getColorChannelBrightness() {
+        return Math.random() * 0.7 + 0.3f;
+    }
+
+    /**
+     * Generates a random 3D point within a cube centered at the origin.
+     * @param amplitude the half-size of the cube
+     * @return a random point within [-amplitude, amplitude] on each axis
+     */
+    private static Point3D getRandomPoint(final double amplitude) {
+        return new Point3D((Math.random() * amplitude * 2d) - amplitude,
+                (Math.random() * amplitude * 2d) - amplitude, (Math.random()
+                * amplitude * 2d)
+                - amplitude);
+    }
+
+    /**
+     * Entry point for the random polygons demo.
+     * @param args command line arguments (ignored)
+     */
+    public static void main(final String[] args) {
+
+        final ViewFrame viewFrame = new ViewFrame();
+
+        final ShapeCollection shapeCollection = viewFrame.getViewPanel()
+                .getRootShapeCollection();
+
+        // add grid
+        final LineAppearance appearance = new LineAppearance(5, new Color(100,
+                100, 255, 60));
+
+        shapeCollection.addShape(new Grid3D(new Point3D(1000, -1000, -1000),
+                new Point3D(-1000, 1000, 1000), 300, appearance));
+
+        // add random polygons
+        for (int i = 0; i < POLYGON_COUNT; i++)
+            addRandomPolygon(shapeCollection);
+
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/ShadedShapesDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/ShadedShapesDemo.java
new file mode 100644 (file)
index 0000000..c0cbd50
--- /dev/null
@@ -0,0 +1,199 @@
+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.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 eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCylinder;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonPyramid;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonSphere;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Demo showing a shaded sphere, cube, pyramid, and cylinder with multiple colored light sources.
+ * Ten light sources orbit around the shapes on different paths to demonstrate dynamic lighting.
+ */
+public class ShadedShapesDemo {
+
+    /** Number of orbiting light sources in the scene. */
+    private static final int LIGHT_COUNT = 10;
+
+    /**
+     * Entry point for the shaded shapes demo.
+     * @param args command line arguments (ignored)
+     */
+    public static void main(String[] args) {
+        ViewFrame viewFrame = new ViewFrame();
+        ViewPanel viewPanel = viewFrame.getViewPanel();
+        ShapeCollection shapes = viewPanel.getRootShapeCollection();
+
+        LightingManager lightingManager = new LightingManager();
+        lightingManager.setAmbientLight(new Color(25, 25, 25));
+
+        Random random = new Random(42);
+        List<OrbitingLight> orbitingLights = new ArrayList<>();
+
+        for (int i = 0; i < LIGHT_COUNT; i++) {
+            Color color = new Color(
+                    random.nextInt(256),
+                    random.nextInt(256),
+                    random.nextInt(256)
+            );
+
+            double orbitRadius = 500 + random.nextInt(200);
+            double speed = 0.01 + random.nextDouble() * 0.03;
+            double angleOffset = random.nextDouble() * Math.PI * 2;
+            double intensity = 2.0 + random.nextDouble() * 3.0;
+
+            int axis = random.nextInt(3);
+            double ellipseFactor = 0.7 + random.nextDouble() * 0.3;
+
+            LightSource light = new LightSource(new Point3D(0, 0, 0), color, intensity);
+            LightSourceMarker marker = new LightSourceMarker(light.getPosition(), color);
+
+            lightingManager.addLight(light);
+            shapes.addShape(marker);
+
+            orbitingLights.add(new OrbitingLight(
+                    light, marker,
+                    orbitRadius, speed, angleOffset, axis, ellipseFactor
+            ));
+        }
+
+        // Sphere
+        SolidPolygonSphere sphere = new SolidPolygonSphere(
+                new Point3D(-400, 0, 0), 150, 28, new Color(200, 210, 255)
+        );
+        sphere.setLightingManager(lightingManager);
+        shapes.addShape(sphere);
+
+        // Pyramid
+        SolidPolygonPyramid pyramid = new SolidPolygonPyramid(
+                new Point3D(0, 130, 0), 150, 260, new Color(255, 200, 200)
+        );
+        pyramid.setLightingManager(lightingManager);
+        shapes.addShape(pyramid);
+
+        // Cube
+        SolidPolygonCube cube = new SolidPolygonCube(
+                new Point3D(400, 0, 0), 150, new Color(200, 255, 200)
+        );
+        cube.setLightingManager(lightingManager);
+        shapes.addShape(cube);
+
+        // Cylinder
+        SolidPolygonCylinder cylinder = new SolidPolygonCylinder(
+                new Point3D(800, 0, 0), 120, 260, 24, new Color(255, 220, 180)
+        );
+        cylinder.setLightingManager(lightingManager);
+        shapes.addShape(cylinder);
+
+        // Camera
+        viewPanel.getCamera().getTransform().setTranslation(new Point3D(200, -250, -900));
+
+        MultiLightAnimator animator = new MultiLightAnimator(orbitingLights);
+        viewPanel.addFrameListener(animator);
+
+        viewPanel.repaintDuringNextViewUpdate();
+    }
+
+    /**
+     * Represents a light source that orbits around the scene center.
+     * Each light follows an elliptical path on one of three possible axes.
+     */
+    private static class OrbitingLight {
+        final LightSource light;
+        final LightSourceMarker marker;
+        final double orbitRadius;
+        final double speed;
+        final int axis;
+        final double ellipseFactor;
+        double angle;
+
+        /**
+         * Creates an orbiting light with the specified parameters.
+         * @param light the light source to orbit
+         * @param marker the visual marker for the light position
+         * @param orbitRadius the base radius of the orbit
+         * @param speed the angular speed of orbit
+         * @param angleOffset the starting angle offset
+         * @param axis the axis of orbit (0, 1, or 2)
+         * @param ellipseFactor the ellipse distortion factor
+         */
+        OrbitingLight(LightSource light, LightSourceMarker marker,
+                      double orbitRadius, double speed, double angleOffset,
+                      int axis, 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 each frame.
+     * Updates light positions based on their individual orbital parameters.
+     */
+    private static class MultiLightAnimator implements eu.svjatoslav.sixth.e3d.gui.FrameListener {
+        private final List<OrbitingLight> lights;
+
+        /**
+         * Creates an animator for the specified lights.
+         * @param lights the list of lights to animate
+         */
+        MultiLightAnimator(List<OrbitingLight> lights) {
+            this.lights = lights;
+        }
+
+        /**
+         * Updates all light positions each frame.
+         * @param viewPanel the view panel
+         * @param millisecondsSinceLastFrame time elapsed since last frame
+         * @return true to continue animation
+         */
+        @Override
+        public boolean onFrame(ViewPanel viewPanel, int millisecondsSinceLastFrame) {
+            for (OrbitingLight orbitingLight : lights) {
+                orbitingLight.angle += orbitingLight.speed * millisecondsSinceLastFrame / 100;
+
+                double r = orbitingLight.orbitRadius;
+                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;
+                }
+
+                Point3D newPosition = new Point3D(x, y + 50, z);
+                orbitingLight.light.setPosition(newPosition);
+                orbitingLight.marker.setTransform(new eu.svjatoslav.sixth.e3d.math.Transform(newPosition, 0, 0));
+            }
+            return true;
+        }
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/TextEditorDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/TextEditorDemo.java
new file mode 100644 (file)
index 0000000..ec5c6b6
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Rectangle;
+import eu.svjatoslav.sixth.e3d.gui.Camera;
+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.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.Grid2D;
+
+/**
+ * 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 {
+
+    /**
+     * 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();
+        final ViewPanel viewPanel = viewFrame.getViewPanel();
+
+        final ShapeCollection shapeCollection = viewFrame.getViewPanel()
+                .getRootShapeCollection();
+
+        setCameraLocation(viewPanel);
+
+        addGrid(shapeCollection);
+
+        addTextEditors(viewPanel, shapeCollection);
+    }
+
+    /**
+     * 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 = new Transform(new Point3D(0, 100, 0), 0,
+                Math.PI / 2);
+
+        final Rectangle rectangle = new Rectangle(2000);
+        final LineAppearance appearance = new LineAppearance(10, new Color(
+                "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(new Point3D(x, 0, z)), viewPanel,
+                        new Point2D(200, 120), new LookAndFeel());
+
+                shapeCollection.addShape(textEditor);
+            }
+    }
+
+    /**
+     * Sets the camera to an initial viewing position.
+     * @param viewPanel the view panel whose camera to configure
+     */
+    private static void setCameraLocation(ViewPanel viewPanel) {
+        Camera camera = viewPanel.getCamera();
+        camera.getTransform().setTranslation(new Point3D(500, -300, -800));
+        camera.getTransform().setRotation(0.6, -0.5);
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/TextEditorDemo2.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/TextEditorDemo2.java
new file mode 100644 (file)
index 0000000..887fb30
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * 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.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Rectangle;
+import eu.svjatoslav.sixth.e3d.gui.Camera;
+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.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.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;
+
+/**
+ * "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 {
+
+    /**
+     * 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();
+        }
+    }
+
+    /**
+     * 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();
+        final ViewPanel viewPanel = viewFrame.getViewPanel();
+
+        final ShapeCollection shapeCollection = viewFrame.getViewPanel()
+                .getRootShapeCollection();
+
+        setCameraLocation(viewPanel);
+
+        addGrid(shapeCollection);
+
+        addCity(viewPanel, shapeCollection);
+    }
+
+    /**
+     * 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 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 = new Transform(new Point3D(0, 100, 0), 0,
+                Math.PI / 2);
+
+        final Rectangle rectangle = new Rectangle(10000);
+        final LineAppearance appearance = new LineAppearance(10, new Color(
+                "00b3ad"));
+
+        shapeCollection.addShape(new Grid2D(transform, rectangle, 50, 50, appearance));
+    }
+
+
+    /**
+     * 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 {
+        Transform transform = new Transform(new Point3D(x, -390, z-200));
+        addTextEditor(viewPanel, shapeCollection, transform);
+
+        transform = new Transform(new Point3D(x, -390, z+200),Math.PI, 0);
+        addTextEditor(viewPanel, shapeCollection, transform);
+
+        transform = new Transform(new Point3D(x-200, -390, z),Math.PI/2, 0);
+        addTextEditor(viewPanel, shapeCollection, transform);
+
+        transform = new Transform(new Point3D(x+200, -390, z),Math.PI/2*3f, 0);
+        addTextEditor(viewPanel, shapeCollection, transform);
+    }
+
+    /**
+     * 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 = new Color(20, 20, 50, 150);
+        lookAndFeel.tabStopBackground = lookAndFeel.background;
+        lookAndFeel.foreground = new Color(150, 150, 255,250);
+        return lookAndFeel;
+    }
+
+    /**
+     * 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()));
+            }
+        }
+    }
+
+    /**
+     * Sets the camera to an initial viewing position for the city scene.
+     * @param viewPanel the view panel whose camera to configure
+     */
+    private static void setCameraLocation(ViewPanel viewPanel) {
+        Camera camera = viewPanel.getCamera();
+        camera.getTransform().setTranslation(new Point3D(500, -300, -800));
+        camera.getTransform().setRotation(0.6, -0.5);
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/Galaxy.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/Galaxy.java
new file mode 100755 (executable)
index 0000000..a632268
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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));
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/PointCloudDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/PointCloudDemo.java
new file mode 100644 (file)
index 0000000..bc9f227
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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 {
+
+    /**
+     * 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();
+
+        final ShapeCollection geometryCollection = viewFrame.getViewPanel()
+                .getRootShapeCollection();
+
+        Transform transform = new Transform(new Point3D(0, -1000, 1000), 0, 0);
+
+        // add galaxy
+        geometryCollection.addShape(new Galaxy(500, 3, 10000, transform));
+
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/ApplicationListPanel.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/ApplicationListPanel.java
new file mode 100644 (file)
index 0000000..424406d
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * 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.*;
+import eu.svjatoslav.sixth.e3d.examples.galaxy_demo.PointCloudDemo;
+import eu.svjatoslav.sixth.e3d.examples.ShadedShapesDemo;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+
+/**
+ * Panel containing buttons to launch each demo application.
+ * Displays a vertical list of buttons organized in a sequential group layout.
+ */
+class ApplicationListPanel extends JPanel {
+    private static final long serialVersionUID = 2012721856427052560L;
+
+    /** Constructs the panel with all demo launcher buttons. */
+    ApplicationListPanel() {
+        final GroupLayout groupLayout = new GroupLayout(this);
+        GroupLayout.SequentialGroup sequentialGroup = groupLayout.createSequentialGroup();
+        sequentialGroup.addComponent(new JLabel("Choose an example to launch:"));
+        sequentialGroup.addComponent(new JButton(new ShowOctree()));
+        sequentialGroup.addComponent(new JButton(new ShowMathGraphs()));
+        sequentialGroup.addComponent(new JButton(new ShowPointCloud()));
+        sequentialGroup.addComponent(new JButton(new ShowRain()));
+        sequentialGroup.addComponent(new JButton(new ShowTextEditors()));
+        sequentialGroup.addComponent(new JButton(new ShowTextEditors2()));
+        sequentialGroup.addComponent(new JButton(new ShowGameOfLife()));
+        sequentialGroup.addComponent(new JButton(new ShowRandomPolygons()));
+        sequentialGroup.addComponent(new JButton(new ShowShadedShapes()));
+        sequentialGroup.addComponent(new JButton(new ShowFillRateTest()));
+    }
+
+    /** Action to launch the TextEditorDemo. */
+    private static class ShowTextEditors extends AbstractAction {
+        ShowTextEditors() {
+            putValue(NAME, "Text editors");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            TextEditorDemo.main(null);
+        }
+    }
+
+    /** Action to launch the TextEditorDemo2 (city view). */
+    private static class ShowTextEditors2 extends AbstractAction {
+        ShowTextEditors2() {
+            putValue(NAME, "Text editors city");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            TextEditorDemo2.main(null);
+        }
+    }
+
+
+    /** Action to launch the RainingNumbersDemo. */
+    private static class ShowRain extends AbstractAction {
+        ShowRain() {
+            putValue(NAME, "Raining numbers");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            RainingNumbersDemo.main(null);
+        }
+    }
+
+    /** Action to launch the PointCloudDemo (galaxy simulation). */
+    private static class ShowPointCloud extends AbstractAction {
+        ShowPointCloud() {
+            putValue(NAME, "Point cloud galaxy");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            PointCloudDemo.main(null);
+        }
+    }
+
+    /** Action to launch the GraphDemo (mathematical graphs). */
+    private static class ShowMathGraphs extends AbstractAction {
+        ShowMathGraphs() {
+            putValue(NAME, "Mathematical graphs");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            GraphDemo.main(null);
+        }
+    }
+
+    /** Action to launch the RandomPolygonsDemo. */
+    private static class ShowRandomPolygons extends AbstractAction {
+        ShowRandomPolygons() {
+            putValue(NAME, "Random polygons");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            RandomPolygonsDemo.main(null);
+        }
+    }
+
+    /** Action to launch the OctreeDemo (volumetric rendering). */
+    private static class ShowOctree extends AbstractAction {
+        ShowOctree() {
+            putValue(NAME, "Volumetric Octree");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            OctreeDemo.main(null);
+        }
+    }
+
+    /** Action to launch the Game of Life 3D demo. */
+    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);
+        }
+    }
+
+    /** Action to launch the ShadedShapesDemo (lighting demonstration). */
+    private static class ShowShadedShapes extends AbstractAction {
+        ShowShadedShapes() {
+            putValue(NAME, "Shaded Shapes with Lights");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            ShadedShapesDemo.main(null);
+        }
+    }
+
+    /** Action to launch the FillRateTest benchmark. */
+    private static class ShowFillRateTest extends AbstractAction {
+        ShowFillRateTest() {
+            putValue(NAME, "Fill-rate Test");
+        }
+
+        @Override
+        public void actionPerformed(final ActionEvent e) {
+            FillRateTest.main(null);
+        }
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/Main.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/Main.java
new file mode 100755 (executable)
index 0000000..9f87947
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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.setSize(400, 300);
+
+        frame.setLocationRelativeTo(null); // center frame on screen
+        frame.setVisible(true);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Cell.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Cell.java
new file mode 100755 (executable)
index 0000000..80622ff
--- /dev/null
@@ -0,0 +1,159 @@
+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 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());
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Main.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Main.java
new file mode 100644 (file)
index 0000000..86ac7f1
--- /dev/null
@@ -0,0 +1,127 @@
+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.Camera;
+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 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.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(
+            new Point3D() // position matrix in the center of the scene
+    );
+
+    /**
+     * 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();
+
+        final ShapeCollection shapeCollection = viewFrame.getViewPanel()
+                .getRootShapeCollection();
+
+        // add matrix
+        shapeCollection.addShape(MATRIX);
+
+        // add wire-frame grid (optional)
+        shapeCollection.addShape(createGrid());
+
+        final ViewPanel viewPanel = viewFrame.getViewPanel();
+
+        setCameraOrientation(viewPanel.getCamera());
+
+        // 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(
+                new Transform(
+                        new Point3D( // Grid positioning:
+                                0, // center
+                                100, // below the main scene
+                                0), // center
+
+                        // Grid orientation:
+                        0, // no rotation along XZ axis
+                        Math.PI / 2), // face down
+
+                new Rectangle(800), // large enough, square grid
+
+                5, 5, // grid will be divided to 5x5 segments
+
+                new LineAppearance(3, // line thickness
+                        new Color("FF000050") // red and quite transparent
+                )
+        );
+    }
+
+    /**
+     * Sets the camera to an initial viewing position for the matrix.
+     * @param camera the camera to configure
+     */
+    private void setCameraOrientation(final Camera camera) {
+        camera.getTransform().setTranslation(new Point3D(100, -50, -200));
+        camera.getTransform().setRotation(0.2f, -0.7f);
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Matrix.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Matrix.java
new file mode 100644 (file)
index 0000000..44fedcc
--- /dev/null
@@ -0,0 +1,181 @@
+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.
+     */
+    private void shiftStarsUp() {
+
+        for (final SubShape subShape : getGroup(GROUP_STARS))
+            ((Star) subShape.getShape()).getLocation().translateY(-10);
+    }
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Star.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Star.java
new file mode 100644 (file)
index 0000000..f3303db
--- /dev/null
@@ -0,0 +1,52 @@
+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
+        );
+    }
+
+}
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/package-info.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/package-info.java
new file mode 100755 (executable)
index 0000000..0c166bd
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * 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;
+
diff --git a/src/main/resources/demo.txt b/src/main/resources/demo.txt
new file mode 100644 (file)
index 0000000..fffb8fe
--- /dev/null
@@ -0,0 +1,505 @@
+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