-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
XR Cursor #5065
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
XR Cursor #5065
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,9 +52,11 @@ module.exports.Component = registerComponent('cursor', { | |
| fuseTimeout: {default: 1500, min: 0}, | ||
| mouseCursorStylesEnabled: {default: true}, | ||
| upEvents: {default: []}, | ||
| rayOrigin: {default: 'entity', oneOf: ['mouse', 'entity']} | ||
| rayOrigin: {default: 'entity', oneOf: ['mouse', 'entity', 'xrselect']} | ||
| }, | ||
|
|
||
| multiple: true, | ||
|
|
||
| init: function () { | ||
| var self = this; | ||
|
|
||
|
|
@@ -63,6 +65,7 @@ module.exports.Component = registerComponent('cursor', { | |
| this.intersectedEl = null; | ||
| this.canvasBounds = document.body.getBoundingClientRect(); | ||
| this.isCursorDown = false; | ||
| this.activeXRInput = null; | ||
|
|
||
| // Debounce. | ||
| this.updateCanvasBounds = utils.debounce(function updateCanvasBounds () { | ||
|
|
@@ -86,6 +89,19 @@ module.exports.Component = registerComponent('cursor', { | |
| this.updateMouseEventListeners(); | ||
| }, | ||
|
|
||
| tick: function () { | ||
| // Update on frame to allow someone to select and mousemove | ||
| var frame = this.el.sceneEl.frame; | ||
| var inputSource = this.activeXRInput; | ||
| if (this.data.rayOrigin === 'xrselect' && frame && inputSource) { | ||
| this.onMouseMove({ | ||
| frame: frame, | ||
| inputSource: inputSource, | ||
| type: 'fakeselectevent' | ||
| }); | ||
| } | ||
| }, | ||
|
|
||
| play: function () { | ||
| this.addEventListeners(); | ||
| }, | ||
|
|
@@ -208,6 +224,12 @@ module.exports.Component = registerComponent('cursor', { | |
| var point; | ||
| var top; | ||
|
|
||
| var frame; | ||
| var inputSource; | ||
| var referenceSpace; | ||
| var pose; | ||
| var transform; | ||
|
|
||
| camera.parent.updateMatrixWorld(); | ||
|
|
||
| // Calculate mouse position based on the canvas element | ||
|
|
@@ -223,7 +245,19 @@ module.exports.Component = registerComponent('cursor', { | |
| mouse.x = (left / bounds.width) * 2 - 1; | ||
| mouse.y = -(top / bounds.height) * 2 + 1; | ||
|
|
||
| if (camera && camera.isPerspectiveCamera) { | ||
| if (this.data.rayOrigin === 'xrselect' && (evt.type === 'selectstart' || evt.type === 'fakeselectevent')) { | ||
| frame = evt.frame; | ||
| inputSource = evt.inputSource; | ||
| referenceSpace = this.el.renderer.xr.getReferenceSpace(); | ||
| pose = frame.getPose(inputSource.targetRaySpace, referenceSpace); | ||
| transform = pose.transform; | ||
| direction.set(0, 0, -1); | ||
| direction.applyQuaternion(transform.orientation); | ||
| origin.copy(transform.position); | ||
| } else if (evt.type === 'fakeselectout') { | ||
| direction.set(0, 1, 0); | ||
| origin.set(0, 9999, 0); | ||
| } else if (camera && camera.isPerspectiveCamera) { | ||
| origin.setFromMatrixPosition(camera.matrixWorld); | ||
| direction.set(mouse.x, mouse.y, 0.5).unproject(camera).sub(origin).normalize(); | ||
| } else if (camera && camera.isOrthographicCamera) { | ||
|
|
@@ -250,6 +284,23 @@ module.exports.Component = registerComponent('cursor', { | |
| evt.preventDefault(); | ||
| } | ||
|
|
||
| if (this.data.rayOrigin === 'xrselect' && evt.type === 'selectstart') { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. disregard. github was not displaying enough context. sorry |
||
| this.activeXRInput = evt.inputSource; | ||
| this.onMouseMove(evt); | ||
| this.el.components.raycaster.checkIntersections(); | ||
|
|
||
| // if something was tapped on don't do ar-hit-test things | ||
| if ( | ||
| this.el.components.raycaster.intersectedEls.length && | ||
| this.el.sceneEl.components['ar-hit-test'] !== undefined && | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the component There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's not so much a dependency just that things can get messy if both are being used so if the user is tapping on a 3d object then it shouldn't also interact with the real world behind it |
||
| this.el.sceneEl.getAttribute('ar-hit-test').enabled | ||
| ) { | ||
| // Cancel the ar-hit-test behaviours and disable the ar-hit-test | ||
| this.el.sceneEl.setAttribute('ar-hit-test', 'enabled', false); | ||
| this.reenableARHitTest = true; | ||
| } | ||
| } | ||
|
|
||
| this.twoWayEmit(EVENTS.MOUSEDOWN); | ||
| this.cursorDownEl = this.intersectedEl; | ||
| }, | ||
|
|
@@ -269,18 +320,31 @@ module.exports.Component = registerComponent('cursor', { | |
| var data = this.data; | ||
| this.twoWayEmit(EVENTS.MOUSEUP); | ||
|
|
||
| if (this.reenableARHitTest === true) { | ||
| this.el.sceneEl.setAttribute('ar-hit-test', 'enabled', true); | ||
| this.reenableARHitTest = undefined; | ||
| } | ||
|
|
||
| // If intersected entity has changed since the cursorDown, still emit mouseUp on the | ||
| // previously cursorUp entity. | ||
| if (this.cursorDownEl && this.cursorDownEl !== this.intersectedEl) { | ||
| this.intersectedEventDetail.intersection = null; | ||
| this.cursorDownEl.emit(EVENTS.MOUSEUP, this.intersectedEventDetail); | ||
| } | ||
|
|
||
| if ((!data.fuse || data.rayOrigin === 'mouse') && | ||
| if ((!data.fuse || data.rayOrigin === 'mouse' || data.rayOrigin === 'xrselect') && | ||
| this.intersectedEl && this.cursorDownEl === this.intersectedEl) { | ||
| this.twoWayEmit(EVENTS.CLICK); | ||
| } | ||
|
|
||
| // if the current xr input stops selecting then make the ray caster point somewhere else | ||
| if (data.rayOrigin === 'xrselect' && this.activeXRInput === evt.inputSource) { | ||
| this.onMouseMove({ | ||
| type: 'fakeselectout' | ||
| }); | ||
| } | ||
|
|
||
| this.activeXRInput = null; | ||
| this.cursorDownEl = null; | ||
| if (evt.type === 'touchend') { evt.preventDefault(); } | ||
| }, | ||
|
|
@@ -363,7 +427,7 @@ module.exports.Component = registerComponent('cursor', { | |
| } | ||
|
|
||
| // Begin fuse if necessary. | ||
| if (data.fuseTimeout === 0 || !data.fuse) { return; } | ||
| if (data.fuseTimeout === 0 || !data.fuse || data.rayOrigin === 'xrselect' || data.rayOrigin === 'mouse') { return; } | ||
| cursorEl.addState(STATES.FUSING); | ||
| this.twoWayEmit(EVENTS.FUSING); | ||
| this.fuseTimeout = setTimeout(function fuse () { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -366,6 +366,11 @@ module.exports.Component = register('ar-hit-test', { | |
| this.makeBBox(); | ||
| }, | ||
| update: function () { | ||
| // If it is disabled it's cleaned up | ||
| if (this.data.enabled === false) { | ||
| this.hitTest = null; | ||
| this.bboxMesh.visible = false; | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have to restore thee if the component is reenabled? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They get reassigned when an xr selectstart happens the mesh is made visible |
||
| if (this.data.target) { | ||
| if (this.data.target.object3D) { | ||
| this.data.target.addEventListener('model-loaded', this.update); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
multiple components for this might feel like an overkill. wondering if we can simplify somehow. maybe
rayOrigin: mousealso considers selectstart? Not a fan either because makesrayOrigin: mousea bit deceiving (not really only about mouse anymore)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can think of a few situations in which a developer may prefer to handle all of their own XR select behaviours but still take advantage of the mouse cursor.
My XR Input Handling library in particular makes it really simple to start building your x-platform own XR cursor using the
entitymethod of the cursor component component.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any other ideas to avoid the multiple component approach?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
have rayOrigin be an array of strings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
array would weird too I think. probably even weirder. Let's go with the multiple components for now but I don't feel super comfortable. Thanks for the patience