Skip to content

Commit

Permalink
fix: move raycaster logic from nodeOps to TresCanvas
Browse files Browse the repository at this point in the history
  • Loading branch information
alvarosabu committed Apr 5, 2023
1 parent 9cfd312 commit d2200ae
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 60 deletions.
17 changes: 11 additions & 6 deletions playground/src/components/TheBasic.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script setup lang="ts">
import { sRGBEncoding, BasicShadowMap, NoToneMapping } from 'three'
import { sRGBEncoding, BasicShadowMap, NoToneMapping } from 'three'
import { reactive, ref } from 'vue'
import { TresCanvas } from '/@/components/TresCanvas'
import { OrbitControls, TransformControls } from '@tresjs/cientos'
import { OrbitControls, TransformControls } from '@tresjs/cientos'
import { useRenderLoop } from '/@/'
const state = reactive({
Expand All @@ -23,16 +23,21 @@ onLoop(({ elapsed }) => {
if (!sphereRef.value) return
sphereRef.value.position.y += Math.sin(elapsed) * 0.01
})
function onPointerEnter(ev) {
if (ev) {
ev.object.material.color.set('#DFFF45')
}
}
</script>
<template>
<TresCanvas v-bind="state">
<TresPerspectiveCamera :position="[5, 5, 5]" :fov="45" :near="0.1" :far="1000"
:look-at="[0,0,0]" />
<TresPerspectiveCamera :position="[5, 5, 5]" :fov="45" :near="0.1" :far="1000" :look-at="[0, 0, 0]" />
<OrbitControls />
<TresAmbientLight :intensity="0.5" />

<TresMesh ref="sphereRef" :position="[0, 4, 0]" cast-shadow>
<TresSphereGeometry :args="[2,32,32]"/>
<TresMesh ref="sphereRef" :position="[0, 4, 0]" cast-shadow @pointer-enter="onPointerEnter">
<TresSphereGeometry :args="[2, 32, 32]" />
<TresMeshToonMaterial color="cyan" />
</TresMesh>

Expand Down
2 changes: 1 addition & 1 deletion playground/src/components/TheEvents.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ function onPointerLeave(ev) {
<TresPerspectiveCamera :position="[11, 11, 11]" :fov="45" :near="0.1" :far="1000" :look-at="[-8, 3, -3]" />

<TresDirectionalLight :position="[0, 8, 4]" :intensity="0.2" cast-shadow />
<OrbitControls />
<template v-for="x in [-2.5, 0, 2.5]">
<template v-for="y in [-2.5, 0, 2.5]">
<TresMesh
Expand All @@ -53,6 +52,7 @@ function onPointerLeave(ev) {
</TresMesh>
</template>
</template>
<OrbitControls />
<TresAmbientLight :intensity="0.5" />
</TresCanvas>
</template>
2 changes: 1 addition & 1 deletion playground/src/pages/index.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts"></script>
<template>
<Suspense>
<MultipleCanvas />
<TheEvents />
</Suspense>
</template>
27 changes: 27 additions & 0 deletions src/components/TresScene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useCamera, useRenderer, useRenderLoop, useRaycaster, useTres } from '/@
import { extend } from '/@/core/catalogue'
import { RendererPresetsType } from '/@/composables/useRenderer/const'
import { TresObject } from '../types'
import { useEventListener } from '@vueuse/core'

export interface TresSceneProps {
shadows?: boolean
Expand Down Expand Up @@ -80,12 +81,38 @@ export const TresScene = defineComponent<TresSceneProps>({

const { raycaster, pointer } = useRaycaster()

let prevInstance: TresEvent | null = null
let currentInstance: TresEvent | null = null

watchEffect(() => {
if (activeCamera.value) raycaster.value.setFromCamera(pointer.value, activeCamera.value)
})

onLoop(() => {
if (activeCamera.value) renderer.value?.render(scene, activeCamera.value)

if (raycaster.value) {
const intersects = raycaster.value.intersectObjects(scene.children)

if (intersects.length > 0) {
currentInstance = intersects[0]
if (prevInstance === null) {
currentInstance.object.events.onPointerEnter?.(currentInstance)
}
} else {
if (prevInstance !== null) {
currentInstance.object.events.onPointerLeave?.(prevInstance)
currentInstance = null
}
}

prevInstance = currentInstance
}
})

useEventListener(window, 'click', () => {
if (currentInstance === null) return
currentInstance.object.events.onClick?.(currentInstance)
})
}

Expand Down
16 changes: 8 additions & 8 deletions src/composables/useRaycaster/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ import { useTres } from '/@/composables'
import { Raycaster, Vector2 } from 'three'
import { Ref, ref, ShallowRef, shallowRef } from 'vue'

const raycaster = shallowRef(new Raycaster())
const pointer = ref(new Vector2())
const currentInstance = ref(null)

/**
* Raycaster composable return type
*
Expand Down Expand Up @@ -37,10 +33,14 @@ export interface UseRaycasterReturn {
* @return {*} {UseRaycasterReturn}
*/
export function useRaycaster(): UseRaycasterReturn {
const { setState } = useTres()
setState('raycaster', raycaster.value)
setState('pointer', pointer)
setState('currentInstance', currentInstance)
const raycaster = shallowRef(new Raycaster())
const pointer = ref(new Vector2())
const currentInstance = ref(null)
const tres = useTres()
console.log({ tres })
tres.setState('raycaster', raycaster.value)
tres.setState('pointer', pointer)
tres.setState('currentInstance', currentInstance)

function onPointerMove(event: MouseEvent) {
pointer.value.x = (event.clientX / window.innerWidth) * 2 - 1
Expand Down
77 changes: 33 additions & 44 deletions src/core/nodeOps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BufferAttribute, Mesh } from 'three'
import { useCamera, useRaycaster, useRenderLoop, useLogger } from '/@/composables'
import { useCamera, useRaycaster, useRenderLoop, useLogger, useTres } from '/@/composables'
import { RendererOptions } from 'vue'
import { catalogue } from './catalogue'
import { isFunction, useEventListener } from '@vueuse/core'
Expand All @@ -8,13 +8,34 @@ import { isHTMLTag, kebabToCamel } from '../utils'

const { logWarning } = useLogger()

function hasEvents(obj: any) {
for (const prop in obj) {
if (prop.indexOf('on') === 0) {
return true
const onRE = /^on[^a-z]/
export const isOn = (key: string) => onRE.test(key)

export function patchEvent(
el: Element & { _vei?: Record<string, Invoker | undefined> },
rawName: string,
prevValue: EventValue | null,
nextValue: EventValue | null,
instance: ComponentInternalInstance | null = null,
) {
// vei = vue event invokers
const invokers = el._vei || (el._vei = {})
const existingInvoker = invokers[rawName]
if (nextValue && existingInvoker) {
// patch
existingInvoker.value = nextValue
} else {
const [name, options] = parseName(rawName)
if (nextValue) {
// add
const invoker = (invokers[rawName] = createInvoker(nextValue, instance))
addEventListener(el, name, invoker, options)
} else if (existingInvoker) {
// remove
removeEventListener(el, name, existingInvoker, options)
invokers[rawName] = undefined
}
}
return false
}

function noop(fn: string): any {
Expand Down Expand Up @@ -54,6 +75,8 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
else if (instance.isBufferGeometry) instance.attach = 'geometry'
}

instance.events = {}

return instance
},
insert(child, parent, anchor) {
Expand All @@ -72,43 +95,6 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
parent[child.attach] = child
}
}

const { onLoop } = useRenderLoop()

// RayCasting
let prevInstance: TresEvent | null = null
let currentInstance: TresEvent | null = null

if (child && child instanceof Mesh && hasEvents(child)) {
const { raycaster } = useRaycaster()
onLoop(() => {
if (parent?.children && child && raycaster) {
const intersects = raycaster.value.intersectObjects(parent.children)

if (intersects.length > 0 && intersects[0].object.uuid === child.uuid) {
currentInstance = intersects[0]

if (prevInstance === null || prevInstance.object.uuid !== currentInstance?.object.uuid) {
child.onPointerEnter?.(currentInstance)
}

child.onPointerMove?.(currentInstance)
} else {
currentInstance = null
if (prevInstance !== null) {
child.onPointerLeave?.(prevInstance)
}
}

prevInstance = currentInstance
}
})

useEventListener(window, 'click', () => {
if (currentInstance === null) return
child.onClick?.(currentInstance)
})
}
},
remove(node) {
if (!node) return
Expand All @@ -117,7 +103,7 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
parent.removeChild(node)
}
},
patchProp(node, prop, _prevValue, nextValue) {
patchProp(node, prop, _prevValue, nextValue, _isSVG = false, prevChildren, parentComponent) {
if (node) {
let root = node
let key = prop
Expand All @@ -144,6 +130,9 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {

if (!target?.set) root = chain.reduce((acc, key) => acc[kebabToCamel(key)], root)
}
if (isOn(key)) {
node.events[key] = nextValue
}
let value = nextValue
if (value === '') value = true
// Set prop, prefer atomic methods if applicable
Expand Down

0 comments on commit d2200ae

Please sign in to comment.