From: Svjatoslav Agejenko Date: Sat, 15 Jul 2017 09:29:31 +0000 (+0200) Subject: Updated documentation. Added game of life. Refactoring launcher panel. X-Git-Url: http://www2.svjatoslav.eu/gitweb/?p=sixth-3d-demos.git;a=commitdiff_plain;h=151ac55eaaa0f3a2d7a589a0a3ff877b6db09a6f Updated documentation. Added game of life. Refactoring launcher panel. --- diff --git a/doc/index.html b/doc/index.html index 6423947..db7078a 100644 --- a/doc/index.html +++ b/doc/index.html @@ -2,7 +2,7 @@ Sixth 3D engine demos - + @@ -226,35 +226,296 @@ published by the Free Software Foundation.
-

1 Description

+

1 Overview

-Goal of this project is to show off capabilities of Sixth 3D -engine. Also to show examples of its usage. +Goal of this project is to show off capabilities and API usage of +Sixth 3D engine.

+

+All sample scenes below are rendered at interactive framerates. +

+
+
+
+

2 Navigating in space

+
+ + + +++ ++ + + + + + + + + + + + + + + + + + + + + + + +
keyresult
cursor keysmove: left, right, forward, backward
mouse scroll wheelmove: up, down
dragging with mouselook around
+
+
+ +
+

3 Samples

+
+
+

3.1 Raytracing through voxels

+
-

screenshots.png +

raytracing fractal in voxel polygon hybrid scene.png

-Sample scenes rendered at interactive framerates by Sixth 3D engine. +Test scene that is generated simultaneously using: +

+
    +
  • conventional polygons +
      +
    • for realtime navigation, and +
    • +
    +
  • +
  • voxels +
      +
    • for on-demand raytracing +
    • +
    +
  • +
+ +

+Instead of storing voxels in dumb [X * Y * Z] array, dynamically +partitioned octree is used to compress data. Press "r" key anywhere in +the scene to raytrace current view through compressed voxel +datastructure.

+ +
+

3.2 Conway's Game of Life

+
+

+The Game of Life, also known simply as Life, is a cellular automaton +devised by the British mathematician John Horton Conway in 1970. +

+ +
    +
  • https://en.wikipedia.org/wiki/Conway's_Game_of_Life +
      +
    • Game rules: +
        +
      • 2 cell states: alive / dead +
      • +
      • Each cell sees 8 neighboring cells. +
      • +
      • If alive cell neighbors count is 2 or 3, then cell survives, +otherwise it dies. +
      • +
      • Dead cell becomes alive if neighbors count is exactly 3. +
      • +
      +
    • +
    +
  • +
+ + +
+

life.png +

+
+ +

+Current application projects 2D game grid/matrix onto three +dimensional space. Extra dimension (height) is used to visualize +history (previous iterations) using glowing dots suspended in space. +

+ +

+Usage: +

+ + + +++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
keyresult
mouse click on the cell (cell)toggles cell state
<space>next iteration
ENTERnext iteeration with the history
"c"clear the matrix
+
+
+ +
+

3.3 Text editors

+
+ +
+

text editors.png +

+
+ +

+Initial test for creating user interfaces in 3D and: +

+
    +
  • window focus handling +
  • +
  • picking objecs using mouse +
  • +
  • redirecting keyboard input to focused window +
  • +
+ + +

+Window focus acts like a stack. +

+ +

+When window is clicked with the mouse, previously focused window (if +any) is pushed to the focus stack and new window receives focus. Red +frame appears around the window to indicate this. +

+ +

+When ESC key is pressed, window focus is returned to previous window +(if any). +

+ +

+When any window is focused, all keyboard input is redirected to that +window, including cursor keys. To be able to navigate around the world +again, window must be unfocused first using ESC key. +

+ + +
    +
  • TODO: +
      +
    • Improve focus handling: +
        +
      • Perhaps add shortcut to navigate world without exiting entire +stack of focus. +
      • +
      • Possibility to retain and reuse recently focused elements. +
      • +
      • Store user location in the world and view direction with the +focused window. So that when returning focus to far away object, +user is redirected also to proper location in the world. +
      • +
      +
    • +
    • Possibility to store recently visited locations in the world and +return to them. +
    • +
    +
  • +
+
+
+
+

3.4 Mathematical formulas

+
+ +
+

mathematical formulas.png +

+
+ +
    +
  • TODO: instead of projecting 2D visualizations onto 3D space, +visualize some formula using all 3 dimensions avaliable. +
  • +
