mirror of
https://github.com/troisjs/trois.git
synced 2024-11-24 04:12:02 +08:00
improve raycasting #67
This commit is contained in:
parent
995cdf7c49
commit
bea23d4d4d
@ -26,6 +26,16 @@ export interface Object3DPublicInterface extends ComponentPublicInstance, Object
|
|||||||
// return { scene, renderer }
|
// return { scene, renderer }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
export const pointerProps = {
|
||||||
|
onPointerEnter: Function,
|
||||||
|
onPointerOver: Function,
|
||||||
|
onPointerMove: Function,
|
||||||
|
onPointerLeave: Function,
|
||||||
|
onPointerDown: Function,
|
||||||
|
onPointerUp: Function,
|
||||||
|
onClick: Function,
|
||||||
|
}
|
||||||
|
|
||||||
export interface Vector2PropInterface {
|
export interface Vector2PropInterface {
|
||||||
x?: number
|
x?: number
|
||||||
y?: number
|
y?: number
|
||||||
@ -59,6 +69,7 @@ export default defineComponent({
|
|||||||
props: { type: Object, default: () => ({}) },
|
props: { type: Object, default: () => ({}) },
|
||||||
disableAdd: { type: Boolean, default: false },
|
disableAdd: { type: Boolean, default: false },
|
||||||
disableRemove: { type: Boolean, default: false },
|
disableRemove: { type: Boolean, default: false },
|
||||||
|
...pointerProps,
|
||||||
},
|
},
|
||||||
setup(): Object3DSetupInterface {
|
setup(): Object3DSetupInterface {
|
||||||
// return object3DSetup()
|
// return object3DSetup()
|
||||||
@ -74,10 +85,24 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
if (!this.disableRemove) this.removeFromParent()
|
if (!this.disableRemove) this.removeFromParent()
|
||||||
|
if (this.o3d) {
|
||||||
|
if (this.renderer) this.renderer.three.removeIntersectObject(this.o3d)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
initObject3D(o3d: Object3D) {
|
initObject3D(o3d: Object3D) {
|
||||||
this.o3d = o3d
|
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, 'position', o3d)
|
||||||
bindProp(this, 'rotation', o3d)
|
bindProp(this, 'rotation', o3d)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Object3D } from 'three'
|
|
||||||
import { defineComponent, inject, PropType } from 'vue'
|
import { defineComponent, inject, PropType } from 'vue'
|
||||||
import usePointer, { IntersectObject, PointerInterface, PointerIntersectCallbackType } from './usePointer'
|
import usePointer, { PointerInterface, PointerIntersectCallbackType } from './usePointer'
|
||||||
import { RendererInjectionKey, RendererInterface } from './Renderer'
|
import { RendererInjectionKey, RendererInterface } from './Renderer'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
@ -39,7 +38,7 @@ export default defineComponent({
|
|||||||
this.pointer = usePointer({
|
this.pointer = usePointer({
|
||||||
camera: renderer.camera,
|
camera: renderer.camera,
|
||||||
domElement: renderer.canvas,
|
domElement: renderer.canvas,
|
||||||
intersectObjects: this.getIntersectObjects(),
|
intersectObjects: () => renderer.scene ? renderer.scene.children : [],
|
||||||
intersectRecursive: this.intersectRecursive,
|
intersectRecursive: this.intersectRecursive,
|
||||||
onIntersectEnter: this.onPointerEnter,
|
onIntersectEnter: this.onPointerEnter,
|
||||||
onIntersectOver: this.onPointerOver,
|
onIntersectOver: this.onPointerOver,
|
||||||
@ -60,15 +59,6 @@ export default defineComponent({
|
|||||||
this.renderer?.offBeforeRender(this.pointer.intersect)
|
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() {
|
render() {
|
||||||
return []
|
return []
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* 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'
|
import useRaycaster from './useRaycaster'
|
||||||
|
|
||||||
export interface PointerEventInterface {
|
export interface PointerEventInterface {
|
||||||
@ -18,7 +18,6 @@ export interface PointerIntersectEventInterface {
|
|||||||
|
|
||||||
export type PointerCallbackType = (e: PointerEventInterface) => void
|
export type PointerCallbackType = (e: PointerEventInterface) => void
|
||||||
export type PointerIntersectCallbackType = (e: PointerIntersectEventInterface) => void
|
export type PointerIntersectCallbackType = (e: PointerIntersectEventInterface) => void
|
||||||
export type IntersectObject = Mesh | InstancedMesh
|
|
||||||
|
|
||||||
export interface PointerPublicConfigInterface {
|
export interface PointerPublicConfigInterface {
|
||||||
intersectMode?: 'frame'
|
intersectMode?: 'frame'
|
||||||
@ -39,14 +38,14 @@ export interface PointerPublicConfigInterface {
|
|||||||
export interface PointerConfigInterface extends PointerPublicConfigInterface {
|
export interface PointerConfigInterface extends PointerPublicConfigInterface {
|
||||||
camera: Camera
|
camera: Camera
|
||||||
domElement: HTMLCanvasElement
|
domElement: HTMLCanvasElement
|
||||||
intersectObjects: IntersectObject[]
|
intersectObjects: Object3D[] | (() => Object3D[])
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PointerInterface {
|
export interface PointerInterface {
|
||||||
position: Vector2
|
position: Vector2
|
||||||
positionN: Vector2
|
positionN: Vector2
|
||||||
positionV3: Vector3
|
positionV3: Vector3
|
||||||
intersectObjects: IntersectObject[]
|
intersectObjects: Object3D[] | (() => Object3D[])
|
||||||
listeners: boolean
|
listeners: boolean
|
||||||
addListeners(cb: void): void
|
addListeners(cb: void): void
|
||||||
removeListeners(cb: void): void
|
removeListeners(cb: void): void
|
||||||
@ -117,14 +116,15 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
function intersect() {
|
function intersect() {
|
||||||
if (intersectObjects.length) {
|
const _intersectObjects = getIntersectObjects()
|
||||||
const intersects = raycaster.intersect(positionN, intersectObjects, intersectRecursive)
|
if (_intersectObjects.length) {
|
||||||
const offObjects: IntersectObject[] = [...intersectObjects]
|
const intersects = raycaster.intersect(positionN, _intersectObjects, intersectRecursive)
|
||||||
|
const offObjects: Object3D[] = [..._intersectObjects]
|
||||||
const iMeshes: InstancedMesh[] = []
|
const iMeshes: InstancedMesh[] = []
|
||||||
|
|
||||||
intersects.forEach(intersect => {
|
intersects.forEach(intersect => {
|
||||||
const { object } = intersect
|
const { object } = intersect
|
||||||
const { component } = object.userData
|
const component = getComponent(object)
|
||||||
|
|
||||||
// only once for InstancedMesh
|
// only once for InstancedMesh
|
||||||
if (object instanceof InstancedMesh) {
|
if (object instanceof InstancedMesh) {
|
||||||
@ -138,27 +138,27 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
|
|||||||
const enterEvent: PointerIntersectEventInterface = { ...overEvent, type: 'pointerenter' }
|
const enterEvent: PointerIntersectEventInterface = { ...overEvent, type: 'pointerenter' }
|
||||||
onIntersectOver(overEvent)
|
onIntersectOver(overEvent)
|
||||||
onIntersectEnter(enterEvent)
|
onIntersectEnter(enterEvent)
|
||||||
component.onPointerOver?.(overEvent)
|
component?.onPointerOver?.(overEvent)
|
||||||
component.onPointerEnter?.(enterEvent)
|
component?.onPointerEnter?.(enterEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
const moveEvent: PointerIntersectEventInterface = { type: 'pointermove', component, intersect }
|
const moveEvent: PointerIntersectEventInterface = { type: 'pointermove', component, intersect }
|
||||||
onIntersectMove(moveEvent)
|
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 => {
|
offObjects.forEach(object => {
|
||||||
const { component } = object.userData
|
const component = getComponent(object)
|
||||||
if (object.userData.over) {
|
if (object.userData.over) {
|
||||||
object.userData.over = false
|
object.userData.over = false
|
||||||
const overEvent: PointerIntersectEventInterface = { type: 'pointerover', over: false, component }
|
const overEvent: PointerIntersectEventInterface = { type: 'pointerover', over: false, component }
|
||||||
const leaveEvent: PointerIntersectEventInterface = { ...overEvent, type: 'pointerleave' }
|
const leaveEvent: PointerIntersectEventInterface = { ...overEvent, type: 'pointerleave' }
|
||||||
onIntersectOver(overEvent)
|
onIntersectOver(overEvent)
|
||||||
onIntersectLeave(leaveEvent)
|
onIntersectLeave(leaveEvent)
|
||||||
component.onPointerOver?.(overEvent)
|
component?.onPointerOver?.(overEvent)
|
||||||
component.onPointerLeave?.(leaveEvent)
|
component?.onPointerLeave?.(leaveEvent)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -177,12 +177,13 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
|
|||||||
|
|
||||||
function pointerClick(event: TouchEvent | MouseEvent) {
|
function pointerClick(event: TouchEvent | MouseEvent) {
|
||||||
updatePosition(event)
|
updatePosition(event)
|
||||||
if (intersectObjects.length) {
|
const _intersectObjects = getIntersectObjects()
|
||||||
const intersects = raycaster.intersect(positionN, intersectObjects, intersectRecursive)
|
if (_intersectObjects.length) {
|
||||||
|
const intersects = raycaster.intersect(positionN, _intersectObjects, intersectRecursive)
|
||||||
const iMeshes: InstancedMesh[] = []
|
const iMeshes: InstancedMesh[] = []
|
||||||
intersects.forEach(intersect => {
|
intersects.forEach(intersect => {
|
||||||
const { object } = intersect
|
const { object } = intersect
|
||||||
const { component } = object.userData
|
const component = getComponent(object)
|
||||||
|
|
||||||
// only once for InstancedMesh
|
// only once for InstancedMesh
|
||||||
if (object instanceof InstancedMesh) {
|
if (object instanceof InstancedMesh) {
|
||||||
@ -192,7 +193,7 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
|
|||||||
|
|
||||||
const event: PointerIntersectEventInterface = { type: 'click', component, intersect }
|
const event: PointerIntersectEventInterface = { type: 'click', component, intersect }
|
||||||
onIntersectClick(event)
|
onIntersectClick(event)
|
||||||
component.onClick?.(event)
|
component?.onClick?.(event)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
onClick({ type: 'click', position, positionN, positionV3 })
|
onClick({ type: 'click', position, positionN, positionV3 })
|
||||||
@ -203,6 +204,26 @@ export default function usePointer(options: PointerConfigInterface): PointerInte
|
|||||||
onLeave({ type: 'pointerleave' })
|
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() {
|
function addListeners() {
|
||||||
domElement.addEventListener('mouseenter', pointerEnter)
|
domElement.addEventListener('mouseenter', pointerEnter)
|
||||||
domElement.addEventListener('mousemove', pointerMove)
|
domElement.addEventListener('mousemove', pointerMove)
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { Camera, Intersection, Plane, Raycaster, Vector2, Vector3 } from 'three'
|
import { Camera, Intersection, Object3D, Plane, Raycaster, Vector2, Vector3 } from 'three'
|
||||||
import { IntersectObject } from './usePointer'
|
|
||||||
|
|
||||||
export interface RaycasterInterface {
|
export interface RaycasterInterface {
|
||||||
position: Vector3
|
position: Vector3
|
||||||
updatePosition(coords: Vector2): void
|
updatePosition(coords: Vector2): void
|
||||||
intersect(coords: Vector2, objects: IntersectObject[], recursive?: boolean): Intersection[],
|
intersect(coords: Vector2, objects: Object3D[], recursive?: boolean): Intersection[],
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RaycasterConfigInterface {
|
export interface RaycasterConfigInterface {
|
||||||
@ -28,7 +27,7 @@ export default function useRaycaster(options: RaycasterConfigInterface): Raycast
|
|||||||
raycaster.ray.intersectPlane(plane, position)
|
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)
|
raycaster.setFromCamera(coords, camera)
|
||||||
return raycaster.intersectObjects(objects, recursive)
|
return raycaster.intersectObjects(objects, recursive)
|
||||||
}
|
}
|
||||||
|
@ -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 { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
||||||
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.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 {
|
export interface SizeInterface {
|
||||||
width: number
|
width: number
|
||||||
@ -39,8 +39,8 @@ export interface ThreeInterface {
|
|||||||
render(): void
|
render(): void
|
||||||
renderC(): void
|
renderC(): void
|
||||||
setSize(width: number, height: number): void
|
setSize(width: number, height: number): void
|
||||||
addIntersectObject(o: IntersectObject): void
|
addIntersectObject(o: Object3D): void
|
||||||
removeIntersectObject(o: IntersectObject): void
|
removeIntersectObject(o: Object3D): void
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,7 +74,7 @@ export default function useThree(params: ThreeConfigInterface): ThreeInterface {
|
|||||||
|
|
||||||
const beforeRenderCallbacks: {(): void}[] = []
|
const beforeRenderCallbacks: {(): void}[] = []
|
||||||
|
|
||||||
const intersectObjects: IntersectObject[] = []
|
const intersectObjects: Object3D[] = []
|
||||||
|
|
||||||
const renderer = createRenderer()
|
const renderer = createRenderer()
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ export default function useThree(params: ThreeConfigInterface): ThreeInterface {
|
|||||||
/**
|
/**
|
||||||
* add intersect object
|
* add intersect object
|
||||||
*/
|
*/
|
||||||
function addIntersectObject(o: IntersectObject) {
|
function addIntersectObject(o: Object3D) {
|
||||||
if (intersectObjects.indexOf(o) === -1) {
|
if (intersectObjects.indexOf(o) === -1) {
|
||||||
intersectObjects.push(o)
|
intersectObjects.push(o)
|
||||||
}
|
}
|
||||||
@ -204,7 +204,7 @@ export default function useThree(params: ThreeConfigInterface): ThreeInterface {
|
|||||||
/**
|
/**
|
||||||
* remove intersect object
|
* remove intersect object
|
||||||
*/
|
*/
|
||||||
function removeIntersectObject(o: IntersectObject) {
|
function removeIntersectObject(o: Object3D) {
|
||||||
const i = intersectObjects.indexOf(o)
|
const i = intersectObjects.indexOf(o)
|
||||||
if (i !== -1) {
|
if (i !== -1) {
|
||||||
intersectObjects.splice(i, 1)
|
intersectObjects.splice(i, 1)
|
||||||
|
@ -3,16 +3,6 @@ import { BufferGeometry, Material, Mesh as TMesh } from 'three'
|
|||||||
import Object3D, { Object3DSetupInterface } from '../core/Object3D'
|
import Object3D, { Object3DSetupInterface } from '../core/Object3D'
|
||||||
import { bindProp } from '../tools'
|
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 {
|
export interface MeshSetupInterface extends Object3DSetupInterface {
|
||||||
mesh?: TMesh
|
mesh?: TMesh
|
||||||
geometry?: BufferGeometry
|
geometry?: BufferGeometry
|
||||||
@ -35,7 +25,6 @@ const Mesh = defineComponent({
|
|||||||
props: {
|
props: {
|
||||||
castShadow: Boolean,
|
castShadow: Boolean,
|
||||||
receiveShadow: Boolean,
|
receiveShadow: Boolean,
|
||||||
...pointerProps,
|
|
||||||
},
|
},
|
||||||
setup(): MeshSetupInterface {
|
setup(): MeshSetupInterface {
|
||||||
return {}
|
return {}
|
||||||
@ -52,21 +41,10 @@ const Mesh = defineComponent({
|
|||||||
methods: {
|
methods: {
|
||||||
initMesh() {
|
initMesh() {
|
||||||
const mesh = new TMesh(this.geometry, this.material)
|
const mesh = new TMesh(this.geometry, this.material)
|
||||||
mesh.userData.component = this
|
|
||||||
|
|
||||||
bindProp(this, 'castShadow', mesh)
|
bindProp(this, 'castShadow', mesh)
|
||||||
bindProp(this, 'receiveShadow', 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.mesh = mesh
|
||||||
this.initObject3D(mesh)
|
this.initObject3D(mesh)
|
||||||
},
|
},
|
||||||
@ -95,9 +73,6 @@ const Mesh = defineComponent({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
if (this.mesh) {
|
|
||||||
if (this.renderer) this.renderer.three.removeIntersectObject(this.mesh)
|
|
||||||
}
|
|
||||||
// for predefined mesh (geometry/material are not unmounted)
|
// for predefined mesh (geometry/material are not unmounted)
|
||||||
if (this.geometry) this.geometry.dispose()
|
if (this.geometry) this.geometry.dispose()
|
||||||
if (this.material) this.material.dispose()
|
if (this.material) this.material.dispose()
|
||||||
|
Loading…
Reference in New Issue
Block a user