1
0
mirror of https://github.com/troisjs/trois.git synced 2024-11-23 20:02:32 +08:00

improve raycasting #67

This commit is contained in:
Kevin Levron 2021-06-08 19:27:05 +02:00
parent 995cdf7c49
commit bea23d4d4d
6 changed files with 77 additions and 67 deletions

View File

@ -26,6 +26,16 @@ export interface Object3DPublicInterface extends ComponentPublicInstance, Object
// return { scene, renderer }
// }
export const pointerProps = {
onPointerEnter: Function,
onPointerOver: Function,
onPointerMove: Function,
onPointerLeave: Function,
onPointerDown: Function,
onPointerUp: Function,
onClick: Function,
}
export interface Vector2PropInterface {
x?: number
y?: number
@ -59,6 +69,7 @@ export default defineComponent({
props: { type: Object, default: () => ({}) },
disableAdd: { type: Boolean, default: false },
disableRemove: { type: Boolean, default: false },
...pointerProps,
},
setup(): Object3DSetupInterface {
// return object3DSetup()
@ -74,10 +85,24 @@ export default defineComponent({
},
unmounted() {
if (!this.disableRemove) this.removeFromParent()
if (this.o3d) {
if (this.renderer) this.renderer.three.removeIntersectObject(this.o3d)
}
},
methods: {
initObject3D(o3d: Object3D) {
this.o3d = o3d
o3d.userData.component = this
if (this.onPointerEnter ||
this.onPointerOver ||
this.onPointerMove ||
this.onPointerLeave ||
this.onPointerDown ||
this.onPointerUp ||
this.onClick) {
if (this.renderer) this.renderer.three.addIntersectObject(o3d)
}
bindProp(this, 'position', o3d)
bindProp(this, 'rotation', o3d)

View File

@ -1,6 +1,5 @@
import { Object3D } from 'three'
import { defineComponent, inject, PropType } from 'vue'
import usePointer, { IntersectObject, PointerInterface, PointerIntersectCallbackType } from './usePointer'
import usePointer, { PointerInterface, PointerIntersectCallbackType } from './usePointer'
import { RendererInjectionKey, RendererInterface } from './Renderer'
// eslint-disable-next-line @typescript-eslint/no-empty-function
@ -39,7 +38,7 @@ export default defineComponent({
this.pointer = usePointer({
camera: renderer.camera,
domElement: renderer.canvas,
intersectObjects: this.getIntersectObjects(),
intersectObjects: () => renderer.scene ? renderer.scene.children : [],
intersectRecursive: this.intersectRecursive,
onIntersectEnter: this.onPointerEnter,
onIntersectOver: this.onPointerOver,
@ -60,15 +59,6 @@ export default defineComponent({
this.renderer?.offBeforeRender(this.pointer.intersect)
}
},
methods: {
getIntersectObjects() {
if (this.renderer && this.renderer.scene) {
const children = this.renderer.scene.children.filter((c: Object3D) => ['Mesh', 'InstancedMesh'].includes(c.type))
return children as IntersectObject[]
}
return []
},
},
render() {
return []
},

View File

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-empty-function */
import { Camera, InstancedMesh, Intersection, Mesh, Vector2, Vector3 } from 'three'
import { Camera, InstancedMesh, Intersection, Object3D, Vector2, Vector3 } from 'three'
import useRaycaster from './useRaycaster'
export interface PointerEventInterface {
@ -18,7 +18,6 @@ export interface PointerIntersectEventInterface {
export type PointerCallbackType = (e: PointerEventInterface) => void
export type PointerIntersectCallbackType = (e: PointerIntersectEventInterface) => void
export type IntersectObject = Mesh | InstancedMesh
export interface PointerPublicConfigInterface {
intersectMode?: 'frame'
@ -39,14 +38,14 @@ export interface PointerPublicConfigInterface {
export interface PointerConfigInterface extends PointerPublicConfigInterface {
camera: Camera
domElement: HTMLCanvasElement
intersectObjects: IntersectObject[]
intersectObjects: Object3D[] | (() => Object3D[])
}
export interface PointerInterface {
position: Vector2
positionN: Vector2
positionV3: Vector3
intersectObjects: IntersectObject[]
intersectObjects: Object3D[] | (() => Object3D[])
listeners: boolean
addListeners(cb: void): void
removeListeners(cb: void): void
@ -117,14 +116,15 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
}
function intersect() {
if (intersectObjects.length) {
const intersects = raycaster.intersect(positionN, intersectObjects, intersectRecursive)
const offObjects: IntersectObject[] = [...intersectObjects]
const _intersectObjects = getIntersectObjects()
if (_intersectObjects.length) {
const intersects = raycaster.intersect(positionN, _intersectObjects, intersectRecursive)
const offObjects: Object3D[] = [..._intersectObjects]
const iMeshes: InstancedMesh[] = []
intersects.forEach(intersect => {
const { object } = intersect
const { component } = object.userData
const component = getComponent(object)
// only once for InstancedMesh
if (object instanceof InstancedMesh) {
@ -138,27 +138,27 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
const enterEvent: PointerIntersectEventInterface = { ...overEvent, type: 'pointerenter' }
onIntersectOver(overEvent)
onIntersectEnter(enterEvent)
component.onPointerOver?.(overEvent)
component.onPointerEnter?.(enterEvent)
component?.onPointerOver?.(overEvent)
component?.onPointerEnter?.(enterEvent)
}
const moveEvent: PointerIntersectEventInterface = { type: 'pointermove', component, intersect }
onIntersectMove(moveEvent)
component.onPointerMove?.(moveEvent)
component?.onPointerMove?.(moveEvent)
offObjects.splice(offObjects.indexOf((<IntersectObject>object)), 1)
offObjects.splice(offObjects.indexOf((<Object3D>object)), 1)
})
offObjects.forEach(object => {
const { component } = object.userData
const component = getComponent(object)
if (object.userData.over) {
object.userData.over = false
const overEvent: PointerIntersectEventInterface = { type: 'pointerover', over: false, component }
const leaveEvent: PointerIntersectEventInterface = { ...overEvent, type: 'pointerleave' }
onIntersectOver(overEvent)
onIntersectLeave(leaveEvent)
component.onPointerOver?.(overEvent)
component.onPointerLeave?.(leaveEvent)
component?.onPointerOver?.(overEvent)
component?.onPointerLeave?.(leaveEvent)
}
})
}
@ -177,12 +177,13 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
function pointerClick(event: TouchEvent | MouseEvent) {
updatePosition(event)
if (intersectObjects.length) {
const intersects = raycaster.intersect(positionN, intersectObjects, intersectRecursive)
const _intersectObjects = getIntersectObjects()
if (_intersectObjects.length) {
const intersects = raycaster.intersect(positionN, _intersectObjects, intersectRecursive)
const iMeshes: InstancedMesh[] = []
intersects.forEach(intersect => {
const { object } = intersect
const { component } = object.userData
const component = getComponent(object)
// only once for InstancedMesh
if (object instanceof InstancedMesh) {
@ -192,7 +193,7 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
const event: PointerIntersectEventInterface = { type: 'click', component, intersect }
onIntersectClick(event)
component.onClick?.(event)
component?.onClick?.(event)
})
}
onClick({ type: 'click', position, positionN, positionV3 })
@ -203,6 +204,26 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
onLeave({ type: 'pointerleave' })
}
function getComponent(object: Object3D) {
if (object.userData.component) return object.userData.component
let parent = object.parent
while (parent) {
if (parent.userData.component) {
return parent.userData.component
}
parent = parent.parent
}
return undefined
}
function getIntersectObjects() {
if (typeof intersectObjects === 'function') {
return intersectObjects()
} else return intersectObjects
}
function addListeners() {
domElement.addEventListener('mouseenter', pointerEnter)
domElement.addEventListener('mousemove', pointerMove)

View File

@ -1,10 +1,9 @@
import { Camera, Intersection, Plane, Raycaster, Vector2, Vector3 } from 'three'
import { IntersectObject } from './usePointer'
import { Camera, Intersection, Object3D, Plane, Raycaster, Vector2, Vector3 } from 'three'
export interface RaycasterInterface {
position: Vector3
updatePosition(coords: Vector2): void
intersect(coords: Vector2, objects: IntersectObject[], recursive?: boolean): Intersection[],
intersect(coords: Vector2, objects: Object3D[], recursive?: boolean): Intersection[],
}
export interface RaycasterConfigInterface {
@ -28,7 +27,7 @@ export default function useRaycaster(options: RaycasterConfigInterface): Raycast
raycaster.ray.intersectPlane(plane, position)
}
const intersect = (coords: Vector2, objects: IntersectObject[], recursive = false) => {
const intersect = (coords: Vector2, objects: Object3D[], recursive = false) => {
raycaster.setFromCamera(coords, camera)
return raycaster.intersectObjects(objects, recursive)
}

View File

@ -1,7 +1,7 @@
import { Camera, OrthographicCamera, PerspectiveCamera, Scene, WebGLRenderer } from 'three'
import { Camera, Object3D, OrthographicCamera, PerspectiveCamera, Scene, WebGLRenderer } from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import usePointer, { IntersectObject, PointerConfigInterface, PointerPublicConfigInterface, PointerInterface } from './usePointer'
import usePointer, { PointerConfigInterface, PointerPublicConfigInterface, PointerInterface } from './usePointer'
export interface SizeInterface {
width: number
@ -39,8 +39,8 @@ export interface ThreeInterface {
render(): void
renderC(): void
setSize(width: number, height: number): void
addIntersectObject(o: IntersectObject): void
removeIntersectObject(o: IntersectObject): void
addIntersectObject(o: Object3D): void
removeIntersectObject(o: Object3D): void
}
/**
@ -74,7 +74,7 @@ export default function useThree(params: ThreeConfigInterface): ThreeInterface {
const beforeRenderCallbacks: {(): void}[] = []
const intersectObjects: IntersectObject[] = []
const intersectObjects: Object3D[] = []
const renderer = createRenderer()
@ -191,7 +191,7 @@ export default function useThree(params: ThreeConfigInterface): ThreeInterface {
/**
* add intersect object
*/
function addIntersectObject(o: IntersectObject) {
function addIntersectObject(o: Object3D) {
if (intersectObjects.indexOf(o) === -1) {
intersectObjects.push(o)
}
@ -204,7 +204,7 @@ export default function useThree(params: ThreeConfigInterface): ThreeInterface {
/**
* remove intersect object
*/
function removeIntersectObject(o: IntersectObject) {
function removeIntersectObject(o: Object3D) {
const i = intersectObjects.indexOf(o)
if (i !== -1) {
intersectObjects.splice(i, 1)

View File

@ -3,16 +3,6 @@ import { BufferGeometry, Material, Mesh as TMesh } from 'three'
import Object3D, { Object3DSetupInterface } from '../core/Object3D'
import { bindProp } from '../tools'
export const pointerProps = {
onPointerEnter: Function,
onPointerOver: Function,
onPointerMove: Function,
onPointerLeave: Function,
onPointerDown: Function,
onPointerUp: Function,
onClick: Function,
}
export interface MeshSetupInterface extends Object3DSetupInterface {
mesh?: TMesh
geometry?: BufferGeometry
@ -35,7 +25,6 @@ const Mesh = defineComponent({
props: {
castShadow: Boolean,
receiveShadow: Boolean,
...pointerProps,
},
setup(): MeshSetupInterface {
return {}
@ -52,21 +41,10 @@ const Mesh = defineComponent({
methods: {
initMesh() {
const mesh = new TMesh(this.geometry, this.material)
mesh.userData.component = this
bindProp(this, 'castShadow', mesh)
bindProp(this, 'receiveShadow', mesh)
if (this.onPointerEnter ||
this.onPointerOver ||
this.onPointerMove ||
this.onPointerLeave ||
this.onPointerDown ||
this.onPointerUp ||
this.onClick) {
if (this.renderer) this.renderer.three.addIntersectObject(mesh)
}
this.mesh = mesh
this.initObject3D(mesh)
},
@ -95,9 +73,6 @@ const Mesh = defineComponent({
},
},
unmounted() {
if (this.mesh) {
if (this.renderer) this.renderer.three.removeIntersectObject(this.mesh)
}
// for predefined mesh (geometry/material are not unmounted)
if (this.geometry) this.geometry.dispose()
if (this.material) this.material.dispose()