Entity parenting relations in component.js

I have the following setup in my <a-scene>:

<a-entity
    gltf-model="#room-model"
    position="0 -.01 -4"
    scale="4 4 4"
    reflections
    shadow="cast: false"
    xrextras-pinch-scale="max: 15"
    xrextras-two-finger-rotate
    xrextras-hold-drag="riseHeight: 0.25">
    <a-entity
      gltf-model="#char-model"
      reflections
      shadow
      animation-mixer="clip: *; loop: repeat"></a-entity>
    <a-entity
      gltf-model="#can-model"
      reflections
      shadow
      animation-mixer="clip: *; loop: repeat"></a-entity>
  </a-entity>

The idea is that the room entity can be manipulated with tough gestures with all its contents following along.

I am trying to replicate this setup in my component.js with a setup based on the model-spawn component from the Image Target + Object Manipulation demo project, but I cannot figure out how to properly append my child entities to the parent object.

This is my current setup, granted it might be very inefficient and inherently flawed (I’ll admit, I am far from fully understanding what I am doing here):

const multiSpawnComponent = {
  init() {
    // const model = document.getElementById('model')
    const scene = this.el.sceneEl
    const {object3D} = this.el
    let found = false

    const showObject = ({detail}) => {
      if (!found) {
        console.log('target found')
        // entities for room, can, character
        const room = document.createElement('a-entity')
        const can = document.createElement('a-entity')
        const char = document.createElement('a-entity')

        // camera for easy reference later
        const cam = this.el.sceneEl.camera.el

        const sceneScale = '6 6 6'

        room.setAttribute('visible', 'false')
        room.setAttribute('gltf-model', require('./assets/rb-valo-room.glb'))
        room.setAttribute('shadow', {receive: true})
        room.setAttribute('scale', sceneScale)
        room.setAttribute('reflections', '')

        can.setAttribute('visible', 'false')
        can.setAttribute('gltf-model', require('./assets/rb-valo-can.glb'))
        can.setAttribute('shadow', {receive: true})
        can.setAttribute('scale', sceneScale)
        can.setAttribute('reflections', '')

        char.setAttribute('visible', 'false')
        char.setAttribute('gltf-model', require('./assets/rb-valo-killjoy.glb'))
        char.setAttribute('shadow', {receive: true})
        char.setAttribute('scale', sceneScale)
        char.setAttribute('reflections', '')

        const posX = detail.position.x
        const posY = -0.2
        const posZ = detail.position.z

        this.el.sceneEl.appendChild(room)
        this.el.sceneEl.appendChild(can)
        this.el.sceneEl.appendChild(char)

        char.addEventListener('model-loaded', () => {
          console.log('char loaded')
          char.setAttribute('visible', 'true')
          char.setAttribute('animation-mixer', 'clip: *; loop: repeat')
          can.setAttribute('visible', 'true')
          can.setAttribute('animation-mixer', 'clip: *; loop: repeat')
          room.setAttribute('visible', 'true')

          room.object3D.position.set(posX, posY, posZ)
          can.object3D.position.set(posX, posY, posZ)
          char.object3D.position.set(posX, posY, posZ)

          room.object3D.lookAt(cam.object3D.position.x, room.object3D.position.y, cam.object3D.position.z)
          can.object3D.lookAt(cam.object3D.position.x, can.object3D.position.y, cam.object3D.position.z)
          char.object3D.lookAt(cam.object3D.position.x, char.object3D.position.y, cam.object3D.position.z)
        })

        found = true
      }
    }
    this.el.sceneEl.addEventListener('xrimagefound', showObject)
  },
}
export {multiSpawnComponent}

The component is triggered by an image target.

My issue is that, from what I’ve gathered, event listeners only work once the entity has been appended to the scene and therefore won’t work if the entity is only the child of another appended entity. Since I do not want the other objects in the scene to show up until my character, the by far heaviest object to load, has been loaded, I am somewhat relying on the model-loaded event to trigger the visibility toggle.

This might be a dead end, but if it’s possible, I’d be grateful for any suggestions!

Hey Marcel,

I’d be glad to help you with this! Whenever I hit a roadblock like this, I find it useful to step back from the code and sketch out a simple diagram that highlights the issues.

Here’s an example of what I mean:

This would also give me a better visual to understand and assist you more effectively!

1 Like

Here is my attempt at visually explaining my thought process:

Quick breakdown:

  • User scans the scene for the image target
  • Image Target triggers component
  • Component fetches camera and image target position
  • Component places the room object and its child objects in the scene with visibility set to false
  • The room and prop wait until the character object is loaded before turning visible (and enabling animations, since I’ve had issues with animations being offset in Studio with models taking different time to load)
  • Result: The room object is aligned with the Image Target and can be moved with gestures, character and prop follow the rooms position and orientation

I am using a spawning component because I need my 3D objects to stay in the scene once the target has been scanned, whether or not the image target is still visible.

I hope this helps explaining my issue and doesn’t add to the confusion :sweat_smile:

Well, I managed to fix it, and I feel stupid but it was simpler than I thought…

I just had to use:

scene.appendChild(room)
room.appendChild(char)
room.appendChild(prop)

I couldn’t even tell you what led me so far off track when the answer was right in front of me…

Thank you for your help regardless!

Awesome! Glad you figured it out :slight_smile:

This topic was automatically closed 4 days after the last reply. New replies are no longer allowed.