+
+
+
+

3.5 Sinus heightmaps and sphere

+
+ +
+

sinus heightmaps and sphere.png +

+
+ +

+Simple test scene. Easy to implement and looks nice. +

+
+
+
diff --git a/doc/index.org b/doc/index.org index 1333873..1375cac 100644 --- a/doc/index.org +++ b/doc/index.org @@ -27,10 +27,98 @@ #+HTML_HEAD: pre {background-color: #111; color: #ccc;} #+HTML_HEAD: -* Description -Goal of this project is to show off capabilities of [[http://www2.svjatoslav.eu/gitbrowse/sixth-3d/doc/index.html][Sixth 3D]] -engine. Also to show examples of its usage. +* Overview +Goal of this project is to show off capabilities and API usage of +[[http://www2.svjatoslav.eu/gitbrowse/sixth-3d/doc/index.html][Sixth 3D]] engine. -[[file:screenshots/index.html][file:screenshots.png]] +All sample scenes below are rendered at interactive framerates. +* Navigating in space +| key | result | +|--------------------------------+--------------------------------------| +| cursor keys | move: left, right, forward, backward | +| mouse scroll wheel | move: up, down | +| dragging with mouse | look around | -Sample scenes rendered at interactive framerates by Sixth 3D engine. +* Samples +** Raytracing through voxels +[[file:screenshots/raytracing fractal in voxel polygon hybrid scene.png]] + +Test scene that is generated simultaneously using: ++ conventional polygons + + for realtime navigation, and ++ voxels + + for on-demand raytracing + +Instead of storing voxels in dumb [X * Y * Z] array, dynamically +partitioned [[https://en.wikipedia.org/wiki/Octree][octree]] is used to compress data. Press "r" key anywhere in +the scene to raytrace current view through compressed voxel +datastructure. + +** Conway's Game of Life +The Game of Life, also known simply as Life, is a cellular automaton +devised by the British mathematician John Horton Conway in 1970. + ++ https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life + + Game rules: + + 2 cell states: alive / dead + + Each cell sees 8 neighboring cells. + + If alive cell neighbors count is 2 or 3, then cell survives, + otherwise it dies. + + Dead cell becomes alive if neighbors count is exactly 3. + +[[file:screenshots/life.png]] + +Current application projects 2D game grid/matrix onto three +dimensional space. Extra dimension (height) is used to visualize +history (previous iterations) using glowing dots suspended in space. + +Usage: +| key | result | +|--------------------------------+--------------------------------------| +| mouse click on the cell (cell) | toggles cell state | +| | next iteration | +| ENTER | next iteeration with the history | +| "c" | clear the matrix | + +** Text editors +[[file:screenshots/text editors.png]] + +Initial test for creating user interfaces in 3D and: ++ window focus handling ++ picking objecs using mouse ++ redirecting keyboard input to focused window + + +Window focus acts like a stack. + +When window is clicked with the mouse, previously focused window (if +any) is pushed to the focus stack and new window receives focus. Red +frame appears around the window to indicate this. + +When ESC key is pressed, window focus is returned to previous window +(if any). + +When any window is focused, all keyboard input is redirected to that +window, including cursor keys. To be able to navigate around the world +again, window must be unfocused first using ESC key. + + ++ TODO: + + Improve focus handling: + + Perhaps add shortcut to navigate world without exiting entire + stack of focus. + + Possibility to retain and reuse recently focused elements. + + Store user location in the world and view direction with the + focused window. So that when returning focus to far away object, + user is redirected also to proper location in the world. + + Possibility to store recently visited locations in the world and + return to them. +** Mathematical formulas +[[file:screenshots/mathematical formulas.png]] + ++ TODO: instead of projecting 2D visualizations onto 3D space, + visualize some formula using all 3 dimensions avaliable. +** Sinus heightmaps and sphere +[[file:screenshots/sinus heightmaps and sphere.png]] + +Simple test scene. Easy to implement and looks nice. diff --git a/doc/screenshots/.thumbnails/another test scene (5362752B).jpeg b/doc/screenshots/.thumbnails/another test scene (5362752B).jpeg deleted file mode 100644 index 68e88df..0000000 Binary files a/doc/screenshots/.thumbnails/another test scene (5362752B).jpeg and /dev/null differ diff --git a/doc/screenshots/.thumbnails/another test scene (62C8D31D).jpeg b/doc/screenshots/.thumbnails/another test scene (62C8D31D).jpeg deleted file mode 100644 index 37b2943..0000000 Binary files a/doc/screenshots/.thumbnails/another test scene (62C8D31D).jpeg and /dev/null differ diff --git a/doc/screenshots/.thumbnails/mathematical formulas (7454DC19).jpeg b/doc/screenshots/.thumbnails/mathematical formulas (7454DC19).jpeg deleted file mode 100644 index edd43e5..0000000 Binary files a/doc/screenshots/.thumbnails/mathematical formulas (7454DC19).jpeg and /dev/null differ diff --git a/doc/screenshots/.thumbnails/mathematical formulas (82926553).jpeg b/doc/screenshots/.thumbnails/mathematical formulas (82926553).jpeg deleted file mode 100644 index 83db0ac..0000000 Binary files a/doc/screenshots/.thumbnails/mathematical formulas (82926553).jpeg and /dev/null differ diff --git a/doc/screenshots/.thumbnails/metadata_6.dat b/doc/screenshots/.thumbnails/metadata_6.dat deleted file mode 100644 index d041cab..0000000 Binary files a/doc/screenshots/.thumbnails/metadata_6.dat and /dev/null differ diff --git a/doc/screenshots/.thumbnails/raytracing fractal in voxel polygon hybrid scene (1F22E82D).jpeg b/doc/screenshots/.thumbnails/raytracing fractal in voxel polygon hybrid scene (1F22E82D).jpeg deleted file mode 100644 index 774f66a..0000000 Binary files a/doc/screenshots/.thumbnails/raytracing fractal in voxel polygon hybrid scene (1F22E82D).jpeg and /dev/null differ diff --git a/doc/screenshots/.thumbnails/raytracing fractal in voxel polygon hybrid scene (534B3488).jpeg b/doc/screenshots/.thumbnails/raytracing fractal in voxel polygon hybrid scene (534B3488).jpeg deleted file mode 100644 index f385768..0000000 Binary files a/doc/screenshots/.thumbnails/raytracing fractal in voxel polygon hybrid scene (534B3488).jpeg and /dev/null differ diff --git a/doc/screenshots/.thumbnails/text editors (C0263A2).jpeg b/doc/screenshots/.thumbnails/text editors (C0263A2).jpeg deleted file mode 100644 index f8543d5..0000000 Binary files a/doc/screenshots/.thumbnails/text editors (C0263A2).jpeg and /dev/null differ diff --git a/doc/screenshots/.thumbnails/text editors (CBB27271).jpeg b/doc/screenshots/.thumbnails/text editors (CBB27271).jpeg deleted file mode 100644 index 59b35ea..0000000 Binary files a/doc/screenshots/.thumbnails/text editors (CBB27271).jpeg and /dev/null differ diff --git a/doc/screenshots/another test scene.png b/doc/screenshots/another test scene.png deleted file mode 100644 index 9ddf563..0000000 Binary files a/doc/screenshots/another test scene.png and /dev/null differ diff --git a/doc/screenshots/index.html b/doc/screenshots/index.html deleted file mode 100644 index c07b289..0000000 --- a/doc/screenshots/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - -

