# VPS Mesh & Touch intersection

**URL:** <https://forum.8thwall.com/t/vps-mesh-touch-intersection/5707>\
**Category:** Technical Support\
**Created:** [March 24, 2025, 4:03am UTC](https://forum.8thwall.com/t/vps-mesh-touch-intersection/5707 "2025-03-24T04:03:59Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Alex\_Di\_Guida](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.8thwall.com/alex_di_guida/32/369_2.png) [@Alex\_Di\_Guida](https://forum.8thwall.com/u/Alex_Di_Guida)\
**Post date:** [March 24, 2025, 4:03am UTC](https://forum.8thwall.com/t/vps-mesh-touch-intersection/5707/1 "2025-03-24T04:03:59Z")

</div>

Hi everyone!

I would be very interested in getting feedback on how to make touch intersections work on a VPS mesh, so that I can add a simple 3D box at the intersection coordinates.

Here my component `vps-detect-mesh`:

```auto
import * as ecs from '@8thwall/ecs'
const {THREE} = window as any

const MESH_NAME = 'vps-mesh'
const touchPosition = new THREE.Vector2()
const raycaster = new THREE.Raycaster()

ecs.registerComponent({
  name: 'VPS Detect Mesh',
  schema: {
  },
  data: {
    state: ecs.string,
  },
  add: (world, component) => {
    /** BASE */
    const {eid} = component
    const {scene} = world.three

    let meshFound = false
    let mesh = null

    /** VPS DETECT */
    const foundMesh = (event) => {
      const {bufferGeometry, ...others} = event.data

      const existingMesh = scene.getObjectByName(MESH_NAME)
      if (existingMesh) {
        existingMesh.visible = true // Show the mesh again if it exists
        return
      }

      if (meshFound === true) return

      // Construct VPS mesh
      const vpsMeshMaterial = new THREE.MeshBasicMaterial({
        visible: true,
        transparent: true,
        opacity: 1,
        wireframe: true,
      })
      mesh = new THREE.Mesh(bufferGeometry, vpsMeshMaterial)
      mesh.name = MESH_NAME

      // mesh.quaternion.copy(others.rotation)
      // mesh.position.copy(others.position)

      // const vpsObject = world.three.entityToObject.get(eid)
      // vpsObject.add(mesh) // Test adding mesh as an element's child

      scene.add(mesh) // Test adding mesh in the scene

      meshFound = true

      world.events.dispatch(eid, 'start_drawing')

      const axesHelper = new THREE.AxesHelper(5) 
      mesh.add(axesHelper) // Debug VPS position

      console.log('-> VPS Mesh Found', {others})
    }

    const meshLost = () => {
      const existingMesh = scene.getObjectByName(MESH_NAME)
      if (existingMesh) {
        existingMesh.visible = false // Hide the mesh until we find it again
        world.events.dispatch(component.eid, 'end_drawing')

        console.log('-> VPS Mesh Lost')
      }
    }

    const locationFound = () => {
      console.log('-> VPS Location Found')
    }

    const locationLost = (event) => {
      console.log('-> VPS Location Lost. Please look around to relocate yourself.')
    }

    // Set up listeners for all our VPS events
    world.events.addListener(world.events.globalId, 'reality.meshfound', foundMesh)
    world.events.addListener(world.events.globalId, 'reality.meshlost', meshLost)

    world.events.addListener(world.events.globalId, 'reality.locationfound', locationFound)
    world.events.addListener(world.events.globalId, 'reality.locationlost', locationLost)
  },

  stateMachine: ({world, eid, dataAttribute, schemaAttribute}) => {
    const {renderer: {domElement}, scene, activeCamera} = world.three

    // UPDATE STATE--------------------------------------

    const updateState = (newState) => {
      dataAttribute.set(eid, {state: newState})
    }

    // DEBUG BOX-----------------------------------------

    const debugAddBox = (event) => {
      const vpsMesh = scene.getObjectByName(MESH_NAME)
      if (!vpsMesh) return

      event.preventDefault()

      // Use the renderer's canvas bounds for proper NDC conversion.
      const rect = domElement.getBoundingClientRect()
      touchPosition.x = ((event.touches[0].clientX - rect.left) / rect.width) * 2 - 1
      touchPosition.y = -((event.touches[0].clientY - rect.top) / rect.height) * 2 + 1

      activeCamera.updateMatrixWorld()
      vpsMesh.updateMatrixWorld()

      raycaster.setFromCamera(touchPosition, activeCamera)
      const intersects = raycaster.intersectObject(vpsMesh, true)

      if (intersects.length > 0) {
        const {point} = intersects[0]
        console.log('World Intersection:', point)

        // Small debug box:
        const geometry = new THREE.BoxGeometry(0.05, 0.05, 0.05)
        const material = new THREE.MeshBasicMaterial({color: 'hotpink'})
        const box = new THREE.Mesh(geometry, material)
        box.position.copy(point)

        scene.add(box)
      } else {
        console.log('No intersection found for debug box.')
      }
    }

    // CALLBACKS-------------------------------------------

    const handleTouchStart = (event) => {
      debugAddBox(event)
    }

    // STATES-------------------------------------------

    ecs.defineState('idle')
      .initial()
      .onEnter(() => updateState('idle'))
      .onEvent('start_drawing', 'drawing')

    ecs.defineState('drawing')
      .onEnter(() => {
        updateState('drawing')

        domElement.addEventListener('touchstart', handleTouchStart)
      })
      .onExit(() => {
        domElement.removeEventListener('touchstart', handleTouchStart)
      })
      .onEvent('end_drawing', 'idle')

    // .listen(eid, ecs.input.SCREEN_TOUCH_START, (event) => {
    // console.log('SCREEN_TOUCH_START', {event})
    // })
    // .listen(eid, ecs.input.SCREEN_TOUCH_MOVE, (event) => {
    // console.log('SCREEN_TOUCH_MOVE', {event})
    // })
    // .listen(eid, ecs.input.SCREEN_TOUCH_END, (event) => {
    // console.log('SCREEN_TOUCH_END', {event})
    // })
  },

  remove: (world) => {
    const mesh = world.three.scene.getObjectByName(MESH_NAME)
    if (mesh) {
      world.three.scene.remove(mesh)
      mesh.material.dispose()
      mesh.geometry.dispose()
    }
  },
})

```

This code uses the logic of the VPS mesh being found, and once the mesh is available, I simply want to add a 3D box at the intersection point of my finger touch event and the VPS mesh.

- I commented out the listeners from `ecs.input.SCREEN_TOUCH_...` because they weren’t working. I believe this is because the VPS mesh is being added directly to the scene using `scene.add(mesh)`.
- I created touch events based on the domElement/canvas from the renderer.

The main issue I’m facing right now is that this code isn’t working as expected, only one specific area of the VPS mesh responds to the raycaster intersection, and when the box is placed, it doesn’t align with the finger’s intersection point.

Although, this code is working fine with 8thWall + A-FRAME and with 8thWall + THREE.JS but it is not working with Studio.

If anyone can give me feedback on what I did wrong and help me understand how to make the touch intersection work across the entire mesh and adding the box correctly it would be really helpful.

* * *

You can easily replicate this issue without a VPS mesh: (1) by creating an empty object in your scene, (2) attaching a custom component to it, but not setting a mesh or geometry initially. (3) Instead, create the mesh by code inside the component. For example inside your add function add these lines of code:

```auto
const geometry = new THREE.BoxGeometry(0.05, 0.05, 0.05)
const material = new THREE.MeshBasicMaterial({color: 'hotpink'})
const box = new THREE.Mesh(geometry, material)

```

and then either one or the other way to add your mesh in the scene:

```auto
const object = world.three.entityToObject.get(eid)
object.add(box)

```

or

```auto
scene.add(box)

```

In this case, events like `ecs.input.SCREEN_TOUCH_...` won’t work, and if you create a `THREE.Raycaster`, the intersection won’t work across the entire mesh either.

* * *

I’m sorry for the long message, I just wanted to share everything I’ve experienced in case someone can help. Any feedback would be greatly appreciated. Thank you!

---

<div class="post-metadata">

**Author:** ![Alex\_Di\_Guida](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.8thwall.com/alex_di_guida/32/369_2.png) [@Alex\_Di\_Guida](https://forum.8thwall.com/u/Alex_Di_Guida)\
**Post date:** [March 24, 2025, 10:43pm UTC](https://forum.8thwall.com/t/vps-mesh-touch-intersection/5707/2 "2025-03-24T22:43:58Z")

</div>

Here an example of what I did in the past with the Cloud Editor: it’s using VPS mesh to draw vines on it.

This is something similar that I’m trying to implement now with Studio but because of the problem I explained above I can’t find a way to make it work:

[![](https://us1.discourse-cdn.com/flex019/uploads/x8thwall/original/2X/9/945605dffecfbb061cc6ce3fa8c0943f5c939acf.jpeg "WebAR Vine Explorer #8thwall #augmentedreality #webar #nianticmap #niantic") ](https://www.youtube.com/watch?v=gcipZ3umJmc)

---

<div class="post-metadata">

**Author:** ![GeorgeButler](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.8thwall.com/georgebutler/32/1903_2.png) [@GeorgeButler](https://forum.8thwall.com/u/GeorgeButler)\
**Post date:** [March 25, 2025, 5:47pm UTC](https://forum.8thwall.com/t/vps-mesh-touch-intersection/5707/3 "2025-03-25T17:47:03Z")

</div>

This is awesome! Great question, we’re working on making this significantly easier. I’ll see if I can come up with a ad-hoc solution in the meantime.
