Skip to content

Commit

Permalink
improve raycasting #67
Browse files Browse the repository at this point in the history
  • Loading branch information
klevron committed Jun 8, 2021
1 parent 995cdf7 commit bea23d4
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 67 deletions.
25 changes: 25 additions & 0 deletions src/core/Object3D.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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)
Expand Down
14 changes: 2 additions & 12 deletions src/core/Raycaster.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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 []
},
Expand Down
59 changes: 40 additions & 19 deletions src/core/usePointer.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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'
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
}
})
}
Expand All @@ -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) {
Expand All @@ -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 })
Expand All @@ -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)
Expand Down
7 changes: 3 additions & 4 deletions src/core/useRaycaster.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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)
}
Expand Down
14 changes: 7 additions & 7 deletions src/core/useThree.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
}

/**
Expand Down Expand Up @@ -74,7 +74,7 @@ export default function useThree(params: ThreeConfigInterface): ThreeInterface {

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

const intersectObjects: IntersectObject[] = []
const intersectObjects: Object3D[] = []

const renderer = createRenderer()

Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
Expand Down
25 changes: 0 additions & 25 deletions src/meshes/Mesh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -35,7 +25,6 @@ const Mesh = defineComponent({
props: {
castShadow: Boolean,
receiveShadow: Boolean,
...pointerProps,
},
setup(): MeshSetupInterface {
return {}
Expand All @@ -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)
},
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit bea23d4

Please sign in to comment.