Gallery

-

- - - -

-
-
-another test scene
(1680x1026, 840x513) -
-
-mathematical formulas
(1680x1026, 840x513) -
-
-raytracing fractal in voxel polygon hybrid scene
(1680x1026, 840x513) -
-
-text editors
(1672x977, 836x488) -
diff --git a/doc/screenshots/life.png b/doc/screenshots/life.png new file mode 100644 index 0000000..dbdc2dd Binary files /dev/null and b/doc/screenshots/life.png differ diff --git a/doc/screenshots/sinus heightmaps and sphere.png b/doc/screenshots/sinus heightmaps and sphere.png new file mode 100644 index 0000000..9ddf563 Binary files /dev/null and b/doc/screenshots/sinus heightmaps and sphere.png differ diff --git a/pom.xml b/pom.xml index 808d8d3..d58b8b2 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ eu.svjatoslav sixth-3d - 1.0 + 1.1 diff --git a/sixth-3d-demos.iml b/sixth-3d-demos.iml index a81cbb2..e629421 100644 --- a/sixth-3d-demos.iml +++ b/sixth-3d-demos.iml @@ -11,7 +11,7 @@ - + diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/MenuPanel.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/MenuPanel.java index e77b53f..1f868b9 100644 --- a/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/MenuPanel.java +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/launcher/MenuPanel.java @@ -21,33 +21,15 @@ class MenuPanel extends JPanel { public MenuPanel() { - final JLabel lblNewLabel = new JLabel("Choose an example to launch."); + final JLabel chooseExample = new JLabel("Choose an example to launch."); - final JButton btnNewButton = new JButton("Demo 1"); - btnNewButton.addActionListener(e -> { - }); - Action action = new SwingAction(); - btnNewButton.setAction(action); + final JButton showTextEditors = new JButton(new ShowTextEditors()); + final JButton showSinusMap = new JButton(new ShowSinusMap()); + final JButton showRain = new JButton(new ShowRain()); + final JButton showPointCloud = new JButton(new ShowPointcloud()); + final JButton showMathGraphs = new JButton(new ShowMathGraphs()); + final JButton showOctree = new JButton(new ShowOctree()); - final JButton btnNewButton_1 = new JButton("Demo 2"); - Action action_1 = new SwingAction_1(); - btnNewButton_1.setAction(action_1); - - final JButton button = new JButton("New button"); - Action action_2 = new SwingAction_2(); - button.setAction(action_2); - - final JButton btnNewButton_2 = new JButton("New button"); - Action action_3 = new SwingAction_3(); - btnNewButton_2.setAction(action_3); - - final JButton btnNewButton_3 = new JButton("New button"); - Action action_4 = new SwingAction_4(); - btnNewButton_3.setAction(action_4); - - final JButton btnNewButton_4 = new JButton("New button"); - Action action_5 = new SwingAction_5(); - btnNewButton_4.setAction(action_5); final GroupLayout groupLayout = new GroupLayout(this); groupLayout .setHorizontalGroup(groupLayout @@ -65,7 +47,7 @@ class MenuPanel extends JPanel { groupLayout .createSequentialGroup() .addComponent( - lblNewLabel, + chooseExample, GroupLayout.PREFERRED_SIZE, 426, GroupLayout.PREFERRED_SIZE) @@ -81,37 +63,37 @@ class MenuPanel extends JPanel { .createParallelGroup( Alignment.TRAILING) .addComponent( - btnNewButton_4, + showOctree, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 331, Short.MAX_VALUE) .addComponent( - btnNewButton_3, + showMathGraphs, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 331, Short.MAX_VALUE) .addComponent( - btnNewButton_2, + showPointCloud, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 331, Short.MAX_VALUE) .addComponent( - button, + showRain, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 331, Short.MAX_VALUE) .addComponent( - btnNewButton_1, + showSinusMap, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent( - btnNewButton, + showTextEditors, GroupLayout.DEFAULT_SIZE, 331, Short.MAX_VALUE)) @@ -121,31 +103,30 @@ class MenuPanel extends JPanel { groupLayout .createSequentialGroup() .addGap(7) - .addComponent(lblNewLabel, GroupLayout.PREFERRED_SIZE, + .addComponent(chooseExample, GroupLayout.PREFERRED_SIZE, 58, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.RELATED) - .addComponent(btnNewButton) + .addComponent(showTextEditors) .addPreferredGap(ComponentPlacement.RELATED) - .addComponent(btnNewButton_1) + .addComponent(showSinusMap) .addPreferredGap(ComponentPlacement.RELATED) - .addComponent(button) + .addComponent(showRain) .addPreferredGap(ComponentPlacement.RELATED) - .addComponent(btnNewButton_2) + .addComponent(showPointCloud) .addPreferredGap(ComponentPlacement.RELATED) - .addComponent(btnNewButton_3) + .addComponent(showMathGraphs) .addPreferredGap(ComponentPlacement.RELATED) - .addComponent(btnNewButton_4) + .addComponent(showOctree) .addContainerGap(137, Short.MAX_VALUE))); setLayout(groupLayout); } - private class SwingAction extends AbstractAction { + private class ShowTextEditors extends AbstractAction { private static final long serialVersionUID = 5197962166765841015L; - public SwingAction() { + public ShowTextEditors() { putValue(NAME, "Text editors"); - putValue(SHORT_DESCRIPTION, "Some short description"); } @Override @@ -154,27 +135,24 @@ class MenuPanel extends JPanel { } } - private class SwingAction_1 extends AbstractAction { + private class ShowSinusMap extends AbstractAction { private static final long serialVersionUID = -896479509963403828L; - public SwingAction_1() { + public ShowSinusMap() { putValue(NAME, "Wireframe sphere and ploygon landscape"); - putValue(SHORT_DESCRIPTION, "Some short description"); } @Override public void actionPerformed(final ActionEvent e) { SphereDemo.main(null); - } } - private class SwingAction_2 extends AbstractAction { + private class ShowRain extends AbstractAction { private static final long serialVersionUID = 8566009849873897321L; - public SwingAction_2() { + public ShowRain() { putValue(NAME, "Raining numbers"); - putValue(SHORT_DESCRIPTION, "Some short description"); } @Override @@ -187,12 +165,11 @@ class MenuPanel extends JPanel { } } - private class SwingAction_3 extends AbstractAction { + private class ShowPointcloud extends AbstractAction { private static final long serialVersionUID = -5369105936409884389L; - public SwingAction_3() { + public ShowPointcloud() { putValue(NAME, "Pointcloud galaxy"); - putValue(SHORT_DESCRIPTION, "Some short description"); } @Override @@ -201,12 +178,11 @@ class MenuPanel extends JPanel { } } - private class SwingAction_4 extends AbstractAction { + private class ShowMathGraphs extends AbstractAction { private static final long serialVersionUID = -8486796142555764460L; - public SwingAction_4() { + public ShowMathGraphs() { putValue(NAME, "Mathematical graphs"); - putValue(SHORT_DESCRIPTION, "Some short description"); } @Override @@ -219,12 +195,11 @@ class MenuPanel extends JPanel { } } - private class SwingAction_5 extends AbstractAction { + private class ShowOctree extends AbstractAction { private static final long serialVersionUID = -6210703594848004946L; - public SwingAction_5() { + public ShowOctree() { putValue(NAME, "Volumetric Octree"); - putValue(SHORT_DESCRIPTION, "Some short description"); } @Override diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Cell.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Cell.java new file mode 100755 index 0000000..4d1cca0 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Cell.java @@ -0,0 +1,128 @@ +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. + */ +public class Cell extends AbstractCompositeShape implements + MouseInteractionController { + + /** + * cell visual size + */ + public 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("55F5"); + /** + * Color of the inactive cell (R, G, B, A) while mouse is over it. + */ + private static final Color INACTIVE_COLOR_MOUSE_OVER = new Color("77F7"); + /** + * 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 / 2; + + // 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 void mouseClicked() { + setActive(!isActive()); + } + + @Override + public void mouseEntered() { + setMouseOver(true); + } + + @Override + public void mouseExited() { + setMouseOver(false); + } + + 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()); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Main.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Main.java new file mode 100644 index 0000000..527a6a2 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Main.java @@ -0,0 +1,107 @@ +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.geometry.Transform; +import eu.svjatoslav.sixth.e3d.gui.Avatar; +import eu.svjatoslav.sixth.e3d.gui.ViewContext; +import eu.svjatoslav.sixth.e3d.gui.ViewFrame; +import eu.svjatoslav.sixth.e3d.gui.humaninput.WorldNavigationTracker; +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; + + +class Main extends WorldNavigationTracker { + + 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 void keyPressed(final KeyEvent event, final ViewContext viewContext) { + 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: + super.keyPressed(event, viewContext); + } + } + + private void run() { + + // create application frame visible to the user + final ViewFrame viewFrame = new ViewFrame(); + + final ShapeCollection shapeCollection = viewFrame.getView() + .getContext().getRootShapeCollection(); + + // add matrix + shapeCollection.addShape(MATRIX); + + // add wireframe grid (optional) + shapeCollection.addShape(createGrid()); + + final ViewContext context = viewFrame.getView().getContext(); + + setAvatarOrientation(context.getAvatar()); + + // enable receiving of keyboard events + context.getKeyboardFocusTracker().setFocusOwner(this); + + // Done! World is built. So ensure screen is updated too. + context.getView().repaintDuringNextViewUpdate(); + } + + /** + * Create pink wireframe 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); + } + +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Matrix.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Matrix.java new file mode 100644 index 0000000..ee7b0aa --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Matrix.java @@ -0,0 +1,156 @@ +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); + } +} diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Star.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Star.java new file mode 100644 index 0000000..81e7198 --- /dev/null +++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/life/Star.java @@ -0,0 +1,41 @@ +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; + +public class Star extends GlowingPoint{ + public static final int STAR_SIZE = 10; + + public static final int UNIQUE_STARS_COUNT = 30; + + private static final List 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 + ); + } + +}