--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ *
+*/
+
+package eu.svjatoslav.sixth.e3d.examples.launcher;
+
+import eu.svjatoslav.sixth.e3d.examples.*;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+
+class ApplicationListPanel extends JPanel {
+ private static final long serialVersionUID = 2012721856427052560L;
+
+ ApplicationListPanel() {
+ final GroupLayout groupLayout = new GroupLayout(this);
+ GroupLayout.SequentialGroup sequentialGroup = groupLayout.createSequentialGroup();
+ sequentialGroup.addComponent(new JLabel("Choose an example to launch:"));
+ sequentialGroup.addComponent(new JButton(new ShowOctree()));
+ sequentialGroup.addComponent(new JButton(new ShowMathGraphs()));
+ sequentialGroup.addComponent(new JButton(new ShowPointCloud()));
+ sequentialGroup.addComponent(new JButton(new ShowRain()));
+ sequentialGroup.addComponent(new JButton(new ShowTextEditors()));
+ sequentialGroup.addComponent(new JButton(new ShowTextEditors2()));
+ sequentialGroup.addComponent(new JButton(new ShowGameOfLife()));
+ sequentialGroup.addComponent(new JButton(new ShowRandomPolygons()));
+ }
+
+ 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) {
+ GraphDemo.main(null);
+ }
+ }
+
+ 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");
+ }
+
+ @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);
+ }
+ }
+
+}
import javax.swing.*;
import java.awt.*;
-class Main extends javax.swing.JFrame {
-
- private static final long serialVersionUID = -3679656169594556137L;
-
- private Main() {
- super();
- initGUI();
- }
+class Main {
public static void main(final String[] args) {
- SwingUtilities.invokeLater(() -> {
- final Main inst = new Main();
- final BorderLayout instLayout = new BorderLayout();
- inst.setLocationRelativeTo(null);
- inst.setVisible(true);
- inst.getContentPane().setLayout(instLayout);
- });
+ buildAndShowGuiWindow();
}
- private void initGUI() {
- getContentPane().add(new MenuPanel());
- pack();
- setSize(390, 300);
+ private static void buildAndShowGuiWindow() {
+ JFrame frame = new JFrame("Sixth 3D engine demos");
+
+ // Keep application running until last frame is closed.
+ frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+
+ frame.getContentPane().setLayout(new BorderLayout());
+ frame.getContentPane().add(new ApplicationListPanel(), BorderLayout.CENTER);
+ frame.setSize(400, 300);
+
+ frame.setLocationRelativeTo(null); // center frame on screen
+ frame.setVisible(true);
}
}
+++ /dev/null
-/*
- * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
- * This project is released under Creative Commons Zero (CC0) license.
- *
-*/
-
-package eu.svjatoslav.sixth.e3d.examples.launcher;
-
-import eu.svjatoslav.sixth.e3d.examples.*;
-
-import javax.swing.*;
-import java.awt.event.ActionEvent;
-
-class MenuPanel extends JPanel {
- private static final long serialVersionUID = 2012721856427052560L;
-
- MenuPanel() {
- final GroupLayout groupLayout = new GroupLayout(this);
- GroupLayout.SequentialGroup sequentialGroup = groupLayout.createSequentialGroup();
- sequentialGroup.addComponent(new JLabel("Choose an example to launch:"));
- sequentialGroup.addComponent(new JButton(new ShowOctree()));
- sequentialGroup.addComponent(new JButton(new ShowMathGraphs()));
- sequentialGroup.addComponent(new JButton(new ShowPointCloud()));
- sequentialGroup.addComponent(new JButton(new ShowRain()));
- sequentialGroup.addComponent(new JButton(new ShowTextEditors()));
- sequentialGroup.addComponent(new JButton(new ShowTextEditors2()));
- sequentialGroup.addComponent(new JButton(new ShowGameOfLife()));
- sequentialGroup.addComponent(new JButton(new ShowRandomPolygons()));
- }
-
- 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, "Pointcloud 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) {
- GraphDemo.main(null);
- }
- }
-
- 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");
- }
-
- @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.Main.main(null);
- }
- }
-
-}
+++ /dev/null
-package eu.svjatoslav.sixth.e3d.examples.life;
-
-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;
-
-/**
- * This class corresponds to a single cell within matrix.
- */
-class Cell extends AbstractCompositeShape implements
- MouseInteractionController {
-
- /**
- * cell visual size
- */
- static final int SIZE = 20;
- /**
- * Color of the active cell (R, G, B, A)
- */
- private static final Color ACTIVE_COLOR = new Color("A8FF");
- /**
- * Color of the active cell (R, G, B, A) while mouse is over it.
- */
- private static final Color ACTIVE_COLOR_MOUSE_OVER = new Color("F9FF");
- /**
- * Color of the inactive cell (R, G, B, A)
- */
- private static final Color INACTIVE_COLOR = new Color("55F8");
- /**
- * Color of the inactive cell (R, G, B, A) while mouse is over it.
- */
- private static final Color INACTIVE_COLOR_MOUSE_OVER = new Color("77F8");
- /**
- * A placeholder variable to help in next generation computation. Indicates
- * whether cell is going to survive within next generation.
- */
- public boolean survives;
- /**
- * Indicates whether cell is currently active
- */
- private boolean active;
- /**
- * Indicates whether mouse pointer is currently over this cell.
- */
- private boolean isMouseOver = false;
-
- public Cell(final Point3D center) {
- super(center);
-
- createCellShape();
-
- // enable receiving of mouse events
- setMouseInteractionController(this);
- }
-
- 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()));
- }
-
- /**
- * Compute cell color depending if cell is active and if mouse is over the
- * cell.
- */
- 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;
- }
-
- public boolean isActive() {
- return active;
- }
-
- public void setActive(final boolean active) {
- this.active = active;
- updateColor();
- }
-
- @Override
- public boolean mouseClicked() {
- setActive(!isActive());
- return true;
- }
-
- @Override
- public boolean mouseEntered() {
- setMouseOver(true);
- return true;
- }
-
- @Override
- public boolean mouseExited() {
- setMouseOver(false);
- return true;
- }
-
- private void setMouseOver(final boolean isMouseOver) {
- this.isMouseOver = isMouseOver;
- updateColor();
- }
-
- /**
- * This method is called when cell status is changed to update its color
- * too.
- */
- private void updateColor() {
- setColor(computeCellColor());
- }
-
-}
+++ /dev/null
-package eu.svjatoslav.sixth.e3d.examples.life;
-
-import eu.svjatoslav.sixth.e3d.geometry.Point3D;
-import eu.svjatoslav.sixth.e3d.geometry.Rectangle;
-import eu.svjatoslav.sixth.e3d.gui.Avatar;
-import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
-import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
-import eu.svjatoslav.sixth.e3d.gui.humaninput.WorldNavigationUserInputTracker;
-import eu.svjatoslav.sixth.e3d.math.Transform;
-import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
-import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid2D;
-
-import java.awt.event.KeyEvent;
-
-
-public class Main extends WorldNavigationUserInputTracker {
-
- private static final Matrix MATRIX = new Matrix(
- new Point3D() // position matrix in the center of the scene
- );
-
- 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;
- }
-
- private void run() {
-
- // create application frame visible to the user
- final ViewFrame viewFrame = new ViewFrame();
-
- final ShapeCollection shapeCollection = viewFrame.getViewPanel()
- .getRootShapeCollection();
-
- // add matrix
- shapeCollection.addShape(MATRIX);
-
- // add wire-frame grid (optional)
- shapeCollection.addShape(createGrid());
-
- final ViewPanel viewPanel = viewFrame.getViewPanel();
-
- setAvatarOrientation(viewPanel.getAvatar());
-
- // enable receiving of keyboard events
- viewPanel.getKeyboardFocusStack().pushFocusOwner(this);
-
- // Done! World is built. So ensure screen is updated too.
- viewPanel.repaintDuringNextViewUpdate();
- }
-
- /**
- * Create pink wire-frame grid below (for decorative purposes).
- */
- private Grid2D createGrid() {
- return new Grid2D(
- new Transform(
- new Point3D( // Grid positioning:
- 0, // center
- 100, // below the main scene
- 0), // center
-
- // Grid orientation:
- 0, // no rotation along XZ axis
- Math.PI / 2), // face down
-
- new Rectangle(800), // large enough, square grid
-
- 5, 5, // grid will be divided to 5x5 segments
-
- new LineAppearance(3, // line thickness
- new Color("FF000050") // red and quite transparent
- )
- );
- }
-
- /**
- * Set Avatar/Camera initial position in the world.
- */
- private void setAvatarOrientation(final Avatar avatar) {
- avatar.setLocation(new Point3D(100, -50, -200));
- avatar.setAngleXZ(0.2f);
- avatar.setAngleYZ(-0.7f);
- }
-
-}
+++ /dev/null
-package eu.svjatoslav.sixth.e3d.examples.life;
-
-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;
-
-/**
- * This is our 2D game of life world. It contains 2D array of {@link Cell}'s.
- */
-class Matrix extends AbstractCompositeShape {
-
- /**
- * Empty space between cells.
- */
- private static final int BORDER = 5;
-
- /**
- * Matrix size X and Y. (Amount of cells.)
- */
- private static final int SIZE = 30;
-
- /**
- * Object marker in 3D scene. Allows to locate all stars from the scene.
- */
- private static final String GROUP_STARS = "stars";
-
- /**
- * Object marker in 3D scene. Allows to locate surface on the scene.
- */
- private static final String GROUP_SURFACE = "surface";
-
- /**
- * 2 dimensional matrix of cells
- */
- private final Cell[][] cells = new Cell[SIZE][];
-
- 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);
- }
-
- /**
- * Clear matrix.
- */
- 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);
- }
-
- /**
- * Compute survived cells based on the rules of Conway's Game of Life.
- */
- 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);
-
- }
-
- 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;
- }
- }
-
- 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;
- }
-
- 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();
- }
-
- /**
- * Evolve matrix for one iteration
- */
- public void evolve(final boolean preserveHistory) {
- if (preserveHistory)
- markActiveCells();
-
- shiftStarsUp();
-
- computeSurvivedCells();
- }
-
- 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);
- }
-
- /**
- * Leave trail of active cells as stars above matrix.
- */
- 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);
- }
-
- /**
- * Find all history tracking stars and shift them up.
- */
- private void shiftStarsUp() {
-
- for (final SubShape subShape : getGroup(GROUP_STARS))
- ((Star) subShape.getShape()).getLocation().translateY(-10);
- }
-}
+++ /dev/null
-package eu.svjatoslav.sixth.e3d.examples.life;
-
-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;
-
-class Star extends GlowingPoint {
- public static final int STAR_SIZE = 10;
-
- public static final int UNIQUE_STARS_COUNT = 30;
-
- private static final List<Color> uniqueStarColors = new ArrayList<>();
-
- /**
- * A little hack to save RAM. We are going to have potentially lot of stars.
- * Instead of creating new individual texture for each star, Sixth 3D engine
- * uses internal optimization and reuses existing star textures, if star with
- * identical color already exists. To take advantage ot such optimization
- * we create here limited set of precomputed star colors and later reuse them.
- */
- 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));
- }
-
- public Star(Point3D location) {
- super(location,
- STAR_SIZE,
- uniqueStarColors.get((int) (Math.random() * uniqueStarColors.size())) // pick random pre-generated color
- );
- }
-
-}
--- /dev/null
+package eu.svjatoslav.sixth.e3d.examples.life_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+
+/**
+ * This class corresponds to a single cell within matrix.
+ */
+class Cell extends AbstractCompositeShape implements
+ MouseInteractionController {
+
+ /**
+ * cell visual size
+ */
+ static final int SIZE = 20;
+ /**
+ * Color of the active cell (R, G, B, A)
+ */
+ private static final Color ACTIVE_COLOR = new Color("A8FF");
+ /**
+ * Color of the active cell (R, G, B, A) while mouse is over it.
+ */
+ private static final Color ACTIVE_COLOR_MOUSE_OVER = new Color("F9FF");
+ /**
+ * Color of the inactive cell (R, G, B, A)
+ */
+ private static final Color INACTIVE_COLOR = new Color("55F8");
+ /**
+ * Color of the inactive cell (R, G, B, A) while mouse is over it.
+ */
+ private static final Color INACTIVE_COLOR_MOUSE_OVER = new Color("77F8");
+ /**
+ * A placeholder variable to help in next generation computation. Indicates
+ * whether cell is going to survive within next generation.
+ */
+ public boolean survives;
+ /**
+ * Indicates whether cell is currently active
+ */
+ private boolean active;
+ /**
+ * Indicates whether mouse pointer is currently over this cell.
+ */
+ private boolean isMouseOver = false;
+
+ public Cell(final Point3D center) {
+ super(center);
+
+ createCellShape();
+
+ // enable receiving of mouse events
+ setMouseInteractionController(this);
+ }
+
+ 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()));
+ }
+
+ /**
+ * Compute cell color depending if cell is active and if mouse is over the
+ * cell.
+ */
+ 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;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void setActive(final boolean active) {
+ this.active = active;
+ updateColor();
+ }
+
+ @Override
+ public boolean mouseClicked() {
+ setActive(!isActive());
+ return true;
+ }
+
+ @Override
+ public boolean mouseEntered() {
+ setMouseOver(true);
+ return true;
+ }
+
+ @Override
+ public boolean mouseExited() {
+ setMouseOver(false);
+ return true;
+ }
+
+ private void setMouseOver(final boolean isMouseOver) {
+ this.isMouseOver = isMouseOver;
+ updateColor();
+ }
+
+ /**
+ * This method is called when cell status is changed to update its color
+ * too.
+ */
+ private void updateColor() {
+ setColor(computeCellColor());
+ }
+
+}
--- /dev/null
+package eu.svjatoslav.sixth.e3d.examples.life_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.geometry.Rectangle;
+import eu.svjatoslav.sixth.e3d.gui.Avatar;
+import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
+import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.WorldNavigationUserInputTracker;
+import eu.svjatoslav.sixth.e3d.math.Transform;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.Grid2D;
+
+import java.awt.event.KeyEvent;
+
+
+public class Main extends WorldNavigationUserInputTracker {
+
+ private static final Matrix MATRIX = new Matrix(
+ new Point3D() // position matrix in the center of the scene
+ );
+
+ 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;
+ }
+
+ private void run() {
+
+ // create application frame visible to the user
+ final ViewFrame viewFrame = new ViewFrame();
+
+ final ShapeCollection shapeCollection = viewFrame.getViewPanel()
+ .getRootShapeCollection();
+
+ // add matrix
+ shapeCollection.addShape(MATRIX);
+
+ // add wire-frame grid (optional)
+ shapeCollection.addShape(createGrid());
+
+ final ViewPanel viewPanel = viewFrame.getViewPanel();
+
+ setAvatarOrientation(viewPanel.getAvatar());
+
+ // enable receiving of keyboard events
+ viewPanel.getKeyboardFocusStack().pushFocusOwner(this);
+
+ // Done! World is built. So ensure screen is updated too.
+ viewPanel.repaintDuringNextViewUpdate();
+ }
+
+ /**
+ * Create pink wire-frame grid below (for decorative purposes).
+ */
+ private Grid2D createGrid() {
+ return new Grid2D(
+ new Transform(
+ new Point3D( // Grid positioning:
+ 0, // center
+ 100, // below the main scene
+ 0), // center
+
+ // Grid orientation:
+ 0, // no rotation along XZ axis
+ Math.PI / 2), // face down
+
+ new Rectangle(800), // large enough, square grid
+
+ 5, 5, // grid will be divided to 5x5 segments
+
+ new LineAppearance(3, // line thickness
+ new Color("FF000050") // red and quite transparent
+ )
+ );
+ }
+
+ /**
+ * Set Avatar/Camera initial position in the world.
+ */
+ private void setAvatarOrientation(final Avatar avatar) {
+ avatar.setLocation(new Point3D(100, -50, -200));
+ avatar.setAngleXZ(0.2f);
+ avatar.setAngleYZ(-0.7f);
+ }
+
+}
--- /dev/null
+package eu.svjatoslav.sixth.e3d.examples.life_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.SubShape;
+
+/**
+ * This is our 2D game of life world. It contains 2D array of {@link Cell}'s.
+ */
+class Matrix extends AbstractCompositeShape {
+
+ /**
+ * Empty space between cells.
+ */
+ private static final int BORDER = 5;
+
+ /**
+ * Matrix size X and Y. (Amount of cells.)
+ */
+ private static final int SIZE = 30;
+
+ /**
+ * Object marker in 3D scene. Allows to locate all stars from the scene.
+ */
+ private static final String GROUP_STARS = "stars";
+
+ /**
+ * Object marker in 3D scene. Allows to locate surface on the scene.
+ */
+ private static final String GROUP_SURFACE = "surface";
+
+ /**
+ * 2 dimensional matrix of cells
+ */
+ private final Cell[][] cells = new Cell[SIZE][];
+
+ 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);
+ }
+
+ /**
+ * Clear matrix.
+ */
+ 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);
+ }
+
+ /**
+ * Compute survived cells based on the rules of Conway's Game of Life.
+ */
+ 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);
+
+ }
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+
+ 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();
+ }
+
+ /**
+ * Evolve matrix for one iteration
+ */
+ public void evolve(final boolean preserveHistory) {
+ if (preserveHistory)
+ markActiveCells();
+
+ shiftStarsUp();
+
+ computeSurvivedCells();
+ }
+
+ 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);
+ }
+
+ /**
+ * Leave trail of active cells as stars above matrix.
+ */
+ 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);
+ }
+
+ /**
+ * Find all history tracking stars and shift them up.
+ */
+ private void shiftStarsUp() {
+
+ for (final SubShape subShape : getGroup(GROUP_STARS))
+ ((Star) subShape.getShape()).getLocation().translateY(-10);
+ }
+}
--- /dev/null
+package eu.svjatoslav.sixth.e3d.examples.life_demo;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.GlowingPoint;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class Star extends GlowingPoint {
+ public static final int STAR_SIZE = 10;
+
+ public static final int UNIQUE_STARS_COUNT = 30;
+
+ private static final List<Color> uniqueStarColors = new ArrayList<>();
+
+ /*
+ * A little hack to save RAM. We are going to have potentially lot of stars.
+ * Instead of creating new individual texture for each star, Sixth 3D engine
+ * uses internal optimization and reuses existing star textures, if star with
+ * identical color already exists. To take advantage ot such optimization
+ * we create here limited set of precomputed star colors and later reuse them.
+ */
+ 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));
+ }
+
+ public Star(Point3D location) {
+ super(location,
+ STAR_SIZE,
+ uniqueStarColors.get((int) (Math.random() * uniqueStarColors.size())) // pick random pre-generated color
+ );
+ }
+
+}