```bash
# Run the launcher
java -cp target/sixth-3d-demos.jar eu.svjatoslav.sixth.e3d.examples.launcher.Main
-
-# Run Game of Life demo
-java -cp target/sixth-3d-demos.jar eu.svjatoslav.sixth.e3d.examples.life_demo.Main
-
-# Run Point Cloud Galaxy demo
-java -cp target/sixth-3d-demos.jar eu.svjatoslav.sixth.e3d.examples.galaxy_demo.PointCloudDemo
-
-# Run Winding Order demo (tests backface culling)
-java -cp target/sixth-3d-demos.jar eu.svjatoslav.sixth.e3d.examples.WindingOrderDemo
```
### Testing
│ └── PointCloudDemo.java
├── RandomPolygonsDemo.java
├── OctreeDemo.java
-├── GraphDemo.java
+├── graph_demo/ - 3D graph visualization demos
+│ ├── MathGraphsDemo.java
+│ └── SurfaceGraph3D.java
├── TextEditorDemo.java
├── TextEditorDemo2.java
├── RainingNumbersDemo.java
### 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
final SolidPolygonArrow redArrow = new SolidPolygonArrow(
new Point3D(0, 150, 0),
new Point3D(0, -150, 0),
- 8, // body radius
- 20, // tip radius
- 40, // tip length
- 16, // segments
- Color.RED
+ 8, Color.RED
);
shapes.addShape(redArrow);
final SolidPolygonArrow greenArrow = new SolidPolygonArrow(
new Point3D(-200, 0, 0),
new Point3D(0, 0, 0),
- 6,
- 15,
- 30,
- 12,
- Color.GREEN
+ 6, Color.GREEN
);
shapes.addShape(greenArrow);
final SolidPolygonArrow blueArrow = new SolidPolygonArrow(
new Point3D(0, 0, -200),
new Point3D(0, 0, 0),
- 6,
- 15,
- 30,
- 12,
- Color.BLUE
+ 6, Color.BLUE
);
shapes.addShape(blueArrow);
final SolidPolygonArrow yellowArrow = new SolidPolygonArrow(
new Point3D(100, 100, 100),
new Point3D(300, -100, 300),
- 10,
- 25,
- 50,
- 16,
- Color.YELLOW
+ 10, Color.YELLOW
);
shapes.addShape(yellowArrow);
final SolidPolygonArrow transparentCyanArrow = new SolidPolygonArrow(
new Point3D(-150, 50, -100),
new Point3D(-50, -100, 100),
- 8,
- 20,
- 40,
- 12,
- new Color(0, 255, 255, 128)
+ 8, new Color(0, 255, 255, 128)
);
shapes.addShape(transparentCyanArrow);
final SolidPolygonArrow transparentMagentaArrow = new SolidPolygonArrow(
new Point3D(50, 200, 50),
new Point3D(50, 0, 50),
- 12,
- 30,
- 60,
- 20,
- new Color(255, 0, 255, 64)
+ 12, new Color(255, 0, 255, 64)
);
shapes.addShape(transparentMagentaArrow);
final double endX = (radius + 80) * Math.cos(angle);
final double endZ = (radius + 80) * Math.sin(angle);
- final SolidPolygonArrow circleArrow = new SolidPolygonArrow(
- new Point3D(startX, -80, startZ),
- new Point3D(endX, -80, endZ),
- 4,
- 10,
- 20,
- 8,
- Color.WHITE
- );
+final SolidPolygonArrow circleArrow = new SolidPolygonArrow(
+ new Point3D(startX, -80, startZ),
+ new Point3D(endX, -80, endZ),
+ 4, Color.WHITE
+ );
shapes.addShape(circleArrow);
}
+++ /dev/null
-/*
- * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
- * This project is released under Creative Commons Zero (CC0) license.
- *
- */
-package eu.svjatoslav.sixth.e3d.examples;
-
-import eu.svjatoslav.sixth.e3d.geometry.Point3D;
-import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
-import eu.svjatoslav.sixth.e3d.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;
-
-/**
- * Minimal example demonstrating how to create a basic 3D scene.
- * <p>
- * Creates a window with a single red box. This is the "Create Your First 3D Scene"
- * example from the Sixth 3D documentation.
- */
-public class MinimalExample {
-
- /**
- * Entry point for the minimal scene demo.
- * @param args command line arguments (ignored)
- */
- public static void main(String[] args) {
- ViewFrame viewFrame = new ViewFrame("Minimal Example");
- ShapeCollection shapes = viewFrame.getViewPanel().getRootShapeCollection();
-
- viewFrame.getViewPanel().getCamera().getTransform().setTranslation(new Point3D(0, -100, -300));
-
- Transform boxTransform = Transform.fromAngles(0, 0, 0, 0, 0, 0);
- SolidPolygonRectangularBox box = new SolidPolygonRectangularBox(
- new Point3D(-50, -50, -50),
- new Point3D(50, 50, 50),
- Color.RED
- );
- box.setTransform(boxTransform);
- shapes.addShape(box);
-
- viewFrame.getViewPanel().repaintDuringNextViewUpdate();
- }
-}
\ No newline at end of file
/*
- * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
* This project is released under Creative Commons Zero (CC0) license.
*
-*/
+ */
package eu.svjatoslav.sixth.e3d.examples;
import eu.svjatoslav.sixth.e3d.math.Transform;
import eu.svjatoslav.sixth.e3d.renderer.octree.IntegerPoint;
import eu.svjatoslav.sixth.e3d.renderer.octree.OctreeVolume;
-import eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.RaytracingCamera;
import eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.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;
public class OctreeDemo extends WorldNavigationUserInputTracker {
/**
- * Creates a new OctreeDemo instance.
+ * Scale factor for rendering octree voxels in the scene.
*/
- public OctreeDemo() {
- }
-
- /** Scale factor for rendering octree voxels in the scene. */
private static final double magnification = 5;
private final LineAppearance gridAppearance = new LineAppearance(40, new Color(255,
0, 0, 60));
private 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) {
/**
* 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 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) {
+ final float brightness) {
shapeCollection.addShape(new LightSourceMarker(new Point3D(location)
.scaleUp(magnification), color));
new Point3D(location).scaleUp(magnification), color, brightness * 50));
}
- /** Creates a colorful spiral pattern of voxels in the octree. */
+ /**
+ * Creates a colorful spiral pattern of voxels in the octree.
+ */
private void dotSpiral() {
for (double i = 0; i < 20; i = i + .1) {
/**
* Recursively creates a fractal pattern of rectangles.
- * @param x center X coordinate
- * @param y center Y coordinate
- * @param z center Z coordinate
+ *
+ * @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
*/
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 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) {
}
}
- /** Initializes the demo scene with grid, lights, shapes, and fractal pattern. */
+ /**
+ * Initializes the demo scene with grid, lights, shapes, and fractal pattern.
+ */
private void init() {
- final ViewFrame viewFrame = new ViewFrame("Volumetric Octree");
+ 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);
new Color(200, 255, 200, 100));
putRect(new IntegerPoint(-3, 0, -30),
- new IntegerPoint( 12, 3, 300),
+ new IntegerPoint(12, 3, 300),
new Color(255, 200, 200, 100));
putRect(new IntegerPoint(-20, 20, -20),
/**
* Handles keyboard input for triggering raytracing.
- * @param event the key event
+ *
+ * @param event the key event
* @param viewPanel the view panel
* @return true if the event was consumed
*/
/**
* 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 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,
/**
* 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 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) {
octreeVolume.fillRectangle(p1, p2, color);
}
- /** Starts a raytracing render of the current view in a background thread. */
+ /**
+ * 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);
thread.start();
}
- /** Creates a tiled floor pattern using small rectangular tiles. */
+ /**
+ * Creates a tiled floor pattern using small rectangular tiles.
+ */
private void tiledFloor() {
final int step = 40;
final int size = step - 15;
+++ /dev/null
-/*
- * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
- * This project is released under Creative Commons Zero (CC0) license.
- *
- */
-
-package eu.svjatoslav.sixth.e3d.examples;
-
-import eu.svjatoslav.sixth.e3d.geometry.Point3D;
-import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
-import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
-import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid3D;
-
-/**
- * Demo showing 1000 randomly positioned and colored triangles in 3D space.
- * Demonstrates the engine's ability to render many semi-transparent polygons
- * with proper depth sorting.
- */
-public class RandomPolygonsDemo {
-
- /**
- * Creates a new RandomPolygonsDemo instance.
- */
- public RandomPolygonsDemo() {
- }
-
- /** Average size of each random polygon. */
- private static final double POLYGON_AVERAGE_SIZE = 130;
- /** Number of polygons to generate. */
- private static final int POLYGON_COUNT = 1000;
-
- /**
- * Adds a single random triangle to the scene.
- * @param geometryCollection the collection to add the polygon to
- */
- private static void addRandomPolygon(final ShapeCollection geometryCollection) {
- final Point3D polygonLocation = getRandomPoint(1000);
-
- final Point3D point1 = new Point3D(polygonLocation);
- point1.add(getRandomPoint(POLYGON_AVERAGE_SIZE));
-
- final Point3D point2 = new Point3D(polygonLocation);
- point2.add(getRandomPoint(POLYGON_AVERAGE_SIZE));
-
- final Point3D point3 = new Point3D(polygonLocation);
- point3.add(getRandomPoint(POLYGON_AVERAGE_SIZE));
-
- final Color color = new Color(
- getColorChannelBrightness(),
- getColorChannelBrightness(),
- getColorChannelBrightness(),
- 1);
-
- final SolidPolygon polygon = new SolidPolygon(point1, point2, point3,
- color);
- geometryCollection.addShape(polygon);
- }
-
- /**
- * Generates a random color channel brightness.
- * Ensures minimum brightness of 0.3 to avoid very dark polygons.
- * @return a brightness value between 0.3 and 1.0
- */
- private static double getColorChannelBrightness() {
- return Math.random() * 0.7 + 0.3f;
- }
-
- /**
- * Generates a random 3D point within a cube centered at the origin.
- * @param amplitude the half-size of the cube
- * @return a random point within [-amplitude, amplitude] on each axis
- */
- private static Point3D getRandomPoint(final double amplitude) {
- return new Point3D((Math.random() * amplitude * 2d) - amplitude,
- (Math.random() * amplitude * 2d) - amplitude, (Math.random()
- * amplitude * 2d)
- - amplitude);
- }
-
- /**
- * Entry point for the random polygons demo.
- * @param args command line arguments (ignored)
- */
- public static void main(final String[] args) {
-
- final ViewFrame viewFrame = new ViewFrame("Random Polygons");
-
- viewFrame.getViewPanel().getCamera().getTransform().set(-52.96, -239.61, -1293.29, -0.09, -0.36, 0);
-
- final ShapeCollection shapeCollection = viewFrame.getViewPanel()
- .getRootShapeCollection();
-
- // add grid
- final LineAppearance appearance = new LineAppearance(5, new Color(100,
- 100, 255, 60));
-
- shapeCollection.addShape(new Grid3D(new Point3D(1000, -1000, -1000),
- new Point3D(-1000, 1000, 1000), 300, appearance));
-
- // add random polygons
- for (int i = 0; i < POLYGON_COUNT; i++)
- addRandomPolygon(shapeCollection);
-
- viewFrame.getViewPanel().repaintDuringNextViewUpdate();
-
- }
-}
+++ /dev/null
-package eu.svjatoslav.sixth.e3d.examples;
-
-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.math.Transform.fromAngles;
-
-/**
- * Gallery demo showcasing all available 3D shapes in the Sixth 3D engine.
- *
- * <p>This demo displays a 7x2 grid of shapes organized by type (columns) and
- * rendering style (rows). The top row shows solid polygon shapes, and the bottom
- * row shows wireframe shapes. Each column represents a different shape type:</p>
- *
- * <pre>
- * Arrow Cone Cube Cylinder Pyramid Box Sphere
- * ┌────────┬────────┬────────┬────────┬────────┬────────┬────────┐
- * Solid │ ████ │ /\\ │ ████ │ ║║║ │ /\\ │ ████ │ () │
- * │ ████ │ / \\ │ ████ │ ║║║ │ /__\\ │ ████ │ ( ) │
- * ├────────┼────────┼────────┼────────┼────────┼────────┼────────┤
- * Wirefrm │ ╔══╗ │ /\\ │ ╔══╗ │ ║║║ │ /\\ │ ╔══╗ │ () │
- * │ ╚══╝ │ / \\ │ ╚══╝ │ ║║║ │ /__\\ │ ╚══╝ │ ( ) │
- * └────────┴────────┴────────┴────────┴────────┴────────┴────────┘
- * </pre>
- *
- * <p>Features:</p>
- * <ul>
- * <li>Orbiting colored lights for dynamic lighting demonstration</li>
- * <li>Floor grid showing the layout structure</li>
- * <li>Text labels identifying each shape type</li>
- * </ul>
- */
-public class ShadedShapesDemo {
-
- /**
- * 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 = {
- new Color(255, 100, 100), // Arrow - Red
- new Color(255, 180, 100), // Cone - Orange
- new Color(255, 255, 100), // Cube - Yellow
- new Color(100, 255, 100), // Cylinder - Green
- new Color(100, 255, 255), // Pyramid - Cyan
- new Color(100, 150, 255), // RectangularBox - Blue
- new Color(200, 150, 255), // 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(new Color(40, 40, 50));
-
- // 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, new Color(80, 80, 100));
-
- // 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 = new Point3D(-halfWidth, 0, -CELL_SIZE / 2);
- final Point3D cornerB = new Point3D(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 = new Point3D(x, 0, 0);
- createSolidShape(shapes, col, solidPos, color);
- createLabel(shapes, solidPos, name, color);
-
- // Row 1: Wireframe shapes
- final Point3D wireframePos = new Point3D(x, 0, CELL_SIZE);
- createWireframeShape(shapes, col, wireframePos, 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(
- new Point3D(pos.x, SHAPE_RADIUS / 2, pos.z),
- new Point3D(pos.x, -SHAPE_RADIUS / 2, pos.z),
- 8, 20, 40, 12, color);
- arrow.setShadingEnabled(true);
- shapes.addShape(arrow);
- break;
-
- case 1: // Cone (pointing upward)
- final SolidPolygonCone cone = new SolidPolygonCone(
- new Point3D(pos.x, -SHAPE_RADIUS, pos.z), // apex above
- new Point3D(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(
- new Point3D(pos.x, SHAPE_RADIUS, pos.z),
- new Point3D(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(
- new Point3D(pos.x, -SHAPE_RADIUS, pos.z), // apex above
- new Point3D(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(
- new Point3D(pos.x - SHAPE_RADIUS * 0.7, pos.y - SHAPE_RADIUS * 0.5, pos.z - SHAPE_RADIUS * 0.6),
- new Point3D(pos.x + SHAPE_RADIUS * 0.7, pos.y + SHAPE_RADIUS * 0.5, pos.z + SHAPE_RADIUS * 0.6),
- 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(
- new Point3D(pos.x, SHAPE_RADIUS / 2, pos.z),
- new Point3D(pos.x, -SHAPE_RADIUS / 2, pos.z),
- 8, 20, 40, 12, appearance);
- shapes.addShape(arrow);
- break;
-
- case 1: // Cone
- final WireframeCone cone = new WireframeCone(
- new Point3D(pos.x, -SHAPE_RADIUS, pos.z), // apex above
- new Point3D(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(
- new Point3D(pos.x, SHAPE_RADIUS, pos.z),
- new Point3D(pos.x, -SHAPE_RADIUS, pos.z),
- SHAPE_RADIUS * 0.7, SEGMENTS, appearance);
- shapes.addShape(cylinder);
- break;
-
- case 4: // Pyramid
- final WireframePyramid pyramid = new WireframePyramid(
- new Point3D(pos.x, -SHAPE_RADIUS, pos.z), // apex above
- new Point3D(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(
- new Point3D(pos.x - SHAPE_RADIUS * 0.7, pos.y - SHAPE_RADIUS * 0.5, pos.z - SHAPE_RADIUS * 0.6),
- new Point3D(pos.x + SHAPE_RADIUS * 0.7, pos.y + SHAPE_RADIUS * 0.5, pos.z + SHAPE_RADIUS * 0.6),
- 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 = new Point3D(pos.x, pos.y + LABEL_Y_OFFSET, pos.z);
- final ForwardOrientedTextBlock label = new ForwardOrientedTextBlock(
- labelPos, 0.8, 2, text, color);
- shapes.addShape(label);
- }
-
- /**
- * Creates orbiting light sources around the scene.
- *
- * @param viewPanel the view panel
- * @param shapes the shape collection
- */
- private static void createOrbitingLights(final ViewPanel viewPanel, final ShapeCollection shapes) {
- final Random random = new Random(42);
- final List<OrbitingLight> orbitingLights = new ArrayList<>();
-
- for (int i = 0; i < LIGHT_COUNT; i++) {
- final Color color = new Color(
- random.nextInt(256),
- random.nextInt(256),
- random.nextInt(256)
- );
-
- final double orbitRadius = 600 + random.nextInt(200);
- final double speed = 0.01 + random.nextDouble() * 0.02;
- final double angleOffset = random.nextDouble() * Math.PI * 2;
- final double intensity = 2.0 + random.nextDouble() * 2.0;
-
- final int axis = random.nextInt(3);
- final double ellipseFactor = 0.7 + random.nextDouble() * 0.3;
-
- final LightSource light = new LightSource(new Point3D(0, 0, CELL_SIZE / 2), color, intensity);
- final LightSourceMarker marker = new LightSourceMarker(light.getPosition(), color);
-
- viewPanel.getLightingManager().addLight(light);
- shapes.addShape(marker);
-
- orbitingLights.add(new OrbitingLight(
- light, marker,
- orbitRadius, speed, angleOffset, axis, ellipseFactor
- ));
- }
-
- final MultiLightAnimator animator = new MultiLightAnimator(orbitingLights);
- viewPanel.addFrameListener(animator);
- }
-
- /**
- * Represents a light source that orbits around the scene center.
- */
- private static class OrbitingLight {
- final LightSource light;
- final LightSourceMarker marker;
- final double orbitRadius;
- final double speed;
- final int axis;
- final double ellipseFactor;
- double angle;
-
- OrbitingLight(final LightSource light, final LightSourceMarker marker,
- final double orbitRadius, final double speed, final double angleOffset,
- final int axis, final double ellipseFactor) {
- this.light = light;
- this.marker = marker;
- this.orbitRadius = orbitRadius;
- this.speed = speed;
- this.angle = angleOffset;
- this.axis = axis;
- this.ellipseFactor = ellipseFactor;
- }
- }
-
- /**
- * Frame listener that animates all orbiting lights.
- */
- private record MultiLightAnimator(List<OrbitingLight> lights) implements FrameListener {
-
- @Override
- public boolean onFrame(final ViewPanel viewPanel, final int millisecondsSinceLastFrame) {
- final double centerY = CELL_SIZE / 2;
-
- for (final OrbitingLight orbitingLight : lights) {
- orbitingLight.angle += orbitingLight.speed * millisecondsSinceLastFrame / 100;
-
- final double r = orbitingLight.orbitRadius;
- final double e = orbitingLight.ellipseFactor;
-
- double x, y, z;
- switch (orbitingLight.axis) {
- case 0:
- x = r * Math.cos(orbitingLight.angle);
- y = r * e * Math.sin(orbitingLight.angle);
- z = r * 0.5 * Math.sin(orbitingLight.angle * 2);
- break;
- case 1:
- x = r * e * Math.sin(orbitingLight.angle * 2);
- y = r * Math.cos(orbitingLight.angle);
- z = r * 0.5 * Math.sin(orbitingLight.angle);
- break;
- default:
- x = r * 0.5 * Math.sin(orbitingLight.angle);
- y = r * Math.sin(orbitingLight.angle * 2);
- z = r * e * Math.cos(orbitingLight.angle);
- break;
- }
-
- final Point3D newPosition = new Point3D(x, y + centerY, z);
- orbitingLight.light.setPosition(newPosition);
- orbitingLight.marker.setTransform(fromAngles(
- newPosition.x, newPosition.y, newPosition.z, 0, 0, 0));
- }
- return true;
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
- * This project is released under Creative Commons Zero (CC0) license.
- *
- */
-package eu.svjatoslav.sixth.e3d.examples;
-
-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;
-
-/**
- * Demo to test winding order and backface culling documentation.
- * <p>
- * Creates one triangle with CCW winding (front face) following the docs:
- * <ul>
- * <li>upper-center → lower-left → lower-right</li>
- * <li>Backface culling enabled</li>
- * </ul>
- * <p>
- * Expected: green triangle visible (CCW = front face).
- */
-public class WindingOrderDemo {
-
- /**
- * Creates a new WindingOrderDemo instance.
- */
- public WindingOrderDemo() {
- }
-
- /**
- * Entry point for the winding order demo.
- * @param args command line arguments (ignored)
- */
- public static void main(String[] args) {
- ViewFrame viewFrame = new ViewFrame("Winding Order Demo");
- ViewPanel viewPanel = viewFrame.getViewPanel();
- ShapeCollection shapes = viewPanel.getRootShapeCollection();
-
- viewPanel.getCamera().getTransform().setTranslation(new Point3D(0, 0, -500));
-
- double size = 150;
-
- Point3D upperCenter = new Point3D(0, -size, 0);
- Point3D lowerLeft = new Point3D(-size, +size, 0);
- Point3D lowerRight = new Point3D(+size, +size, 0);
-
- SolidPolygon triangle = new SolidPolygon(upperCenter, lowerLeft, lowerRight, Color.GREEN);
- triangle.setBackfaceCulling(true);
-
- shapes.addShape(triangle);
-
- viewPanel.repaintDuringNextViewUpdate();
- }
-}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine 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;
+
+/**
+ * Demo displaying coordinate system axes using 3D arrows.
+ *
+ * <p>This demo illustrates the Sixth 3D coordinate system with three colored
+ * arrows originating from the origin (0,0,0), each pointing along a positive
+ * axis direction:</p>
+ *
+ * <ul>
+ * <li><b>Red arrow</b> — +X axis (points RIGHT)</li>
+ * <li><b>Green arrow</b> — +Y axis (points DOWN visually)</li>
+ * <li><b>Blue arrow</b> — +Z axis (points AWAY from viewer)</li>
+ * </ul>
+ *
+ * <p>A reference grid at Y=0 provides spatial context. Text labels identify
+ * each axis.</p>
+ *
+ * @see SolidPolygonArrow
+ * @see ForwardOrientedTextBlock
+ */
+public class AxisArrowsDemo {
+
+ /**
+ * 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 axis arrows demo.
+ *
+ * @param args command line arguments (ignored)
+ */
+ public static void main(final String[] args) {
+ final ViewFrame viewFrame = new ViewFrame("Axis Arrows 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, new Color(80, 80, 100));
+ final Grid3D grid = new Grid3D(
+ new Point3D(-300, 0, -300),
+ new Point3D(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(
+ new Point3D(0, 0, 0),
+ new Point3D(ARROW_LENGTH, 0, 0),
+ BODY_RADIUS, new Color(255, 0, 0, 180)
+ );
+ shapes.addShape(xArrow);
+ createLabel(shapes, "X", new Point3D(ARROW_LENGTH + LABEL_OFFSET, 0, 0), Color.RED);
+
+ // Y-axis arrow (GREEN) - points in positive Y direction (DOWN)
+ final SolidPolygonArrow yArrow = new SolidPolygonArrow(
+ new Point3D(0, 0, 0),
+ new Point3D(0, ARROW_LENGTH, 0),
+ BODY_RADIUS, new Color(0, 255, 0, 180)
+ );
+ shapes.addShape(yArrow);
+ createLabel(shapes, "Y", new Point3D(0, ARROW_LENGTH + LABEL_OFFSET, 0), Color.GREEN);
+
+ // Z-axis arrow (BLUE) - points in positive Z direction (AWAY)
+ final SolidPolygonArrow zArrow = new SolidPolygonArrow(
+ new Point3D(0, 0, 0),
+ new Point3D(0, 0, ARROW_LENGTH),
+ BODY_RADIUS, new Color(0, 0, 255, 180)
+ );
+ shapes.addShape(zArrow);
+ createLabel(shapes, "Z", new Point3D(0, 0, ARROW_LENGTH + LABEL_OFFSET), Color.BLUE);
+ }
+
+ /**
+ * Creates a text label at the specified position.
+ *
+ * @param shapes the shape collection to add the label to
+ * @param text the label text
+ * @param position the 3D position of the label
+ * @param color the text color
+ */
+ private static void createLabel(final ShapeCollection shapes,
+ final String text,
+ final Point3D position,
+ final Color color) {
+ final ForwardOrientedTextBlock label = new ForwardOrientedTextBlock(
+ position, LABEL_SCALE, 2, text, color
+ );
+ shapes.addShape(label);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ *
+ */
+package eu.svjatoslav.sixth.e3d.examples.essentials;
+
+import eu.svjatoslav.sixth.e3d.csg.CSG;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.ForwardOrientedTextBlock;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCube;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonMesh;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonSphere;
+
+/**
+ * Demo showcasing Constructive Solid Geometry (CSG) boolean operations.
+ *
+ * <p>This demo displays three CSG operations side by side:</p>
+ * <ul>
+ * <li><b>Subtract:</b> A cube with a spherical cavity carved out</li>
+ * <li><b>Union:</b> A cube merged with a sphere</li>
+ * <li><b>Intersect:</b> The volume shared by a cube and sphere</li>
+ * </ul>
+ *
+ * <p>Shapes are rendered with slight transparency (85% opacity) to allow
+ * seeing internal structure. The demo demonstrates how existing composite
+ * shapes like {@link SolidPolygonCube} and {@link SolidPolygonSphere} can
+ * be used with CSG operations.</p>
+ *
+ * <p><b>Run this demo:</b></p>
+ * <pre>{@code
+ * java -cp target/sixth-3d-demos.jar eu.svjatoslav.sixth.e3d.examples.CSGDemo
+ * }</pre>
+ *
+ * @see CSG the CSG solid class
+ * @see SolidPolygonMesh the renderable mesh
+ */
+public class CSGDemo {
+
+ /**
+ * Distance between shapes on the X axis.
+ */
+ private static final double SPACING = 500;
+
+ /**
+ * Size of the cube (half-edge length).
+ */
+ private static final double CUBE_SIZE = 80;
+
+ /**
+ * Radius of the sphere (slightly larger for interesting intersection).
+ */
+ private static final double SPHERE_RADIUS = 96;
+
+ /**
+ * Number of segments for the sphere (smoothness).
+ */
+ private static final int SPHERE_SEGMENTS = 12;
+
+ /**
+ * 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(0, -150, -600, 0, 0, 0);
+
+ // Set up lighting
+ viewPanel.getLightingManager().setAmbientLight(new Color(60, 60, 70));
+
+ // Create lights
+ createLights(viewPanel, shapes);
+
+ // Create the three CSG demonstrations
+ createSubtractDemo(shapes, -SPACING, 0, 0);
+ createUnionDemo(shapes, 0, 0, 0);
+ createIntersectDemo(shapes, SPACING, 0, 0);
+
+ // Add labels
+ createLabels(shapes);
+
+ viewPanel.repaintDuringNextViewUpdate();
+ }
+
+ /**
+ * Creates the subtract operation demo: cube - sphere.
+ * Results in a cube with a spherical cavity.
+ *
+ * @param shapes the shape collection
+ * @param x the X position
+ * @param y the Y position
+ * @param z the Z position
+ */
+ private static void createSubtractDemo(final ShapeCollection shapes,
+ final double x, final double y, final double z) {
+ final SolidPolygonCube cube = new SolidPolygonCube(
+ new Point3D(0, 0, 0), CUBE_SIZE, Color.WHITE);
+ final SolidPolygonSphere sphere = new SolidPolygonSphere(
+ new Point3D(0, 0, 0), SPHERE_RADIUS, SPHERE_SEGMENTS, Color.WHITE);
+
+ final CSG cubeCSG = CSG.fromCompositeShape(cube);
+ final CSG sphereCSG = CSG.fromCompositeShape(sphere);
+
+ final CSG result = cubeCSG.subtract(sphereCSG);
+
+ final SolidPolygonMesh mesh = result.toMesh(new Color(255, 100, 100, 217), new Point3D(x, y, z));
+ mesh.setShadingEnabled(true);
+ mesh.setBackfaceCulling(true);
+
+ shapes.addShape(mesh);
+
+ System.out.println("Subtract (cube - sphere): " + mesh.getTriangleCount() + " triangles");
+ }
+
+ /**
+ * Creates the union operation demo: cube + sphere.
+ * Results in a combined shape.
+ *
+ * @param shapes the shape collection
+ * @param x the X position
+ * @param y the Y position
+ * @param z the Z position
+ */
+ private static void createUnionDemo(final ShapeCollection shapes,
+ final double x, final double y, final double z) {
+ final SolidPolygonCube cube = new SolidPolygonCube(
+ new Point3D(0, 0, 0), CUBE_SIZE, Color.WHITE);
+ final SolidPolygonSphere sphere = new SolidPolygonSphere(
+ new Point3D(0, 0, 0), SPHERE_RADIUS, SPHERE_SEGMENTS, Color.WHITE);
+
+ final CSG cubeCSG = CSG.fromCompositeShape(cube);
+ final CSG sphereCSG = CSG.fromCompositeShape(sphere);
+
+ final CSG result = cubeCSG.union(sphereCSG);
+
+ final SolidPolygonMesh mesh = result.toMesh(new Color(100, 255, 100, 217), new Point3D(x, y, z));
+ mesh.setShadingEnabled(true);
+ mesh.setBackfaceCulling(true);
+
+ shapes.addShape(mesh);
+
+ System.out.println("Union (cube + sphere): " + mesh.getTriangleCount() + " triangles");
+ }
+
+ /**
+ * Creates the intersect operation demo: cube ∩ sphere.
+ * Results in the volume shared by both shapes.
+ *
+ * @param shapes the shape collection
+ * @param x the X position
+ * @param y the Y position
+ * @param z the Z position
+ */
+ private static void createIntersectDemo(final ShapeCollection shapes,
+ final double x, final double y, final double z) {
+ final SolidPolygonCube cube = new SolidPolygonCube(
+ new Point3D(0, 0, 0), CUBE_SIZE, Color.WHITE);
+ final SolidPolygonSphere sphere = new SolidPolygonSphere(
+ new Point3D(0, 0, 0), SPHERE_RADIUS, SPHERE_SEGMENTS, Color.WHITE);
+
+ final CSG cubeCSG = CSG.fromCompositeShape(cube);
+ final CSG sphereCSG = CSG.fromCompositeShape(sphere);
+
+ final CSG result = cubeCSG.intersect(sphereCSG);
+
+ final SolidPolygonMesh mesh = result.toMesh(new Color(100, 150, 255, 217), new Point3D(x, y, z));
+ mesh.setShadingEnabled(true);
+ mesh.setBackfaceCulling(true);
+
+ shapes.addShape(mesh);
+
+ System.out.println("Intersect (cube ∩ sphere): " + mesh.getTriangleCount() + " triangles");
+ }
+
+ /**
+ * Creates labels for each operation.
+ *
+ * @param shapes the shape collection
+ */
+ private static void createLabels(final ShapeCollection shapes) {
+ final double labelY = -150;
+ final double labelZ = 0;
+
+ // Subtract label
+ shapes.addShape(new ForwardOrientedTextBlock(
+ new Point3D(-SPACING, labelY, labelZ),
+ 8.0, 2, "Subtract", Color.RED
+ ));
+
+ // Union label
+ shapes.addShape(new ForwardOrientedTextBlock(
+ new Point3D(0, labelY, labelZ),
+ 8.0, 2, "Union", Color.GREEN
+ ));
+
+ // Intersect label
+ shapes.addShape(new ForwardOrientedTextBlock(
+ new Point3D(SPACING, labelY, labelZ),
+ 8.0, 2, "Intersect", Color.BLUE
+ ));
+ }
+
+ /**
+ * 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
+ final LightSource mainLight = new LightSource(
+ new Point3D(0, -300, -400),
+ new Color(255, 255, 255),
+ 1.5
+ );
+ viewPanel.getLightingManager().addLight(mainLight);
+
+ // Fill light from the side
+ final LightSource fillLight = new LightSource(
+ new Point3D(500, 100, -200),
+ new Color(150, 150, 200),
+ 0.8
+ );
+ viewPanel.getLightingManager().addLight(fillLight);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ *
+ */
+package eu.svjatoslav.sixth.e3d.examples.essentials;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.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;
+
+/**
+ * Minimal example demonstrating how to create a basic 3D scene.
+ * <p>
+ * Creates a window with a single red box. This is the "Create Your First 3D Scene"
+ * example from the Sixth 3D documentation.
+ */
+public class MinimalExample {
+
+ /**
+ * Entry point for the minimal scene demo.
+ *
+ * @param args command line arguments (ignored)
+ */
+ public static void main(String[] args) {
+ ViewFrame viewFrame = new ViewFrame("Minimal example");
+ ShapeCollection shapes = viewFrame.getViewPanel().getRootShapeCollection();
+
+ viewFrame.getViewPanel().getCamera().getTransform().setTranslation(new Point3D(0, -100, -300));
+
+ Transform boxTransform = Transform.fromAngles(0, 0, 0, 0, 0, 0);
+ SolidPolygonRectangularBox box = new SolidPolygonRectangularBox(
+ new Point3D(-50, -50, -50),
+ new Point3D(50, 50, 50),
+ Color.RED
+ );
+ box.setTransform(boxTransform);
+ shapes.addShape(box);
+
+ viewFrame.getViewPanel().repaintDuringNextViewUpdate();
+ }
+}
\ No newline at end of file
--- /dev/null
+package eu.svjatoslav.sixth.e3d.examples.essentials;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.FrameListener;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.lighting.LightSource;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.ForwardOrientedTextBlock;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.LightSourceMarker;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.*;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import static eu.svjatoslav.sixth.e3d.math.Transform.fromAngles;
+
+/**
+ * Gallery demo showcasing all available 3D shapes in the Sixth 3D engine.
+ *
+ * <p>This demo displays a 7x2 grid of shapes organized by type (columns) and
+ * rendering style (rows). The top row shows solid polygon shapes, and the bottom
+ * row shows wireframe shapes. Each column represents a different shape type.</p>
+ *
+ * <p>Features:</p>
+ * <ul>
+ * <li>Orbiting colored lights for dynamic lighting demonstration</li>
+ * <li>Floor grid showing the layout structure</li>
+ * <li>Text labels identifying each shape type</li>
+ * </ul>
+ */
+public class ShapeGalleryDemo {
+
+ /**
+ * Number of columns (shape types).
+ */
+ private static final int COLUMN_COUNT = 7;
+
+ /**
+ * Size of each cell in world units.
+ */
+ private static final double CELL_SIZE = 250;
+
+ /**
+ * Radius/size for most shapes.
+ */
+ private static final double SHAPE_RADIUS = 50;
+
+ /**
+ * Number of segments for smooth curved shapes.
+ */
+ private static final int SEGMENTS = 16;
+
+ /**
+ * Y offset for labels above shapes.
+ */
+ private static final double LABEL_Y_OFFSET = -100;
+
+ /**
+ * Number of orbiting light sources in the scene.
+ */
+ private static final int LIGHT_COUNT = 10;
+
+ /**
+ * Color palette - one color per column (shape type).
+ */
+ private static final Color[] SHAPE_COLORS = {
+ new Color(255, 100, 100), // Arrow - Red
+ new Color(255, 180, 100), // Cone - Orange
+ new Color(255, 255, 100), // Cube - Yellow
+ new Color(100, 255, 100), // Cylinder - Green
+ new Color(100, 255, 255), // Pyramid - Cyan
+ new Color(100, 150, 255), // RectangularBox - Blue
+ new Color(200, 150, 255), // 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(new Color(40, 40, 50));
+
+ // 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, new Color(80, 80, 100));
+
+ // 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 = new Point3D(-halfWidth, 0, -CELL_SIZE / 2);
+ final Point3D cornerB = new Point3D(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 = new Point3D(x, 0, 0);
+ createSolidShape(shapes, col, solidPos, color);
+ createLabel(shapes, solidPos, name, color);
+
+ // Row 1: Wireframe shapes
+ final Point3D wireframePos = new Point3D(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(
+ new Point3D(pos.x, SHAPE_RADIUS, pos.z),
+ new Point3D(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(
+ new Point3D(pos.x, -SHAPE_RADIUS, pos.z), // apex above
+ new Point3D(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(
+ new Point3D(pos.x, SHAPE_RADIUS, pos.z),
+ new Point3D(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(
+ new Point3D(pos.x, -SHAPE_RADIUS, pos.z), // apex above
+ new Point3D(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(
+ new Point3D(pos.x - SHAPE_RADIUS * 0.35, pos.y - SHAPE_RADIUS, pos.z - SHAPE_RADIUS * 0.35),
+ new Point3D(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(
+ new Point3D(pos.x, SHAPE_RADIUS, pos.z),
+ new Point3D(pos.x, -SHAPE_RADIUS, pos.z),
+ 8, appearance);
+ shapes.addShape(arrow);
+ break;
+
+ case 1: // Cone
+ final WireframeCone cone = new WireframeCone(
+ new Point3D(pos.x, -SHAPE_RADIUS, pos.z), // apex above
+ new Point3D(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(
+ new Point3D(pos.x, SHAPE_RADIUS, pos.z),
+ new Point3D(pos.x, -SHAPE_RADIUS, pos.z),
+ SHAPE_RADIUS * 0.7, SEGMENTS, appearance);
+ shapes.addShape(cylinder);
+ break;
+
+ case 4: // Pyramid
+ final WireframePyramid pyramid = new WireframePyramid(
+ new Point3D(pos.x, -SHAPE_RADIUS, pos.z), // apex above
+ new Point3D(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(
+ new Point3D(pos.x - SHAPE_RADIUS * 0.35, pos.y - SHAPE_RADIUS, pos.z - SHAPE_RADIUS * 0.35),
+ new Point3D(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 = new Point3D(pos.x, pos.y + LABEL_Y_OFFSET, pos.z);
+ final ForwardOrientedTextBlock label = new ForwardOrientedTextBlock(
+ labelPos, 8.0, 2, text, color);
+ shapes.addShape(label);
+ }
+
+ /**
+ * Creates orbiting light sources around the scene.
+ *
+ * @param viewPanel the view panel
+ * @param shapes the shape collection
+ */
+ private static void createOrbitingLights(final ViewPanel viewPanel, final ShapeCollection shapes) {
+ final Random random = new Random(42);
+ final List<OrbitingLight> orbitingLights = new ArrayList<>();
+
+ for (int i = 0; i < LIGHT_COUNT; i++) {
+ final Color color = new Color(
+ random.nextInt(256),
+ random.nextInt(256),
+ random.nextInt(256)
+ );
+
+ final double orbitRadius = 600 + random.nextInt(200);
+ final double speed = 0.01 + random.nextDouble() * 0.02;
+ final double angleOffset = random.nextDouble() * Math.PI * 2;
+ final double intensity = 2.0 + random.nextDouble() * 2.0;
+
+ final int axis = random.nextInt(3);
+ final double ellipseFactor = 0.7 + random.nextDouble() * 0.3;
+
+ final LightSource light = new LightSource(new Point3D(0, 0, CELL_SIZE / 2), color, intensity);
+ final LightSourceMarker marker = new LightSourceMarker(light.getPosition(), color);
+
+ viewPanel.getLightingManager().addLight(light);
+ shapes.addShape(marker);
+
+ orbitingLights.add(new OrbitingLight(
+ light, marker,
+ orbitRadius, speed, angleOffset, axis, ellipseFactor
+ ));
+ }
+
+ final MultiLightAnimator animator = new MultiLightAnimator(orbitingLights);
+ viewPanel.addFrameListener(animator);
+ }
+
+ /**
+ * Represents a light source that orbits around the scene center.
+ */
+ private static class OrbitingLight {
+ final LightSource light;
+ final LightSourceMarker marker;
+ final double orbitRadius;
+ final double speed;
+ final int axis;
+ final double ellipseFactor;
+ double angle;
+
+ OrbitingLight(final LightSource light, final LightSourceMarker marker,
+ final double orbitRadius, final double speed, final double angleOffset,
+ final int axis, final double ellipseFactor) {
+ this.light = light;
+ this.marker = marker;
+ this.orbitRadius = orbitRadius;
+ this.speed = speed;
+ this.angle = angleOffset;
+ this.axis = axis;
+ this.ellipseFactor = ellipseFactor;
+ }
+ }
+
+ /**
+ * Frame listener that animates all orbiting lights.
+ */
+ private record MultiLightAnimator(List<OrbitingLight> lights) implements FrameListener {
+
+ @Override
+ public boolean onFrame(final ViewPanel viewPanel, final int millisecondsSinceLastFrame) {
+ final double centerY = CELL_SIZE / 2;
+
+ for (final OrbitingLight orbitingLight : lights) {
+ orbitingLight.angle += orbitingLight.speed * millisecondsSinceLastFrame / 100;
+
+ final double r = orbitingLight.orbitRadius;
+ final double e = orbitingLight.ellipseFactor;
+
+ double x, y, z;
+ switch (orbitingLight.axis) {
+ case 0:
+ x = r * Math.cos(orbitingLight.angle);
+ y = r * e * Math.sin(orbitingLight.angle);
+ z = r * 0.5 * Math.sin(orbitingLight.angle * 2);
+ break;
+ case 1:
+ x = r * e * Math.sin(orbitingLight.angle * 2);
+ y = r * Math.cos(orbitingLight.angle);
+ z = r * 0.5 * Math.sin(orbitingLight.angle);
+ break;
+ default:
+ x = r * 0.5 * Math.sin(orbitingLight.angle);
+ y = r * Math.sin(orbitingLight.angle * 2);
+ z = r * e * Math.cos(orbitingLight.angle);
+ break;
+ }
+
+ final Point3D newPosition = new Point3D(x, y + centerY, z);
+ orbitingLight.light.setPosition(newPosition);
+ orbitingLight.marker.setTransform(fromAngles(
+ newPosition.x, newPosition.y, newPosition.z, 0, 0, 0));
+ }
+ return true;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ *
+ */
+package eu.svjatoslav.sixth.e3d.examples.essentials;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
+
+/**
+ * Demo to test winding order and backface culling documentation.
+ * <p>
+ * Creates one triangle with CCW winding (front face) following the docs:
+ * <ul>
+ * <li>upper-center → lower-left → lower-right</li>
+ * <li>Backface culling enabled</li>
+ * </ul>
+ * <p>
+ * Expected: green triangle visible (CCW = front face).
+ */
+public class WindingOrderDemo {
+
+ /**
+ * Creates a new WindingOrderDemo instance.
+ */
+ public WindingOrderDemo() {
+ }
+
+ /**
+ * Entry point for the winding order demo.
+ * @param args command line arguments (ignored)
+ */
+ public static void main(String[] args) {
+ ViewFrame viewFrame = new ViewFrame("Winding Order Demo");
+ ViewPanel viewPanel = viewFrame.getViewPanel();
+ ShapeCollection shapes = viewPanel.getRootShapeCollection();
+
+ viewPanel.getCamera().getTransform().setTranslation(new Point3D(0, 0, -500));
+
+ double size = 150;
+
+ Point3D upperCenter = new Point3D(0, -size, 0);
+ Point3D lowerLeft = new Point3D(-size, +size, 0);
+ Point3D lowerRight = new Point3D(+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
/*
- * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
* This project is released under Creative Commons Zero (CC0) license.
*
*/
package eu.svjatoslav.sixth.e3d.examples.launcher;
-import eu.svjatoslav.sixth.e3d.examples.terrain_demo.TerrainDemo;
-import eu.svjatoslav.sixth.e3d.examples.SineHeightmap;
-import eu.svjatoslav.sixth.e3d.examples.graph_demo.MathGraphsDemo;
-import eu.svjatoslav.sixth.e3d.examples.MinimalExample;
import eu.svjatoslav.sixth.e3d.examples.OctreeDemo;
-import eu.svjatoslav.sixth.e3d.examples.RandomPolygonsDemo;
import eu.svjatoslav.sixth.e3d.examples.RainingNumbersDemo;
-import eu.svjatoslav.sixth.e3d.examples.ShadedShapesDemo;
+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.AxisArrowsDemo;
+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 a vertical list with button and description for each demo.
+ * 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("Axis arrows",
+ "Coordinate system axes: X (red), Y (green), Z (blue)",
+ new ShowAxisArrows()),
+ 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 record DemoEntry(String name, String description, AbstractAction action) {}
-
- private static final DemoEntry[] DEMOS = {
- new DemoEntry("Minimal Example",
- "Minimal example showing a single red box",
- new ShowMinimalExample()),
- 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("Random polygons",
- "1000 semi-transparent triangles with depth sorting",
- new ShowRandomPolygons()),
- new DemoEntry("Shaded Shapes",
- "Shapes lit by orbiting colored light sources",
- new ShowShadedShapes()),
- 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()),
+ 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.ParallelGroup horizontalButtonGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING);
final GroupLayout.ParallelGroup horizontalDescGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING);
- final JLabel headerLabel = new JLabel("Choose an example to launch:");
- headerLabel.setFont(headerLabel.getFont().deriveFont(java.awt.Font.BOLD));
+ 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);
+ }
- for (final DemoEntry entry : DEMOS) {
+ /**
+ * 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 java.awt.Dimension(BUTTON_WIDTH, button.getPreferredSize().height));
+ button.setPreferredSize(new Dimension(BUTTON_WIDTH, button.getPreferredSize().height));
final JLabel descLabel = new JLabel(entry.description());
- descLabel.setFont(descLabel.getFont().deriveFont(java.awt.Font.PLAIN));
+ descLabel.setFont(descLabel.getFont().deriveFont(Font.PLAIN));
verticalGroup.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(button)
horizontalButtonGroup.addComponent(button);
horizontalDescGroup.addComponent(descLabel);
}
+ }
- layout.setVerticalGroup(verticalGroup);
- layout.setHorizontalGroup(layout.createSequentialGroup()
- .addGroup(horizontalButtonGroup)
- .addGap(GAP)
- .addGroup(horizontalDescGroup));
+ private record DemoEntry(String name, String description, AbstractAction action) {
}
private static class ShowMinimalExample extends AbstractAction {
}
}
+ 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");
}
}
- private static class ShowRandomPolygons extends AbstractAction {
- ShowRandomPolygons() {
- putValue(NAME, "Random polygons");
- }
-
- @Override
- public void actionPerformed(final ActionEvent e) {
- RandomPolygonsDemo.main(null);
- }
- }
-
private static class ShowOctree extends AbstractAction {
ShowOctree() {
- putValue(NAME, "Volumetric Octree");
+ putValue(NAME, "Volumetric octree");
}
@Override
private static class ShowShadedShapes extends AbstractAction {
ShowShadedShapes() {
- putValue(NAME, "Shaded Shapes");
+ putValue(NAME, "Shape gallery");
+ }
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ ShapeGalleryDemo.main(null);
+ }
+ }
+
+ private static class ShowAxisArrows extends AbstractAction {
+ ShowAxisArrows() {
+ putValue(NAME, "Axis arrows");
}
@Override
public void actionPerformed(final ActionEvent e) {
- ShadedShapesDemo.main(null);
+ AxisArrowsDemo.main(null);
}
}
private static class ShowTerrainDemo extends AbstractAction {
ShowTerrainDemo() {
- putValue(NAME, "Procedural Terrain");
+ putValue(NAME, "Procedural terrain");
}
@Override
private static class ShowGraphicsBenchmark extends AbstractAction {
ShowGraphicsBenchmark() {
- putValue(NAME, "Graphics Benchmark");
+ putValue(NAME, "Graphics benchmark");
}
@Override
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
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.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.textcanvas.TextCanvas;
import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid2D;
import java.awt.event.KeyEvent;
* Main entry point for Conway's Game of Life 3D demo.
* Creates a 30x30 cell matrix where cells evolve based on Conway's rules.
* The user can interact with cells by clicking to toggle their state.
- *
+ *
* <p>Key controls:
* <ul>
* <li>Space - Evolve one generation</li>
public class Main extends WorldNavigationUserInputTracker {
/**
- * Creates a new Main instance for the Game of Life demo.
+ * The game of life matrix, centered at the origin.
*/
- public Main() {
- }
-
- /** The game of life matrix, centered at the origin. */
private static final Matrix MATRIX = new Matrix(
new Point3D() // position matrix in the center of the scene
);
+ /**
+ * 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) {
return true;
}
- /** Initializes and displays the Game of Life demo. */
+ /**
+ * 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(100, -50, -200, 0.2f, -0.7f, 0);
+ 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());
/**
* Creates a pink wire-frame grid below the matrix for decorative purposes.
+ *
* @return a Grid2D positioned below the game matrix
*/
private Grid2D createGrid() {
);
}
+ /**
+ * 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(
+ new Point3D(500, -80, 0),
+ -Math.PI / 2,
+ 0
+ );
+
+ final TextPointer dimensions = new TextPointer(16, 45);
+
+ final TextCanvas panel = new TextCanvas(
+ location,
+ dimensions,
+ Color.WHITE,
+ new Color(0, 0, 80, 200)
+ );
+
+ panel.setText(helpText);
+
+ return panel;
+ }
+
}
* 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. */
+ /**
+ * Empty space between adjacent cells.
+ */
private static final int BORDER = 5;
- /** Number of cells along each dimension (30x30 grid). */
+ /**
+ * Number of cells along each dimension (30x30 grid).
+ */
private static final int SIZE = 30;
- /** Group ID for history tracking stars. */
+ /**
+ * Group ID for history-tracking stars.
+ */
private static final String GROUP_STARS = "stars";
- /** Group ID for the cell surface. */
+ /**
+ * Group ID for the cell surface.
+ */
private static final String GROUP_SURFACE = "surface";
- /** 2D array of cells forming the game board. */
+ /**
+ * 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) {
/**
* 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
*/
/**
* 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
/**
* 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
/**
* Evolves the matrix by one generation.
+ *
* @param preserveHistory if true, places stars at positions of previously active cells
*/
public void evolve(final boolean preserveHistory) {
/**
* 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
* @param args command line arguments (ignored)
*/
public static void main(final String[] args) {
- final ViewFrame viewFrame = new ViewFrame("Procedural Terrain");
+ 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);