External Api call for 3d products

I’d like to connect to an API and when the AR experience launches I make a call to the API and load it into the scene

import * as ecs from ‘@8thwall/ecs’

const {XR8} = window as any

ecs.registerComponent({
name: ‘random-model-api’,
schema: {},
schemaDefaults: {},
data: {
modelUrl: ecs.string,
name: ecs.string,
},
add: (world, component) => {
fetch(‘https://immersive-commerce.vercel.app/api/models’)
.then(res => res.json())
.then((data) => {
// Select a random model from the fetched data
const randomIndex = Math.floor(Math.random() * data.length)
const selectedModel = data[randomIndex]

    // Set model data
    component.data.modelUrl = selectedModel.glbUrl
    component.data.name = selectedModel.name

    // Create an entity for the selected model
    const entity = world.createEntity()

    // Add components to the entity
    entity.addComponent(ecs.GltfModel, {
      url: component.data.modelUrl,
      scale: {x: 0.5, y: 0.5, z: 0.5},
    })
    entity.addComponent(ecs.Position, {
      x: 0,
      y: 0,
      z: -3,
    })
    entity.addComponent(ecs.Collider, {
      type: 'box',
    })

    // Check if the GLTF model was loaded
    world.on(ecs.events.GLTF_MODEL_LOADED, (event) => {
      if (event.model === entity.getComponent(ecs.GltfModel).model) {
        console.log(`Model loaded: ${component.data.name}`)
      }
    })

    // Update UI with the model name
    ecs.Ui.mutate(world, component.eid, (cursor) => {
      const text = `Loaded Model: ${component.data.name}`
      cursor.text = text
      cursor.width = text.length * cursor.fontSize * 0.2
      return false
    })
  })
  .catch((e) => {
    console.error('Error fetching 3D models:', e)
  })

// Center the view on click or touch
window.addEventListener('click', XR8.XrController.recenter)
window.addEventListener('touchstart', XR8.XrController.recenter)

},
tick: (world, component) => {
// Logic to be executed on each tick if necessary
},
remove: (world, component) => {
// Cleanup logic here if necessary
},
})

This is my code, but it’s not working I get the error entity.addComponent is not a function

Okay so I’ve got my API call working but the 3D model part isnt’ working

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

const {XR8} = window as any

ecs.registerComponent({
  name: 'random-model-api',
  schema: {},
  schemaDefaults: {},
  data: {
    glbUrl: ecs.string,
    name: ecs.string,
  },
  add: (world, component) => {
    // Fetch the 3D models from the API
    fetch('https://immersive-commerce.vercel.app/api/models')
      .then(res => res.json())
      .then((data) => {
        // console.log('API Response:', JSON.stringify(data, null, 2))

        // Select a random model from the fetched data
        const randomIndex = Math.floor(Math.random() * data.length)
        const selectedModel = data[randomIndex]
        console.log('Selected:', JSON.stringify(selectedModel, null, 2))

        // Set the model URL and name in the component's data
        component.data.glbUrl = selectedModel.glbUrl
        component.data.name = selectedModel.name

        console.log('Selected:', JSON.stringify(selectedModel.glbUrl, null, 2))

        // Create a new entity in ECS
        const entity = world.createEntity()
        console.log('Created entity:', entity)  // Debug entity creation

        if (entity && typeof entity.addComponent === 'function') {
          // Add a GLTF model component to the entity
          entity.addComponent(ecs.GltfModel, {
            url: component.data.glbUrl,
            scale: {x: 0.5, y: 0.5, z: 0.5},
          })
          console.log('GLTF Model Component added with URL:', component.data.glbUrl)

          // Add a position component to the entity
          entity.addComponent(ecs.Position, {
            x: 0,
            y: 0,
            z: -3,
          })

          // Add a collider component for interaction
          entity.addComponent(ecs.Collider, {
            type: 'box',
          })

          // Listen for the model load event
          world.on(ecs.events.GLTF_MODEL_LOADED, (event) => {
            console.log('GLTF_MODEL_LOADED event:', event)
            if (event.model === entity.getComponent(ecs.GltfModel).model) {
              console.log(`Model loaded: ${component.data.name}`)
            }
          })

          // Update the ECS UI to show the model's name
          ecs.Ui.mutate(world, component.eid, (cursor) => {
            const text = `Loaded Model: ${component.data.name}`
            cursor.text = text
            cursor.width = text.length * cursor.fontSize * 0.2

            return false
          })
        } else {
          console.error('Entity creation failed or addComponent is not a function')
        }
      })
      .catch((e) => {
        console.error('Error fetching 3D models:', e)
      })

    // Center the view on click or touch
    window.addEventListener('click', XR8.XrController.recenter)
    window.addEventListener('touchstart', XR8.XrController.recenter)
  },
  tick: (world, component) => {
    // Logic to be executed on each tick if necessary
  },
  remove: (world, component) => {
    // Cleanup: Remove event listeners
    window.removeEventListener('click', XR8.XrController.recenter)
    window.removeEventListener('touchstart', XR8.XrController.recenter)

    // Optionally remove the entity from the ECS world
    const entity = world.getEntity(component.eid)
    if (entity) {
      world.deleteEntity(entity)
    }
  },
})

Hi! I’ll take a look and help you get this figured out. In the meantime I’ve edited your post to add code formatting to make it easier to read.

@Dwayne_Paisley-Marsh

There were a couple issues with your component so I went ahead and mostly re-did it. The edits should provide more context to how ECS works in Niantic Studio.

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

const {XR8} = window as any

ecs.registerComponent({
  name: 'random-model-api',
  schema: {},
  schemaDefaults: {},
  data: {
    glbUrl: ecs.string,
    name: ecs.string,
  },
  add: (world, component) => {
    const {eid} = component

    fetch('https://immersive-commerce.vercel.app/api/models')
      .then(res => res.json())
      .then((data) => {
        const randomIndex = Math.floor(Math.random() * data.length)
        const selectedModel = data[randomIndex]

        component.data.glbUrl = selectedModel.glbUrl
        component.data.name = selectedModel.name

        const entity = world.createEntity()

        ecs.GltfModel.set(world, entity, {
          url: component.data.glbUrl,
        })

        ecs.Scale.set(world, entity, {
          x: 0.5,
          y: 0.5,
          z: 0.5,
        })

        world.setPosition(entity, 0, 0, -3)

        ecs.Collider.set(world, entity, {
          shape: ecs.ColliderShape.Box,
        })

        world.events.addListener(entity, ecs.events.GLTF_MODEL_LOADED, () => {
          console.log(component.data.name)

          ecs.Ui.set(world, eid, {
            text: component.data.name,
            width: component.data.name.length * 16 * 0.2,
          })
        })
      })
      .catch((e) => {
        console.error('Error fetching 3D models:', e)
      })

    // Center the view on click or touch
    window.addEventListener('click', XR8.XrController.recenter)
    window.addEventListener('touchstart', XR8.XrController.recenter)
  },
  tick: (world, component) => {
    // Logic to be executed on each tick if necessary
  },
  remove: (world, component) => {
    // Cleanup: Remove event listeners
    window.removeEventListener('click', XR8.XrController.recenter)
    window.removeEventListener('touchstart', XR8.XrController.recenter)
  },
})

OMG thank you so so much.

This was the cherry on top that I needed.

However, I’d like to load all the models not just a random one :slight_smile:

Would you load all the models into one position?

Hmm no in a circle on the z axis

Try this

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

const {XR8} = window as any

ecs.registerComponent({
  name: 'random-model-api',
  schema: {},
  schemaDefaults: {},
  data: {},
  add: (world, component) => {
    fetch('https://immersive-commerce.vercel.app/api/models')
      .then(res => res.json())
      .then((data) => {
        const numModels = data.length
        const radius = 3  // Radius
        const angleIncrement = (2 * Math.PI) / numModels

        data.forEach((modelData, index) => {
          const angle = angleIncrement * index
          const x = radius * Math.cos(angle)
          const z = radius * Math.sin(angle)
          const y = 0  // Height

          const entity = world.createEntity()

          ecs.GltfModel.set(world, entity, {
            url: modelData.glbUrl,
          })

          ecs.Scale.set(world, entity, {
            x: 0.5,
            y: 0.5,
            z: 0.5,
          })

          world.setPosition(entity, x, y, z)

          ecs.Collider.set(world, entity, {
            shape: ecs.ColliderShape.Box,
          })

          world.events.addListener(entity, ecs.events.GLTF_MODEL_LOADED, () => {
            console.log(modelData.name)

            ecs.Ui.set(world, entity, {
              type: '3d',
              text: modelData.name,
              width: modelData.name.length * 12,
            })
          })
        })
      })
      .catch((e) => {
        console.error('Error fetching 3D models:', e)
      })

    // Center the view on click or touch
    window.addEventListener('click', XR8.XrController.recenter)
    window.addEventListener('touchstart', XR8.XrController.recenter)
  },
  tick: (world, component) => {
    // Logic to be executed on each tick if necessary
  },
  remove: (world, component) => {
    // Cleanup: Remove event listeners
    window.removeEventListener('click', XR8.XrController.recenter)
    window.removeEventListener('touchstart', XR8.XrController.recenter)
  },
})