Skip to content

Commit

Permalink
feat: Add planar movement (#882)
Browse files Browse the repository at this point in the history
* feat: Create a new FREE Gizmo

* feat: Toggle gizmos beween free and the selected in the toolbar

* feat: Add plannar movement

* fix: Missing snapValue for Free Gizmo

* fix: tests
  • Loading branch information
cyaiox authored Jan 31, 2024
1 parent ece036d commit a988b80
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 10 deletions.
15 changes: 12 additions & 3 deletions packages/@dcl/inspector/src/components/Toolbar/Gizmos/Gizmos.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,18 @@ export const Gizmos = withSdk(({ sdk }) => {

const [selection, setSelection] = useComponentValue(entity || ROOT, sdk.components.Selection)

const handlePositionGizmo = useCallback(() => setSelection({ gizmo: GizmoType.POSITION }), [setSelection])
const handleRotationGizmo = useCallback(() => setSelection({ gizmo: GizmoType.ROTATION }), [setSelection])
const handleScaleGizmo = useCallback(() => setSelection({ gizmo: GizmoType.SCALE }), [setSelection])
const handlePositionGizmo = useCallback(
() => setSelection({ gizmo: selection.gizmo !== GizmoType.POSITION ? GizmoType.POSITION : GizmoType.FREE }),
[selection, setSelection]
)
const handleRotationGizmo = useCallback(
() => setSelection({ gizmo: selection.gizmo !== GizmoType.ROTATION ? GizmoType.ROTATION : GizmoType.FREE }),
[selection, setSelection]
)
const handleScaleGizmo = useCallback(
() => setSelection({ gizmo: selection.gizmo !== GizmoType.SCALE ? GizmoType.SCALE : GizmoType.FREE }),
[selection, setSelection]
)

const {
isPositionGizmoWorldAligned,
Expand Down
1 change: 1 addition & 0 deletions packages/@dcl/inspector/src/hooks/editor/useSnap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { GizmoType } from '../../lib/utils/gizmo'
function getSnapValue(gizmo: GizmoType) {
switch (gizmo) {
case GizmoType.POSITION:
case GizmoType.FREE:
return snapManager.getPositionSnap()
case GizmoType.ROTATION:
return Math.round(snapManager.getRotationSnap() * (180 / Math.PI))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,12 @@ describe('GizmoManager', () => {
})
describe('When getting the gizmo types', () => {
it('should return the gizmo types', () => {
expect(gizmos.getGizmoTypes()).toEqual([GizmoType.POSITION, GizmoType.ROTATION, GizmoType.SCALE])
expect(gizmos.getGizmoTypes()).toEqual([
GizmoType.POSITION,
GizmoType.ROTATION,
GizmoType.SCALE,
GizmoType.FREE
])
})
})
describe('When setting the gizmo type', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import mitt from 'mitt'
import { IAxisDragGizmo, PickingInfo, Quaternion, Node } from '@babylonjs/core'
import {
IAxisDragGizmo,
PickingInfo,
Quaternion,
Node,
Vector3,
PointerDragBehavior,
AbstractMesh
} from '@babylonjs/core'
import { Entity, TransformType } from '@dcl/ecs'
import { Vector3 as DclVector3, Quaternion as DclQuaternion } from '@dcl/ecs-math'
import { GizmoType } from '../../utils/gizmo'
Expand Down Expand Up @@ -54,8 +62,12 @@ export function createGizmoManager(context: SceneContext) {
let isEnabled = true
const parentMapper: Map<Entity, Node> = new Map()

function getSelectedEntities() {
return context.operations.getSelectedEntities()
}

function areMultipleEntitiesSelected() {
return context.operations.getSelectedEntities().length > 1
return getSelectedEntities().length > 1
}

function fixRotationGizmoAlignment(value: TransformType) {
Expand Down Expand Up @@ -142,7 +154,7 @@ export function createGizmoManager(context: SceneContext) {

// Update entity transform for all the selected entities
if (areMultipleEntitiesSelected()) {
for (const entityId of context.operations.getSelectedEntities()) {
for (const entityId of getSelectedEntities()) {
if (entityId === lastEntity.entityId) continue
const entity = context.getEntityOrNull(entityId)!
const transform = getTransform(entity)
Expand All @@ -154,7 +166,7 @@ export function createGizmoManager(context: SceneContext) {
function initTransform() {
if (lastEntity === null) return
if (areMultipleEntitiesSelected()) {
for (const entityId of context.operations.getSelectedEntities()) {
for (const entityId of getSelectedEntities()) {
if (entityId === lastEntity.entityId) continue
const entity = context.getEntityOrNull(entityId)!
parentMapper.set(entityId, entity.parent!)
Expand Down Expand Up @@ -250,6 +262,25 @@ export function createGizmoManager(context: SceneContext) {
}
})

const meshPointerDragBehavior = new PointerDragBehavior({
dragPlaneNormal: new Vector3(0, 1, 0)
})

context.scene.onPointerDown = function (_e, pickResult) {
if (lastEntity === null || pickResult.pickedMesh === null || !gizmoManager.freeGizmoEnabled) return
const selectedEntities = getSelectedEntities().map((entityId) => context.getEntityOrNull(entityId)!)
if (selectedEntities.some((entity) => pickResult.pickedMesh!.isDescendantOf(entity))) {
initTransform()
meshPointerDragBehavior.attach(lastEntity as unknown as AbstractMesh)
}
}

context.scene.onPointerUp = function () {
if (lastEntity === null || !gizmoManager.freeGizmoEnabled) return
updateTransform()
meshPointerDragBehavior.detach()
}

if (canvas) {
canvas.addEventListener('pointerenter', () => {
if (movingNode) {
Expand Down Expand Up @@ -291,12 +322,13 @@ export function createGizmoManager(context: SceneContext) {
unsetEntity()
},
getGizmoTypes() {
return [GizmoType.POSITION, GizmoType.ROTATION, GizmoType.SCALE] as const
return [GizmoType.POSITION, GizmoType.ROTATION, GizmoType.SCALE, GizmoType.FREE] as const
},
setGizmoType(type: GizmoType) {
gizmoManager.positionGizmoEnabled = type === GizmoType.POSITION
gizmoManager.rotationGizmoEnabled = type === GizmoType.ROTATION
gizmoManager.scaleGizmoEnabled = type === GizmoType.SCALE
gizmoManager.freeGizmoEnabled = type === GizmoType.FREE
events.emit('change')
},
isPositionGizmoWorldAligned,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,20 @@ export class PatchedRotationGizmo extends RotationGizmo {
}

export class PatchedGizmoManager extends GizmoManager {
protected _gizmosEnabled: {
positionGizmo: boolean
rotationGizmo: boolean
scaleGizmo: boolean
boundingBoxGizmo: boolean
freeGizmo: boolean
} = {
positionGizmo: false,
rotationGizmo: false,
scaleGizmo: false,
boundingBoxGizmo: false,
freeGizmo: false
}

set rotationGizmoEnabled(value: boolean) {
if (value) {
if (!this.gizmos.rotationGizmo) {
Expand All @@ -388,4 +402,12 @@ export class PatchedGizmoManager extends GizmoManager {
get rotationGizmoEnabled() {
return this._gizmosEnabled.rotationGizmo
}

set freeGizmoEnabled(value: boolean) {
this._gizmosEnabled.freeGizmo = value
}

get freeGizmoEnabled(): boolean {
return this._gizmosEnabled.freeGizmo
}
}
23 changes: 23 additions & 0 deletions packages/@dcl/inspector/src/lib/babylon/setup/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { keyState, Keys } from '../decentraland/keys'
import { getAncestors, isAncestor, mapNodes } from '../../sdk/nodes'

let isSnapEnabled = snapManager.isEnabled()
let clickStartTimer: ReturnType<typeof setTimeout>
let isDragging = false

export function initKeyboard(canvas: HTMLCanvasElement, scene: BABYLON.Scene) {
canvas.addEventListener('keydown', (e) => {
Expand All @@ -25,6 +27,17 @@ export function initKeyboard(canvas: HTMLCanvasElement, scene: BABYLON.Scene) {
keyState[e.keyCode] = false
})

// When the canvas lost the focus, clear the special keys state
canvas.addEventListener('blur', () => {
keyState[Keys.KEY_SHIFT] = false
keyState[Keys.KEY_CTRL] = false
})

// Event to store the ctrlKey when the canvas has lost the focus
canvas.addEventListener('contextmenu', (e) => {
keyState[Keys.KEY_CTRL] = e.ctrlKey
})

scene.onPointerObservable.add((e) => {
if (e.type === BABYLON.PointerEventTypes.POINTERDOWN) {
const evt = e.event as PointerEvent
Expand Down Expand Up @@ -69,6 +82,10 @@ export function interactWithScene(
const entity = mesh && findParentEntity(mesh)

if (entity && pointerEvent === 'pointerDown') {
clickStartTimer = setTimeout(() => {
isDragging = true
}, 150) // 150ms to detect if the user is dragging
} else if (entity && pointerEvent === 'pointerUp' && !isDragging) {
const context = entity.context.deref()!
const { operations, engine, editorComponents } = context
const ancestors = getAncestors(engine, entity.entityId)
Expand All @@ -77,4 +94,10 @@ export function interactWithScene(
operations.updateSelectedEntity(entity.entityId, !!keyState[Keys.KEY_CTRL])
void operations.dispatch()
}

// Clear isDragging flag each pointerUp
if (pointerEvent === 'pointerUp') {
clearTimeout(clickStartTimer)
isDragging = false
}
}
3 changes: 2 additions & 1 deletion packages/@dcl/inspector/src/lib/utils/gizmo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export enum GizmoType {
POSITION = 0,
ROTATION = 1,
SCALE = 2
SCALE = 2,
FREE = 3
}

0 comments on commit a988b80

Please sign in to comment.