kopia lustrzana https://github.com/jameshball/osci-render
Accellerate camera draw with GPU
rodzic
2be2006667
commit
136faea4f1
5
pom.xml
5
pom.xml
|
@ -165,6 +165,11 @@
|
|||
<artifactId>java-data-front</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aparapi</groupId>
|
||||
<artifactId>aparapi</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<profiles>
|
||||
|
|
|
@ -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<Shape> 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<Shape> getFrame(List<Vector3> vertexPath) {
|
||||
List<Shape> 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;
|
||||
}
|
||||
|
|
|
@ -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<Shape> draw(Camera camera, WorldObject object) {
|
||||
if (prevObject != object) {
|
||||
List<Vector3> 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;
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -44,13 +44,7 @@ public class WorldObject {
|
|||
}
|
||||
|
||||
public List<Vector3> getVertexPath() {
|
||||
List<Vector3> newVertices = new ArrayList<>();
|
||||
|
||||
for (Vector3 vertex : vertexPath) {
|
||||
newVertices.add(vertex.rotate(rotation).add(position));
|
||||
}
|
||||
|
||||
return newVertices;
|
||||
return vertexPath;
|
||||
}
|
||||
|
||||
public void getDrawPath(Set<Line3D> 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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
Ładowanie…
Reference in New Issue