http://blog.rogach.org/2015/08/how-to-create-your-own-simple-3d-render.html
+ Improve triangulation. Read: https://ianthehenry.com/posts/delaunay/
+
+** Fix camera rotation for voxel raytracer
\ No newline at end of file
* Represents the viewer's camera in the 3D world, with position, orientation, and movement.
*
* <p>The camera is the user's "eyes" in the 3D scene. It has a position (location),
- * a looking direction (defined by XZ and YZ angles), and a movement system with
+ * a looking direction (defined by a quaternion), and a movement system with
* velocity, acceleration, and friction for smooth camera navigation.</p>
*
* <p>By default, the user can navigate using arrow keys (handled by
* // Set camera position
* camera.getTransform().setTranslation(new Point3D(0, -50, -200));
*
- * // Set camera orientation (radians)
- * camera.getTransform().setRotation(0, 0); // angleXZ, angleYZ
+ * // Set camera orientation using a quaternion
+ * camera.getTransform().getRotation().setQuaternion(Quaternion.fromAngles(0.5, -0.3));
*
* // Copy camera state from another camera
* Camera snapshot = new Camera(camera);
import eu.svjatoslav.sixth.e3d.geometry.Point3D;
-import static java.lang.Math.atan2;
-
/**
- * Represents a rotation in 3D space using a quaternion internally.
+ * Represents a rotation in 3D space using a quaternion.
*
- * <p>Historically used two Euler angles (XZ and YZ). Now stores a quaternion
- * for better numerical stability and to avoid gimbal lock.</p>
+ * <p>Quaternions provide smooth interpolation and avoid gimbal lock
+ * compared to Euler angles.</p>
*
* @see Transform
* @see Quaternion
quaternion = Quaternion.identity();
}
- /**
- * Creates a rotation with the specified Euler angles.
- *
- * @param angleXZ the angle around the XZ axis (yaw) in radians
- * @param angleYZ the angle around the YZ axis (pitch) in radians
- */
- public Rotation(final double angleXZ, final double angleYZ) {
- quaternion = Quaternion.fromAngles(angleXZ, angleYZ);
- }
-
/**
* Creates a copy of this rotation with the same orientation.
*
toMatrix().transform(point3d, point3d);
}
- /**
- * Adds the specified angles to this rotation.
- *
- * @param angleXZ the angle to add around the XZ axis in radians
- * @param angleYZ the angle to add around the YZ axis in radians
- */
- public void rotate(final double angleXZ, final double angleYZ) {
- final Quaternion delta = Quaternion.fromAngles(angleXZ, angleYZ);
- quaternion = delta.multiply(quaternion);
- }
-
- /**
- * Sets the rotation angles.
- *
- * @param angleXZ the angle around the XZ axis (yaw) in radians
- * @param angleYZ the angle around the YZ axis (pitch) in radians
- */
- public void setAngles(final double angleXZ, final double angleYZ) {
- quaternion = Quaternion.fromAngles(angleXZ, angleYZ);
- }
-
- /**
- * Copies the rotation from another rotation.
- *
- * @param rotation the rotation to copy from
- */
- public void setAngles(final Rotation rotation) {
- quaternion = new Quaternion(
- rotation.quaternion.w,
- rotation.quaternion.x,
- rotation.quaternion.y,
- rotation.quaternion.z
- );
- }
-
/**
* Sets the rotation from a quaternion.
*
return quaternion;
}
- /**
- * Returns the angle around the XZ axis (yaw) in radians.
- *
- * <p>This is a compatibility shim that extracts the angle from the quaternion.
- * May not be accurate for extreme pitch angles.</p>
- *
- * @return the XZ angle
- */
- public double getAngleXZ() {
- final Matrix3x3 m = toMatrix();
- return atan2(m.m02, m.m00);
- }
-
- /**
- * Returns the angle around the YZ axis (pitch) in radians.
- *
- * <p>This is a compatibility shim that extracts the angle from the quaternion.
- * May not be accurate for extreme yaw angles.</p>
- *
- * @return the YZ angle
- */
- public double getAngleYZ() {
- final Matrix3x3 m = toMatrix();
- return atan2(-m.m21, m.m11);
- }
-
/**
* Converts this rotation to a 3x3 transformation matrix.
*
}
/**
- * Creates a transform with the specified translation and rotation angles.
+ * Creates a transform with the specified translation and rotation from Euler angles.
*
* @param translation the translation
* @param angleXZ the angle around the XZ axis (yaw) in radians
* @param angleYZ the angle around the YZ axis (pitch) in radians
+ * @return a new transform with the specified translation and rotation
*/
- public Transform(final Point3D translation, final double angleXZ,
- final double angleYZ) {
-
- this.translation = translation;
- rotation = new Rotation(angleXZ, angleYZ);
+ public static Transform fromAngles(final Point3D translation, final double angleXZ, final double angleYZ) {
+ final Transform t = new Transform(translation);
+ t.rotation.setQuaternion(Quaternion.fromAngles(angleXZ, angleYZ));
+ return t;
}
/**
return translation;
}
- /**
+ /**
* Applies this transform to a point: rotation followed by translation.
*
* @param point the point to transform (modified in place)
point.add(translation);
}
- /**
- * Sets the rotation angles for this transform.
- *
- * @param angleXZ the angle around the XZ axis (yaw) in radians
- * @param angleYZ the angle around the YZ axis (pitch) in radians
- */
- public void setRotation(final double angleXZ, final double angleYZ) {
- rotation.setAngles(angleXZ, angleYZ);
- }
-
/**
* Sets the translation for this transform by copying the values from the given point.
*
this.translation.z = translation.z;
}
-}
+}
\ No newline at end of file
import eu.svjatoslav.sixth.e3d.geometry.Point3D;
import eu.svjatoslav.sixth.e3d.gui.Camera;
-import eu.svjatoslav.sixth.e3d.math.Rotation;
+import eu.svjatoslav.sixth.e3d.math.Matrix3x3;
import static eu.svjatoslav.sixth.e3d.renderer.octree.raytracer.RaytracingCamera.SIZE;
* @param zoom the zoom level (scales the view frustum)
*/
public CameraView(final Camera camera, final double zoom) {
- // compute camera view coordinates as if camera is at (0,0,0) and look at (0,0,1)
final float viewAngle = (float) .6;
cameraCenter = new Point3D();
topLeft = new Point3D(0, 0, SIZE).rotate(-viewAngle, -viewAngle);
bottomLeft = new Point3D(0, 0, SIZE).rotate(-viewAngle, viewAngle);
bottomRight = new Point3D(0, 0, SIZE).rotate(viewAngle, viewAngle);
- Rotation rotation = camera.getTransform().getRotation();
- topLeft.rotate(-rotation.getAngleXZ(), -rotation.getAngleYZ());
- topRight.rotate(-rotation.getAngleXZ(), -rotation.getAngleYZ());
- bottomLeft.rotate(-rotation.getAngleXZ(), -rotation.getAngleYZ());
- bottomRight.rotate(-rotation.getAngleXZ(), -rotation.getAngleYZ());
+ final Matrix3x3 m = camera.getTransform().getRotation().toMatrix();
+ final Point3D temp = new Point3D();
+
+ temp.clone(topLeft);
+ m.transform(temp, topLeft);
+
+ temp.clone(topRight);
+ m.transform(temp, topRight);
+
+ temp.clone(bottomLeft);
+ m.transform(temp, bottomLeft);
+
+ temp.clone(bottomRight);
+ m.transform(temp, bottomRight);
- // place camera view at camera location
camera.getTransform().getTranslation().clone().scaleDown(zoom).addTo(cameraCenter, topLeft, topRight, bottomLeft, bottomRight);
}
import eu.svjatoslav.sixth.e3d.geometry.Point3D;
import eu.svjatoslav.sixth.e3d.gui.Camera;
-import eu.svjatoslav.sixth.e3d.math.Rotation;
+import eu.svjatoslav.sixth.e3d.math.Matrix3x3;
import eu.svjatoslav.sixth.e3d.math.Transform;
import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
bottomLeft.rotate(cameraCenter, -viewAngle, viewAngle);
bottomRight.rotate(cameraCenter, viewAngle, viewAngle);
- Rotation rotation = camera.getTransform().getRotation();
- topLeft.rotate(cameraCenter, -rotation.getAngleXZ(), -rotation.getAngleYZ());
- topRight.rotate(cameraCenter, -rotation.getAngleXZ(), -rotation.getAngleYZ());
- bottomLeft.rotate(cameraCenter, -rotation.getAngleXZ(), -rotation.getAngleYZ());
- bottomRight.rotate(cameraCenter, -rotation.getAngleXZ(), -rotation.getAngleYZ());
+ final Matrix3x3 m = camera.getTransform().getRotation().toMatrix();
+ final Point3D temp = new Point3D();
+
+ temp.clone(topLeft);
+ temp.subtract(cameraCenter);
+ m.transform(temp, topLeft);
+ topLeft.add(cameraCenter);
+
+ temp.clone(topRight);
+ temp.subtract(cameraCenter);
+ m.transform(temp, topRight);
+ topRight.add(cameraCenter);
+
+ temp.clone(bottomLeft);
+ temp.subtract(cameraCenter);
+ m.transform(temp, bottomLeft);
+ bottomLeft.add(cameraCenter);
+
+ temp.clone(bottomRight);
+ temp.subtract(cameraCenter);
+ m.transform(temp, bottomRight);
+ bottomRight.add(cameraCenter);
final Color cameraColor = new Color(255, 255, 0, 255);
final LineAppearance appearance = new LineAppearance(2, cameraColor);
final Camera camera = viewPanel.getCamera();
- cameraRotationTransform.getRotation().setAngles(camera.getTransform().getRotation());
+ cameraRotationTransform.getRotation().setQuaternion(
+ camera.getTransform().getRotation().getQuaternion());
transformStack.addTransform(cameraRotationTransform);
final Point3D cameraLocation = camera.getTransform().getTranslation();
@Test
public void testFromAnglesMatchesRotation() {
- final Rotation rotation = new Rotation(0.5, 0.3);
+ final Rotation rotation = new Rotation();
+ rotation.setQuaternion(Quaternion.fromAngles(0.5, 0.3));
final Matrix3x3 rotationMatrix = rotation.toMatrix();
final Quaternion quaternion = Quaternion.fromAngles(0.5, 0.3);
@Test
public void testToMatrixMatchesRotate() {
- final Rotation rotation = new Rotation(0.5, 0.3);
+ final Rotation rotation = new Rotation();
+ rotation.setQuaternion(Quaternion.fromAngles(0.5, 0.3));
final Point3D original = new Point3D(1, 2, 3);
final Point3D viaRotate = new Point3D(original);