diff --git a/pom.xml b/pom.xml
index 3ac0a2ce..468ca1ff 100644
--- a/pom.xml
+++ b/pom.xml
@@ -165,6 +165,11 @@
java-data-front
2.0.0
+
+ com.aparapi
+ aparapi
+ 3.0.0
+
diff --git a/src/main/java/sh/ball/engine/Camera.java b/src/main/java/sh/ball/engine/Camera.java
index 201eb2fe..4aa838b5 100644
--- a/src/main/java/sh/ball/engine/Camera.java
+++ b/src/main/java/sh/ball/engine/Camera.java
@@ -1,8 +1,5 @@
package sh.ball.engine;
-import java.util.HashMap;
-import java.util.Map;
-
import sh.ball.shapes.Line;
import sh.ball.shapes.Shape;
import sh.ball.shapes.Vector2;
@@ -23,6 +20,8 @@ public class Camera {
private static final int SAMPLE_RENDER_SAMPLES = 50;
private static final int VERTEX_SAMPLES = 1000;
+ public final CameraDrawKernel kernel = new CameraDrawKernel();
+
private double focalLength;
private Vector3 pos;
@@ -36,7 +35,7 @@ public class Camera {
}
public List draw(WorldObject worldObject) {
- return getFrame(worldObject.getVertexPath());
+ return kernel.draw(this, worldObject);
}
// Automatically finds the correct Z position to use to view the world object properly.
@@ -94,24 +93,11 @@ public class Camera {
double focal = focalLength;
return new Vector2(
- vertex.getX() * focal / (vertex.getZ() - pos.getZ()) + pos.getX(),
- vertex.getY() * focal / (vertex.getZ() - pos.getZ()) + pos.getY()
+ vertex.x * focal / (vertex.z - pos.z) + pos.x,
+ vertex.y * focal / (vertex.z - pos.z) + pos.y
);
}
- public List getFrame(List vertexPath) {
- List lines = new ArrayList<>();
-
- for (int i = 0; i < vertexPath.size(); i += 2) {
- lines.add(new Line(
- project(vertexPath.get(i)),
- project(vertexPath.get(i + 1))
- ));
- }
-
- return lines;
- }
-
public void setFocalLength(double focalLength) {
this.focalLength = focalLength;
}
diff --git a/src/main/java/sh/ball/engine/CameraDrawKernel.java b/src/main/java/sh/ball/engine/CameraDrawKernel.java
new file mode 100644
index 00000000..43995a06
--- /dev/null
+++ b/src/main/java/sh/ball/engine/CameraDrawKernel.java
@@ -0,0 +1,127 @@
+package sh.ball.engine;
+
+import com.aparapi.Kernel;
+import com.aparapi.Range;
+import com.aparapi.exception.QueryFailedException;
+import sh.ball.shapes.Line;
+import sh.ball.shapes.Shape;
+import sh.ball.shapes.Vector2;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class CameraDrawKernel extends Kernel {
+
+ private WorldObject prevObject = null;
+ private float[] vertices;
+ private float[] vertexResult;
+ private Shape[] lines;
+ private float rotationX;
+ private float rotationY;
+ private float rotationZ;
+ private float positionX;
+ private float positionY;
+ private float positionZ;
+ private float cameraPosX;
+ private float cameraPosY;
+ private float cameraPosZ;
+ private float focalLength;
+
+ public CameraDrawKernel() {}
+
+ public List draw(Camera camera, WorldObject object) {
+ if (prevObject != object) {
+ List vertices = object.getVertexPath();
+ this.vertices = new float[vertices.size() * 3];
+ this.vertexResult = new float[vertices.size() * 2];
+ this.lines = new Line[vertices.size() / 2];
+ for (int i = 0; i < vertices.size(); i++) {
+ Vector3 vertex = vertices.get(i);
+ this.vertices[3 * i] = (float) vertex.x;
+ this.vertices[3 * i + 1] = (float) vertex.y;
+ this.vertices[3 * i + 2] = (float) vertex.z;
+ }
+ }
+ prevObject = object;
+ Vector3 rotation = object.getRotation();
+ Vector3 position = object.getPosition();
+ this.rotationX = (float) rotation.x;
+ this.rotationY = (float) rotation.y;
+ this.rotationZ = (float) rotation.z;
+ this.positionX = (float) position.x;
+ this.positionY = (float) position.y;
+ this.positionZ = (float) position.z;
+ Vector3 cameraPos = camera.getPos();
+ this.cameraPosX = (float) cameraPos.x;
+ this.cameraPosY = (float) cameraPos.y;
+ this.cameraPosZ = (float) cameraPos.z;
+ this.focalLength = (float) camera.getFocalLength();
+
+ int maxGroupSize = 256;
+ try {
+ maxGroupSize = getKernelMaxWorkGroupSize(getTargetDevice());
+ } catch (QueryFailedException e) {
+ e.printStackTrace();
+ }
+
+ execute(Range.create(roundUp(vertices.length / 3, maxGroupSize), maxGroupSize));
+
+ for (int i = 0; i < vertices.length / 3; i += 2) {
+ lines[i / 2] = new Line(
+ new Vector2(vertexResult[2 * i], vertexResult[2 * i + 1]),
+ new Vector2(vertexResult[2 * i + 2], vertexResult[2 * i + 3])
+ );
+ }
+
+ return Arrays.asList(lines);
+ }
+
+ int roundUp(int round, int multiple) {
+ if (multiple == 0) {
+ return round;
+ }
+
+ int remainder = round % multiple;
+ if (remainder == 0) {
+ return round;
+ }
+
+ return round + multiple - remainder;
+ }
+
+ @Override
+ public void run() {
+ int i = getGlobalId();
+ // get vertex
+ float x = vertices[3 * i];
+ float y = vertices[3 * i + 1];
+ float z = vertices[3 * i + 2];
+
+ // rotate around x-axis
+ float cosValue = cos(rotationX);
+ float sinValue = sin(rotationX);
+ float y2 = cosValue * y - sinValue * z;
+ float z2 = sinValue * y + cosValue * z;
+
+ // rotate around y-axis
+ cosValue = cos(rotationY);
+ sinValue = sin(rotationY);
+ float x2 = cosValue * x + sinValue * z2;
+ float z3 = -sinValue * x + cosValue * z2;
+
+ // rotate around z-axis
+ cosValue = cos(rotationZ);
+ sinValue = sin(rotationZ);
+ float x3 = cosValue * x2 - sinValue * y2;
+ float y3 = sinValue * x2 + cosValue * y2;
+
+ // add position
+ x = x3 + positionX;
+ y = y3 + positionY;
+ z = z3 + positionZ;
+
+ // projection
+ vertexResult[2 * i] = x * focalLength / (z - cameraPosZ) + cameraPosX;
+ vertexResult[2 * i + 1] = y * focalLength / (z - cameraPosZ) + cameraPosY;
+ }
+}
diff --git a/src/main/java/sh/ball/engine/Vector3.java b/src/main/java/sh/ball/engine/Vector3.java
index 5372f348..37313b4a 100644
--- a/src/main/java/sh/ball/engine/Vector3.java
+++ b/src/main/java/sh/ball/engine/Vector3.java
@@ -5,7 +5,7 @@ import java.util.Objects;
public final class Vector3 {
- private final double x, y, z;
+ public final double x, y, z;
public Vector3(double x, double y, double z) {
this.x = x;
@@ -21,18 +21,6 @@ public final class Vector3 {
this(0, 0, 0);
}
- public double getX() {
- return x;
- }
-
- public double getY() {
- return y;
- }
-
- public double getZ() {
- return z;
- }
-
public Vector3 add(Vector3 other) {
return new Vector3(
x + other.x,
diff --git a/src/main/java/sh/ball/engine/WorldObject.java b/src/main/java/sh/ball/engine/WorldObject.java
index 18edad5d..6215a899 100644
--- a/src/main/java/sh/ball/engine/WorldObject.java
+++ b/src/main/java/sh/ball/engine/WorldObject.java
@@ -44,13 +44,7 @@ public class WorldObject {
}
public List getVertexPath() {
- List newVertices = new ArrayList<>();
-
- for (Vector3 vertex : vertexPath) {
- newVertices.add(vertex.rotate(rotation).add(position));
- }
-
- return newVertices;
+ return vertexPath;
}
public void getDrawPath(Set edges) {
@@ -89,6 +83,10 @@ public class WorldObject {
}
}
+ public Vector3 getRotation() {
+ return rotation;
+ }
+
public void setRotation(Vector3 rotation) {
this.rotation = rotation;
}
@@ -105,6 +103,10 @@ public class WorldObject {
position = position.add(translation);
}
+ public Vector3 getPosition() {
+ return position;
+ }
+
public void resetPosition() {
position = new Vector3();
}
diff --git a/src/main/java/sh/ball/gui/controller/ObjController.java b/src/main/java/sh/ball/gui/controller/ObjController.java
index 8501b87a..7c097d14 100644
--- a/src/main/java/sh/ball/gui/controller/ObjController.java
+++ b/src/main/java/sh/ball/gui/controller/ObjController.java
@@ -124,11 +124,11 @@ public class ObjController implements Initializable, SubController {
public Element save(Document document) {
Element element = document.createElement("objectRotation");
Element x = document.createElement("x");
- x.appendChild(document.createTextNode(Double.toString(rotation.getX())));
+ x.appendChild(document.createTextNode(Double.toString(rotation.x)));
Element y = document.createElement("y");
- y.appendChild(document.createTextNode(Double.toString(rotation.getY())));
+ y.appendChild(document.createTextNode(Double.toString(rotation.y)));
Element z = document.createElement("z");
- z.appendChild(document.createTextNode(Double.toString(rotation.getZ())));
+ z.appendChild(document.createTextNode(Double.toString(rotation.z)));
element.appendChild(x);
element.appendChild(y);
element.appendChild(z);
diff --git a/src/main/java/sh/ball/shapes/CubicBezierCurve.java b/src/main/java/sh/ball/shapes/CubicBezierCurve.java
index 782d5c15..14058939 100644
--- a/src/main/java/sh/ball/shapes/CubicBezierCurve.java
+++ b/src/main/java/sh/ball/shapes/CubicBezierCurve.java
@@ -7,12 +7,13 @@ public class CubicBezierCurve extends Shape {
private final Vector2 p2;
private final Vector2 p3;
+ private double length = INVALID_LENGTH;
+
public CubicBezierCurve(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) {
this.p0 = p0;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
- this.length = new Line(p0, p3).length;
}
@Override
@@ -45,4 +46,12 @@ public class CubicBezierCurve extends Shape {
return new CubicBezierCurve(p0.translate(vector), p1.translate(vector), p2.translate(vector),
p3.translate(vector));
}
+
+ @Override
+ public double getLength() {
+ if (length == INVALID_LENGTH) {
+ length = new Line(p0, p3).getLength();
+ }
+ return length;
+ }
}
diff --git a/src/main/java/sh/ball/shapes/Ellipse.java b/src/main/java/sh/ball/shapes/Ellipse.java
index 0d6ccc89..21c6b6c6 100644
--- a/src/main/java/sh/ball/shapes/Ellipse.java
+++ b/src/main/java/sh/ball/shapes/Ellipse.java
@@ -7,13 +7,13 @@ public final class Ellipse extends Shape {
private final double rotation;
private final Vector2 position;
+ private double length = INVALID_LENGTH;
+
public Ellipse(double a, double b, double rotation, Vector2 position) {
this.a = a;
this.b = b;
this.rotation = rotation;
this.position = position;
- // Approximation of length.
- this.length = 2 * Math.PI * Math.sqrt((a * a + b * b) / 2);
}
public Ellipse(double a, double b, Vector2 position) {
@@ -57,4 +57,13 @@ public final class Ellipse extends Shape {
public Ellipse translate(Vector2 vector) {
return new Ellipse(a, b, rotation, position.translate(vector));
}
+
+ @Override
+ public double getLength() {
+ if (length == INVALID_LENGTH) {
+ // Approximation of length.
+ length = 2 * Math.PI * Math.sqrt((a * a + b * b) / 2);
+ }
+ return length;
+ }
}
diff --git a/src/main/java/sh/ball/shapes/Line.java b/src/main/java/sh/ball/shapes/Line.java
index 0a740a33..1961a349 100644
--- a/src/main/java/sh/ball/shapes/Line.java
+++ b/src/main/java/sh/ball/shapes/Line.java
@@ -8,23 +8,17 @@ public final class Line extends Shape {
private final Vector2 a;
private final Vector2 b;
+ private double length = INVALID_LENGTH;
+
public Line(Vector2 a, Vector2 b) {
this.a = a;
this.b = b;
- this.length = calculateLength();
}
public Line(double x1, double y1, double x2, double y2) {
this(new Vector2(x1, y1), new Vector2(x2, y2));
}
- private double calculateLength() {
- double ac = Math.abs(getY2() - getY1());
- double cb = Math.abs(getX2() - getX1());
-
- return Math.hypot(ac, cb);
- }
-
@Override
public Line rotate(double theta) {
return new Line(a.rotate(theta), b.rotate(theta));
@@ -35,6 +29,17 @@ public final class Line extends Shape {
return new Line(a.translate(vector), b.translate(vector));
}
+ @Override
+ public double getLength() {
+ if (length == INVALID_LENGTH) {
+ double ac = Math.abs(getY2() - getY1());
+ double cb = Math.abs(getX2() - getX1());
+
+ length = Math.hypot(ac, cb);
+ }
+ return length;
+ }
+
@Override
public Line scale(double factor) {
return new Line(a.scale(factor), b.scale(factor));
diff --git a/src/main/java/sh/ball/shapes/Shape.java b/src/main/java/sh/ball/shapes/Shape.java
index 7b93d773..cde9f1f1 100644
--- a/src/main/java/sh/ball/shapes/Shape.java
+++ b/src/main/java/sh/ball/shapes/Shape.java
@@ -8,7 +8,7 @@ import java.util.function.Function;
public abstract class Shape {
- protected double length;
+ protected static final double INVALID_LENGTH = -1;
public abstract Vector2 nextVector(double drawingProgress);
@@ -20,9 +20,7 @@ public abstract class Shape {
public abstract Shape translate(Vector2 vector);
- public double getLength() {
- return length;
- }
+ public abstract double getLength();
/* SHAPE HELPER FUNCTIONS */
diff --git a/src/main/java/sh/ball/shapes/Vector2.java b/src/main/java/sh/ball/shapes/Vector2.java
index 1229853a..f1047d60 100644
--- a/src/main/java/sh/ball/shapes/Vector2.java
+++ b/src/main/java/sh/ball/shapes/Vector2.java
@@ -82,6 +82,11 @@ public final class Vector2 extends Shape {
return new Vector2(getX() + vector.getX(), getY() + vector.getY());
}
+ @Override
+ public double getLength() {
+ return 0;
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {