1
0
mirror of https://github.com/troisjs/trois.git synced 2024-11-24 04:12:02 +08:00

improve pointer / raycaster (#34)

This commit is contained in:
Kevin Levron 2021-04-02 22:38:37 +02:00
parent 0a7086398f
commit acbcd0fb50
3 changed files with 106 additions and 42 deletions

40
src/core/Raycaster.js Normal file
View File

@ -0,0 +1,40 @@
import usePointer from './usePointer';
export default {
name: 'Raycaster',
inject: ['three', 'rendererComponent'],
props: {
onPointerEnter: { type: Function, default: () => {} },
onPointerOver: { type: Function, default: () => {} },
onPointerMove: { type: Function, default: () => {} },
onPointerLeave: { type: Function, default: () => {} },
onPointerClick: { type: Function, default: () => {} },
},
mounted() {
this.rendererComponent.onMounted(() => {
this.pointer = usePointer({
camera: this.three.camera,
domElement: this.three.renderer.domElement,
intersectObjects: this.getIntersectObjects(),
onIntersectEnter: this.onPointerEnter,
onIntersectOver: this.onPointerOver,
onIntersectMove: this.onPointerMove,
onIntersectLeave: this.onPointerLeave,
onIntersectClick: this.onPointerClick,
});
this.pointer.addListeners();
});
},
unmounted() {
if (this.pointer) this.pointer.removeListeners();
},
methods: {
getIntersectObjects() {
return this.three.scene.children.filter(e => e.type === 'Mesh');
},
},
render() {
return [];
},
__hmrId: 'Raycaster',
};

View File

@ -8,18 +8,17 @@ export default function usePointer(options) {
intersectObjects, intersectObjects,
touch = true, touch = true,
resetOnEnd = false, resetOnEnd = false,
resetPosition = new Vector2(), resetPosition = new Vector2(Infinity, Infinity),
resetPositionV3 = new Vector3(), resetPositionV3 = new Vector3(Infinity, Infinity, Infinity),
// onEnter = () => {}, onIntersectEnter = () => {},
// onLeave = () => {}, onIntersectOver = () => {},
// onMove = () => {}, onIntersectMove = () => {},
// onDown = () => {}, onIntersectLeave = () => {},
// onUp = () => {}, onIntersectClick = () => {},
// onClick = () => {},
} = options; } = options;
const position = resetPosition.clone(); const position = resetPosition.clone();
const positionN = new Vector2(); const positionN = new Vector2(Infinity, Infinity);
const raycaster = useRaycaster({ camera }); const raycaster = useRaycaster({ camera });
const positionV3 = raycaster.position; const positionV3 = raycaster.position;
@ -28,9 +27,11 @@ export default function usePointer(options) {
position, position,
positionN, positionN,
positionV3, positionV3,
intersectObjects,
listeners: false, listeners: false,
addListeners, addListeners,
removeListeners, removeListeners,
intersect,
}; };
return obj; return obj;
@ -58,12 +59,7 @@ export default function usePointer(options) {
raycaster.updatePosition(positionN); raycaster.updatePosition(positionN);
}; };
function pointerEnter(event) { function intersect() {
updatePosition(event);
// onEnter();
};
function pointerChange() {
if (intersectObjects.length) { if (intersectObjects.length) {
const intersects = raycaster.intersect(positionN, intersectObjects); const intersects = raycaster.intersect(positionN, intersectObjects);
const offObjects = [...intersectObjects]; const offObjects = [...intersectObjects];
@ -81,10 +77,17 @@ export default function usePointer(options) {
if (!object.over) { if (!object.over) {
object.over = true; object.over = true;
if (component.onPointerOver) component.onPointerOver({ over: true, component, intersect }); const overEvent = { type: 'pointerover', over: true, component, intersect };
if (component.onPointerEnter) component.onPointerEnter({ component, intersect }); const enterEvent = { ...overEvent, type: 'pointerenter' };
onIntersectOver(overEvent);
onIntersectEnter(enterEvent);
if (component.onPointerOver) component.onPointerOver(overEvent);
if (component.onPointerEnter) component.onPointerEnter(enterEvent);
} }
if (component.onPointerMove) component.onPointerMove({ component, intersect });
const moveEvent = { type: 'pointermove', component, intersect };
onIntersectMove(moveEvent);
if (component.onPointerMove) component.onPointerMove(moveEvent);
offObjects.splice(offObjects.indexOf(object), 1); offObjects.splice(offObjects.indexOf(object), 1);
}); });
@ -93,34 +96,50 @@ export default function usePointer(options) {
const { component } = object; const { component } = object;
if (object.over) { if (object.over) {
object.over = false; object.over = false;
if (component.onPointerOver) component.onPointerOver({ over: false, component }); const overEvent = { type: 'pointerover', over: false, component };
if (component.onPointerLeave) component.onPointerLeave({ component }); const leaveEvent = { ...overEvent, type: 'pointerleave' };
onIntersectOver(overEvent);
onIntersectLeave(leaveEvent);
if (component.onPointerOver) component.onPointerOver(overEvent);
if (component.onPointerLeave) component.onPointerLeave(leaveEvent);
} }
}); });
} }
}; };
function pointerEnter(event) {
updatePosition(event);
};
function pointerMove(event) { function pointerMove(event) {
updatePosition(event); updatePosition(event);
pointerChange(); intersect();
// onMove();
}; };
function pointerClick(event) { function pointerClick(event) {
updatePosition(event); updatePosition(event);
if (intersectObjects.length) { if (intersectObjects.length) {
const intersects = raycaster.intersect(positionN, intersectObjects); const intersects = raycaster.intersect(positionN, intersectObjects);
const iMeshes = [];
intersects.forEach(intersect => { intersects.forEach(intersect => {
const { object } = intersect; const { object } = intersect;
const { component } = object; const { component } = object;
if (component.onClick) component.onClick({ component, intersect });
// only once for InstancedMesh
if (object instanceof InstancedMesh) {
if (iMeshes.indexOf(object) !== -1) return;
iMeshes.push(object);
}
const event = { type: 'click', component, intersect };
onIntersectClick(event);
if (component.onClick) component.onClick(event);
}); });
} }
}; };
function pointerLeave(event) { function pointerLeave() {
if (resetOnEnd) reset(); if (resetOnEnd) reset();
// onLeave();
}; };
function addListeners() { function addListeners() {
@ -140,6 +159,7 @@ export default function usePointer(options) {
domElement.removeEventListener('mouseenter', pointerEnter); domElement.removeEventListener('mouseenter', pointerEnter);
domElement.removeEventListener('mousemove', pointerMove); domElement.removeEventListener('mousemove', pointerMove);
domElement.removeEventListener('mouseleave', pointerLeave); domElement.removeEventListener('mouseleave', pointerLeave);
domElement.removeEventListener('click', pointerClick);
domElement.removeEventListener('touchstart', pointerEnter); domElement.removeEventListener('touchstart', pointerEnter);
domElement.removeEventListener('touchmove', pointerMove); domElement.removeEventListener('touchmove', pointerMove);

View File

@ -1,9 +1,5 @@
import { import { WebGLRenderer } from 'three';
WebGLRenderer,
} from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import usePointer from './usePointer'; import usePointer from './usePointer';
/** /**
@ -17,7 +13,7 @@ export default function useThree() {
alpha: false, alpha: false,
autoClear: true, autoClear: true,
orbit_ctrl: false, orbit_ctrl: false,
use_pointer: false, pointer: false,
resize: false, resize: false,
width: 300, width: 300,
height: 150, height: 150,
@ -35,6 +31,8 @@ export default function useThree() {
let afterResizeCallbacks = []; let afterResizeCallbacks = [];
let beforeRenderCallbacks = []; let beforeRenderCallbacks = [];
const intersectObjects = [];
// returned object // returned object
const obj = { const obj = {
conf, conf,
@ -43,7 +41,6 @@ export default function useThree() {
cameraCtrl: null, cameraCtrl: null,
scene: null, scene: null,
pointer: null, pointer: null,
intersectObjects: [],
size, size,
init, init,
dispose, dispose,
@ -103,13 +100,18 @@ export default function useThree() {
}; };
function initPointer() { function initPointer() {
obj.pointer = usePointer({ let pointerConf = {
camera: obj.camera, camera: obj.camera,
domElement: obj.renderer.domElement, domElement: obj.renderer.domElement,
intersectObjects: obj.intersectObjects, intersectObjects,
}); };
if (conf.use_pointer || obj.intersectObjects.length) { if (conf.pointer && conf.pointer instanceof Object) {
pointerConf = { ...pointerConf, ...conf.pointer };
}
obj.pointer = usePointer(pointerConf);
if (conf.pointer || intersectObjects.length) {
obj.pointer.addListeners(); obj.pointer.addListeners();
} }
} }
@ -154,6 +156,7 @@ export default function useThree() {
*/ */
function render() { function render() {
if (obj.orbitCtrl) obj.orbitCtrl.update(); if (obj.orbitCtrl) obj.orbitCtrl.update();
// if (obj.pointer) obj.pointer.intersect();
beforeRenderCallbacks.forEach(c => c()); beforeRenderCallbacks.forEach(c => c());
obj.renderer.render(obj.scene, obj.camera); obj.renderer.render(obj.scene, obj.camera);
} }
@ -163,6 +166,7 @@ export default function useThree() {
*/ */
function renderC() { function renderC() {
if (obj.orbitCtrl) obj.orbitCtrl.update(); if (obj.orbitCtrl) obj.orbitCtrl.update();
// if (obj.pointer) obj.pointer.intersect();
beforeRenderCallbacks.forEach(c => c()); beforeRenderCallbacks.forEach(c => c());
obj.composer.render(); obj.composer.render();
} }
@ -171,8 +175,8 @@ export default function useThree() {
* add intersect object * add intersect object
*/ */
function addIntersectObject(o) { function addIntersectObject(o) {
if (obj.intersectObjects.indexOf(o) === -1) { if (intersectObjects.indexOf(o) === -1) {
obj.intersectObjects.push(o); intersectObjects.push(o);
} }
// add listeners if needed // add listeners if needed
if (obj.pointer && !obj.pointer.listeners) { if (obj.pointer && !obj.pointer.listeners) {
@ -184,18 +188,18 @@ export default function useThree() {
* remove intersect object * remove intersect object
*/ */
function removeIntersectObject(o) { function removeIntersectObject(o) {
const i = obj.intersectObjects.indexOf(o); const i = intersectObjects.indexOf(o);
if (i !== -1) { if (i !== -1) {
obj.intersectObjects.splice(i, 1); intersectObjects.splice(i, 1);
} }
// remove listeners if needed // remove listeners if needed
if (obj.pointer && !conf.use_pointer && obj.intersectObjects.length === 0) { if (obj.pointer && !conf.use_pointer && intersectObjects.length === 0) {
obj.pointer.removeListeners(); obj.pointer.removeListeners();
} }
} }
/** /**
* remove listeners * remove listeners and dispose
*/ */
function dispose() { function dispose() {
beforeRenderCallbacks = []; beforeRenderCallbacks = [];