Skip to content

Commit bea23d4

Browse files
committed
improve raycasting #67
1 parent 995cdf7 commit bea23d4

File tree

6 files changed

+77
-67
lines changed

6 files changed

+77
-67
lines changed

src/core/Object3D.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ export interface Object3DPublicInterface extends ComponentPublicInstance, Object
2626
// return { scene, renderer }
2727
// }
2828

29+
export const pointerProps = {
30+
onPointerEnter: Function,
31+
onPointerOver: Function,
32+
onPointerMove: Function,
33+
onPointerLeave: Function,
34+
onPointerDown: Function,
35+
onPointerUp: Function,
36+
onClick: Function,
37+
}
38+
2939
export interface Vector2PropInterface {
3040
x?: number
3141
y?: number
@@ -59,6 +69,7 @@ export default defineComponent({
5969
props: { type: Object, default: () => ({}) },
6070
disableAdd: { type: Boolean, default: false },
6171
disableRemove: { type: Boolean, default: false },
72+
...pointerProps,
6273
},
6374
setup(): Object3DSetupInterface {
6475
// return object3DSetup()
@@ -74,10 +85,24 @@ export default defineComponent({
7485
},
7586
unmounted() {
7687
if (!this.disableRemove) this.removeFromParent()
88+
if (this.o3d) {
89+
if (this.renderer) this.renderer.three.removeIntersectObject(this.o3d)
90+
}
7791
},
7892
methods: {
7993
initObject3D(o3d: Object3D) {
8094
this.o3d = o3d
95+
o3d.userData.component = this
96+
97+
if (this.onPointerEnter ||
98+
this.onPointerOver ||
99+
this.onPointerMove ||
100+
this.onPointerLeave ||
101+
this.onPointerDown ||
102+
this.onPointerUp ||
103+
this.onClick) {
104+
if (this.renderer) this.renderer.three.addIntersectObject(o3d)
105+
}
81106

82107
bindProp(this, 'position', o3d)
83108
bindProp(this, 'rotation', o3d)

src/core/Raycaster.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { Object3D } from 'three'
21
import { defineComponent, inject, PropType } from 'vue'
3-
import usePointer, { IntersectObject, PointerInterface, PointerIntersectCallbackType } from './usePointer'
2+
import usePointer, { PointerInterface, PointerIntersectCallbackType } from './usePointer'
43
import { RendererInjectionKey, RendererInterface } from './Renderer'
54

65
// eslint-disable-next-line @typescript-eslint/no-empty-function
@@ -39,7 +38,7 @@ export default defineComponent({
3938
this.pointer = usePointer({
4039
camera: renderer.camera,
4140
domElement: renderer.canvas,
42-
intersectObjects: this.getIntersectObjects(),
41+
intersectObjects: () => renderer.scene ? renderer.scene.children : [],
4342
intersectRecursive: this.intersectRecursive,
4443
onIntersectEnter: this.onPointerEnter,
4544
onIntersectOver: this.onPointerOver,
@@ -60,15 +59,6 @@ export default defineComponent({
6059
this.renderer?.offBeforeRender(this.pointer.intersect)
6160
}
6261
},
63-
methods: {
64-
getIntersectObjects() {
65-
if (this.renderer && this.renderer.scene) {
66-
const children = this.renderer.scene.children.filter((c: Object3D) => ['Mesh', 'InstancedMesh'].includes(c.type))
67-
return children as IntersectObject[]
68-
}
69-
return []
70-
},
71-
},
7262
render() {
7363
return []
7464
},

src/core/usePointer.ts

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable @typescript-eslint/no-empty-function */
2-
import { Camera, InstancedMesh, Intersection, Mesh, Vector2, Vector3 } from 'three'
2+
import { Camera, InstancedMesh, Intersection, Object3D, Vector2, Vector3 } from 'three'
33
import useRaycaster from './useRaycaster'
44

55
export interface PointerEventInterface {
@@ -18,7 +18,6 @@ export interface PointerIntersectEventInterface {
1818

1919
export type PointerCallbackType = (e: PointerEventInterface) => void
2020
export type PointerIntersectCallbackType = (e: PointerIntersectEventInterface) => void
21-
export type IntersectObject = Mesh | InstancedMesh
2221

2322
export interface PointerPublicConfigInterface {
2423
intersectMode?: 'frame'
@@ -39,14 +38,14 @@ export interface PointerPublicConfigInterface {
3938
export interface PointerConfigInterface extends PointerPublicConfigInterface {
4039
camera: Camera
4140
domElement: HTMLCanvasElement
42-
intersectObjects: IntersectObject[]
41+
intersectObjects: Object3D[] | (() => Object3D[])
4342
}
4443

4544
export interface PointerInterface {
4645
position: Vector2
4746
positionN: Vector2
4847
positionV3: Vector3
49-
intersectObjects: IntersectObject[]
48+
intersectObjects: Object3D[] | (() => Object3D[])
5049
listeners: boolean
5150
addListeners(cb: void): void
5251
removeListeners(cb: void): void
@@ -117,14 +116,15 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
117116
}
118117

119118
function intersect() {
120-
if (intersectObjects.length) {
121-
const intersects = raycaster.intersect(positionN, intersectObjects, intersectRecursive)
122-
const offObjects: IntersectObject[] = [...intersectObjects]
119+
const _intersectObjects = getIntersectObjects()
120+
if (_intersectObjects.length) {
121+
const intersects = raycaster.intersect(positionN, _intersectObjects, intersectRecursive)
122+
const offObjects: Object3D[] = [..._intersectObjects]
123123
const iMeshes: InstancedMesh[] = []
124124

125125
intersects.forEach(intersect => {
126126
const { object } = intersect
127-
const { component } = object.userData
127+
const component = getComponent(object)
128128

129129
// only once for InstancedMesh
130130
if (object instanceof InstancedMesh) {
@@ -138,27 +138,27 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
138138
const enterEvent: PointerIntersectEventInterface = { ...overEvent, type: 'pointerenter' }
139139
onIntersectOver(overEvent)
140140
onIntersectEnter(enterEvent)
141-
component.onPointerOver?.(overEvent)
142-
component.onPointerEnter?.(enterEvent)
141+
component?.onPointerOver?.(overEvent)
142+
component?.onPointerEnter?.(enterEvent)
143143
}
144144

145145
const moveEvent: PointerIntersectEventInterface = { type: 'pointermove', component, intersect }
146146
onIntersectMove(moveEvent)
147-
component.onPointerMove?.(moveEvent)
147+
component?.onPointerMove?.(moveEvent)
148148

149-
offObjects.splice(offObjects.indexOf((<IntersectObject>object)), 1)
149+
offObjects.splice(offObjects.indexOf((<Object3D>object)), 1)
150150
})
151151

152152
offObjects.forEach(object => {
153-
const { component } = object.userData
153+
const component = getComponent(object)
154154
if (object.userData.over) {
155155
object.userData.over = false
156156
const overEvent: PointerIntersectEventInterface = { type: 'pointerover', over: false, component }
157157
const leaveEvent: PointerIntersectEventInterface = { ...overEvent, type: 'pointerleave' }
158158
onIntersectOver(overEvent)
159159
onIntersectLeave(leaveEvent)
160-
component.onPointerOver?.(overEvent)
161-
component.onPointerLeave?.(leaveEvent)
160+
component?.onPointerOver?.(overEvent)
161+
component?.onPointerLeave?.(leaveEvent)
162162
}
163163
})
164164
}
@@ -177,12 +177,13 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
177177

178178
function pointerClick(event: TouchEvent | MouseEvent) {
179179
updatePosition(event)
180-
if (intersectObjects.length) {
181-
const intersects = raycaster.intersect(positionN, intersectObjects, intersectRecursive)
180+
const _intersectObjects = getIntersectObjects()
181+
if (_intersectObjects.length) {
182+
const intersects = raycaster.intersect(positionN, _intersectObjects, intersectRecursive)
182183
const iMeshes: InstancedMesh[] = []
183184
intersects.forEach(intersect => {
184185
const { object } = intersect
185-
const { component } = object.userData
186+
const component = getComponent(object)
186187

187188
// only once for InstancedMesh
188189
if (object instanceof InstancedMesh) {
@@ -192,7 +193,7 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
192193

193194
const event: PointerIntersectEventInterface = { type: 'click', component, intersect }
194195
onIntersectClick(event)
195-
component.onClick?.(event)
196+
component?.onClick?.(event)
196197
})
197198
}
198199
onClick({ type: 'click', position, positionN, positionV3 })
@@ -203,6 +204,26 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
203204
onLeave({ type: 'pointerleave' })
204205
}
205206

207+
function getComponent(object: Object3D) {
208+
if (object.userData.component) return object.userData.component
209+
210+
let parent = object.parent
211+
while (parent) {
212+
if (parent.userData.component) {
213+
return parent.userData.component
214+
}
215+
parent = parent.parent
216+
}
217+
218+
return undefined
219+
}
220+
221+
function getIntersectObjects() {
222+
if (typeof intersectObjects === 'function') {
223+
return intersectObjects()
224+
} else return intersectObjects
225+
}
226+
206227
function addListeners() {
207228
domElement.addEventListener('mouseenter', pointerEnter)
208229
domElement.addEventListener('mousemove', pointerMove)

src/core/useRaycaster.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { Camera, Intersection, Plane, Raycaster, Vector2, Vector3 } from 'three'
2-
import { IntersectObject } from './usePointer'
1+
import { Camera, Intersection, Object3D, Plane, Raycaster, Vector2, Vector3 } from 'three'
32

43
export interface RaycasterInterface {
54
position: Vector3
65
updatePosition(coords: Vector2): void
7-
intersect(coords: Vector2, objects: IntersectObject[], recursive?: boolean): Intersection[],
6+
intersect(coords: Vector2, objects: Object3D[], recursive?: boolean): Intersection[],
87
}
98

109
export interface RaycasterConfigInterface {
@@ -28,7 +27,7 @@ export default function useRaycaster(options: RaycasterConfigInterface): Raycast
2827
raycaster.ray.intersectPlane(plane, position)
2928
}
3029

31-
const intersect = (coords: Vector2, objects: IntersectObject[], recursive = false) => {
30+
const intersect = (coords: Vector2, objects: Object3D[], recursive = false) => {
3231
raycaster.setFromCamera(coords, camera)
3332
return raycaster.intersectObjects(objects, recursive)
3433
}

src/core/useThree.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { Camera, OrthographicCamera, PerspectiveCamera, Scene, WebGLRenderer } from 'three'
1+
import { Camera, Object3D, OrthographicCamera, PerspectiveCamera, Scene, WebGLRenderer } from 'three'
22
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
33
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
4-
import usePointer, { IntersectObject, PointerConfigInterface, PointerPublicConfigInterface, PointerInterface } from './usePointer'
4+
import usePointer, { PointerConfigInterface, PointerPublicConfigInterface, PointerInterface } from './usePointer'
55

66
export interface SizeInterface {
77
width: number
@@ -39,8 +39,8 @@ export interface ThreeInterface {
3939
render(): void
4040
renderC(): void
4141
setSize(width: number, height: number): void
42-
addIntersectObject(o: IntersectObject): void
43-
removeIntersectObject(o: IntersectObject): void
42+
addIntersectObject(o: Object3D): void
43+
removeIntersectObject(o: Object3D): void
4444
}
4545

4646
/**
@@ -74,7 +74,7 @@ export default function useThree(params: ThreeConfigInterface): ThreeInterface {
7474

7575
const beforeRenderCallbacks: {(): void}[] = []
7676

77-
const intersectObjects: IntersectObject[] = []
77+
const intersectObjects: Object3D[] = []
7878

7979
const renderer = createRenderer()
8080

@@ -191,7 +191,7 @@ export default function useThree(params: ThreeConfigInterface): ThreeInterface {
191191
/**
192192
* add intersect object
193193
*/
194-
function addIntersectObject(o: IntersectObject) {
194+
function addIntersectObject(o: Object3D) {
195195
if (intersectObjects.indexOf(o) === -1) {
196196
intersectObjects.push(o)
197197
}
@@ -204,7 +204,7 @@ export default function useThree(params: ThreeConfigInterface): ThreeInterface {
204204
/**
205205
* remove intersect object
206206
*/
207-
function removeIntersectObject(o: IntersectObject) {
207+
function removeIntersectObject(o: Object3D) {
208208
const i = intersectObjects.indexOf(o)
209209
if (i !== -1) {
210210
intersectObjects.splice(i, 1)

src/meshes/Mesh.ts

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,6 @@ import { BufferGeometry, Material, Mesh as TMesh } from 'three'
33
import Object3D, { Object3DSetupInterface } from '../core/Object3D'
44
import { bindProp } from '../tools'
55

6-
export const pointerProps = {
7-
onPointerEnter: Function,
8-
onPointerOver: Function,
9-
onPointerMove: Function,
10-
onPointerLeave: Function,
11-
onPointerDown: Function,
12-
onPointerUp: Function,
13-
onClick: Function,
14-
}
15-
166
export interface MeshSetupInterface extends Object3DSetupInterface {
177
mesh?: TMesh
188
geometry?: BufferGeometry
@@ -35,7 +25,6 @@ const Mesh = defineComponent({
3525
props: {
3626
castShadow: Boolean,
3727
receiveShadow: Boolean,
38-
...pointerProps,
3928
},
4029
setup(): MeshSetupInterface {
4130
return {}
@@ -52,21 +41,10 @@ const Mesh = defineComponent({
5241
methods: {
5342
initMesh() {
5443
const mesh = new TMesh(this.geometry, this.material)
55-
mesh.userData.component = this
5644

5745
bindProp(this, 'castShadow', mesh)
5846
bindProp(this, 'receiveShadow', mesh)
5947

60-
if (this.onPointerEnter ||
61-
this.onPointerOver ||
62-
this.onPointerMove ||
63-
this.onPointerLeave ||
64-
this.onPointerDown ||
65-
this.onPointerUp ||
66-
this.onClick) {
67-
if (this.renderer) this.renderer.three.addIntersectObject(mesh)
68-
}
69-
7048
this.mesh = mesh
7149
this.initObject3D(mesh)
7250
},
@@ -95,9 +73,6 @@ const Mesh = defineComponent({
9573
},
9674
},
9775
unmounted() {
98-
if (this.mesh) {
99-
if (this.renderer) this.renderer.three.removeIntersectObject(this.mesh)
100-
}
10176
// for predefined mesh (geometry/material are not unmounted)
10277
if (this.geometry) this.geometry.dispose()
10378
if (this.material) this.material.dispose()

0 commit comments

Comments
 (0)