|
7 | 7 |
|
8 | 8 | import { HandleStore } from '@pmndrs/handle'; |
9 | 9 | import { PointerEventsMap } from '@pmndrs/pointer-events'; |
10 | | -import { createSystem, Entity } from '../ecs/index.js'; |
| 10 | +import { createSystem, Entity, Types } from '../ecs/index.js'; |
11 | 11 | import { Object3D, Object3DEventMap } from '../runtime/index.js'; |
12 | 12 | import { DistanceGrabbable } from './distance-grabbable.js'; |
13 | 13 | import { DistanceGrabHandle, MovementMode, Handle } from './handles.js'; |
@@ -48,20 +48,30 @@ import { TwoHandsGrabbable } from './two-hands-grabbable.js'; |
48 | 48 | * @see {@link TwoHandsGrabbable} |
49 | 49 | * @see {@link DistanceGrabbable} |
50 | 50 | */ |
51 | | -export class GrabSystem extends createSystem({ |
52 | | - oneHandGrabbables: { |
53 | | - required: [OneHandGrabbable], |
| 51 | +export class GrabSystem extends createSystem( |
| 52 | + { |
| 53 | + oneHandGrabbables: { |
| 54 | + required: [OneHandGrabbable], |
| 55 | + }, |
| 56 | + twoHandsGrabbables: { |
| 57 | + required: [TwoHandsGrabbable], |
| 58 | + }, |
| 59 | + distanceGrabbables: { |
| 60 | + required: [DistanceGrabbable], |
| 61 | + }, |
| 62 | + handles: { |
| 63 | + required: [Handle], |
| 64 | + }, |
54 | 65 | }, |
55 | | - twoHandsGrabbables: { |
56 | | - required: [TwoHandsGrabbable], |
| 66 | + { |
| 67 | + /** |
| 68 | + * Controls whether hand pinch gestures are forwarded as squeeze events for grab interactions. |
| 69 | + * Set to `false` to disable pinch-to-grab and require the physical squeeze button instead. |
| 70 | + * @default false |
| 71 | + */ |
| 72 | + useHandPinchForGrab: { type: Types.Boolean, default: false }, |
57 | 73 | }, |
58 | | - distanceGrabbables: { |
59 | | - required: [DistanceGrabbable], |
60 | | - }, |
61 | | - handles: { |
62 | | - required: [Handle], |
63 | | - }, |
64 | | -}) { |
| 74 | +) { |
65 | 75 | init() { |
66 | 76 | // Ensure Handle component is registered in the world before queries rely on it |
67 | 77 | if (!Handle.bitmask) { |
@@ -110,7 +120,27 @@ export class GrabSystem extends createSystem({ |
110 | 120 | this.input.multiPointers.right.toggleSubPointer('grab', true); |
111 | 121 | } |
112 | 122 |
|
113 | | - update(delta: number): void { |
| 123 | + update(delta: number, time: number): void { |
| 124 | + // Forward hand pinch events to squeeze for grab interactions |
| 125 | + // Only forward if the system-level useHandPinchForGrab flag is enabled |
| 126 | + if (this.config.useHandPinchForGrab) { |
| 127 | + (['left', 'right'] as const).forEach((handedness) => { |
| 128 | + if (this.input.isPrimary('hand', handedness)) { |
| 129 | + const timeStamp = time * 1000; |
| 130 | + if (this.input.gamepads[handedness]?.getSelectStart()) { |
| 131 | + this.input.multiPointers[handedness].routeDown('squeeze', 'grab', { |
| 132 | + timeStamp, |
| 133 | + }); |
| 134 | + } |
| 135 | + if (this.input.gamepads[handedness]?.getSelectEnd()) { |
| 136 | + this.input.multiPointers[handedness].routeUp('squeeze', 'grab', { |
| 137 | + timeStamp, |
| 138 | + }); |
| 139 | + } |
| 140 | + } |
| 141 | + }); |
| 142 | + } |
| 143 | + |
114 | 144 | // Unified handle updates |
115 | 145 | this.queries.handles.entities.forEach((entity) => { |
116 | 146 | const h = Handle.data.instance[entity.index] as |
|
0 commit comments