feat: rotation gizmo improvements
This commit is contained in:
Binary file not shown.
@@ -1,16 +1,16 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
import 'package:thermion_dart/thermion_dart.dart';
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
|
|
||||||
class _Gizmo {
|
class _Gizmo {
|
||||||
bool isVisible = false;
|
bool isVisible = false;
|
||||||
|
|
||||||
final ThermionViewer viewer;
|
final ThermionViewer viewer;
|
||||||
final GizmoAsset _gizmo;
|
final GizmoAsset _gizmo;
|
||||||
|
|
||||||
ThermionEntity? _attachedTo;
|
ThermionEntity? _attachedTo;
|
||||||
|
final GizmoType type;
|
||||||
|
|
||||||
_Gizmo(this._gizmo, this.viewer);
|
_Gizmo(this._gizmo, this.viewer, this.type);
|
||||||
|
|
||||||
final _onEntityTransformUpdated = StreamController<
|
final _onEntityTransformUpdated = StreamController<
|
||||||
({ThermionEntity entity, Matrix4 transform})>.broadcast();
|
({ThermionEntity entity, Matrix4 transform})>.broadcast();
|
||||||
@@ -18,7 +18,21 @@ class _Gizmo {
|
|||||||
Axis? _active;
|
Axis? _active;
|
||||||
Axis? get active => _active;
|
Axis? get active => _active;
|
||||||
|
|
||||||
Vector3? _activeCords;
|
|
||||||
|
double _getAngleBetweenVectors(Vector2 v1, Vector2 v2) {
|
||||||
|
// Normalize vectors to ensure consistent rotation regardless of distance from center
|
||||||
|
v1.normalize();
|
||||||
|
v2.normalize();
|
||||||
|
|
||||||
|
// Calculate angle using atan2
|
||||||
|
double angle = atan2(v2.y, v2.x) - atan2(v1.y, v1.x);
|
||||||
|
|
||||||
|
// Ensure angle is between -π and π
|
||||||
|
if (angle > pi) angle -= 2 * pi;
|
||||||
|
if (angle < -pi) angle += 2 * pi;
|
||||||
|
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
|
||||||
void checkHover(int x, int y) async {
|
void checkHover(int x, int y) async {
|
||||||
_gizmo.pick(x, y, handler: (result, coords) async {
|
_gizmo.pick(x, y, handler: (result, coords) async {
|
||||||
@@ -29,16 +43,10 @@ class _Gizmo {
|
|||||||
break;
|
break;
|
||||||
case GizmoPickResultType.AxisX:
|
case GizmoPickResultType.AxisX:
|
||||||
_active = Axis.X;
|
_active = Axis.X;
|
||||||
_activeCords = coords;
|
|
||||||
|
|
||||||
case GizmoPickResultType.AxisY:
|
case GizmoPickResultType.AxisY:
|
||||||
_active = Axis.Y;
|
_active = Axis.Y;
|
||||||
_activeCords = coords;
|
|
||||||
|
|
||||||
case GizmoPickResultType.AxisZ:
|
case GizmoPickResultType.AxisZ:
|
||||||
_active = Axis.Z;
|
_active = Axis.Z;
|
||||||
_activeCords = coords;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -79,11 +87,20 @@ class _Gizmo {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == GizmoType.translation) {
|
||||||
|
await _updateTranslation(currentPosition, delta);
|
||||||
|
} else if (type == GizmoType.rotation) {
|
||||||
|
await _updateRotation(currentPosition, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onEntityTransformUpdated
|
||||||
|
.add((entity: _attachedTo!, transform: gizmoTransform!));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _updateTranslation(Vector2 currentPosition, Vector2 delta) async {
|
||||||
var view = await viewer.getViewAt(0);
|
var view = await viewer.getViewAt(0);
|
||||||
var camera = await viewer.getActiveCamera();
|
var camera = await viewer.getActiveCamera();
|
||||||
|
|
||||||
var viewport = await view.getViewport();
|
var viewport = await view.getViewport();
|
||||||
|
|
||||||
var projectionMatrix = await viewer.getCameraProjectionMatrix();
|
var projectionMatrix = await viewer.getCameraProjectionMatrix();
|
||||||
var viewMatrix = await camera.getViewMatrix();
|
var viewMatrix = await camera.getViewMatrix();
|
||||||
var inverseViewMatrix = await camera.getModelMatrix();
|
var inverseViewMatrix = await camera.getModelMatrix();
|
||||||
@@ -98,13 +115,17 @@ class _Gizmo {
|
|||||||
|
|
||||||
var gizmoNdc = gizmoClipSpace / gizmoClipSpace.w;
|
var gizmoNdc = gizmoClipSpace / gizmoClipSpace.w;
|
||||||
|
|
||||||
var gizmoScreenSpace = Vector2(((gizmoNdc.x / 2) + 0.5) * viewport.width,
|
var gizmoScreenSpace = Vector2(
|
||||||
|
((gizmoNdc.x / 2) + 0.5) * viewport.width,
|
||||||
viewport.height - (((gizmoNdc.y / 2) + 0.5) * viewport.height));
|
viewport.height - (((gizmoNdc.y / 2) + 0.5) * viewport.height));
|
||||||
|
|
||||||
gizmoScreenSpace += delta;
|
gizmoScreenSpace += delta;
|
||||||
|
|
||||||
gizmoNdc = Vector4(((gizmoScreenSpace.x / viewport.width) - 0.5) * 2,
|
gizmoNdc = Vector4(
|
||||||
(((gizmoScreenSpace.y / viewport.height)) - 0.5) * -2, gizmoNdc.z, 1.0);
|
((gizmoScreenSpace.x / viewport.width) - 0.5) * 2,
|
||||||
|
(((gizmoScreenSpace.y / viewport.height)) - 0.5) * -2,
|
||||||
|
gizmoNdc.z,
|
||||||
|
1.0);
|
||||||
|
|
||||||
var gizmoViewSpace = inverseProjectionMatrix * gizmoNdc;
|
var gizmoViewSpace = inverseProjectionMatrix * gizmoNdc;
|
||||||
gizmoViewSpace /= gizmoViewSpace.w;
|
gizmoViewSpace /= gizmoViewSpace.w;
|
||||||
@@ -118,12 +139,79 @@ class _Gizmo {
|
|||||||
.setTranslation(gizmoTransform!.getTranslation() + worldSpaceDelta);
|
.setTranslation(gizmoTransform!.getTranslation() + worldSpaceDelta);
|
||||||
|
|
||||||
await viewer.setTransform(_attachedTo!, gizmoTransform!);
|
await viewer.setTransform(_attachedTo!, gizmoTransform!);
|
||||||
|
}
|
||||||
|
|
||||||
_onEntityTransformUpdated
|
Future<void> _updateRotation(Vector2 currentPosition, Vector2 delta) async {
|
||||||
.add((entity: _attachedTo!, transform: gizmoTransform!));
|
|
||||||
|
var view = await viewer.getViewAt(0);
|
||||||
|
var camera = await viewer.getActiveCamera();
|
||||||
|
var viewport = await view.getViewport();
|
||||||
|
var projectionMatrix = await viewer.getCameraProjectionMatrix();
|
||||||
|
var viewMatrix = await camera.getViewMatrix();
|
||||||
|
|
||||||
|
// Get gizmo center in screen space
|
||||||
|
var gizmoPositionWorldSpace = gizmoTransform!.getTranslation();
|
||||||
|
Vector4 gizmoClipSpace = projectionMatrix *
|
||||||
|
viewMatrix *
|
||||||
|
Vector4(gizmoPositionWorldSpace.x, gizmoPositionWorldSpace.y,
|
||||||
|
gizmoPositionWorldSpace.z, 1.0);
|
||||||
|
|
||||||
|
var gizmoNdc = gizmoClipSpace / gizmoClipSpace.w;
|
||||||
|
var gizmoScreenSpace = Vector2(
|
||||||
|
((gizmoNdc.x / 2) + 0.5) * viewport.width,
|
||||||
|
viewport.height - (((gizmoNdc.y / 2) + 0.5) * viewport.height));
|
||||||
|
|
||||||
|
// Calculate vectors from gizmo center to previous and current mouse positions
|
||||||
|
var prevVector = (currentPosition - delta) - gizmoScreenSpace;
|
||||||
|
var currentVector = currentPosition - gizmoScreenSpace;
|
||||||
|
|
||||||
|
// Calculate rotation angle based on the active axis
|
||||||
|
double rotationAngle = 0.0;
|
||||||
|
switch (_active) {
|
||||||
|
case Axis.X:
|
||||||
|
// For X axis, project onto YZ plane
|
||||||
|
var prev = Vector2(prevVector.y, -prevVector.x);
|
||||||
|
var curr = Vector2(currentVector.y, -currentVector.x);
|
||||||
|
rotationAngle = _getAngleBetweenVectors(prev, curr);
|
||||||
|
break;
|
||||||
|
case Axis.Y:
|
||||||
|
// For Y axis, project onto XZ plane
|
||||||
|
var prev = Vector2(prevVector.x, -prevVector.y);
|
||||||
|
var curr = Vector2(currentVector.x, -currentVector.y);
|
||||||
|
rotationAngle = _getAngleBetweenVectors(prev, curr);
|
||||||
|
break;
|
||||||
|
case Axis.Z:
|
||||||
|
// For Z axis, use screen plane directly
|
||||||
|
rotationAngle = -1 * _getAngleBetweenVectors(prevVector, currentVector);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create rotation matrix based on the active axis
|
||||||
|
var rotationMatrix = Matrix4.identity();
|
||||||
|
switch (_active) {
|
||||||
|
case Axis.X:
|
||||||
|
rotationMatrix.setRotationX(rotationAngle);
|
||||||
|
break;
|
||||||
|
case Axis.Y:
|
||||||
|
rotationMatrix.setRotationY(rotationAngle);
|
||||||
|
break;
|
||||||
|
case Axis.Z:
|
||||||
|
rotationMatrix.setRotationZ(rotationAngle);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply rotation to the current transform
|
||||||
|
gizmoTransform = rotationMatrix * gizmoTransform!;
|
||||||
|
await viewer.setTransform(_attachedTo!, gizmoTransform!);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class GizmoInputHandler extends InputHandler {
|
class GizmoInputHandler extends InputHandler {
|
||||||
final InputHandler wrapped;
|
final InputHandler wrapped;
|
||||||
final ThermionViewer viewer;
|
final ThermionViewer viewer;
|
||||||
@@ -151,9 +239,9 @@ class GizmoInputHandler extends InputHandler {
|
|||||||
final view = await viewer.getViewAt(0);
|
final view = await viewer.getViewAt(0);
|
||||||
|
|
||||||
var tg = await viewer.createGizmo(view, GizmoType.translation);
|
var tg = await viewer.createGizmo(view, GizmoType.translation);
|
||||||
this.translationGizmo = _Gizmo(tg, viewer);
|
this.translationGizmo = _Gizmo(tg, viewer, GizmoType.translation);
|
||||||
var rg = await viewer.createGizmo(view, GizmoType.rotation);
|
var rg = await viewer.createGizmo(view, GizmoType.rotation);
|
||||||
this.rotationGizmo = _Gizmo(rg, viewer);
|
this.rotationGizmo = _Gizmo(rg, viewer, GizmoType.rotation);
|
||||||
|
|
||||||
_initialized.complete(true);
|
_initialized.complete(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ ROTATION_GIZMO_GLB_PACKAGE:
|
|||||||
ROTATION_GIZMO_GLB_ROTATION_GIZMO_OFFSET:
|
ROTATION_GIZMO_GLB_ROTATION_GIZMO_OFFSET:
|
||||||
.int 0
|
.int 0
|
||||||
ROTATION_GIZMO_GLB_ROTATION_GIZMO_SIZE:
|
ROTATION_GIZMO_GLB_ROTATION_GIZMO_SIZE:
|
||||||
.int 78876
|
.int 163152
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ _ROTATION_GIZMO_GLB_PACKAGE:
|
|||||||
_ROTATION_GIZMO_GLB_ROTATION_GIZMO_OFFSET:
|
_ROTATION_GIZMO_GLB_ROTATION_GIZMO_OFFSET:
|
||||||
.int 0
|
.int 0
|
||||||
_ROTATION_GIZMO_GLB_ROTATION_GIZMO_SIZE:
|
_ROTATION_GIZMO_GLB_ROTATION_GIZMO_SIZE:
|
||||||
.int 78876
|
.int 163152
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -155,7 +155,7 @@ namespace thermion
|
|||||||
View *_view;
|
View *_view;
|
||||||
Material *_material;
|
Material *_material;
|
||||||
|
|
||||||
float _scale = 8.0f;
|
float _scale = 10.0f;
|
||||||
|
|
||||||
utils::Entity _parent;
|
utils::Entity _parent;
|
||||||
std::vector<SceneAsset *> _axes;
|
std::vector<SceneAsset *> _axes;
|
||||||
|
|||||||
Reference in New Issue
Block a user