From: Svjatoslav Agejenko Date: Sat, 4 Apr 2026 09:53:56 +0000 (+0300) Subject: Initial commit X-Git-Url: http://www2.svjatoslav.eu/gitweb/?a=commitdiff_plain;h=f1d76017c55316dd84de9fb4fdc9830a19e45da2;p=sixth-3d-demos.git Initial commit --- f1d76017c55316dd84de9fb4fdc9830a19e45da2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..31378ad --- /dev/null +++ b/.gitignore @@ -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 index 0000000..d5e1345 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,130 @@ +# Project Overview + +Sixth 3D Demos is a collection of demo applications showcasing the Sixth 3D rendering engine. It features interactive 3D +visualizations including Conway's Game of Life, galaxy simulations, and various geometric demos. + +## Build and Test Commands + +### Build the project + +```bash +mvn clean package +``` + +### Compile only (without packaging) + +```bash +mvn clean compile +``` + +### Run the application + +```bash +java -jar target/sixth-3d-demos.jar +``` + +### Generate API documentation + +```bash +mvn javadoc:javadoc +``` + +### Run a specific demo directly + +```bash +# Run the launcher +java -cp target/sixth-3d-demos.jar eu.svjatoslav.sixth.e3d.examples.launcher.Main +``` + +### Testing + +This project currently has no automated tests. Testing is done manually by running the demo applications. + +## Code Organization + +``` +src/main/java/eu/svjatoslav/sixth/e3d/examples/ +├── launcher/ - Application launcher and GUI +│ ├── Main.java - Main entry point +│ └── ApplicationListPanel.java +├── life_demo/ - Conway's Game of Life 3D demo +│ ├── Main.java +│ ├── Cell.java +│ ├── Matrix.java +│ └── Star.java +├── terrain_demo/ - Procedural terrain generation demo +│ ├── DiamondSquareTerrain.java +│ ├── FractalTree.java +│ └── TerrainDemo.java +├── galaxy_demo/ - Galaxy simulation demo +│ ├── Galaxy.java +│ └── PointCloudDemo.java +├── RandomPolygonsDemo.java +├── OctreeDemo.java +├── graph_demo/ - 3D graph visualization demos +│ ├── MathGraphsDemo.java +│ └── SurfaceGraph3D.java +├── TextEditorDemo.java +├── TextEditorDemo2.java +├── RainingNumbersDemo.java +├── WindingOrderDemo.java - Tests winding order & backface culling +└── package-info.java +``` + +## Code Style Guidelines + +### Java Version + +- Java 21 (source and target compatibility) + +### Indentation and Formatting + +- Use 4 spaces for indentation (no tabs) + +## External Dependencies + +| Dependency | Version | Description | +|-------------------|--------------|---------------------| +| sixth-3d | 1.3-SNAPSHOT | 3D rendering engine | +| svjatoslavcommons | 1.8 | Utility library | + +Both are available from the svjatoslav.eu Maven repository. + +## Common Patterns in This Codebase + +### Creating a Demo Application + +1. Create a class with a `main` method +2. Create a `ViewFrame` to display the 3D scene +3. Get the `ShapeCollection` from the view panel +4. Add shapes to the collection +5. Optionally set up user input handling + +### Extending AbstractCompositeShape + +Use this pattern for creating composite 3D objects: + +```java +public class MyShape extends AbstractCompositeShape { + public MyShape(Transform transform) { + super(transform); + // Add child shapes using addShape() + } +} +``` + +### Handling User Input + +Implement `MouseInteractionController` for mouse events, or extend input tracker classes for keyboard input. + +### Polygon Winding Order + +When creating triangles with backface culling enabled, use CCW winding in screen space: + +- Vertex order: top → lower-left → lower-right (as seen from camera) +- `signedArea < 0` = front-facing = visible +- See `WindingOrderDemo.java` for a minimal example + +## Documentation + +Always make sure that documentation in`doc/index.org` stays up to date. diff --git a/COPYING b/COPYING new file mode 100644 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 index 0000000..304bf94 --- /dev/null +++ b/Tools/Open with IntelliJ IDEA @@ -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 index 0000000..26537ed --- /dev/null +++ b/Tools/Update web site @@ -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 index 0000000..0cc8fd2 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,2 @@ +apidocs/ +sixth-3d-demos.jar diff --git a/doc/Screenshots/Benchmark.png b/doc/Screenshots/Benchmark.png new file mode 100644 index 0000000..5029be0 Binary files /dev/null and b/doc/Screenshots/Benchmark.png differ diff --git a/doc/Screenshots/Essentials/CSG demo.png b/doc/Screenshots/Essentials/CSG demo.png new file mode 100644 index 0000000..5602255 Binary files /dev/null and b/doc/Screenshots/Essentials/CSG demo.png differ diff --git a/doc/Screenshots/Essentials/Coordinate system.png b/doc/Screenshots/Essentials/Coordinate system.png new file mode 100644 index 0000000..7896688 Binary files /dev/null and b/doc/Screenshots/Essentials/Coordinate system.png differ diff --git a/doc/Screenshots/Essentials/Minimal example.png b/doc/Screenshots/Essentials/Minimal example.png new file mode 100644 index 0000000..6bee1cb Binary files /dev/null and b/doc/Screenshots/Essentials/Minimal example.png differ diff --git a/doc/Screenshots/Essentials/Shape gallery.png b/doc/Screenshots/Essentials/Shape gallery.png new file mode 100644 index 0000000..76a4fe5 Binary files /dev/null and b/doc/Screenshots/Essentials/Shape gallery.png differ diff --git a/doc/Screenshots/Essentials/Winding order.png b/doc/Screenshots/Essentials/Winding order.png new file mode 100644 index 0000000..f939231 Binary files /dev/null and b/doc/Screenshots/Essentials/Winding order.png differ diff --git a/doc/Screenshots/Life.png b/doc/Screenshots/Life.png new file mode 100644 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 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 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/Sine heightmap and sphere.png b/doc/Screenshots/Sine heightmap and sphere.png new file mode 100644 index 0000000..0d3e92b Binary files /dev/null and b/doc/Screenshots/Sine heightmap and sphere.png differ diff --git a/doc/Screenshots/Text editors 2.png b/doc/Screenshots/Text editors 2.png new file mode 100644 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 index 0000000..8567b8b Binary files /dev/null and b/doc/Screenshots/Text editors.png differ diff --git a/doc/index.org b/doc/index.org new file mode 100644 index 0000000..734d9cb --- /dev/null +++ b/doc/index.org @@ -0,0 +1,539 @@ +#+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 + +#+end_export + + +* Overview +:PROPERTIES: +:CUSTOM_ID: overview +:ID: 4430626f-21f4-42d7-89b1-7097a0b99916 +:END: + + +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px +[[file:overview.png]] + +The goal of this project is to show off capabilities and API usage of +[[https://www3.svjatoslav.eu/projects/sixth-3d/][Sixth 3D]] engine. + +All [[id:5f88b493-6ab3-4659-8280-803f75dbd5e0][example scenes in this repository]] render at interactive +framerates. + +You can download pre-compiled runnable JAR file with embedded demos +from here: [[file:sixth-3d-demos.jar]] + +It requires Java 21 or newer to run. + +To start the demo application, use command: +: java -jar sixth-3d-demos.jar + +* Essentials + +*Resources to help you understand the Sixth 3D library:* +- Read online [[https://www3.svjatoslav.eu/projects/sixth-3d/apidocs/][JavaDoc]]. +- See [[https://www3.svjatoslav.eu/projects/sixth-3d/graphs/][Sixth 3D class diagrams]]. (Diagrams were generated by using + [[https://www3.svjatoslav.eu/projects/javainspect/][JavaInspect]] utility) + +** Minimal example +:PROPERTIES: +:CUSTOM_ID: minimal-example +:ID: 1a2b3c4d-5e6f-7890-abcd-ef1234567890 +:END: + +This is the "Hello World" of *Sixth 3D* - the minimal boilerplate +needed to render any 3D geometry. + +: eu.svjatoslav.sixth.e3d.examples.essentials.MinimalExample + +*Brief tutorial:* + +Here we guide you through creating your first 3D scene with Sixth 3D +engine. + +Prerequisites: +- Java 21 or later installed +- Maven 3.x +- Basic Java knowledge + +*** Add Dependency to Your Project +:PROPERTIES: +:CUSTOM_ID: add-dependency-to-your-project +:ID: 3fffc32e-ae66-40b7-ad7d-fab6093c778b +:END: + +Add *Sixth 3D* to your pom.xml: + +#+BEGIN_SRC xml + + + eu.svjatoslav + sixth-3d + 1.4-SNAPSHOT + + + + + + svjatoslav.eu + Svjatoslav repository + https://www3.svjatoslav.eu/maven/ + + +#+END_SRC + + +*** Create Your First 3D Scene +:PROPERTIES: +:CUSTOM_ID: create-your-first-3d-scene +:ID: 564fa596-9b2b-418a-9df9-baa46f0d0a66 +:END: + + +#+BEGIN_SRC java + import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; + import eu.svjatoslav.sixth.e3d.gui.ViewFrame; + import eu.svjatoslav.sixth.e3d.math.Transform; + import eu.svjatoslav.sixth.e3d.renderer.raster.Color; + import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; + import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonRectangularBox; + + public class MyFirstScene { + public static void main(String[] args) { + // Create the application window + ViewFrame viewFrame = new ViewFrame("My First Scene"); + + // Get the collection where you add 3D shapes + ShapeCollection shapes = viewFrame.getViewPanel().getRootShapeCollection(); + + // Create a red box centered at origin + final Transform boxTransform = new Transform(); + SolidPolygonRectangularBox box = new SolidPolygonRectangularBox( + point(-50, -50, -50), + point(50, 50, 50), + Color.RED + ); + box.setTransform(boxTransform); + shapes.addShape(box); + + // Position your camera + viewFrame.getViewPanel().getCamera().getTransform() + .setTranslation(point(0, -100, -300)); + + // Trigger initial render + viewFrame.getViewPanel().repaintDuringNextViewUpdate(); + } + } +#+END_SRC + +Compile and run *MyFirstScene* class. A new window should open that will +display 3D scene with red box. + + +You should see this: +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px +[[file:Screenshots/Essentials/Minimal example.png]] + + +*Navigating the scene:* + +| Input | Action | +|--------------------+-----------------------------| +| Arrow Up | Move forward | +| Arrow Down | Move backward | +| Arrow Left | Move left (strafe) | +| Arrow Right | Move right (strafe) | +| Mouse drag | Look around (rotate camera) | +| Mouse scroll wheel | Move up / down | + +Movement uses physics-based acceleration for smooth, natural +motion. The faster you're moving, the more acceleration builds up, +creating an intuitive flying experience. + +Press *F12* in any demo to open the [[https://www3.svjatoslav.eu/projects/sixth-3d/#outline-container-developer-tools][Developer Tools panel]]. This +debugging interface provides real-time insight into the rendering +pipeline with diagnostic toggles. + +** Coordinate system +:PROPERTIES: +:CUSTOM_ID: coordinate-system +:ID: 2b3c4d5e-6f7a-8901-bcde-f12345678901 +:END: + +: eu.svjatoslav.sixth.e3d.examples.essentials.CoordinateSystemDemo + +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px +[[file:Screenshots/Essentials/Coordinate system.png]] + +Visual reference for the Sixth 3D coordinate system using three colored +arrows originating from (0,0,0): + +| Axis | Color | Direction | +|------+-------+------------------------------------| +| X | Red | Points RIGHT (positive X) | +| Y | Green | Points DOWN (positive Y) | +| Z | Blue | Points AWAY from viewer (positive Z) | + +A wireframe grid on the Y=0 plane provides spatial context and scale. Text +labels (X, Y, Z) appear at each arrow tip. + +This demo is essential for understanding Sixth 3D's coordinate system where: +- Positive Y goes down (screen coordinates convention) +- Positive Z goes into the screen (away from camera) +- Positive X goes to the right + +See the [[https://www3.svjatoslav.eu/projects/sixth-3d/#coordinate-system][Coordinate System]] section in the Sixth 3D documentation +for a detailed explanation of the axis conventions and coordinate math. + +** Winding order +:PROPERTIES: +:CUSTOM_ID: winding-order +:ID: 3c4d5e6f-7a8b-9012-cdef-123456789012 +:END: + +: eu.svjatoslav.sixth.e3d.examples.essentials.WindingOrderDemo + +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px +[[file:Screenshots/Essentials/Winding order.png]] + +This demo demonstrates polygon winding order and backface culling - fundamental +concepts for efficient 3D rendering. + +The green triangle uses counter-clockwise (CCW) vertex ordering: +1. Upper-center vertex +2. Lower-left vertex +3. Lower-right vertex + +With backface culling enabled: +- *CCW winding* (negative signed area) = front-facing = visible +- *CW winding* (positive signed area) = back-facing = culled (not rendered) + +This optimization prevents rendering polygons facing away from the camera, +improving performance by roughly 50% for closed meshes. Navigate around the +scene and observe that the triangle becomes invisible when viewed from behind, +demonstrating the culling effect. + +See the [[https://www3.svjatoslav.eu/projects/sixth-3d/#winding-order-backface-culling][Winding Order & Backface Culling]] section +in the Sixth 3D documentation for a detailed explanation with diagrams. + +** Shape gallery +:PROPERTIES: +:CUSTOM_ID: shape-gallery +:ID: 4d5e6f7a-8b9c-0123-def0-234567890123 +:END: + +: eu.svjatoslav.sixth.e3d.examples.essentials.ShapeGalleryDemo + +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px +[[file:Screenshots/Essentials/Shape gallery.png]] + +Comprehensive showcase of all primitive 3D shapes available in Sixth 3D, +organized in a 7×2 grid: + +| Column | Shape Type | Description | +|--------+------------+--------------------------------| +| 1 | Arrow | 3D arrow with conical tip | +| 2 | Cone | Circular base cone | +| 3 | Cube | Regular hexahedron | +| 4 | Cylinder | Tube with circular caps | +| 5 | Pyramid | Square base pyramid | +| 6 | Box | Rectangular parallelepiped | +| 7 | Sphere | Geodesic sphere approximation | + +*Top row:* Wireframe versions - edges only, no surfaces +*Bottom row:* Solid polygon versions - filled surfaces with lighting + +Additional features demonstrated: +- Dynamic lighting from 10 orbiting colored light sources (visible as small + glowing dots) +- Per-shape color assignment for visual distinction +- Grid floor showing spatial layout and alignment +- Text labels identifying each shape type +- Shading enabled on solid shapes to show lighting effects + +See the [[https://www3.svjatoslav.eu/projects/sixth-3d/#mesh][Mesh]] section in the Sixth 3D documentation +for an explanation of 3D mesh fundamentals and how shapes are constructed. + +** CSG demo +:PROPERTIES: +:CUSTOM_ID: csg-demo +:ID: 5e6f7a8b-9c0d-1234-ef01-345678901234 +:END: + +: eu.svjatoslav.sixth.e3d.examples.essentials.CSGDemo + +#+attr_html: :class responsive-img +#+attr_latex: :width 1000px +[[file:Screenshots/Essentials/CSG demo.png]] + +Constructive Solid Geometry (CSG) boolean operations on 3D meshes. Three +operations displayed left to right: + +1. *Subtract (Cube - Sphere)* - A red cube with a spherical cavity carved out + where a blue sphere intersects it. Shows the cube's interior surfaces. + +2. *Union (Cube + Sphere)* - A merged shape combining both volumes. Red + surfaces from the cube, blue surfaces from the sphere, seamlessly joined. + +3. *Intersect (Cube ∩ Sphere)* - Only the volume shared by both shapes + remains. Shows which parts of space were occupied by both the cube and + sphere simultaneously. + +Color scheme: +- *Red* = geometry from the first operand (cube) +- *Blue* = geometry from the second operand (sphere) + +Each operation includes a text description panel below the shape. The demo +uses: +- SolidPolygonCube (red) +- SolidPolygonSphere (blue) +- CSGSolid.subtract(), union(), intersect() methods +- toMesh() conversion for rendering +- Backface culling and shading enabled + +CSG is powerful for procedural modeling, allowing complex shapes to be built +from simple primitives through boolean combinations. + +* Advanced examples +:PROPERTIES: +:CUSTOM_ID: example-scenes +:ID: 5f88b493-6ab3-4659-8280-803f75dbd5e0 +:END: + + +** Conway's Game of Life +:PROPERTIES: +:CUSTOM_ID: conways-game-of-life +:ID: 08914390-742b-4c78-88bf-602ab9640082 +:END: + +: eu.svjatoslav.sixth.e3d.examples.life_demo.Main + +The Game of Life, also known simply as Life, is a cellular automaton +devised by the British mathematician John Horton Conway in 1970. + ++ https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life + + Game rules: + + 2 cell states: alive / dead + + Each cell sees 8 neighboring cells. + + If alive cell neighbors count is 2 or 3, then cell survives, + otherwise it dies. + + Dead cell becomes alive if neighbors count is exactly 3. + +[[file:Screenshots/Life.png]] + +This demo projects the 2D game grid onto three-dimensional space. The extra +dimension (height) visualizes history (previous iterations) using glowing dots +suspended in space. + +Usage: +| key | result | +|--------------------------------+--------------------------------------| +| mouse click on the cell (cell) | toggles cell state | +| | next iteration | +| ENTER | next iteration with the history | +| "c" | clear the matrix | + +** Text editors +:PROPERTIES: +:CUSTOM_ID: text-editors +:ID: 39250157-db8e-4861-a21b-8568912bd160 +:END: + +: eu.svjatoslav.sixth.e3d.examples.TextEditorDemo + +[[file:Screenshots/Text editors.png]] + +Initial test for creating user interfaces in 3D, demonstrating: ++ window focus handling ++ picking objects using the mouse ++ redirecting keyboard input to the focused window + +Window focus acts like a stack. + +When a window is clicked with the mouse, the previously focused window (if +any) is pushed to the focus stack and the new window receives focus. A red +frame appears around the window to indicate this. + +When the ESC key is pressed, focus is returned to the previous window (if any). + +When any window is focused, all keyboard input is redirected to that window, +including cursor keys. To navigate around the world again, the window must be +unfocused first by pressing the ESC key. + + ++ TODO: + + Improve focus handling: + + Perhaps add shortcut to navigate world without exiting entire + stack of focus. + + Possibility to retain and reuse recently focused elements. + + Store user location in the world and view direction with the + focused window. So that when returning focus to far away object, + user is redirected also to proper location in the world. + + Possibility to store recently visited locations in the world and + return to them. + +** Text editors demo gallery +:PROPERTIES: +:CUSTOM_ID: text-editors-demo-gallery +:ID: a7b8c9d0-e1f2-3456-0123-567890123456 +:END: + +: eu.svjatoslav.sixth.e3d.examples.TextEditorDemo2 + +*Quite a lot of text editors can be rendered:* + +[[file:Screenshots/Text editors 2.png]] + +See also this [[https://hackers-1995.vercel.app/][similar-looking web-based demo]]! + +** Math graphs demo +:PROPERTIES: +:CUSTOM_ID: mathematical-formulas +:ID: b1c2d3e4-f5a6-7890-bcde-f12345678901 +:END: + +: eu.svjatoslav.sixth.e3d.examples.graph_demo.MathGraphsDemo + +[[file:Screenshots/Mathematical formulas.png]] + ++ TODO: instead of projecting 2D visualizations onto 3D space, + visualize some formula using all 3 dimensions available. + +** Sine heightmap and sphere +:PROPERTIES: +:CUSTOM_ID: sine-heightmaps-and-sphere +:ID: b2c3d4e5-f6a7-8901-bcde-f12345678901 +:END: + +: eu.svjatoslav.sixth.e3d.examples.SineHeightmap + +[[file:Screenshots/Sine heightmap and sphere.png]] + +Simple test scene. Easy to implement and looks nice. + +** Raytracing through voxels +:PROPERTIES: +:CUSTOM_ID: raytracing-through-voxels +:ID: c3d4e5f6-a7b8-9012-cdef-123456789012 +:END: + +: eu.svjatoslav.sixth.e3d.examples.OctreeDemo + +[[file:Screenshots/Raytracing fractal in voxel polygon hybrid scene.png]] + +Test scene that is generated simultaneously using: ++ conventional polygons + + for realtime navigation, and ++ voxels + + for on-demand raytracing + +Instead of storing voxels in a naive [X × Y × Z] array, a dynamically +partitioned [[https://en.wikipedia.org/wiki/Octree][octree]] compresses the data. Press the "r" key anywhere in +the scene to raytrace the current view through the compressed voxel +data structure. + +* Graphics Benchmark +:PROPERTIES: +:CUSTOM_ID: graphics-benchmark +:ID: e5f6a7b8-c9d0-1234-ef01-345678901234 +:END: + +: eu.svjatoslav.sixth.e3d.examples.benchmark.GraphicsBenchmark + +An automated graphics benchmark that measures the engine's rendering +performance across different rendering modes. + +[[file:Screenshots/Benchmark.png]] + +The benchmark cycles through scenes that utilize different rendering +primitives (textured polygons, billboards, solid polygons, etc.) to measure +their relative performance. + +The camera follows a deterministic orbital path around the scene, +ensuring reproducible results across runs. + +Upon completion, the benchmark outputs a report suitable for preservation +and later comparisons. + +Example benchmark report: +#+begin_example +================================================================================ + GRAPHICS BENCHMARK RESULTS +================================================================================ +Date: 2026-03-16 19:17:51 +Resolution: 1920x1080 +Cubes: 4096 (16x16x16 grid) +Duration: 30 seconds per test + +-------------------------------------------------------------------------------- +SYSTEM INFORMATION +-------------------------------------------------------------------------------- +CPU Name: AMD Ryzen AI 9 HX 370 w/ Radeon 890M +Arch: amd64 +CPU cores: 24 + +-------------------------------------------------------------------------------- +Test Avg FPS +-------------------------------------------------------------------------------- +Solid Cubes 49.65 +Lit Solid Cubes 41.40 +Textured Cubes 32.80 +Wireframe Cubes 42.84 +Star Grid 318.97 +================================================================================ +#+end_example + +* Source code +:PROPERTIES: +:CUSTOM_ID: source-code +:ID: d4e5f6a7-b8c9-0123-def0-234567890123 +:END: + +*This program is free software, released under the Creative Commons Zero +(CC0) license.* + +*Program author:* +- Svjatoslav Agejenko +- Homepage: https://svjatoslav.eu +- Email: mailto://svjatoslav@svjatoslav.eu +- See also: [[https://www.svjatoslav.eu/projects/][Other software projects hosted at svjatoslav.eu]] + +*Getting the source code:* +- [[https://www2.svjatoslav.eu/gitweb/?p=sixth-3d-demos.git;a=snapshot;h=HEAD;sf=tgz][Download latest snapshot in TAR GZ format]] +- [[https://www2.svjatoslav.eu/gitweb/?p=sixth-3d-demos.git;a=summary][Browse Git repository online]] +- Clone Git repository using command: + : git clone https://www2.svjatoslav.eu/git/sixth-3d-demos.git + +*Understanding the Sixth 3D demos source code:* +- Read the online [[https://www3.svjatoslav.eu/projects/sixth-3d-demos/apidocs/][JavaDoc]]. +- Study the underlying [[https://www3.svjatoslav.eu/projects/sixth-3d/][Sixth 3D]] engine. diff --git a/doc/overview.png b/doc/overview.png new file mode 100644 index 0000000..ba977ab Binary files /dev/null and b/doc/overview.png differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..4baf761 --- /dev/null +++ b/pom.xml @@ -0,0 +1,173 @@ + + 4.0.0 + eu.svjatoslav + sixth-3d-demos + 1.0-SNAPSHOT + Sixth 3D demos + 3D engine demos + + + UTF-8 + UTF-8 + 21 + 21 + + + + svjatoslav.eu + https://svjatoslav.eu + + + + + eu.svjatoslav + sixth-3d + 1.4-SNAPSHOT + + + eu.svjatoslav + svjatoslavcommons + 1.8 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 21 + 21 + true + UTF-8 + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.0 + + + attach-javadocs + + jar + + + + + + + + foo + bar + + + + ${java.home}/bin/javadoc + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.4.3 + + UTF-8 + + + + + maven-assembly-plugin + + + + + eu.svjatoslav.sixth.e3d.examples.launcher.Main + + + + jar-with-dependencies + + sixth-3d-demos + false + + + + + package-jar-with-dependencies + package + + single + + + + jar-with-dependencies + + + + eu.svjatoslav.sixth.e3d.examples.launcher.Main + + + + + + + + + + + org.apache.maven.wagon + wagon-ssh-external + 2.6 + + + + + + + + svjatoslav.eu + svjatoslav.eu + scpexe://svjatoslav.eu:10006/srv/maven + + + svjatoslav.eu + svjatoslav.eu + scpexe://svjatoslav.eu:10006/srv/maven + + + + + + svjatoslav.eu + Svjatoslav repository + https://www3.svjatoslav.eu/maven/ + + + + + scm:git:ssh://n0@svjatoslav.eu/home/git/repositories/sixth-3d-demos.git + scm:git:ssh://n0@svjatoslav.eu/home/git/repositories/sixth-3d-demos.git + + + + + diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/ArrowDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/ArrowDemo.java new file mode 100644 index 0000000..3b45854 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/ArrowDemo.java @@ -0,0 +1,131 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ +package eu.svjatoslav.sixth.e3d.examples; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonArrow; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCone; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid3D; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; +import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex; + +/** + * Demo showcasing the SolidPolygonArrow shape with various colors, sizes, + * orientations, and transparency levels. + * + *

