gizmo & picking improvements

This commit is contained in:
Nick Fisher
2024-11-30 13:57:31 +08:00
parent c2077cb6b1
commit aa7350c419
5 changed files with 110 additions and 38 deletions

View File

@@ -6,10 +6,14 @@ class _Gizmo {
bool isVisible = false; bool isVisible = false;
final ThermionViewer viewer; final ThermionViewer viewer;
final nonPickable = <ThermionEntity>{}; final GizmoAsset _gizmo;
final GizmoAsset _asset;
_Gizmo(this._asset, this.viewer); ThermionEntity? _attachedTo;
_Gizmo(this._gizmo, this.viewer);
final _onEntityTransformUpdated = StreamController<
({ThermionEntity entity, Matrix4 transform})>.broadcast();
Axis? _active; Axis? _active;
Axis? get active => _active; Axis? get active => _active;
@@ -17,10 +21,10 @@ class _Gizmo {
Vector3? _activeCords; Vector3? _activeCords;
void checkHover(int x, int y) async { void checkHover(int x, int y) async {
_asset.pick(x, y, handler: (result, coords) async { _gizmo.pick(x, y, handler: (result, coords) async {
switch (result) { switch (result) {
case GizmoPickResultType.None: case GizmoPickResultType.None:
await _asset.unhighlight(); await _gizmo.unhighlight();
_active = null; _active = null;
break; break;
case GizmoPickResultType.AxisX: case GizmoPickResultType.AxisX:
@@ -43,29 +47,35 @@ class _Gizmo {
Matrix4? gizmoTransform; Matrix4? gizmoTransform;
Future attach(ThermionEntity entity) async { Future attach(ThermionEntity entity) async {
if (_asset.nonPickableEntities.contains(entity)) { print("Attached to ${entity}");
return;
if (_attachedTo != null && entity != _attachedTo) {
await viewer.setParent(_attachedTo!, 0);
} }
final transform = await viewer.getWorldTransform(entity);
transform.setRotation(Matrix3.identity()); _attachedTo = entity;
transform.setDiagonal(Vector4.all(1.0));
await viewer.setTransform(_asset.entity, transform); await viewer.setParent(_gizmo.entity, entity);
await viewer.setParent(entity, _asset.entity); await viewer.setTransform(_gizmo.entity, Matrix4.identity());
if (!isVisible) { if (!isVisible) {
await _asset.addToScene(); await _gizmo.addToScene();
isVisible = true; isVisible = true;
} }
gizmoTransform = await viewer.getWorldTransform(_asset.entity); gizmoTransform = await viewer.getWorldTransform(entity);
} }
Future detach() async { Future detach() async {
await _asset.removeFromScene(); await _gizmo.removeFromScene();
if (_attachedTo != null) {
await viewer.setParent(_attachedTo!, 0);
}
_active = null; _active = null;
isVisible = false; isVisible = false;
} }
void _updateTransform(Vector2 currentPosition, Vector2 delta) async { void _updateTransform(Vector2 currentPosition, Vector2 delta) async {
if (gizmoTransform == null) { if (_attachedTo == null) {
return; return;
} }
@@ -104,9 +114,13 @@ class _Gizmo {
Vector3 worldSpaceDelta = newPosition - gizmoTransform!.getTranslation(); Vector3 worldSpaceDelta = newPosition - gizmoTransform!.getTranslation();
worldSpaceDelta.multiply(_active!.asVector()); worldSpaceDelta.multiply(_active!.asVector());
gizmoTransform!.setTranslation(gizmoTransform!.getTranslation() + worldSpaceDelta); gizmoTransform!
.setTranslation(gizmoTransform!.getTranslation() + worldSpaceDelta);
await viewer.setTransform(_asset.entity, gizmoTransform!); await viewer.setTransform(_attachedTo!, gizmoTransform!);
_onEntityTransformUpdated
.add((entity: _attachedTo!, transform: gizmoTransform!));
} }
} }
@@ -115,11 +129,16 @@ class GizmoInputHandler extends InputHandler {
final ThermionViewer viewer; final ThermionViewer viewer;
late final _Gizmo translationGizmo; late final _Gizmo translationGizmo;
Stream<({ThermionEntity entity, Matrix4 transform})>
get onEntityTransformUpdated =>
translationGizmo._onEntityTransformUpdated.stream;
GizmoInputHandler({required this.wrapped, required this.viewer}) { GizmoInputHandler({required this.wrapped, required this.viewer}) {
initialize(); initialize();
} }
final _initialized = Completer<bool>(); final _initialized = Completer<bool>();
Future initialize() async { Future initialize() async {
if (_initialized.isCompleted) { if (_initialized.isCompleted) {
throw Exception("Already initialized"); throw Exception("Already initialized");
@@ -137,7 +156,7 @@ class GizmoInputHandler extends InputHandler {
@override @override
Future dispose() async { Future dispose() async {
await viewer.removeEntity(translationGizmo._asset); await viewer.removeEntity(translationGizmo._gizmo);
} }
@override @override
@@ -166,9 +185,21 @@ class GizmoInputHandler extends InputHandler {
if (!_initialized.isCompleted) { if (!_initialized.isCompleted) {
return; return;
} }
if (isMiddle) {
return;
}
await viewer.pick(localPosition.x.toInt(), localPosition.y.toInt(), await viewer.pick(localPosition.x.toInt(), localPosition.y.toInt(),
(result) { (result) async {
translationGizmo.attach(result.entity); if (translationGizmo._gizmo.isNonPickable(result.entity) ||
result.entity == FILAMENT_ENTITY_NULL) {
await translationGizmo.detach();
return;
}
if (!translationGizmo._gizmo.isGizmoEntity(result.entity)) {
translationGizmo.attach(result.entity);
}
}); });
} }
@@ -235,4 +266,25 @@ class GizmoInputHandler extends InputHandler {
void setActionForType(InputType gestureType, InputAction gestureAction) { void setActionForType(InputType gestureType, InputAction gestureAction) {
throw UnimplementedError(); throw UnimplementedError();
} }
Future detach(ThermionAsset asset) async {
if (translationGizmo._attachedTo == asset.entity) {
await translationGizmo.detach();
return;
}
final childEntities = await asset.getChildEntities();
for(final childEntity in childEntities) {
if(translationGizmo._attachedTo == childEntity) {
await translationGizmo.detach();
return;
}
}
}
// @override
// Future setCamera(Camera camera) async {
// await wrapped.setCamera(camera);
// }
} }

View File

@@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart'; import 'package:vector_math/vector_math_64.dart';
enum InputType { enum InputType {
@@ -37,8 +38,17 @@ abstract class InputHandler {
Future<void> onPointerMove( Future<void> onPointerMove(
Vector2 localPosition, Vector2 delta, bool isMiddle); Vector2 localPosition, Vector2 delta, bool isMiddle);
Future<void> onPointerUp(bool isMiddle); Future<void> onPointerUp(bool isMiddle);
Future<void> onScaleStart(Vector2 focalPoint, int pointerCount, Duration? sourceTimestamp); Future<void> onScaleStart(
Future<void> onScaleUpdate(Vector2 focalPoint, Vector2 focalPointDelta, double horizontalScale, double verticalScale, double scale, int pointerCount, double rotation, Duration? sourceTimestamp); Vector2 focalPoint, int pointerCount, Duration? sourceTimestamp);
Future<void> onScaleUpdate(
Vector2 focalPoint,
Vector2 focalPointDelta,
double horizontalScale,
double verticalScale,
double scale,
int pointerCount,
double rotation,
Duration? sourceTimestamp);
Future<void> onScaleEnd(int pointerCount, double velocity); Future<void> onScaleEnd(int pointerCount, double velocity);
Future<bool> get initialized; Future<bool> get initialized;
Future dispose(); Future dispose();
@@ -48,4 +58,6 @@ abstract class InputHandler {
void keyDown(PhysicalKey key); void keyDown(PhysicalKey key);
void keyUp(PhysicalKey key); void keyUp(PhysicalKey key);
// Future setCamera(Camera camera);
} }

View File

@@ -9,6 +9,7 @@ import 'ffi_view.dart';
class FFIGizmo extends FFIAsset implements GizmoAsset { class FFIGizmo extends FFIAsset implements GizmoAsset {
final Set<ThermionEntity> nonPickableEntities; final Set<ThermionEntity> nonPickableEntities;
final Set<ThermionEntity> gizmoEntities;
late NativeCallable<GizmoPickCallbackFunction> _nativeCallback; late NativeCallable<GizmoPickCallbackFunction> _nativeCallback;
void Function(GizmoPickResultType axis, Vector3 coords)? _callback; void Function(GizmoPickResultType axis, Vector3 coords)? _callback;
@@ -19,14 +20,18 @@ class FFIGizmo extends FFIAsset implements GizmoAsset {
_callback?.call(GizmoPickResultType.values[resultType], Vector3(x, y, z)); _callback?.call(GizmoPickResultType.values[resultType], Vector3(x, y, z));
} }
bool isNonPickable(ThermionEntity entity) =>
nonPickableEntities.contains(entity);
bool isGizmoEntity(ThermionEntity entity) => gizmoEntities.contains(entity);
FFIGizmo( FFIGizmo(
this._view, this._view,
super.pointer, super.pointer,
super.sceneManager, super.sceneManager,
super.renderableManager, super.renderableManager,
super.unlitMaterialProvider, super.unlitMaterialProvider,
this.nonPickableEntities this.nonPickableEntities,
) { this.gizmoEntities) {
_nativeCallback = _nativeCallback =
NativeCallable<GizmoPickCallbackFunction>.listener(_onPickResult); NativeCallable<GizmoPickCallbackFunction>.listener(_onPickResult);
} }

View File

@@ -81,5 +81,6 @@ abstract class GizmoAsset extends ThermionAsset {
{Future Function(GizmoPickResultType axis, Vector3 coords)? handler}); {Future Function(GizmoPickResultType axis, Vector3 coords)? handler});
Future highlight(Axis axis); Future highlight(Axis axis);
Future unhighlight(); Future unhighlight();
Set<ThermionEntity> get nonPickableEntities; bool isNonPickable(ThermionEntity entity);
bool isGizmoEntity(ThermionEntity entity);
} }

View File

@@ -28,11 +28,12 @@ namespace thermion
auto parent = entityManager.create(); auto parent = entityManager.create();
auto *parentMaterialInstance = _material->createInstance(); // auto *parentMaterialInstance = _material->createInstance();
parentMaterialInstance->setParameter("baseColorFactor", math::float4{0.0f, 1.0f, 1.0f, 1.0f}); // parentMaterialInstance->setParameter("baseColorFactor", math::float4{1.0f, 1.0f, 1.0f, 0.0f});
parentMaterialInstance->setParameter("scale", 4.0f); // parentMaterialInstance->setParameter("scale", 4.0f);
// parentMaterialInstance->setDoubleSided(false);
_materialInstances.push_back(parentMaterialInstance); // _materialInstances.push_back(parentMaterialInstance);
// Create center cube vertices // Create center cube vertices
float centerCubeSize = 0.1f; float centerCubeSize = 0.1f;
@@ -73,11 +74,11 @@ namespace thermion
RenderableManager::Builder(1) RenderableManager::Builder(1)
.boundingBox({{-centerCubeSize, -centerCubeSize, -centerCubeSize}, .boundingBox({{-centerCubeSize, -centerCubeSize, -centerCubeSize},
{centerCubeSize, centerCubeSize, centerCubeSize}}) {centerCubeSize, centerCubeSize, centerCubeSize}})
.material(0, parentMaterialInstance) // .material(0, parentMaterialInstance)
.layerMask(0xFF, 1u << SceneManager::LAYERS::OVERLAY) .layerMask(0xFF, 1u << SceneManager::LAYERS::OVERLAY)
.priority(7) .priority(0)
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, centerCubeVb, centerCubeIb, 0, 36) .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, centerCubeVb, centerCubeIb, 0, 36)
.culling(false) .culling(true)
.build(*_engine, parent); .build(*_engine, parent);
auto parentTransformInstance = transformManager.getInstance(parent); auto parentTransformInstance = transformManager.getInstance(parent);
@@ -265,6 +266,7 @@ namespace thermion
_materialInstances.push_back(materialInstance); _materialInstances.push_back(materialInstance);
materialInstance->setParameter("baseColorFactor", math::float4{0.0f, 0.0f, 0.0f, 0.0f}); materialInstance->setParameter("baseColorFactor", math::float4{0.0f, 0.0f, 0.0f, 0.0f});
materialInstance->setParameter("scale", 4.0f); materialInstance->setParameter("scale", 4.0f);
materialInstance->setDepthFunc(MaterialInstance::DepthFunc::A);
RenderableManager::Builder(1) RenderableManager::Builder(1)
.boundingBox({{-volumeWidth / 2, -volumeDepth / 2, 0}, {volumeWidth / 2, volumeDepth / 2, volumeLength}}) .boundingBox({{-volumeWidth / 2, -volumeDepth / 2, 0}, {volumeWidth / 2, volumeDepth / 2, volumeLength}})