import static java.lang.Math.cos;
import static java.lang.Math.sin;
+/**
+ * The avatar is a user-controlled object in the 3D world.
+ */
public class Avatar implements ViewRenderListener {
+ /**
+ * Avatar movement speed, relative to the world. When avatar coordinates are
+ * updated within the world, avatar orientation relative to the world is
+ * taken into account.
+ */
public static final double SPEED_LIMIT = 30;
/**
* Just in case we want to adjust global speed for some reason.
setAngleYZ(angleYZ);
}
+
@Override
public boolean beforeRender(final ViewPanel viewPanel, final int millisecondsSinceLastFrame) {
return movementVector.getVectorLength();
}
+ /**
+ * Apply friction to avatar movement vector.
+ *
+ * @param millisecondsPassedSinceLastFrame We want avatar movement to be independent of framerate.
+ * Therefore, we take frame rendering time into account when translating
+ * avatar between consecutive frames.
+ */
private void applyFrictionToUserMovement(int millisecondsPassedSinceLastFrame) {
for (int i = 0; i < millisecondsPassedSinceLastFrame; i++)
applyMillisecondFrictionToUserMovementVector();
}
+ /**
+ * Apply friction to avatar movement vector.
+ */
private void applyMillisecondFrictionToUserMovementVector() {
getMovementVector().x /= MILLISECOND_FRICTION;
getMovementVector().y /= MILLISECOND_FRICTION;
* Translate coordinates based on avatar movement vector and avatar orientation in the world.
*
* @param millisecondsPassedSinceLastFrame We want avatar movement to be independent of framerate.
- * Therefore we take frame rendering time into account when translating
+ * Therefore, we take frame rendering time into account when translating
* avatar between consecutive frames.
*/
private void translateAvatarLocationBasedOnMovementVector(int millisecondsPassedSinceLastFrame) {
*/
package eu.svjatoslav.sixth.e3d.gui;
+/**
+ * A pointer to a character in a text.
+ */
public class TextPointer implements Comparable<TextPointer> {
public int row;
}
+ /**
+ * Checks if this pointer is between the specified pointers.
+ *
+ * @param start
+ * The start pointer.
+ * @param end
+ * The end pointer.
+ * @return True if this pointer is between the specified pointers.
+ */
public boolean isBetween(final TextPointer start, final TextPointer end) {
if (start == null)
if (end == null)
return false;
+ // Make sure that start is smaller than end.
TextPointer smaller;
TextPointer bigger;
bigger = start;
}
+ // Check if this pointer is between the specified pointers.
return (compareTo(smaller) >= 0) && (bigger.compareTo(this) > 0);
-
}
}
import eu.svjatoslav.sixth.e3d.geometry.Point3D;
import eu.svjatoslav.sixth.e3d.math.GeometryCoordinate;
-import eu.svjatoslav.sixth.e3d.math.TransformPipe;
+import eu.svjatoslav.sixth.e3d.math.TransformsPipeline;
public class UserRelativityTracker {
}
- public void analyze(final TransformPipe transformPipe,
+ public void analyze(final TransformsPipeline transformPipe,
final RenderingContext renderingContext) {
center.transform(transformPipe, renderingContext);
*/
package eu.svjatoslav.sixth.e3d.gui.textEditorComponent;
+/**
+ * A character in a text editor.
+ */
public class Character {
+ /**
+ * The character value.
+ */
char value;
- // TODO: background and foreground colors
-
public Character(final char value) {
this.value = value;
}
import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+/**
+ * A look and feel of a text editor.
+ */
public class LookAndFeel {
public Color foreground = new Color(255, 255, 255);
import java.util.ArrayList;
import java.util.List;
+/**
+ * A page in a text editor.
+ */
public class Page {
+ /**
+ * The text lines.
+ */
public List<TextLine> rows = new ArrayList<>();
public void ensureMaxTextLine(final int row) {
rows.add(new TextLine());
}
+ /**
+ * Returns the character at the specified location.
+ * If the location is out of bounds, returns a space.
+ *
+ * @return The character at the specified location.
+ */
public char getChar(final int row, final int column) {
if (rows.size() <= row)
return ' ';
return rows.get(row).getCharForLocation(column);
}
+ /**
+ * Returns the specified line.
+ *
+ * @param row
+ * The line number.
+ * @return The line.
+ */
public TextLine getLine(final int row) {
ensureMaxTextLine(row);
return rows.get(row);
}
+ /**
+ * Returns the length of the specified line.
+ *
+ * @param row
+ * The line number.
+ * @return The length of the line.
+ */
public int getLineLength(final int row) {
if (rows.size() <= row)
return 0;
return rows.get(row).getLength();
}
+ /**
+ * Returns the number of lines in the page.
+ *
+ * @return The number of lines in the page.
+ */
public int getLinesCount() {
pack();
return rows.size();
}
+ /**
+ * Returns the text of the page.
+ *
+ * @return The text of the page.
+ */
public String getText() {
pack();
rows.add(row, textLine);
}
+ /**
+ * Removes empty lines from the end of the page.
+ */
private void pack() {
int newLength = 0;
rows = rows.subList(0, newLength);
}
+ /**
+ * Removes the specified character from the page.
+ *
+ * @param row
+ * The line number.
+ * @param col
+ * The character number.
+ */
public void removeCharacter(final int row, final int col) {
if (rows.size() <= row)
return;
getLine(row).removeCharacter(col);
}
+ /**
+ * Removes the specified line from the page.
+ *
+ * @param row
+ * The line number.
+ */
public void removeLine(final int row) {
if (rows.size() <= row)
return;
*/
private final Set<Integer> dirtyRows = new HashSet<>();
+
private final TextCanvas textCanvas;
public int scrolledCharacters = 0, scrolledLines = 0;
public boolean selecting = false;
+
+ /**
+ * Selection start and end pointers.
+ */
public TextPointer selectionStart = new TextPointer(0, 0);
public TextPointer selectionEnd = new TextPointer(0, 0);
+
+
public TextPointer cursorLocation = new TextPointer(0, 0);
Page page = new Page();
LookAndFeel lookAndFeel;
+
+ /**
+ * If true, the page will be repainted on the next update.
+ */
boolean repaintPage = false;
public TextEditComponent(final Transform transform,
dirtyRows.clear();
}
- // public void setCaret(final int x, final int y) {
- // selecting = false;
- // cursorLocation.column = (x / characterWidth) + scrolledCharacters;
- // cursorLocation.row = (y / characterHeight) + scrolledLines;
- // repaintPage();
- // }
-
/**
* Scroll full page to given amount of lines or charancters.
*/
return result.toString();
}
+
public void cutFromBeginning(int charactersToCut) {
if (charactersToCut > chars.size())
import eu.svjatoslav.sixth.e3d.geometry.Point3D;
import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
+/**
+ * A point in 3D space with a transformed and on-screen coordinates.
+ */
public class GeometryCoordinate {
+ /**
+ * The original coordinate.
+ */
public Point3D coordinate;
+
+ /**
+ * The transformed coordinate.
+ */
public Point3D transformedCoordinate;
+
+ /**
+ * The on-screen coordinate.
+ */
public Point2D onScreenCoordinate;
private int lastTransformedFrame;
onScreenCoordinate = new Point2D();
}
- public void transform(final TransformPipe transforms,
+ /**
+ * Transforms the coordinate.
+ *
+ * @param transforms
+ * The transform pipe.
+ * @param renderContext
+ * The rendering context.
+ */
+ public void transform(final TransformsPipeline transforms,
final RenderingContext renderContext) {
if (lastTransformedFrame == renderContext.frameNumber)
public class Orientation implements Cloneable {
+
private double s1, c1, s2, c2;
+ /**
+ * The angle of rotation around the XZ axis.
+ */
private double angleXZ = 0;
+
+ /**
+ * The angle of rotation around the YZ axis.
+ */
private double angleYZ = 0;
public Orientation() {
return new Orientation(angleXZ, angleYZ);
}
+ /**
+ * Computes the sine and cosine of the angles.
+ */
private void computeMultipliers() {
s1 = Math.sin(angleXZ);
c1 = Math.cos(angleXZ);
orientation = new Orientation();
}
+ /**
+ * Creates a new transform with the specified translation.
+ *
+ * @param translation
+ * the translation
+ */
public Transform(final Point3D translation) {
this.translation = translation;
orientation = new Orientation();
}
+ /**
+ * Creates a new transform with the specified translation and orientation.
+ *
+ * @param translation
+ * the translation
+ * @param angleXZ
+ * the angle around the XZ axis
+ * @param angleYZ
+ * the angle around the YZ axis
+ */
public Transform(final Point3D translation, final double angleXZ,
final double angleYZ) {
orientation = new Orientation(angleXZ, angleYZ);
}
+ /**
+ * Creates a new transform with the specified translation and orientation.
+ *
+ * @param translation
+ * the translation
+ * @param orientation
+ * the orientation
+ */
public Transform(final Point3D translation, final Orientation orientation) {
this.translation = translation;
this.orientation = orientation;
+++ /dev/null
-/*
- * Sixth 3D engine. Author: Svjatoslav Agejenko.
- * This project is released under Creative Commons Zero (CC0) license.
- */
-package eu.svjatoslav.sixth.e3d.math;
-
-import eu.svjatoslav.sixth.e3d.geometry.Point3D;
-
-public class TransformPipe {
-
- private Transform[] transforms = new Transform[100];
-
- private int transformsCount = 0;
-
- public void addTransform(final Transform transform) {
- transforms[transformsCount] = transform;
- transformsCount++;
- }
-
- public void clear() {
- transformsCount = 0;
- }
-
- public void dropTransform() {
- transformsCount--;
- }
-
- public void transform(final Point3D source, final Point3D destination) {
-
- destination.clone(source);
-
- for (int i = transformsCount - 1; i >= 0; i--)
- transforms[i].transform(destination);
- }
-}
--- /dev/null
+/*
+ * Sixth 3D engine. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+package eu.svjatoslav.sixth.e3d.math;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+
+public class TransformsPipeline {
+
+
+ private Transform[] transforms = new Transform[100];
+
+ /**
+ * The number of transforms in the pipeline.
+ */
+ private int transformsCount = 0;
+
+ /**
+ * Adds a transform to the pipeline.
+ *
+ * @param transform
+ * The transform to add.
+ */
+ public void addTransform(final Transform transform) {
+ transforms[transformsCount] = transform;
+ transformsCount++;
+ }
+
+ /**
+ * Clears the pipeline.
+ */
+ public void clear() {
+ transformsCount = 0;
+ }
+
+ /**
+ * Drops the last transform from the pipeline.
+ */
+ public void dropTransform() {
+ transformsCount--;
+ }
+
+ /**
+ * Transforms a point.
+ *
+ * @param orinigalPoint
+ * Original point to transform. Original point is not modified.
+ * @param transformedPoint
+ * Transformed point.
+ */
+ public void transform(final Point3D orinigalPoint, final Point3D transformedPoint) {
+
+ transformedPoint.clone(orinigalPoint);
+
+ // apply transforms in reverse order
+ for (int i = transformsCount - 1; i >= 0; i--)
+ transforms[i].transform(transformedPoint);
+ }
+}
public class OctreeVolume {
+ // cell is not hit by the ray
public static final int TRACE_NO_HIT = -1;
- /**
- * Single solid color.
- */
+
+ // solid cell (contains color and illumination)
private static final int CELL_STATE_SOLID = -2;
+
+ // unused cell
private static final int CELL_STATE_UNUSED = -1;
final LineAppearance factory = new LineAppearance();
+
+
public int ce1[];
public int ce2[];
public int ce3[];
}
/**
- * @return intersecting cell pointer or -1 if no cell in intersecting this
- * ray.
+ * Trace ray through the world and return pointer to intersecting cell.
+ *
+ * @return pointer to intersecting cell or TRACE_NO_HIT if no intersection.
*/
public int traceCell(final int cellX, final int cellY, final int cellZ,
final int cellSize, final int pointer, final Ray ray) {
public class Ray {
+ // ray origin
public double x, y, z;
+ // ray direction
public double xp, yp, zp;
+ // ray hit point
public double hitX, hitY, hitZ;
+
public int hitCellSize;
public int hitCellX, hitCellY, hitCellZ;
- /*
- * public int orientation;
- *
- * public static final int ORIENTATION_RIGHT = 0;
- *
- * public static final int ORIENTATION_LEFT = 1;
- *
- * public static final int ORIENTATION_DOWN = 2;
- *
- * public static final int ORIENTATION_UP = 3;
- *
- * public static final int ORIENTATION_FORWARD = 4;
- *
- * public static final int ORIENTATION_BACK = 5;
- *
- * public static final String orientations[] = { "RIGHT", "LEFT", "DOWN",
- * "UP", "FORWARD", "BACK" };
- */
-
public Ray(final double X, final double Y, final double Z, final double Xp,
final double Yp, final double Zp) {
x = X;
xp = Xp;
yp = Yp;
zp = Zp;
- // calculateOrientation();
}
- /*
- * public void calculateOrientation() { float axp = Math.abs(xp); float ayp
- * = Math.abs(yp); float azp = Math.abs(zp);
- *
- * if (axp > ayp) { if (axp > azp) { if (xp > 0) { orientation =
- * ORIENTATION_RIGHT; } else { orientation = ORIENTATION_LEFT; } } else { if
- * (zp > 0) { orientation = ORIENTATION_FORWARD; } else { orientation =
- * ORIENTATION_BACK; } } } else { if (ayp > azp) { if (yp > 0) { orientation
- * = ORIENTATION_DOWN; } else { orientation = ORIENTATION_UP; } } else { if
- * (zp > 0) { orientation = ORIENTATION_FORWARD; } else { orientation =
- * ORIENTATION_BACK; } } }
- *
- *
- * }
- */
+
@Override
public String toString() {
return "Ray \n" + " x " + x + "\n" + " y " + y + "\n" + " z " + z
+ "\n" + " xp " + xp + "\n" + " yp " + yp + "\n" + " zp " + zp
- + "\n"; /*
- * + " orientation " + orientations[orientation];
- */
+ + "\n";
}
}
* Sixth 3D engine. Author: Svjatoslav Agejenko.
* This project is released under Creative Commons Zero (CC0) license.
*
-*
* Various 3D renderers utilizing different rendering approaches.
+ *
*/
package eu.svjatoslav.sixth.e3d.renderer;
import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
import eu.svjatoslav.sixth.e3d.math.Transform;
-import eu.svjatoslav.sixth.e3d.math.TransformPipe;
+import eu.svjatoslav.sixth.e3d.math.TransformsPipeline;
import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape;
import java.util.ArrayList;
public class ShapeCollection {
private final RenderAggregator aggregator = new RenderAggregator();
- private final TransformPipe transformPipe = new TransformPipe();
+ private final TransformsPipeline transformPipe = new TransformsPipeline();
private final List<AbstractShape> shapes = new ArrayList<>();
public synchronized void addShape(final AbstractShape shape) {
import eu.svjatoslav.sixth.e3d.geometry.Point3D;
import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
import eu.svjatoslav.sixth.e3d.math.GeometryCoordinate;
-import eu.svjatoslav.sixth.e3d.math.TransformPipe;
+import eu.svjatoslav.sixth.e3d.math.TransformsPipeline;
import eu.svjatoslav.sixth.e3d.renderer.raster.RenderAggregator;
import java.util.concurrent.atomic.AtomicInteger;
public abstract void paint(RenderingContext renderBuffer);
@Override
- public void transform(final TransformPipe transforms,
+ public void transform(final TransformsPipeline transforms,
final RenderAggregator aggregator,
final RenderingContext renderingContext) {
import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
-import eu.svjatoslav.sixth.e3d.math.TransformPipe;
+import eu.svjatoslav.sixth.e3d.math.TransformsPipeline;
import eu.svjatoslav.sixth.e3d.renderer.raster.RenderAggregator;
public abstract class AbstractShape {
this.mouseInteractionController = mouseInteractionController;
}
- public abstract void transform(final TransformPipe transforms,
+ public abstract void transform(final TransformsPipeline transforms,
final RenderAggregator aggregator,
final RenderingContext renderingContext);
import eu.svjatoslav.sixth.e3d.gui.UserRelativityTracker;
import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
import eu.svjatoslav.sixth.e3d.math.Transform;
-import eu.svjatoslav.sixth.e3d.math.TransformPipe;
+import eu.svjatoslav.sixth.e3d.math.TransformsPipeline;
import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
import eu.svjatoslav.sixth.e3d.renderer.raster.RenderAggregator;
import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractShape;
* This method should be overridden by anyone wanting to customize shape
* before it is rendered.
*/
- public void beforeTransformHook(final TransformPipe transformPipe,
+ public void beforeTransformHook(final TransformsPipeline transformPipe,
final RenderingContext context) {
}
}
@Override
- public void transform(final TransformPipe transformPipe,
+ public void transform(final TransformsPipeline transformPipe,
final RenderAggregator aggregator, final RenderingContext context) {
// add current composite shape transform to the end of the transform
import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
import eu.svjatoslav.sixth.e3d.gui.TextPointer;
import eu.svjatoslav.sixth.e3d.math.Transform;
-import eu.svjatoslav.sixth.e3d.math.TransformPipe;
+import eu.svjatoslav.sixth.e3d.math.TransformsPipeline;
import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.TexturedRectangle;
}
@Override
- public void beforeTransformHook(final TransformPipe transformPipe,
+ public void beforeTransformHook(final TransformsPipeline transformPipe,
final RenderingContext context) {
final double textRelativeSize = context.width