You can now force panning/rotating mode by holding ctrl while clicking and dragging the mouse.

This commit is contained in:
Daan Vanden Bosch 2021-04-13 21:35:07 +02:00
parent 585ce44667
commit d6751b0151
5 changed files with 68 additions and 6 deletions

View File

@ -1,8 +1,11 @@
package world.phantasmal.web.core.rendering package world.phantasmal.web.core.rendering
import org.w3c.dom.HTMLCanvasElement 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.core.disposable.TrackedDisposable
import world.phantasmal.web.externals.three.* import world.phantasmal.web.externals.three.*
import world.phantasmal.webui.dom.disposableListener
import world.phantasmal.webui.obj import world.phantasmal.webui.obj
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.floor import kotlin.math.floor
@ -15,7 +18,10 @@ class OrbitalCameraInputManager(
screenSpacePanning: Boolean, screenSpacePanning: Boolean,
enableRotate: Boolean = true, enableRotate: Boolean = true,
) : TrackedDisposable(), InputManager { ) : TrackedDisposable(), InputManager {
private val controls = OrbitControls(camera, canvas) private lateinit var controls: OrbitControls
@Suppress("JoinDeclarationAndAssignment")
private val pointerDownListener: Disposable
var enabled: Boolean var enabled: Boolean
get() = controls.enabled get() = controls.enabled
@ -24,11 +30,37 @@ class OrbitalCameraInputManager(
} }
init { 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<PointerEvent>("pointerdown", {
if (it.ctrlKey || it.metaKey || it.shiftKey) {
controls.mouseButtons = obj {
LEFT = MOUSE.ROTATE
MIDDLE = MOUSE.DOLLY
RIGHT = MOUSE.PAN
}
} else {
controls.mouseButtons = obj { controls.mouseButtons = obj {
LEFT = MOUSE.PAN LEFT = MOUSE.PAN
MIDDLE = MOUSE.DOLLY MIDDLE = MOUSE.DOLLY
RIGHT = MOUSE.ROTATE 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) camera.position.copy(position)
controls.screenSpacePanning = screenSpacePanning controls.screenSpacePanning = screenSpacePanning
@ -40,6 +72,7 @@ class OrbitalCameraInputManager(
override fun dispose() { override fun dispose() {
controls.dispose() controls.dispose()
pointerDownListener.dispose()
super.dispose() super.dispose()
} }

View File

@ -12,6 +12,11 @@ external interface OrbitControlsMouseButtons {
var RIGHT: MOUSE var RIGHT: MOUSE
} }
external interface OrbitControlsMouseTouches {
var ONE: TOUCH
var TWO: TOUCH
}
external class OrbitControls(`object`: Camera, domElement: HTMLElement = definedExternally) { external class OrbitControls(`object`: Camera, domElement: HTMLElement = definedExternally) {
var enabled: Boolean var enabled: Boolean
var enablePan: Boolean var enablePan: Boolean
@ -23,6 +28,8 @@ external class OrbitControls(`object`: Camera, domElement: HTMLElement = defined
var mouseButtons: OrbitControlsMouseButtons var mouseButtons: OrbitControlsMouseButtons
var touches: OrbitControlsMouseTouches
fun update(): Boolean fun update(): Boolean
fun saveState() fun saveState()

View File

@ -744,6 +744,13 @@ external enum class MOUSE {
PAN, PAN,
} }
external enum class TOUCH {
ROTATE,
PAN,
DOLLY_PAN,
DOLLY_ROTATE,
}
external class Raycaster( external class Raycaster(
origin: Vector3 = definedExternally, origin: Vector3 = definedExternally,
direction: Vector3 = definedExternally, direction: Vector3 = definedExternally,

View File

@ -73,7 +73,7 @@ class QuestInputManager(
renderContext.canvas.observeEntityDrop(::onEntityDrop), renderContext.canvas.observeEntityDrop(::onEntityDrop),
) )
// Ensure OrbitalCameraControls attaches its listeners after ours. // Ensure OrbitalCameraControls attaches its listeners after we've attached ours.
cameraInputManager = OrbitalCameraInputManager( cameraInputManager = OrbitalCameraInputManager(
renderContext.canvas, renderContext.canvas,
renderContext.camera, renderContext.camera,

View File

@ -19,9 +19,18 @@ class IdleState(
private var shouldCheckHighlight = false private var shouldCheckHighlight = false
override fun processEvent(event: Evt): State { 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) { when (event) {
is KeyboardEvt -> { is KeyboardEvt -> {
if (entityManipulationEnabled) { if (forcedPanningRotatingMode) {
ctx.setHighlightedEntity(null)
} else if (entityManipulationEnabled) {
val quest = ctx.quest.value val quest = ctx.quest.value
val entity = ctx.selectedEntity.value val entity = ctx.selectedEntity.value
@ -32,7 +41,9 @@ class IdleState(
} }
is PointerDownEvt -> { is PointerDownEvt -> {
val pick = pickEntity(event.pointerDevicePosition) val pick =
if (forcedPanningRotatingMode) null
else pickEntity(event.pointerDevicePosition)
when (event.buttons) { when (event.buttons) {
1 -> { 1 -> {
@ -93,10 +104,14 @@ class IdleState(
is PointerMoveEvt -> { is PointerMoveEvt -> {
if (!panning && !rotating && !zooming) { if (!panning && !rotating && !zooming) {
// User is hovering. // User is hovering.
if (forcedPanningRotatingMode) {
ctx.setHighlightedEntity(null)
} else {
pointerDevicePosition.copy(event.pointerDevicePosition) pointerDevicePosition.copy(event.pointerDevicePosition)
shouldCheckHighlight = true shouldCheckHighlight = true
} }
} }
}
is PointerOutEvt -> { is PointerOutEvt -> {
ctx.setHighlightedEntity(null) ctx.setHighlightedEntity(null)