From d6751b015155bdd7a713b10a69579a5ca0ba5045 Mon Sep 17 00:00:00 2001 From: Daan Vanden Bosch Date: Tue, 13 Apr 2021 21:35:07 +0200 Subject: [PATCH] You can now force panning/rotating mode by holding ctrl while clicking and dragging the mouse. --- .../rendering/OrbitalCameraInputManager.kt | 35 ++++++++++++++++++- .../web/externals/three/OrbitControls.kt | 7 ++++ .../phantasmal/web/externals/three/three.kt | 7 ++++ .../rendering/input/QuestInputManager.kt | 2 +- .../rendering/input/state/IdleState.kt | 23 +++++++++--- 5 files changed, 68 insertions(+), 6 deletions(-) diff --git a/web/src/main/kotlin/world/phantasmal/web/core/rendering/OrbitalCameraInputManager.kt b/web/src/main/kotlin/world/phantasmal/web/core/rendering/OrbitalCameraInputManager.kt index de94f2c4..f3b37ae0 100644 --- a/web/src/main/kotlin/world/phantasmal/web/core/rendering/OrbitalCameraInputManager.kt +++ b/web/src/main/kotlin/world/phantasmal/web/core/rendering/OrbitalCameraInputManager.kt @@ -1,8 +1,11 @@ package world.phantasmal.web.core.rendering import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.pointerevents.PointerEvent +import world.phantasmal.core.disposable.Disposable import world.phantasmal.core.disposable.TrackedDisposable import world.phantasmal.web.externals.three.* +import world.phantasmal.webui.dom.disposableListener import world.phantasmal.webui.obj import kotlin.math.ceil import kotlin.math.floor @@ -15,7 +18,10 @@ class OrbitalCameraInputManager( screenSpacePanning: Boolean, enableRotate: Boolean = true, ) : TrackedDisposable(), InputManager { - private val controls = OrbitControls(camera, canvas) + private lateinit var controls: OrbitControls + + @Suppress("JoinDeclarationAndAssignment") + private val pointerDownListener: Disposable var enabled: Boolean get() = controls.enabled @@ -24,12 +30,38 @@ class OrbitalCameraInputManager( } init { + // Switch mouse button actions when certain modifier keys are pressed to counteract + // OrbitControls switching left and right click behavior in that case. + pointerDownListener = canvas.disposableListener("pointerdown", { + if (it.ctrlKey || it.metaKey || it.shiftKey) { + controls.mouseButtons = obj { + LEFT = MOUSE.ROTATE + MIDDLE = MOUSE.DOLLY + RIGHT = MOUSE.PAN + } + } else { + controls.mouseButtons = obj { + LEFT = MOUSE.PAN + MIDDLE = MOUSE.DOLLY + RIGHT = MOUSE.ROTATE + } + } + }) + + // Ensure OrbitControls is instantiated after the pointerdown event listener is attached. + controls = OrbitControls(camera, canvas) + controls.mouseButtons = obj { LEFT = MOUSE.PAN MIDDLE = MOUSE.DOLLY RIGHT = MOUSE.ROTATE } + controls.touches = obj { + ONE = TOUCH.PAN + TWO = TOUCH.DOLLY_ROTATE + } + camera.position.copy(position) controls.screenSpacePanning = screenSpacePanning controls.enableRotate = enableRotate @@ -40,6 +72,7 @@ class OrbitalCameraInputManager( override fun dispose() { controls.dispose() + pointerDownListener.dispose() super.dispose() } diff --git a/web/src/main/kotlin/world/phantasmal/web/externals/three/OrbitControls.kt b/web/src/main/kotlin/world/phantasmal/web/externals/three/OrbitControls.kt index 8c047227..38714cc2 100644 --- a/web/src/main/kotlin/world/phantasmal/web/externals/three/OrbitControls.kt +++ b/web/src/main/kotlin/world/phantasmal/web/externals/three/OrbitControls.kt @@ -12,6 +12,11 @@ external interface OrbitControlsMouseButtons { var RIGHT: MOUSE } +external interface OrbitControlsMouseTouches { + var ONE: TOUCH + var TWO: TOUCH +} + external class OrbitControls(`object`: Camera, domElement: HTMLElement = definedExternally) { var enabled: Boolean var enablePan: Boolean @@ -23,6 +28,8 @@ external class OrbitControls(`object`: Camera, domElement: HTMLElement = defined var mouseButtons: OrbitControlsMouseButtons + var touches: OrbitControlsMouseTouches + fun update(): Boolean fun saveState() diff --git a/web/src/main/kotlin/world/phantasmal/web/externals/three/three.kt b/web/src/main/kotlin/world/phantasmal/web/externals/three/three.kt index 18bb8810..efffd1d2 100644 --- a/web/src/main/kotlin/world/phantasmal/web/externals/three/three.kt +++ b/web/src/main/kotlin/world/phantasmal/web/externals/three/three.kt @@ -744,6 +744,13 @@ external enum class MOUSE { PAN, } +external enum class TOUCH { + ROTATE, + PAN, + DOLLY_PAN, + DOLLY_ROTATE, +} + external class Raycaster( origin: Vector3 = definedExternally, direction: Vector3 = definedExternally, diff --git a/web/src/main/kotlin/world/phantasmal/web/questEditor/rendering/input/QuestInputManager.kt b/web/src/main/kotlin/world/phantasmal/web/questEditor/rendering/input/QuestInputManager.kt index 9e0c92e3..e0d7c762 100644 --- a/web/src/main/kotlin/world/phantasmal/web/questEditor/rendering/input/QuestInputManager.kt +++ b/web/src/main/kotlin/world/phantasmal/web/questEditor/rendering/input/QuestInputManager.kt @@ -73,7 +73,7 @@ class QuestInputManager( renderContext.canvas.observeEntityDrop(::onEntityDrop), ) - // Ensure OrbitalCameraControls attaches its listeners after ours. + // Ensure OrbitalCameraControls attaches its listeners after we've attached ours. cameraInputManager = OrbitalCameraInputManager( renderContext.canvas, renderContext.camera, diff --git a/web/src/main/kotlin/world/phantasmal/web/questEditor/rendering/input/state/IdleState.kt b/web/src/main/kotlin/world/phantasmal/web/questEditor/rendering/input/state/IdleState.kt index 5ecc9af5..0744411d 100644 --- a/web/src/main/kotlin/world/phantasmal/web/questEditor/rendering/input/state/IdleState.kt +++ b/web/src/main/kotlin/world/phantasmal/web/questEditor/rendering/input/state/IdleState.kt @@ -19,9 +19,18 @@ class IdleState( private var shouldCheckHighlight = false override fun processEvent(event: Evt): State { + // Don't highlight or manipulate entities in forced panning/rotating mode. + val forcedPanningRotatingMode = when (event) { + is KeyboardEvt -> event.key == "Control" + is PointerEvt -> event.ctrlKey + else -> false + } + when (event) { is KeyboardEvt -> { - if (entityManipulationEnabled) { + if (forcedPanningRotatingMode) { + ctx.setHighlightedEntity(null) + } else if (entityManipulationEnabled) { val quest = ctx.quest.value val entity = ctx.selectedEntity.value @@ -32,7 +41,9 @@ class IdleState( } is PointerDownEvt -> { - val pick = pickEntity(event.pointerDevicePosition) + val pick = + if (forcedPanningRotatingMode) null + else pickEntity(event.pointerDevicePosition) when (event.buttons) { 1 -> { @@ -93,8 +104,12 @@ class IdleState( is PointerMoveEvt -> { if (!panning && !rotating && !zooming) { // User is hovering. - pointerDevicePosition.copy(event.pointerDevicePosition) - shouldCheckHighlight = true + if (forcedPanningRotatingMode) { + ctx.setHighlightedEntity(null) + } else { + pointerDevicePosition.copy(event.pointerDevicePosition) + shouldCheckHighlight = true + } } }