Help needed for Image Target + SLAM for Splat

Hello. I have to do something preatty close to this Image Target + SLAM Manipulate 3D Model | 8th Wall Playground | 8th Wall but with a splat (spz) instead of a classic 3d model.
I’ve been banging my head against this for days, looking for templates that do what I need or trying to modify them for that purpose, but with no success. Can anyone give me a hand?

Hi, welcome to the forum!

You can do this pretty easily by doing the following:

  1. Add an Image Target to your scene.
  2. Add the Splat as a child of the Image Target.
  3. Create a Custom Component and attach it to the Splat so you can move it around with touch.
import * as ecs from '@8thwall/ecs'

ecs.registerComponent({
  name: 'Manipulate',
  schema: {
  },
  schemaDefaults: {
  },
  stateMachine: ({world, eid, schemaAttribute, dataAttribute}) => {
    ecs.defineState('default')
      .initial()
      .listen(eid, ecs.input.SCREEN_TOUCH_MOVE, (e) => {
        const {change} = e.data as any

        world.transform.translateWorld(eid, ecs.math.vec3.xyz(change.x * 10, 0, change.y * 10))
      })
  },
})

You’d have to do some more work to make it also do scaling and rotation

Thank you very much for your answer. Actually, I’m not interested in moving it, My main interest is in it to use the tracker for just for scaling and initial position, while the splat spawn on the ground surface trackedy by SLAM.

You should be good just adding the splat as a child of the image target then.

This way it follows the track image, and it vanish when image is not in the view. In the exemple, the 3d model spawn at the feet of the tracker, but then it is anchored on the ground, even when the tracker is not in view.

Ah, a custom component that hides / shows the model but the model isn’t a child is what you want.

import * as ecs from '@8thwall/ecs'

ecs.registerComponent({
  name: 'Hide Splat on Image Target Lost',
  schema: {
    imageTargetName: ecs.string,
  },
  schemaDefaults: {
    imageTargetName: '',
  },
  stateMachine: ({world, eid, schemaAttribute, dataAttribute}) => {
    ecs.defineState('default')
      .initial()
      .listen(world.events.globalId, 'reality.imagefound', (e) => {
        const {name} = e.data as any
        const {imageTargetName} = schemaAttribute.get(eid)

        if (name === imageTargetName) {
          ecs.Hidden.remove(world, eid)
        }
      })
      .listen(world.events.globalId, 'reality.imagelost', (e) => {
        const {name} = e.data as any
        const {imageTargetName} = schemaAttribute.get(eid)

        if (name === imageTargetName) {
          ecs.Hidden.set(world, eid)
        }
      })
  },
})

Add this component to the Splat, and make sure to set the Image Target name in the Inspector to the correct Image Target name.

I’m very sorry I wasn’t able to explain myself clearly. I would like the tracker to be used only for the initial scale, but for the splat to then be anchored to the floor, instead of the tracker, and to remain visible even when the tracker is no longer in view. (This is exactly what the example I provided does, but that one doesn’t seem to support splats. If it were possible to add a splat to that template instead, it would already be perfect, but I haven’t been able to do it).

You can do that by copying the scale reported from the ImageFound event and adding the re-scaling in the above component.

Tried a lot, but the splat always find wrong location. It seams to work right in the simulator, than it get everything wrong in reality.

My last attempt:

import * as ecs from '@8thwall/ecs'

ecs.registerComponent({
  name: 'ImageTargetSlamSplat',
  schema: {
    imageTargetEntity: ecs.eid,
    groundOffset: ecs.f32,
  },
  schemaDefaults: {
    groundOffset: 0,
  },
  data: {
    isInitialized: ecs.boolean,
  },
  stateMachine: ({world, eid, schemaAttribute, dataAttribute}) => {
    ecs.defineState('default')
      .initial()
      .listen(world.events.globalId, 'reality.imagefound', (e) => {
        const {name, position, rotation, scale} = e.data as any
        const {imageTargetEntity, groundOffset} = schemaAttribute.get(eid)
        const {isInitialized} = dataAttribute.get(eid)

        // Get the name of our image target entity to compare
        const imageTarget = ecs.ImageTarget.get(world, imageTargetEntity)
        if (imageTarget && name === imageTarget.target) {
          // Show splat
          ecs.Hidden.remove(world, eid)

          // Position splat only on first detection
          if (!isInitialized) {
            // Set position using image target X,Z but ground level Y
            ecs.Position.set(world, eid, {
              x: position.x,
              y: groundOffset,
              z: position.z,
            })

            // Set rotation (only Y to keep upright)
            ecs.Rotation.set(world, eid, {
              x: 0,
              y: rotation.y,
              z: 0,
            })

            // Set scale
            ecs.Scale.set(world, eid, scale)

            // Anchor to SLAM
            ecs.WorldTracked.set(world, eid, {})

            // Mark as initialized
            dataAttribute.set(eid, {isInitialized: true})

            console.log('Splat positioned and anchored to SLAM')
          }
        }
      })
      .listen(world.events.globalId, 'reality.imagelost', (e) => {
        const {name} = e.data as any
        const {imageTargetEntity} = schemaAttribute.get(eid)
        const {isInitialized} = dataAttribute.get(eid)

        // Get the name of our image target entity to compare
        const imageTarget = ecs.ImageTarget.get(world, imageTargetEntity)
        if (imageTarget && name === imageTarget.target) {
          // Keep visible if already anchored to SLAM, otherwise hide
          if (!isInitialized) {
            ecs.Hidden.set(world, eid)
          }
          // If initialized, splat stays visible thanks to SLAM anchoring
        }
      })
  },
})

Are you seeing any errors in the console? I’m pretty sure the WorldTracked component doesn’t exist. Instead you should be enabling or disabling SLAM on the active Camera.