feat: simplify FixedOrbitCameraRotationDelegate
This commit is contained in:
@@ -5,26 +5,30 @@ import '../../../viewer/viewer.dart';
|
|||||||
import '../../input.dart';
|
import '../../input.dart';
|
||||||
import '../input_handler.dart';
|
import '../input_handler.dart';
|
||||||
|
|
||||||
|
///
|
||||||
|
/// An [InputHandlerDelegate] that orbits the camera around a fixed
|
||||||
|
/// point.
|
||||||
|
///
|
||||||
class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate {
|
class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate {
|
||||||
final ThermionViewer viewer;
|
final ThermionViewer viewer;
|
||||||
late Future<Camera> _camera;
|
late Future<Camera> _camera;
|
||||||
final double minimumDistance;
|
final double minimumDistance;
|
||||||
Future<double?> Function(Vector3)? getDistanceToTarget;
|
late final Vector3 target;
|
||||||
|
|
||||||
Vector2 _queuedRotationDelta = Vector2.zero();
|
Vector2 _queuedRotationDelta = Vector2.zero();
|
||||||
double _queuedZoomDelta = 0.0;
|
double _queuedZoomDelta = 0.0;
|
||||||
|
|
||||||
static final _up = Vector3(0, 1, 0);
|
|
||||||
Timer? _updateTimer;
|
Timer? _updateTimer;
|
||||||
|
|
||||||
FixedOrbitRotateInputHandlerDelegate(
|
FixedOrbitRotateInputHandlerDelegate(
|
||||||
this.viewer, {
|
this.viewer, {
|
||||||
this.getDistanceToTarget,
|
Vector3? target,
|
||||||
this.minimumDistance = 10.0,
|
this.minimumDistance = 10.0,
|
||||||
}) {
|
}) {
|
||||||
|
this.target = target ?? Vector3.zero();
|
||||||
_camera = viewer.getMainCamera().then((Camera cam) async {
|
_camera = viewer.getMainCamera().then((Camera cam) async {
|
||||||
var viewMatrix = makeViewMatrix(Vector3(0.0, 0, -minimumDistance),
|
var viewMatrix = makeViewMatrix(Vector3(0.0, 0, -minimumDistance),
|
||||||
Vector3.zero(), Vector3(0.0, 1.0, 0.0));
|
this.target, Vector3(0.0, 1.0, 0.0));
|
||||||
viewMatrix.invert();
|
viewMatrix.invert();
|
||||||
|
|
||||||
await cam.setTransform(viewMatrix);
|
await cam.setTransform(viewMatrix);
|
||||||
@@ -81,99 +85,53 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate {
|
|||||||
var inverseProjectionMatrix = projectionMatrix.clone()..invert();
|
var inverseProjectionMatrix = projectionMatrix.clone()..invert();
|
||||||
Vector3 currentPosition = modelMatrix.getTranslation();
|
Vector3 currentPosition = modelMatrix.getTranslation();
|
||||||
|
|
||||||
Vector3 forward = -currentPosition.normalized();
|
Vector3 forward = modelMatrix.forward;
|
||||||
|
|
||||||
if (forward.length == 0) {
|
if (forward.length == 0) {
|
||||||
forward = Vector3(0, 0, -1);
|
forward = Vector3(0, 0, -1);
|
||||||
currentPosition = Vector3(0, 0, minimumDistance);
|
currentPosition = Vector3(0, 0, minimumDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 right = _up.cross(forward).normalized();
|
|
||||||
Vector3 up = forward.cross(right);
|
|
||||||
|
|
||||||
// Calculate the point where the camera forward ray intersects with the
|
|
||||||
// surface of the target sphere
|
|
||||||
var distanceToTarget =
|
|
||||||
(await getDistanceToTarget?.call(currentPosition)) ?? 0;
|
|
||||||
|
|
||||||
Vector3 intersection =
|
|
||||||
(-forward).scaled(currentPosition.length - distanceToTarget);
|
|
||||||
|
|
||||||
final intersectionInViewSpace = viewMatrix *
|
|
||||||
Vector4(intersection.x, intersection.y, intersection.z, 1.0);
|
|
||||||
final intersectionInClipSpace = projectionMatrix * intersectionInViewSpace;
|
|
||||||
final intersectionInNdcSpace =
|
|
||||||
intersectionInClipSpace / intersectionInClipSpace.w;
|
|
||||||
|
|
||||||
// Calculate new camera position based on rotation
|
|
||||||
final ndcX = 2 * ((-_queuedRotationDelta.x) / viewport.width);
|
|
||||||
final ndcY = 2 * ((_queuedRotationDelta.y) / viewport.height);
|
|
||||||
final ndc = Vector4(ndcX, ndcY, intersectionInNdcSpace.z, 1.0);
|
|
||||||
|
|
||||||
var clipSpace = Vector4(
|
|
||||||
ndc.x * intersectionInClipSpace.w,
|
|
||||||
ndcY * intersectionInClipSpace.w,
|
|
||||||
ndc.z * intersectionInClipSpace.w,
|
|
||||||
intersectionInClipSpace.w);
|
|
||||||
Vector4 cameraSpace = inverseProjectionMatrix * clipSpace;
|
|
||||||
Vector4 worldSpace = modelMatrix * cameraSpace;
|
|
||||||
|
|
||||||
var worldSpace3 = worldSpace.xyz.normalized() * currentPosition.length;
|
|
||||||
currentPosition = worldSpace3;
|
|
||||||
|
|
||||||
// Zoom
|
// Zoom
|
||||||
if (_queuedZoomDelta != 0.0) {
|
if (_queuedZoomDelta != 0.0) {
|
||||||
var distToIntersection =
|
var newPosition = currentPosition +
|
||||||
(currentPosition - intersection).length - minimumDistance;
|
(currentPosition - target).scaled(_queuedZoomDelta * 0.1);
|
||||||
|
|
||||||
|
var distToTarget = (newPosition - target).length;
|
||||||
|
|
||||||
// if we somehow overshot the minimum distance, reset the camera to the minimum distance
|
// if we somehow overshot the minimum distance, reset the camera to the minimum distance
|
||||||
if (distToIntersection < 0) {
|
if (distToTarget >= minimumDistance) {
|
||||||
currentPosition +=
|
currentPosition = newPosition;
|
||||||
(intersection.normalized().scaled(-distToIntersection * 10));
|
// Calculate view matrix
|
||||||
} else {
|
forward = (currentPosition - target).normalized();
|
||||||
bool zoomingOut = _queuedZoomDelta > 0;
|
var right = modelMatrix.up.cross(forward).normalized();
|
||||||
late Vector3 offset;
|
var up = forward.cross(right);
|
||||||
|
|
||||||
// when zooming, we don't always use fractions of the distance from
|
Matrix4 newViewMatrix = makeViewMatrix(currentPosition, target, up);
|
||||||
// the camera to the target (this is due to float precision issues at
|
newViewMatrix.invert();
|
||||||
// large distances, and also it doesn't work well for UI).
|
|
||||||
|
|
||||||
// if we're zooming out and the distance is less than 10m, we zoom out by 1 unit
|
await (await _camera).setModelMatrix(newViewMatrix);
|
||||||
if (zoomingOut) {
|
|
||||||
if (distToIntersection < 10) {
|
|
||||||
offset = intersection.normalized();
|
|
||||||
} else {
|
|
||||||
offset = intersection.normalized().scaled(distToIntersection / 10);
|
|
||||||
}
|
|
||||||
// if we're zooming in and the distance is less than 5m, zoom in by 1/2 the distance,
|
|
||||||
// otherwise 1/10 of the distance each time
|
|
||||||
} else {
|
|
||||||
if (distToIntersection < 5) {
|
|
||||||
offset = intersection.normalized().scaled(-distToIntersection / 2);
|
|
||||||
} else {
|
|
||||||
offset = intersection.normalized().scaled(-distToIntersection / 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset.length > distToIntersection) {
|
|
||||||
offset = Vector3.zero();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentPosition += offset;
|
|
||||||
}
|
}
|
||||||
|
} else if (_queuedRotationDelta.length != 0) {
|
||||||
|
double rotateX = _queuedRotationDelta.x * 0.01;
|
||||||
|
double rotateY = _queuedRotationDelta.y * 0.01;
|
||||||
|
|
||||||
|
var modelMatrix = await viewer.getCameraModelMatrix();
|
||||||
|
|
||||||
|
// for simplicity, we always assume a fixed coordinate system where
|
||||||
|
// we are rotating around world Y and camera X
|
||||||
|
var rot1 = Matrix4.identity()
|
||||||
|
..setRotation(Quaternion.axisAngle(Vector3(0, 1, 0), -rotateX)
|
||||||
|
.asRotationMatrix());
|
||||||
|
var rot2 = Matrix4.identity()
|
||||||
|
..setRotation(Quaternion.axisAngle(modelMatrix.right, rotateY)
|
||||||
|
.asRotationMatrix());
|
||||||
|
|
||||||
|
modelMatrix = rot1 *
|
||||||
|
rot2 * modelMatrix;
|
||||||
|
await (await _camera).setModelMatrix(modelMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate view matrix
|
|
||||||
forward = -currentPosition.normalized();
|
|
||||||
right = _up.cross(forward).normalized();
|
|
||||||
up = forward.cross(right);
|
|
||||||
|
|
||||||
Matrix4 newViewMatrix = makeViewMatrix(currentPosition, Vector3.zero(), up);
|
|
||||||
newViewMatrix.invert();
|
|
||||||
|
|
||||||
// Set the camera model matrix
|
|
||||||
var camera = await _camera;
|
|
||||||
await camera.setModelMatrix(newViewMatrix);
|
|
||||||
|
|
||||||
// Reset queued deltas
|
// Reset queued deltas
|
||||||
_queuedRotationDelta = Vector2.zero();
|
_queuedRotationDelta = Vector2.zero();
|
||||||
_queuedZoomDelta = 0.0;
|
_queuedZoomDelta = 0.0;
|
||||||
|
|||||||
Reference in New Issue
Block a user