This demo displays arrows pointing in different directions to demonstrate + * the flexibility of the arrow shape. A 3D grid provides spatial reference.

+ */ +public class ArrowDemo { + + /** + * Entry point for the arrow demo. + * + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + final ViewFrame viewFrame = new ViewFrame("Arrow Demo"); + final ViewPanel viewPanel = viewFrame.getViewPanel(); + final ShapeCollection shapes = viewPanel.getRootShapeCollection(); + + // Position camera to view the scene + viewPanel.getCamera().getTransform().setTranslation(point(0, -200, -600)); + + // Add a 3D grid for spatial reference + final LineAppearance gridAppearance = new LineAppearance(1, hex("64646450")); + final Grid3D grid = new Grid3D( + point(-300, -200, -300), + point(300, 200, 300), + 100, + gridAppearance + ); + shapes.addShape(grid); + + // Create arrows pointing in different directions with different properties + + // Red arrow pointing up (negative Y direction) + final SolidPolygonArrow redArrow = new SolidPolygonArrow( + point(0, 150, 0), + point(0, -150, 0), + 8, Color.RED + ); + shapes.addShape(redArrow); + + // Green arrow pointing along positive X + final SolidPolygonArrow greenArrow = new SolidPolygonArrow( + point(-200, 0, 0), + point(0, 0, 0), + 6, Color.GREEN + ); + shapes.addShape(greenArrow); + + // Blue arrow pointing along positive Z + final SolidPolygonArrow blueArrow = new SolidPolygonArrow( + point(0, 0, -200), + point(0, 0, 0), + 6, Color.BLUE + ); + shapes.addShape(blueArrow); + + // Yellow arrow pointing diagonally + final SolidPolygonArrow yellowArrow = new SolidPolygonArrow( + point(100, 100, 100), + point(300, -100, 300), + 10, Color.YELLOW + ); + shapes.addShape(yellowArrow); + + // Semi-transparent cyan arrow (50% opacity) + final SolidPolygonArrow transparentCyanArrow = new SolidPolygonArrow( + point(-150, 50, -100), + point(-50, -100, 100), + 8, hex("00FFFF80") + ); + shapes.addShape(transparentCyanArrow); + + // Semi-transparent magenta arrow (25% opacity) + final SolidPolygonArrow transparentMagentaArrow = new SolidPolygonArrow( + point(50, 200, 50), + point(50, 0, 50), + 12, hex("FF00FF40") + ); + shapes.addShape(transparentMagentaArrow); + + // Small white arrows forming a circle pattern + for (int i = 0; i < 8; i++) { + final double angle = i * Math.PI / 4; + final double radius = 250; + final double startX = radius * Math.cos(angle); + final double startZ = radius * Math.sin(angle); + final double endX = (radius + 80) * Math.cos(angle); + final double endZ = (radius + 80) * Math.sin(angle); + + final SolidPolygonArrow circleArrow = new SolidPolygonArrow( + point(startX, -80, startZ), + point(endX, -80, endZ), + 4, Color.WHITE + ); + shapes.addShape(circleArrow); + } + + // A standalone cone to demonstrate SolidPolygonCone + final SolidPolygonCone standaloneCone = new SolidPolygonCone( + point(-300, 0, 0), + 40, + 80, + 16, + hex("FF8000FF") + ); + shapes.addShape(standaloneCone); + + viewPanel.repaintDuringNextViewUpdate(); + } +} \ No newline at end of file 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 index 0000000..d4e1c9c --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/OctreeDemo.java @@ -0,0 +1,262 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + * + */ + +package eu.svjatoslav.sixth.e3d.examples; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.gui.humaninput.WorldNavigationUserInputTracker; +import eu.svjatoslav.sixth.e3d.math.Transform; +import eu.svjatoslav.sixth.e3d.renderer.octree.IntegerPoint; +import eu.svjatoslav.sixth.e3d.renderer.octree.OctreeVolume; +import eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.RayTracer; +import eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.RaytracingCamera; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.GlowingPoint; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.LightSourceMarker; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonRectangularBox; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid3D; + +import java.awt.event.KeyEvent; +import java.util.Vector; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; +import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex; + +/** + * Demo showing volumetric octree rendering with raytracing capability. + * Creates a 3D scene with various geometric shapes stored in an octree data structure. + * Press 'r' to render the current view using software raytracing. + */ +public class OctreeDemo extends WorldNavigationUserInputTracker { + + /** + * Scale factor for rendering octree voxels in the scene. + */ + private static final double magnification = 5; + private final LineAppearance gridAppearance = new LineAppearance(40, hex("FF00003C")); + private final Vector lights = new Vector<>(); + private OctreeVolume octreeVolume; + private ShapeCollection shapeCollection; + private ViewPanel viewPanel; + + /** + * Creates a new OctreeDemo instance. + */ + public OctreeDemo() { + } + + /** + * Entry point for the octree demo. + * + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + new OctreeDemo().init(); + } + + /** + * Adds a light source to both the raytracer and rasterizer lighting systems. + * + * @param location position in octree space (will be scaled by magnification for display) + * @param color color of the light + * @param brightness intensity of the light + */ + private void addLightToBothSystems(final Point3D location, final Color color, + final float brightness) { + shapeCollection.addShape(new LightSourceMarker(new Point3D(location) + .multiply(magnification), color)); + + lights.add(new eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.LightSource( + location, color, brightness)); + + viewPanel.getLightingManager().addLight(new LightSource( + new Point3D(location).multiply(magnification), color, brightness * 50)); + } + + /** + * Creates a colorful spiral pattern of voxels in the octree. + */ + private void dotSpiral() { + for (double i = 0; i < 20; i = i + .1) { + + double w = 1; + double h = 1; + + final double x = Math.sin(i) * 20f * h; + final double y = Math.cos(i) * 20f * w; + final double c1 = (Math.cos(i * 3f) * 100) + 127; + final double c2 = (Math.cos(i * 5.3332f) * 100f) + 127; + final double c3 = (Math.cos(i * 1.342f) * 100f) + 127; + + putPixel((int) x, (int) y, (int) (i * 4f), new Color((int) c1, + (int) c2, (int) c3, 255)); + } + } + + /** + * Recursively creates a fractal pattern of rectangles. + * + * @param x center X coordinate + * @param y center Y coordinate + * @param z center Z coordinate + * @param size size of the current rectangle + * @param step recursion depth counter + */ + private void fractal(final int x, final int y, final int z, final int size, + final int step) { + final double c1 = (Math.cos(y / 7f) * 100f) + 127; + final double c2 = (Math.cos(x / 10f) * 100f) + 127; + final double c3 = (Math.cos(z / 12f) * 100f) + 127; + + putRect( + new IntegerPoint(x - size, y - size, z - size), + new IntegerPoint(x + size, y + size, z + size), + new Color((int) c1, (int) c2, (int) c3, 200)); + + if (size > 1) { + fractal(x, y - (size * 3), z, size / 2, step + 1); + fractal(x + (size * 3), y, z, size / 2, step + 1); + fractal(x, y, z + (size * 3), size / 2, step + 1); + } + } + + /** + * Initializes the demo scene with grid, lights, shapes, and fractal pattern. + */ + private void init() { + + final ViewFrame viewFrame = new ViewFrame("Volumetric octree"); + viewPanel = viewFrame.getViewPanel(); + + viewPanel.getCamera().getTransform().set(104.13, -65.04, -370.53, 0.12, 0.14, 0); + + viewPanel.getLightingManager().setAmbientLight(new Color(50, 50, 50)); + + octreeVolume = new OctreeVolume(); + + shapeCollection = viewPanel.getRootShapeCollection(); + + shapeCollection.addShape(new Grid3D( + point(-10000, -10000, -10000), point(10000, 10000, + 10000), 4000, gridAppearance)); + + addLightToBothSystems(point(20, -450, 240), new Color(255, 255, 255), 100); + addLightToBothSystems(point(-150, -116, 141), new Color(255, 0, 0), 10); + + dotSpiral(); + + // arbitrary rectangles + putRect(new IntegerPoint(-10, -10, -10), + new IntegerPoint(10, 10, -20), + hex("C8FFC864")); + + putRect(new IntegerPoint(-3, 0, -30), + new IntegerPoint(12, 3, 300), + hex("FFC8C864")); + + putRect(new IntegerPoint(-20, 20, -20), + new IntegerPoint(20, 80, 20), + hex("FFC8FF64")); + + tiledFloor(); + + fractal(-50, 20, 100, 32, 1); + + final TextCanvas message = new TextCanvas(new Transform(point( + -10, 20, -180)), "Press \"r\" to raytrace current view", + Color.WHITE, Color.PURPLE); + shapeCollection.addShape(message); + + viewPanel.getKeyboardFocusStack().pushFocusOwner(this); + viewPanel.repaintDuringNextViewUpdate(); + } + + /** + * Handles keyboard input for triggering raytracing. + * + * @param event the key event + * @param viewPanel the view panel + * @return true if the event was consumed + */ + @Override + public boolean keyPressed(final KeyEvent event, final ViewPanel viewPanel) { + + if ('r' == event.getKeyChar()) { + raytrace(); + return true; + } + return super.keyPressed(event, viewPanel); + } + + /** + * Places a single voxel at the specified position with the given color. + * + * @param x X coordinate in octree space + * @param y Y coordinate in octree space + * @param z Z coordinate in octree space + * @param color the color of the voxel + */ + private void putPixel(final int x, final int y, final int z, + final Color color) { + shapeCollection.addShape(new GlowingPoint(point(x, y, z) + .multiply(magnification), 3 * magnification, color)); + octreeVolume.putCell(x, y, z, color); + + } + + /** + * Fills a rectangular region in the octree with the specified color. + * + * @param p1 first corner of the rectangle + * @param p2 opposite corner of the rectangle + * @param color the color to fill + */ + private void putRect(IntegerPoint p1, IntegerPoint p2, final Color color) { + final SolidPolygonRectangularBox box = new SolidPolygonRectangularBox( + new Point3D(p1).multiply(magnification), + new Point3D(p2).multiply(magnification), color); + box.setShadingEnabled(true); + shapeCollection.addShape(box); + octreeVolume.fillRectangle(p1, p2, color); + } + + /** + * Starts a raytracing render of the current view in a background thread. + */ + private void raytrace() { + // create and add camera object to scene + final RaytracingCamera raytracingCamera = new RaytracingCamera(viewPanel.getCamera(), magnification); + shapeCollection.addShape(raytracingCamera); + + // initialize and start Raytracer in a separate thread + final RayTracer rayTracer = new RayTracer(raytracingCamera.getTexture(), + octreeVolume, lights, raytracingCamera, viewPanel); + final Thread thread = new Thread(rayTracer); + thread.start(); + } + + /** + * Creates a tiled floor pattern using small rectangular tiles. + */ + private void tiledFloor() { + final int step = 40; + final int size = step - 15; + Color color = hex("FFFFFF64"); + for (int x = -200; x < 200; x += step) + for (int z = -200; z < 200; z += step) + putRect( + new IntegerPoint(x, 100, z), + new IntegerPoint(x + size, 110, z + size), + color); + } + +} 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 index 0000000..bc6f6b8 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/RainingNumbersDemo.java @@ -0,0 +1,110 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + * +*/ + +package eu.svjatoslav.sixth.e3d.examples; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.gui.FrameListener; +import eu.svjatoslav.sixth.e3d.math.Transform; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas; + +import java.util.Collection; +import java.util.Random; + +/** + * Demo showing numbers falling through 3D space like rain. + * Creates 1000 randomly positioned text canvases displaying digits 0-9, + * which continuously fall downward and wrap around to create a "Matrix" style effect. + */ +public class RainingNumbersDemo implements FrameListener { + + /** + * Creates a new RainingNumbersDemo instance. + */ + public RainingNumbersDemo() { + } + + /** Number of falling numbers in the scene. */ + private static final int NUMBERS_COUNT = 1000; + /** Size of the cubic area containing the numbers. */ + private final static int AREA = 600; + /** Half of the area for positioning calculations. */ + private final static int AREA_HALF = AREA / 2; + + /** + * Entry point for the raining numbers demo. + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + new RainingNumbersDemo().run(); + } + + /** + * Animates all text canvases to fall downward each frame. + * Numbers that fall below the area wrap around to the top. + * @param viewPanel the view panel + * @param millisecondsSinceLastFrame time elapsed since last frame + * @return true to continue animation + */ + @Override + public boolean onFrame(final ViewPanel viewPanel, + final int millisecondsSinceLastFrame) { + + final Collection shapes = viewPanel + .getRootShapeCollection().getShapes(); + + final double translateAmount = millisecondsSinceLastFrame / 50d; + + shapes.stream().filter(shape -> shape instanceof TextCanvas).forEach(shape -> { + final TextCanvas block = (TextCanvas) shape; + final Point3D location = block.getLocation(); + location.translateY(translateAmount); + + if (location.y > AREA_HALF) + location.y -= AREA; + }); + + return true; + } + + /** Initializes the demo by creating the view frame and adding falling numbers. */ + private void run() { + final ViewFrame viewFrame = new ViewFrame("Raining Numbers"); + + viewFrame.getViewPanel().getCamera().getTransform().set(-129.75, 228.27, -220.69, -0.55, 0.54, 0); + + final ShapeCollection geometryCollection = viewFrame.getViewPanel() + .getRootShapeCollection(); + + Random random = new Random(); + + for (int i = 0; i < NUMBERS_COUNT; i++) { + final Point3D location = point((Math.random() * AREA) + - AREA_HALF, (Math.random() * AREA) - AREA_HALF, + (Math.random() * AREA) - AREA_HALF); + + final Color color = new Color(Math.random(), Math.random(), + Math.random(), Math.random()); + + final TextCanvas textCanvas = new TextCanvas( + new Transform(location), String.valueOf(random.nextInt(10)), color, + Color.TRANSPARENT); + + geometryCollection.addShape(textCanvas); + } + + viewFrame.getViewPanel().addFrameListener(this); + + viewFrame.getViewPanel().repaintDuringNextViewUpdate(); + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/SineHeightmap.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/SineHeightmap.java new file mode 100755 index 0000000..64433da --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/SineHeightmap.java @@ -0,0 +1,117 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + * + */ + +package eu.svjatoslav.sixth.e3d.examples; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.WireframeSphere; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin; +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; +import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex; + +/** + * Demo showing a sine heightmap surface with a central wireframe sphere. + * Two wobbly surfaces are positioned above and below the sphere. + */ +public class SineHeightmap { + + /** + * Frequency of the wave pattern in the wobbly surfaces. + */ + private static final double WAVE_FREQUENCY = 50d; + /** + * Amplitude of the wave pattern in the wobbly surfaces. + */ + private static final double WAVE_AMPLITUDE = 50d; + /** + * Color for the square plates in the wobbly surfaces. + */ + private static final Color SQUARE_PLATE_COLOR = hex("88F7"); + /** + * Creates a new GraphDemo instance. + */ + public SineHeightmap() { + } + + /** + * Creates a single square plate at the specified position. + * + * @param shapeCollection the collection to add the plate to + * @param y the Y coordinate (elevation) + * @param x the X coordinate + * @param z the Z coordinate + */ + private static void makeSquarePlate(final ShapeCollection shapeCollection, + final double y, final double x, final double z) { + final Point3D p1 = point(x, y, z); + final Point3D p2 = point(x + 20, y, z); + final Point3D p3 = point(x, y, z + 20); + final Point3D p4 = point(x + 20, y, z + 20); + final SolidPolygon quad = SolidPolygon.quad(p1, p2, p4, p3, SQUARE_PLATE_COLOR); + shapeCollection.addShape(quad); + } + + /** + * Creates a wobbly surface composed of square plates arranged in a wave pattern. + * + * @param shapeCollection the collection to add plates to + * @param surfaceElevation the base Y elevation of the surface + */ + private static void addWobblySurface(final ShapeCollection shapeCollection, + final double surfaceElevation) { + for (double x = -500; x < 500; x += 20) + for (double z = -500; z < 500; z += 20) { + + final double distanceFromCenter = Math.sqrt((x * x) + (z * z)); + + double plateElevation = Math.sin(distanceFromCenter / WAVE_FREQUENCY) * WAVE_AMPLITUDE; + + makeSquarePlate(shapeCollection, plateElevation + surfaceElevation, x, + z); + } + } + + /** + * Entry point for the graph demo. + * + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + + final ViewFrame viewFrame = new ViewFrame("Sine Heightmap"); + final ShapeCollection geometryCollection = viewFrame.getViewPanel() + .getRootShapeCollection(); + + viewFrame.getViewPanel().getCamera().getTransform().setTranslation(point(0, 0, -500)); + + addSphere(geometryCollection); + addWobblySurface(geometryCollection, 200); + addWobblySurface(geometryCollection, -200); + + viewFrame.getViewPanel().repaintDuringNextViewUpdate(); + } + + /** + * Adds a wireframe sphere at the center of the scene. + * + * @param geometryCollection the collection to add the sphere to + */ + private static void addSphere(ShapeCollection geometryCollection) { + geometryCollection.addShape(new WireframeSphere(origin(), + 100, + new LineAppearance( + 4, + hex("FF00001E")) + )); + } + +} 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 index 0000000..79fa5ad --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/TextEditorDemo.java @@ -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.Rectangle; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.LookAndFeel; +import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.TextEditComponent; +import eu.svjatoslav.sixth.e3d.math.Transform; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid2D; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; +import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex; + +/** + * Demo showing a grid of 3D text editor components. + * Creates a 5x5 grid of text editors floating in 3D space with a decorative grid + * below them. This demonstrates the basic text editing capabilities of the Sixth 3D engine. + */ +public class TextEditorDemo { + + /** + * Creates a new TextEditorDemo instance. + */ + public TextEditorDemo() { + } + + /** + * Entry point for the text editor demo. + * + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + + final ViewFrame viewFrame = new ViewFrame("Text Editors"); + final ViewPanel viewPanel = viewFrame.getViewPanel(); + + viewPanel.getCamera().getTransform().set(500, -300, -800, 0.6, -0.5, 0); + + final ShapeCollection shapeCollection = viewFrame.getViewPanel() + .getRootShapeCollection(); + + addGrid(shapeCollection); + + addTextEditors(viewPanel, shapeCollection); + + viewFrame.getViewPanel().repaintDuringNextViewUpdate(); + } + + /** + * Adds a decorative grid below the text editors. + * + * @param shapeCollection the collection to add the grid to + */ + private static void addGrid(ShapeCollection shapeCollection) { + final Transform transform = Transform.fromAngles(0, 100, 0, 0, Math.PI / 2, 0); + + final Rectangle rectangle = new Rectangle(2000); + final LineAppearance appearance = new LineAppearance(10, hex("00b3ad")); + + shapeCollection.addShape(new Grid2D(transform, rectangle, 10, 10, appearance)); + } + + /** + * Creates a grid of text editor components arranged in 3D space. + * + * @param viewPanel the view panel for the text editors + * @param shapeCollection the collection to add editors to + */ + private static void addTextEditors(ViewPanel viewPanel, ShapeCollection shapeCollection) { + final double m = 1.5; + for (double z = -500 * m; z <= (500 * m); z += 250 * m) + for (double x = -500 * m; x <= (500 * m); x += 250 * m) { + + final TextEditComponent textEditor = new TextEditComponent( + new Transform(point(x, 0, z)), viewPanel, + new Point2D(200, 120), new LookAndFeel()); + + shapeCollection.addShape(textEditor); + } + } + +} 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 index 0000000..ed38515 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/TextEditorDemo2.java @@ -0,0 +1,190 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + * + */ + +package eu.svjatoslav.sixth.e3d.examples; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.geometry.Rectangle; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.LookAndFeel; +import eu.svjatoslav.sixth.e3d.gui.textEditorComponent.TextEditComponent; +import eu.svjatoslav.sixth.e3d.math.Transform; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid2D; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URISyntaxException; +import java.util.stream.Collectors; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; +import static eu.svjatoslav.sixth.e3d.math.Transform.fromAngles; +import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex; +import static java.lang.Math.PI; + +/** + * "Text Editor City" demo showing a 3D city of text editor components. + * Creates a grid of buildings where each building has four text editor panels + * facing different directions. This demonstrates the 3D text editing capabilities + * of the Sixth 3D engine in a large-scale scene. + */ +public class TextEditorDemo2 { + + /** + * Creates a new TextEditorDemo2 instance. + */ + public TextEditorDemo2() { + } + + /** + * Entry point for the text editor city demo. + * + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + try { + new TextEditorDemo2().build(); + } catch (URISyntaxException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Adds a grid to the scene for visual reference. + * + * @param shapeCollection the collection to add the grid to + */ + private static void addGrid(ShapeCollection shapeCollection) { + final Transform transform = fromAngles(0, 100, 0, 0, PI / 2, 0); + + final Rectangle rectangle = new Rectangle(10000); + final LineAppearance appearance = new LineAppearance(10, hex("00b3ad")); + + shapeCollection.addShape(new Grid2D(transform, rectangle, 50, 50, appearance)); + } + + /** + * Reads a resource file as a string. + * + * @param fileName the name of the resource file + * @return the file contents as a string, or null if not found + * @throws IOException if an I/O error occurs + */ + static String getResourceFileAsString(String fileName) throws IOException { + ClassLoader classLoader = ClassLoader.getSystemClassLoader(); + try (InputStream is = classLoader.getResourceAsStream(fileName)) { + if (is == null) return null; + try (InputStreamReader isr = new InputStreamReader(is); + BufferedReader reader = new BufferedReader(isr)) { + return reader.lines().collect(Collectors.joining(System.lineSeparator())); + } + } + } + + /** + * Builds and displays the 3D city scene with text editors. + * + * @throws URISyntaxException if resource URI is malformed + * @throws IOException if demo text file cannot be read + */ + public void build() throws URISyntaxException, IOException { + final ViewFrame viewFrame = new ViewFrame("Text Editors City"); + final ViewPanel viewPanel = viewFrame.getViewPanel(); + + viewPanel.getCamera().getTransform().set(500, -300, -800, 0.6, -0.5, 0); + + final ShapeCollection shapeCollection = viewFrame.getViewPanel() + .getRootShapeCollection(); + + addGrid(shapeCollection); + + addCity(viewPanel, shapeCollection); + + viewFrame.getViewPanel().repaintDuringNextViewUpdate(); + } + + /** + * Creates a grid of buildings in the city. + * + * @param viewPanel the view panel for the text editors + * @param shapeCollection the collection to add buildings to + * @throws URISyntaxException if resource URI is malformed + * @throws IOException if demo text file cannot be read + */ + private void addCity(ViewPanel viewPanel, ShapeCollection shapeCollection) throws URISyntaxException, IOException { + int citySize = 4000; + for (int z = -citySize; z < citySize; z += 1000) { + for (int x = -citySize; x < citySize; x += 1000) { + addBuilding(viewPanel, shapeCollection, x, z); + } + } + } + + /** + * Adds a single building with four text editor panels facing different directions. + * + * @param viewPanel the view panel for the text editors + * @param shapeCollection the collection to add the building to + * @param x the X coordinate of the building center + * @param z the Z coordinate of the building center + * @throws URISyntaxException if resource URI is malformed + * @throws IOException if demo text file cannot be read + */ + private void addBuilding(ViewPanel viewPanel, ShapeCollection shapeCollection, double x, double z) throws URISyntaxException, IOException { + addTextEditor(viewPanel, shapeCollection, new Transform(point(x, -390, z - 200))); + + addTextEditor(viewPanel, shapeCollection, fromAngles(x, -390, z + 200, PI, 0, 0)); + + addTextEditor(viewPanel, shapeCollection, fromAngles(x - 200, -390, z, PI / 2, 0, 0)); + + addTextEditor(viewPanel, shapeCollection, fromAngles(x + 200, -390, z, PI / 2 * 3f, 0, 0)); + } + + /** + * Adds a single text editor component at the specified transform. + * + * @param viewPanel the view panel for the text editor + * @param shapeCollection the collection to add the editor to + * @param transform the position and orientation of the editor + * @throws IOException if demo text file cannot be read + */ + private void addTextEditor(ViewPanel viewPanel, ShapeCollection shapeCollection, Transform transform) throws IOException { + LookAndFeel lookAndFeel = getLookAndFeel(); + + final TextEditComponent textEditor = new TextEditComponent( + transform, + viewPanel, + new Point2D(400, 1000), + lookAndFeel + ); + + String text = getResourceFileAsString("demo.txt"); + + textEditor.setText(text); + textEditor.goToLine((int) (Math.random() * 200f)); + shapeCollection.addShape(textEditor); + } + + /** + * Creates the look and feel for text editors with a dark theme. + * + * @return a LookAndFeel configured with dark blue background and light text + */ + private LookAndFeel getLookAndFeel() { + LookAndFeel lookAndFeel = new LookAndFeel(); + lookAndFeel.background = hex("141E3296"); + lookAndFeel.tabStopBackground = lookAndFeel.background; + lookAndFeel.foreground = hex("9696FFFA"); + return lookAndFeel; + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/BenchmarkTest.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/BenchmarkTest.java new file mode 100644 index 0000000..14ab974 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/BenchmarkTest.java @@ -0,0 +1,42 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.sixth.e3d.examples.benchmark; + +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; + +/** + * Interface for a single benchmark test. + * Implementations define how to set up and tear down the test scene. + */ +public interface BenchmarkTest { + + /** + * Returns the display name for this benchmark test. + * @return the test name + */ + String getName(); + + /** + * Sets up the test scene by adding shapes to the collection. + * @param shapes the shape collection to populate + */ + void setup(ShapeCollection shapes); + + /** + * Tears down the test scene by removing all shapes added during setup. + * @param shapes the shape collection to clean up + */ + void teardown(ShapeCollection shapes); + + /** + * Called after setup to provide the view panel for tests that need animation. + * Default implementation does nothing. + * @param viewPanel the view panel + */ + default void setViewPanel(ViewPanel viewPanel) { + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/GraphicsBenchmark.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/GraphicsBenchmark.java new file mode 100644 index 0000000..f44bd6e --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/GraphicsBenchmark.java @@ -0,0 +1,345 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.sixth.e3d.examples.benchmark; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.Camera; +import eu.svjatoslav.sixth.e3d.gui.FrameListener; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.gui.humaninput.KeyboardInputHandler; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; + +import javax.swing.*; +import java.awt.*; +import java.awt.datatransfer.StringSelection; +import java.awt.event.KeyEvent; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +/** + * Automated graphics benchmark that tests the engine's rendering performance. + * Runs multiple tests sequentially, each for a fixed duration, and displays + * results in a dialog with copy-to-clipboard functionality. + * + *

The benchmark creates a 16x16x16 grid of cubes (4096 total) with the camera + * following a deterministic orbital path. Each test runs for 30 seconds by default.

+ * + *

Press Space to skip to the next test immediately.

+ * + *

Available tests:

+ *
    + *
  • {@link SolidCubesTest} - Semi-transparent solid polygon rendering
  • + *
  • {@link LitSolidCubesTest} - Opaque solid polygons with dynamic lighting
  • + *
  • {@link TexturedCubesTest} - Textured polygon rendering
  • + *
  • {@link WireframeCubesTest} - Line rendering
  • + *
  • {@link StarGridTest} - Billboard (glowing point) rendering
  • + *
+ */ +public class GraphicsBenchmark implements FrameListener, KeyboardInputHandler { + + private static final int WINDOW_WIDTH = 1920; + private static final int WINDOW_HEIGHT = 1080; + private static final int GRID_SIZE = 16; + private static final double ORBIT_DISTANCE = 1200; + private static final double ORBIT_SPEED = 0.0003; + private static final double WOBBLE_AMPLITUDE = 800; + private static final int TEST_DURATION_MS = 30000; + + private ViewFrame viewFrame; + private ViewPanel viewPanel; + private ShapeCollection shapes; + private Camera camera; + + private double orbitAngle = 0; + private long testStartTime; + private long frameCount; + private BenchmarkTest currentTest; + private boolean testFinished = false; + private boolean benchmarkFinished = false; + private boolean pendingTestTransition = false; + private final List results = new ArrayList<>(); + private final List tests = new ArrayList<>(); + + /** + * Entry point for the graphics benchmark. + * @param args command line arguments (ignored) + */ + public static void main(String[] args) { + SwingUtilities.invokeLater(() -> { + if (showIntroDialog()) { + new GraphicsBenchmark(); + } + }); + } + + private static boolean showIntroDialog() { + String message = + "
" + + "

Graphics Benchmark

" + + "

This will run a series of performance tests.

" + + "
    " + + "
  • Each test runs for 30 seconds
  • " + + "
  • Press SPACE to skip any test
    " + + "(skipping reduces measurement precision)
  • " + + "
  • A summary will be shown at the end
  • " + + "
" + + "
"; + + int choice = JOptionPane.showConfirmDialog( + null, + message, + "Graphics Benchmark", + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.INFORMATION_MESSAGE + ); + + return choice == JOptionPane.OK_OPTION; + } + + /** + * Constructs and runs the graphics benchmark. + */ + public GraphicsBenchmark() { + registerTests(); + initializeWindow(); + } + + private void initializeWindow() { + viewFrame = new ViewFrame("Graphics Benchmark", WINDOW_WIDTH, WINDOW_HEIGHT); + viewPanel = viewFrame.getViewPanel(); + viewPanel.setFrameRate(0); + shapes = viewPanel.getRootShapeCollection(); + + camera = viewPanel.getCamera(); + camera.getTransform().set(0, -500, -800, 0, -0.5, 0); + + viewPanel.addFrameListener(this); + viewPanel.getKeyboardFocusStack().pushFocusOwner(this); + viewPanel.repaintDuringNextViewUpdate(); + } + + private void registerTests() { + tests.add(new SolidCubesTest()); + tests.add(new LitSolidCubesTest()); + tests.add(new TexturedCubesTest()); + tests.add(new WireframeCubesTest()); + tests.add(new StarGridTest()); + } + + private void startNextTest() { + int nextIndex = results.size(); + if (nextIndex >= tests.size()) { + scheduleBenchmarkFinish(); + return; + } + + currentTest = tests.get(nextIndex); + testFinished = false; + orbitAngle = 0; + frameCount = 0; + testStartTime = System.currentTimeMillis(); + + currentTest.setup(shapes); + currentTest.setViewPanel(viewPanel); + } + + private void finishCurrentTest() { + if (currentTest == null || testFinished) { + return; + } + + testFinished = true; + long elapsed = System.currentTimeMillis() - testStartTime; + double durationSeconds = elapsed / 1000.0; + results.add(new TestResult(currentTest.getName(), frameCount, durationSeconds)); + + pendingTestTransition = true; + } + + private void performTestTransition() { + if (currentTest != null) { + currentTest.teardown(shapes); + } + startNextTest(); + } + + private void scheduleBenchmarkFinish() { + benchmarkFinished = true; + SwingUtilities.invokeLater(this::finishBenchmark); + } + + private void finishBenchmark() { + currentTest = null; + viewPanel.getKeyboardFocusStack().popFocusOwner(); + viewPanel.removeFrameListener(this); + viewPanel.stop(); + viewFrame.dispose(); + + showResultsDialog(); + } + + private String formatResults() { + StringBuilder sb = new StringBuilder(); + String separator = "================================================================================"; + String thinSeparator = "--------------------------------------------------------------------------------"; + + Runtime runtime = Runtime.getRuntime(); + + sb.append(separator).append("\n"); + sb.append(" GRAPHICS BENCHMARK RESULTS\n"); + sb.append(separator).append("\n"); + sb.append("Date: ").append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("\n"); + sb.append("Resolution: ").append(WINDOW_WIDTH).append("x").append(WINDOW_HEIGHT).append("\n"); + sb.append("Cubes: ").append(GRID_SIZE * GRID_SIZE * GRID_SIZE) + .append(" (").append(GRID_SIZE).append("x").append(GRID_SIZE).append("x").append(GRID_SIZE).append(" grid)\n"); + sb.append("Duration: ").append(TEST_DURATION_MS / 1000).append(" seconds per test\n"); + sb.append("\n"); + sb.append(thinSeparator).append("\n"); + sb.append("SYSTEM INFORMATION\n"); + sb.append(thinSeparator).append("\n"); + sb.append("CPU Name: ").append(getCpuName()).append("\n"); + sb.append("Arch: ").append(System.getProperty("os.arch")).append("\n"); + sb.append("CPU cores: ").append(runtime.availableProcessors()).append("\n"); + sb.append("\n"); + sb.append(thinSeparator).append("\n"); + sb.append(String.format("%-28s %s%n", "Test", "Avg FPS")); + sb.append(thinSeparator).append("\n"); + + for (TestResult result : results) { + sb.append(String.format("%-28s %.2f%n", result.testName, result.averageFps)); + } + + sb.append(separator).append("\n"); + + return sb.toString(); + } + + private void showResultsDialog() { + String resultsText = formatResults(); + + JTextArea textArea = new JTextArea(resultsText); + textArea.setEditable(false); + textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13)); + textArea.setCaretPosition(0); + + JScrollPane scrollPane = new JScrollPane(textArea); + scrollPane.setPreferredSize(new Dimension(600, 400)); + + JButton copyButton = new JButton("Copy to Clipboard"); + JButton closeButton = new JButton("Close"); + + copyButton.addActionListener(e -> { + StringSelection selection = new StringSelection(resultsText); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null); + copyButton.setText("Copied!"); + copyButton.setEnabled(false); + }); + + closeButton.addActionListener(e -> { + Window window = SwingUtilities.getWindowAncestor(closeButton); + if (window != null) { + window.dispose(); + } + }); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + buttonPanel.add(copyButton); + buttonPanel.add(closeButton); + + JPanel panel = new JPanel(new BorderLayout(0, 10)); + panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + panel.add(scrollPane, BorderLayout.CENTER); + panel.add(buttonPanel, BorderLayout.SOUTH); + + JOptionPane.showMessageDialog( + null, + panel, + "Benchmark Results", + JOptionPane.PLAIN_MESSAGE + ); + } + + private String getCpuName() { + try { + java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.FileReader("/proc/cpuinfo")); + String line; + while ((line = reader.readLine()) != null) { + if (line.startsWith("model name")) { + reader.close(); + return line.substring(line.indexOf(':') + 1).trim(); + } + } + reader.close(); + } catch (Exception ignored) { + } + return System.getProperty("java.vm.name", "Unknown"); + } + + @Override + public boolean onFrame(ViewPanel viewPanel, int millisecondsSinceLastFrame) { + if (pendingTestTransition) { + pendingTestTransition = false; + performTestTransition(); + } + + if (currentTest == null && !benchmarkFinished && results.isEmpty()) { + startNextTest(); + } + + if (benchmarkFinished) { + return false; + } + + if (currentTest == null) { + return false; + } + + orbitAngle += ORBIT_SPEED * millisecondsSinceLastFrame; + + double x = Math.sin(orbitAngle) * ORBIT_DISTANCE; + double z = Math.cos(orbitAngle) * ORBIT_DISTANCE; + double y = Math.sin(orbitAngle * 1.8934) * WOBBLE_AMPLITUDE; + + camera.getTransform().setTranslation(new Point3D(x, y, z)); + camera.lookAt(new Point3D(0, 0, 0)); + + frameCount++; + + long elapsed = System.currentTimeMillis() - testStartTime; + if (elapsed >= TEST_DURATION_MS && !testFinished) { + finishCurrentTest(); + } + + return true; + } + + @Override + public boolean keyPressed(KeyEvent event, ViewPanel viewPanel) { + if (event.getKeyChar() == ' ') { + finishCurrentTest(); + return true; + } + return false; + } + + @Override + public boolean keyReleased(KeyEvent event, ViewPanel viewPanel) { + return false; + } + + @Override + public boolean focusLost(ViewPanel viewPanel) { + return false; + } + + @Override + public boolean focusReceived(ViewPanel viewPanel) { + return false; + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/LitSolidCubesTest.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/LitSolidCubesTest.java new file mode 100644 index 0000000..4932cdf --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/LitSolidCubesTest.java @@ -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.benchmark; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.FrameListener; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource; +import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightingManager; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.LightSourceMarker; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCube; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static eu.svjatoslav.sixth.e3d.math.Transform.*; + +/** + * Benchmark test for solid opaque cubes with dynamic lighting. + * Renders a grid of fully opaque cubes lit by three orbiting light sources + * to test shaded polygon rasterization performance. + */ +public class LitSolidCubesTest implements BenchmarkTest { + + private static final int GRID_SIZE = 16; + private static final double SPACING = 80; + private static final double CUBE_SIZE = 25; + private static final long RANDOM_SEED = 42; + + private final Random random = new Random(RANDOM_SEED); + private final List cubes = new ArrayList<>(); + private final List orbitingLights = new ArrayList<>(); + private FrameListener animator; + private ViewPanel viewPanel; + + @Override + public String getName() { + return "Lit Solid Cubes"; + } + + @Override + public void setup(final ShapeCollection shapes) { + random.setSeed(RANDOM_SEED); + + // Note: lights will be added when setViewPanel is called + final Color[] lightColors = { + new Color(255, 100, 100), + new Color(100, 255, 100), + new Color(100, 100, 255) + }; + + for (int i = 0; i < 3; i++) { + final double angleOffset = i * (Math.PI * 2 / 3); + final LightSource light = new LightSource( + new Point3D(0, 0, 0), + lightColors[i], + 2.5 + ); + + final LightSourceMarker marker = new LightSourceMarker(light.getPosition(), lightColors[i]); + shapes.addShape(marker); + + orbitingLights.add(new OrbitingLight(light, marker, 600, 0.002, angleOffset, i)); + } + + final double offset = -(GRID_SIZE - 1) * SPACING / 2; + + for (int x = 0; x < GRID_SIZE; x++) { + for (int y = 0; y < GRID_SIZE; y++) { + for (int z = 0; z < GRID_SIZE; z++) { + final double px = offset + x * SPACING; + final double py = offset + y * SPACING; + final double pz = offset + z * SPACING; + + final Color color = new Color( + 150 + random.nextInt(105), + 150 + random.nextInt(105), + 150 + random.nextInt(105) + ); + + final SolidPolygonCube cube = new SolidPolygonCube( + new Point3D(px, py, pz), + CUBE_SIZE, + color + ); + cube.setShadingEnabled(true); + shapes.addShape(cube); + cubes.add(cube); + } + } + } + } + + @Override + public void teardown(final ShapeCollection shapes) { + shapes.clear(); + cubes.clear(); + + for (final OrbitingLight ol : orbitingLights) { + if (viewPanel != null) { + viewPanel.getLightingManager().removeLight(ol.light); + } + } + orbitingLights.clear(); + + if (viewPanel != null && animator != null) { + viewPanel.removeFrameListener(animator); + } + viewPanel = null; + animator = null; + } + + /** + * Sets the view panel for animation callbacks. + * @param viewPanel the view panel + */ + public void setViewPanel(final ViewPanel viewPanel) { + this.viewPanel = viewPanel; + if (viewPanel != null) { + // Configure global lighting + LightingManager lightingManager = viewPanel.getLightingManager(); + lightingManager.setAmbientLight(new Color(15, 15, 20)); + for (final OrbitingLight ol : orbitingLights) { + lightingManager.addLight(ol.light); + } + + animator = new LightAnimator(); + viewPanel.addFrameListener(animator); + } + } + + private static class OrbitingLight { + final LightSource light; + final LightSourceMarker marker; + final double orbitRadius; + final double speed; + final int axisIndex; + double angle; + + OrbitingLight(final LightSource light, final LightSourceMarker marker, + final double orbitRadius, final double speed, final double angleOffset, final int axisIndex) { + this.light = light; + this.marker = marker; + this.orbitRadius = orbitRadius; + this.speed = speed; + this.angle = angleOffset; + this.axisIndex = axisIndex; + } + } + + private class LightAnimator implements FrameListener { + @Override + public boolean onFrame(final ViewPanel vp, final int millisecondsSinceLastFrame) { + for (final OrbitingLight ol : orbitingLights) { + ol.angle += ol.speed * millisecondsSinceLastFrame; + + double x, y, z; + switch (ol.axisIndex) { + case 0: + x = ol.orbitRadius * Math.cos(ol.angle); + y = ol.orbitRadius * 0.3 * Math.sin(ol.angle * 2); + z = ol.orbitRadius * Math.sin(ol.angle); + break; + case 1: + x = ol.orbitRadius * Math.sin(ol.angle); + y = ol.orbitRadius * Math.cos(ol.angle); + z = ol.orbitRadius * 0.3 * Math.sin(ol.angle * 2); + break; + default: + x = ol.orbitRadius * 0.3 * Math.sin(ol.angle * 2); + y = ol.orbitRadius * Math.sin(ol.angle); + z = ol.orbitRadius * Math.cos(ol.angle); + break; + } + + final Point3D newPos = new Point3D(x, y, z); + ol.light.setPosition(newPos); + ol.marker.setTransform(fromAngles(newPos, 0, 0)); + } + return true; + } + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/SolidCubesTest.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/SolidCubesTest.java new file mode 100644 index 0000000..01ec844 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/SolidCubesTest.java @@ -0,0 +1,78 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.sixth.e3d.examples.benchmark; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCube; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * Benchmark test for solid-color cubes. + * Renders a grid of cubes with random semi-transparent colors to test + * solid polygon rasterization performance. + */ +public class SolidCubesTest implements BenchmarkTest { + + /** + * Creates a new SolidCubesTest instance. + */ + public SolidCubesTest() { + } + + private static final int GRID_SIZE = 16; + private static final double SPACING = 80; + private static final double CUBE_SIZE = 25; + private static final long RANDOM_SEED = 42; + + private final Random random = new Random(RANDOM_SEED); + private final List cubes = new ArrayList<>(); + + @Override + public String getName() { + return "Solid Cubes"; + } + + @Override + public void setup(ShapeCollection shapes) { + random.setSeed(RANDOM_SEED); + double offset = -(GRID_SIZE - 1) * SPACING / 2; + + for (int x = 0; x < GRID_SIZE; x++) { + for (int y = 0; y < GRID_SIZE; y++) { + for (int z = 0; z < GRID_SIZE; z++) { + double px = offset + x * SPACING; + double py = offset + y * SPACING; + double pz = offset + z * SPACING; + + Color color = new Color( + 50 + random.nextInt(150), + 100 + random.nextInt(155), + 50 + random.nextInt(150), + 40 + ); + SolidPolygonCube cube = new SolidPolygonCube( + new Point3D(px, py, pz), + CUBE_SIZE, + color + ); + shapes.addShape(cube); + cubes.add(cube); + } + } + } + } + + @Override + public void teardown(ShapeCollection shapes) { + shapes.clear(); + cubes.clear(); + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/StarGridTest.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/StarGridTest.java new file mode 100644 index 0000000..2e80dc3 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/StarGridTest.java @@ -0,0 +1,89 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.sixth.e3d.examples.benchmark; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.GlowingPoint; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * Benchmark test for glowing point (star) billboards. + * Renders a grid of glowing points arranged in a cube formation to test + * billboard rendering and texture blending performance. + */ +public class StarGridTest implements BenchmarkTest { + + /** + * Creates a new StarGridTest instance. + */ + public StarGridTest() { + } + + private static final int GRID_SIZE = 16; + private static final double SPACING = 80; + private static final double STAR_SIZE = 20; + private static final int UNIQUE_COLORS_COUNT = 30; + private static final long RANDOM_SEED = 42; + + private final Random random = new Random(RANDOM_SEED); + private final List stars = new ArrayList<>(); + private List colors; + + @Override + public String getName() { + return "Star Grid"; + } + + @Override + public void setup(ShapeCollection shapes) { + initializeColors(); + random.setSeed(RANDOM_SEED); + double offset = -(GRID_SIZE - 1) * SPACING / 2; + + for (int x = 0; x < GRID_SIZE; x++) { + for (int y = 0; y < GRID_SIZE; y++) { + for (int z = 0; z < GRID_SIZE; z++) { + double px = offset + x * SPACING; + double py = offset + y * SPACING; + double pz = offset + z * SPACING; + + Color color = colors.get(random.nextInt(colors.size())); + GlowingPoint star = new GlowingPoint( + new Point3D(px, py, pz), + STAR_SIZE, + color + ); + shapes.addShape(star); + stars.add(star); + } + } + } + } + + @Override + public void teardown(ShapeCollection shapes) { + shapes.clear(); + stars.clear(); + } + + private void initializeColors() { + colors = new ArrayList<>(); + random.setSeed(RANDOM_SEED); + for (int i = 0; i < UNIQUE_COLORS_COUNT; i++) { + colors.add(new Color( + random.nextDouble() + 0.5, + random.nextDouble() + 0.5, + random.nextDouble() + 0.5, + 255 + )); + } + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/TestResult.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/TestResult.java new file mode 100644 index 0000000..9c7eef2 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/TestResult.java @@ -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.benchmark; + +/** + * Holds the results of a single benchmark test. + */ +public class TestResult { + + /** The name of the benchmark test. */ + public final String testName; + + /** Total number of frames rendered during the test. */ + public final long frameCount; + + /** Duration of the test in seconds. */ + public final double durationSeconds; + + /** Average frames per second achieved during the test. */ + public final double averageFps; + + /** + * Creates a test result record. + * @param testName the name of the test + * @param frameCount total frames rendered + * @param durationSeconds test duration in seconds + */ + public TestResult(String testName, long frameCount, double durationSeconds) { + this.testName = testName; + this.frameCount = frameCount; + this.durationSeconds = durationSeconds; + this.averageFps = frameCount / durationSeconds; + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/TexturedCube.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/TexturedCube.java new file mode 100644 index 0000000..f4ddb33 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/TexturedCube.java @@ -0,0 +1,76 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.sixth.e3d.examples.benchmark; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.math.Vertex; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedTriangle; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; +import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture; + +/** + * A cube composed of textured polygons. + * Used by the {@link TexturedCubesTest} benchmark to measure texture rendering performance. + */ +public class TexturedCube extends AbstractCompositeShape { + + /** + * Creates a textured cube centered at the given position. + * @param center the center position of the cube + * @param size half the edge length of the cube + * @param texture the texture to apply to all faces + */ + public TexturedCube(Point3D center, double size, Texture texture) { + double s = size; + Point3D p1 = new Point3D(center.x - s, center.y - s, center.z - s); + Point3D p7 = new Point3D(center.x + s, center.y + s, center.z + s); + + Point3D p2 = new Point3D(p7.x, p1.y, p1.z); + Point3D p3 = new Point3D(p7.x, p1.y, p7.z); + Point3D p4 = new Point3D(p1.x, p1.y, p7.z); + Point3D p5 = new Point3D(p1.x, p7.y, p1.z); + Point3D p6 = new Point3D(p7.x, p7.y, p1.z); + Point3D p8 = new Point3D(p1.x, p7.y, p7.z); + + Point2D t00 = new Point2D(0, 0); + Point2D t10 = new Point2D(64, 0); + Point2D t01 = new Point2D(0, 64); + Point2D t11 = new Point2D(64, 64); + + addTexturedFace(p1, p2, p3, p4, t00, t10, t11, t01, texture); + addTexturedFace(p5, p8, p7, p6, t00, t01, t11, t10, texture); + addTexturedFace(p1, p5, p6, p2, t00, t01, t11, t10, texture); + addTexturedFace(p3, p7, p8, p4, t00, t10, t11, t01, texture); + addTexturedFace(p1, p4, p8, p5, t00, t10, t11, t01, texture); + addTexturedFace(p2, p6, p7, p3, t00, t01, t11, t10, texture); + + setBackfaceCulling(true); + } + + private void addTexturedFace(Point3D p1, Point3D p2, Point3D p3, Point3D p4, + Point2D t1, Point2D t2, Point2D t3, Point2D t4, + Texture texture) { + TexturedTriangle tri1 = new TexturedTriangle( + new Vertex(p1, t1), + new Vertex(p2, t2), + new Vertex(p3, t3), + texture + ); + tri1.setBackfaceCulling(true); + + TexturedTriangle tri2 = new TexturedTriangle( + new Vertex(p1, t1), + new Vertex(p3, t3), + new Vertex(p4, t4), + texture + ); + tri2.setBackfaceCulling(true); + + addShape(tri1); + addShape(tri2); + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/TexturedCubesTest.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/TexturedCubesTest.java new file mode 100644 index 0000000..887b29d --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/TexturedCubesTest.java @@ -0,0 +1,124 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.sixth.e3d.examples.benchmark; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * Benchmark test for textured cubes. + * Renders a grid of cubes with textures to test textured polygon rendering + * and texture sampling performance. + */ +public class TexturedCubesTest implements BenchmarkTest { + + /** + * Creates a new TexturedCubesTest instance. + */ + public TexturedCubesTest() { + } + + private static final int GRID_SIZE = 16; + private static final double SPACING = 80; + private static final double CUBE_SIZE = 25; + private static final int TEXTURE_COUNT = 20; + private static final long RANDOM_SEED = 42; + + private final Random random = new Random(RANDOM_SEED); + private final List cubes = new ArrayList<>(); + private Texture[] textures; + private int[] cubeTextureIndices; + + @Override + public String getName() { + return "Textured Cubes"; + } + + @Override + public void setup(ShapeCollection shapes) { + initializeTextures(); + initializeCubeTextureIndices(); + + double offset = -(GRID_SIZE - 1) * SPACING / 2; + int idx = 0; + + for (int x = 0; x < GRID_SIZE; x++) { + for (int y = 0; y < GRID_SIZE; y++) { + for (int z = 0; z < GRID_SIZE; z++) { + double px = offset + x * SPACING; + double py = offset + y * SPACING; + double pz = offset + z * SPACING; + + Texture tex = textures[cubeTextureIndices[idx]]; + TexturedCube cube = new TexturedCube( + new Point3D(px, py, pz), + CUBE_SIZE, + tex + ); + shapes.addShape(cube); + cubes.add(cube); + idx++; + } + } + } + } + + @Override + public void teardown(ShapeCollection shapes) { + shapes.clear(); + cubes.clear(); + } + + private void initializeTextures() { + textures = new Texture[TEXTURE_COUNT]; + for (int i = 0; i < TEXTURE_COUNT; i++) { + textures[i] = createGlowTexture( + 50 + random.nextInt(200), + 50 + random.nextInt(200), + 50 + random.nextInt(200) + ); + } + } + + private void initializeCubeTextureIndices() { + random.setSeed(RANDOM_SEED); + cubeTextureIndices = new int[GRID_SIZE * GRID_SIZE * GRID_SIZE]; + for (int i = 0; i < cubeTextureIndices.length; i++) { + cubeTextureIndices[i] = random.nextInt(TEXTURE_COUNT); + } + } + + private Texture createGlowTexture(int r, int g, int b) { + int texSize = 64; + Texture texture = new Texture(texSize, texSize, 2); + + java.awt.Graphics2D gr = texture.graphics; + gr.setBackground(new java.awt.Color(r, g, b, 30)); + gr.clearRect(0, 0, texSize, texSize); + + int glowWidth = 6; + for (int i = 0; i < glowWidth; i++) { + int intensity = (int) (120.0 * (glowWidth - i) / glowWidth); + java.awt.Color glowColor = new java.awt.Color( + Math.min(255, r + intensity), + Math.min(255, g + intensity), + Math.min(255, b + intensity), + 50 - i * 8 + ); + gr.setColor(glowColor); + gr.drawRect(i, i, texSize - 1 - 2 * i, texSize - 1 - 2 * i); + } + + gr.dispose(); + texture.resetResampledBitmapCache(); + return texture; + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/WireframeCubesTest.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/WireframeCubesTest.java new file mode 100644 index 0000000..23dc72f --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/WireframeCubesTest.java @@ -0,0 +1,80 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.sixth.e3d.examples.benchmark; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.WireframeBox; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * Benchmark test for wireframe cubes. + * Renders a grid of wireframe cube outlines to test line rendering performance. + */ +public class WireframeCubesTest implements BenchmarkTest { + + /** + * Creates a new WireframeCubesTest instance. + */ + public WireframeCubesTest() { + } + + private static final int GRID_SIZE = 16; + private static final double SPACING = 80; + private static final double CUBE_SIZE = 25; + private static final long RANDOM_SEED = 42; + + private final Random random = new Random(RANDOM_SEED); + private final List cubes = new ArrayList<>(); + + @Override + public String getName() { + return "Wireframe Cubes"; + } + + @Override + public void setup(ShapeCollection shapes) { + random.setSeed(RANDOM_SEED); + double offset = -(GRID_SIZE - 1) * SPACING / 2; + + for (int x = 0; x < GRID_SIZE; x++) { + for (int y = 0; y < GRID_SIZE; y++) { + for (int z = 0; z < GRID_SIZE; z++) { + double px = offset + x * SPACING; + double py = offset + y * SPACING; + double pz = offset + z * SPACING; + + Color color = new Color( + 100 + random.nextInt(155), + 100 + random.nextInt(155), + 100 + random.nextInt(155), + 50 + ); + LineAppearance appearance = new LineAppearance(5.5, color); + + Point3D center = new Point3D(px, py, pz); + Point3D p1 = new Point3D(center.x - CUBE_SIZE, center.y - CUBE_SIZE, center.z - CUBE_SIZE); + Point3D p2 = new Point3D(center.x + CUBE_SIZE, center.y + CUBE_SIZE, center.z + CUBE_SIZE); + + WireframeBox cube = new WireframeBox(p1, p2, appearance); + shapes.addShape(cube); + cubes.add(cube); + } + } + } + } + + @Override + public void teardown(ShapeCollection shapes) { + shapes.clear(); + cubes.clear(); + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/package-info.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/package-info.java new file mode 100644 index 0000000..f95f2b4 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/package-info.java @@ -0,0 +1,20 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +/** + * Graphics benchmark components for measuring the Sixth 3D engine's rendering performance. + * + *

The benchmark suite includes:

+ *
    + *
  • {@link eu.svjatoslav.sixth.e3d.examples.benchmark.GraphicsBenchmark} - Main benchmark runner
  • + *
  • {@link eu.svjatoslav.sixth.e3d.examples.benchmark.BenchmarkTest} - Interface for test implementations
  • + *
  • {@link eu.svjatoslav.sixth.e3d.examples.benchmark.SolidCubesTest} - Solid-color cube rendering test
  • + *
  • {@link eu.svjatoslav.sixth.e3d.examples.benchmark.TexturedCubesTest} - Textured cube rendering test
  • + *
  • {@link eu.svjatoslav.sixth.e3d.examples.benchmark.WireframeCubesTest} - Wireframe cube rendering test
  • + *
  • {@link eu.svjatoslav.sixth.e3d.examples.benchmark.StarGridTest} - Billboard (glowing point) rendering test
  • + *
+ */ + +package eu.svjatoslav.sixth.e3d.examples.benchmark; \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/CSGDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/CSGDemo.java new file mode 100644 index 0000000..cf33722 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/CSGDemo.java @@ -0,0 +1,233 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + * + */ +package eu.svjatoslav.sixth.e3d.examples.essentials; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.TextPointer; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.math.Transform; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCube; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonSphere; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin; +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; +import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.*; + +/** + * Demo showcasing Constructive Solid Geometry (CSG) boolean operations. + * + *

This demo displays three CSG operations side by side:

+ *
    + *
  • Subtract: A cube with a spherical cavity carved out
  • + *
  • Union: A cube merged with a sphere
  • + *
  • Intersect: The volume shared by a cube and sphere
  • + *
+ * + *

All operations use a consistent color scheme: red for the first argument + * (cube) and blue for the second argument (sphere). This makes it easy to see + * which parts of the result came from which input shape.

+ * + *

Run this demo:

+ *
{@code
+ * java -cp target/sixth-3d-demos.jar eu.svjatoslav.sixth.e3d.examples.essentials.CSGDemo
+ * }
+ * + * @see eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape#union + * @see eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape#subtract + * @see eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape#intersect + */ +public class CSGDemo { + + /** + * Distance between shapes on the X axis. + */ + private static final double SPACING = 400; + + // ==================== SHAPE FACTORY METHODS ==================== + + /** + * Creates a cube for CSG operations with current demo parameters. + * + *

The cube is created with shading enabled for realistic lighting.

+ * + * @return a new SolidPolygonCube centered at origin with shading enabled + */ + private static SolidPolygonCube createCube() { + final SolidPolygonCube cube = new SolidPolygonCube(origin(), 80, hex("39FF14D9")); + cube.setShadingEnabled(true); + return cube; + } + + /** + * Creates a sphere for CSG operations with current demo parameters. + * + *

The sphere is created with shading disabled, causing it to glow + * at full intensity regardless of lighting conditions.

+ * + * @return a new SolidPolygonSphere centered at origin with shading disabled + */ + private static SolidPolygonSphere createSphere() { + final SolidPolygonSphere sphere = new SolidPolygonSphere(origin(), 96, 12, hex("FF6600C0")); + sphere.setShadingEnabled(false); + return sphere; + } + + // ==================== MAIN ENTRY POINT ==================== + + /** + * Entry point for the CSG demo. + * + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + final ViewFrame viewFrame = new ViewFrame("CSG demo - Boolean operations"); + final ViewPanel viewPanel = viewFrame.getViewPanel(); + final ShapeCollection shapes = viewPanel.getRootShapeCollection(); + + // Position camera to view all three shapes + viewPanel.getCamera().getTransform().set(-244.24, 254.40, -458.83, -0.26, 0.24, -0.00); + + // Set up lighting + viewPanel.getLightingManager().setAmbientLight(hex("0D0D0D")); + + // Create lights + createLights(viewPanel, shapes); + + // Create the three CSG demonstrations + createSubtractDemo(shapes, point(-SPACING, 0, 0)); + createUnionDemo(shapes, origin()); + createIntersectDemo(shapes, point(SPACING, 0, 0)); + + viewPanel.repaintDuringNextViewUpdate(); + } + + /** + * Creates the subtract operation demo: cube - sphere. + * Results in a cube with a spherical cavity. + * + * @param shapes the shape collection + * @param location the position for the result + */ + private static void createSubtractDemo(final ShapeCollection shapes, final Point3D location) { + + final SolidPolygonCube cube = createCube(); + cube.subtract(createSphere()); + + shapes.addShape(cube + .setTransform(new Transform(location)) + .setBackfaceCulling(true)); + + final String description = + "Subtract: Cube - Sphere\n" + + "\n" + + "Green = Cube (kept)\n" + + "Orange = Sphere (carved out)"; + + shapes.addShape(createDescriptionPanel(location, description)); + } + + /** + * Creates the union operation demo: cube + sphere. + * Results in a combined shape. + * + * @param shapes the shape collection + * @param location the position for the result + */ + private static void createUnionDemo(final ShapeCollection shapes, final Point3D location) { + + final SolidPolygonCube cube = createCube(); + cube.union(createSphere()); + + shapes.addShape(cube + .setTransform(new Transform(location)) + .setBackfaceCulling(true)); + + final String description = + "Union: Cube + Sphere\n" + + "\n" + + "Green = Cube\n" + + "Orange = Sphere"; + + shapes.addShape(createDescriptionPanel(location, description)); + } + + /** + * Creates the intersect operation demo: cube ∩ sphere. + * Results in the volume shared by both shapes. + * + * @param shapes the shape collection + * @param location the position for the result + */ + private static void createIntersectDemo(final ShapeCollection shapes, final Point3D location) { + + final SolidPolygonCube cube = createCube(); + cube.intersect(createSphere()); + + shapes.addShape(cube + .setTransform(new Transform(location)) + .setBackfaceCulling(true)); + + final String description = + "Intersect: Cube ∩ Sphere\n" + + "\n" + + "Green = from Cube\n" + + "Orange = from Sphere"; + + shapes.addShape(createDescriptionPanel(location, description)); + } + + /** + * Creates a description panel positioned below a shape. + * + * @param location the position of the associated shape + * @param text the description text to display + * @return a TextCanvas positioned below the shape + */ + private static TextCanvas createDescriptionPanel(final Point3D location, final String text) { + final Transform transform = Transform.fromAngles( + point(location.x, location.y + 220, location.z), + 0, 0); + + final TextCanvas panel = new TextCanvas( + transform, + new TextPointer(5, 35), + WHITE, + TRANSPARENT); + + panel.setText(text); + return panel; + } + + /** + * Creates lighting for the scene. + * + * @param viewPanel the view panel + * @param shapes the shape collection + */ + private static void createLights(final ViewPanel viewPanel, final ShapeCollection shapes) { + // Main light from above-front - bright white + final LightSource mainLight = new LightSource( + point(0, -300, -400), + hex("FFFFFF"), + 5.5 + ); + viewPanel.getLightingManager().addLight(mainLight); + + // Fill light from the side - warm orange tint + final LightSource fillLight = new LightSource( + point(500, 100, -200), + hex("FF6600"), + 5.5 + ); + viewPanel.getLightingManager().addLight(fillLight); + + viewPanel.getLightingManager().setAmbientLight(hex("3D3D5D")); + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/CoordinateSystemDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/CoordinateSystemDemo.java new file mode 100644 index 0000000..dafd300 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/CoordinateSystemDemo.java @@ -0,0 +1,152 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ +package eu.svjatoslav.sixth.e3d.examples.essentials; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.ForwardOrientedTextBlock; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonArrow; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid3D; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin; +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; +import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex; + +/** + * Demo displaying coordinate system axes using 3D arrows. + * + *

This demo illustrates the Sixth 3D coordinate system with three colored + * arrows originating from the origin (0,0,0), each pointing along a positive + * axis direction:

+ * + *
    + *
  • Red arrow — +X axis (points RIGHT)
  • + *
  • Green arrow — +Y axis (points DOWN visually)
  • + *
  • Blue arrow — +Z axis (points AWAY from viewer)
  • + *
+ * + *

A reference grid at Y=0 provides spatial context. Text labels identify + * each axis.

+ * + * @see SolidPolygonArrow + * @see ForwardOrientedTextBlock + */ +public class CoordinateSystemDemo { + + /** + * Length of each axis arrow. + */ + private static final double ARROW_LENGTH = 200; + + /** + * Radius of the arrow body (cylindrical shaft). + */ + private static final double BODY_RADIUS = 6; + + /** + * Distance from arrow tip to text label position. + */ + private static final double LABEL_OFFSET = 20; + + /** + * Scale factor for text labels. + */ + private static final double LABEL_SCALE = 20.0; + + /** + * Entry point for the coordinate system demo. + * + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + final ViewFrame viewFrame = new ViewFrame("Coordinate System Demo"); + final ViewPanel viewPanel = viewFrame.getViewPanel(); + final ShapeCollection shapes = viewPanel.getRootShapeCollection(); + + // Position camera to view all three axes + viewPanel.getCamera().getTransform().set(130.66, -65.49, -248.18, -0.06, -0.36, -0.00); + + // Create reference grid at Y=0 plane + createReferenceGrid(shapes); + + // Create axis arrows + createAxisArrows(shapes); + + viewPanel.repaintDuringNextViewUpdate(); + } + + /** + * Creates a reference grid on the Y=0 plane. + * + * @param shapes the shape collection to add the grid to + */ + private static void createReferenceGrid(final ShapeCollection shapes) { + final LineAppearance gridAppearance = new LineAppearance( + 1, hex("505064FF")); + final Grid3D grid = new Grid3D( + point(-300, 0, -300), + point(300, 0, 300), + 50, + gridAppearance + ); + shapes.addShape(grid); + } + + /** + * Creates the three axis arrows and their labels. + * + * @param shapes the shape collection to add the arrows to + */ + private static void createAxisArrows(final ShapeCollection shapes) { + // X-axis arrow (RED) - points in positive X direction (RIGHT) + final SolidPolygonArrow xArrow = new SolidPolygonArrow( + origin(), + point(ARROW_LENGTH, 0, 0), + BODY_RADIUS, hex("FF0000B4") + ); + shapes.addShape(xArrow); + createLabel(shapes, "X", point(ARROW_LENGTH + LABEL_OFFSET, 0, 0), Color.RED); + + // Y-axis arrow (GREEN) - points in positive Y direction (DOWN) + final SolidPolygonArrow yArrow = new SolidPolygonArrow( + origin(), + point(0, ARROW_LENGTH, 0), + BODY_RADIUS, hex("00FF00B4") + ); + shapes.addShape(yArrow); + createLabel(shapes, "Y", point(0, ARROW_LENGTH + LABEL_OFFSET, 0), Color.GREEN); + + // Z-axis arrow (BLUE) - points in positive Z direction (AWAY) + final SolidPolygonArrow zArrow = new SolidPolygonArrow( + origin(), + point(0, 0, ARROW_LENGTH), + BODY_RADIUS, hex("0000FFB4") + ); + shapes.addShape(zArrow); + createLabel(shapes, "Z", point(0, 0, ARROW_LENGTH + LABEL_OFFSET), Color.BLUE); + } + + /** + * Creates a text label at the specified position. + * + * @param shapes the shape collection to add the label to + * @param text the label text + * @param position the 3D position of the label + * @param color the text color + */ + private static void createLabel(final ShapeCollection shapes, + final String text, + final Point3D position, + final Color color) { + final ForwardOrientedTextBlock label = new ForwardOrientedTextBlock( + position, LABEL_SCALE, 2, text, color + ); + shapes.addShape(label); + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/MinimalExample.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/MinimalExample.java new file mode 100644 index 0000000..aa4347c --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/MinimalExample.java @@ -0,0 +1,46 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + * + */ +package eu.svjatoslav.sixth.e3d.examples.essentials; + +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.math.Transform; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonRectangularBox; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; + +/** + * Minimal example demonstrating how to create a basic 3D scene. + *

+ * Creates a window with a single red box. This is the "Create Your First 3D Scene" + * example from the Sixth 3D documentation. + */ +public class MinimalExample { + + /** + * Entry point for the minimal scene demo. + * + * @param args command line arguments (ignored) + */ + public static void main(String[] args) { + ViewFrame viewFrame = new ViewFrame("Minimal example"); + ShapeCollection shapes = viewFrame.getViewPanel().getRootShapeCollection(); + + viewFrame.getViewPanel().getCamera().getTransform().setTranslation(point(0, -100, -300)); + + final Transform boxTransform = new Transform(); + SolidPolygonRectangularBox box = new SolidPolygonRectangularBox( + point(-50, -50, -50), + point(50, 50, 50), + Color.RED + ); + box.setTransform(boxTransform); + shapes.addShape(box); + + viewFrame.getViewPanel().repaintDuringNextViewUpdate(); + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/ShapeGalleryDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/ShapeGalleryDemo.java new file mode 100644 index 0000000..9efa14b --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/ShapeGalleryDemo.java @@ -0,0 +1,441 @@ +package eu.svjatoslav.sixth.e3d.examples.essentials; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.FrameListener; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.ForwardOrientedTextBlock; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.LightSourceMarker; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.*; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; +import static eu.svjatoslav.sixth.e3d.math.Transform.fromAngles; +import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex; + +/** + * Gallery demo showcasing all available 3D shapes in the Sixth 3D engine. + * + *

This demo displays a 7x2 grid of shapes organized by type (columns) and + * rendering style (rows). The top row shows solid polygon shapes, and the bottom + * row shows wireframe shapes. Each column represents a different shape type.

+ * + *

Features:

+ *
    + *
  • Orbiting colored lights for dynamic lighting demonstration
  • + *
  • Floor grid showing the layout structure
  • + *
  • Text labels identifying each shape type
  • + *
+ */ +public class ShapeGalleryDemo { + + /** + * Number of columns (shape types). + */ + private static final int COLUMN_COUNT = 7; + + /** + * Size of each cell in world units. + */ + private static final double CELL_SIZE = 250; + + /** + * Radius/size for most shapes. + */ + private static final double SHAPE_RADIUS = 50; + + /** + * Number of segments for smooth curved shapes. + */ + private static final int SEGMENTS = 16; + + /** + * Y offset for labels above shapes. + */ + private static final double LABEL_Y_OFFSET = -100; + + /** + * Number of orbiting light sources in the scene. + */ + private static final int LIGHT_COUNT = 10; + + /** + * Color palette - one color per column (shape type). + */ + private static final Color[] SHAPE_COLORS = { + hex("FF6464FF"), // Arrow - Red + hex("FFB464FF"), // Cone - Orange + hex("FFFF64FF"), // Cube - Yellow + hex("64FF64FF"), // Cylinder - Green + hex("64FFFFFF"), // Pyramid - Cyan + hex("6496FFFF"), // RectangularBox - Blue + hex("C896FFFF"), // Sphere - Purple + }; + + /** + * Shape type names for labels. + */ + private static final String[] SHAPE_NAMES = { + "Arrow", + "Cone", + "Cube", + "Cylinder", + "Pyramid", + "Box", + "Sphere", + }; + + /** + * Entry point for the shaded shapes demo. + * + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + final ViewFrame viewFrame = new ViewFrame("3D Shape Gallery"); + final ViewPanel viewPanel = viewFrame.getViewPanel(); + final ShapeCollection shapes = viewPanel.getRootShapeCollection(); + + // Position camera to view the entire gallery + viewPanel.getCamera().getTransform().set(-615.31, -299.50, -378.64, -0.62, -0.66, 0.00); + + // Set up lighting + viewPanel.getLightingManager().setAmbientLight(hex("282832FF")); + + // Create floor grid + createFloorGrid(shapes); + + // Create all shapes + createAllShapes(shapes); + + // Create orbiting lights + createOrbitingLights(viewPanel, shapes); + + viewPanel.repaintDuringNextViewUpdate(); + } + + /** + * Creates the floor grid that outlines the gallery cells. + * + * @param shapes the shape collection to add the grid to + */ + private static void createFloorGrid(final ShapeCollection shapes) { + final LineAppearance gridAppearance = new LineAppearance(1, hex("505064FF")); + + // Calculate grid bounds: 7 columns, 2 rows, centered around origin + final double halfWidth = (COLUMN_COUNT * CELL_SIZE) / 2.0; + final double gridDepth = 2 * CELL_SIZE; + + final Point3D cornerA = point(-halfWidth, 0, -CELL_SIZE / 2); + final Point3D cornerB = point(halfWidth, 0, gridDepth - CELL_SIZE / 2); + + final Grid3D grid = new Grid3D(cornerA, cornerB, CELL_SIZE, gridAppearance); + shapes.addShape(grid); + } + + /** + * Creates all solid and wireframe shapes in the gallery grid. + * + * @param shapes the shape collection to add shapes to + */ + private static void createAllShapes(final ShapeCollection shapes) { + for (int col = 0; col < COLUMN_COUNT; col++) { + final double x = getColumnX(col); + final Color color = SHAPE_COLORS[col]; + final String name = SHAPE_NAMES[col]; + + // Row 0: Solid polygon shapes + final Point3D solidPos = point(x, 0, 0); + createSolidShape(shapes, col, solidPos, color); + createLabel(shapes, solidPos, name, color); + + // Row 1: Wireframe shapes + final Point3D wireframePos = point(x, 0, CELL_SIZE); + createWireframeShape(shapes, col, wireframePos, color); + createLabel(shapes, wireframePos, "Wireframe " + name, color); + } + } + + /** + * Calculates the X coordinate for a given column index. + * + * @param col the column index (0-6) + * @return the X coordinate in world units + */ + private static double getColumnX(final int col) { + return (col - (COLUMN_COUNT - 1) / 2.0) * CELL_SIZE; + } + + /** + * Creates a solid polygon shape for the given column. + * + * @param shapes the shape collection + * @param col the column index (determines shape type) + * @param pos the center position + * @param color the shape color + */ + private static void createSolidShape(final ShapeCollection shapes, final int col, + final Point3D pos, final Color color) { + switch (col) { + case 0: // Arrow + final SolidPolygonArrow arrow = new SolidPolygonArrow( + point(pos.x, SHAPE_RADIUS, pos.z), + point(pos.x, -SHAPE_RADIUS, pos.z), + 8, color); + arrow.setShadingEnabled(true); + shapes.addShape(arrow); + break; + + case 1: // Cone (pointing upward) + final SolidPolygonCone cone = new SolidPolygonCone( + point(pos.x, -SHAPE_RADIUS, pos.z), // apex above + point(pos.x, SHAPE_RADIUS / 2, pos.z), // base below + SHAPE_RADIUS * 0.8, SEGMENTS, color); + cone.setShadingEnabled(true); + shapes.addShape(cone); + break; + + case 2: // Cube + final SolidPolygonCube cube = new SolidPolygonCube(pos, SHAPE_RADIUS * 0.8, color); + cube.setShadingEnabled(true); + shapes.addShape(cube); + break; + + case 3: // Cylinder + final SolidPolygonCylinder cylinder = new SolidPolygonCylinder( + point(pos.x, SHAPE_RADIUS, pos.z), + point(pos.x, -SHAPE_RADIUS, pos.z), + SHAPE_RADIUS * 0.7, SEGMENTS, color); + cylinder.setShadingEnabled(true); + shapes.addShape(cylinder); + break; + + case 4: // Pyramid + final SolidPolygonPyramid pyramid = new SolidPolygonPyramid( + point(pos.x, -SHAPE_RADIUS, pos.z), // apex above + point(pos.x, SHAPE_RADIUS / 2, pos.z), // base below + SHAPE_RADIUS * 0.8, color); + pyramid.setShadingEnabled(true); + shapes.addShape(pyramid); + break; + + case 5: // RectangularBox + final SolidPolygonRectangularBox box = new SolidPolygonRectangularBox( + point(pos.x - SHAPE_RADIUS * 0.35, pos.y - SHAPE_RADIUS, pos.z - SHAPE_RADIUS * 0.35), + point(pos.x + SHAPE_RADIUS * 0.35, pos.y + SHAPE_RADIUS, pos.z + SHAPE_RADIUS * 0.35), + color); + box.setShadingEnabled(true); + shapes.addShape(box); + break; + + case 6: // Sphere + final SolidPolygonSphere sphere = new SolidPolygonSphere(pos, SHAPE_RADIUS, SEGMENTS, color); + sphere.setShadingEnabled(true); + shapes.addShape(sphere); + break; + + default: + break; + } + } + + /** + * Creates a wireframe shape for the given column. + * + * @param shapes the shape collection + * @param col the column index (determines shape type) + * @param pos the center position + * @param color the line color + */ + private static void createWireframeShape(final ShapeCollection shapes, final int col, + final Point3D pos, final Color color) { + final LineAppearance appearance = new LineAppearance(2, color); + + switch (col) { + case 0: // Arrow + final WireframeArrow arrow = new WireframeArrow( + point(pos.x, SHAPE_RADIUS, pos.z), + point(pos.x, -SHAPE_RADIUS, pos.z), + 8, appearance); + shapes.addShape(arrow); + break; + + case 1: // Cone + final WireframeCone cone = new WireframeCone( + point(pos.x, -SHAPE_RADIUS, pos.z), // apex above + point(pos.x, SHAPE_RADIUS / 2, pos.z), // base below + SHAPE_RADIUS * 0.8, SEGMENTS, appearance); + shapes.addShape(cone); + break; + + case 2: // Cube + final WireframeCube cube = new WireframeCube(pos, SHAPE_RADIUS * 0.8, appearance); + shapes.addShape(cube); + break; + + case 3: // Cylinder + final WireframeCylinder cylinder = new WireframeCylinder( + point(pos.x, SHAPE_RADIUS, pos.z), + point(pos.x, -SHAPE_RADIUS, pos.z), + SHAPE_RADIUS * 0.7, SEGMENTS, appearance); + shapes.addShape(cylinder); + break; + + case 4: // Pyramid + final WireframePyramid pyramid = new WireframePyramid( + point(pos.x, -SHAPE_RADIUS, pos.z), // apex above + point(pos.x, SHAPE_RADIUS / 2, pos.z), // base below + SHAPE_RADIUS * 0.8, appearance); + shapes.addShape(pyramid); + break; + + case 5: // Box (WireframeBox) + final WireframeBox box = new WireframeBox( + point(pos.x - SHAPE_RADIUS * 0.35, pos.y - SHAPE_RADIUS, pos.z - SHAPE_RADIUS * 0.35), + point(pos.x + SHAPE_RADIUS * 0.35, pos.y + SHAPE_RADIUS, pos.z + SHAPE_RADIUS * 0.35), + appearance); + shapes.addShape(box); + break; + + case 6: // Sphere + final WireframeSphere sphere = new WireframeSphere(pos, (float) SHAPE_RADIUS, appearance); + shapes.addShape(sphere); + break; + + default: + break; + } + } + + /** + * Creates a text label above a shape. + * + * @param shapes the shape collection + * @param pos the shape position + * @param text the label text + * @param color the text color + */ + private static void createLabel(final ShapeCollection shapes, final Point3D pos, + final String text, final Color color) { + final Point3D labelPos = point(pos.x, pos.y + LABEL_Y_OFFSET, pos.z); + final ForwardOrientedTextBlock label = new ForwardOrientedTextBlock( + labelPos, 8.0, 2, text, color); + shapes.addShape(label); + } + + /** + * Creates orbiting light sources around the scene. + * + * @param viewPanel the view panel + * @param shapes the shape collection + */ + private static void createOrbitingLights(final ViewPanel viewPanel, final ShapeCollection shapes) { + final Random random = new Random(42); + final List orbitingLights = new ArrayList<>(); + + for (int i = 0; i < LIGHT_COUNT; i++) { + final Color color = new Color( + random.nextInt(256), + random.nextInt(256), + random.nextInt(256) + ); + + final double orbitRadius = 600 + random.nextInt(200); + final double speed = 0.01 + random.nextDouble() * 0.02; + final double angleOffset = random.nextDouble() * Math.PI * 2; + final double intensity = 2.0 + random.nextDouble() * 2.0; + + final int axis = random.nextInt(3); + final double ellipseFactor = 0.7 + random.nextDouble() * 0.3; + + final LightSource light = new LightSource(point(0, 0, CELL_SIZE / 2), color, intensity); + final LightSourceMarker marker = new LightSourceMarker(light.getPosition(), color); + + viewPanel.getLightingManager().addLight(light); + shapes.addShape(marker); + + orbitingLights.add(new OrbitingLight( + light, marker, + orbitRadius, speed, angleOffset, axis, ellipseFactor + )); + } + + final MultiLightAnimator animator = new MultiLightAnimator(orbitingLights); + viewPanel.addFrameListener(animator); + } + + /** + * Represents a light source that orbits around the scene center. + */ + private static class OrbitingLight { + final LightSource light; + final LightSourceMarker marker; + final double orbitRadius; + final double speed; + final int axis; + final double ellipseFactor; + double angle; + + OrbitingLight(final LightSource light, final LightSourceMarker marker, + final double orbitRadius, final double speed, final double angleOffset, + final int axis, final double ellipseFactor) { + this.light = light; + this.marker = marker; + this.orbitRadius = orbitRadius; + this.speed = speed; + this.angle = angleOffset; + this.axis = axis; + this.ellipseFactor = ellipseFactor; + } + } + + /** + * Frame listener that animates all orbiting lights. + */ + private record MultiLightAnimator(List lights) implements FrameListener { + + @Override + public boolean onFrame(final ViewPanel viewPanel, final int millisecondsSinceLastFrame) { + final double centerY = CELL_SIZE / 2; + + for (final OrbitingLight orbitingLight : lights) { + orbitingLight.angle += orbitingLight.speed * millisecondsSinceLastFrame / 100; + + final double r = orbitingLight.orbitRadius; + final double e = orbitingLight.ellipseFactor; + + double x, y, z; + switch (orbitingLight.axis) { + case 0: + x = r * Math.cos(orbitingLight.angle); + y = r * e * Math.sin(orbitingLight.angle); + z = r * 0.5 * Math.sin(orbitingLight.angle * 2); + break; + case 1: + x = r * e * Math.sin(orbitingLight.angle * 2); + y = r * Math.cos(orbitingLight.angle); + z = r * 0.5 * Math.sin(orbitingLight.angle); + break; + default: + x = r * 0.5 * Math.sin(orbitingLight.angle); + y = r * Math.sin(orbitingLight.angle * 2); + z = r * e * Math.cos(orbitingLight.angle); + break; + } + + final Point3D newPosition = point(x, y + centerY, z); + orbitingLight.light.setPosition(newPosition); + orbitingLight.marker.setTransform(fromAngles( + newPosition.x, newPosition.y, newPosition.z, 0, 0, 0)); + } + return true; + } + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/WindingOrderDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/WindingOrderDemo.java new file mode 100644 index 0000000..fceba7b --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/essentials/WindingOrderDemo.java @@ -0,0 +1,60 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + * + */ +package eu.svjatoslav.sixth.e3d.examples.essentials; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; + +/** + * Demo to test winding order and backface culling documentation. + *

+ * Creates one triangle with CCW winding (front face) following the docs: + *

    + *
  • upper-center → lower-left → lower-right
  • + *
  • Backface culling enabled
  • + *
+ *

+ * Expected: green triangle visible (CCW = front face). + */ +public class WindingOrderDemo { + + /** + * Creates a new WindingOrderDemo instance. + */ + public WindingOrderDemo() { + } + + /** + * Entry point for the winding order demo. + * @param args command line arguments (ignored) + */ + public static void main(String[] args) { + ViewFrame viewFrame = new ViewFrame("Winding order demo"); + ViewPanel viewPanel = viewFrame.getViewPanel(); + ShapeCollection shapes = viewPanel.getRootShapeCollection(); + + viewPanel.getCamera().getTransform().setTranslation(point(0, 0, -500)); + + double size = 150; + + Point3D upperCenter = point(0, -size, 0); + Point3D lowerLeft = point(-size, +size, 0); + Point3D lowerRight = point(+size, +size, 0); + + SolidPolygon triangle = new SolidPolygon(upperCenter, lowerLeft, lowerRight, Color.GREEN); + triangle.setBackfaceCulling(true); + + shapes.addShape(triangle); + + viewPanel.repaintDuringNextViewUpdate(); + } +} \ No newline at end of file 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 index 0000000..a632268 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/Galaxy.java @@ -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 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 index 0000000..b382ad1 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/PointCloudDemo.java @@ -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.galaxy_demo; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.math.Transform; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; + +/** + * Demo showing a point cloud galaxy simulation. + * Creates a spiral galaxy with 10,000 glowing points using the Galaxy class. + */ +public class PointCloudDemo { + + /** + * Creates a new PointCloudDemo instance. + */ + public PointCloudDemo() { + } + + /** + * Entry point for the point cloud demo. + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + + final ViewFrame viewFrame = new ViewFrame("Point Cloud Galaxy"); + + viewFrame.getViewPanel().getCamera().getTransform().set(-1099.85, -2862.44, 144.32, -1.09, -0.60, 0); + + final ShapeCollection geometryCollection = viewFrame.getViewPanel() + .getRootShapeCollection(); + + Transform transform = Transform.fromAngles(0, -1000, 1000, 0, 0, 0); + + // add galaxy + geometryCollection.addShape(new Galaxy(500, 3, 10000, transform)); + + viewFrame.getViewPanel().repaintDuringNextViewUpdate(); + + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/package-info.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/package-info.java new file mode 100644 index 0000000..a80d64b --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/galaxy_demo/package-info.java @@ -0,0 +1,22 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +/** + * Galaxy simulation demo using glowing points. + * + *

Creates a spiral galaxy visualization using + * {@link eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.GlowingPoint} + * shapes arranged in a mathematical spiral pattern.

+ * + *

Key classes:

+ *
    + *
  • {@link eu.svjatoslav.sixth.e3d.examples.galaxy_demo.Galaxy} - The galaxy shape
  • + *
  • {@link eu.svjatoslav.sixth.e3d.examples.galaxy_demo.PointCloudDemo} - Demo entry point
  • + *
+ * + * @see eu.svjatoslav.sixth.e3d.examples.galaxy_demo.Galaxy + */ + +package eu.svjatoslav.sixth.e3d.examples.galaxy_demo; \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/graph_demo/MathGraphsDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/graph_demo/MathGraphsDemo.java new file mode 100644 index 0000000..508fb7c --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/graph_demo/MathGraphsDemo.java @@ -0,0 +1,211 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + * + */ + +package eu.svjatoslav.sixth.e3d.examples.graph_demo; + +import eu.svjatoslav.sixth.e3d.geometry.Point2D; +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin; +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.Graph; + +import java.util.ArrayList; +import java.util.List; + +/** + * Demo showing mathematical function graphs rendered in 3D. + * Displays sine, cosine, tangent, and composite function graphs. + * Also includes a 3D surface graph showing z = (x² - y²) / 20 with + * intersecting planes and highlighted intersection curves. + */ +public class MathGraphsDemo { + + private static final double GRAPH_SCALE = 50d; + + /** + * Creates a new MathGraphsDemo instance. + */ + public MathGraphsDemo() { + } + + /** + * Creates a graph of the cosine function. + * @param location the position of the graph in 3D space + * @return a Graph component showing y = cos(x) + */ + private static Graph getCosineGraph(final Point3D location) { + final List 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 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 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 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 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 data = new ArrayList<>(); + for (double x = 0; x < 20; x += 0.25) { + double y = Math.tan(x); + + if (y > 2) + y = 2; + if (y < -2) + y = -2; + + final Point2D p = new Point2D(x, y); + data.add(p); + } + + return new Graph(GRAPH_SCALE, data, "Tangent", location); + } + + /** + * Entry point for the math graphs demo. + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + + final ViewFrame viewFrame = new ViewFrame("Math Graphs"); + final ShapeCollection geometryCollection = viewFrame.getViewPanel() + .getRootShapeCollection(); + + viewFrame.getViewPanel().getCamera().getTransform().set(-613.58, -408.70, -351.41, -0.60, -0.54, -0.00); + + addMathFormulas(geometryCollection); + addSurfaceGraph(geometryCollection); + + viewFrame.getViewPanel().repaintDuringNextViewUpdate(); + } + + /** + * Adds all mathematical formula graphs to the scene. + * @param geometryCollection the collection to add graphs to + */ + private static void addMathFormulas(ShapeCollection geometryCollection) { + int z = 1000; + Point3D location = point(-600, -300, z); + geometryCollection.addShape(getSineGraph(location)); + + location = point(600, -300, z); + geometryCollection.addShape(getFormula1Graph(location)); + + location = point(-600, 0, z); + geometryCollection.addShape(getCosineGraph(location)); + + location = point(600, 0, z); + geometryCollection.addShape(getFormula2Graph(location)); + + location = point(-600, 300, z); + geometryCollection.addShape(getTangentGraph(location)); + + location = point(600, 300, z); + geometryCollection.addShape(getFormula3Graph(location)); + } + + private static void addSurfaceGraph(ShapeCollection geometryCollection) { + final double range = 10; + final double step = 0.5; + final double scale = 30; + + final SurfaceGraph3D.MathFunction3D function = (x, y) -> (x * x - y * y) / 20; + + SurfaceGraph3D surface = new SurfaceGraph3D( + -range, range, step, + -range, range, step, + function, + new Color(150, 150, 150, 180), + Color.WHITE, + scale, + origin() + ); + + surface.addYIntersectionCurve(4, function, new Color(255, 255, 0, 220), 1.2); + surface.addXIntersectionCurve(-4, function, new Color(255, 120, 0, 220), 1.2); + + geometryCollection.addShape(surface); + } + +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/graph_demo/SurfaceGraph3D.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/graph_demo/SurfaceGraph3D.java new file mode 100644 index 0000000..c4507d3 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/graph_demo/SurfaceGraph3D.java @@ -0,0 +1,193 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ +package eu.svjatoslav.sixth.e3d.examples.graph_demo; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.Line; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; + +/** + * A 3D surface graph that visualizes mathematical functions of the form z = f(x, y). + * + *

The surface is rendered as a grid of quadrilaterals (split into triangles) with + * optional wireframe overlay. The surface color can be customized, and the wireframe + * lines are drawn in a contrasting color.

+ * + *

Usage example:

+ *
{@code
+ * // Create a saddle surface: z = x^2 - y^2
+ * SurfaceGraph3D saddle = new SurfaceGraph3D(
+ *     -5, 5, 0.5,      // x range and step
+ *     -5, 5, 0.5,      // y range and step
+ *     (x, y) -> x * x - y * y,  // function z = f(x, y)
+ *     new Color(180, 180, 180, 200),  // gray semi-transparent surface
+ *     new Color(255, 255, 255),       // white grid lines
+ *     new Point3D(0, 0, 0)            // position in 3D space
+ * );
+ * shapeCollection.addShape(saddle);
+ * }
+ * + * @see AbstractCompositeShape + * @see SolidPolygon + * @see Line + */ +public class SurfaceGraph3D extends AbstractCompositeShape { + + private final double xMin; + private final double xMax; + private final double xStep; + private final double yMin; + private final double yMax; + private final double yStep; + private final double scale; + private final Color surfaceColor; + private final Color gridColor; + private final double lineWidth; + + /** + * Creates a 3D surface graph at the specified location. + * + * @param xMin minimum X value of the domain + * @param xMax maximum X value of the domain + * @param xStep step size between grid points along X axis + * @param yMin minimum Y value of the domain + * @param yMax maximum Y value of the domain + * @param yStep step size between grid points along Y axis + * @param function the mathematical function z = f(x, y) to visualize + * @param surfaceColor color of the surface polygons (use semi-transparent for see-through effect) + * @param gridColor color of the wireframe grid lines + * @param lineWidth width of grid lines in world units + * @param scale scale factor applied to all generated vertices + * @param location the 3D position of the graph's origin + */ + public SurfaceGraph3D(final double xMin, final double xMax, final double xStep, + final double yMin, final double yMax, final double yStep, + final MathFunction3D function, + final Color surfaceColor, final Color gridColor, + final double lineWidth, final double scale, final Point3D location) { + super(location); + + this.xMin = xMin; + this.xMax = xMax; + this.yMin = yMin; + this.yMax = yMax; + this.xStep = xStep; + this.yStep = yStep; + this.scale = scale; + this.surfaceColor = surfaceColor; + this.gridColor = gridColor; + this.lineWidth = lineWidth; + + generateSurface(function); + } + + public SurfaceGraph3D(final double xMin, final double xMax, final double xStep, + final double yMin, final double yMax, final double yStep, + final MathFunction3D function, + final Color surfaceColor, final Color gridColor, + final double scale, final Point3D location) { + this(xMin, xMax, xStep, yMin, yMax, yStep, function, surfaceColor, gridColor, 0.1, scale, location); + } + + private void generateSurface(final MathFunction3D function) { + final int xCount = (int) ((xMax - xMin) / xStep) + 1; + final int yCount = (int) ((yMax - yMin) / yStep) + 1; + + final Point3D[][] vertices = new Point3D[xCount][yCount]; + + for (int i = 0; i < xCount; i++) { + for (int j = 0; j < yCount; j++) { + final double x = xMin + i * xStep; + final double y = yMin + j * yStep; + final double z = function.apply(x, y); + vertices[i][j] = new Point3D(x * scale, -z * scale, y * scale); + } + } + + for (int i = 0; i < xCount - 1; i++) { + for (int j = 0; j < yCount - 1; j++) { + final Point3D p1 = vertices[i][j]; + final Point3D p2 = vertices[i + 1][j]; + final Point3D p3 = vertices[i][j + 1]; + final Point3D p4 = vertices[i + 1][j + 1]; + + addQuad(p1, p2, p3, p4); + addGridLines(p1, p2, p3, p4); + } + } + } + + private void addQuad(final Point3D p1, final Point3D p2, final Point3D p3, final Point3D p4) { + final SolidPolygon quad = SolidPolygon.quad(p1, p2, p4, p3, surfaceColor); + quad.setBackfaceCulling(false); + addShape(quad); + } + + private void addGridLines(final Point3D p1, final Point3D p2, final Point3D p3, final Point3D p4) { + addShape(new Line(p1, p2, gridColor, lineWidth)); + addShape(new Line(p2, p4, gridColor, lineWidth)); + addShape(new Line(p3, p4, gridColor, lineWidth)); + addShape(new Line(p1, p3, gridColor, lineWidth)); + } + + /** + * Adds a curve highlighting the intersection of the surface with a vertical plane at constant X. + * + * @param xValue the X coordinate of the intersecting plane + * @param function the mathematical function z = f(x, y) + * @param color the color of the intersection curve + * @param width the line width + */ + public void addXIntersectionCurve(final double xValue, final MathFunction3D function, + final Color color, final double width) { + Point3D prevPoint = null; + for (double y = yMin; y <= yMax; y += 0.15) { + final double z = function.apply(xValue, y); + final Point3D currentPoint = new Point3D(xValue * scale, -z * scale, y * scale); + if (prevPoint != null) { + addShape(new Line(prevPoint, currentPoint, color, width)); + } + prevPoint = currentPoint; + } + } + + /** + * Adds a curve highlighting the intersection of the surface with a vertical plane at constant Y. + * + * @param yValue the Y coordinate of the intersecting plane + * @param function the mathematical function z = f(x, y) + * @param color the color of the intersection curve + * @param width the line width + */ + public void addYIntersectionCurve(final double yValue, final MathFunction3D function, + final Color color, final double width) { + Point3D prevPoint = null; + for (double x = xMin; x <= xMax; x += 0.15) { + final double z = function.apply(x, yValue); + final Point3D currentPoint = new Point3D(x * scale, -z * scale, yValue * scale); + if (prevPoint != null) { + addShape(new Line(prevPoint, currentPoint, color, width)); + } + prevPoint = currentPoint; + } + } + + /** + * Functional interface for 3D mathematical functions of the form z = f(x, y). + */ + @FunctionalInterface + public interface MathFunction3D { + /** + * Computes the Z value for given X and Y coordinates. + * + * @param x the X coordinate + * @param y the Y coordinate + * @return the Z value (height) at the given (x, y) position + */ + double apply(double x, double y); + } +} 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 index 0000000..550e60d --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/ApplicationListPanel.java @@ -0,0 +1,339 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + * + */ + +package eu.svjatoslav.sixth.e3d.examples.launcher; + +import eu.svjatoslav.sixth.e3d.examples.OctreeDemo; +import eu.svjatoslav.sixth.e3d.examples.RainingNumbersDemo; +import eu.svjatoslav.sixth.e3d.examples.SineHeightmap; +import eu.svjatoslav.sixth.e3d.examples.TextEditorDemo; +import eu.svjatoslav.sixth.e3d.examples.TextEditorDemo2; +import eu.svjatoslav.sixth.e3d.examples.essentials.CoordinateSystemDemo; +import eu.svjatoslav.sixth.e3d.examples.essentials.CSGDemo; +import eu.svjatoslav.sixth.e3d.examples.essentials.MinimalExample; +import eu.svjatoslav.sixth.e3d.examples.essentials.ShapeGalleryDemo; +import eu.svjatoslav.sixth.e3d.examples.essentials.WindingOrderDemo; +import eu.svjatoslav.sixth.e3d.examples.benchmark.GraphicsBenchmark; +import eu.svjatoslav.sixth.e3d.examples.galaxy_demo.PointCloudDemo; +import eu.svjatoslav.sixth.e3d.examples.graph_demo.MathGraphsDemo; +import eu.svjatoslav.sixth.e3d.examples.terrain_demo.TerrainDemo; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.ActionEvent; + +/** + * Panel containing buttons to launch each demo application. + * Displays demos organized into sections with header labels. + */ +class ApplicationListPanel extends JPanel { + private static final long serialVersionUID = 2012721856427052560L; + + private static final int BUTTON_WIDTH = 160; + private static final int GAP = 12; + private static final int SECTION_TOP_MARGIN = 16; + + private static final DemoEntry[] ESSENTIALS = { + new DemoEntry("Minimal example", + "Minimal example showing a single red box", + new ShowMinimalExample()), + new DemoEntry("Winding order", + "Triangle demo for winding order & backface culling", + new ShowWindingOrder()), + new DemoEntry("Coordinate system", + "Coordinate system axes: X (red), Y (green), Z (blue)", + new ShowCoordinateSystem()), + new DemoEntry("Shape gallery", + "All 3D shapes with orbiting colored light sources", + new ShowShadedShapes()), + new DemoEntry("CSG demo", + "Boolean operations: union, subtract, intersect on 3D shapes", + new ShowCSG()), + }; + + private static final DemoEntry[] OTHER_DEMOS = { + new DemoEntry("Volumetric Octree", + "Octree-based rendering with on-demand raytracing", + new ShowOctree()), + new DemoEntry("Sine heightmap", + "Two wobbly sine wave surfaces with central sphere", + new ShowSineHeightmap()), + new DemoEntry("Math graphs demo", + "Function graphs (sin, cos, tan) rendered in 3D", + new ShowMathGraphs()), + new DemoEntry("Point cloud galaxy", + "Spiral galaxy with 10,000 glowing points", + new ShowPointCloud()), + new DemoEntry("Raining numbers", + "Numbers falling through 3D space like rain", + new ShowRain()), + new DemoEntry("Text editors", + "5x5 grid of 3D text editor components", + new ShowTextEditors()), + new DemoEntry("Text editors city", + "3D city of text editor panels as buildings", + new ShowTextEditors2()), + new DemoEntry("Game of Life", + "Conway's Game of Life with 3D visualization", + new ShowGameOfLife()), + new DemoEntry("Procedural Terrain", + "Procedural mountains with 3 colored light sources", + new ShowTerrainDemo()), + new DemoEntry("Graphics benchmark", + "Automated performance measuring FPS across modes", + new ShowGraphicsBenchmark()), + }; + + ApplicationListPanel() { + final GroupLayout layout = new GroupLayout(this); + setLayout(layout); + + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + + final GroupLayout.SequentialGroup verticalGroup = layout.createSequentialGroup(); + final GroupLayout.ParallelGroup horizontalButtonGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING); + final GroupLayout.ParallelGroup horizontalDescGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING); + + addSectionHeader("Essentials", verticalGroup, horizontalButtonGroup, horizontalDescGroup, layout, false); + addDemoEntries(ESSENTIALS, verticalGroup, horizontalButtonGroup, horizontalDescGroup, layout); + + addSectionHeader("Demos", verticalGroup, horizontalButtonGroup, horizontalDescGroup, layout, true); + addDemoEntries(OTHER_DEMOS, verticalGroup, horizontalButtonGroup, horizontalDescGroup, layout); + + layout.setVerticalGroup(verticalGroup); + layout.setHorizontalGroup(layout.createSequentialGroup() + .addGroup(horizontalButtonGroup) + .addGap(GAP) + .addGroup(horizontalDescGroup)); + } + + /** + * Adds a section header label. + * + * @param title the section title + * @param verticalGroup the vertical layout group + * @param horizontalButtonGroup the horizontal group for buttons + * @param horizontalDescGroup the horizontal group for descriptions + * @param layout the GroupLayout + * @param addTopMargin whether to add extra top margin for visual separation + */ + private void addSectionHeader(final String title, + final GroupLayout.SequentialGroup verticalGroup, + final GroupLayout.ParallelGroup horizontalButtonGroup, + final GroupLayout.ParallelGroup horizontalDescGroup, + final GroupLayout layout, + final boolean addTopMargin) { + final JLabel headerLabel = new JLabel(title); + headerLabel.setFont(headerLabel.getFont().deriveFont(Font.BOLD, 14f)); + if (addTopMargin) { + headerLabel.setBorder(new EmptyBorder(SECTION_TOP_MARGIN, 0, 0, 0)); + } + + verticalGroup.addComponent(headerLabel); + horizontalButtonGroup.addComponent(headerLabel); + horizontalDescGroup.addComponent(headerLabel); + } + + /** + * Adds demo entry buttons to the layout. + * + * @param entries the demo entries to add + * @param verticalGroup the vertical layout group + * @param horizontalButtonGroup the horizontal group for buttons + * @param horizontalDescGroup the horizontal group for descriptions + * @param layout the GroupLayout + */ + private void addDemoEntries(final DemoEntry[] entries, + final GroupLayout.SequentialGroup verticalGroup, + final GroupLayout.ParallelGroup horizontalButtonGroup, + final GroupLayout.ParallelGroup horizontalDescGroup, + final GroupLayout layout) { + for (final DemoEntry entry : entries) { + final JButton button = new JButton(entry.action()); + button.setPreferredSize(new Dimension(BUTTON_WIDTH, button.getPreferredSize().height)); + + final JLabel descLabel = new JLabel(entry.description()); + descLabel.setFont(descLabel.getFont().deriveFont(Font.PLAIN)); + + verticalGroup.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(button) + .addComponent(descLabel)); + + horizontalButtonGroup.addComponent(button); + horizontalDescGroup.addComponent(descLabel); + } + } + + private record DemoEntry(String name, String description, AbstractAction action) { + } + + private static class ShowMinimalExample extends AbstractAction { + ShowMinimalExample() { + putValue(NAME, "Minimal example"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + MinimalExample.main(null); + } + } + + private static class ShowWindingOrder extends AbstractAction { + ShowWindingOrder() { + putValue(NAME, "Winding order"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + WindingOrderDemo.main(null); + } + } + + private static class ShowTextEditors extends AbstractAction { + ShowTextEditors() { + putValue(NAME, "Text editors"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + TextEditorDemo.main(null); + } + } + + private static class ShowTextEditors2 extends AbstractAction { + ShowTextEditors2() { + putValue(NAME, "Text editors city"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + TextEditorDemo2.main(null); + } + } + + private static class ShowRain extends AbstractAction { + ShowRain() { + putValue(NAME, "Raining numbers"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + RainingNumbersDemo.main(null); + } + } + + private static class ShowPointCloud extends AbstractAction { + ShowPointCloud() { + putValue(NAME, "Point cloud galaxy"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + PointCloudDemo.main(null); + } + } + + private static class ShowMathGraphs extends AbstractAction { + ShowMathGraphs() { + putValue(NAME, "Mathematical graphs"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + MathGraphsDemo.main(null); + } + } + + private static class ShowSineHeightmap extends AbstractAction { + ShowSineHeightmap() { + putValue(NAME, "Sine heightmap"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + SineHeightmap.main(null); + } + } + + private static class ShowOctree extends AbstractAction { + ShowOctree() { + putValue(NAME, "Volumetric octree"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + OctreeDemo.main(null); + } + } + + private static class ShowGameOfLife extends AbstractAction { + ShowGameOfLife() { + putValue(NAME, "Game of Life"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + eu.svjatoslav.sixth.e3d.examples.life_demo.Main.main(null); + } + } + + private static class ShowShadedShapes extends AbstractAction { + ShowShadedShapes() { + putValue(NAME, "Shape gallery"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + ShapeGalleryDemo.main(null); + } + } + + private static class ShowCoordinateSystem extends AbstractAction { + ShowCoordinateSystem() { + putValue(NAME, "Coordinate system"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + CoordinateSystemDemo.main(null); + } + } + + private static class ShowTerrainDemo extends AbstractAction { + ShowTerrainDemo() { + putValue(NAME, "Procedural terrain"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + TerrainDemo.main(null); + } + } + + private static class ShowGraphicsBenchmark extends AbstractAction { + ShowGraphicsBenchmark() { + putValue(NAME, "Graphics benchmark"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + GraphicsBenchmark.main(null); + } + } + + private static class ShowCSG extends AbstractAction { + ShowCSG() { + putValue(NAME, "CSG demo"); + } + + @Override + public void actionPerformed(final ActionEvent e) { + CSGDemo.main(null); + } + } +} \ No newline at end of file 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 index 0000000..c04d42e --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/Main.java @@ -0,0 +1,49 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + * +*/ + +package eu.svjatoslav.sixth.e3d.examples.launcher; + +import javax.swing.*; +import java.awt.*; + +import static java.awt.BorderLayout.CENTER; +import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE; + + +/** + * Launcher application for all Sixth 3D demos. + * Displays a window with buttons to launch each demo application. + */ +class Main { + + /** + * Entry point for the demo launcher. + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + buildAndShowGuiWindow(); + } + + /** + * Builds and shows the main window of the application. + */ + private static void buildAndShowGuiWindow() { + JFrame frame = new JFrame("Sixth 3D engine demos"); + + // Keep application running until last frame is closed. + frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE); + + frame.getContentPane().setLayout(new BorderLayout()); + frame.getContentPane().add(new ApplicationListPanel(), CENTER); + + frame.pack(); + frame.setMinimumSize(frame.getSize()); + + frame.setLocationRelativeTo(null); // center frame on screen + frame.setVisible(true); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/package-info.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/package-info.java new file mode 100644 index 0000000..560327b --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/package-info.java @@ -0,0 +1,15 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +/** + * Demo launcher application. + * + *

Provides a GUI window with buttons to launch each demo application. + * The main entry point is {@link eu.svjatoslav.sixth.e3d.examples.launcher.Main}.

+ * + * @see eu.svjatoslav.sixth.e3d.examples.launcher.Main + */ + +package eu.svjatoslav.sixth.e3d.examples.launcher; \ No newline at end of file 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 index 0000000..c9be293 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Cell.java @@ -0,0 +1,173 @@ +package eu.svjatoslav.sixth.e3d.examples.life_demo; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; + +/** + * Represents a single cell in Conway's Game of Life matrix. + * Each cell can be active (alive) or inactive (dead), and responds to mouse + * interactions for toggling its state. The cell changes color based on its + * state and whether the mouse is hovering over it. + */ +class Cell extends AbstractCompositeShape implements + MouseInteractionController { + + /** + * Visual size of each cell in world units. + */ + static final int SIZE = 20; + /** + * Color of the active cell (R, G, B, A) + */ + private static final Color ACTIVE_COLOR = new Color("A8FF"); + /** + * Color of the active cell (R, G, B, A) while the mouse is over it. + */ + private static final Color ACTIVE_COLOR_MOUSE_OVER = new Color("F9FF"); + /** + * Color of the inactive cell (R, G, B, A) + */ + private static final Color INACTIVE_COLOR = new Color("55F8"); + /** + * Color of the inactive cell (R, G, B, A) while mouse is over it. + */ + private static final Color INACTIVE_COLOR_MOUSE_OVER = new Color("77F8"); + /** + * A placeholder variable to help in next generation computation. Indicates + * whether cell is going to survive within next generation. + */ + public boolean survives; + /** + * Indicates whether cell is currently active + */ + private boolean active; + /** + * Indicates whether mouse pointer is currently over this cell. + */ + private boolean isMouseOver = false; + + /** + * Constructs a cell at the specified center position. + * + * @param center the 3D position of the cell center + */ + public Cell(final Point3D center) { + super(center); + + createCellShape(); + + // enable receiving of mouse events + setMouseInteractionController(this); + } + + /** + * Creates the visual representation of the cell as two triangles. + */ + private void createCellShape() { + final double halfSize = SIZE / 2f; + + // define 4 points corresponding to cell borders + final Point3D p1 = new Point3D(-halfSize, 0, -halfSize); + + final Point3D p2 = new Point3D(+halfSize, 0, -halfSize); + + final Point3D p3 = new Point3D(+halfSize, 0, +halfSize); + + final Point3D p4 = new Point3D(-halfSize, 0, +halfSize); + + // connect 4 points with 2 polygons + addShape(new SolidPolygon(p1, p2, p3, computeCellColor())); + addShape(new SolidPolygon(p1, p4, p3, computeCellColor())); + } + + /** + * Computes the cell color based on active state and mouse hover. + * + * @return the appropriate color for the current state + */ + private Color computeCellColor() { + if (active) + if (isMouseOver) + return ACTIVE_COLOR_MOUSE_OVER; + else + return ACTIVE_COLOR; + else if (isMouseOver) + return INACTIVE_COLOR_MOUSE_OVER; + else + return INACTIVE_COLOR; + } + + /** + * Returns whether this cell is currently active (alive). + * + * @return true if the cell is active + */ + public boolean isActive() { + return active; + } + + /** + * Sets the active state of this cell and updates its color. + * + * @param active true to make the cell active, false for inactive + */ + public void setActive(final boolean active) { + this.active = active; + updateColor(); + } + + /** + * Handles mouse click by toggling the cell state. + * + * @param button the mouse button that was clicked + * @return true to indicate the event was consumed + */ + @Override + public boolean mouseClicked(int button) { + setActive(!isActive()); + return true; + } + + /** + * Handles mouse entering the cell area by highlighting it. + * + * @return true to indicate the event was consumed + */ + @Override + public boolean mouseEntered() { + setMouseOver(true); + return true; + } + + /** + * Handles mouse exiting the cell area by removing highlight. + * + * @return true to indicate the event was consumed + */ + @Override + public boolean mouseExited() { + setMouseOver(false); + return true; + } + + /** + * Sets the mouse-over state and updates the cell color. + * + * @param isMouseOver true if mouse is over the cell + */ + private void setMouseOver(final boolean isMouseOver) { + this.isMouseOver = isMouseOver; + updateColor(); + } + + /** + * Updates the cell's visual color to match its current state. + */ + private void updateColor() { + setColor(computeCellColor()); + } + +} 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 index 0000000..d60dad7 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Main.java @@ -0,0 +1,173 @@ +package eu.svjatoslav.sixth.e3d.examples.life_demo; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.geometry.Rectangle; +import eu.svjatoslav.sixth.e3d.gui.TextPointer; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.gui.humaninput.WorldNavigationUserInputTracker; +import eu.svjatoslav.sixth.e3d.math.Transform; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin; +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; +import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.textcanvas.TextCanvas; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid2D; + +import java.awt.event.KeyEvent; + + +/** + * Main entry point for Conway's Game of Life 3D demo. + * Creates a 30x30 cell matrix where cells evolve based on Conway's rules. + * The user can interact with cells by clicking to toggle their state. + * + *

Key controls: + *

    + *
  • Space - Evolve one generation
  • + *
  • Enter - Evolve with history (leaves stars marking previous positions)
  • + *
  • C - Clear the matrix
  • + *
+ */ +public class Main extends WorldNavigationUserInputTracker { + + /** + * The game of life matrix, centered at the origin. + */ + private static final Matrix MATRIX = new Matrix( + origin() // position matrix in the center of the scene + ); + + /** + * Creates a new Main instance for the Game of Life demo. + */ + public Main() { + } + + /** + * Entry point for the Game of Life demo. + * + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + new Main().run(); + } + + /** + * Handle keyboard input. + */ + @Override + public boolean keyPressed(final KeyEvent event, final ViewPanel viewPanel) { + switch (event.getKeyChar()) { + case ' ': // space key + MATRIX.evolve(false); + break; + case 10: // ENTER + MATRIX.evolve(true); + break; + case 'c': // reset matrix + MATRIX.clear(); + break; + default: + return super.keyPressed(event, viewPanel); + } + return true; + } + + /** + * Initializes and displays the Game of Life demo. + */ + private void run() { + + // create application frame visible to the user + final ViewFrame viewFrame = new ViewFrame("Game of Life"); + final ViewPanel viewPanel = viewFrame.getViewPanel(); + + viewPanel.getCamera().getTransform().set(855.83, -174.40, -31.46, 1.58, -0.02, -0.00); + + final ShapeCollection shapeCollection = viewPanel.getRootShapeCollection(); + + // add matrix + shapeCollection.addShape(MATRIX); + + // add help panel explaining rules and controls + shapeCollection.addShape(createHelpPanel()); + + // add wire-frame grid (optional) + shapeCollection.addShape(createGrid()); + + // enable receiving of keyboard events + viewPanel.getKeyboardFocusStack().pushFocusOwner(this); + + // Done! World is built. So ensure screen is updated too. + viewPanel.repaintDuringNextViewUpdate(); + } + + /** + * Creates a pink wire-frame grid below the matrix for decorative purposes. + * + * @return a Grid2D positioned below the game matrix + */ + private Grid2D createGrid() { + return new Grid2D( + Transform.fromAngles(0, 100, 0, 0, Math.PI / 2, 0), + + new Rectangle(800), + + 5, 5, + + new LineAppearance(3, + hex("FF000050") + ) + ); + } + + /** + * Creates a help panel displaying game rules and controls. + * The panel is positioned to the right of the matrix and faces toward the viewer. + * + * @return a TextCanvas with help information + */ + private TextCanvas createHelpPanel() { + final String helpText = + "=== CONWAY'S GAME OF LIFE ===\n" + + "\n" + + "RULES:\n" + + "- Live cell with 2-3 neighbors survives\n" + + "- Dead cell with 3 neighbors becomes alive\n" + + "- All other cells die or stay dead\n" + + "\n" + + "KEYBOARD:\n" + + "Space - Evolve one generation\n" + + "Enter - Evolve with history trail\n" + + "C - Clear the matrix\n" + + "Arrows - Move camera\n" + + "\n" + + "MOUSE:\n" + + "Click - Toggle cell state\n" + + "Hover - Highlight cell"; + + final Transform location = Transform.fromAngles( + point(500, -80, 0), + -Math.PI / 2, + 0 + ); + + final TextPointer dimensions = new TextPointer(16, 45); + + final TextCanvas panel = new TextCanvas( + location, + dimensions, + Color.WHITE, + hex("000050C8") + ); + + panel.setText(helpText); + + return panel; + } + +} 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 index 0000000..adf0a8a --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Matrix.java @@ -0,0 +1,201 @@ +package eu.svjatoslav.sixth.e3d.examples.life_demo; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.SubShape; + +/** + * Represents the 2D game board for Conway's Game of Life. + * Contains a 30x30 grid of cells that can evolve according to Conway's rules. + * Supports optional history tracking where stars mark previously active cells. + * + * @see Cell + * @see Star + */ +class Matrix extends AbstractCompositeShape { + + /** + * Empty space between adjacent cells. + */ + private static final int BORDER = 5; + + /** + * Number of cells along each dimension (30x30 grid). + */ + private static final int SIZE = 30; + + /** + * Group ID for history-tracking stars. + */ + private static final String GROUP_STARS = "stars"; + + /** + * Group ID for the cell surface. + */ + private static final String GROUP_SURFACE = "surface"; + + /** + * 2D array of cells forming the game board. + */ + private final Cell[][] cells = new Cell[SIZE][]; + + /** + * Constructs a game matrix at the specified location. + * + * @param location the center position of the matrix in 3D space + */ + public Matrix(final Point3D location) { + super(location); + + + for (int x = 0; x < SIZE; x++) { + cells[x] = new Cell[SIZE]; + + // init Y row + for (int z = 0; z < SIZE; z++) { + // create cell and register it + final Cell cell = new Cell(getCellLocation(x, z)); + cells[x][z] = cell; + addShape(cell); + } + } + + setGroupForUngrouped(GROUP_SURFACE); + } + + /** + * Clears all cells to inactive state and removes history stars. + */ + public void clear() { + + // mark every cell as inactive + for (int x = 0; x < SIZE; x++) + for (int y = 0; y < SIZE; y++) + cells[x][y].setActive(false); + + // remove history stars + removeGroup(GROUP_STARS); + } + + /** + * Computes the next generation using Conway's Game of Life rules. + * Each cell's survival is determined by its number of alive neighbors. + */ + private void computeSurvivedCells() { + + for (int y = 0; y < SIZE; y++) + for (int x = 0; x < SIZE; x++) + processCell(x, y); + + for (int y = 0; y < SIZE; y++) + for (int x = 0; x < SIZE; x++) + cells[x][y].setActive(cells[x][y].survives); + + } + + /** + * Processes a single cell to determine if it survives to the next generation. + * + * @param x the X coordinate of the cell + * @param y the Y coordinate of the cell + */ + private void processCell(int x, int y) { + int aliveNeighbours = countNeighbours(x, y); + + if (cells[x][y].isActive()) { + cells[x][y].survives = ((aliveNeighbours == 2) || (aliveNeighbours == 3)); + } else { + cells[x][y].survives = aliveNeighbours == 3; + } + } + + /** + * Counts the number of alive neighbors around the specified cell. + * + * @param x the X coordinate of the cell + * @param y the Y coordinate of the cell + * @return the count of alive neighboring cells + */ + private int countNeighbours(int x, int y) { + int result = 0; + for (int ny = y - 1; ny <= y + 1; ny++) + for (int nx = x - 1; nx <= x + 1; nx++) + if (isCellAlive(nx, ny)) result++; + + if (isCellAlive(x, y)) result--; + + return result; + } + + /** + * Checks if the cell at the specified coordinates is alive. + * + * @param x the X coordinate of the cell + * @param y the Y coordinate of the cell + * @return true if the cell is within bounds and active + */ + private boolean isCellAlive(int x, int y) { + if (x < 0) return false; + if (x >= SIZE) return false; + if (y < 0) return false; + if (y >= SIZE) return false; + return cells[x][y].isActive(); + } + + /** + * Evolves the matrix by one generation. + * + * @param preserveHistory if true, places stars at positions of previously active cells + */ + public void evolve(final boolean preserveHistory) { + if (preserveHistory) + markActiveCells(); + + shiftStarsUp(); + + computeSurvivedCells(); + } + + /** + * Computes the world position of a cell given its grid coordinates. + * + * @param x the X grid coordinate + * @param z the Z grid coordinate + * @return the 3D world position of the cell center + */ + private Point3D getCellLocation(final int x, final int z) { + final int shift = -((SIZE / 2) * (Cell.SIZE + BORDER)); + + return new Point3D( + (x * (Cell.SIZE + BORDER)) + shift, + 0, + (z * (Cell.SIZE + BORDER)) + shift); + } + + /** + * Marks all currently active cells with stars for history tracking. + * Stars are added to the GROUP_STARS group. + */ + private void markActiveCells() { + // mark survived cells + for (int x = 0; x < SIZE; x++) + for (int y = 0; y < SIZE; y++) + if (cells[x][y].isActive()) + addShape(new Star(getCellLocation(x, y))); + + setGroupForUngrouped(GROUP_STARS); + } + + /** + * Shifts all history tracking stars upward to create a trailing effect. + * Also invalidates the Matrix's cached bounding box so frustum culling + * uses the updated star positions. + */ + private void shiftStarsUp() { + for (final SubShape subShape : getGroup(GROUP_STARS)) { + final Star star = (Star) subShape.getShape(); + star.translate(0, -10, 0); + } + invalidateBounds(); + } +} 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 index 0000000..f3303db --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/Star.java @@ -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 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/life_demo/package-info.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/package-info.java new file mode 100644 index 0000000..423b24b --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life_demo/package-info.java @@ -0,0 +1,23 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +/** + * Conway's Game of Life 3D demo. + * + *

Implements a 30x30 cell matrix where cells evolve based on Conway's rules. + * Users can interact by clicking to toggle cell states. The simulation runs + * in a 3D view with camera navigation.

+ * + *

Key classes:

+ *
    + *
  • {@link eu.svjatoslav.sixth.e3d.examples.life_demo.Main} - Demo entry point
  • + *
  • {@link eu.svjatoslav.sixth.e3d.examples.life_demo.Cell} - Individual cell representation
  • + *
  • {@link eu.svjatoslav.sixth.e3d.examples.life_demo.Matrix} - The cell grid
  • + *
+ * + * @see eu.svjatoslav.sixth.e3d.examples.life_demo.Main + */ + +package eu.svjatoslav.sixth.e3d.examples.life_demo; \ No newline at end of file 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 index 0000000..0c166bd --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/package-info.java @@ -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/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/DiamondSquareTerrain.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/DiamondSquareTerrain.java new file mode 100644 index 0000000..47a96aa --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/DiamondSquareTerrain.java @@ -0,0 +1,102 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.sixth.e3d.examples.terrain_demo; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.math.DiamondSquare; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; + +/** + * A procedurally generated terrain mesh using the diamond-square algorithm. + * Creates a grid of triangular polygons with height variation for mountains. + */ +public class DiamondSquareTerrain extends AbstractCompositeShape { + + private static final double TERRAIN_SIZE = 800.0; + + private final double[][] heightmap; + private final int gridSize; + + /** + * Creates a diamond-square terrain with the specified parameters. + * + * @param gridSize the size of the heightmap grid (must be 2^n + 1) + * @param minHeight the minimum terrain height + * @param maxHeight the maximum terrain height + * @param seed random seed for reproducible terrain + */ + public DiamondSquareTerrain(final int gridSize, final double minHeight, final double maxHeight, final long seed) { + super(); + + this.gridSize = gridSize; + this.heightmap = DiamondSquare.generateMap(gridSize, minHeight, maxHeight, seed); + createPolygons(gridSize, heightmap); + setBackfaceCulling(true); + } + + /** + * Returns the terrain height at the specified world XZ coordinates. + * + * @param worldX the world X coordinate + * @param worldZ the world Z coordinate + * @return the terrain height at that position, or 0 if outside terrain bounds + */ + public double getHeightAt(final double worldX, final double worldZ) { + final double cellSize = TERRAIN_SIZE / (gridSize - 1); + final double offsetX = -TERRAIN_SIZE / 2; + final double offsetZ = -TERRAIN_SIZE / 2; + + final int gridX = (int) Math.round((worldX - offsetX) / cellSize); + final int gridZ = (int) Math.round((worldZ - offsetZ) / cellSize); + + if (gridX < 0 || gridX >= gridSize || gridZ < 0 || gridZ >= gridSize) { + return 0; + } + + return heightmap[gridZ][gridX]; + } + + /** + * Creates triangular polygons from the heightmap. + */ + private void createPolygons(final int gridSize, final double[][] heightmap) { + final double cellSize = TERRAIN_SIZE / (gridSize - 1); + final double offsetX = -TERRAIN_SIZE / 2; + final double offsetZ = -TERRAIN_SIZE / 2; + + for (int z = 0; z < gridSize - 1; z++) { + for (int x = 0; x < gridSize - 1; x++) { + final double x0 = offsetX + x * cellSize; + final double x1 = offsetX + (x + 1) * cellSize; + final double z0 = offsetZ + z * cellSize; + final double z1 = offsetZ + (z + 1) * cellSize; + + final double y00 = heightmap[z][x]; + final double y10 = heightmap[z][x + 1]; + final double y01 = heightmap[z + 1][x]; + final double y11 = heightmap[z + 1][x + 1]; + + final Color color = new Color(10, 10, 10); + + final Point3D p00 = new Point3D(x0, y00, z0); + final Point3D p10 = new Point3D(x1, y10, z0); + final Point3D p01 = new Point3D(x0, y01, z1); + final Point3D p11 = new Point3D(x1, y11, z1); + + final SolidPolygon tri1 = new SolidPolygon(p00, p10, p01, color); + final SolidPolygon tri2 = new SolidPolygon(p10, p11, p01, color); + + tri1.setShadingEnabled(true); + tri2.setShadingEnabled(true); + + addShape(tri1); + addShape(tri2); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/FractalTree.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/FractalTree.java new file mode 100644 index 0000000..8b78e72 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/FractalTree.java @@ -0,0 +1,54 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.sixth.e3d.examples.terrain_demo; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCylinder; + +/** + * A simple tree trunk made of a single cylinder. + * Positioned to extend through the full terrain height range. + * + * @see TerrainDemo + * @see SolidPolygonCylinder + */ +public class FractalTree extends AbstractCompositeShape { + + private static final Color BROWN = new Color(139, 90, 43); + + /** + * Creates a tree trunk at the specified base position. + * + * @param basePosition the position of the trunk base (on the terrain surface) + * @param trunkHeight the height of the trunk + * @param trunkRadius the radius of the trunk base + */ + public FractalTree(final Point3D basePosition, final double trunkHeight, + final double trunkRadius) { + super(); + + // In Sixth 3D, Y increases downward (screen-space coordinates). + // The trunk extends upward from basePosition, so the top has a smaller Y value. + final Point3D topPosition = new Point3D( + basePosition.x, + basePosition.y - trunkHeight, + basePosition.z + ); + + final SolidPolygonCylinder trunk = new SolidPolygonCylinder( + basePosition, // start (bottom of trunk) + topPosition, // end (top of trunk) + trunkRadius, + 8, + BROWN); + trunk.setShadingEnabled(true); + + addShape(trunk); + setBackfaceCulling(true); + } +} \ No newline at end of file diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/TerrainDemo.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/TerrainDemo.java new file mode 100644 index 0000000..1bf03ec --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/terrain_demo/TerrainDemo.java @@ -0,0 +1,87 @@ +/* + * Sixth 3D engine demos. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.sixth.e3d.examples.terrain_demo; + +import eu.svjatoslav.sixth.e3d.geometry.Point3D; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.gui.ViewPanel; +import eu.svjatoslav.sixth.e3d.renderer.raster.Color; + +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.origin; +import static eu.svjatoslav.sixth.e3d.geometry.Point3D.point; +import static eu.svjatoslav.sixth.e3d.renderer.raster.Color.hex; +import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection; +import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource; +import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightingManager; +import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.LightSourceMarker; + +/** + * Demo showing a procedurally generated mountain landscape using the diamond-square algorithm. + * Three colored light sources (warm orange, cool cyan, neutral white) illuminate the terrain + * from above, demonstrating dynamic flat shading. A fractal tree stands at the center of the terrain. + */ +public class TerrainDemo { + + /** + * Entry point for the terrain demo. + * + * @param args command line arguments (ignored) + */ + public static void main(final String[] args) { + final ViewFrame viewFrame = new ViewFrame("Procedural terrain"); + ViewPanel viewPanel = viewFrame.getViewPanel(); + + viewPanel.getCamera().getTransform().set(-76.43, -72.05, -159.08, -0.64, -0.46, -0.00); + + final ShapeCollection shapes = viewPanel.getRootShapeCollection(); + + setLights(viewFrame, shapes); + + final DiamondSquareTerrain terrain = new DiamondSquareTerrain(129, 0.0, 100.0, 42); + shapes.addShape(terrain); + + // Get terrain height at center and place tree trunk on it + final double terrainHeight = terrain.getHeightAt(0, 0); + shapes.addShape(new FractalTree(point(0, terrainHeight, 0), 30, 6)); + + viewPanel.repaintDuringNextViewUpdate(); + } + + /** + * Sets up the lighting for the terrain scene. + * + * @param viewFrame the view frame containing the lighting manager + * @param shapes the shape collection to add light source markers to + */ + private static void setLights(ViewFrame viewFrame, ShapeCollection shapes) { + LightingManager lightingManager = viewFrame.getViewPanel().getLightingManager(); + lightingManager.setAmbientLight(hex("FA9664FF")); + + final LightSource warmLight = new LightSource( + point(-400, -500, 0), + hex("FFB464FF"), + 190.0 + ); + final LightSource coolLight = new LightSource( + point(400, -500, 0), + hex("64C8FFFF"), + 220.0 + ); + final LightSource neutralLight = new LightSource( + point(0, -600, 300), + hex("FFFFFFFF"), + 250.0 + ); + + lightingManager.addLight(warmLight); + lightingManager.addLight(coolLight); + lightingManager.addLight(neutralLight); + + shapes.addShape(new LightSourceMarker(warmLight.getPosition(), hex("FFB464FF"))); + shapes.addShape(new LightSourceMarker(coolLight.getPosition(), hex("64C8FFFF"))); + shapes.addShape(new LightSourceMarker(neutralLight.getPosition(), hex("FFFFFFFF"))); + } +} \ No newline at end of file diff --git a/src/main/resources/demo.txt b/src/main/resources/demo.txt new file mode 100644 index 0000000..fffb8fe --- /dev/null +++ b/src/main/resources/demo.txt @@ -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